Compare commits

...

2 Commits

4 changed files with 32 additions and 190 deletions

View File

@ -10,6 +10,7 @@ function(em_file name)
-o ${CMAKE_BINARY_DIR}/${name}.js -o ${CMAKE_BINARY_DIR}/${name}.js
-s WASM=0 -s WASM=0
-g4 -g4
-s TOTAL_MEMORY=64MB
) )
endfunction(em_file) endfunction(em_file)

View File

@ -2,30 +2,27 @@
#include <iostream> #include <iostream>
Decoder::Decoder(): Decoder::Decoder():
state(empty),
sampleRate(0), sampleRate(0),
channels(0), channels(0),
cachedLength(0), cachedLength(0),
samplesPerFrame(0), samplesPerFrame(0),
glue(new uint8_t[GLUE_LENGTH]), buffer(new uint8_t[BUFFER_LENGTH]),
cachedNext(NULL),
cachedThis(NULL),
cachedError(MAD_ERROR_NONE),
cached(false), cached(false),
synth(new mad_synth()), synth(new mad_synth()),
stream(new mad_stream()), stream(new mad_stream()),
frame(new mad_frame()), frame(new mad_frame()),
context(0), context(0)
pending()
{ {
for (int i = 0; i < GLUE_LENGTH; ++i) { for (int i = 0; i < BUFFER_LENGTH; ++i) {
glue[i] = 0; buffer[i] = 0;
} }
mad_frame_init(frame); mad_frame_init(frame);
mad_stream_init(stream); mad_stream_init(stream);
mad_synth_init(synth); mad_synth_init(synth);
mad_stream_buffer(stream, buffer, 0);
emscripten::val AudioContext = emscripten::val::global("AudioContext"); emscripten::val AudioContext = emscripten::val::global("AudioContext");
if (!AudioContext.as<bool>()) { if (!AudioContext.as<bool>()) {
AudioContext = emscripten::val::global("webkitAudioContext"); AudioContext = emscripten::val::global("webkitAudioContext");
@ -46,52 +43,23 @@ Decoder::~Decoder()
delete stream; delete stream;
delete frame; delete frame;
delete[] glue; delete[] buffer;
} }
void Decoder::addFragment(intptr_t bufferPtr, uint32_t length) void Decoder::addFragment(intptr_t bufferPtr, uint32_t length)
{ {
if (length < GLUE_LENGTH / 2) {
std::cout << "An attempt to add fragment smaller then half of the glue buffer, ignoring";
return;
}
std::cout << "Adding new fragment " << length << " bytes long" << std::endl; std::cout << "Adding new fragment " << length << " bytes long" << std::endl;
uint8_t* buffer = (uint8_t(*))bufferPtr; uint8_t* bf = (uint8_t(*))bufferPtr;
RawBuffer rb = {buffer, length, 0, 0}; uint64_t ol = stream->bufend - stream->buffer;
pending.push_back(rb);
switch (state) { for (int i = 0; i < length; ++i) {
case empty: buffer[ol + i] = bf[i];
mad_stream_buffer(stream, buffer, length);
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = buffer[length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;
prepareNextBuffer();
break;
case onBufferHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}
state = onBufferFull;
break;
case onBufferFull:
break;
case onGlueHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}
state = onGlueFull;
cached = false;
prepareNextBuffer();
break;
case onGlueFull:
break;
} }
//free(bf);
stream->bufend = stream->bufend + length;
cached = false;
framesLeft();
} }
emscripten::val Decoder::decode(uint32_t count) emscripten::val Decoder::decode(uint32_t count)
@ -108,7 +76,7 @@ emscripten::val Decoder::decode(uint32_t count)
chans[i] = ret.call<emscripten::val>("getChannelData", i); chans[i] = ret.call<emscripten::val>("getChannelData", i);
} }
for (int i = 0; success < available; ++i) { for (int i = 0; i < available; ++i) {
int res = mad_frame_decode(frame, stream); int res = mad_frame_decode(frame, stream);
if (res == 0) { if (res == 0) {
@ -128,7 +96,7 @@ emscripten::val Decoder::decode(uint32_t count)
for (int j = 0; j < samplesPerFrame; ++j) { for (int j = 0; j < samplesPerFrame; ++j) {
for (int k = 0; k < channels; ++k) { for (int k = 0; k < channels; ++k) {
float value = mad_f_todouble(synth->pcm.samples[k][j]); float value = mad_f_todouble(synth->pcm.samples[k][j]);
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value)); chans[k].set(std::to_string(i * samplesPerFrame + j), emscripten::val(value));
} }
} }
} }
@ -137,8 +105,7 @@ emscripten::val Decoder::decode(uint32_t count)
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;
if (cachedLength == 0) { if (cachedLength == 0) {
cached = false; framesLeft();
prepareNextBuffer();
} }
} }
@ -147,20 +114,17 @@ emscripten::val Decoder::decode(uint32_t count)
bool Decoder::hasMore() const bool Decoder::hasMore() const
{ {
if (pending.size() == 1) { return stream->bufend != stream->buffer && stream->error != MAD_ERROR_BUFLEN;
return stream->error != MAD_ERROR_BUFLEN;
} else {
return true;
}
} }
uint32_t Decoder::framesLeft(uint32_t max) uint32_t Decoder::framesLeft(uint32_t max)
{ {
if (state == empty || state == onGlueHalf) { if (!hasMore()) {
return 0; return 0;
} }
if (cached == false) { if (cached == false) {
bool found = false;
mad_stream probe; mad_stream probe;
mad_header ph; mad_header ph;
initializeProbe(probe); initializeProbe(probe);
@ -168,6 +132,7 @@ uint32_t Decoder::framesLeft(uint32_t max)
while (cachedLength < max) { while (cachedLength < max) {
if (mad_header_decode(&ph, &probe) == 0) { if (mad_header_decode(&ph, &probe) == 0) {
found = true;
if (sampleRate == 0) { if (sampleRate == 0) {
sampleRate = ph.samplerate; sampleRate = ph.samplerate;
channels = MAD_NCHANNELS(&ph); channels = MAD_NCHANNELS(&ph);
@ -181,6 +146,9 @@ uint32_t Decoder::framesLeft(uint32_t max)
++cachedLength; ++cachedLength;
} }
} else { } else {
if (!found) {
stream->next_frame = probe.next_frame;
}
std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl; std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl;
if (!MAD_RECOVERABLE(probe.error)) { if (!MAD_RECOVERABLE(probe.error)) {
break; break;
@ -188,9 +156,6 @@ uint32_t Decoder::framesLeft(uint32_t max)
} }
} }
cachedNext = probe.next_frame;
cachedThis = probe.this_frame;
cachedError = probe.error;
mad_header_finish(&ph); mad_header_finish(&ph);
mad_stream_finish(&probe); mad_stream_finish(&probe);
std::cout << cachedLength << " frames are available for decoding" << std::endl; std::cout << cachedLength << " frames are available for decoding" << std::endl;
@ -200,69 +165,6 @@ uint32_t Decoder::framesLeft(uint32_t max)
return std::min(cachedLength, max); return std::min(cachedLength, max);
} }
void Decoder::pullBuffer()
{
if (cached == false) {
std::cout << "Error in pullBuffer method!" << std::endl;
}
stream->this_frame = cachedThis;
stream->next_frame = cachedNext;
stream->error = cachedError;
}
void Decoder::changeBuffer()
{
switch (state) {
case empty:
std::cout << "Wrong state on switchBuffer method - empty, aborting" << std::endl;
case onBufferHalf:
switchToGlue();
state = onGlueHalf;
break;
case onBufferFull:
switchToGlue();
state = onGlueFull;
break;
case onGlueHalf:
std::cout << "Wrong state on switchBuffer method - onGlueHalf, aborting" << std::endl;
break;
case onGlueFull:
std::cout << "Having another fragment " << pending[0].length << " bytes long" << std::endl;
switchBuffer(pending[0].ptr, pending[0].length);
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = pending[0].ptr[pending[0].length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;
if (pending.size() > 1) {
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = pending[1].ptr[i];
}
state = onBufferFull;
}
}
cached = false;
}
void Decoder::prepareNextBuffer()
{
bool shift;
do {
shift = false;
framesLeft();
if (cachedLength == 0 && state != empty && state != onGlueHalf) {
pullBuffer();
changeBuffer();
shift = true;
}
} while (shift);
}
void Decoder::initializeProbe(mad_stream& probe) void Decoder::initializeProbe(mad_stream& probe)
{ {
mad_stream_init(&probe); mad_stream_init(&probe);
@ -286,37 +188,3 @@ void Decoder::initializeProbe(mad_stream& probe)
//probe.options = stream->options; //probe.options = stream->options;
//probe.error = stream->error; //probe.error = stream->error;
} }
void Decoder::switchToGlue()
{
std::cout << "Switching to glue" << std::endl;
switchBuffer(glue, GLUE_LENGTH);
std::cout << "Freeing the drained fragment" << std::endl;
free(pending[0].ptr);
pending.pop_front();
}
void Decoder::switchBuffer(uint8_t* bufferPtr, uint32_t length)
{
uint32_t left;
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;
}
if (stream->next_frame != NULL) {
left = stream->bufend - stream->next_frame;
} else {
std::cout << "WARNING: not supposed to happen" << std::endl;
}
if (left > GLUE_LENGTH / 2) {
std::cout << "Error: bytes to read in the buffer are more then glue buffer can fit (" << left << ")" << std::endl;
throw 1;
}
mad_stream_buffer(stream, bufferPtr + GLUE_LENGTH / 2 - left, length - (GLUE_LENGTH / 2 - left));
stream->error = MAD_ERROR_NONE;
while (mad_header_decode(&frame->header, stream) != 0 && stream->error != MAD_ERROR_BUFLEN) {}
}

View File

@ -9,7 +9,7 @@
#include <emscripten/val.h> #include <emscripten/val.h>
#include "mad.h" #include "mad.h"
#define GLUE_LENGTH 6000 #define BUFFER_LENGTH 30000000
class Decoder { class Decoder {
public: public:
@ -22,32 +22,11 @@ public:
uint32_t framesLeft(uint32_t max = UINT32_MAX); uint32_t framesLeft(uint32_t max = UINT32_MAX);
private: private:
enum State {
empty,
onBufferHalf,
onBufferFull,
onGlueHalf,
onGlueFull
};
struct RawBuffer {
uint8_t* ptr;
uint32_t length;
uint32_t offset;
uint32_t gueard;
};
State state;
uint32_t sampleRate; uint32_t sampleRate;
uint8_t channels; uint8_t channels;
uint32_t cachedLength; uint32_t cachedLength;
uint16_t samplesPerFrame; uint16_t samplesPerFrame;
uint8_t* glue; uint8_t* buffer;
uint8_t const* cachedNext;
uint8_t const* cachedThis;
mad_error cachedError;
bool cached; bool cached;
mad_synth* synth; mad_synth* synth;
@ -55,15 +34,9 @@ private:
mad_frame* frame; mad_frame* frame;
emscripten::val context; emscripten::val context;
std::deque<RawBuffer> pending;
private: private:
void pullBuffer();
void changeBuffer();
void prepareNextBuffer();
void initializeProbe(mad_stream& probe); void initializeProbe(mad_stream& probe);
void switchToGlue();
void switchBuffer(uint8_t* bufferPtr, uint32_t length);
}; };
#endif // DECODER_H #endif // DECODER_H

View File

@ -12,8 +12,8 @@
<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 = 60000;
var maxPortion = 40960; var maxPortion = 100000;
var time = 0; var time = 0;
var ctx = new AudioContext(); var ctx = new AudioContext();
ctx.suspend(); ctx.suspend();