Created
October 6, 2011 19:09
-
-
Save lemilonkh/1268331 to your computer and use it in GitHub Desktop.
Hexamin - An air controlled synthesizer
This file contains 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
// hexamin.c | |
// (c) 2011 by Milan Gruner | |
// designed for ATmega8 | |
#define F_CPU 8000000L | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <util/delay.h> | |
// helpers | |
#define _(PIN) (1 << PIN) | |
#define bool uint8_t | |
#define true 1 | |
#define false 0 | |
#define u8 uint8_t | |
#define u16 uint16_t | |
int map(int x, int in_min, int in_max, int out_min, int out_max) { | |
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; | |
} | |
// pinout | |
#define AUDIO _(PB0) // speaker / phono output | |
#define SENSORS PORTC // the whole analog port is used by 6 IR distance sensors | |
#define LED _(PD0) // tempo display | |
// constants | |
#define INACTIVE 50 // the ADC value that means >>deactivate feature<< | |
#define BASE_THRESH 2 | |
#define COUNT 4 // how many ADC measurements will be made for one value | |
#define MIN_VAL 200 // ADC value | |
#define MAX_VAL 900 //ADC value | |
#define PITCH 0 // Control the base tone | |
#define DELAY 1 // Tone Delay | |
#define STEP 2 // Main Loop Delay | |
#define DUTY_CYCLE 3 // Uptime divider | |
#define DRUMS 4 // Beat generation | |
#define RECORD 5 // Select the record channel | |
// vars | |
int step, delay, half_delay; | |
bool output_on = true; | |
u16 base; | |
u16 data[6] = {0,0,0,0,0,0}; // measured values | |
u16 val[6] = {0,0,0,0,0,0}; // computed values | |
bool activated[6] = {0,0,0,0,0,0}; // is the feature activated? | |
const int range[6][2] = { // min and max values | |
{300,4000},// Tone (Hz) | |
{0,500}, // Delay (ms) | |
{50,200},// Step => Main Loop Delay (ms) | |
{1,10}, // Duty Cycle Divider | |
{0,7}, // Drums | |
{0,9} // Record Channels | |
}; | |
// functions | |
u16 adc_read(u8 channel) { | |
ADMUX = channel; // select the channel | |
ADCSRA |= _(ADSC); // get the *next* conversion started | |
while(ADCSRA & _(ADSC)) { } // wait for the ADC to finish measuring | |
// read the value | |
u16 result = ADCL; | |
u16 temp = ADCH; | |
result += (temp << 8); | |
return result; | |
} | |
u16 adc_average(u8 channel, u16 count) { | |
u16 val = 0; | |
for(int i = 0; i <= count; i++) { | |
val += adc_read(channel); | |
} | |
return val / count; | |
} | |
// main function | |
int main(void) { | |
// init hardware | |
DDRC = LED; | |
DDRB = AUDIO; | |
// ADC init | |
ADMUX = 0; //external reference (5v), single ended input ADC0 | |
ADCSRA = _(ADEN) | _(ADPS2) | _(ADPS1) | _(ADPS0); // enable ADC; clock prescaler 1/128 | |
ADCSRA |= _(ADSC); // warm the ADC up (do a first conversion) | |
// init audio timer | |
TCCR1A = 0b00100011; // set output OC1B at 0 and remove it at OCR1B | |
TCCR1B = 0b00011010; // Fast PWM with OCR1A as top, timer with CPU-CLK / 8 => 1MHz | |
// enable interrupts | |
sei(); | |
// initial measurement to calibrate the sensors | |
base = (adc_average(0, COUNT) + adc_average(1, COUNT) + adc_average(2, COUNT) | |
+ adc_average(3, COUNT) + adc_average(4, COUNT) + adc_average(5, COUNT)) / 6; | |
for(;;) { | |
// gather the data | |
for(int i = 0; i < 6; i++) { | |
data[i] = adc_average(i, COUNT); | |
activated[i] = (data[i] <= INACTIVE); | |
} | |
// evaluate it | |
for(int i = 0; i < 6; i++) { | |
if(data[i] < (base- BASE_THRESH)) { | |
val[i] = map(data[i], MIN_VAL, MAX_VAL, range[i][0], range[i][1]); | |
if(data[i] == INACTIVE) | |
val[i] = 0; | |
if(val[i] > range[i][1]) | |
val[i] = range[i][1]; | |
} | |
} | |
// Pitch | |
if(output_on) | |
OCR1A = 1000000 / val[PITCH]; | |
// Delay | |
if(activated[DELAY]) { | |
if(delay == 0) { | |
delay = val[DELAY] / step; | |
half_delay = delay / 2; | |
} else { | |
delay--; | |
} | |
output_on = delay < half_delay; | |
} else { | |
output_on = true; | |
} | |
// Step | |
step = val[STEP]; | |
// Duty Cycle | |
if(activated[DUTY_CYCLE]) { | |
if(output_on) | |
OCR1B = OCR1A / val[DUTY_CYCLE]; | |
} else { | |
if(output_on) | |
OCR1B = OCR1A / 2; | |
} | |
// Drums | |
// TODO yet to implement ;) | |
// Record | |
// TODO yet to implement ;) | |
// blink the LED in the beat | |
PORTD |= LED; | |
_delay_ms(step); | |
PORTD &= ~LED; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment