possible to decode portions, possible to find out how much is still available for one stream decoding

This commit is contained in:
Blue 2018-12-29 21:08:22 +03:00
parent 7a6ebef935
commit aa8ac249a0
3 changed files with 67 additions and 42 deletions

View File

@ -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);
while (decoder.hasMore()) {
var audio = decoder.decode(1);
if (audio === undefined) {
if (decoder.hasMore() === false) {
break;
}
}
schedule();
ctx.resume();
}
reader.readAsArrayBuffer(file);
}
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>

View File

@ -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);
}
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));
ret = context.call<val>("createBuffer", channels, available * samplesPerFrame, sampleRate);
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);

View File

@ -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);
}