first decoding, temp
This commit is contained in:
parent
042fb7d5eb
commit
7a6ebef935
@ -6,6 +6,7 @@ function(em_file name)
|
|||||||
${CMAKE_SOURCE_DIR}/${name}.cpp
|
${CMAKE_SOURCE_DIR}/${name}.cpp
|
||||||
-o ${CMAKE_BINARY_DIR}/${name}.js
|
-o ${CMAKE_BINARY_DIR}/${name}.js
|
||||||
-s WASM=0
|
-s WASM=0
|
||||||
|
-g4
|
||||||
)
|
)
|
||||||
endfunction(em_file)
|
endfunction(em_file)
|
||||||
|
|
||||||
|
24
index.html
24
index.html
@ -21,14 +21,28 @@
|
|||||||
var src = new Uint8Array(buffer);
|
var src = new Uint8Array(buffer);
|
||||||
var ptr = Module._malloc(buffer.byteLength);
|
var ptr = Module._malloc(buffer.byteLength);
|
||||||
var arr = new Uint8Array(Module.HEAPU8.buffer, ptr, buffer.byteLength);
|
var arr = new Uint8Array(Module.HEAPU8.buffer, ptr, buffer.byteLength);
|
||||||
|
var ctx = new AudioContext();
|
||||||
|
var time = 0;
|
||||||
arr.set(src, 0);
|
arr.set(src, 0);
|
||||||
|
|
||||||
var stream = new Module.MadStream();
|
var decoder = new Module.Decoder();
|
||||||
var frame = new Module.MadFrame();
|
decoder.addFragment(ptr, buffer.byteLength);
|
||||||
|
|
||||||
stream.setBuffer(ptr, buffer.byteLength);
|
while (decoder.hasMore()) {
|
||||||
frame.decode(stream);
|
var audio = decoder.decode(1);
|
||||||
debugger;
|
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);
|
reader.readAsArrayBuffer(file);
|
||||||
|
|
||||||
|
152
wrapper.cpp
152
wrapper.cpp
@ -1,4 +1,5 @@
|
|||||||
#include "wrapper.h"
|
#include "wrapper.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
MadHeader * mad_header_create()
|
MadHeader * mad_header_create()
|
||||||
{
|
{
|
||||||
@ -16,13 +17,13 @@ MadFrame * mad_frame_create()
|
|||||||
|
|
||||||
emscripten::val MadFrame::getOverlap() const
|
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));
|
return emscripten::val(typed_memory_view(sizeof *overlap, ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
emscripten::val MadFrame::getSbsample() const
|
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));
|
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);
|
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<bool>()) {
|
||||||
|
printf("No global AudioContext, trying webkitAudioContext\n");
|
||||||
|
AudioContext = emscripten::val::global("webkitAudioContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Got an AudioContext\n");
|
||||||
|
context = AudioContext.new_();
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder::~Decoder()
|
||||||
|
{
|
||||||
|
context.call<void>("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<val>("createBuffer", channels, synth.pcm.length, sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
mad_synth_frame(&synth, &frame);
|
||||||
|
ret = context.call<val>("createBuffer", synth.pcm.channels, synth.pcm.length, synth.pcm.samplerate);
|
||||||
|
for (int i = 0; i < synth.pcm.channels; ++i) {
|
||||||
|
emscripten::val chan = ret.call<val>("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);
|
||||||
|
}
|
||||||
|
|
||||||
|
80
wrapper.h
80
wrapper.h
@ -47,9 +47,6 @@ MadHeader* mad_header_create();
|
|||||||
|
|
||||||
class MadFrame : public mad_frame {
|
class MadFrame : public mad_frame {
|
||||||
public:
|
public:
|
||||||
const MadHeader& getHeader() const {return static_cast<const MadHeader&>(header);}
|
|
||||||
void setHeader(const MadHeader& head) {header = head;}
|
|
||||||
|
|
||||||
int getOptions() const {return options;}
|
int getOptions() const {return options;}
|
||||||
void setOptions(int lay) {options = lay;}
|
void setOptions(int lay) {options = lay;}
|
||||||
|
|
||||||
@ -57,6 +54,7 @@ public:
|
|||||||
val getOverlap() const;
|
val getOverlap() const;
|
||||||
|
|
||||||
int decode(mad_stream* stream);
|
int decode(mad_stream* stream);
|
||||||
|
const MadHeader* header() const {return static_cast<const MadHeader*>(&(mad_frame::header));}
|
||||||
};
|
};
|
||||||
MadFrame* mad_frame_create();
|
MadFrame* mad_frame_create();
|
||||||
|
|
||||||
@ -93,6 +91,56 @@ public:
|
|||||||
};
|
};
|
||||||
MadStream* mad_stream_create();
|
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<const MadPCM*>(&(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) {
|
EMSCRIPTEN_BINDINGS(jsmad) {
|
||||||
enum_<mad_layer>("mad_layer")
|
enum_<mad_layer>("mad_layer")
|
||||||
.value("MAD_LAYER_I", MAD_LAYER_I)
|
.value("MAD_LAYER_I", MAD_LAYER_I)
|
||||||
@ -159,8 +207,8 @@ EMSCRIPTEN_BINDINGS(jsmad) {
|
|||||||
class_<mad_frame>("mad_frame");
|
class_<mad_frame>("mad_frame");
|
||||||
class_<MadFrame, base<mad_frame>>("MadFrame")
|
class_<MadFrame, base<mad_frame>>("MadFrame")
|
||||||
.constructor(&mad_frame_create, allow_raw_pointers())
|
.constructor(&mad_frame_create, allow_raw_pointers())
|
||||||
.property("header", &MadFrame::getHeader, &MadFrame::setHeader)
|
|
||||||
.property("options", &MadFrame::getOptions, &MadFrame::setOptions)
|
.property("options", &MadFrame::getOptions, &MadFrame::setOptions)
|
||||||
|
.function("header", &MadFrame::header, allow_raw_pointers())
|
||||||
.function("sbsample", &MadFrame::getSbsample)
|
.function("sbsample", &MadFrame::getSbsample)
|
||||||
.function("overlap", &MadFrame::getOverlap)
|
.function("overlap", &MadFrame::getOverlap)
|
||||||
.function("decode", &MadFrame::decode, allow_raw_pointers());
|
.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_init", &mad_stream_init, allow_raw_pointers());
|
||||||
function("mad_stream_finish", &mad_stream_finish, allow_raw_pointers());
|
function("mad_stream_finish", &mad_stream_finish, allow_raw_pointers());
|
||||||
function("mad_stream_buffer", &mad_stream_buffer, allow_raw_pointers());
|
function("mad_stream_buffer", &mad_stream_buffer, allow_raw_pointers());
|
||||||
|
|
||||||
|
class_<mad_pcm>("mad_pcm");
|
||||||
|
class_<MadPCM, base<mad_pcm>>("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>("mad_synth");
|
||||||
|
class_<MadSynth, base<mad_synth>>("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>("Decoder")
|
||||||
|
.constructor<>()
|
||||||
|
.function("addFragment", &Decoder::addFragment)
|
||||||
|
.function("hasMore", &Decoder::hasMore)
|
||||||
|
.function("decode", &Decoder::decode);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WRAPPER_H
|
#endif // WRAPPER_H
|
||||||
|
Loading…
Reference in New Issue
Block a user