Skip to content
Snippets Groups Projects
Commit cfe3c0ca authored by q3k's avatar q3k
Browse files

badge23: remove dead instruments code

This is replaced by more mpy-centric code for user interaction plus
bl00mbox for the actual sound framework.
parent 1030d53f
Branches
Tags
1 merge request!20badge23: nearly fully split
void render_audio(
instrument_descriptor_t instrument_descriptor,
instrument_t instance,
unsigned int sample_count,
float * output_vector
){
for(unsigned int i = 0; i < sample_count; i++){
instrument_descriptor->render_audio_sample(instance, sample_count, output_vector[2*i]);
}
}
void render_audio_adding(
instrument_descriptor_t instrument_descriptor,
instrument_t instance,
unsigned int sample_count,
float * output_vector,
float gain
){
if(gain <= 0.0000001) return;
float temp[2];
for(unsigned int i = 0; i < sample_count; i++){
instrument_descriptor->render_audio_sample(instance, sample_count, temp);
output_vector[2*i] += gain * buffer[0];
output_vector[2*i+1] += gain * buffer[1];
}
}
void append_instrument_descriptor(
instrument_descriptor_list_t * list,
void * construct_instrument_descriptor
){
lle_t * element = list;
while(element->next != NULL){
element = element->next;
}
element->next = malloc(sizeof(lle_t);
if(element->next == NULL) return;
element = element->next;
element->next = NULL;
element->content = construct_instrument_descriptor();
}
instrument_descriptor_list_t * list_builtin_instrument_descriptors(){
//really hope we can make this more elegant some day
instrument_descriptor_list_t * list = NULL;
//add your instrument here!
append_instrument_descriptor(list, &minimal_example_descriptor);
return list;
}
void instantiate_instrument(active_instrument_list_t instruments,
instrument_descriptor_t descriptor
){
descriptor->new_instrument
}
void mix_instruments_audio(active_instrument_list_t instruments,
unsigned int sample_count,
float * output_vector
){
active_instrument_t instrument = instruments;
while(instrument->next != NULL){
render_audio_adding(instrument->descriptor,
instrument->instrument,
sample_count,
output_vector,
instrument->gain)
instrument = instrument->next;
}
}
/* wip instrument api for badge23 (in large parts inspired by ladspa)
current status: none of this is hooked up or functional or compiles, just drafting things rn
some core concepts:
- several instruments can run at the same time (e.g., drum computer running in background),
with only one of them being in foreground and using buttons and display
- instruments are as "self contained" as possible, i.e. they access buttons and display with
minimal host involvement and pretty much just produce audio "on their own". aside from
scheduling the host mostly does some cap touch preprocessing and the audio output mixdown
- different timing requirements -> different "threads" for audio, buttons, display each
(leds: special case, see below)
open questions:
- led animations: instruments should be able to output led patterns. maybe keeping a dummy
led array in ram for each running instrument and allowing users to create and run "shaders"
would be a desirable functional mode; do we want this/does this need any extra api?
- for performance reasons: instruments are expected to behave themselves, i.e. not access hw
without permission or write to read-only locations, can we do better than that without
excessive overhead? and if we can, do we _want_ to? (devices that make electronic musical
instruments malfunction on purpose are not uncommon in general)
- badge link/cv data input/output: can probably be added to the descriptor easily? shouldn't
freeze api before tho
*/
//===========================================================================================
//some hardware dummy definitions, move somewhere else someday maybe
typedef struct {
int intensity; //touch strength, considered as no touch if below 0
int rad; //radial position
int az; //cw azimuthal position (only meaningful for top petals);
} petal_t;
typedef int button_t; //0: no press, -1: left, 1: right, 2: down
typedef struct { //read-only (shared between all instruments, unprotected)
petal_t petals[10]; //even: top, odd: bottom, 0 at usb-c jack, count ccw
button_t button; //can be either left or right, depending on host
//handedness settings. the other button is reserved for
//host use and is not exposed here.
} hardware_inputs_t;
//===========================================================================================
typedef void * instrument_t; //contains instrument instance data, not to be interpreted by host
typedef struct _instrument_descriptor_t {
unsigned int unique_id;
//null terminated instrument name
const char * name;
//allocates memory for new instance data and returns pointer to it (mandatory)
instrument_t (* new_instrument)(const struct _instrument_descriptor * descriptor,
unsigned int sample_rate,
hardware_inputs_t * hw);
//frees instance data memory (mandatory)
void (* delete_instrument) (instrument_t instance);
//renders a single stereo audio sample (optional, NULL if not supported)
void (* render_audio_sample) (instrument_t instance);
//handles petal/button/sensor input, ideally runs every 3-8ms (optional, NULL if not supported)
void (* process_user_input) (instrument_t instance);
//only runs when instrument is in foreground (optional, NULL if not supported)
void (* update_display) (instrument_t instance);
//(optional, NULL if not supported)
void (* update_leds) (instrument_t instance);
} instrument_descriptor_t;
// this function is called once per instrument type before use (i.e. around boot) and returns a
// filled out instrument descriptor struct to be used by the host for creating instrument instances
// returns null if allocation fails
const instrument_descriptor_t * contruct_instrument_descriptor();
//===========================================================================================
//host-side helper functions
void render_audio(
instrument_descriptor_t instrument_descriptor,
instrument_t instance,
unsigned int sample_count,
float * output_vector
);
void render_audio_adding(
instrument_descriptor_t instrument_descriptor,
instrument_t instance,
unsigned int sample_count,
float * output_vector,
float gain
);
typedef struct {
void * content;
lle_t * next;
} lle_t; //linked list element
typedef lle_t instrument_descriptor_list_t;
typedef struct {
instrument_t * instrument;
instrument_descriptor_t descriptor;
char is_foreground;
char is_rendering_leds;
float gain;
} active_instrument_t;
void append_instrument_descriptor(
instrument_descriptor_list_t * list,
void * construct_instrument_descriptor
);
instrument_descriptor_list_t * list_builtin_instrument_descriptors();
typedef lle_t active_instrument_list_t;
void mix_instruments_audio(active_instrument_list_t instruments,
unsigned int sample_count,
float * output_vector
);
//===========================================================================================
//simple instrument example implementation
//trad_osc is made up rn, didn't check how the existing implementation works
typedef struct{
unsigned int * sample_rate;
hardware_inputs_t * hw;
trad_osc_t[30] osc;
unsigned int sample_rate;
hardware_inputs_t * hw;
float last_freq; //frequency of last note played to write to display
} minimal_example_t;
void minimal_example_render_sample(instrument_handle inst, float * stereo_output){
float acc = 0;
for(int i = 0; i < 30; i++){
acc += trad_osc_run(&inst.osc[i], inst.sample_rate);
}
stereo_output[0] = acc; // both channels the same -> mono
stereo_output[1] = acc;
}
void minimal_example_process_user_input(instrument_handle inst){
static int petal_prev[10];
for(int i = 0; i < 10; i++){
if(inst->hw.petals[i].intensity > 0){ //if the pad is touched...
if(!petal_prev[i]){ //and it's a new touch...
if(button != 2){ //if button isn't pressed: play single note in different octaves
int j = i + (inst->hw.button + 1) * 10; //choose osc
trad_osc_start(&inst.osc[j]); //play a tone
inst->last_freq = inst.osc[j].freq;
} else { //for button center: all the octaves at once
trad_osc_start(&inst.osc[i]); //play a tone
trad_osc_start(&inst.osc[i+10]); //play a tone
trad_osc_start(&inst.osc[i+20]); //play a tone
inst->last_freq = inst.osc[i+10].freq;
}
}
petal_prev[i] = 1;
} else {
petal_prev[i] = 0;
}
}
}
void minimal_example_update_display(instrument_handle inst){
display_print("%f", inst->last_freq);
}
static float pad_to_freq(int pad){
int j = pad % 10;
if(j) j++; //skip min 2nd
if(j>8) j++; //skip maj 6th
j += (pad/10) * 12; //add octaves
float freq = 440 * pow(2., j/12.);
}
instrument_t new_minimal_example(
const struct _instrument_descriptor * descriptor,
unsigned int sample_rate,
hardware_inputs_t * hw)
{
instrument_t inst = malloc(sizeof(minimal_example_t));
if(inst == NULL) return NULL;
inst->sample_rate = sample_rate;
inst->hw = hw;
for(int i = 0; i < 30; i++){
inst->osc[i] = trad_osc_new(pad_to_freq(i), sample_rate);
//other osc parameters (wave, envelope, etc.) omitted for clarity
}
return inst;
}
void delete_minimal_example(instrument_t inst){
free(inst);
}
const instrument_descriptor_t * minimal_example_descriptor(){
instrument_descriptor_t * inst = malloc(sizeof(instrument_descriptor_t));
if(inst == NULL) return NULL;
inst->unique_id = 0;
inst->name = "simple instrument";
inst->render_audio_sample = &minimal_example_render_sample;
inst->new_instrument = &new_minimal_example;
inst->delete_instrument = &delete_minimal_example;
inst->update_display = NULL;
inst->process_user_input = minimal_example_user_input;
inst->update_leds = NULL;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment