Gate

Status
Not open for further replies.

macaba

Well-known member
It's common in live performance to use gates on things like drum kits to clean up the sound from the mic on each drum.
(https://en.wikipedia.org/wiki/Noise_gate)

From an audio waveform perspective, it looks a little like this:
Gate in action.png
Top trace is before gate, bottom is after gate.

Gates tend to have threshold/attack/hold/release/floor settings.

Graphically shown here with exaggerated settings:
Gate.png
(Threshold: 0.3, Floor: 0.1)

Source (effect_gate.h):
https://github.com/macaba/Audio

I'm happy to create a pull request if this is suitable for the main repo.
 
Cool. Gates are an essential part of my VST based live gigging rig.

How does yours compare with the Envelope filter effect already in the library? I've not used it but it has attack, hold, delay, sustain, release controls.
 
How does yours compare with the Envelope filter effect already in the library? I've not used it but it has attack, hold, delay, sustain, release controls.

It's not a direct comparison as Envelope generator is externally triggered (using noteOn()) whereas a Gate is signal triggered (using threshold(n) to set trigger level).
Envelope = for synthesis, Gate = for audio processing.
To misuse the envelope as a gate would require a bit of glue logic in the user code - polling a 'peak' object to call noteOn() on the envelope. Also the envelope would need to be retriggerable, I'm not sure if the current one is.
 
Last edited:
I recently improved the envelope object, and I'm looking to do more in June. The new version is a huge improvement, but I understand there's more work to be done.

But I need feedback. The most helpful form of feedback is a test program which synthesizes or plays a specific sound and calls the envelope note on/off functions are specific times, to demonstrate the cases where clicks or other undesirable output happens. I get quite a bit of feedback that's really not very helpful. Test programs that actually demonstrate the limitations are the best way to get me to fix something...

I'm also considering adding many more objects to the library with signal control inputs. A gate input to ADSR control signal is one of the ideas on my list. To achieve the envelope, you'd feed that ADSR control signal to the multiplier object (the equivalent of a voltage controlled amplifier). This sort of usage will offer much finer timing and will probably feel familiar to people used to analog & modular synthesis. But it will cost significantly higher CPU usage. The existing envelope effect uses well under 1% CPU on Teensy 3.2. This sort of control signal approach is likely to be around 2% to 5%.
 
A gate input to ADSR control signal is one of the ideas on my list.

I think there is a case of confused terminology here - A Noise Gate isn't a modular synthesis thing, it's an industry standard live audio processing thing.
Example device:
http://www.drawmer.com/company.php#201

I guess it comes down to whether you're just aiming the audio library at synthesis applications, or if live audio is also in scope. The new object I've created is purely a live audio processing concept - it doesn't really have a place in synthesis.

Edit:
Apple have a nice definition here:
https://documentation.apple.com/en/logicstudio/effects/index.html#chapter=4&section=10&tasks=true
 
Last edited:
I think gates can just as well have a place in synthesis. In fact some synthesizers include a rythmical gate effect.
Besides that, I think you're missing some generalisation: a gate is nothing more than an automatic volume control that sets it's volume to 0 when the input falls below some threshold. If the library already provides all the building blocks, one could easily make a gate using those. So split audio signal to a level\peak detector and the vca audio input, compare the output level of the level detector with a DC level (threshold control). Mult the level with a envelope and you should have something like a gate,but the building blocks are generic enough to build other stuff as well, (compressors and what not). Maybe only thing missing is a comparator and maybe an inverter.
 
Lately the effort on the audio library has been for synthesis stuff, mostly because it's been somewhat lacking in synthesis features. Certainly the intention is to support real-time signal processing, and play & record applications. Just because some work is recently going into improving features for one application doesn't mean all development will go that way!
 
Certainly the intention is to support real-time signal processing, and play & record applications.

Perfect, I'll keep releasing real-time oriented objects. When I've used them a lot and they're confirmed to be bug free, I'll look at doing pull requests.

I've seen there is a F32 fork of the audio library for floats but it needed a bit of work before coming 'official' - can I help?

I'd like to get this going for my Teensy 3.6, I think it makes sense to normalise any input source to +/- 1.0 and then objects like mixers won't clip (they'll just give out greater than 1.0, giving any downstream processing a chance to limit it to 1.0 before the output device).
It's also serendipitous that 2^24 is the largest contiguous number that a 32-bit float can do before skipping integer values so a float can be confidently converted back to 24 bit int for those (non-official) 24 bit audio shields.
 
Last edited:
macaba,

Been using your gate object. I love it thanks, was able to get the settings to replicate a noise pedal I got pretty well. Thanks.
 
Hi, may I ask for a pull request to include this great object into the official library?
Best regards,
Christof
 
Hi,
to use this object for a guitar input signal it makes sense to bring in some hysteresis. The level to Switch on has to be higher than the Level to switch off.
My very simple modification:
Code:
.....
switch (state) {
				case S_Floor:
					currentGain = floorGain;
					if(maxAbsSample > (2*thresholdLevelInt)) { // <==== Hysteresis
						state = S_Attack;
						processNextState = true;
					}					
					break;
				case S_Attack:
					currentGain += attackTimeDelta;
					if(currentGain >= 1.0f) {
						currentGain = 1.0f;
						state = S_Hold;
						processNextState = true;
					}
					break;
				case S_Hold:
					currentGain = 1.0f;
					if(maxAbsSample > thresholdLevelInt)
						break;			//Re-triggered. Also prevents infinite loop.
					currentHoldTime += holdTimeDelta;
					if(currentHoldTime >= 1.0f) {
						currentHoldTime = 0.0f;
						state = S_Release;
						processNextState = true;
					}
					break;
				case S_Release:
					if(maxAbsSample > (2*thresholdLevelInt)) { //   <=== Hysteresis
						state = S_Attack;
						processNextState = true;
						break;			//Re-triggered
					}
....

Best regards Christof
 
I recently tried to use gate with my teensy 4.0 and had no such luck... :( It does not produce any errors but also has no effect on the sound. I tried chaging the settings to many different values, but no matter what I do the audio is unaffected. Unfortunately I am not aware of all of the changes in audio library code for a year or so (and since teensy 4.0). Can someone help me by taking a look at effect_gate.cpp and effect_gate.h to see if they can help me figure out what the issue is.

https://github.com/macaba/Audio

Here is my code as well.

Thanks!

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <effect_gate.h>

// GUItool: begin automatically generated code
AudioControlSGTL5000     sgtl5000_1;
AudioOutputI2S            i2s2;
AudioSynthNoiseWhite     noise1; //xy=203,482
AudioSynthSimpleDrum     drum1; //xy=203,522
AudioMixer4              mixer1;         //xy=433,407
AudioEffectGate          gate1;          //xy=687,440
AudioConnection          patchCord1(noise1, 0, mixer1, 0);
AudioConnection          patchCord2(drum1, 0, mixer1, 1);
AudioConnection          patchCord3(mixer1, 0, gate1,0);
AudioConnection          patchCord4(mixer1, 0, i2s2, 0);
AudioConnection          patchCord5(gate1, 0, i2s2, 1);
// GUItool: end automatically generated code

unsigned long last_time = millis();

void setup() {
  AudioMemory(10);
  
  noise1.amplitude(0.02);
  drum1.frequency(180);
  drum1.length(400);
  drum1.secondMix(0.3);
  drum1.pitchMod(0.7);
  mixer1.gain(1,1.6);
  
  gate1.threshold(0.2);
  gate1.attack(.001);
  gate1.release(10);
  gate1.hold(10);
  gate1.floor(.01);
  
  AudioProcessorUsageMaxReset();
  AudioMemoryUsageMaxReset();
}


void loop() {
  if(1) {
    if(millis() - last_time >= 1000) {
      Serial.print("Proc = ");
      Serial.print(AudioProcessorUsage());
      Serial.print(" (");    
      Serial.print(AudioProcessorUsageMax());
      Serial.print("),  Mem = ");
      Serial.print(AudioMemoryUsage());
      Serial.print(" (");    
      Serial.print(AudioMemoryUsageMax());
      Serial.println(")");
      last_time = millis();
      drum1.noteOn();
    }
  }
}
 
I'd also love to have access to a gate effect - I've tried to merge in the changes macaba made above into the latest release, but have been unable to get the effect to work.

I've noticed there's a few people above asking for a pull request for this - wondering if it would be possible to add this to the library as an effect?
 
I'd also love to have access to a gate effect - I've tried to merge in the changes macaba made above into the latest release, but have been unable to get the effect to work.

I've noticed there's a few people above asking for a pull request for this - wondering if it would be possible to add this to the library as an effect?

To install the gate I first found the audio library folder on my computer, for me the folder location is C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio
then copied "effect_gate.h" and "effect_gate.cpp" from https://github.com/macaba/Audio into the "Audio" library folder on my computer.

Then when using it in code, I made sure to include "#include <effect_gate.h>"

I hope that helps.
 
I think I'd prefer AudioEffectNoiseGate so its clear what sort of gate it is (for instance CV/gate interface is a thing in modular synthesis).
 
I made a quick modification to run on Teensy 4.x

I'm using it as part of a gated reverb so wanted something slightly different from it so it now has two inputs:
- input 0 is the main audio input that the gain is applied to
- input 1 is a side chain / control input that is used to activate the gate

If you are looking to use it just as the original then connect your audio input to both input 0 and input 1.

Cheers Paul

effect_gate.h
Code:
/* Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef effect_gate_h_
#define effect_gate_h_

#include "Arduino.h"
#include "AudioStream.h"

const int S_Floor = 0;
const int S_Attack = 1;
const int S_Hold = 2;
const int S_Release = 3;

class AudioEffectGate : public AudioStream
{
public:
	AudioEffectGate(void)
	  : AudioStream(2, inputQueueArray) {
		  currentGain = 0.0;
		  thresholdLevelInt = 16384;	//0.5
		  attackTimeDelta = 1.0f / (50.0f / (1000.0f / (AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES)));			//50ms default
		  holdTimeDelta = 1.0f / (200.0f / (1000.0f / (AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES)));			//200ms default
		  currentHoldTime = 0.0f;
		  releaseTimeDelta = 1.0f / (200.0f / (1000.0f / (AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES)));		//2000ms default
		  floorGain = 0.0f;
		  state = S_Floor;
	  }
	void threshold(float threshold) {
		if (threshold > 1.0f) threshold = 1.0f;
		else if (threshold < 0.0f) threshold = 0.0f;
		thresholdLevelInt = threshold * 32767.0f;
	}
	void attack(float milliseconds) {
		if(milliseconds <=  0.0f) {
			milliseconds = 0.01f; 
		}
		attackTimeDelta = 1.0f / (milliseconds / (1000.0f / (AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES)));
	}
	void hold(float milliseconds) {
		if(milliseconds <=  0.0f) {
			milliseconds = 0.01f; 
		}
		holdTimeDelta = 1.0f / (milliseconds / (1000.0f / (AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES)));
	}
	void release(float milliseconds) {
		if(milliseconds <=  0.0f) {
			milliseconds = 0.01f; 
		}
		releaseTimeDelta = 1.0f / (milliseconds / (1000.0f / (AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES)));
	}
	void floor(float gain) {
		if(gain < 0.0f)
			gain = 0.0f;
		if(gain > 1.0f)
			gain = 1.0f;
		floorGain = gain;
	}
	using AudioStream::release;
	virtual void update(void);
private:
	audio_block_t *inputQueueArray[2];
	int16_t thresholdLevelInt;
	float attackTimeDelta;
	float holdTimeDelta;
	float releaseTimeDelta;
	float floorGain;
	int32_t state;
	//Transient variables
	float currentGain;
	float currentHoldTime;
};

#endif

effect_gate.cpp
Code:
/* Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "effect_gate.h"

#include "utility/dspinst.h"

#if defined(KINETISK) || (__ARM_ARCH_7EM__)
#define MULTI_UNITYGAIN 65536

static void applyGain(int16_t *data, int32_t mult) {
  uint32_t *p = (uint32_t *)data;
  const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);

  do {
    uint32_t tmp32 = *p;  // read 2 samples from *data
    int32_t val1 = signed_multiply_32x16b(mult, tmp32);
    int32_t val2 = signed_multiply_32x16t(mult, tmp32);
    val1 = signed_saturate_rshift(val1, 16, 0);
    val2 = signed_saturate_rshift(val2, 16, 0);
    *p++ = pack_16b_16b(val2, val1);
  } while (p < end);
}

#elif defined(KINETISL)
#define MULTI_UNITYGAIN 256

static void applyGain(int16_t *data, int32_t mult) {
  const int16_t *end = data + AUDIO_BLOCK_SAMPLES;

  do {
    int32_t val = *data * mult;
    *data++ = signed_saturate_rshift(val, 16, 0);
  } while (data < end);
}

#endif

void AudioEffectGate::update(void) {
  audio_block_t *blocka, *blockb;
  uint32_t i;
  int16_t maxAbsSample = 0;
  bool processNextState = true;
  // blocka = audio to be gated
  // blockb = gate control input

  blocka = receiveWritable(0);
  blockb = receiveReadOnly(1);

  if (!blocka) {
    if (blockb) release(blockb);
    return;
  }

  if (!blockb) {
    if (blocka) release(blocka);
    return;
  }

  if (thresholdLevelInt > 0) {  // Only process gate if threshold is non-zero
    for (i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
      if (abs(blockb->data[i]) > maxAbsSample) maxAbsSample = abs(blockb->data[i]);
    }

    while (processNextState) {
      processNextState = false;
      switch (state) {
        case S_Floor:
          currentGain = floorGain;
          if (maxAbsSample > thresholdLevelInt) {
            state = S_Attack;
            processNextState = true;
          }
          break;
        case S_Attack:
          currentGain += attackTimeDelta;
          if (currentGain >= 1.0f) {
            currentGain = 1.0f;
            state = S_Hold;
            processNextState = true;
          }
          break;
        case S_Hold:
          currentGain = 1.0f;
          if (maxAbsSample > thresholdLevelInt) break;  // Re-triggered. Also prevents infinite loop.
          currentHoldTime += holdTimeDelta;
          if (currentHoldTime >= 1.0f) {
            currentHoldTime = 0.0f;
            state = S_Release;
            processNextState = true;
          }
          break;
        case S_Release:
          if (maxAbsSample > thresholdLevelInt) {
            state = S_Attack;
            processNextState = true;
            break;  // Re-triggered
          }
          currentGain -= releaseTimeDelta;
          if (currentGain <= floorGain) {
            currentGain = floorGain;
            state = S_Floor;
            processNextState = true;
          }
          break;
      }
    }

    if (currentGain != 1.0f) {
#if defined(KINETISK) || (__ARM_ARCH_7EM__)
      applyGain(blocka->data, currentGain * 65536.0f);
#elif defined(KINETISL)
      applyGain(blocka->data, currentGain * 256.0f);
#endif
    }
  }

  transmit(blocka);
  release(blocka);
  if (blockb) release(blockb);

}
 
Status
Not open for further replies.
Back
Top