Decoded FLAC to a valid WAV

This commit is contained in:
Blue 2023-07-15 21:15:31 -03:00
parent 59c5878b5e
commit c72269f77a
Signed by: blue
GPG Key ID: 9B203B252A63EE38
7 changed files with 396 additions and 3 deletions

View File

@ -1,7 +1,29 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(
mlc
VERSION 0.0.1
DESCRIPTION "Media Library Compiler: rips your media library to a lossy compilation"
LANGUAGES CXX
)
cmake_policy(SET CMP0076 NEW)
cmake_policy(SET CMP0079 NEW)
project(mlc) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
add_executable(mlc main.cpp) find_package(LAME REQUIRED)
find_package(FLAC REQUIRED)
add_executable(mlc
main.cpp
help.cpp
decoded.cpp
)
target_link_libraries(mlc
${LAME_LIBRARIES}
FLAC
)
install(TARGETS mlc RUNTIME DESTINATION bin) install(TARGETS mlc RUNTIME DESTINATION bin)

20
cmake/FindLAME.cmake Normal file
View File

@ -0,0 +1,20 @@
#copied from here, thank you
#https://github.com/sipwise/sems/blob/master/cmake/FindLame.cmake
FIND_PATH(LAME_INCLUDE_DIR lame/lame.h)
FIND_LIBRARY(LAME_LIBRARIES NAMES mp3lame)
IF(LAME_INCLUDE_DIR AND LAME_LIBRARIES)
SET(LAME_FOUND TRUE)
ENDIF(LAME_INCLUDE_DIR AND LAME_LIBRARIES)
IF(LAME_FOUND)
IF (NOT Lame_FIND_QUIETLY)
MESSAGE(STATUS "Found lame includes: ${LAME_INCLUDE_DIR}/lame/lame.h")
MESSAGE(STATUS "Found lame library: ${LAME_LIBRARIES}")
ENDIF (NOT Lame_FIND_QUIETLY)
ELSE(LAME_FOUND)
IF (Lame_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could NOT find lame development files")
ENDIF (Lame_FIND_REQUIRED)
ENDIF(LAME_FOUND)

202
decoded.cpp Normal file
View File

@ -0,0 +1,202 @@
#include "decoded.h"
#include <iostream>
#include <ios>
#include <fstream>
constexpr uint8_t waveDeclarationSize = 8;
constexpr uint8_t waveDescriptionSize = 36;
constexpr uint8_t waveHeaderSize = waveDeclarationSize + waveDescriptionSize;
Decoded::Decoded():
totalSamples(0),
sampleRate(0),
channels(0),
depth(0),
streamPosition(0),
stream(nullptr),
streamSize(0)
{}
Decoded::~Decoded() {
if (streamInitialized())
delete[] stream;
}
bool Decoded::streamInitialized() const {
return sampleRate != 0;
}
void Decoded::initializeStream(uint64_t total, uint32_t rate, uint16_t channels, uint16_t depth) {
if (streamInitialized())
throw 1;
totalSamples = total;
sampleRate = rate;
Decoded::channels = channels;
Decoded::depth = depth;
streamPosition = 0;
streamSize = pcmSize() + waveHeaderSize;
stream = new uint8_t[streamSize];
}
void Decoded::printInfo() const {
std::cout << "sample rate : " << sampleRate << " Hz" << std::endl;
std::cout << "channels : " << channels << std::endl;
std::cout << "bits per sample : " << depth << std::endl;
std::cout << "total samples : " << totalSamples << std::endl;
}
void Decoded::saveWaveToFile(const std::string& path) {
std::ofstream fp;
fp.open(path, std::ios::out | std::ios::binary);
fp.write((char*)stream, streamSize);
fp.close();
}
uint64_t Decoded::waveStremSize() const {
return streamSize;
}
void Decoded::write(uint16_t data) {
uint16_t* stream16 = (uint16_t*)stream;
stream16[streamPosition / 2] = data;
streamPosition += 2;
}
void Decoded::write(std::string_view data) {
for (const char& letter : data) {
stream[streamPosition] = (uint8_t)letter;
++streamPosition;
}
}
void Decoded::write(uint32_t data) {
uint32_t* stream32 = (uint32_t*)stream;
stream32[streamPosition / 4] = data;
streamPosition += 4;
}
void Decoded::writeWaveHeader() {
uint32_t pcm = pcmSize();
uint8_t bytesPerSample = depth / 8;
write("RIFF"); //4
write(pcm + waveDescriptionSize); //8
write("WAVEfmt "); //8 //16
write((uint32_t)16); //12 //20
write((uint16_t)1); //14 //22
write(channels); //16 //24
write(sampleRate); //20 //28
write((uint32_t)(sampleRate * channels * bytesPerSample)); //24 //32
write((uint16_t)(channels * bytesPerSample));//block align //26 //34
write(depth); //28 //36
write("data"); //32 //40
write(pcm); //36 //44
}
uint64_t Decoded::pcmSize() const {
return totalSamples * channels * (depth / 8);
}
void Decoded::FLACerror(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;
}
void Decoded::FLACmetadata(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data) {
(void)(decoder);
Decoded* decoded = static_cast<Decoded*>(client_data);
/* print some stats */
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
/* save for later */
decoded->initializeStream(
metadata->data.stream_info.total_samples,
metadata->data.stream_info.sample_rate,
metadata->data.stream_info.channels,
metadata->data.stream_info.bits_per_sample
);
decoded->printInfo();
}
}
FLAC__StreamDecoderWriteStatus Decoded::FLACwrite(
const FLAC__StreamDecoder* decoder,
const FLAC__Frame* frame,
const FLAC__int32 * const buffer[],
void* client_data
) {
Decoded* decoded = static_cast<Decoded*>(client_data);
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;
}
Decoded* Decoded::fromFLACFile(const std::string& path) {
FLAC__bool ok = true;
FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
FLAC__StreamDecoderInitStatus init_status;
Decoded* decoded = new Decoded;
if (decoder == NULL) {
std::cout << "error allocating flac decoder" << std::endl;
throw 2;
}
FLAC__stream_decoder_set_md5_checking(decoder, true);
init_status = FLAC__stream_decoder_init_file(decoder, path.c_str(), FLACwrite, FLACmetadata, FLACerror, decoded);
ok = init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK;
if (ok) {
std::cout << "error initializing decoder: " << FLAC__StreamDecoderInitStatusString[init_status] << std::endl;
} else {
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;
}
FLAC__stream_decoder_delete(decoder);
return decoded;
}

44
decoded.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <stdint.h>
#include <string_view>
#include "FLAC/stream_decoder.h"
class Decoded {
public:
Decoded();
~Decoded();
void initializeStream(uint64_t total, uint32_t rate, uint16_t channels, uint16_t depth);
bool streamInitialized() const;
void printInfo() const;
uint64_t pcmSize() const;
uint64_t waveStremSize() const;
void write(uint16_t data);
void write(uint32_t data);
void write(std::string_view data);
void writeWaveHeader();
void saveWaveToFile(const std::string& path);
static Decoded* fromFLACFile(const std::string& path);
static void FLACerror(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
static void FLACmetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static FLAC__StreamDecoderWriteStatus FLACwrite(
const FLAC__StreamDecoder *decoder,
const FLAC__Frame *frame,
const FLAC__int32 * const buffer[],
void *client_data
);
private:
uint64_t totalSamples;
uint32_t sampleRate;
uint16_t channels;
uint16_t depth;
uint64_t streamPosition;
uint8_t* stream;
uint64_t streamSize;
};

7
help.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "help.h"
#include "iostream"
void printHelp() {
std::cout << "Sorry, there is no help yet. It will arrive one day... I hope" << std::endl;
}

3
help.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void printHelp ();

View File

@ -1,6 +1,101 @@
#include <iostream> #include <iostream>
#include <string>
#include "FLAC/stream_decoder.h"
#include <lame/lame.h>
#include "help.h"
#include "decoded.h"
int main(int argc, char **argv) { int main(int argc, char **argv) {
std::cout << "Hello, world!" << std::endl; if (argc < 2) {
std::cout << "Insufficient amount of arguments, launch with \"--help\" argument to see usage" << std::endl;
return 1;
}
const std::string firstArgument(argv[1]);
if (firstArgument == "--help") {
printHelp();
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;
return 0; return 0;
} }