<commit_before>#include <boost/lockfree/queue.hpp>
#include <csound/cwindow.h>
#include <nan.h>

// CsoundCallback is a subclass of Nan::Callback
// <https://github.com/nodejs/nan/blob/master/doc/callback.md> to make it easier
// to store JavaScript functions as Csound callback functions. CsoundCallback
// instances maintain an async handle <http://docs.libuv.org/en/v1.x/async.html>
// and a lock-free queue of callback arguments of type T. Argument classes must
// have a static member T::argc storing the number of arguments of the callback,
// a method T::getArgv that puts Csound callback arguments into an array of V8
// values, and a method T::wereSent that can clean up after arguments are sent.
// When the static method CsoundCallback::asyncCallback runs after a call to
// uv_async_send, the CsoundCallback instance pops arguments off its queue into
// arrays of V8 values, which are then passed to a JavaScript function by
// Nan::Callback::Call(). Using a lock-free queue is from csound.node by Michael
// Gogins, which is available as part of Csound
// <https://github.com/csound/csound/tree/develop/frontends/nwjs>.
template <typename T>
struct CsoundCallback : public Nan::Callback {
  uv_async_t handle;
  boost::lockfree::queue<T> argumentsQueue;

  static void asyncCallback(uv_async_t *handle) {
    ((CsoundCallback *)handle->data)->executeCalls();
  }

  CsoundCallback(const v8::Local<v8::Function> &function) : Nan::Callback(function), argumentsQueue(0) {
    assert(uv_async_init(uv_default_loop(), &handle, asyncCallback) == 0);
    handle.data = this;
  }

  void executeCalls() {
    Nan::HandleScope scope;
    T arguments;
    while (argumentsQueue.pop(arguments)) {
      v8::Local<v8::Value> argv[T::argc];
      arguments.getArgv(argv);
      Call(T::argc, argv);
      arguments.wereSent();
    }
  }
};

// These structs store arguments from Csound callbacks.
struct CsoundFileOpenCallbackArguments {
  static const int argc = 4;
  char *path;
  int type;
  int isOpenForWriting;
  int isTemporary;

  static CsoundFileOpenCallbackArguments create(const char *path, int type, int isOpenForWriting, int isTemporary) {
    return {strdup(path), type, isOpenForWriting, isTemporary};
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    argv[0] = Nan::New(path).ToLocalChecked();
    argv[1] = Nan::New(type);
    argv[2] = Nan::New((bool)isOpenForWriting);
    argv[3] = Nan::New((bool)isTemporary);
  }

  void wereSent() {
    free(path);
  }
};

struct CsoundMessageCallbackArguments {
  static const int argc = 2;
  int attributes;
  char *message;

  static CsoundMessageCallbackArguments create(int attributes, const char *format, va_list argumentList) {
    CsoundMessageCallbackArguments arguments;
    arguments.attributes = attributes;

    // When determining the length of the string in the first call to vsnprintf,
    // pass a copy of the argument list so that the original argument list is
    // preserved when writing the string in the second call to vsnprintf.
    va_list argumentListCopy;
    va_copy(argumentListCopy, argumentList);
    int length = vsnprintf(NULL, 0, format, argumentListCopy) + 1;
    va_end(argumentListCopy);
    arguments.message = (char *)malloc(sizeof(char) * length);
    vsnprintf(arguments.message, length, format, argumentList);

    return arguments;
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    argv[0] = Nan::New(attributes);
    argv[1] = Nan::New(message).ToLocalChecked();
  }

  void wereSent() {
    free(message);
  }
};

static Nan::Persistent<v8::Function> WINDATProxyConstructor;

struct WINDATWrapper : public Nan::ObjectWrap {
  WINDAT *data;
  MYFLT *fdataCopy;

  static NAN_METHOD(New) {
    (new WINDATWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  static WINDAT *dataFromPropertyCallbackInfo(Nan::NAN_GETTER_ARGS_TYPE info) {
    return Unwrap<WINDATWrapper>(info.This())->data;
  }
  static NAN_GETTER(windid) { info.GetReturnValue().Set(Nan::New((unsigned int)dataFromPropertyCallbackInfo(info)->windid)); }
  static NAN_GETTER(caption) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->caption).ToLocalChecked()); }
  static NAN_GETTER(polarity) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->polarity)); }
  static NAN_GETTER(max) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->max)); }
  static NAN_GETTER(min) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->min)); }
  static NAN_GETTER(oabsmax) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->oabsmax)); }

  static NAN_GETTER(fdata) {
    WINDATWrapper *wrapper = WINDATWrapper::Unwrap<WINDATWrapper>(info.This());
    v8::Local<v8::Array> fdata = Nan::New<v8::Array>(wrapper->data->npts);
    for (int32 i = 0; i < wrapper->data->npts; i++) {
      fdata->Set(i, Nan::New(wrapper->fdataCopy[i]));
    }
    info.GetReturnValue().Set(fdata);
  }
};

struct CsoundGraphCallbackArguments {
  static const int argc = 1;
  WINDAT *data;
  MYFLT *fdata;

  static CsoundGraphCallbackArguments create(WINDAT *data) {
    CsoundGraphCallbackArguments arguments;
    arguments.setData(data);
    return arguments;
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    v8::Local<v8::Object> proxy = Nan::New(WINDATProxyConstructor)->NewInstance();
    WINDATWrapper *wrapper = WINDATWrapper::Unwrap<WINDATWrapper>(proxy);
    wrapper->data = data;
    wrapper->fdataCopy = fdata;
    argv[0] = proxy;
  }

  void setData(WINDAT *data) {
    this->data = data;
    fdata = (MYFLT *)malloc(sizeof(MYFLT) * data->npts);
    for (int32 i = 0; i < data->npts; i++) {
      fdata[i] = data->fdata[i];
    }
  }

  void wereSent() {
    free(fdata);
  }
};

struct CsoundMakeGraphCallbackArguments : public CsoundGraphCallbackArguments {
  static const int argc = 2;
  const char *name;

  static CsoundMakeGraphCallbackArguments create(WINDAT *data, const char *name) {
    CsoundMakeGraphCallbackArguments arguments;
    arguments.setData(data);
    arguments.name = name;
    return arguments;
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    CsoundGraphCallbackArguments::getArgv(argv);
    argv[1] = Nan::New(name).ToLocalChecked();
  }
};

// CSOUNDWrapper instances perform tasks related to callbacks. They also store
// V8 values passed as host data from JavaScript.
static Nan::Persistent<v8::Function> CSOUNDProxyConstructor;
struct CSOUNDWrapper : public Nan::ObjectWrap {
  CSOUND *Csound;
  Nan::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value> > hostData;

  CsoundCallback<CsoundFileOpenCallbackArguments> *CsoundFileOpenCallbackObject;

  CsoundCallback<CsoundMessageCallbackArguments> *CsoundMessageCallbackObject;

  CsoundCallback<CsoundMakeGraphCallbackArguments> *CsoundMakeGraphCallbackObject;
  CsoundCallback<CsoundGraphCallbackArguments> *CsoundDrawGraphCallbackObject;
  CsoundCallback<CsoundGraphCallbackArguments> *CsoundKillGraphCallbackObject;

  // Require Csound 6.04 or later to use debugger functions.
#define CSOUND_6_04_OR_LATER CS_VERSION >= 6 && CS_SUBVER >= 4
#if CSOUND_6_04_OR_LATER
  Nan::Callback *CsoundBreakpointCallbackObject;
#endif

  static NAN_METHOD(New) {
    (new CSOUNDWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  void queueFileOpen(const char *path, int type, int isOpenForWriting, int isTemporary) {
    CsoundFileOpenCallbackObject->argumentsQueue.push(CsoundFileOpenCallbackArguments::create(path, type, isOpenForWriting, isTemporary));
    uv_async_send(&(CsoundFileOpenCallbackObject->handle));
  }

  void queueMessage(int attributes, const char *format, va_list argumentList) {
    CsoundMessageCallbackObject->argumentsQueue.push(CsoundMessageCallbackArguments::create(attributes, format, argumentList));
    uv_async_send(&(CsoundMessageCallbackObject->handle));
  }

  void queueMakeGraph(WINDAT *windowData, const char *name) {
    CsoundMakeGraphCallbackObject->argumentsQueue.push(CsoundMakeGraphCallbackArguments::create(windowData, name));
    uv_async_send(&(CsoundMakeGraphCallbackObject->handle));
  }

  void queueDrawGraph(WINDAT *windowData) {
    CsoundDrawGraphCallbackObject->argumentsQueue.push(CsoundGraphCallbackArguments::create(windowData));
    uv_async_send(&(CsoundDrawGraphCallbackObject->handle));
  }

  void queueKillGraph(WINDAT *windowData) {
    CsoundKillGraphCallbackObject->argumentsQueue.push(CsoundGraphCallbackArguments::create(windowData));
    uv_async_send(&(CsoundKillGraphCallbackObject->handle));
  }
};

// The CSOUND_CALLBACK_METHOD macro associates a Csound callback function with a
// CsoundCallback instance using an appropriate argument type. For example,
// CSOUND_CALLBACK_METHOD(Message) associates csoundSetMessageCallback with
// CsoundCallback<CsoundMessageCallbackArguments>;
// CSOUND_CALLBACK_METHOD(MakeGraph) associates csoundSetMakeGraphCallback with
// CsoundCallback<CsoundMakeGraphCallbackArguments>; and so on.
#define CSOUND_CALLBACK_METHOD_1(methodNameStem) CSOUND_CALLBACK_METHOD_2(methodNameStem, methodNameStem)
#define CSOUND_CALLBACK_METHOD_2(methodNameStem, argumentsNameStem) \
NAN_METHOD(Set ## methodNameStem ## Callback) { \
  CSOUNDWrapper *wrapper = Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>()); \
  v8::Local<v8::Value> value = info[1]; \
  if (value->IsFunction()) { \
    wrapper->Csound ## methodNameStem ## CallbackObject = new CsoundCallback<Csound ## argumentsNameStem ## CallbackArguments>(value.As<v8::Function>()); \
    csoundSet ## methodNameStem ## Callback(wrapper->Csound, Csound ## methodNameStem ## Callback); \
  } else if (wrapper->Csound ## methodNameStem ## CallbackObject) { \
    delete wrapper->Csound ## methodNameStem ## CallbackObject; \
    wrapper->Csound ## methodNameStem ## CallbackObject = NULL; \
    csoundSet ## methodNameStem ## Callback(wrapper->Csound, NULL); \
  } \
}
#ifndef _MSC_VER
#  define CSOUND_CALLBACK_METHOD(...) BOOST_PP_OVERLOAD(CSOUND_CALLBACK_METHOD_,__VA_ARGS__)(__VA_ARGS__)
#else
#  define CSOUND_CALLBACK_METHOD(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(CSOUND_CALLBACK_METHOD_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif

// Helper function to set return values to either a string or null.
static void setReturnValueWithCString(Nan::ReturnValue<v8::Value> returnValue, const char *string) {
  if (string)
    returnValue.Set(Nan::New(string).ToLocalChecked());
  else
    returnValue.SetNull();
}

static NAN_METHOD(Initialize) {
  info.GetReturnValue().Set(csoundInitialize(info[0]->Int32Value()));
}

struct CsoundInitializationOption {
  static NAN_GETTER(NoSignalHandlers) { info.GetReturnValue().Set(CSOUNDINIT_NO_SIGNAL_HANDLER); }
  static NAN_GETTER(NoExitFunction) { info.GetReturnValue().Set(CSOUNDINIT_NO_ATEXIT); }
};

static NAN_METHOD(Create) {
  v8::Local<v8::Object> proxy = Nan::New(CSOUNDProxyConstructor)->NewInstance();
  CSOUNDWrapper *wrapper = Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(proxy);
  CSOUND *Csound = csoundCreate(wrapper);
  if (Csound) {
    wrapper->Csound = Csound;
    wrapper->hostData.Reset(info[0]);
    info.GetReturnValue().Set(proxy);
  } else {
    info.GetReturnValue().SetNull();
  }
}

static CSOUND *CsoundFromFunctionCallbackInfo(Nan::NAN_METHOD_ARGS_TYPE info) {
  return Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>())->Csound;
}

static NAN_METHOD(Destroy) {
  csoundDestroy(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(GetVersion) {
  info.GetReturnValue().Set(Nan::New(csoundGetVersion()));
}

static NAN_METHOD(GetAPIVersion) {
  info.GetReturnValue().Set(Nan::New(csoundGetAPIVersion()));
}

static Nan::Persistent<v8::Function> ORCTOKENProxyConstructor;
struct ORCTOKENWrapper : public Nan::ObjectWrap {
  ORCTOKEN *token;

  static NAN_METHOD(New) {
    (new ORCTOKENWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  static ORCTOKEN *tokenFromPropertyCallbackInfo(Nan::NAN_GETTER_ARGS_TYPE info) {
    return Unwrap<ORCTOKENWrapper>(info.This())->token;
  }

  static NAN_GETTER(type) { info.GetReturnValue().Set(Nan::New(tokenFromPropertyCallbackInfo(info)->type)); }
  static NAN_GETTER(value) { info.GetReturnValue().Set(Nan::New(tokenFromPropertyCallbackInfo(info)->value)); }
  static NAN_GETTER(fvalue) { info.GetReturnValue().Set(Nan::New(tokenFromPropertyCallbackInfo(info)->fvalue)); }
  static NAN_GETTER(lexeme) { setReturnValueWithCString(info.GetReturnValue(), tokenFromPropertyCallbackInfo(info)->lexeme); }
  static NAN_GETTER(optype) { setReturnValueWithCString(info.GetReturnValue(), tokenFromPropertyCallbackInfo(info)->optype); }

  static NAN_GETTER(next) {
    ORCTOKEN *token = tokenFromPropertyCallbackInfo(info)->next;
    if (token) {
      v8::Local<v8::Object> proxy = Nan::New(ORCTOKENProxyConstructor)->NewInstance();
      Unwrap<ORCTOKENWrapper>(proxy)->token = token;
      info.GetReturnValue().Set(proxy);
    } else {
      info.GetReturnValue().SetNull();
    }
  }
};

static Nan::Persistent<v8::Function> TREEProxyConstructor;
struct TREEWrapper : public Nan::ObjectWrap {
  TREE *tree;

  static NAN_METHOD(New) {
    (new TREEWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  static TREE *treeFromPropertyCallbackInfo(Nan::NAN_GETTER_ARGS_TYPE info) {
    return Unwrap<TREEWrapper>(info.This())->tree;
  }

  static NAN_GETTER(value) {
    ORCTOKEN *token = treeFromPropertyCallbackInfo(info)->value;
    if (token) {
      v8::Local<v8::Object> proxy = Nan::New(ORCTOKENProxyConstructor)->NewInstance();
      Unwrap<ORCTOKENWrapper>(proxy)->token = token;
      info.GetReturnValue().Set(proxy);
    } else {
      info.GetReturnValue().SetNull();
    }
  }
  static NAN_GETTER(type) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->type)); }
  static NAN_GETTER(rate) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->rate)); }
  static NAN_GETTER(len) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->len)); }
  static NAN_GETTER(line) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->line)); }
  static NAN_GETTER(locn) { info.GetReturnValue().Set(Nan::New(static_cast<double>(treeFromPropertyCallbackInfo(info)->locn))); }

  static void setPropertyCallbackInfoReturnValueWithTree(Nan::NAN_GETTER_ARGS_TYPE info, TREE *tree) {
    if (tree) {
      v8::Local<v8::Object> proxy = Nan::New(TREEProxyConstructor)->NewInstance();
      Unwrap<TREEWrapper>(proxy)->tree = tree;
      info.GetReturnValue().Set(proxy);
    } else {
      info.GetReturnValue().SetNull();
    }
  }
  static NAN_GETTER(left) { setPropertyCallbackInfoReturnValueWithTree(info, treeFromPropertyCallbackInfo(info)->left); }
  static NAN_GETTER(right) { setPropertyCallbackInfoReturnValueWithTree(info, treeFromPropertyCallbackInfo(info)->right); }
  static NAN_GETTER(next) { setPropertyCallbackInfoReturnValueWithTree(info, treeFromPropertyCallbackInfo(info)->next); }
};

static NAN_METHOD(ParseOrc) {
  TREE *tree = csoundParseOrc(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]));
  if (tree) {
    v8::Local<v8::Object> proxy = Nan::New(TREEProxyConstructor)->NewInstance();
    Nan::ObjectWrap::Unwrap<TREEWrapper>(proxy)->tree = tree;
    info.GetReturnValue().Set(proxy);
  } else {
    info.GetReturnValue().SetNull();
  }
}

static NAN_METHOD(CompileTree) {
  info.GetReturnValue().Set(Nan::New(csoundCompileTree(CsoundFromFunctionCallbackInfo(info), Nan::ObjectWrap::Unwrap<TREEWrapper>(info[1].As<v8::Object>())->tree)));
}

static NAN_METHOD(DeleteTree) {
  csoundDeleteTree(CsoundFromFunctionCallbackInfo(info), Nan::ObjectWrap::Unwrap<TREEWrapper>(info[1].As<v8::Object>())->tree);
}

