This is a learning project - doing the steps from scratch is more important than polished performance of the final product.
Objective: Control brightness of three LEDs based on the signal strength in the low/mid/high frequency range of an analog audio signal.
What I can do already:
- My home-cooked FFT (written in the Arduino IDE) works correctly on discretized sinusoidal functions. It may well be slow compared to more professional products. So far I have only tested it by printing out the FFT array.
- I can feed byte blocks from an mp3-file via Python; I can visualize the volume of the entirety of the signal. (Tested with a short techno mp3 - yes, the beat indeed drops.)
As a mathematician (as opposed to an engineer), I'd like to ask for some pointers on where to pick up some knowledge regarding the next step: feeding in an audio signal via either audio output (headphone jack) or USB (like I would for running a USB headset).
I am vaguely aware of the following:
- Analog audio signals are symmetric AC signals and need to be biased around 1.5-ish volts and kept 0 to 3-ish Volts peak-to-peak to be used by the analog input pins for the Due. For this, I'd like to deploy a simple solution (we are not talking about high-fidelity signal processing here) that can be done on a breadboard.
I have no knowledge at all of the properties of the USB audio signal that drives a headset, but again, I'm not asking you to teach that to me, just to make my search for tutorials a bit more focused.
Here's my block-reading and processing loop, if you are interested:
const int numberLED = 3;
const int buffersPerPacket = 32;
const int bufferSize = 80;
const int packetSize = buffersPerPacket * bufferSize;
const int analogOutPin[numberLED] = {9, 10, 11}; // Analog output pin that the LED is attached to
unsigned int packetData[packetSize] = {};
unsigned int channelData[numberLED] = {}; // value output to the PWM (analog out)
unsigned char buffer[packetSize] = {};
void setup() {
// initialize serial communications at 9600 bps:
for(int k=0; k<numberLED; k++){
pinMode(analogOutPin[k], OUTPUT);
}
Serial.setTimeout(1);
Serial.begin(500000);
}
void processPacket(unsigned int packetData[packetSize], unsigned int channelData[numberLED])
{
// omitted, works as intended
}
void loop() {
// Do nothing while there is not enough data
while (Serial.available() < bufferSize){;}
// Read sound packet into array
for (int b=0; b<buffersPerPacket; b++){
Serial.readBytes(buffer, bufferSize);
for(int k=0; k<bufferSize; k++){
packetData[b*bufferSize + k] = buffer[k];
}
while (Serial.available() < bufferSize){;}
}
// Send packet for processing, receive channel data for three LEDs
processPacket(packetData, channelData);
// Set brightness of three LEDs
for(int k=0; k<numberLED; k++){
analogWrite(analogOutPin[k], channelData[k]);
}
}
And here's the Python loop feeding the blocks from mp3:
#mp3 processing omitted
ardu = serial.Serial('/dev/cu.usbmodem14101', baudrate=500000, timeout=.1)
packetSize = 80
numberOfPackets = 10000
# bsignal contains the sound in the range from 0 to 255.
#signal contains the blocks, ready to be shipped one-by-one via a struct
signal = [bsignal[i:i+packetSize] for i in range(0, packetSize*numberOfPackets, packetSize)]
time.sleep(1)
start_time = time.monotonic()
for sig in signal:
ardu.write(struct.pack(str(packetSize)+'c', *sig))
end_time = time.monotonic()
print(timedelta(seconds=end_time - start_time))
ardu.close()