Single Timer PCM 6 Voices at 21khz [Code]

Edit: I did a totally new code that is easier to handle and has reverse, pitch, velocity (volume per voice) and other neat options. See my other posts below to check for the new code, but here's a direct link:

http://www.wusik.com/arduino/Libraries/WDM_PCM/Wusik_DM_Sound_V2.zip

Here's an MP3 example:

http://www.wusik.com/arduino/Libraries/WDM_PCM/WDM_Example_01.mp3

Here's a picture of my protoboard:


And the youtube video:


Here I'm using the 16 bit timer 1 of the Arduino 2009 to reproduce PCM sounds at 21052hz sample-rate. (around 10.5khz sound) This still requires some work, but so far it sounds pretty great.

The great thing about this is that it uses Timer1 for both sample-reading-interrupt and pwm-output, so only a single timer is required for all 6 voices. Just put 10k resistor on Pins 9 and 10 and connect to a speaker or headphones. A filter may be required for better sound, but so far I'm using it connected directly to my headphones which already has a low-pass filter. (its a DJ headphone)

The sketch below requires the following file and outputs a loop sound that I set in the loop() part of the code. Just remove it and do whatever you want, or use the serial code included to trigger sounds.

I'm also going to work, next week, on a better WAV to C code so you can use any WAV file for this. The current sounds are not in the correct sample-rate, sorry for that, I will update the sounds next week, as I don't have much free time to work on this anymore.

And yes, please, contribute to our projects when possible at http://arduino.wusik.com

http://www.wusik.com/arduino/Libraries/Multi_PCM/PCM_Sound.h

http://www.wusik.com/arduino/Libraries/Multi_PCM/Wusik_DM_Sound.pde

All files + samples:

http://www.wusik.com/arduino/Libraries/Multi_PCM/Wusik_DM_Sound.zip

/*

  Created by WilliamK @ Wusik Dot Com (c) 2011 - http://arduino.wusik.com
  
  8-Bit PCM Sound using PWM on Pin 9 and Pin 10 - Uses the 16-Bit Timer1 to sum 3 voices in each Pin (total of 6 voices)
  
*/

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

// ======================================================================================= //
unsigned int sampleLen[6] = {0,0,0,0,0,0};
unsigned int sample[6] = {0,0,0,0,0,0};
unsigned char* samplePCM[6] = {0,0,0,0,0,0};
unsigned long mixer[2] = {(128*3),(128*3)};
byte inByte = 0;
unsigned char voice = 0;

// ======================================================================================= //
void setup()
{   
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
  
    Serial.begin(9600);

    // 16 Bit Timer Setup //
    // 16000000 (CPU CLOCK) / 760 (10 Bits) = 21052 samples-per-second (put a filter to remove anything above 10.5khz)
    TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11); // Fast PWM Timer on Pins 9 and 10
    TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // Non-Phase Inverting - No Prescaler
    ICR1 = (760-1); // Max value of 760 (9-bits is 511 and 10-bits is 1023) - 1 (the processor counts over)
    TIMSK1 = _BV(TOIE1); // timer overflow interrupt
    sei(); // enable global interrupts
    OCR1A = (128*3); // Initial PWM Pin 9
    OCR1B = (128*3); // Initial PWM Pin 10
}

// ======================================================================================= //
unsigned char getFreeVoice(void)
{
  for (int x=0; x<6; x++)
  {
    if (samplePCM[x] == 0)
    {
      return x;
      break;
    }
  }
  
  return 5;
}

// ======================================================================================= //
unsigned char findHiHat(void)
{
  for (int x=0; x<6; x++)
  {
    if (samplePCM[x] == HHopen_PCM)
    {
      return x;
      break;
    }
  }
  
  return getFreeVoice();
}

