Go Down

Topic: Help with Autocorrelation in Frequency detection (Read 707 times) previous topic - next topic

el_supremo

Quote
https://www.pjrc.com/teensy/td_libs_FreqMeasure.html
That is not even close to what you've been asking to do. That is 8 channels of digital signals - not a musical instrument. It is reasonably easy (as is evident from that Youtube video) to measure the frequency of a digital signal with considerable accuracy and precision. But that is not the same as using an FFT to try to determine the frequency of a musical note.


Pete
Don't send me technical questions via Private Message.

3DPiper

#16
May 11, 2016, 03:43 am Last Edit: May 11, 2016, 03:58 am by 3DPiper
Quote
That is not even close to what you've been asking to do.
Correct, as I said it was not sampling a music instrument.

Maybe this is a job for a Raspberry Pi, they seem much more powerful..?



tmd3

You asked:
Any way to get the code in the original post working for A0 input?
Yes.  Below is a sketch that fetches analogs from A0, prints them to the serial monitor, waits for input, and does it all again.  It's simple and straightforward, and annotated to a reasonable degree.  You should be able to adapt it.  The analog acquisition routine is blocking code; nothing else will happen while you're getting analogs.

If you decide to use the code from this "instructable" - http://www.instructables.com/id/Reliable-Frequency-Detection-Using-DSP-Techniques - be aware that the reported frequency has a bit of bias.  The sketch looks for a peak, a value that's adjacent on either side to smaller values, and then reports the last value it examined.  A better estimate would be one less than that - the lag where the highest value was calculated.  The "instructable" code reports the the period as the value of the lag just beyond the peak, and it's too long.

I also think that there's no value in calculating sums for lags bigger than half the number of samples.  Autocorrelation needs two cycles to have a reasonable chance of estimating the frequency.  If you examine lags bigger than half the sample size, you're looking at frequencies that will autocorrelate one cycle, and part of another, and then run out of samples.  I think the calculations should stop when the lag is half the number of samples.

Over at instructables, they have a "say something nice," policy, no matter how stupid the post might be.  So, when something stupid gets posted, it tends to stay stupid forever.  We don't have a policy like that in this forum.  You may get roughed up a bit, but you'll see that erroneous code generally gets called out, and somebody fixes it.  Somebody may point out something foolish in the sketch below, and you'll only benefit from that.

The sketch below has a constant definition for the analog reading when the input is zero.  If you use an AGC microphone from Adafruit, like one of the projects you mentioned, you'll probably find that its quiescent value is something other than midscale on the ADC.  Autocorrelation is a bit sensitive to a DC offset, so you'll want to set that value to something consistent with your hardware.

You say:
One of my tuners is based on an Arduino ...
In return for this code, I hope you'll tell us about that tuner, with a link to a project somewhere, and with your impressions of how well it performs.

All that said, here's the sketch:
Code: [Select]
const uint16_t nSamples = 512;
int8_t analogs[nSamples];
const uint8_t analogZero = 128;

void setup() {
  DIDR0 = 0x01;  // A0 digital input buffer off

  Serial.begin(115200);

  ADMUX = 0x60;  // AVCC, left-justified, channel 0
  ADCSRA = 0xF5; // Set up ADC
  // Enable, Start, Auto-trigger,
  // Free-run, Clear flag, disble interrupt,
  // clock divisor 32, Rate ~ 38462/sec adc0
}

void loop() {
  acquire(analogs, nSamples);
  show(analogs, nSamples);
  waitKey();
}

void acquire(int8_t * a, uint16_t n) {
  ADCSRA = 0xF5;      // Clear intr flag, keep other settings
  for (uint16_t i = 0 ; i < n ; i++) {
    while (!(ADCSRA & 0x10)); // Wait for conversion
    ADCSRA = 0xF5;    // Clear intr flag, keep other settings
    int16_t j = ADCH; // Get high byte of ADC
    j -= analogZero;  // Subtract offset
    a[i] = (int8_t)j; // Save it
  }
}

void show(int8_t * a, uint16_t n) {
  for (uint16_t i = 0 ; i < n ; i++) {
    Serial.print(i);
    Serial.print(" ");
    Serial.print(a[i]);
    Serial.println();
  }
}

void waitKey() {
  // Wait for a character on the Serial interface
  //   Clear the buffer, discard everything
  while (!Serial.available()) {}
  while (Serial.available()) {
    Serial.read(); // Read and discard a character
    delay(4);      // Wait to see if another char comes
  }
}

Paul Stoffregen

You probably want the YIN algorithm, with is based on autocorrelation.

There's an implementation in the Teensy Audio Library, contributed by Collin Duffy.  Here's the documentation (look at the right-side panel):

http://www.pjrc.com/teensy/gui/?info=AudioAnalyzeNoteFrequency

There's no hope of ever running this on normal 8 bit AVR-based Arduino.  It's a bit taxing for Teensy 3.2, but runs well and detects the fundamental frequency quite well, even when its harmonics are very strong.

Paul Stoffregen

There's an example that comes with Teensyduino.  With Teensy selected in the Tools > Boards menu, you can find it in File > Examples > Audio > Analysis > NoteFrequency.

The example plays a guitar sample and analyzes it.  Of course you can use the design tool to connect live audio input to the notefreq analysis object... but for an example program to demonstrate how it work, recorded samples give you a good idea of its performance on known sounds, and you can just run it with only a Teensy 3.2 and Audio Shield (or even without the shield, if you connect the object to send the sound to the DAC pin).

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy