Created
October 15, 2011 00:08
-
-
Save matthewSorensen/1288734 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************************** | |
* Hacked down to size, simplified, and tone quality improved. | |
* Also very heavily adapted for an Arduino-based Ondes Martenot. | |
* Matthew Sorensen, NOV-DEC 2009 | |
* | |
* (original copyright 2009. Adrian Freed) | |
**************************************************************/ | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
//Physical aspect of the device: | |
#define POT_PIN 1 //Potentiometer pin, analog 1 | |
#define VOL_PIN 0 //FSR volume pin, analog 0 | |
#define PWM_PIN 3 //The pin used to output sound (connected to the low-pass and audio-out, digital 3) | |
#define SWITCH_PIN 4 //Pin for switching between onboard-synth and pd, digital 4 | |
#define TONE_PIN 5 //Pin for switching waveforms, digital 5 | |
//For the interface to pure-data: | |
#define BAUD 115200 //Must be the same as the baud-rate for the PD comport | |
#define POT_CHAN 1 //[parser 1] returns the potentiometer data | |
#define VOL_CHAN 0 //[parser 0] returns the IR data | |
//Various things for the onboard synth: | |
#define PWM_DEST OCR2B //Place to output the new wave value | |
#define PWM_INTERRUPT TIMER2_OVF_vect //The interrupt triggered by PWM counter-overflow | |
#define INT_MAX 65535 //256*256 | |
#define SIGMAX 1023 //2^10-1, largest signal value | |
//Parameters controlling aspects of the synth: | |
#define BASEFREQ 100 //Lowest frequency possible | |
#define SCALE 1.7 //SIGMAX*SCALE + BASEFREQ = highest frequency possible | |
#define DELTA_AMP 100 //Responsiveness | |
#define THRES 100 //Lowest volume we can hear | |
#define SIG_THRES 10 //Input value that corresponds to THRES | |
const unsigned int LUTsize = 1<<8; | |
const unsigned int LUTmask = 0xFF; | |
int8_t* wavetable; | |
// A wave-form table designed to replicate the Ondes Martenot - from Lennart's Fourier analysis. | |
int8_t ondestable[LUTsize] PROGMEM = | |
{ | |
127,134,141,148,155,162,169,176,183,189,195,201,207,212, | |
217,222,227,231,234,238,241,244,246,248,250,251,252,253, | |
253,254,253,253,252,251,250,248,246,244,242,240,238,235, | |
233,230,227,225,222,219,216,214,211,208,206,203,201,199, | |
196,194,192,190,189,187,185,184,183,181,180,179,178,178, | |
177,176,176,175,175,174,174,174,173,173,173,173,172,172, | |
172,171,171,171,170,170,169,169,168,168,167,166,166,165, | |
164,163,162,161,160,159,158,157,156,155,153,152,151,150, | |
148,147,146,144,143,142,140,139,137,136,135,133,132,131, | |
129,128,127,125,124,122,121,120,118,117,116,114,113,111, | |
110,109,107,106,105,103,102,101,100,98,97,96,95,94,93,92, | |
91,90,89,88,87,87,86,85,85,84,84,83,83,82,82,82,81,81,81, | |
80,80,80,80,79,79,79,78,78,77,77,76,75,75,74,73,72,70,69, | |
68,66,64,63,61,59,57,54,52,50,47,45,42,39,37,34,31,28,26, | |
23,20,18,15,13,11,9,7,5,3,2,1,0,0,0,0,0,1,2,3,5,7,9,12,15, | |
19,22,26,31,36,41,46,52,58,64,70,77,84,91,98,105,112,119 | |
}; | |
int8_t sintable[LUTsize] PROGMEM = { // already biased with +127 | |
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173, | |
176,179,182,184,187,190,193,195,198,200,203,205,208,210,213,215, | |
217,219,221,224,226,228,229,231,233,235,236,238,239,241,242,244, | |
245,246,247,248,249,250,251,251,252,253,253,254,254,254,254,254, | |
255,254,254,254,254,254,253,253,252,251,251,250,249,248,247,246, | |
245,244,242,241,239,238,236,235,233,231,229,228,226,224,221,219, | |
217,215,213,210,208,205,203,200,198,195,193,190,187,184,182,179, | |
176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130, | |
127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81, | |
78,75,72,70,67,64,61,59,56,54,51,49,46,44,41,39, | |
37,35,33,30,28,26,25,23,21,19,18,16,15,13,12,10, | |
9,8,7,6,5,4,3,3,2,1,1,0,0,0,0,0, | |
0,0,0,0,0,0,1,1,2,3,3,4,5,6,7,8, | |
9,10,12,13,15,16,18,19,21,23,25,26,28,30,33,35, | |
37,39,41,44,46,49,51,54,56,59,61,64,67,70,72,75, | |
78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124 | |
}; | |
const int timerPrescale=1<<9; | |
//The oscillator variables | |
uint32_t phase;//Current position in phase | |
int32_t phase_increment;//Controls pitch | |
uint16_t amp;//Current amplitude | |
uint16_t idealAmp;//Amplitude we want | |
const int fractionalbits = 16; // 16 bits fractional phase | |
// compute a phase increment from a frequency | |
unsigned long phaseinc(float frequency_in_Hz){ | |
return LUTsize *(1l<<fractionalbits)* frequency_in_Hz/(F_CPU/timerPrescale); | |
} | |
void initializeTimer() { | |
// Set up PWM with Clock/256 (i.e. 31.25kHz on Arduino 16MHz; | |
// and phase accurate | |
//Timer 2, 8 bit | |
/* _BV(WGM20) (bit 0) toggles phase correct PWM, | |
* _BV(COM2B1)(bit 5) clears OC2B on match, sets 0C2B to bottom*/ | |
TCCR2A = _BV(COM2B1) | _BV(WGM20); | |
/* Bits 2:0 of TCCR2B select the clock source, in this case | |
* no prescaler */ | |
TCCR2B = _BV(CS20); | |
/*Enables the interrupt on counter overflow */ | |
TIMSK2 = _BV(TOIE2); | |
pinMode(PWM_PIN,OUTPUT); | |
} | |
void setup(){ | |
Serial.begin(BAUD); | |
pinMode(SWITCH_PIN,INPUT); | |
phase = 0; //Start out oscillator | |
phase_increment = 0 ;//0 hertz | |
amp = 0; //No sound | |
initializeTimer(); | |
wavetable = ondestable; | |
} | |
void loop() { | |
idealAmp =255*256;//Max | |
phase_increment = phaseinc(440.0); | |
while(1){ | |
if(digitalRead(SWITCH_PIN)){ | |
doXSerial(200); | |
}else{ | |
doXSynth(200); | |
} | |
} | |
} | |
void doXSynth(int x){ | |
while(x-->0){ | |
if(analogRead(TONE_PIN)){ | |
wavetable = sintable; | |
}else{ | |
wavetable = ondestable; | |
} | |
phase_increment = phaseinc((float)analogRead(POT_PIN)*SCALE+BASEFREQ);//Set freq | |
int sig = analogRead(VOL_PIN); | |
if(sig>SIG_THRES){//Any reading smaller than 10 means off | |
if(THRES>amp){//If off, turn on to lowest volume | |
amp=THRES; | |
} | |
idealAmp = THRES+(sig-1)*((INT_MAX-THRES)/(SIGMAX-SIG_THRES));//Set volume, scaling to 0-255 | |
}else{//Else turn off | |
amp =0; | |
idealAmp=0; | |
} | |
} | |
} | |
void doXSerial(int x){ | |
while(x-->0){ | |
send(POT_CHAN,analogRead(POT_PIN)); | |
send(VOL_CHAN,analogRead(VOL_PIN)); | |
} | |
} | |
//Look up wave in sine table, scale by amp, increment phase... | |
//Rinse and repeat. | |
int8_t outputvalue =0; | |
SIGNAL(PWM_INTERRUPT){ | |
PWM_DEST = outputvalue; //output first for better tone, then calc new | |
outputvalue = (((uint8_t)(amp>>8)) * pgm_read_byte(wavetable+((phase>>16)&LUTmask)))>>8; | |
phase += (uint32_t)phase_increment;//Overflow is good | |
if(amp!=idealAmp){//If the volume isn't where we want, change it slowly | |
amp += (amp>idealAmp)? -DELTA_AMP:DELTA_AMP; | |
} | |
} | |
//Will fail if chan>31, and data is truncated to 10 bits | |
//0000001111100000 0x3E0 | |
//0000000000011111 0x1F | |
//0000000011100000 0xE0 | |
void send(unsigned char chan,int data){ | |
if(chan<32){ | |
Serial.print(chan,BYTE);//Channel | |
Serial.print(((data&0x3E0)>>5)|0xE0,BYTE);//High | |
Serial.print((data&0x1F)|0xE0,BYTE); //Low | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment