encoding settings
This commit is contained in:
parent
03e7f29d84
commit
5c3a4a592e
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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(encoder, vbr_default);
|
||||||
lame_set_VBR_quality(encoder, 0);
|
lame_set_VBR_quality(encoder, outputQuality);
|
||||||
lame_set_quality(encoder, 0);
|
} 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
|
||||||
);
|
);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user