diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f4e5ee..e9b9969 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,9 @@ function(em_file name) ${CMAKE_SOURCE_DIR}/decoder.cpp ${CMAKE_SOURCE_DIR}/${name}.cpp -o ${CMAKE_BINARY_DIR}/${name}.js - -s FILESYSTEM=0 - -s ENVIRONMENT=web - -Oz - --llvm-lto 1 + -s WASM=0 + -g4 + -s TOTAL_MEMORY=64MB ) endfunction(em_file) diff --git a/decoder.cpp b/decoder.cpp index e4c7d0e..450762c 100644 --- a/decoder.cpp +++ b/decoder.cpp @@ -2,30 +2,27 @@ #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), +buffer(new uint8_t[BUFFER_LENGTH]), cached(false), synth(new mad_synth()), stream(new mad_stream()), frame(new mad_frame()), -context(0), -pending() +context(0) { - for (int i = 0; i < GLUE_LENGTH; ++i) { - glue[i] = 0; + for (int i = 0; i < BUFFER_LENGTH; ++i) { + buffer[i] = 0; } mad_frame_init(frame); mad_stream_init(stream); mad_synth_init(synth); + mad_stream_buffer(stream, buffer, 0); + emscripten::val AudioContext = emscripten::val::global("AudioContext"); if (!AudioContext.as()) { AudioContext = emscripten::val::global("webkitAudioContext"); @@ -46,58 +43,23 @@ Decoder::~Decoder() delete stream; delete frame; - delete[] glue; + delete[] buffer; } -void Decoder::addFragment(const emscripten::val& array) +void Decoder::addFragment(intptr_t bufferPtr, uint32_t length) { - 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]; + std::cout << "Adding new fragment " << length << " bytes long" << std::endl; + uint8_t* bf = (uint8_t(*))bufferPtr; + uint64_t ol = stream->bufend - stream->buffer; for (int i = 0; i < length; ++i) { - buffer[i] = array[std::to_string(i)].as(); + buffer[ol + i] = bf[i]; } + //free(bf); - 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; - } + stream->bufend = stream->bufend + length; + cached = false; + framesLeft(); } emscripten::val Decoder::decode(uint32_t count) @@ -114,10 +76,12 @@ emscripten::val Decoder::decode(uint32_t count) chans[i] = ret.call("getChannelData", i); } - for (int i = 0; success < available; ++i) { + for (int i = 0; i < available; ++i) { int res = mad_frame_decode(frame, stream); - if (res != 0) { + if (res == 0) { + ++success; + } else { if (MAD_RECOVERABLE(stream->error)) { std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl; @@ -132,19 +96,16 @@ emscripten::val Decoder::decode(uint32_t count) 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)); + chans[k].set(std::to_string(i * 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(); + framesLeft(); } } @@ -153,144 +114,64 @@ emscripten::val Decoder::decode(uint32_t count) bool Decoder::hasMore() const { - if (pending.size() == 1) { - return stream->error != MAD_ERROR_BUFLEN; - } else { - return true; - } + return stream->bufend != stream->buffer && stream->error != MAD_ERROR_BUFLEN; } uint32_t Decoder::framesLeft(uint32_t max) { - if (state == empty || state == onGlueHalf) { + if (!hasMore()) { return 0; } if (cached == false) { + bool found = 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) { + found = true; 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; - } + break; } } if (probe.next_frame > probe.this_frame) { ++cachedLength; } } else { - #if DEBUGGING + if (!found) { + stream->next_frame = probe.next_frame; + } 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.skiplen = stream->skiplen; //probe.sync = stream->sync; //probe.freerate = stream->freerate; //probe.this_frame = stream->this_frame; @@ -307,41 +188,3 @@ void Decoder::initializeProbe(mad_stream& probe) //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) {} -} diff --git a/decoder.h b/decoder.h index 3f030e6..7745403 100644 --- a/decoder.h +++ b/decoder.h @@ -9,43 +9,24 @@ #include #include "mad.h" -#define GLUE_LENGTH 6000 +#define BUFFER_LENGTH 30000000 class Decoder { public: Decoder(); ~Decoder(); - void addFragment(const emscripten::val& array); + void addFragment(intptr_t bufferPtr, uint32_t length); emscripten::val decode(uint32_t count = UINT32_MAX); bool hasMore() const; uint32_t framesLeft(uint32_t max = UINT32_MAX); private: - - enum State { - empty, - onBufferHalf, - onBufferFull, - onGlueHalf, - onGlueFull - }; - - struct RawBuffer { - uint8_t* ptr; - uint32_t length; - }; - - State state; - uint32_t sampleRate; uint8_t channels; uint32_t cachedLength; uint16_t samplesPerFrame; - uint8_t* glue; - uint8_t const* cachedNext; - uint8_t const* cachedThis; - mad_error cachedError; + uint8_t* buffer; bool cached; mad_synth* synth; @@ -53,15 +34,9 @@ private: mad_frame* frame; emscripten::val context; - std::deque pending; - private: - void pullBuffer(); - void changeBuffer(); - void prepareNextBuffer(); void initializeProbe(mad_stream& probe); - void switchToGlue(); - void switchBuffer(uint8_t* bufferPtr, uint32_t length); + }; #endif // DECODER_H diff --git a/index.html b/index.html index f6fd8b9..1db3e3b 100644 --- a/index.html +++ b/index.html @@ -12,13 +12,14 @@