~hecanjog/pippi

93c34c6da42fe23944a077546913f2873e6e0d6f — Erik Schoster 3 months ago e219e63
Move cython renderer later into pippi, first pass support for new style python instruments
8 files changed, 186 insertions(+), 131 deletions(-)

M astrid/Makefile
M astrid/orc/pulsar.c
M astrid/src/astrid.c
M astrid/src/astrid.h
M banner.png
R astrid/cython/cyrenderer.pxd => pippi/renderer.pxd
R astrid/cython/cyrenderer.pyx => pippi/renderer.pyx
M setup.py
M astrid/Makefile => astrid/Makefile +4 -22
@@ 1,5 1,3 @@
.PHONY: cython astrid follow-log

default: build

PYLIB := $(shell python -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBRARY')[3:-2])")


@@ 28,7 26,7 @@ LPSOURCES = $(LPDIR)/vendor/fft/fft.c \
	$(LPDIR)/src/pippicore.c

LPDBSOURCES = $(LPDIR)/vendor/sqlite3/sqlite3.c
LPINCLUDES = -Isrc -I$(LPDIR)/vendor -I$(LPDIR)/src -I${LPDIR}/vendor/linenoise -I${LPDIR}/vendor/libpqueue/src -I${LPDIR}/vendor/lmdb/libraries/liblmdb
LPINCLUDES = -Isrc -I${LPDIR}../pippi -I$(LPDIR)/vendor -I$(LPDIR)/src -I${LPDIR}/vendor/linenoise -I${LPDIR}/vendor/libpqueue/src -I${LPDIR}/vendor/lmdb/libraries/liblmdb
LPDBINCLUDES = -I${LPDIR}/vendor/sqlite3
LPFLAGS = -g -std=gnu2x -Wall -Wextra -pedantic -O0 -DNOPYTHON
LPLIBS = -lm -ldl -lpthread -lrt -ljack


@@ 60,15 58,11 @@ astrid-ipc:
	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

astrid-sessiondb:
	echo "Building astrid session db tools...";
	gcc $(LPFLAGS) -DLPSESSIONDB $(LPINCLUDES) $(LPDBINCLUDES) $(LPSOURCES) $(LPDBSOURCES) src/astrid.c src/createsessiondb.c $(LPLIBS) -o build/astrid-createsessiondb

astrid-q:
	echo "Building astrid queue reader...";
	gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/qreader.c $(LPLIBS) -o build/astrid-qreader
	gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/qserver.c $(LPLIBS) -o build/astrid-qserver
	gcc $(LPFLAGS) -DLPSESSIONDB $(LPINCLUDES) $(LPDBINCLUDES) $(LPSOURCES) $(LPDBSOURCES) src/astrid.c src/msg.c $(LPLIBS) -o build/astrid-msg
	gcc $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c src/msg.c $(LPLIBS) -o build/astrid-msg

astrid-midimap:
	mkdir -p build


@@ 109,21 103,9 @@ astrid-pulsar:

	echo "Building astrid pulsar...";

	../venv/bin/cython cython/cyrenderer.pyx
	$(CC) -g -std=gnu2x -Wall -Wextra -fno-strict-aliasing $(LPINCLUDES) `python3-config --cflags` -Icython -l$(PYLIB) $(LPLIBS) src/astrid.c orc/pulsar.c $(LPSOURCES) cython/cyrenderer.c -o build/astrid-pulsar

	#clang $(LPFLAGS) -fsanitize=address -fno-omit-frame-pointer $(LPINCLUDES) $(LPSOURCES) src/astrid.c orc/pulsar.c $(LPLIBS) -o build/astrid-pulsar
	#clang $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c orc/pulsar.c $(LPLIBS) -o build/astrid-pulsar
	#gcc $(LPFLAGS) -fanalyzer $(LPINCLUDES) $(LPSOURCES) src/astrid.c orc/pulsar.c $(LPLIBS) -o build/astrid-pulsar

follow-log:
ifeq ($(shell uname),Darwin)
	log stream --predicate 'subsystem == "astrid"'
else
	journalctl -xf --output=json | python -u python/colorize_logs.py
endif
	$(CC) $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c orc/pulsar.c $(LPLIBS) -o build/astrid-pulsar

build: clean astrid-q astrid-seriallistener astrid-ipc astrid-devices astrid-voicestatus astrid-midimap astrid-pulsar
build: clean astrid-q astrid-seriallistener astrid-ipc astrid-devices astrid-midimap astrid-pulsar

install: 
	cp build/astrid-* /usr/local/bin/

M astrid/orc/pulsar.c => astrid/orc/pulsar.c +8 -11
@@ 132,8 132,8 @@ void audio_callback(int channels, size_t blocksize, float ** input, float ** out
    }
}

int main(int argc, char ** argv) {
    lpinstrument_t instrument = {0};
int main() {
    lpinstrument_t * instrument;
    
    // create local context struct
    localctx_t * ctx = (localctx_t *)calloc(1, sizeof(localctx_t));


@@ 163,11 163,8 @@ int main(int argc, char ** argv) {
    }

    // Set the callbacks for streaming, async renders and param updates
    instrument.stream = audio_callback;
    //instrument.renderer = renderer_callback;
    instrument.updates = param_update_callback;

    if(astrid_instrument_start(NAME, CHANNELS, (void*)ctx, &instrument, argc, argv) < 0) {
    if((instrument = astrid_instrument_start(NAME, CHANNELS, (void*)ctx, 
                    audio_callback, renderer_callback, param_update_callback)) == NULL) {
        fprintf(stderr, "Could not start instrument: (%d) %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }


@@ 176,15 173,15 @@ int main(int argc, char ** argv) {
    for(int i=0; i < NUMFREQS; i++) {
        ctx->selected_freqs[i] = scale[LPRand.randint(0, NUMFREQS*2) % NUMFREQS] * 0.5f + LPRand.rand(0.f, 1.f);
    }
    astrid_instrument_set_param_float_list(&instrument, PARAM_FREQS, ctx->selected_freqs, NUMFREQS);
    astrid_instrument_set_param_float_list(instrument, PARAM_FREQS, ctx->selected_freqs, NUMFREQS);

    /* twiddle thumbs until shutdown */
    while(instrument.is_running) {
        astrid_instrument_tick(&instrument);
    while(instrument->is_running) {
        astrid_instrument_tick(instrument);
    }

    /* stop jack and cleanup threads */
    if(astrid_instrument_stop(&instrument) < 0) {
    if(astrid_instrument_stop(instrument) < 0) {
        fprintf(stderr, "There was a problem stopping the instrument. (%d) %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

M astrid/src/astrid.c => astrid/src/astrid.c +58 -81
@@ 1,6 1,3 @@
#ifndef NOPYTHON
#include "cyrenderer.h"
#endif
#include "astrid.h"

static volatile int * astrid_instrument_is_running;


@@ 1246,7 1243,7 @@ int lpmidi_trigger_notemap(int device_id, int note) {
            msg.instrument_name
        );

        if(send_message(msg) < 0) {
        if(send_message(msg.instrument_name, msg) < 0) {
            syslog(LOG_ERR, "Could not schedule msg for sending during notemap trigger. Error: %s\n", strerror(errno));
            return -1;
        }


@@ 1571,16 1568,16 @@ int send_play_message(lpmsg_t msg) {
    return 0;
}

int send_message(lpmsg_t msg) {
int send_message(char * qname, lpmsg_t msg) {
    mqd_t mqd;
    struct mq_attr attr;

    attr.mq_maxmsg = ASTRID_MQ_MAXMSG;
    attr.mq_msgsize = sizeof(lpmsg_t);

    /*syslog(LOG_DEBUG, "Sending message type %d to %s\n", msg.type, msg.instrument_name);*/
    syslog(LOG_DEBUG, "Sending message type %d on queue %s\n", msg.type, qname);

    if((mqd = mq_open(ASTRID_MSGQ_PATH, O_CREAT | O_WRONLY, LPIPC_PERMS, &attr)) == (mqd_t) -1) {
    if((mqd = mq_open(qname, O_CREAT | O_WRONLY, LPIPC_PERMS, &attr)) == (mqd_t) -1) {
        syslog(LOG_ERR, "send_message mq_open: Error opening message queue. Error: %s\n", strerror(errno));
        return -1;
    }


@@ 1613,11 1610,13 @@ mqd_t astrid_playq_open(const char * instrument_name) {
    qname_length = (LPMAXQNAME >= qname_length) ? LPMAXQNAME : qname_length;
    snprintf(qname, qname_length, "%s-%s", LPPLAYQ, instrument_name);

    syslog(LOG_DEBUG, "Opening playq %s\n", qname);
    if((mqd = mq_open(qname, O_CREAT | O_RDONLY, LPIPC_PERMS, &attr)) == (mqd_t) -1) {
        syslog(LOG_ERR, "astrid_playq_open mq_open: Error opening message queue. Error: %s\n", strerror(errno));
        return -1;
    }

    syslog(LOG_DEBUG, "Opened playq mqd:%d\n", mqd);
    return mqd;
}



@@ 1645,18 1644,20 @@ int astrid_playq_read(mqd_t mqd, lpmsg_t * msg) {
    return 0;
}

mqd_t astrid_msgq_open() {
mqd_t astrid_msgq_open(char * qname) {
    mqd_t mqd;
    struct mq_attr attr;

    attr.mq_maxmsg = ASTRID_MQ_MAXMSG;
    attr.mq_msgsize = sizeof(lpmsg_t);

    if((mqd = mq_open(ASTRID_MSGQ_PATH, O_CREAT | O_RDONLY, LPIPC_PERMS, &attr)) == (mqd_t) -1) {
    syslog(LOG_DEBUG, "Opening msgq %s\n", qname);
    if((mqd = mq_open(qname, O_CREAT | O_RDONLY, LPIPC_PERMS, &attr)) == (mqd_t) -1) {
        syslog(LOG_ERR, "astrid_playq_open mq_open: Error opening message queue. Error: %s\n", strerror(errno));
        return (mqd_t) -1;
    }

    syslog(LOG_DEBUG, "Opened msgq mqd:%d\n", mqd);
    return mqd;
}



@@ 1669,19 1670,24 @@ int astrid_msgq_close(mqd_t mqd) {
    return 0;
}

int astrid_msgq_read(mqd_t mqd, lpmsg_t * msg) {
lpmsg_t astrid_msgq_read(mqd_t mqd) {
    lpmsg_t msg = {0};
    char * msgp;
    ssize_t read_result;
    unsigned int msg_priority;
    unsigned int msg_priority = 0;

    msgp = (char *)msg;
    syslog(LOG_DEBUG, "Reading from msgq mqd:%d\n", mqd);
    syslog(LOG_DEBUG, "Reading from msg.msg:%s\n", msg.msg);
    syslog(LOG_DEBUG, "Reading from msg.instrument_name:%s\n", msg.instrument_name);
    msgp = (char *)(&msg);

    syslog(LOG_DEBUG, "calling mq_receive:%d\n", mqd);
    if((read_result = mq_receive(mqd, msgp, sizeof(lpmsg_t), &msg_priority)) < 0) {
        syslog(LOG_ERR, "astrid_msgq_read mq_receive: Error reading message. (Got %ld bytes) Error: %s\n", read_result, strerror(errno));
        return -1;
    }

    return 0;
    syslog(LOG_DEBUG, "done calling mq_receive:%d\n", mqd);
    return msg;
}

int astrid_get_playback_device_id() {


@@ 2396,6 2402,8 @@ int astrid_instrument_jack_callback(jack_nframes_t nframes, void * arg) {
    float * input_channels[instrument->channels];
    size_t i;
    int c;
    syslog(LOG_DEBUG, "inside the jack callback. %s\n", instrument->name);
    syslog(LOG_DEBUG, "inside the jack callback. init: %d\n", instrument->has_been_initialized);

    if(!instrument->is_running) return 0;



@@ 2573,6 2581,17 @@ void * instrument_message_thread(void * arg) {
        syslog(LOG_DEBUG, "MSG: %d (msg.voice_id)\n", (int)instrument->msg.voice_id);
        syslog(LOG_DEBUG, "MSG: %d (msg.type)\n", (int)instrument->msg.type);

#ifndef NOPYTHON
        // Relay a copy of all messages to python
        if(instrument->python_is_enabled) {
            if(send_message(instrument->python_message_relay_name, instrument->msg) < 0) {
                //PyErr_Print();
                syslog(LOG_ERR, "CPython error during renderer loop\n");
            }
        }
#endif


        switch(instrument->msg.type) {
            case LPMSG_SHUTDOWN:
                syslog(LOG_DEBUG, "MSG: shutdown\n");


@@ 2593,21 2612,11 @@ void * instrument_message_thread(void * arg) {

            case LPMSG_UPDATE:
                syslog(LOG_DEBUG, "MSG: update\n");
                instrument->updates(instrument);
                if(instrument->updates != NULL) instrument->updates(instrument);
                break;

            case LPMSG_PLAY:
                syslog(LOG_DEBUG, "MSG: play\n");
#ifndef NOPYTHON
                // Schedule a python render if there is a renderer
                if(instrument->python_is_enabled) {
                    if(instrument->schedule_python_render(&instrument->msg) < 0) {
                        PyErr_Print();
                        syslog(LOG_ERR, "CPython error during renderer loop\n");
                    }
                }
#endif

                // Schedule a C callback render if there's a callback defined
                if(instrument->renderer != NULL) {
                    /* Do the render: FIXME do this in another thread */


@@ 2641,22 2650,10 @@ void * instrument_message_thread(void * arg) {

            case LPMSG_LOAD:
                syslog(LOG_DEBUG, "MSG: load\n");
#ifndef NOPYTHON
                if(astrid_reload_instrument(instrument->python_instrument_path) < 0) {
                    PyErr_Print();
                    syslog(LOG_ERR, "Error while attempting to load astrid instrument\n");
                }
#endif
                break;

            case LPMSG_TRIGGER:
                syslog(LOG_DEBUG, "MSG: trigger\n");
#ifndef NOPYTHON
                if(astrid_schedule_python_triggers(&instrument->msg) < 0) {
                    PyErr_Print();
                    syslog(LOG_ERR, "CPython error during trigger planning loop\n");
                }
#endif
                break;

            case LPMSG_SCHEDULE:


@@ 2726,19 2723,15 @@ int astrid_instrument_seq_start(lpinstrument_t * instrument) {
    return 0;
}

int astrid_instrument_start(
lpinstrument_t * astrid_instrument_start(
    const char * name, 
    int channels, 
    void * ctx,
    lpinstrument_t * instrument,
#ifndef NOPYTHON
    int argc,
    char ** argv
#else
    __attribute__((unused)) int argc,
    __attribute__((unused)) char ** argv
#endif
    void (*stream)(int channels, size_t blocksize, float ** input, float ** output, void * instrument),
    lpbuffer_t * (*renderer)(void * instrument),
    void (*updates)(void * instrument)
) {
    lpinstrument_t * instrument;
    struct sigaction shutdown_action;
    jack_status_t jack_status;
    jack_options_t jack_options = JackNullOption;


@@ 2747,12 2740,19 @@ int astrid_instrument_start(
    char inport_name[50];
    int c = 0;

    instrument = (lpinstrument_t *)LPMemoryPool.alloc(1, sizeof(lpinstrument_t));
    memset(instrument, 0, sizeof(lpinstrument_t));

    syslog(LOG_DEBUG, "starting %s instrument...\n", name);

    instrument->name = name;
    instrument->channels = channels;
    instrument->context = ctx;

    instrument->stream = stream;
    instrument->renderer = renderer;
    instrument->updates = updates;

    openlog(name, LOG_PID, LOG_USER);

    /* Seed the random number generator */


@@ 2785,26 2785,13 @@ int astrid_instrument_start(
     **/
    instrument->async_mixer = scheduler_create(1, instrument->channels, instrument->samplerate);

#ifndef NOPYTHON
    if(argc > 1) {
        printf("Enabling embedded python renderer and setting python script path to:\n\t%s\n", argv[1]);
        instrument->python_instrument_path = argv[1];
        instrument->python_is_enabled = 1;
    }
#endif
    printf("Python is enabled\n");
    instrument->python_is_enabled = 1;
    snprintf(instrument->python_message_relay_name, sizeof(instrument->python_message_relay_name), "/%s-python-relay", instrument->name);

    /* Open the LMDB session */
    astrid_instrument_session_open(instrument);

#ifndef NOPYTHON
    /* Start the embedded python renderer */
    if(instrument->python_is_enabled) {
        if(astrid_instrument_renderer_python_start(instrument, instrument->python_instrument_path) < 0) {
            syslog(LOG_ERR, "%s Could not start embedded python renderer. (%d) %s\n", name, errno, strerror(errno));
        }
    }
#endif

    /* Set up JACK */
    instrument->inports = (jack_port_t **)calloc(channels, sizeof(jack_port_t *));
    instrument->outports = (jack_port_t **)calloc(channels, sizeof(jack_port_t *));


@@ 2885,26 2872,26 @@ int astrid_instrument_start(
    // Ready for some messages now! Open the message queue and start up the seq threads...
    if((instrument->playqd = astrid_playq_open(instrument->name)) == (mqd_t) -1) {
        syslog(LOG_CRIT, "Could not open playq for instrument %s. Error: %s\n", instrument->name, strerror(errno));
        return -1;
        return NULL;
    }
    memcpy(instrument->msg.instrument_name, instrument->name, strlen(instrument->name));
    memcpy(instrument->cmd.instrument_name, instrument->name, strlen(instrument->name));
    syslog(LOG_DEBUG, "Opened play queue for %s with fd %d\n", instrument->name, instrument->playqd);
    if(astrid_instrument_seq_start(instrument) < 0) {
        syslog(LOG_CRIT, "Could not start message sequence threads for instrument %s. Error: %s\n", instrument->name, strerror(errno));
        return -1;
        return NULL;
    }

    /* setup linenoise repl */
    linenoiseHistoryLoad("history.txt"); // FIXME this goes in the instrument config dir / or share?

    return 0;
    return instrument;

astrid_instrument_shutdown_with_error:
    instrument->is_running = 0;
    jack_client_close(instrument->jack_client);
    closelog();
    return 1;
    return NULL;
}

int astrid_instrument_stop(lpinstrument_t * instrument) {


@@ 2953,13 2940,6 @@ int astrid_instrument_stop(lpinstrument_t * instrument) {

    if(instrument->async_mixer != NULL) scheduler_destroy(instrument->async_mixer);

#ifndef NOPYTHON
    if(instrument->python_is_enabled) {
        syslog(LOG_DEBUG, "Stopping python renderer...\n");
        astrid_instrument_renderer_python_stop();
    }
#endif

    syslog(LOG_DEBUG, "All done, see ya later!\n");
    closelog();
    return 0;


@@ 3027,10 3007,9 @@ int astrid_instrument_get_or_create_datadir(const char * name, char * dbpath) {

int astrid_instrument_session_open(lpinstrument_t * instrument) {
    int rc;
    char dbpath[PATH_MAX];

    if(astrid_instrument_get_or_create_datadir(instrument->name, dbpath) < 0) {
        syslog(LOG_ERR, "session data path (%s) mkdir: (%d) %s\n", dbpath, errno, strerror(errno));
    if(astrid_instrument_get_or_create_datadir(instrument->name, instrument->datapath) < 0) {
        syslog(LOG_ERR, "session data path (%s) mkdir: (%d) %s\n", instrument->datapath, errno, strerror(errno));
        return -1;
    }



@@ 3042,7 3021,7 @@ int astrid_instrument_session_open(lpinstrument_t * instrument) {
    }

    /* open it at the db directory */
	rc = mdb_env_open(instrument->dbenv, dbpath, 0, 0664);
	rc = mdb_env_open(instrument->dbenv, instrument->datapath, 0, 0664);
    if(rc != MDB_SUCCESS) {
        syslog(LOG_ERR, "mdb_env_open: (%d) %s\n", rc, mdb_strerror(rc));
        return -1;


@@ 3149,7 3128,6 @@ int astrid_instrument_publish_bufstr(char * instrument_name, unsigned char * buf
int astrid_instrument_tick(lpinstrument_t * instrument) {
    char * line;


    if(instrument->is_running == 0) return 0;

    line = linenoise("^_- ");


@@ 3330,13 3308,13 @@ size_t lpdecode_with_prefix(char * encoded) {
}

#ifndef NOPYTHON
#if 0
int astrid_instrument_renderer_python_start(lpinstrument_t * instrument, char * python_script_path) {
    PyObject * pmodule;
    PyConfig config;
    PyStatus status;

    syslog(LOG_INFO, "Starting python renderer...\n");

    /* Prepare cyrenderer module for import */
    if(PyImport_AppendInittab("cyrenderer", PyInit_cyrenderer) == -1) {
        syslog(LOG_ERR, "Error: could not extend in-built modules table for renderer\n");


@@ 3357,7 3335,6 @@ int astrid_instrument_renderer_python_start(lpinstrument_t * instrument, char * 
    if (PyStatus_Exception(status)) {
        return -1;
    }

    /* Import cyrenderer */
    pmodule = PyImport_ImportModule("cyrenderer");
    if(!pmodule) {


@@ 3366,7 3343,6 @@ int astrid_instrument_renderer_python_start(lpinstrument_t * instrument, char * 
        syslog(LOG_ERR, "Error: could not import cython renderer module\n");
        return -1;
    }

    /* Import python instrument module */
    if(astrid_load_instrument(python_script_path) < 0) {
        PyErr_Print();


@@ 3388,3 3364,4 @@ int astrid_instrument_renderer_python_stop() {
    return 0;
}
#endif
#endif

M astrid/src/astrid.h => astrid/src/astrid.h +7 -13
@@ 184,11 184,10 @@ typedef struct lpinstrument_t {
    jack_port_t ** outports;
    jack_client_t * jack_client;

#ifndef NOPYTHON
    char datapath[PATH_MAX];

    char python_message_relay_name[NAME_MAX];
    int python_is_enabled;
    int (*schedule_python_render)(void *);
    char * python_instrument_path;
#endif

    // Optional local context struct for callbacks
    void * context;


@@ 261,7 260,7 @@ int parse_message_from_cmdline(char * cmdline, lpmsg_t * msg);

ssize_t astrid_get_voice_id();

int send_message(lpmsg_t msg);
int send_message(char * qname, lpmsg_t msg);
int send_serial_message(lpmsg_t msg);
int send_play_message(lpmsg_t msg);
int get_play_message(char * instrument_name, lpmsg_t * msg);


@@ 270,9 269,9 @@ mqd_t astrid_playq_open(const char * instrument_name);
int astrid_playq_read(mqd_t mqd, lpmsg_t * msg);
int astrid_playq_close(mqd_t mqd);

mqd_t astrid_msgq_open();
mqd_t astrid_msgq_open(char * qname);
int astrid_msgq_close(mqd_t mqd);
int astrid_msgq_read(mqd_t mqd, lpmsg_t * msg);
lpmsg_t astrid_msgq_read(mqd_t mqd);


/* TODO add POSIX message queues for these too */


@@ 320,7 319,7 @@ int lpipc_destroyvalue(char * id_path);

void lptimeit_since(struct timespec * start);

int astrid_instrument_start(const char * name, int channels, void * ctx, lpinstrument_t * instrument, int argc, char ** argv);
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));
int astrid_instrument_stop(lpinstrument_t * instrument);

void astrid_instrument_set_param_float(lpinstrument_t * instrument, int param_index, lpfloat_t value);


@@ 336,11 335,6 @@ int astrid_instrument_publish_bufstr(char * instrument_name, unsigned char * buf
int lpencode_with_prefix(char * prefix, size_t val, char * encoded);
size_t lpdecode_with_prefix(char * encoded);

#ifndef NOPYTHON
int astrid_instrument_renderer_python_start(lpinstrument_t * instrument, char * python_script_path);
int astrid_instrument_renderer_python_stop();
#endif

#ifdef LPSESSIONDB
#include <sqlite3.h>
int lpsessiondb_create(sqlite3 ** db);

M banner.png => banner.png +0 -0
R astrid/cython/cyrenderer.pxd => pippi/renderer.pxd +28 -2
@@ 3,7 3,6 @@
from libc.stdint cimport uint16_t
from pippi.soundbuffer cimport SoundBuffer


cdef extern from "pippicore.h":
    ctypedef double lpfloat_t



@@ 54,6 53,7 @@ cdef extern from "astrid.h":
    cdef const int ASTRID_SAMPLERATE
    cdef const int ASTRID_CHANNELS
    cdef const char * LPADC_BUFFER_PATH
    cdef const int NAME_MAX

    cdef enum LPMessageTypes:
        LPMSG_EMPTY,


@@ 96,6 96,16 @@ cdef extern from "astrid.h":
        int group
        int device

    ctypedef struct lpinstrument_t:
        const char * name
        volatile int is_running
        volatile int is_waiting
        int has_been_initialized
        int playqd
        char python_message_relay_name[NAME_MAX]
        int python_is_enabled
        lpscheduler_t * async_mixer

    int lpadc_create()
    int lpadc_destroy()
    int lpadc_write_block(float * block, size_t blocksize_in_samples)


@@ 106,12 116,16 @@ cdef extern from "astrid.h":
    int lpipc_getid(char * path)
    ssize_t astrid_get_voice_id()

    int send_message(lpmsg_t msg)
    int send_message(char * qname, lpmsg_t msg)

    int midi_triggerq_open()
    int midi_triggerq_schedule(int qfd, lpmidievent_t t)
    int midi_triggerq_close(int qfd)

    int astrid_msgq_open(char * qname)
    lpmsg_t astrid_msgq_read(int mqd)
    int astrid_msgq_close(int mqd)

    int lpmidi_setcc(int device_id, int cc, int value)
    int lpmidi_getcc(int device_id, int cc)
    int lpmidi_setnote(int device_id, int note, int velocity)


@@ 121,6 135,18 @@ cdef extern from "astrid.h":

    int lpscheduler_get_now_seconds(double * now)
    int astrid_instrument_publish_bufstr(char * instrument_name, unsigned char * bufstr, size_t size)
    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)
    )

    int astrid_instrument_stop(lpinstrument_t * instrument)
    void scheduler_cleanup_nursery(lpscheduler_t * s)



cdef class MessageEvent:

R astrid/cython/cyrenderer.pyx => pippi/renderer.pyx +62 -2
@@ 14,7 14,9 @@ from pathlib import Path
import platform
import struct
import subprocess
import sys
import threading
import time

from pippi import dsp, midi
from pippi.soundbuffer cimport SoundBuffer


@@ 120,7 122,7 @@ cdef class MessageEvent:
        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))
        return send_message(self.msg[0])
        return send_message(self.msg.instrument_name, self.msg[0])

    def __dealloc__(self):
        if self.msg is not NULL:


@@ 449,8 451,10 @@ def _load_instrument(name, path):
        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)
    instrument.register_midi_triggers()
    #logger.info('registering midi triggers')
    #instrument.register_midi_triggers()
    return instrument

cdef tuple collect_players(object instrument):


@@ 650,6 654,8 @@ cdef public int astrid_load_instrument(char * path) except -1:
    _path = path.decode('utf-8')
    name = Path(_path).stem

    print('NAME, _PATH', name, _path)

    ASTRID_INSTRUMENT = _load_instrument(name, _path)
    return 0



@@ 698,3 704,57 @@ cdef public int astrid_schedule_python_triggers(void * msgp) except -1:
        logger.exception('Error during scheduling of python triggers: %s' % e)
        return -1

def run_forever(str instrument_name, str script_path):
    global ASTRID_INSTRUMENT
    cdef lpinstrument_t * instrument
    cdef lpmsg_t msg
    path = os.path.dirname(sys.argv[0])
    print('PATH', script_path) 

    # Start the stream and setup the instrument
    
    instrument = astrid_instrument_start('fake', 2, NULL, NULL, NULL, NULL)

    if instrument == NULL:
        logger.error('Error trying to start instrument. Shutting down...')
        return

    if astrid_load_instrument('fake.py') < 0:
        raise Exception(f'Could not load instrument at path {path}')

    logger.info(f'loaded instrument {ASTRID_INSTRUMENT=}')
    name = ASTRID_INSTRUMENT.name.encode('ascii')
    logger.info(f'{name=}')
    cdef int q = astrid_msgq_open('/astridq-fake')
    logger.info(f'opened msgq {q}')

    while True:
        logger.info('reading messages...')
        msg = astrid_msgq_read(q)

        print('Got a message!')
        print('msg.type', msg.type)
        print('msg.msg', msg.msg)

        if msg.type == LPMSG_SHUTDOWN:
            break

        elif msg.type == LPMSG_PLAY:
            if astrid_schedule_python_render(&msg) < 0:
                logger.error('Error trying to schedule python render...')

        elif msg.type == LPMSG_TRIGGER:
            if astrid_schedule_python_triggers(&msg) < 0:
                logger.error('Error trying to schedule python triggers...')

        elif msg.type == LPMSG_LOAD:
            if astrid_reload_instrument('fake.py') < 0:
                logger.error('Error trying to reload python module...')

        scheduler_cleanup_nursery(instrument.async_mixer)

    if astrid_msgq_close(q) < 0:
        logger.error('Could not close the python message queue')

    if astrid_instrument_stop(instrument) < 0:
        logger.error('Could not stop the background instrument threads')

M setup.py => setup.py +19 -0
@@ 17,6 17,25 @@ if dev:
    DIRECTIVES['binding'] = True

ext_modules = cythonize([
        Extension('pippi.renderer', [
                'pippi/renderer.pyx',
                'libpippi/vendor/linenoise/linenoise.c',
                'libpippi/vendor/libpqueue/src/pqueue.c',
                'libpippi/vendor/lmdb/libraries/liblmdb/mdb.c',
                'libpippi/vendor/lmdb/libraries/liblmdb/midl.c',
                'libpippi/src/pippicore.c',
                'astrid/src/astrid.c',
            ],
            libraries=['jack', 'rt'], 
            include_dirs=INCLUDES+[
                'libpippi/vendor/libpqueue/src', 
                'libpippi/vendor/linenoise', 
                'libpippi/vendor/lmdb/libraries/liblmdb',
                'astrid/src'
            ],           
            define_macros=MACROS
        ),

        Extension('pippi.microcontrollers', ['pippi/microcontrollers.pyx'],
            include_dirs=INCLUDES, 
            define_macros=MACROS