diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ccc1b8..a19f1a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable(mlc main.cpp help.cpp decoded.cpp + flactomp3.cpp ) target_link_libraries(mlc diff --git a/flactomp3.cpp b/flactomp3.cpp new file mode 100644 index 0000000..07789a3 --- /dev/null +++ b/flactomp3.cpp @@ -0,0 +1,150 @@ +#include "flactomp3.h" + +constexpr uint16_t flacBlockMaxSize = 4096; + +FLACtoMP3::FLACtoMP3(uint8_t size) : + inPath(), + outPath(), + decoder(FLAC__stream_decoder_new()), + encoder(lame_init()), + statusFLAC(), + output(), + pcmCounter(0), + pcmSize(size * flacBlockMaxSize), + pcm(new int16_t[pcmSize * 2]), + mp3(new uint8_t[pcmSize]) +{ +} + +FLACtoMP3::~FLACtoMP3() { + lame_close(encoder); + FLAC__stream_decoder_delete(decoder); + delete[] pcm; + delete[] mp3; +} + +void FLACtoMP3::run() { + FLAC__bool ok = FLAC__stream_decoder_process_until_end_of_stream(decoder); + std::cout << "decoding: "; + if (ok) + std::cout << "succeeded"; + else + std::cout << "FAILED"; + + std::cout << std::endl; + std::cout << " state: " << FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(decoder)] << std::endl; + + flush(); + int nwrite = lame_encode_flush(encoder, mp3, pcmSize); + output.write((char*)mp3, nwrite); + + // // 7. Write INFO tag (OPTIONAL) + // // lame_mp3_tags_fid(lame, mp3); + output.close(); +} + +void FLACtoMP3::setInputFile(const std::string& path) { + if (inPath.size() > 0) + throw 1; + + inPath = path; + + FLAC__stream_decoder_set_md5_checking(decoder, true); + statusFLAC = FLAC__stream_decoder_init_file(decoder, path.c_str(), write, metadata, error, this); +} + +void FLACtoMP3::setOutputFile(const std::string& path) { + if (outPath.size() > 0) + throw 2; + + outPath = path; + + // 3. Do some settings (OPTIONAL) + lame_set_in_samplerate(encoder, 44100); + lame_set_VBR(encoder, vbr_default); + lame_set_VBR_quality(encoder, 2); + + // 4. Initialize parameters + int ret = lame_init_params(encoder); + if (ret < 0) { + std::cout << "Error occurred during parameters initializing. Code = " << ret << std::endl; + throw 3; + } + + output.open(path, std::ios::out | std::ios::binary); +} + +void 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; + } + + if (pcmCounter == pcmSize) + flush(); +} + +void FLACtoMP3::flush() { + int nwrite = lame_encode_buffer_interleaved( + encoder, + pcm, + pcmCounter, + mp3, + pcmSize + ); + output.write((char*)mp3, nwrite); + pcmCounter = 0; +} + +void FLACtoMP3::metadata(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data) { + +} + +FLAC__StreamDecoderWriteStatus FLACtoMP3::write( + const FLAC__StreamDecoder* decoder, + const FLAC__Frame* frame, + const FLAC__int32 * const buffer[], + void* client_data +){ + FLACtoMP3* self = static_cast(client_data); + self->decodeFrame(buffer, frame->header.blocksize); + + // 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; + // } + if (frame->header.channels != 2) { + std::cout << "ERROR: This frame contains " << frame->header.channels << " channels (should be 2)" << std::endl; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + if (buffer[0] == NULL) { + std::cout << "ERROR: buffer [0] is NULL" << std::endl; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + if (buffer[1] == NULL) { + std::cout << "ERROR: buffer [1] is NULL" << std::endl; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + // /* write WAVE header before we write the first frame */ + // if (frame->header.number.sample_number == 0) + // decoded->writeWaveHeader(); + // + // /* write decoded PCM samples */ + // for (size_t i = 0; i < frame->header.blocksize; i++) { + // decoded->write((uint16_t)buffer[0][i]); + // decoded->write((uint16_t)buffer[1][i]); + // } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void FLACtoMP3::error(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErrorStatus status, void* client_data) { + (void)decoder, (void)client_data; + std::cout << "Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << std::endl; +} diff --git a/flactomp3.h b/flactomp3.h new file mode 100644 index 0000000..c9f38a4 --- /dev/null +++ b/flactomp3.h @@ -0,0 +1,46 @@ +#pragma once + +#include "FLAC/stream_decoder.h" +#include + +#include +#include +#include +#include + +class FLACtoMP3 { +public: + FLACtoMP3(uint8_t size = 4); + ~FLACtoMP3(); + + void setInputFile(const std::string& path); + void setOutputFile(const std::string& path); + void run(); + +private: + void decodeFrame(const int32_t * const buffer[], uint32_t size); + void flush(); + + 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 FLAC__StreamDecoderWriteStatus write( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 * const buffer[], + void *client_data + ); + +private: + std::string inPath; + std::string outPath; + + FLAC__StreamDecoder *decoder; + lame_t encoder; + FLAC__StreamDecoderInitStatus statusFLAC; + + std::ofstream output; + uint32_t pcmCounter; + uint32_t pcmSize; + int16_t* pcm; + uint8_t* mp3; +}; diff --git a/main.cpp b/main.cpp index a1e2599..56cb002 100644 --- a/main.cpp +++ b/main.cpp @@ -5,7 +5,7 @@ #include #include "help.h" -#include "decoded.h" +#include "flactomp3.h" int main(int argc, char **argv) { if (argc < 2) { @@ -20,82 +20,10 @@ int main(int argc, char **argv) { return 0; } - Decoded* decoded = Decoded::fromFLACFile(firstArgument); - decoded->saveWaveToFile("./out.wav"); - // FILE *mp3 = fopen("output.mp3", "wb"); - // size_t nread; - // int ret, nwrite; - // - // // 1. Get lame version (OPTIONAL) - // std::cout << "Using LAME v" << get_lame_version() << std::endl; - // - // const int PCM_SIZE = 8192; - // const int MP3_SIZE = 8192; - // - // short pcm_buffer[PCM_SIZE * 2]; - // unsigned char mp3_buffer[MP3_SIZE]; - // - // // 2. Initializing - // lame_t lame = lame_init(); - // - // // 3. Do some settings (OPTIONAL) - // // lame_set_in_samplerate(lame, 48000); - // lame_set_VBR(lame, vbr_default); - // // lame_set_VBR_quality(lame, 2); - // - // // 4. Initialize parameters - // ret = lame_init_params(lame); - // if (ret < 0) { - // std::cout << "Error occurred during parameters initializing. Code = " << ret << std::endl; - // return 3; - // } - // - // do { - // // Read PCM_SIZE of array - // nread = fread(pcm_buffer, 2 * sizeof(short), PCM_SIZE, pcm); - // if (nread != 0) { - // // 5. Encode - // int nsamples = nread; - // short buffer_l[nsamples]; - // short buffer_r[nsamples]; - // - // printf("nread = %d\n", nread); - // printf("pcm_buffer.length = %d\n", sizeof(pcm_buffer)/sizeof(short)); - // - // int j = 0; - // int i = 0; - // for (i = 0; i < nsamples; i++) { - // buffer_l[i] = pcm_buffer[j++]; - // buffer_r[i] = pcm_buffer[j++]; - // - // } - // - // nwrite = lame_encode_buffer(lame, buffer_l, buffer_r, nread, - // mp3_buffer, MP3_SIZE); - // } else { - // // 6. Flush and give some final frames - // nwrite = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); - // } - // - // if (nwrite < 0) { - // printf("Error occurred during encoding. Code = %d\n", nwrite); - // return 1; - // } - // - // fwrite(mp3_buffer, nwrite, 1, mp3); - // } while (nread != 0); - // - // // 7. Write INFO tag (OPTIONAL) - // // lame_mp3_tags_fid(lame, mp3); - // - // // 8. Free internal data structures - // lame_close(lame); - // - // fclose(mp3); - - - - delete decoded; + FLACtoMP3 pipe; + pipe.setInputFile(firstArgument); + pipe.setOutputFile("out.mp3"); + pipe.run(); return 0; }