-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit (based on code by Michael Smith).
- Loading branch information
0 parents
commit ae3f463
Showing
3 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/* | ||
* 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 <[email protected]> | ||
*/ | ||
|
||
#include <stdint.h> | ||
#include <avr/interrupt.h> | ||
#include <avr/io.h> | ||
#include <avr/pgmspace.h> | ||
|
||
#define SAMPLE_RATE 8000 | ||
|
||
#if defined(ARDUINO) && ARDUINO >= 100 | ||
#include "Arduino.h" | ||
#else | ||
#include "WProgram.h" | ||
#endif | ||
|
||
#include "PCM.h" | ||
|
||
/* | ||
* 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 | ||
*/ | ||
|
||
int speakerPin = 11; | ||
unsigned char const *sounddata_data=0; | ||
int sounddata_length=0; | ||
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(unsigned char const *data, int length) | ||
{ | ||
sounddata_data = data; | ||
sounddata_length = length; | ||
|
||
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* 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 <[email protected]> | ||
*/ | ||
|
||
/* | ||
* 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 | ||
*/ | ||
|
||
#include <avr/pgmspace.h> | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
void startPlayback(unsigned char const *data, int length); | ||
void stopPlayback(); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif |
Large diffs are not rendered by default.
Oops, something went wrong.