~hecanjog/pippi

4c7fba740d8f0b0b7498a0f0443659e15c3c0bf0 — Erik Schoster 4 months ago 29327ea
First pass save/restore astrid param session, misc tweaks to littlefield instrument
4 files changed, 252 insertions(+), 25 deletions(-)

M astrid/orc/littlefield.c
M astrid/src/astrid.c
M astrid/src/astrid.h
M pippi/renderer.pyx
M astrid/orc/littlefield.c => astrid/orc/littlefield.c +31 -24
@@ 7,7 7,7 @@
#define ADC_LENGTH 30
#define MIC_ATTENUATION 0.2f

#define NUMOSCS 20
#define NUMOSCS 10
#define WTSIZE 4096
#define MAXNUMFREQS NUMOSCS



@@ 176,6 176,13 @@ int param_map_callback(void * arg, char * keystr, char * valstr) {
    } else if(strcmp(keystr, "freqs") == 0) {
        num_freqs = extract_floatlist_from_token(valstr, val_floatlist, MAXNUMFREQS);
        astrid_instrument_set_param_float_list(instrument, PARAM_OSC_FREQS, val_floatlist, num_freqs);
    } else if(strcmp(keystr, "oo1") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_OSC_TO_OUT1, val_f);
    } else if(strcmp(keystr, "oo2") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_OSC_TO_OUT2, val_f);


    } else if(strcmp(keystr, "gamp") == 0) {
        extract_float_from_token(valstr, &val_f);


@@ 208,7 215,14 @@ int param_map_callback(void * arg, char * keystr, char * valstr) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_GATE_DRIFT_PERIODICITY, val_f);

    } else if(strcmp(keystr, "gpat") == 0) {
    } else if(strcmp(keystr, "go1") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_GATE_TO_OUT1, val_f);
    } else if(strcmp(keystr, "go2") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_GATE_TO_OUT2, val_f);

    } else if(strcmp(keystr, "pat") == 0) {
        extract_patternbuf_from_token(valstr, val_pattern.pattern, &val_pattern.length);
        astrid_instrument_set_param_patternbuf(instrument, PARAM_GATE_PATTERN, &val_pattern);
    } else if(strcmp(keystr, "greset") == 0) {


@@ 230,20 244,6 @@ int param_map_callback(void * arg, char * keystr, char * valstr) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_IN2_TO_OUT2, val_f);

    } else if(strcmp(keystr, "oo1") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_OSC_TO_OUT1, val_f);
    } else if(strcmp(keystr, "oo2") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_OSC_TO_OUT2, val_f);

    } else if(strcmp(keystr, "go1") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_GATE_TO_OUT1, val_f);
    } else if(strcmp(keystr, "go2") == 0) {
        extract_float_from_token(valstr, &val_f);
        astrid_instrument_set_param_float(instrument, PARAM_GATE_TO_OUT2, val_f);

    } else if(strcmp(keystr, "disttype") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        if(val_i32 < 0 || val_i32 >= NUM_DISTORTIONS) val_i32 = DISTORTION_FOLDBACK;


@@ 258,6 258,13 @@ int param_map_callback(void * arg, char * keystr, char * valstr) {
    } 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);

    } else if(strcmp(keystr, "save") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        astrid_instrument_save_param_session_snapshot(instrument, NUMPARAMS, val_i32);
    } else if(strcmp(keystr, "restore") == 0) {
        extract_int32_from_token(valstr, &val_i32);
        astrid_instrument_restore_param_session_snapshot(instrument, val_i32);
    }    

    return 0;


