Further development, got all controls working, still need to filter pots

pull/1/head
Steve Lascos 6 years ago
parent 5c4be4bc2a
commit 10818bcd78
  1. 67
      examples/BAExpansionCalibrate/BAExpansionCalibrate.ino
  2. 113
      examples/Delay/AnalogDelayDemoExpansion/AnalogDelayDemoExpansion.ino
  3. 19
      examples/Delay/AnalogDelayDemoExpansion/name.c
  4. 1
      src/BAGuitar.h
  5. 24
      src/BAHardware.h
  6. 148
      src/BAPhysicalControls.h
  7. 220
      src/peripherals/BAPhysicalControls.cpp
  8. 6
      src/peripherals/BASpiMemory.cpp

@ -0,0 +1,67 @@
#define TGA_PRO_REVB
#define TGA_PRO_EXPAND_REV2
#include "BAGuitar.h"
using namespace BALibrary;
// Create physical controls for Expansion Board, 2 switches, 3 pots, 0 encoders, 2 LEDs
BAPhysicalControls controls(BA_EXPAND_NUM_SW, BA_EXPAND_NUM_POT, 0, BA_EXPAND_NUM_LED);
void setup() {
delay(100);
Serial.begin(57600);
delay(500); // long delay to wait for Serial to init
// put your setup code here, to run once:
Serial.println("Calibrating POT1");
Potentiometer::Calib pot1Calib = Potentiometer::calibrate(BA_EXPAND_POT1_PIN);
if (pot1Calib.min == pot1Calib.max) { Serial.println("\n!!! The knob didn't appear to move. Are you SURE you're turning the right knob? !!!"); }
Serial.println("\nCalibrating POT2");
Potentiometer::Calib pot2Calib = Potentiometer::calibrate(BA_EXPAND_POT2_PIN);
if (pot2Calib.min == pot2Calib.max) { Serial.println("\n!!! The knob didn't appear to move. Are you SURE you're turning the right knob? !!!"); }
Serial.println("\nCalibrating POT3");
Potentiometer::Calib pot3Calib = Potentiometer::calibrate(BA_EXPAND_POT3_PIN);
if (pot3Calib.min == pot3Calib.max) { Serial.println("\n!!! The knob didn't appear to move. Are you SURE you're turning the right knob? !!!"); }
// Create the controls using the calib values
controls.addPot(BA_EXPAND_POT1_PIN, pot1Calib.min, pot1Calib.max, pot1Calib.swap);
controls.addPot(BA_EXPAND_POT2_PIN, pot2Calib.min, pot2Calib.max, pot2Calib.swap);
controls.addPot(BA_EXPAND_POT3_PIN, pot3Calib.min, pot3Calib.max, pot3Calib.swap);
// Add the pushbuttons
controls.addSwitch(BA_EXPAND_SW1_PIN);
controls.addSwitch(BA_EXPAND_SW2_PIN);
// Setup the LEDs
controls.addOutput(BA_EXPAND_LED1_PIN);
controls.setOutput(BA_EXPAND_LED1_PIN, 0);
controls.addOutput(BA_EXPAND_LED2_PIN);
controls.setOutput(BA_EXPAND_LED2_PIN, 0);
Serial.println("DONE SETUP! Try turning knobs and pushing buttons!\n");
}
void loop() {
// put your main code here, to run repeatedly:
float value;
for (unsigned i=0; i<BA_EXPAND_NUM_POT; i++) {
if (controls.checkPotValue(i, value)) {
Serial.println(String("POT") + (i+1) + String(" new value: ") + value);
}
}
// Check pushbuttons
for (unsigned i=0; i<BA_EXPAND_NUM_SW; i++) {
if (controls.isSwitchToggled(i)) {
Serial.println(String("Button") + (i+1) + String(" pushed!"));
controls.toggleOutput(i);
}
}
delay(10);
}

