Added feedback clearing to AudioEffectSOS and SoundOnSoundDemo example

pull/1/head
Steve Lascos 6 years ago
parent 90441b5668
commit ba4f138d4c
  1. 191
      examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino
  2. 19
      examples/Delay/SoundOnSoundDemo/name.c
  3. 5
      src/AudioEffectSOS.h
  4. 27
      src/effects/AudioEffectSOS.cpp

@ -0,0 +1,191 @@
/*************************************************************************
* This demo uses the BAGuitar library to provide enhanced control of
* the TGA Pro board.
*
* The latest copy of the BA Guitar library can be obtained from
* https://github.com/Blackaddr/BAGuitar
*
* This demo will provide an audio passthrough, as well as exercise the
* MIDI interface.
*
* It can optionally exercise the SPI MEM0 if installed on the TGA Pro board.
*
*/
#include <Wire.h>
#include <Audio.h>
#include <MIDI.h>
#include <SPI.h>
#include "BAGuitar.h"
#include <midi_UsbTransport.h>
static const unsigned sUsbTransportBufferSize = 16;
typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport;
UsbTransport sUsbTransport;
MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, uMIDI);
MIDI_CREATE_DEFAULT_INSTANCE();
using namespace midi;
using namespace BAEffects;
#define MIDI_DEBUG
using namespace BALibrary;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
BAAudioControlWM8731 codec;
// External SRAM is required for this effect due to the very long
// delays required.
ExternalSramManager externalSram;
ExtMemSlot delaySlot; // Declare an external memory slot.
AudioEffectSOS sos(&delaySlot);
// Add some effects for our soloing channel
AudioEffectDelay delayModule; // we'll add a little slapback echo
AudioMixer4 gainModule; // This will be used simply to reduce the gain before the reverb
AudioEffectReverb reverb; // Add a bit of 'verb to our tone
AudioFilterBiquad cabFilter; // We'll want something to cut out the highs and smooth the tone, just like a guitar cab.
AudioMixer4 mixer;
// Connect the input
AudioConnection inputToSos(i2sIn, 0, sos, 0);
AudioConnection inputToSolo(i2sIn, 0, delayModule, 0);
// Patch cables for the SOLO channel
AudioConnection inputToGain(delayModule, 0, gainModule, 0);
AudioConnection inputToReverb(gainModule, 0, reverb, 0);
// Output Mixer
AudioConnection mixer0input(i2sIn, 0, mixer, 0); // SOLO Dry Channel
AudioConnection mixer1input(reverb, 0, mixer, 1); // SOLO Wet Channel
AudioConnection mixer2input(sos, 0, mixer, 2); // SOS Channel
AudioConnection inputToCab(mixer, 0, cabFilter, 0);
// CODEC Outputs
AudioConnection outputLeft(cabFilter, 0, i2sOut, 0);
AudioConnection outputRight(cabFilter, 0, i2sOut, 1);
int loopCount = 0;
void OnControlChange(byte channel, byte control, byte value) {
sos.processMidi(channel-1, control, value);
#ifdef MIDI_DEBUG
Serial.print("Control Change, ch=");
Serial.print(channel, DEC);
Serial.print(", control=");
Serial.print(control, DEC);
Serial.print(", value=");
Serial.print(value, DEC);
Serial.println();
#endif
}
void setup() {
delay(100);
Serial.begin(57600); // Start the serial port
// Disable the codec first
codec.disable();
delay(100);
AudioMemory(128);
delay(5);
// Enable the codec
Serial.println("Enabling codec...\n");
codec.enable();
delay(100);
// We have to request memory be allocated to our slot.
externalSram.requestMemory(&delaySlot, SPI_MEM0_SIZE_BYTES, MemSelect::MEM0, true);
//externalSram.requestMemory(&delaySlot, 50.0f, MemSelect::MEM0, true);
// Setup MIDI
MIDI.begin(MIDI_CHANNEL_OMNI);
MIDI.setHandleControlChange(OnControlChange);
uMIDI.begin(MIDI_CHANNEL_OMNI);
uMIDI.setHandleControlChange(OnControlChange);
// Configure the LED to indicate the gate status
sos.setGateLedGpio(USR_LED_ID);
// Configure which MIDI CC's will control the effect parameters
sos.mapMidiControl(AudioEffectSOS::BYPASS,16);
sos.mapMidiControl(AudioEffectSOS::CLEAR_FEEDBACK_TRIGGER,22);
sos.mapMidiControl(AudioEffectSOS::GATE_TRIGGER,23);
sos.mapMidiControl(AudioEffectSOS::GATE_OPEN_TIME,20);
sos.mapMidiControl(AudioEffectSOS::GATE_CLOSE_TIME,21);
sos.mapMidiControl(AudioEffectSOS::FEEDBACK,24);
sos.mapMidiControl(AudioEffectSOS::VOLUME,17);
// Besure to enable the delay. When disabled, audio is is completely blocked
// to minimize resources to nearly zero.
sos.enable();
// Set some default values.
// These can be changed by sending MIDI CC messages over the USB using
// the BAMidiTester application.
sos.bypass(false);
sos.gateOpenTime(3000.0f);
sos.gateCloseTime(1000.0f);
sos.feedback(0.9f);
// Setup effects on the SOLO channel
gainModule.gain(0, 0.25); // the reverb unit clips easily if the input is too high
delayModule.delay(0, 50.0f); // 50 ms slapback delay
// Setup 2-stages of LPF, cutoff 4500 Hz, Q-factor 0.7071 (a 'normal' Q-factor)
cabFilter.setLowpass(0, 4500, .7071);
cabFilter.setLowpass(1, 4500, .7071);
// Setup the Mixer
mixer.gain(0, 0.5f); // SOLO Dry gain
mixer.gain(1, 0.5f); // SOLO Wet gain
mixer.gain(1, 1.0f); // SOS gain
}
void loop() {
// usbMIDI.read() needs to be called rapidly from loop(). When
// each MIDI messages arrives, it return true. The message must
// be fully processed before usbMIDI.read() is called again.
if (loopCount % 524288 == 0) {
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage());
Serial.print("% ");
Serial.print(" sos: "); Serial.print(sos.processorUsage());
Serial.println("%");
}
loopCount++;
MIDI.read();
uMIDI.read();
// // check for new MIDI from USB
// if (usbMIDI.read()) {
// // this code entered only if new MIDI received
// byte type, channel, data1, data2, cable;
// type = usbMIDI.getType(); // which MIDI message, 128-255
// channel = usbMIDI.getChannel(); // which MIDI channel, 1-16
// data1 = usbMIDI.getData1(); // first data byte of message, 0-127
// data2 = usbMIDI.getData2(); // second data byte of message, 0-127
// Serial.println(String("Received a MIDI message on channel ") + channel);
//
// if (type == MidiType::ControlChange) {
// // if type is 3, it's a CC MIDI Message
// // Note: the Arduino MIDI library encodes channels as 1-16 instead
// // of 0 to 15 as it should, so we must subtract one.
// OnControlChange(channel-1, data1, data2);
// }
// }
}

@ -0,0 +1,19 @@
// To give your project a unique name, this code must be
// placed into a .c file (its own tab). It can not be in
// a .cpp file or your main sketch (the .ino file).
#include "usb_names.h"
// Edit these lines to create your own name. The length must
// match the number of characters in your custom name.
#define MIDI_NAME {'B','l','a','c','k','a','d','d','r',' ','A','u','d','i','o',' ','T','G','A',' ','P','r','o'}
#define MIDI_NAME_LEN 23
// Do not change this part. This exact format is required by USB.
struct usb_string_descriptor_struct usb_string_product_name = {
2 + MIDI_NAME_LEN * 2,
3,
MIDI_NAME
};

@ -40,6 +40,7 @@ public:
GATE_OPEN_TIME, ///< controls how long it takes to open the gate GATE_OPEN_TIME, ///< controls how long it takes to open the gate
//GATE_HOLD_TIME, ///< controls how long the gate stays open at unity //GATE_HOLD_TIME, ///< controls how long the gate stays open at unity
GATE_CLOSE_TIME, ///< controls how long it takes to close the gate (release) GATE_CLOSE_TIME, ///< controls how long it takes to close the gate (release)
CLEAR_FEEDBACK_TRIGGER, ///< begins the sequence to clear out the looping feedback
FEEDBACK, ///< controls the amount of feedback, more gives longer SOS sustain FEEDBACK, ///< controls the amount of feedback, more gives longer SOS sustain
VOLUME, ///< controls the output volume level VOLUME, ///< controls the output volume level
NUM_CONTROLS ///< this can be used as an alias for the number of MIDI controls NUM_CONTROLS ///< this can be used as an alias for the number of MIDI controls
@ -132,8 +133,8 @@ private:
float m_volume = 1.0f; float m_volume = 1.0f;
// Automated Controls // Automated Controls
BALibrary::ParameterAutomationSequence<float> m_inputGateAuto = BALibrary::ParameterAutomationSequence<float> m_inputGateAuto = BALibrary::ParameterAutomationSequence<float>(3);
BALibrary::ParameterAutomationSequence<float>(3); BALibrary::ParameterAutomationSequence<float> m_clearFeedbackAuto = BALibrary::ParameterAutomationSequence<float>(3);
// Private functions // Private functions
void m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal); void m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal);

@ -16,7 +16,7 @@ constexpr int MIDI_CHANNEL = 0;
constexpr int MIDI_CONTROL = 1; constexpr int MIDI_CONTROL = 1;
constexpr float MAX_GATE_OPEN_TIME_MS = 3000.0f; constexpr float MAX_GATE_OPEN_TIME_MS = 3000.0f;
constexpr float MAX_GATE_CLOSE_TIME_MS = 3000.0f; constexpr float MAX_GATE_CLOSE_TIME_MS = 1000.0f;
constexpr int GATE_OPEN_STAGE = 0; constexpr int GATE_OPEN_STAGE = 0;
constexpr int GATE_HOLD_STAGE = 1; constexpr int GATE_HOLD_STAGE = 1;
@ -69,6 +69,10 @@ void AudioEffectSOS::enable(void)
m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL); m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
m_inputGateAuto.setupParameter(GATE_HOLD_STAGE, 1.0f, 1.0f, m_maxDelaySamples, ParameterAutomation<float>::Function::HOLD); m_inputGateAuto.setupParameter(GATE_HOLD_STAGE, 1.0f, 1.0f, m_maxDelaySamples, ParameterAutomation<float>::Function::HOLD);
m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL); m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
m_clearFeedbackAuto.setupParameter(GATE_OPEN_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
m_clearFeedbackAuto.setupParameter(GATE_HOLD_STAGE, 0.0f, 0.0f, m_maxDelaySamples, ParameterAutomation<float>::Function::HOLD);
m_clearFeedbackAuto.setupParameter(GATE_CLOSE_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
} }
void AudioEffectSOS::update(void) void AudioEffectSOS::update(void)
@ -172,12 +176,14 @@ void AudioEffectSOS::gateOpenTime(float milliseconds)
// TODO - change the paramter automation to an automation sequence // TODO - change the paramter automation to an automation sequence
m_openTimeMs = milliseconds; m_openTimeMs = milliseconds;
m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, m_openTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL); m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, m_openTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
//m_clearFeedbackAuto.setupParameter(GATE_OPEN_STAGE, 1.0f, 0.0f, m_openTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
} }
void AudioEffectSOS::gateCloseTime(float milliseconds) void AudioEffectSOS::gateCloseTime(float milliseconds)
{ {
m_closeTimeMs = milliseconds; m_closeTimeMs = milliseconds;
m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, m_closeTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL); m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, m_closeTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
//m_clearFeedbackAuto.setupParameter(GATE_CLOSE_STAGE, 0.0f, 1.0f, m_closeTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
} }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@ -230,11 +236,19 @@ void AudioEffectSOS::processMidi(int channel, int control, int value)
if ((m_midiConfig[GATE_TRIGGER][MIDI_CHANNEL] == channel) && if ((m_midiConfig[GATE_TRIGGER][MIDI_CHANNEL] == channel) &&
(m_midiConfig[GATE_TRIGGER][MIDI_CONTROL] == control)) { (m_midiConfig[GATE_TRIGGER][MIDI_CONTROL] == control)) {
// The gate is trigged by any value // The gate is triggered by any value
Serial.println(String("AudioEffectSOS::Gate Triggered!")); Serial.println(String("AudioEffectSOS::Gate Triggered!"));
m_inputGateAuto.trigger(); m_inputGateAuto.trigger();
return; return;
} }
if ((m_midiConfig[CLEAR_FEEDBACK_TRIGGER][MIDI_CHANNEL] == channel) &&
(m_midiConfig[CLEAR_FEEDBACK_TRIGGER][MIDI_CONTROL] == control)) {
// The gate is triggered by any value
Serial.println(String("AudioEffectSOS::Clear feedback Triggered!"));
m_clearFeedbackAuto.trigger();
return;
}
} }
void AudioEffectSOS::mapMidiControl(int parameter, int midiCC, int midiChannel) void AudioEffectSOS::mapMidiControl(int parameter, int midiCC, int midiChannel)
@ -254,13 +268,14 @@ void AudioEffectSOS::m_preProcessing (audio_block_t *out, audio_block_t *input,
if ( out && input && delayedSignal) { if ( out && input && delayedSignal) {
// Multiply the input signal by the automated gate value // Multiply the input signal by the automated gate value
// Multiply the delayed signal by the user set feedback value // Multiply the delayed signal by the user set feedback value
// Then combine the two
float gateVol = m_inputGateAuto.getNextValue(); float gateVol = m_inputGateAuto.getNextValue();
float feedbackAdjust = m_clearFeedbackAuto.getNextValue();
//float gateVol = 1.0f;
audio_block_t tempAudioBuffer; audio_block_t tempAudioBuffer;
gainAdjust(out, input, gateVol, 0); // last paremeter is coeff shift, 0 bits gainAdjust(out, input, gateVol, 0); // last paremeter is coeff shift, 0 bits
gainAdjust(&tempAudioBuffer, delayedSignal, m_feedback, 0); // last parameter is coeff shift, 0 bits gainAdjust(&tempAudioBuffer, delayedSignal, m_feedback*feedbackAdjust, 0); // last parameter is coeff shift, 0 bits
combine(out, out, &tempAudioBuffer); combine(out, out, &tempAudioBuffer);
} else if (input) { } else if (input) {
@ -269,7 +284,7 @@ void AudioEffectSOS::m_preProcessing (audio_block_t *out, audio_block_t *input,
// Update the gate LED // Update the gate LED
if (m_gateLedPinId >= 0) { if (m_gateLedPinId >= 0) {
if (m_inputGateAuto.isFinished()) { if (m_inputGateAuto.isFinished() && m_clearFeedbackAuto.isFinished()) {
digitalWriteFast(m_gateLedPinId, 0x0); digitalWriteFast(m_gateLedPinId, 0x0);
} else { } else {
digitalWriteFast(m_gateLedPinId, 0x1); digitalWriteFast(m_gateLedPinId, 0x1);

Loading…
Cancel
Save