Fixed a bug in the SPI memory library for accessing MEM1. Also added some more demos, including support for BAAudioEffectExternalDelay

master
Steve Lascos 7 years ago
parent ece11f5bea
commit 80bb07abdf
  1. 175
      examples/TGA_Pro_1MEM/TGA_Pro_1MEM.ino
  2. 232
      examples/TGA_Pro_2MEM/TGA_Pro_2MEM.ino
  3. 68
      examples/TGA_Pro_ExternalDelay_demo/TGA_Pro_ExternalDelay_demo.ino
  4. 75
      examples/TGA_Pro_delay_reverb/TGA_Pro_delay_reverb.ino
  5. 2
      keywords.txt
  6. 15
      src/BAAudioControlWM8731.cpp
  7. 3
      src/BAAudioControlWM8731.h
  8. 322
      src/BAAudioEffectDelayExternal.cpp
  9. 104
      src/BAAudioEffectDelayExternal.h
  10. 1
      src/BAGuitar.h
  11. 21
      src/BASpiMemory.cpp

@ -0,0 +1,175 @@
/*************************************************************************
* 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 will also perform a sweeo of the SPI MEM0 external RAM.
*
*/
#include <Wire.h>
#include <Audio.h>
#include <MIDI.h>
#include "BAGuitar.h"
MIDI_CREATE_DEFAULT_INSTANCE();
using namespace midi;
using namespace BAGuitar;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
// Audio Thru Connection
AudioConnection patch0(i2sIn,0, i2sOut, 0);
AudioConnection patch1(i2sIn,1, i2sOut, 1);
BAAudioControlWM8731 codecControl;
BAGpio gpio; // access to User LED
BASpiMemory spiMem0(SpiDeviceId::SPI_DEVICE0);
BASpiMemory spiMem1(SpiDeviceId::SPI_DEVICE1);
unsigned long t=0;
// SPI stuff
int spiAddress0 = 0;
int spiData0 = 0xff;
int spiErrorCount0 = 0;
int spiAddress1 = 0;
int spiData1 = 0xff;
int spiErrorCount1 = 0;
void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
Serial.begin(57600);
delay(5);
// If the codec was already powered up (due to reboot) power itd own first
codecControl.disable();
delay(100);
AudioMemory(24);
Serial.println("Enabling codec...\n");
codecControl.enable();
delay(100);
}
void loop() {
//////////////////////////////////////////////////////////////////
// Write test data to the SPI Memory 0
//////////////////////////////////////////////////////////////////
for (spiAddress0=0; spiAddress0 <= SPI_MAX_ADDR; spiAddress0++) {
if ((spiAddress0 % 32768) == 0) {
//Serial.print("Writing to ");
//Serial.println(spiAddress0, HEX);
}
//mem0Write(spiAddress0, spiData0);
spiMem0.write(spiAddress0, spiData0);
spiData0 = (spiData0-1) & 0xff;
}
Serial.println("SPI0 writing DONE!");
///////////////////////////////////////////////////////////////////
// Read back from the SPI Memory 0
///////////////////////////////////////////////////////////////////
spiErrorCount0 = 0;
spiAddress0 = 0;
spiData0 = 0xff;
for (spiAddress0=0; spiAddress0 <= SPI_MAX_ADDR; spiAddress0++) {
if ((spiAddress0 % 32768) == 0) {
//Serial.print("Reading ");
//Serial.print(spiAddress0, HEX);
}
//int data = mem0Read(spiAddress0);
int data = spiMem0.read(spiAddress0);
if (data != spiData0) {
spiErrorCount0++;
Serial.println("");
Serial.print("ERROR MEM0: (expected) (actual):");
Serial.print(spiData0, HEX); Serial.print(":");
Serial.println(data, HEX);
delay(100);
}
if ((spiAddress0 % 32768) == 0) {
//Serial.print(", data = ");
//Serial.println(data, HEX);
}
spiData0 = (spiData0-1) & 0xff;
// Break out of test once the error count reaches 10
if (spiErrorCount0 > 10) { break; }
}
if (spiErrorCount0 == 0) { Serial.println("SPI0 TEST PASSED!!!"); }
///////////////////////////////////////////////////////////////////////
// MIDI TESTING
// Connect a loopback cable between the MIDI IN and MIDI OUT on the
// GTA Pro. This test code will periodically send MIDI events which
// will loop back and get printed in the Serial Monitor.
///////////////////////////////////////////////////////////////////////
DataByte note, velocity, channel, d1, d2;
// Send MIDI OUT
int cc, val=0xA, channelSend = 1;
for (cc=32; cc<40; cc++) {
MIDI.sendControlChange(cc, val, channelSend); val++; channelSend++;
delay(100);
MIDI.sendNoteOn(10, 100, channelSend);
delay(100);
}
if (MIDI.read()) { // Is there a MIDI message incoming ?
MidiType type = MIDI.getType();
Serial.println(String("MIDI IS WORKING!!!"));
switch (type) {
case NoteOn:
note = MIDI.getData1();
velocity = MIDI.getData2();
channel = MIDI.getChannel();
if (velocity > 0) {
Serial.println(String("Note On: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
} else {
Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
}
break;
case NoteOff:
note = MIDI.getData1();
velocity = MIDI.getData2();
channel = MIDI.getChannel();
Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
break;
default:
d1 = MIDI.getData1();
d2 = MIDI.getData2();
Serial.println(String("Message, type=") + type + ", data = " + d1 + " " + d2);
}
t = millis();
}
if (millis() - t > 10000) {
t += 10000;
Serial.println("(no MIDI activity, check cables)");
}
// Toggle the USR LED state
gpio.toggleLed();
}

@ -0,0 +1,232 @@
/*************************************************************************
* 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 will also peform a sweep of SPI MEM0 and MEM1.
*
* NOTE: SPI MEM0 can be used by a Teensy 3.1/3.2/3.5/3.6. SPI MEM1
* can only be used by a Teensy 3.5/3.6 since it is mapped to the extended
* pins.
*
*/
#include <Wire.h>
#include <Audio.h>
#include <MIDI.h>
#include "BAGuitar.h"
MIDI_CREATE_DEFAULT_INSTANCE();
using namespace midi;
using namespace BAGuitar;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
// Audio Thru Connection
AudioConnection patch0(i2sIn,0, i2sOut, 0);
AudioConnection patch1(i2sIn,1, i2sOut, 1);
BAAudioControlWM8731 codecControl;
BAGpio gpio; // access to User LED
BASpiMemory spiMem0(SpiDeviceId::SPI_DEVICE0);
BASpiMemory spiMem1(SpiDeviceId::SPI_DEVICE1);
unsigned long t=0;
// SPI stuff
int spiAddress0 = 0;
int spiData0 = 0xff;
int spiErrorCount0 = 0;
int spiAddress1 = 0;
int spiData1 = 0xff;
int spiErrorCount1 = 0;
void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
Serial.begin(57600);
delay(5);
// If the codec was already powered up (due to reboot) power itd own first
codecControl.disable();
delay(100);
AudioMemory(24);
Serial.println("Enabling codec...\n");
codecControl.enable();
delay(100);
}
void loop() {
//////////////////////////////////////////////////////////////////
// Write test data to the SPI Memory 0
//////////////////////////////////////////////////////////////////
for (spiAddress0=0; spiAddress0 <= SPI_MAX_ADDR; spiAddress0++) {
if ((spiAddress0 % 32768) == 0) {
//Serial.print("Writing to ");
//Serial.println(spiAddress0, HEX);
}
//mem0Write(spiAddress0, spiData0);
spiMem0.write(spiAddress0, spiData0);
spiData0 = (spiData0-1) & 0xff;
}
Serial.println("SPI0 writing DONE!");
///////////////////////////////////////////////////////////////////
// Read back from the SPI Memory 0
///////////////////////////////////////////////////////////////////
spiErrorCount0 = 0;
spiAddress0 = 0;
spiData0 = 0xff;
for (spiAddress0=0; spiAddress0 <= SPI_MAX_ADDR; spiAddress0++) {
if ((spiAddress0 % 32768) == 0) {
//Serial.print("Reading ");
//Serial.print(spiAddress0, HEX);
}
//int data = mem0Read(spiAddress0);
int data = spiMem0.read(spiAddress0);
if (data != spiData0) {
spiErrorCount0++;
Serial.println("");
Serial.print("ERROR MEM0: (expected) (actual):");
Serial.print(spiData0, HEX); Serial.print(":");
Serial.println(data, HEX);
delay(100);
}
if ((spiAddress0 % 32768) == 0) {
//Serial.print(", data = ");
//Serial.println(data, HEX);
}
spiData0 = (spiData0-1) & 0xff;
// Break out of test once the error count reaches 10
if (spiErrorCount0 > 10) { break; }
}
if (spiErrorCount0 == 0) { Serial.println("SPI0 TEST PASSED!!!"); }
//////////////////////////////////////////////////////////////////
// Write test data to the SPI Memory 1
//////////////////////////////////////////////////////////////////
for (spiAddress1=0; spiAddress1 <= SPI_MAX_ADDR; spiAddress1++) {
if ((spiAddress1 % 32768) == 0) {
//Serial.print("Writing to ");
//Serial.println(spiAddress1, HEX);
}
//mem0Write(spiAddress1, spiData1);
spiMem1.write(spiAddress1, spiData1);
spiData1 = (spiData1-1) & 0xff;
}
Serial.println("SPI1 writing DONE!");
///////////////////////////////////////////////////////////////////
// Read back from the SPI Memory 1
///////////////////////////////////////////////////////////////////
spiErrorCount1 = 0;
spiAddress1 = 0;
spiData1 = 0xff;
for (spiAddress1=0; spiAddress1 <= SPI_MAX_ADDR; spiAddress1++) {
if ((spiAddress1 % 32768) == 0) {
//Serial.print("Reading ");
//Serial.print(spiAddress1, HEX);
}
int data = spiMem1.read(spiAddress1);
if (data != spiData1) {
spiErrorCount1++;
Serial.println("");
Serial.print("ERROR MEM1: (expected) (actual):");
Serial.print(spiData1, HEX); Serial.print(":");
Serial.println(data, HEX);
delay(100);
}
if ((spiAddress1 % 32768) == 0) {
//Serial.print(", data = ");
//Serial.println(data, HEX);
}
spiData1 = (spiData1-1) & 0xff;
// Break out of test once the error count reaches 10
if (spiErrorCount1 > 10) { break; }
}
if (spiErrorCount1 == 0) { Serial.println("SPI1 TEST PASSED!!!"); }
///////////////////////////////////////////////////////////////////////
// MIDI TESTING
// Connect a loopback cable between the MIDI IN and MIDI OUT on the
// GTA Pro. This test code will periodically send MIDI events which
// will loop back and get printed in the Serial Monitor.
///////////////////////////////////////////////////////////////////////
DataByte note, velocity, channel, d1, d2;
// Send MIDI OUT
int cc, val=0xA, channelSend = 1;
for (cc=32; cc<40; cc++) {
MIDI.sendControlChange(cc, val, channelSend); val++; channelSend++;
delay(100);
MIDI.sendNoteOn(10, 100, channelSend);
delay(100);
}
if (MIDI.read()) { // Is there a MIDI message incoming ?
MidiType type = MIDI.getType();
Serial.println(String("MIDI IS WORKING!!!"));
switch (type) {
case NoteOn:
note = MIDI.getData1();
velocity = MIDI.getData2();
channel = MIDI.getChannel();
if (velocity > 0) {
Serial.println(String("Note On: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
} else {
Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
}
break;
case NoteOff:
note = MIDI.getData1();
velocity = MIDI.getData2();
channel = MIDI.getChannel();
Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
break;
default:
d1 = MIDI.getData1();
d2 = MIDI.getData2();
Serial.println(String("Message, type=") + type + ", data = " + d1 + " " + d2);
}
t = millis();
}
if (millis() - t > 10000) {
t += 10000;
Serial.println("(no MIDI activity, check cables)");
}
// Toggle the USR LED state
gpio.toggleLed();
}

@ -0,0 +1,68 @@
/*************************************************************************
* 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 demonstrates how to override the default AudioEffectDelayExternal
* in the Teensy Library with the one in the BAGuitar library. This is necessary
* because the SPI pins in AudioEffectDelayExternal are hard-coded and not the
* same at the TGA Pro.
*
* Simply replace AudioEffectDelayExternal with BAAudioEffectDelayExternal
* and it should work the same as the default Audio library.
*
* This demo mixes the original guitar signal with one delayed by 1.45 ms
* the the external SRAM MEM0 on the TGA Pro
*
*/
#include <Wire.h>
//#include <Audio.h>
#include <MIDI.h>
#include <SPI.h>
#include "BAGuitar.h"
using namespace BAGuitar;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
BAAudioControlWM8731 codecControl;
BAAudioEffectDelayExternal longDelay;
AudioMixer4 delayMixer;
// Audio Connections
AudioConnection fromInput(i2sIn,0, longDelay, 0);
AudioConnection fromDelayL(longDelay, 0, delayMixer, 0);
AudioConnection dry(i2sIn, 1, delayMixer, 1);
AudioConnection outputLeft(delayMixer, 0, i2sOut, 0);
AudioConnection outputRight(delayMixer, 0, i2sOut, 1);
void setup() {
Serial.begin(57600);
delay(1000);
// If the codec was already powered up (due to reboot) power itd own first
codecControl.disable();
delay(100);
AudioMemory(128);
Serial.println("Enabling codec...\n");
codecControl.enable();
delay(1000);
longDelay.delay(0, 1450.0f);
delayMixer.gain(0, 1.0f);
delayMixer.gain(1, 1.0f);
}
void loop() {
}

@ -0,0 +1,75 @@
/*************************************************************************
* 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 provides an example guitar tone consisting of some slap-back delay,
* followed by a reverb and a low-pass cabinet filter.
*
*/
#include <Wire.h>
#include <Audio.h>
#include <MIDI.h>
#include "BAGuitar.h"
using namespace BAGuitar;
BAAudioControlWM8731 codecControl;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
AudioMixer4 gainModule; // This will be used simply to reduce the gain before the reverb
AudioEffectDelay delayModule; // we'll add a little slapback echo
AudioEffectReverb reverb; // Add a bit of 'verb to our tone
AudioMixer4 mixer; // Used to mix the original dry with the wet (effects) path.
AudioFilterBiquad cabFilter; // We'll want something to cut out the highs and smooth the tone, just like a guitar cab.
// Audio Connections
AudioConnection patchIn(i2sIn,0, delayModule, 0); // route the input to the delay
AudioConnection patch2(delayModule,0, gainModule, 0); // send the delay to the gain module
AudioConnection patch2b(gainModule, 0, reverb, 0); // then to the reverb
AudioConnection patch1(i2sIn,1, mixer,0); // mixer input 0 is our original dry signal
AudioConnection patch3(reverb, 0, mixer, 1); // mixer input 1 is our wet
AudioConnection patch4(mixer, 0, cabFilter, 0); // mixer outpt to the cabinet filter
AudioConnection patch5(cabFilter, 0, i2sOut, 0); // connect the cab filter to the output.
void setup() {
delay(5); // wait a few ms to make sure the GTA Pro is fully powered up
AudioMemory(48);
// If the codec was already powered up (due to reboot) power itd own first
codecControl.disable();
delay(100);
codecControl.enable();
delay(100);
// Configure our effects
delayModule.delay(0, 50.0f); // 50 ms slapback delay
gainModule.gain(0, 0.25); // the reverb unit clips easily if the input is too high
mixer.gain(0, 1.0f); // unity gain on the dry
mixer.gain(1, 1.0f); // unity gain on the wet
// 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() {
// The audio flows automatically through the Teensy Audio Library
}

@ -8,6 +8,8 @@
BAAudioControlWM8731 KEYWORD1 BAAudioControlWM8731 KEYWORD1
BASpiMemory KEYWORD1 BASpiMemory KEYWORD1
BAGpio KEYWORD1
BAAudioEffectDelayExternal KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)

@ -74,6 +74,10 @@ constexpr int WM8731_DAC_MUTE_SHIFT = 3;
constexpr int WM8731_HPF_DISABLE_ADDR = 5; constexpr int WM8731_HPF_DISABLE_ADDR = 5;
constexpr int WM8731_HPF_DISABLE_MASK = 0x1; constexpr int WM8731_HPF_DISABLE_MASK = 0x1;
constexpr int WM8731_HPF_DISABLE_SHIFT = 0; constexpr int WM8731_HPF_DISABLE_SHIFT = 0;
// Register 7
constexpr int WM8731_LRSWAP_ADDR = 5;
constexpr int WM8731_LRSWAP_MASK = 0x20;
constexpr int WM8731_LRSWAPE_SHIFT = 5;
// Register 9 // Register 9
constexpr int WM8731_ACTIVATE_ADDR = 9; constexpr int WM8731_ACTIVATE_ADDR = 9;
@ -228,6 +232,17 @@ void BAAudioControlWM8731::setRightInMute(bool val)
write(WM8731_RIGHT_INPUT_MUTE_ADDR, regArray[WM8731_RIGHT_INPUT_MUTE_ADDR]); write(WM8731_RIGHT_INPUT_MUTE_ADDR, regArray[WM8731_RIGHT_INPUT_MUTE_ADDR]);
} }
// Left/right swap control
void BAAudioControlWM8731::setLeftRightSwap(bool val)
{
if (val) {
regArray[WM8731_LRSWAP_ADDR] |= WM8731_LRSWAP_MASK;
} else {
regArray[WM8731_LRSWAP_ADDR] &= ~WM8731_LRSWAP_MASK;
}
write(WM8731_LRSWAP_ADDR, regArray[WM8731_LRSWAP_ADDR]);
}
// Dac output mute control // Dac output mute control
void BAAudioControlWM8731::setDacMute(bool val) void BAAudioControlWM8731::setDacMute(bool val)
{ {

@ -69,6 +69,9 @@ public:
/// affect both channels. /// affect both channels.
/// @param val when true, channels are linked, when false, they are controlled separately /// @param val when true, channels are linked, when false, they are controlled separately
void setLinkLeftRightIn(bool val); void setLinkLeftRightIn(bool val);
/// Swaps the left and right channels in the codec.
///param val when true, channels are swapped, else normal.
void setLeftRightSwap(bool val);
/// Mute/unmute the output DAC, affects both Left and Right output channels. /// Mute/unmute the output DAC, affects both Left and Right output channels.
/// @param when true, output DAC is muted, when false, unmuted. /// @param when true, output DAC is muted, when false, unmuted.

@ -0,0 +1,322 @@
/*
* BAAudioControlWM8731.h
*
* Created on: November 1, 2017
* 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 "BAAudioEffectDelayExternal.h"
namespace BAGuitar {
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
//#include "effect_delay_ext.h"
//#define INTERNAL_TEST
// While 20 MHz (Teensy actually uses 16 MHz in most cases) and even 24 MHz
// have worked well in testing at room temperature with 3.3V power, to fully
// meet all the worst case timing specs, the SPI clock low time would need
// to be 40ns (12.5 MHz clock) for the single chip case and 51ns (9.8 MHz
// clock) for the 6-chip memoryboard with 74LCX126 buffers.
//
// Timing analysis and info is here:
// https://forum.pjrc.com/threads/29276-Limits-of-delay-effect-in-audio-library?p=97506&viewfull=1#post97506
#define SPISETTING SPISettings(20000000, MSBFIRST, SPI_MODE0)
// Use these with the audio adaptor board (should be adjustable by the user...)
//#define SPIRAM_MOSI_PIN 7
//#define SPIRAM_MISO_PIN 12
//#define SPIRAM_SCK_PIN 14
//
//#define SPIRAM_CS_PIN 6
// Use with TGA Pro
#define SPIRAM_MOSI_PIN 7
#define SPIRAM_MISO_PIN 8
#define SPIRAM_SCK_PIN 14
#define SPIRAM_CS_PIN 15
#define MEMBOARD_CS0_PIN 2
#define MEMBOARD_CS1_PIN 3
#define MEMBOARD_CS2_PIN 4
void BAAudioEffectDelayExternal::update(void)
{
audio_block_t *block;
uint32_t n, channel, read_offset;
// grab incoming data and put it into the memory
block = receiveReadOnly();
if (memory_type >= BA_AUDIO_MEMORY_UNDEFINED) {
// ignore input and do nothing if undefined memory type
release(block);
return;
}
if (block) {
if (head_offset + AUDIO_BLOCK_SAMPLES <= memory_length) {
// a single write is enough
write(head_offset, AUDIO_BLOCK_SAMPLES, block->data);
head_offset += AUDIO_BLOCK_SAMPLES;
} else {
// write wraps across end-of-memory
n = memory_length - head_offset;
write(head_offset, n, block->data);
head_offset = AUDIO_BLOCK_SAMPLES - n;
write(0, head_offset, block->data + n);
}
release(block);
} else {
// if no input, store zeros, so later playback will
// not be random garbage previously stored in memory
if (head_offset + AUDIO_BLOCK_SAMPLES <= memory_length) {
zero(head_offset, AUDIO_BLOCK_SAMPLES);
head_offset += AUDIO_BLOCK_SAMPLES;
} else {
n = memory_length - head_offset;
zero(head_offset, n);
head_offset = AUDIO_BLOCK_SAMPLES - n;
zero(0, head_offset);
}
}
// transmit the delayed outputs
for (channel = 0; channel < 8; channel++) {
if (!(activemask & (1<<channel))) continue;
block = allocate();
if (!block) continue;
// compute the delayed location where we read
if (delay_length[channel] <= head_offset) {
read_offset = head_offset - delay_length[channel];
} else {
read_offset = memory_length + head_offset - delay_length[channel];
}
if (read_offset + AUDIO_BLOCK_SAMPLES <= memory_length) {
// a single read will do it
read(read_offset, AUDIO_BLOCK_SAMPLES, block->data);
} else {
// read wraps across end-of-memory
n = memory_length - read_offset;
read(read_offset, n, block->data);
read(0, AUDIO_BLOCK_SAMPLES - n, block->data + n);
}
transmit(block, channel);
release(block);
}
}
uint32_t BAAudioEffectDelayExternal::allocated[2] = {0, 0};
void BAAudioEffectDelayExternal::initialize(BAAudioEffectDelayMemoryType_t type, uint32_t samples)
{
uint32_t memsize, avail;
activemask = 0;
head_offset = 0;
memory_type = type;
SPI.setMOSI(SPIRAM_MOSI_PIN);
SPI.setMISO(SPIRAM_MISO_PIN);
SPI.setSCK(SPIRAM_SCK_PIN);
SPI.begin();
if (type == BA_AUDIO_MEMORY_23LC1024) {
#ifdef INTERNAL_TEST
memsize = 8000;
#else
memsize = 65536;
#endif
pinMode(SPIRAM_CS_PIN, OUTPUT);
digitalWriteFast(SPIRAM_CS_PIN, HIGH);
} else if (type == BA_AUDIO_MEMORY_MEMORYBOARD) {
memsize = 393216;
pinMode(MEMBOARD_CS0_PIN, OUTPUT);
pinMode(MEMBOARD_CS1_PIN, OUTPUT);
pinMode(MEMBOARD_CS2_PIN, OUTPUT);
digitalWriteFast(MEMBOARD_CS0_PIN, LOW);
digitalWriteFast(MEMBOARD_CS1_PIN, LOW);
digitalWriteFast(MEMBOARD_CS2_PIN, LOW);
} else if (type == BA_AUDIO_MEMORY_CY15B104) {
#ifdef INTERNAL_TEST
memsize = 8000;
#else
memsize = 262144;
#endif
pinMode(SPIRAM_CS_PIN, OUTPUT);
digitalWriteFast(SPIRAM_CS_PIN, HIGH);
} else {
return;
}
avail = memsize - allocated[type];
if (avail < AUDIO_BLOCK_SAMPLES*2+1) {
memory_type = BA_AUDIO_MEMORY_UNDEFINED;
return;
}
if (samples > avail) samples = avail;
memory_begin = allocated[type];
allocated[type] += samples;
memory_length = samples;
zero(0, memory_length);
}
#ifdef INTERNAL_TEST
static int16_t testmem[8000]; // testing only
#endif
void BAAudioEffectDelayExternal::read(uint32_t offset, uint32_t count, int16_t *data)
{
uint32_t addr = memory_begin + offset;
#ifdef INTERNAL_TEST
while (count) { *data++ = testmem[addr++]; count--; } // testing only
#else
if (memory_type == BA_AUDIO_MEMORY_23LC1024 ||
memory_type == BA_AUDIO_MEMORY_CY15B104) {
addr *= 2;
SPI.beginTransaction(SPISETTING);
digitalWriteFast(SPIRAM_CS_PIN, LOW);
SPI.transfer16((0x03 << 8) | (addr >> 16));
SPI.transfer16(addr & 0xFFFF);
while (count) {
*data++ = (int16_t)(SPI.transfer16(0));
count--;
}
digitalWriteFast(SPIRAM_CS_PIN, HIGH);
SPI.endTransaction();
} else if (memory_type == BA_AUDIO_MEMORY_MEMORYBOARD) {
SPI.beginTransaction(SPISETTING);
while (count) {
uint32_t chip = (addr >> 16) + 1;
digitalWriteFast(MEMBOARD_CS0_PIN, chip & 1);
digitalWriteFast(MEMBOARD_CS1_PIN, chip & 2);
digitalWriteFast(MEMBOARD_CS2_PIN, chip & 4);
uint32_t chipaddr = (addr & 0xFFFF) << 1;
SPI.transfer16((0x03 << 8) | (chipaddr >> 16));
SPI.transfer16(chipaddr & 0xFFFF);
uint32_t num = 0x10000 - (addr & 0xFFFF);
if (num > count) num = count;
count -= num;
addr += num;
do {
*data++ = (int16_t)(SPI.transfer16(0));
} while (--num > 0);
}
digitalWriteFast(MEMBOARD_CS0_PIN, LOW);
digitalWriteFast(MEMBOARD_CS1_PIN, LOW);
digitalWriteFast(MEMBOARD_CS2_PIN, LOW);
SPI.endTransaction();
}
#endif
}
void BAAudioEffectDelayExternal::write(uint32_t offset, uint32_t count, const int16_t *data)
{
uint32_t addr = memory_begin + offset;
#ifdef INTERNAL_TEST
while (count) { testmem[addr++] = *data++; count--; } // testing only
#else
if (memory_type == BA_AUDIO_MEMORY_23LC1024) {
addr *= 2;
SPI.beginTransaction(SPISETTING);
digitalWriteFast(SPIRAM_CS_PIN, LOW);
SPI.transfer16((0x02 << 8) | (addr >> 16));
SPI.transfer16(addr & 0xFFFF);
while (count) {
int16_t w = 0;
if (data) w = *data++;
SPI.transfer16(w);
count--;
}
digitalWriteFast(SPIRAM_CS_PIN, HIGH);
SPI.endTransaction();
} else if (memory_type == BA_AUDIO_MEMORY_CY15B104) {
addr *= 2;
SPI.beginTransaction(SPISETTING);
digitalWriteFast(SPIRAM_CS_PIN, LOW);
SPI.transfer(0x06); //write-enable before every write
digitalWriteFast(SPIRAM_CS_PIN, HIGH);
asm volatile ("NOP\n NOP\n NOP\n NOP\n NOP\n NOP\n");
digitalWriteFast(SPIRAM_CS_PIN, LOW);
SPI.transfer16((0x02 << 8) | (addr >> 16));
SPI.transfer16(addr & 0xFFFF);
while (count) {
int16_t w = 0;
if (data) w = *data++;
SPI.transfer16(w);
count--;
}
digitalWriteFast(SPIRAM_CS_PIN, HIGH);
SPI.endTransaction();
} else if (memory_type == BA_AUDIO_MEMORY_MEMORYBOARD) {
SPI.beginTransaction(SPISETTING);
while (count) {
uint32_t chip = (addr >> 16) + 1;
digitalWriteFast(MEMBOARD_CS0_PIN, chip & 1);
digitalWriteFast(MEMBOARD_CS1_PIN, chip & 2);
digitalWriteFast(MEMBOARD_CS2_PIN, chip & 4);
uint32_t chipaddr = (addr & 0xFFFF) << 1;
SPI.transfer16((0x02 << 8) | (chipaddr >> 16));
SPI.transfer16(chipaddr & 0xFFFF);
uint32_t num = 0x10000 - (addr & 0xFFFF);
if (num > count) num = count;
count -= num;
addr += num;
do {
int16_t w = 0;
if (data) w = *data++;
SPI.transfer16(w);
} while (--num > 0);
}
digitalWriteFast(MEMBOARD_CS0_PIN, LOW);
digitalWriteFast(MEMBOARD_CS1_PIN, LOW);
digitalWriteFast(MEMBOARD_CS2_PIN, LOW);
SPI.endTransaction();
}
#endif
}
} /* namespace BAGuitar */

@ -0,0 +1,104 @@
/*
* BAAudioEffectDelayExternal.h
*
* Created on: Nov 5, 2017
* Author: slascos
*/
#ifndef __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H
#define __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H
#include <Audio.h>
namespace BAGuitar {
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "Arduino.h"
#include "AudioStream.h"
#include "spi_interrupt.h"
enum BAAudioEffectDelayMemoryType_t {
BA_AUDIO_MEMORY_23LC1024 = 0, // 128k x 8 S-RAM
BA_AUDIO_MEMORY_MEMORYBOARD = 1,
BA_AUDIO_MEMORY_CY15B104 = 2, // 512k x 8 F-RAM
BA_AUDIO_MEMORY_UNDEFINED = 3
};
class BAAudioEffectDelayExternal : public AudioStream
{
public:
BAAudioEffectDelayExternal() : AudioStream(1, inputQueueArray) {
initialize(BA_AUDIO_MEMORY_23LC1024, 65536);
}
BAAudioEffectDelayExternal(BAAudioEffectDelayMemoryType_t type, float milliseconds=1e6)
: AudioStream(1, inputQueueArray) {
uint32_t n = (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f;
initialize(type, n);
}
void delay(uint8_t channel, float milliseconds) {
if (channel >= 8 || memory_type >= BA_AUDIO_MEMORY_UNDEFINED) return;
if (milliseconds < 0.0) milliseconds = 0.0;
uint32_t n = (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f;
n += AUDIO_BLOCK_SAMPLES;
if (n > memory_length - AUDIO_BLOCK_SAMPLES)
n = memory_length - AUDIO_BLOCK_SAMPLES;
delay_length[channel] = n;
uint8_t mask = activemask;
if (activemask == 0) AudioStartUsingSPI();
activemask = mask | (1<<channel);
}
void disable(uint8_t channel) {
if (channel >= 8) return;
uint8_t mask = activemask & ~(1<<channel);
activemask = mask;
if (mask == 0) AudioStopUsingSPI();
}
virtual void update(void);
private:
void initialize(BAAudioEffectDelayMemoryType_t type, uint32_t samples);
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) {
write(address, count, NULL);
}
uint32_t memory_begin; // the first address in the memory we're using
uint32_t memory_length; // the amount of memory we're using
uint32_t head_offset; // head index (incoming) data into external memory
uint32_t delay_length[8]; // # of sample delay for each channel (128 = no delay)
uint8_t activemask; // which output channels are active
uint8_t memory_type; // 0=23LC1024, 1=Frank's Memoryboard
static uint32_t allocated[2];
audio_block_t *inputQueueArray[1];
};
} /* namespace BAGuitar */
#endif /* __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H */

@ -24,6 +24,7 @@
#include "BAAudioControlWM8731.h" // Codec Control #include "BAAudioControlWM8731.h" // Codec Control
#include "BASpiMemory.h" #include "BASpiMemory.h"
#include "BAGpio.h" #include "BAGpio.h"
#include "BAAudioEffectDelayExternal.h"
/**************************************************************************//** /**************************************************************************//**
* BAGuitar is a namespace/Library for Guitar processing from Blackaddr Audio. * BAGuitar is a namespace/Library for Guitar processing from Blackaddr Audio.

@ -71,6 +71,7 @@ void BASpiMemory::m_Init()
SPI.setMOSI(SPI_MOSI_MEM0); SPI.setMOSI(SPI_MOSI_MEM0);
SPI.setMISO(SPI_MISO_MEM0); SPI.setMISO(SPI_MISO_MEM0);
SPI.setSCK(SPI_SCK_MEM0); SPI.setSCK(SPI_SCK_MEM0);
SPI.begin();
break; break;
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
@ -79,6 +80,7 @@ void BASpiMemory::m_Init()
SPI1.setMOSI(SPI_MOSI_MEM1); SPI1.setMOSI(SPI_MOSI_MEM1);
SPI1.setMISO(SPI_MISO_MEM1); SPI1.setMISO(SPI_MISO_MEM1);
SPI1.setSCK(SPI_SCK_MEM1); SPI1.setSCK(SPI_SCK_MEM1);
SPI1.begin();
break; break;
#endif #endif
@ -87,7 +89,6 @@ void BASpiMemory::m_Init()
return; return;
} }
SPI.begin();
pinMode(m_csPin, OUTPUT); pinMode(m_csPin, OUTPUT);
digitalWrite(m_csPin, HIGH); digitalWrite(m_csPin, HIGH);
@ -99,12 +100,10 @@ BASpiMemory::~BASpiMemory() {
// Single address write // Single address write
void BASpiMemory::write(int address, int data) void BASpiMemory::write(int address, int data)
{ {
SPI.beginTransaction(m_settings);
digitalWrite(m_csPin, LOW);
switch (m_memDeviceId) { switch (m_memDeviceId) {
case SpiDeviceId::SPI_DEVICE0 : case SpiDeviceId::SPI_DEVICE0 :
SPI.beginTransaction(m_settings);
digitalWrite(m_csPin, LOW);
SPI.transfer(SPI_WRITE_CMD); SPI.transfer(SPI_WRITE_CMD);
SPI.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); SPI.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT);
SPI.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); SPI.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT);
@ -115,7 +114,8 @@ void BASpiMemory::write(int address, int data)
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
case SpiDeviceId::SPI_DEVICE1 : case SpiDeviceId::SPI_DEVICE1 :
digitalWrite(m_csPin, HIGH); SPI1.beginTransaction(m_settings);
digitalWrite(m_csPin, LOW);
SPI1.transfer(SPI_WRITE_CMD); SPI1.transfer(SPI_WRITE_CMD);
SPI1.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); SPI1.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT);
SPI1.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); SPI1.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT);
@ -137,10 +137,11 @@ int BASpiMemory::read(int address)
{ {
int data = -1; int data = -1;
SPI.beginTransaction(m_settings);
digitalWrite(m_csPin, LOW);
switch (m_memDeviceId) { switch (m_memDeviceId) {
case SpiDeviceId::SPI_DEVICE0 : case SpiDeviceId::SPI_DEVICE0 :
SPI.beginTransaction(m_settings);
digitalWrite(m_csPin, LOW);
SPI.transfer(SPI_READ_CMD); SPI.transfer(SPI_READ_CMD);
SPI.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); SPI.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT);
SPI.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); SPI.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT);
@ -151,11 +152,13 @@ int BASpiMemory::read(int address)
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
case SpiDeviceId::SPI_DEVICE1 : case SpiDeviceId::SPI_DEVICE1 :
SPI1.beginTransaction(m_settings);
digitalWrite(m_csPin, LOW);
SPI1.transfer(SPI_READ_CMD); SPI1.transfer(SPI_READ_CMD);
SPI1.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); SPI1.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT);
SPI1.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); SPI1.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT);
SPI1.transfer((address & SPI_ADDR_0_MASK)); SPI1.transfer((address & SPI_ADDR_0_MASK));
data = SPI.transfer(0); data = SPI1.transfer(0);
SPI1.endTransaction(); SPI1.endTransaction();
break; break;
#endif #endif

Loading…
Cancel
Save