Ok, no, i’m confused…
I modified a bit this code, and got this:
/*
* 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>
#define SAMPLE_RATE 8000
/*
* 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 "sounddata.h"
int ledPin = 13;
int speakerPin = 10; // Can be either 3 or 11, two PWM outputs connected to Timer 2
volatile uint16_t sample;
byte lastSample;
int lastSampleNo;
int firstSampleNo;
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 is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
if (sample >= lastSampleNo) {
stopPlayback();
}
else {
OCR2A = pgm_read_byte(&sounddata_data[sample]);
}
++sample;
}
void startPlayback(int letterNo)
{
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);
if(speakerPin==10){
// 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]);
OCR2A = 127;
}
else {
// Do non-inverting PWM on pin OC2B (p.155)
// On the Arduino this is pin 3.
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0);
TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
//OCR2B = pgm_read_byte(&sounddata_data[0]);
OCR2B = 128;
}
// 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]);
//Serial.print("Sampel no. ");
//Serial.print(letterNo);
//delay(500);
// firstSampleNo = borders[letterNo][0];
// lastSampleNo = borders[letterNo][1];
firstSampleNo = 1110;
lastSampleNo = 2999;
firstSampleNo = borders[0][0];
lastSampleNo = borders[0][1];
//Serial.println(". Poczatek: ");
//Serial.print(firstSampleNo);
//Serial.print(". Koniec: ");
//Serial.println(lastSampleNo);
sample = firstSampleNo;
//delay(500);
sei();
}
void setup()
{
//Serial.begin(9600);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
startPlayback(0);
startPlayback(1);
}
void loop()
{
//startPlayback(0);
//startPlayback(1);
//startPlayback(1);
}
It seems to work quite well but only for first ‘play’. I can not find why…
Part of sounddata:
// const int aa_sampleRate = 8000;
const int sounddata_length = 5417;
const int borders[][2] = {
{0, 2496}, //a
{2496, 5417}, //b
};
const signed char sounddata_data[] PROGMEM ={
//a
126,
125, 126, 126, 125, 126, 126, 125, 126, 125, 126, 126, 125, 126, 124, 125, 124, 126, 125, 126, 124,
126, 126, 126, 125, 127, 125, 126, 125, 126, 125, 125, 123, 120, 119, 113, 121, 122, 131, 131, 131,
124, 120, 120, 122, 127, 129, 131, 128, 130, 127, ...};
edit.
Forget it 