M astrid/Makefile => astrid/Makefile +6 -5
@@ 52,11 52,12 @@ astrid-ipc:
echo "Building astrid ipc tools...";
gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/getvoiceid.c $(LPLIBS) -o build/astrid-getvoiceid
- gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/createsharedbuffer.c $(LPLIBS) -o build/astrid-createsharedbuffer
- gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipccreatevalue.c $(LPLIBS) -o build/astrid-ipccreatevalue
- gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipcgetvalue.c $(LPLIBS) -o build/astrid-ipcgetvalue
- gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipcsetvalue.c $(LPLIBS) -o build/astrid-ipcsetvalue
- gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipcdestroyvalue.c $(LPLIBS) -o build/astrid-ipcdestroyvalue
+ #gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/createsharedbuffer.c $(LPLIBS) -o build/astrid-createsharedbuffer
+ #gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipccreatevalue.c $(LPLIBS) -o build/astrid-ipccreatevalue
+ #gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipcgetvalue.c $(LPLIBS) -o build/astrid-ipcgetvalue
+ #gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipcsetvalue.c $(LPLIBS) -o build/astrid-ipcsetvalue
+ #gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/ipcdestroyvalue.c $(LPLIBS) -o build/astrid-ipcdestroyvalue
+ gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/sampler.c $(LPLIBS) -o build/astrid-sampler
astrid-q:
echo "Building astrid queue reader...";
M astrid/orc/pulsar.c => astrid/orc/pulsar.c +10 -26
@@ 4,6 4,7 @@
#define SR 48000
#define CHANNELS 2
+#define ADC_LENGTH 30
#define NUMOSCS (CHANNELS * 10)
#define WTSIZE 4096
@@ 34,7 35,6 @@ enum InstrumentParams {
typedef struct localctx_t {
lppulsarosc_t * oscs[NUMOSCS];
- lpbuffer_t * ringbuf;
lpbuffer_t * env;
int last;
@@ 58,21 58,21 @@ void param_update_callback(void * arg) {
astrid_instrument_set_param_float(instrument, PARAM_PW, LPRand.rand(0.05f, 1.f));
}
+#if 0
lpbuffer_t * renderer_callback(void * arg) {
lpbuffer_t * out;
-
lpinstrument_t * instrument = (lpinstrument_t *)arg;
- localctx_t * ctx = (localctx_t *)instrument->context;
- out = LPBuffer.cut(ctx->ringbuf, LPRand.randint(0, SR*20), LPRand.randint(SR, SR*10));
+ out = LPBuffer.cut(instrument->adcbuf, LPRand.randint(0, instrument->adcbuf->length/2), LPRand.randint(SR, instrument->adcbuf->length-2));
LPFX.norm(out, LPRand.rand(0.26f, 0.5f));
return out;
}
+#endif
-void audio_callback(int channels, size_t blocksize, float ** input, float ** output, void * arg) {
- size_t idx, i;
- int j, c;
+void audio_callback(size_t blocksize, __attribute__((unused)) float ** input, float ** output, void * arg) {
+ size_t i;
+ int j;
lpfloat_t freqs[NUMFREQS];
lpfloat_t sample, left, right, amp, pw, saturation, pan;
lpinstrument_t * instrument = (lpinstrument_t *)arg;
@@ 80,14 80,6 @@ void audio_callback(int channels, size_t blocksize, float ** input, float ** out
if(!instrument->is_running) return;
- for(i=0; i < blocksize; i++) {
- idx = (ctx->ringbuf->pos + i) % ctx->ringbuf->length;
- for(c=0; c < channels; c++) {
- ctx->ringbuf->data[idx * channels + c] = input[c][i];
- }
- }
- ctx->ringbuf->pos += blocksize;
-
amp = astrid_instrument_get_param_float(instrument, PARAM_AMP, 0.08f);
pw = astrid_instrument_get_param_float(instrument, PARAM_PW, 1.f);
astrid_instrument_get_param_float_list(instrument, PARAM_FREQS, NUMFREQS, freqs);
@@ 118,12 110,6 @@ void audio_callback(int channels, size_t blocksize, float ** input, float ** out
while(ctx->curves[j]->phase >= 1.f) ctx->curves[j]->phase -= 1.f;
}
- /*
- for(c=0; c < channels; c++) {
- output[c][i] += (float)sample * 0.5f;
- }
- */
-
left = right = sample;
pan_stereo_constant(pan, left, right, &left, &right);
@@ 142,9 128,8 @@ int main() {
exit(1);
}
- // create env and ringbuf
+ // create env window for the footballs
ctx->env = LPWindow.create(WIN_HANNOUT, 4096);
- ctx->ringbuf = LPBuffer.create(SR * 30, CHANNELS, SR);
// setup oscs and curves
for(int i=0; i < NUMOSCS; i++) {
@@ 163,8 148,8 @@ int main() {
}
// Set the callbacks for streaming, async renders and param updates
- if((instrument = astrid_instrument_start(NAME, CHANNELS, (void*)ctx,
- audio_callback, renderer_callback, param_update_callback)) == NULL) {
+ if((instrument = astrid_instrument_start(NAME, CHANNELS, ADC_LENGTH, (void*)ctx,
+ audio_callback, NULL, param_update_callback)) == NULL) {
fprintf(stderr, "Could not start instrument: (%d) %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
@@ 192,7 177,6 @@ int main() {
LPBuffer.destroy(ctx->curves[o]);
}
- LPBuffer.destroy(ctx->ringbuf);
LPBuffer.destroy(ctx->env);
free(ctx);
M astrid/orc/simple.py => astrid/orc/simple.py +20 -2
@@ 1,12 1,30 @@
-from pippi import dsp, oscs, renderer, fx
+from pippi import dsp, oscs, renderer, fx, ugens
+
+def stream(ctx):
+ graph = ugens.Graph()
+ graph.add_node('s0b', 'sine', freq=0.1)
+ graph.add_node('s0a', 'sine', freq=0.1)
+ graph.add_node('s0', 'sine', freq=100)
+ graph.add_node('s1', 'sine')
+ graph.add_node('s2', 'sine')
+
+ graph.connect('s0b.output', 's0a.freq', 0.1, 200)
+ graph.connect('s0a.output', 's0.freq', 100, 300)
+ graph.connect('s0.output', 's1.freq', 0.1, 1)
+ graph.connect('s0.output', 'main.output', mult=0.1)
+ graph.connect('s1.output', 's2.freq', 60, 100)
+ graph.connect('s2.output', 'main.output', mult=0.2)
+ graph.connect('s2.freq', 's0b.freq', 0.15, 0.5, 60, 100)
+
+ return graph
def trigger(ctx):
return [ctx.t.play(0, 'simple'), ctx.t.trigger(dsp.rand(0.5, 1), 'simple')]
- #return None
def play(ctx):
ctx.log('Rendering simple tone!')
out = oscs.SineOsc(freq=dsp.rand(200, 1000), amp=dsp.rand(0.1, 0.5)).play(10).env('pluckout')
+ out = out & ctx.adc(out.dur)
if dsp.rand() > 0.5:
out = fx.fold(out, dsp.rand(1, 2))
M astrid/src/astrid.c => astrid/src/astrid.c +308 -25
@@ 907,6 907,261 @@ int lpadc_read_block_of_samples(size_t offset, size_t size, lpfloat_t * out) {
return 0;
}
+int lpsampler_get_path(char * name, char * path) {
+ snprintf(path, PATH_MAX, "/astrid-sampler-%s", name);
+ return 0;
+}
+
+int lpsampler_get_data_path(char * name, char * path) {
+ snprintf(path, PATH_MAX, "/astrid-sampler-%s-data", name);
+ return 0;
+}
+
+int lpsampler_create(char * name, double length_in_seconds, int channels, int samplerate) {
+ int semvalue, shmfd;
+ sem_t * sem;
+ lpbuffer_t * buf;
+ size_t bufsize;
+ char path[PATH_MAX] = {0};
+ size_t length = (size_t)(length_in_seconds * samplerate);
+
+ lpsampler_get_path(name, path);
+
+ /* Determine the size of the shared memory segment */
+ bufsize = sizeof(lpbuffer_t) + (length * channels * sizeof(lpfloat_t));
+ printf("bufsize=%ld samplerate=%d length=%ld channels=%d\n",
+ bufsize, samplerate, length, channels);
+
+ /* Create the POSIX semaphore and initialize it to 1 */
+ if((sem = sem_open(path, O_CREAT, LPIPC_PERMS, 1)) == NULL) {
+ syslog(LOG_ERR, "lpipc_buffer_create Could not create semaphore. (%s) %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if(sem_getvalue(sem, &semvalue) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_create Could not get semaphore value. (%s) %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ /* initialize the shared memory segment for the lpbuffer_t struct */
+ if((shmfd = shm_open(path, O_CREAT | O_RDWR, LPIPC_PERMS)) < 0) {
+ syslog(LOG_ERR, "Could not create shared memory segment. (%s) %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if(ftruncate(shmfd, bufsize) < 0) {
+ syslog(LOG_ERR, "Could not truncate shared memory segment to size %ld. (%s) %s\n", sizeof(lpbuffer_t), path, strerror(errno));
+ return -1;
+ }
+
+ if((buf = mmap(NULL, bufsize, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)) == MAP_FAILED) {
+ syslog(LOG_ERR, "Could not mmap shared memory segment to size %ld. (%s) %s\n", sizeof(lpbuffer_t), path, strerror(errno));
+ return -1;
+ }
+
+ memset(buf, 0, bufsize);
+ buf->channels = channels;
+ buf->length = length;
+ buf->samplerate = samplerate;
+ buf->boundry = length-1;
+ buf->range = length;
+
+ return 0;
+}
+
+int lpsampler_aquire(char * name, lpbuffer_t ** buf) {
+ struct stat statbuf;
+ int fd;
+ sem_t * sem;
+ char path[PATH_MAX] = {0};
+ lpsampler_get_path(name, path);
+
+ /* Open the semaphore */
+ if((sem = sem_open(path, 0)) == SEM_FAILED) {
+ syslog(LOG_ERR, "lpipc_buffer_aquire failed to open semaphore %s. Error: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ /* Aquire a lock on the semaphore */
+ if(sem_wait(sem) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_aquire failed to decrementsem %s. Error: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ /* Get the file descriptor for the shared memory segment */
+ if((fd = shm_open(path, O_RDWR, LPIPC_PERMS)) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_aquire Could not open shared memory segment. (%s) %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ /* Get the size of the segment */
+ if(fstat(fd, &statbuf) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_aquire Could not stat shm. Error: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* Attach the shared memory to the pointer */
+ if((*buf = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ syslog(LOG_ERR, "lpipc_buffer_aquire Could not mmap shared memory segment to size %ld. (%s) %s\n", statbuf.st_size, path, strerror(errno));
+ return -1;
+ }
+
+ /* Clean up sempahore resources */
+ if(sem_close(sem) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_aquire sem_close Could not close semaphore\n");
+ return -1;
+ }
+
+ //close(fd);
+
+ return 0;
+}
+
+int lpsampler_release(char * name) {
+ sem_t * sem;
+ char path[PATH_MAX] = {0};
+ lpsampler_get_path(name, path);
+
+ /* Open the semaphore */
+ if((sem = sem_open(path, 0)) == SEM_FAILED) {
+ syslog(LOG_ERR, "lpipc_buffer_release failed to open semaphore %s. Error: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ /* Release the lock on the semaphore */
+ if(sem_post(sem) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_release failed to unlock %s. Error: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ /* Clean up sempahore resources */
+ if(sem_close(sem) < 0) {
+ syslog(LOG_ERR, "lpipc_buffer_release sem_close Could not close semaphore\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int lpsampler_destroy(char * name) {
+ char path[PATH_MAX] = {0};
+ lpsampler_get_path(name, path);
+
+ /* Unlink the shared memory buffer */
+ if(shm_unlink(path) < 0) {
+ syslog(LOG_ERR, "lpsampler_destroy shm_unlink. Error: %s\n", strerror(errno));
+ return -1;
+
+ }
+
+ /* Unlink the sempahore */
+ if(sem_unlink(path) < 0) {
+ syslog(LOG_ERR, "lpsampler_destroy sem_unlink Could not destroy semaphore\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int lpsampler_write_ringbuffer_block(char * name, float ** block, int channels, size_t blocksize_in_samples) {
+ size_t write_pos, insert_pos, i, boundry;
+ int c;
+ lpbuffer_t * buf;
+ float sample = 0;
+ char path[PATH_MAX] = {0};
+
+ boundry = blocksize_in_samples * channels;
+ lpsampler_get_path(name, path);
+
+ /* Aquire a lock on the buffer */
+ if(lpsampler_aquire(name, &buf) < 0) {
+ syslog(LOG_ERR, "lpsampler_write_ringbuffer_block: Could not aquire ADC buffer shm for update\n");
+ return -1;
+ }
+
+ printf("pos: %ld\n", buf->pos);
+ write_pos = buf->pos;
+
+ /* Copy the block of samples */
+ for(i=0; i < blocksize_in_samples; i++) {
+ for(c=0; c < channels; c++) {
+ insert_pos = ((write_pos+i) * channels + c) % boundry;
+ sample = *block[c]++;
+ buf->data[insert_pos] = sample;
+ printf("sample: %f\n", buf->data[insert_pos]);
+ }
+ }
+
+ /* Increment the write position */
+ write_pos += blocksize_in_samples;
+ while(write_pos >= boundry) {
+ write_pos -= boundry;
+ }
+
+ /* Store the new write position */
+ buf->pos = write_pos;
+ printf("pos: %ld\n", buf->pos);
+
+ /* Release the lock on the ADC buffer shm */
+ if(lpsampler_release(name) < 0) {
+ syslog(LOG_ERR, "lpsampler_write_ringbuffer_block: Could not release buffer shm after update\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int lpsampler_read_block_of_samples(char * name, size_t offset, size_t size, lpfloat_t * out) {
+ size_t read_pos, start, end, readsize, lastreadsize, boundry;
+ lpbuffer_t * buf;
+ char path[PATH_MAX] = {0};
+
+ lpsampler_get_path(name, path);
+ boundry = buf->length * buf->channels;
+
+ /* Aquire a lock on the buffer */
+ if(lpsampler_aquire(name, &buf) < 0) {
+ syslog(LOG_ERR, "Could not aquire ADC buffer shm for update\n");
+ return -1;
+ }
+
+ /* Maximum read size == buffer length */
+ if(size > boundry) size = boundry;
+
+ /* Get the read position with the offset and read as far
+ * to the start of the buffer before wrapping as possible */
+ if(buf->pos >= offset) {
+ read_pos = (buf->pos - offset) % boundry;
+ } else {
+ read_pos = (buf->pos + (boundry - offset)) % boundry;
+ }
+
+ start = read_pos >= size ? read_pos - size : 0;
+ end = read_pos;
+ readsize = end - start;
+ memcpy(out, buf->data + start, readsize * sizeof(lpfloat_t));
+
+ /* If there are remaining samples to be read on the other end of
+ * the buffer, read those samples back from the end. */
+if (readsize < size) {
+ lastreadsize = readsize;
+ start = boundry - (size - readsize);
+ end = boundry;
+ readsize = end - start;
+ memcpy(out + lastreadsize, buf->data + start, readsize * sizeof(lpfloat_t));
+ }
+
+ /* Release the lock on the buffer shm */
+ if(lpsampler_release(name) < 0) {
+ syslog(LOG_ERR, "Could not release ADC buffer shm after update\n");
+ return -1;
+ }
+
+ return 0;
+}
+
int lpadc_read_sample(size_t offset, lpfloat_t * sample) {
size_t write_pos;
@@ 1523,7 1778,7 @@ lpbuffer_t * deserialize_buffer(char * buffer_code, lpmsg_t * msg) {
buf->channels = channels;
buf->samplerate = samplerate;
buf->is_looping = is_looping;
- buf->data = audio;
+ memcpy(buf->data, audio, audiosize);
buf->onset = onset;
buf->phase = 0.f;
@@ 2369,12 2624,12 @@ void scheduler_cleanup_nursery(lpscheduler_t * s) {
}
}
-int send_serial_message(lpmsg_t msg) {
+int send_serial_message(lpmsg_t msg, char * tty) {
int fp;
ssize_t bytes_written;
- if((fp = open(msg.instrument_name, O_WRONLY)) < 0) {
- syslog(LOG_CRIT, "Could not open %s for writing\n", msg.instrument_name);
+ if((fp = open(tty, O_WRONLY)) < 0) {
+ syslog(LOG_CRIT, "Could not open %s for writing\n", tty);
return -1;
}
@@ 2398,6 2653,9 @@ int astrid_instrument_jack_callback(jack_nframes_t nframes, void * arg) {
float * input_channels[instrument->channels];
size_t i;
int c;
+ char path[PATH_MAX];
+
+ snprintf(path, PATH_MAX, "%s-adc", instrument->name);
if(!instrument->is_running) return 0;
@@ 2415,6 2673,11 @@ int astrid_instrument_jack_callback(jack_nframes_t nframes, void * arg) {
memset(output_channels[c], 0, nframes * sizeof(float));
}
+ /* write the block into the adc ringbuffer */
+ if(lpsampler_write_ringbuffer_block(path, input_channels, instrument->channels, nframes) < 0) {
+ syslog(LOG_ERR, "Error writing into adc ringbuf\n");
+ return 0;
+ }
/* mix in async renders */
if(instrument->async_mixer != NULL) {
@@ 2428,7 2691,6 @@ int astrid_instrument_jack_callback(jack_nframes_t nframes, void * arg) {
if(instrument->stream != NULL) {
instrument->stream(
- instrument->channels,
(size_t)nframes,
input_channels, output_channels,
(void *)instrument
@@ 2748,8 3010,9 @@ int astrid_instrument_seq_start(lpinstrument_t * instrument) {
lpinstrument_t * astrid_instrument_start(
const char * name,
int channels,
+ double adc_length,
void * ctx,
- void (*stream)(int channels, size_t blocksize, float ** input, float ** output, void * instrument),
+ void (*stream)(size_t blocksize, float ** input, float ** output, void * instrument),
lpbuffer_t * (*renderer)(void * instrument),
void (*updates)(void * instrument)
) {
@@ 2796,17 3059,6 @@ lpinstrument_t * astrid_instrument_start(
exit(1);
}
- /* init scheduler
- *
- * The scheduler is shared between the miniaudio callback
- * and astrid buffer feed threads. The buffer feed thread
- * may schedule buffers by adding them to the internal linked
- * list in the scheduler. The miniaudio callback may read from
- * the linked list, increment counts in playing buffers and
- * flag buffers as having playback completed.
- **/
- instrument->async_mixer = scheduler_create(1, instrument->channels, instrument->samplerate);
-
// Set the message q names
snprintf(instrument->qname, NAME_MAX, "/%s-msgq", instrument->name);
snprintf(instrument->external_relay_name, NAME_MAX, "/%s-extrelay-msgq", instrument->name);
@@ 2833,9 3085,31 @@ lpinstrument_t * astrid_instrument_start(
if((jack_status & JackServerStarted) == JackServerStarted) {
syslog(LOG_INFO, "Jack server started!\n");
+ }
+
+ /* Get the samplerate from JACK */
+ /* FIXME could use the JACK D-Bus API here to let instruments change the samplerate? */
+ instrument->samplerate = (lpfloat_t)jack_get_sample_rate(instrument->jack_client);
+
+ /* Create the internal ringbuffer for sampling from the adc */
+ snprintf(instrument->adc_name, PATH_MAX, "%s-adc", instrument->name);
+ if(lpsampler_create(instrument->adc_name, adc_length, instrument->channels, instrument->samplerate) < 0) {
+ syslog(LOG_INFO, "Could not create instrument ADC buffer\n");
goto astrid_instrument_shutdown_with_error;
}
+ /* init scheduler
+ *
+ * The scheduler is shared between the miniaudio callback
+ * and astrid buffer feed threads. The buffer feed thread
+ * may schedule buffers by adding them to the internal linked
+ * list in the scheduler. The miniaudio callback may read from
+ * the linked list, increment counts in playing buffers and
+ * flag buffers as having playback completed.
+ **/
+ instrument->async_mixer = scheduler_create(1, instrument->channels, instrument->samplerate);
+
+
jack_set_process_callback(instrument->jack_client, astrid_instrument_jack_callback, (void *)instrument);
for(c=0; c < channels; c++) {
snprintf(outport_name, sizeof(outport_name), "out%d", c);
@@ 2864,25 3138,25 @@ lpinstrument_t * astrid_instrument_start(
/* connect ports */
if((ports = jack_get_ports(instrument->jack_client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) {
- fprintf(stderr, "Cannot find any physical capture ports");
- exit(1);
+ syslog(LOG_CRIT, "Cannot find any physical capture ports");
+ goto astrid_instrument_shutdown_with_error;
}
for(c=0; c < instrument->channels; c++) {
if(jack_connect(instrument->jack_client, ports[c], jack_port_name(instrument->inports[c]))) {
- fprintf(stderr, "cannot connect input ports\n");
+ syslog(LOG_NOTICE, "Could not connect input port %d. (There probably aren't enough available inputs on this device.)\n", c);
}
}
free(ports);
if((ports = jack_get_ports(instrument->jack_client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) {
- fprintf(stderr, "Cannot find any physical playback ports");
- exit(1);
+ syslog(LOG_CRIT, "Cannot find any physical playback ports");
+ goto astrid_instrument_shutdown_with_error;
}
for(c=0; c < instrument->channels; c++) {
if(jack_connect(instrument->jack_client, jack_port_name(instrument->outports[c]), ports[c])) {
- fprintf(stderr, "cannot connect output ports\n");
+ syslog(LOG_NOTICE, "Could not connect output port %d. (There probably aren't enough available outputs on this device.)\n", c);
}
}
free(ports);
@@ 2944,6 3218,8 @@ int astrid_instrument_stop(lpinstrument_t * instrument) {
}
}
+
+
syslog(LOG_DEBUG, "Stopping message seq threads...\n");
syslog(LOG_DEBUG, "Joining with message thread...\n");
@@ 2978,6 3254,13 @@ int astrid_instrument_stop(lpinstrument_t * instrument) {
if(instrument->async_mixer != NULL) scheduler_destroy(instrument->async_mixer);
+ syslog(LOG_DEBUG, "Cleaning up adc ringbuf...\n");
+ if(lpsampler_destroy(instrument->adc_name) < 0) {
+ syslog(LOG_ERR, "Error while removing adc ringbuf, dang! Other cleanup is done tho.\n");
+ closelog();
+ return -1;
+ }
+
syslog(LOG_DEBUG, "All done, see ya later!\n");
closelog();
return 0;
@@ 3181,7 3464,7 @@ int astrid_instrument_console_readline(char * instrument_name) {
free(line);
if(cmd.type == LPMSG_SERIAL) {
- if(send_serial_message(cmd) < 0) {
+ if(send_serial_message(cmd, cmd.instrument_name) < 0) {
syslog(LOG_ERR, "Could not send serial message...\n");
return -1;
}
@@ 3217,7 3500,7 @@ int astrid_instrument_tick(lpinstrument_t * instrument) {
if(line != NULL) {
if(instrument->cmd.type == LPMSG_SERIAL) {
- if(send_serial_message(instrument->cmd) < 0) {
+ if(send_serial_message(instrument->cmd, instrument->cmd.instrument_name) < 0) {
syslog(LOG_ERR, "Could not send serial message...\n");
return -1;
}
M astrid/src/astrid.h => astrid/src/astrid.h +16 -4
@@ 106,6 106,7 @@ typedef struct lpmsgpq_node_t {
* they schedule them (with the onset value, which
* is relative to *now*) by sending this struct over
* the MIDI trigger fifo. */
+// FIXME these can probably just be normal lpmsg_t messages...?
typedef struct lpmidievent_t {
double onset;
double now;
@@ 171,6 172,9 @@ typedef struct lpinstrument_t {
// the XDG config dir where LMDB sessions live
char datapath[PATH_MAX];
+ // The adc ringbuf name
+ char adc_name[PATH_MAX];
+
// The instrument message q(s)
char qname[NAME_MAX];
char external_relay_name[NAME_MAX]; // just python, really
@@ 204,7 208,7 @@ typedef struct lpinstrument_t {
lpbuffer_t * (*renderer)(void * instrument);
// Stream callback
- void (*stream)(int channels, size_t blocksize, float ** input, float ** output, void * instrument);
+ void (*stream)(size_t blocksize, float ** input, float ** output, void * instrument);
// Shutdown signal handler (SIGTERM & SIGKILL)
void (*shutdown)(int sig);
@@ 266,7 270,7 @@ int parse_message_from_cmdline(char * cmdline, lpmsg_t * msg);
ssize_t astrid_get_voice_id();
int send_message(char * qname, lpmsg_t msg);
-int send_serial_message(lpmsg_t msg);
+int send_serial_message(lpmsg_t msg, char * tty);
int send_play_message(lpmsg_t msg);
int get_play_message(char * instrument_name, lpmsg_t * msg);
@@ 306,16 310,24 @@ int lpadc_aquire(lpipc_buffer_t ** adcbuf);
int lpadc_increment_and_release(lpipc_buffer_t * adcbuf, size_t increment_in_samples);
int lpadc_write_block(const void * block, size_t blocksize);
int lpadc_read_sample(size_t offset, lpfloat_t * sample);
-/*int lpadc_read_block_of_samples(size_t offset, size_t size, lpfloat_t (*out)[LPADCBUFSAMPLES]);*/
int lpadc_read_block_of_samples(size_t offset, size_t size, lpfloat_t * out);
+int lpsampler_aquire(char * name, lpbuffer_t ** buf);
+int lpsampler_release(char * name);
+int lpsampler_read_block_of_samples(char * name, size_t offset, size_t size, lpfloat_t * out);
+int lpsampler_write_ringbuffer_block(char * name, float ** block, int channels, size_t blocksize_in_samples);
+int lpsampler_create(char * name, double length_in_seconds, int channels, int samplerate);
+int lpsampler_destroy(char * name);
+
int lpipc_buffer_create(char * id_path, size_t length, int channels, int samplerate);
int lpipc_buffer_aquire(char * id_path, lpipc_buffer_t ** buf);
int lpipc_buffer_release(char * id_path);
int lpipc_buffer_tolpbuffer(lpipc_buffer_t * ipcbuf, lpbuffer_t ** buf);
int lpipc_buffer_destroy(char * id_path);
+
int lpipc_setid(char * path, int id);
int lpipc_getid(char * path);
+
int lpipc_createvalue(char * path, size_t size);
int lpipc_setvalue(char * path, void * value, size_t size);
int lpipc_getvalue(char * path, void ** value);
@@ 324,7 336,7 @@ int lpipc_destroyvalue(char * id_path);
void lptimeit_since(struct timespec * start);
-lpinstrument_t * astrid_instrument_start(const char * name, int channels, void * ctx, void (*stream)(int channels, size_t blocksize, float ** input, float ** output, void * instrument), lpbuffer_t * (*renderer)(void * instrument), void (*updates)(void * instrument));
+lpinstrument_t * astrid_instrument_start(const char * name, int channels, double adc_length, void * ctx, void (*stream)(size_t blocksize, float ** input, float ** output, void * instrument), lpbuffer_t * (*renderer)(void * instrument), void (*updates)(void * instrument));
int astrid_instrument_stop(lpinstrument_t * instrument);
void astrid_instrument_set_param_float(lpinstrument_t * instrument, int param_index, lpfloat_t value);
M astrid/src/msg.c => astrid/src/msg.c +1 -1
@@ 13,7 13,7 @@ int main(int argc, char * argv[]) {
syslog(LOG_DEBUG, " %d (msg.type)\n", (int)msg.type);
if(msg.type == LPMSG_SERIAL) {
- if(send_serial_message(msg) < 0) {
+ if(send_serial_message(msg, msg.instrument_name) < 0) {
fprintf(stderr, "astrid-msg: Could not send serial message...\n");
return 1;
}
M banner.png => banner.png +0 -0
M libpippi/Makefile => libpippi/Makefile +2 -2
@@ 161,8 161,8 @@ convolution-examples:
echo "Building convolution.c example...";
gcc $(LPFLAGS) examples/convolution.c $(LPSOURCES) $(LPLIBS) -o build/convolution
- echo "Building convolution2.c example...";
- gcc $(LPFLAGS) examples/convolution2.c $(LPSOURCES) $(LPLIBS) -o build/convolution2
+ #echo "Building convolution2.c example...";
+ #gcc $(LPFLAGS) examples/convolution2.c $(LPSOURCES) $(LPLIBS) -o build/convolution2
soundfile-examples:
mkdir -p build renders
M libpippi/examples/crossings.c => libpippi/examples/crossings.c +4 -0
@@ 31,6 31,10 @@ int main() {
}
/* increment the read index and add a fixed padding */
wavesets[c]->boundry += wavesets[c]->pos + 222;
+ if(wavesets[c]->boundry >= wavesets[c]->length) {
+ wavesets[c]->boundry -= wavesets[c]->length;
+ }
+
/* reset the position - new waveset */
wavesets[c]->pos = 0;
} else {
A libpippi/examples/raw.png => libpippi/examples/raw.png +0 -0
M libpippi/scripts/render_examples.sh => libpippi/scripts/render_examples.sh +1 -0
@@ 1,4 1,5 @@
#!/bin/bash
+set -e
for b in build/*; do
echo
M libpippi/src/oscs.pulsar.c => libpippi/src/oscs.pulsar.c +4 -0
@@ 96,6 96,8 @@ void create_pulsarosc_wavetable_stack(lppulsarosc_t * p, int numtables, va_list
LPMemoryPool.free(p->wavetable_onsets);
LPMemoryPool.free(p->wavetable_lengths);
+ if(numtables == 0) return;
+
p->num_wavetables = numtables;
p->wavetable_onsets = (size_t *)LPMemoryPool.alloc(numtables, sizeof(size_t));
p->wavetable_lengths = (size_t *)LPMemoryPool.alloc(numtables, sizeof(size_t));
@@ 107,6 109,8 @@ void create_pulsarosc_window_stack(lppulsarosc_t * p, int numtables, va_list vl)
LPMemoryPool.free(p->window_onsets);
LPMemoryPool.free(p->window_lengths);
+ if(numtables == 0) return;
+
p->num_windows = numtables;
p->window_onsets = (size_t *)LPMemoryPool.alloc(numtables, sizeof(size_t));
p->window_lengths = (size_t *)LPMemoryPool.alloc(numtables, sizeof(size_t));
M libpippi/src/oscs.tape.c => libpippi/src/oscs.tape.c +2 -2
@@ 55,7 55,7 @@ void process_tapeosc(lptapeosc_t * osc) {
channels = osc->buf->channels;
boundry = osc->range + osc->start;
- printf("phase %f channels %i boundry %ld speed %f\n", osc->phase, channels, boundry, osc->speed);
+ //printf("phase %f channels %i boundry %ld speed %f\n", osc->phase, channels, boundry, osc->speed);
f = osc->phase - (int)osc->phase;
idxa = (size_t)osc->phase;
@@ 69,7 69,7 @@ void process_tapeosc(lptapeosc_t * osc) {
}
osc->phase += osc->speed;
- printf("phase %f channels %i boundry %ld speed %f\n", osc->phase, channels, boundry, osc->speed);
+ //printf("phase %f channels %i boundry %ld speed %f\n", osc->phase, channels, boundry, osc->speed);
if(osc->phase >= boundry) {
osc->phase = osc->start;
M libpippi/src/pippicore.c => libpippi/src/pippicore.c +4 -14
@@ 298,29 298,20 @@ void destroy_array(lparray_t * array) {
* */
lpbuffer_t * create_buffer(size_t length, int channels, int samplerate) {
lpbuffer_t * buf;
- buf = (lpbuffer_t*)LPMemoryPool.alloc(1, sizeof(lpbuffer_t));
+ size_t bufsize = sizeof(lpbuffer_t) + (length * channels * sizeof(lpfloat_t));
+
+ buf = (lpbuffer_t*)LPMemoryPool.alloc(1, bufsize);
if(buf == NULL) {
fprintf(stderr, "Could not alloc memory for buffer struct\n");
return NULL;
}
- buf->data = (lpfloat_t*)LPMemoryPool.alloc(length * channels, sizeof(lpfloat_t));
- if(buf->data == NULL) {
- fprintf(stderr, "Could not alloc memory for buffer data\n");
- if(buf != NULL) LPMemoryPool.free(buf);
- return NULL;
- }
- memset(buf->data, 0, length * channels * sizeof(lpfloat_t));
-
+ memset(buf, 0, bufsize);
buf->channels = channels;
buf->length = length;
buf->samplerate = samplerate;
- buf->phase = 0.f;
- buf->pos = 0;
buf->boundry = length-1;
buf->range = length;
- buf->onset = 0;
- buf->is_looping = 0;
return buf;
}
@@ 1224,7 1215,6 @@ lpbuffer_t * resize_buffer(lpbuffer_t * buf, size_t length) {
void destroy_buffer(lpbuffer_t * buf) {
if(buf != NULL) {
- LPMemoryPool.free(buf->data);
LPMemoryPool.free(buf);
}
}
M libpippi/src/pippitypes.h => libpippi/src/pippitypes.h +1 -10
@@ 7,7 7,6 @@ typedef double lpfloat_t;
/* Core datatypes */
typedef struct lpbuffer_t {
- lpfloat_t * data;
size_t length;
int samplerate;
int channels;
@@ 19,6 18,7 @@ typedef struct lpbuffer_t {
size_t pos;
size_t onset;
int is_looping;
+ lpfloat_t data[];
} lpbuffer_t;
// Used for messaging between astrid instruments,
@@ 86,15 86,6 @@ typedef struct lpmsg_t {
char instrument_name[LPMAXNAME];
} lpmsg_t;
-typedef struct lpserialevent_t {
- double onset;
- double now;
- double length;
- lpmsg_t msg;
- int group;
- int device;
-} lpserialevent_t;
-
typedef struct lparray_t {
int * data;
size_t length;
M pippi/buffers.pxd => pippi/buffers.pxd +4 -2
@@ 35,14 35,16 @@ cdef extern from "pippicore.h":
NUM_PANMETHODS
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
-
lpfloat_t phase
size_t boundry
+ size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lpwavetable_factory_t:
lpbuffer_t * (*create)(int name, size_t length)
M pippi/grains2.pxd => pippi/grains2.pxd +3 -1
@@ 18,7 18,6 @@ cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
@@ 26,6 25,9 @@ cdef extern from "pippicore.h":
size_t boundry
size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lpstack_t:
lpbuffer_t ** stack
M pippi/mir.pxd => pippi/mir.pxd +3 -1
@@ 8,7 8,6 @@ cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
@@ 16,6 15,9 @@ cdef extern from "pippicore.h":
size_t boundry
size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
cdef extern from "mir.h":
ctypedef struct lpyin_t:
M pippi/noise.pxd => pippi/noise.pxd +3 -1
@@ 13,7 13,6 @@ cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
@@ 21,6 20,9 @@ cdef extern from "pippicore.h":
size_t boundry
size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lpstack_t:
lpbuffer_t ** stack
M pippi/pulsar2d.pxd => pippi/pulsar2d.pxd +3 -1
@@ 5,7 5,6 @@ from pippi.interpolation cimport interp_point_t
cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
@@ 13,6 12,9 @@ cdef extern from "pippicore.h":
size_t boundry
size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lparray_t:
int * data
M pippi/renderer.pxd => pippi/renderer.pxd +33 -37
@@ 7,14 7,16 @@ cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
-
lpfloat_t phase
size_t boundry
+ size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lpbuffer_factory_t:
lpbuffer_t * (*create)(size_t, int, int)
@@ 73,10 75,10 @@ cdef extern from "astrid.h":
NUM_LPMESSAGETYPES
ctypedef struct lpmsg_t:
- double initiated;
- double scheduled;
- double completed;
- double max_processing_time;
+ double initiated
+ double scheduled
+ double completed
+ double max_processing_time
size_t onset_delay
size_t voice_id
size_t count
@@ 97,13 99,6 @@ cdef extern from "astrid.h":
char bank_lsb
char channel
- ctypedef struct lpserialevent_t:
- double onset
- double now
- lpmsg_t msg
- int group
- int device
-
ctypedef struct lpinstrument_t:
const char * name
int channels
@@ 121,12 116,7 @@ cdef extern from "astrid.h":
lpscheduler_t * async_mixer
- int lpadc_create()
- int lpadc_destroy()
- int lpadc_write_block(float * block, size_t blocksize_in_samples)
- int lpadc_read_sample(size_t pos, lpfloat_t * sample)
- #int lpadc_read_block_of_samples(size_t offset, size_t size, lpfloat_t (*out)[LPADCBUFSAMPLES])
- int lpadc_read_block_of_samples(size_t offset, size_t size, lpfloat_t * out)
+ int lpsampler_read_block_of_samples(char * name, size_t offset, size_t size, lpfloat_t * out)
int lpipc_getid(char * path)
ssize_t astrid_get_voice_id()
@@ 157,8 147,9 @@ cdef extern from "astrid.h":
lpinstrument_t * astrid_instrument_start(
const char * name,
int channels,
+ double adc_length,
void * ctx,
- void (*stream)(int channels, size_t blocksize, float ** input, float ** output, void * instrument),
+ void (*stream)(size_t blocksize, float ** input, float ** output, void * instrument),
lpbuffer_t * (*renderer)(void * instrument),
void (*updates)(void * instrument)
)
@@ 168,6 159,7 @@ cdef extern from "astrid.h":
int astrid_instrument_console_readline(char * instrument_name)
int relay_message_to_seq(lpinstrument_t * instrument)
int send_play_message(lpmsg_t msg)
+ int send_serial_message(lpmsg_t msg, char * tty);
cdef class MessageEvent:
cdef lpmsg_t * msg
@@ 177,10 169,6 @@ cdef class MidiEvent:
cdef lpmidievent_t * event
cpdef int schedule(MidiEvent self, double now)
-cdef class SerialEvent:
- cdef lpserialevent_t * event
- cpdef int schedule(SerialEvent self, double now)
-
cdef class SessionParamBucket:
cdef object _bus
@@ 201,6 189,21 @@ cdef class MidiEventListenerProxy:
cdef class SerialEventListenerProxy:
cpdef lpfloat_t ctl(self, int ctl, int device_id=*)
+cdef class Instrument:
+ cdef public str name
+ cdef public str path
+ cdef public object renderer
+ cdef public object graph
+ cdef public dict cache
+ cdef public lpmsg_t msg # a copy of the last message received
+ cdef public size_t last_reload
+ cdef public double max_processing_time
+ cdef public int default_midi_device
+ cdef lpinstrument_t * i
+ cpdef EventContext get_event_context(Instrument self, bint with_graph=*)
+ cpdef lpmsg_t get_message(Instrument self)
+ cdef SoundBuffer read_from_adc(Instrument self, double length, double offset=*, int channels=*, int samplerate=*)
+
cdef class EventContext:
cdef public dict cache
cdef public ParamBucket p
@@ 210,23 213,16 @@ cdef class EventContext:
cdef public SerialEventListenerProxy b
cdef public str instrument_name
cdef public object sounds
+ cdef public object graph
cdef public int count
cdef public int tick
cdef public int vid
cdef public double now
cdef public double max_processing_time
+ cdef public Instrument instrument
-cdef class Instrument:
- cdef public str name
- cdef public str path
- cdef public object renderer
- cdef public dict cache
- cdef public size_t last_reload
- cdef public double max_processing_time
- cdef public int default_midi_device
-
-cdef tuple collect_players(object instrument)
-cdef int render_event(object instrument, lpmsg_t * msg)
-cdef set collect_trigger_planners(object instrument)
-cdef int trigger_events(object instrument, lpmsg_t * msg)
+cdef tuple collect_players(Instrument instrument)
+cdef int render_event(Instrument instrument)
+cdef set collect_trigger_planners(Instrument instrument)
+cdef int trigger_events(Instrument instrument)
M pippi/renderer.pyx => pippi/renderer.pyx +172 -168
@@ 18,10 18,13 @@ import sys
import time
import warnings
-from pippi import dsp, midi
+from pippi import dsp, midi, ugens
from pippi.soundbuffer cimport SoundBuffer
+class InstrumentError(Exception):
+ pass
+
logger = logging.getLogger('astrid-cyrenderer')
if not logger.handlers:
if platform.system() == 'Darwin':
@@ 35,7 38,7 @@ if not logger.handlers:
cdef lpfloat_t[LPADCBUFSAMPLES] adc_block
-cdef bytes serialize_buffer(SoundBuffer buf, int is_looping, lpmsg_t * msg):
+cdef bytes serialize_buffer(SoundBuffer buf, int is_looping, lpmsg_t msg):
cdef bytearray strbuf
cdef size_t audiosize, length, msgsize
cdef int channels, samplerate
@@ 63,31 66,12 @@ cdef bytes serialize_buffer(SoundBuffer buf, int is_looping, lpmsg_t * msg):
for c in range(channels):
strbuf += struct.pack('d', buf.frames[i,c])
- msgview = <unsigned char[:msgsize]>(<unsigned char *>msg)
+ msgview = <unsigned char[:msgsize]>(<unsigned char *>&msg)
for i in range(msgsize):
strbuf.append(msgview[i])
return bytes(strbuf)
-cdef SoundBuffer read_from_adc(double length, double offset=0, int channels=2, int samplerate=48000):
- cdef size_t i
- cdef int c
-
- cdef SoundBuffer snd = SoundBuffer(length=length, channels=channels, samplerate=samplerate)
- cdef size_t length_in_frames = len(snd)
- cdef size_t offset_in_frames = <size_t>(offset * samplerate)
-
- if lpadc_read_block_of_samples(offset_in_frames * channels, length_in_frames * channels, adc_block) < 0:
- logger.error('cyrenderer ADC read: failed to read %d frames at offset %d from ADC' % (length_in_frames, offset_in_frames))
- return snd
-
- for i in range(length_in_frames):
- for c in range(channels):
- snd.frames[i,c] = adc_block[i * channels + c]
-
- return snd
-
-
cdef class MessageEvent:
def __cinit__(self,
double onset,
@@ 96,7 80,6 @@ cdef class MessageEvent:
str params,
double max_processing_time
):
-
cdef size_t onset_frames = 0
params_byte_string = params.encode('utf-8')
@@ 121,52 104,14 @@ cdef class MessageEvent:
cpdef int schedule(MessageEvent self, double now):
self.msg.initiated = now
- #logger.debug('CYRENDERER: Sending message type %d to %s' % (self.msg.type, self.msg.instrument_name))
- #logger.debug('initiated=%f scheduled=%f voice_id=%d' % (self.msg.initiated, self.msg.scheduled, self.msg.voice_id))
+ if self.msg.type == LPMSG_SERIAL:
+ return send_serial_message(self.msg[0], self.msg.instrument_name)
return send_play_message(self.msg[0])
def __dealloc__(self):
if self.msg is not NULL:
free(self.msg)
-cdef class SerialEvent:
- def __cinit__(self,
- double onset,
- lpmsg_t msg,
- int group=0,
- int device=0
- ):
-
- self.event = <lpserialevent_t *>calloc(1, sizeof(lpserialevent_t))
- self.event.onset = onset
- self.event.now = 0
- self.event.msg = msg
- self.event.group = group
- self.event.device = device
-
- cpdef int schedule(SerialEvent self, double now):
- """
- self.event.now = now
- cdef int qfd = midi_triggerq_open()
- if qfd < 0:
- logger.exception('Error opening MIDI fifo q')
- return -1
-
- if midi_triggerq_schedule(qfd, self.event[0]) < 0:
- logger.exception('Error scheduling MidiEvent')
- return -1
-
- if midi_triggerq_close(qfd) < 0:
- logger.exception('Error closing MIDI fifo q')
- return -1
- """
-
- return 0
-
- def __dealloc__(self):
- if self.event is not NULL:
- free(self.event)
-
cdef class MidiEvent:
def __cinit__(self,
@@ 280,6 225,9 @@ cdef class EventTriggerFactory:
params = self._parse_params(*args, **kwargs)
return MessageEvent(onset, instrument_name, LPMSG_TRIGGER, params, 0)
+ def serial(self, double onset, str tty, *args, **kwargs):
+ params = self._parse_params(*args, **kwargs)
+ return MessageEvent(onset, tty, LPMSG_SERIAL, params, 0)
cdef class SessionParamBucket:
@@ 334,9 282,10 @@ cdef class ParamBucket:
cdef class EventContext:
def __cinit__(self,
- str instrument_name=None,
+ Instrument instrument,
str msg=None,
dict cache=None,
+ object graph=None,
int voice_id=-1,
int default_midi_device=1,
double max_processing_time=1,
@@ 345,21 294,22 @@ cdef class EventContext:
cdef double now = 0
+ self.graph = graph
self.cache = cache
self.p = ParamBucket(str(msg))
self.s = SessionParamBucket()
self.t = EventTriggerFactory()
self.m = MidiEventListenerProxy(default_midi_device)
- self.instrument_name = instrument_name
+ self.instrument = instrument
self.vid = voice_id
self.count = count
if lpscheduler_get_now_seconds(&now) < 0:
- logger.exception('Error getting now seconds during %s event ctx init' % instrument_name)
+ logger.exception('Error getting now seconds during %s event ctx init' % instrument.name)
now = 0
self.now = now
def adc(self, length=1, offset=0, channels=2):
- return read_from_adc(length, offset=offset, channels=channels)
+ return self.instrument.read_from_adc(length, offset=offset, channels=channels)
def log(self, msg):
logger.info('ctx.log[%s] %s' % (self.instrument_name, msg))
@@ 368,14 318,112 @@ cdef class EventContext:
return self.p._params
cdef class Instrument:
- def __init__(self, str name, str path, object renderer):
+ def __init__(self, str name, str path, int channels, double adc_length):
+ instrument_byte_string = name.encode('UTF-8')
+ cdef char * _instrument_ascii_name = instrument_byte_string
+
self.name = name
self.path = path
- self.renderer = renderer
self.cache = {}
self.last_reload = 0
self.max_processing_time = 0
+ self.i = astrid_instrument_start(_instrument_ascii_name, channels, adc_length, NULL, NULL, NULL, NULL)
+ if self.i == NULL:
+ raise InstrumentError('Could not initialize lpinstrument_t')
+
+ self.load_renderer(name, path)
+
+ def load_renderer(self, name, path):
+ """ Loads a renderer module from the script
+ at self.path
+
+ Failure to load the module raises an
+ InstrumentNotFoundError
+ """
+ try:
+ logger.debug('Loading instrument %s from %s' % (name, path))
+ spec = importlib.util.spec_from_file_location(name, path)
+ if spec is not None:
+ renderer = importlib.util.module_from_spec(spec)
+ try:
+ spec.loader.exec_module(renderer)
+ except Exception as e:
+ logger.exception('Error loading instrument module: %s' % str(e))
+
+ if not hasattr(renderer, '_'):
+ renderer._ = None
+
+ self.renderer = renderer
+
+ # fill the cache if there is something to fill:
+ if hasattr(self.renderer, 'cache'):
+ self.cache = self.renderer.cache()
+
+ if hasattr(self.renderer, 'MIDI_DEVICE'):
+ self.default_midi_device = self.renderer.MIDI_DEVICE
+ else:
+ self.default_midi_device = 1
+
+ else:
+ logger.error('Could not load instrument - spec is None: %s %s' % (path, name))
+ raise InstrumentError('Instrument renderer has a null spec')
+
+ except TypeError as e:
+ logger.exception('TypeError loading renderer module: %s' % str(e))
+ raise InstrumentError('Problem loading the python renderer') from e
+
+ logger.info('loaded instrument, setting metadata')
+ self.last_reload = os.path.getmtime(path)
+ #logger.info('registering midi triggers')
+ #self.register_midi_triggers()
+
+ cpdef lpmsg_t get_message(Instrument self):
+ cdef lpmsg_t msg
+ if astrid_msgq_read(self.i.exmsgq, &msg) < 0:
+ raise InstrumentError('Could not get the instrument message')
+ self.msg = msg # so many copies omg
+ return msg
+
+ cpdef EventContext get_event_context(Instrument self, bint with_graph=False):
+ cdef bytes render_params = self.msg.msg
+ msgstr = render_params.decode('ascii')
+ graph = None
+ if with_graph:
+ graph = ugens.Graph()
+
+ return EventContext.__new__(EventContext,
+ self,
+ msg=msgstr,
+ cache=self.cache,
+ graph=graph,
+ voice_id=self.msg.voice_id,
+ max_processing_time=self.max_processing_time,
+ default_midi_device=self.default_midi_device,
+ count=self.msg.count,
+ )
+
+ cdef SoundBuffer read_from_adc(Instrument self, double length, double offset=0, int channels=2, int samplerate=48000):
+ cdef size_t i
+ cdef int c
+ name_byte_string = ("%s-adc" % self.name).encode('UTF-8')
+ cdef char * _ascii_name = name_byte_string
+
+ cdef SoundBuffer snd = SoundBuffer(length=length, channels=channels, samplerate=samplerate)
+ cdef size_t length_in_frames = len(snd)
+ cdef size_t offset_in_frames = <size_t>(offset * samplerate)
+
+ # fixme add dcblock
+ if lpsampler_read_block_of_samples(_ascii_name, offset_in_frames * channels, length_in_frames * channels, adc_block) < 0:
+ logger.error('pippi.renderer ADC read: failed to read %d frames at offset %d from ADC' % (length_in_frames, offset_in_frames))
+ return snd
+
+ for i in range(length_in_frames):
+ for c in range(channels):
+ snd.frames[i,c] = adc_block[i * channels + c]
+
+ return snd
+
def reload(self):
logger.debug('Reloading instrument %s from %s' % (self.name, self.path))
spec = importlib.util.spec_from_file_location(self.name, self.path)
@@ 408,56 456,27 @@ cdef class Instrument:
devices = [ self.renderer.MIDI ]
-class InstrumentNotFoundError(Exception):
- def __init__(self, instrument_name, *args, **kwargs):
- self.message = 'No instrument named %s found' % instrument_name
+cdef int stream_graph_update(object instrument):
+ cdef EventContext ctx
-def _load_instrument(name, path):
- """ Loads a renderer module from the script
- at self.path
+ if not hasattr(instrument.renderer, 'stream'):
+ return 0
- Failure to load the module raises an
- InstrumentNotFoundError
- """
+ ctx = instrument.get_event_context()
try:
- logger.debug('Loading instrument %s from %s' % (name, path))
- spec = importlib.util.spec_from_file_location(name, path)
- if spec is not None:
- renderer = importlib.util.module_from_spec(spec)
- try:
- spec.loader.exec_module(renderer)
- except Exception as e:
- logger.exception('Error loading instrument module: %s' % str(e))
-
- if not hasattr(renderer, '_'):
- renderer._ = None
-
- instrument = Instrument(name, path, renderer)
-
- # fill the cache if there is something to fill:
- if hasattr(instrument.renderer, 'cache'):
- instrument.cache = instrument.renderer.cache()
+ # Update the graph
+ instrument.graph = instrument.renderer.stream(ctx)
+ except Exception as e:
+ logger.exception('Error during %s stream graph update: %s' % (ctx.instrument.name, e))
+ return -1
- if hasattr(instrument.renderer, 'MIDI_DEVICE'):
- instrument.default_midi_device = instrument.renderer.MIDI_DEVICE
- else:
- instrument.default_midi_device = 1
+ return 0
- else:
- logger.error('Could not load instrument - spec is None: %s %s' % (path, name))
- raise InstrumentNotFoundError(name)
- except TypeError as e:
- logger.exception('TypeError loading instrument module: %s' % str(e))
- raise InstrumentNotFoundError(name) from e
-
- logger.info('loaded instrument, setting metadata')
- instrument.last_reload = os.path.getmtime(path)
- #logger.info('registering midi triggers')
- #instrument.register_midi_triggers()
- return instrument
-
-cdef tuple collect_players(object instrument):
+cdef tuple collect_players(Instrument instrument):
loop = False
+ # FIXME it's still nice to support this, but
+ # it should just schedule a message instead of
+ # storing a flag on the serialized buffer...
if hasattr(instrument.renderer, 'LOOP'):
loop = instrument.renderer.LOOP
@@ 482,25 501,17 @@ cdef tuple collect_players(object instrument):
return players, loop
-cdef int render_event(object instrument, lpmsg_t * msg):
+cdef int render_event(Instrument instrument):
cdef set players
cdef bint loop
- cdef EventContext ctx
- cdef str msgstr
cdef bytes bufstr
- cdef bytes render_params = msg.msg
+ cdef bytes render_params = instrument.msg.msg
cdef int dacid = 0
+ cdef EventContext ctx
+ instrument_byte_string = instrument.name.encode('UTF-8')
+ cdef char * _instrument_ascii_name = instrument_byte_string
- msgstr = render_params.decode('ascii')
- ctx = EventContext.__new__(EventContext,
- instrument_name=instrument.name,
- msg=msgstr,
- cache=instrument.cache,
- voice_id=msg.voice_id,
- default_midi_device=instrument.default_midi_device,
- max_processing_time=instrument.max_processing_time,
- count=msg.count,
- )
+ ctx = instrument.get_event_context()
logger.debug('rendering event %s w/params %s' % (str(instrument), render_params))
@@ 525,14 536,14 @@ cdef int render_event(object instrument, lpmsg_t * msg):
else:
dacid = 0
- bufstr = serialize_buffer(snd, loop, msg)
- astrid_instrument_publish_bufstr(msg.instrument_name, bufstr, len(bufstr))
+ bufstr = serialize_buffer(snd, loop, instrument.msg)
+ astrid_instrument_publish_bufstr(_instrument_ascii_name, bufstr, len(bufstr))
except Exception as e:
- logger.exception('Error during %s generator render: %s' % (ctx.instrument_name, e))
+ logger.exception('Error during %s generator render: %s' % (instrument.name, e))
return 1
except Exception as e:
- logger.exception('Error allocating generator for %s render: %s' % (ctx.instrument_name, e))
+ logger.exception('Error allocating generator for %s render: %s' % (instrument.name, e))
return 1
if hasattr(instrument.renderer, 'done'):
@@ 540,7 551,7 @@ cdef int render_event(object instrument, lpmsg_t * msg):
return 0
-cdef set collect_trigger_planners(object instrument):
+cdef set collect_trigger_planners(Instrument instrument):
# find all trigger planner functions
planners = set()
@@ 562,30 573,20 @@ cdef set collect_trigger_planners(object instrument):
return planners
-cdef int trigger_events(object instrument, lpmsg_t * msg):
+cdef int trigger_events(Instrument instrument):
""" Collect the trigger functions in the instrument module
and compute the triggers to be scheduled.
"""
cdef set planners
cdef bint loop
cdef EventContext ctx
- cdef str msgstr
cdef list eventlist
cdef int qfd
cdef double now = 0
- cdef bytes trigger_params = msg.msg
+ cdef bytes trigger_params = instrument.msg.msg
cdef list trigger_events = []
- msgstr = trigger_params.decode('ascii')
- ctx = EventContext.__new__(EventContext,
- instrument_name=instrument.name,
- msg=msgstr,
- cache=instrument.cache,
- voice_id=msg.voice_id,
- max_processing_time=instrument.max_processing_time,
- default_midi_device=instrument.default_midi_device,
- count=msg.count,
- )
+ ctx = instrument.get_event_context()
#logger.debug('trigger generation event %s w/params %s' % (str(instrument), trigger_params))
@@ 604,7 605,7 @@ cdef int trigger_events(object instrument, lpmsg_t * msg):
continue
trigger_events += eventlist
except Exception as e:
- logger.exception('Error during %s trigger generation: %s' % (ctx.instrument_name, e))
+ logger.exception('Error during %s trigger generation: %s' % (ctx.instrument.name, e))
return 1
# Schedule the trigger events
@@ 628,8 629,7 @@ cdef int trigger_events(object instrument, lpmsg_t * msg):
return 0
-cdef int astrid_schedule_python_render(Instrument instrument, void * msgp) except -1:
- cdef lpmsg_t * msg = <lpmsg_t *>msgp
+cdef int astrid_schedule_python_render(Instrument instrument) except -1:
cdef size_t last_edit = os.path.getmtime(instrument.path)
cdef int render_result
cdef double start = 0
@@ 644,7 644,7 @@ cdef int astrid_schedule_python_render(Instrument instrument, void * msgp) excep
instrument.reload()
instrument.last_reload = last_edit
- render_result = render_event(instrument, msg)
+ render_result = render_event(instrument)
if lpscheduler_get_now_seconds(&end) < 0:
logger.exception('Error getting now seconds')
@@ 656,8 656,7 @@ cdef int astrid_schedule_python_render(Instrument instrument, void * msgp) excep
return render_result
-cdef int astrid_schedule_python_triggers(Instrument instrument, void * msgp) except -1:
- cdef lpmsg_t * msg = <lpmsg_t *>msgp
+cdef int astrid_schedule_python_triggers(Instrument instrument) except -1:
cdef size_t last_edit = os.path.getmtime(instrument.path)
# Reload instrument
@@ 666,7 665,7 @@ cdef int astrid_schedule_python_triggers(Instrument instrument, void * msgp) exc
instrument.last_reload = last_edit
try:
- return trigger_events(instrument, msg)
+ return trigger_events(instrument)
except Exception as e:
logger.exception('Error during scheduling of python triggers: %s' % e)
return -1
@@ 690,9 689,8 @@ def _wait_on_commands_forever(str instrument_name, stop_event):
logger.info('Console has signaled stop, shutting down command loop...')
break
-def _run_forever(str script_path, str instrument_name, int channels, stop_event):
+def _run_forever(str script_path, str instrument_name, int channels, double adc_length, stop_event):
cdef Instrument instrument = None
- cdef lpinstrument_t * i = NULL
cdef lpmsg_t msg
instrument_byte_string = instrument_name.encode('UTF-8')
cdef char * _instrument_ascii_name = instrument_byte_string
@@ 700,20 698,21 @@ def _run_forever(str script_path, str instrument_name, int channels, stop_event)
logger.info(f'running forever... {script_path=} {instrument_name=}')
# Load the script as a module into the ASTRID_INSTRUMENT global
- logger.info(f'loading python instrument... {script_path=} {instrument_name=}')
- instrument = _load_instrument(instrument_name, script_path)
- logger.info(f'loaded instrument {instrument=}')
-
- # Start the stream and setup the instrument
- logger.info(f'starting instrument... {script_path=} {instrument_name=}')
- i = astrid_instrument_start(_instrument_ascii_name, channels, NULL, NULL, NULL, NULL)
- if i == NULL:
+ try:
+ logger.info(f'loading python instrument... {script_path=} {instrument_name=}')
+ instrument = Instrument(instrument_name, script_path, channels, adc_length)
+ logger.info(f'loaded instrument {instrument=}')
+ except InstrumentError as e:
logger.error('Error trying to start instrument. Shutting down...')
return
+ # Start the stream and setup the instrument
+ logger.info(f'started instrument... {script_path=} {instrument_name=}')
while True:
logger.info('reading messages...')
- if astrid_msgq_read(i.exmsgq, &msg) < 0:
+ try:
+ msg = instrument.get_message()
+ except InstrumentError as e:
print('There was a problem reading from the msg q. Maybe try turning it off and on again?')
continue
@@ 722,19 721,24 @@ def _run_forever(str script_path, str instrument_name, int channels, stop_event)
stop_event.set()
break
+ elif msg.type == LPMSG_UPDATE:
+ logger.info('PY MSG: update')
+ if stream_graph_update(instrument) < 0:
+ logger.error('Error trying to schedule python render...')
+
elif msg.type == LPMSG_PLAY:
logger.info('PY MSG: play')
- if astrid_schedule_python_render(instrument, &msg) < 0:
+ if astrid_schedule_python_render(instrument) < 0:
logger.error('Error trying to schedule python render...')
elif msg.type == LPMSG_TRIGGER:
logger.info('PY MSG: trigger')
- if astrid_schedule_python_triggers(instrument, &msg) < 0:
+ if astrid_schedule_python_triggers(instrument) < 0:
logger.error('Error trying to schedule python triggers...')
elif msg.type == LPMSG_LOAD:
if instrument is None:
- instrument = _load_instrument(instrument_name, script_path)
+ instrument = Instrument(instrument_name, script_path)
else:
last_edit = os.path.getmtime(instrument.path)
if last_edit > instrument.last_reload:
@@ 743,10 747,10 @@ def _run_forever(str script_path, str instrument_name, int channels, stop_event)
logger.info('python instrument shutting down...')
-def run_forever(str script_path, str instrument_name=None, channels=2):
+def run_forever(str script_path, str instrument_name=None, int channels=2, double adc_length=30):
instrument_name = instrument_name if instrument_name is not None else Path(script_path).stem
stop_event = Event()
- render_process = Process(target=_run_forever, args=(script_path, instrument_name, channels, stop_event))
+ render_process = Process(target=_run_forever, args=(script_path, instrument_name, channels, adc_length, stop_event))
render_process.start()
try:
M pippi/ugens.pxd => pippi/ugens.pxd +5 -2
@@ 4,14 4,16 @@ cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
-
lpfloat_t phase
size_t boundry
+ size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lpbuffer_factory_t:
lpbuffer_t * (*create)(size_t, int, int)
@@ 199,3 201,4 @@ cdef class Node:
cdef class Graph:
cdef dict nodes
cdef object outputs
+ cdef double next_sample(Graph self)
M pippi/ugens.pyx => pippi/ugens.pyx +21 -1
@@ 112,6 112,7 @@ cdef class Node:
stack = []
for b in value:
if isinstance(b, str):
+ # translate the string into a libpippi constant
b = dsp.wt(b)
stack += [dsp.buffer(b)]
stack = dsp.join(stack)
@@ 181,7 182,26 @@ cdef class Graph:
self.nodes[anodename].connections[aportname] += [(bnodename, bportname, _mult, _add)]
- def render(self, double length, int samplerate=DEFAULT_SAMPLERATE, int channels=DEFAULT_CHANNELS):
+ cdef double next_sample(Graph self):
+ cdef double sample = 0
+
+ # first process all the nodes
+ for _, node in self.nodes.items():
+ node.process()
+
+ # connect the outputs to the inputs
+ for portname, connections in node.connections.items():
+ port = node.get_output(portname)
+ for connode, conport, mult, add in connections:
+ value = port * mult + add
+ if connode == 'main' and conport == 'output':
+ sample += value
+ else:
+ self.nodes[connode].set_param(conport, value)
+
+ return sample
+
+ def render(Graph self, double length, int samplerate=DEFAULT_SAMPLERATE, int channels=DEFAULT_CHANNELS):
cdef size_t framelength = <size_t>(length * samplerate)
cdef double sample = 0
cdef size_t i
M pippi/wavetables.pxd => pippi/wavetables.pxd +4 -2
@@ 4,14 4,16 @@ cdef extern from "pippicore.h":
ctypedef double lpfloat_t
ctypedef struct lpbuffer_t:
- lpfloat_t * data
size_t length
int samplerate
int channels
-
lpfloat_t phase
size_t boundry
+ size_t range
size_t pos
+ size_t onset
+ int is_looping
+ lpfloat_t data[]
ctypedef struct lpwavetable_factory_t:
lpbuffer_t * (*create)(const char * name, size_t length)
M tests/test_ugens.py => tests/test_ugens.py +1 -1
@@ 3,7 3,7 @@ from pippi import dsp, fx, ugens
import numpy as np
class TestUgens(TestCase):
- """ FIXME pulsar init is different now
+ """ There's a better way to handle param updates...
def test_ugen_pulsar(self):
graph = ugens.Graph()
graph.add_node('s1', 'sine', freq=100)