I decided to clean up the astrid instruments in the repo a bit on a whim tonight, and add a makefile template (partly inspired by the electro-smith daisy project, and partly anti-inspired by arduino’s annoyingly magic tooling) that can be used as a base to make it easier to make little standalone astrid C instrument projects.
Here’s an example program (variable operator FM) and a makefile for it, with a render attached. With seven operators it sounds a bit like a sputtering lawnmower?
When it runs, it opens a basic command console. To set the base
frequency to 256.4 type u freq=256.4 for example. To quit,
type q. (Other commands invoke things like async renders
and trigger callbacks, neither of which are used by this particular
instrument.)
# Makefile
LPDIR := ../../../libpippi
NAME := simple
include $(LPDIR)/../astrid/Makefile.base
// simple.c
#include "astrid.h"
#define NAME "simple"
#define MAXOSCS 20
#define INITIAL_OSCS 7
enum InstrumentParams {
PARAM_NUMOSCS,
PARAM_BASE_FREQ,
PARAM_MOD_FREQS,
PARAM_MOD_DEPTHS,
NUMPARAMS
};
typedef struct localctx_t {
lpsineosc_t * ops[MAXOSCS];
lpsineosc_t * osc;
lpfloat_t base_freq;
lpfloat_t mod_freqs[MAXOSCS];
lpfloat_t mod_depths[MAXOSCS];
} localctx_t;
int param_map_callback(void * arg, char * keystr, char * valstr) {
lpinstrument_t * instrument = (lpinstrument_t *)arg;
float val_f = 0;
int32_t val_i32 = 0;
int num_vals;
lpfloat_t val_floatlist[MAXOSCS];
if(strcmp(keystr, "freq") == 0) {
extract_float_from_token(valstr, &val_f);
astrid_instrument_set_param_float(instrument, PARAM_BASE_FREQ, val_f);
} else if(strcmp(keystr, "mods") == 0) {
num_vals = extract_floatlist_from_token(valstr, val_floatlist, MAXOSCS);
astrid_instrument_set_param_float_list(instrument, PARAM_MOD_FREQS, val_floatlist, num_vals);
} else if(strcmp(keystr, "depths") == 0) {
num_vals = extract_floatlist_from_token(valstr, val_floatlist, MAXOSCS);
astrid_instrument_set_param_float_list(instrument, PARAM_MOD_DEPTHS, val_floatlist, num_vals);
} else if(strcmp(keystr, "oscs") == 0) {
extract_int32_from_token(valstr, &val_i32);
astrid_instrument_set_param_int32(instrument, PARAM_NUMOSCS, 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;
}
int audio_callback(
size_t blocksize,
__attribute__((unused)) float ** input,
float ** output, void * arg
) {
size_t i;
int num_oscs, c, j;
lpfloat_t out, mod, freq;
lpinstrument_t * instrument = (lpinstrument_t *)arg;
localctx_t * ctx = (localctx_t *)instrument->context;
freq = astrid_instrument_get_param_float(instrument, PARAM_BASE_FREQ, 200.f);
num_oscs = astrid_instrument_get_param_int32(instrument, PARAM_NUMOSCS, INITIAL_OSCS);
astrid_instrument_get_param_float_list(instrument, PARAM_MOD_FREQS, num_oscs, ctx->mod_freqs);
astrid_instrument_get_param_float_list(instrument, PARAM_MOD_DEPTHS, num_oscs, ctx->mod_depths);
for(i=0; i < blocksize; i++) {
out=0.f, mod=0.f;
for(j=0; j < num_oscs; j++) {
ctx->ops[j]->freq = ctx->mod_freqs[j];
mod += LPSineOsc.process(ctx->ops[j]) * ctx->mod_depths[j];
}
ctx->osc->freq = freq + (mod/(lpfloat_t)num_oscs);
out = LPSineOsc.process(ctx->osc);
for(c=0; c < instrument->output_channels; c++) {
output[c][i] = out * 0.1f;
}
}
return 0;
}
int main() {
lpinstrument_t * instrument;
localctx_t * ctx;
// Set up a struct that will be available in the audio callback
ctx = (localctx_t *)calloc(1, sizeof(localctx_t));
// Create the sinewave oscs
for(int i=0; i < MAXOSCS; i++) {
ctx->ops[i] = LPSineOsc.create();
ctx->mod_freqs[i] = LPRand.rand(0.01f, LPRand.rand(3.f, 100.f));
ctx->mod_depths[i] = LPRand.rand(1.f, 1000.f);
}
ctx->osc = LPSineOsc.create();
// Init the config, which sets the default config values
lpinstrument_config_t config = astrid_instrument_init_config(NAME);
// Set a reference to the callbacks and the local context struct
config.stream_callback = audio_callback;
config.update_callback = param_map_callback;
config.ctx = (void*)ctx;
// Start the instrument from the config
if((instrument = astrid_instrument_start_from_config(config)) == NULL) {
fprintf(stderr, "Could not start instrument: (%d) %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
// Run until a shutdown event or signal triggers shutdown
while(instrument->is_running) {
astrid_instrument_tick(instrument);
}
// Clean up resources and stop running processes
return astrid_instrument_stop(instrument);
}