1
0
forked from blue/mlc

album art scaling and now scaling coping

This commit is contained in:
Blue 2023-07-18 20:05:32 -03:00
parent e34c7f4fba
commit f16e4b4873
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
3 changed files with 192 additions and 25 deletions

View File

@ -14,6 +14,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
find_package(LAME REQUIRED) find_package(LAME REQUIRED)
find_package(FLAC REQUIRED) find_package(FLAC REQUIRED)
find_package(JPEG REQUIRED)
add_executable(mlc add_executable(mlc
main.cpp main.cpp
@ -25,6 +26,7 @@ add_executable(mlc
target_link_libraries(mlc target_link_libraries(mlc
${LAME_LIBRARIES} ${LAME_LIBRARIES}
FLAC FLAC
JPEG::JPEG
) )
install(TARGETS mlc RUNTIME DESTINATION bin) install(TARGETS mlc RUNTIME DESTINATION bin)

View File

@ -1,6 +1,6 @@
#include "flactomp3.h" #include "flactomp3.h"
constexpr uint16_t flacBlockMaxSize = 4096; constexpr uint16_t flacDefaultMaxBlockSize = 4096;
static const std::string_view title ("TITLE="); static const std::string_view title ("TITLE=");
static const std::string_view artist ("ARTIST="); static const std::string_view artist ("ARTIST=");
@ -10,6 +10,8 @@ static const std::string_view genre ("GENRE=");
static const std::string_view track ("TRACKNUMBER="); static const std::string_view track ("TRACKNUMBER=");
static const std::string_view date ("DATE="); static const std::string_view date ("DATE=");
static const std::string_view jpeg ("image/jpeg");
static const std::map<std::string, const std::string> knownTags { static const std::map<std::string, const std::string> knownTags {
{"ALBUMARTIST=", "TPE2="}, {"ALBUMARTIST=", "TPE2="},
{"PUBLISHER=", "TPUB="}, {"PUBLISHER=", "TPUB="},
@ -26,19 +28,21 @@ FLACtoMP3::FLACtoMP3(uint8_t size) :
encoder(lame_init()), encoder(lame_init()),
statusFLAC(), statusFLAC(),
output(nullptr), output(nullptr),
bufferMultiplier(size),
flacMaxBlockSize(0),
pcmCounter(0), pcmCounter(0),
pcmSize(size * flacBlockMaxSize), pcmSize(0),
pcm(new int16_t[pcmSize * 2]), pcm(nullptr),
outputBuffer(new uint8_t[pcmSize * 2]), outputBuffer(nullptr),
outputInitilized(false) outputBufferSize(0),
outputInitilized(false),
downscaleAlbumArt(false)
{ {
} }
FLACtoMP3::~FLACtoMP3() { FLACtoMP3::~FLACtoMP3() {
lame_close(encoder); lame_close(encoder);
FLAC__stream_decoder_delete(decoder); FLAC__stream_decoder_delete(decoder);
delete[] pcm;
delete[] outputBuffer;
} }
void FLACtoMP3::run() { void FLACtoMP3::run() {
@ -51,9 +55,28 @@ void 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);
// // 7. Write INFO tag (OPTIONAL) if (downscaleAlbumArt) {
lame_mp3_tags_fid(encoder, output); lame_mp3_tags_fid(encoder, output);
} else {
int tag1Size = lame_get_id3v1_tag(encoder, outputBuffer, 128);
if (tag1Size > 128)
std::cout << std::endl << "couldn't write id3v1 tag";
else
fwrite((char*)outputBuffer, tag1Size, 1, output);
fseek(output, 0, SEEK_SET);
int tag2Size = lame_get_id3v2_tag(encoder, outputBuffer, outputBufferSize);
if (tag2Size > outputBufferSize) {
std::cout << std::endl << "couldn't write id3v1 tag";
} else
fwrite((char*)outputBuffer, tag2Size, 1, output);
int vbrTagSize = lame_get_lametag_frame(encoder, outputBuffer, outputBufferSize);
if (vbrTagSize > outputBufferSize)
std::cout << std::endl << "couldn't write vbr tag";
fwrite((char*)outputBuffer, vbrTagSize, 1, output);
}
} else { } else {
std::cout << "FAILED"; std::cout << "FAILED";
} }
@ -64,6 +87,15 @@ void FLACtoMP3::run() {
if (outputInitilized) { if (outputInitilized) {
fclose(output); fclose(output);
output = nullptr; output = nullptr;
delete[] pcm;
delete[] outputBuffer;
pcm = nullptr;
outputBuffer = nullptr;
pcmSize = 0;
flacMaxBlockSize = 0;
outputBufferSize = 0;
} }
} }
@ -87,6 +119,8 @@ 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() {
@ -108,13 +142,34 @@ bool FLACtoMP3::initializeOutput() {
return false; return false;
} }
if (flacMaxBlockSize == 0)
flacMaxBlockSize = flacDefaultMaxBlockSize;
pcmSize = lame_get_num_channels(encoder) * flacMaxBlockSize * bufferMultiplier;
outputBufferSize = pcmSize / 2;
if (!downscaleAlbumArt) {
int tag2Size = lame_get_id3v2_tag(encoder, nullptr, 0);
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];
outputBuffer = new uint8_t[outputBufferSize];
outputInitilized = true; outputInitilized = true;
return true; return true;
} }
void FLACtoMP3::processInfo(const FLAC__StreamMetadata_StreamInfo& info) { void FLACtoMP3::processInfo(const FLAC__StreamMetadata_StreamInfo& info) {
lame_set_in_samplerate(encoder, info.sample_rate); lame_set_in_samplerate(encoder, info.sample_rate);
lame_set_num_channels(encoder, info.channels); lame_set_num_channels(encoder, info.channels);
flacMaxBlockSize = info.max_blocksize;
//std::cout << "bits per sample: " << info.bits_per_sample << std::endl;;
} }
void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) { void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
@ -155,6 +210,85 @@ void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
} }
} }
void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) {
if (downscaleAlbumArt && picture.data_length > LAME_MAXALBUMART) {
std::cout << "embeded picture is too big (" << picture.data_length << " bytes) " << std::endl;
std::cout << "mime type is " << picture.mime_type << std::endl;
if (picture.mime_type == jpeg) {
if (scaleJPEG(picture)) {
std::cout << "successfully scaled album art" << std::endl;
} else {
std::cout << "failed to album art" << std::endl;
}
}
} else {
int result = id3tag_set_albumart(encoder, (const char*)picture.data, picture.data_length);
if (result != 0) {
std::cout << "couldn't set album art tag, errcode: " << result << std::endl;
}
}
}
bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
// Variables for the decompressor itself
struct jpeg_decompress_struct dinfo;
struct jpeg_error_mgr derr;
dinfo.err = jpeg_std_error(&derr);
jpeg_create_decompress(&dinfo);
jpeg_mem_src(&dinfo, picture.data, picture.data_length);
int rc = jpeg_read_header(&dinfo, TRUE);
if (rc != 1) {
std::cout << "error reading jpeg header" << std::endl;
return false;
}
uint64_t mem_size = LAME_MAXALBUMART - 1024 * 16;
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
dinfo.scale_denom = 3;
jpeg_start_decompress(&dinfo);
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr cerr;
cinfo.err = jpeg_std_error(&cerr);
jpeg_create_compress(&cinfo);
jpeg_mem_dest(&cinfo, &mem, &mem_size);
cinfo.image_width = dinfo.output_width;
cinfo.image_height = dinfo.output_height;
cinfo.input_components = dinfo.output_components;
cinfo.in_color_space = dinfo.out_color_space;
jpeg_set_defaults(&cinfo);
jpeg_start_compress(&cinfo, TRUE);
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);
jpeg_write_scanlines(&cinfo, &row, 1);
}
jpeg_finish_decompress(&dinfo);
jpeg_destroy_decompress(&dinfo);
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
// And free the input buffer
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;
}
bool FLACtoMP3::tryKnownTag(std::string_view source) { bool FLACtoMP3::tryKnownTag(std::string_view source) {
for (const std::pair<const std::string, const std::string>& pair : knownTags) { for (const std::pair<const std::string, const std::string>& pair : knownTags) {
if (source.find(pair.first) == 0) { if (source.find(pair.first) == 0) {
@ -179,28 +313,53 @@ bool FLACtoMP3::decodeFrame(const int32_t * const buffer[], uint32_t size) {
} }
for (size_t i = 0; i < size; ++i) { for (size_t i = 0; i < size; ++i) {
pcm[2 * pcmCounter] = (int16_t)buffer[0][i]; pcm[pcmCounter++] = (int16_t)buffer[0][i];
pcm[2 * pcmCounter + 1] = (int16_t)buffer[1][i]; pcm[pcmCounter++] = (int16_t)buffer[1][i];
++pcmCounter;
}
if (pcmCounter == pcmSize) if (pcmCounter == pcmSize)
return flush(); return flush();
}
return true; return true;
} }
bool FLACtoMP3::flush() { bool FLACtoMP3::flush() {
int nwrite = lame_encode_buffer_interleaved( int nwrite = lame_encode_buffer_interleaved(
encoder,
pcm,
pcmCounter / 2,
outputBuffer,
outputBufferSize
);
while (nwrite == -1) { //-1 is returned when there was not enough space in the given buffer
std::cout << outputBufferSize << " bytes in the output buffer wasn't enough" << std::endl;
outputBufferSize = outputBufferSize * 2;
delete[] outputBuffer;
outputBuffer = new uint8_t[outputBufferSize];
std::cout << "allocating " << outputBufferSize << " bytes" << std::endl;
nwrite = lame_encode_buffer_interleaved(
encoder, encoder,
pcm, pcm,
pcmCounter, pcmCounter,
outputBuffer, outputBuffer,
pcmSize * 2 outputBufferSize
); );
}
if (nwrite > 0) {
int actuallyWritten = fwrite((char*)outputBuffer, nwrite, 1, output); int actuallyWritten = fwrite((char*)outputBuffer, nwrite, 1, output);
pcmCounter = 0; pcmCounter = 0;
return actuallyWritten == 1; return actuallyWritten == 1;
} else {
if (nwrite == 0) {
std::cout << "encoding flush encoded 0 bytes, skipping write" << std::endl;
return true;
} else {
std::cout << "encoding flush failed, error: " << nwrite << std::endl;
return false;
}
}
} }
void FLACtoMP3::metadata(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data) { void FLACtoMP3::metadata(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data) {
@ -214,6 +373,9 @@ void FLACtoMP3::metadata(const FLAC__StreamDecoder* decoder, const FLAC__StreamM
case FLAC__METADATA_TYPE_VORBIS_COMMENT: case FLAC__METADATA_TYPE_VORBIS_COMMENT:
self->processTags(metadata->data.vorbis_comment); self->processTags(metadata->data.vorbis_comment);
break; break;
case FLAC__METADATA_TYPE_PICTURE:
self->processPicture(metadata->data.picture);
break;
default: default:
break; break;
} }
@ -226,10 +388,6 @@ FLAC__StreamDecoderWriteStatus FLACtoMP3::write(
void* client_data void* client_data
) { ) {
(void)(decoder); (void)(decoder);
// if (decoded->totalSamples == 0) {
// std::cout << "ERROR: this example only works for FLAC files that have a total_samples count in STREAMINFO" << std::endl;
// return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
// }
// if (decoded->channels != 2 || decoded->depth != 16) { // if (decoded->channels != 2 || decoded->depth != 16) {
// std::cout << "ERROR: this example only supports 16bit stereo streams" << std::endl; // std::cout << "ERROR: this example only supports 16bit stereo streams" << std::endl;
// return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; // return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;

View File

@ -2,6 +2,7 @@
#include "FLAC/stream_decoder.h" #include "FLAC/stream_decoder.h"
#include <lame/lame.h> #include <lame/lame.h>
#include <jpeglib.h>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -21,10 +22,12 @@ public:
private: private:
void processTags(const FLAC__StreamMetadata_VorbisComment& tags); void processTags(const FLAC__StreamMetadata_VorbisComment& tags);
void processInfo(const FLAC__StreamMetadata_StreamInfo& info); void processInfo(const FLAC__StreamMetadata_StreamInfo& info);
void processPicture(const FLAC__StreamMetadata_Picture& picture);
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 tryKnownTag(std::string_view source);
bool scaleJPEG(const FLAC__StreamMetadata_Picture& picture);
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);
@ -44,9 +47,13 @@ private:
FLAC__StreamDecoderInitStatus statusFLAC; FLAC__StreamDecoderInitStatus statusFLAC;
FILE* output; FILE* output;
uint8_t bufferMultiplier;
uint32_t flacMaxBlockSize;
uint32_t pcmCounter; uint32_t pcmCounter;
uint32_t pcmSize; uint32_t pcmSize;
int16_t* pcm; int16_t* pcm;
uint8_t* outputBuffer; uint8_t* outputBuffer;
uint32_t outputBufferSize;
bool outputInitilized; bool outputInitilized;
bool downscaleAlbumArt;
}; };