December
double densed uncounthest hour of allbleakest age with a bad of wind and a barrel of rain
double densed uncounthest hour of allbleakest age with a bad of wind and a barrel of rain is an in-progress piece for resonators and brass. I’m keeping a composition log here as I work on it.
There are sure to be many detours. Getting it in shape might involve:
- more work on libpippi unit generators & integrating streams into astrid
- testing serial triggers with the solenoid tree & relay system built into the passive mixer
- finishing the passive mixer / relay system and firmware (what to do about the enclosure!?)
- general astrid debugging and quality-of-life improvements…
- composing maybe?
Monday December 30th
I’m cross-posting again. This was posted on izzzzi yesterday, but’s really an astrid dev log post I guess!
I started reworking autotriggers in astrid again this weekend… I think this time they’re nicer, simpler: basically just an array of structs in shared memory.
The structs have an interval, a phase, and a payload field with a corresponding enum that indicates the type of the payload.
There’s a thread whose sole job is to aquire a lock on them once every ~1ms, copy the trigger table (and then release the lock) and then loop over the active triggers and do one of three things based on the type:
-
treat the payload field as an astrid command string and parse it / send the message, (like
p foo=bar
sends a play message with thefoo=bar
params, etc) -
treat the payload field as an astrid msg struct and just send it right away, (maybe useful for automated messages? not using it at the moment)
-
treat the payload as a 4096 step wavetable and send the current value interpolated at the phase as an astrid update message on every tick (every ~1ms!)
So mode #3 acts as a continuous LFO basically – sending update messages at the ~1ms control rate, which are handled by the instrument’s update callback if it’s been implemented – while modes #1/2 are more like gate generators where edges make messages and those only get sent once per interval.
I’ve only tested them by hard-coding in an array of autotriggers so far. There’s still a lot to do so they can be updated via messages (added/removed/changed) and ultimately configured in the instrument scripts, but maybe I can do that tomorrow night…
The (as of right now, imaginary) python interface for this in astrid instruments will look something like:
LFOS=(
# send play messages at 0.4hz
(0.4, 'p foo=bar'),
# send control messages
# w/the value of a hann window
# with a period of 1.2hz
(1.2, dsp.win('hann')),
# send a trigger message at 0.1hz
(0.1, 't drum=hat'),
)
For more complicated sequencing there is already a trigger callback, where these can be scheduled arbitrarily, but I’m working on a simpler interface to set up patterns with generators defined as a tuple of intervals in hertz and some polymorphic payload…
Friday December 27th
This is a cross-post from izzzzi
where I have been writing things as hecanjog
. Let me know
if you join & add me, and I’ll add you too. It’s a neat project.
–
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 {
* ops[MAXOSCS];
lpsineosc_t * osc;
lpsineosc_t ;
lpfloat_t base_freq[MAXOSCS];
lpfloat_t mod_freqs[MAXOSCS];
lpfloat_t mod_depths} localctx_t;
int param_map_callback(void * arg, char * keystr, char * valstr) {
* instrument = (lpinstrument_t *)arg;
lpinstrument_t float val_f = 0;
int32_t val_i32 = 0;
int num_vals;
[MAXOSCS];
lpfloat_t val_floatlist
if(strcmp(keystr, "freq") == 0) {
(valstr, &val_f);
extract_float_from_token(instrument, PARAM_BASE_FREQ, val_f);
astrid_instrument_set_param_float} else if(strcmp(keystr, "mods") == 0) {
= extract_floatlist_from_token(valstr, val_floatlist, MAXOSCS);
num_vals (instrument, PARAM_MOD_FREQS, val_floatlist, num_vals);
astrid_instrument_set_param_float_list} else if(strcmp(keystr, "depths") == 0) {
= extract_floatlist_from_token(valstr, val_floatlist, MAXOSCS);
num_vals (instrument, PARAM_MOD_DEPTHS, val_floatlist, num_vals);
astrid_instrument_set_param_float_list} else if(strcmp(keystr, "oscs") == 0) {
(valstr, &val_i32);
extract_int32_from_token(instrument, PARAM_NUMOSCS, val_i32);
astrid_instrument_set_param_int32} else if(strcmp(keystr, "save") == 0) {
(valstr, &val_i32);
extract_int32_from_token(instrument, NUMPARAMS, val_i32);
astrid_instrument_save_param_session_snapshot} else if(strcmp(keystr, "restore") == 0) {
(valstr, &val_i32);
extract_int32_from_token(instrument, val_i32);
astrid_instrument_restore_param_session_snapshot}
return 0;
}
int audio_callback(
size_t blocksize,
((unused)) float ** input,
__attribute__float ** output, void * arg
) {
size_t i;
int num_oscs, c, j;
, mod, freq;
lpfloat_t out
* instrument = (lpinstrument_t *)arg;
lpinstrument_t * ctx = (localctx_t *)instrument->context;
localctx_t
= astrid_instrument_get_param_float(instrument, PARAM_BASE_FREQ, 200.f);
freq = astrid_instrument_get_param_int32(instrument, PARAM_NUMOSCS, INITIAL_OSCS);
num_oscs (instrument, PARAM_MOD_FREQS, num_oscs, ctx->mod_freqs);
astrid_instrument_get_param_float_list(instrument, PARAM_MOD_DEPTHS, num_oscs, ctx->mod_depths);
astrid_instrument_get_param_float_list
for(i=0; i < blocksize; i++) {
=0.f, mod=0.f;
outfor(j=0; j < num_oscs; j++) {
->ops[j]->freq = ctx->mod_freqs[j];
ctx+= LPSineOsc.process(ctx->ops[j]) * ctx->mod_depths[j];
mod }
->osc->freq = freq + (mod/(lpfloat_t)num_oscs);
ctx= LPSineOsc.process(ctx->osc);
out
for(c=0; c < instrument->output_channels; c++) {
[c][i] = out * 0.1f;
output}
}
return 0;
}
int main() {
* instrument;
lpinstrument_t * ctx;
localctx_t
// Set up a struct that will be available in the audio callback
= (localctx_t *)calloc(1, sizeof(localctx_t));
ctx
// Create the sinewave oscs
for(int i=0; i < MAXOSCS; i++) {
->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();
ctx
// Init the config, which sets the default config values
= astrid_instrument_init_config(NAME);
lpinstrument_config_t config
// Set a reference to the callbacks and the local context struct
.stream_callback = audio_callback;
config.update_callback = param_map_callback;
config.ctx = (void*)ctx;
config
// Start the instrument from the config
if((instrument = astrid_instrument_start_from_config(config)) == NULL) {
(stderr, "Could not start instrument: (%d) %s\n", errno, strerror(errno));
fprintf(EXIT_FAILURE);
exit}
// Run until a shutdown event or signal triggers shutdown
while(instrument->is_running) {
(instrument);
astrid_instrument_tick}
// Clean up resources and stop running processes
return astrid_instrument_stop(instrument);
}
Tuesday December 3rd
Woah, it’s already December. It’s been a year now since I started journaling about this rain piece for resonators and brass. Since then the project has expanded to incorporate more computer-controled solenoid and motor-driven activation of the resonators, and the focus has shifted gradually away from brass again for a while.
I’m trying to finish some recording projects over the winter, some of which involve vague ideas about overdubs and additional instrumental arrangements, so I’m hoping to give the brass aspect of rain another look while I work on some of those things.
LMDB
I was using the API correctly… adding new astrid interfaces for batches of param updates is still a good idea though, I think – I only made it partway through the last time I picked it all up.
Next Astrid Instruments
Recent live outings have been mostly non-computer-based, but I’d like to take it back inside the computer again somewhat after prototyping some ideas with mixers and pedals. Sometimes it’s easier to see where the computer is most useful by removing it everywhere possible first.
In short: sampling, of course (at all time scales) and sequencing, of course, are places where the computer really shines. I think I can squeeze as many as 12 outputs from a headless setup around my old laptop mainboard, and potentially do much of the routing and filtering in a secondary layer with a mix of passive electronics and guitar pedals…
Log November 2024
Log October 2024
(oops I missed September!)
Log August 2024
Log July 2024
Log June 2024
Log May 2024
Log April 2024
Log March 2024
Log February 2024
Log January 2024
Log December 2023