~hecanjog/pippi

dbc71a0b4ce08d493ef20769d7898bec53ac1e66 — Erik Schoster 3 months ago 2e1e69c
lttlefield.c, pippi, astrid & libpippi updates

littlefield.c:
- Final adjustments to littlefield.c from TX FS recording

astrid:
- first pass new `lpparamset_t` type for managing param hashes and types

libpippi
- add 1 pole highpass `fx_hpf1`

pippi
- move some info level logging to debug level
- revert to old graincloud implementation from grains module
M astrid/Makefile => astrid/Makefile +6 -0
@@ 102,6 102,12 @@ astrid-bufstr:
	echo "Building astrid bufstr...";
	$(CC) $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c orc/bufstr.c $(LPLIBS) -o build/astrid-bufstr

astrid-paramset:
	mkdir -p build

	echo "Building astrid paramset...";
	$(CC) $(LPFLAGS) $(LPINCLUDES) $(LPSOURCES) src/astrid.c orc/paramset.c $(LPLIBS) -o build/astrid-paramset

littlefield:
	mkdir -p build


M astrid/orc/littlefield.c => astrid/orc/littlefield.c +141 -67
@@ 13,6 13,7 @@
#define MAXNUMFREQS NUMOSCS

int num_freqs = MAXNUMFREQS;
float ** pre;

