... that was about 5 hours ago.
I can't even get this thing to play intelligible audio via 115kbps serial. In the best mode I could find - 11050Hz, 8 bit audio " << 3" padded - it made a loud and scratchy noise that barely even resembled the original audio playing slowly.
It was kinda like it was--... hmm. Like, playing 2 bytes per sample, somehow broken up or otherwise distorted by the transmitter. Or misinterpreted by the receiver. OK, so that's a possibility. 115Kbps serial maybe dropping something? Maybe my terminal software (Tera Term, to send the binary data) was scrambling the data. Maybe the Serial.available() was seeing 2 bytes (data, then null to end?)...? So I made a simple "LCD console" sketch that spits the hex values onto screen as they come in over serial (using 2 line buffers so it can do a scrolling-line-break when it reaches the end). That worked pretty well after a little debugging. Every button I pressed resulted in the correct ASCII value on screen (1 = "31", 9 = "39", etc). Cool...
... Then I sent my test "ffff.bin" file to test it. "ffff.bin" is just what it implies, a big chunk (of unknown Ctrl+C + Ctrl+VVV length) of FF bytes. What'd the LCD do? "3F3F3F3F"... WHAT?! So I poked through the options. And right on the file send dialog (which looks just like a standard Open dialog) there was an extra checkbox: "Binary". SERIOUSLY?! It's 2011, and we have a "Binary" on/off selection? Honestly, I'd always wished that old 7-bit encodings would disappear completely. FTP doesn't need it (a huge PITA of data corruption), and serial sure doesn't need it unless 7-bit mode is selected in the connection options. /facepalm. There goes about 4 hours of debugging.
In glee, I re-uploaded the SerialSound sketch, and selected my file ("Fischerspooner - Emerge (Naughty's Chiefrocker Remix).wav" - eat that, vinyl collectors!) to send. Bnchh! There goes the song, in perfect tune, out my test board's little speaker.
So yeah, the WaveShield can indeed be used for generated audio. Next stop to play with: real-time mixing and sequencing with Arduino!
(well, first I need to find a way to reduce the number of clock cycles used by the writer, determine how much "free time" it has after sending the data bits, and figure out how to *very quickly* read from some kind of storage!)
Here's the SerialAudio:
Code: Select all
#include <mcpDac.h> // part of WaveHC library "included with" WaveShield
byte audioData;
// byte lohi = 0;
void setup() {
mcpDacInit();
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
Serial.begin(115200);
mcpDacSend(0); // clear it, in case it... well... yeah.
}
void loop() {
if (Serial.available()) {
// lo byte or hi byte?
// if (lohi ^= 0xFF) {
// audioData = Serial.read(); // lo byte
// return;
// } else {
// audioData |= Serial.read() << 8; // hi byte
// Serial.println(audioData,HEX);
// }
audioData = Serial.read();
} else {
mcpDacSend(audioData << 4);
}
}
showSerialOnLCD:
Code: Select all
#include <LiquidCrystal.h>
LiquidCrystal lcd(A5, A4, A3, A2, A1, A0); // was originally using full WaveShield with SD and audio pins... oh, BTW, did I mention I'm using a BoArduino with wires hanging all over the place through a ScrewShield to the WaveShield? =P
char lcdline1[17] = " ";
char lcdline2[17] = " ";
char valbuf[6];
byte bufptr = 0;
void lcdPrint(char *data) {
if (bufptr >= 15){
strcpy(lcdline1, lcdline2);
bufptr = 0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print(lcdline1);
}
lcd.setCursor(0,1);
strcpy(lcdline2 + bufptr,data);
lcd.print(lcdline2);
bufptr += strlen(data);
}
void lcdPrint(uint16_t val, uint8_t base) {
utoa(val,valbuf,base);
lcdPrint(valbuf);
}
void setup() {
lcd.begin(16,2);
Serial.begin(115200);
}
void loop() {
if (Serial.available()) {
lcdPrint(Serial.read(), HEX);
}
}
edit: WOW. With a little combination of the two above programs (dump value to LCD with scrolling, and playing audio from serial), I see that there are only 5 "mcpDacSend" iterations between audio samples... if there is no serial data, it sends the same sample again, and increments a counter. When it gets a new sample, it resets the counter. Timer1 is set to 256 prescaler and has an overflow interrupt that sets "doLCDUpdate = 1;". If doLCDUpdate is set when a new sample is grabbed, it updates the LCD with the count and resets doLCDUpdate. And it shows I don't really have much room to move unless I optimize the data transfer a bit
editedit: As an example of what I could do with generated values... I have quite the nice-sounding "echo" going on right now. I created a 1.5KB audio buffer in RAM (after pushing aside much of the garbage static functions added by including mcpDac.h, which ate up 2 512-byte buffers discovered using "avr-nm"), then wrote/read to the rolling buffer and averaged the N + (N+1/2buf) samples whenever a new sample is read via serial. Amusingly, this processing added almost zero size at all (program actually shrunk from 6000 bytes to about 3,400 after integrating the mcpDac.h definitions into the code and removing the #include), and the timing is still about the same. Definitely the most action my AVR chip has ever seen in RAM