Skip to content
This repository has been archived by the owner on Jun 15, 2018. It is now read-only.

Commit

Permalink
use ev_async for play notification instead of eio_custom, this also f…
Browse files Browse the repository at this point in the history
…rees up usage of the node thread pool and now we can play audio on all the channels at exactly the same time because we don't starve the actual thread pool with synchronous wait for audio played behaviour
  • Loading branch information
japj committed May 13, 2011
1 parent 08e184b commit 938ec09
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 64 deletions.
140 changes: 78 additions & 62 deletions src/node-sdlmixer.cc
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,19 +3,9 @@
using namespace node_sdlmixer; using namespace node_sdlmixer;


static int numChannels = 0; static int numChannels = 0;
static int curChannel = 0;


static deque<int> availableChannels; static deque<int> availableChannels;


/**
* Call this to determine if a channel is still playing
* Returns 0 if the channel is not playing, 1 if it is playing
* @param channel Channel to check for playing
*/
static int still_playing(int channel) {
return (Mix_Playing(channel));
}

/** /**
* Call this to claim an audio channel * Call this to claim an audio channel
* Returns either an available channel (>=0) or * Returns either an available channel (>=0) or
Expand Down Expand Up @@ -46,45 +36,21 @@ static int DoPlay(eio_req *req) {
/* Load the requested wave file */ /* Load the requested wave file */
pi->wave = Mix_LoadWAV(pi->name); pi->wave = Mix_LoadWAV(pi->name);


printf("Playing [%s] on channel[%d]\n", pi->name, pi->channel); //printf("Playing [%s] on channel[%d]\n", pi->name, pi->channel);
/* Play and then exit */ /* Play and then exit */
Mix_PlayChannel(pi->channel, pi->wave, 0); Mix_PlayChannel(pi->channel, pi->wave, 0);


while (still_playing(pi->channel)) {
SDL_Delay(1);

} /* while still_playing() loop... */

Mix_FreeChunk(pi->wave);
pi->wave = NULL;
return 0; return 0;
} }


static int NotifyPlayed(eio_req *req) { int SDLMixer::NotifyPlayed(eio_req *req) {
HandleScope scope; HandleScope scope;
ev_unref( EV_DEFAULT_UC); ev_unref( EV_DEFAULT_UC);
struct playInfo * pi = (struct playInfo *) req->data; //printf("SDLMixer::NotifyPlayed\n");

releaseAudioChannel(pi->channel);

Local<Value> argv[2];
argv[0] = Local<Value>::New(String::New(pi->name));
argv[1] = Local<Value>::New(Integer::New(pi->channel));

if (pi->doCallback) {
TryCatch try_catch;
pi->cb->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
}

pi->cb.Dispose();
free(pi);
return 0; return 0;
} }


