Merge pull request #5 from Blackaddr/feature/layout_change

Library structure layout change, new classes and effects for ParamterAutomation and SoundOnSound
pull/1/head
Blackaddr Audio 6 years ago committed by GitHub
commit 5c4be4bc2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      examples/BA1_TGA_Pro_demo/BA1_TGA_Pro_demo.ino
  2. 3
      examples/BA2_TGA_Pro_1MEM/BA2_TGA_Pro_1MEM.ino
  3. 3
      examples/BA3_TGA_Pro_2MEM/BA3_TGA_Pro_2MEM.ino
  4. 3
      examples/BA4_TGA_Pro_delay_reverb/BA4_TGA_Pro_delay_reverb.ino
  5. 3
      examples/BA5_TGA_Pro_ExternalDelay_demo/BA5_TGA_Pro_ExternalDelay_demo.ino
  6. 3
      examples/Delay/AnalogDelayDemo/AnalogDelayDemo.ino
  7. 191
      examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino
  8. 19
      examples/Delay/SoundOnSoundDemo/name.c
  9. 14
      src/AudioEffectAnalogDelay.h
  10. 146
      src/AudioEffectSOS.h
  11. 10
      src/BAAudioControlWM8731.h
  12. 18
      src/BAAudioEffectDelayExternal.h
  13. 10
      src/BAGpio.h
  14. 1
      src/BAGuitar.h
  15. 18
      src/BAHardware.h
  16. 28
      src/BASpiMemory.h
  17. 10
      src/BATypes.h
  18. 115
      src/LibBasicFunctions.h
  19. 18
      src/LibMemoryManagement.h
  20. 16
      src/common/AudioDelay.cpp
  21. 7
      src/common/AudioHelpers.cpp
  22. 2
      src/common/ExtMemSlot.cpp
  23. 22
      src/common/ExternalSramManager.cpp
  24. 2
      src/common/IirBiquadFilter.cpp
  25. 241
      src/common/ParameterAutomation.cpp
  26. 9
      src/effects/AudioEffectAnalogDelay.cpp
  27. 2
      src/effects/AudioEffectAnalogDelayFilters.h
  28. 304
      src/effects/AudioEffectSOS.cpp
  29. 8
      src/effects/BAAudioEffectDelayExternal.cpp
  30. 4
      src/peripherals/BAAudioControlWM8731.cpp
  31. 4
      src/peripherals/BAGpio.cpp
  32. 4
      src/peripherals/BASpiMemory.cpp

@ -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;

@ -23,7 +23,8 @@
MIDI_CREATE_DEFAULT_INSTANCE();
using namespace midi;
using namespace BAGuitar;
using namespace BALibrary;
using namespace BAEffects;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;

@ -24,7 +24,8 @@
MIDI_CREATE_DEFAULT_INSTANCE();
using namespace midi;
using namespace BAGuitar;
using namespace BAEffects;
using namespace BALibrary;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;

@ -14,7 +14,8 @@
#include <MIDI.h>
#include "BAGuitar.h"
using namespace BAGuitar;
using namespace BAEffects;
using namespace BALibrary;
BAAudioControlWM8731 codecControl;

@ -14,7 +14,8 @@
#include "BAGuitar.h"
using namespace BAGuitar;
using namespace BAEffects;
using namespace BALibrary;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;

@ -2,7 +2,8 @@
#include "BAGuitar.h"
using namespace midi;
using namespace BAGuitar;
using namespace BAEffects;
using namespace BALibrary;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;

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

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

@ -22,13 +22,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H
#define __BAGUITAR_BAAUDIOEFFECTANALOGDELAY_H
#ifndef __BAEFFECTS_BAAUDIOEFFECTANALOGDELAY_H
#define __BAEFFECTS_BAAUDIOEFFECTANALOGDELAY_H
#include <Audio.h>
#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 */

@ -0,0 +1,146 @@
/**************************************************************************//**
* @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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAEFFECTS_BAAUDIOEFFECTSOS_H
#define __BAEFFECTS_BAAUDIOEFFECTSOS_H
#include <Audio.h>
#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_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
};
// *** 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.
/// @param slot A pointer to the ExtMemSlot to use for the delay.
AudioEffectSOS(BALibrary::ExtMemSlot *slot); // requires sufficiently sized pre-allocated memory
virtual ~AudioEffectSOS(); ///< Destructor
void setGateLedGpio(int pinId);
// *** 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; }
/// 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
void volume(float vol) {m_volume = vol; }
// ** ENABLE / DISABLE **
/// Enables audio processing. Note: when not enabled, CPU load is nearly zero.
void enable();
/// 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;
BALibrary::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;
int m_gateLedPinId = -1;
// 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::ParameterAutomationSequence<float> m_inputGateAuto = BALibrary::ParameterAutomationSequence<float>(3);
BALibrary::ParameterAutomationSequence<float> m_clearFeedbackAuto = BALibrary::ParameterAutomationSequence<float>(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 *input);
};
}
#endif /* __BAEFFECTS_BAAUDIOEFFECTSOS_H */

@ -22,10 +22,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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 */

@ -22,15 +22,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H
#define __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H
#ifndef __BAEFFECTS_BAAUDIOEFFECTDELAYEXTERNAL_H
#define __BAEFFECTS_BAAUDIOEFFECTDELAYEXTERNAL_H
#include <Audio.h>
#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 */

@ -20,12 +20,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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 */

@ -29,6 +29,7 @@
#include "BAGpio.h"
#include "BAAudioEffectDelayExternal.h"
#include "AudioEffectAnalogDelay.h"
#include "AudioEffectSOS.h"
#include "LibBasicFunctions.h"
#include "LibMemoryManagement.h"

@ -20,15 +20,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAGUTIAR_BAHARDWARE_H
#define __BAGUTIAR_BAHARDWARE_H
#ifndef __BALIBRARY_BAHARDWARE_H
#define __BALIBRARY_BAHARDWARE_H
#include <cstdint>
/**************************************************************************//**
* 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
@ -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
@ -87,7 +93,7 @@ constexpr int SPI_MAX_ADDR = 131071; ///< Max address size per chip
#endif
} // namespace BAGuitar
} // namespace BALibrary
#endif /* __BAGUTIAR_BAHARDWARE_H */
#endif /* __BALIBRARY_BAHARDWARE_H */

@ -20,8 +20,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAGUITAR_BASPIMEMORY_H
#define __BAGUITAR_BASPIMEMORY_H
#ifndef __BALIBRARY_BASPIMEMORY_H
#define __BALIBRARY_BASPIMEMORY_H
#include <SPI.h>
#include <DmaSpi.h>
@ -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 */

@ -19,10 +19,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#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 */

@ -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
@ -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 T>
class RingBuffer; // forward declare so AudioDelay can use it.
@ -128,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);
@ -147,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.
@ -161,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
@ -177,6 +191,7 @@ private:
MemType m_type; ///< when 0, INTERNAL memory, when 1, external MEMORY.
RingBuffer<audio_block_t *> *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.
};
/**************************************************************************//**
@ -246,7 +261,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 +312,91 @@ private:
};
}
} // namespace BALibrary
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 <typename T>
class ParameterAutomation
{
public:
enum class Function : unsigned {
NOT_CONFIGURED = 0, ///< Initial, unconfigured stage
HOLD, ///< f(x) = constant
LINEAR, ///< f(x) = x
EXPONENTIAL, ///< f(x) = exp(-k*x)
LOGARITHMIC, ///< f(x) =
PARABOLIC, ///< f(x) = x^2
LOOKUP_TABLE ///< f(x) = lut(x)
};
ParameterAutomation();
ParameterAutomation(T startValue, T endValue, size_t durationSamples, Function function = Function::LINEAR);
ParameterAutomation(T startValue, T endValue, float durationMilliseconds, Function function = Function::LINEAR);
virtual ~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();
bool isFinished() { return !m_running; }
private:
Function m_function;
T m_startValue;
T m_endValue;
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_slopeX;
float m_scaleY;
bool m_positiveSlope = true;
};
// 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 <typename T>
class ParameterAutomationSequence
{
public:
ParameterAutomationSequence() = delete;
ParameterAutomationSequence(int numStages);
virtual ~ParameterAutomationSequence();
void setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation<T>::Function function);
void setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation<T>::Function function);
/// Trigger a the automation sequence until numStages is reached or a Function is ParameterAutomation<T>::Function::NOT_CONFIGURED
void trigger();
T getNextValue();
bool isFinished();
private:
ParameterAutomation<T> *m_paramArray[MAX_PARAMETER_SEQUENCES];
int m_currentIndex = 0;
int m_numStages = 0;
bool m_running = false;
};
} // BALibrary
#endif /* __BAGUITAR_LIBBASICFUNCTIONS_H */
#endif /* __BALIBRARY_LIBBASICFUNCTIONS_H */

