I am working on a project to play WAV files on an Arduino Mega 2560 using an SD card, without any sound-related libraries. I am converting the PCM data from the WAV file to PWM signals, then using a low-pass filter (LPF), amplifier (AMP), and speaker to produce sound.
Currently, it can produce sound, and the There is also a slight loss in tone, and there is an big issue with the tempo lagging. The timer interrupt (OCR1A) is set to 44.1kHz, and PCM data is uploaded to OCRnx at every interrupt. Then OCRnx generates the PWM signal (using Timer2 & Timer4 at 62.5kHz, 8-bit fast PWM). The WAV files also have a 44.1kHz sampling rate.
Additionally, not only is the sound lagging in tempo, but noise is also generated, and eventually, the Arduino resets itself.
#include <SD.h>
#include <SPI.h>
#define SD_CS 53
#define SAMPLE_RATE 44.1e3
File root, wav;
unsigned char buf[6000];
volatile uint16_t playing_Index = 0;
volatile uint16_t write_Index = 3000;
volatile bool bufferFlag = false;
uint32_t dataSize;
uint16_t endTime;
volatile uint16_t audioSum = 0;
void setup() {
SD.begin(SD_CS);
initializeWav(0);
initializePins();
initializeTimerInterrupt();
}
void loop() {
if (playing_Index >= 6000) {
playing_Index = 0;
} else if (playing_Index == 3000 || playing_Index == 0) {
updateBuffer();
}
}
void initializeWav(int8_t fileIndex) {
playing_Index = 0;
dataSize = 0;
wav = SD.open("LIGHT441.WAV");
wav.seek(40);
for (uint8_t i = 0; i < 4; i++)
dataSize += (uint32_t)wav.read() << (8 * i);
endTime = dataSize / (4 * SAMPLE_RATE);
wav.read(buf, 6000);
}
void initializePins() {
DDRB |= 0b00010000;
DDRH |= 0b01011000;
}
void initializeTimerInterrupt() {
TCCR4B &= ~_BV(WGM43);
TCCR4B |= _BV(WGM42);
TCCR4A &= ~_BV(WGM41);
TCCR4A |= _BV(WGM40);
TCCR4A |= _BV(COM4A1);
TCCR4A &= ~_BV(COM4A0);
TCCR4A |= _BV(COM4B1);
TCCR4A &= ~_BV(COM4B0);
TCCR4B &= ~_BV(CS42);
TCCR4B &= ~_BV(CS41);
TCCR4B |= _BV(CS40);
TCCR2B &= ~_BV(WGM22);
TCCR2A |= _BV(WGM21);
TCCR2A |= _BV(WGM20);
TCCR2A |= _BV(COM2A1);
TCCR2A &= ~_BV(COM2A0);
TCCR2A |= _BV(COM2B1);
TCCR2A &= ~_BV(COM2B0);
TCCR2B &= ~_BV(CS22);
TCCR2B &= ~_BV(CS21);
TCCR2B |= _BV(CS20);
TCCR1B &= ~_BV(WGM13);
TCCR1B |= _BV(WGM12);
TCCR1A &= ~_BV(WGM11);
TCCR1A &= ~_BV(WGM10);
TCCR1B &= ~_BV(CS12);
TCCR1B &= ~_BV(CS11);
TCCR1B |= _BV(CS10);
OCR1A = 362;
TCNT1 = 0;
TIMSK1 |= _BV(OCIE1A);
TIFR1 |= _BV(OCF1A);
sei();
}
void updateBuffer() {
bufferFlag = false;
wav.read(&buf[write_Index], 3000);
write_Index = (write_Index == 0) ? 3000 : 0;
}
ISR(TIMER1_COMPA_vect) {
audioSum = (buf[playing_Index] | (buf[playing_Index + 1] << 8)) + 0x8000;
OCR2B = audioSum & 0x00FF;
OCR2A = (audioSum & 0xFF00) >> 8;
audioSum = (buf[playing_Index + 2] | (buf[playing_Index + 3] << 8)) + 0x8000;
OCR4B = audioSum & 0x00FF;
OCR4A = (audioSum & 0xFF00) >> 8;
playing_Index += 4;
}