Compare commits

...

2 Commits

4 changed files with 49 additions and 36 deletions

View File

@ -8,8 +8,10 @@ function(em_file name)
${CMAKE_SOURCE_DIR}/decoder.cpp ${CMAKE_SOURCE_DIR}/decoder.cpp
${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 FILESYSTEM=0
-g4 -s ENVIRONMENT=web
-Oz
--llvm-lto 1
) )
endfunction(em_file) endfunction(em_file)

View File

@ -49,15 +49,21 @@ Decoder::~Decoder()
delete[] glue; delete[] glue;
} }
void Decoder::addFragment(intptr_t bufferPtr, uint32_t length) void Decoder::addFragment(const emscripten::val& array)
{ {
uint32_t length = array["length"].as<uint32_t>();
if (length < GLUE_LENGTH / 2) { if (length < GLUE_LENGTH / 2) {
std::cout << "An attempt to add fragment smaller then half of the glue buffer, ignoring"; std::cout << "Error: an attempt to add fragment smaller then half of the glue buffer, ignoring";
return; return;
} }
std::cout << "Adding new fragment " << length << " bytes long" << std::endl; uint8_t* buffer = new uint8_t[length];
uint8_t* buffer = (uint8_t(*))bufferPtr;
RawBuffer rb = {buffer, length, 0, 0}; for (int i = 0; i < length; ++i) {
buffer[i] = array[std::to_string(i)].as<uint8_t>();
}
RawBuffer rb = {buffer, length};
pending.push_back(rb); pending.push_back(rb);
switch (state) { switch (state) {
@ -111,9 +117,7 @@ emscripten::val Decoder::decode(uint32_t count)
for (int i = 0; success < available; ++i) { for (int i = 0; success < available; ++i) {
int res = mad_frame_decode(frame, stream); int res = mad_frame_decode(frame, stream);
if (res == 0) { if (res != 0) {
++success;
} else {
if (MAD_RECOVERABLE(stream->error)) { if (MAD_RECOVERABLE(stream->error)) {
std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl; std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl;
@ -131,11 +135,13 @@ emscripten::val Decoder::decode(uint32_t count)
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value)); chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value));
} }
} }
++success;
} }
cachedLength -= available; cachedLength -= available;
#if DEBUGGING
std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(stream) << std::endl; std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(stream) << std::endl;
#endif
if (cachedLength == 0) { if (cachedLength == 0) {
cached = false; cached = false;
prepareNextBuffer(); prepareNextBuffer();
@ -165,6 +171,7 @@ uint32_t Decoder::framesLeft(uint32_t max)
mad_header ph; mad_header ph;
initializeProbe(probe); initializeProbe(probe);
mad_header_init(&ph); mad_header_init(&ph);
sampleRate = 0;
while (cachedLength < max) { while (cachedLength < max) {
if (mad_header_decode(&ph, &probe) == 0) { if (mad_header_decode(&ph, &probe) == 0) {
@ -174,14 +181,24 @@ uint32_t Decoder::framesLeft(uint32_t max)
samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32; //not sure why 32, it's in libmad source samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32; //not sure why 32, it's in libmad source
} else { } else {
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) { if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
break; if (cachedLength > 0) {
#if DEBUGGING
std::cout << "sample rate " << sampleRate << " -> " << ph.samplerate << std::endl;
std::cout << "channels " << channels << " -> " << MAD_NCHANNELS(&ph) << std::endl;
std::cout << "samples per frame " << samplesPerFrame << " -> " << MAD_NSBSAMPLES(&ph) * 32 << std::endl;
#endif
probe.next_frame = probe.this_frame;
break;
}
} }
} }
if (probe.next_frame > probe.this_frame) { if (probe.next_frame > probe.this_frame) {
++cachedLength; ++cachedLength;
} }
} else { } else {
#if DEBUGGING
std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl; std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl;
#endif
if (!MAD_RECOVERABLE(probe.error)) { if (!MAD_RECOVERABLE(probe.error)) {
break; break;
} }
@ -193,7 +210,9 @@ uint32_t Decoder::framesLeft(uint32_t max)
cachedError = probe.error; cachedError = probe.error;
mad_header_finish(&ph); mad_header_finish(&ph);
mad_stream_finish(&probe); mad_stream_finish(&probe);
#if DEBUGGING
std::cout << cachedLength << " frames are available for decoding" << std::endl; std::cout << cachedLength << " frames are available for decoding" << std::endl;
#endif
cached = true; cached = true;
} }
@ -227,7 +246,9 @@ void Decoder::changeBuffer()
std::cout << "Wrong state on switchBuffer method - onGlueHalf, aborting" << std::endl; std::cout << "Wrong state on switchBuffer method - onGlueHalf, aborting" << std::endl;
break; break;
case onGlueFull: case onGlueFull:
#if DEBUGGING
std::cout << "Having another fragment " << pending[0].length << " bytes long" << std::endl; std::cout << "Having another fragment " << pending[0].length << " bytes long" << std::endl;
#endif
switchBuffer(pending[0].ptr, pending[0].length); switchBuffer(pending[0].ptr, pending[0].length);
@ -269,7 +290,7 @@ void Decoder::initializeProbe(mad_stream& probe)
probe.buffer = stream->buffer; probe.buffer = stream->buffer;
probe.bufend = stream->bufend; probe.bufend = stream->bufend;
//probe.skiplen = stream->skiplen; probe.skiplen = stream->skiplen;
//probe.sync = stream->sync; //probe.sync = stream->sync;
//probe.freerate = stream->freerate; //probe.freerate = stream->freerate;
//probe.this_frame = stream->this_frame; //probe.this_frame = stream->this_frame;
@ -289,11 +310,15 @@ void Decoder::initializeProbe(mad_stream& probe)
void Decoder::switchToGlue() void Decoder::switchToGlue()
{ {
#if DEBUGGING
std::cout << "Switching to glue" << std::endl; std::cout << "Switching to glue" << std::endl;
#endif
switchBuffer(glue, GLUE_LENGTH); switchBuffer(glue, GLUE_LENGTH);
#if DEBUGGING
std::cout << "Freeing the drained fragment" << std::endl; std::cout << "Freeing the drained fragment" << std::endl;
free(pending[0].ptr); #endif
delete[] pending[0].ptr;
pending.pop_front(); pending.pop_front();
} }
@ -302,7 +327,7 @@ void Decoder::switchBuffer(uint8_t* bufferPtr, uint32_t length)
uint32_t left; uint32_t left;
if (stream->error != MAD_ERROR_BUFLEN) { if (stream->error != MAD_ERROR_BUFLEN) {
std::cout << "WARNING: Switching buffers while the previous one is not drained, error: " << mad_stream_errorstr(stream) << std::endl; std::cout << "WARNING: Switching buffers while the previous one is not drained, last error: " << mad_stream_errorstr(stream) << std::endl;
} }
if (stream->next_frame != NULL) { if (stream->next_frame != NULL) {
left = stream->bufend - stream->next_frame; left = stream->bufend - stream->next_frame;

View File

@ -16,7 +16,7 @@ public:
Decoder(); Decoder();
~Decoder(); ~Decoder();
void addFragment(intptr_t bufferPtr, uint32_t length); void addFragment(const emscripten::val& array);
emscripten::val decode(uint32_t count = UINT32_MAX); emscripten::val decode(uint32_t count = UINT32_MAX);
bool hasMore() const; bool hasMore() const;
uint32_t framesLeft(uint32_t max = UINT32_MAX); uint32_t framesLeft(uint32_t max = UINT32_MAX);
@ -34,8 +34,6 @@ private:
struct RawBuffer { struct RawBuffer {
uint8_t* ptr; uint8_t* ptr;
uint32_t length; uint32_t length;
uint32_t offset;
uint32_t gueard;
}; };
State state; State state;

View File

@ -12,14 +12,13 @@
<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 minPortion = 20480; var minPortion = 4096;
var maxPortion = 40960; var maxPortion = 40960;
var time = 0; var time = 0;
var ctx = new AudioContext(); var ctx = new AudioContext();
ctx.suspend(); ctx.suspend();
var decoder; var decoder;
var fragments = []; var fragments = [];
var noize = [];
var decoding = false; var decoding = false;
function onChange(e) { function onChange(e) {
var file = e.target.files[0]; var file = e.target.files[0];
@ -51,21 +50,20 @@
setTimeout(schedule, Math.floor(audio.duration * 200)); setTimeout(schedule, Math.floor(audio.duration * 200));
decoding = true; decoding = true;
} else { } else {
//decoder.delete(); decoder.delete();
} }
} else { } else {
//decoder.delete(); decoder.delete();
} }
} }
function scheduleData() { function scheduleData() {
if (fragments.length) { if (fragments.length) {
var frag = fragments.shift(); var frag = fragments.shift();
decoder.addFragment(frag);
decoder.addFragment(frag.ptr, frag.length);
if (fragments.length) { if (fragments.length) {
setTimeout(scheduleData, 50); setTimeout(scheduleData, 100);
} }
if (!decoding) { if (!decoding) {
@ -80,20 +78,10 @@
while (shoved !== src.length) { while (shoved !== src.length) {
var ps = Math.min(Math.floor(Math.random() * (maxPortion - minPortion) + minPortion), src.length - shoved); var ps = Math.min(Math.floor(Math.random() * (maxPortion - minPortion) + minPortion), src.length - shoved);
var ptr = Module._malloc(ps);
var arr = new Uint8Array(Module.HEAPU8.buffer, ptr, ps);
var portion = new Uint8Array(buffer, shoved, ps) var portion = new Uint8Array(buffer, shoved, ps)
arr.set(portion, 0);
shoved += ps; shoved += ps;
fragments.push(portion);
fragments.push({
ptr: ptr,
length: ps
});
noize.push(Module._malloc(256));
} }
} }