static NAN_METHOD(CompileOrc) {
  info.GetReturnValue().Set(Nan::New(csoundCompileOrc(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(EvalCode) {
  info.GetReturnValue().Set(Nan::New(csoundEvalCode(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

// Helper function to pass V8 values to csoundCompileArgs and csoundCompile.
#if CS_VERSION >= 6 && CS_SUBVER >= 8
#define CSOUND_ARGUMENTS_TYPE const char **
#else
#define CSOUND_ARGUMENTS_TYPE char **
#endif
static Nan::NAN_METHOD_RETURN_TYPE performCsoundCompileFunction(Nan::NAN_METHOD_ARGS_TYPE info, int (*compileFunction)(CSOUND *, int, CSOUND_ARGUMENTS_TYPE)) {
  v8::Local<v8::Array> array = info[1].As<v8::Array>();
  uint32_t argumentCount = array->Length();
  if (argumentCount > 0) {
    char **arguments = (char **)malloc(sizeof(char *) * argumentCount);
    for (uint32_t i = 0; i < argumentCount; i++) {
      arguments[i] = strdup(*Nan::Utf8String(array->Get(i)));
    }
    info.GetReturnValue().Set(Nan::New(compileFunction(CsoundFromFunctionCallbackInfo(info), argumentCount, (CSOUND_ARGUMENTS_TYPE)arguments)));
    for (uint32_t i = 0; i < argumentCount; i++) {
      free(arguments[i]);
    }
    free(arguments);
  }
}

static NAN_METHOD(CompileArgs) {
  performCsoundCompileFunction(info, csoundCompileArgs);
}

static NAN_METHOD(Start) {
  info.GetReturnValue().Set(csoundStart(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Compile) {
  performCsoundCompileFunction(info, csoundCompile);
}

static NAN_METHOD(CompileCsd) {
  info.GetReturnValue().Set(Nan::New(csoundCompileCsd(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(Perform) {
  info.GetReturnValue().Set(csoundPerform(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(PerformKsmps) {
  info.GetReturnValue().Set((bool)csoundPerformKsmps(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(PerformBuffer) {
  info.GetReturnValue().Set(csoundPerformBuffer(CsoundFromFunctionCallbackInfo(info)));
}

struct CsoundPerformanceWorker : public Nan::AsyncWorker {
  CSOUND *Csound;
  int result;

  CsoundPerformanceWorker(Nan::Callback *callback, CSOUND *Csound) : Nan::AsyncWorker(callback), Csound(Csound) {}
  ~CsoundPerformanceWorker() {};

  void Execute() {
    result = csoundPerform(Csound);
  }

  void HandleOKCallback() {
    const int argc = 1;
    v8::Local<v8::Value> argv[argc];
    argv[0] = Nan::New(result);
    callback->Call(argc, argv);
  }
};

static NAN_METHOD(PerformAsync) {
  Nan::AsyncQueueWorker(new CsoundPerformanceWorker(new Nan::Callback(info[1].As<v8::Function>()), CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Stop) {
  csoundStop(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(Cleanup) {
  info.GetReturnValue().Set(csoundCleanup(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Reset) {
  csoundReset(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(GetSr) {
  info.GetReturnValue().Set(csoundGetSr(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetKr) {
  info.GetReturnValue().Set(csoundGetKr(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetKsmps) {
  info.GetReturnValue().Set(csoundGetKsmps(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetNchnls) {
  info.GetReturnValue().Set(csoundGetNchnls(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetNchnlsInput) {
  info.GetReturnValue().Set(csoundGetNchnlsInput(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Get0dBFS) {
  info.GetReturnValue().Set(csoundGet0dBFS(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetCurrentTimeSamples) {
  info.GetReturnValue().Set(static_cast<double>(csoundGetCurrentTimeSamples(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(GetSizeOfMYFLT) {
  info.GetReturnValue().Set(csoundGetSizeOfMYFLT());
}

static NAN_METHOD(GetHostData) {
  info.GetReturnValue().Set(Nan::New(Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>())->hostData));
}

static NAN_METHOD(SetHostData) {
  Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>())->hostData.Reset(info[1]);
}

static NAN_METHOD(SetOption) {
  info.GetReturnValue().Set(Nan::New(csoundSetOption(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(GetDebug) {
  info.GetReturnValue().Set((bool)csoundGetDebug(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(SetDebug) {
  csoundSetDebug(CsoundFromFunctionCallbackInfo(info), info[1]->BooleanValue());
}

static NAN_METHOD(GetOutputName) {
  setReturnValueWithCString(info.GetReturnValue(), csoundGetOutputName(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(SetOutput) {
  v8::Local<v8::Value> fileNameValue = info[1];
  if (!fileNameValue->IsString())
    return;
  CSOUND *Csound = CsoundFromFunctionCallbackInfo(info);
  Nan::Utf8String fileName(fileNameValue);
  v8::Local<v8::Value> typeValue = info[2];
  v8::Local<v8::Value> formatValue = info[3];

#if CS_VERSION >= 6 && CS_SUBVER >= 8
  csoundSetOutput(Csound, *fileName, typeValue->IsString() ? *Nan::Utf8String(typeValue) : NULL, formatValue->IsString() ? *Nan::Utf8String(formatValue) : NULL);
#else
  // csoundSetOutput is broken in Csound 6.07 and earlier. Use csoundSetOption
  // as a workaround (https://github.com/csound/csound/issues/700).
  std::string option("--output=");
  option.append(*fileName);
  csoundSetOption(Csound, (char *)option.c_str());
  if (typeValue->IsString()) {
    option.assign("--format=");
    option.append(*Nan::Utf8String(typeValue));
    if (formatValue->IsString()) {
      option.append(1, ':');
      option.append(*Nan::Utf8String(formatValue));
    }
    csoundSetOption(Csound, (char *)option.c_str());
  }
#endif
}

struct CsoundFileType {
  static NAN_GETTER(RawAudio) { info.GetReturnValue().Set(CSFTYPE_RAW_AUDIO); }
  static NAN_GETTER(IRCAM) { info.GetReturnValue().Set(CSFTYPE_IRCAM); }
  static NAN_GETTER(AIFF) { info.GetReturnValue().Set(CSFTYPE_AIFF); }
  static NAN_GETTER(AIFC) { info.GetReturnValue().Set(CSFTYPE_AIFC); }
  static NAN_GETTER(WAVE) { info.GetReturnValue().Set(CSFTYPE_WAVE); }
  static NAN_GETTER(AU) { info.GetReturnValue().Set(CSFTYPE_AU); }
  static NAN_GETTER(SD2) { info.GetReturnValue().Set(CSFTYPE_SD2); }
  static NAN_GETTER(W64) { info.GetReturnValue().Set(CSFTYPE_W64); }
  static NAN_GETTER(WAVEX) { info.GetReturnValue().Set(CSFTYPE_WAVEX); }
  static NAN_GETTER(FLAC) { info.GetReturnValue().Set(CSFTYPE_FLAC); }
  static NAN_GETTER(CAF) { info.GetReturnValue().Set(CSFTYPE_CAF); }
  static NAN_GETTER(WVE) { info.GetReturnValue().Set(CSFTYPE_WVE); }
  static NAN_GETTER(OGG) { info.GetReturnValue().Set(CSFTYPE_OGG); }
  static NAN_GETTER(MPC2K) { info.GetReturnValue().Set(CSFTYPE_MPC2K); }
  static NAN_GETTER(RF64) { info.GetReturnValue().Set(CSFTYPE_RF64); }
  static NAN_GETTER(AVR) { info.GetReturnValue().Set(CSFTYPE_AVR); }
  static NAN_GETTER(HTK) { info.GetReturnValue().Set(CSFTYPE_HTK); }
  static NAN_GETTER(MAT4) { info.GetReturnValue().Set(CSFTYPE_MAT4); }
  static NAN_GETTER(MAT5) { info.GetReturnValue().Set(CSFTYPE_MAT5); }
  static NAN_GETTER(NIST) { info.GetReturnValue().Set(CSFTYPE_NIST); }
  static NAN_GETTER(PAF) { info.GetReturnValue().Set(CSFTYPE_PAF); }
  static NAN_GETTER(PVF) { info.GetReturnValue().Set(CSFTYPE_PVF); }
  static NAN_GETTER(SDS) { info.GetReturnValue().Set(CSFTYPE_SDS); }
  static NAN_GETTER(SVX) { info.GetReturnValue().Set(CSFTYPE_SVX); }
  static NAN_GETTER(VOC) { info.GetReturnValue().Set(CSFTYPE_VOC); }
  static NAN_GETTER(XI) { info.GetReturnValue().Set(CSFTYPE_XI); }
  static NAN_GETTER(UnknownAudio) { info.GetReturnValue().Set(CSFTYPE_UNKNOWN_AUDIO); }
};

static void CsoundFileOpenCallback(CSOUND *Csound, const char *path, int type, int isOpenForWriting, int isTemporary) {
  ((CSOUNDWrapper *)csoundGetHostData(Csound))->queueFileOpen(path, type, isOpenForWriting, isTemporary);
}
static CSOUND_CALLBACK_METHOD(FileOpen)

static NAN_METHOD(ReadScore) {
  info.GetReturnValue().Set(Nan::New(csoundReadScore(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(GetScoreTime) {
  info.GetReturnValue().Set(Nan::New(csoundGetScoreTime(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(IsScorePending) {
  info.GetReturnValue().Set(Nan::New(csoundIsScorePending(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(SetScorePending) {
  csoundSetScorePending(CsoundFromFunctionCallbackInfo(info), info[1]->BooleanValue());
}

static NAN_METHOD(GetScoreOffsetSeconds) {
  info.GetReturnValue().Set(Nan::New(csoundGetScoreOffsetSeconds(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(SetScoreOffsetSeconds) {
  csoundSetScoreOffsetSeconds(CsoundFromFunctionCallbackInfo(info), info[1]->NumberValue());
}

static NAN_METHOD(RewindScore) {
  csoundRewindScore(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(Message) {
  csoundMessage(CsoundFromFunctionCallbackInfo(info), "%s", *Nan::Utf8String(info[1]));
}

static NAN_METHOD(MessageS) {
  csoundMessageS(CsoundFromFunctionCallbackInfo(info), info[1]->Int32Value(), "%s", *Nan::Utf8String(info[2]));
}

struct CsoundMessageType {
  static NAN_GETTER(Default) { info.GetReturnValue().Set(CSOUNDMSG_DEFAULT); }
  static NAN_GETTER(Error) { info.GetReturnValue().Set(CSOUNDMSG_ERROR); }
  static NAN_GETTER(OrchestraOpcode) { info.GetReturnValue().Set(CSOUNDMSG_ORCH); }
  static NAN_GETTER(RealTime) { info.GetReturnValue().Set(CSOUNDMSG_REALTIME); }
  static NAN_GETTER(Warning) { info.GetReturnValue().Set(CSOUNDMSG_WARNING); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_TYPE_MASK); }
};

struct CsoundMessageForegroundColor {
  static NAN_GETTER(Black) { info.GetReturnValue().Set(CSOUNDMSG_FG_BLACK); }
  static NAN_GETTER(Red) { info.GetReturnValue().Set(CSOUNDMSG_FG_RED); }
  static NAN_GETTER(Green) { info.GetReturnValue().Set(CSOUNDMSG_FG_GREEN); }
  static NAN_GETTER(Yellow) { info.GetReturnValue().Set(CSOUNDMSG_FG_YELLOW); }
  static NAN_GETTER(Blue) { info.GetReturnValue().Set(CSOUNDMSG_FG_BLUE); }
  static NAN_GETTER(Magenta) { info.GetReturnValue().Set(CSOUNDMSG_FG_MAGENTA); }
  static NAN_GETTER(Cyan) { info.GetReturnValue().Set(CSOUNDMSG_FG_CYAN); }
  static NAN_GETTER(White) { info.GetReturnValue().Set(CSOUNDMSG_FG_WHITE); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_FG_COLOR_MASK); }
};

struct CsoundMessageAttribute {
  static NAN_GETTER(Bold) { info.GetReturnValue().Set(CSOUNDMSG_FG_BOLD); }
  static NAN_GETTER(Underline) { info.GetReturnValue().Set(CSOUNDMSG_FG_UNDERLINE); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_FG_ATTR_MASK); }
};

struct CsoundMessageBackgroundColor {
  static NAN_GETTER(Black) { info.GetReturnValue().Set(CSOUNDMSG_BG_BLACK); }
  static NAN_GETTER(Red) { info.GetReturnValue().Set(CSOUNDMSG_BG_RED); }
  static NAN_GETTER(Green) { info.GetReturnValue().Set(CSOUNDMSG_BG_GREEN); }
  static NAN_GETTER(Orange) { info.GetReturnValue().Set(CSOUNDMSG_BG_ORANGE); }
  static NAN_GETTER(Blue) { info.GetReturnValue().Set(CSOUNDMSG_BG_BLUE); }
  static NAN_GETTER(Magenta) { info.GetReturnValue().Set(CSOUNDMSG_BG_MAGENTA); }
  static NAN_GETTER(Cyan) { info.GetReturnValue().Set(CSOUNDMSG_BG_CYAN); }
  static NAN_GETTER(Grey) { info.GetReturnValue().Set(CSOUNDMSG_BG_GREY); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_BG_COLOR_MASK); }
};

static CsoundCallback<CsoundMessageCallbackArguments> *CsoundDefaultMessageCallbackObject;
static void CsoundDefaultMessageCallback(CSOUND *Csound, int attributes, const char *format, va_list argumentList) {
  CsoundDefaultMessageCallbackObject->argumentsQueue.push(CsoundMessageCallbackArguments::create(attributes, format, argumentList));
  uv_async_send(&(CsoundDefaultMessageCallbackObject->handle));
}
static NAN_METHOD(SetDefaultMessageCallback) {
  v8::Local<v8::Value> value = info[0];
  if (value->IsFunction()) {
    CsoundDefaultMessageCallbackObject = new CsoundCallback<CsoundMessageCallbackArguments>(value.As<v8::Function>());
    csoundSetDefaultMessageCallback(CsoundDefaultMessageCallback);
  } else if (CsoundDefaultMessageCallbackObject) {
    delete CsoundDefaultMessageCallbackObject;
    CsoundDefaultMessageCallbackObject = NULL;
    csoundSetDefaultMessageCallback(NULL);
  }
}

static void CsoundMessageCallback(CSOUND *Csound, int attributes, const char *format, va_list argumentList) {
  ((CSOUNDWrapper *)csoundGetHostData(Csound))->queueMessage(attributes, format, argumentList);
}
static CSOUND_CALLBACK_METHOD(Message)

static NAN_METHOD(GetMessageLevel) {
  info.GetReturnValue().Set(Nan::New(csoundGetMessageLevel(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(SetMessageLevel) {
  csoundSetMessageLevel(CsoundFromFunctionCallbackInfo(info), info[1]->Int32Value());
}

static NAN_METHOD(CreateMessageBuffer) {
  csoundCreateMessageBuffer(CsoundFromFunctionCallbackInfo(info), info[1]->Int32Value());
}

static NAN_METHOD(GetFirstMessage ) {
  setReturnValueWithCString(info.GetReturnValue(), csoundGetFirstMessage(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetFirstMessageAttr) {
  info.GetReturnValue().Set(Nan::New(csoundGetFirstMessageAttr(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(PopFirstMessage) {
  csoundPopFirstMessage(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(GetMessageCnt) {
  info.GetReturnValue().Set(Nan::New(csoundGetMessageCnt(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(DestroyMessageBuffer) {
  csoundDestroyMessageBuffer(CsoundFromFunctionCallbackInfo(info));
}

struct CsoundControlChannelType {
  static NAN_GETTER(Control) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL); }
  static NAN_GETTER(Audio) { info.GetReturnValue().Set(CSOUND_AUDIO_CHANNEL); }
  static NAN_GETTER(String) { info.GetReturnValue().Set(CSOUND_STRING_CHANNEL); }
  static NAN_GETTER(PhaseVocoder) { info.GetReturnValue().Set(CSOUND_PVS_CHANNEL); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUND_CHANNEL_TYPE_MASK); }
};

struct CsoundControlChannelMode {
  static NAN_GETTER(Input) { info.GetReturnValue().Set(CSOUND_INPUT_CHANNEL); }
  static NAN_GETTER(Output) { info.GetReturnValue().Set(CSOUND_OUTPUT_CHANNEL); }
};

struct CsoundControlChannelBehavior {
  static NAN_GETTER(None) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_NO_HINTS); }
  static NAN_GETTER(Integer) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_INT); }
  static NAN_GETTER(Linear) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_LIN); }
  static NAN_GETTER(Exponential) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_EXP); }
};

// Lack of coverage for GetPrivate/GetHiddenValue and SetPrivate/SetHiddenValue
// in NAN is a known issue (https://github.com/nodejs/nan/issues/587) that
// should be fixed when https://github.com/nodejs/nan/pull/599 is merged. (NAN
// is MIT licensed (https://github.com/nodejs/nan/blob/master/LICENSE.md).)
namespace Nan {
inline v8::Local<v8::Value> GetPrivate(
    v8::Local<v8::Object> object,
    v8::Local<v8::String> key) {
#if (NODE_MODULE_VERSION >= NODE_6_0_MODULE_VERSION)
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
  v8::Local<v8::Private> private_key = v8::Private::ForApi(isolate, key);
  v8::Local<v8::Value> value;
  if (object->HasPrivate(context, private_key).FromMaybe(false)
      && object->GetPrivate(context, private_key).ToLocal(&value))
    return value;
  return v8::Local<v8::Value>();
#else
  return object->GetHiddenValue(key);
#endif
}

inline void SetPrivate(
    v8::Local<v8::Object> object,
    v8::Local<v8::String> key,
    v8::Local<v8::Value> value) {
#if (NODE_MODULE_VERSION >= NODE_6_0_MODULE_VERSION)
  if (value.IsEmpty()) return;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
  v8::Local<v8::Private> private_key = v8::Private::ForApi(isolate, key);
  object->SetPrivate(context, private_key, value);
#else
  object->SetHiddenValue(key, value);
#endif
}
} // namespace Nan

// There doesn’t appear to be a plan to cover DeletePrivate/DeleteHiddenValue
// in NAN.
namespace Nan {
inline void DeletePrivate(
    v8::Local<v8::Object> object,
    v8::Local<v8::String> key) {
#if (NODE_MODULE_VERSION >= NODE_6_0_MODULE_VERSION)
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
  v8::Local<v8::Private> private_key = v8::Private::ForApi(isolate, key);
  v8::Local<v8::Value> value;
  if (object->HasPrivate(context, private_key).FromMaybe(false))
    object->DeletePrivate(context, private_key);
#else
  object->DeleteHiddenValue(key);
#endif
}
} // namespace Nan

// These Csound API functions populate an array, passed by reference, with items
// of a particular type:
//   - csoundListChannels
//   - csoundNewOpcodeList
// These functions destroy arrays:
//   - csoundDeleteChannelList
//   - csoundDisposeOpcodeList
//   - csoundDeleteUtilityList
// performCsoundListCreationFunction, performCsoundListDestructionFunction, and
// the CsoundListItemWrapper class generalize the bindings for these Csound API
// functions.
template <typename ItemType, typename WrapperType>
static void performCsoundListCreationFunction(Nan::NAN_METHOD_ARGS_TYPE info, int (*listCreationFunction)(CSOUND *, ItemType **), Nan::Persistent<v8::Function> *ListProxyConstructorRef, Nan::Persistent<v8::Function> *ItemProxyConstructorRef) {
  ItemType *list = NULL;
  int length = listCreationFunction(CsoundFromFunctionCallbackInfo(info), &list);
  if (list && length >= 0) {
    v8::Local<v8::Object> listProxy = Nan::New(*ListProxyConstructorRef)->NewInstance();
    listProxy->SetAlignedPointerInInternalField(0, list);
    v8::Local<v8::Array> array = info[1].As<v8::Array>();
    Nan::SetPrivate(array, Nan::New("Csound::listProxy").ToLocalChecked(), listProxy);
    for (int i = 0; i < length; i++) {
      v8::Local<v8::Object> itemProxy = Nan::New(*ItemProxyConstructorRef)->NewInstance();
      Nan::ObjectWrap::Unwrap<WrapperType>(itemProxy)->setItem(list[i]);
      array->Set(i, itemProxy);
    }
  }
  info.GetReturnValue().Set(Nan::New(length));
}

template <typename ItemType>
static void performCsoundListDestructionFunction(Nan::NAN_METHOD_ARGS_TYPE info, void (*listDestructionFunction)(CSOUND *, ItemType *)) {
  v8::Local<v8::Array> array = info[1].As<v8::Array>();
  array->Set(Nan::New("length").ToLocalChecked(), Nan::New(0));
  v8::Local<v8::String> key = Nan::New("Csound::listProxy").ToLocalChecked();
  v8::Local<v8::Object> listProxy = Nan::GetPrivate(array, key).As<v8::Object>();
  ItemType *list = (ItemType *)listProxy->GetAlignedPointerFromInternalField(0);
  listProxy->SetAlignedPointerInInternalField(0, NULL);
  Nan::DeletePrivate(array, key);
  listDestructionFunction(CsoundFromFunctionCallbackInfo(info), list);
}

template <typename ItemType>
struct CsoundListItemWrapper : public Nan::ObjectWrap {
  ItemType item;

  void setItem(ItemType item) {
    this->item = item;
  }
};

static void addControlChannelHintsToObject(controlChannelHints_t hints, v8::Local<v8::Object> object) {<commit_msg>Update for NAN 2.5.0<commit_after>#include <boost/lockfree/queue.hpp>
#include <csound/cwindow.h>
#include <nan.h>

// CsoundCallback is a subclass of Nan::Callback
// <https://github.com/nodejs/nan/blob/master/doc/callback.md> to make it easier
// to store JavaScript functions as Csound callback functions. CsoundCallback
// instances maintain an async handle <http://docs.libuv.org/en/v1.x/async.html>
// and a lock-free queue of callback arguments of type T. Argument classes must
// have a static member T::argc storing the number of arguments of the callback,
// a method T::getArgv that puts Csound callback arguments into an array of V8
// values, and a method T::wereSent that can clean up after arguments are sent.
// When the static method CsoundCallback::asyncCallback runs after a call to
// uv_async_send, the CsoundCallback instance pops arguments off its queue into
// arrays of V8 values, which are then passed to a JavaScript function by
// Nan::Callback::Call(). Using a lock-free queue is from csound.node by <NAME>, which is available as part of Csound
// <https://github.com/csound/csound/tree/develop/frontends/nwjs>.
template <typename T>
struct CsoundCallback : public Nan::Callback {
  uv_async_t handle;
  boost::lockfree::queue<T> argumentsQueue;

  static void asyncCallback(uv_async_t *handle) {
    ((CsoundCallback *)handle->data)->executeCalls();
  }

  CsoundCallback(const v8::Local<v8::Function> &function) : Nan::Callback(function), argumentsQueue(0) {
    assert(uv_async_init(uv_default_loop(), &handle, asyncCallback) == 0);
    handle.data = this;
  }

  void executeCalls() {
    Nan::HandleScope scope;
    T arguments;
    while (argumentsQueue.pop(arguments)) {
      v8::Local<v8::Value> argv[T::argc];
      arguments.getArgv(argv);
      Call(T::argc, argv);
      arguments.wereSent();
    }
  }
};

// These structs store arguments from Csound callbacks.
struct CsoundFileOpenCallbackArguments {
  static const int argc = 4;
  char *path;
  int type;
  int isOpenForWriting;
  int isTemporary;

  static CsoundFileOpenCallbackArguments create(const char *path, int type, int isOpenForWriting, int isTemporary) {
    return {strdup(path), type, isOpenForWriting, isTemporary};
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    argv[0] = Nan::New(path).ToLocalChecked();
    argv[1] = Nan::New(type);
    argv[2] = Nan::New((bool)isOpenForWriting);
    argv[3] = Nan::New((bool)isTemporary);
  }

  void wereSent() {
    free(path);
  }
};

struct CsoundMessageCallbackArguments {
  static const int argc = 2;
  int attributes;
  char *message;

  static CsoundMessageCallbackArguments create(int attributes, const char *format, va_list argumentList) {
    CsoundMessageCallbackArguments arguments;
    arguments.attributes = attributes;

    // When determining the length of the string in the first call to vsnprintf,
    // pass a copy of the argument list so that the original argument list is
    // preserved when writing the string in the second call to vsnprintf.
    va_list argumentListCopy;
    va_copy(argumentListCopy, argumentList);
    int length = vsnprintf(NULL, 0, format, argumentListCopy) + 1;
    va_end(argumentListCopy);
    arguments.message = (char *)malloc(sizeof(char) * length);
    vsnprintf(arguments.message, length, format, argumentList);

    return arguments;
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    argv[0] = Nan::New(attributes);
    argv[1] = Nan::New(message).ToLocalChecked();
  }

  void wereSent() {
    free(message);
  }
};

static Nan::Persistent<v8::Function> WINDATProxyConstructor;

struct WINDATWrapper : public Nan::ObjectWrap {
  WINDAT *data;
  MYFLT *fdataCopy;

  static NAN_METHOD(New) {
    (new WINDATWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  static WINDAT *dataFromPropertyCallbackInfo(Nan::NAN_GETTER_ARGS_TYPE info) {
    return Unwrap<WINDATWrapper>(info.This())->data;
  }
  static NAN_GETTER(windid) { info.GetReturnValue().Set(Nan::New((unsigned int)dataFromPropertyCallbackInfo(info)->windid)); }
  static NAN_GETTER(caption) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->caption).ToLocalChecked()); }
  static NAN_GETTER(polarity) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->polarity)); }
  static NAN_GETTER(max) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->max)); }
  static NAN_GETTER(min) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->min)); }
  static NAN_GETTER(oabsmax) { info.GetReturnValue().Set(Nan::New(dataFromPropertyCallbackInfo(info)->oabsmax)); }

  static NAN_GETTER(fdata) {
    WINDATWrapper *wrapper = WINDATWrapper::Unwrap<WINDATWrapper>(info.This());
    v8::Local<v8::Array> fdata = Nan::New<v8::Array>(wrapper->data->npts);
    for (int32 i = 0; i < wrapper->data->npts; i++) {
      fdata->Set(i, Nan::New(wrapper->fdataCopy[i]));
    }
    info.GetReturnValue().Set(fdata);
  }
};

struct CsoundGraphCallbackArguments {
  static const int argc = 1;
  WINDAT *data;
  MYFLT *fdata;

  static CsoundGraphCallbackArguments create(WINDAT *data) {
    CsoundGraphCallbackArguments arguments;
    arguments.setData(data);
    return arguments;
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    v8::Local<v8::Object> proxy = Nan::New(WINDATProxyConstructor)->NewInstance();
    WINDATWrapper *wrapper = WINDATWrapper::Unwrap<WINDATWrapper>(proxy);
    wrapper->data = data;
    wrapper->fdataCopy = fdata;
    argv[0] = proxy;
  }

  void setData(WINDAT *data) {
    this->data = data;
    fdata = (MYFLT *)malloc(sizeof(MYFLT) * data->npts);
    for (int32 i = 0; i < data->npts; i++) {
      fdata[i] = data->fdata[i];
    }
  }

  void wereSent() {
    free(fdata);
  }
};

struct CsoundMakeGraphCallbackArguments : public CsoundGraphCallbackArguments {
  static const int argc = 2;
  const char *name;

  static CsoundMakeGraphCallbackArguments create(WINDAT *data, const char *name) {
    CsoundMakeGraphCallbackArguments arguments;
    arguments.setData(data);
    arguments.name = name;
    return arguments;
  }

  void getArgv(v8::Local<v8::Value> *argv) const {
    CsoundGraphCallbackArguments::getArgv(argv);
    argv[1] = Nan::New(name).ToLocalChecked();
  }
};

// CSOUNDWrapper instances perform tasks related to callbacks. They also store
// V8 values passed as host data from JavaScript.
static Nan::Persistent<v8::Function> CSOUNDProxyConstructor;
struct CSOUNDWrapper : public Nan::ObjectWrap {
  CSOUND *Csound;
  Nan::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value> > hostData;

  CsoundCallback<CsoundFileOpenCallbackArguments> *CsoundFileOpenCallbackObject;

  CsoundCallback<CsoundMessageCallbackArguments> *CsoundMessageCallbackObject;

  CsoundCallback<CsoundMakeGraphCallbackArguments> *CsoundMakeGraphCallbackObject;
  CsoundCallback<CsoundGraphCallbackArguments> *CsoundDrawGraphCallbackObject;
  CsoundCallback<CsoundGraphCallbackArguments> *CsoundKillGraphCallbackObject;

  // Require Csound 6.04 or later to use debugger functions.
#define CSOUND_6_04_OR_LATER CS_VERSION >= 6 && CS_SUBVER >= 4
#if CSOUND_6_04_OR_LATER
  Nan::Callback *CsoundBreakpointCallbackObject;
#endif

  static NAN_METHOD(New) {
    (new CSOUNDWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  void queueFileOpen(const char *path, int type, int isOpenForWriting, int isTemporary) {
    CsoundFileOpenCallbackObject->argumentsQueue.push(CsoundFileOpenCallbackArguments::create(path, type, isOpenForWriting, isTemporary));
    uv_async_send(&(CsoundFileOpenCallbackObject->handle));
  }

  void queueMessage(int attributes, const char *format, va_list argumentList) {
    CsoundMessageCallbackObject->argumentsQueue.push(CsoundMessageCallbackArguments::create(attributes, format, argumentList));
    uv_async_send(&(CsoundMessageCallbackObject->handle));
  }

  void queueMakeGraph(WINDAT *windowData, const char *name) {
    CsoundMakeGraphCallbackObject->argumentsQueue.push(CsoundMakeGraphCallbackArguments::create(windowData, name));
    uv_async_send(&(CsoundMakeGraphCallbackObject->handle));
  }

  void queueDrawGraph(WINDAT *windowData) {
    CsoundDrawGraphCallbackObject->argumentsQueue.push(CsoundGraphCallbackArguments::create(windowData));
    uv_async_send(&(CsoundDrawGraphCallbackObject->handle));
  }

  void queueKillGraph(WINDAT *windowData) {
    CsoundKillGraphCallbackObject->argumentsQueue.push(CsoundGraphCallbackArguments::create(windowData));
    uv_async_send(&(CsoundKillGraphCallbackObject->handle));
  }
};

// The CSOUND_CALLBACK_METHOD macro associates a Csound callback function with a
// CsoundCallback instance using an appropriate argument type. For example,
// CSOUND_CALLBACK_METHOD(Message) associates csoundSetMessageCallback with
// CsoundCallback<CsoundMessageCallbackArguments>;
// CSOUND_CALLBACK_METHOD(MakeGraph) associates csoundSetMakeGraphCallback with
// CsoundCallback<CsoundMakeGraphCallbackArguments>; and so on.
#define CSOUND_CALLBACK_METHOD_1(methodNameStem) CSOUND_CALLBACK_METHOD_2(methodNameStem, methodNameStem)
#define CSOUND_CALLBACK_METHOD_2(methodNameStem, argumentsNameStem) \
NAN_METHOD(Set ## methodNameStem ## Callback) { \
  CSOUNDWrapper *wrapper = Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>()); \
  v8::Local<v8::Value> value = info[1]; \
  if (value->IsFunction()) { \
    wrapper->Csound ## methodNameStem ## CallbackObject = new CsoundCallback<Csound ## argumentsNameStem ## CallbackArguments>(value.As<v8::Function>()); \
    csoundSet ## methodNameStem ## Callback(wrapper->Csound, Csound ## methodNameStem ## Callback); \
  } else if (wrapper->Csound ## methodNameStem ## CallbackObject) { \
    delete wrapper->Csound ## methodNameStem ## CallbackObject; \
    wrapper->Csound ## methodNameStem ## CallbackObject = NULL; \
    csoundSet ## methodNameStem ## Callback(wrapper->Csound, NULL); \
  } \
}
#ifndef _MSC_VER
#  define CSOUND_CALLBACK_METHOD(...) BOOST_PP_OVERLOAD(CSOUND_CALLBACK_METHOD_,__VA_ARGS__)(__VA_ARGS__)
#else
#  define CSOUND_CALLBACK_METHOD(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(CSOUND_CALLBACK_METHOD_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif

// Helper function to set return values to either a string or null.
static void setReturnValueWithCString(Nan::ReturnValue<v8::Value> returnValue, const char *string) {
  if (string)
    returnValue.Set(Nan::New(string).ToLocalChecked());
  else
    returnValue.SetNull();
}

static NAN_METHOD(Initialize) {
  info.GetReturnValue().Set(csoundInitialize(info[0]->Int32Value()));
}

struct CsoundInitializationOption {
  static NAN_GETTER(NoSignalHandlers) { info.GetReturnValue().Set(CSOUNDINIT_NO_SIGNAL_HANDLER); }
  static NAN_GETTER(NoExitFunction) { info.GetReturnValue().Set(CSOUNDINIT_NO_ATEXIT); }
};

static NAN_METHOD(Create) {
  v8::Local<v8::Object> proxy = Nan::New(CSOUNDProxyConstructor)->NewInstance();
  CSOUNDWrapper *wrapper = Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(proxy);
  CSOUND *Csound = csoundCreate(wrapper);
  if (Csound) {
    wrapper->Csound = Csound;
    wrapper->hostData.Reset(info[0]);
    info.GetReturnValue().Set(proxy);
  } else {
    info.GetReturnValue().SetNull();
  }
}

static CSOUND *CsoundFromFunctionCallbackInfo(Nan::NAN_METHOD_ARGS_TYPE info) {
  return Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>())->Csound;
}

static NAN_METHOD(Destroy) {
  csoundDestroy(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(GetVersion) {
  info.GetReturnValue().Set(Nan::New(csoundGetVersion()));
}

static NAN_METHOD(GetAPIVersion) {
  info.GetReturnValue().Set(Nan::New(csoundGetAPIVersion()));
}

static Nan::Persistent<v8::Function> ORCTOKENProxyConstructor;
struct ORCTOKENWrapper : public Nan::ObjectWrap {
  ORCTOKEN *token;

  static NAN_METHOD(New) {
    (new ORCTOKENWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  static ORCTOKEN *tokenFromPropertyCallbackInfo(Nan::NAN_GETTER_ARGS_TYPE info) {
    return Unwrap<ORCTOKENWrapper>(info.This())->token;
  }

  static NAN_GETTER(type) { info.GetReturnValue().Set(Nan::New(tokenFromPropertyCallbackInfo(info)->type)); }
  static NAN_GETTER(value) { info.GetReturnValue().Set(Nan::New(tokenFromPropertyCallbackInfo(info)->value)); }
  static NAN_GETTER(fvalue) { info.GetReturnValue().Set(Nan::New(tokenFromPropertyCallbackInfo(info)->fvalue)); }
  static NAN_GETTER(lexeme) { setReturnValueWithCString(info.GetReturnValue(), tokenFromPropertyCallbackInfo(info)->lexeme); }
  static NAN_GETTER(optype) { setReturnValueWithCString(info.GetReturnValue(), tokenFromPropertyCallbackInfo(info)->optype); }

  static NAN_GETTER(next) {
    ORCTOKEN *token = tokenFromPropertyCallbackInfo(info)->next;
    if (token) {
      v8::Local<v8::Object> proxy = Nan::New(ORCTOKENProxyConstructor)->NewInstance();
      Unwrap<ORCTOKENWrapper>(proxy)->token = token;
      info.GetReturnValue().Set(proxy);
    } else {
      info.GetReturnValue().SetNull();
    }
  }
};

static Nan::Persistent<v8::Function> TREEProxyConstructor;
struct TREEWrapper : public Nan::ObjectWrap {
  TREE *tree;

  static NAN_METHOD(New) {
    (new TREEWrapper())->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  }

  static TREE *treeFromPropertyCallbackInfo(Nan::NAN_GETTER_ARGS_TYPE info) {
    return Unwrap<TREEWrapper>(info.This())->tree;
  }

  static NAN_GETTER(value) {
    ORCTOKEN *token = treeFromPropertyCallbackInfo(info)->value;
    if (token) {
      v8::Local<v8::Object> proxy = Nan::New(ORCTOKENProxyConstructor)->NewInstance();
      Unwrap<ORCTOKENWrapper>(proxy)->token = token;
      info.GetReturnValue().Set(proxy);
    } else {
      info.GetReturnValue().SetNull();
    }
  }
  static NAN_GETTER(type) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->type)); }
  static NAN_GETTER(rate) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->rate)); }
  static NAN_GETTER(len) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->len)); }
  static NAN_GETTER(line) { info.GetReturnValue().Set(Nan::New(treeFromPropertyCallbackInfo(info)->line)); }
  static NAN_GETTER(locn) { info.GetReturnValue().Set(Nan::New(static_cast<double>(treeFromPropertyCallbackInfo(info)->locn))); }

  static void setPropertyCallbackInfoReturnValueWithTree(Nan::NAN_GETTER_ARGS_TYPE info, TREE *tree) {
    if (tree) {
      v8::Local<v8::Object> proxy = Nan::New(TREEProxyConstructor)->NewInstance();
      Unwrap<TREEWrapper>(proxy)->tree = tree;
      info.GetReturnValue().Set(proxy);
    } else {
      info.GetReturnValue().SetNull();
    }
  }
  static NAN_GETTER(left) { setPropertyCallbackInfoReturnValueWithTree(info, treeFromPropertyCallbackInfo(info)->left); }
  static NAN_GETTER(right) { setPropertyCallbackInfoReturnValueWithTree(info, treeFromPropertyCallbackInfo(info)->right); }
  static NAN_GETTER(next) { setPropertyCallbackInfoReturnValueWithTree(info, treeFromPropertyCallbackInfo(info)->next); }
};

static NAN_METHOD(ParseOrc) {
  TREE *tree = csoundParseOrc(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]));
  if (tree) {
    v8::Local<v8::Object> proxy = Nan::New(TREEProxyConstructor)->NewInstance();
    Nan::ObjectWrap::Unwrap<TREEWrapper>(proxy)->tree = tree;
    info.GetReturnValue().Set(proxy);
  } else {
    info.GetReturnValue().SetNull();
  }
}

static NAN_METHOD(CompileTree) {
  info.GetReturnValue().Set(Nan::New(csoundCompileTree(CsoundFromFunctionCallbackInfo(info), Nan::ObjectWrap::Unwrap<TREEWrapper>(info[1].As<v8::Object>())->tree)));
}

static NAN_METHOD(DeleteTree) {
  csoundDeleteTree(CsoundFromFunctionCallbackInfo(info), Nan::ObjectWrap::Unwrap<TREEWrapper>(info[1].As<v8::Object>())->tree);
}

static NAN_METHOD(CompileOrc) {
  info.GetReturnValue().Set(Nan::New(csoundCompileOrc(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(EvalCode) {
  info.GetReturnValue().Set(Nan::New(csoundEvalCode(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

// Helper function to pass V8 values to csoundCompileArgs and csoundCompile.
#if CS_VERSION >= 6 && CS_SUBVER >= 8
#define CSOUND_ARGUMENTS_TYPE const char **
#else
#define CSOUND_ARGUMENTS_TYPE char **
#endif
static Nan::NAN_METHOD_RETURN_TYPE performCsoundCompileFunction(Nan::NAN_METHOD_ARGS_TYPE info, int (*compileFunction)(CSOUND *, int, CSOUND_ARGUMENTS_TYPE)) {
  v8::Local<v8::Array> array = info[1].As<v8::Array>();
  uint32_t argumentCount = array->Length();
  if (argumentCount > 0) {
    char **arguments = (char **)malloc(sizeof(char *) * argumentCount);
    for (uint32_t i = 0; i < argumentCount; i++) {
      arguments[i] = strdup(*Nan::Utf8String(array->Get(i)));
    }
    info.GetReturnValue().Set(Nan::New(compileFunction(CsoundFromFunctionCallbackInfo(info), argumentCount, (CSOUND_ARGUMENTS_TYPE)arguments)));
    for (uint32_t i = 0; i < argumentCount; i++) {
      free(arguments[i]);
    }
    free(arguments);
  }
}

static NAN_METHOD(CompileArgs) {
  performCsoundCompileFunction(info, csoundCompileArgs);
}

static NAN_METHOD(Start) {
  info.GetReturnValue().Set(csoundStart(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Compile) {
  performCsoundCompileFunction(info, csoundCompile);
}

static NAN_METHOD(CompileCsd) {
  info.GetReturnValue().Set(Nan::New(csoundCompileCsd(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(Perform) {
  info.GetReturnValue().Set(csoundPerform(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(PerformKsmps) {
  info.GetReturnValue().Set((bool)csoundPerformKsmps(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(PerformBuffer) {
  info.GetReturnValue().Set(csoundPerformBuffer(CsoundFromFunctionCallbackInfo(info)));
}

struct CsoundPerformanceWorker : public Nan::AsyncWorker {
  CSOUND *Csound;
  int result;

  CsoundPerformanceWorker(Nan::Callback *callback, CSOUND *Csound) : Nan::AsyncWorker(callback), Csound(Csound) {}
  ~CsoundPerformanceWorker() {};

  void Execute() {
    result = csoundPerform(Csound);
  }

  void HandleOKCallback() {
    const int argc = 1;
    v8::Local<v8::Value> argv[argc];
    argv[0] = Nan::New(result);
    callback->Call(argc, argv);
  }
};

static NAN_METHOD(PerformAsync) {
  Nan::AsyncQueueWorker(new CsoundPerformanceWorker(new Nan::Callback(info[1].As<v8::Function>()), CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Stop) {
  csoundStop(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(Cleanup) {
  info.GetReturnValue().Set(csoundCleanup(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Reset) {
  csoundReset(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(GetSr) {
  info.GetReturnValue().Set(csoundGetSr(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetKr) {
  info.GetReturnValue().Set(csoundGetKr(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetKsmps) {
  info.GetReturnValue().Set(csoundGetKsmps(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetNchnls) {
  info.GetReturnValue().Set(csoundGetNchnls(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetNchnlsInput) {
  info.GetReturnValue().Set(csoundGetNchnlsInput(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(Get0dBFS) {
  info.GetReturnValue().Set(csoundGet0dBFS(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetCurrentTimeSamples) {
  info.GetReturnValue().Set(static_cast<double>(csoundGetCurrentTimeSamples(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(GetSizeOfMYFLT) {
  info.GetReturnValue().Set(csoundGetSizeOfMYFLT());
}

static NAN_METHOD(GetHostData) {
  info.GetReturnValue().Set(Nan::New(Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>())->hostData));
}

static NAN_METHOD(SetHostData) {
  Nan::ObjectWrap::Unwrap<CSOUNDWrapper>(info[0].As<v8::Object>())->hostData.Reset(info[1]);
}

static NAN_METHOD(SetOption) {
  info.GetReturnValue().Set(Nan::New(csoundSetOption(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(GetDebug) {
  info.GetReturnValue().Set((bool)csoundGetDebug(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(SetDebug) {
  csoundSetDebug(CsoundFromFunctionCallbackInfo(info), info[1]->BooleanValue());
}

static NAN_METHOD(GetOutputName) {
  setReturnValueWithCString(info.GetReturnValue(), csoundGetOutputName(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(SetOutput) {
  v8::Local<v8::Value> fileNameValue = info[1];
  if (!fileNameValue->IsString())
    return;
  CSOUND *Csound = CsoundFromFunctionCallbackInfo(info);
  Nan::Utf8String fileName(fileNameValue);
  v8::Local<v8::Value> typeValue = info[2];
  v8::Local<v8::Value> formatValue = info[3];

#if CS_VERSION >= 6 && CS_SUBVER >= 8
  csoundSetOutput(Csound, *fileName, typeValue->IsString() ? *Nan::Utf8String(typeValue) : NULL, formatValue->IsString() ? *Nan::Utf8String(formatValue) : NULL);
#else
  // csoundSetOutput is broken in Csound 6.07 and earlier. Use csoundSetOption
  // as a workaround (https://github.com/csound/csound/issues/700).
  std::string option("--output=");
  option.append(*fileName);
  csoundSetOption(Csound, (char *)option.c_str());
  if (typeValue->IsString()) {
    option.assign("--format=");
    option.append(*Nan::Utf8String(typeValue));
    if (formatValue->IsString()) {
      option.append(1, ':');
      option.append(*Nan::Utf8String(formatValue));
    }
    csoundSetOption(Csound, (char *)option.c_str());
  }
#endif
}

struct CsoundFileType {
  static NAN_GETTER(RawAudio) { info.GetReturnValue().Set(CSFTYPE_RAW_AUDIO); }
  static NAN_GETTER(IRCAM) { info.GetReturnValue().Set(CSFTYPE_IRCAM); }
  static NAN_GETTER(AIFF) { info.GetReturnValue().Set(CSFTYPE_AIFF); }
  static NAN_GETTER(AIFC) { info.GetReturnValue().Set(CSFTYPE_AIFC); }
  static NAN_GETTER(WAVE) { info.GetReturnValue().Set(CSFTYPE_WAVE); }
  static NAN_GETTER(AU) { info.GetReturnValue().Set(CSFTYPE_AU); }
  static NAN_GETTER(SD2) { info.GetReturnValue().Set(CSFTYPE_SD2); }
  static NAN_GETTER(W64) { info.GetReturnValue().Set(CSFTYPE_W64); }
  static NAN_GETTER(WAVEX) { info.GetReturnValue().Set(CSFTYPE_WAVEX); }
  static NAN_GETTER(FLAC) { info.GetReturnValue().Set(CSFTYPE_FLAC); }
  static NAN_GETTER(CAF) { info.GetReturnValue().Set(CSFTYPE_CAF); }
  static NAN_GETTER(WVE) { info.GetReturnValue().Set(CSFTYPE_WVE); }
  static NAN_GETTER(OGG) { info.GetReturnValue().Set(CSFTYPE_OGG); }
  static NAN_GETTER(MPC2K) { info.GetReturnValue().Set(CSFTYPE_MPC2K); }
  static NAN_GETTER(RF64) { info.GetReturnValue().Set(CSFTYPE_RF64); }
  static NAN_GETTER(AVR) { info.GetReturnValue().Set(CSFTYPE_AVR); }
  static NAN_GETTER(HTK) { info.GetReturnValue().Set(CSFTYPE_HTK); }
  static NAN_GETTER(MAT4) { info.GetReturnValue().Set(CSFTYPE_MAT4); }
  static NAN_GETTER(MAT5) { info.GetReturnValue().Set(CSFTYPE_MAT5); }
  static NAN_GETTER(NIST) { info.GetReturnValue().Set(CSFTYPE_NIST); }
  static NAN_GETTER(PAF) { info.GetReturnValue().Set(CSFTYPE_PAF); }
  static NAN_GETTER(PVF) { info.GetReturnValue().Set(CSFTYPE_PVF); }
  static NAN_GETTER(SDS) { info.GetReturnValue().Set(CSFTYPE_SDS); }
  static NAN_GETTER(SVX) { info.GetReturnValue().Set(CSFTYPE_SVX); }
  static NAN_GETTER(VOC) { info.GetReturnValue().Set(CSFTYPE_VOC); }
  static NAN_GETTER(XI) { info.GetReturnValue().Set(CSFTYPE_XI); }
  static NAN_GETTER(UnknownAudio) { info.GetReturnValue().Set(CSFTYPE_UNKNOWN_AUDIO); }
};

static void CsoundFileOpenCallback(CSOUND *Csound, const char *path, int type, int isOpenForWriting, int isTemporary) {
  ((CSOUNDWrapper *)csoundGetHostData(Csound))->queueFileOpen(path, type, isOpenForWriting, isTemporary);
}
static CSOUND_CALLBACK_METHOD(FileOpen)

static NAN_METHOD(ReadScore) {
  info.GetReturnValue().Set(Nan::New(csoundReadScore(CsoundFromFunctionCallbackInfo(info), *Nan::Utf8String(info[1]))));
}

static NAN_METHOD(GetScoreTime) {
  info.GetReturnValue().Set(Nan::New(csoundGetScoreTime(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(IsScorePending) {
  info.GetReturnValue().Set(Nan::New(csoundIsScorePending(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(SetScorePending) {
  csoundSetScorePending(CsoundFromFunctionCallbackInfo(info), info[1]->BooleanValue());
}

static NAN_METHOD(GetScoreOffsetSeconds) {
  info.GetReturnValue().Set(Nan::New(csoundGetScoreOffsetSeconds(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(SetScoreOffsetSeconds) {
  csoundSetScoreOffsetSeconds(CsoundFromFunctionCallbackInfo(info), info[1]->NumberValue());
}

static NAN_METHOD(RewindScore) {
  csoundRewindScore(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(Message) {
  csoundMessage(CsoundFromFunctionCallbackInfo(info), "%s", *Nan::Utf8String(info[1]));
}

static NAN_METHOD(MessageS) {
  csoundMessageS(CsoundFromFunctionCallbackInfo(info), info[1]->Int32Value(), "%s", *Nan::Utf8String(info[2]));
}

struct CsoundMessageType {
  static NAN_GETTER(Default) { info.GetReturnValue().Set(CSOUNDMSG_DEFAULT); }
  static NAN_GETTER(Error) { info.GetReturnValue().Set(CSOUNDMSG_ERROR); }
  static NAN_GETTER(OrchestraOpcode) { info.GetReturnValue().Set(CSOUNDMSG_ORCH); }
  static NAN_GETTER(RealTime) { info.GetReturnValue().Set(CSOUNDMSG_REALTIME); }
  static NAN_GETTER(Warning) { info.GetReturnValue().Set(CSOUNDMSG_WARNING); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_TYPE_MASK); }
};

struct CsoundMessageForegroundColor {
  static NAN_GETTER(Black) { info.GetReturnValue().Set(CSOUNDMSG_FG_BLACK); }
  static NAN_GETTER(Red) { info.GetReturnValue().Set(CSOUNDMSG_FG_RED); }
  static NAN_GETTER(Green) { info.GetReturnValue().Set(CSOUNDMSG_FG_GREEN); }
  static NAN_GETTER(Yellow) { info.GetReturnValue().Set(CSOUNDMSG_FG_YELLOW); }
  static NAN_GETTER(Blue) { info.GetReturnValue().Set(CSOUNDMSG_FG_BLUE); }
  static NAN_GETTER(Magenta) { info.GetReturnValue().Set(CSOUNDMSG_FG_MAGENTA); }
  static NAN_GETTER(Cyan) { info.GetReturnValue().Set(CSOUNDMSG_FG_CYAN); }
  static NAN_GETTER(White) { info.GetReturnValue().Set(CSOUNDMSG_FG_WHITE); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_FG_COLOR_MASK); }
};

struct CsoundMessageAttribute {
  static NAN_GETTER(Bold) { info.GetReturnValue().Set(CSOUNDMSG_FG_BOLD); }
  static NAN_GETTER(Underline) { info.GetReturnValue().Set(CSOUNDMSG_FG_UNDERLINE); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_FG_ATTR_MASK); }
};

struct CsoundMessageBackgroundColor {
  static NAN_GETTER(Black) { info.GetReturnValue().Set(CSOUNDMSG_BG_BLACK); }
  static NAN_GETTER(Red) { info.GetReturnValue().Set(CSOUNDMSG_BG_RED); }
  static NAN_GETTER(Green) { info.GetReturnValue().Set(CSOUNDMSG_BG_GREEN); }
  static NAN_GETTER(Orange) { info.GetReturnValue().Set(CSOUNDMSG_BG_ORANGE); }
  static NAN_GETTER(Blue) { info.GetReturnValue().Set(CSOUNDMSG_BG_BLUE); }
  static NAN_GETTER(Magenta) { info.GetReturnValue().Set(CSOUNDMSG_BG_MAGENTA); }
  static NAN_GETTER(Cyan) { info.GetReturnValue().Set(CSOUNDMSG_BG_CYAN); }
  static NAN_GETTER(Grey) { info.GetReturnValue().Set(CSOUNDMSG_BG_GREY); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUNDMSG_BG_COLOR_MASK); }
};

static CsoundCallback<CsoundMessageCallbackArguments> *CsoundDefaultMessageCallbackObject;
static void CsoundDefaultMessageCallback(CSOUND *Csound, int attributes, const char *format, va_list argumentList) {
  CsoundDefaultMessageCallbackObject->argumentsQueue.push(CsoundMessageCallbackArguments::create(attributes, format, argumentList));
  uv_async_send(&(CsoundDefaultMessageCallbackObject->handle));
}
static NAN_METHOD(SetDefaultMessageCallback) {
  v8::Local<v8::Value> value = info[0];
  if (value->IsFunction()) {
    CsoundDefaultMessageCallbackObject = new CsoundCallback<CsoundMessageCallbackArguments>(value.As<v8::Function>());
    csoundSetDefaultMessageCallback(CsoundDefaultMessageCallback);
  } else if (CsoundDefaultMessageCallbackObject) {
    delete CsoundDefaultMessageCallbackObject;
    CsoundDefaultMessageCallbackObject = NULL;
    csoundSetDefaultMessageCallback(NULL);
  }
}

static void CsoundMessageCallback(CSOUND *Csound, int attributes, const char *format, va_list argumentList) {
  ((CSOUNDWrapper *)csoundGetHostData(Csound))->queueMessage(attributes, format, argumentList);
}
static CSOUND_CALLBACK_METHOD(Message)

static NAN_METHOD(GetMessageLevel) {
  info.GetReturnValue().Set(Nan::New(csoundGetMessageLevel(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(SetMessageLevel) {
  csoundSetMessageLevel(CsoundFromFunctionCallbackInfo(info), info[1]->Int32Value());
}

static NAN_METHOD(CreateMessageBuffer) {
  csoundCreateMessageBuffer(CsoundFromFunctionCallbackInfo(info), info[1]->Int32Value());
}

static NAN_METHOD(GetFirstMessage ) {
  setReturnValueWithCString(info.GetReturnValue(), csoundGetFirstMessage(CsoundFromFunctionCallbackInfo(info)));
}

static NAN_METHOD(GetFirstMessageAttr) {
  info.GetReturnValue().Set(Nan::New(csoundGetFirstMessageAttr(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(PopFirstMessage) {
  csoundPopFirstMessage(CsoundFromFunctionCallbackInfo(info));
}

static NAN_METHOD(GetMessageCnt) {
  info.GetReturnValue().Set(Nan::New(csoundGetMessageCnt(CsoundFromFunctionCallbackInfo(info))));
}

static NAN_METHOD(DestroyMessageBuffer) {
  csoundDestroyMessageBuffer(CsoundFromFunctionCallbackInfo(info));
}

struct CsoundControlChannelType {
  static NAN_GETTER(Control) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL); }
  static NAN_GETTER(Audio) { info.GetReturnValue().Set(CSOUND_AUDIO_CHANNEL); }
  static NAN_GETTER(String) { info.GetReturnValue().Set(CSOUND_STRING_CHANNEL); }
  static NAN_GETTER(PhaseVocoder) { info.GetReturnValue().Set(CSOUND_PVS_CHANNEL); }
  static NAN_GETTER(Mask) { info.GetReturnValue().Set(CSOUND_CHANNEL_TYPE_MASK); }
};

struct CsoundControlChannelMode {
  static NAN_GETTER(Input) { info.GetReturnValue().Set(CSOUND_INPUT_CHANNEL); }
  static NAN_GETTER(Output) { info.GetReturnValue().Set(CSOUND_OUTPUT_CHANNEL); }
};

struct CsoundControlChannelBehavior {
  static NAN_GETTER(None) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_NO_HINTS); }
  static NAN_GETTER(Integer) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_INT); }
  static NAN_GETTER(Linear) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_LIN); }
  static NAN_GETTER(Exponential) { info.GetReturnValue().Set(CSOUND_CONTROL_CHANNEL_EXP); }
};

// These Csound API functions populate an array, passed by reference, with items
// of a particular type:
//   - csoundListChannels
//   - csoundNewOpcodeList
// These functions destroy arrays:
//   - csoundDeleteChannelList
//   - csoundDisposeOpcodeList
//   - csoundDeleteUtilityList
// performCsoundListCreationFunction, performCsoundListDestructionFunction, and
// the CsoundListItemWrapper class generalize the bindings for these Csound API
// functions.
template <typename ItemType, typename WrapperType>
static void performCsoundListCreationFunction(Nan::NAN_METHOD_ARGS_TYPE info, int (*listCreationFunction)(CSOUND *, ItemType **), Nan::Persistent<v8::Function> *ListProxyConstructorRef, Nan::Persistent<v8::Function> *ItemProxyConstructorRef) {
  ItemType *list = NULL;
  int length = listCreationFunction(CsoundFromFunctionCallbackInfo(info), &list);
  if (list && length >= 0) {
    v8::Local<v8::Object> listProxy = Nan::New(*ListProxyConstructorRef)->NewInstance();
    listProxy->SetAlignedPointerInInternalField(0, list);
    v8::Local<v8::Array> array = info[1].As<v8::Array>();
    Nan::SetPrivate(array, Nan::New("Csound::listProxy").ToLocalChecked(), listProxy);
    for (int i = 0; i < length; i++) {
      v8::Local<v8::Object> itemProxy = Nan::New(*ItemProxyConstructorRef)->NewInstance();
      Nan::ObjectWrap::Unwrap<WrapperType>(itemProxy)->setItem(list[i]);
      array->Set(i, itemProxy);
    }
  }
  info.GetReturnValue().Set(Nan::New(length));
}

template <typename ItemType>
static void performCsoundListDestructionFunction(Nan::NAN_METHOD_ARGS_TYPE info, void (*listDestructionFunction)(CSOUND *, ItemType *)) {
  v8::Local<v8::Array> array = info[1].As<v8::Array>();
  array->Set(Nan::New("length").ToLocalChecked(), Nan::New(0));
  v8::Local<v8::String> key = Nan::New("Csound::listProxy").ToLocalChecked();
  v8::Local<v8::Object> listProxy = Nan::GetPrivate(array, key).ToLocalChecked().As<v8::Object>();
  ItemType *list = (ItemType *)listProxy->GetAlignedPointerFromInternalField(0);
  listProxy->SetAlignedPointerInInternalField(0, NULL);
  Nan::DeletePrivate(array, key);
  listDestructionFunction(CsoundFromFunctionCallbackInfo(info), list);
}

template <typename ItemType>
struct CsoundListItemWrapper : public Nan::ObjectWrap {
  ItemType item;

  void setItem(ItemType item) {
    this->item = item;
  }
};

static void addControlChannelHintsToObject(controlChannelHints_t hints, v8::Local<v8::Object> object) {
{'fraction_non_alphanumeric': 0.13681664956763886, 'fraction_numerical': 0.007269529532463725, 'mean_word_length': 6.662099943851769, 'pattern_counts': {'":': 0, '<': 211, '<?xml version=': 0, '>': 441, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4287736', 'n_tokens_mistral': 23250, 'n_tokens_neox': 22538, 'n_words': 3434}