static Handle<Value> Play(const Arguments& args) { Handle<Value> SDLMixer::Play(const Arguments& args) {
HandleScope scope; HandleScope scope;


const char *usage = "usage: play(fileName, <callbackFunc>)"; const char *usage = "usage: play(fileName, <callbackFunc>)";
Expand All @@ -111,20 +77,22 @@ static Handle<Value> Play(const Arguments& args) {
pi->wave = NULL; pi->wave = NULL;
strncpy(pi->name, *fileName, fileName.length() + 1); strncpy(pi->name, *fileName, fileName.length() + 1);


playInfoChannelList[channel] = pi;

eio_custom(DoPlay, EIO_PRI_DEFAULT, NotifyPlayed, pi); eio_custom(DoPlay, EIO_PRI_DEFAULT, NotifyPlayed, pi);
ev_ref( EV_DEFAULT_UC); ev_ref( EV_DEFAULT_UC);


return scope.Close(args[0]); return scope.Close(args[0]);
} }


Persistent<FunctionTemplate> SDLMixer::constructor_template; Persistent<FunctionTemplate> SDLMixer::constructor_template;
vector<playInfo *> SDLMixer::playInfoChannelList;
SDLMixer::AsyncPlayDone *SDLMixer::playDoneEvent = NULL;


SDLMixer::SDLMixer() { SDLMixer::SDLMixer() {

} }


SDLMixer::~SDLMixer() { SDLMixer::~SDLMixer() {

} }


void SDLMixer::Initialize(Handle<Object> target) { void SDLMixer::Initialize(Handle<Object> target) {
Expand All @@ -136,16 +104,15 @@ void SDLMixer::Initialize(Handle<Object> target) {
constructor_template->SetClassName(String::NewSymbol("SDLMixer")); constructor_template->SetClassName(String::NewSymbol("SDLMixer"));


NODE_SET_PROTOTYPE_METHOD(constructor_template, "play", Play); NODE_SET_PROTOTYPE_METHOD(constructor_template, "play", Play);
target->Set(String::NewSymbol("SDLMixer"), constructor_template->GetFunction()); target->Set(String::NewSymbol("SDLMixer"),
constructor_template->GetFunction());
} }


Handle<Value> SDLMixer::New(const Arguments &args) { Handle<Value> SDLMixer::New(const Arguments &args) {
HandleScope scope; HandleScope scope;


if (SDL_Init(SDL_INIT_AUDIO) < 0) { if (SDL_Init(SDL_INIT_AUDIO) < 0) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(String::New(SDL_GetError())));
String::New(SDL_GetError()))
);
} }


int audio_rate; int audio_rate;
Expand All @@ -160,42 +127,91 @@ Handle<Value> SDLMixer::New(const Arguments &args) {
/* Open the audio device */ /* Open the audio device */
if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) { if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) {
SDL_Quit(); SDL_Quit();
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(String::New(SDL_GetError())));
String::New(SDL_GetError()))
);
} }


Mix_ChannelFinished(SDLMixer::ChannelFinished);

numChannels = Mix_AllocateChannels(32); numChannels = Mix_AllocateChannels(32);


playInfoChannelList.resize(numChannels);

for (int x = 0; x < numChannels; x++) { for (int x = 0; x < numChannels; x++) {
availableChannels.push_back(x); availableChannels.push_back(x);
playInfoChannelList[x] = NULL;
} }


Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);


args.This()->Set(String::NewSymbol("audioRate"), args.This()->Set(String::NewSymbol("audioRate"), Integer::New(audio_rate),
Integer::New(audio_rate), ReadOnly); ReadOnly);
args.This()->Set(String::NewSymbol("audioFormat"), args.This()->Set(String::NewSymbol("audioFormat"), Integer::New((audio_format
Integer::New((audio_format & 0xFF)), ReadOnly); & 0xFF)), ReadOnly);
args.This()->Set(String::NewSymbol("audioChannels"), args.This()->Set(String::NewSymbol("audioChannels"), String::New(
String::New((audio_channels > 2) ? "surround" (audio_channels > 2) ? "surround" : (audio_channels > 1) ? "stereo"
: (audio_channels > 1) ? "stereo" : "mono"), ReadOnly); : "mono"), ReadOnly);
args.This()->Set(String::NewSymbol("numberOfAudioChannels"), args.This()->Set(String::NewSymbol("numberOfAudioChannels"), Integer::New(
Integer::New(numChannels)); numChannels));


SDLMixer *sdlmixer = new SDLMixer(); SDLMixer *sm = new SDLMixer();
sdlmixer->Wrap(args.This());
// TODO: switch to singleton for ChannelFinished notification?
if (playDoneEvent == NULL) {
//printf("Construct playDoneEvent\n");
playDoneEvent = new AsyncPlayDone(sm, PlayDoneCallback);
}

sm->Wrap(args.This());
return args.This(); return args.This();
} }


void SDLMixer::ChannelFinished(int channel) {
//printf("SDLMixer::ChannelFinished(%d)\n", channel);


playInfo *item = playInfoChannelList[channel];
//printf("item %d, playDoneEvent %d\n", item, playDoneEvent);
if ((item != NULL) && (playDoneEvent != NULL)) {
// TODO: switch to singleton for ChannelFinished notification?
playDoneEvent->send(item);
}
}


extern "C" void init(Handle<Object> target) { void SDLMixer::PlayDoneCallback(SDLMixer *sm, playInfo *pi) {
HandleScope scope; HandleScope scope;
//printf("SDLMixer::PlayDoneCallback(%d)\n", pi->channel);

playInfoChannelList[pi->channel] = NULL;
releaseAudioChannel(pi->channel);

//printf("availableChannels[%d]\n", (unsigned int)availableChannels.size());

if (availableChannels.size() == (unsigned int)numChannels) {
//printf("Delete playDoneEvent\n");
delete playDoneEvent;
playDoneEvent = NULL;
}

Local<Value> argv[2];
argv[0] = Local<Value>::New(String::New(pi->name));
argv[1] = Local<Value>::New(Integer::New(pi->channel));

if (pi->doCallback) {
TryCatch try_catch;
pi->cb->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
}


//initSDL(); Mix_FreeChunk(pi->wave);
pi->wave = NULL;


//NODE_SET_METHOD(target, "play", Play); pi->cb.Dispose();
free(pi);
}

extern "C" void init(Handle<Object> target) {
HandleScope scope;


SDLMixer::Initialize(target); SDLMixer::Initialize(target);
} }
14 changes: 14 additions & 0 deletions src/node-sdlmixer.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
#include <node.h> #include <node.h>


#include <deque> #include <deque>
#include <vector>


#include "SDL.h" #include "SDL.h"
#include "SDL_mixer.h" #include "SDL_mixer.h"


#include <pthread.h>
#include "async.h"

using namespace v8; using namespace v8;
using namespace node; using namespace node;
using namespace std; using namespace std;
Expand All @@ -28,15 +32,25 @@ class SDLMixer: public ObjectWrap {
static Persistent<FunctionTemplate> constructor_template; static Persistent<FunctionTemplate> constructor_template;


static void Initialize(Handle<Object> target); static void Initialize(Handle<Object> target);

typedef Async<playInfo, SDLMixer> AsyncPlayDone;
protected: protected:


static int NotifyPlayed(eio_req *req);
static Handle<Value> Play(const Arguments& args);

static void ChannelFinished(int channel);
static void PlayDoneCallback(SDLMixer *sm, playInfo *pi);

static Handle<Value> New(const Arguments& args); static Handle<Value> New(const Arguments& args);


SDLMixer(); SDLMixer();
~SDLMixer(); ~SDLMixer();


private: private:
static AsyncPlayDone *playDoneEvent;


static vector<playInfo *> playInfoChannelList;
}; };


} // namespace node_sdlmixer } // namespace node_sdlmixer
Expand Down
12 changes: 12 additions & 0 deletions test/simple.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,12 @@
var sdlmixer = require("../sdlmixer"),
sys = require("sys"),
puts = sys.puts;

puts("Opened audio at " + sdlmixer.audioRate + " Hz " + sdlmixer.audioFormat + " bit " +
sdlmixer.audioChannels + " and " + sdlmixer.numberOfAudioChannels + " channels.")

// play with a callback

sdlmixer.play(__dirname + "/../wavs/sfx/alarm.wav", function () {
puts("done")
});
5 changes: 3 additions & 2 deletions wscript
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def configure(conf):


def build(bld): def build(bld):
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon') obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'node-sdlmixer' obj.target = "node-sdlmixer"
obj.source = 'src/node-sdlmixer.cc' obj.cxxflags = ["-pthread", "-Wall"]
obj.source = "src/node-sdlmixer.cc"
obj.uselib = "SDL" obj.uselib = "SDL"

0 comments on commit 938ec09

Please sign in to comment.