possible to decode portions, possible to find out how much is still available for one stream decoding
This commit is contained in:
parent
7a6ebef935
commit
aa8ac249a0
34
index.html
34
index.html
@ -12,41 +12,49 @@
|
||||
<script src="wrapper.js"></script>
|
||||
<input type="file" id="file" name="file" />
|
||||
<script>
|
||||
var time = 0;
|
||||
var ctx = new AudioContext();
|
||||
ctx.suspend();
|
||||
var decoder;
|
||||
function onChange(e) {
|
||||
var file = e.target.files[0];
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
decoder = new Module.Decoder();
|
||||
var buffer = reader.result;
|
||||
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 decoder = new Module.Decoder();
|
||||
decoder.addFragment(ptr, buffer.byteLength);
|
||||
schedule();
|
||||
ctx.resume();
|
||||
}
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
||||
while (decoder.hasMore()) {
|
||||
var audio = decoder.decode(1);
|
||||
if (audio === undefined) {
|
||||
if (decoder.hasMore() === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function schedule() {
|
||||
if (decoder.hasMore()) {
|
||||
var audio = decoder.decode(50);
|
||||
if (audio) {
|
||||
var source = ctx.createBufferSource();
|
||||
source.buffer = audio;
|
||||
source.connect(ctx.destination);
|
||||
source.start(time);
|
||||
time += audio.duration;
|
||||
}
|
||||
|
||||
setTimeout(schedule, Math.floor(audio.duration * 200));
|
||||
} else {
|
||||
decoder.delete();
|
||||
}
|
||||
} else {
|
||||
decoder.delete();
|
||||
}
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
||||
}
|
||||
|
||||
document.getElementById("file").addEventListener("change", onChange, false);
|
||||
</script>
|
||||
</body>
|
||||
|
62
wrapper.cpp
62
wrapper.cpp
@ -1,5 +1,6 @@
|
||||
#include "wrapper.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
MadHeader * mad_header_create()
|
||||
{
|
||||
@ -74,6 +75,7 @@ Decoder::Decoder():
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
cachedLength(0),
|
||||
samplesPerFrame(0),
|
||||
currentBuffer(0),
|
||||
currentBufferLength(0),
|
||||
synth(),
|
||||
@ -121,29 +123,36 @@ void Decoder::addFragment(intptr_t bufferPtr, unsigned long length)
|
||||
framesLeft();
|
||||
}
|
||||
|
||||
emscripten::val Decoder::decode(int count)
|
||||
emscripten::val Decoder::decode(uint32_t 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);
|
||||
}
|
||||
ret = context.call<val>("createBuffer", channels, available * samplesPerFrame, 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));
|
||||
std::vector<emscripten::val> chans(channels, emscripten::val::undefined());
|
||||
for (int i = 0; i < channels; ++i) {
|
||||
chans[i] = ret.call<val>("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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
cachedLength -= available;
|
||||
|
||||
if (cachedLength == 0) {
|
||||
framesLeft();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -158,38 +167,43 @@ bool Decoder::hasMore() const
|
||||
return stream.error != MAD_ERROR_BUFLEN;
|
||||
}
|
||||
|
||||
uint64_t Decoder::framesLeft(uint64_t max)
|
||||
uint32_t Decoder::framesLeft(uint32_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_buffer(&probe, currentBuffer, currentBufferLength);
|
||||
mad_stream_skip(&probe, stream.skiplen);
|
||||
while (probe.error != MAD_ERROR_BUFLEN || cachedLength < max) {
|
||||
|
||||
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)) {
|
||||
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);
|
||||
|
@ -124,14 +124,15 @@ public:
|
||||
~Decoder();
|
||||
|
||||
void addFragment(intptr_t bufferPtr, unsigned long length);
|
||||
val decode(int count);
|
||||
val decode(uint32_t count = UINT32_MAX);
|
||||
bool hasMore() const;
|
||||
uint64_t framesLeft(uint64_t max = UINT64_MAX);
|
||||
uint32_t framesLeft(uint32_t max = UINT32_MAX);
|
||||
|
||||
private:
|
||||
uint32_t sampleRate;
|
||||
uint8_t channels;
|
||||
uint64_t cachedLength;
|
||||
uint32_t cachedLength;
|
||||
uint16_t samplesPerFrame;
|
||||
unsigned char* currentBuffer;
|
||||
unsigned long currentBufferLength;
|
||||
|
||||
@ -236,6 +237,7 @@ 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());
|
||||
function("mad_stream_sync", &mad_stream_sync, allow_raw_pointers());
|
||||
|
||||
class_<mad_pcm>("mad_pcm");
|
||||
class_<MadPCM, base<mad_pcm>>("MadPCM")
|
||||
@ -259,6 +261,7 @@ EMSCRIPTEN_BINDINGS(jsmad) {
|
||||
.constructor<>()
|
||||
.function("addFragment", &Decoder::addFragment)
|
||||
.function("hasMore", &Decoder::hasMore)
|
||||
.function("framesLeft", &Decoder::framesLeft)
|
||||
.function("decode", &Decoder::decode);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user