Monday, November 15, 2010

Digital Chromatic Guitar Tuner (2008)

Back in 2008, we organized a family gift exchange for Christmas, and one of the items on my brother-in-law's want list was a chromatic guitar tuner for his band Guajira.  Seeing this, I began thinking about how one would go about building such a device, and, thinking it would make a cool Christmas vacation project, I decided to try my hand at it.  After a bit of research, I started designing on December 19th; after much programming, building and testing later, I finished with moments to spare on December 24th.


Here is the video I posted shortly thereafter:




Over time the video has elicited many technical questions from viewers, questions which I couldn't answer in full detail until now for reasons unnamed.  I'll try to provide a bit more detail than usual in this writeup, but keep in mind this project is almost two years old so the details are getting hazy.  :P  Also, since I no longer have the device in my possession, having given it away at the gift exchange, I'm unable to take new pictures of it, and I'm stuck with cruddy webcam pics.


The Tuner


As seen in the video, the device has a few functions.  It accepts analog input from a standard 1/4" TRS jack, performs monophonic pitch detection on it, and either displays tuning information or outputs MIDI signals based on the detected pitch.  Tuning can be adjusted by moving A up or down a few hertz from 440.  There is a bypass switch, a backlight switch for the LCD and a contrast ratio pot; the whole thing runs on either DC power or a 9V battery.


You can get the code from the following links:

Original code used in the device, tested; builds against Arduino 0.22.  Dropbox / Google Drive
Altered code which compiles against Arduino 1.01 and 1.05.  This code is untested, since I do not have a physical device on which to do so, but I'm fairly confident it should work.  Dropbox / Google Drive

For the Google Docs link, you can use the File/Save option to save the archive to your disk.

The Electronics


First off, I am far from being proficient with hardware design; my experience is quite limited, and I'm much more comfortable with digital than analog.  I didn't have an oscilloscope when I built the tuner, so I did a lot of fiddling with component values using just a multimeter and a few speakers to listen for noise.  (I've since acquired and assembled a DPScope, they're excellent.)  As such, the design process was very much empirical, and I had fun swapping parts to see what worked best without the worry of frying a few pennies' worth of electronics.


Also, missing from these schematics is an Arduino.  I used a Real Bare-Bones Board from Modern Device, which I recommend; cheap, tiny, easy to assemble.  Signals named A5, D4, D5, D6 etc.  in the following schematics connect to the pins with the same name on the ATmega168.


There were a few LED connections and simple pushbuttons, whose circuitry I didn't outline here.  If you're wondering how to connect such devices to an Arduino, I would suggest the Arduino Playground as a starting point - it's a great wiki.


  The Power Stage

The power stage, which provides regulated 9V power to the rest of the circuit.


POWERSW is the system's power switch, which does its job by connecting the system ground to the power jack's ground pin (pin 3 on DCEXT).


In the case where no external jack is connected, DCEXT's pin 2 is connected to pin 3; the LM317T remains unused, and the 9V battery directly feeds VCC and GND.


In the case where an external power jack is connected, DCEXT's pin 2 remains floating, which disconnects the battery ground from the system.  The LM317T turns on and regulates outside power; voltage is adjusted to 9V using VADJ, as per the 317's datasheet.  CFILTPOWER is there to smooth out ripple, acting as a low-pass filter.  I had a 100uF lying around so I used that.  (The LM317 datasheet recommends 10uF or more.)


What I learned:
  • The battery leaks 6mA through R2 and VADJ, which is quite crappy.
  • You mileage may vary when adding a filtering capacitor before the LM317T; I found it made no discernable difference in this case.
  The Input/Bypass Stage


Input and bypass.


Well, it doesn't get much simpler than this: a dual-pole, dual-throw switch that either routes the signal from the input jack to the circuit or right back out, like a 2-way multiplexer.


What I learned:
  • The foot switch was difficult to source, and once I found an appropriate one, it took up LOTS of space in the case - on the order of two cubic inches.  I hadn't really planned for this.
The Buttons

