Go Down

Topic: Arduino Polyphonic Synth (Read 10293 times) previous topic - next topic

amacmullen14

Jul 17, 2011, 03:27 am Last Edit: Oct 02, 2011, 02:40 pm by sciguy Reason: 1
EDIT: See the last post for the most current info.  I just kept it in the same thread.


So I've learned to use the internal timers on the arduino, and I've gotten it to make 8 bit noises using PWM and a lowpass filter.
So, I built a MIDI synth!
Hardware is super simple, just an RC lowpass filter connected to pin 11.  (I can't exactly remember what values)
One timer is set up for fast PWM on pin 11, another timer updates the compare register for the first timer at the correct frequency.  
It can play any 16-step (could be more or less, 16 just seemed like a good number) 8 bit waveform, like sawtooth, square, or triangle.
Here's the code:
Code: [Select]
#include <Frequencies.h>
#include <MIDI.h>

//  A MIDI simple monophonic synth, with wavetable synthesis.
//  Uses timer2 to generate PWM representing an analog output.  A simple RC lowpass filter
//  filters to an analog output.  (in the initial setup, R=100ohm  C=0.33uF
//
//  Eventually, the tone may be controlled in a timer interrupt routine
//  For now, manually coded in loop()

const int notes[] = {
 NOTE_B0, NOTE_C1, NOTE_CS1, NOTE_D1,    // other octaves by multiplying by power of 2
 NOTE_DS1, NOTE_E1, NOTE_F1, NOTE_FS1,
 NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1,
};

byte waves[][16] = {
 {
 0x00, 0x20, 0x40, 0x60,
 0x80, 0xa0, 0xc0, 0xe0,
 0xff, 0xe0, 0xc0, 0xa0,
 0x80, 0x60, 0x40, 0x20
 }
 ,
 {
 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff,
 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00
 }
 ,
 {
 0xff, 0xee, 0xdd, 0xcc,
 0xbb, 0xaa, 0x99, 0x88,
 0x77, 0x66, 0x55, 0x44,
 0x33, 0x22, 0x11, 0x00
 }

};
//  Triangle wave, just wanted something different from the typical square wave.

MIDI_Class input;
int currentNote;
long currentPeriod;
int currentIndex;
unsigned long lastToggle;
int currentWave = 0;

void setup()  {
 pinMode(11, OUTPUT);  //set OC2A pin to output

 TCCR2A |= 1 << WGM20;  //     \
 TCCR2A |= 1 << WGM21;  //   }- Set timer to phase fast PWM mode
 TCCR2B &= ~(1 << WGM22);  //  /  

 TCCR2A |= 1 << COM2A1;  //      }- Set to non-inverting PWM
 TCCR2A &= ~(1 << COM2A0);  //  /

 TCCR2B |= 1<< CS20;  //      \
 TCCR2B &= ~(1 << CS21);  //   }- Set to prescalar of 1 (no prescalar).
 TCCR2B &= ~(1 << CS22);  //  /           62500 Hz PWM

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

 //Setting up timer1 for CTC mode
 TCCR1A &= ~(1 << WGM10);  
 TCCR1A &= ~(1 << WGM11);
 TCCR1B |= 1 << WGM12;
 TCCR1B &= ~(1 << WGM13);

 //Prescalar 1
 TCCR1B |= 1<< CS10;
 TCCR1B &= ~(1 << CS11);
 TCCR1B &= ~(1 << CS12);

 TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
 sei();

 input.begin();  //initialize MIDI input
 
 pinMode(2, INPUT);
 digitalWrite(2, HIGH);
 pinMode(3, INPUT);
 digitalWrite(3, HIGH);

}


void loop()  {
 int count = 0;
 if(digitalRead(2) == LOW)  {
   count++;
 }
 if(digitalRead(3) == LOW)  {
   count++;
 }
 currentWave = count;
 
 
 //Midi input handling

 if(input.read() == true)  {  //do we get signal
   if(input.getType() == NoteOn && input.getData2() != 0)  {  //is it a noteOn event,
     currentNote = input.getData1();                          //  and not with 0 velocity?

     int noteIndex = (currentNote - 23) % 12;  //figure which value in note array corresponds to
     //  recieved MIDI note
     int noteOctave = (currentNote - 23) / 12; //figure out what octave the note is
     unsigned long Hz = notes[noteIndex] * (1 << noteOctave);
     wOCR1A(hz2ocr(Hz * 4));
     wTCNT1(0);


   }
   else if(input.getType() == NoteOff || input.getData2() == 0)  {  //is it a NoteOff event,
     if(input.getData1() == currentNote)  {                         //  or 0 velocity NoteOn?
       currentNote = 0;
     }
   }
 }

 //  End of midi input handling



 if(currentNote != 0)  {
   TIMSK1 |= 1 << OCIE1A;
 }
 else  {
   TIMSK1 &= ~(1 << OCIE1A);
   OCR2A = 0;
 }


}


unsigned int hz2ocr(unsigned long hertz)  {
 unsigned long ocr = (16000000L / (hertz *2)) - 1;
 return int(ocr);
}


ISR(TIMER1_COMPA_vect)  {
 currentIndex = (++currentIndex) % 16;
 OCR2A = waves[currentWave][currentIndex];
}

void wOCR1A(unsigned int value) {
 unsigned char sreg;
 
 /* Save global interrupt flag */
 sreg = SREG;

 /* Disable interrupts */
 cli();

 /* Set TCNT1 to i */
 OCR1A = value;

 /* Restore global interrupt flag */
 SREG = sreg;
 
 sei();
}


void wTCNT1(unsigned int value) {
 unsigned char sreg;
 
 /* Save global interrupt flag */
 sreg = SREG;

 /* Disable interrupts */
 cli();

 /* Set TCNT1 to i */
 TCNT1 = value;

 /* Restore global interrupt flag */
 SREG = sreg;
 
 sei();
}


frequencies.h is a file with #defines for all the note frequencies.  It is copied and pasted from those found in the tone library, so including the tone library would work.
In the current rough state of the synth (stuff haphazardly put into a breadboard) different waveforms are selected by connecting pins 2 or 3 to ground.  0 pins connected, triangle.  1 pin, square.  2, sawtooth.

A song snippet I made while playing around with the synth and my kp3:
song

And hopefully soon to come:
http://www.youtube.com/watch?v=AtBuGlE_FVM
Original video got stuck processing.  Reuploaded, fixed.

Utopia

Really nice sound! If you ever find out the values for the rc circuit, please post them.

amacmullen14

Okay, I checked out the circuit,and
R=100ohm
C=0.33uF

And a 100uF dc blocking cap directly after the filter, before the output jack.

Utopia

I apologize if I sound dense, but is the wiring (both of the parts and to the speaker) complicated?

amacmullen14

Not at all complicated!

All it was (I've made progress on putting it inside the enclosure, it's no longer on the breadboard) was
Arduino pin 11 -----/\/\/\---------*-------------| |--------------------1/4 inch jack  ---->> mpc or kp3
                        resistor                            |             DC blocking cap                                    |
                                                                |                                                                            |
                                                               =  filter cap                                          Ground for jack
                                                               |
                                                        Ground

Hopefully my ASCII schematic does not get messed up!

keeper63

Was that entire song demo done with it? Regardless, it's catchy... :)
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

Utopia


Not at all complicated!


Thanks - I will have to put one together

amacmullen14

#7
Jul 18, 2011, 12:20 am Last Edit: Jul 18, 2011, 12:23 am by sciguy Reason: 1
Quote
Was that entire song demo done with it?


No, the drums and looping are from the kaoss pad, and the compressor is the mpc.  It was just something I did when I was bored and decided to play around with it.  
The synths in the video's music are also the arduino synth, run thru the kaoss pad for effects.

Oh, and so far, I have everything wired up in the enclosure except the audio circuitry.  So it has an external USB port, power jack and switch, MIDI in/out/thru jacks, with a switch to connect or disconnect the midi serial lines.  And an 8*16 RG led matrix, and 6 pots.  And in the future, 6 buttons.

amacmullen14

#8
Jul 26, 2011, 12:46 am Last Edit: Jul 26, 2011, 01:09 am by sciguy Reason: 1
So I made some not apparently relevant progress on the synth.
I've been working on the graphics side of it, the 8*16 led matrix.

As of now, it can do sprite manipulation, with a function that takes the sprite data and a pair of coordinates and displays the sprite at those coordinates.
And, which I'm more proud of, it can draw lines between any two given points on the screen! (or off the screen, it just doesn't show all of it)

