encoding settings

This commit is contained in:
Blue 2023-10-13 16:10:08 -03:00
parent 03e7f29d84
commit 5c3a4a592e
Signed by: blue
GPG Key ID: 9B203B252A63EE38
8 changed files with 148 additions and 22 deletions

View File

@ -1,7 +1,8 @@
# Changelog # Changelog
## MLC 1.3.3 (UNRELEASED) ## MLC 1.3.3 (October 13, 2023)
- Regex to specify non-music files to copy - Regex to specify non-music files to copy
- Encoding settings (VBR/CBR, encoding quality, output quality)
## MLC 1.3.2 (October 10, 2023) ## MLC 1.3.2 (October 10, 2023)
- A release purely for CI - A release purely for CI

View File

@ -51,3 +51,48 @@
# Allowed value are: [all, none] or regex without any additional syntax, # Allowed value are: [all, none] or regex without any additional syntax,
# for example: filesToCopy cover\.jpe?g # for example: filesToCopy cover\.jpe?g
#filesToCopy all #filesToCopy all
# Encoding quality
# Sets up encoding quality (NOT OUTPUT QUALITY)
# The higher quality the slower the encoding process
# 0 is the highest quality and slowest process
# 9 is the lowest quality and the fastest process fastest
# Allowed values are: [0, 1, 2, ... 9]
#encodingQuality 0
# Output quality
# Sets up output quality
# The higher quality the less information is lonst in compression
# 0 is the highest possible quality for selected mode and type and results in the biggest file
# 9 is the lowest quality but results in the smalest file
# Allowed values are [0, 1, 2, ... 9]
# For the constant bitrate modes (CBR) the following table is valid
# Quality | MP3 |
# --------+-----+--
# 0 | 320 |
# --------+-----+--
# 1 | 288 |
# --------+-----+--
# 2 | 256 |
# --------+-----+--
# 3 | 224 |
# --------+-----+--
# 4 | 192 |
# --------+-----+--
# 5 | 160 |
# --------+-----+--
# 6 | 128 |
# --------+-----+--
# 7 | 96 |
# --------+-----+--
# 8 | 64 |
# --------+-----+--
# 9 | 32 |
#outputQuality 0
# Variable bitrate
# Switches on or off variable bitrate
# VBR files are usually smaller, but not supped to be worse
# in terms of quality. VBR files might be a bit more tricky for the player
# Allowedvalues are: [true, false]
#vbr true

View File

@ -12,6 +12,18 @@ constexpr std::string_view jpeg ("image/jpeg");
const std::map<std::string, std::string> textIdentificationReplacements({ const std::map<std::string, std::string> textIdentificationReplacements({
{"PUBLISHER", "TPUB"} {"PUBLISHER", "TPUB"}
}); });
constexpr std::array<int, 10> bitrates({
320,
288,
256,
224,
192,
160,
128,
96,
64,
32
});
FLACtoMP3::FLACtoMP3(Logger::Severity severity, uint8_t size) : FLACtoMP3::FLACtoMP3(Logger::Severity severity, uint8_t size) :
logger(severity), logger(severity),
@ -46,7 +58,7 @@ bool FLACtoMP3::run() {
if (pcmCounter > 0) if (pcmCounter > 0)
flush(); flush();
int nwrite = lame_encode_flush(encoder, outputBuffer, pcmSize * 2); int nwrite = lame_encode_flush(encoder, outputBuffer, outputBufferSize);
fwrite((char*)outputBuffer, nwrite, 1, output); fwrite((char*)outputBuffer, nwrite, 1, output);
@ -98,9 +110,21 @@ void FLACtoMP3::setOutputFile(const std::string& path) {
outPath = path; outPath = path;
lame_set_VBR(encoder, vbr_default); }
lame_set_VBR_quality(encoder, 0);
lame_set_quality(encoder, 0); void FLACtoMP3::setParameters(unsigned char encodingQuality, unsigned char outputQuality, bool vbr) {
if (vbr) {
logger.info("Encoding to VBR with quality " + std::to_string(outputQuality));
lame_set_VBR(encoder, vbr_default);
lame_set_VBR_quality(encoder, outputQuality);
} else {
int bitrate = bitrates[outputQuality];
logger.info("Encoding to CBR " + std::to_string(bitrate));
lame_set_VBR(encoder, vbr_off);
lame_set_brate(encoder, bitrate);
}
lame_set_quality(encoder, encodingQuality);
} }
bool FLACtoMP3::initializeOutput() { bool FLACtoMP3::initializeOutput() {
@ -306,7 +330,7 @@ bool FLACtoMP3::flush() {
nwrite = lame_encode_buffer_interleaved( nwrite = lame_encode_buffer_interleaved(
encoder, encoder,
pcm, pcm,
pcmCounter, pcmCounter / 2,
outputBuffer, outputBuffer,
outputBufferSize outputBufferSize
); );

View File

@ -8,6 +8,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <map> #include <map>
#include <array>
#include <stdio.h> #include <stdio.h>
#include "logger/accumulator.h" #include "logger/accumulator.h"
@ -19,6 +20,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 setParameters(unsigned char encodingQuality, unsigned char outputQuality, bool vbr);
bool run(); bool run();
std::list<Logger::Message> getHistory() const; std::list<Logger::Message> getHistory() const;

View File

@ -17,6 +17,9 @@ enum class Option {
destination, destination,
parallel, parallel,
filesToCopy, filesToCopy,
encodingQuality,
outputQuality,
vbr,
_optionsSize _optionsSize
}; };
@ -38,13 +41,19 @@ constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> o
"source", "source",
"destination", "destination",
"parallel", "parallel",
"filesToCopy" "filesToCopy",
"encodingQuality",
"outputQuality",
"vbr"
}); });
constexpr std::array<std::string_view, Settings::_typesSize> types({ constexpr std::array<std::string_view, Settings::_typesSize> types({
"mp3" "mp3"
}); });
constexpr unsigned int maxQuality = 9;
constexpr unsigned int minQuality = 0;
bool is_space(char ch){ bool is_space(char ch){
return std::isspace(static_cast<unsigned char>(ch)); return std::isspace(static_cast<unsigned char>(ch));
} }
@ -77,7 +86,10 @@ Settings::Settings(int argc, char ** argv):
logLevel(std::nullopt), logLevel(std::nullopt),
configPath(std::nullopt), configPath(std::nullopt),
threads(std::nullopt), threads(std::nullopt),
nonMusic(std::nullopt) nonMusic(std::nullopt),
encodingQuality(std::nullopt),
outputQuality(std::nullopt),
vbr(std::nullopt)
{ {
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
arguments.push_back(argv[i]); arguments.push_back(argv[i]);
@ -187,6 +199,27 @@ unsigned int Settings::getThreads() const {
return 0; return 0;
} }
unsigned char Settings::getOutputQuality() const {
if (outputQuality.has_value())
return outputQuality.value();
else
return minQuality; //actually, it means max possible quality
}
unsigned char Settings::getEncodingQuality() const {
if (encodingQuality.has_value())
return encodingQuality.value();
else
return minQuality; //actually, it means max possible quality
}
bool Settings::getVBR() const {
if (vbr.has_value())
return vbr.value();
else
return true;
}
void Settings::strip(std::string& line) { void Settings::strip(std::string& line) {
line.erase(line.begin(), std::find_if(line.begin(), line.end(), std::not_fn(is_space))); line.erase(line.begin(), std::find_if(line.begin(), line.end(), std::not_fn(is_space)));
line.erase(std::find_if(line.rbegin(), line.rend(), std::not_fn(is_space)).base(), line.end()); line.erase(std::find_if(line.rbegin(), line.rend(), std::not_fn(is_space)).base(), line.end());
@ -280,6 +313,21 @@ void Settings::readConfigLine(const std::string& line) {
nonMusic = regex; nonMusic = regex;
} }
} break; } break;
case Option::outputQuality: {
unsigned int value;
if (!outputQuality.has_value() && stream >> value)
outputQuality = std::clamp(value, minQuality, maxQuality);
} break;
case Option::encodingQuality: {
unsigned int value;
if (!encodingQuality.has_value() && stream >> value)
encodingQuality = std::clamp(value, minQuality, maxQuality);
} break;
case Option::vbr: {
bool value;
if (!vbr.has_value() && stream >> std::boolalpha >> value)
vbr = value;
} break;
default: default:
break; break;
} }

View File

@ -40,6 +40,9 @@ public:
Action getAction() const; Action getAction() const;
unsigned int getThreads() const; unsigned int getThreads() const;
bool matchNonMusic(const std::string& fileName) const; bool matchNonMusic(const std::string& fileName) const;
unsigned char getEncodingQuality() const;
unsigned char getOutputQuality() const;
bool getVBR() const;
bool readConfigFile(); bool readConfigFile();
void readConfigLine(const std::string& line); void readConfigLine(const std::string& line);
@ -66,4 +69,7 @@ private:
std::optional<std::string> configPath; std::optional<std::string> configPath;
std::optional<unsigned int> threads; std::optional<unsigned int> threads;
std::optional<std::regex> nonMusic; std::optional<std::regex> nonMusic;
std::optional<unsigned char> encodingQuality;
std::optional<unsigned char> outputQuality;
std::optional<bool> vbr;
}; };

View File

@ -119,14 +119,15 @@ unsigned int TaskManager::getCompleteTasks() const {
return completeTasks; return completeTasks;
} }
TaskManager::JobResult TaskManager::execute(const Job& job) { TaskManager::JobResult TaskManager::execute(Job& job) {
switch (job.type) { switch (job.type) {
case Job::copy: case Job::copy:
return copyJob(job, settings->getLogLevel()); return copyJob(job, settings);
case Job::convert: case Job::convert:
switch (settings->getType()) { switch (settings->getType()) {
case Settings::mp3: case Settings::mp3:
return mp3Job(job, settings->getLogLevel()); job.destination.replace_extension("mp3");
return mp3Job(job, settings);
default: default:
break; break;
} }
@ -162,19 +163,18 @@ void TaskManager::printResilt(const TaskManager::Job& job, const TaskManager::Jo
); );
} }
TaskManager::JobResult TaskManager::mp3Job( TaskManager::JobResult TaskManager::mp3Job(const TaskManager::Job& job, const std::shared_ptr<Settings>& settings) {
const TaskManager::Job& job, FLACtoMP3 convertor(settings->getLogLevel());
Logger::Severity logLevel)
{
FLACtoMP3 convertor(logLevel);
convertor.setInputFile(job.source); convertor.setInputFile(job.source);
convertor.setOutputFile(job.destination.string() + ".mp3"); convertor.setOutputFile(job.destination);
convertor.setParameters(settings->getEncodingQuality(), settings->getOutputQuality(), settings->getVBR());
bool result = convertor.run(); bool result = convertor.run();
return {result, convertor.getHistory()}; return {result, convertor.getHistory()};
} }
TaskManager::JobResult TaskManager::copyJob(const TaskManager::Job& job, Logger::Severity logLevel) { TaskManager::JobResult TaskManager::copyJob(const TaskManager::Job& job, const std::shared_ptr<Settings>& settings) {
(void)(logLevel); (void)(settings);
bool success = std::filesystem::copy_file( bool success = std::filesystem::copy_file(
job.source, job.source,
job.destination, job.destination,

View File

@ -35,10 +35,10 @@ public:
private: private:
void loop(); void loop();
JobResult execute(const Job& job); JobResult execute(Job& job);
void printResilt(const Job& job, const JobResult& result); void printResilt(const Job& job, const JobResult& result);
static JobResult mp3Job(const Job& job, Logger::Severity logLevel); static JobResult mp3Job(const Job& job, const std::shared_ptr<Settings>& settings);
static JobResult copyJob(const Job& job, Logger::Severity logLevel); static JobResult copyJob(const Job& job, const std::shared_ptr<Settings>& settings);
private: private:
std::shared_ptr<Settings> settings; std::shared_ptr<Settings> settings;