1
0
forked from blue/mlc

first working prototype

This commit is contained in:
Blue 2023-07-19 19:12:18 -03:00
parent f16e4b4873
commit aa65cc6851
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
6 changed files with 189 additions and 22 deletions

View File

@ -21,6 +21,7 @@ add_executable(mlc
help.cpp help.cpp
decoded.cpp decoded.cpp
flactomp3.cpp flactomp3.cpp
collection.cpp
) )
target_link_libraries(mlc target_link_libraries(mlc

32
README.md Normal file
View File

@ -0,0 +1,32 @@
# MLC - Music Library Compiler
This is a program for compilation of your loseless music library to lossy formats
### Prerequisites
- flac
- lame
- jpeg
### Building
```
$ git clone https://git.macaw.me/blue/mlc
$ cd mlc
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
```
### Usage
```
./mlc path/to/loseless/library path/to/store/lossy/library
```
For now the program is very primitive, it only works to convert `.flac` files (it trusts the suffix) to `.mp3` VBR best possible quality slowest possible conversion algorithm.
MLC keeps file structure, copies all the non `.flac` files, tries to adapt some `.flac` (vorbis) tags to id3v1 and id3v2 of destination `.mp3` file.
For now it's siglethread, no interrupt controll, not even sure converted files are valid, no exotic cases handled, no conversion options can be passed. May be will improve in the futer.

109
collection.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "collection.h"
namespace fs = std::filesystem;
static const std::string flac(".flac");
Collection::Collection(const std::string& path) :
path(fs::canonical(path)),
countMusical(0),
counted(false),
convertedSoFar(0)
{
}
Collection::Collection(const std::filesystem::path& path):
path(fs::canonical(path)),
countMusical(0),
counted(false),
convertedSoFar(0)
{
}
Collection::~Collection() {
}
void Collection::list() const {
if (fs::is_regular_file(path))
return;
for (const fs::directory_entry & entry : fs::directory_iterator(path))
std::cout << entry.path() << std::endl;
}
uint32_t Collection::countMusicFiles() const {
if (counted)
return countMusical;
if (fs::is_regular_file(path)) {
if (isMusic(path))
++countMusical;
} else if (fs::is_directory(path)) {
for (const fs::directory_entry& entry : fs::directory_iterator(path)) {
switch (entry.status().type()) {
case fs::file_type::regular:
if (isMusic(entry.path()))
++countMusical;
break;
case fs::file_type::directory: {
Collection collection(entry.path());
countMusical += collection.countMusicFiles();
} break;
default:
break;
}
}
}
counted = true;
return countMusical;
}
void Collection::convert(const std::string& outPath, std::function<void()> progress) {
convertedSoFar = 0;
fs::path out = fs::absolute(outPath);
if (progress == nullptr) {
progress = std::bind(&Collection::prg, this);
}
fs::create_directories(out);
out = fs::canonical(outPath);
for (const fs::directory_entry& entry : fs::directory_iterator(path)) {
switch (entry.status().type()) {
case fs::file_type::regular: {
fs::path sourcePath = entry.path();
fs::path dstPath = out / sourcePath.filename();
if (isMusic(sourcePath)) {
dstPath.replace_extension(".mp3");
FLACtoMP3 convertor;
convertor.setInputFile(sourcePath);
convertor.setOutputFile(dstPath);
convertor.run();
progress();
} else {
fs::copy_file(sourcePath, dstPath);
}
//std::cout << sourcePath << " => " << dstPath << std::endl;
} break;
case fs::file_type::directory: {
fs::path sourcePath = entry.path();
Collection collection(sourcePath);
fs::path::iterator itr = sourcePath.end();
--itr;
collection.convert(std::string(out / *itr), progress);
} break;
default:
break;
}
}
}
bool Collection::isMusic(const std::filesystem::path& path) {
return path.extension() == flac; //I know, it's primitive yet, but it's the fastest
}
void Collection::prg() const {
std::cout << "\r" << ++convertedSoFar << "/" << countMusicFiles() << std::flush;
}

30
collection.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <string>
#include <iostream>
#include <filesystem>
#include <functional>
#include "flactomp3.h"
class Collection {
public:
Collection(const std::string& path);
Collection(const std::filesystem::path& path);
~Collection();
void list() const;
uint32_t countMusicFiles() const;
void convert(const std::string& outPath, std::function<void()> progress = nullptr);
private:
static bool isMusic(const std::filesystem::path& path);
void prg() const;
private:
std::filesystem::path path;
mutable uint32_t countMusical;
mutable bool counted;
mutable uint32_t convertedSoFar;
};

View File

@ -45,13 +45,12 @@ FLACtoMP3::~FLACtoMP3() {
FLAC__stream_decoder_delete(decoder); FLAC__stream_decoder_delete(decoder);
} }
void FLACtoMP3::run() { bool FLACtoMP3::run() {
FLAC__bool ok = FLAC__stream_decoder_process_until_end_of_stream(decoder); FLAC__bool ok = FLAC__stream_decoder_process_until_end_of_stream(decoder);
std::cout << "decoding: ";
if (ok) { if (ok) {
std::cout << "succeeded"; if (pcmCounter > 0)
flush(); flush();
int nwrite = lame_encode_flush(encoder, outputBuffer, pcmSize * 2); int nwrite = lame_encode_flush(encoder, outputBuffer, pcmSize * 2);
fwrite((char*)outputBuffer, nwrite, 1, output); fwrite((char*)outputBuffer, nwrite, 1, output);
@ -77,12 +76,9 @@ void FLACtoMP3::run() {
fwrite((char*)outputBuffer, vbrTagSize, 1, output); fwrite((char*)outputBuffer, vbrTagSize, 1, output);
} }
} else {
std::cout << "FAILED";
} }
std::cout << std::endl; // std::cout << " state: " << FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(decoder)] << std::endl;
std::cout << " state: " << FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(decoder)] << std::endl;
if (outputInitilized) { if (outputInitilized) {
fclose(output); fclose(output);
@ -96,7 +92,11 @@ void FLACtoMP3::run() {
pcmSize = 0; pcmSize = 0;
flacMaxBlockSize = 0; flacMaxBlockSize = 0;
outputBufferSize = 0; outputBufferSize = 0;
return ok;
} }
return false;
} }
void FLACtoMP3::setInputFile(const std::string& path) { void FLACtoMP3::setInputFile(const std::string& path) {
@ -230,7 +230,6 @@ void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) {
} }
bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) { bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
// Variables for the decompressor itself
struct jpeg_decompress_struct dinfo; struct jpeg_decompress_struct dinfo;
struct jpeg_error_mgr derr; struct jpeg_error_mgr derr;
@ -243,7 +242,7 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
std::cout << "error reading jpeg header" << std::endl; std::cout << "error reading jpeg header" << std::endl;
return false; return false;
} }
uint64_t mem_size = LAME_MAXALBUMART - 1024 * 16; uint64_t mem_size = LAME_MAXALBUMART;
uint8_t *mem = new uint8_t[mem_size]; uint8_t *mem = new uint8_t[mem_size];
dinfo.scale_num = 2; //need to tune it, feels like 500 by 500 is a good size dinfo.scale_num = 2; //need to tune it, feels like 500 by 500 is a good size
@ -277,13 +276,9 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
jpeg_finish_compress(&cinfo); jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo); jpeg_destroy_compress(&cinfo);
// And free the input buffer
delete[] row; delete[] row;
std::cout << "writing " << mem_size << std::endl;
int result = id3tag_set_albumart(encoder, (const char*)mem, mem_size); int result = id3tag_set_albumart(encoder, (const char*)mem, mem_size);
std::cout << "deallocating" << std::endl;
delete[] mem; delete[] mem;
return result == 0; return result == 0;
@ -353,7 +348,7 @@ bool FLACtoMP3::flush() {
return actuallyWritten == 1; return actuallyWritten == 1;
} else { } else {
if (nwrite == 0) { if (nwrite == 0) {
std::cout << "encoding flush encoded 0 bytes, skipping write" << std::endl; //std::cout << "encoding flush encoded 0 bytes, skipping write" << std::endl;
return true; return true;
} else { } else {
std::cout << "encoding flush failed, error: " << nwrite << std::endl; std::cout << "encoding flush failed, error: " << nwrite << std::endl;

View File

@ -17,7 +17,7 @@ public:
void setInputFile(const std::string& path); void setInputFile(const std::string& path);
void setOutputFile(const std::string& path); void setOutputFile(const std::string& path);
void run(); bool run();
private: private:
void processTags(const FLAC__StreamMetadata_VorbisComment& tags); void processTags(const FLAC__StreamMetadata_VorbisComment& tags);