diff options
author | James <olpc@localhost.localdomain> | 2007-02-20 06:38:42 (GMT) |
---|---|---|
committer | James <olpc@localhost.localdomain> | 2007-02-20 06:38:42 (GMT) |
commit | b061c6cc84e810a87598290d8ba709a6b538ec8a (patch) | |
tree | bd12a71ac5b53c4f9b4d8da5b9d09ec98babe4ec /Util/Clooper | |
parent | 1d15172e215d42c7cdf82893f1216ab449a9c1bc (diff) |
beginning of aclient
Diffstat (limited to 'Util/Clooper')
-rw-r--r-- | Util/Clooper/Makefile | 7 | ||||
-rw-r--r-- | Util/Clooper/aclient.cpp | 778 | ||||
-rwxr-xr-x | Util/Clooper/aclient.so | bin | 0 -> 223913 bytes |
3 files changed, 783 insertions, 2 deletions
diff --git a/Util/Clooper/Makefile b/Util/Clooper/Makefile index c2a246d..a83f9d2 100644 --- a/Util/Clooper/Makefile +++ b/Util/Clooper/Makefile @@ -1,14 +1,17 @@ CC=gcc -CFLAGS+=-g -Wall -Werror -fPIC -O3 -finline +CFLAGS+=-g -Wall -Werror -fPIC #-O3 -finline PYTHON=/usr/include/python2.4 -all : sclient.so +all : sclient.so aclient.so sclient.so : SoundClient.cpp g++ $(CFLAGS) -shared -o $@ $< -lpython2.4 -lcsound +aclient.so : aclient.cpp + g++ $(CFLAGS) -shared -o $@ $< -lpython2.4 -lcsound -lasound + clean : rm sclient.so diff --git a/Util/Clooper/aclient.cpp b/Util/Clooper/aclient.cpp new file mode 100644 index 0000000..17e2470 --- /dev/null +++ b/Util/Clooper/aclient.cpp @@ -0,0 +1,778 @@ +#include <python2.4/Python.h> + +#include <pthread.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <sys/time.h> + +#include <csound/csound.h> +//#include "SoundClient.h" +#include <vector> +#include <map> +#include <cmath> + +using namespace std; + +static double pytime(const struct timeval * tv) +{ + return (double) tv->tv_sec + (double) tv->tv_usec / 1000000.0; +} + +struct ev_t +{ + char type; + int onset; + bool time_in_ticks; + MYFLT prev_secs_per_tick; + MYFLT duration, attack, decay; + std::vector<MYFLT> param; + + ev_t(char type, MYFLT * p, int np, bool in_ticks) + : type(type), onset(0), time_in_ticks(in_ticks), param(np) + { + assert(np >= 4); + onset = (int) p[1]; + duration = p[2]; + attack = np > 8 ? p[8]: 0.0; //attack + decay = np > 9 ? p[9]: 0.0; //decay + prev_secs_per_tick = -1.0; + for (int i = 0; i < np; ++i) param[i] = p[i]; + + param[1] = 0.0; //onset + } + bool operator<(const ev_t &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, "\n"); + } + void update(int idx, MYFLT val) + { + if ( (unsigned)idx >= param.size()) + { + fprintf(stderr, "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; + } + } + + void event(CSOUND * csound, MYFLT secs_per_tick) + { + if (time_in_ticks && (secs_per_tick != prev_secs_per_tick)) + { + param[2] = duration * secs_per_tick; + param[8] = max(0.002f, attack * secs_per_tick); + param[9] = max(0.002f, decay * secs_per_tick); + prev_secs_per_tick = secs_per_tick; + if (0) fprintf(stdout, "setting duration to %f\n", param[5]); + } + csoundScoreEvent(csound, type, ¶m[0], param.size()); + } +}; +struct EvLoop +{ + FILE * _debug; + + int tick_prev; + int tickMax; + MYFLT rtick; + MYFLT secs_per_tick; + MYFLT ticks_per_ksmp; + typedef std::pair<int, ev_t *> pair_t; + typedef std::multimap<int, ev_t *>::iterator iter_t; + typedef std::map<int, iter_t>::iterator idmap_t; + + std::multimap<int, ev_t *> ev; + std::multimap<int, ev_t *>::iterator ev_pos; + std::map<int, iter_t> idmap; + CSOUND * csound; + void * mutex; + + EvLoop(CSOUND * cs) : _debug(stderr), tick_prev(0), tickMax(1), rtick(0.0), ev(), ev_pos(ev.end()), csound(cs), mutex(NULL) + { + setTickDuration(0.05); + mutex = csoundCreateMutex(0); + } + ~EvLoop() + { + csoundLockMutex(mutex); + for (iter_t i = ev.begin(); i != ev.end(); ++i) + { + delete i->second; + } + csoundUnlockMutex(mutex); + csoundDestroyMutex(mutex); + } + void clear() + { + csoundLockMutex(mutex); + for (iter_t i = ev.begin(); i != ev.end(); ++i) + { + delete i->second; + } + ev.erase(ev.begin(), ev.end()); + ev_pos = ev.end(); + idmap.erase(idmap.begin(), idmap.end()); + csoundUnlockMutex(mutex); + } + int getTick() + { + return (int)rtick % tickMax; + } + void setNumTicks(int nticks) + { + tickMax = nticks; + if ((int)rtick > nticks) + { + int t = (int)rtick % nticks; + rtick = t; + } + } + void setTick(int t) + { + t = t % tickMax; + rtick = (MYFLT)(t % tickMax); + //TODO: binary search would be faster + csoundLockMutex(mutex); + ev_pos = ev.lower_bound( t ); + csoundUnlockMutex(mutex); + } + void setTickDuration(MYFLT d) + { + if (!csound) { + fprintf(stderr, "skipping setTickDuration, csound==NULL\n"); + return; + } + secs_per_tick = d; + ticks_per_ksmp = 1.0 / (d * csoundGetKr(csound)); + if (0) fprintf(stderr, "INFO: duration %lf -> ticks_pr_skmp %lf\n", d, ticks_per_ksmp); + } + void step(FILE * logf) + { + rtick += ticks_per_ksmp; + int tick = (int)rtick % tickMax; + if (tick == tick_prev) return; + + csoundLockMutex(mutex); + int events = 0; + int loop0 = 0; + int loop1 = 0; + const int eventMax = 8; //NOTE: events beyond this number will be ignored!!! + if (!ev.empty()) + { + if (tick < tick_prev) // should be true only after the loop wraps (not after insert) + { + while (ev_pos != ev.end()) + { + if (logf) ev_pos->second->ev_print(logf); + if (events < 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 (logf) ev_pos->second->ev_print(logf); + if (events < eventMax) ev_pos->second->event(csound, secs_per_tick); + ++ev_pos; + ++events; + ++loop1; + } + } + csoundUnlockMutex(mutex); + tick_prev = tick; + if (logf && (events >= eventMax)) fprintf(logf, "WARNING: %i/%i events at once (%i, %i)\n", events,ev.size(),loop0,loop1); + } + void addEvent(int id, char type, MYFLT * p, int np, bool in_ticks) + { + ev_t * e = new ev_t(type, p, np, in_ticks); + + idmap_t id_iter = idmap.find(id); + if (id_iter == idmap.end()) + { + //this is a new id + csoundLockMutex(mutex); + + 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; + + csoundUnlockMutex(mutex); + } + else + { + if (_debug) fprintf(_debug, "ERROR: skipping request to add duplicate note %i\n", id); + } + } + void delEvent(int id) + { + idmap_t id_iter = idmap.find(id); + if (id_iter == idmap.end()) + { + if (_debug) fprintf(_debug, "ERROR: delEvent request for unknown note %i\n", id); + } + else + { + csoundLockMutex(mutex); + 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); + + csoundUnlockMutex(mutex); + } + } + void updateEvent(int id, int idx, float val) + { + idmap_t id_iter = idmap.find(id); + if (id_iter == idmap.end()) + { + if (_debug) fprintf(_debug, "ERROR: updateEvent request for unknown note %i\n", id); + return; + } + + //this is a new id + csoundLockMutex(mutex); + iter_t e_iter = id_iter->second; + ev_t * e = e_iter->second; + int onset = e->onset; + e->update(idx, val); + 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; + } + csoundUnlockMutex(mutex); + } +}; +struct TamTamSound +{ + void * ThreadID; + CSOUND * csound; + char * csound_orc; + enum {CONTINUE, STOP} PERF_STATUS; + int verbosity; + FILE * _debug; + FILE * _light; + int thread_playloop; + int thread_measurelag; + EvLoop * loop; + + TamTamSound(char * orc) + : ThreadID(NULL), csound(NULL), PERF_STATUS(STOP), verbosity(3), _debug(stderr), + _light(fopen("/sys/bus/platform/devices/leds-olpc/leds:olpc:keyboard/brightness", "w")), + thread_playloop(0), thread_measurelag(0), loop(NULL) + { + if (1) + { + csound = csoundCreate(NULL); + } + else + { + csound = NULL; + } + csound_orc = strdup(orc); + + loop = new EvLoop(csound); + } + ~TamTamSound() + { + if (csound) + { + stop(); + delete loop; + csoundDestroy(csound); + } + free(csound_orc); + if (_debug) fclose(_debug); + } + uintptr_t thread_fn() + { + struct timeval tv; + double t_prev = 0.0; //value will be ignored + double m = 0.0; + + int loops = 0; + + while ( (csoundPerformBuffer(csound) == 0) + && (PERF_STATUS == CONTINUE)) + { + long nsamples = csoundGetOutputBufferSize(csound); + MYFLT *buf = csoundGetOutputBuffer(csound); + MYFLT ssq = 0; + for (int i = 0; i < nsamples; ++i) ssq += buf[i] * buf[i]; + if (_light) + { + char one = '1'; + char zero = '0'; + if (ssq > 0.2) fwrite(&one, 1, 1, _light); + else fwrite(&zero, 1, 1, _light); + fflush(_light); + } + if (thread_measurelag) + { + gettimeofday(&tv, 0); + double t_this = pytime(&tv); + if (loops) + { + if (m < t_this - t_prev) + { + m = t_this - t_prev; + if (_debug) fprintf(_debug, "maximum lag %lf\n", m); + } + } + t_prev = t_this; + } + if (thread_playloop) + { + loop->step(NULL); + } + ++loops; + } + fprintf(stderr, "INFO: aborted performance thread\n"); + return 0; + } + static uintptr_t csThread(void *clientData) + { + return ((TamTamSound*)clientData)->thread_fn(); + } + int start() + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return 1; + } + if (!ThreadID) + { + int argc=3; + char **argv = (char**)malloc(argc*sizeof(char*)); + argv[0] = "csound"; + argv[1] ="-m0"; + argv[2] = csound_orc; + if (_debug) fprintf(_debug, "loading file %s\n", csound_orc); + + csoundInitialize(&argc, &argv, 0); + int result = csoundCompile(csound, argc, &(argv[0])); + free(argv); + + if (!result) + { + PERF_STATUS = CONTINUE; + ThreadID = csoundCreateThread(csThread, (void*)this); + return 0; + } + else + { + if (_debug) fprintf(_debug, "ERROR: failed to compile orchestra\n"); + ThreadID = NULL; + return 1; + } + } + return 1; + } + int stop() + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return 1; + } + if (ThreadID) + { + PERF_STATUS = STOP; + if (_debug) fprintf(_debug, "INFO: stop()"); + uintptr_t rval = csoundJoinThread(ThreadID); + if (rval) if (_debug) fprintf(_debug, "WARNING: thread returned %zu\n", rval); + ThreadID = NULL; + csoundReset(csound); + return 0; + } + return 1; + } + + void scoreEvent(char type, MYFLT * p, int np) + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return ; + } + if ((verbosity > 2) && _debug) + { + 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); + } + void inputMessage(const char * msg) + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return ; + } + if (_debug && (verbosity > 2)) fprintf(_debug, "%s\n", msg); + csoundInputMessage(csound, msg); + } + bool good() + { + return csound != NULL; + } + + void setMasterVolume(MYFLT vol) + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return ; + } + MYFLT *p; + if (!(csoundGetChannelPtr(csound, &p, "masterVolume", CSOUND_CONTROL_CHANNEL | CSOUND_INPUT_CHANNEL))) + *p = (MYFLT) vol; + else + { + if (_debug) fprintf(_debug, "ERROR: failed to set master volume\n"); + } + } + + void setTrackpadX(MYFLT value) + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return ; + } + MYFLT *p; + if (!(csoundGetChannelPtr(csound, &p, "trackpadX", CSOUND_CONTROL_CHANNEL | CSOUND_INPUT_CHANNEL))) + *p = (MYFLT) value; + else + { + fprintf(_debug, "ERROR: failed to set trackpad X value\n"); + } + } + + void setTrackpadY(MYFLT value) + { + if (!csound) { + fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__); + return ; + } + MYFLT *p; + if (!(csoundGetChannelPtr(csound, &p, "trackpadY", CSOUND_CONTROL_CHANNEL | CSOUND_INPUT_CHANNEL))) + *p = (MYFLT) value; + else + { + fprintf(_debug, "ERROR: failed to set trackpad Y value\n"); + } + } +}; + +TamTamSound * sc_tt = NULL; + +static void cleanup(void) +{ + if (sc_tt) + { + delete sc_tt; + sc_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 (sc_tt) + { + delete sc_tt; + sc_tt = NULL; + } + RetNone; +} +//call once at startup, should return 0 +DECL(sc_initialize) //(char * csd) +{ + char * str; + if (!PyArg_ParseTuple(args, "s", &str )) + { + return NULL; + } + sc_tt = new TamTamSound(str); + atexit(&cleanup); + if (sc_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) +{ + if (!PyArg_ParseTuple(args, "" )) + { + return NULL; + } + return Py_BuildValue("i", sc_tt->start()); +} +//stop csound rendering thread, disconnect from sound device, clear tables. +DECL(sc_stop) +{ + if (!PyArg_ParseTuple(args, "" )) + { + return NULL; + } + return Py_BuildValue("i", sc_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); + fprintf(stderr, "writeable buffer of length %zu at %p\n", len, ptr); + float * fptr = (float*)ptr; + size_t flen = len / sizeof(float); + sc_tt->scoreEvent(ev_type, fptr, flen); + + Py_INCREF(Py_None); + return Py_None; + } + else + { + assert(!"asdf"); + } + } + assert(!"not reached"); + return NULL; +} +DECL(sc_setMasterVolume) //(float v) +{ + float v; + if (!PyArg_ParseTuple(args, "f", &v)) + { + return NULL; + } + sc_tt->setMasterVolume(v); + Py_INCREF(Py_None); + return Py_None; +} +DECL(sc_setTrackpadX) //(float v) +{ + float v; + if (!PyArg_ParseTuple(args, "f", &v)) + { + return NULL; + } + sc_tt->setTrackpadX(v); + Py_INCREF(Py_None); + return Py_None; +} +DECL(sc_setTrackpadY) //(float v) +{ + float v; + if (!PyArg_ParseTuple(args, "f", &v)) + { + return NULL; + } + sc_tt->setTrackpadY(v); + Py_INCREF(Py_None); + return Py_None; +} +DECL(sc_loop_getTick) // -> int +{ + if (!PyArg_ParseTuple(args, "" )) + { + return NULL; + } + return Py_BuildValue("i", sc_tt->loop->getTick()); +} +DECL(sc_loop_setNumTicks) //(int nticks) +{ + int nticks; + if (!PyArg_ParseTuple(args, "i", &nticks )) + { + return NULL; + } + sc_tt->loop->setNumTicks(nticks); + RetNone; +} +DECL(sc_loop_setTick) // (int ctick) +{ + int ctick; + if (!PyArg_ParseTuple(args, "i", &ctick )) + { + return NULL; + } + sc_tt->loop->setTick(ctick); + RetNone; +} +DECL(sc_loop_setTickDuration) // (MYFLT secs_per_tick) +{ + float spt; + if (!PyArg_ParseTuple(args, "f", &spt )) + { + return NULL; + } + sc_tt->loop->setTickDuration(spt); + RetNone; +} +DECL(sc_loop_addScoreEvent) // (int id, int duration_in_ticks, char type, farray param) +{ + int qid; + int inticks; + char ev_type; + PyObject *o; + if (!PyArg_ParseTuple(args, "iicO", &qid, &inticks, &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); + fprintf(stderr, "writeable buffer of length %zu at %p\n", len, ptr); + float * fptr = (float*)ptr; + size_t flen = len / sizeof(float); + sc_tt->loop->addEvent(qid, ev_type, fptr, flen, inticks); + + Py_INCREF(Py_None); + return Py_None; + } + else + { + assert(!"asdf"); + } + } + assert(!"not reached"); + return NULL; +} +DECL(sc_loop_delScoreEvent) // (int id) +{ + int id; + if (!PyArg_ParseTuple(args, "i", &id )) + { + return NULL; + } + sc_tt->loop->delEvent(id); + RetNone; +} +DECL(sc_loop_updateEvent) // (int id) +{ + int id; + int idx; + float val; + if (!PyArg_ParseTuple(args, "iif", &id, &idx, &val )) + { + return NULL; + } + sc_tt->loop->updateEvent(id, idx, val); + RetNone; +} +DECL(sc_loop_clear) +{ + if (!PyArg_ParseTuple(args, "" )) + { + return NULL; + } + sc_tt->loop->clear(); + RetNone; +} +DECL(sc_loop_playing) // (int tf) +{ + int i; + if (!PyArg_ParseTuple(args, "i", &i )) + { + return NULL; + } + sc_tt->thread_playloop = i; + RetNone; +} +DECL (sc_inputMessage) //(const char *msg) +{ + char * msg; + if (!PyArg_ParseTuple(args, "s", &msg )) + { + return NULL; + } + sc_tt->inputMessage(msg); + RetNone; +} + +#define MDECL(s) {""#s, s, METH_VARARGS, "documentation of "#s"... nothing!"}, +static PyMethodDef SpamMethods[] = { + {"sc_destroy", sc_destroy, METH_VARARGS,""}, + {"sc_initialize", sc_initialize, METH_VARARGS,""}, + {"sc_start", sc_start, METH_VARARGS,""}, + {"sc_stop", sc_stop, METH_VARARGS,""}, + {"sc_scoreEvent", sc_scoreEvent, METH_VARARGS, ""}, + {"sc_setMasterVolume", sc_setMasterVolume, METH_VARARGS, ""}, + {"sc_setTrackpadX", sc_setTrackpadX, METH_VARARGS, ""}, + {"sc_setTrackpadY", sc_setTrackpadY, METH_VARARGS, ""}, + MDECL(sc_loop_getTick) + MDECL(sc_loop_setNumTicks) + MDECL(sc_loop_setTick) + MDECL(sc_loop_setTickDuration) + MDECL(sc_loop_delScoreEvent) + MDECL(sc_loop_addScoreEvent) // (int id, int duration_in_ticks, char type, farray param) + MDECL(sc_loop_updateEvent) // (int id) + MDECL(sc_loop_clear) + MDECL(sc_loop_playing) + MDECL(sc_inputMessage) + {NULL, NULL, 0, NULL} /*end of list */ +}; + +PyMODINIT_FUNC +initaclient(void) +{ + (void) Py_InitModule("aclient", SpamMethods); +} + + diff --git a/Util/Clooper/aclient.so b/Util/Clooper/aclient.so Binary files differnew file mode 100755 index 0000000..2311d63 --- /dev/null +++ b/Util/Clooper/aclient.so |