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