/*
* Copyright (c) 2012 Peter Brinkmann ([email protected])
* Copyright (c) 2022 libpd team
*
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
*
* See https://github.com/libpd/libpd/wiki for documentation
*
*/
#include "z_queued.h"
#include <stdlib.h>
#include <string.h>
#include "z_hooks.h"
#include "z_ringbuffer.h"
#define BUFFER_SIZE 16384
typedef struct _queued_stuff {
t_libpdhooks hooks;
t_libpd_printhook printhook;
ring_buffer *pd_receive_buffer;
ring_buffer *midi_receive_buffer;
char temp_buffer[BUFFER_SIZE];
} queued_stuff;
#define QUEUEDSTUFF ((queued_stuff *)(LIBPDSTUFF->i_queued))
typedef struct _pd_params {
enum {
LIBPD_PRINT, LIBPD_BANG, LIBPD_FLOAT,
LIBPD_SYMBOL, LIBPD_LIST, LIBPD_MESSAGE,
} type;
const char *src;
t_float x;
const char *sym;
int argc;
} pd_params;
typedef struct _midi_params {
enum {
LIBPD_NOTEON, LIBPD_CONTROLCHANGE, LIBPD_PROGRAMCHANGE, LIBPD_PITCHBEND,
LIBPD_AFTERTOUCH, LIBPD_POLYAFTERTOUCH, LIBPD_MIDIBYTE
} type;
int midi1;
int midi2;
int midi3;
} midi_params;
#define S_PD_PARAMS sizeof(pd_params)
#define S_MIDI_PARAMS sizeof(midi_params)
#define S_ATOM sizeof(t_atom)
static void receive_print(pd_params *p, char **buffer) {
if (QUEUEDSTUFF->printhook) {
QUEUEDSTUFF->printhook(*buffer);
}
*buffer += p->argc;
}
static void receive_bang(pd_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_banghook) {
QUEUEDSTUFF->hooks.h_banghook(p->src);
}
}
static void receive_float(pd_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_floathook) {
QUEUEDSTUFF->hooks.h_floathook(p->src, (float)p->x);
}
else if (QUEUEDSTUFF->hooks.h_doublehook) {
QUEUEDSTUFF->hooks.h_doublehook(p->src, (double)p->x);
}
}
static void receive_symbol(pd_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_symbolhook) {
QUEUEDSTUFF->hooks.h_symbolhook(p->src, p->sym);
}
}
static void receive_list(pd_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_listhook) {
QUEUEDSTUFF->hooks.h_listhook(p->src, p->argc, (t_atom *) *buffer);
}
*buffer += p->argc * S_ATOM;
}
static void receive_message(pd_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_messagehook) {
QUEUEDSTUFF->hooks.h_messagehook(p->src, p->sym, p->argc, (t_atom *) *buffer);
}
*buffer += p->argc * S_ATOM;
}
#define LIBPD_WORD_ALIGN 8
static void internal_printhook(const char *s) {
static char padding[LIBPD_WORD_ALIGN];
queued_stuff *queued = QUEUEDSTUFF;
int len = (int) strlen(s) + 1; // remember terminating null char
int rest = len % LIBPD_WORD_ALIGN;
if (rest) rest = LIBPD_WORD_ALIGN - rest;
int total = len + rest;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + total) {
pd_params p = {LIBPD_PRINT, NULL, 0.0f, NULL, total};
rb_write_to_buffer(queued->pd_receive_buffer, 3,
(const char *)&p, S_PD_PARAMS, s, len, padding, rest);
}
}
static void internal_banghook(const char *src) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {
pd_params p = {LIBPD_BANG, src, 0.0f, NULL, 0};
rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);
}
}
static void internal_floathook(const char *src, float x) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {
pd_params p = {LIBPD_FLOAT, src, x, NULL, 0};
rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);
}
}
static void internal_doublehook(const char *src, double x) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {
pd_params p = {LIBPD_FLOAT, src, (t_float)x, NULL, 0};
rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);
}
}
static void internal_symbolhook(const char *src, const char *sym) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS) {
pd_params p = {LIBPD_SYMBOL, src, 0.0f, sym, 0};
rb_write_to_buffer(queued->pd_receive_buffer, 1, (const char *)&p, S_PD_PARAMS);
}
}
static void internal_listhook(const char *src, int argc, t_atom *argv) {
queued_stuff *queued = QUEUEDSTUFF;
int n = argc * S_ATOM;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + n) {
pd_params p = {LIBPD_LIST, src, 0.0f, NULL, argc};
rb_write_to_buffer(queued->pd_receive_buffer, 2,
(const char *)&p, S_PD_PARAMS, (const char *)argv, n);
}
}
static void internal_messagehook(const char *src, const char* sym,
int argc, t_atom *argv) {
queued_stuff *queued = QUEUEDSTUFF;
int n = argc * S_ATOM;
if (rb_available_to_write(queued->pd_receive_buffer) >= S_PD_PARAMS + n) {
pd_params p = {LIBPD_MESSAGE, src, 0.0f, sym, argc};
rb_write_to_buffer(queued->pd_receive_buffer, 2,
(const char *)&p, S_PD_PARAMS, (const char *)argv, n);
}
}
static void receive_noteon(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_noteonhook) {
QUEUEDSTUFF->hooks.h_noteonhook(p->midi1, p->midi2, p->midi3);
}
}
static void receive_controlchange(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_controlchangehook) {
QUEUEDSTUFF->hooks.h_controlchangehook(p->midi1, p->midi2, p->midi3);
}
}
static void receive_programchange(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_programchangehook) {
QUEUEDSTUFF->hooks.h_programchangehook(p->midi1, p->midi2);
}
}
static void receive_pitchbend(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_pitchbendhook) {
QUEUEDSTUFF->hooks.h_pitchbendhook(p->midi1, p->midi2);
}
}
static void receive_aftertouch(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_aftertouchhook) {
QUEUEDSTUFF->hooks.h_aftertouchhook(p->midi1, p->midi2);
}
}
static void receive_polyaftertouch(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_polyaftertouchhook) {
QUEUEDSTUFF->hooks.h_polyaftertouchhook(p->midi1, p->midi2, p->midi3);
}
}
static void receive_midibyte(midi_params *p, char **buffer) {
if (QUEUEDSTUFF->hooks.h_midibytehook) {
QUEUEDSTUFF->hooks.h_midibytehook(p->midi1, p->midi2);
}
}
static void internal_noteonhook(int channel, int pitch, int velocity) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_NOTEON, channel, pitch, velocity};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
static void internal_controlchangehook(int channel, int controller, int value) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_CONTROLCHANGE, channel, controller, value};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
static void internal_programchangehook(int channel, int value) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_PROGRAMCHANGE, channel, value, 0};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
static void internal_pitchbendhook(int channel, int value) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_PITCHBEND, channel, value, 0};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
static void internal_aftertouchhook(int channel, int value) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_AFTERTOUCH, channel, value, 0};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
static void internal_polyaftertouchhook(int channel, int pitch, int value) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_POLYAFTERTOUCH, channel, pitch, value};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
static void internal_midibytehook(int port, int byte) {
queued_stuff *queued = QUEUEDSTUFF;
if (rb_available_to_write(queued->midi_receive_buffer) >= S_MIDI_PARAMS) {
midi_params p = {LIBPD_MIDIBYTE, port, byte, 0};
rb_write_to_buffer(queued->midi_receive_buffer, 1, (const char *)&p, S_MIDI_PARAMS);
}
}
void libpd_set_queued_printhook(const t_libpd_printhook hook) {
QUEUEDSTUFF->printhook = hook;
}
void libpd_set_queued_banghook(const t_libpd_banghook hook) {
QUEUEDSTUFF->hooks.h_banghook = hook;
}
void libpd_set_queued_floathook(const t_libpd_floathook hook) {
QUEUEDSTUFF->hooks.h_floathook = hook;
QUEUEDSTUFF->hooks.h_doublehook = NULL;
}
void libpd_set_queued_doublehook(const t_libpd_doublehook hook) {
QUEUEDSTUFF->hooks.h_floathook = NULL;
QUEUEDSTUFF->hooks.h_doublehook = hook;
}
void libpd_set_queued_symbolhook(const t_libpd_symbolhook hook) {
QUEUEDSTUFF->hooks.h_symbolhook = hook;
}
void libpd_set_queued_listhook(const t_libpd_listhook hook) {
QUEUEDSTUFF->hooks.h_listhook = hook;
}
void libpd_set_queued_messagehook(const t_libpd_messagehook hook) {
QUEUEDSTUFF->hooks.h_messagehook = hook;
}
void libpd_set_queued_noteonhook(const t_libpd_noteonhook hook) {
QUEUEDSTUFF->hooks.h_noteonhook = hook;
}
void libpd_set_queued_controlchangehook(const t_libpd_controlchangehook hook) {
QUEUEDSTUFF->hooks.h_controlchangehook = hook;
}
void libpd_set_queued_programchangehook(const t_libpd_programchangehook hook) {
QUEUEDSTUFF->hooks.h_programchangehook = hook;
}
void libpd_set_queued_pitchbendhook(const t_libpd_pitchbendhook hook) {
QUEUEDSTUFF->hooks.h_pitchbendhook = hook;
}
void libpd_set_queued_aftertouchhook(const t_libpd_aftertouchhook hook) {
QUEUEDSTUFF->hooks.h_aftertouchhook = hook;
}
void libpd_set_queued_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) {
QUEUEDSTUFF->hooks.h_polyaftertouchhook = hook;
}
void libpd_set_queued_midibytehook(const t_libpd_midibytehook hook) {
QUEUEDSTUFF->hooks.h_midibytehook = hook;
}
static void queued_stuff_free(void *p) {
queued_stuff *queued = (queued_stuff *)p;
if (queued->pd_receive_buffer) rb_free(queued->pd_receive_buffer);
if (queued->midi_receive_buffer) rb_free(queued->midi_receive_buffer);
}
int libpd_queued_init(void) {
int ret = libpd_init();
libpd_set_printhook(internal_printhook);
libpd_set_banghook(internal_banghook);
libpd_set_doublehook(internal_doublehook);
libpd_set_symbolhook(internal_symbolhook);
libpd_set_listhook(internal_listhook);
libpd_set_messagehook(internal_messagehook);
libpd_set_noteonhook(internal_noteonhook);
libpd_set_controlchangehook(internal_controlchangehook);
libpd_set_programchangehook(internal_programchangehook);
libpd_set_pitchbendhook(internal_pitchbendhook);
libpd_set_aftertouchhook(internal_aftertouchhook);
libpd_set_polyaftertouchhook(internal_polyaftertouchhook);
libpd_set_midibytehook(internal_midibytehook);
t_libpdimp *imp = LIBPDSTUFF;
if (!imp->i_queued) {
queued_stuff *queued = (queued_stuff *)calloc(1, sizeof(queued_stuff));
if(!queued) goto cleanup;
queued->pd_receive_buffer = rb_create(BUFFER_SIZE);
if (!queued->pd_receive_buffer) goto cleanup;
queued->midi_receive_buffer = rb_create(BUFFER_SIZE);
if (!queued->midi_receive_buffer) goto cleanup;
imp->i_queued = (void *)queued;
imp->i_queued_freehook = queued_stuff_free;
}
return ret;
cleanup:
libpd_queued_release();
return -2;
}
void libpd_queued_release(void) {
t_libpdimp *imp = LIBPDSTUFF;
if (imp->i_queued) {
queued_stuff_free(imp->i_queued);
imp->i_queued = NULL;
imp->i_queued_freehook = NULL;
}
}
void libpd_queued_receive_pd_messages(void) {
queued_stuff *queued = QUEUEDSTUFF;
size_t available = rb_available_to_read(queued->pd_receive_buffer);
if (!available) return;
rb_read_from_buffer(queued->pd_receive_buffer, queued->temp_buffer, (int)available);
char *end = queued->temp_buffer + available;
char *buffer = queued->temp_buffer;
while (buffer < end) {
pd_params *p = (pd_params *)buffer;
buffer += S_PD_PARAMS;
switch (p->type) {
case LIBPD_PRINT: {
receive_print(p, &buffer);
break;
}
case LIBPD_BANG: {
receive_bang(p, &buffer);
break;
}
case LIBPD_FLOAT: {
receive_float(p, &buffer);
break;
}
case LIBPD_SYMBOL: {
receive_symbol(p, &buffer);
break;
}
case LIBPD_LIST: {
receive_list(p, &buffer);
break;
}
case LIBPD_MESSAGE: {
receive_message(p, &buffer);
break;
}
default:
break;
}
}
}
void libpd_queued_receive_midi_messages(void) {
queued_stuff *queued = QUEUEDSTUFF;
size_t available = rb_available_to_read(queued->midi_receive_buffer);
if (!available) return;
rb_read_from_buffer(queued->midi_receive_buffer, queued->temp_buffer, (int)available);
char *end = queued->temp_buffer + available;
char *buffer = queued->temp_buffer;
while (buffer < end) {
midi_params *p = (midi_params *)buffer;
buffer += S_MIDI_PARAMS;
switch (p->type) {
case LIBPD_NOTEON: {
receive_noteon(p, &buffer);
break;
}
case LIBPD_CONTROLCHANGE: {
receive_controlchange(p, &buffer);
break;
}
case LIBPD_PROGRAMCHANGE: {
receive_programchange(p, &buffer);
break;
}
case LIBPD_PITCHBEND: {
receive_pitchbend(p, &buffer);
break;
}
case LIBPD_AFTERTOUCH: {
receive_aftertouch(p, &buffer);
break;
}
case LIBPD_POLYAFTERTOUCH: {
receive_polyaftertouch(p, &buffer);
break;
}
case LIBPD_MIDIBYTE: {
receive_midibyte(p, &buffer);
break;
}
default:
break;
}
}
}