// ======================================================================================= //
void loop()
{  
  if (Serial.available() > 0)
  {
    inByte = Serial.read();
    
    if (inByte == '4') voice = findHiHat(); else voice = getFreeVoice();
    sample[voice] = 0;
  
    switch (inByte)
    {
      case '1': sampleLen[voice] = Kick_Len;      samplePCM[voice] = Kick_PCM;     break;
      case '2': sampleLen[voice] = Snare_Len;     samplePCM[voice] = Snare_PCM;    break;
      case '3': sampleLen[voice] = Clap_Len;      samplePCM[voice] = Clap_PCM;     break;
      case '4': sampleLen[voice] = HHclosed_Len;  samplePCM[voice] = HHclosed_PCM; break;
      case '5': sampleLen[voice] = HHopen_Len;    samplePCM[voice] = HHopen_PCM;   break;
      case '6': sampleLen[voice] = Clav_Len;      samplePCM[voice] = Clav_PCM;     break;
      case '7': sampleLen[voice] = FX1_Len;       samplePCM[voice] = FX1_PCM;      break;
    }
  }
}

// ======================================================================================= //
ISR(TIMER1_OVF_vect)
{   
  // Do this first so the PWM is updated faster //
  OCR1A = mixer[0];
  OCR1B = mixer[1];

  // Now Calculate the next sample //
  if (sample[0] >= sampleLen[0]) samplePCM[0] = 0; if (samplePCM[0] != 0) { mixer[0] = (unsigned int)pgm_read_byte(&samplePCM[0][sample[0]]); sample[0]++; } else mixer[0] = 128;
  if (sample[1] >= sampleLen[1]) samplePCM[1] = 0; if (samplePCM[1] != 0) { mixer[0] += (unsigned int)pgm_read_byte(&samplePCM[1][sample[1]]); sample[1]++; } else mixer[0] += 128;
  if (sample[2] >= sampleLen[2]) samplePCM[2] = 0; if (samplePCM[2] != 0) { mixer[0] += (unsigned int)pgm_read_byte(&samplePCM[2][sample[2]]); sample[2]++; } else mixer[0] += 128;

  if (sample[3] >= sampleLen[3]) samplePCM[3] = 0; if (samplePCM[3] != 0) { mixer[1] = (unsigned int)pgm_read_byte(&samplePCM[3][sample[3]]); sample[3]++; } else mixer[1] = 128;
  if (sample[4] >= sampleLen[4]) samplePCM[4] = 0; if (samplePCM[4] != 0) { mixer[1] += (unsigned int)pgm_read_byte(&samplePCM[4][sample[4]]); sample[4]++; } else mixer[1] += 128;
  if (sample[5] >= sampleLen[5]) samplePCM[5] = 0; if (samplePCM[5] != 0) { mixer[1] += (unsigned int)pgm_read_byte(&samplePCM[5][sample[5]]); sample[5]++; } else mixer[1] += 128;
}

And here's my WAV2Code solution...

http://www.wusik.com/arduino/Libraries/WAV2Code/Wusik_WAV2Code.zip
(all files, examples and source)

http://www.wusik.com/arduino/Libraries/WAV2Code/WWav2C.exe
(windows executable)

http://www.wusik.com/arduino/Libraries/WAV2Code/builder.cpp
(source)

It works pretty well here. I will post the code I use for this in another thread. :wink:

Wk

Here's the final code, with new samples. It takes numbers from 1 to 7 from the Serial input (9600) and finds a free voice from the 6 voices.

http://www.wusik.com/arduino/Libraries/Multi_PCM/Wusik_DM_Sound.zip

That's the complete code + samples and everything needed. I tested with headphones, using a 10k resistor on pins 9 and 10 directly connected to the set. It sounds pretty good. Needs a filter for a better sound, but heck, it still sounds great. :wink:

Wk

Here's an MP3 example:

http://www.wusik.com/arduino/Libraries/Multi_PCM/8-bit-Drums.mp3

And here's the YouTube video:

Best Regards, WilliamK

wow ... quite impressive to play 6 voices of sample with a single Arduino.

Did you check this ? : PCM Sound Generator

