Decoded FLAC to a valid WAV
This commit is contained in:
parent
59c5878b5e
commit
c72269f77a
@ -1,7 +1,29 @@
|
||||
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)
|
||||
|
20
cmake/FindLAME.cmake
Normal file
20
cmake/FindLAME.cmake
Normal 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
202
decoded.cpp
Normal 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
44
decoded.h
Normal 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
7
help.cpp
Normal 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;
|
||||
}
|
97
main.cpp
97
main.cpp
@ -1,6 +1,101 @@
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user