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:
#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:
songAnd hopefully soon to come:
http://www.youtube.com/watch?v=AtBuGlE_FVMOriginal video got stuck processing. Reuploaded, fixed.