Go Down

Topic: Having some fun with PCM multi voice code ... (Read 3452 times) previous topic - next topic

WilliamK Govinda

Feb 14, 2011, 04:37 pm Last Edit: Feb 14, 2011, 04:45 pm by WilliamK Reason: 1
Just a quick play with some PCM code found in the Playground area.  :smiley-mr-green:

Code: [Select]

 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 Sound Test using PWM on Pin 11

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#define SAMPLE_RATE 44100
volatile uint16_t sample[4] = {0,0,0,0};
unsigned long mixer = 0;

// Sound Data //
// 44100 Pulse //
const unsigned char sounddata_data1[] PROGMEM = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

const unsigned char sounddata_data2[] PROGMEM = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,

const unsigned char sounddata_data3[] PROGMEM = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,

const unsigned char sounddata_data4[] PROGMEM = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,

const int sounddata_length[4]={sizeof(sounddata_data1),sizeof(sounddata_data2),sizeof(sounddata_data3),sizeof(sounddata_data4)};

ISR(TIMER1_COMPA_vect) { nextSample(); }

inline void nextSample()
   mixer = pgm_read_byte(&sounddata_data1[sample[0]]);
   mixer += pgm_read_byte(&sounddata_data2[sample[1]]);
   mixer += pgm_read_byte(&sounddata_data3[sample[2]]);
   mixer += pgm_read_byte(&sounddata_data4[sample[3]]);
   mixer /= 4;
   OCR2A = (unsigned char)mixer;
   for (char x=0; x<4; x++)
     if (sample[x] >= sounddata_length[x]) sample[x] = 0; // Loop //

void setup()
   pinMode(11, OUTPUT); // Speaker uses Pin 11 - this is fixed and can't be changed //
   ASSR &= ~(_BV(EXCLK) | _BV(AS2));
   TCCR2A |= _BV(WGM21) | _BV(WGM20);
   TCCR2B &= ~_BV(WGM22);
   TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
   TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
   TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
   TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
   TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
   TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
   OCR1A = F_CPU / SAMPLE_RATE;    // 16e6 / 8000 = 2000
   TIMSK1 |= _BV(OCIE1A);

void loop()
   while (true);

WilliamK Govinda

Here I was able to mix 4 different pulse waveforms, each with a different pitch. There's room for optimization, I'm sure, so if anyone would like to give some hints... ;-)

I'm using a 16 bit variable for the mixer, but I wonder if there's a better way to handle that.

Also, the code runs at 44100hz, I haven't tested how many voices can be added before sample-rate must be lowered.  :.


WilliamK Govinda

BTW: here I just wired a small speaker to Pin 11 without any external filter and it still sounds pretty nice.  :smiley-eek-blue:


WilliamK Govinda

I wonder if it would be possible to change the code and have each voice output on its own pin? I see that the 2009 board has 6 PWM outputs, could I drive each pin to a different sample instead? That way I get a better resolution, compared to voice1+voice2+voice3+voice4/4 - Not to mention even 3 stereo outputs if I wanted. ;-)

The single 16-bit timer would be shared, just the PWM timer for each output wouldn't.


WilliamK Govinda

Ok, my final code has 6 voices at 32000hz, and it sounds pretty good. The problem is when more than one voice plays at the same time, resolution is reduced for the mixdown. So I'm still wanting to check if I could have 6 separated fast-pwm outputs, and how to manage the ASM code for that.

I got some nice sounds in the Flash area, and got some free room for more code.



Go Up