• General
  • Assign parameters sent via OSC -> C++ local variables

I'm sending various parameters from PD on my laptop to Bela (C++)over OSC. The basic syntax is: the parameter label ('fmSynth' for example) and the value (a float or int). There are about 250 different labels. My challenge is: what's the best way to convert the OSC transmissions to local variables on the Bela.

My initial method has been to use an unordered map:
std::unordered_map<std::string, float> state

Setting a received parameter/value (between call to renders, when the code looks for new OSC messages) looks like this:
state[parameter] = value;

And retrieving it looks like this:

auto it = state.find(parameter);
	if(it!= state.end()) {
		return it->second;
	}

This seems to work well logically, however, after I retrieve more than a few parameters for each sample processed, the CPU usage shoots up 50% and I start to get buffer underruns.

Any suggestion? I'm trying to avoid an approach where I have to compare each incoming parameter to a known variable and end up with 250 'if' statements. In reflection, perhaps I could use more specific OSC addresses ('audio/fmSynth', for example)? Ultimately performance is the most important for me, even if it requires more code than less. Thanks!

Why did you use std::unordered_mapinstead of std::map? The latter is ordered so it should be much faster at accessing elements. Even faster would be to use an array/vector and send an index into the array instead of a string.

    giuliomoro I had assumed that retrieval for unordered map would be faster than an ordered map… but that’s not so?

    That said, I would be interested in approaching this with the array/vector. Could you describe this a little more? If I want to retrieve a value (such as ‘fmSynth’) how would I know which index to access? Would I be assigning values to indexes names rather than parameter names on my laptop before they’re sent to Bela?

    5 days later

    to assign a recognizable name to an integer, you could use an enum:

    // declare enum globally
    enum Parameter {
    fmSynth,
    amSynth,
    cutoff,
    amplitude,
    numParameters
    };
    std::array<float,numParameters> parameters;
    
    // when receiving a generic index / value pair as over OSC, you can then store it in the array
    Parameter p = Parameter(myIndex);
    parameters[myIndex] = myValue;
    
    // then when you need to retrieve it in specific parts of your code ...
    void processFmSynth(){
    ...
    // use parameters[fmSynth]
    }
    void processCutoff(){
    ...
    // use parameters[cutoff]
    }

    Thanks for this!

    Two follow up questions:

    1)the 'index' i'm sending from my laptop, can it be a string ('fmSynth', for example), or does it need to be an integer?

    when is 'p' (from the line: Parameter p = Parameter(myIndex);) used again? It looks to be of the enum type 'Parameter' and receive the index but i'm not seeing it used after this line...

    Thanks again

      violapwr 1)the 'index' i'm sending from my laptop, can it be a string ('fmSynth', for example), or does it need to be an integer?

      It was meant to be an integer, in which case your laptop should also keep a copy of the enum. If it is a string, then you'll need to map that to a Parameter or int vai a std::map, which would make part of the process pointless.

      violapwr when is 'p' (from the line: Parameter p = Parameter(myIndex)😉 used again? I

      It is not, I was meant to remove that line.

      which would make part of the process pointless

      To elaborate on this: the part of the process that would be pointless is that you still need a std::map, this time as std::map<std::string, Parameter> to convert the incoming string to a Parameter that can then be used to index parameters , e.g.: parameters[mypar] = myValue. This is still an expensive-ish operation, but one would expect incoming OSC messages to come in much less often than you need to access these parameters from the DSP routine. This means that the most common type of access (from the DSP routine) would be fast (as you just use the Parameter as an index into the parameter array, while the least common type of access (processing OSC message) would be slower, but less impactful on the overall performance. Note that the validity of these considerations depends on the actual program you write.

        giuliomoro thanks for the clarification!

        In the end I decided to go a different route: I added a second OSC address to the messages so I can further specify their destination (1st address specifies string (of a stringed instrument) and 2nd address specifies module (synth, matrix, effect, etc). And from there I'll use a series of if statements (located at the point of destination) to determine which variables to assign them. This, I think, will be preferable to the unified approach (of using a map or array) since, as I build out this project, many of the variables will be nested within a slew of different objects.

        That said, really appreciate your help @giuliomoro & thank you again