Good work WillK and thanks for sharing.

Yeah, I saw that, but there's a typo, its not 255 "voices" but 255 sounds. From what I've seem it only play one at a time. :wink: But still a nice design.

In any event, I was wondering on how fast I could work with a SD card for even more and longer sounds. :wink: I got one here, just need to take the time to check it out. Still, not sure how many concurrent sounds I could play directly from a SD card. I saw a guy doing a MOD player and he couldn't read directly from an EEPROM so he had to use an external SRAM or something like that. Still, the SD card reads much faster than an EEPROM. XD

Anyway, the current design, there's still room for more voices, I just didn't do it to leave room for something else instead. :fearful:

Wk

BTW: the noise on the MP3 example is not from the PWM output, its from my sound-card input. :blush:

Another nice thing, the HiHat is in its own mute group, so the close hihat stops the open hihat. :wink:

Wk

Very nice!!!!! BTW, which MIDI board are you using????

I'm using SparkFun's Midi breakout. :grin:

Wk

Really great job! Going to make myself a nice drum machine!! Big thanks for sharing this!

One question though. Can I make the code to playback some beats by itself so it would be more like a drum machine? I tried to give inByte some values inside the loop and separate the sounds with delay but it wouldn't play back the sounds. Any ideas?

I went little bit further with the drum machine idea. Got stuck and can't get the beats playing continuously =(. Can't figure out what I'm doing wrong. Any ideas?

/*

  Created by WilliamK @ Wusik Dot Com (c) 2011 - http://arduino.wusik.com
  
  8-Bit PCM Sound using PWM on Pin 9 and Pin 10 - Uses the 16-Bit Timer1 to sum 3 voices in each Pin (total of 6 voices)
  
*/

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

// ======================================================================================= //
unsigned int sampleLen[6] = {0,0,0,0,0,0};
unsigned int sample[6] = {0,0,0,0,0,0};
unsigned char* samplePCM[6] = {0,0,0,0,0,0};
unsigned long mixer[2] = {(128*3),(128*3)};

unsigned char voice = 0;
int delayTime = 500;

byte bar1[] = {

2, 2, 1, 6, 2, 4, 1, 2, 1, 3, 1, 2, 2, 1, 3, 4

};

// ======================================================================================= //
void setup()
{   
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);

    // 16 Bit Timer Setup //
    // 16000000 (CPU CLOCK) / 760 (10 Bits) = 21052 samples-per-second (put a filter to remove anything above 10.5khz)
    TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11); // Fast PWM Timer on Pins 9 and 10
    TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // Non-Phase Inverting - No Prescaler
    ICR1 = (760-1); // Max value of 760 (9-bits is 511 and 10-bits is 1023) - 1 (the processor counts over)
    TIMSK1 = _BV(TOIE1); // timer overflow interrupt
    sei(); // enable global interrupts
    OCR1A = (128*3); // Initial PWM Pin 9
    OCR1B = (128*3); // Initial PWM Pin 10
}

// ======================================================================================= //
unsigned char getFreeVoice(void)
{
  for (int x=0; x<6; x++)
  {
    if (samplePCM[x] == 0)
    {
      return x;
      break;
    }
  }
  
  return 5;
}

// ======================================================================================= //
unsigned char findHiHat(void)
{
  for (int x=0; x<6; x++)
  {
    if (samplePCM[x] == HHopen_PCM)
    {
      return x;
      break;
    }
  }
  
  return getFreeVoice();
}

// ======================================================================================= //
void loop()
{  
  
    for(int j = 0; j < 16; j ++) {
   inByte(bar1[j]);
   
   }
   
}

// ======================================================================================= //

    void inByte(byte beat) {
    if (beat == 1) {
      sampleLen[voice] = Kick_Len;
      samplePCM[voice] = Kick_PCM;
    }
    else if (beat == 2) {sampleLen[voice] = Snare_Len;     samplePCM[voice] = Snare_PCM;}
    else if (beat == 3) {sampleLen[voice] = Clap_Len;      samplePCM[voice] = Clap_PCM;}
    else if (beat == 4) {sampleLen[voice] = HHclosed_Len;  samplePCM[voice] = HHclosed_PCM;}
    else if (beat == 5) {sampleLen[voice] = HHopen_Len;    samplePCM[voice] = HHopen_PCM;}
    else if (beat == 6) {sampleLen[voice] = Clav_Len;      samplePCM[voice] = Clav_PCM;}
    else if (beat == 7) {sampleLen[voice] = FX1_Len;       samplePCM[voice] = FX1_PCM;}
    delay(delayTime);
}

ISR(TIMER1_OVF_vect)
{   
  // Do this first so the PWM is updated faster //
  OCR1A = mixer[0];
  OCR1B = mixer[1];

  // Now Calculate the next sample //
  if (sample[0] >= sampleLen[0]) samplePCM[0] = 0; if (samplePCM[0] != 0) { mixer[0] = (unsigned int)pgm_read_byte(&samplePCM[0][sample[0]]); sample[0]++; } else mixer[0] = 128;
  if (sample[1] >= sampleLen[1]) samplePCM[1] = 0; if (samplePCM[1] != 0) { mixer[0] += (unsigned int)pgm_read_byte(&samplePCM[1][sample[1]]); sample[1]++; } else mixer[0] += 128;
  if (sample[2] >= sampleLen[2]) samplePCM[2] = 0; if (samplePCM[2] != 0) { mixer[0] += (unsigned int)pgm_read_byte(&samplePCM[2][sample[2]]); sample[2]++; } else mixer[0] += 128;

  if (sample[3] >= sampleLen[3]) samplePCM[3] = 0; if (samplePCM[3] != 0) { mixer[1] = (unsigned int)pgm_read_byte(&samplePCM[3][sample[3]]); sample[3]++; } else mixer[1] = 128;
  if (sample[4] >= sampleLen[4]) samplePCM[4] = 0; if (samplePCM[4] != 0) { mixer[1] += (unsigned int)pgm_read_byte(&samplePCM[4][sample[4]]); sample[4]++; } else mixer[1] += 128;
  if (sample[5] >= sampleLen[5]) samplePCM[5] = 0; if (samplePCM[5] != 0) { mixer[1] += (unsigned int)pgm_read_byte(&samplePCM[5][sample[5]]); sample[5]++; } else mixer[1] += 128;
}

I won't have time to help you today, sorry, but I will try to make a small sequencer for this soon.

In the meanwhile, check this out:

https://www.kickstarter.com/projects/ruggedcircuits/arduino-drum-machine-step-sequencer-groove-box/posts

:wink:

Wk

I'm doing some big changes to the code, to make it more usable, and also adding some extra features, but the processor can only do so much... Still, I will post the updated code soon. 8)

I'm trying to add velocity to the voices, but the processor really hates divisions, so I'm thinking on doing a small matrix of 5 velocities, which would take around 1k of RAM, but should be much faster than a regular \ in the code. I will see if I have some free time to test this out tomorrow. I'm also trying to add rate to the thing. So far regular 1, 2, 3, 4 rates are easy, but I want to add fine-rates too maybe with some basic linear interpolation, but we will see if that works out. :wink:

Wk

Wow… these are some really great features. Can't wait to test this stuff out. :slight_smile:

I have played around with the Little-scale drum machine (little-scale: Arduino Drum Machine) code and have always wondered if it is possible to add sample volume and trigger it with something like MPC pads. This would be really great leap forward on Arduino live drumming 8)

I did a totally new code that is easier to handle and has reverse, pitch, velocity (volume per voice) and other neat options. Here's a direct link:

http://www.wusik.com/arduino/Libraries/WDM_PCM/Wusik_DM_Sound_V2.zip

Here's an MP3 example:

http://www.wusik.com/arduino/Libraries/WDM_PCM/WDM_Example_01.mp3

Here's a picture of my protoboard:

http://www.wusik.com/arduino/Libraries/WDM_PCM/Protoboard_01.JPG

And the youtube video:

Don't forget to check our other projects and also contribute when possible. :wink:

http://arduino.wusik.com

Best Regards, WilliamK

BTW: the zip file above is a snapshot of the code used on the video also above. :wink:

The zip file includes everything, including the WAV to Code new format, which uses a 0 to 242 number instead of the regular 0 to 255 of 8-bit sounds. I did like this to accommodate 3 voices per 22068hz output. (its a crazy math)

Now, the code MIDI input is not used, as I just added a sort of drum pattern directly in the loop area. But removing that and replacing the midi serial init, you can use this like a drum-box. Just needs some special midi-cc to change the drums reverse and pitch per voice or per sound. If I have any free time, I may just do that. :wink:

Wk

You are making history with this stuff!! :smiley:

I got the velocity sensitive piezo drum pads working with the V2. It is proof of concept and not very stable one, but with little bit tinkering it might be a great live drumming example. I used some of the code from Spooky's MIDI drum project…Spooky Arduino Projects #4 – Musical Arduino – todbot blog

/*

  Created by WilliamK @ Wusik Dot Com (c) 2011 - http://arduino.wusik.com
  
  8-Bit PCM Sound using PWM on the 16-bit Timer1 - a total of 6 voices at 22068hz
  Sound on Pins 9 and 10 (use a 10k resistor on each pin and a capacitor to filter any noise)
  
  A note, in order to keep the code at 22khz we used a near 8-bit value of 242, while 8-bit would be 255.
  The included WAV to Code converter will output samples using 242 as the max value possible instead.
  
*/

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

// ======================================================================================= //
#define MIDIactivityPin 13
#define SHOWFREEMEM 1
#define MAXVOICES 6
unsigned long MIDIactivityMillis = millis()+25;
byte MIDIactivity = 0;
unsigned int sampleLen[MAXVOICES];
int samplePos[MAXVOICES][2]; // tune/fine //
unsigned char* samplePCM[MAXVOICES];
unsigned int mixer[2] = {363,363};
unsigned char voice = 0;
char rate[MAXVOICES][2]; // tune/fine //
unsigned char notevel[MAXVOICES];
unsigned char noteVelMatrix[6][242]; // This is used to add Velocity without doing any heavy processing divisions //
byte incomingByte;
byte note;
byte noteOn = 0;
byte state = 0;
byte sound = 0;
int treshold = 100;
int val = 0;
int t = 0;

// ======================================================================================= //
// Checks the RAM left - the ATmega328 has only 2K of RAM //
#if SHOWFREEMEM
  extern unsigned int __data_start;
  extern unsigned int __data_end;
  extern unsigned int __bss_start;
  extern unsigned int __bss_end;
  extern unsigned int __heap_start;
  extern void *__brkval;
  
  int freeMemory()
  {
    int free_memory;
  
    if((int)__brkval == 0)
       free_memory = ((int)&free_memory) - ((int)&__bss_end);
    else
      free_memory = ((int)&free_memory) - ((int)__brkval);
  
    return free_memory;
  }
#endif

