Audio architecture advice

Hi everyone !

I'm working on a small synth living in the terminal (source code here)

I have three concurrent thread :

  • ui, that register key presses and updates interface
  • midi, that receive midi event for new note and parameter modifications
  • audio, that runs the synth and compute samples

The way I do it now is that UI and MIDI have their copy of a HashMap containing all the parameters, they share this map with mutex. Both update this hashmap when new values comes, and then send the new status of the modified parameter to the audio thread through a channel. The audio thread checks for new messages before each buffer. If there is a new parameter, it updates it's own copy of the parameters Hashmap.

It works, but maybe their is a more efficient way.
I have one problem with this implementation though : the HashMap.
It is quite handy for retrieving parameter value in the audio thread without having to remember their index, by using names, and it is easy to add a new parameters. But I must use Strings of text to get parameter values, and that don't trigger error before running the program: I'd like a behaviour closer to that of the enum (autocompletion would be nice too).
Also, the hashmap doesn't keep order, which I need to have a constant interface (I iterate thought it to print parameter names and values).
I've got 10-20 Parameters and there are know at compile time, the hashmap seems a bit overkill here, the only real plus is for convenience while writing the code.

I'm not super savvy about data structures and was wondering if there was any good candidates ?

I was wondering if I could something like an enum :

pub enum Parameters{
filter_cutoff(Parameters{display_name: "filter".to_string, value: 35, ...}),
volume(Parameters{display_name: "volume".to_string, value: 32, ...}),
}

struct Parameter{
display_name: String,
value: i32,
...
}

But from what is gathered enum can't hold a fixed value, so I would have to declare them here and define them elsewhere, which isn't perfect either... I would also need to wrap all this in a bigger vector containing one instance of each parameter, not elegant.

Using a struct with

struct Parameters{
frequence_cutoff: Parameter,
volume: Parameter,
}

impl Parameters{
pub fn new()->Self{
Parameters{
frequence_cutoff = Parameter{/*default value*/},
volume = Parameter{/*default value*/},
}}
}

would probably be the closest thing, but I don't know, it feel like there must be something more appropriate somewhere, also it is not very convenient for iteration...

Thanks for reading !

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.