@ -26,15 +26,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAGUITAR_LIBMEMORYMANAGEMENT_H
#define __BAGUITAR_LIBMEMORYMANAGEMENT_H
#ifndef __BALIBRARY_LIBMEMORYMANAGEMENT_H
#define __BALIBRARY_LIBMEMORYMANAGEMENT_H
#include <cstddef>
#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 */

@ -21,7 +21,7 @@
#include "Audio.h"
#include "LibBasicFunctions.h"
namespace BAGuitar {
namespace BALibrary {
////////////////////////////////////////////////////
// AudioDelay
@ -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<audio_block_t *>(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,15 @@ audio_block_t* AudioDelay::getBlock(size_t index)
return ret;
}
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;
}
bool AudioDelay::getSamples(audio_block_t *dest, size_t offsetSamples, size_t numSamples)
{
if (!dest) {
@ -159,8 +170,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;
}
}

@ -21,7 +21,7 @@
#include "Audio.h"
#include "LibBasicFunctions.h"
namespace BAGuitar {
namespace BALibrary {
size_t calcAudioSamples(float milliseconds)
{
@ -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);

@ -23,7 +23,7 @@
#include "Audio.h"
#include "LibMemoryManagement.h"
namespace BAGuitar {
namespace BALibrary {
/////////////////////////////////////////////////////////////////////////////
// MEM SLOT

@ -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)
@ -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;
@ -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<BAGuitar::SpiDeviceId>(mem));
m_memConfig[mem].m_spi = new BALibrary::BASpiMemoryDMA(static_cast<BALibrary::SpiDeviceId>(mem));
slot->m_useDma = true;
} else {
m_memConfig[mem].m_spi = new BAGuitar::BASpiMemory(static_cast<BAGuitar::SpiDeviceId>(mem));
m_memConfig[mem].m_spi = new BALibrary::BASpiMemory(static_cast<BALibrary::SpiDeviceId>(mem));
slot->m_useDma = false;
}
if (!m_memConfig[mem].m_spi) {
@ -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;
}
}

@ -21,7 +21,7 @@
#include "Audio.h"
#include "LibBasicFunctions.h"
namespace BAGuitar {
namespace BALibrary {
////////////////////////////////////////////////////
// IirBiQuadFilter

@ -0,0 +1,241 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "LibBasicFunctions.h"
namespace BALibrary {
///////////////////////////////////////////////////////////////////////////////
// ParameterAutomation
///////////////////////////////////////////////////////////////////////////////
constexpr int LINEAR_SLOPE = 0;
constexpr float EXPONENTIAL_K = 5.0f;
constexpr float EXP_EXPONENTIAL_K = expf(EXPONENTIAL_K);
template <class T>
ParameterAutomation<T>::ParameterAutomation()
{
reconfigure(0.0f, 0.0f, static_cast<size_t>(0), Function::NOT_CONFIGURED);
}
template <class T>
ParameterAutomation<T>::ParameterAutomation(T startValue, T endValue, float durationMilliseconds, Function function)
{
reconfigure(startValue, endValue, calcAudioSamples(durationMilliseconds), function);
}
template <class T>
ParameterAutomation<T>::ParameterAutomation(T startValue, T endValue, size_t durationSamples, Function function)
{
reconfigure(startValue, endValue, durationSamples, function);
}
template <class T>
ParameterAutomation<T>::~ParameterAutomation()
{
}
template <class T>
void ParameterAutomation<T>::reconfigure(T startValue, T endValue, float durationMilliseconds, Function function)
{
reconfigure(startValue, endValue, calcAudioSamples(durationMilliseconds), function);
}
template <class T>
void ParameterAutomation<T>::reconfigure(T startValue, T endValue, size_t durationSamples, Function function)
{
m_function = function;
m_startValue = startValue;
m_endValue = endValue;
m_currentValueX = 0.0f;
m_duration = durationSamples;
m_running = false;
float duration = m_duration / static_cast<float>(AUDIO_BLOCK_SAMPLES);
m_slopeX = (1.0f / static_cast<float>(duration));
m_scaleY = abs(endValue - startValue);
if (endValue >= startValue) {
// value is increasing
m_positiveSlope = true;
} else {
// value is decreasing
m_positiveSlope = false;
}
}
template <class T>
void ParameterAutomation<T>::trigger()
{
m_currentValueX = 0.0f;
m_running = true;
}
template <class T>
T ParameterAutomation<T>::getNextValue()
{
if (m_running == false) {
return m_startValue;
}
m_currentValueX += m_slopeX;
float value;
float returnValue;
switch(m_function) {
case Function::EXPONENTIAL :
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;
break;
case Function::LOOKUP_TABLE :
case Function::LINEAR :
default :
value = m_currentValueX;
break;
}
// Check if the automation is finished.
if (m_currentValueX >= 1.0f) {
m_currentValueX = 0.0f;
m_running = false;
return m_endValue;
}
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);
return returnValue;
}
// Template instantiation
template class ParameterAutomation<float>;
template class ParameterAutomation<int>;
template class ParameterAutomation<unsigned>;
///////////////////////////////////////////////////////////////////////////////
// ParameterAutomationSequence
///////////////////////////////////////////////////////////////////////////////
template <class T>
ParameterAutomationSequence<T>::ParameterAutomationSequence(int numStages)
{
if (numStages < MAX_PARAMETER_SEQUENCES) {
for (int i=0; i<numStages; i++) {
m_paramArray[i] = new ParameterAutomation<T>();
}
for (int i=numStages; i<MAX_PARAMETER_SEQUENCES; i++) {
m_paramArray[i] = nullptr;
}
m_numStages = numStages;
}
}
template <class T>
ParameterAutomationSequence<T>::~ParameterAutomationSequence()
{
for (int i=0; i<m_numStages; i++) {
if (m_paramArray[i]) {
delete m_paramArray[i];
}
}
}
template <class T>
void ParameterAutomationSequence<T>::setupParameter(int index, T startValue, T endValue, size_t durationSamples, typename ParameterAutomation<T>::Function function)
{
Serial.println(String("setupParameter() called with samples: ") + durationSamples);
m_paramArray[index]->reconfigure(startValue, endValue, durationSamples, function);
m_currentIndex = 0;
}
template <class T>
void ParameterAutomationSequence<T>::setupParameter(int index, T startValue, T endValue, float durationMilliseconds, typename ParameterAutomation<T>::Function function)
{
Serial.print(String("setupParameter() called with time: ")); Serial.println(durationMilliseconds, 6);
m_paramArray[index]->reconfigure(startValue, endValue, durationMilliseconds, function);
m_currentIndex = 0;
}
template <class T>
void ParameterAutomationSequence<T>::trigger(void)
{
m_currentIndex = 0;
m_paramArray[0]->trigger();
m_running = true;
//Serial.println("ParameterAutomationSequence<T>::trigger() called");
}
template <class T>
T ParameterAutomationSequence<T>::getNextValue()
{
// Get the next value
T nextValue = m_paramArray[m_currentIndex]->getNextValue();
if (m_running) {
//Serial.println(String("ParameterAutomationSequence<T>::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
Serial.println("Last stage finished");
m_running = false;
m_currentIndex = 0;
} else {
// trigger the next stage
m_paramArray[m_currentIndex]->trigger();
}
}
}
return nextValue;
}
template <class T>
bool ParameterAutomationSequence<T>::isFinished()
{
return !m_running;
}
// Template instantiation
template class ParameterAutomationSequence<float>;
template class ParameterAutomationSequence<int>;
template class ParameterAutomationSequence<unsigned>;
}

