Edit: this project was updated and moved to: https://github.com/Beat707/BeatVox
So, I updated my code and now I got 6 voices using Pins 9 and 10 PWM. Thanks to the Timer1 16-bit I was able to get near 10-bit resolution, so I could just mix 3 8-bit voices without having to apply any / which would reduce audio-quality.
The example includes a simple loop I created in the Loop() area.
Timer2 was used to read samples, while Timer1 does the PWM at near 10-bits.
Just "mix" the output of Pins 9 and 10, add 10k resistors and a filter (capacitor) which I don't know the value yet, sorry.
Download files:
http://www.wusik.com/arduino/Libraries/Multi_PCM/Sound_Test_16Bits.pde
http://www.wusik.com/arduino/Libraries/Multi_PCM/PCM_Data.h
Please, contribute to the project at: http://arduino.wusik.com
/*
Created by WilliamK @ Wusik Dot Com (c) 2011 - http://arduino.wusik.com
Code excerpts from: http://www.arduino.cc/playground/Code/PCMAudio - Michael Smith <michael@hurts.ca>
8-Bit PCM Sound using PWM on Pin 9 and Pin 10 - Uses the 16-Bit timer to sum 3 voices in each Pin
Uses Timer2 and Timer1
*/
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <EEPROM.h>
#include "PCM_Data.h"
// ======================================================================================= //
#define SAMPLE_RATE 32000
unsigned int sample[6] = {2047,2047,2047,2047,2047,2047};
unsigned long mixer[2] = {0,0};
byte inByte = 0;
// ======================================================================================= //
void setup()
{
Serial.begin(9600);
Serial.println("Type 1 to 6 for Sample Playback");
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
// 16 Bit Timer Setup //
// 16000000 (CPU CLOCK) / 765 (near 10 Bits) = 20.9khz (put a filter to remove anything above 16khz) //
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10);
ICR1 = 765;
OCR1A = (128*3);
OCR1B = (128*3);
/*
Timer 1 Registers - ATmega328 Datasheet Page 113 (16 Bit Timer)
Bit 7 6 5 4 3 2 1 0
TCCR1A = COM1A1 COM1A0 COM1B1 COM1B0 R R WGM11 WGM10
TCCR1B = ICNC1 ICES1 R WGM13 WGM12 CS12 CS11 CS10
COM1A = Out Pin 9
COM1B = Out Pin 10
WGM12 WGM10 = Mode 5 Fast PWM 8 Bits
WGM12 WGM11 WGM10 = Mode 7 Fast PWM 10 Bits
WGM13 WGM12 WGM11 = Mode 14 Fast PWM Top=ICR1
CS10 = No PreScaler
*/
// Timer 2 - used to load Samples //
startTimer();
}
// ======================================================================================= //
void loop()
{
sample[0] = 0 ;
delay(255);
sample[2] = 0 ;
delay(255);
sample[0] = 0 ;
sample[1] = 0 ;
delay(255);
sample[2] = 0 ;
delay(255);
sample[0] = 0 ;
delay(255);
sample[2] = 0 ;
delay(255);
sample[0] = 0 ;
sample[1] = 0 ;
delay(255/2);
sample[2] = 0 ;
delay(255/2);
sample[3] = 0 ;
delay(255/2);
sample[4] = 0 ;
delay(255/2);
if (Serial.available() > 0)
{
inByte = Serial.read();
switch (inByte)
{
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
sample[inByte-'1'] = 0 ;
break;
}
}
}
// ======================================================================================= //
ISR(TIMER2_COMPA_vect)
{
if (sample[0] < 2047) { mixer[0] = (unsigned int)pgm_read_byte(&PCM_Data[0][sample[0]]); sample[0]++; } else mixer[0] = 128;
if (sample[1] < 2047) { mixer[0] += (unsigned int)pgm_read_byte(&PCM_Data[1][sample[1]]); sample[1]++; } else mixer[0] += 128;
if (sample[2] < 2047) { mixer[0] += (unsigned int)pgm_read_byte(&PCM_Data[2][sample[2]]); sample[2]++; } else mixer[0] += 128;
if (sample[3] < 2047) { mixer[1] = (unsigned int)pgm_read_byte(&PCM_Data[3][sample[3]]); sample[3]++; } else mixer[1] = 128;
if (sample[4] < 2047) { mixer[1] += (unsigned int)pgm_read_byte(&PCM_Data[4][sample[4]]); sample[4]++; } else mixer[1] += 128;
if (sample[5] < 2047) { mixer[1] += (unsigned int)pgm_read_byte(&PCM_Data[5][sample[5]]); sample[5]++; } else mixer[1] += 128;
OCR1A = mixer[0];
OCR1B = mixer[1];
}
// ======================================================================================= //
void startTimer()
{
TCCR2A = 0;
TCCR2B = 0;
bitWrite(TCCR2A, WGM21, 1);
bitWrite(TCCR2B, CS20, 1);
uint32_t ocr = F_CPU / SAMPLE_RATE - 1;
uint8_t prescalarbits = 0b001;
if (ocr > 255)
{
ocr = F_CPU / SAMPLE_RATE / 8 - 1;
prescalarbits = 0b010;
if (ocr > 255)
{
ocr = F_CPU / SAMPLE_RATE / 32 - 1;
prescalarbits = 0b011;
}
if (ocr > 255)
{
ocr = F_CPU / SAMPLE_RATE / 64 - 1;
prescalarbits = 0b100;
if (ocr > 255)
{
ocr = F_CPU / SAMPLE_RATE / 128 - 1;
prescalarbits = 0b101;
}
if (ocr > 255)
{
ocr = F_CPU / SAMPLE_RATE / 256 - 1;
prescalarbits = 0b110;
if (ocr > 255)
{
ocr = F_CPU / SAMPLE_RATE / 1024 - 1;
prescalarbits = 0b111;
if (ocr > 255)
{
return;
}
}
}
}
}
TCCR2B = prescalarbits;
OCR2A = ocr;
bitWrite(TIMSK2, OCIE2A, 1);
}