2019-01-14 02:19:37 +00:00
|
|
|
#include "decoder.h"
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
Decoder::Decoder():
|
|
|
|
state(empty),
|
|
|
|
sampleRate(0),
|
|
|
|
channels(0),
|
|
|
|
cachedLength(0),
|
|
|
|
samplesPerFrame(0),
|
|
|
|
glue(new uint8_t[GLUE_LENGTH]),
|
2019-01-14 14:50:41 +00:00
|
|
|
cachedNext(NULL),
|
|
|
|
cachedThis(NULL),
|
|
|
|
cachedError(MAD_ERROR_NONE),
|
2019-01-14 02:19:37 +00:00
|
|
|
cached(false),
|
|
|
|
synth(),
|
|
|
|
stream(),
|
|
|
|
frame(),
|
|
|
|
context(0),
|
|
|
|
pending()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < GLUE_LENGTH; ++i) {
|
|
|
|
glue[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mad_frame_init(&frame);
|
|
|
|
mad_stream_init(&stream);
|
|
|
|
mad_synth_init(&synth);
|
|
|
|
|
|
|
|
emscripten::val AudioContext = emscripten::val::global("AudioContext");
|
|
|
|
if (!AudioContext.as<bool>()) {
|
|
|
|
AudioContext = emscripten::val::global("webkitAudioContext");
|
|
|
|
}
|
|
|
|
|
|
|
|
context = AudioContext.new_();
|
|
|
|
}
|
|
|
|
|
|
|
|
Decoder::~Decoder()
|
|
|
|
{
|
|
|
|
context.call<void>("close");
|
|
|
|
|
|
|
|
mad_synth_finish(&synth);
|
|
|
|
mad_stream_finish(&stream);
|
|
|
|
mad_frame_finish(&frame);
|
|
|
|
|
|
|
|
delete[] glue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
uint8_t* buffer = (uint8_t(*))bufferPtr;
|
|
|
|
RawBuffer rb = {buffer, length, 0, 0};
|
|
|
|
pending.push_back(rb);
|
|
|
|
|
|
|
|
std::cout << "The state now is " << state << std::endl;
|
|
|
|
switch (state) {
|
|
|
|
case empty:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emscripten::val Decoder::decode(uint32_t count)
|
|
|
|
{
|
|
|
|
emscripten::val ret = emscripten::val::undefined();
|
|
|
|
|
|
|
|
int available = framesLeft(count);
|
|
|
|
int success = 0;
|
|
|
|
if (available > 0) {
|
|
|
|
ret = context.call<emscripten::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<emscripten::val>("getChannelData", i);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; success < available; ++i) {
|
|
|
|
int res = mad_frame_decode(&frame, &stream);
|
|
|
|
|
|
|
|
if (res == 0) {
|
|
|
|
++success;
|
|
|
|
} else {
|
|
|
|
if (MAD_RECOVERABLE(stream.error)) {
|
|
|
|
|
|
|
|
std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(&stream) << std::endl;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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(success * samplesPerFrame + j), emscripten::val(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cachedLength -= available;
|
|
|
|
std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(&stream) << std::endl;
|
|
|
|
|
|
|
|
if (cachedLength == 0) {
|
|
|
|
cached = false;
|
|
|
|
prepareNextBuffer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Decoder::hasMore() const
|
|
|
|
{
|
|
|
|
if (pending.size() == 1) {
|
|
|
|
return stream.error != MAD_ERROR_BUFLEN;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Decoder::framesLeft(uint32_t max)
|
|
|
|
{
|
|
|
|
if (state == empty || state == onGlueHalf) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cached == false) {
|
|
|
|
mad_stream probe;
|
|
|
|
mad_header ph;
|
|
|
|
initializeProbe(probe);
|
|
|
|
mad_header_init(&ph);
|
|
|
|
|
|
|
|
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) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (probe.next_frame > probe.this_frame) {
|
|
|
|
++cachedLength;
|
|
|
|
}
|
|
|
|
} else {
|
2019-01-14 14:50:41 +00:00
|
|
|
std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl;
|
2019-01-14 02:19:37 +00:00
|
|
|
if (!MAD_RECOVERABLE(probe.error)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 14:50:41 +00:00
|
|
|
cachedNext = probe.next_frame;
|
|
|
|
cachedThis = probe.this_frame;
|
|
|
|
cachedError = probe.error;
|
2019-01-14 02:19:37 +00:00
|
|
|
mad_header_finish(&ph);
|
|
|
|
mad_stream_finish(&probe);
|
|
|
|
std::cout << cachedLength << " frames are available for decoding" << std::endl;
|
|
|
|
cached = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::min(cachedLength, max);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Decoder::pullBuffer()
|
|
|
|
{
|
2019-01-14 14:50:41 +00:00
|
|
|
if (cached == false) {
|
|
|
|
std::cout << "Error in pullBuffer method!" << std::endl;
|
2019-01-14 02:19:37 +00:00
|
|
|
}
|
2019-01-14 14:50:41 +00:00
|
|
|
stream.this_frame = cachedThis;
|
|
|
|
stream.next_frame = cachedNext;
|
|
|
|
stream.error = cachedError;
|
2019-01-14 02:19:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Decoder::changeBuffer()
|
|
|
|
{
|
|
|
|
uint32_t left;
|
|
|
|
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;
|
|
|
|
|
2019-01-14 14:50:41 +00:00
|
|
|
switchBuffer(pending[0].ptr, pending[0].length);
|
2019-01-14 02:19:37 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
mad_stream_init(&probe);
|
|
|
|
|
|
|
|
probe.buffer = stream.buffer;
|
|
|
|
probe.bufend = stream.bufend;
|
|
|
|
probe.skiplen = stream.skiplen;
|
|
|
|
probe.sync = stream.sync;
|
|
|
|
probe.freerate = stream.freerate;
|
|
|
|
probe.this_frame = stream.this_frame;
|
|
|
|
probe.next_frame = stream.next_frame;
|
|
|
|
probe.ptr.byte = stream.ptr.byte;
|
|
|
|
probe.ptr.cache = stream.ptr.cache;
|
|
|
|
probe.ptr.cache = stream.ptr.cache;
|
|
|
|
probe.anc_ptr.byte = stream.anc_ptr.byte;
|
|
|
|
probe.anc_ptr.cache = stream.anc_ptr.cache;
|
|
|
|
probe.anc_ptr.cache = stream.anc_ptr.cache;
|
|
|
|
probe.anc_bitlen = stream.anc_bitlen;
|
|
|
|
//probe.main_data = stream.main_data;
|
|
|
|
//probe.md_len = stream.md_len;
|
|
|
|
probe.options = stream.options;
|
|
|
|
probe.error = stream.error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Decoder::switchToGlue()
|
|
|
|
{
|
|
|
|
std::cout << "Switching to glue" << std::endl;
|
|
|
|
|
2019-01-14 14:50:41 +00:00
|
|
|
switchBuffer(glue, GLUE_LENGTH);
|
2019-01-14 02:19:37 +00:00
|
|
|
stream.error = MAD_ERROR_NONE;
|
|
|
|
|
|
|
|
std::cout << "Freeing the drained fragment" << std::endl;
|
|
|
|
free(pending[0].ptr);
|
|
|
|
pending.pop_front();
|
|
|
|
}
|
2019-01-14 14:50:41 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|