@ -0,0 +1,113 @@
#include <MIDI.h>
#define TGA_PRO_REVB
#define TGA_PRO_EXPAND_REV2
#include "BAGuitar.h"
using namespace midi;
using namespace BAEffects;
using namespace BALibrary;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
BAAudioControlWM8731 codec;
//#define USE_EXT // uncomment this line to use External MEM0
#ifdef USE_EXT
// If using external SPI memory, we will instantiance an SRAM
// manager and create an external memory slot to use as the memory
// for our audio delay
ExternalSramManager externalSram;
ExtMemSlot delaySlot; // Declare an external memory slot.
// Instantiate the AudioEffectAnalogDelay to use external memory by
/// passing it the delay slot.
AudioEffectAnalogDelay analogDelay(&delaySlot);
#else
// If using internal memory, we will instantiate the AudioEffectAnalogDelay
// by passing it the maximum amount of delay we will use in millseconds. Note that
// audio delay lengths are very limited when using internal memory due to limited
// internal RAM size.
AudioEffectAnalogDelay analogDelay(200.0f); // max delay of 200 ms.
#endif
AudioFilterBiquad cabFilter; // We'll want something to cut out the highs and smooth the tone, just like a guitar cab.
// Simply connect the input to the delay, and the output
// to both i2s channels
AudioConnection input(i2sIn,0, analogDelay,0);
AudioConnection delayOut(analogDelay, 0, cabFilter, 0);
AudioConnection leftOut(cabFilter,0, i2sOut, 0);
AudioConnection rightOut(cabFilter,0, i2sOut, 1);
//////////////////////////////////////////
// SETUP PHYSICAL CONTROLS
//////////////////////////////////////////
BAPhysicalControls controls(BA_EXPAND_NUM_SW, BA_EXPAND_NUM_POT, 0);
int loopCount = 0;
void setup() {
delay(100);
Serial.begin(57600); // Start the serial port
delay(100);
// Setup the controls
controls.addSwitch(BA_EXPAND_SW1_PIN);
controls.addSwitch(BA_EXPAND_SW2_PIN);
controls.addPot(BA_EXPAND_POT1_PIN,
// Disable the codec first
codec.disable();
AudioMemory(128);
// Enable the codec
Serial.println("Enabling codec...\n");
codec.enable();
// If using external memory request request memory from the manager
// for the slot
#ifdef USE_EXT
Serial.println("Using EXTERNAL memory");
// We have to request memory be allocated to our slot.
externalSram.requestMemory(&delaySlot, 500.0f, MemSelect::MEM0, true);
#else
Serial.println("Using INTERNAL memory");
#endif
// Besure to enable the delay. When disabled, audio is is completely blocked
// to minimize resources to nearly zero.
analogDelay.enable();
// Set some default values.
// These can be changed using the controls on the Blackaddr Audio Expansion Board
analogDelay.bypass(false);
analogDelay.mix(0.5f);
analogDelay.feedback(0.0f);
//////////////////////////////////
// AnalogDelay filter selection //
// Uncomment to tryout the 3 different built-in filters.
//analogDelay.setFilter(AudioEffectAnalogDelay::Filter::DM3); // The default filter. Naturally bright echo (highs stay, lows fade away)
//analogDelay.setFilter(AudioEffectAnalogDelay::Filter::WARM); // A warm filter with a smooth frequency rolloff above 2Khz
//analogDelay.setFilter(AudioEffectAnalogDelay::Filter::DARK); // A very dark filter, with a sharp rolloff above 1Khz
// Setup 2-stages of LPF, cutoff 4500 Hz, Q-factor 0.7071 (a 'normal' Q-factor)
cabFilter.setLowpass(0, 4500, .7071);
cabFilter.setLowpass(1, 4500, .7071);
}
void loop() {
if (loopCount % 524288 == 0) {
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage());
Serial.print("% ");
Serial.print(" analogDelay: "); Serial.print(analogDelay.processorUsage());
Serial.println("%");
}
loopCount++;
}

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

