~hecanjog/pippi

556c0f8a292aca78a1abf26c57ddde30545c6d78 — Erik Schoster 8 months ago a06042b
Fix message sequencing in new style astrid instruments
M astrid/orc/simple.py => astrid/orc/simple.py +10 -2
@@ 1,8 1,16 @@
from pippi import dsp, oscs, renderer
from pippi import dsp, oscs, renderer, fx

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!')
    yield oscs.SineOsc(freq=dsp.rand(200, 300), amp=1).play(10).env('pluckout')
    out = oscs.SineOsc(freq=dsp.rand(200, 1000), amp=dsp.rand(0.1, 0.5)).play(10).env('pluckout')

    if dsp.rand() > 0.5:
        out = fx.fold(out, dsp.rand(1, 2))
    yield out

if __name__ == '__main__':
    renderer.run_forever(__file__)

M astrid/src/astrid.c => astrid/src/astrid.c +31 -20
@@ 2577,6 2577,9 @@ int relay_message_to_seq(lpinstrument_t * instrument) {
    seq_delay = instrument->msg.scheduled - (instrument->msg.max_processing_time * 2);
    d->timestamp = instrument->msg.initiated + seq_delay;

    // Remove the scheduled flag before relaying the message
    d->msg.flags &= ~LPFLAG_IS_SCHEDULED;

    if(pqueue_insert(instrument->msgpq, (void *)d) < 0) {
        syslog(LOG_ERR, "Error while inserting message into pq during msgq loop: %s\n", strerror(errno));
        return -1;


@@ 2590,46 2593,53 @@ void * instrument_message_thread(void * arg) {
    lpbuffer_t * buf; // async renders: FIXME, do renders in a thread if possible... or fork out early for the python interpreter maybe?
    double processing_time_so_far, onset_delay_in_seconds, now=0;
    lpinstrument_t * instrument = (lpinstrument_t *)arg;
    int is_scheduled = 0;

    instrument->is_waiting = 1;
    while(instrument->is_running) {
        instrument->msg.scheduled = 0;
        if(astrid_msgq_read(instrument->msgq, &instrument->msg) == (mqd_t) -1) {
            syslog(LOG_ERR, "%s renderer: Could not read message from playq. Error: (%d) %s\n", instrument->name, errno, strerror(errno));
            usleep((useconds_t)10000);
            return NULL;
        }

        syslog(LOG_DEBUG, "MSG: %s\n", instrument->msg.instrument_name);
        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);

        // Relay a copy of all messages to python (or in the future maybe other things?)
        // except render complete messages which always get handled here...
        if(instrument->msg.type == LPMSG_RENDER_COMPLETE) {
            if(send_message(instrument->external_relay_name, instrument->msg) < 0) {
                syslog(LOG_ERR, "Could not relay message\n");
            }
        }
        is_scheduled = ((instrument->msg.flags & LPFLAG_IS_SCHEDULED) == LPFLAG_IS_SCHEDULED);
        syslog(LOG_DEBUG, "C MSG: name=%s\n", instrument->msg.instrument_name);
        syslog(LOG_DEBUG, "C MSG: scheduled=%f\n", instrument->msg.scheduled);
        syslog(LOG_DEBUG, "C MSG: voice_id=%d\n", (int)instrument->msg.voice_id);
        syslog(LOG_DEBUG, "C MSG: type=%d\n", (int)instrument->msg.type);
        syslog(LOG_DEBUG, "C MSG: flags=%d\n", (int)instrument->msg.flags);
        syslog(LOG_DEBUG, "C MSG: is_scheduled=%d\n", is_scheduled);

        // Handle shutdown early
        if(instrument->msg.type == LPMSG_SHUTDOWN) {
            syslog(LOG_DEBUG, "MSG: shutdown\n");
            syslog(LOG_DEBUG, "C MSG: shutdown\n");
            instrument->is_running = 0;

            // send the shutdown message to the seq thread too
            // send the shutdown message to the seq thread and external relay
            if(relay_message_to_seq(instrument) < 0) {
                syslog(LOG_ERR, "%s renderer: Could not read relay message to seq. Error: (%d) %s\n", instrument->name, errno, strerror(errno));
            }
            if(send_message(instrument->external_relay_name, instrument->msg) < 0) {
                syslog(LOG_ERR, "Could not relay message\n");
            }

            break;
        }

        // Scheduled messages get sent to the sequencer for handling
        if(instrument->msg.scheduled > 0) {
        if(is_scheduled) {
            // Scheduled messages get sent to the sequencer for handling later
            syslog(LOG_ERR, "C IS SCHEDULED msg.scheduled %f\n", instrument->msg.scheduled);
            if(relay_message_to_seq(instrument) < 0) {
                syslog(LOG_ERR, "%s renderer: Could not read relay message to seq. Error: (%d) %s\n", instrument->name, errno, strerror(errno));
            }
            continue;
        } else {
            // All other messages get relayed externally, too
            syslog(LOG_DEBUG, "C MSG: relaying to ext\n");
            if(send_message(instrument->external_relay_name, instrument->msg) < 0) {
                syslog(LOG_ERR, "Could not relay message\n");
            }
        }

        // Now the fun stuff


@@ 2645,16 2655,17 @@ void * instrument_message_thread(void * arg) {
                break;

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

            case LPMSG_PLAY:
                syslog(LOG_DEBUG, "MSG: play\n");
                syslog(LOG_DEBUG, "C MSG: play\n");
                // Schedule a C callback render if there's a callback defined
                // python renders will also be triggered at this point if we're 
                // inside a python instrument because of the message relay
                if(instrument->renderer != NULL) {
                    syslog(LOG_DEBUG, "C MSG: rendering play...\n");
                    /* FIXME do this in another thread */
                    buf = instrument->renderer(instrument);



@@ 2687,13 2698,13 @@ void * instrument_message_thread(void * arg) {
            case LPMSG_LOAD:
                // it would be interesting to explore live reloading of C modules
                // in the tradition of CLIVE, but at the moment only python handles these
                syslog(LOG_DEBUG, "MSG: load\n");
                syslog(LOG_DEBUG, "C MSG: load\n");
                break;

            case LPMSG_TRIGGER:
                // Python only handles these for now, but maybe it would be nice to have optional 
                // C callbacks here and maybe support raspberry pi GPIO pin toggling / triggers?
                syslog(LOG_DEBUG, "MSG: trigger\n");
                syslog(LOG_DEBUG, "C MSG: trigger\n");
                break;

            default:

M libpippi/src/pippiconstants.h => libpippi/src/pippiconstants.h +7 -1
@@ 60,7 60,7 @@
#endif

#define LPMAXNAME 24
#define LPMAXMSG (PIPE_BUF - (sizeof(double) * 4) - (sizeof(size_t) * 3) - sizeof(uint16_t) - LPMAXNAME)
#define LPMAXMSG (PIPE_BUF - (sizeof(double) * 4) - (sizeof(size_t) * 3) - (sizeof(uint16_t) * 2) - LPMAXNAME)


enum Wavetables {


@@ 101,6 101,12 @@ enum PanMethods {
    NUM_PANMETHODS
};

enum LPMessageFlags {
    LPFLAG_NONE,
    LPFLAG_IS_SCHEDULED,
    NUM_LPMESSAGEFLAGS
};

enum LPMessageTypes {
    LPMSG_EMPTY,
    LPMSG_PLAY,

M libpippi/src/pippitypes.h => libpippi/src/pippitypes.h +1 -0
@@ 80,6 80,7 @@ typedef struct lpmsg_t {
    size_t voice_id;
    size_t count;

    uint16_t flags;
    uint16_t type;
    char msg[LPMAXMSG];
    char instrument_name[LPMAXNAME];

M pippi/renderer.pxd => pippi/renderer.pxd +6 -0
@@ 54,6 54,11 @@ cdef extern from "astrid.h":
    ctypedef struct lpscheduler_t:
        pass

    cdef enum LPMessageFlags:
        LPFLAG_NONE,
        LPFLAG_IS_SCHEDULED,
        NUM_LPMESSAGEFLAGS

    cdef enum LPMessageTypes:
        LPMSG_EMPTY,
        LPMSG_PLAY,


@@ 75,6 80,7 @@ cdef extern from "astrid.h":
        size_t onset_delay
        size_t voice_id
        size_t count
        uint16_t flags
        uint16_t type
        char msg[LPMAXMSG]
        char instrument_name[LPMAXNAME]

M pippi/renderer.pyx => pippi/renderer.pyx +15 -18
@@ 114,6 114,7 @@ cdef class MessageEvent:
        self.msg.voice_id = 0
        self.msg.count = 0
        self.msg.type = msgtype
        self.msg.flags = LPFLAG_IS_SCHEDULED if onset > 0 else LPFLAG_NONE

        strcpy(self.msg.msg, byte_params)
        strcpy(self.msg.instrument_name, byte_instrument_name)


@@ 122,7 123,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.instrument_name, self.msg[0])
        return send_play_message(self.msg[0])

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


@@ 272,14 273,12 @@ cdef class EventTriggerFactory:
        return [noteon, noteoff]

    def play(self, double onset, str instrument_name, *args, **kwargs):
        global ASTRID_INSTRUMENT
        params = self._parse_params(*args, **kwargs)
        return MessageEvent(onset, instrument_name, LPMSG_PLAY, params, ASTRID_INSTRUMENT.max_processing_time)
        return MessageEvent(onset, instrument_name, LPMSG_PLAY, params, 0)

    def trigger(self, double onset, str instrument_name, *args, **kwargs):
        global ASTRID_INSTRUMENT
        params = self._parse_params(*args, **kwargs)
        return MessageEvent(onset, instrument_name, LPMSG_TRIGGER, params, ASTRID_INSTRUMENT.max_processing_time)
        return MessageEvent(onset, instrument_name, LPMSG_TRIGGER, params, 0)





@@ 694,8 693,7 @@ def _wait_on_commands_forever(str instrument_name, stop_event):
def _run_forever(str script_path, str instrument_name, int channels, stop_event):
    cdef Instrument instrument = None
    cdef lpinstrument_t * i = NULL
    cdef lpmsg_t msg, bufmsg
    cdef lpbuffer_t * buf = NULL
    cdef lpmsg_t msg
    instrument_byte_string = instrument_name.encode('UTF-8')
    cdef char * _instrument_ascii_name = instrument_byte_string



@@ 715,25 713,26 @@ def _run_forever(str script_path, str instrument_name, int channels, stop_event)

    while True:
        logger.info('reading messages...')
        i.msg.scheduled = 0
        if astrid_msgq_read(i.exmsgq, &i.msg) < 0:
        if astrid_msgq_read(i.exmsgq, &msg) < 0:
            print('There was a problem reading from the msg q. Maybe try turning it off and on again?')
            continue

        if i.msg.type == LPMSG_SHUTDOWN:
            logger.info('Message loop got shutdown, breaking out!')
        if msg.type == LPMSG_SHUTDOWN:
            logger.info('PY MSG: shutdown')
            stop_event.set()
            break

        elif i.msg.type == LPMSG_PLAY:
            if astrid_schedule_python_render(instrument, &i.msg) < 0:
        elif msg.type == LPMSG_PLAY:
            logger.info('PY MSG: play')
            if astrid_schedule_python_render(instrument, &msg) < 0:
                logger.error('Error trying to schedule python render...')

        elif i.msg.type == LPMSG_TRIGGER:
            if astrid_schedule_python_triggers(instrument, &i.msg) < 0:
        elif msg.type == LPMSG_TRIGGER:
            logger.info('PY MSG: trigger')
            if astrid_schedule_python_triggers(instrument, &msg) < 0:
                logger.error('Error trying to schedule python triggers...')

        elif i.msg.type == LPMSG_LOAD:
        elif msg.type == LPMSG_LOAD:
            if instrument is None:
                instrument = _load_instrument(instrument_name, script_path)
            else:


@@ 742,9 741,7 @@ def _run_forever(str script_path, str instrument_name, int channels, stop_event)
                    instrument.reload()
                    instrument.last_reload = last_edit


    logger.info('python instrument shutting down...')
    logger.info('done running forever!?')

def run_forever(str script_path, str instrument_name=None, channels=2):
    instrument_name = instrument_name if instrument_name is not None else Path(script_path).stem