@@ 307,19 314,19 @@ 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, 0.f);
    osc_amp = astrid_instrument_get_param_float(instrument, PARAM_OSC_AMP, 1.f);
    osc_out1_mix = astrid_instrument_get_param_float(instrument, PARAM_OSC_TO_OUT1, 0.5f);
    osc_out2_mix = astrid_instrument_get_param_float(instrument, PARAM_OSC_TO_OUT2, 0.5f);
    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.001f) * 1000.f + 1.f;
    osc_drift_amount = astrid_instrument_get_param_float(instrument, PARAM_OSC_DRIFT_DEPTH, 0.f) * 10.f;
    osc_env_speed = astrid_instrument_get_param_float(instrument, PARAM_OSC_ENVELOPE_SPEED, 0.001f) * 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, 0);
    octave_offset = astrid_instrument_get_param_int32(instrument, PARAM_OSC_OCTAVE_OFFSET, 0);
    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);
    astrid_instrument_get_param_float_list(instrument, PARAM_OSC_FREQS, num_freqs, freqs);

    gate_amp = astrid_instrument_get_param_float(instrument, PARAM_GATE_AMP, 0.5f);
    gate_amp = astrid_instrument_get_param_float(instrument, PARAM_GATE_AMP, 0.f);
    gate_out1_mix = astrid_instrument_get_param_float(instrument, PARAM_GATE_TO_OUT1, 0.5f);
    gate_out2_mix = astrid_instrument_get_param_float(instrument, PARAM_GATE_TO_OUT2, 0.5f);
    gate_reset = astrid_instrument_get_param_int32(instrument, PARAM_GATE_PHASE_RESET, 0);


@@ 373,7 380,7 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
                last_p = p;
            }

            tracked_freq = LPFX.lpf1(tracked_freq, &ctx->freqsmooth, 20.f, SR);
            tracked_freq = LPFX.lpf1(tracked_freq, &ctx->freqsmooth, 100.f, SR);

            // track in a ~melodic range
            while(tracked_freq >= 800) tracked_freq *= 0.5f;


@@ 429,7 436,7 @@ int audio_callback(size_t blocksize, float ** input, float ** output, void * arg
        output[1][i] += (sample * osc_out2_mix) + (gated_sample * gate_out2_mix) + (in2 * in1_out2_mix) + (in2 * in2_out2_mix);

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

M astrid/src/astrid.c => astrid/src/astrid.c +210 -1
@@ 3285,7 3285,7 @@ void * instrument_midi_listener_thread(void * arg) {
        return 0;
    }

    if((ret = snd_seq_connect_from(seq_handle, port, 20, 0)) < 0) {
    if((ret = snd_seq_connect_from(seq_handle, port, 24, 0)) < 0) {
        syslog(LOG_ERR, "%s midi listener: Could not connect to ALSA port. Error: (%d) %s\n", instrument->name, ret, snd_strerror(ret));
        snd_seq_close(seq_handle);
        return 0;


@@ 3891,6 3891,215 @@ int astrid_instrument_tick(lpinstrument_t * instrument) {
    return 0;
}


int astrid_instrument_save_param_session_snapshot(lpinstrument_t * instrument, int num_params, int snapshot_id) {
    int shmfd;
    sem_t * sem;
    void * shm_base;
    size_t shm_size = 0;
    size_t offset = 0;
    char path[PATH_MAX] = {0};

    // Generate the path for the shared memory and semaphore
    snprintf(path, PATH_MAX, "%s-%s-%d", ASTRID_SESSION_SNAPSHOT_NAME, instrument->name, snapshot_id);

    // Create and initialize the semaphore
    if((sem = sem_open(path, O_CREAT, LPIPC_PERMS, 1)) == SEM_FAILED) {
        syslog(LOG_ERR, "Could not create semaphore. (%s) %s\n", path, strerror(errno));
        return -1;
    }

    // Calculate the required size for shared memory
    for(int i = 0; i < num_params; i++) {
        MDB_val key, data;
        key.mv_size = sizeof(int);
        key.mv_data = &i;

        int rc = mdb_txn_renew(instrument->dbtxn_read);
        if(rc == 0 && mdb_get(instrument->dbtxn_read, instrument->dbi, &key, &data) == 0) {
            shm_size += sizeof(int) + sizeof(size_t) + data.mv_size;
        }
        mdb_txn_reset(instrument->dbtxn_read);
    }

    // Create and truncate the shared memory segment
    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));
        sem_close(sem);
        sem_unlink(path);
        return -1;
    }

    if(ftruncate(shmfd, shm_size) < 0) {
        syslog(LOG_ERR, "Could not truncate shared memory segment to size %ld. (%s) %s\n", shm_size, path, strerror(errno));
        close(shmfd);
        sem_close(sem);
        sem_unlink(path);
        return -1;
    }

    if((shm_base = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)) == MAP_FAILED) {
        syslog(LOG_ERR, "Could not mmap shared memory segment. (%s) %s\n", path, strerror(errno));
        close(shmfd);
        sem_close(sem);
        sem_unlink(path);
        return -1;
    }

    // Lock the semaphore
    sem_wait(sem);

    // Write data to shared memory
    for(int i = 0; i < num_params; i++) {
        MDB_val key, data;
        key.mv_size = sizeof(int);
        key.mv_data = &i;

        int rc = mdb_txn_renew(instrument->dbtxn_read);
        if (rc == 0 && mdb_get(instrument->dbtxn_read, instrument->dbi, &key, &data) == 0) {
            memcpy((char *)shm_base + offset, &i, sizeof(int));
            offset += sizeof(int);
            size_t data_size = data.mv_size;
            memcpy((char *)shm_base + offset, &data_size, sizeof(size_t));
            offset += sizeof(size_t);
            memcpy((char *)shm_base + offset, data.mv_data, data_size);
            offset += data_size;
        }
        mdb_txn_reset(instrument->dbtxn_read);
    }

    // Unlock the semaphore
    sem_post(sem);

    // Clean up
    close(shmfd);
    sem_close(sem);

    return 0;
}

