first working prototype
This commit is contained in:
parent
f16e4b4873
commit
aa65cc6851
@ -21,6 +21,7 @@ add_executable(mlc
|
||||
help.cpp
|
||||
decoded.cpp
|
||||
flactomp3.cpp
|
||||
collection.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(mlc
|
||||
|
32
README.md
Normal file
32
README.md
Normal 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
109
collection.cpp
Normal 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
30
collection.h
Normal 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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user