From 80bb07abdff30b05d556e2f5ae46a57dbc866a62 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Sun, 5 Nov 2017 20:23:27 -0500 Subject: [PATCH] Fixed a bug in the SPI memory library for accessing MEM1. Also added some more demos, including support for BAAudioEffectExternalDelay --- examples/TGA_Pro_1MEM/TGA_Pro_1MEM.ino | 175 ++++++++++ examples/TGA_Pro_2MEM/TGA_Pro_2MEM.ino | 232 +++++++++++++ .../TGA_Pro_ExternalDelay_demo.ino | 68 ++++ .../TGA_Pro_delay_reverb.ino | 75 ++++ keywords.txt | 2 + src/BAAudioControlWM8731.cpp | 15 + src/BAAudioControlWM8731.h | 3 + src/BAAudioEffectDelayExternal.cpp | 322 ++++++++++++++++++ src/BAAudioEffectDelayExternal.h | 104 ++++++ src/BAGuitar.h | 1 + src/BASpiMemory.cpp | 21 +- 11 files changed, 1009 insertions(+), 9 deletions(-) create mode 100644 examples/TGA_Pro_1MEM/TGA_Pro_1MEM.ino create mode 100644 examples/TGA_Pro_2MEM/TGA_Pro_2MEM.ino create mode 100644 examples/TGA_Pro_ExternalDelay_demo/TGA_Pro_ExternalDelay_demo.ino create mode 100644 examples/TGA_Pro_delay_reverb/TGA_Pro_delay_reverb.ino create mode 100644 src/BAAudioEffectDelayExternal.cpp create mode 100644 src/BAAudioEffectDelayExternal.h diff --git a/examples/TGA_Pro_1MEM/TGA_Pro_1MEM.ino b/examples/TGA_Pro_1MEM/TGA_Pro_1MEM.ino new file mode 100644 index 0000000..2e644df --- /dev/null +++ b/examples/TGA_Pro_1MEM/TGA_Pro_1MEM.ino @@ -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 +#include +#include +#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(); + +} + diff --git a/examples/TGA_Pro_2MEM/TGA_Pro_2MEM.ino b/examples/TGA_Pro_2MEM/TGA_Pro_2MEM.ino new file mode 100644 index 0000000..b7760fe --- /dev/null +++ b/examples/TGA_Pro_2MEM/TGA_Pro_2MEM.ino @@ -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 +#include +#include +#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(); + +} + diff --git a/examples/TGA_Pro_ExternalDelay_demo/TGA_Pro_ExternalDelay_demo.ino b/examples/TGA_Pro_ExternalDelay_demo/TGA_Pro_ExternalDelay_demo.ino new file mode 100644 index 0000000..aafb481 --- /dev/null +++ b/examples/TGA_Pro_ExternalDelay_demo/TGA_Pro_ExternalDelay_demo.ino @@ -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 +//#include +#include +#include +#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() { + + + +} + diff --git a/examples/TGA_Pro_delay_reverb/TGA_Pro_delay_reverb.ino b/examples/TGA_Pro_delay_reverb/TGA_Pro_delay_reverb.ino new file mode 100644 index 0000000..9953821 --- /dev/null +++ b/examples/TGA_Pro_delay_reverb/TGA_Pro_delay_reverb.ino @@ -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 +#include +#include +#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 + +} + diff --git a/keywords.txt b/keywords.txt index 3b1044b..bac835a 100644 --- a/keywords.txt +++ b/keywords.txt @@ -8,6 +8,8 @@ BAAudioControlWM8731 KEYWORD1 BASpiMemory KEYWORD1 +BAGpio KEYWORD1 +BAAudioEffectDelayExternal KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) diff --git a/src/BAAudioControlWM8731.cpp b/src/BAAudioControlWM8731.cpp index aa7dbc6..6c9b964 100644 --- a/src/BAAudioControlWM8731.cpp +++ b/src/BAAudioControlWM8731.cpp @@ -74,6 +74,10 @@ constexpr int WM8731_DAC_MUTE_SHIFT = 3; constexpr int WM8731_HPF_DISABLE_ADDR = 5; constexpr int WM8731_HPF_DISABLE_MASK = 0x1; 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 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]); } +// 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 void BAAudioControlWM8731::setDacMute(bool val) { diff --git a/src/BAAudioControlWM8731.h b/src/BAAudioControlWM8731.h index 0f30eaa..538f557 100644 --- a/src/BAAudioControlWM8731.h +++ b/src/BAAudioControlWM8731.h @@ -69,6 +69,9 @@ public: /// affect both channels. /// @param val when true, channels are linked, when false, they are controlled separately 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. /// @param when true, output DAC is muted, when false, unmuted. diff --git a/src/BAAudioEffectDelayExternal.cpp b/src/BAAudioEffectDelayExternal.cpp new file mode 100644 index 0000000..d60918e --- /dev/null +++ b/src/BAAudioEffectDelayExternal.cpp @@ -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 . +*/ + +#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<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 */ diff --git a/src/BAAudioEffectDelayExternal.h b/src/BAAudioEffectDelayExternal.h new file mode 100644 index 0000000..f4cf7d5 --- /dev/null +++ b/src/BAAudioEffectDelayExternal.h @@ -0,0 +1,104 @@ +/* + * BAAudioEffectDelayExternal.h + * + * Created on: Nov 5, 2017 + * Author: slascos + */ + +#ifndef __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H +#define __BAGUITAR_BAAUDIOEFFECTDELAYEXTERNAL_H + +#include + +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<= 8) return; + uint8_t mask = activemask & ~(1<> SPI_ADDR_2_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__) case SpiDeviceId::SPI_DEVICE1 : - digitalWrite(m_csPin, HIGH); + SPI1.beginTransaction(m_settings); + digitalWrite(m_csPin, LOW); SPI1.transfer(SPI_WRITE_CMD); SPI1.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); SPI1.transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); @@ -137,10 +137,11 @@ int BASpiMemory::read(int address) { int data = -1; - SPI.beginTransaction(m_settings); - digitalWrite(m_csPin, LOW); + switch (m_memDeviceId) { case SpiDeviceId::SPI_DEVICE0 : + SPI.beginTransaction(m_settings); + digitalWrite(m_csPin, LOW); SPI.transfer(SPI_READ_CMD); SPI.transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_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__) case SpiDeviceId::SPI_DEVICE1 : + SPI1.beginTransaction(m_settings); + digitalWrite(m_csPin, LOW); SPI1.transfer(SPI_READ_CMD); 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_0_MASK)); - data = SPI.transfer(0); + data = SPI1.transfer(0); SPI1.endTransaction(); break; #endif