Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
88d69a503a | |||
7dc9c22fda |
@ -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)
|
||||||
|
|
||||||
|
184
decoder.cpp
184
decoder.cpp
@ -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) {}
|
|
||||||
}
|
|
||||||
|
33
decoder.h
33
decoder.h
@ -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
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user