From ba4f138d4caeca451df5d74ceff37a2ee72f3e2b Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Mon, 16 Jul 2018 20:02:58 -0400 Subject: [PATCH] Added feedback clearing to AudioEffectSOS and SoundOnSoundDemo example --- .../SoundOnSoundDemo/SoundOnSoundDemo.ino | 191 ++++++++++++++++++ examples/Delay/SoundOnSoundDemo/name.c | 19 ++ src/AudioEffectSOS.h | 5 +- src/effects/AudioEffectSOS.cpp | 29 ++- 4 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino create mode 100644 examples/Delay/SoundOnSoundDemo/name.c diff --git a/examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino b/examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino new file mode 100644 index 0000000..ec22c9a --- /dev/null +++ b/examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino @@ -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 +#include +#include +#include +#include "BAGuitar.h" + +#include +static const unsigned sUsbTransportBufferSize = 16; +typedef midi::UsbTransport 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); +// } +// } + +} + diff --git a/examples/Delay/SoundOnSoundDemo/name.c b/examples/Delay/SoundOnSoundDemo/name.c new file mode 100644 index 0000000..5ea00fe --- /dev/null +++ b/examples/Delay/SoundOnSoundDemo/name.c @@ -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 +}; diff --git a/src/AudioEffectSOS.h b/src/AudioEffectSOS.h index 14b43bf..d72f5ef 100644 --- a/src/AudioEffectSOS.h +++ b/src/AudioEffectSOS.h @@ -40,6 +40,7 @@ public: 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_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 VOLUME, ///< controls the output volume level 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; // Automated Controls - BALibrary::ParameterAutomationSequence m_inputGateAuto = - BALibrary::ParameterAutomationSequence(3); + BALibrary::ParameterAutomationSequence m_inputGateAuto = BALibrary::ParameterAutomationSequence(3); + BALibrary::ParameterAutomationSequence m_clearFeedbackAuto = BALibrary::ParameterAutomationSequence(3); // Private functions void m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal); diff --git a/src/effects/AudioEffectSOS.cpp b/src/effects/AudioEffectSOS.cpp index 40ae3a6..2512261 100644 --- a/src/effects/AudioEffectSOS.cpp +++ b/src/effects/AudioEffectSOS.cpp @@ -16,7 +16,7 @@ constexpr int MIDI_CHANNEL = 0; constexpr int MIDI_CONTROL = 1; 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_HOLD_STAGE = 1; @@ -69,6 +69,10 @@ void AudioEffectSOS::enable(void) m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation::Function::EXPONENTIAL); m_inputGateAuto.setupParameter(GATE_HOLD_STAGE, 1.0f, 1.0f, m_maxDelaySamples, ParameterAutomation::Function::HOLD); m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation::Function::EXPONENTIAL); + + m_clearFeedbackAuto.setupParameter(GATE_OPEN_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation::Function::EXPONENTIAL); + m_clearFeedbackAuto.setupParameter(GATE_HOLD_STAGE, 0.0f, 0.0f, m_maxDelaySamples, ParameterAutomation::Function::HOLD); + m_clearFeedbackAuto.setupParameter(GATE_CLOSE_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation::Function::EXPONENTIAL); } void AudioEffectSOS::update(void) @@ -172,12 +176,14 @@ void AudioEffectSOS::gateOpenTime(float milliseconds) // TODO - change the paramter automation to an automation sequence m_openTimeMs = milliseconds; m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, m_openTimeMs, ParameterAutomation::Function::EXPONENTIAL); + //m_clearFeedbackAuto.setupParameter(GATE_OPEN_STAGE, 1.0f, 0.0f, m_openTimeMs, ParameterAutomation::Function::EXPONENTIAL); } void AudioEffectSOS::gateCloseTime(float milliseconds) { m_closeTimeMs = milliseconds; m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, m_closeTimeMs, ParameterAutomation::Function::EXPONENTIAL); + //m_clearFeedbackAuto.setupParameter(GATE_CLOSE_STAGE, 0.0f, 1.0f, m_closeTimeMs, ParameterAutomation::Function::EXPONENTIAL); } //////////////////////////////////////////////////////////////////////// @@ -230,11 +236,19 @@ void AudioEffectSOS::processMidi(int channel, int control, int value) if ((m_midiConfig[GATE_TRIGGER][MIDI_CHANNEL] == channel) && (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!")); m_inputGateAuto.trigger(); 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) @@ -254,13 +268,14 @@ void AudioEffectSOS::m_preProcessing (audio_block_t *out, audio_block_t *input, if ( out && input && delayedSignal) { // Multiply the input signal by the automated gate value // Multiply the delayed signal by the user set feedback value + // Then combine the two - float gateVol = m_inputGateAuto.getNextValue(); - - //float gateVol = 1.0f; + float gateVol = m_inputGateAuto.getNextValue(); + float feedbackAdjust = m_clearFeedbackAuto.getNextValue(); audio_block_t tempAudioBuffer; + 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); } else if (input) { @@ -269,7 +284,7 @@ void AudioEffectSOS::m_preProcessing (audio_block_t *out, audio_block_t *input, // Update the gate LED if (m_gateLedPinId >= 0) { - if (m_inputGateAuto.isFinished()) { + if (m_inputGateAuto.isFinished() && m_clearFeedbackAuto.isFinished()) { digitalWriteFast(m_gateLedPinId, 0x0); } else { digitalWriteFast(m_gateLedPinId, 0x1);