jsmad/decoder.cpp

348 lines
10 KiB
C++

#include "decoder.h"
#include <iostream>
Decoder::Decoder():
state(empty),
sampleRate(0),
channels(0),
cachedLength(0),
samplesPerFrame(0),
glue(new uint8_t[GLUE_LENGTH]),
cachedNext(NULL),
cachedThis(NULL),
cachedError(MAD_ERROR_NONE),
cached(false),
synth(new mad_synth()),
stream(new mad_stream()),
frame(new mad_frame()),
context(0),
pending()
{
for (int i = 0; i < GLUE_LENGTH; ++i) {
glue[i] = 0;
}
mad_frame_init(frame);
mad_stream_init(stream);
mad_synth_init(synth);
emscripten::val AudioContext = emscripten::val::global("AudioContext");
if (!AudioContext.as<bool>()) {
AudioContext = emscripten::val::global("webkitAudioContext");
}
context = AudioContext.new_();
}
Decoder::~Decoder()
{
context.call<void>("close");
mad_synth_finish(synth);
mad_stream_finish(stream);
mad_frame_finish(frame);
delete synth;
delete stream;
delete frame;
delete[] glue;
}
void Decoder::addFragment(const emscripten::val& array)
{
uint32_t length = array["length"].as<uint32_t>();
if (length < GLUE_LENGTH / 2) {
std::cout << "Error: an attempt to add fragment smaller then half of the glue buffer, ignoring";
return;
}
uint8_t* buffer = new uint8_t[length];
for (int i = 0; i < length; ++i) {
buffer[i] = array[std::to_string(i)].as<uint8_t>();
}
RawBuffer rb = {buffer, length};
pending.push_back(rb);
switch (state) {
case empty:
mad_stream_buffer(stream, buffer, length);
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = buffer[length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;
prepareNextBuffer();
break;
case onBufferHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}
state = onBufferFull;
break;
case onBufferFull:
break;
case onGlueHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}
state = onGlueFull;
cached = false;
prepareNextBuffer();
break;
case onGlueFull:
break;
}
}
emscripten::val Decoder::decode(uint32_t count)
{
emscripten::val ret = emscripten::val::undefined();
int available = framesLeft(count);
int success = 0;
if (available > 0) {
ret = context.call<emscripten::val>("createBuffer", channels, available * samplesPerFrame, sampleRate);
std::vector<emscripten::val> chans(channels, emscripten::val::undefined());
for (int i = 0; i < channels; ++i) {
chans[i] = ret.call<emscripten::val>("getChannelData", i);
}
for (int i = 0; success < available; ++i) {
int res = mad_frame_decode(frame, stream);
if (res != 0) {
if (MAD_RECOVERABLE(stream->error)) {
std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl;
continue;
} else {
break;
}
}
mad_synth_frame(synth, frame);
for (int j = 0; j < samplesPerFrame; ++j) {
for (int k = 0; k < channels; ++k) {
float value = mad_f_todouble(synth->pcm.samples[k][j]);
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value));
}
}
++success;
}
cachedLength -= available;
#if DEBUGGING
std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(stream) << std::endl;
#endif
if (cachedLength == 0) {
cached = false;
prepareNextBuffer();
}
}
return ret;
}
bool Decoder::hasMore() const
{
if (pending.size() == 1) {
return stream->error != MAD_ERROR_BUFLEN;
} else {
return true;
}
}
uint32_t Decoder::framesLeft(uint32_t max)
{
if (state == empty || state == onGlueHalf) {
return 0;
}
if (cached == false) {
mad_stream probe;
mad_header ph;
initializeProbe(probe);
mad_header_init(&ph);
sampleRate = 0;
while (cachedLength < max) {
if (mad_header_decode(&ph, &probe) == 0) {
if (sampleRate == 0) {
sampleRate = ph.samplerate;
channels = MAD_NCHANNELS(&ph);
samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32; //not sure why 32, it's in libmad source
} else {
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
if (cachedLength > 0) {
#if DEBUGGING
std::cout << "sample rate " << sampleRate << " -> " << ph.samplerate << std::endl;
std::cout << "channels " << channels << " -> " << MAD_NCHANNELS(&ph) << std::endl;
std::cout << "samples per frame " << samplesPerFrame << " -> " << MAD_NSBSAMPLES(&ph) * 32 << std::endl;
#endif
probe.next_frame = probe.this_frame;
break;
}
}
}
if (probe.next_frame > probe.this_frame) {
++cachedLength;
}
} else {
#if DEBUGGING
std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl;
#endif
if (!MAD_RECOVERABLE(probe.error)) {
break;
}
}
}
cachedNext = probe.next_frame;
cachedThis = probe.this_frame;
cachedError = probe.error;
mad_header_finish(&ph);
mad_stream_finish(&probe);
#if DEBUGGING
std::cout << cachedLength << " frames are available for decoding" << std::endl;
#endif
cached = true;
}
return std::min(cachedLength, max);
}
void Decoder::pullBuffer()
{
if (cached == false) {
std::cout << "Error in pullBuffer method!" << std::endl;
}
stream->this_frame = cachedThis;
stream->next_frame = cachedNext;
stream->error = cachedError;
}
void Decoder::changeBuffer()
{
switch (state) {
case empty:
std::cout << "Wrong state on switchBuffer method - empty, aborting" << std::endl;
case onBufferHalf:
switchToGlue();
state = onGlueHalf;
break;
case onBufferFull:
switchToGlue();
state = onGlueFull;
break;
case onGlueHalf:
std::cout << "Wrong state on switchBuffer method - onGlueHalf, aborting" << std::endl;
break;
case onGlueFull:
#if DEBUGGING
std::cout << "Having another fragment " << pending[0].length << " bytes long" << std::endl;
#endif
switchBuffer(pending[0].ptr, pending[0].length);
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = pending[0].ptr[pending[0].length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;
if (pending.size() > 1) {
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = pending[1].ptr[i];
}
state = onBufferFull;
}
}
cached = false;
}
void Decoder::prepareNextBuffer()
{
bool shift;
do {
shift = false;
framesLeft();
if (cachedLength == 0 && state != empty && state != onGlueHalf) {
pullBuffer();
changeBuffer();
shift = true;
}
} while (shift);
}
void Decoder::initializeProbe(mad_stream& probe)
{
mad_stream_init(&probe);
probe.buffer = stream->buffer;
probe.bufend = stream->bufend;
probe.skiplen = stream->skiplen;
//probe.sync = stream->sync;
//probe.freerate = stream->freerate;
//probe.this_frame = stream->this_frame;
probe.next_frame = stream->next_frame;
//probe.ptr.byte = stream->ptr.byte;
//probe.ptr.cache = stream->ptr.cache;
//probe.ptr.cache = stream->ptr.cache;
//probe.anc_ptr.byte = stream->anc_ptr.byte;
//probe.anc_ptr.cache = stream->anc_ptr.cache;
//probe.anc_ptr.cache = stream->anc_ptr.cache;
//probe.anc_bitlen = stream->anc_bitlen;
//probe.main_data = stream.main_data;
//probe.md_len = stream.md_len;
//probe.options = stream->options;
//probe.error = stream->error;
}
void Decoder::switchToGlue()
{
#if DEBUGGING
std::cout << "Switching to glue" << std::endl;
#endif
switchBuffer(glue, GLUE_LENGTH);
#if DEBUGGING
std::cout << "Freeing the drained fragment" << std::endl;
#endif
delete[] pending[0].ptr;
pending.pop_front();
}
void Decoder::switchBuffer(uint8_t* bufferPtr, uint32_t length)
{
uint32_t left;
if (stream->error != MAD_ERROR_BUFLEN) {
std::cout << "WARNING: Switching buffers while the previous one is not drained, last error: " << mad_stream_errorstr(stream) << std::endl;
}
if (stream->next_frame != NULL) {
left = stream->bufend - stream->next_frame;
} else {
std::cout << "WARNING: not supposed to happen" << std::endl;
}
if (left > GLUE_LENGTH / 2) {
std::cout << "Error: bytes to read in the buffer are more then glue buffer can fit (" << left << ")" << std::endl;
throw 1;
}
mad_stream_buffer(stream, bufferPtr + GLUE_LENGTH / 2 - left, length - (GLUE_LENGTH / 2 - left));
stream->error = MAD_ERROR_NONE;
while (mad_header_decode(&frame->header, stream) != 0 && stream->error != MAD_ERROR_BUFLEN) {}
}