#include <ScriptProcessorNode.h>
#include <AudioContext.h>

namespace webaudio {

AudioBuffer::AudioBuffer(uint32_t sampleRate, Local<Array> buffers) : sampleRate(sampleRate), buffers(buffers) {}
AudioBuffer::~AudioBuffer() {}
Handle<Object> AudioBuffer::Initialize(Isolate *isolate) {
  Nan::EscapableHandleScope scope;

  // constructor
  Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(New);
  ctor->InstanceTemplate()->SetInternalFieldCount(1);
  ctor->SetClassName(JS_STR("AudioBuffer"));

  // prototype
  Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
  Nan::SetAccessor(proto, JS_STR("sampleRate"), SampleRate);
  Nan::SetAccessor(proto, JS_STR("length"), Length);
  Nan::SetAccessor(proto, JS_STR("numberOfChannels"), NumberOfChannels);
  Nan::SetMethod(proto, "getChannelData", GetChannelData);
  Nan::SetMethod(proto, "copyFromChannel", CopyFromChannel);
  Nan::SetMethod(proto, "copyToChannel", CopyToChannel);

  Local<Function> ctorFn = ctor->GetFunction();

  return scope.Escape(ctorFn);
}
NAN_METHOD(AudioBuffer::New) {
  Nan::HandleScope scope;

  if (info[0]->IsNumber() && info[1]->IsNumber() && info[2]->IsNumber()) {
    uint32_t numOfChannels = info[0]->Uint32Value();
    uint32_t length = info[1]->Uint32Value();
    uint32_t sampleRate = info[2]->Uint32Value();
    Local<Array> buffers;
    if (info[3]->IsArray()) {
      buffers = Local<Array>::Cast(info[3]);
    } else {
      buffers = Nan::New<Array>(numOfChannels);
      for (size_t i = 0; i < numOfChannels; i++) {
        Local<ArrayBuffer> arrayBuffer = ArrayBuffer::New(Isolate::GetCurrent(), length * sizeof(float));
        Local<Float32Array> float32Array = Float32Array::New(arrayBuffer, 0, length);
        buffers->Set(i, float32Array);
      }
    }

    AudioBuffer *audioBuffer = new AudioBuffer(sampleRate, buffers);
    Local<Object> audioBufferObj = info.This();
    audioBuffer->Wrap(audioBufferObj);
  } else {
    Nan::ThrowError("AudioBuffer:New: invalid arguments");
  }
}
NAN_GETTER(AudioBuffer::SampleRate) {
  Nan::HandleScope scope;

  AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(info.This());
  info.GetReturnValue().Set(JS_INT(audioBuffer->sampleRate));
}
NAN_GETTER(AudioBuffer::Length) {
  Nan::HandleScope scope;

  AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(info.This());
  Local<Array> buffers = Nan::New(audioBuffer->buffers);
  if (buffers->Length() > 0) {
    Local<Float32Array> firstBuffer = Local<Float32Array>::Cast(buffers->Get(0));
    uint32_t numFrames = firstBuffer->Length();

    info.GetReturnValue().Set(JS_INT(numFrames));
  } else {
    info.GetReturnValue().Set(JS_INT(0));
  }
}
NAN_GETTER(AudioBuffer::NumberOfChannels) {
  Nan::HandleScope scope;

  AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(info.This());
  Local<Array> buffers = Nan::New(audioBuffer->buffers);

  info.GetReturnValue().Set(JS_INT(buffers->Length()));
}
NAN_METHOD(AudioBuffer::GetChannelData) {
  Nan::HandleScope scope;

  if (info[0]->IsNumber()) {
    uint32_t channelIndex = info[0]->Uint32Value();

    AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(info.This());
    Local<Array> buffers = Nan::New(audioBuffer->buffers);
    Local<Value> channelData = buffers->Get(channelIndex);

    info.GetReturnValue().Set(channelData);
  } else {
    Nan::ThrowError("AudioBuffer:GetChannelData: invalid arguments");
  }
}
NAN_METHOD(AudioBuffer::CopyFromChannel) {
  Nan::HandleScope scope;

  if (info[0]->IsFloat32Array() && info[1]->IsNumber()) {
    Local<Float32Array> destinationFloat32Array = Local<Float32Array>::Cast(info[0]);
    uint32_t channelIndex = info[1]->Uint32Value();

    AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(info.This());
    Local<Array> buffers = Nan::New(audioBuffer->buffers);
    Local<Value> channelData = buffers->Get(channelIndex);

    if (channelData->BooleanValue()) {
      Local<Float32Array> channelDataFloat32Array = Local<Float32Array>::Cast(channelData);
      uint32_t offset = std::min<uint32_t>(info[2]->IsNumber() ? info[2]->Uint32Value() : 0, channelDataFloat32Array->Length());
      size_t copyLength = std::min<size_t>(channelDataFloat32Array->Length() - offset, destinationFloat32Array->Length());
      for (size_t i = 0; i < copyLength; i++) {
        destinationFloat32Array->Set(i, channelDataFloat32Array->Get(offset + i));
      }
    } else {
      Nan::ThrowError("AudioBuffer:CopyFromChannel: invalid channel index");
    }
  } else {
    Nan::ThrowError("AudioBuffer:CopyFromChannel: invalid arguments");
  }
}
NAN_METHOD(AudioBuffer::CopyToChannel) {
  Nan::HandleScope scope;

  if (info[0]->IsFloat32Array() && info[1]->IsNumber()) {
    Local<Float32Array> sourceFloat32Array = Local<Float32Array>::Cast(info[0]);
    uint32_t channelIndex = info[1]->Uint32Value();

    AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(info.This());
    Local<Array> buffers = Nan::New(audioBuffer->buffers);
    Local<Value> channelData = buffers->Get(channelIndex);

    if (channelData->BooleanValue()) {
      Local<Float32Array> channelDataFloat32Array = Local<Float32Array>::Cast(channelData);
      uint32_t offset = std::min<uint32_t>(info[2]->IsNumber() ? info[2]->Uint32Value() : 0, channelDataFloat32Array->Length());
      size_t copyLength = std::min<size_t>(channelDataFloat32Array->Length() - offset, sourceFloat32Array->Length());
      for (size_t i = 0; i < copyLength; i++) {
        channelDataFloat32Array->Set(offset + i, sourceFloat32Array->Get(i));
      }
    } else {
      Nan::ThrowError("AudioBuffer:CopyToChannel: invalid channel index");
    }
  } else {
    Nan::ThrowError("AudioBuffer:CopyToChannel: invalid arguments");
  }
}

AudioBufferSourceNode::AudioBufferSourceNode() {
  audioNode.reset(new lab::FinishableSourceNode([this](lab::ContextRenderLock &r){
    QueueOnMainThread(r, std::bind(ProcessInMainThread, this));
  }));
}
AudioBufferSourceNode::~AudioBufferSourceNode() {}
Handle<Object> AudioBufferSourceNode::Initialize(Isolate *isolate) {
  Nan::EscapableHandleScope scope;

  // constructor
  Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(New);
  ctor->InstanceTemplate()->SetInternalFieldCount(1);
  ctor->SetClassName(JS_STR("AudioBufferSourceNode"));

  // prototype
  Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
  AudioNode::InitializePrototype(proto);
  AudioBufferSourceNode::InitializePrototype(proto);

  Local<Function> ctorFn = ctor->GetFunction();

  return scope.Escape(ctorFn);
}
void AudioBufferSourceNode::InitializePrototype(Local<ObjectTemplate> proto) {
  Nan::SetAccessor(proto, JS_STR("buffer"), BufferGetter, BufferSetter);
  Nan::SetAccessor(proto, JS_STR("onended"), OnEndedGetter, OnEndedSetter);
  Nan::SetMethod(proto, "start", Start);
  Nan::SetMethod(proto, "stop", Stop);
}
NAN_METHOD(AudioBufferSourceNode::New) {
  Nan::HandleScope scope;

  if (info[0]->IsObject() && info[0]->ToObject()->Get(JS_STR("constructor"))->ToObject()->Get(JS_STR("name"))->StrictEquals(JS_STR("AudioContext"))) {
    Local<Object> audioContextObj = Local<Object>::Cast(info[0]);
    AudioContext *audioContext = ObjectWrap::Unwrap<AudioContext>(audioContextObj);

    AudioBufferSourceNode *audioBufferSourceNode = new AudioBufferSourceNode();
    Local<Object> audioBufferSourceNodeObj = info.This();
    audioBufferSourceNode->Wrap(audioBufferSourceNodeObj);

    audioBufferSourceNode->context.Reset(audioContextObj);

    info.GetReturnValue().Set(audioBufferSourceNodeObj);
  } else {
    Nan::ThrowError("AudioBufferSourceNode:New: invalid arguments");
  }
}
NAN_METHOD(AudioBufferSourceNode::Start) {
  Nan::HandleScope scope;

  AudioBufferSourceNode *audioBufferSourceNode = ObjectWrap::Unwrap<AudioBufferSourceNode>(info.This());
  ((lab::FinishableSourceNode *)audioBufferSourceNode->audioNode.get())->start(0);
}
NAN_METHOD(AudioBufferSourceNode::Stop) {
  Nan::HandleScope scope;

  AudioBufferSourceNode *audioBufferSourceNode = ObjectWrap::Unwrap<AudioBufferSourceNode>(info.This());
  ((lab::FinishableSourceNode *)audioBufferSourceNode->audioNode.get())->stop(0);
}
NAN_GETTER(AudioBufferSourceNode::BufferGetter) {
  Nan::HandleScope scope;

  AudioBufferSourceNode *audioBufferSourceNode = ObjectWrap::Unwrap<AudioBufferSourceNode>(info.This());
  Local<Object> buffer = Nan::New(audioBufferSourceNode->buffer);
  info.GetReturnValue().Set(buffer);
}
NAN_SETTER(AudioBufferSourceNode::BufferSetter) {
  Nan::HandleScope scope;

  AudioBufferSourceNode *audioBufferSourceNode = ObjectWrap::Unwrap<AudioBufferSourceNode>(info.This());
  Local<Object> audioContextObj = Nan::New(audioBufferSourceNode->context);
  AudioContext *audioContext = ObjectWrap::Unwrap<AudioContext>(audioContextObj);
  lab::FinishableSourceNode *audioNode = (lab::FinishableSourceNode *)audioBufferSourceNode->audioNode.get();

  if (value->IsObject() && value->ToObject()->Get(JS_STR("constructor"))->ToObject()->Get(JS_STR("name"))->StrictEquals(JS_STR("AudioBuffer"))) {
    Local<Object> audioBufferObj = Local<Object>::Cast(value);
    audioBufferSourceNode->buffer.Reset(audioBufferObj);

    AudioBuffer *audioBuffer = ObjectWrap::Unwrap<AudioBuffer>(audioBufferObj);
    Local<Array> buffers = Nan::New(audioBuffer->buffers);
    size_t numChannels = buffers->Length();
    size_t numFrames = numChannels > 0 ? Local<Float32Array>::Cast(buffers->Get(0))->Length() : 0;

    unique_ptr<float *[]> frames(new float*[numChannels]);
    for (size_t i = 0; i < numChannels; i++) {
      Local<Float32Array> bufferFramesFloat32Array = Local<Float32Array>::Cast(buffers->Get(i));
      size_t numBufferFrames = bufferFramesFloat32Array->Length();
      Local<ArrayBuffer> bufferFramesArrayBuffer = bufferFramesFloat32Array->Buffer();
      frames[i] = (float *)((unsigned char *)bufferFramesArrayBuffer->GetContents().Data() + bufferFramesFloat32Array->ByteOffset());
    }

    shared_ptr<lab::AudioBus> audioBus(lab::MakeBusFromRawBuffer(audioContext->audioContext->sampleRate(), numChannels, numFrames, frames.get(), false).release());

    {
      lab::ContextRenderLock lock(audioContext->audioContext, "AudioBufferSourceNode::buffer");
      audioNode->reset(lock);
      audioNode->setBus(lock, audioBus);
    }
  } else {
    audioBufferSourceNode->buffer.Reset();

    {
      lab::ContextRenderLock lock(audioContext->audioContext, "AudioBufferSourceNode::buffer");
      audioNode->reset(lock);
      audioNode->setBus(lock, nullptr);
    }
  }
}
NAN_GETTER(AudioBufferSourceNode::OnEndedGetter) {
  Nan::HandleScope scope;

  AudioBufferSourceNode *audioBufferSourceNode = ObjectWrap::Unwrap<AudioBufferSourceNode>(info.This());
  Local<Function> onended = Nan::New(audioBufferSourceNode->onended);
  info.GetReturnValue().Set(onended);
}
NAN_SETTER(AudioBufferSourceNode::OnEndedSetter) {
  Nan::HandleScope scope;

  AudioBufferSourceNode *audioBufferSourceNode = ObjectWrap::Unwrap<AudioBufferSourceNode>(info.This());

  if (value->IsFunction()) {
    Local<Function> onended = Local<Function>::Cast(value);
    audioBufferSourceNode->onended.Reset(onended);
  } else {
    audioBufferSourceNode->onended.Reset();
  }
}
void AudioBufferSourceNode::ProcessInMainThread(AudioBufferSourceNode *self) {
  Nan::HandleScope scope;

  if (!self->onended.IsEmpty()) {
    Local<Function> onended = Nan::New(self->onended);
    onended->Call(Nan::Null(), 0, nullptr);
  }
}

AudioProcessingEvent::AudioProcessingEvent(Local<Object> inputBuffer, Local<Object> outputBuffer) : inputBuffer(inputBuffer), outputBuffer(outputBuffer) {}
AudioProcessingEvent::~AudioProcessingEvent() {}
Handle<Object> AudioProcessingEvent::Initialize(Isolate *isolate) {
  Nan::EscapableHandleScope scope;

  // constructor
  Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(New);
  ctor->InstanceTemplate()->SetInternalFieldCount(1);
  ctor->SetClassName(JS_STR("AudioProcessingEvent"));

  // prototype
  Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
  Nan::SetAccessor(proto, JS_STR("inputBuffer"), InputBufferGetter);
  Nan::SetAccessor(proto, JS_STR("outputBuffer"), OutputBufferGetter);
  Nan::SetAccessor(proto, JS_STR("numberOfInputChannels"), NumberOfInputChannelsGetter);
  Nan::SetAccessor(proto, JS_STR("numberOfOutputChannels"), NumberOfOutputChannelsGetter);

  Local<Function> ctorFn = ctor->GetFunction();

  return scope.Escape(ctorFn);
}
NAN_METHOD(AudioProcessingEvent::New) {
  Nan::HandleScope scope;

  if (info[0]->IsObject() && info[1]->IsObject()) {
    Local<Object> inputBuffer = Local<Object>::Cast(info[0]);
    Local<Object> outputBuffer = Local<Object>::Cast(info[1]);

    AudioProcessingEvent *audioProcessingEvent = new AudioProcessingEvent(inputBuffer, outputBuffer);
    Local<Object> audioProcessingEventObj = info.This();
    audioProcessingEvent->Wrap(audioProcessingEventObj);
  } else {
    Nan::ThrowError("AudioProcessingEvent:New: invalid arguments");
  }
}
NAN_GETTER(AudioProcessingEvent::InputBufferGetter) {
  Nan::HandleScope scope;

  AudioProcessingEvent *audioProcessingEvent = ObjectWrap::Unwrap<AudioProcessingEvent>(info.This());
  Local<Object> inputBufferObj = Nan::New(audioProcessingEvent->inputBuffer);
  info.GetReturnValue().Set(inputBufferObj);
}
NAN_GETTER(AudioProcessingEvent::OutputBufferGetter) {
  Nan::HandleScope scope;

  AudioProcessingEvent *audioProcessingEvent = ObjectWrap::Unwrap<AudioProcessingEvent>(info.This());
  Local<Object> outputBufferObj = Nan::New(audioProcessingEvent->outputBuffer);
  info.GetReturnValue().Set(outputBufferObj);
}
NAN_GETTER(AudioProcessingEvent::NumberOfInputChannelsGetter) {
  Nan::HandleScope scope;

  AudioProcessingEvent *audioProcessingEvent = ObjectWrap::Unwrap<AudioProcessingEvent>(info.This());
  Local<Object> inputBufferObj = Nan::New(audioProcessingEvent->inputBuffer);
  Local<Value> numberOfChannels = inputBufferObj->Get(JS_STR("numberOfChannels"));
  info.GetReturnValue().Set(numberOfChannels);
}
NAN_GETTER(AudioProcessingEvent::NumberOfOutputChannelsGetter) {
  Nan::HandleScope scope;

  AudioProcessingEvent *audioProcessingEvent = ObjectWrap::Unwrap<AudioProcessingEvent>(info.This());
  Local<Object> outpuctBufferObj = Nan::New(audioProcessingEvent->outputBuffer);
  Local<Value> numberOfChannels = outpuctBufferObj->Get(JS_STR("numberOfChannels"));
  info.GetReturnValue().Set(numberOfChannels);
}

ScriptProcessorNode::ScriptProcessorNode(uint32_t bufferSize, uint32_t numberOfInputChannels, uint32_t numberOfOutputChannels) : bufferSize(bufferSize), numberOfInputChannels(numberOfInputChannels), numberOfOutputChannels(numberOfOutputChannels), bufferIndex(0) {
  for (size_t i = 0; i < numberOfInputChannels; i++) {
    inputBuffers.emplace_back((size_t)bufferSize);
    outputBuffers.emplace_back((size_t)bufferSize);
  }
}
ScriptProcessorNode::~ScriptProcessorNode() {}
Handle<Object> ScriptProcessorNode::Initialize(Isolate *isolate, Local<Value> audioBufferCons, Local<Value> audioProcessingEventCons) {
  Nan::EscapableHandleScope scope;

  // constructor
  Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(New);
  ctor->InstanceTemplate()->SetInternalFieldCount(1);
  ctor->SetClassName(JS_STR("ScriptProcessorNode"));

  // prototype
  Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
  AudioNode::InitializePrototype(proto);
  ScriptProcessorNode::InitializePrototype(proto);

  Local<Function> ctorFn = ctor->GetFunction();

  ctorFn->Set(JS_STR("AudioBuffer"), audioBufferCons);
  ctorFn->Set(JS_STR("AudioProcessingEvent"), audioProcessingEventCons);

  return scope.Escape(ctorFn);
}
void ScriptProcessorNode::InitializePrototype(Local<ObjectTemplate> proto) {
  Nan::SetAccessor(proto, JS_STR("onaudioprocess"), OnAudioProcessGetter, OnAudioProcessSetter);
}
NAN_METHOD(ScriptProcessorNode::New) {
  Nan::HandleScope scope;

  if (
    info[0]->IsNumber() && info[1]->IsNumber() && info[2]->IsNumber() &&
    info[3]->IsObject() && info[3]->ToObject()->Get(JS_STR("constructor"))->ToObject()->Get(JS_STR("name"))->StrictEquals(JS_STR("AudioContext"))
  ) {
    uint32_t bufferSize = info[0]->Uint32Value();
    uint32_t numberOfInputChannels = info[1]->Uint32Value();
    uint32_t numberOfOutputChannels = info[2]->Uint32Value();

    if (numberOfInputChannels == numberOfOutputChannels) {
      Local<Object> audioContextObj = Local<Object>::Cast(info[3]);

      ScriptProcessorNode *scriptProcessorNode = new ScriptProcessorNode(bufferSize, numberOfInputChannels, numberOfOutputChannels);
      Local<Object> scriptProcessorNodeObj = info.This();
      scriptProcessorNode->Wrap(scriptProcessorNodeObj);

      scriptProcessorNode->context.Reset(audioContextObj);
      lab::ScriptProcessorNode *labScriptProcessorNode = new lab::ScriptProcessorNode(numberOfInputChannels, [scriptProcessorNode](lab::ContextRenderLock& r, vector<const float*> sources, vector<float*> destinations, size_t framesToProcess) {
        scriptProcessorNode->ProcessInAudioThread(r, sources, destinations, framesToProcess);
      });
      scriptProcessorNode->audioNode.reset(labScriptProcessorNode);

      Local<Function> audioBufferConstructor = Local<Function>::Cast(scriptProcessorNodeObj->Get(JS_STR("constructor"))->ToObject()->Get(JS_STR("AudioBuffer")));
      scriptProcessorNode->audioBufferConstructor.Reset(audioBufferConstructor);
      Local<Function> audioProcessingEventConstructor = Local<Function>::Cast(scriptProcessorNodeObj->Get(JS_STR("constructor"))->ToObject()->Get(JS_STR("AudioProcessingEvent")));
      scriptProcessorNode->audioProcessingEventConstructor.Reset(audioProcessingEventConstructor);

      info.GetReturnValue().Set(scriptProcessorNodeObj);
    } else {
      Nan::ThrowError("ScriptProcessorNode: numberOfInputChannels and numberOfOutputChannels do not match");
    }
  } else {
    Nan::ThrowError("ScriptProcessorNode: invalid arguments");
  }
}
NAN_GETTER(ScriptProcessorNode::OnAudioProcessGetter) {
  Nan::HandleScope scope;

  ScriptProcessorNode *scriptProcessorNode = ObjectWrap::Unwrap<ScriptProcessorNode>(info.This());

  Local<Function> onAudioProcessLocal = Nan::New(scriptProcessorNode->onAudioProcess);
  info.GetReturnValue().Set(onAudioProcessLocal);
}
NAN_SETTER(ScriptProcessorNode::OnAudioProcessSetter) {
  Nan::HandleScope scope;

  ScriptProcessorNode *scriptProcessorNode = ObjectWrap::Unwrap<ScriptProcessorNode>(info.This());

  if (value->IsFunction()) {
    Local<Function> onAudioProcessLocal = Local<Function>::Cast(value);
    scriptProcessorNode->onAudioProcess.Reset(onAudioProcessLocal);
  } else {
    scriptProcessorNode->onAudioProcess.Reset();
  }
}
void ScriptProcessorNode::ProcessInAudioThread(lab::ContextRenderLock& r, vector<const float*> sources, vector<float*> destinations, size_t framesToProcess) {
  for (size_t i = 0; i < sources.size(); i++) {
    const float *source = sources[i];
    float *inputBuffer = inputBuffers[i].data() + bufferIndex;
    memcpy(inputBuffer, source, framesToProcess * sizeof(float));
  }
  for (size_t i = 0; i < destinations.size(); i++) {
    float *destination = destinations[i];
    const float *outputBuffer = outputBuffers[i].data() + bufferIndex;
    memcpy(destination, outputBuffer, framesToProcess * sizeof(float));
  }
  bufferIndex += framesToProcess;

  if (bufferIndex >= bufferSize) {
    QueueOnMainThread(r, std::bind(ProcessInMainThread, this));
    bufferIndex -= bufferSize;
  }
}
void ScriptProcessorNode::ProcessInMainThread(ScriptProcessorNode *self) {
  Nan::HandleScope scope;

  size_t framesToProcess = self->bufferSize;
  vector<vector<float>> &sources = self->inputBuffers;
  vector<vector<float>> &destinations = self->outputBuffers;

  Local<Array> outputAudioNodes = Nan::New(self->outputAudioNodes);
  size_t numOutputAudioNodes = outputAudioNodes->Length();
  bool connectedOutput = false;
  for (size_t i = 0; i < numOutputAudioNodes; i++) {
    if (outputAudioNodes->Get(i)->BooleanValue()) {
      connectedOutput = true;
      break;
    }
  }

  if (connectedOutput && !self->onAudioProcess.IsEmpty()) {
    Local<Function> onAudioProcessLocal = Nan::New(self->onAudioProcess);

    Local<Object> audioContextObj = Nan::New(self->context);
    AudioContext *audioContext = ObjectWrap::Unwrap<AudioContext>(audioContextObj);

    Local<Array> sourcesArray = Nan::New<Array>(sources.size());
    for (size_t i = 0; i < sources.size(); i++) {
      const float *source = sources[i].data();
      Local<ArrayBuffer> sourceArrayBuffer = ArrayBuffer::New(Isolate::GetCurrent(), (void *)source, framesToProcess * sizeof(float));
      Local<Float32Array> sourceFloat32Array = Float32Array::New(sourceArrayBuffer, 0, framesToProcess);
      sourcesArray->Set(i, sourceFloat32Array);
    }
    Local<Function> audioBufferConstructorFn = Nan::New(self->audioBufferConstructor);
    Local<Value> argv1[] = {
      JS_INT((uint32_t)sources.size()),
      JS_INT((uint32_t)framesToProcess),
      JS_INT((uint32_t)audioContext->audioContext->sampleRate()),
      sourcesArray,
    };
    Local<Object> inputBuffer = audioBufferConstructorFn->NewInstance(Isolate::GetCurrent()->GetCurrentContext(), sizeof(argv1)/sizeof(argv1[0]), argv1).ToLocalChecked();

    Local<Array> destinationsArray = Nan::New<Array>(destinations.size());
    for (size_t i = 0; i < destinations.size(); i++) {
      float *destination = destinations[i].data();
      Local<ArrayBuffer> destinationArrayBuffer = ArrayBuffer::New(Isolate::GetCurrent(), destination, framesToProcess * sizeof(float));
      Local<Float32Array> destinationFloat32Array = Float32Array::New(destinationArrayBuffer, 0, framesToProcess);
      destinationsArray->Set(i, destinationFloat32Array);
    }
    Local<Value> argv2[] = {
      JS_INT((uint32_t)sources.size()),
      JS_INT((uint32_t)framesToProcess),
      JS_INT((uint32_t)audioContext->audioContext->sampleRate()),
      destinationsArray,
    };
    Local<Object> outputBuffer = audioBufferConstructorFn->NewInstance(Isolate::GetCurrent()->GetCurrentContext(), sizeof(argv2)/sizeof(argv2[0]), argv2).ToLocalChecked();

    Local<Function> audioProcessingEventConstructorFn = Nan::New(self->audioProcessingEventConstructor);
    Local<Value> argv3[] = {
      inputBuffer,
      outputBuffer,
      JS_INT((uint32_t)framesToProcess)
    };
    Local<Object> audioProcessingEventObj = audioProcessingEventConstructorFn->NewInstance(Isolate::GetCurrent()->GetCurrentContext(), sizeof(argv3)/sizeof(argv3[0]), argv3).ToLocalChecked();

    Local<Value> argv4[] = {
      audioProcessingEventObj,
    };
    onAudioProcessLocal->Call(Nan::Null(), sizeof(argv4)/sizeof(argv4[0]), argv4);

    for (size_t i = 0; i < sourcesArray->Length(); i++) {
      Local<Float32Array> sourceFloat32Array = Local<Float32Array>::Cast(sourcesArray->Get(i));
      sourceFloat32Array->Buffer()->Neuter();
    }
    for (size_t i = 0; i < destinationsArray->Length(); i++) {
      Local<Float32Array> destinationFloat32Array = Local<Float32Array>::Cast(destinationsArray->Get(i));
      destinationFloat32Array->Buffer()->Neuter();
    }
  } else {
    for (size_t i = 0; i < sources.size(); i++) {
      memcpy(destinations[i].data(), sources[i].data(), framesToProcess * sizeof(float));
    }
  }
}

}
