Products > Programming

Wanted: Algorithm for a Guitar tuner

(1/5) > >>

hamster_nz:
I've got everything I need for a DSP guitar tuner project.

- a poorly tuned 12-string guitar, that has been under the bed for a few years unplayed

- A Sipeed MAIX board (64-bit RISC-V, plenty of RAM and Flash, with floating point, I2S mic, 2" LCD and an I2S microphone)

- A working RISC-V SDK kit on my laptop

I've had a few attempts at various solutions, but nothing that will actually allow me to reliably tune a string.

I've got raw samples from the Mic at 48,000S/s, and the mic has an AGC so that is fine.

Most of my attempts have been around correlation of a few thousand sample with the desired frequency, an narrow bands either side, then showing that on the LCD. It of works, and you have to tune for a symmetric curve around the center target frequency.

Last night I tried correlation only against target frequency, looking at the phase of that signal relative to the phase of the same correlation a fixed number of samples later (about 1/48th of a second), hoping to measure how much the phase has drifted due to the the string being detuned.
That doesn't seem to be working.

I assume that everybody will say "FFT!" but when working with 8192 samples @ 48000S/s you get ~6Hz wide bins, which is audibly out of tune.

The fundamental frequency range for guitar strings are between about 200 Hz and 800 Hz, so I do have the option sample-rate conversion down to 3kS/s or so, but CPU cycles are supposed to be cheap  ;D

The range of indication is most likely +/- 10% of the target frequency, as strings can be about 1/3 of an octave apart.

Any hint/ideas?

Maybe a relatively bandpass around the target frequency, then count the period on the result?

Any idea of keywords for the underlying math I can read up on?

T3sl4co1l:
Right, 8192 samples isn't a lot.  But, as you say, cycles are free, so, why not more? :)

Note that you can't determine the frequency much better than the amount of time spent determining it.  That is, if you spend about 1/6th of a second acquiring samples, expect around a 6Hz uncertainty.

Can you do better?  Sure.  Obvious first step, add adjacent bins together and calculate the midpoint -- find the peak assuming it's somewhere smeared across a few bins.  That gets you about as much as you need.

Can you do better?  Sure.  But you will sacrifice some of the confidence for the most general method.  These are probably fine for a fairly periodic signal as you'd expect from a guitar.  You could calculate zero crossings (bad for nasty waveforms, mind) and count samples, including interpolating the sub-sample crossings, for example.

And then of course you just need a PID loop controlling the servo driving the tuner, but that's practically trivial right?... :-DD

Tim

KE5FX:
Hint: The output of an FFT consists of two components at each bin, magnitude and _______ :)

hamster_nz:

--- Quote from: KE5FX on May 30, 2019, 01:14:29 am ---Hint: The output of an FFT consists of two components at each bin, magnitude and _______ :)

--- End quote ---

I think that this what I attempted last night:

- Multiply the time-series samples with a sine and cosine of the target frequency and sum (i.e. almost the same as calculating the FFT bin of interest)

- Resolve that back to a phase angle with atan2()

- And then a short time later (say 1/10th of a second) calculate the same again.

- Adjust the phase of one of them for the expected difference in phase if it was perfectly in tune. (e.g. at 48kS/s and with a string frequency of 209.33 Hz 4800 samples away should have a phase difference of 20.933 cycles, or -0.067*2*PI.

- Calculate the difference in phase angles - it should be zero when perfectly in tune (but will also be 0 when out of tune by 10Hz  :D )

- Show in screen

As you will only get a +/- pi phase difference it limits things somewhat. If the "short time later" is 0.1s, you can only cleanly resolve at best +/- 5Hz, which is a bit too narrow as the notes either side are 221.77 Hz and 197.58 Hz.

Sounds like it should work, but...

(oh and 8192 samples is arbitrary, but picked so I don't have to overlap LCD and I2S transfers, and I want a reasonable frame rate / interactivity although 5 frames per sec is pretty sucky).

Tom45:
How about running a microphone into a zero crossing detector.  Then use the zero crossing detector to gate a reference oscillator which feeds a counter, and also to provide an interrupt on, say, the negative to positive zero crossing.

Then on each interrupt read the counter and increment the corresponding histogram bin.

[#] Next page

Go to full version