parent
d38be82ea6
commit
070c305322
@ -0,0 +1,180 @@ |
|||||||
|
/*************************************************************************
|
||||||
|
* This demo uses the BALibrary 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/BALibrary
|
||||||
|
*
|
||||||
|
* This example demonstrates teh BAAudioEffectsPitchShift effect. It can |
||||||
|
* be controlled using the Blackaddr Audio "Expansion Control Board". |
||||||
|
*
|
||||||
|
* POT1 (left) controls amount of delay |
||||||
|
* POT2 (right) controls amount of feedback |
||||||
|
* POT3 (center) controls the wet/dry mix |
||||||
|
* SW1 will enable/bypass the audio effect. LED1 will be on when effect is enabled. |
||||||
|
* SW2 will cycle through the 3 pre-programmed analog filters. LED2 will be on when SW2 is pressed. |
||||||
|
*
|
||||||
|
*
|
||||||
|
* Using the Serial Montitor, send 'u' and 'd' characters to increase or decrease |
||||||
|
* the headphone volume between values of 0 and 9. |
||||||
|
*/ |
||||||
|
#define TGA_PRO_REVB // Set which hardware revision of the TGA Pro we're using
|
||||||
|
#define TGA_PRO_EXPAND_REV2 // pull in the pin definitions for the Blackaddr Audio Expansion Board.
|
||||||
|
|
||||||
|
#include "BALibrary.h" |
||||||
|
#include "BAEffects.h" |
||||||
|
|
||||||
|
using namespace BAEffects; |
||||||
|
using namespace BALibrary; |
||||||
|
|
||||||
|
AudioInputI2S i2sIn; |
||||||
|
AudioOutputI2S i2sOut; |
||||||
|
BAAudioControlWM8731 codec; |
||||||
|
|
||||||
|
AudioEffectPitchShift pitchShift; |
||||||
|
|
||||||
|
AudioFilterBiquad cabFilter; // We'll want something to cut out the highs and smooth the tone, just like a guitar cab.
|
||||||
|
|
||||||
|
// Simply connect the input to the delay, and the output
|
||||||
|
// to both i2s channels
|
||||||
|
AudioConnection input(i2sIn,0, pitchShift,0); |
||||||
|
AudioConnection effectOut(pitchShift, 0, cabFilter, 0); |
||||||
|
AudioConnection leftOut(cabFilter,0, i2sOut, 0); |
||||||
|
AudioConnection rightOut(cabFilter,0, i2sOut, 1); |
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// SETUP PHYSICAL CONTROLS
|
||||||
|
// - POT1 (left) will control the rate
|
||||||
|
// - POT2 (right) will control the depth
|
||||||
|
// - POT3 (centre) will control the volume
|
||||||
|
// - SW1 (left) will be used as a bypass control
|
||||||
|
// - LED1 (left) will be illuminated when the effect is ON (not bypass)
|
||||||
|
// - SW2 (right) will be used to cycle through the the waveforms
|
||||||
|
// - LED2 (right) will illuminate when pressing SW2.
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// To get the calibration values for your particular board, first run the
|
||||||
|
// BAExpansionCalibrate.ino example and
|
||||||
|
constexpr int potCalibMin = 1; |
||||||
|
constexpr int potCalibMax = 1018; |
||||||
|
constexpr bool potSwapDirection = true; |
||||||
|
|
||||||
|
// Create a control object using the number of switches, pots, encoders and outputs on the
|
||||||
|
// Blackaddr Audio Expansion Board.
|
||||||
|
BAPhysicalControls controls(BA_EXPAND_NUM_SW, BA_EXPAND_NUM_POT, BA_EXPAND_NUM_ENC, BA_EXPAND_NUM_LED); |
||||||
|
|
||||||
|
int loopCount = 0; |
||||||
|
constexpr unsigned MAX_HEADPHONE_VOL = 10; |
||||||
|
unsigned headphoneVolume = 8; // control headphone volume from 0 to 10.
|
||||||
|
|
||||||
|
// BAPhysicalControls returns a handle when you register a new control. We'll uses these handles when working with the controls.
|
||||||
|
int bypassHandle, volumeHandle, led1Handle, led2Handle; // Handles for the various controls
|
||||||
|
|
||||||
|
void setup() { |
||||||
|
delay(100); // wait a bit for serial to be available
|
||||||
|
Serial.begin(57600); // Start the serial port
|
||||||
|
delay(100); |
||||||
|
|
||||||
|
// Setup the controls. The return value is the handle to use when checking for control changes, etc.
|
||||||
|
// pushbuttons
|
||||||
|
bypassHandle = controls.addSwitch(BA_EXPAND_SW1_PIN); // will be used for bypass control
|
||||||
|
//button2Handle = controls.addSwitch(BA_EXPAND_SW2_PIN); // will be used for stepping through filters
|
||||||
|
// pots
|
||||||
|
//rateHandle = controls.addPot(BA_EXPAND_POT1_PIN, potCalibMin, potCalibMax, potSwapDirection); // control the amount of delay
|
||||||
|
//depthHandle = controls.addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
|
||||||
|
volumeHandle = controls.addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
|
||||||
|
// leds
|
||||||
|
led1Handle = controls.addOutput(BA_EXPAND_LED1_PIN); |
||||||
|
led2Handle = controls.addOutput(BA_EXPAND_LED2_PIN); // will illuminate when pressing SW2
|
||||||
|
|
||||||
|
// Disable the audio codec first
|
||||||
|
codec.disable(); |
||||||
|
AudioMemory(128); |
||||||
|
|
||||||
|
// Enable and configure the codec
|
||||||
|
Serial.println("Enabling codec...\n"); |
||||||
|
codec.enable(); |
||||||
|
codec.setHeadphoneVolume(1.0f); // Max headphone volume
|
||||||
|
|
||||||
|
// Besure to enable the pitchShift. When disabled, audio is is completely blocked by the effect
|
||||||
|
// to minimize resource usage to nearly to nearly zero.
|
||||||
|
pitchShift.enable();
|
||||||
|
|
||||||
|
// Set some default values.
|
||||||
|
// These can be changed using the controls on the Blackaddr Audio Expansion Board
|
||||||
|
pitchShift.bypass(false); |
||||||
|
|
||||||
|
// Guitar cabinet: 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); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
|
||||||
|
float potValue; |
||||||
|
|
||||||
|
// Check if SW1 has been toggled (pushed)
|
||||||
|
if (controls.isSwitchToggled(bypassHandle)) { |
||||||
|
bool bypass = pitchShift.isBypass(); // get the current state
|
||||||
|
bypass = !bypass; // change it
|
||||||
|
pitchShift.bypass(bypass); // set the new state
|
||||||
|
controls.setOutput(led1Handle, !bypass); // Set the LED when NOT bypassed
|
||||||
|
Serial.println(String("BYPASS is ") + bypass); |
||||||
|
} |
||||||
|
|
||||||
|
// // Use SW2 to cycle through the waveforms
|
||||||
|
// controls.setOutput(led2Handle, controls.getSwitchValue(led2Handle));
|
||||||
|
// if (controls.isSwitchToggled(waveformHandle)) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Use POT1 (left) to control the rate setting
|
||||||
|
// if (controls.checkPotValue(rateHandle, potValue)) {
|
||||||
|
// // Pot has changed
|
||||||
|
// Serial.println(String("New RATE setting: ") + potValue);
|
||||||
|
// pitchShift.rate(potValue);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Use POT2 (right) to control the depth setting
|
||||||
|
// if (controls.checkPotValue(depthHandle, potValue)) {
|
||||||
|
// // Pot has changed
|
||||||
|
// Serial.println(String("New DEPTH setting: ") + potValue);
|
||||||
|
// pitchShift.depth(potValue);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Use POT3 (centre) to control the volume setting
|
||||||
|
if (controls.checkPotValue(volumeHandle, potValue)) { |
||||||
|
// Pot has changed
|
||||||
|
Serial.println(String("New VOLUME setting: ") + potValue); |
||||||
|
pitchShift.volume(potValue); |
||||||
|
} |
||||||
|
|
||||||
|
// Use the 'u' and 'd' keys to adjust volume across ten levels.
|
||||||
|
if (Serial) { |
||||||
|
if (Serial.available() > 0) { |
||||||
|
while (Serial.available()) { |
||||||
|
char key = Serial.read(); |
||||||
|
if (key == 'u') {
|
||||||
|
headphoneVolume = (headphoneVolume + 1) % MAX_HEADPHONE_VOL; |
||||||
|
Serial.println(String("Increasing HEADPHONE volume to ") + headphoneVolume); |
||||||
|
} |
||||||
|
else if (key == 'd') {
|
||||||
|
headphoneVolume = (headphoneVolume - 1) % MAX_HEADPHONE_VOL; |
||||||
|
Serial.println(String("Decreasing HEADPHONE volume to ") + headphoneVolume); |
||||||
|
} |
||||||
|
codec.setHeadphoneVolume(static_cast<float>(headphoneVolume) / static_cast<float>(MAX_HEADPHONE_VOL)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Use the loopCounter to roughly measure human timescales. Every few seconds, print the CPU usage
|
||||||
|
// to the serial port. About 500,000 loops!
|
||||||
|
//if (loopCount % 524288 == 0) {
|
||||||
|
if (loopCount % 25000 == 0) { |
||||||
|
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage()); |
||||||
|
Serial.print("% "); |
||||||
|
Serial.print(" pitchShift: "); Serial.print(pitchShift.processorUsage()); |
||||||
|
Serial.println("%"); |
||||||
|
} |
||||||
|
loopCount++; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,180 @@ |
|||||||
|
/*************************************************************************
|
||||||
|
* This demo uses the BALibrary 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/BALibrary
|
||||||
|
*
|
||||||
|
* This example demonstrates teh BAAudioEffectsTemplate effect. It can |
||||||
|
* be controlled using the Blackaddr Audio "Expansion Control Board". |
||||||
|
*
|
||||||
|
* POT1 (left) controls amount of delay |
||||||
|
* POT2 (right) controls amount of feedback |
||||||
|
* POT3 (center) controls the wet/dry mix |
||||||
|
* SW1 will enable/bypass the audio effect. LED1 will be on when effect is enabled. |
||||||
|
* SW2 will cycle through the 3 pre-programmed analog filters. LED2 will be on when SW2 is pressed. |
||||||
|
*
|
||||||
|
*
|
||||||
|
* Using the Serial Montitor, send 'u' and 'd' characters to increase or decrease |
||||||
|
* the headphone volume between values of 0 and 9. |
||||||
|
*/ |
||||||
|
#define TGA_PRO_REVB // Set which hardware revision of the TGA Pro we're using
|
||||||
|
#define TGA_PRO_EXPAND_REV2 // pull in the pin definitions for the Blackaddr Audio Expansion Board.
|
||||||
|
|
||||||
|
#include "BALibrary.h" |
||||||
|
#include "BAEffects.h" |
||||||
|
|
||||||
|
using namespace BAEffects; |
||||||
|
using namespace BALibrary; |
||||||
|
|
||||||
|
AudioInputI2S i2sIn; |
||||||
|
AudioOutputI2S i2sOut; |
||||||
|
BAAudioControlWM8731 codec; |
||||||
|
|
||||||
|
AudioEffectTemplate templateEffect; |
||||||
|
|
||||||
|
AudioFilterBiquad cabFilter; // We'll want something to cut out the highs and smooth the tone, just like a guitar cab.
|
||||||
|
|
||||||
|
// Simply connect the input to the delay, and the output
|
||||||
|
// to both i2s channels
|
||||||
|
AudioConnection input(i2sIn,0, templateEffect,0); |
||||||
|
AudioConnection effectOut(templateEffect, 0, cabFilter, 0); |
||||||
|
AudioConnection leftOut(cabFilter,0, i2sOut, 0); |
||||||
|
AudioConnection rightOut(cabFilter,0, i2sOut, 1); |
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// SETUP PHYSICAL CONTROLS
|
||||||
|
// - POT1 (left) will control the rate
|
||||||
|
// - POT2 (right) will control the depth
|
||||||
|
// - POT3 (centre) will control the volume
|
||||||
|
// - SW1 (left) will be used as a bypass control
|
||||||
|
// - LED1 (left) will be illuminated when the effect is ON (not bypass)
|
||||||
|
// - SW2 (right) will be used to cycle through the the waveforms
|
||||||
|
// - LED2 (right) will illuminate when pressing SW2.
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// To get the calibration values for your particular board, first run the
|
||||||
|
// BAExpansionCalibrate.ino example and
|
||||||
|
constexpr int potCalibMin = 1; |
||||||
|
constexpr int potCalibMax = 1018; |
||||||
|
constexpr bool potSwapDirection = true; |
||||||
|
|
||||||
|
// Create a control object using the number of switches, pots, encoders and outputs on the
|
||||||
|
// Blackaddr Audio Expansion Board.
|
||||||
|
BAPhysicalControls controls(BA_EXPAND_NUM_SW, BA_EXPAND_NUM_POT, BA_EXPAND_NUM_ENC, BA_EXPAND_NUM_LED); |
||||||
|
|
||||||
|
int loopCount = 0; |
||||||
|
constexpr unsigned MAX_HEADPHONE_VOL = 10; |
||||||
|
unsigned headphoneVolume = 8; // control headphone volume from 0 to 10.
|
||||||
|
|
||||||
|
// BAPhysicalControls returns a handle when you register a new control. We'll uses these handles when working with the controls.
|
||||||
|
int bypassHandle, volumeHandle, led1Handle, led2Handle; // Handles for the various controls
|
||||||
|
|
||||||
|
void setup() { |
||||||
|
delay(100); // wait a bit for serial to be available
|
||||||
|
Serial.begin(57600); // Start the serial port
|
||||||
|
delay(100); |
||||||
|
|
||||||
|
// Setup the controls. The return value is the handle to use when checking for control changes, etc.
|
||||||
|
// pushbuttons
|
||||||
|
bypassHandle = controls.addSwitch(BA_EXPAND_SW1_PIN); // will be used for bypass control
|
||||||
|
//button2Handle = controls.addSwitch(BA_EXPAND_SW2_PIN); // will be used for stepping through filters
|
||||||
|
// pots
|
||||||
|
//rateHandle = controls.addPot(BA_EXPAND_POT1_PIN, potCalibMin, potCalibMax, potSwapDirection); // control the amount of delay
|
||||||
|
//depthHandle = controls.addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
|
||||||
|
volumeHandle = controls.addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
|
||||||
|
// leds
|
||||||
|
led1Handle = controls.addOutput(BA_EXPAND_LED1_PIN); |
||||||
|
led2Handle = controls.addOutput(BA_EXPAND_LED2_PIN); // will illuminate when pressing SW2
|
||||||
|
|
||||||
|
// Disable the audio codec first
|
||||||
|
codec.disable(); |
||||||
|
AudioMemory(128); |
||||||
|
|
||||||
|
// Enable and configure the codec
|
||||||
|
Serial.println("Enabling codec...\n"); |
||||||
|
codec.enable(); |
||||||
|
codec.setHeadphoneVolume(1.0f); // Max headphone volume
|
||||||
|
|
||||||
|
// Besure to enable the templateEffect. When disabled, audio is is completely blocked by the effect
|
||||||
|
// to minimize resource usage to nearly to nearly zero.
|
||||||
|
templateEffect.enable();
|
||||||
|
|
||||||
|
// Set some default values.
|
||||||
|
// These can be changed using the controls on the Blackaddr Audio Expansion Board
|
||||||
|
templateEffect.bypass(false); |
||||||
|
|
||||||
|
// Guitar cabinet: 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); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
|
||||||
|
float potValue; |
||||||
|
|
||||||
|
// Check if SW1 has been toggled (pushed)
|
||||||
|
if (controls.isSwitchToggled(bypassHandle)) { |
||||||
|
bool bypass = templateEffect.isBypass(); // get the current state
|
||||||
|
bypass = !bypass; // change it
|
||||||
|
templateEffect.bypass(bypass); // set the new state
|
||||||
|
controls.setOutput(led1Handle, !bypass); // Set the LED when NOT bypassed
|
||||||
|
Serial.println(String("BYPASS is ") + bypass); |
||||||
|
} |
||||||
|
|
||||||
|
// // Use SW2 to cycle through the waveforms
|
||||||
|
// controls.setOutput(led2Handle, controls.getSwitchValue(led2Handle));
|
||||||
|
// if (controls.isSwitchToggled(waveformHandle)) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Use POT1 (left) to control the rate setting
|
||||||
|
// if (controls.checkPotValue(rateHandle, potValue)) {
|
||||||
|
// // Pot has changed
|
||||||
|
// Serial.println(String("New RATE setting: ") + potValue);
|
||||||
|
// templateEffect.rate(potValue);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Use POT2 (right) to control the depth setting
|
||||||
|
// if (controls.checkPotValue(depthHandle, potValue)) {
|
||||||
|
// // Pot has changed
|
||||||
|
// Serial.println(String("New DEPTH setting: ") + potValue);
|
||||||
|
// templateEffect.depth(potValue);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Use POT3 (centre) to control the volume setting
|
||||||
|
if (controls.checkPotValue(volumeHandle, potValue)) { |
||||||
|
// Pot has changed
|
||||||
|
Serial.println(String("New VOLUME setting: ") + potValue); |
||||||
|
templateEffect.volume(potValue); |
||||||
|
} |
||||||
|
|
||||||
|
// Use the 'u' and 'd' keys to adjust volume across ten levels.
|
||||||
|
if (Serial) { |
||||||
|
if (Serial.available() > 0) { |
||||||
|
while (Serial.available()) { |
||||||
|
char key = Serial.read(); |
||||||
|
if (key == 'u') {
|
||||||
|
headphoneVolume = (headphoneVolume + 1) % MAX_HEADPHONE_VOL; |
||||||
|
Serial.println(String("Increasing HEADPHONE volume to ") + headphoneVolume); |
||||||
|
} |
||||||
|
else if (key == 'd') {
|
||||||
|
headphoneVolume = (headphoneVolume - 1) % MAX_HEADPHONE_VOL; |
||||||
|
Serial.println(String("Decreasing HEADPHONE volume to ") + headphoneVolume); |
||||||
|
} |
||||||
|
codec.setHeadphoneVolume(static_cast<float>(headphoneVolume) / static_cast<float>(MAX_HEADPHONE_VOL)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Use the loopCounter to roughly measure human timescales. Every few seconds, print the CPU usage
|
||||||
|
// to the serial port. About 500,000 loops!
|
||||||
|
//if (loopCount % 524288 == 0) {
|
||||||
|
if (loopCount % 25000 == 0) { |
||||||
|
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage()); |
||||||
|
Serial.print("% "); |
||||||
|
Serial.print(" templateEffect: "); Serial.print(templateEffect.processorUsage()); |
||||||
|
Serial.println("%"); |
||||||
|
} |
||||||
|
loopCount++; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,148 @@ |
|||||||
|
/**************************************************************************//**
|
||||||
|
* @file |
||||||
|
* @author Steve Lascos |
||||||
|
* @company Blackaddr Audio |
||||||
|
* |
||||||
|
* PitchShift for creating your own audio effects |
||||||
|
* |
||||||
|
* @copyright This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY__BAEFFECTS_AUDIOEFFECTDELAYEXTERNAL_H; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*****************************************************************************/ |
||||||
|
|
||||||
|
#ifndef __BAEFFECTS_AUDIOEFFECTPITCHSHIFT_H |
||||||
|
#define __BAEFFECTS_AUDIOEFFECTPITCHSHIFT_H |
||||||
|
|
||||||
|
#include <Audio.h> |
||||||
|
#include "BATypes.h" |
||||||
|
#include "LibBasicFunctions.h" |
||||||
|
|
||||||
|
namespace BAEffects { |
||||||
|
|
||||||
|
/**************************************************************************//**
|
||||||
|
* AudioEffectPitchShift |
||||||
|
*****************************************************************************/ |
||||||
|
class AudioEffectPitchShift : public AudioStream { |
||||||
|
public: |
||||||
|
|
||||||
|
static constexpr unsigned ANALYSIS_SIZE = 1024; |
||||||
|
static constexpr unsigned FFT_OVERSAMPLE_FACTOR = 1; |
||||||
|
static constexpr float FFT_OVERSAMPLE_FACTOR_F = 1.0f; |
||||||
|
static constexpr unsigned SYNTHESIS_SIZE = ANALYSIS_SIZE * FFT_OVERSAMPLE_FACTOR; |
||||||
|
static constexpr float SYNTHESIS_SIZE_F = (float)(ANALYSIS_SIZE * FFT_OVERSAMPLE_FACTOR); |
||||||
|
static constexpr float OVERLAP_FACTOR_F = (float)ANALYSIS_SIZE / (float)AUDIO_BLOCK_SAMPLES; |
||||||
|
|
||||||
|
///< List of AudioEffectTremolo MIDI controllable parameters
|
||||||
|
enum { |
||||||
|
BYPASS = 0, ///< controls effect bypass
|
||||||
|
VOLUME, ///< controls the output volume level
|
||||||
|
NUM_CONTROLS ///< this can be used as an alias for the number of MIDI controls
|
||||||
|
}; |
||||||
|
|
||||||
|
// *** CONSTRUCTORS ***
|
||||||
|
AudioEffectPitchShift(); |
||||||
|
|
||||||
|
virtual ~AudioEffectPitchShift(); ///< Destructor
|
||||||
|
|
||||||
|
// *** PARAMETERS ***
|
||||||
|
|
||||||
|
/// Bypass the effect.
|
||||||
|
/// @param byp when true, bypass wil disable the effect, when false, effect is enabled.
|
||||||
|
/// Note that audio still passes through when bypass is enabled.
|
||||||
|
void bypass(bool byp) { m_bypass = byp; } |
||||||
|
|
||||||
|
/// Get if the effect is bypassed
|
||||||
|
/// @returns true if bypassed, false if not bypassed
|
||||||
|
bool isBypass() { return m_bypass; } |
||||||
|
|
||||||
|
/// Toggle the bypass effect
|
||||||
|
void toggleBypass() { m_bypass = !m_bypass; } |
||||||
|
|
||||||
|
/// Set the output volume. This affect both the wet and dry signals.
|
||||||
|
/// @details The default is 1.0.
|
||||||
|
/// @param vol Sets the output volume between -1.0 and +1.0
|
||||||
|
void volume(float vol) {m_volume = vol; } |
||||||
|
|
||||||
|
// ** ENABLE / DISABLE **
|
||||||
|
|
||||||
|
/// Enables audio processing. Note: when not enabled, CPU load is nearly zero.
|
||||||
|
void enable() { m_enable = true; } |
||||||
|
|
||||||
|
/// Disables audio process. When disabled, CPU load is nearly zero.
|
||||||
|
void disable() { m_enable = false; } |
||||||
|
|
||||||
|
// ** MIDI **
|
||||||
|
|
||||||
|
/// Sets whether MIDI OMNI channel is processig on or off. When on,
|
||||||
|
/// all midi channels are used for matching CCs.
|
||||||
|
/// @param isOmni when true, all channels are processed, when false, channel
|
||||||
|
/// must match configured value.
|
||||||
|
void setMidiOmni(bool isOmni) { m_isOmni = isOmni; } |
||||||
|
|
||||||
|
/// Configure an effect parameter to be controlled by a MIDI CC
|
||||||
|
/// number on a particular channel.
|
||||||
|
/// @param parameter one of the parameter names in the class enum
|
||||||
|
/// @param midiCC the CC number from 0 to 127
|
||||||
|
/// @param midiChannel the effect will only response to the CC on this channel
|
||||||
|
/// when OMNI mode is off.
|
||||||
|
void mapMidiControl(int parameter, int midiCC, int midiChannel = 0); |
||||||
|
|
||||||
|
/// process a MIDI Continous-Controller (CC) message
|
||||||
|
/// @param channel the MIDI channel from 0 to 15)
|
||||||
|
/// @param midiCC the CC number from 0 to 127
|
||||||
|
/// @param value the CC value from 0 to 127
|
||||||
|
void processMidi(int channel, int midiCC, int value); |
||||||
|
|
||||||
|
|
||||||
|
virtual void update(void); ///< update automatically called by the Teesny Audio Library
|
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_t *m_inputQueueArray[1]; |
||||||
|
//BALibrary::RingBuffer<audio_block_t*> m_inputFifo = BALibrary::RingBuffer<audio_block_t*>(ANALYSIS_SIZE/AUDIO_BLOCK_SAMPLES);
|
||||||
|
float m_analysisBuffer[ANALYSIS_SIZE]; |
||||||
|
float m_analysisFreqBuffer[2*ANALYSIS_SIZE]; |
||||||
|
float m_synthesisFreqBuffer[2*SYNTHESIS_SIZE]; |
||||||
|
float m_synthesisBuffer[SYNTHESIS_SIZE]; |
||||||
|
|
||||||
|
bool m_initFailed = false; |
||||||
|
|
||||||
|
unsigned m_frameIndex = 0; |
||||||
|
|
||||||
|
// arm_rfft_instance_f32 fftFwdReal, fftInvReal;
|
||||||
|
// arm_cfft_radix4_instance_f32 fftFwdComplex, fftInvComplex;
|
||||||
|
// float32_t *bufInputReal;
|
||||||
|
// float32_t *bufInputComplex;
|
||||||
|
// float32_t *bufOutputReal;
|
||||||
|
// float32_t *bufOutputComplex;
|
||||||
|
|
||||||
|
//arm_cfft_radix4_instance_f32 fft_inst_fwd, fft_inst_inv;
|
||||||
|
//arm_rfft_instance_f32 rfftForwardInst, rfftInverseInst;
|
||||||
|
arm_cfft_radix4_instance_f32 cfftForwardInst, cfftInverseInst; |
||||||
|
|
||||||
|
//uint8_t ifftFlag = 0; // 0 is FFT, 1 is IFFT
|
||||||
|
//uint8_t doBitReverse = 1;
|
||||||
|
|
||||||
|
int m_midiConfig[NUM_CONTROLS][2]; // stores the midi parameter mapping
|
||||||
|
bool m_isOmni = false; |
||||||
|
bool m_bypass = true; |
||||||
|
bool m_enable = false; |
||||||
|
|
||||||
|
float m_volume = 1.0f; |
||||||
|
float m_pitchScale = 1.0f; |
||||||
|
|
||||||
|
void m_ocean(float *inputFreq, float *outputFreq, float frameIndex, float pitchScale); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#endif /* __BAEFFECTS_AUDIOEFFECTPITCHSHIFT_H */ |
@ -0,0 +1,113 @@ |
|||||||
|
/**************************************************************************//**
|
||||||
|
* @file |
||||||
|
* @author Steve Lascos |
||||||
|
* @company Blackaddr Audio |
||||||
|
* |
||||||
|
* Template for creating your own audio effects |
||||||
|
* |
||||||
|
* @copyright This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY__BAEFFECTS_AUDIOEFFECTDELAYEXTERNAL_H; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*****************************************************************************/ |
||||||
|
|
||||||
|
#ifndef __BAEFFECTS_AUDIOEFFECTTEMPLATE_H |
||||||
|
#define __BAEFFECTS_AUDIOEFFECTTEMPLATE_H |
||||||
|
|
||||||
|
#include <Audio.h> |
||||||
|
#include "LibBasicFunctions.h" |
||||||
|
|
||||||
|
namespace BAEffects { |
||||||
|
|
||||||
|
/**************************************************************************//**
|
||||||
|
* AudioEffectTemplate |
||||||
|
*****************************************************************************/ |
||||||
|
class AudioEffectTemplate : public AudioStream { |
||||||
|
public: |
||||||
|
|
||||||
|
///< List of AudioEffectTremolo MIDI controllable parameters
|
||||||
|
enum { |
||||||
|
BYPASS = 0, ///< controls effect bypass
|
||||||
|
VOLUME, ///< controls the output volume level
|
||||||
|
NUM_CONTROLS ///< this can be used as an alias for the number of MIDI controls
|
||||||
|
}; |
||||||
|
|
||||||
|
// *** CONSTRUCTORS ***
|
||||||
|
AudioEffectTemplate(); |
||||||
|
|
||||||
|
virtual ~AudioEffectTemplate(); ///< Destructor
|
||||||
|
|
||||||
|
// *** PARAMETERS ***
|
||||||
|
|
||||||
|
/// Bypass the effect.
|
||||||
|
/// @param byp when true, bypass wil disable the effect, when false, effect is enabled.
|
||||||
|
/// Note that audio still passes through when bypass is enabled.
|
||||||
|
void bypass(bool byp) { m_bypass = byp; } |
||||||
|
|
||||||
|
/// Get if the effect is bypassed
|
||||||
|
/// @returns true if bypassed, false if not bypassed
|
||||||
|
bool isBypass() { return m_bypass; } |
||||||
|
|
||||||
|
/// Toggle the bypass effect
|
||||||
|
void toggleBypass() { m_bypass = !m_bypass; } |
||||||
|
|
||||||
|
/// Set the output volume. This affect both the wet and dry signals.
|
||||||
|
/// @details The default is 1.0.
|
||||||
|
/// @param vol Sets the output volume between -1.0 and +1.0
|
||||||
|
void volume(float vol) {m_volume = vol; } |
||||||
|
|
||||||
|
// ** ENABLE / DISABLE **
|
||||||
|
|
||||||
|
/// Enables audio processing. Note: when not enabled, CPU load is nearly zero.
|
||||||
|
void enable() { m_enable = true; } |
||||||
|
|
||||||
|
/// Disables audio process. When disabled, CPU load is nearly zero.
|
||||||
|
void disable() { m_enable = false; } |
||||||
|
|
||||||
|
// ** MIDI **
|
||||||
|
|
||||||
|
/// Sets whether MIDI OMNI channel is processig on or off. When on,
|
||||||
|
/// all midi channels are used for matching CCs.
|
||||||
|
/// @param isOmni when true, all channels are processed, when false, channel
|
||||||
|
/// must match configured value.
|
||||||
|
void setMidiOmni(bool isOmni) { m_isOmni = isOmni; } |
||||||
|
|
||||||
|
/// Configure an effect parameter to be controlled by a MIDI CC
|
||||||
|
/// number on a particular channel.
|
||||||
|
/// @param parameter one of the parameter names in the class enum
|
||||||
|
/// @param midiCC the CC number from 0 to 127
|
||||||
|
/// @param midiChannel the effect will only response to the CC on this channel
|
||||||
|
/// when OMNI mode is off.
|
||||||
|
void mapMidiControl(int parameter, int midiCC, int midiChannel = 0); |
||||||
|
|
||||||
|
/// process a MIDI Continous-Controller (CC) message
|
||||||
|
/// @param channel the MIDI channel from 0 to 15)
|
||||||
|
/// @param midiCC the CC number from 0 to 127
|
||||||
|
/// @param value the CC value from 0 to 127
|
||||||
|
void processMidi(int channel, int midiCC, int value); |
||||||
|
|
||||||
|
|
||||||
|
virtual void update(void); ///< update automatically called by the Teesny Audio Library
|
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_t *m_inputQueueArray[1]; |
||||||
|
int m_midiConfig[NUM_CONTROLS][2]; // stores the midi parameter mapping
|
||||||
|
bool m_isOmni = false; |
||||||
|
bool m_bypass = true; |
||||||
|
bool m_enable = false; |
||||||
|
|
||||||
|
float m_volume = 1.0f; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#endif /* __BAEFFECTS_AUDIOEFFECTTEMPLATE_H */ |
@ -0,0 +1,203 @@ |
|||||||
|
/*
|
||||||
|
* AudioEffectPitchShift.cpp |
||||||
|
* |
||||||
|
* Created on: June 20, 2019 |
||||||
|
* Author: slascos |
||||||
|
*/ |
||||||
|
#include <cmath> // std::roundf |
||||||
|
#include "AudioEffectPitchShift.h" |
||||||
|
|
||||||
|
using namespace BALibrary; |
||||||
|
|
||||||
|
namespace BAEffects { |
||||||
|
|
||||||
|
constexpr int MIDI_CHANNEL = 0; |
||||||
|
constexpr int MIDI_CONTROL = 1; |
||||||
|
|
||||||
|
constexpr unsigned NUM_AUDIO_BLOCKS = AudioEffectPitchShift::ANALYSIS_SIZE / AUDIO_BLOCK_SAMPLES; |
||||||
|
constexpr uint32_t FFT_FORWARD = 0; |
||||||
|
constexpr uint32_t FFT_INVERSE = 1; |
||||||
|
constexpr uint32_t FFT_DO_BIT_REVERSE = 1; |
||||||
|
|
||||||
|
AudioEffectPitchShift::AudioEffectPitchShift() |
||||||
|
: AudioStream(1, m_inputQueueArray) |
||||||
|
{ |
||||||
|
// clear the audio buffer to avoid pops
|
||||||
|
for (unsigned i=0; i<AudioEffectPitchShift::ANALYSIS_SIZE; i++) { |
||||||
|
m_analysisBuffer[i] = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
// Configure the FFT
|
||||||
|
// arm_rfft_init_f32(&rfftForwardInst, &cfftForwardInst, AudioEffectPitchShift::ANALYSIS_SIZE,
|
||||||
|
// FFT_FORWARD, FFT_DO_BIT_REVERSE);
|
||||||
|
// arm_rfft_init_f32(&rfftInverseInst, &cfftInverseInst, AudioEffectPitchShift::SYNTHESIS_SIZE,
|
||||||
|
// FFT_INVERSE, FFT_DO_BIT_REVERSE);
|
||||||
|
unsigned ret; |
||||||
|
ret = arm_cfft_radix4_init_f32(&cfftForwardInst, ANALYSIS_SIZE, FFT_FORWARD, FFT_DO_BIT_REVERSE); //init FFT
|
||||||
|
if (!ret) { m_initFailed = true; }; |
||||||
|
ret = arm_cfft_radix4_init_f32(&cfftInverseInst, SYNTHESIS_SIZE, FFT_INVERSE, FFT_DO_BIT_REVERSE); //init FFT
|
||||||
|
if (!ret) { m_initFailed = true; }; |
||||||
|
} |
||||||
|
|
||||||
|
AudioEffectPitchShift::~AudioEffectPitchShift() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectPitchShift::update(void) |
||||||
|
{ |
||||||
|
audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples
|
||||||
|
|
||||||
|
// Check is block is disabled
|
||||||
|
if (m_enable == false) { |
||||||
|
// do not transmit or process any audio, return as quickly as possible.
|
||||||
|
if (inputAudioBlock) release(inputAudioBlock); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Check is block is bypassed, if so either transmit input directly or create silence
|
||||||
|
if (m_bypass == true) { |
||||||
|
// transmit the input directly
|
||||||
|
if (!inputAudioBlock) { |
||||||
|
// create silence
|
||||||
|
inputAudioBlock = allocate(); |
||||||
|
if (!inputAudioBlock) { return; } // failed to allocate
|
||||||
|
else { |
||||||
|
clearAudioBlock(inputAudioBlock); |
||||||
|
} |
||||||
|
} |
||||||
|
transmit(inputAudioBlock, 0); |
||||||
|
release(inputAudioBlock); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// DO PROCESSING HERE
|
||||||
|
// Update the fifo
|
||||||
|
// m_inputFifo.push_back(inputAudioBlock); // insert the new block
|
||||||
|
// release(m_inputFifo.front()); //
|
||||||
|
// m_inputFifo.pop_front();
|
||||||
|
|
||||||
|
// Convert the contents of the audio blocks to the contiguous buffer
|
||||||
|
// 1) Be aware the audio library stores audio samples in reverse temporal order.
|
||||||
|
// This means the first sample (in time) is in the last location of the buffer.
|
||||||
|
// 2) the oldest audio is at the front of the queue, the latest at the back
|
||||||
|
float *analysisPtr = &m_analysisBuffer[0]; |
||||||
|
float *analysisFreqPtr = &m_analysisFreqBuffer[0]; |
||||||
|
float *synthesisFreqPtr = &m_synthesisFreqBuffer[0]; |
||||||
|
float *synthesisPtr = &m_synthesisBuffer[0]; |
||||||
|
|
||||||
|
// first shift the contents of the float buffer up by AUDIO_BLOCK SAMPLES
|
||||||
|
for (unsigned i=0; i<NUM_AUDIO_BLOCKS-1; i++) { |
||||||
|
memcpy(&analysisPtr[i*AUDIO_BLOCK_SAMPLES], &analysisPtr[(i+1)*AUDIO_BLOCK_SAMPLES], AUDIO_BLOCK_SAMPLES*sizeof(float)); |
||||||
|
} |
||||||
|
// Convert the newest incoming audio block to float
|
||||||
|
arm_q15_to_float(inputAudioBlock->data, &analysisPtr[(NUM_AUDIO_BLOCKS-1)*AUDIO_BLOCK_SAMPLES], AUDIO_BLOCK_SAMPLES); |
||||||
|
release(inputAudioBlock); // were done with it now
|
||||||
|
|
||||||
|
//if (m_initFailed) { Serial.println("FFT INIT FAILED"); }
|
||||||
|
|
||||||
|
// Construct the interleaved FFT buffer
|
||||||
|
unsigned idx = 0; |
||||||
|
for (unsigned i=0; i<ANALYSIS_SIZE; i++) { |
||||||
|
m_analysisFreqBuffer[idx] = analysisPtr[i]; |
||||||
|
m_analysisFreqBuffer[idx+1] = 0; |
||||||
|
idx += 2; |
||||||
|
} |
||||||
|
|
||||||
|
// Perform the FFT
|
||||||
|
arm_cfft_radix4_f32(&cfftForwardInst, analysisFreqPtr); |
||||||
|
|
||||||
|
|
||||||
|
// perform the ocean pitch shift
|
||||||
|
m_ocean(analysisFreqPtr, synthesisFreqPtr, (float)(m_frameIndex), m_pitchScale); |
||||||
|
//memcpy(synthesisFreqPtr, analysisFreqPtr, 2*ANALYSIS_SIZE*sizeof(float));
|
||||||
|
|
||||||
|
// Perform the inverse FFT
|
||||||
|
arm_cfft_radix4_f32(&cfftInverseInst, synthesisFreqPtr); |
||||||
|
|
||||||
|
// Deinterleave the synthesis buffer
|
||||||
|
idx = 0; |
||||||
|
for (unsigned i=0; i<(2*SYNTHESIS_SIZE); i=i+2) { |
||||||
|
m_synthesisBuffer[idx] = synthesisFreqPtr[i]; |
||||||
|
idx++; |
||||||
|
} |
||||||
|
|
||||||
|
// Convert the float buffer back to integer
|
||||||
|
audio_block_t *outputBlock = allocate(); |
||||||
|
arm_float_to_q15 (synthesisPtr, outputBlock->data, AUDIO_BLOCK_SAMPLES); |
||||||
|
|
||||||
|
transmit(outputBlock); |
||||||
|
release(outputBlock); |
||||||
|
m_frameIndex++; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectPitchShift::processMidi(int channel, int control, int value) |
||||||
|
{ |
||||||
|
|
||||||
|
float val = (float)value / 127.0f; |
||||||
|
|
||||||
|
if ((m_midiConfig[BYPASS][MIDI_CHANNEL] == channel) && |
||||||
|
(m_midiConfig[BYPASS][MIDI_CONTROL] == control)) { |
||||||
|
// Bypass
|
||||||
|
if (value >= 65) { bypass(false); Serial.println(String("AudioEffectPitchShift::not bypassed -> ON") + value); } |
||||||
|
else { bypass(true); Serial.println(String("AudioEffectPitchShift::bypassed -> OFF") + value); } |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if ((m_midiConfig[VOLUME][MIDI_CHANNEL] == channel) && |
||||||
|
(m_midiConfig[VOLUME][MIDI_CONTROL] == control)) { |
||||||
|
// Volume
|
||||||
|
Serial.println(String("AudioEffectPitchShift::volume: ") + 100*val + String("%")); |
||||||
|
volume(val); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectPitchShift::mapMidiControl(int parameter, int midiCC, int midiChannel) |
||||||
|
{ |
||||||
|
if (parameter >= NUM_CONTROLS) { |
||||||
|
return ; // Invalid midi parameter
|
||||||
|
} |
||||||
|
m_midiConfig[parameter][MIDI_CHANNEL] = midiChannel; |
||||||
|
m_midiConfig[parameter][MIDI_CONTROL] = midiCC; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectPitchShift::m_ocean(float *inputFreq, float *outputFreq, float frameIndex, float pitchScale) |
||||||
|
{ |
||||||
|
// zero the output buffer
|
||||||
|
for (unsigned i=0; i<(2*SYNTHESIS_SIZE); i++) { |
||||||
|
outputFreq[i] = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float phaseAdjustFactor = -((2.0f*((float)(M_PI))*frameIndex) |
||||||
|
/ (OVERLAP_FACTOR_F * FFT_OVERSAMPLE_FACTOR_F * SYNTHESIS_SIZE_F)); |
||||||
|
|
||||||
|
for (unsigned k=1; k < SYNTHESIS_SIZE/2; k++) { |
||||||
|
|
||||||
|
float a = (float)k; |
||||||
|
// b = mka + 0.5
|
||||||
|
// where m is the FFT oversample factor, k is the pitch scaling, a
|
||||||
|
// is the original bin number
|
||||||
|
float b = std::roundf( (FFT_OVERSAMPLE_FACTOR_F * pitchScale * a)); |
||||||
|
unsigned b_int = (unsigned)(b); |
||||||
|
|
||||||
|
if (b_int < SYNTHESIS_SIZE/2) { |
||||||
|
|
||||||
|
// phaseAdjust = (b-ma) * phaseAdjustFactor
|
||||||
|
float phaseAdjust = (b - (FFT_OVERSAMPLE_FACTOR_F * a)) * phaseAdjustFactor; |
||||||
|
|
||||||
|
float a_real = inputFreq[2*k]; |
||||||
|
float a_imag = inputFreq[2*k+1]; |
||||||
|
|
||||||
|
outputFreq[2*b_int] = (a_real * arm_cos_f32(phaseAdjust)) - (a_imag * arm_sin_f32(phaseAdjust)); |
||||||
|
outputFreq[2*b_int+1] = (a_real * arm_sin_f32(phaseAdjust)) + (a_imag * arm_cos_f32(phaseAdjust)); |
||||||
|
} |
||||||
|
|
||||||
|
// update the imag components
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,94 @@ |
|||||||
|
/*
|
||||||
|
* AudioEffectTemplate.cpp |
||||||
|
* |
||||||
|
* Created on: June 20, 2019 |
||||||
|
* Author: slascos |
||||||
|
*/ |
||||||
|
#include <cmath> // std::roundf |
||||||
|
#include "AudioEffectTemplate.h" |
||||||
|
|
||||||
|
using namespace BALibrary; |
||||||
|
|
||||||
|
namespace BAEffects { |
||||||
|
|
||||||
|
constexpr int MIDI_CHANNEL = 0; |
||||||
|
constexpr int MIDI_CONTROL = 1; |
||||||
|
|
||||||
|
AudioEffectTemplate::AudioEffectTemplate() |
||||||
|
: AudioStream(1, m_inputQueueArray) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
AudioEffectTemplate::~AudioEffectTemplate() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectTemplate::update(void) |
||||||
|
{ |
||||||
|
audio_block_t *inputAudioBlock = receiveWritable(); // get the next block of input samples
|
||||||
|
|
||||||
|
// Check is block is disabled
|
||||||
|
if (m_enable == false) { |
||||||
|
// do not transmit or process any audio, return as quickly as possible.
|
||||||
|
if (inputAudioBlock) release(inputAudioBlock); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Check is block is bypassed, if so either transmit input directly or create silence
|
||||||
|
if (m_bypass == true) { |
||||||
|
// transmit the input directly
|
||||||
|
if (!inputAudioBlock) { |
||||||
|
// create silence
|
||||||
|
inputAudioBlock = allocate(); |
||||||
|
if (!inputAudioBlock) { return; } // failed to allocate
|
||||||
|
else { |
||||||
|
clearAudioBlock(inputAudioBlock); |
||||||
|
} |
||||||
|
} |
||||||
|
transmit(inputAudioBlock, 0); |
||||||
|
release(inputAudioBlock); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// DO PROCESSING HERE
|
||||||
|
|
||||||
|
transmit(inputAudioBlock); |
||||||
|
release(inputAudioBlock); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectTemplate::processMidi(int channel, int control, int value) |
||||||
|
{ |
||||||
|
|
||||||
|
float val = (float)value / 127.0f; |
||||||
|
|
||||||
|
if ((m_midiConfig[BYPASS][MIDI_CHANNEL] == channel) && |
||||||
|
(m_midiConfig[BYPASS][MIDI_CONTROL] == control)) { |
||||||
|
// Bypass
|
||||||
|
if (value >= 65) { bypass(false); Serial.println(String("AudioEffectTemplate::not bypassed -> ON") + value); } |
||||||
|
else { bypass(true); Serial.println(String("AudioEffectTemplate::bypassed -> OFF") + value); } |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if ((m_midiConfig[VOLUME][MIDI_CHANNEL] == channel) && |
||||||
|
(m_midiConfig[VOLUME][MIDI_CONTROL] == control)) { |
||||||
|
// Volume
|
||||||
|
Serial.println(String("AudioEffectTemplate::volume: ") + 100*val + String("%")); |
||||||
|
volume(val); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectTemplate::mapMidiControl(int parameter, int midiCC, int midiChannel) |
||||||
|
{ |
||||||
|
if (parameter >= NUM_CONTROLS) { |
||||||
|
return ; // Invalid midi parameter
|
||||||
|
} |
||||||
|
m_midiConfig[parameter][MIDI_CHANNEL] = midiChannel; |
||||||
|
m_midiConfig[parameter][MIDI_CONTROL] = midiCC; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in new issue