Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
Blue | af045f1e67 | |
Blue | 7de90304e4 |
|
@ -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)
|
||||||
|
|
||||||
|
|
51
decoder.cpp
51
decoder.cpp
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
24
index.html
24
index.html
|
@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue