C++ help and advice - changing string variables?

Hey,

Still working away on my first Teensy synth.

It’s 8 voice polyphonic, so for changing the parameters across multiple voices, I’ve been using variables like this:

const float DIV127 = (1.0 / 127.0);
float pinknoisevol = 0;
float osc1vol = (20.0 * DIV127);
float osc2vol = (20.0 * DIV127);
float karplusvol = (20.0 * DIV127);

void loop() {
        usbMIDI.read();
        
        //listen for value changes

        mixer1.gain(0, pinknoisevol);
        mixer1.gain(1, osc1vol);
        mixer1.gain(2, osc2vol);
        mixer1.gain(3, karplusvol);
        mixer8.gain(0, pinknoisevol);
        mixer8.gain(1, osc1vol);
        mixer8.gain(2, osc2vol);
        mixer8.gain(3, karplusvol);
}

void myControlChange(byte channel, byte control, byte value) {
  switch (control) {

    case 106:
      switch (value) {
        //preset knob, first preset range
        case 0 ... 31:
        //voice mix
        pinknoisevol = 0;
        osc1vol = (70.0 * DIV127);
        osc2vol = (70.0 * DIV127);
        karplusvol = (18.0 * DIV127);
}


I was wondering what the best approach was for switching waveforms? Is there a similar sort of approach I can take?

Right now, I’m just writing out the waveform options for all the voices:

        waveformMod1.begin(WAVEFORM_SINE);
        waveformMod2.begin(WAVEFORM_SINE);
        waveform1.begin(WAVEFORM_SINE);
        waveformMod3.begin(WAVEFORM_SINE);
        waveformMod4.begin(WAVEFORM_SINE);
        waveform2.begin(WAVEFORM_SINE);

As there’s 8 voices, I’d rather avoid doing this and be able to just change the waves for osc 1, osc 2, and the 8 LFOs just once rather than writing it out.

Can I just create a string variable with the waveform name:

String osc1 = “WAVEFORM_SINE”

In loop:
waveformMod1.begin(osc1)

In midi loop, based on some midi value:
osc1 = “WAVEFORM_TRIANGLE”

I’m not familiar with this library but it looks as though WAVEFORM_SINE is not the value of a string variable but is a symbol, the name of a constant of I don’t know what type. If it is for example an int you’d need instead:

int osc1 = WAVEFORM_SINE

and then e.g.

waveformMod1.begin(osc1)

But you’d have to look into the documentation or source code for the library to find out what type WAVEFORM_SINE and WAVEFORM_TRIANGLE are.

2 Likes

Ah I see, that makes sense. I’ll have a look at the oscillator library and see where that gets me!

for lop to step through all 8?? ( don’t reust my syntax mind…)

for (int wav = 1; wav < 9; wav++) {
waveformMod[wav].begin(WAVEFORM_SINE);
waveform[wav].begin(WAVEFORM_SINE);
}

WAVEFORM_SINE is a macro, as are the other WAVEFORM options. Every time you type WAVEFORM_SINE it is replaced with the value of 0 when your code is compiled, WAVEFORM_SAWTOOTH is replaced with 1, and so on. They each reference a LUT storing the values of the various waveforms which are indexed according to the oscillator phase. So while this would work:

waveformMod1.begin(0);

It would be very poor coding practice. There are important reasons to use the macros:

  1. Readability. As a general rule of thumb, code as though someone else is going to have to read and understand your code. Even though this may never actually be the case, when you come back to it in weeks, months or years, because you want to improve it in some way, if you use the macro, the purpose of that line of code will be obvious, but otherwise it will be obscure. In situations like the current one, where you have posted your code, using the macro makes it easy to offer advice.
  2. Error avoidance. The only correct values for the waveforms are the integers 0-12. If you stick to the macros, you are never going to accidentally try to change to a non-existent memory location, and you are never going to change to an unintended waveform.

The macros are there to make it easier to produce readable, functional code. Just copy and paste. Anything else is false economy.

Sorry, I’ve just reread the original post, and realised that I misunderstood what you meant by “writing it out.”

You sort of have the right idea, but the variable type is short (int would also be fine, it will default to short):

short osc1 = WAVEFORM_SINE;

There is an example provided with the Teensy library (Waveforms.ino). Just change your board type to Teensy and it will show up in the Audio folder.

As explained above, WAVEFORM_SINE etc are macros. In C++, when you see code using all caps like that, it’s typically a macro, and if you want to assign the value of that macro to a variable, you need to know what kind of variable it will be expanded to.

2 Likes

Thanks for your reply - I had a go at implementing this and it has almost worked perfectly.

For some reason, it doesn’t seem to like the bandlimit waveforms being allocated in this way. I had a string voice using WAVEFORM_BANDLIMIT_SAWTOOTH before, which now sounds awful and causes some sort of panic/crash.

If I change to one of the non-‘bandlimit’ waves, the issue completely disappears…

Are you using the Arduino IDE? If so, is it up-to-date? Are you using the current Teensy library?

I was using the teensyduino programme (on macOS) - I’ll double check it’s up to date this morning. Only downloaded it a couple of months ago.

It’s a strange issue, if I just set the oscillators to band limited waveforms manually it’s fine. If I loop through to set them it gets really unhappy. It sounds like osc1 and 2 are out of phase and bitcrushed, then after a min or so it just makes a long beep and resets.

All other waveforms are fine but the non-band limited ones definitely don’t sound as nice…

Yes, I am using the latest Arduino IDE. It makes no difference if I upload the code via the standalone teensyduino (which is Arduino 1.8.19) or Arduino IDE (2.3.2)

In my experience if a program is doing something an crashes after a while similar to what you describe, you may be addressing memory that is not allocated (to your program) or there is some sort of memory leak (your are using new memory all the time without freeing old memory). Might it be that you are using waveforms beyond the list of existing ones? E.g. you are using waveform 6 in a list of less than 6?

1 Like

Thanks, I have sorted it now.

It was a memory issue, the band limited waveforms have some extra initialisation code that I was repeatedly calling because I was setting the waveform.begin parameter in the main loop.

I moved all of the parameters outside the main loop and into the relevant midi cc switch statements so they only update when a change is made, and it’s all working perfectly now.

4 Likes