#include "wrapper.h" #include #include MadHeader * mad_header_create() { MadHeader* header = new MadHeader; mad_header_init(header); return header; } MadFrame * mad_frame_create() { MadFrame* frame = new MadFrame; mad_frame_init(frame); return frame; } emscripten::val MadFrame::getOverlap() const { unsigned char* ptr = (unsigned char(*))*overlap; return emscripten::val(typed_memory_view(sizeof *overlap, ptr)); } emscripten::val MadFrame::getSbsample() const { unsigned char* ptr = (unsigned char(*))sbsample; return emscripten::val(typed_memory_view(sizeof sbsample, ptr)); } MadStream* mad_stream_create() { MadStream* stream = new MadStream; mad_stream_init(stream); return stream; } emscripten::val MadStream::mainData() { return emscripten::val(typed_memory_view(MAD_BUFFER_MDLEN, (unsigned char(*))main_data)); } void MadStream::setBuffer(intptr_t bufferPtr, unsigned long length) { mad_stream_buffer(this, (const unsigned char(*))bufferPtr, length); } int MadFrame::decode(mad_stream* stream) { return mad_frame_decode(this, stream); } emscripten::val MadPCM::samples() const { mad_fixed_t* ptr = (mad_fixed_t(*))mad_pcm::samples; return emscripten::val(typed_memory_view(sizeof mad_pcm::samples, ptr)); } MadPCM* mad_pcm_create() { MadPCM* pcm = new MadPCM(); return pcm; } MadSynth* mad_synth_create() { MadSynth* synth = new MadSynth(); mad_synth_init(synth); return synth; } void MadSynth::frame(const mad_frame* frame) { mad_synth_frame(this, frame); } Decoder::Decoder(): sampleRate(0), channels(0), cachedLength(0), samplesPerFrame(0), currentBuffer(0), currentBufferLength(0), synth(), stream(), frame(), context(0) { mad_frame_init(&frame); mad_stream_init(&stream); mad_synth_init(&synth); emscripten::val AudioContext = emscripten::val::global("AudioContext"); if (!AudioContext.as()) { printf("No global AudioContext, trying webkitAudioContext\n"); AudioContext = emscripten::val::global("webkitAudioContext"); } printf("Got an AudioContext\n"); context = AudioContext.new_(); } Decoder::~Decoder() { context.call("close"); mad_synth_finish(&synth); mad_stream_finish(&stream); mad_frame_finish(&frame); if (currentBuffer != 0) { free(currentBuffer); } } void Decoder::addFragment(intptr_t bufferPtr, unsigned long length) { if (currentBuffer != 0) { printf("Adding more then 1 fragment is not supported yet"); throw 1; } currentBuffer = (unsigned char(*))bufferPtr; currentBufferLength = length; mad_stream_buffer(&stream, currentBuffer, currentBufferLength); framesLeft(); } emscripten::val Decoder::decode(uint32_t count) { emscripten::val ret = emscripten::val::undefined(); int available = framesLeft(count); 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) { mad_frame_decode(&frame, &stream); 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(i * samplesPerFrame + j), val(value)); } } } cachedLength -= available; if (cachedLength == 0) { framesLeft(); } } return ret; } bool Decoder::hasMore() const { if (currentBuffer == 0) { return false; } return stream.error != MAD_ERROR_BUFLEN; } uint32_t Decoder::framesLeft(uint32_t max) { if (currentBuffer == 0) { return 0; } if (cachedLength == 0) { mad_stream probe; mad_header ph; mad_stream_init(&probe); mad_header_init(&ph); mad_stream_buffer(&probe, currentBuffer, currentBufferLength); mad_stream_skip(&probe, stream.skiplen); 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) { break; } } ++cachedLength; } else { if (!MAD_RECOVERABLE(probe.error)) { std::cout << "framesLeft::" << probe.error << std::endl; break; } } } mad_header_finish(&ph); mad_stream_finish(&probe); } return std::min(cachedLength, max); }