I’m making a step sequencer, using a teensy4.0 + audio board + flash chip, and an adafruit neotrellis board connected via i2c to trigger flash chip raw samples, unfortunately it all locks up / freezes if playing audio while i have a moving LED on the neotrellis,
if i rename the raw files in the sketch to file names that don't exist (so no sound is played) the problem never happens,
if i use the moving LED to trigger pads the sound is distorted until the lock up happens then sounds play without distortion,
but its all locked up, cant be stopped and needs a reboot,
anyone experience this? or have any idea what is going on?
if i rename the raw files in the sketch to file names that don't exist (so no sound is played) the problem never happens,
if i use the moving LED to trigger pads the sound is distorted until the lock up happens then sounds play without distortion,
but its all locked up, cant be stopped and needs a reboot,
anyone experience this? or have any idea what is going on?
Code:
// Step Sequencer Sample Player
// Teensy 4.0 + audio board + adafruit neotrellis rgb
// uses adafruit seesaw library https://github.com/adafruit/Adafruit_Seesaw
#include <Audio.h>
//#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "Adafruit_NeoTrellis.h"
// to do: audio
// to do: pink traveller + fast tempo + play pads fast causes freeze up, could be spi transfer problem
// to do: pink traveller + slow tempo + play pads causes freeze up less often
// ----------------------------------------FOOTSWITCH NEOPIXELS---------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN 5 // (Led Pin)
#define PIXEL_COUNT 2
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// ----------------------------------------FOOTSWITCH NEOPIXELS---------------------------------------------
// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlaySerialflashRaw playFlashRaw0;
AudioPlaySerialflashRaw playFlashRaw1;
AudioPlaySerialflashRaw playFlashRaw2;
AudioPlaySerialflashRaw playFlashRaw3;
AudioPlaySerialflashRaw playFlashRaw4;
AudioPlaySerialflashRaw playFlashRaw5;
AudioPlaySerialflashRaw playFlashRaw6;
AudioPlaySerialflashRaw playFlashRaw7;
AudioPlaySerialflashRaw playFlashRaw8;
AudioPlaySerialflashRaw playFlashRaw9;
AudioPlaySerialflashRaw playFlashRaw10;
AudioPlaySerialflashRaw playFlashRaw11;
AudioPlaySerialflashRaw playFlashRaw12;
AudioPlaySerialflashRaw playFlashRaw13;
AudioPlaySerialflashRaw playFlashRaw14;
AudioPlaySerialflashRaw playFlashRaw15;
AudioMixer4 mix1;
AudioMixer4 mix2;
AudioMixer4 mix3;
AudioMixer4 mix4;
AudioMixer4 mix5;
//AudioOutputAnalog dac;
AudioOutputI2S headphones;
// Create Audio connections between the components
//
AudioConnection patchCord1(playFlashRaw0, 0, mix1, 0);
AudioConnection patchCord2(playFlashRaw1, 0, mix1, 1);
AudioConnection patchCord3(playFlashRaw2, 0, mix1, 2);
AudioConnection patchCord4(playFlashRaw3, 0, mix1, 3);
AudioConnection patchCord5(playFlashRaw4, 0, mix2, 0);
AudioConnection patchCord6(playFlashRaw5, 0, mix2, 1);
AudioConnection patchCord7(playFlashRaw6, 0, mix2, 2);
AudioConnection patchCord8(playFlashRaw7, 0, mix2, 3);
AudioConnection patchCord9(playFlashRaw8, 0, mix3, 0);
AudioConnection patchCord10(playFlashRaw9, 0, mix3, 1);
AudioConnection patchCord11(playFlashRaw10, 0, mix3, 2);
AudioConnection patchCord12(playFlashRaw11, 0, mix3, 3);
AudioConnection patchCord13(playFlashRaw12, 0, mix4, 0);
AudioConnection patchCord14(playFlashRaw13, 0, mix4, 1);
AudioConnection patchCord15(playFlashRaw14, 0, mix4, 2);
AudioConnection patchCord16(playFlashRaw15, 0, mix4, 3);
AudioConnection patchCord17(mix1, 0, mix5, 0);
AudioConnection patchCord18(mix2, 0, mix5, 1);
AudioConnection patchCord19(mix3, 0, mix5, 2);
AudioConnection patchCord20(mix4, 0, mix5, 3);
//AudioConnection patchCord21(mix5, dac);
AudioConnection patchCord22(mix5, 0, headphones, 0);
AudioConnection patchCord23(mix5, 0, headphones, 1);
// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;
const int FlashChipSelect = 6; // digital pin for flash chip CS pin
Adafruit_NeoTrellis trellis;
int FSW2 = 2; // Start/stop footswitch
int FSW2State = HIGH;
int button1 = 3; // tap footswitch
int pad = -1;
int ssLedState = -1; //this variable tracks the startStop LED, negative if off, positive if on
int ttLedState = LOW; // ledState used to set the tempo LED
int lastTapState = LOW; //the last tap button state
int activeButton = 0;
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 500; // the debounce time; increase if the output flickers
unsigned long currentTimer[2] = { 750, 750 }; // array of most recent tap counts
unsigned long timeoutTime = 0; //this is when the timer will trigger next
unsigned long timeoutTime2 = 0; //this is when the timer will trigger next
unsigned long lastTap = 0; //this vairable determines the time of the most recent tap, used when recording data when button is pressed
unsigned long indicatorTimeout; //variable used to determine the duration of the led blinks on state
unsigned long indicatorTimeout2; //variable used to determine the duration of the pad X4 blinks on state
const byte NPATTERNS = 16;
byte patternBank[NPATTERNS][16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,};
int currentPattern[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,};
elapsedMillis button_hold_counter = 0;
int8_t held_button_id = -1;
int latchingMode = false;
int currentPad;
/****************************************************************************************************************/
void setup() {
Serial.begin(9600);
trellis.begin();
trellis.pixels.setBrightness(50);
pixels.begin(); // for start/stop and tempo LEDs
pixels.setBrightness(50);
pixels.setPixelColor(1, 220, 20, 60);
pixels.show();
pinMode( button1, INPUT_PULLUP ); //tap tempo button
pinMode(FSW2, INPUT_PULLUP);
Serial.println("NeoPixel Trellis started");
AudioMemory(40);
audioShield.enable();
audioShield.volume(0.9);
// reduce the gain on mixer channels, so more than 1
// sound can play simultaneously without clipping
mix1.gain(0, 0.9);
mix1.gain(1, 0.9);
mix1.gain(2, 0.9);
mix1.gain(3, 0.9);
mix2.gain(0, 0.9);
mix2.gain(1, 0.9);
mix2.gain(2, 0.9);
mix2.gain(3, 1);
mix3.gain(0, 0.9);
mix3.gain(1, 0.9);
mix3.gain(2, 0.9);
mix3.gain(3, 0.7);
mix4.gain(0, 0.9);
mix4.gain(1, 0.9);
mix4.gain(2, 0.9);
mix4.gain(3, 0.9);
mix5.gain(1, 1);
mix5.gain(2, 1);
SPI.setSCK(14); // Audio shield has SCK on pin 14
SPI.setMOSI(7); // Audio shield has MOSI on pin 7
SerialFlash.begin(FlashChipSelect);
//activate momentary pads and set callbacks
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis.registerCallback(i, momentary);
}
//do a little animation to show we're on
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255)));
trellis.pixels.show();
delay(25);
}
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
trellis.pixels.setPixelColor(i, 0x000000);
trellis.pixels.show();
delay(25);
}
}
/****************************************************************************************************************/
void loop() {
trellis.read();
delay(10); // stops freeze up in stop mode when playing pads quickly
startStopFSW();
runSeq();
}
/****************************************************************************************************************/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void playSamples() {
// if pad has a pattern, play that pads sample when pink traveller hits a latched pad
// on main screen only, blink samples pad led everytime its sample is triggered
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/****************************************************************************************************************/
void playSound(int num) {
char fileName[5];
sprintf(fileName, "%d.RAW", num);
switch (num) {
case 0: playFlashRaw0.play(fileName); break;
case 1: playFlashRaw1.play(fileName); break;
case 2: playFlashRaw2.play(fileName); break;
case 3: playFlashRaw3.play(fileName); break;
case 4: playFlashRaw4.play(fileName); break;
case 5: playFlashRaw5.play(fileName); break;
case 6: playFlashRaw6.play(fileName); break;
case 7: playFlashRaw7.play(fileName); break;
case 8: playFlashRaw8.play(fileName); break;
case 9: playFlashRaw9.play(fileName); break;
case 10: playFlashRaw10.play(fileName); break;
case 11: playFlashRaw11.play(fileName); break;
case 12: playFlashRaw12.play(fileName); break;
case 13: playFlashRaw12.play(fileName); break;
case 14: playFlashRaw14.play(fileName); break;
case 15: playFlashRaw15.play(fileName); break;
}
}
void runSeq() { // tap tempo & pink traveller
if (ssLedState > 0) { // if footswitch start
int tapState = digitalRead( button1 ); // button is first pressed down
if ( tapState == LOW && tapState != lastTapState ) {
currentTimer[1] = currentTimer[0]; // this takes the tap time done before it (measured in millis), sets it one back in the array,
currentTimer[0] = millis() - lastTap; // and then sets the other slot of the array to the length of the most recent tap time
lastTap = millis();
timeoutTime = 0;
timeoutTime2 = 0;
}
lastTapState = tapState; //variable used to determine the most recent state of the button
if ( millis() >= timeoutTime ) { //checks to see if it is time to turn on the tap fsw LED
indicatorTimeout = millis() + 150; //determines the length of time that the LED is on
//this will set the new tempo
timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);
}
if ( millis() >= timeoutTime2 ) { //checks to see if it is time to turn on the trellis LEDs
indicatorTimeout2 = millis() + 150; //determines the length of time that the trellis LED is on
//this will set the new tempo
timeoutTime2 = millis() + ((currentTimer[0] + currentTimer[1]) / 8); // Divide by 8 to make it 4 times faster
pad++;
pad %= 16;
}
//turns on tempo led for the length of indicatorTimeout and then turns it off
if ( millis() < indicatorTimeout ) {
pixels.setPixelColor(0, 0, 255, 255); // Tempo LED on
pixels.show();
}
pixels.setPixelColor(0, 0, 0, 0); // Tempo LED off
pixels.show();
//turns on trellis led for the length of indicatorTimeout, then turns it off, then moves to next led
if ( millis() < indicatorTimeout2 ) {
if (pad == held_button_id && latchingMode == true) {
trellis.pixels.setPixelColor(held_button_id, 0, 255, 0); // Make held LED green as pink traveller passes
trellis.pixels.show();
} else {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
trellis.pixels.setPixelColor(pad, 220, 20, 60); // The Pink Traveller
trellis.pixels.show();
// playSound(1); // not distorted
// playSamples();
// when pink traveller lands on a pad, read pattern arrays, then play any latched on sample here!!!
// pink pad on = read that pads saved Pattern array, play that pads sample when pink traveller lands on a latched pad
// if current Pads pattern
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
}
if (currentPattern[pad] == 1 && latchingMode == true) {
trellis.pixels.setPixelColor(pad, 0x0000ff); // make latched LEDs Blue again after pink traveller passes
playSound(1); // distorted with pink traveller, but not after trellis lockup ?
delay(5);
}
else {
trellis.pixels.setPixelColor(pad, 0); // Turn off pink traveller after it passes
}
}
}
/****************************************************************************************************************/
void startStopFSW() {
FSW2State = digitalRead(FSW2); //sample the state of the button - is it pressed or not?
if ( (millis() - lastDebounceTime) > debounceDelay) { //filter out any noise by setting a time buffer
if ( (FSW2State == LOW) && (ssLedState < 0) ) { //if the button has been pressed, lets toggle the LED from "off to on" or "on to off"
pixels.setPixelColor(1, 124, 252, 0); // start LED lawn green:
pixels.show();
// runSeq(); // START runSeq FUNCTION
Serial.println("Start");
pad = -1;
ssLedState = -ssLedState; //now the LED is on, we need to change the state
lastDebounceTime = millis(); //set the current time
}
else if ( (FSW2State == LOW) && (ssLedState > 0) ) { // Start/Stop LED State, starts & stops runSeq
pixels.setPixelColor(1, 220, 20, 60); // stop LED Red
pixels.show();
Serial.println("Stop");
// for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
// trellis.pixels.setPixelColor(i, 0); // stray pink traveller off when stopped
// trellis.pixels.show();
// } // causes some latched leds to turn off, possibly caused by ending runSeq abruptly?
ssLedState = -ssLedState; //
lastDebounceTime = millis(); //set the current time
}
} //close if
}
/****************************************************************************************************************/
void clearTrellis() {
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
trellis.pixels.setPixelColor(i, 0); // trellis leds off
}
}
/****************************************************************************************************************/
TrellisCallback momentary(keyEvent evt) { // Trellis Momentary Mode, Main Page, Start Page
if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) { // Check is the pad pressed?
Serial.println("NeoPixel Pressed");
if (evt.bit.NUM >= 0 && evt.bit.NUM <= 15) {
playSound(evt.bit.NUM);
delay(10);
}
trellis.pixels.setPixelColor(evt.bit.NUM, Wheel(map(evt.bit.NUM, 0, trellis.pixels.numPixels(), 0, 255))); //on rising
//reset timer for detecting a long press, which will take us to latching pattern page if held for 0.8secs
button_hold_counter = 0;
held_button_id = evt.bit.NUM;
}
else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) { // or is the pad released?
//Serial.println("NeoPixel Released");
trellis.pixels.setPixelColor(evt.bit.NUM, 0); //off falling
if (held_button_id == evt.bit.NUM) { // if the held button has been released,
if (button_hold_counter >= 800) { // if the button was held for 800 ms
Serial.println("Long press detected");
// *Load pattern input page for pressed pad HERE*
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // Activate latching trellis buttons
trellis.registerCallback(i, latching);
}
latchingMode = true;
loadPattern(held_button_id);
}
else {
held_button_id = -1; // Resetting the held button id means we are no longer tracking that long press.
}
}
}
trellis.pixels.show(); // Turn on/off the neopixels
return 0;
}
/****************************************************************************************************************/
TrellisCallback latching(keyEvent evt) { // Trellis Latching Mode
if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) { // if any pad button is pressed
//reset timer for detecting a long press, which will take us to momentary main page if held for 0.8secs
button_hold_counter = 0;
{
if (currentPattern[evt.bit.NUM] == 0) {
trellis.pixels.setPixelColor(evt.bit.NUM, 0x0000FF); // if off latch on
currentPattern[evt.bit.NUM] = 1; // save state to array
savePattern(held_button_id); // save array to array bank
}
else if (currentPattern[evt.bit.NUM] == 1) {
trellis.pixels.setPixelColor(evt.bit.NUM, 0); // if on turn off
currentPattern[evt.bit.NUM] = 0; // save state to array
savePattern(held_button_id); // save array to array bank
}
trellis.pixels.show();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// long press any pad to exit pattern mode without changing current pattern
if (button_hold_counter >= 800) {
Serial.println("Long press detected, loading main page");
if (currentPattern[evt.bit.NUM] == 1) {
currentPattern[evt.bit.NUM] = 0;
savePattern(held_button_id); // save array to array bank
}
else if (currentPattern[evt.bit.NUM] == 0) { // if on turn off
currentPattern[evt.bit.NUM] = 1;
savePattern(held_button_id); // save array to array bank
}
// long press any pad to exit pattern mode without changing current pattern
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // Activate momentary trellis buttons
trellis.registerCallback(i, momentary);
}
clearTrellis();
latchingMode = false;
}
trellis.pixels.show();
return 0;
}
/****************************************************************************************************************/
void loadPattern(byte patternToLoad)
{
Serial.print("loading pattern "); Serial.println(patternToLoad);
for (byte ii = 0; ii < 15; ii++)
currentPattern[ii] = patternBank[patternToLoad][ii];
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { // load stored pattern
if (currentPattern[i] == 1) {
trellis.pixels.setPixelColor(i, 0x0000FF); // stored blue leds on
}
}
trellis.pixels.setPixelColor(held_button_id, 0, 255, 0); // held button green
}
/****************************************************************************************************************/
void savePattern(byte patternToSave)
{
Serial.print("saving pattern "); Serial.println(patternToSave);
Serial.print("held button id "); Serial.println(held_button_id);
for (byte ii = 0; ii < 15; ii++)
patternBank[patternToSave][ii] = currentPattern[ii];
}
/****************************************************************************************************************/
// Input a value 0 to 255 to get a rainbow color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
if (WheelPos < 85) {
return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if (WheelPos < 170) {
WheelPos -= 85;
return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
return 0;
}