jsmad/wrapper.cpp

212 lines
5.3 KiB
C++

#include "wrapper.h"
#include <string>
#include <iostream>
MadHeader * mad_header_create()
{
MadHeader* header = new MadHeader;
mad_header_init(header);
return header;
}
MadFrame * mad_frame_create()
{
MadFrame* frame = new MadFrame;
mad_frame_init(frame);
return frame;
}
emscripten::val MadFrame::getOverlap() const
{
unsigned char* ptr = (unsigned char(*))*overlap;
return emscripten::val(typed_memory_view(sizeof *overlap, ptr));
}
emscripten::val MadFrame::getSbsample() const
{
unsigned char* ptr = (unsigned char(*))sbsample;
return emscripten::val(typed_memory_view(sizeof sbsample, ptr));
}
MadStream* mad_stream_create() {
MadStream* stream = new MadStream;
mad_stream_init(stream);
return stream;
}
emscripten::val MadStream::mainData()
{
return emscripten::val(typed_memory_view(MAD_BUFFER_MDLEN, (unsigned char(*))main_data));
}
void MadStream::setBuffer(intptr_t bufferPtr, unsigned long length)
{
mad_stream_buffer(this, (const unsigned char(*))bufferPtr, length);
}
int MadFrame::decode(mad_stream* stream)
{
return mad_frame_decode(this, stream);
}
emscripten::val MadPCM::samples() const
{
mad_fixed_t* ptr = (mad_fixed_t(*))mad_pcm::samples;
return emscripten::val(typed_memory_view(sizeof mad_pcm::samples, ptr));
}
MadPCM* mad_pcm_create() {
MadPCM* pcm = new MadPCM();
return pcm;
}
MadSynth* mad_synth_create() {
MadSynth* synth = new MadSynth();
mad_synth_init(synth);
return synth;
}
void MadSynth::frame(const mad_frame* frame)
{
mad_synth_frame(this, frame);
}
Decoder::Decoder():
sampleRate(0),
channels(0),
cachedLength(0),
samplesPerFrame(0),
currentBuffer(0),
currentBufferLength(0),
synth(),
stream(),
frame(),
context(0)
{
mad_frame_init(&frame);
mad_stream_init(&stream);
mad_synth_init(&synth);
emscripten::val AudioContext = emscripten::val::global("AudioContext");
if (!AudioContext.as<bool>()) {
printf("No global AudioContext, trying webkitAudioContext\n");
AudioContext = emscripten::val::global("webkitAudioContext");
}
printf("Got an AudioContext\n");
context = AudioContext.new_();
}
Decoder::~Decoder()
{
context.call<void>("close");
mad_synth_finish(&synth);
mad_stream_finish(&stream);
mad_frame_finish(&frame);
if (currentBuffer != 0) {
free(currentBuffer);
}
}
void Decoder::addFragment(intptr_t bufferPtr, unsigned long length)
{
if (currentBuffer != 0) {
printf("Adding more then 1 fragment is not supported yet");
throw 1;
}
currentBuffer = (unsigned char(*))bufferPtr;
currentBufferLength = length;
mad_stream_buffer(&stream, currentBuffer, currentBufferLength);
framesLeft();
}
emscripten::val Decoder::decode(uint32_t count)
{
emscripten::val ret = emscripten::val::undefined();
int available = framesLeft(count);
if (available > 0) {
ret = context.call<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<val>("getChannelData", i);
}
for (int i = 0; i < available; ++i) {
mad_frame_decode(&frame, &stream);
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(i * samplesPerFrame + j), val(value));
}
}
}
cachedLength -= available;
if (cachedLength == 0) {
framesLeft();
}
}
return ret;
}
bool Decoder::hasMore() const
{
if (currentBuffer == 0) {
return false;
}
return stream.error != MAD_ERROR_BUFLEN;
}
uint32_t Decoder::framesLeft(uint32_t max)
{
if (currentBuffer == 0) {
return 0;
}
if (cachedLength == 0) {
mad_stream probe;
mad_header ph;
mad_stream_init(&probe);
mad_header_init(&ph);
mad_stream_buffer(&probe, currentBuffer, currentBufferLength);
mad_stream_skip(&probe, stream.skiplen);
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;
}
}
++cachedLength;
} else {
if (!MAD_RECOVERABLE(probe.error)) {
std::cout << "framesLeft::" << probe.error << std::endl;
break;
}
}
}
mad_header_finish(&ph);
mad_stream_finish(&probe);
}
return std::min(cachedLength, max);
}