#include "decoder.h" #include Decoder::Decoder(): sampleRate(0), channels(0), cachedLength(0), samplesPerFrame(0), buffer(new uint8_t[BUFFER_LENGTH]), cached(false), synth(new mad_synth()), stream(new mad_stream()), frame(new mad_frame()), context(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"); } 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[] buffer; } void Decoder::addFragment(intptr_t bufferPtr, uint32_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[ol + i] = bf[i]; } //free(bf); stream->bufend = stream->bufend + length; cached = false; framesLeft(); } 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; i < available; ++i) { int res = mad_frame_decode(frame, stream); if (res == 0) { ++success; } else { 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)); } } } cachedLength -= available; std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(stream) << std::endl; if (cachedLength == 0) { framesLeft(); } } return ret; } bool Decoder::hasMore() const { return stream->bufend != stream->buffer && stream->error != MAD_ERROR_BUFLEN; } uint32_t Decoder::framesLeft(uint32_t max) { if (!hasMore()) { return 0; } if (cached == false) { bool found = false; mad_stream probe; mad_header ph; initializeProbe(probe); mad_header_init(&ph); 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) { break; } } if (probe.next_frame > probe.this_frame) { ++cachedLength; } } else { if (!found) { stream->next_frame = probe.next_frame; } std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl; if (!MAD_RECOVERABLE(probe.error)) { break; } } } mad_header_finish(&ph); mad_stream_finish(&probe); std::cout << cachedLength << " frames are available for decoding" << std::endl; cached = true; } return std::min(cachedLength, max); } 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; }