All from scratch, no help from teh internets
(I found out about the Bresenham line algorithm after I made the code.  I unknowingly recreated the algorithm in my code before I had even heard of it!   :smiley-roll-blue:)

I'll post the code once I have some little bugs fixed, but in the meantime I'll post a video!
http://www.youtube.com/watch?v=X6L4a3UT9qw

amacmullen14

#9
Aug 18, 2011, 03:18 am Last Edit: Aug 19, 2011, 10:28 pm by sciguy Reason: 1
More Progress!!!

I added 2 1/4 inch jacks, one for the audio output, one for something else in the future, meybe a cv output, i dunno.

So, I have a fully working simple midi synth inside the plastic enclosure!  Selectable waveform by the first knob.
I still have to work out a couple bugs.  The main issue right now is that for some reason, only the square wave is outputted correctly.  The triangle has a weird peak where it should be at its lowest point, and the saw only seems to output the first few steps of the waveform, then it zeroes out.

Fixing that will be for another day, I'm happy with the progress for now.


By the next time I post (unless consulting for a problem) I will have a (hopefully polyphonic) 8bit synth with a GUI!

amacmullen14

Update:

I lied, I don't have a working synth with gui yet, because I'm having some problems with MIDI input.
But I finally went to radioshack to get some more knobs, so now it has knobs on all 6 pots.
I also got some buttons, so now it has 6 buttons, 4 in a d-pad orientation to the left of the screen, and 2 larger buttons to the right of the screen. 
So in addition to making a synth with audio generation capabilities of a gameboy, I could have a little gamesystem that runs on a 2 bit color 8*16 display!

salemlab

very nice project !!!

"(°_°)"

FireyFate

Keep the updates coming!  I'm interested to see where you are going with the screen implementation. 

amacmullen14

I'm working on getting polyphony an arpeggiation right now, so no screen yet. 

But I'm a bit stuck on a bit of code...   :(
(see my topic under audio about me being puzzled over a bit of code)

amacmullen14

Still haven't made progress on the audio code, still stuck on the same problem.  (here: problem?)

But I rewired the led matrix shield some (wirewrapping ftw) changing some pins, and now all 6 shift registers are daisychained and controlled thru hardware SPI, for extra speed.  This also means that I can use SPI to interface to a parallel-in, serial-out register for button inputs.

Thankfully, this practically does not interfere with the rest of the code at all, because the other OC pin for timer2 is pin 3, I'd just have to change the timer configuration a bit to use the B half of timer2.

Go Up