// ======================================================================================= //
void setup()
{   
    memset(sampleLen,0,sizeof(sampleLen));
    memset(samplePos,0,sizeof(samplePos));
    memset(samplePCM,0,sizeof(samplePCM));
    memset(rate,0,sizeof(rate));
    memset(notevel,3,sizeof(notevel));
    
    // Create the Velocity Matrix Values //
    float val = 0.0f;
    float valr = 1.0f;
    for (int x=0; x<6; x++)
    {
      if (x == 1)        valr = 0.80f;
        else if (x == 2) valr = 0.60f;
        else if (x == 3) valr = 0.35f;
        else if (x == 4) valr = 0.20f;
        else if (x == 5) valr = 0.08f;

      for (int v=0; v<242; v++)
      {
        if (x == 0) noteVelMatrix[x][v] = v;
        else
        {
          val = (((2.0f/242.0f)*float(v))-1.0f)*valr;
          noteVelMatrix[x][v] = (unsigned int)((val+1.0f)*(242.0f/2.0f));
        }
      }
    }
      
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
    
    pinMode(A0,INPUT);
    pinMode(A1,INPUT);
    
    pinMode(A2,INPUT); // piezo 1
    pinMode(A3,INPUT); // piezo 2
    
    pinMode(A4,INPUT);
    pinMode(A5,INPUT);
    
  //  pinMode(MIDIactivityPin,OUTPUT);
  //  digitalWrite(MIDIactivityPin,LOW);
  
    // 16 Bit Timer Setup //
    // 16000000 (CPU CLOCK) / 726 (10 Bits) = 22068 samples-per-second (put a filter to remove anything above 10.5khz)
    TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11); // Fast PWM Timer
    TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // Non-Phase Inverting - No Prescaler
    ICR1 = (726-1); // Max value of 726 (9-bits is 511 and 10-bits is 1023) - 1 (the processor counts over)
    TIMSK1 = _BV(TOIE1); // timer overflow interrupt
    OCR1A = OCR1B = 363; // (242/2)*3
    
    sei(); // enable global interrupts
    
  #if SHOWFREEMEM
      Serial.begin(38400);  
      Serial.print("Free Mem: ");
      Serial.println(freeMemory());
      Serial.println("");
    #endif
  
    // MIDI Startup //
    // !!! Tests // Serial.begin(31250);    
}

// ======================================================================================= //
boolean pingVoice = true;
unsigned char getFreeVoice(void)
{
  if (pingVoice)
  {
    pingVoice = false;
    if (samplePCM[0] == 0) return 0;
    if (samplePCM[1] == 0) return 1;
    if (samplePCM[2] == 0) return 2;
    if (samplePCM[3] == 0) return 3;
    if (samplePCM[4] == 0) return 4;
    if (samplePCM[5] == 0) return 5;
  }
  else
  {
    pingVoice = true;
    if (samplePCM[5] == 0) return 5;
    if (samplePCM[4] == 0) return 4;
    if (samplePCM[3] == 0) return 3;
    if (samplePCM[2] == 0) return 2;
    if (samplePCM[1] == 0) return 1;
    if (samplePCM[0] == 0) return 0;
  }
  
  return 5;
}

// ======================================================================================= //
void playNote(unsigned char note, unsigned char velocity)
{
    if (note < 60) return;
    sound = note-60;
    if (sound >= PCMtotal) return;
      
    voice = getFreeVoice();
    samplePCM[voice] = 0; // First stop any sound reference //
    // !!! TEST // rate[voice][0] = 1; // Tune
    // !!! TEST // rate[voice][1] = 0; // Fine
    if (rate[voice][0] > 0) samplePos[voice][0] = 0; else samplePos[voice][0] = PCMlen[sound]-1;
    samplePos[voice][1] = 0;
    notevel[voice] = 5-min(5,velocity/25);
    sampleLen[voice] = PCMlen[sound];
    samplePCM[voice] = PCMdata[sound];    
}

// ======================================================================================= //
boolean flipFlop = false;
boolean isReverse = false;
void checkInterface(void)
{
  isReverse = !digitalRead(A0);
  if (digitalRead(A1))
  {
    rate[0][1] = rate[1][1] = rate[2][1] = rate[3][1] = rate[4][1] = rate[5][1] = 0;
    rate[0][0] = rate[1][0] = rate[2][0] = rate[3][0] = rate[4][0] = rate[5][0] = ((isReverse) ? -1 : 1);
  }
  else
  {
    rate[0][1] = rate[1][1] = rate[2][1] = rate[3][1] = rate[4][1] = rate[5][1] = analogRead(A5)/11;
    rate[0][0] = rate[1][0] = rate[2][0] = rate[3][0] = rate[4][0] = rate[5][0] = (analogRead(A4)/110) * ((isReverse) ? -1 : 1);
  }
}

// ======================================================================================= //
void loop()
{
 // Piezos in analog pins 2 and 3
 // 1M resistor also between pin and ground
  
 val = analogRead(A2);
  if( val >= treshold ) {
   t=0;
    while(analogRead(A2) >= treshold/2) {
      t++;
    }
    checkInterface();
    playNote(62, val);
    delay(t);
  }
  
   val = analogRead(A3);
  if( val >= treshold ) {
   t=0;
    while(analogRead(A3) >= treshold/2) {
      t++;
    }
    checkInterface();
    playNote(62, val);
    delay(t);
  }

  
 /* checkInterface();
  if (flipFlop) playNote(62, 127); else playNote(63, 127);
  delay(40);
  checkInterface();
  if (flipFlop) playNote(62, 60); else playNote(63, 60);
  delay(40);
  checkInterface();
  if (flipFlop) playNote(62, 40); else playNote(63, 40);
  delay(40);
  
  checkInterface();
  if (flipFlop) playNote(62, 20); else playNote(63, 20);
  checkInterface();
  if (flipFlop) playNote(61, 127); else playNote(60, 127);
  delay(60);
  checkInterface();
  if (flipFlop) playNote(61, 40); else playNote(60, 40);
  delay(60);
  checkInterface();
  
  checkInterface();
  if (flipFlop) playNote(66, 127); else playNote(65, 127);
  if (flipFlop) playNote(64, 40); playNote(67, 40); 
  delay(40);
  checkInterface();
  if (flipFlop) playNote(66, 60); else playNote(65, 60);
  if (flipFlop) playNote(64, 20); playNote(67, 20); 
  delay(40);
  checkInterface();
  if (flipFlop) playNote(66, 40); else playNote(65, 40);
  delay(40);
  checkInterface();
  
  flipFlop = !flipFlop; */
  
  
}

// ======================================================================================= //
#define nextSample(nv,nm)\
  if (samplePos[nv][0] >= sampleLen[nv] || samplePos[nv][0] < 0) samplePCM[nv] = 0;\
  if (samplePCM[nv] != 0)\
  {\
    mixer[nm] += noteVelMatrix[notevel[nv]][(unsigned int)pgm_read_byte(&samplePCM[nv][samplePos[nv][0]])];\
    samplePos[nv][0] += rate[nv][0];\
    samplePos[nv][1] += rate[nv][1];\
    if (samplePos[nv][1] > 120) { samplePos[nv][1] = 0; if (rate[nv][0] > 0) samplePos[nv][0]++; else samplePos[nv][0]--; }\
  } else mixer[nm] += 121;

// ======================================================================================= //
ISR(TIMER1_OVF_vect)
{   
  // Do this first so the PWM is updated faster //
  OCR1A = mixer[0];
  OCR1B = mixer[1];

  // Now Calculate the next samples //
  mixer[0] = mixer[1] = 0;
  nextSample(0,0);
  nextSample(1,0);
  nextSample(2,0);
  nextSample(3,1);
  nextSample(4,1);
  nextSample(5,1);
}

WilliamK:
Now, the code MIDI input is not used, as I just added a sort of drum pattern directly in the loop area.

I tried to create a drum pattern on my own inside the code like you did but the playback seems to be pretty random. The idea is to have some preprogrammed patterns on the Arduino so it could be used as a standalone instrument. Sometimes it seems to play only half of the pattern and other times it seems to trigger some random sample. Am I missing something here? :~

I haven't really created any sequencer code inside this, it was more of a test, not a real sequencer. Maybe with time I could do that, but to be honest, I'm more inclined on keeping an external sequencer like Beat707.com and this as a "Vox" for Beat707.com :wink:

Wk

Thats nice, and a lot of code is done, but not really anything new or historic.
See this:
Audio and video from a single Atmega:
http://www.linusakesson.net/scene/craft/