lpfloat_t terry[] = {
    1.f,         // P1  C


@@ 30,20 31,8 @@ lpfloat_t terry[] = {
};

// this is just used as the default freq list on startup
lpfloat_t scale[] = {
    55.000f, 
    110.000f, 
    122.222f, 
    137.500f, 
    146.667f, 
    165.000f, 
    185.625f, 
    206.250f, 
    220.000f, 
    244.444f, 
    275.000f, 
    293.333f
};
lpfloat_t scale[12] = {234.666f};


enum DistortionTypes {
    DISTORTION_FOLDBACK,


@@ 86,9 75,16 @@ enum InstrumentParams {
    PARAM_DISTORTION_TYPE,
    PARAM_DISTORTION_AMOUNT,
    PARAM_DISTORTION_MIX,
    PARAM_OSC_FILTER_LOWPASS,
    PARAM_OSC_FILTER_HIGHPASS,
    PARAM_MIC_FILTER_LOWPASS,
    PARAM_MIC_FILTER_HIGHPASS,

    PARAM_SAMPLEBANK1_REC_ENABLED,
    PARAM_SAMPLEBANK2_REC_ENABLED,
    PARAM_SAMPLEBANK3_REC_ENABLED,
    PARAM_SAMPLEBANK4_REC_ENABLED,


    PARAM_MIC1_AMP,
    PARAM_MIC1_PAN,


@@ 106,17 102,19 @@ typedef struct localctx_t {
    lpfloat_t freqsmooth;
    lpenvelopefollower_t * envf;

    lpbfilter_t * osc_hp;
    lpbfilter_t * osc_lp;
    lpbfilter_t * mic_hp;
    lpbfilter_t * mic_lp;

    lpfloat_t ampmodsmooth;
    lpfloat_t oscampsmooth;
    lpfloat_t gateampsmooth;

    lpfloat_t fold1prev;
    lpfloat_t fold2prev;
    lpfloat_t foldprev;

    lpfloat_t limit1prev;
    lpfloat_t limit2prev;
    lpbuffer_t * limit1buf;
    lpbuffer_t * limit2buf;
    lpfloat_t limitprev;
    lpbuffer_t * limitbuf;

    lppulsarosc_t * gate1;
    lppulsarosc_t * gate2;


@@ 134,8 132,14 @@ typedef struct localctx_t {

    char samplebank1name[PATH_MAX];
    char samplebank2name[PATH_MAX];
    char samplebank3name[PATH_MAX];
    char samplebank4name[PATH_MAX];

    lpbuffer_t * samplebank1buf;
    lpbuffer_t * samplebank2buf;
    lpbuffer_t * samplebank3buf;
    lpbuffer_t * samplebank4buf;

} localctx_t;

lpfloat_t osc_mix(lpfloat_t pos, int chan) {


@@ 278,6 282,21 @@ int param_map_callback(void * arg, char * keystr, char * valstr) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_DISTORTION_AMOUNT, val_f);

    } else if(strcmp(keystr, "ohp") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_OSC_FILTER_HIGHPASS, val_f);
    } else if(strcmp(keystr, "olp") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_OSC_FILTER_LOWPASS, val_f);

    } else if(strcmp(keystr, "mhp") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_MIC_FILTER_HIGHPASS, val_f);
    } else if(strcmp(keystr, "mlp") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_MIC_FILTER_LOWPASS, val_f);


    } else if(strcmp(keystr, "mtrak") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        astrid_instrument_set_param_int32(instrument, PARAM_MIC_PITCH_TRACKING_ENABLED, val_i32);


@@ 294,6 313,12 @@ int param_map_callback(void * arg, char * keystr, char * valstr) {
    } else if(strcmp(keystr, "rec2") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        astrid_instrument_set_param_int32(instrument, PARAM_SAMPLEBANK2_REC_ENABLED, val_i32);
    } else if(strcmp(keystr, "rec3") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        astrid_instrument_set_param_int32(instrument, PARAM_SAMPLEBANK3_REC_ENABLED, val_i32);
    } else if(strcmp(keystr, "rec4") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        astrid_instrument_set_param_int32(instrument, PARAM_SAMPLEBANK4_REC_ENABLED, val_i32);

    } else if(strcmp(keystr, "save") == 0) {
        extract_int32_from_token(valstr, &val_i32);


@@ 328,22 353,27 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
    size_t i;
    int j;
    lpfloat_t freqs[MAXNUMFREQS];
    lpfloat_t osc_sample, osc_sample_group1, osc_sample_group2, 
              osc_amp, osc_pan, env1, env2, in1, in2, d1, d2, p, last_p=-1,
              osc_left, osc_right, gate_left, gate_right, mic1_left, mic1_right, mic2_left, mic2_right,
              distortion_amount, distortion_mix,
    lpfloat_t osc_sample, osc_sample_group1, osc_sample_group2, out_sample,
              osc_amp, env1, env2, in1, in2, dist_sample, p, last_p=-1,
              osc_pan, osc_left=0.f, osc_right=0.f,
              mic_pan, mic_left=0.f, mic_right=0.f,
              mic_sample, distortion_amount, distortion_mix,
              osc_pw, osc_saturation, osc_env_speed, 
              osc_drift_amount, drift, osc_drift_speed,
              gate1_sample, gate2_sample, gate1_amp, gate2_amp, gate_pan, amp_mod,
              mic1_amp, mic2_amp, mic1_pan, mic2_pan,
              gate1_sample, gate2_sample, gate1_amp, gate2_amp, amp_mod,
              gate_sample, mic1_amp, mic2_amp, 
              gate_shape, gate_speed, gate1_speed_multiplier, gate2_speed_multiplier, 
              gate_pw, gate_drift_amount, gate_drift_speed,
              mic_highpass, mic_lowpass, osc_highpass, osc_lowpass,
              mic_highpass_cutoff, mic_lowpass_cutoff,
              osc_highpass_cutoff, osc_lowpass_cutoff,
              gate_drift_stability, gate_drift_density, gate_drift_periodicity,
              gate_drift, gate_saturation, tracked_freq=220.f;
    int32_t octave_spread, octave_offset, gate_reset, gate_repeat, 
            freq_snap, pitch_tracking_is_enabled, distortion_type,
            envelope_tracking_is_enabled, 
            samplebank1_rec_enabled, samplebank2_rec_enabled;
            samplebank1_rec_enabled, samplebank2_rec_enabled,
            samplebank3_rec_enabled, samplebank4_rec_enabled;
    lppatternbuf_t gate_pattern;
    lpinstrument_t * instrument = (lpinstrument_t *)arg;
    localctx_t * ctx = (localctx_t *)instrument->context;


@@ 354,17 384,27 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
    distortion_mix = astrid_instrument_get_param_float(instrument, PARAM_DISTORTION_MIX, 0.f);
    distortion_type = astrid_instrument_get_param_int32(instrument, PARAM_DISTORTION_TYPE, 0);

    osc_amp = astrid_instrument_get_param_float(instrument, PARAM_OSC_AMP, 1.f);
    mic_lowpass = astrid_instrument_get_param_float(instrument, PARAM_MIC_FILTER_LOWPASS, 0.f);
    mic_highpass = astrid_instrument_get_param_float(instrument, PARAM_MIC_FILTER_HIGHPASS, 0.f);
    mic_highpass_cutoff = lpsv(mic_highpass, 10.f, 19990.f);
    mic_lowpass_cutoff = lpsv(1.f-mic_lowpass, 10.f, 10000.f);

    osc_lowpass = astrid_instrument_get_param_float(instrument, PARAM_OSC_FILTER_LOWPASS, 0.f);
    osc_highpass = astrid_instrument_get_param_float(instrument, PARAM_OSC_FILTER_HIGHPASS, 0.8f);
    osc_highpass_cutoff = lpsv(osc_highpass, 10.f, 19990.f);
    osc_lowpass_cutoff = lpsv(1.f-osc_lowpass, 10.f, 10000.f);

    osc_amp = astrid_instrument_get_param_float(instrument, PARAM_OSC_AMP, 0.f);
    osc_amp = LPFX.lpf1(osc_amp, &ctx->oscampsmooth, 1000.f, SR);
    osc_pan = astrid_instrument_get_param_float(instrument, PARAM_OSC_PAN, 0.5f);
    osc_pan = astrid_instrument_get_param_float(instrument, PARAM_OSC_PAN, 0.f);
    osc_pw = astrid_instrument_get_param_float(instrument, PARAM_OSC_PULSEWIDTH, 1.f);
    osc_saturation = astrid_instrument_get_param_float(instrument, PARAM_OSC_SATURATION, 1.f);
    osc_env_speed = astrid_instrument_get_param_float(instrument, PARAM_OSC_ENVELOPE_SPEED, 0.01f) * 100.f + 1.f;
    osc_drift_amount = astrid_instrument_get_param_float(instrument, PARAM_OSC_DRIFT_DEPTH, 0.f);
    osc_drift_speed = astrid_instrument_get_param_float(instrument, PARAM_OSC_DRIFT_SPEED, 0.5f);
    octave_spread = astrid_instrument_get_param_int32(instrument, PARAM_OSC_OCTAVE_SPREAD, 6);
    octave_offset = astrid_instrument_get_param_int32(instrument, PARAM_OSC_OCTAVE_OFFSET, -2);
    freq_snap = astrid_instrument_get_param_int32(instrument, PARAM_OSC_FREQ_UPDATE_ON_ENV_RESET, 0);
    octave_spread = astrid_instrument_get_param_int32(instrument, PARAM_OSC_OCTAVE_SPREAD, 1);
    octave_offset = astrid_instrument_get_param_int32(instrument, PARAM_OSC_OCTAVE_OFFSET, 0);
    freq_snap = astrid_instrument_get_param_int32(instrument, PARAM_OSC_FREQ_UPDATE_ON_ENV_RESET, 1);
    astrid_instrument_get_param_float_list(instrument, PARAM_OSC_FREQS, num_freqs, freqs);

    gate1_amp = astrid_instrument_get_param_float(instrument, PARAM_GATE1_AMP, 0.f);


@@ 373,7 413,6 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
    gate2_amp = astrid_instrument_get_param_float(instrument, PARAM_GATE2_AMP, 0.f);
    gate2_amp = LPFX.lpf1(gate2_amp, &ctx->gateampsmooth, 1000.f, SR);

    gate_pan = astrid_instrument_get_param_float(instrument, PARAM_GATE_PAN, 0.5f);
    gate_reset = astrid_instrument_get_param_int32(instrument, PARAM_GATE_PHASE_RESET, 0);
    gate_repeat = astrid_instrument_get_param_int32(instrument, PARAM_GATE_REPEAT, 1);
    gate_shape = astrid_instrument_get_param_float(instrument, PARAM_GATE_SHAPE, 0.f);


@@ 390,16 429,22 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
    gate_pattern = astrid_instrument_get_param_patternbuf(instrument, PARAM_GATE_PATTERN);

    mic1_amp = astrid_instrument_get_param_float(instrument, PARAM_MIC1_AMP, 0.f);
    mic1_pan = astrid_instrument_get_param_float(instrument, PARAM_MIC1_PAN, 0.5f);
    mic2_amp = astrid_instrument_get_param_float(instrument, PARAM_MIC2_AMP, 0.f);
    mic2_pan = astrid_instrument_get_param_float(instrument, PARAM_MIC2_PAN, 0.5f);
    mic_pan = astrid_instrument_get_param_float(instrument, PARAM_MIC1_PAN, 0.f);

    pitch_tracking_is_enabled = astrid_instrument_get_param_int32(instrument, PARAM_MIC_PITCH_TRACKING_ENABLED, 0);
    envelope_tracking_is_enabled = astrid_instrument_get_param_int32(instrument, PARAM_MIC_ENVELOPE_TRACKING_ENABLED, 0);
    //ctx->yin->threshold = astrid_instrument_get_param_int32(instrument, PARAM_MIC_PITCH_TRACKING_THRESHOLD, 0.8f);

    ctx->mic_hp->freq = mic_highpass_cutoff;
    ctx->mic_lp->freq = mic_lowpass_cutoff;
    ctx->osc_hp->freq = osc_highpass_cutoff;
    ctx->osc_lp->freq = osc_lowpass_cutoff;

    samplebank1_rec_enabled = astrid_instrument_get_param_int32(instrument, PARAM_SAMPLEBANK1_REC_ENABLED, 1);
    samplebank2_rec_enabled = astrid_instrument_get_param_int32(instrument, PARAM_SAMPLEBANK2_REC_ENABLED, 1);
    samplebank3_rec_enabled = astrid_instrument_get_param_int32(instrument, PARAM_SAMPLEBANK3_REC_ENABLED, 1);
    samplebank4_rec_enabled = astrid_instrument_get_param_int32(instrument, PARAM_SAMPLEBANK4_REC_ENABLED, 1);

    // set gate osc params
    ctx->gate_drifter->freq = gate_drift_speed;


@@ 446,7 491,7 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg


    for(i=0; i < blocksize; i++) {
        osc_sample_group1 = osc_sample_group2 = d1 = d2 = 0.f;
        osc_sample_group1 = osc_sample_group2 = dist_sample = mic_sample = 0.f;

        // attenuate the signal with the envelope follower if enabled
        if(envelope_tracking_is_enabled) {


@@ 500,6 545,10 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
                ctx->octave_spreads[j] = pow(2, octave_spread);
                ctx->octave_offsets[j] = pow(2, octave_offset);

                //syslog(LOG_ERR, "FILTERPOS=%f\n", filter_position);
                //syslog(LOG_ERR, "HPCUTOFF=%f\n", highpass_cutoff);
                //syslog(LOG_ERR, "LPCUTOFF=%f\n", lowpass_cutoff);

                /*
                syslog(LOG_ERR, "GDRIFT=%f GDRIFTSPEED=%f GDRIFTAMOUNT=%f\n",
                        gate_drift, gate_drift_speed, gate_drift_amount);


@@ 523,52 572,60 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg

        // osc output
        osc_sample = osc_sample_group1 + osc_sample_group2;
        //osc_sample = LPFilter.process_blp(ctx->osc_lp, osc_sample);
        //osc_sample = lpzapgremlins(osc_sample);
        //osc_sample = LPFilter.process_bhp(ctx->osc_hp, osc_sample);
        //osc_sample = lpzapgremlins(osc_sample);
        pre[0][i] = pre[1][i] = osc_sample * 0.1f;
        osc_sample *= osc_amp * amp_mod;
        gate_sample = gate1_sample + gate2_sample;

        // mic feedback
        in1 = input[0][i] * MIC_ATTENUATION * mic1_amp;
        in2 = input[1][i] * MIC_ATTENUATION * mic2_amp;
        mic_sample = in1 + in2;
        mic_sample = LPFilter.process_blp(ctx->mic_lp, mic_sample);
        mic_sample = LPFilter.process_bhp(ctx->mic_hp, mic_sample);

        osc_left = osc_right = gate_left = gate_right = mic1_left = mic1_right = mic2_left = mic2_right = 0.f;
        pan_stereo_constant(osc_pan, osc_sample, osc_sample, &osc_left, &osc_right);
        pan_stereo_constant(gate_pan, gate1_sample+gate2_sample, gate1_sample+gate2_sample, &gate_left, &gate_right);
        pan_stereo_constant(mic1_pan, in1, in1, &mic1_left, &mic1_right);
        pan_stereo_constant(mic2_pan, in2, in2, &mic2_left, &mic2_right);

        // mix everything
        //output[0][i] += (osc_sample * osc_out1_mix) + ((gate1_sample + gate2_sample) * gate_out1_mix) + (in1 * in1_out1_mix) + (in2 * in2_out1_mix);
        //output[1][i] += (osc_sample * osc_out2_mix) + ((gate1_sample + gate2_sample) * gate_out2_mix) + (in2 * in1_out2_mix) + (in2 * in2_out2_mix);

        output[0][i] += osc_left + gate_left + mic1_left + mic2_left;
        output[1][i] += osc_right + gate_right + mic1_right + mic2_right;
        //out_sample = gate_sample;

        // apply distortion
        if(distortion_amount > 0) {
            switch(distortion_type) {
                case DISTORTION_FOLDBACK:
                    d1 = LPFX.fold(output[0][i] * (distortion_amount+1), &ctx->fold1prev, instrument->samplerate);
                    d2 = LPFX.fold(output[1][i] * (distortion_amount+1), &ctx->fold2prev, instrument->samplerate);
                    dist_sample = LPFX.fold(out_sample * (distortion_amount+1), &ctx->foldprev, instrument->samplerate);
                    break;

                case DISTORTION_BITCRUSH:
                    d1 = LPFX.crush(output[0][i], (1.f-distortion_amount) * 10.f + 1.f);
                    d2 = LPFX.crush(output[1][i], (1.f-distortion_amount) * 10.f + 1.f);
                    dist_sample = LPFX.crush(out_sample, (1.f-distortion_amount) * 10.f + 1.f);
                    break;

                default:
                    break;
            }

            d1 *= distortion_mix;
            d2 *= distortion_mix;

            output[0][i] = (output[0][i] * (1-distortion_mix)) + d1;
            output[1][i] = (output[1][i] * (1-distortion_mix)) + d2;
            dist_sample *= distortion_mix;
            out_sample = (out_sample * (1-distortion_mix)) + dist_sample;
        }

        // limit
        output[0][i] = LPFX.limit(output[0][i], &ctx->limit1prev, 1.f, 0.2f, ctx->limit1buf);
        output[1][i] = LPFX.limit(output[1][i], &ctx->limit2prev, 1.f, 0.2f, ctx->limit2buf);
        osc_sample = LPFX.limit(osc_sample+gate_sample, &ctx->limitprev, 1.f, 0.2f, ctx->limitbuf);

        pan_stereo_constant(osc_pan, osc_sample, osc_sample, &osc_left, &osc_right);
        pan_stereo_constant(mic_pan, mic_sample, mic_sample, &mic_left, &mic_right);
        output[1][i] = mic_left + osc_left;
        output[0][i] = mic_right + osc_right;
    }

    if(samplebank3_rec_enabled) {
        if(lpsampler_write_ringbuffer_block(ctx->samplebank3name, ctx->samplebank3buf, pre, instrument->channels, blocksize) < 0) {
            syslog(LOG_ERR, "Error writing into samplebank 3 ringbuf\n");
        }
    }
    if(samplebank4_rec_enabled) {
        if(lpsampler_write_ringbuffer_block(ctx->samplebank4name, ctx->samplebank4buf, pre, instrument->channels, blocksize) < 0) {
            syslog(LOG_ERR, "Error writing into samplebank 4 ringbuf\n");
        }
    }

    return 0;


@@ 584,14 641,21 @@ int main() {
        exit(1);
    }

    pre = (float**)LPMemoryPool.alloc(CHANNELS * 40960, sizeof(float *));
    for(int i=0; i < CHANNELS; i++) {
        pre[i] = (float*)LPMemoryPool.alloc(40960, sizeof(float));
    }

    // create env window for the footballs
    ctx->env = LPWindow.create(WIN_HANNOUT, 4096);
    ctx->fold1prev = 0.f;
    ctx->fold2prev = 0.f;
    ctx->limit1prev = 1.f;
    ctx->limit2prev = 1.f;
    ctx->limit1buf = LPBuffer.create(1024, 1, (lpfloat_t)SR);
    ctx->limit2buf = LPBuffer.create(1024, 1, (lpfloat_t)SR);
    ctx->foldprev = 0.f;
    ctx->limitprev = 1.f;
    ctx->limitbuf = LPBuffer.create(1024, 1, (lpfloat_t)SR);

    ctx->mic_hp = LPFilter.create_bhp(1000, SR);
    ctx->osc_hp = LPFilter.create_bhp(1000, SR);
    ctx->mic_lp = LPFilter.create_blp(1000, SR);
    ctx->osc_lp = LPFilter.create_blp(1000, SR);

    // setup oscs
    for(int i=0; i < NUMOSCS; i++) {


@@ 648,12 712,21 @@ int main() {
    // set up the sampler circular buffers
    snprintf(ctx->samplebank1name, PATH_MAX, "%s-bank1", NAME);
    snprintf(ctx->samplebank2name, PATH_MAX, "%s-bank2", NAME);
    snprintf(ctx->samplebank3name, PATH_MAX, "%s-bank3", NAME);
    snprintf(ctx->samplebank4name, PATH_MAX, "%s-bank4", NAME);

    if((ctx->samplebank1buf = lpsampler_create(ctx->samplebank1name, 30, CHANNELS, SR)) == NULL) {
        syslog(LOG_ERR, "Could not create instrument samplebank1 buffer\n");
    }
    if((ctx->samplebank2buf = lpsampler_create(ctx->samplebank2name, 30, CHANNELS, SR)) == NULL) {
        syslog(LOG_ERR, "Could not create instrument samplebank2 buffer\n");
    }
    if((ctx->samplebank3buf = lpsampler_create(ctx->samplebank3name, 30, CHANNELS, SR)) == NULL) {
        syslog(LOG_ERR, "Could not create instrument samplebank3 buffer\n");
    }
    if((ctx->samplebank4buf = lpsampler_create(ctx->samplebank4name, 30, CHANNELS, SR)) == NULL) {
        syslog(LOG_ERR, "Could not create instrument samplebank4 buffer\n");
    }

    // Set the callbacks for streaming, async renders and param updates
    if((instrument = astrid_instrument_start(NAME, CHANNELS, 0, ADC_LENGTH, RESAMPLER_LENGTH, (void*)ctx, 


@@ 687,10 760,11 @@ int main() {
    LPShapeOsc.destroy(ctx->gate_drifter);
    LPBuffer.destroy(ctx->gate_drift_win);
    LPBuffer.destroy(ctx->env);
    LPBuffer.destroy(ctx->limit1buf);
    LPBuffer.destroy(ctx->limit2buf);
    LPBuffer.destroy(ctx->limitbuf);
    lpsampler_destroy(ctx->samplebank1name);
    lpsampler_destroy(ctx->samplebank2name);
    lpsampler_destroy(ctx->samplebank3name);
    lpsampler_destroy(ctx->samplebank4name);

    free(ctx);


M astrid/src/astrid.c => astrid/src/astrid.c +55 -11
@@ 2899,7 2899,7 @@ int relay_message_to_seq(lpinstrument_t * instrument, lpmsg_t msg) {
    seq_delay = msg.scheduled - (msg.max_processing_time * 2);
    d->timestamp = msg.initiated + seq_delay;

    syslog(LOG_ERR, "d->timestamp=%f msg.scheduled=%f msg.initiated=%f\n",
    syslog(LOG_DEBUG, "d->timestamp=%f msg.scheduled=%f msg.initiated=%f\n",
                    d->timestamp, msg.scheduled, msg.initiated);

    // Remove the scheduled flag before relaying the message


@@ 2946,12 2946,12 @@ void * instrument_message_thread(void * arg) {
        }

        is_scheduled = ((instrument->msg.flags & LPFLAG_IS_SCHEDULED) == LPFLAG_IS_SCHEDULED);
        syslog(LOG_ERR, "C MSG: name=%s\n", instrument->msg.instrument_name);
        syslog(LOG_ERR, "C MSG: scheduled=%f\n", instrument->msg.scheduled);
        syslog(LOG_ERR, "C MSG: voice_id=%d\n", (int)instrument->msg.voice_id);
        syslog(LOG_ERR, "C MSG: type=%d\n", (int)instrument->msg.type);
        syslog(LOG_ERR, "C MSG: flags=%d\n", (int)instrument->msg.flags);
        syslog(LOG_ERR, "C MSG: is_scheduled=%d\n", 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) {


@@ 2977,7 2977,7 @@ void * instrument_message_thread(void * arg) {

        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);
            syslog(LOG_DEBUG, "C IS SCHEDULED msg.scheduled %f\n", instrument->msg.scheduled);
            if(relay_message_to_seq(instrument, instrument->msg) < 0) {
                syslog(LOG_ERR, "%s renderer: Could not read relay message to seq. Error: (%d) %s\n", instrument->name, errno, strerror(errno));
            }


@@ 3267,13 3267,13 @@ void * instrument_serial_listener_thread(void * arg) {
        }

        bytes_read = read(tty, &c, 1);
        syslog(LOG_ERR, "%s serial listener: got %d byte: %c. ready for messages.\n", instrument->name, bytes_read, c);
        syslog(LOG_DEBUG, "%s serial listener: got %d byte: %c. ready for messages.\n", instrument->name, bytes_read, c);
        if(bytes_read < 0) {
            syslog(LOG_ERR, "%s serial listener: Could not read header from the tty. Error: (%d) %s\n", instrument->name, errno, strerror(errno));
            return NULL;
        } 
        if(c == 'c') {
            syslog(LOG_ERR, "%s serial listener: got ready byte %c\n", instrument->name, c);
            syslog(LOG_DEBUG, "%s serial listener: got ready byte %c\n", instrument->name, c);
            tty_is_ready = 1;
        }
    }


@@ 4084,6 4084,51 @@ int astrid_instrument_tick(lpinstrument_t * instrument) {
    return astrid_instrument_process_command_tick(instrument);
}

lpparamset_t astrid_instrument_create_paramset(char * paramset_defs) {
    lpparamset_t paramset;
    char * savea, * saveb, * token, * param_name, * param_type;
    char defs_copy[LPMAXMSG] = {0};
    int param_index = 0;

    memcpy(defs_copy, paramset_defs, LPMAXMSG);
    token = strtok_r(defs_copy, " ", &savea);

    while(token != NULL) {
        param_name = strtok_r(token, ":", &saveb);        
        param_type = strtok_r(NULL, ":", &saveb);

        if(param_name == NULL || param_type == NULL) {
            syslog(LOG_WARNING, "Malformed param given: %s\n", token);
            continue;
        }

        paramset.keys[param_index] = lphashstr(param_name);

        if(strcmp(param_type, "i") == 0) {
            paramset.types[param_index] = LPPARAM_INT32;
        } else if(strcmp(param_type, "i32") == 0) {
            paramset.types[param_index] = LPPARAM_INT32;
        } else if(strcmp(param_type, "f") == 0) {
            paramset.types[param_index] = LPPARAM_FLOAT;
        } else if(strcmp(param_type, "d") == 0) {
            paramset.types[param_index] = LPPARAM_DOUBLE;
        } else if(strcmp(param_type, "fl") == 0) {
            paramset.types[param_index] = LPPARAM_FLOATLIST;
        } else if(strcmp(param_type, "pb") == 0) {
            paramset.types[param_index] = LPPARAM_PATTERNBUF;
        } else {
            paramset.types[param_index] = LPPARAM_NONE;
            syslog(LOG_WARNING, "Unknown param type given: %s\n", param_type);
        }

        token = strtok_r(NULL, " ", &savea);
        param_index += 1;
    }

    paramset.num_params = param_index;

    return paramset;
}

int astrid_instrument_save_param_session_snapshot(lpinstrument_t * instrument, int num_params, int snapshot_id) {
    int shmfd;


@@ 4373,7 4418,6 @@ void astrid_instrument_set_param_float(lpinstrument_t * instrument, int param_in
    }
}


void astrid_instrument_set_param_patternbuf(lpinstrument_t * instrument, int param_index, lppatternbuf_t * patternbuf) {
    int rc;
	MDB_val key, data;

M astrid/src/astrid.h => astrid/src/astrid.h +7 -0
@@ 61,6 61,7 @@

#define LPKEY_MAXLENGTH 4096
#define ASTRID_MAX_CMDLINE 4096
#define ASTRID_MAX_PARAMS 4096

#ifndef NOTE_ON
#define NOTE_ON 144


@@ 221,6 222,11 @@ typedef struct lpinstrument_t {
    void (*shutdown)(int sig);
} lpinstrument_t;

typedef struct lpparamset_t {
   u_int32_t keys[ASTRID_MAX_PARAMS]; 
   u_int32_t types[ASTRID_MAX_PARAMS];
   int num_params;
} lpparamset_t;

void scheduler_schedule_event(lpscheduler_t * s, lpbuffer_t * buf, size_t delay);
void lpscheduler_tick(lpscheduler_t * s);


@@ 356,6 362,7 @@ lpinstrument_t * astrid_instrument_start(

int astrid_instrument_stop(lpinstrument_t * instrument);

lpparamset_t astrid_instrument_create_paramset(char * paramset_defs);
int32_t astrid_instrument_get_param_int32(lpinstrument_t * instrument, int param_index, int32_t default_value);
void astrid_instrument_set_param_int32(lpinstrument_t * instrument, int param_index, int32_t value);
void astrid_instrument_set_param_patternbuf(lpinstrument_t * instrument, int param_index, lppatternbuf_t * patternbuf);

M banner.png => banner.png +0 -0
M libpippi/src/pippiconstants.h => libpippi/src/pippiconstants.h +7 -1
@@ 118,11 118,17 @@ enum LPMessageFlags {
};

enum LPParamTypes {
    LPPARAM_NONE,
    LPPARAM_STRING,
    LPPARAM_INT,
    LPPARAM_INT32,
    LPPARAM_SIZE_T,
    LPPARAM_FLOAT,
    LPPARAM_FLOATLIST,
    LPPARAM_DOUBLE,
    LPPARAM_PATTERNBUF,
    LPPARAM_USER1,
    LPPARAM_USER2,
    LPPARAM_USER3,
    NUM_LPPARAMTYPES,
};


M libpippi/src/pippicore.c => libpippi/src/pippicore.c +15 -7
@@ 65,6 65,7 @@ void destroy_buffer(lpbuffer_t * buf);

lpfloat_t read_skewed_buffer(lpfloat_t freq, lpbuffer_t * buf, lpfloat_t phase, lpfloat_t skew);
lpfloat_t fx_lpf1(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate);
lpfloat_t fx_hpf1(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate);
void fx_convolve(lpbuffer_t * a, lpbuffer_t * b, lpbuffer_t * out);
void fx_norm(lpbuffer_t * buf, lpfloat_t ceiling);
lpfloat_t fx_fold(lpfloat_t val, lpfloat_t * prev, lpfloat_t samplerate);


@@ 132,7 133,7 @@ const lpparam_factory_t LPParam = { param_create_from_float, param_create_from_i
const lpwavetable_factory_t LPWavetable = { create_wavetable, create_wavetable_stack, destroy_wavetable };
const lpwindow_factory_t LPWindow = { create_window, create_window_stack, destroy_window };
const lpringbuffer_factory_t LPRingBuffer = { ringbuffer_create, ringbuffer_fill, ringbuffer_read, ringbuffer_readinto, ringbuffer_writefrom, ringbuffer_write, ringbuffer_readone, ringbuffer_writeone, ringbuffer_dub, ringbuffer_destroy };
const lpfx_factory_t LPFX = { read_skewed_buffer, fx_lpf1, fx_convolve, fx_norm, fx_fold, fx_limit, fx_crush };
const lpfx_factory_t LPFX = { read_skewed_buffer, fx_lpf1, fx_hpf1, fx_convolve, fx_norm, fx_fold, fx_limit, fx_crush };
const lpfilter_factory_t LPFilter = { fx_butthp_create, fx_butthp, fx_buttlp_create, fx_buttlp };

/* Platform-specific random seed, called 


@@ 1259,6 1260,19 @@ lpfloat_t read_skewed_buffer(lpfloat_t freq, lpbuffer_t * buf, lpfloat_t phase, 
    return LPInterpolation.linear(buf, (phase + (warp * buf->length)) * freq);
}

lpfloat_t fx_lpf1(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate) {
    lpfloat_t gamma = 1.f - (lpfloat_t)exp(-(2.f * (lpfloat_t)PI) * (cutoff/samplerate));
    *y = (1.f - gamma) * (*y) + gamma * x;
    return *y;
}

lpfloat_t fx_hpf1(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate) {
    lpfloat_t gamma = 1.f - (lpfloat_t)exp(-(2.f * (lpfloat_t)PI) * (cutoff/samplerate));
    *y = (1.f - gamma) * (*y) + gamma * x;
    return x - *y;
}


/* These butterworth filters were ported from the filters
 * included with Paul Batchelor's Soundpipe, in turn ported 
 * from csound.


@@ 1350,12 1364,6 @@ lpfloat_t fx_buttlp(lpbfilter_t * filter, lpfloat_t in) {
}


lpfloat_t fx_lpf1(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate) {
    lpfloat_t gamma = 1.f - (lpfloat_t)exp(-(2.f * (lpfloat_t)PI) * (cutoff/samplerate));
    *y = (1.f - gamma) * (*y) + gamma * x;
    return *y;
}

lpfloat_t fx_fold(lpfloat_t val, lpfloat_t * prev, lpfloat_t samplerate) {
    // Adapted from https://ccrma.stanford.edu/~jatin/ComplexNonlinearities/Wavefolder.html
    lpfloat_t out = 0;

M libpippi/src/pippicore.h => libpippi/src/pippicore.h +1 -0
@@ 184,6 184,7 @@ typedef struct lpwindow_factory_t {
typedef struct lpfx_factory_t {
    lpfloat_t (*read_skewed_buffer)(lpfloat_t freq, lpbuffer_t * buf, lpfloat_t phase, lpfloat_t skew);
    lpfloat_t (*lpf1)(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate);
    lpfloat_t (*hpf1)(lpfloat_t x, lpfloat_t * y, lpfloat_t cutoff, lpfloat_t samplerate);
    void (*convolve)(lpbuffer_t * a, lpbuffer_t * b, lpbuffer_t * out);
    void (*norm)(lpbuffer_t * buf, lpfloat_t ceiling);
    lpfloat_t (*fold)(lpfloat_t val, lpfloat_t * prev, lpfloat_t samplerate);

M pippi/renderer.pyx => pippi/renderer.pyx +25 -27
@@ 25,7 25,7 @@ cimport numpy as np
from pippi import dsp, midi, ugens
from pippi.soundbuffer cimport SoundBuffer

NUM_COMRADES = 16
NUM_COMRADES = 32

class InstrumentError(Exception):
    pass


@@ 38,7 38,7 @@ if not logger.handlers:
        log_path = '/dev/log'

    logger.addHandler(SysLogHandler(address=log_path))
    logger.setLevel(logging.DEBUG)
    logger.setLevel(logging.INFO)
    warnings.simplefilter('always')




@@ 86,7 86,7 @@ cdef class MessageEvent:
        ):
        cdef size_t onset_frames = 0

        logger.error('MessageEvent params: %s' % params)
        logger.debug('MessageEvent params: %s' % params)

        params_byte_string = params.encode('utf-8')
        instrument_name_byte_string = instrument_name.encode('utf-8')


@@ 201,7 201,7 @@ cdef class EventTriggerFactory:
        params += ' ' 
        params += ' '.join([ '%s=%s' % (k, v) for k, v in kwargs.items() ])

        logger.error('_parse_params params=%s' % params)
        logger.debug('_parse_params params=%s' % params)

        return params



@@ 454,16 454,16 @@ cdef class Instrument:
                    self.default_midi_device = 1

                if hasattr(self.renderer, 'PARAMS'):
                    logger.error('mapping params...')
                    logger.debug('mapping params...')
                    self.instrument_param_type_map = self.renderer.PARAMS
                    self.instrument_param_hash_map = dict()
                    for k, t in self.instrument_param_type_map.items():
                        logger.error('mapping %s...' % k)
                        logger.debug('mapping %s...' % k)
                        k_byte_string = k.encode('UTF-8')
                        _k = k_byte_string
                        logger.error('        %s...' % _k)
                        logger.debug('        %s...' % _k)
                        self.instrument_param_hash_map[k] = lphashstr(_k)
                        logger.error('        %d...' % self.instrument_param_hash_map[k])
                        logger.debug('        %d...' % self.instrument_param_hash_map[k])

            else:
                logger.error('Could not load instrument - spec is None: %s %s' % (path, name))


@@ 473,7 473,7 @@ cdef class Instrument:
            logger.exception('TypeError loading renderer module: %s' % str(e))
            raise InstrumentError('Problem loading the python renderer') from e

        logger.info('loaded instrument, setting metadata')
        logger.debug('loaded instrument, setting metadata')
        self.last_reload = os.path.getmtime(path)

    cpdef lpmsg_t get_message(Instrument self):


@@ 493,7 493,7 @@ cdef class Instrument:
                # or maybe I can ignore this and just decode from utf-8 like a criminal...
                msgstr = render_params.decode('ascii')

        logger.error('PLAY PARAMS: %s' % msgstr)
        logger.debug('PLAY PARAMS: %s' % msgstr)

        graph = None
        if with_graph:


@@ 583,10 583,10 @@ cdef class Instrument:
            return None

        snd = SoundBuffer(framelength=out.length, channels=self.channels, samplerate=self.samplerate)
        logger.error('out.length=%s len(snd)=%s self.samplerate=%s' % (out.length, len(snd), self.samplerate))
        logger.debug('out.length=%s len(snd)=%s self.samplerate=%s' % (out.length, len(snd), self.samplerate))

        if len(snd) == 0:
            logger.error('BLOWUP! self.channels=%d self.samplerate=%s' % (self.channels, self.samplerate))
            logger.error('sampler BLOWUP! len(snd)==0 self.channels=%d self.samplerate=%s' % (self.channels, self.samplerate))
            return None

        for i in range(out.length):


@@ 631,9 631,9 @@ cdef class Instrument:
        cdef char * _name = name_bytes

        # create the shm buffer
        logger.error('Creating %s buffer with frame 10,0=%s' % (name, snd.frames[10,0]))
        logger.debug('Creating %s buffer with frame 10,0=%s' % (name, snd.frames[10,0]))
        out = lpsampler_create(_name, snd.dur, snd.channels, <int>self.samplerate)
        logger.error('out.length=%s len(snd)=%s' % (out.length, len(snd)))
        logger.debug('out.length=%s len(snd)=%s' % (out.length, len(snd)))

        # write the data into it
        for i in range(out.length):


@@ 902,7 902,7 @@ def render_executor(Instrument instrument, object q, int comrade_id):
        msg = q.get()

        if msg is None:
            logger.info('Renderer comrade %d shutting down' % comrade_id)
            logger.debug('Renderer comrade %d shutting down' % comrade_id)
            break

        last_edit = os.path.getmtime(instrument.path)


@@ 923,7 923,7 @@ def render_executor(Instrument instrument, object q, int comrade_id):
            return

        instrument.max_processing_time = max(instrument.max_processing_time, end - start)
        logger.info('%s render time: %f seconds' % (instrument.name, end - start))
        logger.debug('%s render time: %f seconds' % (instrument.name, end - start))

cdef int astrid_schedule_python_triggers(Instrument instrument) except -1:
    cdef size_t last_edit = os.path.getmtime(instrument.path)


@@ 952,39 952,37 @@ def _run_forever(Instrument instrument,
    logger.info(f'PY: running forever... {script_path=} {instrument_name=}')

    while True:
        logger.info('PY MSG: waiting for a message...')
        logger.debug('PY MSG: waiting for a message...')
        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

        logger.info('PY MSG: got one!')

        if msg.type == LPMSG_SHUTDOWN:
            logger.info('PY MSG: shutdown')
            logger.debug('PY MSG: shutdown')
            for _ in range(NUM_COMRADES):
                q.put(None)
            break

        elif msg.type == LPMSG_UPDATE:
            logger.info('PY MSG: update')
            logger.debug('PY MSG: update')
            instrument.handle_update_message(msg.msg.decode('utf-8'))

        elif msg.type == LPMSG_MIDI_FROM_DEVICE:
            logger.info('PY MSG: midi from device')
            logger.debug('PY MSG: midi from device')
            instrument.handle_midi_message(msg.msg)

        elif msg.type == LPMSG_MIDI_TO_DEVICE:
            logger.info('PY MSG: midi to device')
            logger.debug('PY MSG: midi to device')

        elif msg.type == LPMSG_PLAY:
            logger.info('PY MSG: play')
            logger.error('PY MSG: play params: %s' % msg.msg)
            logger.debug('PY MSG: play')
            logger.debug('PY MSG: play params: %s' % msg.msg)
            q.put(msg.msg.decode('utf-8'))

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



@@ 1034,7 1032,7 @@ def run_forever(str script_path, str instrument_name=None, int channels=2, doubl
            # Read messages from the console and relay them to the q
            # times out after a second to allow for render pool cleanup
            if astrid_instrument_tick(instrument.i) < 0:
                logger.info('PY: Could not read console line')
                logger.error('PY: Could not read console line')
                time.sleep(2)
                continue


M pippi/soundbuffer.pyx => pippi/soundbuffer.pyx +3 -1
@@ 20,6 20,7 @@ from pippi cimport fft
from pippi import graph
from pippi.defaults cimport DEFAULT_SAMPLERATE, DEFAULT_CHANNELS, DEFAULT_SOUNDFILE, PI
from pippi cimport grains2
from pippi cimport grains
from pippi cimport soundpipe

np.import_array()


@@ 714,7 715,8 @@ cdef class SoundBuffer:
    def cloud(SoundBuffer self, double length=-1, *args, **kwargs):
        """ Create a new Cloud from this SoundBuffer
        """
        return grains2.Cloud2(self, *args, **kwargs).play(length)
        #return grains2.Cloud2(self, *args, **kwargs).play(length)
        return grains.Cloud(self, *args, **kwargs).play(length)

    def copy(self):
        """ Return a new copy of this SoundBuffer.