Permalink
Browse files

Added code to play sound effects.

  • Loading branch information...
1 parent 1110e67 commit 30a0106ac1b2398fe1c063948242ef54f0038cf1 @eholk committed Apr 3, 2012
Showing with 161 additions and 1 deletion.
  1. +6 −1 Moles/Moles.ino
  2. +155 −0 Moles/audio.cpp
View
@@ -17,7 +17,6 @@
when you whack the mole.
*/
-const int NUM_MOLES = 3;
const int RISE_FACTOR = 10000;
const int MIN_UP = 10000;
const int MAX_UP = 100000;
@@ -34,8 +33,12 @@ mole_t moles[] = {
{7, 2, 0}
};
+const int NUM_MOLES = sizeof(moles) / sizeof(moles[0]);
+
int score = 0;
+void startPlayback();
+
void setup() {
// Initialize random number generator
Serial.begin(9600);
@@ -45,6 +48,8 @@ void setup() {
pinMode(mole.in_pin, INPUT);
digitalWrite(mole.out_pin, HIGH);
}
+
+ startPlayback();
}
void loop() {
View
@@ -0,0 +1,155 @@
+// EDIT: To make this program work with an ATMega1280, use PWM pin 10 as speakerPin.
+
+/*
+ * speaker_pcm
+ *
+ * Plays 8-bit PCM audio on pin 11 using pulse-width modulation (PWM).
+ * For Arduino with Atmega168 at 16 MHz.
+ *
+ * Uses two timers. The first changes the sample value 8000 times a second.
+ * The second holds pin 11 high for 0-255 ticks out of a 256-tick cycle,
+ * depending on sample value. The second timer repeats 62500 times per second
+ * (16000000 / 256), much faster than the playback rate (8000 Hz), so
+ * it almost sounds halfway decent, just really quiet on a PC speaker.
+ *
+ * Takes over Timer 1 (16-bit) for the 8000 Hz timer. This breaks PWM
+ * (analogWrite()) for Arduino pins 9 and 10. Takes Timer 2 (8-bit)
+ * for the pulse width modulation, breaking PWM for pins 11 & 3.
+ *
+ * References:
+ * http://www.uchobby.com/index.php/2007/11/11/arduino-sound-part-1/
+ * http://www.atmel.com/dyn/resources/prod_documents/doc2542.pdf
+ * http://www.evilmadscientist.com/article.php/avrdac
+ * http://gonium.net/md/2006/12/27/i-will-think-before-i-code/
+ * http://fly.cc.fer.hr/GDM/articles/sndmus/speaker2.html
+ * http://www.gamedev.net/reference/articles/article442.asp
+ *
+ * Michael Smith <michael@hurts.ca>
+ */
+
+#include <stdint.h>
+#include <avr/interrupt.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <Arduino.h>
+
+#define SAMPLE_RATE 11025
+
+extern const int sounddata_length;
+extern const signed char *sounddata_data;
+
+void stopPlayback();
+
+/*
+ * The audio data needs to be unsigned, 8-bit, 8000 Hz, and small enough
+ * to fit in flash. 10000-13000 samples is about the limit.
+ *
+ * sounddata.h should look like this:
+ * const int sounddata_length=10000;
+ * const unsigned char sounddata_data[] PROGMEM = { ..... };
+ *
+ * You can use wav2c from GBA CSS:
+ * http://thieumsweb.free.fr/english/gbacss.html
+ * Then add "PROGMEM" in the right place. I hacked it up to dump the samples
+ * as unsigned rather than signed, but it shouldn't matter.
+ *
+ * http://musicthing.blogspot.com/2005/05/tiny-music-makers-pt-4-mac-startup.html
+ * mplayer -ao pcm macstartup.mp3
+ * sox audiodump.wav -v 1.32 -c 1 -r 8000 -u -1 macstartup-8000.wav
+ * sox macstartup-8000.wav macstartup-cut.wav trim 0 10000s
+ * wav2c macstartup-cut.wav sounddata.h sounddata
+ *
+ * (starfox) nb. under sox 12.18 (distributed in CentOS 5), i needed to run
+ * the following command to convert my wav file to the appropriate format:
+ * sox audiodump.wav -c 1 -r 8000 -u -b macstartup-8000.wav
+ */
+
+typedef unsigned char byte;
+
+int ledPin = 13;
+int speakerPin = 11;
+volatile uint16_t sample;
+byte lastSample;
+
+// This is called at 8000 Hz to load the next sample.
+ISR(TIMER1_COMPA_vect) {
+ if (sample >= sounddata_length) {
+ if (sample == sounddata_length + lastSample) {
+ stopPlayback();
+ }
+ else {
+ // Ramp down to zero to reduce the click at the end of playback.
+ OCR2A = sounddata_length + lastSample - sample;
+ }
+ }
+ else {
+ OCR2A = pgm_read_byte(&sounddata_data[sample]);
+ }
+
+ ++sample;
+}
+
+void startPlayback()
+{
+ pinMode(speakerPin, OUTPUT);
+
+ // Set up Timer 2 to do pulse width modulation on the speaker
+ // pin.
+
+ // Use internal clock (datasheet p.160)
+ ASSR &= ~(_BV(EXCLK) | _BV(AS2));
+
+ // Set fast PWM mode (p.157)
+ TCCR2A |= _BV(WGM21) | _BV(WGM20);
+ TCCR2B &= ~_BV(WGM22);
+
+ // Do non-inverting PWM on pin OC2A (p.155)
+ // On the Arduino this is pin 11.
+ TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
+ TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
+
+ // No prescaler (p.158)
+ TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
+
+ // Set initial pulse width to the first sample.
+ OCR2A = pgm_read_byte(&sounddata_data[0]);
+
+
+ // Set up Timer 1 to send a sample every interrupt.
+
+ cli();
+
+ // Set CTC mode (Clear Timer on Compare Match) (p.133)
+ // Have to set OCR1A *after*, otherwise it gets reset to 0!
+ TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
+ TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
+
+ // No prescaler (p.134)
+ TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
+
+ // Set the compare register (OCR1A).
+ // OCR1A is a 16-bit register, so we have to do this with
+ // interrupts disabled to be safe.
+ OCR1A = F_CPU / SAMPLE_RATE; // 16e6 / 8000 = 2000
+
+ // Enable interrupt when TCNT1 == OCR1A (p.136)
+ TIMSK1 |= _BV(OCIE1A);
+
+ lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
+ sample = 0;
+ sei();
+}
+
+void stopPlayback()
+{
+ // Disable playback per-sample interrupt.
+ TIMSK1 &= ~_BV(OCIE1A);
+
+ // Disable the per-sample timer completely.
+ TCCR1B &= ~_BV(CS10);
+
+ // Disable the PWM timer.
+ TCCR2B &= ~_BV(CS10);
+
+ digitalWrite(speakerPin, LOW);
+}

0 comments on commit 30a0106

Please sign in to comment.