Button wiring; not much to see here, just a simple pull-up resistor setup.  As reader Geoff Steele pointed out, you can also simply use the ATmega's internal pull-up resistors.

    The LCD Stage

    LCD connections.

    Once again, pretty standard stuff.  The LCD I used had a backlight, hence BACKLIGHTSW.  Contrast adjustment is done using RCONTRAST, on pin 3.  Pins 4, 6, 11, 12, 13 and 14 form a 4-bit data connection to the Arduino, as described on the Arduino LiquidCrystal page.


    What I learned:
    • The cheap surplus MTC-16205D LCD I used required some extra "convincing" to boot up properly.  I included the LiquidCrystal library directly in the PDE and modified the source to get it working.
      The MIDI Stage

    MIDI connector connections.

    More standard fare; I recommend the ITP page about MIDI output from Arduino, it's great!

      The Amplification Stage

    The amplification stage, which takes the input from the guitar and feeds it to the Arduino.  (See note below about INPUTCOUPLING capacitor, I think I had the wrong value marked down.)

    This stage is the one I spent the most time trying to get right, and (I'm guessing) probably the portion of the circuit which is of most interest to the people trying to build something similar.  I'm positive there are many grievous design errors in here, but in the end I got it working well enough.

    Overall, what this circuit does is take the input, adjust its volume (VADJ), amplify it (the LM386), and convert it to a form which the Arduino can sample (R7/R8/D1/D2).

    I began with the application entitled "Amplifier with Gain = 200" from the LM386 datasheet:
    • Volume adjustment is done using VADJ1 as a voltage divider.  I preferred this approach over adjustable amplification gain because the physical mapping of "angle of the volume potentiometer" to "amplification volume" was easier to control this way; judging by the LM386's typical applications, this seems to be an oft-favoured way of doing things.
    • The LM386 is setup as a fixed gain amplifier, whose gain is adjusted to 200 using CFILT3, as per the datasheet.
    • The output is fed through a low-pass filter built using CFILTAUDIO and R6; this rejects unwanted noise from the amplifier.  Without it, you can hear AM radio faintly, and there is a lot of crackle in the amplified sound.
    • An output coupling capacitor, OUTPUTCOUPLING, blocks any DC bias at the output of the amplifier, preventing it from leaving that stage and entering the following portion of the circuit.  (If a speaker was connected directly to the output of the amplifier, DC would flow through the speaker's coils, which are delicate and may heat up.  Here, instead of a speaker, we have an Arduino; more on this below.)
    You'll also note that I made a few modifications:
    • I added R5 to set the minimum amplification level.  R5 effectively create a "floor" for the voltage divider formed by R5 and VADJ1.
    • I added the INPUTCOUPLING capacitor to fix a buzzing problem; I think I wound up using a bigger value than 0.011uF, however, since that's an unusually small value for a coupling capacitor, one which I think might have filtered the signal a bit too much.  Much like the output coupling capacitor, this prevents DC from entering the op-amp (LM386) and being amplified, offsetting the output signal.  I distinctly recall trying multiple values on for size here.
    • Instead of connecting a speaker to OUTPUTCOUPLING, I connected an analog input on the Arduino so I could sample the signal.
    There is a small problem here: you can't just take the output from the LM386 and connect it to the Arduino pin.  This is because the amplifier, as set up, outputs signals from 0V to 9V, centered around 4.5V; under normal conditions, an Arduino can only sample analog signals between 0V and 5V, so a direct condition would not only saturate the ADC but likely damage the Arduino's input.

    To rectify this situation, we have to do two things.  Firstly, we can add D1 and D2 as shunt diodes to clip the signal to 5V and 0V, respectively.  In practice, the forward bias of the diodes allow the signal to exceed the allowed range slightly, but the excess shouldn't damage your Arduino.  I used fast-switching 1N914 diodes, which I just realized have a forward bias voltage of 1V; this means the signal can still range from -1V to 6V, which is probably a bit much to be safe.  Nevertheless, I've had no problems so far... knock on wood.  :P

    The second thing we must to is a bit trickier.  The output of the LM386 centers around 4.5V, which is half its supply voltage.  The Arduino, however, has a mid-range input of 2.5V, which is half-way between 0V and 5V.  If the Arduino just sampled the signal as clipped by the diodes, we'd be sampling the amplified signal in an asymmetric fashion, with 4.5V of range on the negative side (i.e. anything below 0V on the amplifier input will be below 4.5V on the output) but only 0.5V of range on the positive side.  This means that the Arduino would get a skewed view of the waveform from the LM386, which would make it difficult for us to have an accurate "big picture" of the waveform for frequency analysis.

    To fix this, we must add a DC bias to the signal, to "re-center" it around 2.5V.  This is done with the help of R7 and R8, which form a sort of voltage divider.  Since R7 and R8 have the same value, when no AC is coming through the coupling capacitor, the voltage at the Arduino pin is 2.5V.  When the LM386 is busy amplifying a signal, the AC component of the amplifier's output makes it through OUTPUTCOUPLING, nudging the rest point of 2.5V up and down, if you will.  (It is important to use large values for R7/R8 in order to raise the cutoff frequency of the high-pass filters they form with the OUTPUTCOUPLING capacitor.)

    You may wonder what we do about scaling the range of the signal.  The answer is: nothing, VADJ1 takes care of that for us.  Since different guitar pickups have different signal levels, on the tuner I built, the user adjusts VAJD1 using a potentiometer.  Software-driven LEDs indicate amplified (and sampled) signal strength for calibration.


    What I learned:
    • Despite warnings from my E&CE241 teachers and lab instructors to the contrary, as long as you keep the datasheet warnings in mind and your finger not too far from the kill switch, it is perfectly fine to try various component values in your circuit.  In fact, it's fun and instructive, and I plan to do more of it in the future.
    Notes on the Circuit as a Whole

    Seeing as how it's performing signal analysis, the tuner is fairly sensitive to power supply noise.  About a year after the tuner was made, I took it in for a "service call".  The unit sometimes no longer detected pitch properly; after some testing, I realized this only occurred if the LCD backlight was on and an external power adapter was used.  It seems that when the LCD backlight is on, the extra load on the power supply stage increases the ripple which makes it through to the amplifier stage and Arduino supply (which, in turn, probably affects the reference voltage of the ATmega168's ADC to some degree, even though the Arduino has its own regulator).  My hypothesis is that this is due to failure of some kind in the power supply filtering; possibly my recycled 100uF capacitor gave up the ghost or otherwise deteriorated; perhaps my soldering was suboptimal; perhaps there are gremlins in the box.  In any case, this is very similar to what occurred on a breadboard when I accidentally knocked the 100uF capacitor out.

    The Software

    The software does a few things.  I won't really discuss the pushbutton logic and so on here, since there is a lot of documentation available about that.  The two things I get the most questions about are the pitch-detection algorithm and the method I used to output slim vertical bars on the LCD.

    How to Perform Pitch Detection


    I tried a few "heuristic" algorithms - counting zero crossings, analysing the first-order derivative, and so on - but these often depended on the shape of the signal, which gave erratic results.  (Even a single vibrating string has multiple harmonics which make such approches difficult to work with.)  I wound up settling on a home-grown variation (and simplification) of an algorithm called YIN, which you can find described here.  Based on autocorrellation, this algorithm worked much better than all other approaches.  However, as you might tell from the paper, it is relatively expensive in terms of computation and memory, at least as far as an ATmega168 goes.  (Newer Arduinos now use the ATmega328; double the SRAM goes a long way, but you might be constrained by the speed at which you can process the data.)  The challenge was to adapt YIN to make it work with a 16MHz CPU that has no FPU and a few hundred bytes of available RAM.

    I won't explain the full details of YIN here, but I will explain the gist of it, to show how I adapted it for the Arduino.  Autocorrellation-based approaches are actually reasonably simple to visualize.  Given a quasi-periodic signal like that produced by a vibrating string, the idea is to take the original waveform:

    The basic sampled signal.

    And then, we create a virtual copy of the signal, shifting it over a bit:
    Copy of the original signal.

    Then, we compute some function of the area between the two resulting signals, shown in purple below:
    The idea is to keep sliding the copy over until the area between the two resulting signals is minimized.  Notice how the red copy keeps sliding over to the right, and how it gets closer and closer to "matching" the blue copy again.
    In practice, the steps are much smaller.  I made them a bit larger above to demonstrate the idea.  However, since we're getting quite close to the red waveform overlapping the blue one again, for the purposes of illustration, let's reduce the step size a bit.  (Again, the actual algorithm tries many, many small steps.)

    (Pretty close here!)

    As you can see, in the third picture above, the two curves came pretty close to overlapping.  That corresponds to the moment where the purple area - a function of the area between the curves - was minimized.  Once you figure out the offset between the curves for which this area is minimized, you can calculate the corresponding pitch with the following simple equations:
    So that's the general idea.  But how do we make it run on Arduino?  Put another way: what is it that's prohibitively expensive about YIN and prevents it from running on the ATmega168?  In practice, I found the two constraints were the time spent computing the value of the difference function (related to the "size" of the purple area above), and keeping a tight reign on the memory requirements of the sampling buffer.

    The first hurdle you run into is floating-point math support; simply, there is none on the ATmega168, so you have to give up on floating-point math in any performance-critical portions in the code.  I used fixed-point math where I required fractional quantities in the code.

    Next, as with most every decision when building gizmos, you have to start thinking about tradeoffs.  The difference function used by true YIN works by iterating over all samples in a given window, subtracting the value of the red curve from that of the blue curve and squaring that difference, summing all values thus obtained.  The first change I made was to give up on the squaring operation, which required a lot of expensive multiplications, and instead simply compute the absolute value of the difference at each sample:
    where tau represents the offset at which the difference function is being evaluated.

    In many ways, the revised version is related to the average magnitude difference function (AMDF) mentioned in the YIN paper, only without the averaging operation.  Losing the squaring operation may introduce inaccuracies in our detection, but it keeps the general shape of the resulting function looking pretty close to the original while giving us huge improvements in executing speed, so it works out.  (Critically, the minima and maxima of both equations occur for the same values of tau.)

    That really helped speed up execution, but it was still too slow and memory-intensive.  As it turns out, if you can reduce the size of your buffer, you also gain in computation time, because you have fewer samples to process - two birds with one stone!  So the next step was trying to figure out a buffer size for which the computation was fast enough, while maintaining the required pitch detection precision.

    By "maintaining the required pitch detection precision", I mean that as the wavelengths get shorter and shorter (i.e. as pitch gets higher), a period will take up fewer and fewer samples in our buffer.  See the following exerpt from a small helper worksheet I made:


    Frequency (Hz) Cpu Cycles Per Period seconds per period samples per period
    A 55 290909.09 0.01818 174.83
    A# 58.27 274581.62 0.01716 165.01
    B 61.74 259170.54 0.01620 155.75
    C 65.41 244624.41 0.01529 147.01
    C# 69.3 230894.7 0.01443 138.76
    D 73.42 217935.57 0.01362 130.97
    D# 77.78 205703.79 0.01286 123.62
    E 82.41 194158.52 0.01213 116.68
    F 87.31 183261.24 0.01145 110.13
    F# 92.5 172975.58 0.01081 103.95
    G 98 163267.21 0.01020 98.12
    G# 103.83 154103.72 0.00963 92.61
    A 110 145454.55 0.00909 87.41
    A# 116.54 137290.81 0.00858 82.51
    B 123.47 129585.27 0.00810 77.88
    C 130.81 122312.21 0.00764 73.5
    C# 138.59 115447.35 0.00722 69.38
    D 146.83 108967.79 0.00681 65.49
    D# 155.56 102851.9 0.00643 61.81
    E 164.81 97079.26 0.00607 58.34
    F 174.61 91630.62 0.00573 55.07
    F# 185 86487.79 0.00541 51.98
    G 196 81633.6 0.00510 49.06
    G# 207.65 77051.86 0.00482 46.31
    A 220 72727.27 0.00455 43.71
    A# 233.08 68645.4 0.00429 41.25
    B 246.94 64792.63 0.00405 38.94
    C 261.63 61156.1 0.00382 36.75
    C# 277.18 57723.67 0.00361 34.69
    D 293.66 54483.89 0.00341 32.74
    D# 311.13 51425.95 0.00321 30.91
    E 329.63 48539.63 0.00303 29.17
    F 349.23 45815.31 0.00286 27.53
    F# 369.99 43243.9 0.00270 25.99
    G 392 40816.8 0.00255 24.53
    G# 415.3 38525.93 0.00241 23.15
    A 440 36363.64 0.00227 21.85





    Prescaler 7


    ADC Divider 128


    Clk/sample 13


    F_CPU 16000000


    SampleRate 9615.38




    See at the end there, around the 440Hz mark, rounding up the 21.85 gives 22, and rounding down the 23.15 gives 23 - that means that using integer buffer offsets (integer values of tau) in the formula above, it would become difficult to differentiate consecutive semitones starting around that A.

    One way to increase the precision would be to reduce the prescaler value on the ADC and sample twice as often, which would mean reducing the speed of the code by half and growing the memory by a factor of two.  But what is the exact buffer size we're talking about here?  What would be the minimal buffer size required to work with the YIN and the frequencies we want to analyze?  Well, we need a minimum of two full periods being sampled, in order to be able to copy the first period over the second.  Looking at the above table, if we set our minimal frequency to 55 Hz, 175 samples are required to sample a full period, so the minimum size of the buffer would be 350 samples.

    The above table shows that these settings are sufficient for coarse precision, around 100 cent of a semitone at the highest frequencies.  This is what the code uses for perform pitch detection when in MIDI mode, because it is important to reduce latency (and thus minimize computation).  However, 100 cent is hardly sufficient to perform precise tuning of guitar strings, so we must dive further.

    I use another trick from the YIN paper: sub-sample interpolation.  The idea is simple.  In practice, the offset at which the difference function is minimized will never be an integer value; it will be a fractional value, meaning that you have to shift the red copy of the waveform over by a fractional number of samples for the two copies to line up perfectly.  Unfortunately, we only sampled the signal at specific intervals, so in order to be able to "resample" the signal at fractional offsets, we reconstruct an approximation of the original, true, analog signal by interpolating between the samples we took.  Where YIN uses quadratic (second-order) interpolation, however, I was limited to linear (first-order) interpolation by the processing power.  Nevertheless, this provides greatly-enhanced precision.

    In the end, to get everything integrated and running, I wrote a small set of classes that allowed me to profile single statements as run on the actual hardware.  I also used avr-objdump to disassemble the code produced by the compiler and understand where the inefficiencies were.  (There was no need to hand-assemble code; the compiler does a fine job as long as you use the correct datatypes.)  I fiddled around with the sampling precision (the ADC on an ATmega168 can sample up to 10 bits of precision), the minimal detection frequency, and the interpolation step size a great deal until I settled on 8-bit samples, a minimum frequency of 60 Hz, and a prescaler value of 7, for a final buffer size of 321 bytes.  The sub-sampling code uses fixed-point math with a 5-bit fraction, and it solves up to the least significant bit when finding the minimum in the difference function; thus, the tuner has a theoretical accuracy of 1/32th of a sample, or approximately 2.4 cents of a semitone at 440Hz (3.8 cents at 880Hz).

    (The code applies a small amount of low-pass filtering to the final computed pitch value in order to smooth out the display on the LCD.)

    The LCD Driver

    Rendering the thin vertical bars on the LCD is actually not very complex.  Most LCD drivers have a small area of memory called CGRAM, short for Character Generator Random Access Memory, which allows you to create a few custom characters for display.  Since the display matrix of the LCD I used was 5x7, I merely created five custom characters, each which a different column of dark pixels.  Turning on a given column of pixels then becomes the simple matter of writing the correct custom character to that location on the screen.

    Scary Pictures

    As mentioned above, about a year after hacking this thing together, I took it in to replace the battery.  By this point, my digital camera was repaired, so I snapped a few glorious pictures of the complete mess that it is.








    83 comments:

    1. cool project, im trying to do something similar using the Arduino uno which has the ATmega328...im trying to find the fundamental frequency of each invidivual string. Im new to programming and after reading the YIN method im a little confused with the math. Can you share the code you used to implement the YIN method? It would be a great help, thanks. -jay

      ReplyDelete
    2. Hey Jay,

      The code is actually linked in the post above, it's not very visible I guess, hehe:

      http://dl.dropbox.com/u/2330215/TunerCode.zip

      Enjoy!

      ReplyDelete
    3. great work. hey i am trying to build a guitar tuner using Arduino Uno with ATmega328. but my approach is different, i am using a zero crossing algorithm. do u have any idea on how to set the sampling frequency of the arduino uno ADC. thanx... shawn

      ReplyDelete
    4. Hi Shawn!

      I'm not sure if the Arduino library exposes a function to do so, but you can always go "straight to the metal" yourself, since the Arduino exposes the atmel system headers. You can find the documentation for the atmega328 chip here:

      http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf

      Setting up the conversion frequency is usually a matter of twiddling the ADPS0, ADPS1 and ADPS2 bits in the ADCSRA register. There is an example of this in the tuner code linked on this page, in Tuner::Start(). Note that for very high sampling frequencies, if you're doing a lot of work, you might have to do a bit of math to make sure your CPU can keep up with the ADC.

      Note that before settling on YIN, I tried various simpler approaches - zero crossing, peak counting, combining this with slope analysis, etc. and though they /sometimes/ worked for specific notes, they were still nowhere near as reliable as YIN, because of the harmonic content present in guitar strings. They would work for very simple waveforms with very attenuated or no harmonics, however.

      Best of luck, and have fun!

      ReplyDelete
    5. super,
      i build a guitar2midi converter using your yin implantation from here posted tuner code. i get ~42ms latency. this is ok enough for me.
      i try several other methods from zero detection to complete fft. but the yin method looks like the fastest and also most error resistant method.

      blah blah blah, i only want to say thx for this impressive suggestion, because i was nearly on the point to give up.

      ReplyDelete
    6. Awesome, glad you managed to make use of it! Thanks for the feedback!

      ReplyDelete
    7. hi,
      i post this yin based guitar2midi converter project on the arduino forum Exhibition/Gallery. here is the forums link. http://arduino.cc/forum/index.php/topic,82621.msg620845.html#msg620845

      hopefully this is ok for you, because 95% from my converter code are simply a copy from your code. i only remove all things needed from tuner and lcd display and change some things in the midi note on/off logic.

      ReplyDelete
    8. Great project! :D Thanks for letting me know! Glad you got some use out of it!

      ReplyDelete
      Replies
      1. hello!! please read the blog on the last part

        Delete
    9. This project description was really helpful, thanks! I'm working on a variation of this as a project, using separate string pickups to do polyphonic midi. Did a lot of poking online to find YIN, and then your implementation. I probably need more memory and cycles to do 4-6 strings in parallel, but I'm going to start with the pickup implementation and testing it w/your code. Thanks again!

      ReplyDelete
    10. Wow, that sounds awesome! Let me know what you come up with! Best of luck!

      ReplyDelete
    11. I've more or less created the identical amplifier with some minor tweaks, and am powering it, as well as a universal tuner pick up via vcc on the arduino, but I am trying to use the pulsein function for frequency measurement, not YIN. So long as I take a large enough sample, things thus far, seem to work okay. Fairly uninvasive code as it does not use atmega pin 5 timer counter or libraries. Is there anything fundamentally unsound to what I am doing? Why aren't more people using pulsein samples, and calculating frequency that way? I imagine outside the audible range this method lacks accuracy. But for an audio/instrumental application it seems fine, no?

      ReplyDelete
      Replies
      1. Hello gbcben! Hmm, I have not worked with the pulsein function, but from the documentation it seems it is a variation on the zero-crossings method. I've tried a few, and indeed it is possible to get somewhat workable results (as noted above), but the problem comes in when you have harmonics in your signal... because of the harmonics, the number and interval of zero crossings will vary depending on many factors like your amplifier bias and gain, and so on. With a steep low-pass filter you might be able to get rid of many of the offending higher-order harmonics and make your input signal into more of a sine wave, but that would restrain the range over which your tuner (or pitch detector) would work (unless you applied the low-pass in software and perhaps came up with some kind of adaptive strategy to raise the cutoff frequency of your filter until you let a minimum amount of energy through, perhaps). In any case, even with a few variations on that theme, I found I was still too far from the really accurate results I wanted; for my purposes, YIN was the perfect fit since it did not depend on any amplifier or filter parameters, or the waveform shape. (This last quality allows it to work with any instrument which has a pitch; I use my voice as an example in the video, but it could be anything else.) So... to answer your question, there is absolutely nothing unsound about what you are doing, and if it suits your purposes and needs, then by all means, proceed! I simply found that YIN provided that extra step in accuracy and robustness that I required.

        Delete
      2. Thanks Raptor, the harmonics became a problem. I am getting the higher frequency harmonics on problem pitches. Which is unfortunate, because the rest of the time I get frequency accurate to small fractions of a hertz. I'll be tweaking my circuit, and utilizing a YIN approach.

        If someone were looking for a simple tuner, where you simply get frequencies, and had a preset that you were tuning for, then did some division for scaling, then you could do a very very light weight and accurate tuner with my approach. But if you need the actual frequency of the instrument(not a scale of it) then YIN seems to be the best approach.

        For the sake of anyone doing similar things, I'll update this comment thread after I make adjustments.

        Delete
      3. Many thanks for sharing your experience! I'm sorry to hear it didn't work out in the end, but at least it sounds as though you identified a subset of input cases where the approach makes sense..!

        Delete
    12. i want to do ths project almost exactly except i would like the tuner to control lights not an lcd screen can you help me with the programming ? which part to to take out ? this should actually be much simpler coding i assume im just not sure what is the lcd screen and what is need for the rest of the code operation

      ReplyDelete
      Replies
      1. Thanks for your interest! Actually, do you mean something like this?

        http://www.youtube.com/watch?v=q_wdKhV-b6w&feature=plcp&context=C4cfe68bVDvjVQa1PpcFOO4oxlKhYhAqDW1HXTjQYUBzhONq__Klw%3D

        I've been looking for an excuse to write up that project as well, let me know if that interests you!

        As far as the code goes, for the tuner, you basically only need the Tuner class and the few functions it relies on. However, you can strip the m_lcd member and any code that uses it, along with anything relating to glyphs or the LiquidCrystal class. I think you should be able to set ENABLE_LCD to 0 and have it work, but I have not tested that recently - there may be a few minor compilation nags to fix.

        That's a fairly summary description of what should be needed; let me know if you need more detail!

        Delete
    13. how can i make a simple arduino program that recognizes a frequency and does something. say for each note a different led turns on......
      please help as in your code there's probably something similar.
      thank you in advance !

      ReplyDelete
      Replies
      1. Indeed! You should be able to use the code pretty much as is. I would use TunerMode::Midi (by setting Tuner.m_mode to that value), which will reduce latency if you only need note detection. I also suggest you have a quick look at RenderWideGlyphForNote(), which does something like the following:

        void RenderWideGlyphForNote(int note)
        {
        if (note >= 0)
        {
        int nameIndex = GetNoteNameIndex(note);
        ...

        You could easily use nameIndex to light up a specific LED instead. nameIndex will correspond to one of 12 notes in the scale: A, A#, B, C, etc.

        Delete
    14. This is very cool,

      I'm just curious, what resistance values did you use for R5 and R6? And pin 7 is just left unconnected right?

      ReplyDelete
      Replies
      1. Hey Tyler,

        Thanks for the feedback! I *believe* the values indicated on the schematic are valid - 910 ohms for R5, and 10 ohms for R6 - but since I no longer have the physical gizmo by me, I can't be 100% sure. I readily admit that with the rest of the circuitry in place (op-amp/output stage), the complexity of circuit analysis required to verify the cutoff frequency of the resulting filter vastly exceeds my current back-of-the-envelope calculation abilities, especially since R6 is not placed in a "classical" LPF setup. And yes I believe I left pin 7 floating in my design, though looking at the application notes again I see that I could have used it with a bypass capacitor (i.e. just connect the pin to ground through a capacitor) to isolate the amp from power supply noise. Ah, hindsight :D

        Delete
      2. Ok awesome! Thanks a lot. A friend and I are trying to build this (a simpler version I must say) and this page has been beyond helpful, you did a great job. One other thing that we are struggling with though, is the code. We've been trying to use a modified version of (https://github.com/JorenSix/Pidato) that code but It doesn't seem to read properly. We have tried to go through yours and just filter out the MIDI and LCD parts but neither of us know a whole lot about programming the arudino and yours was rather involved :P

        Delete
      3. Thanks once more for the kind words! As far as filtering out the MIDI and LCD code... as noted (and buried) somewhere in the comments above, I think you should be able to get rid of most of the LCD stuff by #defining ENABLE_LCD to 0 instead of 1, but I admit I haven't tested that compilation path lately. And as for MIDI, you can just comment out everything having to do with Serial - the serial port is only used for MIDI output. That will actually free up the port for you to print debug output to, say, a PC, if you want to modify and debug the code anyhow. Hope this helps a bit..!

        Delete
    15. Hi, Would love to try this out but the dropbox link above is coming up as a 404. Is it still hosted there or has it been moved? Thanks

      ReplyDelete
      Replies
      1. Darn, that's bizarre? I just tried the public link (http://dl.dropbox.com/u/2330215/blog/TunerCode.zip) from two distinct IPs, and everything seems to work fine. You might be able to get it to work over https as well: https://dl.dropbox.com/u/2330215/blog/TunerCode.zip I know this has worked in the past, but there may be some kind of new technical hurdle. If it still doesn't work, let me know and I'll try to find an alternative.

        Thanks for your interest!

        Delete
      2. Awesome - the public link did the job.

        Thanks !

        Delete
    16. Hello Please I need your help im trying to only read the musical tones and active a relay for each individual note.

      my email is cgdemoya@gmail.com

      ReplyDelete
    17. I need to receive thought an audio jack any sound and activate a relay for each note. I prefer to activate it only for 8 basic notes

      ReplyDelete
    18. i do not need LCD or anything like that to visualize. The only thing I have to do is if Im hearing the soud " for elise" in its first 20 seconds ( could be done from an audio jack or small microphone) I need to activate 1 relay for each note heared and only have it activated during the current note .If im hearing LA.. I have to activate the LA relay and have it activated until I hear another note.. If im hearing more than one note for example LA and SOL..then relays for LA and SOL must be activated please help me out Thanks

      ReplyDelete
      Replies
      1. Hello Constantino! Thanks for your interest. This project _might_ be a good starting point for you, but I'm not sure, because it will fall short on two aspects:
        -it is probably not quite fast enough to pick out individual notes in a song like "Für Elise", at least if the song is played at regular speed
        -due to the pitch detection approach used, it will not detect multiple notes at the same time. It will only report the lowest note played, and even then it will probably have trouble doing so when the input signal is polyphonic.

        If these limitations are not acceptable for you, you might have better luck with an approach based on the Fast Fourier Transform (FFT), which you can research by googling "arduino FFT". The FFT will allow you to analyze the signal on a frequency spectrum basis, and you can attempt to pick out any peaks to detect played notes.

        If you still wish to being from this project, I would start by downloading the code linked on this page, setting ENABLE_LCD to 0 and using the MIDI mode code as a starting point. Best of luck!

        Delete
    19. Hi,
      I have found an implementation on YIN witten for the arduino, it's available on github (https://github.com/JorenSix/Pidato/tree/master/libraries/Yin), which I thought may be of use to some people. I was wondering if you (or anyone else) knew of a way to calculate the speed this code may run on the Arduino without actually running it on an arduino. The reason I ask is because I am designing a guitar tuner for a project, but do not have time to build and test it

      ReplyDelete
      Replies
      1. Hi Andy,

        Oof... that is a difficult thing to estimate. From a cursory glance at the code, one can see it uses floating-point math; since the Arduino does not have a floating-point unit, it is safe to assume that the algorithm as written on github would run prohibitively slowly on Arduino. Moreover, depending on the actual Atmega chip you plan on using, the memory requirements can quickly get out of hand, even for a single-precision sample buffer.

        On Arduino/ATMega*8, moving to fixed-point math improves performance by orders of magnitude and cuts down on memory requirements. However, in order to reduce the YIN latency while keeping memory use under control, I had to spend some time looking at the assembler output from the compiler, tweaking and profiling individual statements of code to get them running just barely fast enough for things to be practical. (I'm sure some headroom remains, if anybody feels inclined to carry on the effort. :-) )

        If you are locked into using an Arduino for your project, then my humble suggestion would be to either use the code I provide on this page as a starting point or go down the FFT route. However, if this is something "on paper", perhaps nothing prevents you from changing your design to be centered around a DSP chip with a floating-point unit, an FPGA, or even simply just a much faster general-purpose microcontroller. That would get the number-crunching limitations out of the way and perhaps allow you to concentrate on the tuner-specific parts of the project.

        Best of luck, and thanks for taking the time to comment!

        Delete
    20. Hi, congratulations about this!
      I'm learning so much reading this topic. Can you please check the code link?? i'm trying to download it to get more perspective to the project, understanding it from inside.
      I'm already on a project based on yours and many others from the internet. Right now i'm stuck in the very begining, i don't know if my preamp is properly working.
      I made some little programs trying to test it but were helpless. Do you know any solution to test the preamp without building all the rest of the electronics?

      ReplyDelete
      Replies
      1. Hey there Zangatre!

        Thanks! Hmm, I just checked the link and it seems to work for me. The original link is on dropbox though, that seems to be blocked in a few locations... I added another to Google Drive, let me know if that works.

        The best solution to test the preamp would probably be to borrow an oscilloscope just to see what the output looks like, but if you don't have access to a 'scope perhaps the next best thing would be to temporarily connect a speaker or some cheap headphones and use your ears to determine whether the sound seems "OK". That's what I did, since I didn't have an oscilloscope at the time I built this. Ideally, you'd add a small coupling capacitor before the speaker to make sure you don't send a DC signal through the coils, but if you're just connecting a speaker for a short amount of time and the coils don't have time to heat up to much you might be OK. Your mileage may vary. :) An old PC speaker would do just fine!

        Best of luck!

        Delete
      2. Hi! thanks for the answer!

        The oscilloscope is stucked in my wishlist im afraid hehe.
        We (my brother and me) have builded the power stage and the preamp electronics but we cannot make it works fine.
        On a simple arduino code we got the analog read at the serial monitor, but it is always 1023 (máx read, it doesn't matter about the guitar (lol). This is the beggining of the project and it is the third preamp we've build so... I'm starting to think that we are doing something else wrong.
        It there a chance that you still have the eagle files of the schematics??

        By the way... I finally downloaded the code by the https dropbox link.
        Thanks a lot for the support!

        Delete
      3. Hello again!

        I'm glad you managed to get your hands on the code! :)

        As for your circuit, wow you certainly have a lot of patience if you've built three preamps! I'm sure that will come in handy when trying to diagnose the issue. A couple of things come to mind that you could try without a 'scope. Hopefully one of these will set you on the right track:

        -with no input signal, have you tried measuring the DC bias of the input to your Arduino? The resistor network (R7 and R8 in the diagrams above) should give a bias of around 2.5V for a supply voltage of 5V. That's kind of "neutral ground" if you will; for zero input signal, the arduino should read 512 (for a 10-bit reading, which is what you seem to imply from the 1023 in your above post).

        -the DC bias on the output pin of the LM386 should also be one-half your supply voltage, so 4.5V if you're using a 9V supply. That means pin 5 of the LM386 should read 4.5V without any input connected. In case you're trying to follow the above diagram, it's important to note that what's on the left of the OUTPUTCOUPLING capacitor in the above schematics uses VCC=9V as a supply voltage, whereas the resistor network on the right of the OUTPUTCOUPLING capacitor uses 5V from the Arduino's voltage regulator as supply.

        -have you verified that you are able to read other analog sources from the Arduino pin you are trying to use? Perhaps verify that you are able to read values other than 1023 in software by using a simple potentiometer to vary the voltage supplied to the ADC? Alternatively, perhaps trying using a different ADC pin? It is common for single pins to get "fried" while the rest of the microcontroller works fine.

        -if your multimeter has an AC mode, you might have some success trying to measure AC at various points in the circuit. Make sure the input signal (probably millivolts) is making it to the amplifier, then through the amplifier, then to the Arduino input. And use the DC mode to check that the signal remains centered about the expected value.

        I still do have the EAGLE schematics, but they are of no real functional use... I only used them to create the drawings above, I did not create them with sufficient rigor to allow them to be used in simulation, layout or any such. If you're still interested, let me know.

        That's sort of what comes to mind for now! If that doesn't help, let me know and I'm sure we can come up with more paths to explore :)

        Best of luck! Keep me posted!

        Delete
      4. Hi

        Should the VCC at the MIDI port be 5V also, or is it the 9V VCC used elsewhere?

        Thanks

        Delete
      5. Oooohoho... good find... It should be 5V. If you use 9V, your mileage may vary. In practice there is an optoisolator at the MIDI In end; essentially the TX line (pin 5) on the MIDI output port will be shorted to pin 4 when it's active, so depending on the voltage drop from the diodes within the optoisolator and the sink capacity of whatever you have connected to the TX line, you may or may not burn something out at 9V. So if you want to stay safe... stay with 5V :-) Thanks for pointing that out!

        Delete
    21. Similarly VCC should also be 5V for the switches on D9, D10 & D11.

      Is there a reason not to use the internal pull-up resistors on the Arduino or ATmega for those push button pull-ups?

      I'm pretty close to getting to the board stage on this. Has anyone got any feedback on the value used for the input coupling cap on the amp? I was tempted to use 0.1uF as I have before with this component, but not sure the implications that might cause.

      Thanks, Geoff

      ReplyDelete
    22. Hey there Geoff,

      Wow, after all this time, I finally get around to taking a look at this. Sorry for the delay, and a huge thanks for your questions and comments!

      Indeed, the VCC 9V vs. 5V was quite unclear on these diagrams! I've updated the diagrams with what I hope clearer labelling.

      There is no reason not to use the internal pull-up resistors - in fact, believe it or not, I was unaware of them when I put this project together. I've added a note to this effect - thanks again!

      As far as the input coupling cap goes, unfortunately, I have no news to share. :(

      Thanks for your interest!

      ReplyDelete
    23. Thanks for the clarification. I've been off on other projects - there's always plenty of those on the go. I've just updated the schematic so next trick is the board layout. After all this I'll have to find out where to send you one when they're done.

      Cheers!
      Geoff

      ReplyDelete
      Replies
      1. I totally understand, I always have many things in flight as well. :)

        Haha I'm quite curious to see what you'll come up with! Keep me posted, and have fun hacking! :P

        Delete
    24. Hi Raptor!

      Thank's for a graet project but i have a problem with compiling a program.
      The arduino ide returns me a message: :conflicting declaration 'const uint8_t PROGMEM port_to_input_PGM[]'

      I read the cod over and over and now i'm no idea to fix this problem.

      please help

      regards

      Adam

      ReplyDelete
      Replies
      1. Hi Adam

        I found the same, and that's just one of many errors it throws. I found it doesn't compile since Arduino IDE 1.0 however it's quite happy with that line with Arduino IDE 0.22 and compiles without warning with that version. My fallback plan was to use the older IDE if I couldn't find a fix when the time came to hit the code.

        Geoff

        Delete
      2. Hey guys, I FINALLY got around to altering the code so it builds against Arduino 1.01. It's untested, but the required modifications are relatively minor so I'm confident it should work. YMMV. Links available inline above (look for "Dropbox"). Sorry for the delay!

        Delete
      3. (It also builds against 1.05, which is the latest available release version at the time of this writing.)

        Delete
    25. Thank You Geoff.

      I make a prototype of the tuner without amplification stage.
      I use a 4x20 character display.
      I succesfuly compiled the software with Arduino IDE 0.23
      but the display after "Hello world" displaing : "A=00.0 Hz"
      and i can't change this value by the buttons.

      The buttons are connect property and work OK except changing value of the frequency.

      please help :)

      Adam

      ReplyDelete
      Replies
      1. Hi Adam

        Unfortunately with what you've described it's not possible to debug your work. Hopefully your issue will become obvious for you soon.

        Geoff

        Delete
      2. Hey guys!

        I'm sorry for not answering sooner - I've been terribly busy - but rest assured, your replies are safe in my inbox and not going to fall through the cracks.

        I would maybe try getting rid of the calls to LoadTuning() and SaveTuning() to rule out any EEPROM-based issues, and would try to add some debug statements (prints to LCD or serial output, if you're not using the MIDI port) to make sure the code manages to leave the TunePitch() routine eventually.

        Hopefully I will have time soon to revisit the sketch and update it to build against the latest IDE. I do not have hardware to test it, but at least I can try to make sure it builds. Will keep you posted!

        And Geoff - many thanks for helping out!

        Delete
    26. Hi again,

      Is the potentiometer on the rear of the case VADJ1?

      Thanks
      Geoff

      ReplyDelete
      Replies
      1. Great question! ...no, it's inside the case, like the battery. :$ I adjusted it once so that the contrast was good when standing above the pedal, and that was that. It's somewhat easily accessible in the tangle, though. You can see it in this picture:

        http://2.bp.blogspot.com/_fR69kRaEnLE/TOH7Fro4FcI/AAAAAAAAA0U/ozpxCK4c7sM/s1600/IMG_3945_cropped.jpg

        It's towards the bottom right of the picture, a yellow-brownish multi-turn pot - you see its tip and the adjustment screw. Much easier to get to than the battery, haha. :P

        Delete
      2. Glad I asked! I got that wrong. If VADJ1 is a trimmer, which component is the potentiometer on the case?

        Delete
      3. Wow sorry!! Brain fart there: RCONTRAST is the trimpot inside the case - VADJ1 is the normal pot on the top of the case! Yeah obviously VADJ1 needs to be accessible, to tweak amplification depending on the sensitivity of your input device.

        Sorry, starting my day here, need to shake the sleep out of my brain :P

        Delete
      4. All good. Originally I had them both as trimpots, then I put VADJ1 as a connector on the board so I could put it externally...but thought I'd best check.

        Thanks! Geoff

        Delete
      5. Hi again Frederic,

        Short update: after testing different bits of the design on a breadboard I have now ordered the PCB be made. It's an 8x10cm board which includes space for a 9V battery holder. If it all turns out okay I'll send you one made up. Thanks again for the detailed instructions, and inspiration! Geoff

        Delete
      6. Wow Geoff, that's incredible!! The fact that you've taken your project so far is very heart-warming and inspiring to me. I really hope it will turn out great! You have to send me pictures and stuff :D

        Your offer of sending a board over is both extremely kind and generous! I would of course be honoured to receive one, though it should be said that I don't expect any compensation from this blog and that the interaction with which you have provided me these past few months is already a lot of fuel for me to go on. :D Just want to make sure you don't feel obligated.

        All the best, and please do keep me posted! :-)

        Delete
      7. Hi Frederic, The boards look good (if I say so myself) but need a rework as I introduced an error. Can you drop me a note to geoffs_aus at hotmail dot com? Will send you a progress photo.

        Delete
    27. Playing guitar can be one of the most interesting hobbies to do. It brings out what the person wanted to express from inside. There are only few people who might be willing to learn about playing a guitar and tuning it to make a good blend of music. If you are much interested in learning to tune a guitar, the best thing to do is look for a reliable site or sources that will help you learn about the step by step process. For more about online guitar tuner

      ReplyDelete
    28. Hello Sir. We have proposed a project same as to this one. Would you mind if I can have a copy of the whole schematic diagram of this tuner? Also the materials you used. Please help us out. Thank you, sir.

      ReplyDelete
      Replies
      1. Hi there Bryan! I'm afraid the only schematics I have are the ones already present on this page. I believe the only things not on this page are the LEDs I used to monitor the signal levels, which are trivially connected in series with a small resistor to the microcontroller of your choice. (You may not even need the resistors, depending on the microcontroller you are using.)

        However, a reader by the name of Geoff Steele is building a version of this circuit which combines the Arduino with the tuner directly onto a PCB. I surmise he might post here when done if he chooses to make his designs available.

        As for the materials I used, aside from the component values marked on the diagrams, I'm sorry to report that aside from the Arduino RBBB from Modern Device, most everything that made it into the final build was from a local surplus store and thus from mostly unidentifiable sources. However, you should be able to substitute any equivalent generic parts for the LCD, switches, LEDs, capacitors, diodes, etc. You should be able to procure these from most electronics hobby shops or online sources (of which my favourite are digikey, mouser, and ebay). As long as you stick to somewhat similar component values, I think you should be able to make the tuner work; it's not a particularly sensitive circuit.

        I wish I had the time to take things further and truly make something like an Instructable out of this project, with step-by-step instructions, but as it stands, cloning this project does require a bit of DIY knowledge, patience, and a will to tinker. A handful of people have reproduced the circuit and used the software with success, however, so don't let that discourage you. If you do attempt to build something equivalent and run into any issues, I would be glad to assist to the best of my ability.

        Best of luck!

        Delete
      2. Thank you, sir. We hope we can do it rightly. Thanks for the support as well. :)

        Delete
    29. Follow-up question sir. Did you consider the wattage and the voltage ratings of the resistors and capacitors?

      ReplyDelete
      Replies
      1. I did! However, since nothing in this circuit is neither high voltage or high current, it's usually quite easy to find parts which will do the job. I believe the resistors I used were 1/4W and the capacitors were all >16V, possibly >25V in some cases, which is more than enough derating to accomodate the 9V power source.

        From a cursory glance at the circuit, I think the only components you really need to worry about are the ones around the power supply and the backlight resistor for the LCD. For the former, the datasheet makes the required component ratings clear; these will vary depending on the exact flavour of power regulation circuitry you use (adjustable, fixed, etc.). For the backlight resistor, it is probably safest to measure the backlight current draw (or its documented value, if you are lucky enough to have it) and compute the required resistor wattage rating. In my case, if memory serves, I believe the draw was a bit less than 100mA; with a 10 ohm resistor, this amounts to 0.1W, which is once again well within operating range of a 1/4W resistor.

        Delete
    30. Oryt! That helped! Thanks for the response sir! I still have a question. What is the circuit in your project that can analyze the frequency coming from the guitar? Also, about MIDI, I hardly understand that. What does MIDI do? and does it have a circuit, sir? Thanks again.

      ReplyDelete
      Replies
      1. Hello again Bryan!

        As it turns out, the frequency detection is done in software, on the Arduino. The Arduino has a microcontroller that is able to read the input signal from the guitar and determine what its fundamental frequency is. However, because the raw signal from a guitar is too weak for the Arduino to read it with sufficient precision, we must amplify the signal before feeding it to the microcontroller; that is the role of the "amplification stage" outlined earlier on this page. The software that performs the signal analysis is available from the section entitled "The Tuner" on this page. (There are two download links, but the content is the same.)

        As for MIDI, it's simply an acronym that stands for Musical Instrument Digital Interface (http://en.wikipedia.org/wiki/MIDI). It is a set of protocols and physical interface standards that allows digital instruments to communicate together. In this case, when the tuner detects the fundamental frequency of the analog input signal, it is able to output a digital MIDI signal on its MIDI port saying "play this note". If a digital instrument (for example, a digital piano) is connected as a slave to the tuner and configured to listen for these signals, it will play the same note that was detected from the tuner input. You can actually see this in action in the video embedded on this page around 2:25 (http://www.youtube.com/watch?v=oGKE1vmAWCA&feature=player_detailpage#t=145).

        MIDI itself is quite a simple protocol and does not require much to implement. The MIDI Output side of things is a bit simpler than the MIDI Input, so it's easier to drive instruments than it is to act as a slave. (For example, my synaethesia lamp reads MIDI as input to drive the colors http://deambulatorymatrix.blogspot.ca/2012/09/syneasthesia-lamp-2007.html; you might need a similar setup if you wanted to build, say, a synthesizer.) The tuner's MIDI output port is documented on this page under the section named "The MIDI Stage" and does not require much of anything to implement other than power, a resistor, and the actual physical MIDI connector.

        Delete
    31. Really great efforts dear. I love you work and post. I have something for you. it's guitar tuner apps. I know love to install it.

      ReplyDelete
      Replies
      1. Hehehe, thanks for the link :) Yeah, the motivation for this project was definitely the challenge. Back in 2008 though around where I live smartphones were not yet ubiquitous, so there was less "competition" on that front. :D Sometimes though nothing beats stomping on a box of hardware and knowing nothing but your guitar's signal will make it through to the tuner in a noisy environment.

        All the best, and thanks for the interest!

        Delete
    32. Hello mister Raptor,
      my name is Robin and I am in the last year at school. I'm doing Electricity/Electronics and because it is the last year, I have to do a project. What I want to do is to make a MIDI-guitar. The first thing I have to do is to 'connect' the strings to an Arduino and then I have to use the Arduino to change these readings into MIDI. When I have the MIDI, I'm going to use something like Abelton Live to put some effects on it. Can you help me with the code and a way to connect my strings?
      Really thank you in advance!
      And before I forget; your project is really amazing! I wish I was such a great inventor myself!
      Greatings
      Robin Cherlet

      ReplyDelete
      Replies
      1. Bonjour Robin! (Pardon me for presuming, but I'm from Québec and Robin Cherlet sounds like a French name... am I mistaken?)

        First off, many thanks for your kind words! It it truly heartwarming to see that people seem to find this project inspiring. :-)

        Now for your own idea: that sounds like quite a fun project!! It reminds me of the Ghost pickup system with a Hexpander module, by graphtech:

        http://www.graphtech.com/products/brands/ghost

        My first thoughts are the following:
        -as you seem to imply, in order to get reliable MIDI output, you'll probably have to find some way to connect the strings individually to a sound analysis module. Performing fundamental frequency analysis is a different (and much simpler) beast from detecting multiple notes in a single unified waveform, a bit like Melodyne does (http://www.celemony.com/cms/). Even as an impressive professional product, Melodyne is not a perfect system either.
        -as you can probably tell from the video embedded on this page, even with a single string playing at once, the Arduino has trouble keeping up when in MIDI mode; there is significant latency between the moment the string is played and the moment the MIDI signal is output. My code is certainly not optimal, but in order to perform analysis on six independent channels and reduce latency by say 10x, you'd need a 60x performance improvement (leaving aside any hardware sampling rate limitations). I think it's pretty safe to assume this would be outside the realm of possibility for an Arduino. Which kind of segues into my next thought...
        -since you would need more processing power than an Arduino anyway in order to complete this project, you have a nice opportunity to take things in steps. I think the first thing I would probably try to do is see if it's possible to use an Arduino as an external sampler - it has six analog inputs, that's a great fit for six guitar strings - and feed the sampled data to a PC through a high-bandwidth serial port connection, where you could do much more crunching without worrying too much about software performance. A quick back-of-the-envelope connection tells me that 115200 baud, that leaves about 19200 baud per channel, which is 2400 bytes per second (without counting overhead etc.). Per Nyquist's theorem, this would let you detect signals up to 1200 Hz. If this is not enough, you can see about either increasing the baud rate (might be difficult depending on your PC) or sending a signal with lower bit depth, which might still be sufficient to perform pitch detection.
        -when you get a prototype working on a system with "unlimited" processing power, you could start thinking about moving to an embedded platform. In your case, since the six channels are completely independent, this is a great fit for a multicore platform like the Parallax Propeller. These are a bit more expensive and more complex to program, but there are Arduino-compatible form factors available if that's important to you (http://www.parallax.com/product/32214). In the case of the board above, they included a MCP3208 as an ADC, which seems to support eight input channels (http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010534). I've never used a Propeller before, so I'm not sure the device linked above would fit your needs - a bit more research is probably warranted. :P

        (more coming in another reply - hitting the character limit!)

        Delete
      2. -isolating the inputs from the six strings is probably the most difficult part, hardware-wise. My initial thoughts were along the lines of "separating" the individual coils of a normal pickup to provide twelve output leads, with one coil per string, but my hunch is that there would be too much crosstalk from one string to the next coil over to make this approach viable. I suspect this is probably part of the reason why the Ghost system uses individual piezos, to truly isolate each string's vibration. Funnily enough, I myself am looking to build something like a MIDI fretless/soundboardless zither/harp at some point in the future, and I've been doing a bit of research lately into using either cheap off-the-shelf piezos and adapting them to shrink their size and pick up strings individually, or even growing piezo crystals at home (https://www.google.ca/search?q=rochelle+salt&oq=rochelle+salt&aqs=chrome..69i57j0l5.2243j0j7&sourceid=chrome&espv=210&es_sm=122&ie=UTF-8#es_sm=122&espv=210&q=how+to+make+rochelle+salt). However, I've yet to try building anything physical, so I'm unable to offer any deeper specific advice on this front. Again, my hunch is that buying a handful of cheap piezo transducers and figuring out a way to hack these into the guitar is probably going to yield a more even sound than home-grown crystals. Alternatively, if cost is not a prime factor to you, you might be able to buy just the pickup part of a Ghost (http://www.graphtech.com/products/brands/ghost/ghost-pickups---guitar) or adapt some Kortier harp pickups (http://www.kortier.com/pickups.htm, somewhat pricey if you buy a kid and only want to use six, but you might be able to write them and see if you could only buy six - assuming you can find a way to jury-rig these into a guitar).

        As for the software... well, I think if you can get the hardware working well, I think you will have a much easier time with the software. :) The downloadable code on this page will provide all the required building blocks, and though you'll have to adapt them to work on six strings in parallel and to run on another platform, I believe the core frequency-detection algorithm should work pretty much the same unchanged (aside from maybe increased precision from running on a platform with more horsepower, etc). Depending on the scope of your project, you might also want to enrich the MIDI control signal to provide modulation when you bend your strings and so on. That would be a bit more work, but it's mostly about changing the code that converts the detected frequency to MIDI.

        To be sure, the project you are attempting to complete is quite complex, but I think you will have a lot of fun trying to get it all working! Je vous souhaite la meilleure des chances, and please keep me posted! I'll be glad to assist wherever I can.

        All the best!

        Delete
      3. Bonjour Raptor! First of all, I'm so sorry that I answer so late on what you've said! I've had forgotten that I posted something on your page. But, though, thank you very much for answering and giving me so much information! I will analyse this the coming days and I'll see what I can do with it, I think some things can be very useful for me! What I already have is not that much... I bought a hexadecimal pick-up and built it in in my project guitar. So I can already separate the strings from each other. The next thing that I'm going to do is to look if a can create something that can say like 'oh, that frequency belongs to that bandwith so that means I have to play this note'. The thing is, that won't be easy! What I also have to do is to change the signals the pick-up gives because they are to weak, I still have to measure it, but I already did some research about it. Something else is that the level of the voltage also lays under 0V, and my Arduino can only ready signals from 0V to 5V. I think it's cald 'superponating' (I don't know if it is a right translation... ) to change the voltage of the signals. So I will be very happy if I can make something to let that work!

        And what I may not forget to say, really, really thank you for the support! It helps a lot!! I will now post more frequent on your blog, I won't wait 3 months again! Really thank you for everything!

        Greetings, Robin

        P.S. I actually live in the dutch part of Belgium, so I actually do not speak french. But I can if you want to ;)

        Delete
      4. Hi there Robin!

        Oh wow, if you already have the pickup, then I think that's the hardest part taken care of! In fact, the material on this page should hopefully help you with your two other problems:

        -the software for the tuner (available from a few links near the top of the page) should definitely take a signal, find its fundamental frequency, and translate that to a MIDI signal for you. As mentioned earlier, there is latency, but still. If you want to do this on PC, the theory is the exact same, though you'll probably achieve much greater responsiveness (i.e. smaller latencies).

        -as far as amplifying the input signal from each string, you can use something very similar (if not identical) to the input stage on this page. An LM386 will take a zero-centered signal like the input from a guitar along with a single voltage rail (i.e. +9V in this tuner's case) and output it centered around half the supply voltage, so in the case of this tuner the amplified signal is centered around 4.5V. (Note that I just realized my notes above are incorrect on this topic - I will fix that shortly, sorry!) That means if you were to supply 5V (instead of 9V) to the LM386, you could get it to output the amplified signal right between 0V and 5V with no additional circuitry. (In my case, as described above, I used a small decoupling/bias/clamping circuit to recenter the output around 2.5V, but you may not need that.)

        It's a pleasure to have a dialog with readers - I always to try to answer everybody since it's motivating for me as well and encourages me to keep posting. Let me know how things work out for you, and best of luck! :D

        (Ah, Belgium! Beautiful country. I lived in Europe as a kid, though my memories of our main trip through Belgium are all of rainy days for some reason. :( )

        Delete
    33. Hi, I love your project and I plan to use your design to make my own tuner for my advanced physics investigation. The plan is to make the tuner and use it to identify notes played on a violin, and use this to find the frequency of each note using a data table - unless you know a way to make the tuner instead show the frequency of the note played?? I will then plot graphs of frequency against length, mass (of string) and tension. Could you help me make a list of all the parts I need to get?

      Thanks,

      Kat x

      ReplyDelete
      Replies
      1. Hey Kat-y!! Thanks for your feedback! I must first apologize for the *massive* delay between your comment and my answer. I normally try to be a bit more snappy, but lots has been going on in my professional life lately and I've had little time for the blogging.

        So: the good news is that the tuner does actually compute the frequency internally! This is done inside the function DetermineSignalPitch() (which in retrospect is a somewhat poor name). The value returned by that function is the frequency in Hertz. By default, the tuner goes on to translate the frequency into a MIDI note value, etc. for display and MIDI output, but you can just get rid of all the code after the call to DetermineSignalPitch() and simply make it display the frequency on the LCD readout, write back the value to a PC over a serial port, etc. The best value for you to use as a "source frequency" is probably the float variable "filteredFrequency" used in Tuner::Go(). It will smooth out the value produced by YIN a bit, which should give you something a bit more usable than the raw, instantaneously-detected frequency.

        As for a parts list: do you mean a full bill of materials to assemble a tuner as documented above? Unfortunately, I don't really have such a list readily available, though another reader, Geoff, may eventually provide something along those lines here once he finishes building his own version of the circuit. I definitely won't hold him to it though, since he's been helping out and contributing to this page out of his own time and interest.

        That being said - if your goal is specifically to determine the frequency of a violin's string and you do not care about the specific means about which this end is accomplished, you would probably have a much easier time using a computer, a microphone, and some simple pitch analysis software. For example, Audacity (http://audacity.sourceforge.net/) has a built-in spectrum analyzer (Analyze/Plot Spectrum...) and the pitch detection plugin found here (http://forum.audacityteam.org/viewtopic.php?f=42&t=71854) seems to work pretty well. You would probably obtain much more precise and reliable results with this method than by using a tuner like the one presented on this page.

        I hope this helps somewhat, even though I was unable to help out with your second question. I wish you best of luck and hope your project works out. Keep us posted!

        Delete
    34. So i am working on a resonant frequency project for a technology fair. My objective is to record the sound of the ring a wine glass makes and to play the peak frequency back through a speaker. Would you have any insight on how i could detect the peak frequency and save it as a variable.

      ReplyDelete
      Replies
      1. Hi there!

        Hmmm, great question! The first thing I would do would be to look at the spectral distribution of a wine glass ring and see if the peak frequency is in fact the fundamental frequency of the signal. YIN (and thus, this tuner) detects the fundamental frequency of the signal, not the strongest harmonic, so depending on the spectral power distribution you may or may not be able to use this project as a basis.

        Because of this, there are two paths you could choose to accomplish your task. If you are truly after the peak frequency of the signal and want the most generic approach, I would probably try to use a spectral transform like an FFT and search for the highest peak. There seem to be a few arduino FFT libs floating around; here is a popular article on the topic (http://www.arduinoos.com/2010/10/fast-fourier-transform-fft/). Alternatively, if your goal is not specifically to do all this in hardware and you are at liberty to use a PC, then there are quite a few software options at your disposal. You could implement a small program to perform the FFT yourself and output a tone using the PC's sound card, or you could use software like PureData or Max/MSP to do the same. (I'm not familiar with these pieces of software; though I'm given to understand that this is but one of the types of tasks they are meant to accomplish, I could be wrong.)

        Keep in mind that depending on the precision and responsiveness you require, it may be difficult to perform an FFT of sufficiently high resolution to hone in on the peak frequency on Arduino. On PC, you would be less constrained in terms of computing power.

        If you are happy simply detecting and playing back the fundamental frequency of the signal, then you could definitely start with this tuner project as a basis and simply replace the LCD output code with a small routine to drive a speaker (or an audio output jack) at the appropriate frequency on the Arduino. Otherwise, if you're on a PC, you have a few options. You could use the original YIN paper to implement the algorithm and output a tone using the PC's sound card, or you could again try to use a system like Pure Data or Max/MSP to accomplish your task.

        If you are constrained to Arduino and truly want the peak frequency yet are unable to obtain sufficiently high FFT resolution, then you could perhaps try to combine both approaches in a type of successive refinement: perform a coarse frequency-domain search using an FFT first to determine which "frequency bucket" contains the peak frequency, then use the bounds of the bucket to constrain YIN's time-domain autocorrelation offset search range. YIN should then find a local minimum of the autocorrelation function that corresponds more precisely to the peak frequency. Obviously, the initial frequency-domain search still has to be precise enough to differentiate between the signal's harmonics; if multiple harmonics are lumped into the same bucket, then there is no guarantee that YIN will find the correct one afterwards. (Caveat emptor: I haven't tried this, and it's quite late at the moment so hopefully I'm not saying nonsense... :P )

        Hopefully this gives you a bit more background for your research. Let us know how things turn out for you!

        Delete
    35. hey there! firstly wonderful project! actually i am in the process of making my automatic guitar tuning system using arduino mega 2560.,.....but im facing trouble in the programming! please help in any ways possible! would be really grateful :)

      ReplyDelete
      Replies
      1. Hi there Jubel! You should be able to download all the code for this project from the links higher on this page. Hopefully that gives you a decent starting point; the gist of the algorithm is explained here as well. Let me know if you have any more specific questions and I'll do my best to help out. Best of luck with your project!

        Delete
    36. Hi Raptor,

      Thanks for posting this great project. I am trying to do something similar. I want to be able to see what note is being played by a guitar based on you're YIN code. I want to send signal through Analog pin A0 process it through your YIN code and see the resulting detected frequency. Can I monitor what frequency is detected through the Serial Monitor? I already have a pre-amp so there's that. I've been reading through similar questions above but it still isn't clear to me what I can keep in the code and what is superfluous for my needs. I'm using an Arduino Uno. You're reply is much appreciated.

      ReplyDelete
      Replies
      1. Hello Julian!

        You should be able to use the code relatively easily to do what you want: simply change ENABLE_LCD to 0, and look for the variables "instantFrequency" and "filteredFrequency" in the code. The former is the output from YIN, and the latter is the same but run through a low-pass filter; it should be smoother. I would start by printing filteredFrequency through the serial port and moving on from there.

        You can easily change the analog input pin used by the tuner by passing a different value to the Tuner object constructor in loop(), but I must admit I haven't tested that in a good long while so I can't guarantee 100% it works. By default it uses pin A5.

        Best of luck!

        Delete
      2. Raptor,

        Thanks so much for your quick reply!!! You are very kind. I have a working code of the YIN algorithm now. When I see the frequencies on the serial monitor, I often see an initial trash value after striking each string. Any idea why that is?

        Delete
      3. Hello again, Julian!

        Hmmm, that is a good question! It is quite possible that my tuner has the same issue, but it may be quite difficult to tell from the user's standpoint since I only communicate through an LCD, which is relatively slow to update.

        I'm not 100% sure why this problem might happen, but I can think of a few reasons:
        -possible distortion due to the large peaks that occur when a string is struck
        -some kind of bizarre transient response in the electronics, where it might take a small moment for things to settle into steady-state
        -strangeness due to the initial aperiodicity of the string vibration. When the string is struck, it takes a very small amount of time before it actually settles into its standing wave pattern; thus, the first very short part of the signal is actually not quite periodic, causing issues for Yin.

        This strikes me as an initial condition problem that's not likely to be trivially solved. If you're not too fussy about things, I would probably just skip the first few values or try to filter them out, something like that. That does introduce a small amount of lag into your system, but if you can live with it, that's probably the easiest way out.

        If you do wind up trying to dig deeper, I would maybe try to log out the deeper workings of the YIN algorithm. Since the tuner code is made to run within the boundaries of the 1K of RAM available on an ATmega168, you could use the extra 1K of RAM available on the Uno's ATMega328 to store a small number of samples from the moment a string is struck, dump those out, and visualize them in in a spreadsheet program to see if anything stands out. Do let me know if you find anything!

        Best of luck!

        Delete
      4. Once again thank you for your reply!

        I will most likely try to skip those values for now. Another interesting thing I noted was when I hooked up an audio signal of an acoustic guitar strumming slowly down the open strings to view as a reference. I noticed a periodic spike in the frequencies... Anyways, I'll try to hack these out for now.

        I have a quick question. In order to do processing upon the detected frequency, is it best to do it within the Go() function or to maybe return the frequency to the loop and call another function to do other processing? Is there a difference?

        Thanks again!

        Delete