#include #include #include #include #include #include #include #include #include #include #include #include static double pytime(const struct timeval * tv) { struct timeval t; if (!tv) { tv = &t; gettimeofday(&t, NULL); } return (double) tv->tv_sec + (double) tv->tv_usec / 1000000.0; } #include "log.cpp" #include "audio.cpp" #define FLOAT_TO_SHORT(in,out) __asm__ __volatile__ ("fistps %0" : "=m" (out) : "t" (in) : "st") ; int VERBOSE = 3; FILE * _debug = NULL; struct TamTamSound; struct Music; TamTamSound * g_tt = NULL; Music * g_music = NULL; log_t * g_log = NULL; const int STEP_eventMax = 16; //this is the most events that will be queued by a loop per step() /** * Event is the type of event that Clooper puts in the loop buffer. * It corresponds to a line of csound that starts with an 'i' */ struct Event { char type; ///< if this event were listed in a csound file, the line would begin with this letter int onset; ///< the onset time of this event (its temporal position) bool time_in_ticks; ///< if true, then some parameters will be updated according to the tempo bool active; ///< if true, then event() will actually do something MYFLT prev_secs_per_tick; ///< normally used for ____, sometimes set to -1 to force recalculation of param[] entries MYFLT duration, attack, decay;///< canonical values of some tempo-dependent parameters std::vector param; ///< parameter buffer for csound Event(char type, MYFLT * p, int param_count, bool in_ticks, bool active) : type(type), onset(0), time_in_ticks(in_ticks), active(active), param(param_count) { assert(param_count >= 4); onset = (int) p[1]; duration = p[2]; attack = param_count > 8 ? p[8]: 0.0; //attack decay = param_count > 9 ? p[9]: 0.0; //decay prev_secs_per_tick = -1.0; for (int i = 0; i < param_count; ++i) param[i] = p[i]; param[1] = 0.0; //onset } /* bool operator<(const Event &e) const { return onset < e.onset; } */ void ev_print(FILE *f) { fprintf(f, "INFO: scoreEvent %c ", type); for (size_t i = 0; i < param.size(); ++i) fprintf(f, "%lf ", param[i]); fprintf(f, "[%s]\n", active ? "active": "inactive"); } /** * Update the idx'th param value to have a certain value. * * Certain of the parameters are linked in strange hack-y ways, as defined by * the constructor, and update() (which should be consistent with one another!) * * These events are for use with the file: TamTam/Resources/univorc.csd. * So that file defines how the parameters will be interpreted by csound. */ void update(int idx, MYFLT val) { if ( (unsigned)idx >= param.size()) { if (_debug && (VERBOSE > 0)) fprintf(_debug, "ERROR: updateEvent request for too-high parameter %i\n", idx); return; } if (time_in_ticks) { switch(idx) { case 1: onset = (int) val; break; case 2: duration = val; break; case 8: attack = val; break; case 9: decay = val; break; default: param[idx] = val; break; } prev_secs_per_tick = -1.0; //force recalculation } else { param[idx] = val; } } /** * An Event instance can be in an active or inactive state. If an Event instance * is active, then event() will call a corresponding csoundScoreEvent(). If an * Event instance is inactive, then event() is a noop. */ void activate_cmd(int cmd) { switch(cmd) { case 0: active = false; break; case 1: active = true; break; case 2: active = !active; break; } } /** * Iff this instance is active, this call generates a csound event. * Parameters are passed directly as a buffer of floats. If secs_per_tick * != prev_secs_per_tick (possibly because prev_secs_per_tick was set to -1 * by update() ) then this call will do some floating point ops to * recalculate the parameter buffer. */ void event(CSOUND * csound, MYFLT secs_per_tick) { if (!active) return; if (time_in_ticks && (secs_per_tick != prev_secs_per_tick)) { param[2] = duration * secs_per_tick; if (param.size() > 8) param[8] = std::max(0.002f, attack * param[2]); if (param.size() > 9) param[9] = std::max(0.002f, decay * param[2]); prev_secs_per_tick = secs_per_tick; if (_debug && (VERBOSE > 2)) fprintf(_debug, "setting duration to %f\n", param[5]); } csoundScoreEvent(csound, type, ¶m[0], param.size()); } }; /** * * Loop is a repeat-able loop of Event instances. * */ struct Loop { typedef int onset_t; typedef int id_t; typedef std::pair pair_t; typedef std::multimap::iterator iter_t; typedef std::map::iterator idmap_t; int tick_prev; int tickMax; MYFLT rtick; // a container of all events, sorted by onset time // used for efficient playback std::multimap ev; // the playback head std::multimap::iterator ev_pos; // a container of pointers into ev, indexed by note id // used for deleting, updating notes std::map idmap; int steps; int playing; //true means that step() works, else step() is no-op Loop() : tick_prev(0), tickMax(1), rtick(0.0), ev(), ev_pos(ev.end()), steps(0), playing(0) { } ~Loop() { //TODO: send these events to a recycling queue, don't erase them for (iter_t i = ev.begin(); i != ev.end(); ++i) { delete i->second; } } void deactivateAll() { for (iter_t i = ev.begin(); i != ev.end(); ++i) { i->second->activate_cmd(0); } } MYFLT getTickf() { return fmod(rtick, (MYFLT)tickMax); } void setNumTicks(int nticks) { tickMax = nticks; MYFLT fnticks = nticks; if (rtick > fnticks) { rtick = fmodf(rtick, fnticks); } } void setTickf(float t) { rtick = fmodf(t, (MYFLT) tickMax); ev_pos = ev.lower_bound( (int) rtick ); } /** advance in play loop by rtick_inc ticks, possibly generate some * csoundScoreEvent calls. */ void step(MYFLT rtick_inc, MYFLT secs_per_tick , CSOUND * csound) { if (!playing) return; rtick += rtick_inc; int tick = (int)rtick % tickMax; if (tick == tick_prev) return; int events = 0; int loop0 = 0; int loop1 = 0; if (!ev.empty()) { if (steps && (tick < tick_prev)) // should be true only after the loop wraps (not after insert) { while (ev_pos != ev.end()) { if (_debug && (VERBOSE > 3)) ev_pos->second->ev_print(_debug); if (events < STEP_eventMax) ev_pos->second->event(csound, secs_per_tick); ++ev_pos; ++events; ++loop0; } ev_pos = ev.begin(); } while ((ev_pos != ev.end()) && (tick >= ev_pos->first)) { if (_debug && (VERBOSE > 3)) ev_pos->second->ev_print(_debug); if (events < STEP_eventMax) ev_pos->second->event(csound, secs_per_tick); ++ev_pos; ++events; ++loop1; } } tick_prev = tick; if (_debug && (VERBOSE>1) && (events >= STEP_eventMax)) fprintf(_debug, "WARNING: %i/%i events at once (%i, %i)\n", events, (int)ev.size(),loop0,loop1); ++steps; } void addEvent(int id, char type, MYFLT * p, int np, bool in_ticks, bool active) { Event * e = new Event(type, p, np, in_ticks, active); idmap_t id_iter = idmap.find(id); if (id_iter == idmap.end()) { //this is a new id iter_t e_iter = ev.insert(pair_t(e->onset, e)); //TODO: optimize by thinking about whether to do ev_pos = e_iter ev_pos = ev.upper_bound( tick_prev ); idmap[id] = e_iter; } else { g_log->printf(1, "%s duplicate note %i\n", __FUNCTION__, id); } } void delEvent(int id) { idmap_t id_iter = idmap.find(id); if (id_iter != idmap.end()) { iter_t e_iter = id_iter->second;//idmap[id]; if (e_iter == ev_pos) ++ev_pos; delete e_iter->second; ev.erase(e_iter); idmap.erase(id_iter); } else { g_log->printf( 1, "%s unknown note %i\n", __FUNCTION__, id); } } void updateEvent(int id, int idx, float val, int activate_cmd) { idmap_t id_iter = idmap.find(id); if (id_iter != idmap.end()) { //this is a new id iter_t e_iter = id_iter->second; Event * e = e_iter->second; int onset = e->onset; e->update(idx, val); e->activate_cmd(activate_cmd); if (onset != e->onset) { ev.erase(e_iter); e_iter = ev.insert(pair_t(e->onset, e)); //TODO: optimize by thinking about whether to do ev_pos = e_iter ev_pos = ev.upper_bound( tick_prev ); idmap[id] = e_iter; } } else { g_log->printf(1, "%s unknown note %i\n", __FUNCTION__, id); } } void reset() { steps = 0; } void setPlaying(int tf) { playing = tf; } }; /** management of loops */ struct Music { typedef int loopIdx_t; typedef std::map eventMap_t; eventMap_t loop; int loop_nextIdx; void * mutex; //modification and playing of loops cannot be interwoven Music() : loop(), loop_nextIdx(0), mutex(csoundCreateMutex(0)) { } ~Music() { for (eventMap_t::iterator i = loop.begin(); i != loop.end(); ++i) { delete i->second; } csoundDestroyMutex(mutex); } void step(MYFLT amt, MYFLT secs_per_tick, CSOUND * csound) { csoundLockMutex(mutex); for (eventMap_t::iterator i = loop.begin(); i != loop.end(); ++i) { i->second->step(amt, secs_per_tick, csound); } csoundUnlockMutex(mutex); } /** allocate a new loop, and return its index */ loopIdx_t alloc() { csoundLockMutex(mutex); //find a loop_nextIdx that isn't in loop map already while ( loop.find( loop_nextIdx) != loop.end()) ++loop_nextIdx; loop[loop_nextIdx] = new Loop(); csoundUnlockMutex(mutex); return loop_nextIdx; } /** de-allocate a loop */ void destroy(loopIdx_t loopIdx) { if (loop.find(loopIdx) != loop.end()) { csoundLockMutex(mutex); //TODO: save the note events to a cache for recycling delete loop[loopIdx]; loop.erase(loopIdx); csoundUnlockMutex(mutex); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } /** set the playing flag of the given loop */ void playing(loopIdx_t loopIdx, int tf) { if (loop.find(loopIdx) != loop.end()) { csoundLockMutex(mutex); loop[loopIdx]->setPlaying(tf); csoundUnlockMutex(mutex); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } /** set the playing flag of the given loop */ void addEvent(loopIdx_t loopIdx, int eventId, char type, MYFLT * p, int np, bool in_ticks, bool active) { if (loop.find(loopIdx) != loop.end()) { csoundLockMutex(mutex); loop[loopIdx]->addEvent(eventId, type, p, np, in_ticks, active); csoundUnlockMutex(mutex); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } void delEvent(loopIdx_t loopIdx, int eventId) { if (loop.find(loopIdx) != loop.end()) { csoundLockMutex(mutex); loop[loopIdx]->delEvent(eventId); csoundUnlockMutex(mutex); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } void updateEvent(loopIdx_t loopIdx, int eventId, int pIdx, float pVal, int activate_cmd) { if (loop.find(loopIdx) != loop.end()) { csoundLockMutex(mutex); loop[loopIdx]->updateEvent(eventId, pIdx, pVal, activate_cmd); csoundUnlockMutex(mutex); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } MYFLT getTickf(loopIdx_t loopIdx) { if (loop.find(loopIdx) != loop.end()) { return loop[loopIdx]->getTickf(); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); return 0.0; } } void setTickf(loopIdx_t loopIdx, MYFLT tickf) { if (loop.find(loopIdx) != loop.end()) { loop[loopIdx]->setTickf(tickf); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } void setNumTicks(loopIdx_t loopIdx, int numTicks) { if (loop.find(loopIdx) != loop.end()) { loop[loopIdx]->setNumTicks(numTicks); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } void deactivateAll(loopIdx_t loopIdx) { if (loop.find(loopIdx) != loop.end()) { loop[loopIdx]->deactivateAll(); } else { g_log->printf(1, "%s() called on non-existant loop %i\n", __FUNCTION__ , loopIdx); } } }; /** * The main object of control in the Clooper plugin. * * This guy controls the sound rendering thread, loads and unloads ALSA, * maintains a csound instance, and maintains a subset of notes from the * currently-loaded TamTam. */ struct TamTamSound { /** the id of an running sound-rendering thread, or NULL */ void * ThreadID; /** a flag to tell the thread to continue, or break */ enum {CONTINUE, STOP} PERF_STATUS; /** our csound object, NULL iff there was a problem creating it */ CSOUND * csound; /** our note sources */ Music music; MYFLT secs_per_tick; MYFLT ticks_per_period; MYFLT tick_adjustment; //the default time increment in thread_fn MYFLT tick_total; /** the upsampling ratio from csound */ unsigned int csound_ksmps; snd_pcm_uframes_t csound_frame_rate; snd_pcm_uframes_t csound_period_size; snd_pcm_uframes_t period0; unsigned int period_per_buffer; //should be 2 int up_ratio; //if the hardware only supports a small integer multiple of our effective samplerate, do a real-time conversion log_t * ll; SystemStuff * sys_stuff; TamTamSound(log_t * ll, char * orc, snd_pcm_uframes_t period0, unsigned int ppb, int ksmps, int framerate ) : ThreadID(NULL), PERF_STATUS(STOP), csound(NULL), music(), ticks_per_period(0.0), tick_adjustment(0.0), tick_total(0.0), csound_ksmps(ksmps), //must agree with the orchestra file csound_frame_rate(framerate), //must agree with the orchestra file period0(period0), period_per_buffer(ppb), up_ratio(1), ll( ll ), sys_stuff(NULL) { sys_stuff = new SystemStuff(ll); if (0 > sys_stuff->open(csound_frame_rate, 4, period0, period_per_buffer)) { return; } sys_stuff->close(0); up_ratio = sys_stuff->rate / csound_frame_rate; csound_period_size = (sys_stuff->period_size % up_ratio == 0) ? sys_stuff->period_size / up_ratio : csound_ksmps * 4; csound = csoundCreate(NULL); int argc=3; const char **argv = (const char**)malloc(argc*sizeof(char*)); argv[0] = "csound"; argv[1] = "-m0"; argv[2] = orc; ll->printf(1, "loading csound orchestra file %s\n", orc); //csoundInitialize(&argc, &argv, 0); csoundPreCompile(csound); csoundSetHostImplementedAudioIO(csound, 1, csound_period_size); int result = csoundCompile(csound, argc, (char**)argv); if (result) { csound = NULL; ll->printf( "ERROR: csoundCompile of orchestra %s failed with code %i\n", orc, result); } free(argv); setTickDuration(0.05); } ~TamTamSound() { if (csound) { stop(); ll->printf(2, "Going for csoundDestroy\n"); csoundDestroy(csound); } ll->printf(2, "TamTamSound destroyed\n"); if (sys_stuff) delete sys_stuff; delete ll; } bool good() { return csound != NULL; } uintptr_t thread_fn() { assert(csound); const int nchannels = 2; int nloops = 0; long int csound_nsamples = csoundGetOutputBufferSize(csound); long int csound_nframes = csound_nsamples / nchannels; ll->printf(2, "INFO: nsamples = %li nframes = %li\n", csound_nsamples, csound_nframes); if (0 > sys_stuff->open(csound_frame_rate, 4, period0, period_per_buffer)) { ll->printf( "ERROR: failed to open alsa device, thread abort\n"); return 1; } assert(up_ratio == (signed)(sys_stuff->rate / csound_frame_rate)); bool do_upsample = (signed)sys_stuff->period_size != csound_nframes; short *upbuf = new short[ sys_stuff->period_size * nchannels ]; int cbuf_pos = csound_nframes; // trigger a call to csoundPerformBuffer immediately float *cbuf = NULL; int up_pos = 0; int ratio_pos = 0; tick_total = 0.0f; while (PERF_STATUS == CONTINUE) { if ( do_upsample ) //fill one period of audio buffer data by 0 or more calls to csound { up_pos = 0; int messed = 0; short cursample[2]={0,0}; while(!messed) { if (cbuf_pos == csound_nframes) { cbuf_pos = 0; if (csoundPerformBuffer(csound)) { messed = 1;break;} cbuf = csoundGetOutputBuffer(csound); cursample[0] = (signed short int) (cbuf[cbuf_pos*2+0] * (1<<15)); cursample[1] = (signed short int) (cbuf[cbuf_pos*2+1] * (1<<15)); /* cbuf[cbuf_pos*2+0] *= (float) ((1<<14)); cbuf[cbuf_pos*2+1] *= (float) ((1<<14)); FLOAT_TO_SHORT( cbuf[cbuf_pos*2+0], cursample[0]); FLOAT_TO_SHORT( cbuf[cbuf_pos*2+1], cursample[1]); */ } upbuf[2*up_pos+0] = cursample[0]; upbuf[2*up_pos+1] = cursample[1]; if (++ratio_pos == up_ratio) { ratio_pos = 0; ++cbuf_pos; cursample[0] = (signed short int) (cbuf[cbuf_pos*2+0] * (1<<15)); cursample[1] = (signed short int) (cbuf[cbuf_pos*2+1] * (1<<15)); /*cbuf[cbuf_pos*2+0] *= (float) ((1<<14)); cbuf[cbuf_pos*2+1] *= (float) ((1<<14)); FLOAT_TO_SHORT( cbuf[cbuf_pos*2+0], cursample[0]); FLOAT_TO_SHORT( cbuf[cbuf_pos*2+1], cursample[1]); */ } if (++up_pos == (signed)sys_stuff->period_size) break; } if (messed || (up_pos != (signed)sys_stuff->period_size)) break; if (0 > sys_stuff->writebuf(sys_stuff->period_size, upbuf)) break; } else //fill one period of audio directly from csound { if (csoundPerformBuffer(csound)) break; cbuf = csoundGetOutputBuffer(csound); for (int i = 0; i < csound_nframes * nchannels; ++i) { cbuf[i] *= (float) ((1<<15)-100.0f); FLOAT_TO_SHORT( cbuf[i], upbuf[i]); } if (0 > sys_stuff->writebuf(csound_nframes,upbuf)) break; } if (tick_adjustment > - ticks_per_period) { MYFLT tick_inc = ticks_per_period + tick_adjustment; music.step( tick_inc, secs_per_tick, csound); tick_adjustment = 0.0; tick_total += tick_inc; } else { tick_adjustment += ticks_per_period; } ++nloops; } sys_stuff->close(1); delete [] upbuf; ll->printf(2, "INFO: performance thread returning 0\n"); return 0; } static uintptr_t csThread(void *clientData) { return ((TamTamSound*)clientData)->thread_fn(); } int start(int ) { if (!csound) { ll->printf(1, "skipping %s, csound==NULL\n", __FUNCTION__); return 1; } if (!ThreadID) { PERF_STATUS = CONTINUE; ThreadID = csoundCreateThread(csThread, (void*)this); ll->printf( "INFO(%s:%i) aclient launching performance thread (%p)\n", __FILE__, __LINE__, ThreadID ); return 0; } ll->printf( "INFO(%s:%i) skipping duplicate request to launch a thread\n", __FILE__, __LINE__ ); return 1; } int stop() { if (!csound) { ll->printf(1, "skipping %s, csound==NULL\n", __FUNCTION__); return 1; } if (ThreadID) { PERF_STATUS = STOP; ll->printf( "INFO(%s:%i) aclient joining performance thread\n", __FILE__, __LINE__ ); uintptr_t rval = csoundJoinThread(ThreadID); ll->printf( "INFO(%s:%i) ... joined\n", __FILE__, __LINE__ ); if (rval) ll->printf( "WARNING: thread returned %zu\n", rval); ThreadID = NULL; return 0; } return 1; } /** pass an array event straight through to csound. only works if perf. thread is running */ void scoreEvent(char type, MYFLT * p, int np) { if (!csound) { ll->printf(1, "skipping %s, csound==NULL\n", __FUNCTION__); return; } if (!ThreadID) { if (_debug && (VERBOSE > 1)) fprintf(_debug, "skipping %s, ThreadID==NULL\n", __FUNCTION__); return ; } if (_debug && (VERBOSE > 2)) { fprintf(_debug, "INFO: scoreEvent %c ", type); for (int i = 0; i < np; ++i) fprintf(_debug, "%lf ", p[i]); fprintf(_debug, "\n"); } csoundScoreEvent(csound, type, p, np); } /** pass a string event straight through to csound. only works if perf. thread is running */ void inputMessage(const char * msg) { if (!csound) { ll->printf(1, "skipping %s, csound==NULL\n", __FUNCTION__); return; } if (!ThreadID) { if (_debug && (VERBOSE > 1)) fprintf(_debug, "skipping %s, ThreadID==NULL\n", __FUNCTION__); return ; } if (_debug &&(VERBOSE > 3)) fprintf(_debug, "%s\n", msg); csoundInputMessage(csound, msg); } /** pass a setChannel command through to csound. only works if perf. thread is running */ void setChannel(const char * name, MYFLT vol) { if (!csound) { ll->printf(1, "skipping %s, csound==NULL\n", __FUNCTION__); return; } if (!ThreadID) { if (_debug && (VERBOSE > 1)) fprintf(_debug, "skipping %s, ThreadID==NULL\n", __FUNCTION__); return ; } MYFLT *p; if (!(csoundGetChannelPtr(csound, &p, name, CSOUND_CONTROL_CHANNEL | CSOUND_INPUT_CHANNEL))) *p = (MYFLT) vol; else { if (_debug && (VERBOSE >0)) fprintf(_debug, "ERROR: failed to set channel: %s\n", name); } } /** adjust the global tick value by this much */ void adjustTick(MYFLT dtick) { tick_adjustment += dtick; } void setTickDuration(MYFLT d ) { secs_per_tick = d; ticks_per_period = csound_period_size / ( secs_per_tick * csound_frame_rate); ll->printf( 3, "INFO: duration %lf := ticks_per_period %lf\n", secs_per_tick , ticks_per_period); } MYFLT getTickf() { return tick_total + tick_adjustment; } }; static void cleanup(void) { if (g_tt) { delete g_tt; g_tt = NULL; } } #define DECL(s) static PyObject * s(PyObject * self, PyObject *args) #define RetNone Py_INCREF(Py_None); return Py_None; //call once at end DECL(sc_destroy) { if (!PyArg_ParseTuple(args, "")) { return NULL; } if (g_tt) { delete g_tt; g_tt = NULL; if (_debug) fclose(_debug); } RetNone; } //call once at startup, should return 0 DECL(sc_initialize) //(char * csd) { char * str; char * log_file; int period, ppb, ksmps, framerate; if (!PyArg_ParseTuple(args, "ssiiiii", &str, &log_file, &period, &ppb, &VERBOSE, &ksmps, &framerate )) { return NULL; } if ( log_file[0] ) { _debug = fopen(log_file,"w"); if (_debug==NULL) { fprintf(stderr, "WARNING: fopen(%s) failed, logging to stderr\n", log_file); _debug = stderr; } } else { _debug = NULL; fprintf(stderr, "Logging disabled on purpose\n"); } g_log = new log_t(_debug, VERBOSE); g_tt = new TamTamSound(g_log, str, period, ppb, ksmps, framerate); g_music = & g_tt->music; atexit(&cleanup); if (g_tt->good()) return Py_BuildValue("i", 0); else return Py_BuildValue("i", -1); } //compile the score, connect to device, start a sound rendering thread DECL(sc_start) { int ppb; if (!PyArg_ParseTuple(args, "i", &ppb )) { return NULL; } return Py_BuildValue("i", g_tt->start(ppb)); } //stop csound rendering thread, disconnect from sound device, clear tables. DECL(sc_stop) { if (!PyArg_ParseTuple(args, "" )) { return NULL; } return Py_BuildValue("i", g_tt->stop()); } DECL(sc_scoreEvent) //(char type, farray param) { char ev_type; PyObject *o; if (!PyArg_ParseTuple(args, "cO", &ev_type, &o )) { return NULL; } if (o->ob_type && o->ob_type->tp_as_buffer && (1 == o->ob_type->tp_as_buffer->bf_getsegcount(o, NULL))) { if (o->ob_type->tp_as_buffer->bf_getreadbuffer) { void * ptr; size_t len; len = o->ob_type->tp_as_buffer->bf_getreadbuffer(o, 0, &ptr); float * fptr = (float*)ptr; size_t flen = len / sizeof(float); g_tt->scoreEvent(ev_type, fptr, flen); Py_INCREF(Py_None); return Py_None; } else { assert(!"asdf"); } } assert(!"not reached"); return NULL; } DECL (sc_inputMessage) //(const char *msg) { char * msg; if (!PyArg_ParseTuple(args, "s", &msg )) { return NULL; } g_tt->inputMessage(msg); RetNone; } DECL(sc_setChannel) //(string name, float value) { const char * str; float v; if (!PyArg_ParseTuple(args, "sf", &str,&v)) { return NULL; } g_tt->setChannel(str,v); Py_INCREF(Py_None); return Py_None; } DECL(sc_getTickf) // () -> float { if (!PyArg_ParseTuple(args, "")) { return NULL; } return Py_BuildValue("f", g_tt->getTickf()); } DECL(sc_adjustTick) // (MYFLT ntick) { float spt; if (!PyArg_ParseTuple(args, "f", &spt )) { return NULL; } g_tt->adjustTick(spt); RetNone; } DECL(sc_setTickDuration) // (MYFLT secs_per_tick) { float spt; if (!PyArg_ParseTuple(args, "f", &spt )) { return NULL; } g_tt->setTickDuration(spt); RetNone; } DECL(sc_loop_new) // () -> int { if (!PyArg_ParseTuple(args, "" )) return NULL; return Py_BuildValue("i", g_music->alloc()); } DECL(sc_loop_delete) // (int loopIdx) { int loopIdx; if (!PyArg_ParseTuple(args, "i", &loopIdx )) return NULL; g_music->destroy(loopIdx); RetNone; } DECL(sc_loop_getTickf) // (int loopIdx) -> float { int idx; if (!PyArg_ParseTuple(args, "i", &idx )) { return NULL; } return Py_BuildValue("f", g_music->getTickf(idx)); } DECL(sc_loop_setNumTicks) //(int loopIdx, int nticks) { int loopIdx; int nticks; if (!PyArg_ParseTuple(args, "ii", &loopIdx, &nticks )) return NULL; g_music->setNumTicks(loopIdx, nticks); RetNone; } DECL(sc_loop_setTickf) // (int loopIdx, float pos) { int loopIdx; MYFLT pos; if (!PyArg_ParseTuple(args, "if", &loopIdx, &pos )) return NULL; g_music->setTickf(loopIdx, pos); RetNone; } DECL(sc_loop_addScoreEvent) // (int loopIdx, int id, int duration_in_ticks, char type, farray param) { int loopIdx, qid, inticks, active; char ev_type; PyObject *o; if (!PyArg_ParseTuple(args, "iiiicO", &loopIdx, &qid, &inticks, &active, &ev_type, &o )) return NULL; if (o->ob_type && o->ob_type->tp_as_buffer && (1 == o->ob_type->tp_as_buffer->bf_getsegcount(o, NULL))) { if (o->ob_type->tp_as_buffer->bf_getreadbuffer) { void * ptr; size_t len; len = o->ob_type->tp_as_buffer->bf_getreadbuffer(o, 0, &ptr); float * fptr = (float*)ptr; size_t flen = len / sizeof(float); g_music->addEvent(loopIdx, qid, ev_type, fptr, flen, inticks, active); RetNone; } else { assert(!"asdf"); } } assert(!"not reached"); return NULL; } DECL(sc_loop_delScoreEvent) // (int loopIdx, int id) { int loopIdx, id; if (!PyArg_ParseTuple(args, "ii", &loopIdx, &id )) { return NULL; } g_music->delEvent(loopIdx, id); RetNone; } DECL(sc_loop_updateEvent) // (int loopIdx, int id, int paramIdx, float paramVal, int activate_cmd)) { int loopIdx, eventId; int idx; float val; int cmd; if (!PyArg_ParseTuple(args, "iiifi", &loopIdx, &eventId, &idx, &val, &cmd)) return NULL; g_music->updateEvent(loopIdx, eventId, idx, val, cmd); RetNone; } DECL(sc_loop_deactivate_all) // (int id) { int loopIdx; if (!PyArg_ParseTuple(args, "i", &loopIdx)) return NULL; g_music->deactivateAll(loopIdx); RetNone; } DECL(sc_loop_playing) // (int loopIdx, int tf) { int loopIdx, tf; if (!PyArg_ParseTuple(args, "ii", &loopIdx, &tf )) return NULL; g_music->playing(loopIdx, tf); RetNone; } #define MDECL(s) {""#s, s, METH_VARARGS, "documentation of "#s"... nothing!"} static PyMethodDef SpamMethods[] = { MDECL(sc_destroy), MDECL(sc_initialize), MDECL(sc_start), MDECL(sc_stop), MDECL(sc_setChannel), MDECL(sc_inputMessage), MDECL(sc_scoreEvent), MDECL(sc_getTickf), MDECL(sc_adjustTick), MDECL(sc_setTickDuration), MDECL(sc_loop_new), MDECL(sc_loop_delete), MDECL(sc_loop_getTickf), MDECL(sc_loop_setTickf), MDECL(sc_loop_setNumTicks), MDECL(sc_loop_delScoreEvent), MDECL(sc_loop_addScoreEvent), MDECL(sc_loop_updateEvent), MDECL(sc_loop_deactivate_all), MDECL(sc_loop_playing), {NULL, NULL, 0, NULL} /*end of list */ }; PyMODINIT_FUNC initaclient(void) { (void) Py_InitModule("aclient", SpamMethods); }