first working prototype

This commit is contained in:
Blue 2023-07-19 19:12:18 -03:00
parent f16e4b4873
commit aa65cc6851
Signed by: 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
decoded.cpp
flactomp3.cpp
collection.cpp
)
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);
}
void FLACtoMP3::run() {
bool FLACtoMP3::run() {
FLAC__bool ok = FLAC__stream_decoder_process_until_end_of_stream(decoder);
std::cout << "decoding: ";
if (ok) {
std::cout << "succeeded";
if (pcmCounter > 0)
flush();
flush();
int nwrite = lame_encode_flush(encoder, outputBuffer, pcmSize * 2);
fwrite((char*)outputBuffer, nwrite, 1, output);
@ -77,12 +76,9 @@ void FLACtoMP3::run() {
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) {
fclose(output);
@ -96,7 +92,11 @@ void FLACtoMP3::run() {
pcmSize = 0;
flacMaxBlockSize = 0;
outputBufferSize = 0;
return ok;
}
return false;
}
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) {
// Variables for the decompressor itself
struct jpeg_decompress_struct dinfo;
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;
return false;
}
uint64_t mem_size = LAME_MAXALBUMART - 1024 * 16;
uint64_t mem_size = LAME_MAXALBUMART;
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
@ -267,23 +266,19 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
uint32_t rowSize = dinfo.image_width * dinfo.output_components;
uint8_t* row = new uint8_t[rowSize];
while (dinfo.output_scanline < dinfo.output_height) {
jpeg_read_scanlines(&dinfo, &row, 1);
while (dinfo.output_scanline < dinfo.output_height) {
jpeg_read_scanlines(&dinfo, &row, 1);
jpeg_write_scanlines(&cinfo, &row, 1);
}
jpeg_finish_decompress(&dinfo);
jpeg_destroy_decompress(&dinfo);
}
jpeg_finish_decompress(&dinfo);
jpeg_destroy_decompress(&dinfo);
jpeg_finish_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);
std::cout << "deallocating" << std::endl;
delete[] mem;
return result == 0;
@ -353,7 +348,7 @@ bool FLACtoMP3::flush() {
return actuallyWritten == 1;
} else {
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;
} else {
std::cout << "encoding flush failed, error: " << nwrite << std::endl;

View File

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