I'm working on a "simple" project which involves playing audio. Basically there's a box with a button on the front. You press the button, the music starts playing (a 10 second continuous loop). You press the button again, it stops playing...or perhaps the volume goes up, or perhaps it stops for 3 minutes and starts playing again. I haven't decided on that bit yet :P
Anyway, I bought all the components needed to build the Trinket Audio Player:
https://learn.adafruit.com/trinket-audio-player
I followed the tutorials, made some modifications, wasted a lot of time messing about with Windows 8 issues before finally getting it working. It worked great, until I went to solder it all together for the final version.
If you haven't already figured out the fatal flaw in my project idea yet, it's that the Trinket Audio Player project uses up all 5 of the Trinket's pins, leaving none for the button! Somehow along the way I never realised this, one of the downsides of only being able to work on projects for a few hours a month.
I didn't want to go down the path of using a WaveShield with an SD Card just to play a looping 10 second audio clip, so I did some digging and discovered this thread:
http://forums.adafruit.com/viewtopic.php?f=25&t=49324
So it definitely seems possible.
I have a spare Arduino Uno, so I wired up the Trinket Audio Player circuit to the Uno and managed to get it to read from the SPI Flash chip. It's definitely reading, it told me the correct sample rate and I can get it to dump out the bytes to the Serial console. The Adafruit_Tinyflash library made that very easy to achieve, I was up and running with an hour.
The problem is that all the code related to the playback (in the TrinketPlayer sketch) is targeted specifically to the Trinket, and I can't decipher much of it. I've been hacking and modifying the code, removing lines that cause compiler errors and scouring forums trying to convert it from "Trinket" to "Uno".
I did some research and I believe I can use Digital Pin #3 for a PWM output pin, so I've connected the amplifier input to that. It plays a short burst of audio over and over, like it's stuck on the first frame.
I don't understand any of the timer/counter code, it all goes right over my head. Randomly changing values isn't going to solve the problem, and I don't understand any of the tutorials I've found explaining this.
Here's the code I have, it's a giant mess but it's as far as I've got:
Code: Select all
#include <SPI.h>
#include <Adafruit_TinyFlash.h>
Adafruit_TinyFlash flash;
uint16_t sample_rate, delay_count;
uint32_t samples;
volatile uint32_t index = 0L;
void setup() {
Serial.begin(57600);
uint8_t data[6];
uint32_t bytes;
if(!(bytes = flash.begin())) { // Flash init error?
for(;; PORTB ^= 2, delay(250)); // Blink 2x/sec
}
// First six bytes contain sample rate, number of samples
flash.beginRead(0);
for(uint8_t i=0; i<6; i++) data[i] = flash.readNextByte();
sample_rate = ((uint16_t)data[0] << 8)
| (uint16_t)data[1];
samples = ((uint32_t)data[2] << 24)
| ((uint32_t)data[3] << 16)
| ((uint32_t)data[4] << 8)
| (uint32_t)data[5];
Serial.print("Sample Rate: ");
Serial.println(sample_rate);
// Audio begins at next byte, so DON'T endRead() here
pinMode(3, OUTPUT); // Enable PWM output pin
// NOTE: CODE BELOW IS PROBABLY COMPLETELY WRONG
/* PLLCSR |= _BV(PLLE); // Enable 64 MHz PLL
delayMicroseconds(100); // Stabilize
while(!(PLLCSR & _BV(PLOCK))); // Wait for it...
PLLCSR |= _BV(PCKE); // Timer1 source = PLL*/
// Set up Timer/Counter1 for PWM output
TIMSK0 = 0; // Timer interrupts OFF
TCCR2B = _BV(CS10); // 1:1 prescale
GTCCR = _BV(COM2B1); // PWM B, clear on match
OCR1A = 255; // Full 8-bit PWM cycle
OCR1B = 127; // 50% duty at start
// Set up Timer/Counter0 for sample-playing interrupt.
// TIMER0_OVF_vect is already in use by the Arduino runtime,
// so TIMER0_COMPA_vect is used. This code alters the timer
// interval, making delay(), micros(), etc. useless (the
// overflow interrupt is therefore disabled).
// Timer resolution is limited to either 0.125 or 1.0 uS,
// so it's rare that the playback rate will precisely match
// the data, but the difference is usually imperceptible.
TCCR0A = _BV(WGM01) | _BV(WGM00); // Mode 7 (fast PWM)
if(sample_rate >= 31250) {
TCCR0B = _BV(WGM02) | _BV(CS00); // 1:1 prescale
OCR0A = ((F_CPU + (sample_rate / 2)) / sample_rate) - 1;
} else { // Good down to about 3900 Hz
TCCR0B = _BV(WGM02) | _BV(CS01); // 1:8 prescale
OCR0A = (((F_CPU / 8L) + (sample_rate / 2)) / sample_rate) - 1;
}
//TIMSK1 = _BV(OCIE0A); // Enable compare match, disable overflow
}
void loop() {
//OCR1B = flash.readNextByte(); // Read flash, write PWM reg.
analogWrite(3,flash.readNextByte());
//Serial.println(flash.readNextByte());
if(++index >= samples) { // End of audio data?
index = 0; // We must repeat!
flash.endRead();
flash.beginRead(6); // Skip 6 byte header
}
}
Thanks
Edit: This post did help a little, but I'm still wading through it: http://www.protostack.com/blog/2010/09/ ... atmega168/