#include "decoder.h" #include 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()) { AudioContext = emscripten::val::global("webkitAudioContext"); } context = AudioContext.new_(); } Decoder::~Decoder() { context.call("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(); 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(); } 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("createBuffer", channels, available * samplesPerFrame, sampleRate); std::vector chans(channels, emscripten::val::undefined()); for (int i = 0; i < channels; ++i) { chans[i] = ret.call("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) {} }