Real Time Pitch Shifter

I am trying to make a real time pitch shifter using the Arduino Nano. I ended up going with resampling via a circular buffer as my modulation technique. I sample an audio input at A0, and I send out the data to an R2R ladder connected to PORTD. I have a button attached to D8 that connects the pin to ground in order to trigger an interrupt in the code. The whole program essentially works off of interrupts; one for the ADC conversion, one for the DAC, and one for the state change of the interrupt pin. I was able to get the device to run sound before, but now with the modulation nothing will come out. I am wondering if my current modulation technique simply doesn't work or if I made a stupid mistake. I am changing the value of OC0RA by three with the pushbutton interrupts in order to resample the audio and shift it up by a semitone. I am planning on expanding the capabilities of the pitch shifting, but I want to get the foundation down first. Some of the program is based off code in a technoblogy article which uses the attiny85. Here it is for reference.

And here is my program. Any insights would help, I have no idea why it is not working.

volatile uint8_t circularBuffer[256];
volatile uint8_t readPointer, writePointer, lastPointer, audioSample;
#define modulationPin 8

void setup(){
DDRD = 0xFF;//set port d (digital pin s0-7) as outputs
DDRC = 0x00;//set all analog pins as inputs
pinMode(modulationPin, INPUT_PULLUP); // Setup D11 with pullup

cli();//disable interrupts

//----------PIN CHANGE SETUP----------
PCICR |= 0x04; // turn on Port D
PCMSK0 |= 0x01; // turn on PB0, aka PCINT0

//----------RESAMPLING SETUP----------
TCCR0A = 2<<WGM00; // CTC mode
TCCR0B = 2<<CS00; // set timer0 with prescalar of 8
OCR0A = 51; // 38.5 kHz interrupt
TIMSK0 = TIMSK0 | 1<<OCIE0A; // enable interrupts

//----------ADC SETUP----------
ADCSRA = 0; //clear ADCSRA and ADCSRB registers

ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value to read highest 8 bits from ADCH register only

ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with prescalar of 32
ADCSRA |= (1 << ADATE); //enable auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements

sei();//enable interrupts

//----------WRITING SAMPLES TO BUFFER----------
ISR(ADC_vect) {
circularBuffer[lastPointer] = audioSample;
audioSample = ADCH; //send 8 bit value from analog pin 0 to DAC
circularBuffer[writePointer] = (circularBuffer[writePointer] + audioSample)>>1;
lastPointer = writePointer;
writePointer = (writePointer + 1) & 0xFF;

//----------READING SAMPLES FROM BUFFER----------
PORTD = audioSample;
readPointer = (readPointer + 1) & 0xFF;

//----------MODULATING AUDIO STREAM----------
ISR(PCINT0_vect){ //pin change D8 - D13
int button = PINB;
if (button == 0x00){
OCR0A = OCR0A - 3;

void loop(){

I would recommend NOT using Timer0 for timing since that disables millis(). I'd use Timer1 which has 16-bits of precision and allows much more precise frequency control.

I would not use an interrupt for your input button. I would use normal Arduino programming in loop() to read and debounce buttons for shifting frequency up and down.

I'm a little confused why the new audio sample is averaged with one of the old samples.

I am VERY surprised that the ISR that is supposed to read samples from the buffer, doesn't. It just outputs the latest audio sample and modifies a buffer pointer that is not used for anything.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.