Hello friends! About 7 years ago, I built an LED art project using the Teensy 3.2, OctoWS2811, and an Adafruit dev board for the MAX9814. MAX9814 is an amplifier with auto gain. It is analog out, not I2S.
Because of supply chain issues resulting in Teensy 3.2 not being available, I upgraded to the Teensy 4.1. Everything related to the LEDs and other features seems to be working perfectly, but the audio portion of the project is broken.
I designed my project around the example in File > Examples > OctoWS2811 > SpectrumAnalyzer. The code for this example is pasted below (even though everyone has it with Teensyduino). When I originally made this art piece, I leaned hard on this spectrum analyzer example right down to the hardware pin suggested for the analog input: A3.
I read up on a similar issue someone was having in this PJRC forum thread here. In that thread, Paul suggests a possible fix, but he notes:
So, back to what I am doing. When I upgraded to Teensy 4.1, the OctoWS2811 > SpectrumAnalyzer example no longer works. I think what's going on is that OctoWS2811 uses DMA channels 1, 2 , and 3. But and the fix that Paul suggested uses AudioOutputI2S, which also uses DMA channel 1 and 2.
Given all of this, does anyone know of a workaround that will allow me to continue using my Teensy 4.1, OctoWS2811B, and MAX9814 with audio going to pin A3? I already have PCBs printed and assembled for this project, so changing pins or hardware at this point would be a major bummer.
Because of supply chain issues resulting in Teensy 3.2 not being available, I upgraded to the Teensy 4.1. Everything related to the LEDs and other features seems to be working perfectly, but the audio portion of the project is broken.
I designed my project around the example in File > Examples > OctoWS2811 > SpectrumAnalyzer. The code for this example is pasted below (even though everyone has it with Teensyduino). When I originally made this art piece, I leaned hard on this spectrum analyzer example right down to the hardware pin suggested for the analog input: A3.
I read up on a similar issue someone was having in this PJRC forum thread here. In that thread, Paul suggests a possible fix, but he notes:
(Note: I am aware Paul's workaround here suggests analog into A2 instead of A3 where my audio input is going to).Here is a program which will run on Teensy 4.0 using 1.52-beta4 and gives you FFT of an audio signal on analog pin A2. This will work without the audio shield present, but I2S output (or any other I/O object that causes the library to update) is currently required. Future versions will remove this requirement.
So, back to what I am doing. When I upgraded to Teensy 4.1, the OctoWS2811 > SpectrumAnalyzer example no longer works. I think what's going on is that OctoWS2811 uses DMA channels 1, 2 , and 3. But and the fix that Paul suggested uses AudioOutputI2S, which also uses DMA channel 1 and 2.
Given all of this, does anyone know of a workaround that will allow me to continue using my Teensy 4.1, OctoWS2811B, and MAX9814 with audio going to pin A3? I already have PCBs printed and assembled for this project, so changing pins or hardware at this point would be a major bummer.
Code:
// LED Audio Spectrum Analyzer Display
//
// Creates an impressive LED light show to music input
// using Teensy 3.1 with the OctoWS2811 adaptor board
// http://www.pjrc.com/store/teensy31.html
// http://www.pjrc.com/store/octo28_adaptor.html
//
// Line Level Audio Input connects to analog pin A3
// Recommended input circuit:
// http://www.pjrc.com/teensy/gui/?info=AudioInputAnalog
//
// This example code is in the public domain.
#include <OctoWS2811.h>
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>
// The display size and color to use
const unsigned int matrix_width = 60;
const unsigned int matrix_height = 32;
const unsigned int myColor = 0x400020;
// These parameters adjust the vertical thresholds
const float maxLevel = 0.5; // 1.0 = max, lower is more "sensitive"
const float dynamicRange = 40.0; // total range to display, in decibels
const float linearBlend = 0.3; // useful range is 0 to 0.7
// OctoWS2811 objects
const int ledsPerPin = matrix_width * matrix_height / 8;
DMAMEM int displayMemory[ledsPerPin*6];
int drawingMemory[ledsPerPin*6];
const int config = WS2811_GRB | WS2811_800kHz;
OctoWS2811 leds(ledsPerPin, displayMemory, drawingMemory, config);
// Audio library objects
AudioInputAnalog adc1(A3); //xy=99,55
AudioAnalyzeFFT1024 fft; //xy=265,75
AudioConnection patchCord1(adc1, fft);
// This array holds the volume level (0 to 1.0) for each
// vertical pixel to turn on. Computed in setup() using
// the 3 parameters above.
float thresholdVertical[matrix_height];
// This array specifies how many of the FFT frequency bin
// to use for each horizontal pixel. Because humans hear
// in octaves and FFT bins are linear, the low frequencies
// use a small number of bins, higher frequencies use more.
int frequencyBinsHorizontal[matrix_width] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 3, 4, 4, 4, 4, 4, 5,
5, 5, 6, 6, 6, 7, 7, 7, 8, 8,
9, 9, 10, 10, 11, 12, 12, 13, 14, 15,
15, 16, 17, 18, 19, 20, 22, 23, 24, 25
};
// Run setup once
void setup() {
// the audio library needs to be given memory to start working
AudioMemory(12);
// compute the vertical thresholds before starting
computeVerticalLevels();
// turn on the display
leds.begin();
leds.show();
}
// A simple xy() function to turn display matrix coordinates
// into the index numbers OctoWS2811 requires. If your LEDs
// are arranged differently, edit this code...
unsigned int xy(unsigned int x, unsigned int y) {
if ((y & 1) == 0) {
// even numbered rows (0, 2, 4...) are left to right
return y * matrix_width + x;
} else {
// odd numbered rows (1, 3, 5...) are right to left
return y * matrix_width + matrix_width - 1 - x;
}
}
// Run repetitively
void loop() {
unsigned int x, y, freqBin;
float level;
if (fft.available()) {
// freqBin counts which FFT frequency data has been used,
// starting at low frequency
freqBin = 0;
for (x=0; x < matrix_width; x++) {
// get the volume for each horizontal pixel position
level = fft.read(freqBin, freqBin + frequencyBinsHorizontal[x] - 1);
// uncomment to see the spectrum in Arduino's Serial Monitor
// Serial.print(level);
// Serial.print(" ");
for (y=0; y < matrix_height; y++) {
// for each vertical pixel, check if above the threshold
// and turn the LED on or off
if (level >= thresholdVertical[y]) {
leds.setPixel(xy(x, y), myColor);
} else {
leds.setPixel(xy(x, y), 0x000000);
}
}
// increment the frequency bin count, so we display
// low to higher frequency from left to right
freqBin = freqBin + frequencyBinsHorizontal[x];
}
// after all pixels set, show them all at the same instant
leds.show();
// Serial.println();
}
}
// Run once from setup, the compute the vertical levels
void computeVerticalLevels() {
unsigned int y;
float n, logLevel, linearLevel;
for (y=0; y < matrix_height; y++) {
n = (float)y / (float)(matrix_height - 1);
logLevel = pow10f(n * -1.0 * (dynamicRange / 20.0));
linearLevel = 1.0 - n;
linearLevel = linearLevel * linearBlend;
logLevel = logLevel * (1.0 - linearBlend);
thresholdVertical[y] = (logLevel + linearLevel) * maxLevel;
}
}
Last edited: