album art scaling and now scaling coping
This commit is contained in:
parent
e34c7f4fba
commit
f16e4b4873
@ -14,6 +14,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
find_package(LAME REQUIRED)
|
||||
find_package(FLAC REQUIRED)
|
||||
find_package(JPEG REQUIRED)
|
||||
|
||||
add_executable(mlc
|
||||
main.cpp
|
||||
@ -25,6 +26,7 @@ add_executable(mlc
|
||||
target_link_libraries(mlc
|
||||
${LAME_LIBRARIES}
|
||||
FLAC
|
||||
JPEG::JPEG
|
||||
)
|
||||
|
||||
install(TARGETS mlc RUNTIME DESTINATION bin)
|
||||
|
208
flactomp3.cpp
208
flactomp3.cpp
@ -1,6 +1,6 @@
|
||||
#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 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 date ("DATE=");
|
||||
|
||||
static const std::string_view jpeg ("image/jpeg");
|
||||
|
||||
static const std::map<std::string, const std::string> knownTags {
|
||||
{"ALBUMARTIST=", "TPE2="},
|
||||
{"PUBLISHER=", "TPUB="},
|
||||
@ -26,19 +28,21 @@ FLACtoMP3::FLACtoMP3(uint8_t size) :
|
||||
encoder(lame_init()),
|
||||
statusFLAC(),
|
||||
output(nullptr),
|
||||
bufferMultiplier(size),
|
||||
flacMaxBlockSize(0),
|
||||
pcmCounter(0),
|
||||
pcmSize(size * flacBlockMaxSize),
|
||||
pcm(new int16_t[pcmSize * 2]),
|
||||
outputBuffer(new uint8_t[pcmSize * 2]),
|
||||
outputInitilized(false)
|
||||
pcmSize(0),
|
||||
pcm(nullptr),
|
||||
outputBuffer(nullptr),
|
||||
outputBufferSize(0),
|
||||
outputInitilized(false),
|
||||
downscaleAlbumArt(false)
|
||||
{
|
||||
}
|
||||
|
||||
FLACtoMP3::~FLACtoMP3() {
|
||||
lame_close(encoder);
|
||||
FLAC__stream_decoder_delete(decoder);
|
||||
delete[] pcm;
|
||||
delete[] outputBuffer;
|
||||
}
|
||||
|
||||
void FLACtoMP3::run() {
|
||||
@ -49,11 +53,30 @@ void FLACtoMP3::run() {
|
||||
|
||||
flush();
|
||||
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)
|
||||
lame_mp3_tags_fid(encoder, output);
|
||||
if (downscaleAlbumArt) {
|
||||
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 {
|
||||
std::cout << "FAILED";
|
||||
}
|
||||
@ -64,6 +87,15 @@ void FLACtoMP3::run() {
|
||||
if (outputInitilized) {
|
||||
fclose(output);
|
||||
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_quality(encoder, 0);
|
||||
lame_set_quality(encoder, 0);
|
||||
if (!downscaleAlbumArt)
|
||||
lame_set_write_id3tag_automatic(encoder, 0);
|
||||
}
|
||||
|
||||
bool FLACtoMP3::initializeOutput() {
|
||||
@ -108,13 +142,34 @@ bool FLACtoMP3::initializeOutput() {
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FLACtoMP3::processInfo(const FLAC__StreamMetadata_StreamInfo& info) {
|
||||
lame_set_in_samplerate(encoder, info.sample_rate);
|
||||
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) {
|
||||
@ -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) {
|
||||
for (const std::pair<const std::string, const std::string>& pair : knownTags) {
|
||||
if (source.find(pair.first) == 0) {
|
||||
@ -179,13 +313,12 @@ bool FLACtoMP3::decodeFrame(const int32_t * const buffer[], uint32_t size) {
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
pcm[2 * pcmCounter] = (int16_t)buffer[0][i];
|
||||
pcm[2 * pcmCounter + 1] = (int16_t)buffer[1][i];
|
||||
++pcmCounter;
|
||||
}
|
||||
pcm[pcmCounter++] = (int16_t)buffer[0][i];
|
||||
pcm[pcmCounter++] = (int16_t)buffer[1][i];
|
||||
|
||||
if (pcmCounter == pcmSize)
|
||||
return flush();
|
||||
if (pcmCounter == pcmSize)
|
||||
return flush();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -194,13 +327,39 @@ bool FLACtoMP3::flush() {
|
||||
int nwrite = lame_encode_buffer_interleaved(
|
||||
encoder,
|
||||
pcm,
|
||||
pcmCounter,
|
||||
pcmCounter / 2,
|
||||
outputBuffer,
|
||||
pcmSize * 2
|
||||
outputBufferSize
|
||||
);
|
||||
int actuallyWritten = fwrite((char*)outputBuffer, nwrite, 1, output);
|
||||
pcmCounter = 0;
|
||||
return actuallyWritten == 1;
|
||||
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,
|
||||
pcm,
|
||||
pcmCounter,
|
||||
outputBuffer,
|
||||
outputBufferSize
|
||||
);
|
||||
}
|
||||
|
||||
if (nwrite > 0) {
|
||||
int actuallyWritten = fwrite((char*)outputBuffer, nwrite, 1, output);
|
||||
pcmCounter = 0;
|
||||
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) {
|
||||
@ -214,6 +373,9 @@ void FLACtoMP3::metadata(const FLAC__StreamDecoder* decoder, const FLAC__StreamM
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
self->processTags(metadata->data.vorbis_comment);
|
||||
break;
|
||||
case FLAC__METADATA_TYPE_PICTURE:
|
||||
self->processPicture(metadata->data.picture);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -226,10 +388,6 @@ FLAC__StreamDecoderWriteStatus FLACtoMP3::write(
|
||||
void* client_data
|
||||
) {
|
||||
(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) {
|
||||
// std::cout << "ERROR: this example only supports 16bit stereo streams" << std::endl;
|
||||
// return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "FLAC/stream_decoder.h"
|
||||
#include <lame/lame.h>
|
||||
#include <jpeglib.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@ -21,10 +22,12 @@ public:
|
||||
private:
|
||||
void processTags(const FLAC__StreamMetadata_VorbisComment& tags);
|
||||
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 flush();
|
||||
bool initializeOutput();
|
||||
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 metadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
|
||||
@ -44,9 +47,13 @@ private:
|
||||
FLAC__StreamDecoderInitStatus statusFLAC;
|
||||
|
||||
FILE* output;
|
||||
uint8_t bufferMultiplier;
|
||||
uint32_t flacMaxBlockSize;
|
||||
uint32_t pcmCounter;
|
||||
uint32_t pcmSize;
|
||||
int16_t* pcm;
|
||||
uint8_t* outputBuffer;
|
||||
uint32_t outputBufferSize;
|
||||
bool outputInitilized;
|
||||
bool downscaleAlbumArt;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user