Exponential envelope generator object

Status
Not open for further replies.

h4yn0nnym0u5e

Well-known member
Hi folks

As part of familarising myself with the Audio ecosystem I've made a slightly different envelope generator which can be found at: https://github.com/h4yn0nnym0u5e/Audio/tree/features/expEnvelope . It attempts to emulate the old-school analogue envelopes based on RC circuits, which therefore had exponential transitions rather than the linear ones provided by the existing AudioEffectEnvelope. As such it consumes a bit more CPU than the linear one (I estimate about twice as much), but I've tried to make it more forgiving of noteOn / noteOff calls and parameter changes while the envelope is running.
  • attack, decay and release have a new optional "shape" parameter
  • releaseNoteOn no longer exists (in this object - the linear one still has it, of course)
I think I've updated the GUI, keywords and header files OK.

For some reason my clone of the original repo seems also to have cloned PJRC's "issues" template (I really don't grok github) - I'll try to figure out how to fix that...

If anyone cares to try it out please report back here or via github. If enough people like it I might even be motivated to maintain it!

Cheers

Jonathan
 
Hello, I'm giving it a try on a Teensy4.1 based synth. The effect is good and CPU usage doesn't appear much different to me. It's probably exactly what I'm looking for. However, I'm getting noise at irregular times followed by audio problems. Looking at audio memory usage with
Code:
Serial.println(AudioMemoryUsageMax());
it's all over the place and hits 65535(!) even though the memory size is set to 100 which is slightly higher than I normally use.

Capture.PNG
 
Last edited:
That's odd. I haven't thrashed it in a busy system, so there could easily be gremlins that I didn't catch in testing, but the outer skin is effectively exactly the same as the original linear envelope, so the memory usage in particular is strange, it should still be one block in, one block out. Noise could be because I've lost sync somewhere with the 8-bytes-at-a-time processing ... hmm ... or if I overran the end of the buffer and corrupted the next one ... that'd do it.

I'll take a look. Can you post a minimal stand-alone piece of code that reproduces the problem?

Cheers

Jonathan
 
OK, I've taken a look and made a minor change to some code that "should never happen", but might have caused problems if it did. Please give https://github.com/h4yn0nnym0u5e/Audio/tree/features/expEnvelope a try (you should only need to download effect_expenvelope.cpp, in fact). If the "should never happen" event does occur, you'll find notes releasing unexpectedly, or at least starting to do so, but the noise should no longer happen, and the memory use should remain sane.

[Technical explanation] I had a case in the envelope state machine that was supposed to catch non-existent states, despite the fact that the code never assigns a value other than one from the normal set of delay, attack, hold, decay, sustain, release, idle, and a special "rising decay" (which happens if you raise the sustain level above the current level while the decay phase is in progress). This default case set two samples to 0, whereas all other cases always process 8 samples, so if that happened momentarily when 120 samples had been processed, the next loop would start at 122, and if the state then became valid, the update would terminate with 130 samples processed, which would be a Bad Thing. I still can't see how a nonexistent state can occur, so this may not (a) fix your problem, or (b) be necessary.

A reproducible example would still help, if you can figure one out.

Cheers

Jonathan
 
Oh great, I can't wait to try this! What is the behavour of the envelope if you retrigger it while the output is non-zero (i.e. During any of the ADSR stages)? The default library envelope resets to zero which creates a volume ducking effect. I think it would be more musical for the envelope to start from its current value if you retrigger it.
 
No, it's still there. Significantly, it only occurs on left or right channels, not both at the same time. Also it's polyphonic. I'll try and produce some cut down code.
 
Oh great, I can't wait to try this! What is the behavour of the envelope if you retrigger it while the output is non-zero (i.e. During any of the ADSR stages)? The default library envelope resets to zero which creates a volume ducking effect. I think it would be more musical for the envelope to start from its current value if you retrigger it.
I’ve tried to make the behaviour when retriggerings as logical as possible, so it does start from the prevailing value, unless it’s in the Attack or Hold stages when the retrigger is ignored (you’d hear no difference anyway!). It also tries to do the right thing if a parameter is changed while in that stage, or a “related“ stage, in the case of changing Sustain level during Decay.

Please do give it a go, though it sounds like I still have a bug in there...

Cheers

Jonathan
 
No, it's still there. Significantly, it only occurs on left or right channels, not both at the same time. Also it's polyphonic. I'll try and produce some cut down code.
Thanks. Not understanding the left/right/polyphonic part of your post - the envelope is a monophonic object

Is it more likely at very fast or slow A/D/R speeds, do you know? I probably haven’t tested those as much as I should have...

Cheers

Jonathan
 
I'm playing a polyphonic synth with 12 envelopes. The noise is either on the left or right channel. I'm using A/D/R from 1ms to 12s and sustain 0 to 1. I'm using your envelope as a replacement for the existing one with no changes.

Capture.PNG

Audio file
 
I'm playing a polyphonic synth with 12 envelopes. The noise is either on the left or right channel. I'm using A/D/R from 1ms to 12s and sustain 0 to 1. I'm using your envelope as a replacement for the existing one with no changes.

View attachment 24543

Audio file
That's pretty clear. If I grab your Tsynth from github and replace the envelopes, that's what you're running, yes? Do I need to change parameters in real time, or is there a fixed set that will reproduce the problem? Could I cut the polyphony down and still get the problem? Do you have a MIDI file that produces the problem when played, or could it be done entirely with a fixed piece of code to start and stop notes in a 100% predictable manner?

I won't be able to get back to this until this evening at the earliest, I'm afraid.

Oh, one more question: which Teensy are you using for this? I only have a T4.1.

Cheers

Jonathan
 
Yes it's T4.1. If you want to use the current TSynth code, you can comment out three lines in the loop() like this so that it doesn't try to read non-existent pots and switches:

Code:
void loop() {
  //USB HOST MIDI Class Compliant
  myusb.Task();
  midi1.read(midiChannel);
  //USB Client MIDI
  usbMIDI.read(midiChannel);
  //MIDI 5 Pin DIN
  MIDI.read(midiChannel);
  //checkMux();  //THIS LINE
 // checkSwitches(); //THIS LINE
  //checkEncoder(); //THIS LINE
  //CPUMonitor();
}


and send MIDI via the USB from your pc, and make parameter changes with MIDI CC messages - see MidiCC.h. It should work fine. If you don't have the Audio Board soldered on, you can get digital audio through the same USB connection - the Teensy will appear as a soundcard.
 
Found it! It was a block overrun, as I suspected, but not where I suspected... Grab the latest commit from https://github.com/h4yn0nnym0u5e/Audio/tree/features/expEnvelope and see how you go, sounds OK to me though. I've implemented the TSynth close() function to save you a bit of pain, though it's not what I'd call elegant!

I'll do a separate post relating to my experiences getting TSynth working in "headless" mode.

Cheers

Jonathan
 
Yes it's T4.1. If you want to use the current TSynth code, you can comment out three lines in the loop() like this so that it doesn't try to read non-existent pots and switches:

Code:
void loop() {
  //USB HOST MIDI Class Compliant
  myusb.Task();
  midi1.read(midiChannel);
  //USB Client MIDI
  usbMIDI.read(midiChannel);
  //MIDI 5 Pin DIN
  MIDI.read(midiChannel);
  //checkMux();  //THIS LINE
 // checkSwitches(); //THIS LINE
  //checkEncoder(); //THIS LINE
  //CPUMonitor();
}


and send MIDI via the USB from your pc, and make parameter changes with MIDI CC messages - see MidiCC.h. It should work fine. If you don't have the Audio Board soldered on, you can get digital audio through the same USB connection - the Teensy will appear as a soundcard.

I found a few issues following the above advice, and when making changes to figure out the bug in ExpEnvelope:
  • you can't just comment out checkMux(), as it has to run once to enable the sound output at all
  • cutting down the polyphony by simply changing NO_OF_VOICES in Constants.h doesn't work, it breaks ST7735Display.h (definition of colour[] at line 41, and assumption of 12 voices in lines 174-188: could be more, I didn't look further); also TSynth.ino, voices[] on line 84
  • I suspect it needs a constant value assigned for system volume - I put one in as the first line of checkVolumePot(), as I don't have anything wired to VOLUME_POT
  • I had problems with the duplicated synth_waveform.cpp and data_bandlimit_step.c, and had to rename them temporarily to get the system to compile: this could be due to me using the Arduino IDE

For users like me, who might want to check out TSynth before investing in the hardware, it might be worth the effort to have a #define TSYNTH_HEADLESS_MODE option which if enabled will make it simple for them to get a minimal system up and running without making a lot of obscure changes.

Cheers

Jonathan
 
Status
Not open for further replies.
Back
Top