nathanSuperStar
Member
Hey guys, it's me Nathan Ramanathan; that guy who made MusicWithoutDelay library:https://github.com/nathanRamaNoodles/Polyphonic-Synth-Engine.
Anyway, I've decided to up my game a bit by writing some code that can mix multiple WAV files from an SD card via PWM on a timer interrupt.
**I know the Teensy Audio library has this, but I want to challenge myself. Also, I'm gunna port this code to Arduino Uno for my library.
The good news: it works and is very efficient at mixing many audio files at the same time.
The bad news: There's static noise(even with a Low Pass filter). And the audio quality is not that pretty.
But it still "sounds" possible to get better quality audio. Pun Intended .
I was wondering if you guys could try and optimize my code to better quality. I've commented on my code. I've got two buffers for the SD card and the extracting of the files is done in the main loop(), while the audio is played in the timer interrupt portion.
Thanks,
-Nathan Ramanathan
P.S.
Paul, thanks for the free Teensys. I absolutely love them. Also, I am updating my library to allow playing SD files on an Arduino Uno.
Code:
Anyway, I've decided to up my game a bit by writing some code that can mix multiple WAV files from an SD card via PWM on a timer interrupt.
**I know the Teensy Audio library has this, but I want to challenge myself. Also, I'm gunna port this code to Arduino Uno for my library.
The good news: it works and is very efficient at mixing many audio files at the same time.
The bad news: There's static noise(even with a Low Pass filter). And the audio quality is not that pretty.
But it still "sounds" possible to get better quality audio. Pun Intended .
I was wondering if you guys could try and optimize my code to better quality. I've commented on my code. I've got two buffers for the SD card and the extracting of the files is done in the main loop(), while the audio is played in the timer interrupt portion.
Thanks,
-Nathan Ramanathan
P.S.
Paul, thanks for the free Teensys. I absolutely love them. Also, I am updating my library to allow playing SD files on an Arduino Uno.
Code:
Code:
/*
SD WAV mixer via PWM
by Nathan Ramanathan (Github: https://github.com/nathanRamaNoodles)
Compatibility: Arduino Uno/nano/mini, Teensy 3.x
*/
//In this project, I decided to improve my c++ skills by playing audio from a SD card onto a PWM pin.
//Also, I added the ability to mix WAV file sounds, from my other library: https://github.com/nathanRamaNoodles/Polyphonic-Synth-Engine
//I know this is hard to do, but so far the quality sounds fine on a Arduino UNO and
//on a Teensy. But now, I want to make my code more efficient and sound better.
//I'm using a microSD card adapter from Ebay. Unfortuantely, the SD card only opens when The Teensy's CPU rate is 24 Mhz.
//On an Arduino Uno, the audio outputs at pin 3.
//On Teensy, the Audio is on pin 3(but you can change it below).
//Schematic: https://raw.githubusercontent.com/nathanRamaNoodles/MusicWithoutDelay-LIbrary/master/MusicWithoutDelay.png
//Directions:
//You can do two things:
//1. Do nothing: the microcontroller will play one song.
//2. Type 'p' in Serial: The microcontroller will play two tracks at the same time by mixing them.
#include <SPI.h>
#include "SdFat.h" //faster SD library
SdFat SD;
char dataFilename[] = "zelda32k.wav"; //wav files
char dataFilename2[] = "guardian.wav";
#define FS_music 32E3 //Sample Rate at 32,000 Hz
#if defined(__arm__) && defined(TEENSYDUINO)
#define buffSize 1000 //Buffer Size
static void timerInterrupt();
static IntervalTimer sampleTimer;
const int SD_ChipSelectPin = 10;
const int audioPin = 3;
#else
#define buffSize 128
#define SET(x,y) (x |=(1<<y)) //-Bit set/clear macros
#define CLR(x,y) (x &= (~(1<<y))) // |
const int SD_ChipSelectPin = 4;
#endif
File dataFile;
File guardianFile;
uint8_t *buffPointer; //pointer
uint8_t *buffPointer_2;
uint8_t buf[buffSize]; //buffer
uint8_t buf_2[buffSize];
uint8_t backBuf[buffSize]; //backup buffer for efficiency
uint8_t backBuf_2[buffSize];
uint8_t lastValue = 0; //most recent value from SD card played to Speaker
uint8_t lastValue_2 = 0;
uint32_t filePosition = 0; //Current file position
uint32_t filePosition_2 = 0;
bool buffTrigger = false; //Triggered when Buffer is ready.(not a good idea, due to lag)
bool backBuffFlag = false; //Tells when Speaker has finished playing buffer
bool finished = false; //song is finished
bool finished_2 = false;
bool toggle = false; //alternate between backup and main buffer
void setup() {
Serial.begin(115200);
// while (!Serial);
Serial.println(F("Hi there :)\nType 'p' to play both wav files at same time, or do nothing to let me play only one song."));
Serial.println(F("Starting simple WAV demo\n"));
if (!SD.begin(SD_ChipSelectPin)) {
Serial.println("SD fail");
return;
}
dataFile = SD.open(dataFilename);
guardianFile = SD.open(dataFilename2);
if ( !dataFile || !guardianFile) {
Serial.println("File not opened");
}
dataFile = SD.open(dataFilename, FILE_READ);
guardianFile = SD.open(dataFilename2, FILE_READ);
dataFile.seek(44); //seek to beginning of main wav file data
filePosition = 44;
guardianFile.seek(44);
filePosition_2 = 44;
finished_2 = true; //this stops the guardianFile song
resume(); // setup our timerInterrupts
}
void suspend()
{
#if defined(__AVR__)
CLR(TIMSK1, OCIE1B); //-Stop audio interrupt
#endif
}
void resume()
{
#if defined(__arm__) && defined(TEENSYDUINO)
sampleTimer.begin(timerInterrupt, 1000000.0 / FS_music);
analogWriteFrequency(audioPin, FS_music);
#else
cli();
TCCR1A = 0x00; //-Start audio interrupt
TCCR1B = 0x09;
OCR1A = 16000000.0 / FS_music; //-Auto sample rate
SET(TIMSK1, OCIE1B); //-Start audio interrupt
sei();
TCCR2A = 0xB3; //-8 bit audio PWM
TCCR2B = 0x01; // |
OCR2B = 127;
SET(DDRD, 3); //-PWM pin at PIN 3 on Arduino Uno
#endif
}
// Function to copy 'len' elements from 'src' to 'dst'
void copyArray(uint8_t* src, uint8_t* dst, int len) {
memcpy(dst, src, sizeof(src[0])*len);
}
bool secondSongStarted = false;
void loop() {
// if (!secondSongStarted && filePosition > (dataFile.size() / 2)) { //Starts second song halfway through first song
// secondSongStarted = true;
// finished_2 = false;
// filePosition_2 = 44;
// guardianFile.seek(filePosition_2);
// }
if (Serial.available()) {
char str = Serial.read();
switch (str) {
case 'p':
// secondSongStarted = false;
finished = false;
filePosition = 44;
dataFile.seek(filePosition);
finished_2 = false;
filePosition_2 = 44;
guardianFile.seek(filePosition_2);
break;
}
}
if (!backBuffFlag) {
toggle = !toggle;
if (toggle) {
if (!finished)dataFile.read(backBuf, sizeof(backBuf));
if (!finished_2)guardianFile.read(backBuf_2, sizeof(backBuf_2));
}
else {
if (!finished)dataFile.read(buf, sizeof(buf));
if (!finished_2)guardianFile.read(buf_2, sizeof(buf_2));
}
backBuffFlag = true;
filePosition += buffSize;
filePosition_2 += buffSize;
if (!finished && (dataFile.size() < filePosition)) {
finished = true;
Serial.println("Done 1");
}
if (!finished_2 && (guardianFile.size() < filePosition_2)) {
finished_2 = true;
Serial.println("Done 2");
}
}
else {
if (!buffTrigger) {
if (!finished) {
if (toggle) {
buffPointer = backBuf;
}
else {
buffPointer = buf;
}
}
if (!finished_2) {
if (toggle) {
buffPointer_2 = backBuf_2;
}
else {
buffPointer_2 = buf_2;
}
}
toggle = !toggle;
buffTrigger = true;
backBuffFlag = false;
}
}
}
int location = 0;
#if defined(__AVR__)
SIGNAL(TIMER1_COMPB_vect)
#elif defined(__arm__) && defined(TEENSYDUINO)
static void timerInterrupt()
#endif
{
// if (buffTrigger) { //I've commented this out because its not efficient
if (location < buffSize) {
int sample = ((((finished) ? lastValue : lastValue = buffPointer[location])
+ ((finished_2) ? lastValue_2 : lastValue_2 = buffPointer_2[location])//add our outputs
)
>> 2 //Shift 2 bits to the right. This is faster than dividing by Four.
)
+ 127 //add 127 to make it a bit louder
;
location++; //increment our pointer's location
//OUTPUT to our Audio pin.
#if defined(__arm__) && defined(TEENSYDUINO)
if (sample != 127)analogWrite(audioPin, sample);
#else
if (sample != 127)OCR2B = sample;
#endif
}
else {
buffTrigger = false;
location = 0;
}
// }
}