int astrid_instrument_restore_param_session_snapshot(lpinstrument_t * instrument, int snapshot_id) {
    int shmfd;
    sem_t * sem;
    void * shm_base;
    size_t offset = 0;
    char path[PATH_MAX] = {0};

    // Generate the path for the shared memory and semaphore
    snprintf(path, PATH_MAX, "%s-%s-%d", ASTRID_SESSION_SNAPSHOT_NAME, instrument->name, snapshot_id);

    // Open the semaphore
    if((sem = sem_open(path, 0)) == SEM_FAILED) {
        syslog(LOG_ERR, "Could not open semaphore. (%s) %s\n", path, strerror(errno));
        return -1;
    }

    // Open the shared memory segment
    if((shmfd = shm_open(path, O_RDWR, LPIPC_PERMS)) < 0) {
        syslog(LOG_ERR, "Could not open shared memory segment. (%s) %s\n", path, strerror(errno));
        sem_close(sem);
        return -1;
    }

    // Get the size of the shared memory segment
    struct stat shm_stat;
    if(fstat(shmfd, &shm_stat) < 0) {
        syslog(LOG_ERR, "Could not get shared memory segment size. (%s) %s\n", path, strerror(errno));
        close(shmfd);
        sem_close(sem);
        return -1;
    }
    size_t shm_size = shm_stat.st_size;

    if((shm_base = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)) == MAP_FAILED) {
        syslog(LOG_ERR, "Could not mmap shared memory segment. (%s) %s\n", path, strerror(errno));
        close(shmfd);
        sem_close(sem);
        return -1;
    }

    // Lock the semaphore
    sem_wait(sem);

    // Read data from shared memory
    while(offset < shm_size) {
        int param_index;
        size_t data_size;
        void *data;

        memcpy(&param_index, (char *)shm_base + offset, sizeof(int));
        offset += sizeof(int);
        memcpy(&data_size, (char *)shm_base + offset, sizeof(size_t));
        offset += sizeof(size_t);

        data = malloc(data_size);
        if(!data) {
            syslog(LOG_ERR, "Failed to allocate memory for parameter data.");
            sem_post(sem);
            munmap(shm_base, shm_size);
            close(shmfd);
            sem_close(sem);
            return -1;
        }

        memcpy(data, (char *)shm_base + offset, data_size);
        offset += data_size;

        MDB_val key, mdb_data;
        key.mv_size = sizeof(int);
        key.mv_data = &param_index;
        mdb_data.mv_size = data_size;
        mdb_data.mv_data = data;

        int rc = mdb_txn_begin(instrument->dbenv, NULL, 0, &instrument->dbtxn_write);
        if(rc) {
            syslog(LOG_ERR, "Failed to begin transaction: (%d) %s", rc, mdb_strerror(rc));
            free(data);
            sem_post(sem);
            munmap(shm_base, shm_size);
            close(shmfd);
            sem_close(sem);
            return -1;
        }

        rc = mdb_put(instrument->dbtxn_write, instrument->dbi, &key, &mdb_data, 0);
        if(rc) {
            syslog(LOG_ERR, "Failed to put parameter data: (%d) %s", rc, mdb_strerror(rc));
            free(data);
            mdb_txn_abort(instrument->dbtxn_write);
            sem_post(sem);
            munmap(shm_base, shm_size);
            close(shmfd);
            sem_close(sem);
            return -1;
        }

        rc = mdb_txn_commit(instrument->dbtxn_write);
        if(rc) {
            syslog(LOG_ERR, "Failed to commit transaction: (%d) %s", rc, mdb_strerror(rc));
            free(data);
            sem_post(sem);
            munmap(shm_base, shm_size);
            close(shmfd);
            sem_close(sem);
            return -1;
        }

        free(data);
    }

    // Unlock the semaphore
    sem_post(sem);

    // Clean up
    munmap(shm_base, shm_size);
    close(shmfd);
    sem_close(sem);

    return 0;
}


