FFT Tuner

[Home]   [Puzzles & Projects]    [Delphi Techniques]   [Math topics]   [Library]   [Utilities]

 

 

Search

Search WWW

Search DelphiForFun.org

As of October, 2016, Embarcadero is offering a free release of Delphi (Delphi 10.1 Berlin Starter Edition ).     There are a few restrictions, but it is a welcome step toward making more programmers aware of the joys of Delphi.  They do say "Offer may be withdrawn at any time", so don't delay if you want to check it out.  Please use the feedback link to let me know if the link stops working.

 

Support DFF - Shop

 If you shop at Amazon anyway,  consider using this link. 

     

We receive a few cents from each purchase.  Thanks

 


Support DFF - Donate

 If you benefit from the website,  in terms of knowledge, entertainment value, or something otherwise useful, consider making a donation via PayPal  to help defray the costs.  (No PayPal account necessary to donate via credit card.)  Transaction is secure.

Mensa® Daily Puzzlers

For over 15 years Mensa Page-A-Day calendars have provided several puzzles a year for my programming pleasure.  Coding "solvers" is most fun, but many programs also allow user solving, convenient for "fill in the blanks" type.  Below are Amazon  links to the two most recent years.

Mensa® 365 Puzzlers  Calendar 2017

Mensa® 365 Puzzlers Calendar 2018

(Hint: If you can wait, current year calendars are usually on sale in January.)

Contact

Feedback:  Send an e-mail with your comments about this program (or anything else).

Search DelphiForFun.org only

 

 

 

Problem Description

Here's a first attempt at a "tuner" which analyzes audio data from a microphone or other input and displays the predominant frequency it contains.   

Background & Techniques

Several years ago, I modified a unit which captures sound card data streams using  Windows interface "WaveIn" procedures and data structures.  The result was used to write an Oscilloscope program which at the time was named 'Simple Oscilloscope".  The program has been quite popular and has evolved over time so that is now not so simple.  A fellow Delphi programmer, Christer, did lots of work updating the visual aspects added a mixer to specify the inputs to be used, and a dual trace facility for stereo inputs.    Someplace along the line, I added a facility which will capture a frame of data to analyze and display its frequency content. 

The idea of a "tuner" program has been on the back burner for quite a while, but I dreaded digging into the complex code that the oscilloscope has become.   The trigger that prompted me to tackle it was an email from a sharp 15 year old student in Poland who is learning Delphi and wanted to  write a program which could respond to audio inputs based on the frequency.   I decided to  extract the necessary code from Oscilloscope into the current program to give him a starting point easier to understand than Oscilloscope.     

This initial version  monitors the currently selected input "recording" device, (typically the line or microphone input) and displays the predominant frequency about once per second.  

For simplicity, the frequency at which the input data is sampled and amount of data per sample are fixed at 11025 samples per second with 8192 samples (about 0.8 seconds worth) in each buffer analyzed. The frequency content of each sample is calculated by a technique known as the "Fast Fourier Transform", thus the FFT in the title.

The technique transforms a time series into a set of frequencies with particular amplitude and phase. If the time series values of these frequencies were added and plotted, the result would be an approximation of the original time series data!

FFT was named in honor of 19th century French mathematician and physicist Jean Baptiste Fourier who first developed the idea that this was possible. The "Fast" part of the algorithm reflects that, if the number of points in the sample is restricted to be a power of 2, a shortcut can be used to greatly reduce the number of calculations required.

FFT produces "1/2 sample size" frequency estimates for frequencies up to 1/2 the sampling rate. In our case, this means that 4096 frequencies are estimated for frequencies up to 5512 Hz  (Hz = Hertz = cycles per second), so resolution, the distance between adjacent frequencies  is about 1.3 Hz.

When running, the program display the number of buffers processed and  frequencies with the highest and 2nd highest amplitudes in the current sample. Amplitudes are un-scaled "as calculated" values. The % of the total power in the sample represented by these samples is also displayed.

Controls are simple:  two buttons "Start" and "Stop".  As a disclaimer, no extensive testing has been performed.  With a cheap microphone plugged in and no input, the program displays low level frequency of 59, 60, or 61 Hz, which is encouraging.   For non-Vista systems, my Soundgen program can feed input to the tuner and the values so far look reasonable.   Vista is another story, discussed below.

As usual, your feed back is welcome. 

   
Non-programmers are welcome to read on, but may want to skip to the bottom of this page to download an executable version of the program.

Notes for programmers

As usual, I learned  many things in the process of what was supposed to be essentially a stripped version of the Oscilloscope program.

bulletThe UWavein4 unit in Oscilloscope had been modified to replace the "buffer full"  message handler over to the "OnBufferFull" code in the oscilloscope unit itself.  The change was due to "hangs" when OnBufferFull exit was called.     (See "The Callback Problem" on the oscilloscope web page for more details.)   I decided to have another go at re-implementing the "OnBufferFull" event exit.   It turns out that many operations inside of the event exit are forbidden.     Here is the Remarks section of  Windows SDK Help for WaveInProc

Applications should not call any system-defined functions from inside a callback function, except for EnterCriticalSection, LeaveCriticalSection, midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage, PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent, and timeSetEvent. Calling other wave functions will cause deadlock

The permitted functions  are not normally called from Delphi, but other "forbidden" system-defined functions do get called from Delphi controls.  Specifically, setting TLabel captions inside of the buffer-full event exit will cause cause hangs, access violations, invalid pointers, or stack overflow errors in an unpredictable manner.  At least that's my conclusion after quite a few "trial and error" experiments.   Replacing TLabel updating with TMemo or TStaticText controls seems to have fixed the problems.  (I hope - sometimes the program would run for several minutes before bombing).

bulletThe original FFT module used was part of a Borland Numeric Toolbox published back in the Turbo Pascal days.  Dynamic arrays did not exist "way back then" so pointers to dynamically obtained memory spaces were used extensively.  While fighting the errors discussed above,  all explicit pointers and dereferencing code in the FFT unit (now renamed to U_FFT2) have been eliminated.   This is a good thing from a maintenance standpoint - pointer usage was notorious for causing hard-to-trace errors.      
bullet

 The SinTable and CosTable arrays used in the FFT routines to speed the calculations were formerly rebuilt for each spectrum calculated.  They  are now only rebuilt when the number of points in the time series buffer changes.  

bullet

A change in the audio processing interface in Vista is going to keep a number of programmers fully employed for the next year or so.  If you right click your Volume control icon  and get to the "Advanced" options page which lets you specify volumes for recording devices you're likely to see many fewer devices under Vista than you see under XP.   In Vista, only physical devices show up as controllable.  This means, for example, that the signal from programs which generate sounds is not accessible, at least by default.  I'm looking for an XP style mixer program to run under Vista, but haven't found one yet.

bullet

Aside from the above, the rest of the extraction/conversion from Oscilloscope was quite straightforward; mainly a matter of copying the techniques to start and stop the WaveIn buffer processing and writing the new buffer full procedure procedure to find the predominant frequency and display it.    I did create code to interpolate between to frequency values when the two highest amplitudes were adjacent in the frequency array.   

Running/Exploring the Program  

bulletDownload source
bulletDownload  executable

Suggestions for Further Explorations

User control of sampling rate and buffer size might be valuable for some applications, and not difficult to implement.  For easy usability, additional options should be in an "Advanced Options" dialog which I'll leave for a future update.

 

Original Date: June 13, 2007

Modified: May 11, 2018

 

  [Feedback]   [Newsletters (subscribe/view)] [About me]
Copyright © 2000-2018, Gary Darby    All rights reserved.