From 7a6ebef935b458ebd37f854bbad7575df26c4a2a Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 29 Dec 2018 15:39:49 +0300 Subject: [PATCH] first decoding, temp --- CMakeLists.txt | 1 + index.html | 24 ++++++-- wrapper.cpp | 152 ++++++++++++++++++++++++++++++++++++++++++++++++- wrapper.h | 80 ++++++++++++++++++++++++-- 4 files changed, 246 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4a5944..bf85b4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ function(em_file name) ${CMAKE_SOURCE_DIR}/${name}.cpp -o ${CMAKE_BINARY_DIR}/${name}.js -s WASM=0 + -g4 ) endfunction(em_file) diff --git a/index.html b/index.html index ae986ad..3166bd1 100644 --- a/index.html +++ b/index.html @@ -21,14 +21,28 @@ var src = new Uint8Array(buffer); var ptr = Module._malloc(buffer.byteLength); var arr = new Uint8Array(Module.HEAPU8.buffer, ptr, buffer.byteLength); + var ctx = new AudioContext(); + var time = 0; arr.set(src, 0); - var stream = new Module.MadStream(); - var frame = new Module.MadFrame(); + var decoder = new Module.Decoder(); + decoder.addFragment(ptr, buffer.byteLength); - stream.setBuffer(ptr, buffer.byteLength); - frame.decode(stream); - debugger; + while (decoder.hasMore()) { + var audio = decoder.decode(1); + if (audio === undefined) { + if (decoder.hasMore() === false) { + break; + } + } + var source = ctx.createBufferSource(); + source.buffer = audio; + source.connect(ctx.destination); + source.start(time); + time += audio.duration; + } + + decoder.delete(); } reader.readAsArrayBuffer(file); diff --git a/wrapper.cpp b/wrapper.cpp index 4bdc97a..71e690a 100644 --- a/wrapper.cpp +++ b/wrapper.cpp @@ -1,4 +1,5 @@ #include "wrapper.h" +#include MadHeader * mad_header_create() { @@ -16,13 +17,13 @@ MadFrame * mad_frame_create() emscripten::val MadFrame::getOverlap() const { - unsigned char* ptr = (unsigned char(*))overlap; + 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; + unsigned char* ptr = (unsigned char(*))sbsample; return emscripten::val(typed_memory_view(sizeof sbsample, ptr)); } @@ -47,3 +48,150 @@ 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), + 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(int count) +{ + emscripten::val ret = emscripten::val::undefined(); + int result = -1; + + int available = framesLeft(count); + if (available > 0) { + ret = context.call("createBuffer", channels, synth.pcm.length, sampleRate); + } + + if (result == 0) { + mad_synth_frame(&synth, &frame); + ret = context.call("createBuffer", synth.pcm.channels, synth.pcm.length, synth.pcm.samplerate); + for (int i = 0; i < synth.pcm.channels; ++i) { + emscripten::val chan = ret.call("getChannelData", i); + mad_fixed_t* sChan = synth.pcm.samples[i]; + for (int j = 0; j < synth.pcm.length; ++j) { + float value = mad_f_todouble(sChan[j]); + chan.set(std::to_string(j), val(value)); + } + } + + return ret; + } + + return ret; +} + +bool Decoder::hasMore() const +{ + if (currentBuffer == 0) { + return false; + } + + return stream.error != MAD_ERROR_BUFLEN; +} + +uint64_t Decoder::framesLeft(uint64_t max) +{ + if (currentBuffer == 0) { + return 0; + } + + if (!stream.sync) { + int result = mad_stream_sync(&stream); + if (result != 0) { + return 0; + } + } + + if (cachedLength == 0) { + mad_stream probe; + mad_header ph; + mad_stream_init(&probe); + mad_header_init(&ph); + mad_stream_skip(&probe, stream.skiplen); + while (probe.error != MAD_ERROR_BUFLEN || cachedLength < max) { + if (mad_header_decode(&ph, &probe) == 0) { + if (sampleRate == 0) { + sampleRate = ph.samplerate; + channels = MAD_NCHANNELS(&ph); + } else { + if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph)) { + break; + } + } + ++cachedLength; + } + } + } + + return std::min(cachedLength, max); +} + diff --git a/wrapper.h b/wrapper.h index aaf6763..eb6c366 100644 --- a/wrapper.h +++ b/wrapper.h @@ -47,9 +47,6 @@ MadHeader* mad_header_create(); class MadFrame : public mad_frame { public: - const MadHeader& getHeader() const {return static_cast(header);} - void setHeader(const MadHeader& head) {header = head;} - int getOptions() const {return options;} void setOptions(int lay) {options = lay;} @@ -57,6 +54,7 @@ public: val getOverlap() const; int decode(mad_stream* stream); + const MadHeader* header() const {return static_cast(&(mad_frame::header));} }; MadFrame* mad_frame_create(); @@ -93,6 +91,56 @@ public: }; MadStream* mad_stream_create(); +class MadPCM : public mad_pcm { +public: + unsigned int getSamplerate() const {return samplerate;} + void setSamplerate(unsigned int value) {samplerate = value;} + + unsigned short getChannels() const {return channels;} + void setChannels(unsigned short value) {channels = value;} + + unsigned short getLength() const {return length;} + void setLength(unsigned short value) {length = value;} + + val samples() const; +}; + +MadPCM* mad_pcm_create(); + +class MadSynth : public mad_synth { +public: + unsigned int getPhase() const {return phase;} + void setPhase(unsigned int value) {phase = value;} + + const MadPCM* pcm() const {return static_cast(&(mad_synth::pcm));} + void frame(mad_frame const * frame); +}; + +MadSynth* mad_synth_create(); + +class Decoder { +public: + Decoder(); + ~Decoder(); + + void addFragment(intptr_t bufferPtr, unsigned long length); + val decode(int count); + bool hasMore() const; + uint64_t framesLeft(uint64_t max = UINT64_MAX); + +private: + uint32_t sampleRate; + uint8_t channels; + uint64_t cachedLength; + unsigned char* currentBuffer; + unsigned long currentBufferLength; + + mad_synth synth; + mad_stream stream; + mad_frame frame; + val context; +}; + EMSCRIPTEN_BINDINGS(jsmad) { enum_("mad_layer") .value("MAD_LAYER_I", MAD_LAYER_I) @@ -159,8 +207,8 @@ EMSCRIPTEN_BINDINGS(jsmad) { class_("mad_frame"); class_>("MadFrame") .constructor(&mad_frame_create, allow_raw_pointers()) - .property("header", &MadFrame::getHeader, &MadFrame::setHeader) .property("options", &MadFrame::getOptions, &MadFrame::setOptions) + .function("header", &MadFrame::header, allow_raw_pointers()) .function("sbsample", &MadFrame::getSbsample) .function("overlap", &MadFrame::getOverlap) .function("decode", &MadFrame::decode, allow_raw_pointers()); @@ -188,6 +236,30 @@ EMSCRIPTEN_BINDINGS(jsmad) { function("mad_stream_init", &mad_stream_init, allow_raw_pointers()); function("mad_stream_finish", &mad_stream_finish, allow_raw_pointers()); function("mad_stream_buffer", &mad_stream_buffer, allow_raw_pointers()); + + class_("mad_pcm"); + class_>("MadPCM") + .constructor(&mad_pcm_create, allow_raw_pointers()) + .property("samplerate", &MadPCM::getSamplerate, &MadPCM::setSamplerate) + .property("channels", &MadPCM::getChannels, &MadPCM::setChannels) + .property("length", &MadPCM::getLength, &MadPCM::setLength) + .function("samples", &MadPCM::samples, allow_raw_pointers()); + + function("mad_synth_init", &mad_synth_init, allow_raw_pointers()); + function("mad_synth_frame", &mad_synth_frame, allow_raw_pointers()); + + class_("mad_synth"); + class_>("MadSynth") + .constructor(&mad_synth_create, allow_raw_pointers()) + .property("phase", &MadSynth::getPhase, &MadSynth::setPhase) + .function("pcm", &MadSynth::pcm, allow_raw_pointers()) + .function("frame", &MadSynth::frame, allow_raw_pointers()); + + class_("Decoder") + .constructor<>() + .function("addFragment", &Decoder::addFragment) + .function("hasMore", &Decoder::hasMore) + .function("decode", &Decoder::decode); } #endif // WRAPPER_H