Pages: 1 [2]   Go Down
Author Topic: Single Timer PCM 6 Voices at 21khz [Code]  (Read 3441 times)
0 Members and 1 Guest are viewing this topic.
Brazil
Offline Offline
God Member
*****
Karma: 3
Posts: 616
Wusik Dot Com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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. ;-)

Wk
Logged


Estonia
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You are making history with this stuff!! smiley-grin

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…http://todbot.com/blog/2006/10/29/spooky-arduino-projects-4-and-musical-arduino/

Code:
/*

  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);
}
Logged

Estonia
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?  smiley-confuse
Logged

Brazil
Offline Offline
God Member
*****
Karma: 3
Posts: 616
Wusik Dot Com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 ;-)

Wk
Logged


Portugal
Offline Offline
God Member
*****
Karma: 6
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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/
Logged

Brazil
Offline Offline
God Member
*****
Karma: 3
Posts: 616
Wusik Dot Com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Humm, I don't get what you mean. Why it has to be Historic?! In any event, that site link is good, but old news, and its AVR, not Arduino. ;-)

In any event, I'm not trying to do something like that, I'm just creating my own 8-bit rompler and felt like sharing the code I got so far, which is Arduino compatible.  smiley-mr-green

Wk
Logged


Portugal
Offline Offline
God Member
*****
Karma: 6
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I was in a class so I couldn't write a lot.

The example that I gave is a bit overkill as it is done in assembly, but someone said that its historic, and I dont agree with that.
But I take my hat off to you sir, great coding that is going there smiley-wink

I love 8bit/retro sounds, and I plan to make something like that, so keep up the good work William, and I hope you the best with your awesome Beat707, a trully professional work being done by you!
Logged

Pages: 1 [2]   Go Up
Jump to: