TagLib now helps with the tags
This commit is contained in:
parent
2c913d5f54
commit
d6c1cbde1a
@ -1,5 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## MLC 1.2.0 (UNRELEASED)
|
||||||
|
- Better way of handling tags using TagLib
|
||||||
|
|
||||||
## MLC 1.1.0 (July 23, 2023)
|
## MLC 1.1.0 (July 23, 2023)
|
||||||
- New logging system
|
- New logging system
|
||||||
- Artist, Album artist, Album and Title are now utf16 encoded, should fix broten titles
|
- Artist, Album artist, Album and Title are now utf16 encoded, should fix broten titles
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(
|
project(
|
||||||
mlc
|
mlc
|
||||||
VERSION 1.0.1
|
VERSION 1.2.0
|
||||||
DESCRIPTION "Media Library Compiler: rips your media library to a lossy compilation"
|
DESCRIPTION "Media Library Compiler: rips your media library to a lossy compilation"
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
@ -12,10 +12,13 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
find_package(LAME REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(FLAC REQUIRED)
|
find_package(FLAC REQUIRED)
|
||||||
find_package(JPEG REQUIRED)
|
find_package(JPEG REQUIRED)
|
||||||
|
|
||||||
|
pkg_check_modules(LAME REQUIRED IMPORTED_TARGET lame)
|
||||||
|
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib)
|
||||||
|
|
||||||
add_executable(mlc
|
add_executable(mlc
|
||||||
main.cpp
|
main.cpp
|
||||||
help.cpp
|
help.cpp
|
||||||
@ -27,9 +30,10 @@ add_executable(mlc
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(mlc
|
target_link_libraries(mlc
|
||||||
${LAME_LIBRARIES}
|
FLAC::FLAC
|
||||||
FLAC
|
PkgConfig::LAME
|
||||||
JPEG::JPEG
|
JPEG::JPEG
|
||||||
|
PkgConfig::TAGLIB
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS mlc RUNTIME DESTINATION bin)
|
install(TARGETS mlc RUNTIME DESTINATION bin)
|
||||||
|
303
flactomp3.cpp
303
flactomp3.cpp
@ -2,31 +2,13 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
constexpr uint16_t flacDefaultMaxBlockSize = 4096;
|
#include <tpropertymap.h>
|
||||||
constexpr uint16_t id3v1TagSize = 128;
|
#include <attachedpictureframe.h>
|
||||||
// constexpr std::wstring_view BOM ({(0xFEFF}); //one day...
|
|
||||||
|
|
||||||
constexpr std::string_view title ("TITLE=");
|
constexpr uint16_t flacDefaultMaxBlockSize = 4096;
|
||||||
constexpr std::string_view artist ("ARTIST=");
|
|
||||||
constexpr std::string_view album ("ALBUM=");
|
|
||||||
constexpr std::string_view comment ("COMMENT=");
|
|
||||||
constexpr std::string_view genre ("GENRE=");
|
|
||||||
constexpr std::string_view track ("TRACKNUMBER=");
|
|
||||||
constexpr std::string_view date ("DATE=");
|
|
||||||
|
|
||||||
constexpr std::string_view jpeg ("image/jpeg");
|
constexpr std::string_view jpeg ("image/jpeg");
|
||||||
|
|
||||||
|
|
||||||
static const std::map<std::string, const std::string> knownTags {
|
|
||||||
{"ALBUMARTIST=", "TPE2="},
|
|
||||||
{"PUBLISHER=", "TPUB="},
|
|
||||||
{"LENGTH=", "TLEN="},
|
|
||||||
{"ISRC=", "TSRC="},
|
|
||||||
{"DISCNUMBER=", "TPOS="},
|
|
||||||
{"BPM=", "TBPM="},
|
|
||||||
{"LYRICS=", "USLT="} //but it's not supported in LAME
|
|
||||||
};
|
|
||||||
|
|
||||||
FLACtoMP3::FLACtoMP3(Severity severity, uint8_t size) :
|
FLACtoMP3::FLACtoMP3(Severity severity, uint8_t size) :
|
||||||
Loggable(severity),
|
Loggable(severity),
|
||||||
inPath(),
|
inPath(),
|
||||||
@ -44,7 +26,7 @@ FLACtoMP3::FLACtoMP3(Severity severity, uint8_t size) :
|
|||||||
outputBufferSize(0),
|
outputBufferSize(0),
|
||||||
outputInitilized(false),
|
outputInitilized(false),
|
||||||
downscaleAlbumArt(false),
|
downscaleAlbumArt(false),
|
||||||
usc2convertor()
|
id3v2tag()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,30 +45,9 @@ bool FLACtoMP3::run() {
|
|||||||
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);
|
||||||
|
|
||||||
if (downscaleAlbumArt) {
|
|
||||||
fileSize = ftell(output);
|
|
||||||
lame_mp3_tags_fid(encoder, output);
|
|
||||||
} else {
|
|
||||||
int tag1Size = lame_get_id3v1_tag(encoder, outputBuffer, id3v1TagSize);
|
|
||||||
if (tag1Size > id3v1TagSize)
|
|
||||||
log(warning, "couldn't write id3v1 tag");
|
|
||||||
else
|
|
||||||
fwrite((char*)outputBuffer, tag1Size, 1, output);
|
|
||||||
|
|
||||||
fileSize = ftell(output);
|
fileSize = ftell(output);
|
||||||
fseek(output, 0, SEEK_SET);
|
lame_mp3_tags_fid(encoder, output);
|
||||||
int tag2Size = lame_get_id3v2_tag(encoder, outputBuffer, outputBufferSize);
|
|
||||||
if (tag2Size > outputBufferSize) {
|
|
||||||
log(Loggable::error, "couldn't write id3v2 tag");
|
|
||||||
} else
|
|
||||||
fwrite((char*)outputBuffer, tag2Size, 1, output);
|
|
||||||
|
|
||||||
int vbrTagSize = lame_get_lametag_frame(encoder, outputBuffer, outputBufferSize);
|
|
||||||
if (vbrTagSize > outputBufferSize)
|
|
||||||
log(Loggable::error, "couldn't write vbr tag");
|
|
||||||
|
|
||||||
fwrite((char*)outputBuffer, vbrTagSize, 1, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
@ -136,8 +97,6 @@ void FLACtoMP3::setOutputFile(const std::string& path) {
|
|||||||
lame_set_VBR(encoder, vbr_default);
|
lame_set_VBR(encoder, vbr_default);
|
||||||
lame_set_VBR_quality(encoder, 0);
|
lame_set_VBR_quality(encoder, 0);
|
||||||
lame_set_quality(encoder, 0);
|
lame_set_quality(encoder, 0);
|
||||||
if (!downscaleAlbumArt)
|
|
||||||
lame_set_write_id3tag_automatic(encoder, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FLACtoMP3::initializeOutput() {
|
bool FLACtoMP3::initializeOutput() {
|
||||||
@ -165,14 +124,8 @@ bool FLACtoMP3::initializeOutput() {
|
|||||||
pcmSize = lame_get_num_channels(encoder) * flacMaxBlockSize * bufferMultiplier;
|
pcmSize = lame_get_num_channels(encoder) * flacMaxBlockSize * bufferMultiplier;
|
||||||
outputBufferSize = pcmSize / 2;
|
outputBufferSize = pcmSize / 2;
|
||||||
|
|
||||||
if (!downscaleAlbumArt) {
|
TagLib::ByteVector vector = id3v2tag.render();
|
||||||
int tag2Size = lame_get_id3v2_tag(encoder, nullptr, 0);
|
fwrite((const char*)vector.data(), vector.size(), 1, output);
|
||||||
int vbrTagSize = lame_get_lametag_frame(encoder, nullptr, 0);
|
|
||||||
|
|
||||||
fseek(output, tag2Size + vbrTagSize, SEEK_SET); //reserve place for tags
|
|
||||||
if (tag2Size > outputBufferSize)
|
|
||||||
outputBufferSize = tag2Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pcm = new int16_t[pcmSize];
|
pcm = new int16_t[pcmSize];
|
||||||
outputBuffer = new uint8_t[outputBufferSize];
|
outputBuffer = new uint8_t[outputBufferSize];
|
||||||
@ -192,47 +145,30 @@ void FLACtoMP3::processInfo(const FLAC__StreamMetadata_StreamInfo& info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
|
void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
|
||||||
bool artistSet = false; //in vorbis comment there could be more then 1 artist tag
|
TagLib::PropertyMap props;
|
||||||
for (FLAC__uint32 i = 0; i < tags.num_comments; ++i) { //usualy it's about guested and fetured artists
|
for (FLAC__uint32 i = 0; i < tags.num_comments; ++i) {
|
||||||
const FLAC__StreamMetadata_VorbisComment_Entry& entry = tags.comments[i]; //gonna keep only the first one for now
|
const FLAC__StreamMetadata_VorbisComment_Entry& entry = tags.comments[i];
|
||||||
std::string_view comm((const char*)entry.entry);
|
std::string_view comm((const char*)entry.entry);
|
||||||
if (comm.find(title) == 0) {
|
std::string_view::size_type ePos = comm.find("=");
|
||||||
setTagTitle(comm.substr(title.size()));
|
if (ePos == std::string_view::npos) {
|
||||||
} else if (comm.find(artist) == 0) {
|
log(warning, "couldn't understand tag (" + std::string(comm) + "), symbol '=' is missing, skipping");
|
||||||
if (!artistSet) {
|
continue;
|
||||||
setTagArtist(comm.substr(artist.size()));
|
|
||||||
artistSet = true;
|
|
||||||
} else {
|
|
||||||
log(minor, "more than one artist tag, ignoring " + std::string(comm.substr(artist.size())));
|
|
||||||
}
|
|
||||||
} else if (comm.find(album) == 0) {
|
|
||||||
setTagAlbum(comm.substr(album.size()));
|
|
||||||
} else if (comm.find(comment) == 0) {
|
|
||||||
id3tag_set_comment(encoder, comm.substr(comment.size()).data());
|
|
||||||
} else if (comm.find(genre) == 0) {
|
|
||||||
id3tag_set_genre(encoder, comm.substr(genre.size()).data());
|
|
||||||
} else if (comm.find(track) == 0) {
|
|
||||||
id3tag_set_track(encoder, comm.substr(track.size()).data());
|
|
||||||
} else if (comm.find(date) == 0) {
|
|
||||||
std::string_view fullDate = comm.substr(date.size());
|
|
||||||
if (fullDate.size() == 10) { //1999-11-11 kind of string
|
|
||||||
std::string_view month = fullDate.substr(5, std::size_t(2));
|
|
||||||
std::string_view day = fullDate.substr(8);
|
|
||||||
std::string md = "TDAT=" + std::string(day) + std::string(month);
|
|
||||||
int res = id3tag_set_fieldvalue(encoder, md.c_str());
|
|
||||||
if (res != 0)
|
|
||||||
log(warning, "wasn't able to set the date tag (" + md + ")");
|
|
||||||
|
|
||||||
fullDate = fullDate.substr(0, 4); //year;
|
|
||||||
}
|
|
||||||
id3tag_set_year(encoder, std::string(fullDate).data());
|
|
||||||
} else if (!tryKnownTag(comm)) {
|
|
||||||
std::string tag = "TXXX=" + std::string(comm);
|
|
||||||
int res = id3tag_set_fieldvalue(encoder, tag.c_str());
|
|
||||||
if (res != 0)
|
|
||||||
log(warning, "wasn't able to set user tag (" + tag + ")");
|
|
||||||
}
|
}
|
||||||
|
std::string key(comm.substr(0, ePos));
|
||||||
|
std::string value(comm.substr(ePos + 1));
|
||||||
|
|
||||||
|
if (key == "BPM") { //somehow TagLib lets BPM be fractured
|
||||||
|
std::string::size_type dotPos = value.find("."); //but IDv2.3.0 spec requires it to be integer
|
||||||
|
if (dotPos != std::string::npos) //I don't know better than just to floor it
|
||||||
|
value = value.substr(0, dotPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = props.insert(key, TagLib::String(value, TagLib::String::UTF8));
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
log(warning, "couldn't understand tag (" + key + "), skipping");
|
||||||
}
|
}
|
||||||
|
id3v2tag.setProperties(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) {
|
void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) {
|
||||||
@ -246,9 +182,9 @@ void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) {
|
|||||||
log(warning, "failed to rescale album art");
|
log(warning, "failed to rescale album art");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int result = id3tag_set_albumart(encoder, (const char*)picture.data, picture.data_length);
|
//auch, sorry for copying so much, but I haven't found a way around it yet
|
||||||
if (result != 0)
|
TagLib::ByteVector bytes((const char*)picture.data, picture.data_length);
|
||||||
log(warning, "couldn't set album art tag, errcode: " + std::to_string(result));
|
attachPictureFrame(picture, bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,8 +201,9 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
|
|||||||
log(Loggable::error, "error reading jpeg header");
|
log(Loggable::error, "error reading jpeg header");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
TagLib::ByteVector vector (picture.data_length + 1024 * 4); //I allocate a little bit more not to corrupt someone else's the memory
|
||||||
uint64_t mem_size = 0;
|
uint64_t mem_size = 0;
|
||||||
uint8_t *mem = new uint8_t[LAME_MAXALBUMART + 1024 * 4]; //I allocate a little bit more not to corrupt someone else's the memory
|
uint8_t* data = (uint8_t*)vector.data();
|
||||||
|
|
||||||
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
|
||||||
dinfo.scale_denom = 3;
|
dinfo.scale_denom = 3;
|
||||||
@ -276,7 +213,7 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
|
|||||||
struct jpeg_error_mgr cerr;
|
struct jpeg_error_mgr cerr;
|
||||||
cinfo.err = jpeg_std_error(&cerr);
|
cinfo.err = jpeg_std_error(&cerr);
|
||||||
jpeg_create_compress(&cinfo);
|
jpeg_create_compress(&cinfo);
|
||||||
jpeg_mem_dest(&cinfo, &mem, &mem_size);
|
jpeg_mem_dest(&cinfo, &data, &mem_size);
|
||||||
|
|
||||||
cinfo.image_width = dinfo.output_width;
|
cinfo.image_width = dinfo.output_width;
|
||||||
cinfo.image_height = dinfo.output_height;
|
cinfo.image_height = dinfo.output_height;
|
||||||
@ -290,14 +227,12 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
|
|||||||
uint8_t* row = new uint8_t[rowSize];
|
uint8_t* row = new uint8_t[rowSize];
|
||||||
|
|
||||||
while (dinfo.output_scanline < dinfo.output_height) {
|
while (dinfo.output_scanline < dinfo.output_height) {
|
||||||
|
if (mem_size + rowSize > vector.size()) {
|
||||||
|
vector.resize(vector.size() + rowSize);
|
||||||
|
log(Loggable::major, "allocated memory for resising the image wasn't enougth, resising");
|
||||||
|
}
|
||||||
jpeg_read_scanlines(&dinfo, &row, 1);
|
jpeg_read_scanlines(&dinfo, &row, 1);
|
||||||
jpeg_write_scanlines(&cinfo, &row, 1);
|
jpeg_write_scanlines(&cinfo, &row, 1);
|
||||||
|
|
||||||
if (mem_size > LAME_MAXALBUMART) {
|
|
||||||
delete[] mem;
|
|
||||||
log(Loggable::error, "resized album art exceeded reserved buffer size, stopping before memory corrution");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
jpeg_finish_decompress(&dinfo);
|
jpeg_finish_decompress(&dinfo);
|
||||||
jpeg_destroy_decompress(&dinfo);
|
jpeg_destroy_decompress(&dinfo);
|
||||||
@ -307,42 +242,12 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
|
|||||||
|
|
||||||
delete[] row;
|
delete[] row;
|
||||||
|
|
||||||
int result = id3tag_set_albumart(encoder, (const char*)mem, mem_size);
|
vector.resize(mem_size); //to shrink if down to the actual size
|
||||||
delete[] mem;
|
attachPictureFrame(picture, vector);
|
||||||
|
|
||||||
return result == 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FLACtoMP3::tryKnownTag(std::string_view source) {
|
|
||||||
for (const std::pair<const std::string, const std::string>& pair : knownTags) {
|
|
||||||
if (source.find(pair.first) == 0) {
|
|
||||||
if (pair.first == "LYRICS=") {
|
|
||||||
log(warning, "lyrics tag was not transfered, it is not supported currently");
|
|
||||||
} else if (pair.first == "BPM=") {
|
|
||||||
float bpm = std::atof(source.substr(pair.first.size()).data());
|
|
||||||
bpm = std::round(bpm); //id3 bpm must be integer
|
|
||||||
int result = id3tag_set_fieldvalue(encoder, std::string(pair.second + std::to_string((int)bpm)).c_str());
|
|
||||||
if (result != 0)
|
|
||||||
log(warning, "wasn't able to set BPM tag. Code = " + std::to_string(result));
|
|
||||||
} else if (pair.first == "ALBUMARTIST=") {
|
|
||||||
int res = setTagUSC2("TPE2", source.substr(pair.first.size()));
|
|
||||||
if (res != 0)
|
|
||||||
log(warning, "wasn't able to album artist tag. Code = " + std::to_string(res));
|
|
||||||
} else {
|
|
||||||
std::string tag = pair.second + std::string(source.substr(pair.first.size()));
|
|
||||||
int res = id3tag_set_fieldvalue(encoder, tag.c_str());
|
|
||||||
if (res != 0)
|
|
||||||
log(warning, "wasn't able to set tag (" + std::string(source) + "). Code = " + std::to_string(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool FLACtoMP3::decodeFrame(const int32_t * const buffer[], uint32_t size) {
|
bool FLACtoMP3::decodeFrame(const int32_t * const buffer[], uint32_t size) {
|
||||||
if (!outputInitilized) {
|
if (!outputInitilized) {
|
||||||
bool success = initializeOutput();
|
bool success = initializeOutput();
|
||||||
@ -459,28 +364,106 @@ void FLACtoMP3::error(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErr
|
|||||||
self->log(Loggable::error, "Got error callback: " + errText);
|
self->log(Loggable::error, "Got error callback: " + errText);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FLACtoMP3::setTagTitle(const std::string_view& title) {
|
void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture, const TagLib::ByteVector& bytes) {
|
||||||
id3tag_set_title(encoder, title.data()); //to set at least some possibly encoding broken id3v1 tag
|
TagLib::ID3v2::AttachedPictureFrame* frame = new TagLib::ID3v2::AttachedPictureFrame();
|
||||||
int res = setTagUSC2("TIT2", title);
|
frame->setPicture(bytes);
|
||||||
if (res != 0)
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Media);
|
||||||
log(warning, "Couldn't set Title tag. Code = " + std::to_string(res));
|
frame->setMimeType(picture.mime_type);
|
||||||
|
frame->setDescription(TagLib::String((const char*)picture.description, TagLib::String::UTF8));
|
||||||
|
switch (picture.type) {
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other);
|
||||||
|
log(info, "attached picture is described as \"other\"");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::FileIcon);
|
||||||
|
log(info, "attached picture is a standard file icon");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::OtherFileIcon);
|
||||||
|
log(info, "attached picture is apparently not so standard file icon...");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
|
||||||
|
log(info, "attached picture is a front album cover");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::BackCover);
|
||||||
|
log(info, "attached picture is an back album cover");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::LeafletPage);
|
||||||
|
log(info, "attached picture is a leflet from the album");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Media);
|
||||||
|
log(info, "attached picture is probably an imadge of an album CD");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::LeadArtist);
|
||||||
|
log(info, "attached picture is an image of the lead artist");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Artist);
|
||||||
|
log(info, "attached picture is an image the artist");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Conductor);
|
||||||
|
log(info, "attached picture is an image the conductor");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Band);
|
||||||
|
log(info, "attached picture is an image of the band");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Composer);
|
||||||
|
log(info, "attached picture is an image of composer");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Lyricist);
|
||||||
|
log(info, "attached picture is an image of the lyricist");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::RecordingLocation);
|
||||||
|
log(info, "attached picture is an image recording location");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::DuringRecording);
|
||||||
|
log(info, "attached picture is an image of the process of the recording");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::DuringPerformance);
|
||||||
|
log(info, "attached picture is an image of process of the performance");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::MovieScreenCapture);
|
||||||
|
log(info, "attached picture is an frame from a movie");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::ColouredFish);
|
||||||
|
log(info, "attached picture is ... a bright large colo(u?)red fish...? o_O");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Illustration);
|
||||||
|
log(info, "attached picture is an track related illustration");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::BandLogo);
|
||||||
|
log(info, "attached picture is a band logo");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::PublisherLogo);
|
||||||
|
log(info, "attached picture is a publisher logo");
|
||||||
|
break;
|
||||||
|
case FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED:
|
||||||
|
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other);
|
||||||
|
log(info, "attached picture is something unknown, so, I would assume it's \"other\"");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log(info, "attached picture size: " + std::to_string(frame->picture().size()));
|
||||||
|
std::string description = frame->description().to8Bit();
|
||||||
|
if (description.size() > 0)
|
||||||
|
log(info, "attached picture has a description (b'cuz where else would you ever read it?): " + description);
|
||||||
|
id3v2tag.addFrame(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FLACtoMP3::setTagAlbum(const std::string_view& album) {
|
|
||||||
id3tag_set_album(encoder, album.data()); //to set at least some possibly encoding broken id3v1 tag
|
|
||||||
int res = setTagUSC2("TALB", album);
|
|
||||||
if (res != 0)
|
|
||||||
log(warning, "Couldn't set Album tag. Code = " + std::to_string(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLACtoMP3::setTagArtist(const std::string_view& artist) {
|
|
||||||
id3tag_set_artist(encoder, artist.data()); //to set at least some possibly encoding broken id3v1 tag
|
|
||||||
int res = setTagUSC2("TPE1", artist);
|
|
||||||
if (res != 0)
|
|
||||||
log(warning, "Couldn't set Artist tag. Code = " + std::to_string(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLACtoMP3::setTagUSC2(const std::string_view& field, const std::string_view& value) {
|
|
||||||
std::u16string utf16 = std::u16string({0xFEFF}) + usc2convertor.from_bytes(value.data());
|
|
||||||
return id3tag_set_textinfo_utf16(encoder, field.data(), (unsigned short*)utf16.c_str());
|
|
||||||
}
|
|
||||||
|
17
flactomp3.h
17
flactomp3.h
@ -1,13 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FLAC/stream_decoder.h"
|
#include <FLAC/stream_decoder.h>
|
||||||
#include <lame/lame.h>
|
#include <lame.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include <id3v2tag.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@ -29,14 +28,8 @@ private:
|
|||||||
bool decodeFrame(const int32_t * const buffer[], uint32_t size);
|
bool decodeFrame(const int32_t * const buffer[], uint32_t size);
|
||||||
bool flush();
|
bool flush();
|
||||||
bool initializeOutput();
|
bool initializeOutput();
|
||||||
bool tryKnownTag(std::string_view source);
|
|
||||||
bool scaleJPEG(const FLAC__StreamMetadata_Picture& picture);
|
bool scaleJPEG(const FLAC__StreamMetadata_Picture& picture);
|
||||||
|
void attachPictureFrame(const FLAC__StreamMetadata_Picture& picture, const TagLib::ByteVector& bytes);
|
||||||
void setTagTitle(const std::string_view& title);
|
|
||||||
void setTagAlbum(const std::string_view& album);
|
|
||||||
void setTagArtist(const std::string_view& artist);
|
|
||||||
|
|
||||||
int setTagUSC2(const std::string_view& field, const std::string_view& value);
|
|
||||||
|
|
||||||
static void error(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
|
static void error(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
|
||||||
static void metadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
|
static void metadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
|
||||||
@ -65,5 +58,5 @@ private:
|
|||||||
uint32_t outputBufferSize;
|
uint32_t outputBufferSize;
|
||||||
bool outputInitilized;
|
bool outputInitilized;
|
||||||
bool downscaleAlbumArt;
|
bool downscaleAlbumArt;
|
||||||
std::wstring_convert<std::codecvt_utf8<char16_t>, char16_t> usc2convertor;
|
TagLib::ID3v2::Tag id3v2tag;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user