@ -32,5 +32,6 @@
#include "AudioEffectSOS.h"
#include "LibBasicFunctions.h"
#include "LibMemoryManagement.h"
#include "BAPhysicalControls.h"
#endif /* __BATGUITAR_H */

@ -31,10 +31,12 @@
namespace BALibrary {
// uncomment the line that corresponds to your hardware
//#define TGA_PRO_REVA
#if (!defined(TGA_PRO_REVA) && !defined(TGA_PRO_REVB))
#define TGA_PRO_REVA
//#define TGA_PRO_REVB // does not exist yet
#endif
#ifdef TGA_PRO_REVA
#if defined(TGA_PRO_REVA) || defined(TGA_PRO_REVB)
constexpr uint8_t USR_LED_ID = 16; ///< Teensy IO number for the user LED.
@ -93,6 +95,24 @@ constexpr size_t SPI_MEM1_MAX_AUDIO_SAMPLES = SPI_MEM1_SIZE_BYTES/sizeof(int16_t
#endif
#if defined (TGA_PRO_EXPAND_REV2)
/**************************************************************************//**
* Blackaddr Audio Expansion Board
*****************************************************************************/
constexpr unsigned BA_EXPAND_NUM_POT = 3;
constexpr unsigned BA_EXPAND_NUM_SW = 2;
constexpr unsigned BA_EXPAND_NUM_LED = 2;
constexpr unsigned BA_EXPAND_NUM_ENC = 0;
constexpr uint8_t BA_EXPAND_POT1_PIN = A16; // 35_A16_PWM
constexpr uint8_t BA_EXPAND_POT2_PIN = A17; // 36_A17_PWM
constexpr uint8_t BA_EXPAND_POT3_PIN = A18; // 37_SCL1_A18_PWM
constexpr uint8_t BA_EXPAND_SW1_PIN = 2; // 2)PWM
constexpr uint8_t BA_EXPAND_SW2_PIN = 3; // 3_SCL2_PWM
constexpr uint8_t BA_EXPAND_LED1_PIN = 4; // 4_SDA2_PWM
constexpr uint8_t BA_EXPAND_LED2_PIN = 6; // 6_PWM
#endif
} // namespace BALibrary

@ -0,0 +1,148 @@
/**************************************************************************//**
* @file
* @author Steve Lascos
* @company Blackaddr Audio
*
* BAPhysicalControls is a general purpose class for handling an array of
* pots and switches.
*
* @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 __BAPHYSICALCONTROLS_H
#define __BAPHYSICALCONTROLS_H
#include <vector>
#include <Encoder.h>
#include <Bounce.h>
namespace BALibrary {
class DigitalOutput {
public:
DigitalOutput(uint8_t pin) : m_pin(pin) {}
void set(int val);
void toggle(void);
private:
uint8_t m_pin;
int m_val = 0;
};
class Potentiometer {
public:
struct Calib {
unsigned min;
unsigned max;
bool swap;
};
Potentiometer(uint8_t pin, unsigned minCalibration, unsigned maxCalibration)
: m_pin(pin), m_swapDirection(false), m_minCalibration(minCalibration), m_maxCalibration(maxCalibration) {}
Potentiometer(uint8_t pin, unsigned minCalibration, unsigned maxCalibration, bool swapDirection)
: m_pin(pin), m_swapDirection(swapDirection), m_minCalibration(minCalibration), m_maxCalibration(maxCalibration) {}
bool getValue(float &value);
int getRawValue();
void adjustCalibrationThreshold(float thresholdFactor);
static Calib calibrate(uint8_t pin);
private:
uint8_t m_pin;
bool m_swapDirection;
unsigned m_minCalibration;
unsigned m_maxCalibration;
unsigned m_lastValue = 0;
unsigned m_lastValue2 = 0;
};
constexpr bool ENCODER_SWAP = true;
constexpr bool ENCODER_NOSWAP = false;
class RotaryEncoder : public Encoder {
public:
RotaryEncoder(uint8_t pin1, uint8_t pin2, bool swapDirection = false, int divider = 1) : Encoder(pin1,pin2), m_swapDirection(swapDirection), m_divider(divider) {}
int getChange();
void setDivider(int divider);
private:
bool m_swapDirection;
int32_t m_lastPosition = 0;
int32_t m_divider;
};
/// Specifies the type of control
enum class ControlType : unsigned {
SWITCH_MOMENTARY = 0, ///< a momentary switch, which is only on when pressed.
SWITCH_LATCHING = 1, ///< a latching switch, which toggles between on and off with each press and release
ROTARY_KNOB = 2, ///< a rotary encoder knob
POT = 3, ///< an analog potentiometer
UNDEFINED = 255 ///< undefined or uninitialized
};
class BAPhysicalControls {
public:
BAPhysicalControls() = delete;
BAPhysicalControls(unsigned numSwitches, unsigned numPots, unsigned numEncoders = 0, unsigned numOutputs = 0);
~BAPhysicalControls() {}
/// add a rotary encoders to the controls
/// @param pin1 the pin number corresponding to 'A' on the encoder
/// @param pin2 the pin number corresponding to 'B' on the encoder
/// @param swapDirection When true, reverses which rotation direction is positive, and which is negative
/// @param divider optional, for encoders with high resolution this divides down the rotation measurement.
/// @returns the index in the encoder vector the new encoder was placed at.
unsigned addRotary(uint8_t pin1, uint8_t pin2, bool swapDirection = false, int divider = 1);
/// add a switch to the controls
/// @param pin the pin number connected to the switch
/// @param intervalMilliseconds, optional, specifies the filtering time to debounce a switch
/// @returns the index in the switch vector the new switch was placed at.
unsigned addSwitch(uint8_t pin, unsigned long intervalMilliseconds = 10);
/// add a pot to the controls
/// @param pin the pin number connected to the wiper of the pot
/// @param minCalibration the value corresponding to lowest pot setting
/// @param maxCalibration the value corresponding to the highest pot setting
unsigned addPot(uint8_t pin, unsigned minCalibration, unsigned maxCalibration);
/// add a pot to the controls
/// @param pin the pin number connected to the wiper of the pot
/// @param minCalibration the value corresponding to lowest pot setting
/// @param maxCalibration the value corresponding to the highest pot setting
/// @param swapDirection reverses the which direction is considered pot minimum value
/// @param range the pot raw value will be mapped into a range of 0 to range
unsigned addPot(uint8_t pin, unsigned minCalibration, unsigned maxCalibration, bool swapDirection);
unsigned addOutput(uint8_t pin);
void setOutput(unsigned index, int val);
void toggleOutput(unsigned index);
int getRotaryAdjustUnit(unsigned index);
bool checkPotValue(unsigned index, float &value);
bool isSwitchToggled(unsigned index);
private:
std::vector<Potentiometer> m_pots;
std::vector<RotaryEncoder> m_encoders;
std::vector<Bounce> m_switches;
std::vector<DigitalOutput> m_outputs;
};
} // BALibrary
#endif /* __BAPHYSICALCONTROLS_H */

@ -0,0 +1,220 @@
/*
* BAPhysicalControls.cpp
*
* This file provides a class for handling physical controls such as
* switches, pots and rotary encoders.
*
* 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 "BAPhysicalControls.h"
// These calls must be define in order to get vector to work on arduino
namespace std {
void __throw_bad_alloc() {
Serial.println("Unable to allocate memory");
abort();
}
void __throw_length_error( char const*e ) {
Serial.print("Length Error :"); Serial.println(e);
abort();
}
}
namespace BALibrary {
BAPhysicalControls::BAPhysicalControls(unsigned numSwitches, unsigned numPots, unsigned numEncoders, unsigned numOutputs) {
if (numSwitches > 0) {
m_switches.reserve(numSwitches);
}
if (numPots > 0) {
m_pots.reserve(numPots);
}
if (numEncoders > 0) {
m_encoders.reserve(numEncoders);
}
if (numOutputs > 0) {
m_outputs.reserve(numOutputs);
}
}
unsigned BAPhysicalControls::addRotary(uint8_t pin1, uint8_t pin2, bool swapDirection, int divider) {
m_encoders.emplace_back(pin1, pin2, swapDirection, divider);
pinMode(pin1, INPUT);
pinMode(pin2, INPUT);
return m_encoders.size()-1;
}
unsigned BAPhysicalControls::addSwitch(uint8_t pin, unsigned long intervalMilliseconds) {
m_switches.emplace_back(pin, intervalMilliseconds);
pinMode(pin, INPUT);
return m_switches.size()-1;
}
unsigned BAPhysicalControls::addPot(uint8_t pin, unsigned minCalibration, unsigned maxCalibration) {
m_pots.emplace_back(pin, minCalibration, maxCalibration);
return m_pots.size()-1;
}
unsigned BAPhysicalControls::addPot(uint8_t pin, unsigned minCalibration, unsigned maxCalibration, bool swapDirection) {
m_pots.emplace_back(pin, minCalibration, maxCalibration, swapDirection);
return m_pots.size()-1;
}
unsigned BAPhysicalControls::addOutput(uint8_t pin) {
m_outputs.emplace_back(pin);
pinMode(pin, OUTPUT);
return m_outputs.size()-1;
}
void BAPhysicalControls::setOutput(unsigned index, int val) {
if (index >= m_outputs.size()) { return; }
m_outputs[index].set(val);
}
void BAPhysicalControls::toggleOutput(unsigned index) {
if (index >= m_outputs.size()) { return; }
m_outputs[index].toggle();
}
int BAPhysicalControls::getRotaryAdjustUnit(unsigned index) {
if (index >= m_encoders.size()) { return 0; } // index is greater than number of encoders
int encoderAdjust = m_encoders[index].getChange();
if (encoderAdjust != 0) {
// clip the adjust to maximum abs value of 1.
int encoderAdjust = (encoderAdjust > 0) ? 1 : -1;
}
return encoderAdjust;
}
bool BAPhysicalControls::checkPotValue(unsigned index, float &value) {
if (index >= m_pots.size()) { return false;} // index is greater than number of pots
return m_pots[index].getValue(value);
}
bool BAPhysicalControls::isSwitchToggled(unsigned index) {
if (index >= m_switches.size()) { return 0; } // index is greater than number of switches
Bounce &sw = m_switches[index];
if (sw.update() && sw.fallingEdge()) {
return true;
} else {
return false;
}
}
///////////////////////////
void DigitalOutput::set(int val) {
m_val = val;
digitalWriteFast(m_pin, m_val);
}
void DigitalOutput::toggle(void) {
m_val = !m_val;
digitalWriteFast(m_pin, m_val);
}
bool Potentiometer::getValue(float &value) {
unsigned val = analogRead(m_pin); // read the raw value
// Return false if the value is the same as the last sample, or 2 samples ago. The second check is to
// prevent oscillating between two values.
if ((val == m_lastValue) || (val == m_lastValue2)) {
return false;
}
// Otherwise update the last value
m_lastValue = val;
// constrain it within the calibration values, them map it to the desired range.
val = constrain(val, m_minCalibration, m_maxCalibration);
value = static_cast<float>(val - m_minCalibration) / static_cast<float>(m_maxCalibration);
if (m_swapDirection) {
value = 1.0f - value;
}
return true;
}
int Potentiometer::getRawValue() {
return analogRead(m_pin);
}
void Potentiometer::adjustCalibrationThreshold(float thresholdFactor)
{
float threshold = m_maxCalibration * thresholdFactor;
m_maxCalibration -= static_cast<unsigned>(threshold);
m_minCalibration += static_cast<unsigned>(threshold);
}
Potentiometer::Calib Potentiometer::calibrate(uint8_t pin) {
Calib calib;
Serial.print("Calibration pin "); Serial.println(pin);
Serial.println("Move the pot fully counter-clockwise to the minimum setting and press any key then ENTER");
while (true) {
delay(100);
if (Serial.available() > 0) {
calib.min = analogRead(pin);
while (Serial.available()) { Serial.read(); }
break;
}
}
Serial.println("Move the pot fully clockwise to the maximum setting and press any key then ENTER");
while (true) {
delay(100);
if (Serial.available() > 0) {
calib.max = analogRead(pin);
while (Serial.available()) { Serial.read(); }
break;
}
}
if (calib.min > calib.max) {
unsigned tmp = calib.max;
calib.max = calib.min;
calib.min = tmp;
calib.swap = true;
}
Serial.print("The calibration for pin "); Serial.print(pin);
Serial.print(" is min:"); Serial.print(calib.min);
Serial.print(" max:"); Serial.print(calib.max);
Serial.print(" swap: "); Serial.println(calib.swap);
return calib;
}
int RotaryEncoder::getChange() {
int32_t newPosition = read();
int delta = newPosition - m_lastPosition;
m_lastPosition = newPosition;
if (m_swapDirection) { delta = -delta; }
return delta/m_divider;
}
void RotaryEncoder::setDivider(int divider) {
m_divider = divider;
}
} // BALibrary

@ -381,7 +381,7 @@ void BASpiMemoryDMA::write(size_t address, uint8_t *src, size_t numBytes)
uint8_t *srcPtr = src;
size_t nextAddress = address;
while (bytesRemaining > 0) {
m_txXferCount = min(bytesRemaining, MAX_DMA_XFER_SIZE);
m_txXferCount = min(bytesRemaining, static_cast<size_t>(MAX_DMA_XFER_SIZE));
while ( m_txTransfer[1].busy()) {} // wait until not busy
m_setSpiCmdAddr(SPI_WRITE_CMD, nextAddress, m_txCommandBuffer);
m_txTransfer[1] = DmaSpi::Transfer(m_txCommandBuffer, CMD_ADDRESS_SIZE, nullptr, 0, m_cs, TransferType::NO_END_CS);
@ -402,7 +402,7 @@ void BASpiMemoryDMA::zero(size_t address, size_t numBytes)
size_t bytesRemaining = numBytes;
size_t nextAddress = address;
while (bytesRemaining > 0) {
m_txXferCount = min(bytesRemaining, MAX_DMA_XFER_SIZE);
m_txXferCount = min(bytesRemaining, static_cast<size_t>(MAX_DMA_XFER_SIZE));
while ( m_txTransfer[1].busy()) {} // wait until not busy
m_setSpiCmdAddr(SPI_WRITE_CMD, nextAddress, m_txCommandBuffer);
m_txTransfer[1] = DmaSpi::Transfer(m_txCommandBuffer, CMD_ADDRESS_SIZE, nullptr, 0, m_cs, TransferType::NO_END_CS);
@ -440,7 +440,7 @@ void BASpiMemoryDMA::read(size_t address, uint8_t *dest, size_t numBytes)
m_rxTransfer[1] = DmaSpi::Transfer(m_rxCommandBuffer, CMD_ADDRESS_SIZE, nullptr, 0, m_cs, TransferType::NO_END_CS);
m_spiDma->registerTransfer(m_rxTransfer[1]);
m_rxXferCount = min(bytesRemaining, MAX_DMA_XFER_SIZE);
m_rxXferCount = min(bytesRemaining, static_cast<size_t>(MAX_DMA_XFER_SIZE));
while ( m_rxTransfer[0].busy()) {}
m_rxTransfer[0] = DmaSpi::Transfer(nullptr, m_rxXferCount, destPtr, 0, m_cs, TransferType::NO_START_CS);
m_spiDma->registerTransfer(m_rxTransfer[0]);

Loading…
Cancel
Save