int32_t astrid_instrument_get_param_int32(lpinstrument_t * instrument, int param_index, int32_t default_value) {
    int rc;
    MDB_val key, data;

M astrid/src/astrid.h => astrid/src/astrid.h +5 -0
@@ 57,6 57,8 @@

#define ASTRID_SERIAL_CTLBASE_PATH "/tmp/astrid-serialdevice%d-ctl%d"

#define ASTRID_SESSION_SNAPSHOT_NAME "/astrid-session-snapshot"

#define LPKEY_MAXLENGTH 4096
#define ASTRID_MAX_CMDLINE 4096



@@ 336,6 338,9 @@ void astrid_instrument_set_param_float_list(lpinstrument_t * instrument, int par
void astrid_instrument_get_param_float_list(lpinstrument_t * instrument, int param_index, size_t size, lpfloat_t * list);
lpfloat_t astrid_instrument_get_param_float_list_item(lpinstrument_t * instrument, int param_index, size_t size, int item_index, lpfloat_t default_value);

int astrid_instrument_restore_param_session_snapshot(lpinstrument_t * instrument, int snapshot_id);
int astrid_instrument_save_param_session_snapshot(lpinstrument_t * instrument, int num_params, int snapshot_id);

int astrid_instrument_tick(lpinstrument_t * instrument);
int astrid_instrument_session_open(lpinstrument_t * instrument);
int astrid_instrument_session_close(lpinstrument_t * instrument);

M pippi/renderer.pyx => pippi/renderer.pyx +6 -0
@@ 477,6 477,12 @@ cdef class Instrument:
        cdef EventContext ctx 
        cdef dict params
        cdef str p, k, v
        cdef size_t last_edit

        last_edit = os.path.getmtime(self.path)
        if last_edit > self.last_reload:
            self.reload()
            self.last_reload = last_edit

        if not hasattr(self.renderer, 'update'):
            logger.warning('Ignoring update message: this instrument has no callback registered')