Frequency Detection

Hi everyone,

I am interested in creating various visual effects based on analog audio signals. I started by trying to detect pitches in the chromatic scale for the frequency range of a 22-fret guitar in standard tuning:

82.4 (E2) - 1174.7 Hz (D6) [Open 6th string to 1st string, 22nd fret]

After experimenting with several Arduino FFT libraries found on the web, I keep running into resolution limitations for low frequencies due to frequency bin size. For example, the FFT library from Open Music Labs has a maximum bin size of 256.

Since I do not need to detect frequencies greater than D6, the default sampling rate of 9.6KHz is adequate. However, this gives me a frequency resolution of 37.5Hz, which is insufficient to differentiate pitches at lower frequencies. Ideally, I'd like this to be around 5Hz. To accomplish this, I need a to increase the FFT bin size and/or decrease the sampling rate. It seems the best way, given the processing limitations of the Arduino, is to minimize the sampling rate to ~2350, but that still requires increasing the bin size to 512...which I'm not sure is feasible.

Alternatively, I could use a different algorithm, such as the YIN used in these projects:

http://www.instructables.com/id/Arduino-Frequency-Detection/

http://deambulatorymatrix.blogspot.ca/2010/11/digital-chromatic-guitar-tuner-2008.html

However, I would also like to detect the fundamental frequency of strummed chords, and this method seems vulnerable to complex signals.

I would greatly appreciate any advice on how to approach this problem. I'm beginning to wonder whether the Arduino is up to the task, but I'm looking at my $20 chromatic tuner and wondering how it can do this for so cheap. Maybe I should just hack it to wire the output to the Arduino :)

Fiver:
I’m beginning to wonder whether the Arduino is up to the task, but I’m looking at my $20 chromatic tuner and wondering how it can do this for so cheap.

Remember that the chromatic tuner only deals with one note at a time. It probably just measures time between zero crossings to measure the fundamental frequency.

You need more SRAM if you want a longer FFT. Perhaps an Arduino MEGA would help.

Fiver: I am interested in creating various visual effects based on analog audio signals. I started by trying to detect pitches in the chromatic scale for the frequency range of a 22-fret guitar in standard tuning:

A lot of people have posted on this forum intending to build a guitar tuner with an Arduino Uno, and using the FFT for automatic pitch detection. I haven't seen anyone report success. If you're going to do pitch detection this way, you'll have to be ingenious.

82.4 (E2) - 1174.7 Hz (D6) ... ... sampling rate ... ~2350 ...

Accurately identifying 1174 Hz with a sample rate of 2350 Hz will be challenging. The Nyquist criterion says that the sample rate has to exceed twice the frequency of the highest frequency signal component with significant content, not the highest frequency that you're interested in. 2350 is pretty close to twice 1174, so doing this detection will take a nearly perfect brick-wall filter. You'll need a higher sample rate, and an aggressive filter. Forum member pito has identified an eighth-order single-chip elliptical filter family that uses switched capacitors, whose cutoff frequency depends on either an external capacitor or an external clock signal. The datasheets can be reached from here: http://www.maximintegrated.com/en/datasheet/index.mvp/id/1899.

I keep running into resolution limitations for low frequencies due to frequency bin size.

The resolution of the FFT can be improved with parabolic interpolation. Here's a description from a guy at Stanford: https://ccrma.stanford.edu/~jos/parshl/Peak_Detection_Steps_3.html. The idea is that an input signal whose frequency doesn't exactly match a specific FFT output frequency will show up spread over several adjacent FFT output values. It's estimated by identifying a local peak in the bins, and fitting a parabola to the the peak and its two adjacent values, calculating the axis, and calling that the estimated frequency. I've never tried it with the FFT, but it has worked for me with YIN. Note, though, that the input values to the interpolation are completely different kinds of objects than the output of the FFT. You may get different results.

Alternatively, I could use a different algorithm, such as the YIN used in these projects: http://www.instructables.com/id/Arduino-Frequency-Detection/ http://deambulatorymatrix.blogspot.ca/2010/11/digital-chromatic-guitar-tuner-2008.html

The first referenced project doesn't use YIN. It estimates pitch by examining the time between zero-crossings with the highest slope. That's going to have a high error rate, because guitar harmonics aren't precisely integer multiples of the fundamental - their phase rotates with regard to the phase of the fundamental, so they contribute their slope to the sum at varying points in the cycle. As for what the error rate might be, I can't say. It might be entirely acceptable.

I would also like to detect the fundamental frequency of strummed chords, and this method seems vulnerable to complex signals.

YIN isn't applicable to polyphonic signals. It looks for a near match between one segment of the input and another segment some time later, and uses the time interval between those two segments as the inverse of the frequency. If a signal has two components of roughly equal amplitude at, say, 1 Hz and 1.5 HZ, YIN will estimate the [edit: revise flawed logic] frequency at the least common multiple, 3 Hz. signal's period as the least common multiple of their periods, 1 second and 2/3 seconds. That's 3 seconds, and the frequency estimate will be 1/3 Hz.

If you mean that you want to identify the lowest component of the signal, you're in for a bit of a challenge: chord components can easily be within a minor third of one another, overlapping in the FFT output enough to confound parabolic interpolation. If you want to do that reliably, I think you'll have to look at multiple cycles at fine resolution.

You don't say that you hope to identify individual strings in a polyphonic signal. If you do, that's a lot of work. Here's an Apple patent on that process: https://www.google.com/patents/US8592670?dq=8592670&hl=en&sa=X&ei=Y3_5U_buBZW2yATqj4Jo&ved=0CB0Q6AEwAA. It's feasible with an iPhone, running at more than a gigahertz, with gigabytes of storage; less feasible on an Arduino.

I'm beginning to wonder whether the Arduino is up to the task ...

Think a bit about how much latency you can tolerate. The Arduino can be persuaded to do interesting things if they don't take too much memory, but they sometimes take a long time. I've gotten reliable frequency identification for guitar-like signals in the 80-880 Hz range using YIN, but it takes about 150 milliseeconds to identify a string on the low end of the range. That's way too much latency if I'm trying to control a MIDI device, as the output would be always a sixteenth note behind the rest of the band at 100 beats per minute, but it's OK for other things, like identifying the frequency for a tuning application.

I'm looking at my $20 chromatic tuner and wondering how it can do this for so cheap

One thing to consider is that it doesn't do it all that fast. 150 milliseconds is practically imperceptible in that application. Another is that the tuner may use application-specific ICs to do its work. Here's a link to the only one I can find, quoted at about 6.5 USD in 10K lots: http://www.tuneric.com/literature.html.

Edit: grammar and spelling

Hi, some time ago I used some frequency detection to control some visual fx (via MIDI). The frequencies came form a guitar (sinlge tones only, one by one, not more than about two per second). I used an Arduino UNO (or maybe an older board - don't remember) and the frequency-measurement-library form here: http://interface.khm.de/index.php/lab/experiments/frequency-measurement-library/ (this site is in English) it makes use of the analog comparator of the atmega and it worked fine for my needs. I do not know how it will work with realy deep tones as I used a "nomal" electric guitar, not bass. the signal from the guitar does need some amplification, as the comparator triggers at about 100mV, I used a simple small audio mixer.

Udo

I'm very interested in doing this myself, mainly because I already ordered a 5m strip of LEDs :D

Would it not be possible to run your individual songs through a frequency detection program say on a Windows pc, output the "hits" on certain frequencies to midi, and load that midi into the arduino for each individual song?

Seems like this might be the way to go. However I have no idea how to go about doing a frequency detection program on a windows based PC.

interesting ? - http://forum.arduino.cc/index.php?topic=195085.0 -

Very interesting!

robtillaart:
interesting ? - http://forum.arduino.cc/index.php?topic=195085.0 -

Indeed.

I’m thinking that this line      period = i;would work better if it said      period = i - 1;
Variable i is the lag for the calculation that follows the local maximum, rather than the lag of the local maximum itself. At that point, I think that a better estimate of the period is the lag of the previous calculation. Tests with simple sine functions as input yield better results, within the one-sample-time resolution of the calculation, using i - 1.

Here’s the code I used:

//    FILE: frequencyDSP.ino
//  AUTHOR: rob tillaart (org version akellyirl)
// VERSION: 0.1.03
// PURPOSE: frequency analysis using DSP
//    DATE: 2013-10-27
//     URL: http://www.instructables.com/id/Reliable-Frequency-Detection-Using-DSP-Techniques

char rawData[970]; // CHANGE: deleted sample.h, added this declaration

void setup() 
{
  Serial.begin(115200);
  Serial.println("FrequencyDSP");
  Serial.println("------------");

  load(220.5);  // CHANGE: added this line

  uint32_t start = millis();
  float fr = autoCorrelateFrequency(rawData, sizeof(rawData), 22050);
  uint32_t stop = millis();

  Serial.print("freq:\t");
  Serial.println(fr);
  Serial.print("time:\t");
  Serial.println(stop - start);
}

void loop() 
{
}

float autoCorrelateFrequency(char * sample, int len, float sampleFreq)
{
  long sum = 0;
  long sum_old = 0;
  int thresh = 0;
  byte pd_state = 0;
  int period = 0;  // 0 results in inf

  // Autocorrelation
  for (int i=0; i < len && (pd_state != 3); i++)
  {
    sum_old = sum;
    sum = 0;

    for (int k=0; k <len-i; k++)
    {
      sum += (rawData[k]) * (rawData[k+i]);
    }
    sum /= 256;

    // Peak Detect State Machine
    // 0: initial, set threshold
    // 1: detect positive slope
    // 2: detect negative slope
    // 3: peak found
    if (pd_state == 0)
    {
      thresh = sum / 2;
      pd_state = 1;
    } 
    else if (pd_state == 1 && (sum > thresh) && (sum - sum_old) > 0) 
    {
      pd_state = 2;
    }
    else if (pd_state == 2 && (sum - sum_old) <= 0) 
    {
      period = i-1;  // CHANGE: revised this line
      pd_state = 3;
    } 
  }

  return sampleFreq/period;
}

// CHANGE: Added this function
void load(float f) {
  const float pi = 3.14159265359;
  for (int i=0; i<sizeof(rawData);i++) {
    rawData[i] = round(127*sin(2*pi*f*i/22050));
  }
}

The test frequency of 220.5 is admittedly a bit contrived. It’s selected to get a precise result with the one-sample resolution of the calculation.

[Edit: spelling]

If you can turn the fundamental frequency into a square wave (without the harmonics causing 1->0 or 0->1 changes), you could measure it very accurately with my FreqMeasure and/or FreqCount libraries. You might need an analog comparator circuit with hystersis to get a clean enough digital signal at only the fundamental frequency. This is probably the most realistic approach with limited 8 bit hardware.

But a FFT is really nice, since you can look for the fundamental without the harmonics causing so much trouble. You just need a LOT of memory. I just published an audio library (for Teensy) with a 1024 point FFT, which gives 43 Hz resolution. The actual FFT is based on ARM's DSP math library. Maybe a 2048 point one could be done? The math library has a 2048 point FFT, and I believe there's enough RAM.