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>
|
<script src="wrapper.js"></script>
|
||||||
<input type="file" id="file" name="file" />
|
<input type="file" id="file" name="file" />
|
||||||
<script>
|
<script>
|
||||||
|
var time = 0;
|
||||||
|
var ctx = new AudioContext();
|
||||||
|
ctx.suspend();
|
||||||
|
var decoder;
|
||||||
function onChange(e) {
|
function onChange(e) {
|
||||||
var file = e.target.files[0];
|
var file = e.target.files[0];
|
||||||
|
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
reader.onload = function(e) {
|
reader.onload = function(e) {
|
||||||
|
decoder = new Module.Decoder();
|
||||||
var buffer = reader.result;
|
var buffer = reader.result;
|
||||||
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 decoder = new Module.Decoder();
|
|
||||||
decoder.addFragment(ptr, buffer.byteLength);
|
decoder.addFragment(ptr, buffer.byteLength);
|
||||||
|
schedule();
|
||||||
|
ctx.resume();
|
||||||
|
}
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
|
||||||
while (decoder.hasMore()) {
|
}
|
||||||
var audio = decoder.decode(1);
|
|
||||||
if (audio === undefined) {
|
function schedule() {
|
||||||
if (decoder.hasMore() === false) {
|
if (decoder.hasMore()) {
|
||||||
break;
|
var audio = decoder.decode(50);
|
||||||
}
|
if (audio) {
|
||||||
}
|
|
||||||
var source = ctx.createBufferSource();
|
var source = ctx.createBufferSource();
|
||||||
source.buffer = audio;
|
source.buffer = audio;
|
||||||
source.connect(ctx.destination);
|
source.connect(ctx.destination);
|
||||||
source.start(time);
|
source.start(time);
|
||||||
time += audio.duration;
|
time += audio.duration;
|
||||||
}
|
|
||||||
|
|
||||||
|
setTimeout(schedule, Math.floor(audio.duration * 200));
|
||||||
|
} else {
|
||||||
|
decoder.delete();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
decoder.delete();
|
decoder.delete();
|
||||||
}
|
}
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("file").addEventListener("change", onChange, false);
|
document.getElementById("file").addEventListener("change", onChange, false);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
62
wrapper.cpp
62
wrapper.cpp
@ -1,5 +1,6 @@
|
|||||||
#include "wrapper.h"
|
#include "wrapper.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
MadHeader * mad_header_create()
|
MadHeader * mad_header_create()
|
||||||
{
|
{
|
||||||
@ -74,6 +75,7 @@ Decoder::Decoder():
|
|||||||
sampleRate(0),
|
sampleRate(0),
|
||||||
channels(0),
|
channels(0),
|
||||||
cachedLength(0),
|
cachedLength(0),
|
||||||
|
samplesPerFrame(0),
|
||||||
currentBuffer(0),
|
currentBuffer(0),
|
||||||
currentBufferLength(0),
|
currentBufferLength(0),
|
||||||
synth(),
|
synth(),
|
||||||
@ -121,29 +123,36 @@ void Decoder::addFragment(intptr_t bufferPtr, unsigned long length)
|
|||||||
framesLeft();
|
framesLeft();
|
||||||
}
|
}
|
||||||
|
|
||||||
emscripten::val Decoder::decode(int count)
|
emscripten::val Decoder::decode(uint32_t count)
|
||||||
{
|
{
|
||||||
emscripten::val ret = emscripten::val::undefined();
|
emscripten::val ret = emscripten::val::undefined();
|
||||||
int result = -1;
|
|
||||||
|
|
||||||
int available = framesLeft(count);
|
int available = framesLeft(count);
|
||||||
if (available > 0) {
|
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) {
|
std::vector<emscripten::val> chans(channels, emscripten::val::undefined());
|
||||||
mad_synth_frame(&synth, &frame);
|
for (int i = 0; i < channels; ++i) {
|
||||||
ret = context.call<val>("createBuffer", synth.pcm.channels, synth.pcm.length, synth.pcm.samplerate);
|
chans[i] = ret.call<val>("getChannelData", i);
|
||||||
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 i = 0; i < available; ++i) {
|
||||||
for (int j = 0; j < synth.pcm.length; ++j) {
|
mad_frame_decode(&frame, &stream);
|
||||||
float value = mad_f_todouble(sChan[j]);
|
mad_synth_frame(&synth, &frame);
|
||||||
chan.set(std::to_string(j), val(value));
|
|
||||||
|
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;
|
return ret;
|
||||||
@ -158,38 +167,43 @@ bool Decoder::hasMore() const
|
|||||||
return stream.error != MAD_ERROR_BUFLEN;
|
return stream.error != MAD_ERROR_BUFLEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t Decoder::framesLeft(uint64_t max)
|
uint32_t Decoder::framesLeft(uint32_t max)
|
||||||
{
|
{
|
||||||
if (currentBuffer == 0) {
|
if (currentBuffer == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stream.sync) {
|
|
||||||
int result = mad_stream_sync(&stream);
|
|
||||||
if (result != 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachedLength == 0) {
|
if (cachedLength == 0) {
|
||||||
mad_stream probe;
|
mad_stream probe;
|
||||||
mad_header ph;
|
mad_header ph;
|
||||||
mad_stream_init(&probe);
|
mad_stream_init(&probe);
|
||||||
mad_header_init(&ph);
|
mad_header_init(&ph);
|
||||||
|
|
||||||
|
mad_stream_buffer(&probe, currentBuffer, currentBufferLength);
|
||||||
mad_stream_skip(&probe, stream.skiplen);
|
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 (mad_header_decode(&ph, &probe) == 0) {
|
||||||
if (sampleRate == 0) {
|
if (sampleRate == 0) {
|
||||||
sampleRate = ph.samplerate;
|
sampleRate = ph.samplerate;
|
||||||
channels = MAD_NCHANNELS(&ph);
|
channels = MAD_NCHANNELS(&ph);
|
||||||
|
samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32; //not sure why 32, it's in libmad source
|
||||||
} else {
|
} else {
|
||||||
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph)) {
|
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++cachedLength;
|
++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);
|
return std::min(cachedLength, max);
|
||||||
|
@ -124,14 +124,15 @@ public:
|
|||||||
~Decoder();
|
~Decoder();
|
||||||
|
|
||||||
void addFragment(intptr_t bufferPtr, unsigned long length);
|
void addFragment(intptr_t bufferPtr, unsigned long length);
|
||||||
val decode(int count);
|
val decode(uint32_t count = UINT32_MAX);
|
||||||
bool hasMore() const;
|
bool hasMore() const;
|
||||||
uint64_t framesLeft(uint64_t max = UINT64_MAX);
|
uint32_t framesLeft(uint32_t max = UINT32_MAX);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t sampleRate;
|
uint32_t sampleRate;
|
||||||
uint8_t channels;
|
uint8_t channels;
|
||||||
uint64_t cachedLength;
|
uint32_t cachedLength;
|
||||||
|
uint16_t samplesPerFrame;
|
||||||
unsigned char* currentBuffer;
|
unsigned char* currentBuffer;
|
||||||
unsigned long currentBufferLength;
|
unsigned long currentBufferLength;
|
||||||
|
|
||||||
@ -236,6 +237,7 @@ 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());
|
||||||
|
function("mad_stream_sync", &mad_stream_sync, allow_raw_pointers());
|
||||||
|
|
||||||
class_<mad_pcm>("mad_pcm");
|
class_<mad_pcm>("mad_pcm");
|
||||||
class_<MadPCM, base<mad_pcm>>("MadPCM")
|
class_<MadPCM, base<mad_pcm>>("MadPCM")
|
||||||
@ -259,6 +261,7 @@ EMSCRIPTEN_BINDINGS(jsmad) {
|
|||||||
.constructor<>()
|
.constructor<>()
|
||||||
.function("addFragment", &Decoder::addFragment)
|
.function("addFragment", &Decoder::addFragment)
|
||||||
.function("hasMore", &Decoder::hasMore)
|
.function("hasMore", &Decoder::hasMore)
|
||||||
|
.function("framesLeft", &Decoder::framesLeft)
|
||||||
.function("decode", &Decoder::decode);
|
.function("decode", &Decoder::decode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user