Search
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).
|
| |
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.
| The 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).
| The 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.
|
|
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.
|
|
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. |
|
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
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 |
|
|