[Pkg-javascript-commits] [node-groove] 01/10: Imported Upstream version 2.5.0
Felipe Sateler
fsateler at moszumanska.debian.org
Tue Dec 15 00:15:21 UTC 2015
This is an automated email from the git hooks/post-receive script.
fsateler pushed a commit to branch master
in repository node-groove.
commit e33dcfa4603fc2949886370114063d601d11aeca
Author: Felipe Sateler <fsateler at debian.org>
Date: Mon Dec 14 20:44:49 2015 -0300
Imported Upstream version 2.5.0
---
README.md | 34 +-
binding.gyp | 17 +-
example/playlist.js | 5 +
lib/index.js | 3 +
package.json | 12 +-
src/encoder.cc | 447 +++++++++++++++++++++
src/{gn_encoder.h => encoder.h} | 18 +-
src/file.cc | 332 +++++++++++++++
src/file.h | 35 ++
src/fingerprinter.cc | 381 ++++++++++++++++++
src/fingerprinter.h | 44 ++
src/gn_encoder.cc | 424 -------------------
src/gn_file.cc | 332 ---------------
src/gn_file.h | 39 --
src/gn_fingerprinter.cc | 373 -----------------
src/gn_fingerprinter.h | 43 --
src/gn_player.cc | 357 ----------------
src/gn_player.h | 47 ---
src/gn_playlist.cc | 240 -----------
src/gn_playlist.h | 47 ---
src/gn_playlist_item.cc | 71 ----
src/gn_playlist_item.h | 32 --
src/groove.cc | 120 +++---
...n_loudness_detector.cc => loudness_detector.cc} | 222 +++++-----
...{gn_loudness_detector.h => loudness_detector.h} | 18 +-
src/player.cc | 364 +++++++++++++++++
src/player.h | 43 ++
src/playlist.cc | 232 +++++++++++
src/playlist.h | 44 ++
src/playlist_item.cc | 71 ++++
src/playlist_item.h | 28 ++
test/test.js | 3 +-
32 files changed, 2273 insertions(+), 2205 deletions(-)
diff --git a/README.md b/README.md
index d44bb70..307d6ae 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,21 @@
Node.js bindings to [libgroove](https://github.com/andrewrk/libgroove) -
generic music player backend library.
-Live discussion in #libgroove IRC channel on irc.freenode.org.
+Live discussion in `#libgroove` on [freenode](https://freenode.net/).
## Usage
-1. Install libgroove to your system.
+1. Install libgroove to your system. libgroove is a set of 4 libraries;
+ node-groove depends on all of them. So for example on ubuntu, make sure to
+ install libgroove-dev, libgrooveplayer-dev, libgrooveloudness-dev, and
+ libgroovefingerprinter-dev.
2. `npm install --save groove`
+### Versions
+
+ * node-groove >=2.4.0 depends on libgroove >=4.3.0
+ * use node-groove 2.3.4 if you want to use libgroove <4.3.0
+
### Get Metadata from File
```js
@@ -220,12 +228,16 @@ These are not instantiated directly; instead they are returned from
#### item.file
+Read-only.
+
#### item.gain
A volume adjustment in float format to apply to the file when it plays.
This is typically used for loudness compensation, for example ReplayGain.
To convert from dB to float, use `groove.dBToFloat`
+Read-only. Use `playlist.setItemGain` to modify.
+
#### item.peak
The sample peak of this playlist item is assumed to be 1.0 in float
@@ -234,6 +246,8 @@ may set this value which may allow the volume adjustment to use
a pure amplifier rather than a compressor. This results in slightly
better audio quality.
+Read-only. Use `playlist.setItemPeak` to modify.
+
#### item.id
Every time you obtain a playlist item from groove, you will get a fresh
@@ -241,6 +255,8 @@ JavaScript object, but it might point to the same underlying libgroove pointer
as another. The `id` field is a way to check if two playlist items reference
the same one.
+Read-only.
+
### GroovePlayer
#### groove.getDevices()
@@ -296,6 +312,13 @@ must be a power of 2.
How big the sink buffer should be, in sample frames.
`groove.createPlayer()` defaults this to 8192
+#### player.useExactAudioFormat
+
+If you set this to `true`, `targetAudioFormat` and `actualAudioFormat` are
+ignored and no resampling, channel layout remapping, or sample format
+conversion will occur. The audio device will be reopened with exact parameters
+whenever necessary.
+
#### player.attach(playlist, callback)
Sends audio to sound device.
@@ -323,6 +346,13 @@ Fires when a buffer underrun occurs. Ideally you'll never see this.
`handler()`
+#### player.on('devicereopened', handler)
+
+Fires when you have set `useExactAudioFormat` to `true` and the audio device
+has been closed and re-opened to match incoming audio data.
+
+`handler()`
+
### GrooveEncoder
#### groove.createEncoder()
diff --git a/binding.gyp b/binding.gyp
index b1c647c..edd135a 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -4,19 +4,22 @@
"target_name": "groove",
"sources": [
"src/groove.cc",
- "src/gn_file.cc",
- "src/gn_playlist.cc",
- "src/gn_player.cc",
- "src/gn_playlist_item.cc",
- "src/gn_loudness_detector.cc",
- "src/gn_fingerprinter.cc",
- "src/gn_encoder.cc"
+ "src/file.cc",
+ "src/playlist.cc",
+ "src/player.cc",
+ "src/playlist_item.cc",
+ "src/loudness_detector.cc",
+ "src/fingerprinter.cc",
+ "src/encoder.cc"
],
"libraries": [
"-lgroove",
"-lgrooveplayer",
"-lgrooveloudness",
"-lgroovefingerprinter"
+ ],
+ "include_dirs": [
+ "<!(node -e \"require('nan')\")"
]
}
]
diff --git a/example/playlist.js b/example/playlist.js
index db269af..b122cf7 100644
--- a/example/playlist.js
+++ b/example/playlist.js
@@ -8,6 +8,11 @@ if (process.argv.length < 3) usage();
var playlist = groove.createPlaylist();
var player = groove.createPlayer();
+player.useExactAudioFormat = true;
+
+player.on('devicereopened', function() {
+ console.log("Device re-opened");
+});
player.on('nowplaying', function() {
var current = player.position();
diff --git a/lib/index.js b/lib/index.js
index d28641b..4592877 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -56,6 +56,9 @@ function jsCreatePlayer() {
case bindings._EVENT_BUFFERUNDERRUN:
player.emit('bufferunderrun');
break;
+ case bindings._EVENT_DEVICEREOPENED:
+ player.emit('devicereopened');
+ break;
}
}
}
diff --git a/package.json b/package.json
index cd17f11..3f2e4b3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "groove",
- "version": "2.2.6",
+ "version": "2.5.0",
"description": "bindings to libgroove - generic music player library",
"main": "lib/index.js",
"author": "Andrew Kelley <superjoe30 at gmail.com>",
@@ -13,15 +13,13 @@
"install": "node-gyp rebuild"
},
"license": "MIT",
- "engines": {
- "node": ">=0.10.18"
- },
"devDependencies": {
- "ncp": "~0.6.0",
- "mocha": "~1.21.0"
+ "mocha": "~2.2.5",
+ "ncp": "~2.0.0"
},
"dependencies": {
- "bindings": "~1.2.1"
+ "bindings": "~1.2.1",
+ "nan": "~2.1.0"
},
"gypfile": true,
"bugs": {
diff --git a/src/encoder.cc b/src/encoder.cc
new file mode 100644
index 0000000..66d2c66
--- /dev/null
+++ b/src/encoder.cc
@@ -0,0 +1,447 @@
+#include <node_buffer.h>
+#include "encoder.h"
+#include "playlist.h"
+#include "playlist_item.h"
+
+using namespace v8;
+
+GNEncoder::GNEncoder() {};
+GNEncoder::~GNEncoder() {
+ groove_encoder_destroy(encoder);
+ delete event_context->event_cb;
+ delete event_context;
+};
+
+static Nan::Persistent<v8::Function> constructor;
+
+void GNEncoder::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GrooveEncoder").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(2);
+
+ // Methods
+ Nan::SetPrototypeMethod(tpl, "attach", Attach);
+ Nan::SetPrototypeMethod(tpl, "detach", Detach);
+ Nan::SetPrototypeMethod(tpl, "getBuffer", GetBuffer);
+ Nan::SetPrototypeMethod(tpl, "position", Position);
+
+ constructor.Reset(tpl->GetFunction());
+}
+
+NAN_METHOD(GNEncoder::New) {
+ Nan::HandleScope scope;
+
+ GNEncoder *obj = new GNEncoder();
+ obj->Wrap(info.This());
+
+ info.GetReturnValue().Set(info.This());
+}
+
+Handle<Value> GNEncoder::NewInstance(GrooveEncoder *encoder) {
+ Nan::EscapableHandleScope scope;
+
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
+
+ GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(instance);
+ gn_encoder->encoder = encoder;
+
+ return scope.Escape(instance);
+}
+
+struct AttachReq {
+ uv_work_t req;
+ Nan::Callback *callback;
+ GrooveEncoder *encoder;
+ GroovePlaylist *playlist;
+ int errcode;
+ Nan::Persistent<Object> instance;
+ String::Utf8Value *format_short_name;
+ String::Utf8Value *codec_short_name;
+ String::Utf8Value *filename;
+ String::Utf8Value *mime_type;
+ GNEncoder::EventContext *event_context;
+};
+
+static void EventAsyncCb(uv_async_t *handle
+#if UV_VERSION_MAJOR == 0
+ , int status
+#endif
+ )
+{
+ Nan::HandleScope scope;
+
+ GNEncoder::EventContext *context = reinterpret_cast<GNEncoder::EventContext *>(handle->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ argv[0] = Nan::Undefined();
+
+ TryCatch try_catch;
+ context->event_cb->Call(argc, argv);
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+
+ uv_mutex_lock(&context->mutex);
+ uv_cond_signal(&context->cond);
+ uv_mutex_unlock(&context->mutex);
+}
+
+static void EventThreadEntry(void *arg) {
+ GNEncoder::EventContext *context = reinterpret_cast<GNEncoder::EventContext *>(arg);
+ while (groove_encoder_buffer_peek(context->encoder, 1) > 0) {
+ uv_mutex_lock(&context->mutex);
+ if (context->emit_buffer_ok) {
+ context->emit_buffer_ok = false;
+ uv_async_send(&context->event_async);
+ }
+ uv_cond_wait(&context->cond, &context->mutex);
+ uv_mutex_unlock(&context->mutex);
+ }
+}
+
+static void AttachAsync(uv_work_t *req) {
+ AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
+
+ r->encoder->format_short_name = r->format_short_name ? **r->format_short_name : NULL;
+ r->encoder->codec_short_name = r->codec_short_name ? **r->codec_short_name : NULL;
+ r->encoder->filename = r->filename ? **r->filename : NULL;
+ r->encoder->mime_type = r->mime_type ? **r->mime_type : NULL;
+
+ r->errcode = groove_encoder_attach(r->encoder, r->playlist);
+ if (r->format_short_name) {
+ delete r->format_short_name;
+ r->format_short_name = NULL;
+ }
+ if (r->codec_short_name) {
+ delete r->codec_short_name;
+ r->codec_short_name = NULL;
+ }
+ if (r->filename) {
+ delete r->filename;
+ r->filename = NULL;
+ }
+ if (r->mime_type) {
+ delete r->mime_type;
+ r->mime_type = NULL;
+ }
+
+ GNEncoder::EventContext *context = r->event_context;
+ uv_cond_init(&context->cond);
+ uv_mutex_init(&context->mutex);
+
+ uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
+ context->event_async.data = context;
+
+ uv_thread_create(&context->event_thread, EventThreadEntry, context);
+}
+
+static void AttachAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+ AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->errcode < 0) {
+ argv[0] = Exception::Error(Nan::New<String>("encoder attach failed").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+ Local<Object> actualAudioFormat = Nan::New<Object>();
+ actualAudioFormat->Set(Nan::New<String>("sampleRate").ToLocalChecked(),
+ Nan::New<Number>(r->encoder->actual_audio_format.sample_rate));
+ actualAudioFormat->Set(Nan::New<String>("channelLayout").ToLocalChecked(),
+ Nan::New<Number>(r->encoder->actual_audio_format.channel_layout));
+ actualAudioFormat->Set(Nan::New<String>("sampleFormat").ToLocalChecked(),
+ Nan::New<Number>(r->encoder->actual_audio_format.sample_fmt));
+ Local<Object> o = Nan::New(r->instance);
+ o->Set(Nan::New<String>("actualAudioFormat").ToLocalChecked(), actualAudioFormat);
+ r->instance.Reset(o);
+ }
+
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ r->instance.Reset();
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNEncoder::Create) {
+ Nan::HandleScope scope;
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ GrooveEncoder *encoder = groove_encoder_create();
+ Local<Object> instance = NewInstance(encoder)->ToObject();
+ GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(instance);
+ EventContext *context = new EventContext;
+ gn_encoder->event_context = context;
+ context->emit_buffer_ok = true;
+ context->event_cb = new Nan::Callback(info[0].As<Function>());
+ context->encoder = encoder;
+
+ // set properties on the instance with default values from
+ // GrooveEncoder struct
+ Local<Object> targetAudioFormat = Nan::New<Object>();
+ Nan::Set(targetAudioFormat, Nan::New<String>("sampleRate").ToLocalChecked(),
+ Nan::New<Number>(encoder->target_audio_format.sample_rate));
+ Nan::Set(targetAudioFormat, Nan::New<String>("channelLayout").ToLocalChecked(), Nan::New<Number>(encoder->target_audio_format.channel_layout));
+ Nan::Set(targetAudioFormat, Nan::New<String>("sampleFormat").ToLocalChecked(),
+ Nan::New<Number>(encoder->target_audio_format.sample_fmt));
+
+
+ Nan::Set(instance, Nan::New<String>("bitRate").ToLocalChecked(), Nan::New<Number>(encoder->bit_rate));
+ Nan::Set(instance, Nan::New<String>("actualAudioFormat").ToLocalChecked(), Nan::Null());
+ Nan::Set(instance, Nan::New<String>("targetAudioFormat").ToLocalChecked(), targetAudioFormat);
+ Nan::Set(instance, Nan::New<String>("formatShortName").ToLocalChecked(), Nan::Null());
+ Nan::Set(instance, Nan::New<String>("codecShortName").ToLocalChecked(), Nan::Null());
+ Nan::Set(instance, Nan::New<String>("filename").ToLocalChecked(), Nan::Null());
+ Nan::Set(instance, Nan::New<String>("mimeType").ToLocalChecked(), Nan::Null());
+ Nan::Set(instance, Nan::New<String>("encodedBufferSize").ToLocalChecked(), Nan::New<Number>(encoder->encoded_buffer_size));
+ Nan::Set(instance, Nan::New<String>("sinkBufferSize").ToLocalChecked(), Nan::New<Number>(encoder->sink_buffer_size));
+
+ info.GetReturnValue().Set(instance);
+}
+
+NAN_METHOD(GNEncoder::Attach) {
+ Nan::HandleScope scope;
+
+ GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsObject()) {
+ Nan::ThrowTypeError("Expected object arg[0]");
+ return;
+ }
+ if (info.Length() < 2 || !info[1]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[1]");
+ return;
+ }
+
+ Local<Object> instance = info.This();
+ Local<Value> targetAudioFormatValue = instance->Get(Nan::New<String>("targetAudioFormat").ToLocalChecked());
+ if (!targetAudioFormatValue->IsObject()) {
+ Nan::ThrowTypeError("Expected targetAudioFormat to be an object");
+ return;
+ }
+
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info[0]->ToObject());
+
+ AttachReq *request = new AttachReq;
+
+ request->req.data = request;
+ request->callback = new Nan::Callback(info[1].As<Function>());
+
+ request->instance.Reset(info.This());
+
+ request->playlist = gn_playlist->playlist;
+ request->event_context = gn_encoder->event_context;
+ GrooveEncoder *encoder = gn_encoder->encoder;
+ request->encoder = encoder;
+
+ // copy the properties from our instance to the encoder
+ Local<Value> formatShortName = instance->Get(Nan::New<String>("formatShortName").ToLocalChecked());
+ if (formatShortName->IsNull() || formatShortName->IsUndefined()) {
+ request->format_short_name = NULL;
+ } else {
+ request->format_short_name = new String::Utf8Value(formatShortName->ToString());
+ }
+ Local<Value> codecShortName = instance->Get(Nan::New<String>("codecShortName").ToLocalChecked());
+ if (codecShortName->IsNull() || codecShortName->IsUndefined()) {
+ request->codec_short_name = NULL;
+ } else {
+ request->codec_short_name = new String::Utf8Value(codecShortName->ToString());
+ }
+ Local<Value> filenameStr = instance->Get(Nan::New<String>("filename").ToLocalChecked());
+ if (filenameStr->IsNull() || filenameStr->IsUndefined()) {
+ request->filename = NULL;
+ } else {
+ request->filename = new String::Utf8Value(filenameStr->ToString());
+ }
+ Local<Value> mimeType = instance->Get(Nan::New<String>("mimeType").ToLocalChecked());
+ if (mimeType->IsNull() || mimeType->IsUndefined()) {
+ request->mime_type = NULL;
+ } else {
+ request->mime_type = new String::Utf8Value(mimeType->ToString());
+ }
+
+ Local<Object> targetAudioFormat = targetAudioFormatValue->ToObject();
+ Local<Value> sampleRate = targetAudioFormat->Get(Nan::New<String>("sampleRate").ToLocalChecked());
+ double sample_rate = sampleRate->NumberValue();
+ double channel_layout = targetAudioFormat->Get(Nan::New<String>("channelLayout").ToLocalChecked())->NumberValue();
+ double sample_fmt = targetAudioFormat->Get(Nan::New<String>("sampleFormat").ToLocalChecked())->NumberValue();
+ encoder->target_audio_format.sample_rate = (int)sample_rate;
+ encoder->target_audio_format.channel_layout = (int)channel_layout;
+ encoder->target_audio_format.sample_fmt = (enum GrooveSampleFormat)(int)sample_fmt;
+
+ double bit_rate = instance->Get(Nan::New<String>("bitRate").ToLocalChecked())->NumberValue();
+ encoder->bit_rate = (int)bit_rate;
+
+ double sink_buffer_size = instance->Get(Nan::New<String>("sinkBufferSize").ToLocalChecked())->NumberValue();
+ encoder->sink_buffer_size = (int)sink_buffer_size;
+
+ double encoded_buffer_size = instance->Get(Nan::New<String>("encodedBufferSize").ToLocalChecked())->NumberValue();
+ encoder->encoded_buffer_size = (int)encoded_buffer_size;
+
+ uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
+ (uv_after_work_cb)AttachAfter);
+}
+
+struct DetachReq {
+ uv_work_t req;
+ GrooveEncoder *encoder;
+ Nan::Callback *callback;
+ int errcode;
+ GNEncoder::EventContext *event_context;
+};
+
+static void DetachAsyncFree(uv_handle_t *handle) {
+}
+
+static void DetachAsync(uv_work_t *req) {
+ DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
+ r->errcode = groove_encoder_detach(r->encoder);
+
+ uv_cond_signal(&r->event_context->cond);
+ uv_thread_join(&r->event_context->event_thread);
+ uv_cond_destroy(&r->event_context->cond);
+ uv_mutex_destroy(&r->event_context->mutex);
+ uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
+}
+
+static void DetachAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+ DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->errcode < 0) {
+ argv[0] = Exception::Error(Nan::New<String>("encoder detach failed").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+ }
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNEncoder::Detach) {
+ Nan::HandleScope scope;
+ GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ GrooveEncoder *encoder = gn_encoder->encoder;
+
+ if (!encoder->playlist) {
+ Nan::ThrowTypeError("detach: not attached");
+ return;
+ }
+
+ DetachReq *request = new DetachReq;
+
+ request->req.data = request;
+ request->callback = new Nan::Callback(info[0].As<Function>());
+ request->encoder = encoder;
+ request->event_context = gn_encoder->event_context;
+
+ uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
+ (uv_after_work_cb)DetachAfter);
+
+ return;
+}
+
+static void buffer_free(char *data, void *hint) {
+ GrooveBuffer *buffer = reinterpret_cast<GrooveBuffer*>(hint);
+ groove_buffer_unref(buffer);
+}
+
+NAN_METHOD(GNEncoder::GetBuffer) {
+ Nan::HandleScope scope;
+ GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(info.This());
+ GrooveEncoder *encoder = gn_encoder->encoder;
+
+ GrooveBuffer *buffer;
+ int buf_result = groove_encoder_buffer_get(encoder, &buffer, 0);
+
+ uv_mutex_lock(&gn_encoder->event_context->mutex);
+ gn_encoder->event_context->emit_buffer_ok = true;
+ uv_cond_signal(&gn_encoder->event_context->cond);
+ uv_mutex_unlock(&gn_encoder->event_context->mutex);
+
+ switch (buf_result) {
+ case GROOVE_BUFFER_YES: {
+ Local<Object> object = Nan::New<Object>();
+
+ Nan::MaybeLocal<Object> bufferObject = Nan::NewBuffer(
+ reinterpret_cast<char*>(buffer->data[0]), buffer->size,
+ buffer_free, buffer);
+ Nan::Set(object, Nan::New<String>("buffer").ToLocalChecked(), bufferObject.ToLocalChecked());
+
+ if (buffer->item) {
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(),
+ GNPlaylistItem::NewInstance(buffer->item));
+ } else {
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ }
+
+ Nan::Set(object, Nan::New<String>("pos").ToLocalChecked(), Nan::New<Number>(buffer->pos));
+ Nan::Set(object, Nan::New<String>("pts").ToLocalChecked(), Nan::New<Number>(buffer->pts));
+
+ info.GetReturnValue().Set(object);
+ break;
+ }
+ case GROOVE_BUFFER_END: {
+ Local<Object> object = Nan::New<Object>();
+
+ Nan::Set(object, Nan::New<String>("buffer").ToLocalChecked(), Nan::Null());
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ Nan::Set(object, Nan::New<String>("pos").ToLocalChecked(), Nan::Null());
+ Nan::Set(object, Nan::New<String>("pts").ToLocalChecked(), Nan::Null());
+
+ info.GetReturnValue().Set(object);
+ break;
+ }
+ default:
+ info.GetReturnValue().Set(Nan::Null());
+ }
+}
+
+NAN_METHOD(GNEncoder::Position) {
+ Nan::HandleScope scope;
+
+ GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(info.This());
+ GrooveEncoder *encoder = gn_encoder->encoder;
+
+ GroovePlaylistItem *item;
+ double pos;
+ groove_encoder_position(encoder, &item, &pos);
+
+ Local<Object> obj = Nan::New<Object>();
+ Nan::Set(obj, Nan::New<String>("pos").ToLocalChecked(), Nan::New<Number>(pos));
+ if (item) {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), GNPlaylistItem::NewInstance(item));
+ } else {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ }
+
+ info.GetReturnValue().Set(obj);
+}
diff --git a/src/gn_encoder.h b/src/encoder.h
similarity index 52%
rename from src/gn_encoder.h
rename to src/encoder.h
index 063bf60..aceb580 100644
--- a/src/gn_encoder.h
+++ b/src/encoder.h
@@ -2,7 +2,7 @@
#define GN_ENCODER_H
#include <node.h>
-
+#include <nan.h>
#include <groove/encoder.h>
class GNEncoder : public node::ObjectWrap {
@@ -10,7 +10,7 @@ class GNEncoder : public node::ObjectWrap {
static void Init();
static v8::Handle<v8::Value> NewInstance(GrooveEncoder *encoder);
- static v8::Handle<v8::Value> Create(const v8::Arguments& args);
+ static NAN_METHOD(Create);
struct EventContext {
uv_thread_t event_thread;
@@ -18,7 +18,8 @@ class GNEncoder : public node::ObjectWrap {
uv_cond_t cond;
uv_mutex_t mutex;
GrooveEncoder *encoder;
- v8::Persistent<v8::Function> event_cb;
+ Nan::Callback *event_cb;
+ bool emit_buffer_ok;
};
GrooveEncoder *encoder;
@@ -27,13 +28,12 @@ class GNEncoder : public node::ObjectWrap {
GNEncoder();
~GNEncoder();
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
+ static NAN_METHOD(New);
- static v8::Handle<v8::Value> Attach(const v8::Arguments& args);
- static v8::Handle<v8::Value> Detach(const v8::Arguments& args);
- static v8::Handle<v8::Value> GetBuffer(const v8::Arguments& args);
- static v8::Handle<v8::Value> Position(const v8::Arguments& args);
+ static NAN_METHOD(Attach);
+ static NAN_METHOD(Detach);
+ static NAN_METHOD(GetBuffer);
+ static NAN_METHOD(Position);
};
#endif
diff --git a/src/file.cc b/src/file.cc
new file mode 100644
index 0000000..32e041a
--- /dev/null
+++ b/src/file.cc
@@ -0,0 +1,332 @@
+#include <node.h>
+#include "file.h"
+
+using namespace v8;
+
+GNFile::GNFile() {};
+GNFile::~GNFile() {};
+
+static Nan::Persistent<v8::Function> constructor;
+
+void GNFile::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GrooveFile").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+ Local<ObjectTemplate> proto = tpl->PrototypeTemplate();
+
+ // Fields
+ Nan::SetAccessor(proto, Nan::New<String>("filename").ToLocalChecked(), GetFilename);
+ Nan::SetAccessor(proto, Nan::New<String>("dirty").ToLocalChecked(), GetDirty);
+ Nan::SetAccessor(proto, Nan::New<String>("id").ToLocalChecked(), GetId);
+
+ // Methods
+ Nan::SetPrototypeMethod(tpl, "close", Close);
+ Nan::SetPrototypeMethod(tpl, "getMetadata", GetMetadata);
+ Nan::SetPrototypeMethod(tpl, "setMetadata", SetMetadata);
+ Nan::SetPrototypeMethod(tpl, "metadata", Metadata);
+ Nan::SetPrototypeMethod(tpl, "shortNames", ShortNames);
+ Nan::SetPrototypeMethod(tpl, "save", Save);
+ Nan::SetPrototypeMethod(tpl, "duration", Duration);
+
+ constructor.Reset(tpl->GetFunction());
+}
+
+NAN_METHOD(GNFile::New) {
+ Nan::HandleScope scope;
+ assert(info.IsConstructCall());
+
+ GNFile *obj = new GNFile();
+ obj->Wrap(info.This());
+
+ info.GetReturnValue().Set(info.This());
+}
+
+Handle<Value> GNFile::NewInstance(GrooveFile *file) {
+ Nan::EscapableHandleScope scope;
+
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
+
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(instance);
+ gn_file->file = file;
+
+ return scope.Escape(instance);
+}
+
+NAN_GETTER(GNFile::GetDirty) {
+ Nan::HandleScope scope;
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+ info.GetReturnValue().Set(Nan::New<Boolean>(gn_file->file->dirty));
+}
+
+NAN_GETTER(GNFile::GetId) {
+ Nan::HandleScope scope;
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%p", gn_file->file);
+ info.GetReturnValue().Set(Nan::New<String>(buf).ToLocalChecked());
+}
+
+NAN_GETTER(GNFile::GetFilename) {
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+ info.GetReturnValue().Set(Nan::New<String>(gn_file->file->filename).ToLocalChecked());
+}
+
+NAN_METHOD(GNFile::GetMetadata) {
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsString()) {
+ Nan::ThrowTypeError("Expected string arg[0]");
+ return;
+ }
+
+ int flags = 0;
+ if (info.Length() >= 2) {
+ if (!info[1]->IsNumber()) {
+ Nan::ThrowTypeError("Expected number arg[1]");
+ return;
+ }
+ flags = (int)info[1]->NumberValue();
+ }
+
+ String::Utf8Value key_str(info[0]->ToString());
+ GrooveTag *tag = groove_file_metadata_get(gn_file->file, *key_str, NULL, flags);
+ if (tag)
+ info.GetReturnValue().Set(Nan::New<String>(groove_tag_value(tag)).ToLocalChecked());
+ else
+ info.GetReturnValue().Set(Nan::Null());
+}
+
+NAN_METHOD(GNFile::SetMetadata) {
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsString()) {
+ Nan::ThrowTypeError("Expected string arg[0]");
+ return;
+ }
+
+ if (info.Length() < 2 || !info[0]->IsString()) {
+ Nan::ThrowTypeError("Expected string arg[1]");
+ return;
+ }
+
+ int flags = 0;
+ if (info.Length() >= 3) {
+ if (!info[2]->IsNumber()) {
+ Nan::ThrowTypeError("Expected number arg[2]");
+ return;
+ }
+ flags = (int)info[2]->NumberValue();
+ }
+
+ String::Utf8Value key_str(info[0]->ToString());
+ String::Utf8Value val_str(info[1]->ToString());
+ int err = groove_file_metadata_set(gn_file->file, *key_str, *val_str, flags);
+ if (err < 0) {
+ Nan::ThrowTypeError("set metadata failed");
+ return;
+ }
+ return;
+}
+
+NAN_METHOD(GNFile::Metadata) {
+ Nan::HandleScope scope;
+
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+ Local<Object> metadata = Nan::New<Object>();
+
+ GrooveTag *tag = NULL;
+ while ((tag = groove_file_metadata_get(gn_file->file, "", tag, 0))) {
+ Nan::Set(metadata, Nan::New<String>(groove_tag_key(tag)).ToLocalChecked(),
+ Nan::New<String>(groove_tag_value(tag)).ToLocalChecked());
+ }
+
+ info.GetReturnValue().Set(metadata);
+}
+
+NAN_METHOD(GNFile::ShortNames) {
+ Nan::HandleScope scope;
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+ info.GetReturnValue().Set(Nan::New<String>(groove_file_short_names(gn_file->file)).ToLocalChecked());
+}
+
+NAN_METHOD(GNFile::Duration) {
+ Nan::HandleScope scope;
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+ info.GetReturnValue().Set(Nan::New<Number>(groove_file_duration(gn_file->file)));
+}
+
+struct CloseReq {
+ uv_work_t req;
+ Nan::Callback *callback;
+ GrooveFile *file;
+};
+
+static void CloseAsync(uv_work_t *req) {
+ CloseReq *r = reinterpret_cast<CloseReq *>(req->data);
+ if (r->file) {
+ groove_file_close(r->file);
+ }
+}
+
+static void CloseAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+
+ CloseReq *r = reinterpret_cast<CloseReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->file) {
+ argv[0] = Nan::Null();
+ } else {
+ argv[0] = Exception::Error(Nan::New<String>("file already closed").ToLocalChecked());
+ }
+
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNFile::Close) {
+ Nan::HandleScope scope;
+
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ CloseReq *request = new CloseReq;
+
+ request->callback = new Nan::Callback(info[0].As<Function>());
+ request->file = gn_file->file;
+ request->req.data = request;
+
+ gn_file->file = NULL;
+ uv_queue_work(uv_default_loop(), &request->req, CloseAsync, (uv_after_work_cb)CloseAfter);
+
+ return;
+}
+
+struct OpenReq {
+ uv_work_t req;
+ GrooveFile *file;
+ String::Utf8Value *filename;
+ Nan::Callback *callback;
+};
+
+static void OpenAsync(uv_work_t *req) {
+ OpenReq *r = reinterpret_cast<OpenReq *>(req->data);
+ r->file = groove_file_open(**r->filename);
+}
+
+static void OpenAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+ OpenReq *r = reinterpret_cast<OpenReq *>(req->data);
+
+ Local<Value> argv[2];
+ if (r->file) {
+ argv[0] = Nan::Null();
+ argv[1] = GNFile::NewInstance(r->file);
+ } else {
+ argv[0] = Exception::Error(Nan::New<String>("open file failed").ToLocalChecked());
+ argv[1] = Nan::Null();
+ }
+ TryCatch try_catch;
+ r->callback->Call(2, argv);
+
+ // cleanup
+ delete r->filename;
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNFile::Open) {
+ Nan::HandleScope scope;
+
+ if (info.Length() < 1 || !info[0]->IsString()) {
+ Nan::ThrowTypeError("Expected string arg[0]");
+ return;
+ }
+ if (info.Length() < 2 || !info[1]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[1]");
+ return;
+ }
+ OpenReq *request = new OpenReq;
+
+ request->filename = new String::Utf8Value(info[0]->ToString());
+ request->callback = new Nan::Callback(info[1].As<Function>());
+ request->req.data = request;
+
+ uv_queue_work(uv_default_loop(), &request->req, OpenAsync, (uv_after_work_cb)OpenAfter);
+
+ return;
+}
+
+struct SaveReq {
+ uv_work_t req;
+ Nan::Callback *callback;
+ GrooveFile *file;
+ int ret;
+};
+
+static void SaveAsync(uv_work_t *req) {
+ SaveReq *r = reinterpret_cast<SaveReq *>(req->data);
+ r->ret = groove_file_save(r->file);
+}
+
+static void SaveAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+
+ SaveReq *r = reinterpret_cast<SaveReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->ret) {
+ argv[0] = Exception::Error(Nan::New<String>("Unable to open file").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+ }
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNFile::Save) {
+ Nan::HandleScope scope;
+
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ SaveReq *request = new SaveReq;
+
+ request->callback = new Nan::Callback(info[0].As<Function>());
+ request->file = gn_file->file;
+ request->req.data = request;
+
+ uv_queue_work(uv_default_loop(), &request->req, SaveAsync, (uv_after_work_cb)SaveAfter);
+
+ return;
+}
diff --git a/src/file.h b/src/file.h
new file mode 100644
index 0000000..25012bd
--- /dev/null
+++ b/src/file.h
@@ -0,0 +1,35 @@
+#ifndef GN_FILE_H
+#define GN_FILE_H
+
+#include <node.h>
+#include <nan.h>
+#include <groove/groove.h>
+
+class GNFile : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(GrooveFile *file);
+
+ static NAN_METHOD(Open);
+
+ GrooveFile *file;
+ private:
+ GNFile();
+ ~GNFile();
+
+ static NAN_METHOD(New);
+
+ static NAN_GETTER(GetDirty);
+ static NAN_GETTER(GetId);
+ static NAN_GETTER(GetFilename);
+
+ static NAN_METHOD(Close);
+ static NAN_METHOD(Duration);
+ static NAN_METHOD(GetMetadata);
+ static NAN_METHOD(SetMetadata);
+ static NAN_METHOD(Metadata);
+ static NAN_METHOD(ShortNames);
+ static NAN_METHOD(Save);
+};
+
+#endif
diff --git a/src/fingerprinter.cc b/src/fingerprinter.cc
new file mode 100644
index 0000000..6c7eda2
--- /dev/null
+++ b/src/fingerprinter.cc
@@ -0,0 +1,381 @@
+#include "fingerprinter.h"
+#include "playlist_item.h"
+#include "playlist.h"
+
+using namespace v8;
+
+GNFingerprinter::GNFingerprinter() {};
+GNFingerprinter::~GNFingerprinter() {
+ groove_fingerprinter_destroy(printer);
+};
+
+static Nan::Persistent<v8::Function> constructor;
+
+void GNFingerprinter::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GrooveFingerprinter").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(2);
+
+ // Methods
+ Nan::SetPrototypeMethod(tpl, "attach", Attach);
+ Nan::SetPrototypeMethod(tpl, "detach", Detach);
+ Nan::SetPrototypeMethod(tpl, "getInfo", GetInfo);
+ Nan::SetPrototypeMethod(tpl, "position", Position);
+
+ constructor.Reset(tpl->GetFunction());
+}
+
+NAN_METHOD(GNFingerprinter::New) {
+ Nan::HandleScope scope;
+
+ GNFingerprinter *obj = new GNFingerprinter();
+ obj->Wrap(info.This());
+
+ info.GetReturnValue().Set(info.This());
+}
+
+Handle<Value> GNFingerprinter::NewInstance(GrooveFingerprinter *printer) {
+ Nan::EscapableHandleScope scope;
+
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
+
+ GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(instance);
+ gn_printer->printer = printer;
+
+ return scope.Escape(instance);
+}
+
+NAN_METHOD(GNFingerprinter::Create) {
+ Nan::HandleScope scope;
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ GrooveFingerprinter *printer = groove_fingerprinter_create();
+ if (!printer) {
+ Nan::ThrowTypeError("unable to create fingerprinter");
+ return;
+ }
+
+ // set properties on the instance with default values from
+ // GrooveFingerprinter struct
+ Local<Object> instance = GNFingerprinter::NewInstance(printer)->ToObject();
+ GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(instance);
+ EventContext *context = new EventContext;
+ gn_printer->event_context = context;
+ context->event_cb = new Nan::Callback(info[0].As<Function>());
+ context->printer = printer;
+
+
+ Nan::Set(instance, Nan::New<String>("infoQueueSize").ToLocalChecked(),
+ Nan::New<Number>(printer->info_queue_size));
+ Nan::Set(instance, Nan::New<String>("sinkBufferSize").ToLocalChecked(),
+ Nan::New<Number>(printer->sink_buffer_size));
+
+ info.GetReturnValue().Set(instance);
+}
+
+NAN_METHOD(GNFingerprinter::Position) {
+ Nan::HandleScope scope;
+
+ GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(info.This());
+ GrooveFingerprinter *printer = gn_printer->printer;
+
+ GroovePlaylistItem *item;
+ double pos;
+ groove_fingerprinter_position(printer, &item, &pos);
+
+ Local<Object> obj = Nan::New<Object>();
+ Nan::Set(obj, Nan::New<String>("pos").ToLocalChecked(), Nan::New<Number>(pos));
+ if (item) {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), GNPlaylistItem::NewInstance(item));
+ } else {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ }
+
+ info.GetReturnValue().Set(obj);
+}
+
+NAN_METHOD(GNFingerprinter::GetInfo) {
+ Nan::HandleScope scope;
+
+ GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(info.This());
+ GrooveFingerprinter *printer = gn_printer->printer;
+
+ GrooveFingerprinterInfo print_info;
+ if (groove_fingerprinter_info_get(printer, &print_info, 0) == 1) {
+ Local<Object> object = Nan::New<Object>();
+
+ if (print_info.fingerprint) {
+ Local<Array> int_list = Nan::New<Array>();
+ for (int i = 0; i < print_info.fingerprint_size; i += 1) {
+ Nan::Set(int_list, Nan::New<Number>(i), Nan::New<Number>(print_info.fingerprint[i]));
+ }
+ Nan::Set(object, Nan::New<String>("fingerprint").ToLocalChecked(), int_list);
+ } else {
+ Nan::Set(object, Nan::New<String>("fingerprint").ToLocalChecked(), Nan::Null());
+ }
+ Nan::Set(object, Nan::New<String>("duration").ToLocalChecked(), Nan::New<Number>(print_info.duration));
+
+ if (print_info.item) {
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(), GNPlaylistItem::NewInstance(print_info.item));
+ } else {
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ }
+
+ groove_fingerprinter_free_info(&print_info);
+
+ info.GetReturnValue().Set(object);
+ } else {
+ info.GetReturnValue().Set(Nan::Null());
+ }
+}
+
+struct AttachReq {
+ uv_work_t req;
+ Nan::Callback *callback;
+ GrooveFingerprinter *printer;
+ GroovePlaylist *playlist;
+ int errcode;
+ Nan::Persistent<Object> instance;
+ GNFingerprinter::EventContext *event_context;
+};
+
+static void EventAsyncCb(uv_async_t *handle
+#if UV_VERSION_MAJOR == 0
+ , int status
+#endif
+ )
+{
+ Nan::HandleScope scope;
+
+ GNFingerprinter::EventContext *context = reinterpret_cast<GNFingerprinter::EventContext *>(handle->data);
+
+ // call callback signaling that there is info ready
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ argv[0] = Nan::Null();
+
+ TryCatch try_catch;
+ context->event_cb->Call(argc, argv);
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+
+ uv_mutex_lock(&context->mutex);
+ uv_cond_signal(&context->cond);
+ uv_mutex_unlock(&context->mutex);
+}
+
+static void EventThreadEntry(void *arg) {
+ GNFingerprinter::EventContext *context = reinterpret_cast<GNFingerprinter::EventContext *>(arg);
+ while (groove_fingerprinter_info_peek(context->printer, 1) > 0) {
+ uv_mutex_lock(&context->mutex);
+ uv_async_send(&context->event_async);
+ uv_cond_wait(&context->cond, &context->mutex);
+ uv_mutex_unlock(&context->mutex);
+ }
+}
+
+static void AttachAsync(uv_work_t *req) {
+ AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
+
+ r->errcode = groove_fingerprinter_attach(r->printer, r->playlist);
+
+ GNFingerprinter::EventContext *context = r->event_context;
+
+ uv_cond_init(&context->cond);
+ uv_mutex_init(&context->mutex);
+
+ uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
+ context->event_async.data = context;
+
+ uv_thread_create(&context->event_thread, EventThreadEntry, context);
+}
+
+static void AttachAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+ AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->errcode < 0) {
+ argv[0] = Exception::Error(Nan::New<String>("fingerprinter attach failed").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+ }
+
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ r->instance.Reset();
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNFingerprinter::Attach) {
+ Nan::HandleScope scope;
+
+ GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsObject()) {
+ Nan::ThrowTypeError("Expected object arg[0]");
+ return;
+ }
+ if (info.Length() < 2 || !info[1]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[1]");
+ return;
+ }
+
+ Local<Object> instance = info.This();
+
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info[0]->ToObject());
+
+ AttachReq *request = new AttachReq;
+
+ request->req.data = request;
+ request->callback = new Nan::Callback(info[1].As<Function>());
+
+ request->instance.Reset(info.This());
+
+ request->playlist = gn_playlist->playlist;
+ GrooveFingerprinter *printer = gn_printer->printer;
+ request->printer = printer;
+ request->event_context = gn_printer->event_context;
+
+ // copy the properties from our instance to the player
+ printer->info_queue_size = (int)instance->Get(Nan::New<String>("infoQueueSize").ToLocalChecked())->NumberValue();
+ printer->sink_buffer_size = (int)instance->Get(Nan::New<String>("sinkBufferSize").ToLocalChecked())->NumberValue();
+
+ uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
+ (uv_after_work_cb)AttachAfter);
+
+ return;
+}
+
+struct DetachReq {
+ uv_work_t req;
+ GrooveFingerprinter *printer;
+ Nan::Callback *callback;
+ int errcode;
+ GNFingerprinter::EventContext *event_context;
+};
+
+static void DetachAsyncFree(uv_handle_t *handle) {
+ GNFingerprinter::EventContext *context = reinterpret_cast<GNFingerprinter::EventContext *>(handle->data);
+ delete context->event_cb;
+ delete context;
+}
+
+static void DetachAsync(uv_work_t *req) {
+ DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
+ r->errcode = groove_fingerprinter_detach(r->printer);
+ uv_cond_signal(&r->event_context->cond);
+ uv_thread_join(&r->event_context->event_thread);
+ uv_cond_destroy(&r->event_context->cond);
+ uv_mutex_destroy(&r->event_context->mutex);
+ uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
+}
+
+static void DetachAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+
+ DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->errcode < 0) {
+ argv[0] = Exception::Error(Nan::New<String>("fingerprinter detach failed").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+ }
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNFingerprinter::Detach) {
+ Nan::HandleScope scope;
+ GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ DetachReq *request = new DetachReq;
+
+ request->req.data = request;
+ request->callback = new Nan::Callback(info[0].As<Function>());
+ request->printer = gn_printer->printer;
+ request->event_context = gn_printer->event_context;
+
+ uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
+ (uv_after_work_cb)DetachAfter);
+
+ return;
+}
+
+NAN_METHOD(GNFingerprinter::Encode) {
+ Nan::HandleScope scope;
+
+ if (info.Length() < 1 || !info[0]->IsArray()) {
+ Nan::ThrowTypeError("Expected Array arg[0]");
+ return;
+ }
+
+ Local<Array> int_list = Local<Array>::Cast(info[0]);
+ int len = int_list->Length();
+ int32_t *raw_fingerprint = new int32_t[len];
+ for (int i = 0; i < len; i += 1) {
+ double val = int_list->Get(Nan::New<Number>(i))->NumberValue();
+ raw_fingerprint[i] = (int32_t)val;
+ }
+ char *fingerprint;
+ groove_fingerprinter_encode(raw_fingerprint, len, &fingerprint);
+ delete[] raw_fingerprint;
+ Local<String> js_fingerprint = Nan::New<String>(fingerprint).ToLocalChecked();
+ groove_fingerprinter_dealloc(fingerprint);
+
+ info.GetReturnValue().Set(js_fingerprint);
+}
+
+NAN_METHOD(GNFingerprinter::Decode) {
+ Nan::HandleScope scope;
+
+ if (info.Length() < 1 || !info[0]->IsString()) {
+ Nan::ThrowTypeError("Expected String arg[0]");
+ return;
+ }
+
+ String::Utf8Value utf8fingerprint(info[0]->ToString());
+ char *fingerprint = *utf8fingerprint;
+
+ int32_t *raw_fingerprint;
+ int raw_fingerprint_len;
+ groove_fingerprinter_decode(fingerprint, &raw_fingerprint, &raw_fingerprint_len);
+ Local<Array> int_list = Nan::New<Array>();
+
+ for (int i = 0; i < raw_fingerprint_len; i += 1) {
+ Nan::Set(int_list, Nan::New<Number>(i), Nan::New<Number>(raw_fingerprint[i]));
+ }
+ groove_fingerprinter_dealloc(raw_fingerprint);
+
+ info.GetReturnValue().Set(int_list);
+}
diff --git a/src/fingerprinter.h b/src/fingerprinter.h
new file mode 100644
index 0000000..9e4142c
--- /dev/null
+++ b/src/fingerprinter.h
@@ -0,0 +1,44 @@
+#ifndef GN_FINGERPRINTER_H
+#define GN_FINGERPRINTER_H
+
+#include <node.h>
+#include <nan.h>
+#include <groovefingerprinter/fingerprinter.h>
+
+using Nan::Callback;
+
+class GNFingerprinter : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(GrooveFingerprinter *printer);
+
+ static NAN_METHOD(Create);
+
+ static NAN_METHOD(Encode);
+ static NAN_METHOD(Decode);
+
+ struct EventContext {
+ uv_thread_t event_thread;
+ uv_async_t event_async;
+ uv_cond_t cond;
+ uv_mutex_t mutex;
+ GrooveFingerprinter *printer;
+ Callback *event_cb;
+ };
+
+ EventContext *event_context;
+ GrooveFingerprinter *printer;
+
+ private:
+ GNFingerprinter();
+ ~GNFingerprinter();
+
+ static NAN_METHOD(New);
+
+ static NAN_METHOD(Attach);
+ static NAN_METHOD(Detach);
+ static NAN_METHOD(GetInfo);
+ static NAN_METHOD(Position);
+};
+
+#endif
diff --git a/src/gn_encoder.cc b/src/gn_encoder.cc
deleted file mode 100644
index 0791292..0000000
--- a/src/gn_encoder.cc
+++ /dev/null
@@ -1,424 +0,0 @@
-#include <node.h>
-#include <node_buffer.h>
-#include "gn_encoder.h"
-#include "gn_playlist.h"
-#include "gn_playlist_item.h"
-
-using namespace v8;
-
-GNEncoder::GNEncoder() {};
-GNEncoder::~GNEncoder() {
- groove_encoder_destroy(encoder);
- delete event_context;
-};
-
-Persistent<Function> GNEncoder::constructor;
-
-template <typename target_t, typename func_t>
-static void AddMethod(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
- FunctionTemplate::New(fn)->GetFunction());
-}
-
-void GNEncoder::Init() {
- // Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GrooveEncoder"));
- tpl->InstanceTemplate()->SetInternalFieldCount(2);
- // Methods
- AddMethod(tpl, "attach", Attach);
- AddMethod(tpl, "detach", Detach);
- AddMethod(tpl, "getBuffer", GetBuffer);
- AddMethod(tpl, "position", Position);
-
- constructor = Persistent<Function>::New(tpl->GetFunction());
-}
-
-Handle<Value> GNEncoder::New(const Arguments& args) {
- HandleScope scope;
-
- GNEncoder *obj = new GNEncoder();
- obj->Wrap(args.This());
-
- return scope.Close(args.This());
-}
-
-Handle<Value> GNEncoder::NewInstance(GrooveEncoder *encoder) {
- HandleScope scope;
-
- Local<Object> instance = constructor->NewInstance();
-
- GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(instance);
- gn_encoder->encoder = encoder;
-
- return scope.Close(instance);
-}
-
-struct AttachReq {
- uv_work_t req;
- Persistent<Function> callback;
- GrooveEncoder *encoder;
- GroovePlaylist *playlist;
- int errcode;
- Persistent<Object> instance;
- String::Utf8Value *format_short_name;
- String::Utf8Value *codec_short_name;
- String::Utf8Value *filename;
- String::Utf8Value *mime_type;
- GNEncoder::EventContext *event_context;
-};
-
-static void EventAsyncCb(uv_async_t *handle, int status) {
- HandleScope scope;
-
- GNEncoder::EventContext *context = reinterpret_cast<GNEncoder::EventContext *>(handle->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- argv[0] = Undefined();
-
- TryCatch try_catch;
- context->event_cb->Call(Context::GetCurrent()->Global(), argc, argv);
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-
- uv_mutex_lock(&context->mutex);
- uv_cond_signal(&context->cond);
- uv_mutex_unlock(&context->mutex);
-}
-
-static void EventThreadEntry(void *arg) {
- GNEncoder::EventContext *context = reinterpret_cast<GNEncoder::EventContext *>(arg);
- while (groove_encoder_buffer_peek(context->encoder, 1) > 0) {
- uv_mutex_lock(&context->mutex);
- uv_async_send(&context->event_async);
- uv_cond_wait(&context->cond, &context->mutex);
- uv_mutex_unlock(&context->mutex);
- }
-}
-
-static void AttachAsync(uv_work_t *req) {
- AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
-
- r->encoder->format_short_name = r->format_short_name ? **r->format_short_name : NULL;
- r->encoder->codec_short_name = r->codec_short_name ? **r->codec_short_name : NULL;
- r->encoder->filename = r->filename ? **r->filename : NULL;
- r->encoder->mime_type = r->mime_type ? **r->mime_type : NULL;
-
- r->errcode = groove_encoder_attach(r->encoder, r->playlist);
- if (r->format_short_name) {
- delete r->format_short_name;
- r->format_short_name = NULL;
- }
- if (r->codec_short_name) {
- delete r->codec_short_name;
- r->codec_short_name = NULL;
- }
- if (r->filename) {
- delete r->filename;
- r->filename = NULL;
- }
- if (r->mime_type) {
- delete r->mime_type;
- r->mime_type = NULL;
- }
-
- GNEncoder::EventContext *context = r->event_context;
- uv_cond_init(&context->cond);
- uv_mutex_init(&context->mutex);
-
- uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
- context->event_async.data = context;
-
- uv_thread_create(&context->event_thread, EventThreadEntry, context);
-}
-
-static void AttachAfter(uv_work_t *req) {
- HandleScope scope;
- AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("encoder attach failed"));
- } else {
- argv[0] = Null();
-
- Local<Object> actualAudioFormat = Object::New();
- actualAudioFormat->Set(String::NewSymbol("sampleRate"),
- Number::New(r->encoder->actual_audio_format.sample_rate));
- actualAudioFormat->Set(String::NewSymbol("channelLayout"),
- Number::New(r->encoder->actual_audio_format.channel_layout));
- actualAudioFormat->Set(String::NewSymbol("sampleFormat"),
- Number::New(r->encoder->actual_audio_format.sample_fmt));
-
- r->instance->Set(String::NewSymbol("actualAudioFormat"), actualAudioFormat);
- }
-
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNEncoder::Create(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- GrooveEncoder *encoder = groove_encoder_create();
- Handle<Object> instance = NewInstance(encoder)->ToObject();
- GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(instance);
- EventContext *context = new EventContext;
- gn_encoder->event_context = context;
- context->event_cb = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- context->encoder = encoder;
-
- // set properties on the instance with default values from
- // GrooveEncoder struct
- Local<Object> targetAudioFormat = Object::New();
- targetAudioFormat->Set(String::NewSymbol("sampleRate"),
- Number::New(encoder->target_audio_format.sample_rate));
- targetAudioFormat->Set(String::NewSymbol("channelLayout"),
- Number::New(encoder->target_audio_format.channel_layout));
- targetAudioFormat->Set(String::NewSymbol("sampleFormat"),
- Number::New(encoder->target_audio_format.sample_fmt));
-
- instance->Set(String::NewSymbol("bitRate"), Number::New(encoder->bit_rate));
- instance->Set(String::NewSymbol("actualAudioFormat"), Null());
- instance->Set(String::NewSymbol("targetAudioFormat"), targetAudioFormat);
- instance->Set(String::NewSymbol("formatShortName"), Null());
- instance->Set(String::NewSymbol("codecShortName"), Null());
- instance->Set(String::NewSymbol("filename"), Null());
- instance->Set(String::NewSymbol("mimeType"), Null());
- instance->Set(String::NewSymbol("sinkBufferSize"), Number::New(encoder->sink_buffer_size));
- instance->Set(String::NewSymbol("encodedBufferSize"), Number::New(encoder->encoded_buffer_size));
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNEncoder::Attach(const Arguments& args) {
- HandleScope scope;
-
- GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsObject()) {
- ThrowException(Exception::TypeError(String::New("Expected object arg[0]")));
- return scope.Close(Undefined());
- }
- if (args.Length() < 2 || !args[1]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
- return scope.Close(Undefined());
- }
-
- Local<Object> instance = args.This();
- Local<Value> targetAudioFormatValue = instance->Get(String::NewSymbol("targetAudioFormat"));
- if (!targetAudioFormatValue->IsObject()) {
- ThrowException(Exception::TypeError(String::New("Expected targetAudioFormat to be an object")));
- return scope.Close(Undefined());
- }
-
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args[0]->ToObject());
-
- AttachReq *request = new AttachReq;
-
- request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
- request->instance = Persistent<Object>::New(args.This());
- request->playlist = gn_playlist->playlist;
- request->event_context = gn_encoder->event_context;
- GrooveEncoder *encoder = gn_encoder->encoder;
- request->encoder = encoder;
-
- // copy the properties from our instance to the encoder
- Local<Value> formatShortName = instance->Get(String::NewSymbol("formatShortName"));
- if (formatShortName->IsNull() || formatShortName->IsUndefined()) {
- request->format_short_name = NULL;
- } else {
- request->format_short_name = new String::Utf8Value(formatShortName->ToString());
- }
- Local<Value> codecShortName = instance->Get(String::NewSymbol("codecShortName"));
- if (codecShortName->IsNull() || codecShortName->IsUndefined()) {
- request->codec_short_name = NULL;
- } else {
- request->codec_short_name = new String::Utf8Value(codecShortName->ToString());
- }
- Local<Value> filenameStr = instance->Get(String::NewSymbol("filename"));
- if (filenameStr->IsNull() || filenameStr->IsUndefined()) {
- request->filename = NULL;
- } else {
- request->filename = new String::Utf8Value(filenameStr->ToString());
- }
- Local<Value> mimeType = instance->Get(String::NewSymbol("mimeType"));
- if (mimeType->IsNull() || mimeType->IsUndefined()) {
- request->mime_type = NULL;
- } else {
- request->mime_type = new String::Utf8Value(mimeType->ToString());
- }
-
- Local<Object> targetAudioFormat = targetAudioFormatValue->ToObject();
- Local<Value> sampleRate = targetAudioFormat->Get(String::NewSymbol("sampleRate"));
- double sample_rate = sampleRate->NumberValue();
- double channel_layout = targetAudioFormat->Get(String::NewSymbol("channelLayout"))->NumberValue();
- double sample_fmt = targetAudioFormat->Get(String::NewSymbol("sampleFormat"))->NumberValue();
- encoder->target_audio_format.sample_rate = (int)sample_rate;
- encoder->target_audio_format.channel_layout = (int)channel_layout;
- encoder->target_audio_format.sample_fmt = (enum GrooveSampleFormat)(int)sample_fmt;
-
- double bit_rate = instance->Get(String::NewSymbol("bitRate"))->NumberValue();
- encoder->bit_rate = (int)bit_rate;
-
- double sink_buffer_size = instance->Get(String::NewSymbol("sinkBufferSize"))->NumberValue();
- encoder->sink_buffer_size = (int)sink_buffer_size;
-
- double encoded_buffer_size = instance->Get(String::NewSymbol("encodedBufferSize"))->NumberValue();
- encoder->encoded_buffer_size = (int)encoded_buffer_size;
-
- uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
- (uv_after_work_cb)AttachAfter);
-
- return scope.Close(Undefined());
-}
-
-struct DetachReq {
- uv_work_t req;
- GrooveEncoder *encoder;
- Persistent<Function> callback;
- int errcode;
- GNEncoder::EventContext *event_context;
-};
-
-static void DetachAsyncFree(uv_handle_t *handle) {
-}
-
-static void DetachAsync(uv_work_t *req) {
- DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
- r->errcode = groove_encoder_detach(r->encoder);
-
- uv_cond_signal(&r->event_context->cond);
- uv_thread_join(&r->event_context->event_thread);
- uv_cond_destroy(&r->event_context->cond);
- uv_mutex_destroy(&r->event_context->mutex);
- uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
-}
-
-static void DetachAfter(uv_work_t *req) {
- HandleScope scope;
- DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("encoder detach failed"));
- } else {
- argv[0] = Null();
- }
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNEncoder::Detach(const Arguments& args) {
- HandleScope scope;
- GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- GrooveEncoder *encoder = gn_encoder->encoder;
-
- if (!encoder->playlist) {
- ThrowException(Exception::Error(String::New("detach: not attached")));
- return scope.Close(Undefined());
- }
-
- DetachReq *request = new DetachReq;
-
- request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- request->encoder = encoder;
- request->event_context = gn_encoder->event_context;
-
- uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
- (uv_after_work_cb)DetachAfter);
-
- return scope.Close(Undefined());
-}
-
-static void buffer_free(char *data, void *hint) {
- GrooveBuffer *buffer = reinterpret_cast<GrooveBuffer*>(hint);
- groove_buffer_unref(buffer);
-}
-
-Handle<Value> GNEncoder::GetBuffer(const Arguments& args) {
- HandleScope scope;
- GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(args.This());
- GrooveEncoder *encoder = gn_encoder->encoder;
-
- GrooveBuffer *buffer;
- switch (groove_encoder_buffer_get(encoder, &buffer, 0)) {
- case GROOVE_BUFFER_YES: {
- Local<Object> object = Object::New();
-
- Handle<Object> bufferObject = node::Buffer::New(
- reinterpret_cast<char*>(buffer->data[0]), buffer->size,
- buffer_free, buffer)->handle_;
- object->Set(String::NewSymbol("buffer"), bufferObject);
-
- if (buffer->item) {
- object->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(buffer->item));
- } else {
- object->Set(String::NewSymbol("item"), Null());
- }
- object->Set(String::NewSymbol("pos"), Number::New(buffer->pos));
- object->Set(String::NewSymbol("pts"), Number::New(buffer->pts));
- return scope.Close(object);
- }
- case GROOVE_BUFFER_END: {
- Local<Object> object = Object::New();
- object->Set(String::NewSymbol("buffer"), Null());
- object->Set(String::NewSymbol("item"), Null());
- object->Set(String::NewSymbol("pos"), Null());
- object->Set(String::NewSymbol("pts"), Null());
- return scope.Close(object);
- }
- default:
- return scope.Close(Null());
- }
-}
-
-Handle<Value> GNEncoder::Position(const Arguments& args) {
- HandleScope scope;
-
- GNEncoder *gn_encoder = node::ObjectWrap::Unwrap<GNEncoder>(args.This());
- GrooveEncoder *encoder = gn_encoder->encoder;
-
- GroovePlaylistItem *item;
- double pos;
- groove_encoder_position(encoder, &item, &pos);
-
- Local<Object> obj = Object::New();
- obj->Set(String::NewSymbol("pos"), Number::New(pos));
- if (item) {
- obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
- } else {
- obj->Set(String::NewSymbol("item"), Null());
- }
- return scope.Close(obj);
-}
diff --git a/src/gn_file.cc b/src/gn_file.cc
deleted file mode 100644
index ef38f50..0000000
--- a/src/gn_file.cc
+++ /dev/null
@@ -1,332 +0,0 @@
-#include <node.h>
-#include "gn_file.h"
-
-using namespace v8;
-
-GNFile::GNFile() {};
-GNFile::~GNFile() {};
-
-Persistent<Function> GNFile::constructor;
-
-template <typename target_t, typename func_t>
-static void AddGetter(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->SetAccessor(String::New(name), fn);
-}
-
-template <typename target_t, typename func_t>
-static void AddMethod(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
- FunctionTemplate::New(fn)->GetFunction());
-}
-
-void GNFile::Init() {
- // Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GrooveFile"));
- tpl->InstanceTemplate()->SetInternalFieldCount(1);
- // Fields
- AddGetter(tpl, "filename", GetFilename);
- AddGetter(tpl, "dirty", GetDirty);
- AddGetter(tpl, "id", GetId);
- // Methods
- AddMethod(tpl, "close", Close);
- AddMethod(tpl, "getMetadata", GetMetadata);
- AddMethod(tpl, "setMetadata", SetMetadata);
- AddMethod(tpl, "metadata", Metadata);
- AddMethod(tpl, "shortNames", ShortNames);
- AddMethod(tpl, "save", Save);
- AddMethod(tpl, "duration", Duration);
-
- constructor = Persistent<Function>::New(tpl->GetFunction());
-}
-
-Handle<Value> GNFile::New(const Arguments& args) {
- HandleScope scope;
-
- GNFile *obj = new GNFile();
- obj->Wrap(args.This());
-
- return scope.Close(args.This());
-}
-
-Handle<Value> GNFile::NewInstance(GrooveFile *file) {
- HandleScope scope;
-
- Local<Object> instance = constructor->NewInstance();
-
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(instance);
- gn_file->file = file;
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNFile::GetDirty(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
- return scope.Close(Boolean::New(gn_file->file->dirty));
-}
-
-Handle<Value> GNFile::GetId(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
- char buf[64];
- snprintf(buf, sizeof(buf), "%p", gn_file->file);
- return scope.Close(String::New(buf));
-}
-
-Handle<Value> GNFile::GetFilename(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info.This());
- return scope.Close(String::New(gn_file->file->filename));
-}
-
-Handle<Value> GNFile::GetMetadata(const Arguments& args) {
- HandleScope scope;
-
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsString()) {
- ThrowException(Exception::TypeError(String::New("Expected string arg[0]")));
- return scope.Close(Undefined());
- }
-
- int flags = 0;
- if (args.Length() >= 2) {
- if (!args[1]->IsNumber()) {
- ThrowException(Exception::TypeError(String::New("Expected number arg[1]")));
- return scope.Close(Undefined());
- }
- flags = (int)args[1]->NumberValue();
- }
-
- String::Utf8Value key_str(args[0]->ToString());
- GrooveTag *tag = groove_file_metadata_get(gn_file->file, *key_str, NULL, flags);
- if (tag)
- return scope.Close(String::New(groove_tag_value(tag)));
- return scope.Close(Null());
-}
-
-Handle<Value> GNFile::SetMetadata(const Arguments& args) {
- HandleScope scope;
-
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsString()) {
- ThrowException(Exception::TypeError(String::New("Expected string arg[0]")));
- return scope.Close(Undefined());
- }
-
- if (args.Length() < 2 || !args[0]->IsString()) {
- ThrowException(Exception::TypeError(String::New("Expected string arg[1]")));
- return scope.Close(Undefined());
- }
-
- int flags = 0;
- if (args.Length() >= 3) {
- if (!args[2]->IsNumber()) {
- ThrowException(Exception::TypeError(String::New("Expected number arg[2]")));
- return scope.Close(Undefined());
- }
- flags = (int)args[2]->NumberValue();
- }
-
- String::Utf8Value key_str(args[0]->ToString());
- String::Utf8Value val_str(args[1]->ToString());
- int err = groove_file_metadata_set(gn_file->file, *key_str, *val_str, flags);
- if (err < 0) {
- ThrowException(Exception::Error(String::New("set metadata failed")));
- return scope.Close(Undefined());
- }
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNFile::Metadata(const Arguments& args) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
- Local<Object> metadata = Object::New();
-
- GrooveTag *tag = NULL;
- while ((tag = groove_file_metadata_get(gn_file->file, "", tag, 0)))
- metadata->Set(String::New(groove_tag_key(tag)), String::New(groove_tag_value(tag)));
-
- return scope.Close(metadata);
-}
-
-Handle<Value> GNFile::ShortNames(const Arguments& args) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
- return scope.Close(String::New(groove_file_short_names(gn_file->file)));
-}
-
-Handle<Value> GNFile::Duration(const Arguments& args) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
- return scope.Close(Number::New(groove_file_duration(gn_file->file)));
-}
-
-struct CloseReq {
- uv_work_t req;
- Persistent<Function> callback;
- GrooveFile *file;
-};
-
-static void CloseAsync(uv_work_t *req) {
- CloseReq *r = reinterpret_cast<CloseReq *>(req->data);
- if (r->file) {
- groove_file_close(r->file);
- }
-}
-
-static void CloseAfter(uv_work_t *req) {
- HandleScope scope;
- CloseReq *r = reinterpret_cast<CloseReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->file) {
- argv[0] = Null();
- } else {
- argv[0] = Exception::Error(String::New("file already closed"));
- }
-
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNFile::Close(const Arguments& args) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- CloseReq *request = new CloseReq;
-
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- request->file = gn_file->file;
- request->req.data = request;
-
- gn_file->file = NULL;
- uv_queue_work(uv_default_loop(), &request->req, CloseAsync, (uv_after_work_cb)CloseAfter);
-
- return scope.Close(Undefined());
-}
-
-struct OpenReq {
- uv_work_t req;
- GrooveFile *file;
- String::Utf8Value *filename;
- Persistent<Function> callback;
-};
-
-static void OpenAsync(uv_work_t *req) {
- OpenReq *r = reinterpret_cast<OpenReq *>(req->data);
- r->file = groove_file_open(**r->filename);
-}
-
-static void OpenAfter(uv_work_t *req) {
- HandleScope scope;
- OpenReq *r = reinterpret_cast<OpenReq *>(req->data);
-
- Handle<Value> argv[2];
- if (r->file) {
- argv[0] = Null();
- argv[1] = GNFile::NewInstance(r->file);
- } else {
- argv[0] = Exception::Error(String::New("open file failed"));
- argv[1] = Null();
- }
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), 2, argv);
-
- // cleanup
- delete r->filename;
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNFile::Open(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsString()) {
- ThrowException(Exception::TypeError(String::New("Expected string arg[0]")));
- return scope.Close(Undefined());
- }
- if (args.Length() < 2 || !args[1]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
- return scope.Close(Undefined());
- }
- OpenReq *request = new OpenReq;
-
- request->filename = new String::Utf8Value(args[0]->ToString());
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
- request->req.data = request;
-
- uv_queue_work(uv_default_loop(), &request->req, OpenAsync, (uv_after_work_cb)OpenAfter);
-
- return scope.Close(Undefined());
-}
-
-struct SaveReq {
- uv_work_t req;
- Persistent<Function> callback;
- GrooveFile *file;
- int ret;
-};
-
-static void SaveAsync(uv_work_t *req) {
- SaveReq *r = reinterpret_cast<SaveReq *>(req->data);
- r->ret = groove_file_save(r->file);
-}
-
-static void SaveAfter(uv_work_t *req) {
- HandleScope scope;
- SaveReq *r = reinterpret_cast<SaveReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->ret < 0) {
- argv[0] = Exception::Error(String::New("save failed"));
- } else {
- argv[0] = Null();
- }
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNFile::Save(const Arguments& args) {
- HandleScope scope;
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- SaveReq *request = new SaveReq;
-
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- request->file = gn_file->file;
- request->req.data = request;
-
- uv_queue_work(uv_default_loop(), &request->req, SaveAsync, (uv_after_work_cb)SaveAfter);
-
- return scope.Close(Undefined());
-}
diff --git a/src/gn_file.h b/src/gn_file.h
deleted file mode 100644
index bc4b6c2..0000000
--- a/src/gn_file.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef GN_FILE_H
-#define GN_FILE_H
-
-#include <node.h>
-
-#include <groove/groove.h>
-
-class GNFile : public node::ObjectWrap {
- public:
- static void Init();
- static v8::Handle<v8::Value> NewInstance(GrooveFile *file);
-
- static v8::Handle<v8::Value> Open(const v8::Arguments& args);
-
- GrooveFile *file;
- private:
- GNFile();
- ~GNFile();
-
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
-
- static v8::Handle<v8::Value> GetDirty(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
- static v8::Handle<v8::Value> GetId(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
- static v8::Handle<v8::Value> GetFilename(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
-
- static v8::Handle<v8::Value> Close(const v8::Arguments& args);
- static v8::Handle<v8::Value> Duration(const v8::Arguments& args);
- static v8::Handle<v8::Value> GetMetadata(const v8::Arguments& args);
- static v8::Handle<v8::Value> SetMetadata(const v8::Arguments& args);
- static v8::Handle<v8::Value> Metadata(const v8::Arguments& args);
- static v8::Handle<v8::Value> ShortNames(const v8::Arguments& args);
- static v8::Handle<v8::Value> Save(const v8::Arguments& args);
-};
-
-#endif
diff --git a/src/gn_fingerprinter.cc b/src/gn_fingerprinter.cc
deleted file mode 100644
index 12d64ad..0000000
--- a/src/gn_fingerprinter.cc
+++ /dev/null
@@ -1,373 +0,0 @@
-#include <node.h>
-#include "gn_fingerprinter.h"
-#include "gn_playlist_item.h"
-#include "gn_playlist.h"
-
-using namespace v8;
-
-GNFingerprinter::GNFingerprinter() {};
-GNFingerprinter::~GNFingerprinter() {
- groove_fingerprinter_destroy(printer);
-};
-
-Persistent<Function> GNFingerprinter::constructor;
-
-template <typename target_t, typename func_t>
-static void AddGetter(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
-}
-
-template <typename target_t, typename func_t>
-static void AddMethod(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
- FunctionTemplate::New(fn)->GetFunction());
-}
-
-void GNFingerprinter::Init() {
- // Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GrooveFingerprinter"));
- tpl->InstanceTemplate()->SetInternalFieldCount(2);
- // Methods
- AddMethod(tpl, "attach", Attach);
- AddMethod(tpl, "detach", Detach);
- AddMethod(tpl, "getInfo", GetInfo);
- AddMethod(tpl, "position", Position);
-
- constructor = Persistent<Function>::New(tpl->GetFunction());
-}
-
-Handle<Value> GNFingerprinter::New(const Arguments& args) {
- HandleScope scope;
-
- GNFingerprinter *obj = new GNFingerprinter();
- obj->Wrap(args.This());
-
- return scope.Close(args.This());
-}
-
-Handle<Value> GNFingerprinter::NewInstance(GrooveFingerprinter *printer) {
- HandleScope scope;
-
- Local<Object> instance = constructor->NewInstance();
-
- GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(instance);
- gn_printer->printer = printer;
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNFingerprinter::Create(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- GrooveFingerprinter *printer = groove_fingerprinter_create();
- if (!printer) {
- ThrowException(Exception::Error(String::New("unable to create fingerprinter")));
- return scope.Close(Undefined());
- }
-
- // set properties on the instance with default values from
- // GrooveFingerprinter struct
- Local<Object> instance = GNFingerprinter::NewInstance(printer)->ToObject();
- GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(instance);
- EventContext *context = new EventContext;
- gn_printer->event_context = context;
- context->event_cb = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- context->printer = printer;
-
-
- instance->Set(String::NewSymbol("infoQueueSize"), Number::New(printer->info_queue_size));
- instance->Set(String::NewSymbol("sinkBufferSize"), Number::New(printer->sink_buffer_size));
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNFingerprinter::Position(const Arguments& args) {
- HandleScope scope;
-
- GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(args.This());
- GrooveFingerprinter *printer = gn_printer->printer;
-
- GroovePlaylistItem *item;
- double pos;
- groove_fingerprinter_position(printer, &item, &pos);
-
- Local<Object> obj = Object::New();
- obj->Set(String::NewSymbol("pos"), Number::New(pos));
- if (item) {
- obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
- } else {
- obj->Set(String::NewSymbol("item"), Null());
- }
- return scope.Close(obj);
-}
-
-Handle<Value> GNFingerprinter::GetInfo(const Arguments& args) {
- HandleScope scope;
- GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(args.This());
- GrooveFingerprinter *printer = gn_printer->printer;
-
- GrooveFingerprinterInfo info;
- if (groove_fingerprinter_info_get(printer, &info, 0) == 1) {
- Local<Object> object = Object::New();
-
- if (info.fingerprint) {
- Local<Array> int_list = Array::New();
- for (int i = 0; i < info.fingerprint_size; i += 1) {
- int_list->Set(Number::New(i), Number::New(info.fingerprint[i]));
- }
- object->Set(String::NewSymbol("fingerprint"), int_list);
- } else {
- object->Set(String::NewSymbol("fingerprint"), Null());
- }
- object->Set(String::NewSymbol("duration"), Number::New(info.duration));
-
- if (info.item) {
- object->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(info.item));
- } else {
- object->Set(String::NewSymbol("item"), Null());
- }
-
- groove_fingerprinter_free_info(&info);
- return scope.Close(object);
- } else {
- return scope.Close(Null());
- }
-}
-
-struct AttachReq {
- uv_work_t req;
- Persistent<Function> callback;
- GrooveFingerprinter *printer;
- GroovePlaylist *playlist;
- int errcode;
- Persistent<Object> instance;
- GNFingerprinter::EventContext *event_context;
-};
-
-static void EventAsyncCb(uv_async_t *handle, int status) {
- HandleScope scope;
-
- GNFingerprinter::EventContext *context = reinterpret_cast<GNFingerprinter::EventContext *>(handle->data);
-
- // call callback signaling that there is info ready
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- argv[0] = Null();
-
- TryCatch try_catch;
- context->event_cb->Call(Context::GetCurrent()->Global(), argc, argv);
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-
- uv_mutex_lock(&context->mutex);
- uv_cond_signal(&context->cond);
- uv_mutex_unlock(&context->mutex);
-}
-
-static void EventThreadEntry(void *arg) {
- GNFingerprinter::EventContext *context = reinterpret_cast<GNFingerprinter::EventContext *>(arg);
- while (groove_fingerprinter_info_peek(context->printer, 1) > 0) {
- uv_mutex_lock(&context->mutex);
- uv_async_send(&context->event_async);
- uv_cond_wait(&context->cond, &context->mutex);
- uv_mutex_unlock(&context->mutex);
- }
-}
-
-static void AttachAsync(uv_work_t *req) {
- AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
-
- r->errcode = groove_fingerprinter_attach(r->printer, r->playlist);
-
- GNFingerprinter::EventContext *context = r->event_context;
-
- uv_cond_init(&context->cond);
- uv_mutex_init(&context->mutex);
-
- uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
- context->event_async.data = context;
-
- uv_thread_create(&context->event_thread, EventThreadEntry, context);
-}
-
-static void AttachAfter(uv_work_t *req) {
- HandleScope scope;
- AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("fingerprinter attach failed"));
- } else {
- argv[0] = Null();
- }
-
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNFingerprinter::Attach(const Arguments& args) {
- HandleScope scope;
-
- GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsObject()) {
- ThrowException(Exception::TypeError(String::New("Expected object arg[0]")));
- return scope.Close(Undefined());
- }
- if (args.Length() < 2 || !args[1]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
- return scope.Close(Undefined());
- }
-
- Local<Object> instance = args.This();
-
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args[0]->ToObject());
-
- AttachReq *request = new AttachReq;
-
- request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
- request->instance = Persistent<Object>::New(args.This());
- request->playlist = gn_playlist->playlist;
- GrooveFingerprinter *printer = gn_printer->printer;
- request->printer = printer;
- request->event_context = gn_printer->event_context;
-
- // copy the properties from our instance to the player
- printer->info_queue_size = (int)instance->Get(String::NewSymbol("infoQueueSize"))->NumberValue();
- printer->sink_buffer_size = (int)instance->Get(String::NewSymbol("sinkBufferSize"))->NumberValue();
-
- uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
- (uv_after_work_cb)AttachAfter);
-
- return scope.Close(Undefined());
-}
-
-struct DetachReq {
- uv_work_t req;
- GrooveFingerprinter *printer;
- Persistent<Function> callback;
- int errcode;
- GNFingerprinter::EventContext *event_context;
-};
-
-static void DetachAsyncFree(uv_handle_t *handle) {
- GNFingerprinter::EventContext *context = reinterpret_cast<GNFingerprinter::EventContext *>(handle->data);
- delete context;
-}
-
-static void DetachAsync(uv_work_t *req) {
- DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
- r->errcode = groove_fingerprinter_detach(r->printer);
- uv_cond_signal(&r->event_context->cond);
- uv_thread_join(&r->event_context->event_thread);
- uv_cond_destroy(&r->event_context->cond);
- uv_mutex_destroy(&r->event_context->mutex);
- uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
-}
-
-static void DetachAfter(uv_work_t *req) {
- HandleScope scope;
- DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("fingerprinter detach failed"));
- } else {
- argv[0] = Null();
- }
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNFingerprinter::Detach(const Arguments& args) {
- HandleScope scope;
- GNFingerprinter *gn_printer = node::ObjectWrap::Unwrap<GNFingerprinter>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- DetachReq *request = new DetachReq;
-
- request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- request->printer = gn_printer->printer;
- request->event_context = gn_printer->event_context;
-
- uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
- (uv_after_work_cb)DetachAfter);
-
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNFingerprinter::Encode(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsArray()) {
- ThrowException(Exception::TypeError(String::New("Expected Array arg[0]")));
- return scope.Close(Undefined());
- }
-
- Local<Array> int_list = Local<Array>::Cast(args[0]);
- int len = int_list->Length();
- int32_t *raw_fingerprint = new int32_t[len];
- for (int i = 0; i < len; i += 1) {
- double val = int_list->Get(Number::New(i))->NumberValue();
- raw_fingerprint[i] = (int32_t)val;
- }
- char *fingerprint;
- groove_fingerprinter_encode(raw_fingerprint, len, &fingerprint);
- delete[] raw_fingerprint;
- Local<String> js_fingerprint = String::New(fingerprint);
- groove_fingerprinter_dealloc(fingerprint);
- return scope.Close(js_fingerprint);
-}
-
-Handle<Value> GNFingerprinter::Decode(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsString()) {
- ThrowException(Exception::TypeError(String::New("Expected String arg[0]")));
- return scope.Close(Undefined());
- }
-
- String::Utf8Value utf8fingerprint(args[0]->ToString());
- char *fingerprint = *utf8fingerprint;
-
- int32_t *raw_fingerprint;
- int raw_fingerprint_len;
- groove_fingerprinter_decode(fingerprint, &raw_fingerprint, &raw_fingerprint_len);
- Local<Array> int_list = Array::New();
-
- for (int i = 0; i < raw_fingerprint_len; i += 1) {
- int_list->Set(Number::New(i), Number::New(raw_fingerprint[i]));
- }
- groove_fingerprinter_dealloc(raw_fingerprint);
-
- return scope.Close(int_list);
-}
diff --git a/src/gn_fingerprinter.h b/src/gn_fingerprinter.h
deleted file mode 100644
index 16a5c56..0000000
--- a/src/gn_fingerprinter.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef GN_FINGERPRINTER_H
-#define GN_FINGERPRINTER_H
-
-#include <node.h>
-
-#include <groovefingerprinter/fingerprinter.h>
-
-class GNFingerprinter : public node::ObjectWrap {
- public:
- static void Init();
- static v8::Handle<v8::Value> NewInstance(GrooveFingerprinter *printer);
-
- static v8::Handle<v8::Value> Create(const v8::Arguments& args);
-
- static v8::Handle<v8::Value> Encode(const v8::Arguments& args);
- static v8::Handle<v8::Value> Decode(const v8::Arguments& args);
-
- struct EventContext {
- uv_thread_t event_thread;
- uv_async_t event_async;
- uv_cond_t cond;
- uv_mutex_t mutex;
- GrooveFingerprinter *printer;
- v8::Persistent<v8::Function> event_cb;
- };
-
- EventContext *event_context;
- GrooveFingerprinter *printer;
-
- private:
- GNFingerprinter();
- ~GNFingerprinter();
-
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
-
- static v8::Handle<v8::Value> Attach(const v8::Arguments& args);
- static v8::Handle<v8::Value> Detach(const v8::Arguments& args);
- static v8::Handle<v8::Value> GetInfo(const v8::Arguments& args);
- static v8::Handle<v8::Value> Position(const v8::Arguments& args);
-};
-
-#endif
diff --git a/src/gn_player.cc b/src/gn_player.cc
deleted file mode 100644
index a991353..0000000
--- a/src/gn_player.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-#include <node.h>
-#include "gn_player.h"
-#include "gn_playlist.h"
-#include "gn_playlist_item.h"
-
-using namespace v8;
-
-GNPlayer::GNPlayer() {};
-GNPlayer::~GNPlayer() {
- groove_player_destroy(player);
- delete event_context;
-};
-
-Persistent<Function> GNPlayer::constructor;
-
-template <typename target_t, typename func_t>
-static void AddGetter(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
-}
-
-template <typename target_t, typename func_t>
-static void AddMethod(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
- FunctionTemplate::New(fn)->GetFunction());
-}
-
-void GNPlayer::Init() {
- // Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GroovePlayer"));
- tpl->InstanceTemplate()->SetInternalFieldCount(2);
- // Fields
- AddGetter(tpl, "id", GetId);
- AddGetter(tpl, "playlist", GetPlaylist);
- // Methods
- AddMethod(tpl, "attach", Attach);
- AddMethod(tpl, "detach", Detach);
- AddMethod(tpl, "position", Position);
-
- constructor = Persistent<Function>::New(tpl->GetFunction());
-}
-
-Handle<Value> GNPlayer::New(const Arguments& args) {
- HandleScope scope;
-
- GNPlayer *obj = new GNPlayer();
- obj->Wrap(args.This());
-
- return scope.Close(args.This());
-}
-
-Handle<Value> GNPlayer::NewInstance(GroovePlayer *player) {
- HandleScope scope;
-
- Local<Object> instance = constructor->NewInstance();
-
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
- gn_player->player = player;
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNPlayer::GetId(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
- char buf[64];
- snprintf(buf, sizeof(buf), "%p", gn_player->player);
- return scope.Close(String::New(buf));
-}
-
-Handle<Value> GNPlayer::GetPlaylist(Local<String> property,
- const AccessorInfo &info)
-{
- HandleScope scope;
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
- GroovePlaylist *playlist = gn_player->player->playlist;
- if (playlist) {
- return scope.Close(GNPlaylist::NewInstance(playlist));
- } else {
- return scope.Close(Null());
- }
-}
-
-Handle<Value> GNPlayer::Position(const Arguments& args) {
- HandleScope scope;
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());
- GroovePlaylistItem *item;
- double pos;
- groove_player_position(gn_player->player, &item, &pos);
- Local<Object> obj = Object::New();
- obj->Set(String::NewSymbol("pos"), Number::New(pos));
- if (item) {
- obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
- } else {
- obj->Set(String::NewSymbol("item"), Null());
- }
- return scope.Close(obj);
-}
-
-struct AttachReq {
- uv_work_t req;
- Persistent<Function> callback;
- GroovePlayer *player;
- GroovePlaylist *playlist;
- int errcode;
- Persistent<Object> instance;
- int device_index;
- GNPlayer::EventContext *event_context;
-};
-
-static void EventAsyncCb(uv_async_t *handle, int status) {
- HandleScope scope;
-
- GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(handle->data);
-
- // flush events
- GroovePlayerEvent event;
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- while (groove_player_event_get(context->player, &event, 0) > 0) {
- argv[0] = Number::New(event.type);
-
- TryCatch try_catch;
- context->event_cb->Call(Context::GetCurrent()->Global(), argc, argv);
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
- }
-
- uv_mutex_lock(&context->mutex);
- uv_cond_signal(&context->cond);
- uv_mutex_unlock(&context->mutex);
-}
-
-static void EventThreadEntry(void *arg) {
- GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(arg);
- while (groove_player_event_peek(context->player, 1) > 0) {
- uv_mutex_lock(&context->mutex);
- uv_async_send(&context->event_async);
- uv_cond_wait(&context->cond, &context->mutex);
- uv_mutex_unlock(&context->mutex);
- }
-}
-
-static void AttachAsync(uv_work_t *req) {
- AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
-
- r->player->device_index = r->device_index;
- r->errcode = groove_player_attach(r->player, r->playlist);
-
- GNPlayer::EventContext *context = r->event_context;
-
- uv_cond_init(&context->cond);
- uv_mutex_init(&context->mutex);
-
- uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
- context->event_async.data = context;
-
- uv_thread_create(&context->event_thread, EventThreadEntry, context);
-}
-
-static void AttachAfter(uv_work_t *req) {
- HandleScope scope;
- AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("player attach failed"));
- } else {
- argv[0] = Null();
-
- Local<Object> actualAudioFormat = Object::New();
- actualAudioFormat->Set(String::NewSymbol("sampleRate"),
- Number::New(r->player->actual_audio_format.sample_rate));
- actualAudioFormat->Set(String::NewSymbol("channelLayout"),
- Number::New(r->player->actual_audio_format.channel_layout));
- actualAudioFormat->Set(String::NewSymbol("sampleFormat"),
- Number::New(r->player->actual_audio_format.sample_fmt));
-
- r->instance->Set(String::NewSymbol("actualAudioFormat"), actualAudioFormat);
- }
-
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNPlayer::Create(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- GroovePlayer *player = groove_player_create();
- Handle<Object> instance = NewInstance(player)->ToObject();
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
- EventContext *context = new EventContext;
- gn_player->event_context = context;
- context->event_cb = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- context->player = player;
-
- // set properties on the instance with default values from
- // GroovePlayer struct
- Local<Object> targetAudioFormat = Object::New();
- targetAudioFormat->Set(String::NewSymbol("sampleRate"),
- Number::New(player->target_audio_format.sample_rate));
- targetAudioFormat->Set(String::NewSymbol("channelLayout"),
- Number::New(player->target_audio_format.channel_layout));
- targetAudioFormat->Set(String::NewSymbol("sampleFormat"),
- Number::New(player->target_audio_format.sample_fmt));
-
- instance->Set(String::NewSymbol("deviceIndex"), Null());
- instance->Set(String::NewSymbol("actualAudioFormat"), Null());
- instance->Set(String::NewSymbol("targetAudioFormat"), targetAudioFormat);
- instance->Set(String::NewSymbol("deviceBufferSize"),
- Number::New(player->device_buffer_size));
- instance->Set(String::NewSymbol("sinkBufferSize"),
- Number::New(player->sink_buffer_size));
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNPlayer::Attach(const Arguments& args) {
- HandleScope scope;
-
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsObject()) {
- ThrowException(Exception::TypeError(String::New("Expected object arg[0]")));
- return scope.Close(Undefined());
- }
- if (args.Length() < 2 || !args[1]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
- return scope.Close(Undefined());
- }
-
- Local<Object> instance = args.This();
- Local<Value> targetAudioFormatValue = instance->Get(String::NewSymbol("targetAudioFormat"));
- if (!targetAudioFormatValue->IsObject()) {
- ThrowException(Exception::TypeError(String::New("Expected targetAudioFormat to be an object")));
- return scope.Close(Undefined());
- }
-
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args[0]->ToObject());
-
- AttachReq *request = new AttachReq;
-
- request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
- request->instance = Persistent<Object>::New(args.This());
- request->playlist = gn_playlist->playlist;
- GroovePlayer *player = gn_player->player;
- request->player = player;
- request->event_context = gn_player->event_context;
-
- // copy the properties from our instance to the player
- Local<Value> deviceIndex = instance->Get(String::NewSymbol("deviceIndex"));
-
- if (deviceIndex->IsNull() || deviceIndex->IsUndefined()) {
- request->device_index = -1;
- } else {
- request->device_index = (int) deviceIndex->NumberValue();
- }
- Local<Object> targetAudioFormat = targetAudioFormatValue->ToObject();
- Local<Value> sampleRate = targetAudioFormat->Get(String::NewSymbol("sampleRate"));
- double sample_rate = sampleRate->NumberValue();
- double channel_layout = targetAudioFormat->Get(String::NewSymbol("channelLayout"))->NumberValue();
- double sample_fmt = targetAudioFormat->Get(String::NewSymbol("sampleFormat"))->NumberValue();
- player->target_audio_format.sample_rate = (int)sample_rate;
- player->target_audio_format.channel_layout = (int)channel_layout;
- player->target_audio_format.sample_fmt = (enum GrooveSampleFormat)(int)sample_fmt;
-
- double device_buffer_size = instance->Get(String::NewSymbol("deviceBufferSize"))->NumberValue();
- player->device_buffer_size = (int)device_buffer_size;
-
- double sink_buffer_size = instance->Get(String::NewSymbol("sinkBufferSize"))->NumberValue();
- player->sink_buffer_size = (int)sink_buffer_size;
-
- uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
- (uv_after_work_cb)AttachAfter);
-
- return scope.Close(Undefined());
-}
-
-struct DetachReq {
- uv_work_t req;
- GroovePlayer *player;
- Persistent<Function> callback;
- int errcode;
- GNPlayer::EventContext *event_context;
-};
-
-static void DetachAsyncFree(uv_handle_t *handle) {
-}
-
-static void DetachAsync(uv_work_t *req) {
- DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
- r->errcode = groove_player_detach(r->player);
- uv_cond_signal(&r->event_context->cond);
- uv_thread_join(&r->event_context->event_thread);
- uv_cond_destroy(&r->event_context->cond);
- uv_mutex_destroy(&r->event_context->mutex);
- uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
-}
-
-static void DetachAfter(uv_work_t *req) {
- HandleScope scope;
- DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
-
- const unsigned argc = 1;
- Handle<Value> argv[argc];
- if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("player detach failed"));
- } else {
- argv[0] = Null();
- }
- TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
-
- delete r;
-
- if (try_catch.HasCaught()) {
- node::FatalException(try_catch);
- }
-}
-
-Handle<Value> GNPlayer::Detach(const Arguments& args) {
- HandleScope scope;
- GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());
-
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
- }
-
- DetachReq *request = new DetachReq;
-
- request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
- request->player = gn_player->player;
- request->event_context = gn_player->event_context;
-
- uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
- (uv_after_work_cb)DetachAfter);
-
- return scope.Close(Undefined());
-}
diff --git a/src/gn_player.h b/src/gn_player.h
deleted file mode 100644
index d5092de..0000000
--- a/src/gn_player.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef GN_PLAYER_H
-#define GN_PLAYER_H
-
-#include <node.h>
-
-#include <grooveplayer/player.h>
-
-class GNPlayer : public node::ObjectWrap {
- public:
- static void Init();
- static v8::Handle<v8::Value> NewInstance(GroovePlayer *player);
-
- static v8::Handle<v8::Value> Create(const v8::Arguments& args);
-
- struct EventContext {
- uv_thread_t event_thread;
- uv_async_t event_async;
- uv_cond_t cond;
- uv_mutex_t mutex;
- GroovePlayer *player;
- v8::Persistent<v8::Function> event_cb;
- };
-
-
- GroovePlayer *player;
- EventContext *event_context;
-
- private:
- GNPlayer();
- ~GNPlayer();
-
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
-
- static v8::Handle<v8::Value> GetId(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
-
- static v8::Handle<v8::Value> GetPlaylist(
- v8::Local<v8::String> property, const v8::AccessorInfo &info);
-
- static v8::Handle<v8::Value> Attach(const v8::Arguments& args);
- static v8::Handle<v8::Value> Detach(const v8::Arguments& args);
- static v8::Handle<v8::Value> Position(const v8::Arguments& args);
-};
-
-#endif
-
diff --git a/src/gn_playlist.cc b/src/gn_playlist.cc
deleted file mode 100644
index 050d561..0000000
--- a/src/gn_playlist.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-#include <node.h>
-#include "gn_playlist.h"
-#include "gn_playlist_item.h"
-#include "gn_file.h"
-
-using namespace v8;
-
-GNPlaylist::GNPlaylist() {
-};
-GNPlaylist::~GNPlaylist() {
- // TODO move this somewhere else because we create multiple objects with
- // the same playlist pointer in player.playlist or encoder.playlist
- // for example
- groove_playlist_destroy(playlist);
-};
-
-Persistent<Function> GNPlaylist::constructor;
-
-template <typename target_t, typename func_t>
-static void AddGetter(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
-}
-
-template <typename target_t, typename func_t>
-static void AddMethod(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
- FunctionTemplate::New(fn)->GetFunction());
-}
-
-void GNPlaylist::Init() {
- // Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GroovePlaylist"));
- tpl->InstanceTemplate()->SetInternalFieldCount(1);
- // Fields
- AddGetter(tpl, "id", GetId);
- AddGetter(tpl, "gain", GetGain);
- // Methods
- AddMethod(tpl, "play", Play);
- AddMethod(tpl, "items", Playlist);
- AddMethod(tpl, "pause", Pause);
- AddMethod(tpl, "seek", Seek);
- AddMethod(tpl, "insert", Insert);
- AddMethod(tpl, "remove", Remove);
- AddMethod(tpl, "position", DecodePosition);
- AddMethod(tpl, "playing", Playing);
- AddMethod(tpl, "clear", Clear);
- AddMethod(tpl, "count", Count);
- AddMethod(tpl, "setItemGain", SetItemGain);
- AddMethod(tpl, "setItemPeak", SetItemPeak);
- AddMethod(tpl, "setGain", SetGain);
- AddMethod(tpl, "setFillMode", SetFillMode);
-
- constructor = Persistent<Function>::New(tpl->GetFunction());
-}
-
-Handle<Value> GNPlaylist::New(const Arguments& args) {
- HandleScope scope;
-
- GNPlaylist *obj = new GNPlaylist();
- obj->Wrap(args.This());
-
- return scope.Close(args.This());
-}
-
-Handle<Value> GNPlaylist::NewInstance(GroovePlaylist *playlist) {
- HandleScope scope;
-
- Local<Object> instance = constructor->NewInstance();
-
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(instance);
- gn_playlist->playlist = playlist;
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNPlaylist::GetId(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
- char buf[64];
- snprintf(buf, sizeof(buf), "%p", gn_playlist->playlist);
- return scope.Close(String::New(buf));
-}
-
-Handle<Value> GNPlaylist::GetGain(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
- return scope.Close(Number::New(gn_playlist->playlist->gain));
-}
-
-Handle<Value> GNPlaylist::Play(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- groove_playlist_play(gn_playlist->playlist);
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::Playlist(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
-
- Local<Array> playlist = Array::New();
-
- GroovePlaylistItem *item = gn_playlist->playlist->head;
- int i = 0;
- while (item) {
- playlist->Set(Number::New(i), GNPlaylistItem::NewInstance(item));
- item = item->next;
- i += 1;
- }
-
- return scope.Close(playlist);
-}
-
-Handle<Value> GNPlaylist::Pause(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- groove_playlist_pause(gn_playlist->playlist);
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::Seek(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- GNPlaylistItem *gn_playlist_item =
- node::ObjectWrap::Unwrap<GNPlaylistItem>(args[0]->ToObject());
-
- double pos = args[1]->NumberValue();
- groove_playlist_seek(gn_playlist->playlist, gn_playlist_item->playlist_item, pos);
-
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::Insert(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(args[0]->ToObject());
- double gain = 1.0;
- double peak = 1.0;
- if (!args[1]->IsNull() && !args[1]->IsUndefined()) {
- gain = args[1]->NumberValue();
- }
- if (!args[2]->IsNull() && !args[2]->IsUndefined()) {
- peak = args[2]->NumberValue();
- }
- GroovePlaylistItem *item = NULL;
- if (!args[3]->IsNull() && !args[3]->IsUndefined()) {
- GNPlaylistItem *gn_pl_item =
- node::ObjectWrap::Unwrap<GNPlaylistItem>(args[3]->ToObject());
- item = gn_pl_item->playlist_item;
- }
- GroovePlaylistItem *result = groove_playlist_insert(gn_playlist->playlist,
- gn_file->file, gain, peak, item);
-
- return scope.Close(GNPlaylistItem::NewInstance(result));
-}
-
-Handle<Value> GNPlaylist::Remove(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(args[0]->ToObject());
- groove_playlist_remove(gn_playlist->playlist, gn_pl_item->playlist_item);
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::DecodePosition(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- GroovePlaylistItem *item;
- double pos = -1.0;
- groove_playlist_position(gn_playlist->playlist, &item, &pos);
- Local<Object> obj = Object::New();
- obj->Set(String::NewSymbol("pos"), Number::New(pos));
- if (item) {
- obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
- } else {
- obj->Set(String::NewSymbol("item"), Null());
- }
- return scope.Close(obj);
-}
-
-Handle<Value> GNPlaylist::Playing(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- int playing = groove_playlist_playing(gn_playlist->playlist);
- return scope.Close(Boolean::New(playing));
-}
-
-Handle<Value> GNPlaylist::Clear(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- groove_playlist_clear(gn_playlist->playlist);
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::Count(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- int count = groove_playlist_count(gn_playlist->playlist);
- return scope.Close(Number::New(count));
-}
-
-Handle<Value> GNPlaylist::SetItemGain(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(args[0]->ToObject());
- double gain = args[1]->NumberValue();
- groove_playlist_set_item_gain(gn_playlist->playlist, gn_pl_item->playlist_item, gain);
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::SetItemPeak(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(args[0]->ToObject());
- double peak = args[1]->NumberValue();
- groove_playlist_set_item_peak(gn_playlist->playlist, gn_pl_item->playlist_item, peak);
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::SetGain(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- groove_playlist_set_gain(gn_playlist->playlist, args[0]->NumberValue());
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::SetFillMode(const Arguments& args) {
- HandleScope scope;
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args.This());
- groove_playlist_set_fill_mode(gn_playlist->playlist, args[0]->NumberValue());
- return scope.Close(Undefined());
-}
-
-Handle<Value> GNPlaylist::Create(const Arguments& args) {
- HandleScope scope;
-
- GroovePlaylist *playlist = groove_playlist_create();
- return scope.Close(GNPlaylist::NewInstance(playlist));
-}
diff --git a/src/gn_playlist.h b/src/gn_playlist.h
deleted file mode 100644
index 2dddbd6..0000000
--- a/src/gn_playlist.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef GN_PLAYLIST_H
-#define GN_PLAYLIST_H
-
-#include <node.h>
-
-#include <groove/groove.h>
-
-class GNPlaylist : public node::ObjectWrap {
- public:
- static void Init();
- static v8::Handle<v8::Value> NewInstance(GroovePlaylist *playlist);
-
- static v8::Handle<v8::Value> Create(const v8::Arguments& args);
-
- GroovePlaylist *playlist;
-
-
- private:
- GNPlaylist();
- ~GNPlaylist();
-
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
-
- static v8::Handle<v8::Value> GetId(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
- static v8::Handle<v8::Value> GetGain(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
-
- static v8::Handle<v8::Value> Playlist(const v8::Arguments& args);
- static v8::Handle<v8::Value> Play(const v8::Arguments& args);
- static v8::Handle<v8::Value> Pause(const v8::Arguments& args);
- static v8::Handle<v8::Value> Seek(const v8::Arguments& args);
- static v8::Handle<v8::Value> Insert(const v8::Arguments& args);
- static v8::Handle<v8::Value> Remove(const v8::Arguments& args);
- static v8::Handle<v8::Value> Position(const v8::Arguments& args);
- static v8::Handle<v8::Value> DecodePosition(const v8::Arguments& args);
- static v8::Handle<v8::Value> Playing(const v8::Arguments& args);
- static v8::Handle<v8::Value> Clear(const v8::Arguments& args);
- static v8::Handle<v8::Value> Count(const v8::Arguments& args);
- static v8::Handle<v8::Value> SetItemGain(const v8::Arguments& args);
- static v8::Handle<v8::Value> SetItemPeak(const v8::Arguments& args);
- static v8::Handle<v8::Value> SetGain(const v8::Arguments& args);
- static v8::Handle<v8::Value> SetFillMode(const v8::Arguments& args);
-};
-
-#endif
diff --git a/src/gn_playlist_item.cc b/src/gn_playlist_item.cc
deleted file mode 100644
index db2cb24..0000000
--- a/src/gn_playlist_item.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <node.h>
-#include "gn_playlist_item.h"
-#include "gn_file.h"
-
-using namespace v8;
-
-GNPlaylistItem::GNPlaylistItem() { };
-GNPlaylistItem::~GNPlaylistItem() { };
-
-Persistent<Function> GNPlaylistItem::constructor;
-
-template <typename target_t, typename func_t>
-static void AddGetter(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
-}
-
-void GNPlaylistItem::Init() {
- // Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GroovePlaylistItem"));
- tpl->InstanceTemplate()->SetInternalFieldCount(1);
- // Fields
- AddGetter(tpl, "file", GetFile);
- AddGetter(tpl, "id", GetId);
- AddGetter(tpl, "gain", GetGain);
-
- constructor = Persistent<Function>::New(tpl->GetFunction());
-}
-
-Handle<Value> GNPlaylistItem::New(const Arguments& args) {
- HandleScope scope;
-
- GNPlaylistItem *obj = new GNPlaylistItem();
- obj->Wrap(args.This());
-
- return scope.Close(args.This());
-}
-
-Handle<Value> GNPlaylistItem::NewInstance(GroovePlaylistItem *playlist_item) {
- HandleScope scope;
-
- Local<Object> instance = constructor->NewInstance();
-
- GNPlaylistItem *gn_playlist_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(instance);
- gn_playlist_item->playlist_item = playlist_item;
-
- return scope.Close(instance);
-}
-
-Handle<Value> GNPlaylistItem::GetFile(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
- return scope.Close(GNFile::NewInstance(gn_pl_item->playlist_item->file));
-}
-
-Handle<Value> GNPlaylistItem::GetId(Local<String> property, const AccessorInfo &info) {
- HandleScope scope;
- GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
- char buf[64];
- snprintf(buf, sizeof(buf), "%p", gn_pl_item->playlist_item);
- return scope.Close(String::New(buf));
-}
-
-Handle<Value> GNPlaylistItem::GetGain(Local<String> property,
- const AccessorInfo &info)
-{
- HandleScope scope;
- GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
- double gain = gn_pl_item->playlist_item->gain;
- return scope.Close(Number::New(gain));
-}
diff --git a/src/gn_playlist_item.h b/src/gn_playlist_item.h
deleted file mode 100644
index 15645f7..0000000
--- a/src/gn_playlist_item.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef GN_PLAYLIST_ITEM_H
-#define GN_PLAYLIST_ITEM_H
-
-#include <node.h>
-
-#include <groove/groove.h>
-
-class GNPlaylistItem : public node::ObjectWrap {
- public:
- static void Init();
- static v8::Handle<v8::Value> NewInstance(GroovePlaylistItem *playlist_item);
-
- GroovePlaylistItem *playlist_item;
- private:
- GNPlaylistItem();
- ~GNPlaylistItem();
-
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
-
- static v8::Handle<v8::Value> GetFile(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
- static v8::Handle<v8::Value> GetId(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
- static v8::Handle<v8::Value> GetGain(v8::Local<v8::String> property,
- const v8::AccessorInfo &info);
-
-};
-
-#endif
-
-
diff --git a/src/groove.cc b/src/groove.cc
index f4979a8..71f7ee1 100644
--- a/src/groove.cc
+++ b/src/groove.cc
@@ -1,55 +1,62 @@
#include <node.h>
+#include <nan.h>
#include <cstdlib>
-#include "gn_file.h"
-#include "gn_player.h"
-#include "gn_playlist.h"
-#include "gn_playlist_item.h"
-#include "gn_loudness_detector.h"
-#include "gn_fingerprinter.h"
-#include "gn_encoder.h"
+#include "file.h"
+#include "player.h"
+#include "playlist.h"
+#include "playlist_item.h"
+#include "loudness_detector.h"
+#include "fingerprinter.h"
+#include "encoder.h"
using namespace v8;
-using namespace node;
-Handle<Value> SetLogging(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(SetLogging) {
+ Nan::HandleScope scope;
- if (args.Length() < 1 || !args[0]->IsNumber()) {
- ThrowException(Exception::TypeError(String::New("Expected 1 number argument")));
- return scope.Close(Undefined());
+ if (info.Length() < 1 || !info[0]->IsNumber()) {
+ Nan::ThrowTypeError("Expected 1 number argument");
+ return;
}
- groove_set_logging(args[0]->NumberValue());
- return scope.Close(Undefined());
+ groove_set_logging(info[0]->NumberValue());
}
-Handle<Value> GetDevices(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(GetDevices) {
+ Nan::HandleScope scope;
- Local<Array> deviceList = Array::New();
+ Local<Array> deviceList = Nan::New<Array>();
int device_count = groove_device_count();
for (int i = 0; i < device_count; i += 1) {
const char *name = groove_device_name(i);
- deviceList->Set(Number::New(i), String::New(name));
+ deviceList->Set(Nan::New<Number>(i), Nan::New<String>(name).ToLocalChecked());
}
- return scope.Close(deviceList);
+
+ info.GetReturnValue().Set(deviceList);
}
-Handle<Value> GetVersion(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(GetVersion) {
+ Nan::HandleScope scope;
+
+ Local<Object> version = Nan::New<Object>();
+ Nan::Set(version, Nan::New<String>("major").ToLocalChecked(), Nan::New<Number>(groove_version_major()));
+ Nan::Set(version, Nan::New<String>("minor").ToLocalChecked(), Nan::New<Number>(groove_version_minor()));
+ Nan::Set(version, Nan::New<String>("patch").ToLocalChecked(), Nan::New<Number>(groove_version_patch()));
- Local<Object> version = Object::New();
- version->Set(String::NewSymbol("major"), Number::New(groove_version_major()));
- version->Set(String::NewSymbol("minor"), Number::New(groove_version_minor()));
- version->Set(String::NewSymbol("patch"), Number::New(groove_version_patch()));
- return scope.Close(version);
+ info.GetReturnValue().Set(version);
}
template <typename target_t>
static void SetProperty(target_t obj, const char* name, double n) {
- obj->Set(String::NewSymbol(name), Number::New(n));
+ Nan::Set(obj, Nan::New<String>(name).ToLocalChecked(), Nan::New<Number>(n));
+}
+
+template <typename target_t, typename FNPTR>
+static void SetMethod(target_t obj, const char* name, FNPTR fn) {
+ Nan::Set(obj, Nan::New<String>(name).ToLocalChecked(),
+ Nan::GetFunction(Nan::New<FunctionTemplate>(fn)).ToLocalChecked());
}
-void Initialize(Handle<Object> exports) {
+NAN_MODULE_INIT(Initialize) {
groove_init();
atexit(groove_finish);
@@ -61,33 +68,34 @@ void Initialize(Handle<Object> exports) {
GNEncoder::Init();
GNFingerprinter::Init();
- SetProperty(exports, "LOG_QUIET", GROOVE_LOG_QUIET);
- SetProperty(exports, "LOG_ERROR", GROOVE_LOG_ERROR);
- SetProperty(exports, "LOG_WARNING", GROOVE_LOG_WARNING);
- SetProperty(exports, "LOG_INFO", GROOVE_LOG_INFO);
-
- SetProperty(exports, "TAG_MATCH_CASE", GROOVE_TAG_MATCH_CASE);
- SetProperty(exports, "TAG_DONT_OVERWRITE", GROOVE_TAG_DONT_OVERWRITE);
- SetProperty(exports, "TAG_APPEND", GROOVE_TAG_APPEND);
-
- SetProperty(exports, "EVERY_SINK_FULL", GROOVE_EVERY_SINK_FULL);
- SetProperty(exports, "ANY_SINK_FULL", GROOVE_ANY_SINK_FULL);
-
- SetProperty(exports, "_EVENT_NOWPLAYING", GROOVE_EVENT_NOWPLAYING);
- SetProperty(exports, "_EVENT_BUFFERUNDERRUN", GROOVE_EVENT_BUFFERUNDERRUN);
-
- SetMethod(exports, "setLogging", SetLogging);
- SetMethod(exports, "getDevices", GetDevices);
- SetMethod(exports, "getVersion", GetVersion);
- SetMethod(exports, "open", GNFile::Open);
- SetMethod(exports, "createPlayer", GNPlayer::Create);
- SetMethod(exports, "createPlaylist", GNPlaylist::Create);
- SetMethod(exports, "createLoudnessDetector", GNLoudnessDetector::Create);
- SetMethod(exports, "createEncoder", GNEncoder::Create);
- SetMethod(exports, "createFingerprinter", GNFingerprinter::Create);
-
- SetMethod(exports, "encodeFingerprint", GNFingerprinter::Encode);
- SetMethod(exports, "decodeFingerprint", GNFingerprinter::Decode);
+ SetProperty(target, "LOG_QUIET", GROOVE_LOG_QUIET);
+ SetProperty(target, "LOG_ERROR", GROOVE_LOG_ERROR);
+ SetProperty(target, "LOG_WARNING", GROOVE_LOG_WARNING);
+ SetProperty(target, "LOG_INFO", GROOVE_LOG_INFO);
+
+ SetProperty(target, "TAG_MATCH_CASE", GROOVE_TAG_MATCH_CASE);
+ SetProperty(target, "TAG_DONT_OVERWRITE", GROOVE_TAG_DONT_OVERWRITE);
+ SetProperty(target, "TAG_APPEND", GROOVE_TAG_APPEND);
+
+ SetProperty(target, "EVERY_SINK_FULL", GROOVE_EVERY_SINK_FULL);
+ SetProperty(target, "ANY_SINK_FULL", GROOVE_ANY_SINK_FULL);
+
+ SetProperty(target, "_EVENT_NOWPLAYING", GROOVE_EVENT_NOWPLAYING);
+ SetProperty(target, "_EVENT_BUFFERUNDERRUN", GROOVE_EVENT_BUFFERUNDERRUN);
+ SetProperty(target, "_EVENT_DEVICEREOPENED", GROOVE_EVENT_DEVICEREOPENED);
+
+ SetMethod(target, "setLogging", SetLogging);
+ SetMethod(target, "getDevices", GetDevices);
+ SetMethod(target, "getVersion", GetVersion);
+ SetMethod(target, "open", GNFile::Open);
+ SetMethod(target, "createPlayer", GNPlayer::Create);
+ SetMethod(target, "createPlaylist", GNPlaylist::Create);
+ SetMethod(target, "createLoudnessDetector", GNLoudnessDetector::Create);
+ SetMethod(target, "createEncoder", GNEncoder::Create);
+ SetMethod(target, "createFingerprinter", GNFingerprinter::Create);
+
+ SetMethod(target, "encodeFingerprint", GNFingerprinter::Encode);
+ SetMethod(target, "decodeFingerprint", GNFingerprinter::Decode);
}
NODE_MODULE(groove, Initialize)
diff --git a/src/gn_loudness_detector.cc b/src/loudness_detector.cc
similarity index 50%
rename from src/gn_loudness_detector.cc
rename to src/loudness_detector.cc
index 4d5a218..31677b0 100644
--- a/src/gn_loudness_detector.cc
+++ b/src/loudness_detector.cc
@@ -1,7 +1,6 @@
-#include <node.h>
-#include "gn_loudness_detector.h"
-#include "gn_playlist_item.h"
-#include "gn_playlist.h"
+#include "loudness_detector.h"
+#include "playlist_item.h"
+#include "playlist.h"
using namespace v8;
@@ -10,65 +9,56 @@ GNLoudnessDetector::~GNLoudnessDetector() {
groove_loudness_detector_destroy(detector);
};
-Persistent<Function> GNLoudnessDetector::constructor;
-
-template <typename target_t, typename func_t>
-static void AddGetter(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
-}
-
-template <typename target_t, typename func_t>
-static void AddMethod(target_t tpl, const char* name, func_t fn) {
- tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
- FunctionTemplate::New(fn)->GetFunction());
-}
+static Nan::Persistent<v8::Function> constructor;
void GNLoudnessDetector::Init() {
// Prepare constructor template
- Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
- tpl->SetClassName(String::NewSymbol("GrooveLoudnessDetector"));
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GrooveLoudnessDetector").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(2);
+
// Methods
- AddMethod(tpl, "attach", Attach);
- AddMethod(tpl, "detach", Detach);
- AddMethod(tpl, "getInfo", GetInfo);
- AddMethod(tpl, "position", Position);
+ Nan::SetPrototypeMethod(tpl, "attach", Attach);
+ Nan::SetPrototypeMethod(tpl, "detach", Detach);
+ Nan::SetPrototypeMethod(tpl, "getInfo", GetInfo);
+ Nan::SetPrototypeMethod(tpl, "position", Position);
- constructor = Persistent<Function>::New(tpl->GetFunction());
+ constructor.Reset(tpl->GetFunction());
}
-Handle<Value> GNLoudnessDetector::New(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(GNLoudnessDetector::New) {
+ Nan::HandleScope scope;
GNLoudnessDetector *obj = new GNLoudnessDetector();
- obj->Wrap(args.This());
+ obj->Wrap(info.This());
- return scope.Close(args.This());
+ info.GetReturnValue().Set(info.This());
}
Handle<Value> GNLoudnessDetector::NewInstance(GrooveLoudnessDetector *detector) {
- HandleScope scope;
+ Nan::EscapableHandleScope scope;
- Local<Object> instance = constructor->NewInstance();
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(instance);
gn_detector->detector = detector;
- return scope.Close(instance);
+ return scope.Escape(instance);
}
-Handle<Value> GNLoudnessDetector::Create(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(GNLoudnessDetector::Create) {
+ Nan::HandleScope scope;
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
}
GrooveLoudnessDetector *detector = groove_loudness_detector_create();
if (!detector) {
- ThrowException(Exception::Error(String::New("unable to create loudness detector")));
- return scope.Close(Undefined());
+ Nan::ThrowTypeError("unable to create loudness detector");
+ return;
}
// set properties on the instance with default values from
@@ -77,85 +67,93 @@ Handle<Value> GNLoudnessDetector::Create(const Arguments& args) {
GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(instance);
EventContext *context = new EventContext;
gn_detector->event_context = context;
- context->event_cb = Persistent<Function>::New(Local<Function>::Cast(args[0]));
+ context->event_cb = new Nan::Callback(info[0].As<Function>());
context->detector = detector;
+ Nan::Set(instance, Nan::New<String>("infoQueueSize").ToLocalChecked(),
+ Nan::New<Number>(detector->info_queue_size));
+ Nan::Set(instance, Nan::New<String>("disableAlbum").ToLocalChecked(),
+ Nan::New<Boolean>(detector->disable_album));
+ Nan::Set(instance, Nan::New<String>("sinkBufferSize").ToLocalChecked(),
+ Nan::New<Boolean>(detector->sink_buffer_size));
- instance->Set(String::NewSymbol("infoQueueSize"), Number::New(detector->info_queue_size));
- instance->Set(String::NewSymbol("sinkBufferSize"), Number::New(detector->sink_buffer_size));
- instance->Set(String::NewSymbol("disableAlbum"), Boolean::New(detector->disable_album));
-
- return scope.Close(instance);
+ info.GetReturnValue().Set(instance);
}
-Handle<Value> GNLoudnessDetector::Position(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(GNLoudnessDetector::Position) {
+ Nan::HandleScope scope;
- GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(args.This());
+ GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(info.This());
GrooveLoudnessDetector *detector = gn_detector->detector;
GroovePlaylistItem *item;
double pos;
groove_loudness_detector_position(detector, &item, &pos);
- Local<Object> obj = Object::New();
- obj->Set(String::NewSymbol("pos"), Number::New(pos));
+ Local<Object> obj = Nan::New<Object>();
+ Nan::Set(obj, Nan::New<String>("pos").ToLocalChecked(), Nan::New<Number>(pos));
if (item) {
- obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), GNPlaylistItem::NewInstance(item));
} else {
- obj->Set(String::NewSymbol("item"), Null());
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
}
- return scope.Close(obj);
+ info.GetReturnValue().Set(obj);
}
-Handle<Value> GNLoudnessDetector::GetInfo(const Arguments& args) {
- HandleScope scope;
- GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(args.This());
+NAN_METHOD(GNLoudnessDetector::GetInfo) {
+ Nan::HandleScope scope;
+
+ GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(info.This());
GrooveLoudnessDetector *detector = gn_detector->detector;
- GrooveLoudnessDetectorInfo info;
- if (groove_loudness_detector_info_get(detector, &info, 0) == 1) {
- Local<Object> object = Object::New();
+ GrooveLoudnessDetectorInfo loudness_info;
+ if (groove_loudness_detector_info_get(detector, &loudness_info, 0) == 1) {
+ Local<Object> object = Nan::New<Object>();
- object->Set(String::NewSymbol("loudness"), Number::New(info.loudness));
- object->Set(String::NewSymbol("peak"), Number::New(info.peak));
- object->Set(String::NewSymbol("duration"), Number::New(info.duration));
+ Nan::Set(object, Nan::New<String>("loudness").ToLocalChecked(), Nan::New<Number>(loudness_info.loudness));
+ Nan::Set(object, Nan::New<String>("peak").ToLocalChecked(), Nan::New<Number>(loudness_info.peak));
+ Nan::Set(object, Nan::New<String>("duration").ToLocalChecked(), Nan::New<Number>(loudness_info.duration));
- if (info.item) {
- object->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(info.item));
+ if (loudness_info.item) {
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(), GNPlaylistItem::NewInstance(loudness_info.item));
} else {
- object->Set(String::NewSymbol("item"), Null());
+ Nan::Set(object, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
}
- return scope.Close(object);
+ info.GetReturnValue().Set(object);
} else {
- return scope.Close(Null());
+ info.GetReturnValue().Set(Nan::Null());
}
}
struct AttachReq {
uv_work_t req;
- Persistent<Function> callback;
+ Nan::Callback *callback;
GrooveLoudnessDetector *detector;
GroovePlaylist *playlist;
int errcode;
- Persistent<Object> instance;
+ Nan::Persistent<Object> instance;
GNLoudnessDetector::EventContext *event_context;
};
-static void EventAsyncCb(uv_async_t *handle, int status) {
- HandleScope scope;
+static void EventAsyncCb(uv_async_t *handle
+#if UV_VERSION_MAJOR == 0
+ , int status
+#endif
+ )
+{
+ Nan::HandleScope scope;
GNLoudnessDetector::EventContext *context = reinterpret_cast<GNLoudnessDetector::EventContext *>(handle->data);
// call callback signaling that there is info ready
const unsigned argc = 1;
- Handle<Value> argv[argc];
- argv[0] = Null();
+ Local<Value> argv[argc];
+ argv[0] = Nan::Null();
TryCatch try_catch;
- context->event_cb->Call(Context::GetCurrent()->Global(), argc, argv);
+ context->event_cb->Call(argc, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
@@ -193,20 +191,23 @@ static void AttachAsync(uv_work_t *req) {
}
static void AttachAfter(uv_work_t *req) {
- HandleScope scope;
+ Nan::HandleScope scope;
+
AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
const unsigned argc = 1;
- Handle<Value> argv[argc];
+ Local<Value> argv[argc];
if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("loudness detector attach failed"));
+ argv[0] = Exception::Error(Nan::New<String>("loudness detector attach failed").ToLocalChecked());
} else {
- argv[0] = Null();
+ argv[0] = Nan::Null();
}
TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
+ r->callback->Call(argc, argv);
+ r->instance.Reset();
+ delete r->callback;
delete r;
if (try_catch.HasCaught()) {
@@ -214,55 +215,58 @@ static void AttachAfter(uv_work_t *req) {
}
}
-Handle<Value> GNLoudnessDetector::Attach(const Arguments& args) {
- HandleScope scope;
+NAN_METHOD(GNLoudnessDetector::Attach) {
+ Nan::HandleScope scope;
- GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(args.This());
+ GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(info.This());
- if (args.Length() < 1 || !args[0]->IsObject()) {
- ThrowException(Exception::TypeError(String::New("Expected object arg[0]")));
- return scope.Close(Undefined());
+ if (info.Length() < 1 || !info[0]->IsObject()) {
+ Nan::ThrowTypeError("Expected object arg[0]");
+ return;
}
- if (args.Length() < 2 || !args[1]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
- return scope.Close(Undefined());
+ if (info.Length() < 2 || !info[1]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[1]");
+ return;
}
- Local<Object> instance = args.This();
+ Local<Object> instance = info.This();
- GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args[0]->ToObject());
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info[0]->ToObject());
AttachReq *request = new AttachReq;
request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
- request->instance = Persistent<Object>::New(args.This());
+ request->callback = new Nan::Callback(info[1].As<Function>());
+
+ request->instance.Reset(info.This());
+
request->playlist = gn_playlist->playlist;
GrooveLoudnessDetector *detector = gn_detector->detector;
request->detector = detector;
request->event_context = gn_detector->event_context;
// copy the properties from our instance to the player
- detector->info_queue_size = (int)instance->Get(String::NewSymbol("infoQueueSize"))->NumberValue();
- detector->sink_buffer_size = (int)instance->Get(String::NewSymbol("sinkBufferSize"))->NumberValue();
- detector->disable_album = (int)instance->Get(String::NewSymbol("disableAlbum"))->BooleanValue();
+ detector->info_queue_size = (int)instance->Get(Nan::New<String>("infoQueueSize").ToLocalChecked())->NumberValue();
+ detector->sink_buffer_size = (int)instance->Get(Nan::New<String>("sinkBufferSize").ToLocalChecked())->BooleanValue();
+ detector->disable_album = (int)instance->Get(Nan::New<String>("disableAlbum").ToLocalChecked())->BooleanValue();
uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
(uv_after_work_cb)AttachAfter);
- return scope.Close(Undefined());
+ return;
}
struct DetachReq {
uv_work_t req;
GrooveLoudnessDetector *detector;
- Persistent<Function> callback;
+ Nan::Callback *callback;
int errcode;
GNLoudnessDetector::EventContext *event_context;
};
static void DetachAsyncFree(uv_handle_t *handle) {
GNLoudnessDetector::EventContext *context = reinterpret_cast<GNLoudnessDetector::EventContext *>(handle->data);
+ delete context->event_cb;
delete context;
}
@@ -277,19 +281,21 @@ static void DetachAsync(uv_work_t *req) {
}
static void DetachAfter(uv_work_t *req) {
- HandleScope scope;
+ Nan::HandleScope scope;
+
DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
const unsigned argc = 1;
- Handle<Value> argv[argc];
+ Local<Value> argv[argc];
if (r->errcode < 0) {
- argv[0] = Exception::Error(String::New("loudness detector detach failed"));
+ argv[0] = Exception::Error(Nan::New<String>("loudness detector detach failed").ToLocalChecked());
} else {
- argv[0] = Null();
+ argv[0] = Nan::Null();
}
TryCatch try_catch;
- r->callback->Call(Context::GetCurrent()->Global(), argc, argv);
+ r->callback->Call(argc, argv);
+ delete r->callback;
delete r;
if (try_catch.HasCaught()) {
@@ -297,24 +303,24 @@ static void DetachAfter(uv_work_t *req) {
}
}
-Handle<Value> GNLoudnessDetector::Detach(const Arguments& args) {
- HandleScope scope;
- GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(args.This());
+NAN_METHOD(GNLoudnessDetector::Detach) {
+ Nan::HandleScope scope;
+ GNLoudnessDetector *gn_detector = node::ObjectWrap::Unwrap<GNLoudnessDetector>(info.This());
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
- return scope.Close(Undefined());
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
}
DetachReq *request = new DetachReq;
request->req.data = request;
- request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
+ request->callback = new Nan::Callback(info[0].As<Function>());
request->detector = gn_detector->detector;
request->event_context = gn_detector->event_context;
uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
(uv_after_work_cb)DetachAfter);
- return scope.Close(Undefined());
+ return;
}
diff --git a/src/gn_loudness_detector.h b/src/loudness_detector.h
similarity index 55%
rename from src/gn_loudness_detector.h
rename to src/loudness_detector.h
index 4f89d50..87af0c3 100644
--- a/src/gn_loudness_detector.h
+++ b/src/loudness_detector.h
@@ -2,7 +2,7 @@
#define GN_LOUDNESS_DETECTOR_H
#include <node.h>
-
+#include <nan.h>
#include <grooveloudness/loudness.h>
class GNLoudnessDetector : public node::ObjectWrap {
@@ -10,8 +10,7 @@ class GNLoudnessDetector : public node::ObjectWrap {
static void Init();
static v8::Handle<v8::Value> NewInstance(GrooveLoudnessDetector *detector);
- static v8::Handle<v8::Value> Create(const v8::Arguments& args);
-
+ static NAN_METHOD(Create);
struct EventContext {
uv_thread_t event_thread;
@@ -19,7 +18,7 @@ class GNLoudnessDetector : public node::ObjectWrap {
uv_cond_t cond;
uv_mutex_t mutex;
GrooveLoudnessDetector *detector;
- v8::Persistent<v8::Function> event_cb;
+ Nan::Callback *event_cb;
};
EventContext *event_context;
@@ -29,13 +28,12 @@ class GNLoudnessDetector : public node::ObjectWrap {
GNLoudnessDetector();
~GNLoudnessDetector();
- static v8::Persistent<v8::Function> constructor;
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
+ static NAN_METHOD(New);
- static v8::Handle<v8::Value> Attach(const v8::Arguments& args);
- static v8::Handle<v8::Value> Detach(const v8::Arguments& args);
- static v8::Handle<v8::Value> GetInfo(const v8::Arguments& args);
- static v8::Handle<v8::Value> Position(const v8::Arguments& args);
+ static NAN_METHOD(Attach);
+ static NAN_METHOD(Detach);
+ static NAN_METHOD(GetInfo);
+ static NAN_METHOD(Position);
};
#endif
diff --git a/src/player.cc b/src/player.cc
new file mode 100644
index 0000000..e1aff3a
--- /dev/null
+++ b/src/player.cc
@@ -0,0 +1,364 @@
+#include <node.h>
+#include "player.h"
+#include "playlist.h"
+#include "playlist_item.h"
+
+using namespace v8;
+
+GNPlayer::GNPlayer() {};
+GNPlayer::~GNPlayer() {
+ groove_player_destroy(player);
+ delete event_context->event_cb;
+ delete event_context;
+};
+
+static Nan::Persistent<v8::Function> constructor;
+
+void GNPlayer::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GroovePlayer").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(2);
+ Local<ObjectTemplate> proto = tpl->PrototypeTemplate();
+
+ // Fields
+ Nan::SetAccessor(proto, Nan::New<String>("id").ToLocalChecked(), GetId);
+ Nan::SetAccessor(proto, Nan::New<String>("playlist").ToLocalChecked(), GetPlaylist);
+
+ // Methods
+ Nan::SetPrototypeMethod(tpl, "attach", Attach);
+ Nan::SetPrototypeMethod(tpl, "detach", Detach);
+ Nan::SetPrototypeMethod(tpl, "position", Position);
+
+ constructor.Reset(tpl->GetFunction());
+}
+
+NAN_METHOD(GNPlayer::New) {
+ Nan::HandleScope scope;
+
+ GNPlayer *obj = new GNPlayer();
+ obj->Wrap(info.This());
+
+ info.GetReturnValue().Set(info.This());
+}
+
+Handle<Value> GNPlayer::NewInstance(GroovePlayer *player) {
+ Nan::EscapableHandleScope scope;
+
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
+
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
+ gn_player->player = player;
+
+ return scope.Escape(instance);
+}
+
+NAN_GETTER(GNPlayer::GetId) {
+ Nan::HandleScope scope;
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%p", gn_player->player);
+ info.GetReturnValue().Set(Nan::New<String>(buf).ToLocalChecked());
+}
+
+NAN_GETTER(GNPlayer::GetPlaylist) {
+ Nan::HandleScope scope;
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
+ GroovePlaylist *playlist = gn_player->player->playlist;
+ if (playlist) {
+ Local<Value> tmp = GNPlaylist::NewInstance(playlist);
+ info.GetReturnValue().Set(tmp);
+ } else {
+ info.GetReturnValue().Set(Nan::Null());
+ }
+}
+
+NAN_METHOD(GNPlayer::Position) {
+ Nan::HandleScope scope;
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
+ GroovePlaylistItem *item;
+ double pos;
+ groove_player_position(gn_player->player, &item, &pos);
+ Local<Object> obj = Nan::New<Object>();
+ Nan::Set(obj, Nan::New<String>("pos").ToLocalChecked(), Nan::New<Number>(pos));
+ if (item) {
+ Local<Value> tmp = GNPlaylistItem::NewInstance(item);
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), tmp);
+ } else {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ }
+ info.GetReturnValue().Set(obj);
+}
+
+struct AttachReq {
+ uv_work_t req;
+ Nan::Callback *callback;
+ GroovePlayer *player;
+ GroovePlaylist *playlist;
+ int errcode;
+ Nan::Persistent<Object> instance;
+ int device_index;
+ GNPlayer::EventContext *event_context;
+};
+
+static void EventAsyncCb(uv_async_t *handle
+#if UV_VERSION_MAJOR == 0
+ , int status
+#endif
+ )
+{
+ Nan::HandleScope scope;
+
+ GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(handle->data);
+
+ // flush events
+ GroovePlayerEvent event;
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ while (groove_player_event_get(context->player, &event, 0) > 0) {
+ argv[0] = Nan::New<Number>(event.type);
+
+ TryCatch try_catch;
+ context->event_cb->Call(argc, argv);
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+ }
+
+ uv_mutex_lock(&context->mutex);
+ uv_cond_signal(&context->cond);
+ uv_mutex_unlock(&context->mutex);
+}
+
+static void EventThreadEntry(void *arg) {
+ GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(arg);
+ while (groove_player_event_peek(context->player, 1) > 0) {
+ uv_mutex_lock(&context->mutex);
+ uv_async_send(&context->event_async);
+ uv_cond_wait(&context->cond, &context->mutex);
+ uv_mutex_unlock(&context->mutex);
+ }
+}
+
+static void AttachAsync(uv_work_t *req) {
+ AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
+
+ r->player->device_index = r->device_index;
+ r->errcode = groove_player_attach(r->player, r->playlist);
+
+ GNPlayer::EventContext *context = r->event_context;
+
+ uv_cond_init(&context->cond);
+ uv_mutex_init(&context->mutex);
+
+ uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
+ context->event_async.data = context;
+
+ uv_thread_create(&context->event_thread, EventThreadEntry, context);
+}
+
+static void AttachAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+ AttachReq *r = reinterpret_cast<AttachReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->errcode < 0) {
+ argv[0] = Exception::Error(Nan::New<String>("player attach failed").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+
+ Local<Object> actualAudioFormat = Nan::New<Object>();
+ actualAudioFormat->Set(Nan::New<String>("sampleRate").ToLocalChecked(),
+ Nan::New<Number>(r->player->actual_audio_format.sample_rate));
+ actualAudioFormat->Set(Nan::New<String>("channelLayout").ToLocalChecked(),
+ Nan::New<Number>(r->player->actual_audio_format.channel_layout));
+ actualAudioFormat->Set(Nan::New<String>("sampleFormat").ToLocalChecked(),
+ Nan::New<Number>(r->player->actual_audio_format.sample_fmt));
+
+ Local<Object> o = Nan::New(r->instance);
+ Nan::Set(o, Nan::New<String>("actualAudioFormat").ToLocalChecked(), actualAudioFormat);
+ r->instance.Reset(o);
+ }
+
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ r->instance.Reset();
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNPlayer::Create) {
+ Nan::HandleScope scope;
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ GroovePlayer *player = groove_player_create();
+ Local<Object> instance = NewInstance(player)->ToObject();
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
+ EventContext *context = new EventContext;
+ gn_player->event_context = context;
+ context->event_cb = new Nan::Callback(info[0].As<Function>());
+ context->player = player;
+
+ // set properties on the instance with default values from
+ // GroovePlayer struct
+ Local<Object> targetAudioFormat = Nan::New<Object>();
+ Nan::Set(targetAudioFormat, Nan::New<String>("sampleRate").ToLocalChecked(),
+ Nan::New<Number>(player->target_audio_format.sample_rate));
+ Nan::Set(targetAudioFormat, Nan::New<String>("channelLayout").ToLocalChecked(),
+ Nan::New<Number>(player->target_audio_format.channel_layout));
+ Nan::Set(targetAudioFormat, Nan::New<String>("sampleFormat").ToLocalChecked(),
+ Nan::New<Number>(player->target_audio_format.sample_fmt));
+
+ instance->Set(Nan::New<String>("deviceIndex").ToLocalChecked(), Nan::Null());
+ instance->Set(Nan::New<String>("actualAudioFormat").ToLocalChecked(), Nan::Null());
+ instance->Set(Nan::New<String>("targetAudioFormat").ToLocalChecked(), targetAudioFormat);
+ instance->Set(Nan::New<String>("deviceBufferSize").ToLocalChecked(),
+ Nan::New<Number>(player->device_buffer_size));
+ instance->Set(Nan::New<String>("sinkBufferSize").ToLocalChecked(),
+ Nan::New<Number>(player->sink_buffer_size));
+
+ info.GetReturnValue().Set(instance);
+}
+
+NAN_METHOD(GNPlayer::Attach) {
+ Nan::HandleScope scope;
+
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsObject()) {
+ Nan::ThrowTypeError("Expected object arg[0]");
+ return;
+ }
+ if (info.Length() < 2 || !info[1]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[1]");
+ return;
+ }
+
+ Local<Object> instance = info.This();
+ Local<Value> targetAudioFormatValue = instance->Get(Nan::New<String>("targetAudioFormat").ToLocalChecked());
+ if (!targetAudioFormatValue->IsObject()) {
+ Nan::ThrowTypeError("Expected targetAudioFormat to be an object");
+ return;
+ }
+
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info[0]->ToObject());
+
+ AttachReq *request = new AttachReq;
+
+ request->req.data = request;
+ request->callback = new Nan::Callback(info[1].As<Function>());
+
+ request->instance.Reset(info.This());
+
+ request->playlist = gn_playlist->playlist;
+ GroovePlayer *player = gn_player->player;
+ request->player = player;
+ request->event_context = gn_player->event_context;
+
+ // copy the properties from our instance to the player
+ Local<Value> deviceIndex = instance->Get(Nan::New<String>("deviceIndex").ToLocalChecked());
+
+ Local<Value> useExactAudioFormat = instance->Get(Nan::New<String>("useExactAudioFormat").ToLocalChecked());
+ player->use_exact_audio_format = useExactAudioFormat->BooleanValue();
+
+ if (deviceIndex->IsNull() || deviceIndex->IsUndefined()) {
+ request->device_index = -1;
+ } else {
+ request->device_index = (int) deviceIndex->NumberValue();
+ }
+ Local<Object> targetAudioFormat = targetAudioFormatValue->ToObject();
+ Local<Value> sampleRate = targetAudioFormat->Get(Nan::New<String>("sampleRate").ToLocalChecked());
+ double sample_rate = sampleRate->NumberValue();
+ double channel_layout = targetAudioFormat->Get(Nan::New<String>("channelLayout").ToLocalChecked())->NumberValue();
+ double sample_fmt = targetAudioFormat->Get(Nan::New<String>("sampleFormat").ToLocalChecked())->NumberValue();
+ player->target_audio_format.sample_rate = (int)sample_rate;
+ player->target_audio_format.channel_layout = (int)channel_layout;
+ player->target_audio_format.sample_fmt = (enum GrooveSampleFormat)(int)sample_fmt;
+
+ double device_buffer_size = instance->Get(Nan::New<String>("deviceBufferSize").ToLocalChecked())->NumberValue();
+ player->device_buffer_size = (int)device_buffer_size;
+
+ double sink_buffer_size = instance->Get(Nan::New<String>("sinkBufferSize").ToLocalChecked())->NumberValue();
+ player->sink_buffer_size = (int)sink_buffer_size;
+
+ uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
+ (uv_after_work_cb)AttachAfter);
+}
+
+struct DetachReq {
+ uv_work_t req;
+ GroovePlayer *player;
+ Nan::Callback *callback;
+ int errcode;
+ GNPlayer::EventContext *event_context;
+};
+
+static void DetachAsyncFree(uv_handle_t *handle) {
+}
+
+static void DetachAsync(uv_work_t *req) {
+ DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
+ r->errcode = groove_player_detach(r->player);
+ uv_cond_signal(&r->event_context->cond);
+ uv_thread_join(&r->event_context->event_thread);
+ uv_cond_destroy(&r->event_context->cond);
+ uv_mutex_destroy(&r->event_context->mutex);
+ uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
+}
+
+static void DetachAfter(uv_work_t *req) {
+ Nan::HandleScope scope;
+ DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
+
+ const unsigned argc = 1;
+ Local<Value> argv[argc];
+ if (r->errcode < 0) {
+ argv[0] = Exception::Error(Nan::New<String>("player detach failed").ToLocalChecked());
+ } else {
+ argv[0] = Nan::Null();
+ }
+ TryCatch try_catch;
+ r->callback->Call(argc, argv);
+
+ delete r->callback;
+ delete r;
+
+ if (try_catch.HasCaught()) {
+ node::FatalException(try_catch);
+ }
+}
+
+NAN_METHOD(GNPlayer::Detach) {
+ Nan::HandleScope scope;
+ GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
+
+ if (info.Length() < 1 || !info[0]->IsFunction()) {
+ Nan::ThrowTypeError("Expected function arg[0]");
+ return;
+ }
+
+ DetachReq *request = new DetachReq;
+
+ request->req.data = request;
+ request->callback = new Nan::Callback(info[0].As<Function>());
+ request->player = gn_player->player;
+ request->event_context = gn_player->event_context;
+
+ uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
+ (uv_after_work_cb)DetachAfter);
+
+ return;
+}
diff --git a/src/player.h b/src/player.h
new file mode 100644
index 0000000..f2f8909
--- /dev/null
+++ b/src/player.h
@@ -0,0 +1,43 @@
+#ifndef GN_PLAYER_H
+#define GN_PLAYER_H
+
+#include <node.h>
+#include <nan.h>
+#include <grooveplayer/player.h>
+
+class GNPlayer : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(GroovePlayer *player);
+
+ static NAN_METHOD(Create);
+
+ struct EventContext {
+ uv_thread_t event_thread;
+ uv_async_t event_async;
+ uv_cond_t cond;
+ uv_mutex_t mutex;
+ GroovePlayer *player;
+ Nan::Callback *event_cb;
+ };
+
+
+ GroovePlayer *player;
+ EventContext *event_context;
+
+ private:
+ GNPlayer();
+ ~GNPlayer();
+
+ static NAN_METHOD(New);
+
+ static NAN_GETTER(GetId);
+ static NAN_GETTER(GetPlaylist);
+
+ static NAN_METHOD(Attach);
+ static NAN_METHOD(Detach);
+ static NAN_METHOD(Position);
+};
+
+#endif
+
diff --git a/src/playlist.cc b/src/playlist.cc
new file mode 100644
index 0000000..b727d8d
--- /dev/null
+++ b/src/playlist.cc
@@ -0,0 +1,232 @@
+#include <node.h>
+#include "playlist.h"
+#include "playlist_item.h"
+#include "file.h"
+
+using namespace v8;
+
+GNPlaylist::GNPlaylist() {
+};
+GNPlaylist::~GNPlaylist() {
+ // TODO move this somewhere else because we create multiple objects with
+ // the same playlist pointer in player.playlist or encoder.playlist
+ // for example
+ groove_playlist_destroy(playlist);
+};
+
+static Nan::Persistent<v8::Function> constructor;
+
+void GNPlaylist::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GroovePlaylist").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+ Local<ObjectTemplate> proto = tpl->PrototypeTemplate();
+
+ // Fields
+ Nan::SetAccessor(proto, Nan::New<String>("id").ToLocalChecked(), GetId);
+ Nan::SetAccessor(proto, Nan::New<String>("gain").ToLocalChecked(), GetGain);
+
+ // Methods
+ Nan::SetPrototypeMethod(tpl, "play", Play);
+ Nan::SetPrototypeMethod(tpl, "items", Playlist);
+ Nan::SetPrototypeMethod(tpl, "pause", Pause);
+ Nan::SetPrototypeMethod(tpl, "seek", Seek);
+ Nan::SetPrototypeMethod(tpl, "insert", Insert);
+ Nan::SetPrototypeMethod(tpl, "remove", Remove);
+ Nan::SetPrototypeMethod(tpl, "position", DecodePosition);
+ Nan::SetPrototypeMethod(tpl, "playing", Playing);
+ Nan::SetPrototypeMethod(tpl, "clear", Clear);
+ Nan::SetPrototypeMethod(tpl, "count", Count);
+ Nan::SetPrototypeMethod(tpl, "setItemGain", SetItemGain);
+ Nan::SetPrototypeMethod(tpl, "setItemPeak", SetItemPeak);
+ Nan::SetPrototypeMethod(tpl, "setGain", SetGain);
+ Nan::SetPrototypeMethod(tpl, "setFillMode", SetFillMode);
+
+ constructor.Reset(tpl->GetFunction());
+}
+
+NAN_METHOD(GNPlaylist::New) {
+ Nan::HandleScope scope;
+ assert(info.IsConstructCall());
+
+ GNPlaylist *obj = new GNPlaylist();
+ obj->Wrap(info.This());
+
+ info.GetReturnValue().Set(info.This());
+}
+
+Handle<Value> GNPlaylist::NewInstance(GroovePlaylist *playlist) {
+ Nan::EscapableHandleScope scope;
+
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
+
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(instance);
+ gn_playlist->playlist = playlist;
+
+ return scope.Escape(instance);
+}
+
+NAN_GETTER(GNPlaylist::GetId) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%p", gn_playlist->playlist);
+ info.GetReturnValue().Set(Nan::New<String>(buf).ToLocalChecked());
+}
+
+NAN_GETTER(GNPlaylist::GetGain) {
+ Nan::HandleScope();
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ info.GetReturnValue().Set(Nan::New<Number>(gn_playlist->playlist->gain));
+}
+
+NAN_METHOD(GNPlaylist::Play) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ groove_playlist_play(gn_playlist->playlist);
+ return;
+}
+
+NAN_METHOD(GNPlaylist::Playlist) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+
+ Local<Array> playlist = Nan::New<Array>();
+
+ GroovePlaylistItem *item = gn_playlist->playlist->head;
+ int i = 0;
+ while (item) {
+ Nan::Set(playlist, Nan::New<Number>(i), GNPlaylistItem::NewInstance(item));
+ item = item->next;
+ i += 1;
+ }
+
+ info.GetReturnValue().Set(playlist);
+}
+
+NAN_METHOD(GNPlaylist::Pause) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ groove_playlist_pause(gn_playlist->playlist);
+ return;
+}
+
+NAN_METHOD(GNPlaylist::Seek) {
+ Nan::HandleScope scope;
+
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ GNPlaylistItem *gn_playlist_item =
+ node::ObjectWrap::Unwrap<GNPlaylistItem>(info[0]->ToObject());
+
+ double pos = info[1]->NumberValue();
+ groove_playlist_seek(gn_playlist->playlist, gn_playlist_item->playlist_item, pos);
+
+ return;
+}
+
+NAN_METHOD(GNPlaylist::Insert) {
+ Nan::HandleScope scope;
+
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ GNFile *gn_file = node::ObjectWrap::Unwrap<GNFile>(info[0]->ToObject());
+ double gain = 1.0;
+ double peak = 1.0;
+ if (!info[1]->IsNull() && !info[1]->IsUndefined()) {
+ gain = info[1]->NumberValue();
+ }
+ if (!info[2]->IsNull() && !info[2]->IsUndefined()) {
+ peak = info[2]->NumberValue();
+ }
+ GroovePlaylistItem *item = NULL;
+ if (!info[3]->IsNull() && !info[3]->IsUndefined()) {
+ GNPlaylistItem *gn_pl_item =
+ node::ObjectWrap::Unwrap<GNPlaylistItem>(info[3]->ToObject());
+ item = gn_pl_item->playlist_item;
+ }
+ GroovePlaylistItem *result = groove_playlist_insert(gn_playlist->playlist,
+ gn_file->file, gain, peak, item);
+
+ Local<Value> tmp = GNPlaylistItem::NewInstance(result);
+ info.GetReturnValue().Set(tmp);
+}
+
+NAN_METHOD(GNPlaylist::Remove) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info[0]->ToObject());
+ groove_playlist_remove(gn_playlist->playlist, gn_pl_item->playlist_item);
+ return;
+}
+
+NAN_METHOD(GNPlaylist::DecodePosition) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ GroovePlaylistItem *item;
+ double pos = -1.0;
+ groove_playlist_position(gn_playlist->playlist, &item, &pos);
+ Local<Object> obj = Nan::New<Object>();
+ Nan::Set(obj, Nan::New<String>("pos").ToLocalChecked(), Nan::New<Number>(pos));
+ if (item) {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), GNPlaylistItem::NewInstance(item));
+ } else {
+ Nan::Set(obj, Nan::New<String>("item").ToLocalChecked(), Nan::Null());
+ }
+ info.GetReturnValue().Set(obj);
+}
+
+NAN_METHOD(GNPlaylist::Playing) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ int playing = groove_playlist_playing(gn_playlist->playlist);
+ info.GetReturnValue().Set(Nan::New<Boolean>(playing));
+}
+
+NAN_METHOD(GNPlaylist::Clear) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ groove_playlist_clear(gn_playlist->playlist);
+}
+
+NAN_METHOD(GNPlaylist::Count) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ int count = groove_playlist_count(gn_playlist->playlist);
+ info.GetReturnValue().Set(Nan::New<Number>(count));
+}
+
+NAN_METHOD(GNPlaylist::SetItemGain) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info[0]->ToObject());
+ double gain = info[1]->NumberValue();
+ groove_playlist_set_item_gain(gn_playlist->playlist, gn_pl_item->playlist_item, gain);
+}
+
+NAN_METHOD(GNPlaylist::SetItemPeak) {
+ Nan::HandleScope();
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info[0]->ToObject());
+ double peak = info[1]->NumberValue();
+ groove_playlist_set_item_peak(gn_playlist->playlist, gn_pl_item->playlist_item, peak);
+}
+
+NAN_METHOD(GNPlaylist::SetGain) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ groove_playlist_set_gain(gn_playlist->playlist, info[0]->NumberValue());
+}
+
+NAN_METHOD(GNPlaylist::SetFillMode) {
+ Nan::HandleScope scope;
+ GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(info.This());
+ groove_playlist_set_fill_mode(gn_playlist->playlist, info[0]->NumberValue());
+}
+
+NAN_METHOD(GNPlaylist::Create) {
+ Nan::HandleScope scope;
+ GroovePlaylist *playlist = groove_playlist_create();
+ Local<Value> tmp = GNPlaylist::NewInstance(playlist);
+ info.GetReturnValue().Set(tmp);
+}
diff --git a/src/playlist.h b/src/playlist.h
new file mode 100644
index 0000000..8a287e9
--- /dev/null
+++ b/src/playlist.h
@@ -0,0 +1,44 @@
+#ifndef GN_PLAYLIST_H
+#define GN_PLAYLIST_H
+
+#include <node.h>
+#include <nan.h>
+#include <groove/groove.h>
+
+class GNPlaylist : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(GroovePlaylist *playlist);
+
+ static NAN_METHOD(Create);
+
+ GroovePlaylist *playlist;
+
+
+ private:
+ GNPlaylist();
+ ~GNPlaylist();
+
+ static NAN_METHOD(New);
+
+ static NAN_GETTER(GetId);
+ static NAN_GETTER(GetGain);
+
+ static NAN_METHOD(Playlist);
+ static NAN_METHOD(Play);
+ static NAN_METHOD(Pause);
+ static NAN_METHOD(Seek);
+ static NAN_METHOD(Insert);
+ static NAN_METHOD(Remove);
+ static NAN_METHOD(Position);
+ static NAN_METHOD(DecodePosition);
+ static NAN_METHOD(Playing);
+ static NAN_METHOD(Clear);
+ static NAN_METHOD(Count);
+ static NAN_METHOD(SetItemGain);
+ static NAN_METHOD(SetItemPeak);
+ static NAN_METHOD(SetGain);
+ static NAN_METHOD(SetFillMode);
+};
+
+#endif
diff --git a/src/playlist_item.cc b/src/playlist_item.cc
new file mode 100644
index 0000000..3a3cae6
--- /dev/null
+++ b/src/playlist_item.cc
@@ -0,0 +1,71 @@
+#include "playlist_item.h"
+#include "file.h"
+
+using namespace v8;
+
+GNPlaylistItem::GNPlaylistItem() { };
+GNPlaylistItem::~GNPlaylistItem() { };
+
+static Nan::Persistent<v8::Function> constructor;
+
+void GNPlaylistItem::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+ tpl->SetClassName(Nan::New<String>("GroovePlaylistItem").ToLocalChecked());
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+ Local<ObjectTemplate> proto = tpl->PrototypeTemplate();
+
+ // Fields
+ Nan::SetAccessor(proto, Nan::New<String>("file").ToLocalChecked(), GetFile);
+ Nan::SetAccessor(proto, Nan::New<String>("id").ToLocalChecked(), GetId);
+ Nan::SetAccessor(proto, Nan::New<String>("gain").ToLocalChecked(), GetGain);
+ Nan::SetAccessor(proto, Nan::New<String>("peak").ToLocalChecked(), GetPeak);
+
+ constructor.Reset(tpl->GetFunction());
+}
+
+NAN_METHOD(GNPlaylistItem::New) {
+ Nan::HandleScope scope;
+
+ GNPlaylistItem *obj = new GNPlaylistItem();
+ obj->Wrap(info.This());
+
+ info.GetReturnValue().Set(info.This());
+}
+
+Handle<Value> GNPlaylistItem::NewInstance(GroovePlaylistItem *playlist_item) {
+ Nan::EscapableHandleScope scope;
+
+ Local<Function> cons = Nan::New(constructor);
+ Local<Object> instance = cons->NewInstance();
+
+ GNPlaylistItem *gn_playlist_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(instance);
+ gn_playlist_item->playlist_item = playlist_item;
+
+ return scope.Escape(instance);
+}
+
+NAN_GETTER(GNPlaylistItem::GetFile) {
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
+ Local<Value> tmp = GNFile::NewInstance(gn_pl_item->playlist_item->file);
+ info.GetReturnValue().Set(tmp);
+}
+
+NAN_GETTER(GNPlaylistItem::GetId) {
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%p", gn_pl_item->playlist_item);
+ info.GetReturnValue().Set(Nan::New<String>(buf).ToLocalChecked());
+}
+
+NAN_GETTER(GNPlaylistItem::GetGain) {
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
+ double gain = gn_pl_item->playlist_item->gain;
+ info.GetReturnValue().Set(Nan::New<Number>(gain));
+}
+
+NAN_GETTER(GNPlaylistItem::GetPeak) {
+ GNPlaylistItem *gn_pl_item = node::ObjectWrap::Unwrap<GNPlaylistItem>(info.This());
+ double peak = gn_pl_item->playlist_item->peak;
+ info.GetReturnValue().Set(Nan::New<Number>(peak));
+}
diff --git a/src/playlist_item.h b/src/playlist_item.h
new file mode 100644
index 0000000..4cba511
--- /dev/null
+++ b/src/playlist_item.h
@@ -0,0 +1,28 @@
+#ifndef GN_PLAYLIST_ITEM_H
+#define GN_PLAYLIST_ITEM_H
+
+#include <node.h>
+#include <nan.h>
+#include <groove/groove.h>
+
+class GNPlaylistItem : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(GroovePlaylistItem *playlist_item);
+
+ GroovePlaylistItem *playlist_item;
+ private:
+ GNPlaylistItem();
+ ~GNPlaylistItem();
+
+ static NAN_METHOD(New);
+
+ static NAN_GETTER(GetFile);
+ static NAN_GETTER(GetId);
+ static NAN_GETTER(GetGain);
+ static NAN_GETTER(GetPeak);
+};
+
+#endif
+
+
diff --git a/test/test.js b/test/test.js
index 67846a8..e06e509 100644
--- a/test/test.js
+++ b/test/test.js
@@ -55,7 +55,7 @@ it("update metadata", function(done) {
file.setMetadata('foo new key', "libgroove rules!");
assert.strictEqual(file.getMetadata('foo new key'), 'libgroove rules!');
file.save(function(err) {
- assert.ok(!err);
+ if (err) throw err;
file.close(checkUpdate);
});
}
@@ -155,3 +155,4 @@ it("create, attach, detach fingerprinter", function(done) {
});
});
});
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-groove.git
More information about the Pkg-javascript-commits
mailing list