I think your code now looks something like the following. I made the buffer 1000 samples which seems to help reduce the noise (as it should), but slows down the autocorrelation rate (also expected). Using a function generator on my cell phone and going between 320 Hz and 350 Hz sine wave, I got the plot below. The microphone had to be very close to the phone's speaker. There's some noise in the measurement and a few outliers, but generally it seems to do pretty well.
// Arduino Pitch Detection on A0 with autocorrelation and peak detection
// Original author(s): akellyirl, revised by robtillaart and MrMark
#define E1 329.63
#define B2 246.94
#define G3 196.00
#define D4 146.83
#define A5 110.00
#define E6 82.41
float sampleFrequency;
float frequencyRange = 10;
volatile int interruptCount = 0;
const int arraySize = 1000;
volatile byte rawData[arraySize];
long currentSum, previousSum, nextSum ;
int threshold = 0;
float frequency = 0;
byte pdState = 0;
void setup() {
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TIMSK1 = 0;
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS10);
TIMSK1 |= (1 << OCIE1A);
ADCSRA = 0;
ADCSRB = 0;
ADMUX |= (1 << REFS0);
ADMUX |= (1 << ADLAR);
ADCSRA |= (1 << ADPS1);
ADCSRA |= (1 << ADATE);
ADCSRA |= (1 << ADEN);
ADCSRA |= (1 << ADSC);
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
}
ISR(TIMER1_COMPA_vect) {
// Collect analog signal
rawData[interruptCount] = ADCH;
interruptCount ++;
}
void readData(float baseFreq) {
int OCRvalue = 2 * (16000000 / (baseFreq - frequencyRange)) / arraySize - 1;
OCR1A = OCRvalue;
sampleFrequency = 16000000 / (OCRvalue + 1) ;
interrupts();
while (interruptCount != arraySize); // wait for data
noInterrupts();
interruptCount = 0;
}
void findFrequency() {
// Calculate mean to remove DC offset
long meanSum = 0;
for (int j = 0; j < arraySize; j++) {
meanSum += rawData[j];
}
char mean = meanSum / arraySize;
// Autocorrelation
currentSum = 0;
pdState = 0;
int period = 0;
for (int i = 0; i < arraySize && (pdState != 3); i++) {
// Autocorrelation
previousSum = currentSum;
currentSum = 0;
for (int k = 0; k < arraySize - i; k++) currentSum += (rawData[k] - mean) * (rawData[k + i] - mean);
// Peak detection
if (pdState == 0) {
threshold = currentSum / 2;
pdState = 1;
}
else if (pdState == 1 && (currentSum > threshold) && (currentSum - previousSum) > 0) {
pdState = 2;
}
else if (pdState == 2 && (currentSum - previousSum) <= 0) {
// quadratic interpolation
nextSum = 0 ;
for (int k = 0; k < arraySize - i + 1; k++) nextSum += (rawData[k] - mean) * (rawData[k + i + 1] - mean);
float interpolationValue = 0.5 * (nextSum - previousSum) / (2 * currentSum - previousSum - nextSum);
period = i + interpolationValue;
pdState = 3;
}
}
// Frequency identified in Hz
if (threshold > 100) {
frequency = sampleFrequency / period;
if (frequency < 400) {
Serial.println(frequency);
}
}
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
readData(D4);
digitalWrite(LED_BUILTIN, LOW);
findFrequency();
}
