From 808aedf8c0936a91574bff5d38cdbd313b3dc22a Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Tue, 17 Apr 2018 19:05:39 -0400 Subject: [PATCH 1/7] wip - added SOS class --- src/AudioEffectSOS.h | 136 ++++++++++++++++++ src/BAGuitar.h | 1 + src/LibBasicFunctions.h | 70 ++++++++- src/common/AudioHelpers.cpp | 5 + src/common/ParameterAutomation.cpp | 110 ++++++++++++++ src/effects/AudioEffectSOS.cpp | 221 +++++++++++++++++++++++++++++ 6 files changed, 540 insertions(+), 3 deletions(-) create mode 100644 src/AudioEffectSOS.h create mode 100644 src/common/ParameterAutomation.cpp create mode 100644 src/effects/AudioEffectSOS.cpp diff --git a/src/AudioEffectSOS.h b/src/AudioEffectSOS.h new file mode 100644 index 0000000..841f1f8 --- /dev/null +++ b/src/AudioEffectSOS.h @@ -0,0 +1,136 @@ +/**************************************************************************//** + * @file + * @author Steve Lascos + * @company Blackaddr Audio + * + * AudioEffectSOS is a class f + * + * @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; 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 . + *****************************************************************************/ + +#ifndef __BAGUITAR_BAAUDIOEFFECTSOS_H +#define __BAGUITAR_BAAUDIOEFFECTSOS_H + +#include +#include "LibBasicFunctions.h" + +namespace BAEffects { + +/**************************************************************************//** + * AudioEffectSOS + *****************************************************************************/ +class AudioEffectSOS : public AudioStream { +public: + + ///< List of AudioEffectAnalogDelay MIDI controllable parameters + enum { + BYPASS = 0, ///< controls effect bypass + GATE_TRIGGER, ///< begins the gate sequence + GATE_OPEN_TIME, ///< controls how long it takes to open the gate + GATE_CLOSE_TIME, ///< controls how long it takes to close the gate (release) + 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 + }; + + // *** CONSTRUCTORS *** + AudioEffectSOS() = delete; + + /// Construct an analog delay using external SPI via an ExtMemSlot. The amount of + /// delay will be determined by the amount of memory in the slot. + /// @param slot A pointer to the ExtMemSlot to use for the delay. + AudioEffectSOS(BAGuitar::ExtMemSlot *slot); // requires sufficiently sized pre-allocated memory + + virtual ~AudioEffectSOS(); ///< Destructor + + // *** PARAMETERS *** + void gateOpenTime(float milliseconds); + + void gateCloseTime(float milliseconds); + + void feedback(float feedback) { m_feedback = feedback; } + + /// 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; } + + /// 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]; + bool m_isOmni = false; + bool m_bypass = true; + bool m_enable = false; + BAGuitar::AudioDelay *m_memory = nullptr; + bool m_externalMemory = true; + audio_block_t *m_previousBlock = nullptr; + audio_block_t *m_blockToRelease = nullptr; + size_t m_maxDelaySamples = 0; + + // Controls + int m_midiConfig[NUM_CONTROLS][2]; // stores the midi parameter mapping + size_t m_delaySamples = 0; + float m_openTimeMs = 0.0f; + float m_closeTimeMs = 0.0f; + float m_feedback = 0.0f; + float m_volume = 1.0f; + + // Automated Controls + BALibrary::ParameterAutomation m_inputGateAuto = + BALibrary::ParameterAutomation(0.0f, 1.0f, 0.0f, BALibrary::ParameterAutomation::Function::LINEAR); + + // Private functions + void m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal); + //void m_postProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet); +}; + +} + +#endif /* __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H */ diff --git a/src/BAGuitar.h b/src/BAGuitar.h index 1fc8319..d4c4622 100644 --- a/src/BAGuitar.h +++ b/src/BAGuitar.h @@ -29,6 +29,7 @@ #include "BAGpio.h" #include "BAAudioEffectDelayExternal.h" #include "AudioEffectAnalogDelay.h" +#include "AudioEffectSOS.h" #include "LibBasicFunctions.h" #include "LibMemoryManagement.h" diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 0507e43..26e9c64 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -92,9 +92,15 @@ void alphaBlend(audio_block_t *out, audio_block_t *dry, audio_block_t* wet, floa /// @param out pointer to output audio block /// @param in pointer to input audio block /// @param vol volume cofficient between -1.0 and +1.0 -/// @param coeffShift number of bits to shiftt the coefficient +/// @param coeffShift number of bits to shift the coefficient void gainAdjust(audio_block_t *out, audio_block_t *in, float vol, int coeffShift = 0); +/// Combine two audio blocks through vector addition +/// out[n] = in0[n] + in1[n] +/// @param out pointer to output audio block +/// @param in0 pointer to first input audio block to combine +/// @param in1 pointer to second input audio block to combine +void combine(audio_block_t *out, audio_block_t *in0, audio_block_t *in1); template class RingBuffer; // forward declare so AudioDelay can use it. @@ -246,7 +252,7 @@ public: /// Process the data using the configured IIR filter /// @details output and input can be the same pointer if in-place modification is desired - /// @param output pointer to where the output results will be written + /// @param output poinvoid combine(audio_block_t *out, audio_block_t *in0, audio_block_t *in1)ter to where the output results will be written /// @param input pointer to where the input data will be read from /// @param numSampmles number of samples to process bool process(int16_t *output, int16_t *input, size_t numSamples); @@ -297,7 +303,65 @@ private: }; -} +} // namespace BAGuitar + +namespace BALibrary { + +/**************************************************************************//** + * The class will automate a parameter using a trigger from a start value to an + * end value, using either a preprogrammed function or a user-provided LUT. + *****************************************************************************/ +template +class ParameterAutomation +{ +public: + enum class Function : unsigned { + LINEAR = 0, ///< f(x) = x + EXPONENTIAL, ///< f(x) = e^x + LOGARITHMIC, ///< f(x) = ln(x) + PARABOLIC, ///< f(x) = x^2 + LOOKUP_TABLE ///< f(x) = lut(x) + }; + ParameterAutomation() = delete; + ParameterAutomation(T startValue, T endValue, size_t durationSamples, Function function = Function::LINEAR); + ParameterAutomation(T startValue, T endValue, float durationMilliseconds, Function function = Function::LINEAR); + ~ParameterAutomation(); + + /// set the start and end values for the automation + /// @param function select which automation curve (function) to use + /// @param startValue after reset, parameter automation start from this value + /// @param endValue after the automation duration, paramter will finish at this value + /// @param durationSamples number of samples to transition from startValue to endValue + void reconfigure(T startValue, T endValue, size_t durationSamples, Function function = Function::LINEAR); + void reconfigure(T startValue, T endValue, float durationMilliseconds, Function function = Function::LINEAR); + + /// Start the automation from startValue + void trigger(); + + /// Retrieve the next calculated automation value + /// @returns the calculated parameter value of templated type T + T getNextValue(); + +private: + Function m_function; + T m_startValue; + T m_endValue; + bool m_running = false; + T m_currentValueX; ///< the current value of x in f(x) + size_t m_duration; + T m_coeffs[3]; ///< some general coefficient storage +}; + + +// TODO: initialize with const number of sequences with null type that automatically skips +// then register each new sequence. +template +class ParameterAutomationSequence +{ + +}; + +} // BALibrary #endif /* __BAGUITAR_LIBBASICFUNCTIONS_H */ diff --git a/src/common/AudioHelpers.cpp b/src/common/AudioHelpers.cpp index ae7cfcf..785e5db 100644 --- a/src/common/AudioHelpers.cpp +++ b/src/common/AudioHelpers.cpp @@ -69,6 +69,11 @@ void gainAdjust(audio_block_t *out, audio_block_t *in, float vol, int coeffShift arm_scale_q15(in->data, scale, coeffShift, out->data, AUDIO_BLOCK_SAMPLES); } +void combine(audio_block_t *out, audio_block_t *in0, audio_block_t *in1) +{ + arm_add_q15 (in0->data, in1->data, out->data, AUDIO_BLOCK_SAMPLES); +} + void clearAudioBlock(audio_block_t *block) { memset(block->data, 0, sizeof(int16_t)*AUDIO_BLOCK_SAMPLES); diff --git a/src/common/ParameterAutomation.cpp b/src/common/ParameterAutomation.cpp new file mode 100644 index 0000000..9bf8177 --- /dev/null +++ b/src/common/ParameterAutomation.cpp @@ -0,0 +1,110 @@ +/* + * ParameterAutomation.cpp + * + * Created on: April 14, 2018 + * Author: slascos + * + * 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; 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 . +*/ + +#include "LibBasicFunctions.h" + +using namespace BAGuitar; + +namespace BALibrary { + +constexpr int LINEAR_SLOPE = 0; + +template +ParameterAutomation::ParameterAutomation(T startValue, T endValue, float durationMilliseconds, Function function) +{ + reconfigure(startValue, endValue, calcAudioSamples(durationMilliseconds), function); +} + +template +ParameterAutomation::ParameterAutomation(T startValue, T endValue, size_t durationSamples, Function function) +{ + reconfigure(startValue, endValue, durationSamples, function); +} + +template +void ParameterAutomation::reconfigure(T startValue, T endValue, float durationMilliseconds, Function function) +{ + reconfigure(startValue, endValue, calcAudioSamples(durationMilliseconds), function); +} + +template +void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durationSamples, Function function) +{ + m_function = function; + m_startValue = startValue; + m_endValue = endValue; + m_currentValueX = startValue; + m_duration = durationSamples; + + // Pre-compute any necessary coefficients + switch(m_function) { + case Function::EXPONENTIAL : + break; + case Function::LOGARITHMIC : + break; + case Function::PARABOLIC : + break; + case Function::LOOKUP_TABLE : + break; + + // Default will be same as LINEAR + case Function::LINEAR : + default : + m_coeffs[LINEAR_SLOPE] = (endValue - startValue) / static_cast(m_duration); + break; + } +} + + +template +void ParameterAutomation::trigger() +{ + m_currentValueX = m_startValue; + m_running = true; +} + +template +T ParameterAutomation::getNextValue() +{ + switch(m_function) { + case Function::EXPONENTIAL : + break; + case Function::LOGARITHMIC : + break; + case Function::PARABOLIC : + break; + case Function::LOOKUP_TABLE : + break; + + // Default will be same as LINEAR + case Function::LINEAR : + default : + // output = m_currentValueX + slope + m_currentValueX += m_coeffs[LINEAR_SLOPE]; + if (m_currentValueX >= m_endValue) { + m_currentValueX = m_endValue; + m_running = false; + } + break; + } + return m_currentValueX; +} + +} diff --git a/src/effects/AudioEffectSOS.cpp b/src/effects/AudioEffectSOS.cpp new file mode 100644 index 0000000..f8153ce --- /dev/null +++ b/src/effects/AudioEffectSOS.cpp @@ -0,0 +1,221 @@ +/* + * AudioEffectSOS.cpp + * + * Created on: Apr 14, 2018 + * Author: blackaddr + */ + +#include "AudioEffectSOS.h" +#include "LibBasicFunctions.h" + +using namespace BAGuitar; +using namespace BALibrary; + +namespace BAEffects { + +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; + +AudioEffectSOS::AudioEffectSOS(ExtMemSlot *slot) +: AudioStream(1, m_inputQueueArray) +{ + m_memory = new AudioDelay(slot); + m_maxDelaySamples = (slot->size() / sizeof(int16_t)); + m_delaySamples = m_maxDelaySamples; + m_externalMemory = true; +} + +AudioEffectSOS::~AudioEffectSOS() +{ + +} + +void AudioEffectSOS::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); + + // release all held memory resources + if (m_previousBlock) { + release(m_previousBlock); m_previousBlock = nullptr; + } + if (!m_externalMemory) { + // when using internal memory we have to release all references in the ring buffer + while (m_memory->getRingBuffer()->size() > 0) { + audio_block_t *releaseBlock = m_memory->getRingBuffer()->front(); + m_memory->getRingBuffer()->pop_front(); + if (releaseBlock) release(releaseBlock); + } + } + 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; + } + + // Otherwise perform normal processing + // In order to make use of the SPI DMA, we need to request the read from memory first, + // then do other processing while it fills in the back. + audio_block_t *blockToOutput = nullptr; // this will hold the output audio + blockToOutput = allocate(); + if (!blockToOutput) return; // skip this update cycle due to failure + + // get the data. If using external memory with DMA, this won't be filled until + // later. + m_memory->getSamples(blockToOutput, m_delaySamples); + + // If using DMA, we need something else to do while that read executes, so + // move on to input preprocessing + + // Preprocessing + audio_block_t *preProcessed = allocate(); + // mix the input with the feedback path in the pre-processing stage + m_preProcessing(preProcessed, inputAudioBlock, m_previousBlock); + + // consider doing the BBD post processing here to use up more time while waiting + // for the read data to come back + audio_block_t *blockToRelease = m_memory->addBlock(preProcessed); + + + // BACK TO OUTPUT PROCESSING + // Check if external DMA, if so, we need to be sure the read is completed + if (m_externalMemory && m_memory->getSlot()->isUseDma()) { + // Using DMA + while (m_memory->getSlot()->isReadBusy()) {} + } + + // perform the wet/dry mix mix + //m_postProcessing(blockToOutput, inputAudioBlock, blockToOutput); + transmit(blockToOutput); + + release(inputAudioBlock); + release(m_previousBlock); + m_previousBlock = blockToOutput; + + if (m_blockToRelease) release(m_blockToRelease); + m_blockToRelease = blockToRelease; +} + + +void AudioEffectSOS::gateOpenTime(float milliseconds) +{ + // TODO - change the paramter automation to an automation sequence + m_openTimeMs = milliseconds; + //m_inputGateAuto.reconfigure(); +} + +void AudioEffectSOS::gateCloseTime(float milliseconds) +{ + m_closeTimeMs = milliseconds; +} + +//////////////////////////////////////////////////////////////////////// +// MIDI PROCESSING +//////////////////////////////////////////////////////////////////////// +void AudioEffectSOS::processMidi(int channel, int control, int value) +{ + + float val = (float)value / 127.0f; + + if ((m_midiConfig[GATE_OPEN_TIME][MIDI_CHANNEL] == channel) && + (m_midiConfig[GATE_OPEN_TIME][MIDI_CONTROL] == control)) { + // Gate Open Time + gateOpenTime(val * MAX_GATE_OPEN_TIME_MS); + Serial.println(String("AudioEffectSOS::gate open time (ms): ") + m_openTimeMs); + return; + } + + if ((m_midiConfig[GATE_CLOSE_TIME][MIDI_CHANNEL] == channel) && + (m_midiConfig[GATE_CLOSE_TIME][MIDI_CONTROL] == control)) { + // Gate Close Time + gateCloseTime(val * MAX_GATE_CLOSE_TIME_MS); + Serial.println(String("AudioEffectSOS::gate close time (ms): ") + m_openTimeMs); + return; + } + + if ((m_midiConfig[FEEDBACK][MIDI_CHANNEL] == channel) && + (m_midiConfig[FEEDBACK][MIDI_CONTROL] == control)) { + // Feedback + Serial.println(String("AudioEffectSOS::feedback: ") + 100*val + String("%")); + feedback(val); + return; + } + + if ((m_midiConfig[VOLUME][MIDI_CHANNEL] == channel) && + (m_midiConfig[VOLUME][MIDI_CONTROL] == control)) { + // Volume + Serial.println(String("AudioEffectSOS::volume: ") + 100*val + String("%")); + volume(val); + return; + } + + if ((m_midiConfig[BYPASS][MIDI_CHANNEL] == channel) && + (m_midiConfig[BYPASS][MIDI_CONTROL] == control)) { + // Bypass + if (value >= 65) { bypass(false); Serial.println(String("AudioEffectSOS::not bypassed -> ON") + value); } + else { bypass(true); Serial.println(String("AudioEffectSOS::bypassed -> OFF") + value); } + return; + } + + if ((m_midiConfig[GATE_TRIGGER][MIDI_CHANNEL] == channel) && + (m_midiConfig[GATE_TRIGGER][MIDI_CONTROL] == control)) { + // The gate is trigged by any value + m_inputGateAuto.trigger(); + Serial.println(String("AudioEffectSOS::Gate Triggered!")); + return; + } +} + +void AudioEffectSOS::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; +} + +////////////////////////////////////////////////////////////////////// +// PRIVATE FUNCTIONS +////////////////////////////////////////////////////////////////////// +void AudioEffectSOS::m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal) +{ + if ( out && input && delayedSignal) { + // Multiply the input signal by the automated gate value + // Multiply the delayed signal by the user set feedback value + // then mix together. + float gateVol = m_inputGateAuto.getNextValue(); + audio_block_t tempAudioBuffer; + gainAdjust(out, input, gateVol, 0); // last paremeter is coeff shift, 0 bits + gainAdjust(&tempAudioBuffer, delayedSignal, m_feedback, 0); // last paremeter is coeff shift, 0 bits + combine(out, out, &tempAudioBuffer); + } else if (input) { + memcpy(out->data, input->data, sizeof(int16_t) * AUDIO_BLOCK_SAMPLES); + } +} + + +} // namespace BAEffects + + + From ec4a6b26d56e6e282160ae7b99c6e0b9dc1ec3b8 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Wed, 18 Apr 2018 20:14:22 -0400 Subject: [PATCH 2/7] WIP - Further development work checking --- src/LibBasicFunctions.h | 27 ++++++++-- src/common/ParameterAutomation.cpp | 87 ++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 26e9c64..0afa625 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -316,16 +316,17 @@ class ParameterAutomation { public: enum class Function : unsigned { - LINEAR = 0, ///< f(x) = x + NOT_CONFIGURED = 0, ///< Initial, unconfigured stage + LINEAR, ///< f(x) = x EXPONENTIAL, ///< f(x) = e^x LOGARITHMIC, ///< f(x) = ln(x) PARABOLIC, ///< f(x) = x^2 LOOKUP_TABLE ///< f(x) = lut(x) }; - ParameterAutomation() = delete; + ParameterAutomation(); ParameterAutomation(T startValue, T endValue, size_t durationSamples, Function function = Function::LINEAR); ParameterAutomation(T startValue, T endValue, float durationMilliseconds, Function function = Function::LINEAR); - ~ParameterAutomation(); + virtual ~ParameterAutomation(); /// set the start and end values for the automation /// @param function select which automation curve (function) to use @@ -342,6 +343,8 @@ public: /// @returns the calculated parameter value of templated type T T getNextValue(); + bool isFinished() { return !m_running; } + private: Function m_function; T m_startValue; @@ -355,10 +358,28 @@ private: // TODO: initialize with const number of sequences with null type that automatically skips // then register each new sequence. +constexpr int MAX_PARAMETER_SEQUENCES = 32; template class ParameterAutomationSequence { +public: + ParameterAutomationSequence() = delete; + ParameterAutomationSequence(int numStages); + virtual ~ParameterAutomationSequence(); + + void setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation::Function function); + void setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation::Function function); + + /// Trigger a the automation sequence until numStages is reached or a Function is ParameterAutomation::Function::NOT_CONFIGURED + void trigger(); + + T getNextValue(); + bool isFinished(); +private: + ParameterAutomation *m_paramArray[MAX_PARAMETER_SEQUENCES]; + int m_currentIndex = 0; + int m_numStages = 0; }; } // BALibrary diff --git a/src/common/ParameterAutomation.cpp b/src/common/ParameterAutomation.cpp index 9bf8177..0a59647 100644 --- a/src/common/ParameterAutomation.cpp +++ b/src/common/ParameterAutomation.cpp @@ -24,7 +24,15 @@ using namespace BAGuitar; namespace BALibrary { +/////////////////////////////////////////////////////////////////////////////// +// ParameterAutomation +/////////////////////////////////////////////////////////////////////////////// constexpr int LINEAR_SLOPE = 0; +template +ParameterAutomation::ParameterAutomation() +{ + reconfigure(0.0f, 0.0f, static_cast(0), Function::NOT_CONFIGURED); +} template ParameterAutomation::ParameterAutomation(T startValue, T endValue, float durationMilliseconds, Function function) @@ -38,6 +46,12 @@ ParameterAutomation::ParameterAutomation(T startValue, T endValue, size_t dur reconfigure(startValue, endValue, durationSamples, function); } +template +ParameterAutomation::~ParameterAutomation() +{ + +} + template void ParameterAutomation::reconfigure(T startValue, T endValue, float durationMilliseconds, Function function) { @@ -52,6 +66,7 @@ void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durati m_endValue = endValue; m_currentValueX = startValue; m_duration = durationSamples; + m_running = false; // Pre-compute any necessary coefficients switch(m_function) { @@ -107,4 +122,76 @@ T ParameterAutomation::getNextValue() return m_currentValueX; } +// Template instantiation +//template class MyStack; +template class ParameterAutomation; + +/////////////////////////////////////////////////////////////////////////////// +// ParameterAutomationSequence +/////////////////////////////////////////////////////////////////////////////// +template +ParameterAutomationSequence::ParameterAutomationSequence(int numStages) +{ + //m_paramArray = malloc(sizeof(ParameterAutomation*) * numStages); + if (numStages < MAX_PARAMETER_SEQUENCES) { + for (int i=0; i(); + } + for (int i=numStages; i +ParameterAutomationSequence::~ParameterAutomationSequence() +{ + //if (m_paramArray) { + for (int i=0; i +void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation::Function function) +{ + m_paramArray[index]->reconfigure(startValue, endValue, durationSamples, function); +} + +template +void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation::Function function) +{ + m_paramArray[index]->reconfigure(startValue, endValue, durationMilliseconds, function); +} + +template +void ParameterAutomationSequence::trigger(void) +{ + m_currentIndex = 0; + for (int i=0; itrigger(); + } +} + +template +bool ParameterAutomationSequence::isFinished() +{ + bool finished = true; + for (int i=0; iisFinished()) { + finished = false; + break; + } + } + return finished; +} + +// Template instantiation +template class ParameterAutomationSequence; + } From 7c2e4d4e90db2e4c4ba35a9070c4126e32a66ca5 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Sat, 21 Apr 2018 15:27:45 -0400 Subject: [PATCH 3/7] wip - linear parameter automation is now working --- src/AudioEffectSOS.h | 13 ++- src/BAHardware.h | 8 +- src/LibBasicFunctions.h | 16 +++- src/common/AudioDelay.cpp | 10 ++- src/common/ExternalSramManager.cpp | 8 +- src/common/ParameterAutomation.cpp | 108 ++++++++++++++++++++----- src/effects/AudioEffectAnalogDelay.cpp | 5 ++ src/effects/AudioEffectSOS.cpp | 74 ++++++++++++++--- 8 files changed, 201 insertions(+), 41 deletions(-) diff --git a/src/AudioEffectSOS.h b/src/AudioEffectSOS.h index 841f1f8..1ee8f99 100644 --- a/src/AudioEffectSOS.h +++ b/src/AudioEffectSOS.h @@ -46,6 +46,8 @@ public: // *** CONSTRUCTORS *** AudioEffectSOS() = delete; + AudioEffectSOS(float maxDelayMs); + AudioEffectSOS(size_t numSamples); /// Construct an analog delay using external SPI via an ExtMemSlot. The amount of /// delay will be determined by the amount of memory in the slot. @@ -66,6 +68,9 @@ public: /// Note that audio still passes through when bypass is enabled. void bypass(bool byp) { m_bypass = byp; } + /// Activate the gate automation. Input gate will open, then close. + void trigger() { m_inputGateAuto.trigger(); } + /// 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 @@ -74,7 +79,7 @@ public: // ** ENABLE / DISABLE ** /// Enables audio processing. Note: when not enabled, CPU load is nearly zero. - void enable() { m_enable = true; } + void enable(); /// Disables audio process. When disabled, CPU load is nearly zero. void disable() { m_enable = false; } @@ -123,12 +128,12 @@ private: float m_volume = 1.0f; // Automated Controls - BALibrary::ParameterAutomation m_inputGateAuto = - BALibrary::ParameterAutomation(0.0f, 1.0f, 0.0f, BALibrary::ParameterAutomation::Function::LINEAR); + BALibrary::ParameterAutomationSequence m_inputGateAuto = + BALibrary::ParameterAutomationSequence(3); // Private functions void m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal); - //void m_postProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet); + void m_postProcessing(audio_block_t *out, audio_block_t *input); }; } diff --git a/src/BAHardware.h b/src/BAHardware.h index 5637bca..bb3670d 100644 --- a/src/BAHardware.h +++ b/src/BAHardware.h @@ -74,11 +74,17 @@ constexpr size_t MEM_MAX_ADDR[NUM_MEM_SLOTS] = { 131071, 131071 }; /**************************************************************************//** * General Purpose SPI Interfaces *****************************************************************************/ -enum SpiDeviceId : unsigned { +enum class SpiDeviceId : unsigned { SPI_DEVICE0 = 0, ///< Arduino SPI device SPI_DEVICE1 = 1 ///< Arduino SPI1 device }; constexpr int SPI_MAX_ADDR = 131071; ///< Max address size per chip +constexpr size_t SPI_MEM0_SIZE_BYTES = 131072; +constexpr size_t SPI_MEM0_MAX_AUDIO_SAMPLES = SPI_MEM0_SIZE_BYTES/sizeof(int16_t); + +constexpr size_t SPI_MEM1_SIZE_BYTES = 131072; +constexpr size_t SPI_MEM1_MAX_AUDIO_SAMPLES = SPI_MEM1_SIZE_BYTES/sizeof(int16_t); + #else diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 0afa625..140022f 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -153,6 +153,12 @@ public: /// @returns a pointer to the requested audio_block_t audio_block_t *getBlock(size_t index); + /// Returns the max possible delay samples. For INTERNAL memory, the delay can be equal to + /// the full maxValue specified. For EXTERNAL memory, the max delay is actually one audio + /// block less then the full size to prevent wrapping. + /// @returns the maximum delay offset in units of samples. + size_t getMaxDelaySamples(); + /// Retrieve an audio block (or samples) from the buffer. /// @details when using INTERNAL memory, only supported size is AUDIO_BLOCK_SAMPLES. When using /// EXTERNAL, a size smaller than AUDIO_BLOCK_SAMPLES can be requested. @@ -167,6 +173,8 @@ public: /// @returns pointer to the underlying ExtMemSlot. ExtMemSlot *getSlot() const { return m_slot; } + + /// Ween using INTERNAL memory, thsi function can return a pointer to the underlying RingBuffer that contains /// audio_block_t * pointers. /// @returns pointer to the underlying RingBuffer @@ -183,6 +191,7 @@ private: MemType m_type; ///< when 0, INTERNAL memory, when 1, external MEMORY. RingBuffer *m_ringBuffer = nullptr; ///< When using INTERNAL memory, a RingBuffer will be created. ExtMemSlot *m_slot = nullptr; ///< When using EXTERNAL memory, an ExtMemSlot must be provided. + size_t m_maxDelaySamples = 0; ///< stores the number of audio samples in the AudioDelay. }; /**************************************************************************//** @@ -317,6 +326,7 @@ class ParameterAutomation public: enum class Function : unsigned { NOT_CONFIGURED = 0, ///< Initial, unconfigured stage + HOLD, ///< f(x) = constant LINEAR, ///< f(x) = x EXPONENTIAL, ///< f(x) = e^x LOGARITHMIC, ///< f(x) = ln(x) @@ -350,9 +360,10 @@ private: T m_startValue; T m_endValue; bool m_running = false; - T m_currentValueX; ///< the current value of x in f(x) + float m_currentValueX; ///< the current value of x in f(x) size_t m_duration; - T m_coeffs[3]; ///< some general coefficient storage + float m_coeffs[3]; ///< some general coefficient storage + bool m_positiveSlope = true; }; @@ -380,6 +391,7 @@ private: ParameterAutomation *m_paramArray[MAX_PARAMETER_SEQUENCES]; int m_currentIndex = 0; int m_numStages = 0; + bool m_running = false; }; } // BALibrary diff --git a/src/common/AudioDelay.cpp b/src/common/AudioDelay.cpp index 704233c..b3b4e39 100644 --- a/src/common/AudioDelay.cpp +++ b/src/common/AudioDelay.cpp @@ -34,6 +34,7 @@ AudioDelay::AudioDelay(size_t maxSamples) // INTERNAL memory consisting of audio_block_t data structures. QueuePosition pos = calcQueuePosition(maxSamples); m_ringBuffer = new RingBuffer(pos.index+2); // If the delay is in queue x, we need to overflow into x+1, thus x+2 total buffers. + m_maxDelaySamples = maxSamples; } AudioDelay::AudioDelay(float maxDelayTimeMs) @@ -46,6 +47,7 @@ AudioDelay::AudioDelay(ExtMemSlot *slot) { m_type = (MemType::MEM_EXTERNAL); m_slot = slot; + m_maxDelaySamples = (slot->size() / sizeof(int16_t)) - AUDIO_BLOCK_SAMPLES; } AudioDelay::~AudioDelay() @@ -93,6 +95,11 @@ audio_block_t* AudioDelay::getBlock(size_t index) return ret; } +size_t AudioDelay::getMaxDelaySamples() +{ + return m_maxDelaySamples; +} + bool AudioDelay::getSamples(audio_block_t *dest, size_t offsetSamples, size_t numSamples) { if (!dest) { @@ -159,8 +166,9 @@ bool AudioDelay::getSamples(audio_block_t *dest, size_t offsetSamples, size_t nu return true; } else { - // numSampmles is > than total slot size + // numSamples is > than total slot size Serial.println("getSamples(): ERROR numSamples > total slot size"); + Serial.println(numSamples + String(" > ") + m_slot->size()); return false; } } diff --git a/src/common/ExternalSramManager.cpp b/src/common/ExternalSramManager.cpp index cc0f7f9..98465fc 100644 --- a/src/common/ExternalSramManager.cpp +++ b/src/common/ExternalSramManager.cpp @@ -37,8 +37,8 @@ ExternalSramManager::ExternalSramManager(unsigned numMemories) // Initialize the static memory configuration structs if (!m_configured) { for (unsigned i=0; i < NUM_MEM_SLOTS; i++) { - m_memConfig[i].size = MEM_MAX_ADDR[i]; - m_memConfig[i].totalAvailable = MEM_MAX_ADDR[i]; + m_memConfig[i].size = MEM_MAX_ADDR[i]+1; + m_memConfig[i].totalAvailable = MEM_MAX_ADDR[i]+1; m_memConfig[i].nextAvailable = 0; m_memConfig[i].m_spi = nullptr; @@ -111,7 +111,9 @@ bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BAGu return true; } else { // there is not enough memory available for the request - + Serial.println(String("ExternalSramManager::requestMemory(): Insufficient memory in slot, request/available: ") + + sizeBytes + String(" : ") + + m_memConfig[mem].totalAvailable); return false; } } diff --git a/src/common/ParameterAutomation.cpp b/src/common/ParameterAutomation.cpp index 0a59647..f56ce99 100644 --- a/src/common/ParameterAutomation.cpp +++ b/src/common/ParameterAutomation.cpp @@ -28,6 +28,7 @@ namespace BALibrary { // ParameterAutomation /////////////////////////////////////////////////////////////////////////////// constexpr int LINEAR_SLOPE = 0; + template ParameterAutomation::ParameterAutomation() { @@ -64,10 +65,20 @@ void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durati m_function = function; m_startValue = startValue; m_endValue = endValue; - m_currentValueX = startValue; + m_currentValueX = static_cast(startValue); m_duration = durationSamples; m_running = false; + if (endValue >= startValue) { + // value is increasing + m_positiveSlope = true; + } else { + // value is decreasing + m_positiveSlope = false; + } + + float duration = m_duration / static_cast(AUDIO_BLOCK_SAMPLES); + // Pre-compute any necessary coefficients switch(m_function) { case Function::EXPONENTIAL : @@ -80,9 +91,14 @@ void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durati break; // Default will be same as LINEAR + case Function::HOLD : + m_coeffs[LINEAR_SLOPE] = (1.0f / static_cast(duration)); // convert duration from ms to sec + break; case Function::LINEAR : default : - m_coeffs[LINEAR_SLOPE] = (endValue - startValue) / static_cast(m_duration); + // The number of parameter updates will be duration in samples divided by audio sample block size since + // we only update once per block. + m_coeffs[LINEAR_SLOPE] = static_cast(endValue - startValue) / duration; // convert duration from ms to sec break; } } @@ -91,13 +107,33 @@ void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durati template void ParameterAutomation::trigger() { - m_currentValueX = m_startValue; + if (m_function == Function::HOLD) { + // The HOLD function will move currentValueX from 0 to 1.0 over the desired duration, + // but will always return the startValue. + m_currentValueX = 0.0f; + } else { + m_currentValueX = static_cast(m_startValue); + } m_running = true; + //Serial.println("ParameterAutomation::trigger() called"); } template T ParameterAutomation::getNextValue() { + if (m_running == false) { + return m_startValue; + } + + if (m_function == Function::HOLD) { + // HOLD is treated as a special case + m_currentValueX += m_coeffs[LINEAR_SLOPE]; + if (m_currentValueX >= 1.0) { + m_running = false; + } + return m_startValue; + } + switch(m_function) { case Function::EXPONENTIAL : break; @@ -107,24 +143,28 @@ T ParameterAutomation::getNextValue() break; case Function::LOOKUP_TABLE : break; - - // Default will be same as LINEAR case Function::LINEAR : default : // output = m_currentValueX + slope m_currentValueX += m_coeffs[LINEAR_SLOPE]; - if (m_currentValueX >= m_endValue) { - m_currentValueX = m_endValue; - m_running = false; - } break; } - return m_currentValueX; + + // Check if the automation is finished. + if ( ( m_positiveSlope && (m_currentValueX >= m_endValue)) || + (!m_positiveSlope && (m_currentValueX <= m_endValue)) ) { + m_running = false; + return m_endValue; + } else { + return static_cast(m_currentValueX); + } } // Template instantiation //template class MyStack; template class ParameterAutomation; +template class ParameterAutomation; +template class ParameterAutomation; /////////////////////////////////////////////////////////////////////////////// // ParameterAutomationSequence @@ -132,7 +172,6 @@ template class ParameterAutomation; template ParameterAutomationSequence::ParameterAutomationSequence(int numStages) { - //m_paramArray = malloc(sizeof(ParameterAutomation*) * numStages); if (numStages < MAX_PARAMETER_SEQUENCES) { for (int i=0; i(); @@ -147,35 +186,63 @@ ParameterAutomationSequence::ParameterAutomationSequence(int numStages) template ParameterAutomationSequence::~ParameterAutomationSequence() { - //if (m_paramArray) { - for (int i=0; i void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation::Function function) { m_paramArray[index]->reconfigure(startValue, endValue, durationSamples, function); + m_currentIndex = 0; } template void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation::Function function) { m_paramArray[index]->reconfigure(startValue, endValue, durationMilliseconds, function); + m_currentIndex = 0; } template void ParameterAutomationSequence::trigger(void) { m_currentIndex = 0; - for (int i=0; itrigger(); + m_paramArray[0]->trigger(); + m_running = true; + //Serial.println("ParameterAutomationSequence::trigger() called"); +} + +template +T ParameterAutomationSequence::getNextValue() +{ + // Get the next value + T nextValue = m_paramArray[m_currentIndex]->getNextValue(); + + if (m_running) { + //Serial.println(String("ParameterAutomationSequence::getNextValue() is ") + nextValue + // + String(" from stage ") + m_currentIndex); + + // If current stage is done, trigger the next + if (m_paramArray[m_currentIndex]->isFinished()) { + Serial.println(String("Finished stage ") + m_currentIndex); + m_currentIndex++; + + if (m_currentIndex >= m_numStages) { + // Last stage already finished + m_running = false; + m_currentIndex = 0; + } else { + // trigger the next stage + m_paramArray[m_currentIndex]->trigger(); + } + } } + + return nextValue; } template @@ -188,6 +255,7 @@ bool ParameterAutomationSequence::isFinished() break; } } + m_running = !finished; return finished; } diff --git a/src/effects/AudioEffectAnalogDelay.cpp b/src/effects/AudioEffectAnalogDelay.cpp index 6cf1ead..5a15d4d 100644 --- a/src/effects/AudioEffectAnalogDelay.cpp +++ b/src/effects/AudioEffectAnalogDelay.cpp @@ -166,6 +166,11 @@ void AudioEffectAnalogDelay::delay(float milliseconds) { size_t delaySamples = calcAudioSamples(milliseconds); + if (delaySamples > m_memory->getMaxDelaySamples()) { + // this exceeds max delay value, limit it. + delaySamples = m_memory->getMaxDelaySamples(); + } + if (!m_memory) { Serial.println("delay(): m_memory is not valid"); } if (!m_externalMemory) { diff --git a/src/effects/AudioEffectSOS.cpp b/src/effects/AudioEffectSOS.cpp index f8153ce..bd7ba33 100644 --- a/src/effects/AudioEffectSOS.cpp +++ b/src/effects/AudioEffectSOS.cpp @@ -19,18 +19,51 @@ constexpr int MIDI_CONTROL = 1; constexpr float MAX_GATE_OPEN_TIME_MS = 3000.0f; constexpr float MAX_GATE_CLOSE_TIME_MS = 3000.0f; +constexpr int GATE_OPEN_STAGE = 0; +constexpr int GATE_HOLD_STAGE = 1; +constexpr int GATE_CLOSE_STAGE = 2; + +AudioEffectSOS::AudioEffectSOS(float maxDelayMs) +: AudioStream(1, m_inputQueueArray) +{ + m_memory = new AudioDelay(maxDelayMs); + m_maxDelaySamples = calcAudioSamples(maxDelayMs); + m_externalMemory = false; +} + +AudioEffectSOS::AudioEffectSOS(size_t numSamples) +: AudioStream(1, m_inputQueueArray) +{ + m_memory = new AudioDelay(numSamples); + m_maxDelaySamples = numSamples; + m_externalMemory = false; +} + AudioEffectSOS::AudioEffectSOS(ExtMemSlot *slot) : AudioStream(1, m_inputQueueArray) { m_memory = new AudioDelay(slot); - m_maxDelaySamples = (slot->size() / sizeof(int16_t)); - m_delaySamples = m_maxDelaySamples; m_externalMemory = true; } AudioEffectSOS::~AudioEffectSOS() { + if (m_memory) delete m_memory; +} +void AudioEffectSOS::enable(void) +{ + m_enable = true; + if (m_externalMemory) { + // Because we hold the previous output buffer for an update cycle, the maximum delay is actually + // 1 audio block mess then the max delay returnable from the memory. + m_maxDelaySamples = m_memory->getMaxDelaySamples(); + Serial.println(String("SOS Enabled with delay length ") + m_maxDelaySamples + String(" samples")); + } + m_delaySamples = m_maxDelaySamples; + m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation::Function::LINEAR); + m_inputGateAuto.setupParameter(GATE_HOLD_STAGE, 1.0f, 1.0f, 1000.0f, ParameterAutomation::Function::HOLD); + m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation::Function::LINEAR); } void AudioEffectSOS::update(void) @@ -58,7 +91,7 @@ void AudioEffectSOS::update(void) } // Check is block is bypassed, if so either transmit input directly or create silence - if (m_bypass == true) { + if ( (m_bypass == true) || (!inputAudioBlock) ) { // transmit the input directly if (!inputAudioBlock) { // create silence @@ -73,6 +106,8 @@ void AudioEffectSOS::update(void) return; } + if (!inputAudioBlock) return; + // Otherwise perform normal processing // In order to make use of the SPI DMA, we need to request the read from memory first, // then do other processing while it fills in the back. @@ -83,6 +118,8 @@ void AudioEffectSOS::update(void) // get the data. If using external memory with DMA, this won't be filled until // later. m_memory->getSamples(blockToOutput, m_delaySamples); + //Serial.println(String("Delay samples:") + m_delaySamples); + //Serial.println(String("Use dma: ") + m_memory->getSlot()->isUseDma()); // If using DMA, we need something else to do while that read executes, so // move on to input preprocessing @@ -95,6 +132,8 @@ void AudioEffectSOS::update(void) // consider doing the BBD post processing here to use up more time while waiting // for the read data to come back audio_block_t *blockToRelease = m_memory->addBlock(preProcessed); + //audio_block_t *blockToRelease = m_memory->addBlock(inputAudioBlock); + //Serial.println("Done adding new block"); // BACK TO OUTPUT PROCESSING @@ -105,13 +144,19 @@ void AudioEffectSOS::update(void) } // perform the wet/dry mix mix - //m_postProcessing(blockToOutput, inputAudioBlock, blockToOutput); + m_postProcessing(blockToOutput, blockToOutput); transmit(blockToOutput); release(inputAudioBlock); - release(m_previousBlock); + + if (m_previousBlock) + release(m_previousBlock); m_previousBlock = blockToOutput; + if (m_blockToRelease == m_previousBlock) { + Serial.println("ERROR: POINTER COLLISION"); + } + if (m_blockToRelease) release(m_blockToRelease); m_blockToRelease = blockToRelease; } @@ -121,12 +166,13 @@ void AudioEffectSOS::gateOpenTime(float milliseconds) { // TODO - change the paramter automation to an automation sequence m_openTimeMs = milliseconds; - //m_inputGateAuto.reconfigure(); + m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, m_openTimeMs, ParameterAutomation::Function::LINEAR); } void AudioEffectSOS::gateCloseTime(float milliseconds) { m_closeTimeMs = milliseconds; + m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, m_closeTimeMs, ParameterAutomation::Function::LINEAR); } //////////////////////////////////////////////////////////////////////// @@ -149,7 +195,7 @@ void AudioEffectSOS::processMidi(int channel, int control, int value) (m_midiConfig[GATE_CLOSE_TIME][MIDI_CONTROL] == control)) { // Gate Close Time gateCloseTime(val * MAX_GATE_CLOSE_TIME_MS); - Serial.println(String("AudioEffectSOS::gate close time (ms): ") + m_openTimeMs); + Serial.println(String("AudioEffectSOS::gate close time (ms): ") + m_closeTimeMs); return; } @@ -180,8 +226,8 @@ 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 - m_inputGateAuto.trigger(); Serial.println(String("AudioEffectSOS::Gate Triggered!")); + m_inputGateAuto.trigger(); return; } } @@ -203,17 +249,25 @@ 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 mix together. + float gateVol = m_inputGateAuto.getNextValue(); + + //float gateVol = 1.0f; audio_block_t tempAudioBuffer; gainAdjust(out, input, gateVol, 0); // last paremeter is coeff shift, 0 bits - gainAdjust(&tempAudioBuffer, delayedSignal, m_feedback, 0); // last paremeter is coeff shift, 0 bits + gainAdjust(&tempAudioBuffer, delayedSignal, m_feedback, 0); // last parameter is coeff shift, 0 bits combine(out, out, &tempAudioBuffer); + } else if (input) { memcpy(out->data, input->data, sizeof(int16_t) * AUDIO_BLOCK_SAMPLES); } } +void AudioEffectSOS::m_postProcessing(audio_block_t *out, audio_block_t *in) +{ + gainAdjust(out, out, m_volume, 0); +} + } // namespace BAEffects From 03b38585bbe2e2f1a523d01a6d16d1b677a3536b Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Sat, 21 Apr 2018 20:17:03 -0400 Subject: [PATCH 4/7] Parameter automation cleanup --- src/LibBasicFunctions.h | 8 +-- src/common/ParameterAutomation.cpp | 79 +++++++++++------------------- src/effects/AudioEffectSOS.cpp | 8 +-- 3 files changed, 37 insertions(+), 58 deletions(-) diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 140022f..381b2f8 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -328,8 +328,8 @@ public: NOT_CONFIGURED = 0, ///< Initial, unconfigured stage HOLD, ///< f(x) = constant LINEAR, ///< f(x) = x - EXPONENTIAL, ///< f(x) = e^x - LOGARITHMIC, ///< f(x) = ln(x) + EXPONENTIAL, ///< f(x) = exp(-k*x) + LOGARITHMIC, ///< f(x) = PARABOLIC, ///< f(x) = x^2 LOOKUP_TABLE ///< f(x) = lut(x) }; @@ -362,7 +362,9 @@ private: bool m_running = false; float m_currentValueX; ///< the current value of x in f(x) size_t m_duration; - float m_coeffs[3]; ///< some general coefficient storage + //float m_coeffs[3]; ///< some general coefficient storage + float m_slopeX; + float m_scaleY; bool m_positiveSlope = true; }; diff --git a/src/common/ParameterAutomation.cpp b/src/common/ParameterAutomation.cpp index f56ce99..649e356 100644 --- a/src/common/ParameterAutomation.cpp +++ b/src/common/ParameterAutomation.cpp @@ -28,6 +28,7 @@ namespace BALibrary { // ParameterAutomation /////////////////////////////////////////////////////////////////////////////// constexpr int LINEAR_SLOPE = 0; +constexpr float EXPONENTIAL_K = 5.0f; template ParameterAutomation::ParameterAutomation() @@ -65,10 +66,13 @@ void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durati m_function = function; m_startValue = startValue; m_endValue = endValue; - m_currentValueX = static_cast(startValue); + m_currentValueX = 0.0f; m_duration = durationSamples; m_running = false; + float duration = m_duration / static_cast(AUDIO_BLOCK_SAMPLES); + m_slopeX = (1.0f / static_cast(duration)); + m_scaleY = abs(endValue - startValue); if (endValue >= startValue) { // value is increasing m_positiveSlope = true; @@ -76,46 +80,14 @@ void ParameterAutomation::reconfigure(T startValue, T endValue, size_t durati // value is decreasing m_positiveSlope = false; } - - float duration = m_duration / static_cast(AUDIO_BLOCK_SAMPLES); - - // Pre-compute any necessary coefficients - switch(m_function) { - case Function::EXPONENTIAL : - break; - case Function::LOGARITHMIC : - break; - case Function::PARABOLIC : - break; - case Function::LOOKUP_TABLE : - break; - - // Default will be same as LINEAR - case Function::HOLD : - m_coeffs[LINEAR_SLOPE] = (1.0f / static_cast(duration)); // convert duration from ms to sec - break; - case Function::LINEAR : - default : - // The number of parameter updates will be duration in samples divided by audio sample block size since - // we only update once per block. - m_coeffs[LINEAR_SLOPE] = static_cast(endValue - startValue) / duration; // convert duration from ms to sec - break; - } } template void ParameterAutomation::trigger() { - if (m_function == Function::HOLD) { - // The HOLD function will move currentValueX from 0 to 1.0 over the desired duration, - // but will always return the startValue. - m_currentValueX = 0.0f; - } else { - m_currentValueX = static_cast(m_startValue); - } + m_currentValueX = 0.0f; m_running = true; - //Serial.println("ParameterAutomation::trigger() called"); } template @@ -125,43 +97,44 @@ T ParameterAutomation::getNextValue() return m_startValue; } - if (m_function == Function::HOLD) { - // HOLD is treated as a special case - m_currentValueX += m_coeffs[LINEAR_SLOPE]; - if (m_currentValueX >= 1.0) { - m_running = false; - } - return m_startValue; - } + m_currentValueX += m_slopeX; + float value; switch(m_function) { case Function::EXPONENTIAL : - break; - case Function::LOGARITHMIC : + // f(x) = exp(-k*x) + value = 1.0f - expf(-EXPONENTIAL_K*m_currentValueX); break; case Function::PARABOLIC : + value = m_currentValueX*m_currentValueX; break; case Function::LOOKUP_TABLE : - break; case Function::LINEAR : default : - // output = m_currentValueX + slope - m_currentValueX += m_coeffs[LINEAR_SLOPE]; + value = m_currentValueX; break; } // Check if the automation is finished. - if ( ( m_positiveSlope && (m_currentValueX >= m_endValue)) || - (!m_positiveSlope && (m_currentValueX <= m_endValue)) ) { + if (m_currentValueX >= 1.0f) { + m_currentValueX = 0.0f; m_running = false; return m_endValue; + } + + float returnValue; + if (m_positiveSlope) { + returnValue = m_startValue + (m_scaleY*value); } else { - return static_cast(m_currentValueX); + returnValue = m_startValue - (m_scaleY*value); } +// Serial.println(String("Start/End values: ") + m_startValue + String(":") + m_endValue); +// Serial.print("Parameter m_currentValueX is "); Serial.println(m_currentValueX, 6); +// Serial.print("Parameter returnValue is "); Serial.println(returnValue, 6); + return returnValue; } // Template instantiation -//template class MyStack; template class ParameterAutomation; template class ParameterAutomation; template class ParameterAutomation; @@ -196,6 +169,7 @@ ParameterAutomationSequence::~ParameterAutomationSequence() template void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation::Function function) { + Serial.println("setupParameter() called"); m_paramArray[index]->reconfigure(startValue, endValue, durationSamples, function); m_currentIndex = 0; } @@ -203,6 +177,7 @@ void ParameterAutomationSequence::setupParameter(int index, T startValue, T e template void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation::Function function) { + Serial.println("setupParameter() called"); m_paramArray[index]->reconfigure(startValue, endValue, durationMilliseconds, function); m_currentIndex = 0; } @@ -261,5 +236,7 @@ bool ParameterAutomationSequence::isFinished() // Template instantiation template class ParameterAutomationSequence; +template class ParameterAutomationSequence; +template class ParameterAutomationSequence; } diff --git a/src/effects/AudioEffectSOS.cpp b/src/effects/AudioEffectSOS.cpp index bd7ba33..06d9504 100644 --- a/src/effects/AudioEffectSOS.cpp +++ b/src/effects/AudioEffectSOS.cpp @@ -61,9 +61,9 @@ void AudioEffectSOS::enable(void) Serial.println(String("SOS Enabled with delay length ") + m_maxDelaySamples + String(" samples")); } m_delaySamples = m_maxDelaySamples; - m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation::Function::LINEAR); + 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, 1000.0f, ParameterAutomation::Function::HOLD); - m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation::Function::LINEAR); + m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation::Function::EXPONENTIAL); } void AudioEffectSOS::update(void) @@ -166,13 +166,13 @@ 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::LINEAR); + m_inputGateAuto.setupParameter(GATE_OPEN_STAGE, 0.0f, 1.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::LINEAR); + m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, m_closeTimeMs, ParameterAutomation::Function::EXPONENTIAL); } //////////////////////////////////////////////////////////////////////// From dc63f9871e45ff3c62cd211b5b02ad3c36929293 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Fri, 25 May 2018 17:36:09 -0400 Subject: [PATCH 5/7] Added LED gate pin --- src/AudioEffectSOS.h | 4 ++++ src/common/AudioDelay.cpp | 4 ++++ src/common/ParameterAutomation.cpp | 33 +++++++++++++++--------------- src/effects/AudioEffectSOS.cpp | 17 ++++++++++++++- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/AudioEffectSOS.h b/src/AudioEffectSOS.h index 1ee8f99..1a252a8 100644 --- a/src/AudioEffectSOS.h +++ b/src/AudioEffectSOS.h @@ -38,6 +38,7 @@ public: BYPASS = 0, ///< controls effect bypass GATE_TRIGGER, ///< begins the gate sequence 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) FEEDBACK, ///< controls the amount of feedback, more gives longer SOS sustain VOLUME, ///< controls the output volume level @@ -56,6 +57,8 @@ public: virtual ~AudioEffectSOS(); ///< Destructor + void setGateLedGpio(int pinId); + // *** PARAMETERS *** void gateOpenTime(float milliseconds); @@ -118,6 +121,7 @@ private: audio_block_t *m_previousBlock = nullptr; audio_block_t *m_blockToRelease = nullptr; size_t m_maxDelaySamples = 0; + int m_gateLedPinId = -1; // Controls int m_midiConfig[NUM_CONTROLS][2]; // stores the midi parameter mapping diff --git a/src/common/AudioDelay.cpp b/src/common/AudioDelay.cpp index b3b4e39..f830cc4 100644 --- a/src/common/AudioDelay.cpp +++ b/src/common/AudioDelay.cpp @@ -97,6 +97,10 @@ audio_block_t* AudioDelay::getBlock(size_t index) size_t AudioDelay::getMaxDelaySamples() { + if (m_type == MemType::MEM_EXTERNAL) { + // update the max delay sample size + m_maxDelaySamples = (m_slot->size() / sizeof(int16_t)) - AUDIO_BLOCK_SAMPLES; + } return m_maxDelaySamples; } diff --git a/src/common/ParameterAutomation.cpp b/src/common/ParameterAutomation.cpp index 649e356..deb9c3d 100644 --- a/src/common/ParameterAutomation.cpp +++ b/src/common/ParameterAutomation.cpp @@ -29,6 +29,7 @@ namespace BALibrary { /////////////////////////////////////////////////////////////////////////////// constexpr int LINEAR_SLOPE = 0; constexpr float EXPONENTIAL_K = 5.0f; +constexpr float EXP_EXPONENTIAL_K = expf(EXPONENTIAL_K); template ParameterAutomation::ParameterAutomation() @@ -99,11 +100,18 @@ T ParameterAutomation::getNextValue() m_currentValueX += m_slopeX; float value; + float returnValue; switch(m_function) { case Function::EXPONENTIAL : - // f(x) = exp(-k*x) - value = 1.0f - expf(-EXPONENTIAL_K*m_currentValueX); + + if (m_positiveSlope) { + // Growth: f(x) = exp(k*x) / exp(k) + value = expf(EXPONENTIAL_K*m_currentValueX) / EXP_EXPONENTIAL_K; + } else { + // Decay: f(x) = 1 - exp(-k*x) + value = 1.0f - expf(-EXPONENTIAL_K*m_currentValueX); + } break; case Function::PARABOLIC : value = m_currentValueX*m_currentValueX; @@ -122,15 +130,15 @@ T ParameterAutomation::getNextValue() return m_endValue; } - float returnValue; + if (m_positiveSlope) { returnValue = m_startValue + (m_scaleY*value); } else { returnValue = m_startValue - (m_scaleY*value); } // Serial.println(String("Start/End values: ") + m_startValue + String(":") + m_endValue); -// Serial.print("Parameter m_currentValueX is "); Serial.println(m_currentValueX, 6); -// Serial.print("Parameter returnValue is "); Serial.println(returnValue, 6); + //Serial.print("Parameter m_currentValueX is "); Serial.println(m_currentValueX, 6); + //Serial.print("Parameter returnValue is "); Serial.println(returnValue, 6); return returnValue; } @@ -169,7 +177,7 @@ ParameterAutomationSequence::~ParameterAutomationSequence() template void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation::Function function) { - Serial.println("setupParameter() called"); + Serial.println(String("setupParameter() called with samples: ") + durationSamples); m_paramArray[index]->reconfigure(startValue, endValue, durationSamples, function); m_currentIndex = 0; } @@ -177,7 +185,7 @@ void ParameterAutomationSequence::setupParameter(int index, T startValue, T e template void ParameterAutomationSequence::setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation::Function function) { - Serial.println("setupParameter() called"); + Serial.print(String("setupParameter() called with time: ")); Serial.println(durationMilliseconds, 6); m_paramArray[index]->reconfigure(startValue, endValue, durationMilliseconds, function); m_currentIndex = 0; } @@ -208,6 +216,7 @@ T ParameterAutomationSequence::getNextValue() if (m_currentIndex >= m_numStages) { // Last stage already finished + Serial.println("Last stage finished"); m_running = false; m_currentIndex = 0; } else { @@ -223,15 +232,7 @@ T ParameterAutomationSequence::getNextValue() template bool ParameterAutomationSequence::isFinished() { - bool finished = true; - for (int i=0; iisFinished()) { - finished = false; - break; - } - } - m_running = !finished; - return finished; + return !m_running; } // Template instantiation diff --git a/src/effects/AudioEffectSOS.cpp b/src/effects/AudioEffectSOS.cpp index 06d9504..594fd6d 100644 --- a/src/effects/AudioEffectSOS.cpp +++ b/src/effects/AudioEffectSOS.cpp @@ -51,6 +51,12 @@ AudioEffectSOS::~AudioEffectSOS() if (m_memory) delete m_memory; } +void AudioEffectSOS::setGateLedGpio(int pinId) +{ + m_gateLedPinId = pinId; + pinMode(static_cast(m_gateLedPinId), OUTPUT); +} + void AudioEffectSOS::enable(void) { m_enable = true; @@ -62,7 +68,7 @@ void AudioEffectSOS::enable(void) } m_delaySamples = m_maxDelaySamples; 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, 1000.0f, ParameterAutomation::Function::HOLD); + 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); } @@ -261,6 +267,15 @@ void AudioEffectSOS::m_preProcessing (audio_block_t *out, audio_block_t *input, } else if (input) { memcpy(out->data, input->data, sizeof(int16_t) * AUDIO_BLOCK_SAMPLES); } + + // Update the gate LED + if (m_gateLedPinId >= 0) { + if (m_inputGateAuto.isFinished()) { + digitalWriteFast(m_gateLedPinId, 0x0); + } else { + digitalWriteFast(m_gateLedPinId, 0x1); + } + } } void AudioEffectSOS::m_postProcessing(audio_block_t *out, audio_block_t *in) From 90441b56683e76131026fa2070bfe0e98e19ba55 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Sun, 15 Jul 2018 13:49:25 -0400 Subject: [PATCH 6/7] Reorganization of the library layout --- .../BA1_TGA_Pro_demo/BA1_TGA_Pro_demo.ino | 3 +- .../BA2_TGA_Pro_1MEM/BA2_TGA_Pro_1MEM.ino | 3 +- .../BA3_TGA_Pro_2MEM/BA3_TGA_Pro_2MEM.ino | 3 +- .../BA4_TGA_Pro_delay_reverb.ino | 3 +- .../BA5_TGA_Pro_ExternalDelay_demo.ino | 3 +- .../Delay/AnalogDelayDemo/AnalogDelayDemo.ino | 3 +- src/AudioEffectAnalogDelay.h | 14 +++++----- src/AudioEffectSOS.h | 12 ++++---- src/BAAudioControlWM8731.h | 10 +++---- src/BAAudioEffectDelayExternal.h | 18 ++++++------ src/BAGpio.h | 10 +++---- src/BAHardware.h | 10 +++---- src/BASpiMemory.h | 28 +++++++++++-------- src/BATypes.h | 10 +++---- src/LibBasicFunctions.h | 12 ++++---- src/LibMemoryManagement.h | 18 ++++++------ src/common/AudioDelay.cpp | 2 +- src/common/AudioHelpers.cpp | 2 +- src/common/ExtMemSlot.cpp | 2 +- src/common/ExternalSramManager.cpp | 14 +++++----- src/common/IirBiquadFilter.cpp | 2 +- src/common/ParameterAutomation.cpp | 2 -- src/effects/AudioEffectAnalogDelay.cpp | 4 ++- src/effects/AudioEffectAnalogDelayFilters.h | 2 +- src/effects/AudioEffectSOS.cpp | 1 - src/effects/BAAudioEffectDelayExternal.cpp | 8 ++++-- src/peripherals/BAAudioControlWM8731.cpp | 4 +-- src/peripherals/BAGpio.cpp | 4 +-- src/peripherals/BASpiMemory.cpp | 4 +-- 29 files changed, 112 insertions(+), 99 deletions(-) diff --git a/examples/BA1_TGA_Pro_demo/BA1_TGA_Pro_demo.ino b/examples/BA1_TGA_Pro_demo/BA1_TGA_Pro_demo.ino index d4d19d1..c66cf6c 100644 --- a/examples/BA1_TGA_Pro_demo/BA1_TGA_Pro_demo.ino +++ b/examples/BA1_TGA_Pro_demo/BA1_TGA_Pro_demo.ino @@ -21,7 +21,8 @@ using namespace midi; //#define ENABLE_MEM_TEST // uncomment this line and 'Save As' to a new location to test the SPI memory -using namespace BAGuitar; +using namespace BALibrary; +using namespace BAEffects; AudioInputI2S i2sIn; AudioOutputI2S i2sOut; diff --git a/examples/BA2_TGA_Pro_1MEM/BA2_TGA_Pro_1MEM.ino b/examples/BA2_TGA_Pro_1MEM/BA2_TGA_Pro_1MEM.ino index 2649ae1..8e1211f 100644 --- a/examples/BA2_TGA_Pro_1MEM/BA2_TGA_Pro_1MEM.ino +++ b/examples/BA2_TGA_Pro_1MEM/BA2_TGA_Pro_1MEM.ino @@ -23,7 +23,8 @@ MIDI_CREATE_DEFAULT_INSTANCE(); using namespace midi; -using namespace BAGuitar; +using namespace BALibrary; +using namespace BAEffects; AudioInputI2S i2sIn; AudioOutputI2S i2sOut; diff --git a/examples/BA3_TGA_Pro_2MEM/BA3_TGA_Pro_2MEM.ino b/examples/BA3_TGA_Pro_2MEM/BA3_TGA_Pro_2MEM.ino index 49a83a5..40f0adb 100644 --- a/examples/BA3_TGA_Pro_2MEM/BA3_TGA_Pro_2MEM.ino +++ b/examples/BA3_TGA_Pro_2MEM/BA3_TGA_Pro_2MEM.ino @@ -24,7 +24,8 @@ MIDI_CREATE_DEFAULT_INSTANCE(); using namespace midi; -using namespace BAGuitar; +using namespace BAEffects; +using namespace BALibrary; AudioInputI2S i2sIn; AudioOutputI2S i2sOut; diff --git a/examples/BA4_TGA_Pro_delay_reverb/BA4_TGA_Pro_delay_reverb.ino b/examples/BA4_TGA_Pro_delay_reverb/BA4_TGA_Pro_delay_reverb.ino index 3cc7190..0eb2353 100644 --- a/examples/BA4_TGA_Pro_delay_reverb/BA4_TGA_Pro_delay_reverb.ino +++ b/examples/BA4_TGA_Pro_delay_reverb/BA4_TGA_Pro_delay_reverb.ino @@ -14,7 +14,8 @@ #include #include "BAGuitar.h" -using namespace BAGuitar; +using namespace BAEffects; +using namespace BALibrary; BAAudioControlWM8731 codecControl; diff --git a/examples/BA5_TGA_Pro_ExternalDelay_demo/BA5_TGA_Pro_ExternalDelay_demo.ino b/examples/BA5_TGA_Pro_ExternalDelay_demo/BA5_TGA_Pro_ExternalDelay_demo.ino index 717a9a5..1f3618f 100644 --- a/examples/BA5_TGA_Pro_ExternalDelay_demo/BA5_TGA_Pro_ExternalDelay_demo.ino +++ b/examples/BA5_TGA_Pro_ExternalDelay_demo/BA5_TGA_Pro_ExternalDelay_demo.ino @@ -14,7 +14,8 @@ #include "BAGuitar.h" -using namespace BAGuitar; +using namespace BAEffects; +using namespace BALibrary; AudioInputI2S i2sIn; AudioOutputI2S i2sOut; diff --git a/examples/Delay/AnalogDelayDemo/AnalogDelayDemo.ino b/examples/Delay/AnalogDelayDemo/AnalogDelayDemo.ino index bb6a910..d057c5a 100644 --- a/examples/Delay/AnalogDelayDemo/AnalogDelayDemo.ino +++ b/examples/Delay/AnalogDelayDemo/AnalogDelayDemo.ino @@ -2,7 +2,8 @@ #include "BAGuitar.h" using namespace midi; -using namespace BAGuitar; +using namespace BAEffects; +using namespace BALibrary; AudioInputI2S i2sIn; AudioOutputI2S i2sOut; diff --git a/src/AudioEffectAnalogDelay.h b/src/AudioEffectAnalogDelay.h index 49b6e90..e094a8d 100644 --- a/src/AudioEffectAnalogDelay.h +++ b/src/AudioEffectAnalogDelay.h @@ -22,13 +22,13 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H -#define __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H +#ifndef __BAEFFECTS_BAAUDIOEFFECTANALOGDELAY_H +#define __BAEFFECTS_BAAUDIOEFFECTANALOGDELAY_H #include #include "LibBasicFunctions.h" -namespace BAGuitar { +namespace BAEffects { /**************************************************************************//** * AudioEffectAnalogDelay models BBD based analog delays. It provides controls @@ -72,7 +72,7 @@ public: /// Construct an analog delay using external SPI via an ExtMemSlot. The amount of /// delay will be determined by the amount of memory in the slot. /// @param slot A pointer to the ExtMemSlot to use for the delay. - AudioEffectAnalogDelay(ExtMemSlot *slot); // requires sufficiently sized pre-allocated memory + AudioEffectAnalogDelay(BALibrary::ExtMemSlot *slot); // requires sufficiently sized pre-allocated memory virtual ~AudioEffectAnalogDelay(); ///< Destructor @@ -163,11 +163,11 @@ private: bool m_bypass = true; bool m_enable = false; bool m_externalMemory = false; - AudioDelay *m_memory = nullptr; + BALibrary::AudioDelay *m_memory = nullptr; size_t m_maxDelaySamples = 0; audio_block_t *m_previousBlock = nullptr; audio_block_t *m_blockToRelease = nullptr; - IirBiQuadFilterHQ *m_iir = nullptr; + BALibrary::IirBiQuadFilterHQ *m_iir = nullptr; // Controls int m_midiConfig[NUM_CONTROLS][2]; // stores the midi parameter mapping @@ -185,4 +185,4 @@ private: } -#endif /* __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H */ +#endif /* __BAEFFECTS_BAAUDIOEFFECTANALOGDELAY_H */ diff --git a/src/AudioEffectSOS.h b/src/AudioEffectSOS.h index 1a252a8..14b43bf 100644 --- a/src/AudioEffectSOS.h +++ b/src/AudioEffectSOS.h @@ -19,8 +19,8 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_BAAUDIOEFFECTSOS_H -#define __BAGUITAR_BAAUDIOEFFECTSOS_H +#ifndef __BAEFFECTS_BAAUDIOEFFECTSOS_H +#define __BAEFFECTS_BAAUDIOEFFECTSOS_H #include #include "LibBasicFunctions.h" @@ -53,7 +53,7 @@ public: /// Construct an analog delay using external SPI via an ExtMemSlot. The amount of /// delay will be determined by the amount of memory in the slot. /// @param slot A pointer to the ExtMemSlot to use for the delay. - AudioEffectSOS(BAGuitar::ExtMemSlot *slot); // requires sufficiently sized pre-allocated memory + AudioEffectSOS(BALibrary::ExtMemSlot *slot); // requires sufficiently sized pre-allocated memory virtual ~AudioEffectSOS(); ///< Destructor @@ -116,7 +116,7 @@ private: bool m_isOmni = false; bool m_bypass = true; bool m_enable = false; - BAGuitar::AudioDelay *m_memory = nullptr; + BALibrary::AudioDelay *m_memory = nullptr; bool m_externalMemory = true; audio_block_t *m_previousBlock = nullptr; audio_block_t *m_blockToRelease = nullptr; @@ -133,7 +133,7 @@ private: // Automated Controls BALibrary::ParameterAutomationSequence m_inputGateAuto = - BALibrary::ParameterAutomationSequence(3); + BALibrary::ParameterAutomationSequence(3); // Private functions void m_preProcessing (audio_block_t *out, audio_block_t *input, audio_block_t *delayedSignal); @@ -142,4 +142,4 @@ private: } -#endif /* __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H */ +#endif /* __BAEFFECTS_BAAUDIOEFFECTSOS_H */ diff --git a/src/BAAudioControlWM8731.h b/src/BAAudioControlWM8731.h index 18b3de9..6a38797 100644 --- a/src/BAAudioControlWM8731.h +++ b/src/BAAudioControlWM8731.h @@ -22,10 +22,10 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR__BAAUDIOCONTROLWM8731_H -#define __BAGUITAR__BAAUDIOCONTROLWM8731_H +#ifndef __BALIBRARY_BAAUDIOCONTROLWM8731_H +#define __BALIBRARY_BAAUDIOCONTROLWM8731_H -namespace BAGuitar { +namespace BALibrary { constexpr int WM8731_NUM_REGS = 10; // Number of registers in the internal shadow array @@ -126,6 +126,6 @@ private: }; -} /* namespace BAGuitar */ +} /* namespace BALibrary */ -#endif /* __BAGUITAR__BAAUDIOCONTROLWM8731_H */ +#endif /* __BALIBRARY_BAAUDIOCONTROLWM8731_H */ diff --git a/src/BAAudioEffectDelayExternal.h b/src/BAAudioEffectDelayExternal.h index 587efbf..9e88b21 100644 --- a/src/BAAudioEffectDelayExternal.h +++ b/src/BAAudioEffectDelayExternal.h @@ -22,15 +22,15 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H -#define __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H +#ifndef __BAEFFECTS_BAAUDIOEFFECTDELAYEXTERNAL_H +#define __BAEFFECTS_BAAUDIOEFFECTDELAYEXTERNAL_H #include #include "AudioStream.h" #include "BAHardware.h" -namespace BAGuitar { +namespace BAEffects { /**************************************************************************//** * BAAudioEffectDelayExternal can use external SPI RAM for delay rather than @@ -45,12 +45,12 @@ public: /// Specifiy which external memory to use /// @param type specify which memory to use - BAAudioEffectDelayExternal(BAGuitar::MemSelect type); + BAAudioEffectDelayExternal(BALibrary::MemSelect type); /// Specify external memory, and how much of the memory to use /// @param type specify which memory to use /// @param delayLengthMs maximum delay length in milliseconds - BAAudioEffectDelayExternal(BAGuitar::MemSelect type, float delayLengthMs); + BAAudioEffectDelayExternal(BALibrary::MemSelect type, float delayLengthMs); virtual ~BAAudioEffectDelayExternal(); /// set the actual amount of delay on a given delay tap @@ -67,7 +67,7 @@ public: static unsigned m_usingSPICount[2]; // internal use for all instances private: - void initialize(BAGuitar::MemSelect mem, unsigned delayLength = 1e6); + void initialize(BALibrary::MemSelect mem, unsigned delayLength = 1e6); void read(uint32_t address, uint32_t count, int16_t *data); void write(uint32_t address, uint32_t count, const int16_t *data); void zero(uint32_t address, uint32_t count); @@ -79,7 +79,7 @@ private: static unsigned m_allocated[2]; audio_block_t *m_inputQueueArray[1]; - BAGuitar::MemSelect m_mem; + BALibrary::MemSelect m_mem; SPIClass *m_spi = nullptr; int m_spiChannel = 0; int m_misoPin = 0; @@ -92,6 +92,6 @@ private: }; -} /* namespace BAGuitar */ +} /* namespace BAEffects */ -#endif /* __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H */ +#endif /* __BAEFFECTS_BAAUDIOEFFECTDELAYEXTERNAL_H */ diff --git a/src/BAGpio.h b/src/BAGpio.h index 53ade35..f7766af 100644 --- a/src/BAGpio.h +++ b/src/BAGpio.h @@ -20,12 +20,12 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_BAGPIO_H -#define __BAGUITAR_BAGPIO_H +#ifndef __BALIBRARY_BAGPIO_H +#define __BALIBRARY_BAGPIO_H #include "BAHardware.h" -namespace BAGuitar { +namespace BALibrary { /**************************************************************************//** * BAGpio provides a convince class to easily control the direction and state @@ -71,6 +71,6 @@ private: uint8_t m_ledState; }; -} /* namespace BAGuitar */ +} /* namespace BALibrary */ -#endif /* __BAGUITAR_BAGPIO_H */ +#endif /* __BALIBRARY_BAGPIO_H */ diff --git a/src/BAHardware.h b/src/BAHardware.h index bb3670d..a609731 100644 --- a/src/BAHardware.h +++ b/src/BAHardware.h @@ -20,15 +20,15 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUTIAR_BAHARDWARE_H -#define __BAGUTIAR_BAHARDWARE_H +#ifndef __BALIBRARY_BAHARDWARE_H +#define __BALIBRARY_BAHARDWARE_H #include /**************************************************************************//** * BAGuitar is a namespace/Library for Guitar processing from Blackaddr Audio. *****************************************************************************/ -namespace BAGuitar { +namespace BALibrary { // uncomment the line that corresponds to your hardware #define TGA_PRO_REVA @@ -93,7 +93,7 @@ constexpr size_t SPI_MEM1_MAX_AUDIO_SAMPLES = SPI_MEM1_SIZE_BYTES/sizeof(int16_t #endif -} // namespace BAGuitar +} // namespace BALibrary -#endif /* __BAGUTIAR_BAHARDWARE_H */ +#endif /* __BALIBRARY_BAHARDWARE_H */ diff --git a/src/BASpiMemory.h b/src/BASpiMemory.h index d5395bb..92e83fc 100644 --- a/src/BASpiMemory.h +++ b/src/BASpiMemory.h @@ -20,8 +20,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_BASPIMEMORY_H -#define __BAGUITAR_BASPIMEMORY_H +#ifndef __BALIBRARY_BASPIMEMORY_H +#define __BALIBRARY_BASPIMEMORY_H #include #include @@ -29,7 +29,7 @@ #include "BATypes.h" #include "BAHardware.h" -namespace BAGuitar { +namespace BALibrary { /**************************************************************************//** * This wrapper class uses the Arduino SPI (Wire) library to access the SPI ram. @@ -139,35 +139,41 @@ public: /// initialize and configure the SPI peripheral void begin() override; - /// Write a block of 8-bit data to the specified address + /// Write a block of 8-bit data to the specified address. Be check + /// isWriteBusy() before sending the next DMA transfer. /// @param address the address in the SPI RAM to write to /// @param src pointer to the source data block /// @param numBytes size of the data block in bytes void write(size_t address, uint8_t *src, size_t numBytes) override; - /// Write a block of zeros to the specified address + /// Write a block of zeros to the specified address. Be check + /// isWriteBusy() before sending the next DMA transfer. /// @param address the address in the SPI RAM to write to /// @param numBytes size of the data block in bytes void zero(size_t address, size_t numBytes) override; - /// Write a block of 16-bit data to the specified address + /// Write a block of 16-bit data to the specified address. Be check + /// isWriteBusy() before sending the next DMA transfer. /// @param address the address in the SPI RAM to write to /// @param src pointer to the source data block /// @param numWords size of the data block in 16-bit words void write16(size_t address, uint16_t *src, size_t numWords) override; - /// Write a block of 16-bit zeros to the specified address + /// Write a block of 16-bit zeros to the specified address. Be check + /// isWriteBusy() before sending the next DMA transfer. /// @param address the address in the SPI RAM to write to /// @param numWords size of the data block in 16-bit words void zero16(size_t address, size_t numWords) override; - /// Read a block of 8-bit data from the specified address + /// Read a block of 8-bit data from the specified address. Be check + /// isReadBusy() before sending the next DMA transfer. /// @param address the address in the SPI RAM to write to /// @param dest pointer to the destination /// @param numBytes size of the data block in bytes void read(size_t address, uint8_t *dest, size_t numBytes) override; - /// read a block 16-bit data word from the specified address + /// read a block 16-bit data word from the specified address. Be check + /// isReadBusy() before sending the next DMA transfer. /// @param address the address in the SPI RAM to read from /// @param dest the pointer to the destination /// @param numWords the number of 16-bit words to transfer @@ -210,6 +216,6 @@ private: }; -} /* namespace BAGuitar */ +} /* namespace BALibrary */ -#endif /* __BAGUITAR_BASPIMEMORY_H */ +#endif /* __BALIBRARY_BASPIMEMORY_H */ diff --git a/src/BATypes.h b/src/BATypes.h index 294cb42..01bdc1f 100644 --- a/src/BATypes.h +++ b/src/BATypes.h @@ -19,10 +19,10 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_BATYPES_H -#define __BAGUITAR_BATYPES_H +#ifndef __BALIBRARY_BATYPES_H +#define __BALIBRARY_BATYPES_H -namespace BAGuitar { +namespace BALibrary { #define UNUSED(x) (void)(x) @@ -152,7 +152,7 @@ private: const size_t m_maxSize; ///< maximum size of the queue }; -} // BAGuitar +} // BALibrary -#endif /* __BAGUITAR_BATYPES_H */ +#endif /* __BALIBRARY_BATYPES_H */ diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 381b2f8..25439c2 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -30,10 +30,10 @@ #include "BATypes.h" #include "LibMemoryManagement.h" -#ifndef __BAGUITAR_LIBBASICFUNCTIONS_H -#define __BAGUITAR_LIBBASICFUNCTIONS_H +#ifndef __BALIBRARY_LIBBASICFUNCTIONS_H +#define __BALIBRARY_LIBBASICFUNCTIONS_H -namespace BAGuitar { +namespace BALibrary { /**************************************************************************//** * QueuePosition is used for storing the index (in an array of queues) and the @@ -134,7 +134,7 @@ public: /// @param maxDelayTimeMs max length of time you want in the buffer specified in milliseconds AudioDelay(float maxDelayTimeMs); - /// Construct an audio buffer using a slot configured with the BAGuitar::ExternalSramManager + /// Construct an audio buffer using a slot configured with the BALibrary::ExternalSramManager /// @param slot a pointer to the slot representing the memory you wish to use for the buffer. AudioDelay(ExtMemSlot *slot); @@ -312,7 +312,7 @@ private: }; -} // namespace BAGuitar +} // namespace BALibrary namespace BALibrary { @@ -399,4 +399,4 @@ private: } // BALibrary -#endif /* __BAGUITAR_LIBBASICFUNCTIONS_H */ +#endif /* __BALIBRARY_LIBBASICFUNCTIONS_H */ diff --git a/src/LibMemoryManagement.h b/src/LibMemoryManagement.h index bb450d1..288e581 100644 --- a/src/LibMemoryManagement.h +++ b/src/LibMemoryManagement.h @@ -26,15 +26,15 @@ * along with this program. If not, see . *****************************************************************************/ -#ifndef __BAGUITAR_LIBMEMORYMANAGEMENT_H -#define __BAGUITAR_LIBMEMORYMANAGEMENT_H +#ifndef __BALIBRARY_LIBMEMORYMANAGEMENT_H +#define __BALIBRARY_LIBMEMORYMANAGEMENT_H #include #include "BAHardware.h" #include "BASpiMemory.h" -namespace BAGuitar { +namespace BALibrary { /**************************************************************************//** * MemConfig contains the configuration information associated with a particular @@ -182,7 +182,7 @@ public: /// @details note that currently, memory cannot be allocated. /// @param mem specifies which memory to query, default is memory 0 /// @returns the available memory in bytes - size_t availableMemory(BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0); + size_t availableMemory(BALibrary::MemSelect mem = BALibrary::MemSelect::MEM0); /// Request memory be allocated for the provided slot /// @param slot a pointer to the global slot object to which memory will be allocated @@ -190,7 +190,7 @@ public: /// @param mem specify which external memory to allocate from /// @param useDma when true, DMA is used for SPI port, else transfers block until complete /// @returns true on success, otherwise false on error - bool requestMemory(ExtMemSlot *slot, float delayMilliseconds, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0, bool useDma = false); + bool requestMemory(ExtMemSlot *slot, float delayMilliseconds, BALibrary::MemSelect mem = BALibrary::MemSelect::MEM0, bool useDma = false); /// Request memory be allocated for the provided slot /// @param slot a pointer to the global slot object to which memory will be allocated @@ -198,15 +198,15 @@ public: /// @param mem specify which external memory to allocate from /// @param useDma when true, DMA is used for SPI port, else transfers block until complete /// @returns true on success, otherwise false on error - bool requestMemory(ExtMemSlot *slot, size_t sizeBytes, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0, bool useDma = false); + bool requestMemory(ExtMemSlot *slot, size_t sizeBytes, BALibrary::MemSelect mem = BALibrary::MemSelect::MEM0, bool useDma = false); private: static bool m_configured; ///< there should only be one instance of ExternalSramManager in the whole project - static MemConfig m_memConfig[BAGuitar::NUM_MEM_SLOTS]; ///< store the configuration information for each external memory + static MemConfig m_memConfig[BALibrary::NUM_MEM_SLOTS]; ///< store the configuration information for each external memory }; -} +} // BALibrary -#endif /* __LIBMEMORYMANAGEMENT_H */ +#endif /* __BALIBRARY_LIBMEMORYMANAGEMENT_H */ diff --git a/src/common/AudioDelay.cpp b/src/common/AudioDelay.cpp index f830cc4..9fec4ea 100644 --- a/src/common/AudioDelay.cpp +++ b/src/common/AudioDelay.cpp @@ -21,7 +21,7 @@ #include "Audio.h" #include "LibBasicFunctions.h" -namespace BAGuitar { +namespace BALibrary { //////////////////////////////////////////////////// // AudioDelay diff --git a/src/common/AudioHelpers.cpp b/src/common/AudioHelpers.cpp index 785e5db..49e2a31 100644 --- a/src/common/AudioHelpers.cpp +++ b/src/common/AudioHelpers.cpp @@ -21,7 +21,7 @@ #include "Audio.h" #include "LibBasicFunctions.h" -namespace BAGuitar { +namespace BALibrary { size_t calcAudioSamples(float milliseconds) { diff --git a/src/common/ExtMemSlot.cpp b/src/common/ExtMemSlot.cpp index 57385d7..c857cbe 100644 --- a/src/common/ExtMemSlot.cpp +++ b/src/common/ExtMemSlot.cpp @@ -23,7 +23,7 @@ #include "Audio.h" #include "LibMemoryManagement.h" -namespace BAGuitar { +namespace BALibrary { ///////////////////////////////////////////////////////////////////////////// // MEM SLOT diff --git a/src/common/ExternalSramManager.cpp b/src/common/ExternalSramManager.cpp index 98465fc..7083e0c 100644 --- a/src/common/ExternalSramManager.cpp +++ b/src/common/ExternalSramManager.cpp @@ -23,13 +23,13 @@ #include "Audio.h" #include "LibMemoryManagement.h" -namespace BAGuitar { +namespace BALibrary { ///////////////////////////////////////////////////////////////////////////// // EXTERNAL SRAM MANAGER ///////////////////////////////////////////////////////////////////////////// bool ExternalSramManager::m_configured = false; -MemConfig ExternalSramManager::m_memConfig[BAGuitar::NUM_MEM_SLOTS]; +MemConfig ExternalSramManager::m_memConfig[BALibrary::NUM_MEM_SLOTS]; ExternalSramManager::ExternalSramManager(unsigned numMemories) @@ -60,19 +60,19 @@ ExternalSramManager::~ExternalSramManager() } } -size_t ExternalSramManager::availableMemory(BAGuitar::MemSelect mem) +size_t ExternalSramManager::availableMemory(BALibrary::MemSelect mem) { return m_memConfig[mem].totalAvailable; } -bool ExternalSramManager::requestMemory(ExtMemSlot *slot, float delayMilliseconds, BAGuitar::MemSelect mem, bool useDma) +bool ExternalSramManager::requestMemory(ExtMemSlot *slot, float delayMilliseconds, BALibrary::MemSelect mem, bool useDma) { // convert the time to numer of samples size_t delayLengthInt = (size_t)((delayMilliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); return requestMemory(slot, delayLengthInt * sizeof(int16_t), mem, useDma); } -bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BAGuitar::MemSelect mem, bool useDma) +bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BALibrary::MemSelect mem, bool useDma) { if (m_memConfig[mem].totalAvailable >= sizeBytes) { @@ -86,10 +86,10 @@ bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BAGu if (!m_memConfig[mem].m_spi) { if (useDma) { - m_memConfig[mem].m_spi = new BAGuitar::BASpiMemoryDMA(static_cast(mem)); + m_memConfig[mem].m_spi = new BALibrary::BASpiMemoryDMA(static_cast(mem)); slot->m_useDma = true; } else { - m_memConfig[mem].m_spi = new BAGuitar::BASpiMemory(static_cast(mem)); + m_memConfig[mem].m_spi = new BALibrary::BASpiMemory(static_cast(mem)); slot->m_useDma = false; } if (!m_memConfig[mem].m_spi) { diff --git a/src/common/IirBiquadFilter.cpp b/src/common/IirBiquadFilter.cpp index f2ae1c7..82eec80 100644 --- a/src/common/IirBiquadFilter.cpp +++ b/src/common/IirBiquadFilter.cpp @@ -21,7 +21,7 @@ #include "Audio.h" #include "LibBasicFunctions.h" -namespace BAGuitar { +namespace BALibrary { //////////////////////////////////////////////////// // IirBiQuadFilter diff --git a/src/common/ParameterAutomation.cpp b/src/common/ParameterAutomation.cpp index deb9c3d..bc3e4d2 100644 --- a/src/common/ParameterAutomation.cpp +++ b/src/common/ParameterAutomation.cpp @@ -20,8 +20,6 @@ #include "LibBasicFunctions.h" -using namespace BAGuitar; - namespace BALibrary { /////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/AudioEffectAnalogDelay.cpp b/src/effects/AudioEffectAnalogDelay.cpp index 5a15d4d..52a0438 100644 --- a/src/effects/AudioEffectAnalogDelay.cpp +++ b/src/effects/AudioEffectAnalogDelay.cpp @@ -8,7 +8,9 @@ #include "AudioEffectAnalogDelayFilters.h" #include "AudioEffectAnalogDelay.h" -namespace BAGuitar { +using namespace BALibrary; + +namespace BAEffects { constexpr int MIDI_CHANNEL = 0; constexpr int MIDI_CONTROL = 1; diff --git a/src/effects/AudioEffectAnalogDelayFilters.h b/src/effects/AudioEffectAnalogDelayFilters.h index 71f70bd..473f9cd 100644 --- a/src/effects/AudioEffectAnalogDelayFilters.h +++ b/src/effects/AudioEffectAnalogDelayFilters.h @@ -21,7 +21,7 @@ *****************************************************************************/ #include -namespace BAGuitar { +namespace BAEffects { // The number of stages in the analog-response Biquad filter constexpr unsigned MAX_NUM_FILTER_STAGES = 4; diff --git a/src/effects/AudioEffectSOS.cpp b/src/effects/AudioEffectSOS.cpp index 594fd6d..40ae3a6 100644 --- a/src/effects/AudioEffectSOS.cpp +++ b/src/effects/AudioEffectSOS.cpp @@ -8,7 +8,6 @@ #include "AudioEffectSOS.h" #include "LibBasicFunctions.h" -using namespace BAGuitar; using namespace BALibrary; namespace BAEffects { diff --git a/src/effects/BAAudioEffectDelayExternal.cpp b/src/effects/BAAudioEffectDelayExternal.cpp index acdf3d2..1f1c215 100644 --- a/src/effects/BAAudioEffectDelayExternal.cpp +++ b/src/effects/BAAudioEffectDelayExternal.cpp @@ -20,7 +20,9 @@ #include "BAAudioEffectDelayExternal.h" -namespace BAGuitar { +using namespace BALibrary; + +namespace BAEffects { #define SPISETTING SPISettings(20000000, MSBFIRST, SPI_MODE0) @@ -49,7 +51,7 @@ BAAudioEffectDelayExternal::BAAudioEffectDelayExternal(MemSelect mem) initialize(mem); } -BAAudioEffectDelayExternal::BAAudioEffectDelayExternal(BAGuitar::MemSelect type, float delayLengthMs) +BAAudioEffectDelayExternal::BAAudioEffectDelayExternal(BALibrary::MemSelect type, float delayLengthMs) : AudioStream(1, m_inputQueueArray) { unsigned delayLengthInt = (delayLengthMs*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f; @@ -289,4 +291,4 @@ inline void BAAudioEffectDelayExternal::m_stopUsingSPI(int spiBus) { #endif -} /* namespace BAGuitar */ +} /* namespace BAEffects */ diff --git a/src/peripherals/BAAudioControlWM8731.cpp b/src/peripherals/BAAudioControlWM8731.cpp index f5b2ae4..e1960e3 100644 --- a/src/peripherals/BAAudioControlWM8731.cpp +++ b/src/peripherals/BAAudioControlWM8731.cpp @@ -21,7 +21,7 @@ #include #include "BAAudioControlWM8731.h" -namespace BAGuitar { +namespace BALibrary { // use const instead of define for proper scoping constexpr int WM8731_I2C_ADDR = 0x1A; @@ -341,4 +341,4 @@ bool BAAudioControlWM8731::write(unsigned int reg, unsigned int val) return true; } -} /* namespace BAGuitar */ +} /* namespace BALibrary */ diff --git a/src/peripherals/BAGpio.cpp b/src/peripherals/BAGpio.cpp index ccf09c4..e2d3c79 100644 --- a/src/peripherals/BAGpio.cpp +++ b/src/peripherals/BAGpio.cpp @@ -21,7 +21,7 @@ #include "Arduino.h" #include "BAGpio.h" -namespace BAGuitar { +namespace BALibrary { BAGpio::BAGpio() { @@ -85,4 +85,4 @@ int BAGpio::toggleLed() } -} /* namespace BAGuitar */ +} /* namespace BALibrary */ diff --git a/src/peripherals/BASpiMemory.cpp b/src/peripherals/BASpiMemory.cpp index 05d006d..d91d7a7 100644 --- a/src/peripherals/BASpiMemory.cpp +++ b/src/peripherals/BASpiMemory.cpp @@ -21,7 +21,7 @@ #include "Arduino.h" #include "BASpiMemory.h" -namespace BAGuitar { +namespace BALibrary { // MEM0 Settings constexpr int SPI_CS_MEM0 = 15; @@ -468,4 +468,4 @@ bool BASpiMemoryDMA::isReadBusy(void) const return (m_rxTransfer[0].busy() or m_rxTransfer[1].busy()); } -} /* namespace BAGuitar */ +} /* namespace BALibrary */ From ba4f138d4caeca451df5d74ceff37a2ee72f3e2b Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Mon, 16 Jul 2018 20:02:58 -0400 Subject: [PATCH 7/7] 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);