@ -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;
@ -166,6 +168,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) {

@ -21,7 +21,7 @@
*****************************************************************************/
#include <cstdint>
namespace BAGuitar {
namespace BAEffects {
// The number of stages in the analog-response Biquad filter
constexpr unsigned MAX_NUM_FILTER_STAGES = 4;

@ -0,0 +1,304 @@
/*
* AudioEffectSOS.cpp
*
* Created on: Apr 14, 2018
* Author: blackaddr
*/
#include "AudioEffectSOS.h"
#include "LibBasicFunctions.h"
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 = 1000.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_externalMemory = true;
}
AudioEffectSOS::~AudioEffectSOS()
{
if (m_memory) delete m_memory;
}
void AudioEffectSOS::setGateLedGpio(int pinId)
{
m_gateLedPinId = pinId;
pinMode(static_cast<uint8_t>(m_gateLedPinId), OUTPUT);
}
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<float>::Function::EXPONENTIAL);
m_inputGateAuto.setupParameter(GATE_HOLD_STAGE, 1.0f, 1.0f, m_maxDelaySamples, ParameterAutomation<float>::Function::HOLD);
m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
m_clearFeedbackAuto.setupParameter(GATE_OPEN_STAGE, 1.0f, 0.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
m_clearFeedbackAuto.setupParameter(GATE_HOLD_STAGE, 0.0f, 0.0f, m_maxDelaySamples, ParameterAutomation<float>::Function::HOLD);
m_clearFeedbackAuto.setupParameter(GATE_CLOSE_STAGE, 0.0f, 1.0f, 1000.0f, ParameterAutomation<float>::Function::EXPONENTIAL);
}
void AudioEffectSOS::update(void)
{
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) || (!inputAudioBlock) ) {
// 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;
}
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.
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);
//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
// 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);
//audio_block_t *blockToRelease = m_memory->addBlock(inputAudioBlock);
//Serial.println("Done adding new block");
// 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, blockToOutput);
transmit(blockToOutput);
release(inputAudioBlock);
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;
}
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<float>::Function::EXPONENTIAL);
//m_clearFeedbackAuto.setupParameter(GATE_OPEN_STAGE, 1.0f, 0.0f, m_openTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
}
void AudioEffectSOS::gateCloseTime(float milliseconds)
{
m_closeTimeMs = milliseconds;
m_inputGateAuto.setupParameter(GATE_CLOSE_STAGE, 1.0f, 0.0f, m_closeTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
//m_clearFeedbackAuto.setupParameter(GATE_CLOSE_STAGE, 0.0f, 1.0f, m_closeTimeMs, ParameterAutomation<float>::Function::EXPONENTIAL);
}
////////////////////////////////////////////////////////////////////////
// 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_closeTimeMs);
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 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)
{
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 combine the two
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*feedbackAdjust, 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);
}
// Update the gate LED
if (m_gateLedPinId >= 0) {
if (m_inputGateAuto.isFinished() && m_clearFeedbackAuto.isFinished()) {
digitalWriteFast(m_gateLedPinId, 0x0);
} else {
digitalWriteFast(m_gateLedPinId, 0x1);
}
}
}
void AudioEffectSOS::m_postProcessing(audio_block_t *out, audio_block_t *in)
{
gainAdjust(out, out, m_volume, 0);
}
} // namespace BAEffects

@ -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 */

@ -21,7 +21,7 @@
#include <Wire.h>
#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 */

@ -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 */

@ -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 */

Loading…
Cancel
Save