#include <avr/io.h>
#include <avr/interrupt.h>
// 1. Create a wavetable
#define WAVETABLE_SIZE 256
const uint8_t wavetable[WAVETABLE_SIZE] PROGMEM = {
// 256 sine wave values ranging from 0 to 255, you can generate these using a script
// or use an online tool like: https://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,
0x98,0x9b,0x9e,0xa2,0xa5,0xa7,0xaa,0xad,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbe,0xc1,0xc4,
0xc6,0xc9,0xcb,0xce,0xd0,0xd3,0xd5,0xd7,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,
0xea,0xeb,0xed,0xee,0xf0,0xf1,0xf3,0xf4,
0xf5,0xf6,0xf8,0xf9,0xfa,0xfa,0xfb,0xfc,
0xfd,0xfd,0xfe,0xfe,0xfe,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,0xfd,
0xfd,0xfc,0xfb,0xfa,0xfa,0xf9,0xf8,0xf6,
0xf5,0xf4,0xf3,0xf1,0xf0,0xee,0xed,0xeb,
0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd7,0xd5,0xd3,0xd0,0xce,0xcb,0xc9,
0xc6,0xc4,0xc1,0xbe,0xbc,0xb9,0xb6,0xb3,
0xb0,0xad,0xaa,0xa7,0xa5,0xa2,0x9e,0x9b,
0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,
0x67,0x64,0x61,0x5d,0x5a,0x58,0x55,0x52,
0x4f,0x4c,0x49,0x46,0x43,0x41,0x3e,0x3b,
0x39,0x36,0x34,0x31,0x2f,0x2c,0x2a,0x28,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,
0x15,0x14,0x12,0x11,0xf,0xe,0xc,0xb,
0xa,0x9,0x7,0x6,0x5,0x5,0x4,0x3,
0x2,0x2,0x1,0x1,0x1,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x2,
0x2,0x3,0x4,0x5,0x5,0x6,0x7,0x9,
0xa,0xb,0xc,0xe,0xf,0x11,0x12,0x14,
0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x28,0x2a,0x2c,0x2f,0x31,0x34,0x36,
0x39,0x3b,0x3e,0x41,0x43,0x46,0x49,0x4c,
0x4f,0x52,0x55,0x58,0x5a,0x5d,0x61,0x64,
0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c,
};
#define SAMPLE_RATE 8000
// Timer variables
volatile uint16_t current_phase = 0;
volatile float phase_increment = 0;
// ADC settings
#define ADC_PIN 0
uint8_t sample = 0;
void setup() {
Serial.begin(115200);
// Set up the PWM output on pin 3 (OC2B)
pinMode(3, OUTPUT);
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
OCR2B = 0;
// Set up Timer1 for audio sampling
cli();
TCCR1A = 0;
TCCR1B = _BV(WGM12) | _BV(CS10);
TCNT1 = 0;
OCR1A = F_CPU / SAMPLE_RATE;
TIMSK1 = _BV(OCIE1A);
sei();
// Set up the ADC
ADMUX = _BV(REFS0) | (ADC_PIN & 0x07);
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
ADCSRB = 0;
DIDR0 = 0;
}
ISR(TIMER1_COMPA_vect) {
// 4. Generate the output signal using the wavetable in real-time
uint8_t current_index = current_phase >> 8;
sample = pgm_read_byte(&wavetable[current_index]);
OCR2B = sample; // Set the PWM duty cycle
current_phase += (uint16_t)phase_increment;
}
void loop() {
Serial.println(sample);
// Start the ADC conversion
ADCSRA |= _BV(ADSC);
// Wait for the conversion to complete
while (ADCSRA & _BV(ADSC));
// Read the ADC value (10 bits)
uint16_t adc_value = ADC;
//Serial.println(adc_value);
// Convert the ADC value to a voltage in the range of 0-5V
float voltage = (adc_value / 1023.0) * 5.0;
// Calculate the frequency based on the voltage (1V/Oct)
float base_freq = 55.0; // A1 note as the base frequency
float freq = base_freq * pow(2, voltage);
// Update the phase increment based on the new frequency
phase_increment = (WAVETABLE_SIZE * freq) / SAMPLE_RATE;
}