Go Down

Topic: My stab at an Arduino Synth (Read 14466 times) previous topic - next topic


Hey narbotic

Im trying to modify your code so I can have a bunch of bend sensors control the frequency and the timing so I get random sound based on the sensor but I didnt have any success. I can easily do that by PWM but that doesnt make any interesting sound. Anyways Im gonna include my code for one sensor..If you or anyone can look at it and tell me whats wrong with it Ill appreciate it. again Im not looking for any particular note...Im interested in the noise that it can randomly generate.

#include <math.h>  // requires an Atmega168 chip
#define outpin 4
int val1;
int speakerOut = 4;
int sensor1 = 1;
int freq,t;

void setup() {
     pinMode (speakerOut, OUTPUT);

void loop(){
     val1 = analogRead(sensor1);
     freqout((int)val1, t);

     void freqout(int freq, int t)

 int hperiod;                               //calculate 1/2 period in us
val1 = 440;
hperiod = 500000 / freq;              
long cycles, i;
cycles = ((long)freq * (long)t) / 1000;    // calculate cycles
for (i=0; i<= cycles; i++){              // play note for t ms  
   digitalWrite(speakerOut, HIGH);
   digitalWrite(speakerOut, LOW);
   delayMicroseconds(hperiod);     // - 1 to make up for fractional microsecond in digitaWrite overhead


Sep 28, 2007, 04:22 am Last Edit: Sep 28, 2007, 04:23 am by paulb Reason: 1
Here's my synth code.
Sorry there's not more comments.

It has a bunch of pots hooked up to A/D
and an encoder (to change programs - on pins 2 & ??

I have an R2R (4 bit) ladder on board, but haven't tried it out yet - it needs some serious pin rearranging to get the R2R on better pins for bitmath manipulation.

Code: [Select]
// Synth code by Paul Badger   2007
#include <math.h>

#define outpin 9 // high order output

//analog inputs
#define pitchPot 5
#define modPot  4  
#define speedPot 1
#define phasePot 2
#define jukePot  3

#define stopPin 5

#define LEDbit0 10
#define LEDbit1 11
#define LEDbit2 12
#define LEDbit3 13

int randomWalkLowRange;
int randomWalkHighRange;

int val, runlevel;
volatile int encoder0PinA = 2;
volatile int encoder0PinB = 4;
volatile unsigned int encoder0Pos = 0;
int n = LOW;

int ptime, range, strt, inc;
int   k,  x, dur, freq, t,;
int i, j;
int sens, adder;

float ps, fpitch;         // variable for pow pitchShift routine

float noteval;
//note values
float A     = 14080;
float AS    = 14917.2;
float B     = 15804.3;
float C     = 16744;
float CS    = 17739.7;
float D     = 18794.5;
float DS    = 19912.1;
float E     = 21096.2;
float F     = 22350.6;
float FS    = 23679.6;
float G     = 25087.7;
float GS    = 26579.5;
float A2    = 28160;
float A2S   = 29834.5;
float B2    = 31608.5;
float C2    = 33488.1;
float C2S   = 35479.4;
float D2    = 37589.1;
float D2S   = 39824.3;
float E2    = 42192.3;
float F2    = 44701.2;
float F2S   = 47359.3;
float G2    = 50175.4;
float G2S   = 53159;
float A3    = 56320;
//rhythm values
int wh = 1024;
int h  = 512;
int dq = 448;
int q = 256;
int qt = 170;
int de = 192;
int e = 128;
int et = 85;
int dsx = 96;
int sx = 64;
int thx = 32;

float majScale[] = {
 A,  B,  CS,  D,  E,  FS,  GS,  A2,   B2,  C2S,  D2,  E2,  F2S,  G2S,  A3};
 float minScale[] = {
 A,  B,  C,  D,  E,  F,  GS,  A2,   B2,  C2,  D2,  E2,  F2,  G2S,  A3};
float creme[] =  {
 A,  CS,  D, CS,  D,  E,  CS,  D,   CS,  B,   A};
float cremeDur[] = {
 q,  q,  qt, qt,  qt,  q,  q,  qt,   qt, qt,  q};

void setup() {
 pinMode(outpin, OUTPUT);

 pinMode(LEDbit0, OUTPUT);
 pinMode(LEDbit1, OUTPUT);
 pinMode(LEDbit2, OUTPUT);
 pinMode(LEDbit3, OUTPUT);

 pinMode(stopPin, INPUT);
 digitalWrite(stopPin, HIGH);   // turn on pullups

 pinMode(encoder0PinA, INPUT);
 digitalWrite(encoder0PinA, HIGH);   // turn on pullups
 pinMode(encoder0PinB, INPUT);
 digitalWrite(encoder0PinB, HIGH);   // turn on pullups

 attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2

 pinMode(outpin, INPUT); // turn off audio out

void loop(){

 runlevel = encoder0Pos;
 switch (runlevel){
 case 0:

 case 1:
 case 2:
 case 3:
 case 4:
 case 5:
   case 6:

void doJoker1(){
 sens = analogRead(pitchPot);
 //sens =500;  // hardwire for testing
 adder = max((sens/5),1);

 for (x=sens; x<=(sens + (analogRead(speedPot) * 5)) ; x+=analogRead(jukePot)){    
   if ( digitalRead(stopPin) == 0){
   noteval = x;    // transpose scale up 12 tones - pow function generates transpostion
   dur = 100;
   freqout((int)noteval, sens/47);

   // delay(10);
// endJoker1 ****************************************************
/*void arp2(){
sens = analogRead(pitchPot);
range = abs(512 - sens) * 4.0;
strt =  sens * 1;
inc=abs(512 - sens);
if (inc == 0){
inc = 1;

for (x=strt; ( x > (strt / 4)) && (x < (inc * 30)); x* = (1 + ((sens - 512) / 1023.00))){                      
noteval = x;    // transpose scale up 12 tones - pow function generates transpostion
dur = 100;
freqout((int)noteval, inc);

//   if ((abs(sens - analogRead(0))) > 2){ break;}
}  */

// end  ***************************************************

void doScale2(){
 ps = ((float)analogRead(jukePot)) * 24.0 / 1023.0;         // choose new transpose interval every loop
 for(x= 0; x<=15; x++){  
   if ( digitalRead(stopPin) == 0){
   noteval = (majScale[x] / (float)(analogRead(pitchPot))) * pow(2,ps);    // transpose scale up 12 tones - pow function generates transpostion
   dur = analogRead(speedPot);
   freqout((int)noteval, dur);



void doArp3(){

 fpitch = (float)analogRead(pitchPot) / 64;
 Serial.print("fpitch = ");
 Serial.println(fpitch, DEC);
 if (fpitch == 0){
   fpitch = 1;
 strt = pow(2, fpitch);
 Serial.print("strt = ");
 Serial.println(strt, DEC);

 for(x=strt; x<= strt * 4; strt = (strt * ( 1 + ((float)analogRead(jukePot)/1023)))){  
   if ( digitalRead(stopPin) == 0){
   dur = analogRead(speedPot);
   freqout(strt, dur);
void doCreme4(){
 randomWalkLowRange = 0;
 randomWalkHighRange = 10;

 i = randomWalk(analogRead(speedPot) / 32);
 noteval = creme[i] / ((float)analogRead(pitchPot));
 dur = ((cremeDur[i] * (float)analogRead(jukePot)) / 64.0);
 freqout(noteval, dur);


Hi Paul;

I looked at your synth code but it does not compile as listed. It appears to me that in  several places you call functions which are not there. Could you elucidate on this listing a little?


Oct 06, 2007, 06:15 pm Last Edit: Oct 06, 2007, 06:53 pm by paulb Reason: 1
Sorry, There seems to be a nasty glitch in the forum software that reacts to comment lines in code or something
Funny behavior. Either there's a limit to the size you can paste into the editor or something else is reacting to (comments maybe) something in my code.

I started an "Arduino Synth" page on the playground - find the code there.



Jun 02, 2008, 07:06 pm Last Edit: Jun 03, 2008, 02:47 pm by electri-fire Reason: 1
Not a lot of action in this thread. What a shame. I just left this comment at http://narbotic.net.

I get loads of ideas that will take ages to implement (considering my 2-week arduino ownership) , you may want to cooperate? I just saw your sketch.

Here's your oscillator:
for (i=0; i<= cycles; i++){ // play note for t ms
digitalWrite(speakerOut, HIGH);
digitalWrite(speakerOut, LOW);
delayMicroseconds(hperiod - 1);

On the fly PWM implementation idea: Declare PulsWidth parameter
Shorten the HIGH period , lengthen the LOW period by (PulsWidth)
Have PulsWidth read from analogPin
Make PulsWidth LFO from "Fading" sketch: http://www.arduino.cc/en/Tutorial/Fading

There's a proper synth parameter for you. If you have more analogIn's available (or select DigitalIn switches) you can combine Pulswidth parameters, so PulsWidth will be: PulsWidth read from analogPin + LFO derived value + LFO speed ( by varying increment/deminish amount at  "Fading" portion).

Now if someone could apply this to the enigmatic pitch determenation scheme we can have pitchbend and vibrato....

A first seed on a "Waveform generator" will be posted elsewhere, as this is no longer appropriate for the "Exhibition" section.

I do hope to make this a joint project, my sketch making so far is minimal to say the least.


Unfortunately I haven't had much time to work on this project lately - but glad to hear you're interested!

Those coding ideas sound good -  I'll trying running some of them when i get the chance.

if you haven't seen it already, check out the APP shield from critter & guitari -

the code is a bit hard to follow if you don't speak AVR/C, but it's makes some great sounds


Well, I have a lot of reading up to do I've noticed, been getting my head around the method of having a fast clock timer/counter, and setting up a compare threshold to put out a pulse once the threshold is reached. Much higher frequencies can be reached this way. I hoping to find way to reset the timer.
Tutorial here:

Another route I've come across is playing samples, as in Sebastian Tomczak's sketches (ArduinoBeats, ArduinoDrumMachine)

I haven't quite understood yet what's happening there as well, but I imagine an eight bit waveform sample could be played looped? And then have options like dial in a waveform with analogIn, generate some random bytes or make some presets like sinus, triangle, sawUp, sawDown, some blockwaves with different pulswidth. You can't morph between two bytes can you? I guess not, no such thing as half a bit . You could have a small array of bytes to alternate, wave-sequence or whatever.
Then you can morph between two waveforms once you have figered out wich byte puts out what, but this seems a mega project. Not my cup of tea for the time being.
I've got some 4051 (de)multiplexers now, need to make a board with these and some R2R DA converters, 386 amp and small speaker. Someone might design a shield for this. Seems a usefull addition to me.

The mind overflows with options, but too often I get reminded of my electronics an programming incompetence, so spend lots of time reading stuff on arduino.cc lately.

> if you haven't seen it already, check out the APP shield from critter & guitari -  
http://www.critterandguitari.com/home/store/arduino-piano.php ,
the code is a bit hard to follow if you don't speak AVR/C, but it's makes some great sounds

I've been checking out your site and flicker stuff.  Nice. You've been very productive.


Electri-fire - check out this protoshield mini-project I put up on the Make:blog.  It's just an R/2R DAC without a buffer or anything special, you might find the sine generator sketch @ the bottom interesting.


To play a waveform - set up an array containing bytes that make up the wave you want
then you can write each byte to pins 0-7 all at once with "PORTD="
the time in between each byte can be variable determined by an analogRead value

I guess that's the extent of my current technique :)


I was trying to avoid the portstuff for now, hence the CD4051 (de)multiplexers. These put out one bit at a time so the result may be vastly different.  It's good to see the R2R DAC put enough through for a headphone. I can still add the amp later.

For single cycle waveform playing an array of 32 bytes seems more than sufficient. I have another cool idea.

Draw_Waveform: press "record", draw a waveform from analogIn, press "end record". Depending on how fast you draw you get a different number of samples each recording. Put these in a temporary array, count the number of samples. Now devide this number by x, to get 32 (roughly).

How's that done by the way?

Next take evey x'th sample from the temporary array, put them in the 32 byte waveform array, maybe do some smoothing, play looped.

There's no smiley for "impatient" I'm afraid....


There's no smiley for "impatient" I'm afraid....

lol - I think I see where you're coming from now.  If you're relatively new to this coding (like me) - a bunch of ideal techniques come to mind and the implementation is still a big "?"

The sampling technique sounds interesting - are you just talking about basic waveforms: sine, triangle, saw, etc?



Are you just talking about basic waveforms: sine, triangle, saw, etc?

Partly. Basic waveforms could ofcause be generated with some math functions, but..xssqkrr.. need to scratch now, sorry, math allergy you know?  ;D
You were gonna say that, weren't you?

I will maybe have to resort to that later, for now I will stick to the Array strategy.
But yes, also some preset classic waveforms.  
I want all oscillators to be compatible with each other and make some modular options to choose from.

To do:  

Moph_Waveform: have two "input" WaveArrays, like from the Draw_Waveform function and a selection from the preset arrays.
Read two arrays simultaniously, average both values, put out the avarage in the actual output wavetable.
Now find a way to do a "weighted morph" with a fader at analogIn, so we can have waveA with the fader to the left, waveB with the fader to the right, mixes in between.
Another option: interpolation: play byte
  • from waveA, byte [1] from waveB, byte [3] from waveA , etc, keep alternating till [31] and start over.

    Shift_Phase: have waveA static for convenience, find a way to alter the loop startpoint of waveB, and have it wrap to byte
  • when byte [31] has been reached.
    Combined with Moph_Waveform yields comb filter and PWM effects.

    Then I'm done, except for the smoothing function. Combined this could be FAT.

    I do hope it will not tax the arduino beyond it's capability. We still need some room to actually play the notes!


Jun 09, 2008, 03:45 am Last Edit: Jun 09, 2008, 03:46 am by Narbotic Reason: 1
yes very true - math was never my best subject  :)

I was thinking of combining 2 waves via averaging as well.  I have a feeling there must be a fairly straightforward way to do this.  And ideally we'd like to skew(?) the average with a reading from a pot/analog pin.

I made some arrays for the basic waveforms already - I'll clean them up a bit and post them here


Jun 09, 2008, 03:52 am Last Edit: Jun 09, 2008, 05:46 am by Narbotic Reason: 1
wow - my immediate math knowledge is sucking pretty bad of late :o

to average the 2 array entries:

((sineArray + triangleArray)  / 2)  = average

but weighting/skewing  them i still don't know


hmm - would this work?

Code: [Select]
//specify weightvalue as a floating point number
float weightValue;
float weightRemainder;

void loop(){

//convert to percentage of 100
weightValue = (analogRead(potPin) / 1024);

//find the weightValue's remainder out of 1
weightRemainder = (1 - weightValue);

//find the weighted average
(((weightValue * sineArray[i]) + (weightRemainder * triangleArray[i])) / 2)  == weighted average;


Not quite.

1) weightValue = (analogRead(potPin) / 1023);

2) (weightValue * sineArray) + (weightRemainder * triangleArray)  == weighted average;
i.e. don't divide by 2

- Ben

Go Up