PDA

View Full Version : Different-Range FFT Algorithm



lychrel
12-24-2015, 07:24 PM
I'm using Teensy for an application wherein I'll only be analyzing signals of the frequency domain 0 to 1 kHz, and I'd like to have a higher-precision FFT that spans the 512 bins with a smaller frequency-difference between each two. I'm not very good at this sort of thing; could someone point me in the right direction re how to do this?

Thanks!

cartere
12-24-2015, 08:50 PM
I assume using the audio library? The way you achieve the higher resolution at lower frequencies is to reduce the sample rate Nyquist says 2N for a sample rate where N is the highest frequency of interest. In your case a sample rate of 2khz would give the best resolution. Changing sample rate is not supported with the audio library. I have kludged the library using the ADC input and sampled at 8khz but that was sometime ago and the library is a moving target so might not work now :) Some others (kpc, defragster)? have code that changes the sample rate on the fly but you will have to dig in the forum to find it.

What are you trying to do? It is possible to interpolate values in bins to achieve a much higher resolution.

PaulStoffregen
12-24-2015, 09:24 PM
I believe Frank was working on something like this. Maybe he'll chime in?

Something to consider is the FFT's fundamental trade-off between frequency resolution versus update rate. Higher res means fewer updates per second. For example, if you compute a 1024 point FFT (for 512 bins) on 2 kHz sampled data, you're results will span a 500 ms window of time. There's no escaping this fundamental reality of Fourier Transform.

So, assuming you're ok with updates that span lengthy times (good for analyzing steady signals, much too slow for music or speech), you need to get slower sampled data to feed into a FFT. There's 2 ways to do this...

Simply sampling the analog signal slower probably seems like the simplest way, but it actually comes with some really tough problems if the signal is audio. With a slow sample rate, you must filter away content above half the sample rate. As simple as that sounds, it's actually quite difficult to do. In fact, it's so hard that virtually all ADCs designed for audio using massive oversampling and then they digitally filter the data while reducing it to the desired sample rate.

The other way, which involves a lot of numerical processing but avoids tough analog circuit design, is to digitally filter the already sampled 44.1 kHz data. Even though this seem like a lot of work (and indeed it is a lot of work for the CPU), I'd recommend taking this approach. The audio library already has 3 known-good digital filter objects. So if your goal is a 2 kHz sample rate, you can use those filters to get rid of everything above 1 kHz. Filtering is never a perfect "brick wall", so the filtering involves trade-offs, where you'll need to attenuate some frequencies (perhaps 700 to 1 kHz) in order to get everything above 1 kHz sufficiently removed.

Once you're filtered away the too-high frequencies, then all you have to do is keep every Nth sample and discard the rest. For example, you could keep every 22nd sample and throw away the other 21, to end up with 2.0045 Hz sampled data. The simplest way to do this would be to receive data from the audio lib using the record queue object. See File > Examples > Audio > Recorder. Except instead of writing the data to a SD card, you'll just pick several samples from each 128 sample buffer. You'll probably just copy them into your own 1024 sample buffer. Keep repeating until you've filled up all 1024 samples. Then run that through the ARM Math Library arm_cfft_radix4_q15() function. See the audio library's FFT1024 code (https://github.com/PaulStoffregen/Audio/blob/master/analyze_fft1024.cpp) for an example, where it fills a 1024 sample buffer from the last 8 buffers of 128 samples. You'll do the same, except you'll discard most of the samples and only use 5 or 6 from each 128 (taking care to remember the index between buffers, so you always grab the 22nd sample from the stream). The arm_cfft_radix4_q15() gives you complex pairs of numbers (real and imaginary), so if you only want the magnitude, take the square root of the sum of their squares. Again, refer to the library source for an efficient integer square root. You can probably just copy that code into your program.

The key point is you absolutely must digitally filter the signal before you discard 21 of every 22 samples. The digital filtering is what causes every 22nd sample to properly represent your signal. If you just take every 22nd sample without filter filtering, you'll get absolutely horrible results (assuming your signal is common audio or other content with higher frequencies present).

defragster
12-26-2015, 08:49 AM
I was looking at this when KPC was doing FFT work (https://forum.pjrc.com/threads/27905-1024-point-FFT-30-more-efficient-(experimental)) and Paul provided a better summary than I could of what I learned and direction - though other forum notes may have useful details. The sound FFT I was considering was secondary to my needs and seeing that higher resolution took longer to sample over a smaller frequency without dropping the cpu burden enough to let run after I needed it. Unfortunately my primary focus went to a ESP8266 based Kickstarter item that skipped my Beta hardware release for improved software - then slipped on shipping due to other outside events.

WMXZ
12-26-2015, 09:47 AM
I'm using Teensy for an application wherein I'll only be analyzing signals of the frequency domain 0 to 1 kHz, and I'd like to have a higher-precision FFT that spans the 512 bins with a smaller frequency-difference between each two. I'm not very good at this sort of thing; could someone point me in the right direction re how to do this?

Thanks!

could you please explain a little bit more your problem.
you are saying frequency domain 0 to 1 kHz with 512 FFT bins, so the frequency resolution is about 2 Hz.

So what is the problem?
Is it that you use the Paul's audio library that samples at 44.1 kHz?
or do you have any other fixed sampling rate?

In all cases, before doing the FFT you have to (down) sample the data to a sampling rate of about 2kHz for a 1024 point FFT or better to about 4 kHz for a 2048 point FFT.
So if you sample at 44.1 kHz, then decimating by 11 (apply LP filter with 1/11 cutoff filter (similar to matlab) and take every 11th sample), you would end up with 4.01 kHz and a 2048 point FFT gives you a frequency resolution of about 2 Hz.

Unfortunately Paul's audio-library has not implemented both decimation and 2048 point FFT. The implementation of such features would, however, be not difficult.

Also a block size of 128 is somewhat inconvenient for a decimation of 11. Better would be a block-size that is a multiple of the decimation factor. However if you where happy with a resolution of 2.7 Hz, a decimation of 8 would nicely fit into the buffer.

Davidelvig
12-27-2015, 12:55 AM
lychrel – Audio pitch detection is a key part of my application. My evolving understanding of FFT (and Teensy’s use of it) may help you. I hope others will confirm my understanding (or refute/improve), and help me extend it.

Here’s my train of thought:

FFT is a specialized flavor of a DFT (discrete Fourier transform).

A fairly-readable overview can be found at: blogs.mathworks.com/steve/2014/09/23/sinusoids-and-fft-frequency-bins/
(I’ve read a dozen such overviews over the last year… I’ve found that my view of “fairly-readible” evolves.)


There are key characteristics (constraints) of FFT and the Teensy Audio Library when being used for audio frequency discovery:

Paul S. chose to leverage the sampling rate of 44,100 to be equal to an audio CD’s sample rate. Choosing a single sample rate throughout the audio library allowed simpler design, when compared with a flexible sample rate.
When using FFT, the maximum detectible frequency is of the sample rate (someone named Nyquist gets credit for asserting this). This determines the maximum frequency discoverable by Teensy’s Audio Library at 44,100/2 = 22,050 Hz.
FFT uses a concept of “bins” to store frequency “hits” as it uses math to estimate one or more sine waves that, if added together, would reproduce the input sound wave.
- The number of bins used by FFT is flexible, and the Audio Library offers two options: 256 and 1024 bins. Choosing a number of bins that is a power-of-two allows dramatic math efficiencies – making it the fast Fourier transform (FFT). In these power-of-two cases, many of the calculations can be skipped, since many calculations amount to “multiplying by 1.”
The FFT “bin size” is calculable by [Sample Rate]/[Number-of-Bins]. So, on Teensy Audio, using the FFT1024 object, bin size = 44.3 Hz/bin (from 44,100 Hz/1024 bins).
[Disclaimer: my chosen terms may not be precise – audio engineers please correct me where it adds value. I hope my high-level picture is correct, despite imprecise terms]
With a bin size of 43 Hz, I have been able to detect the pitch of sine waves above about 200Hz, with roughly 1% accuracy. (That is, with an input sine wave of 440 Hz; fed into an FFT1024; plus some additional calculations… I arrive at a frequency between 436 and 444 Hz).
Below about 5 times the bin size, frequency determination is more variable or impossible in my hands.
Smaller bin size (and presumably, lower detectible pitch) can be achieved by changing one of the two numbers that affect bin size:




Increase number of bins – 1024 is maximum easy bin number in Teensy Audio
Decrease Sample Rate (paradoxical, but true) – Sample rate on Teensy Audio is fixed at 44,100 in Teensy Audio



So: to do this using FFT1024 in Teensy Audio, Paul suggests that our best choice is to decimate (maybe not the precise word) the samples. Decimating in powers of 2 provides some advantages in tracking the sample batches (use every 2nd sample, every 4th sample, every 8th sample, etc.).
Teensy offers a batch size of 128 samples.

Decimation is not built in to the Audio Library, but could be added (I think). It would require using a pair of “queue” objects – one of output type and one of input type – doing the decimation between the two queue objects.

The following table lists the possible results of decimation on parameters that matter to my solution:
5905

Hence the trade-off between low-note-detectability (small bin size), and time-to-acquire-sample.

In parallel, here’s what I’ve found for frequencies of some musical inputs:

Trumpet (or soprano voice): Min: 160 Hz Max: 1880 Hz
Trombone (or bass voice): Min: 80 Hz Max: 700 Hz
Tuba 40 Hz? one octave lower?

So, for my brass instrument work:

I’d like to detect pitch correctly down to less than 100 Hz (to use the full range of trumpet or trombone)
I want to be able to detect a note change as fast as possible. In the case of a trumpet, faster than 10 times per second would be best. Good trumpet players can run scales at least that fast.
So my conclusion: For a trumpet pitch detector, I think want to decimate by 2, resulting in a 100 Hz lowest-detectible-pitch, and a time-to-collect-samples of 46.4 ms.


For this reason, I think it’s worth implementing the queue→decimate→queue concept.

It must be fast – in addition to the time to decimate, code on the Teensy will need to do the following:
- sample-acquisition time (table above),
- FFT calculation (audio library)
- FFT interpretation (my code)
- Other application processing (my code)

Paul and other experts… am I on the right track?

Dave

duff
12-27-2015, 01:13 AM
So, for my brass instrument work:


Id like to detect pitch correctly down to less than 100 Hz (to use the full range of trumpet or trombone)
I want to be able to detect a note change as fast as possible. In the case of a trumpet, faster than 10 times per second would be best. Good trumpet players can run scales at least that fast.
So my conclusion: For a trumpet pitch detector, I think want to decimate by 2, resulting in a 100 Hz lowest-detectible-pitch, and a time-to-collect-samples of 46.4 ms.



hmmm, i wrote library that plugs into the audio library (https://github.com/duff2013/AudioTuner) for finding the fundamental frequency down to 29.14 Hz. I use it in my bass and guitar tuner project with good results. It based off the YIN algorithm (http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf). Another problem with using FFT for finding musical instrument pitch is the magnitude of the fundamental can be lower than the overtones. Also see Goertzel algorithm even then you have to make some assumptions because I can say for certain that with real instruments I have a hard time deciphering the fundamental frequency from the overtones but its really fast.

Davidelvig
12-27-2015, 06:48 AM
That's great, duff!
I'll give it a look.
How might I have found your library, short of writing an extended tome as I have here?
I hope it is what I've been looking for!

Is there a similar Goertzel-using Teensy Audio add-on?

Dave

omjanger
12-27-2015, 08:52 PM
This looks great! How fast are you able to detect the fundamental frequency? And do you detect more than one frequency? Like a chord?
Have been looking at the same but not been able to work hard enough to test it yet. Found this site that seems to be on track: http://www.instructables.com/id/Reliable-Frequency-Detection-Using-DSP-Techniques/
Is this what you are doing?

duff
12-27-2015, 09:56 PM
Is there a similar Goertzel-using Teensy Audio add-on?

yes the DialTone_Serial (https://github.com/PaulStoffregen/Audio/blob/master/examples/Analysis/DialTone_Serial/DialTone_Serial.ino) example. The Audio Libraries Goertzel implementation has problem at low frequencies but anything above ~440Hz it does a good job. I wanted to look into this but never did, maybe this is something worth looking into since its data processing is so much faster than the Yin. Plus at frequencies > 1000Hz my implementation of the yin does not do a great job, I just "tuned" it for guitar and bass tunning frequencies. Most likely won't look into expanding it beyond that since I don't have a lot of time right now.



This looks great! How fast are you able to detect the fundamental frequency?

~70ms, this is because I needed to detect low frequencies (29.14Hz) so there is no way to get around that. You have to acquire enough of the signal for analysis. You can edit the number blocks to processes to speed up the detection rate here (https://github.com/duff2013/AudioTuner/blob/master/AudioTuner.h#L39) at the cost of not being able detect lower frequencies.



And do you detect more than one frequency? Like a chord?

No, its for single notes but you can use the passband filter then feed that to the tuner object. I haven't tied this yet though. There are probably many preprocessing techniques you could do before feeding it that processed signal but will add additional latency.

If your lowest frequency in the chord is greater than ~440Hz then you could use multiple goertzel but then you would need a lot of them.

Realtime chord detection is probably beyond the processing capabilities of the Teensy 3.1/2, maybe when the Teensy 3++ comes out it will have enough horsepower to make this possibilty? Though I could be wrong maybe the FFT would be able with lots of post processing?



Found this site that seems to be on track: http://www.instructables.com/id/Reliable-Frequency-Detection-Using-DSP-Techniques/
Is this what you are doing

Yes, it use's Auto Correlation with a bunch error correction techniques to get around the false positives of just using the auto correlation. The nice thing with the Yin algorithm is that it is amplitude agnostic in that the natural decay of the signal does not effect the outcome within reasonable limits.

omjanger
12-27-2015, 10:30 PM
Thanks for the answers! Will test this later when I am home...
I am trying to make a octave down effect for bass guitar. Then I would divide the frequency by 2(one octave down) or 4 (2 octaves down). Then the lowest frequency that should be detected is 40Hz or 80Hz. No point to generate frequencies below 20hz anyway.. Then this might speed up to a usable speed! it needs to be around 10ms to avoid a "lagging" feeling when you play the instrument.
With fretted instruments I guess we also can look at the harmonics of the fundamental freq and detect that faster (and accurate) then the actual fundamental frequency and then use FFT to check if there are lower components. If that is faster..?

Davidelvig
12-27-2015, 11:14 PM
I'm thrilled to see this activity around fundamental pitch detection on the forum.
I'd like to see a summary of the best uses for each (FFT1024, FFT256, YIN, Goertzel), and for each:
- time to acquire frequency in freq range X, Y Z, etc.
- most accurate frequency range
- precision to expect
I'll do this for my own use, and will plan to share it as it evolves.

I have a question (for Paul or duff), regarding the available() method (on the FFT1024 and the AudioTuner objects).
Does each call to available() create a snapshot of the acquired data - until the next call to available?
Without that, I could see the reading of the data (in my loop) conflicting/overlapping with filling of data from the interrupts.

Paul or Jeff?

duff
12-27-2015, 11:19 PM
well 40Hz needs 25ms to cycle once, how would a FFT be able to process this 10ms after you pluck the string? Remember Nyquist rate thing also! Not sure how to reliably detect the harmonics and relate that to the fundamental? I'm sure its possible but how much processing does it require? That might eat up just as much time than going for the fundamental?

Thats why the bass is so hard to make good digital effects for. I personally have been playing bass for 20 years and don't use any effects because in my opinion they just muddle the sound esp. playing in the low registers. Sometimes I use compressor on certain rigs and I gig once in while using a bass wah for solos with a punk band but we where so loud I could be muddled and not bother with a good clean sound.

duff
12-27-2015, 11:36 PM
I have a question (for Paul or duff), regarding the available() method (on the FFT1024 and the AudioTuner objects).
Does each call to available() create a snapshot of the acquired data - until the next call to available?
Without that, I could see the reading of the data (in my loop) conflicting/overlapping with filling of data from the interrupts.

I can't speak for the FFT but for AudioTuner object when "available" is true it has found the fundamental for the last 24 blocks of data it has just processed and then just processes the next 24 blocks. Well, it processes the blocks while collecting the next 24 blocks technically. It dose not use sliding window like the FFT either meaning half of the buffer is from the already processed data, I'm sure there is much more to this than I have a clue about too:)

With my AudioTuner object you don't get access to the raw data since it is only is looking for the largest dip when doing the AutoCorrelation and error correcting techniques. Plus that would require another buffer that eats up precious ram.

As far precision I was getting around 1 cents (http://hyperphysics.phy-astr.gsu.edu/hbase/music/cents.html) for real signals.

Davidelvig
12-28-2015, 02:50 AM
I just did some timing tests...

I can run a loop using fft1024 every 11ms. I do this with a delay(10) in my loop.
As I use delay() shorter than that, I'll get available()=false... More and more as I get shorter.

That's roughly twice as fast as I would expect (see my post above), perhaps because Paul is reusing half of the samples each cycle (is this true?)

Regardless, at this pace, I can detect the fundamental of a trumpet mouthpiece buzz down to 160 Hz... And it seems very reliable. Good for trumpet. Not for trombone.

I'll be looking into the flakier detection below 160, but this is good stuff!

Side note... I spent several hours tracking down rather violent noise in my system... Until I realized I had added one too many Audio library objects, and had not appropriately checked or increased audio memory.

So, out of memory equals, at least, noise.

defragster
12-28-2015, 07:32 AM
This Example under Audio: MemoryAndCpuUsage shows functions allows monitoring some operational details.

PaulStoffregen
12-28-2015, 12:54 PM
hmmm, i wrote library that plugs into the audio library (https://github.com/duff2013/AudioTuner) for finding the fundamental frequency down to 29.14 Hz. I use it in my bass and guitar tuner project with good results. It based off the YIN algorithm (http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf).

Really nice. I especially like the ascii graphic! :)

Would you allow me to merge this with the audio library?

omjanger
12-28-2015, 02:10 PM
well 40Hz needs 25ms to cycle once, how would a FFT be able to process this 10ms after you pluck the string? Remember Nyquist rate thing also! Not sure how to reliably detect the harmonics and relate that to the fundamental? I'm sure its possible but how much processing does it require? That might eat up just as much time than going for the fundamental?

Thats why the bass is so hard to make good digital effects for. I personally have been playing bass for 20 years and don't use any effects because in my opinion they just muddle the sound esp. playing in the low registers. Sometimes I use compressor on certain rigs and I gig once in while using a bass wah for solos with a punk band but we where so loud I could be muddled and not bother with a good clean sound.

You have some good points!
Are your code detecting the frequency with the highest amplitude?
According to this site: http://www.puremix.net/blog/musical-instruments.html that can lead to some things.. Seems like the 3rd harmonics from a typical bass is higher in amplitude then the fundamental. But we can also see that it is possible to know the fundamental if we know the 2nd and 3rd harmonics. -> common multiple. Or do you filter/remove the harmonics?
Think I read somewhere that if the brain hears the harmonics it "hears" the fundamental even if its not there. Have not tested that=)

duff
12-28-2015, 04:37 PM
Would you allow me to merge this with the audio library?

sure! I hope people find it useful.

duff
12-28-2015, 05:55 PM
Are your code detecting the frequency with the highest amplitude?

No, but if harmonics amplitude are much greater than fundamental there would be problems, I find that this not an issue with the instruments i've tested thats why it works good for my guitar tuning project which is on hold now cause I got cute little baby to look after:). Mom works 14 hours a day for her fellowship so i'm Mr. Mom now.



According to this site: http://www.puremix.net/blog/musical-instruments.html that can lead to some things. Seems like the 3rd harmonics from a typical bass is higher in amplitude then the fundamental.

cool site, thanks for sharing, theory is not my strong point I'm trying to learn though, i used to work at a engineering department so lots of brilliant profs and students to bounce ideas off, but not anymore since i moved:(.



But we can also see that it is possible to know the fundamental if we know the 2nd and 3rd harmonics. -> common multiple. Or do you filter/remove the harmonics?

No, there is no filter, but i do use filter objects (low pass & band pass) before feeding it to the AudioTuner object for my tuner project which helps with aliasing and such. I haven't come across any project that use the harmonics to find the fundamental in a realtime system but if you do please share! My next project is to interface the teensy with 1Wamp (http://www.electrosmash.com/1wamp) for electric guitars which will be awesome i think since it has a mp3-aux input. I have that problem of not finishing one project before starting another!

here are some links i used for code and analog:
http://sound.eti.pg.gda.pl/student/eim/synteza/leszczyna/index_ang.htm
http://www.musicdsp.org/index.php
http://cnx.org/contents/i5AAkZCP@2/Pitch-Detection-Algorithms
http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html

http://www.radio-electronics.com/info/circuits/opamp_band_pass_filter/op_amp_bandpassfilter.php
http://sim.okawa-denshi.jp/en/Fkeisan.htm

omjanger
12-28-2015, 08:44 PM
No, but if harmonics amplitude are much greater than fundamental there would be problems, I find that this not an issue with the instruments i've tested thats why it works good for my guitar tuning project which is on hold now cause I got cute little baby to look after:). Mom works 14 hours a day for her fellowship so i'm Mr. Mom now.
Enjoy! Its a good time (to do guitar boxes...) and everything else that needs to be done!



No, there is no filter, but i do use filter objects (low pass & band pass) before feeding it to the AudioTuner object for my tuner project which helps with aliasing and such. I haven't come across any project that use the harmonics to find the fundamental in a realtime system but if you do please share! My next project is to interface the teensy with 1Wamp (http://www.electrosmash.com/1wamp) for electric guitars which will be awesome i think since it has a mp3-aux input. I have that problem of not finishing one project before starting another!

here are some links i used for code and analog:
http://sound.eti.pg.gda.pl/student/eim/synteza/leszczyna/index_ang.htm
http://www.musicdsp.org/index.php
http://cnx.org/contents/i5AAkZCP@2/Pitch-Detection-Algorithms
http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html

http://www.radio-electronics.com/info/circuits/opamp_band_pass_filter/op_amp_bandpassfilter.php
http://sim.okawa-denshi.jp/en/Fkeisan.htm

Good links you have there! Will dive into that.. And the 1Wamp seems like a nice fellow. Maybe not for concerts but more for the living room?
Regarding fast detection of the fundamental I think I will try to make several bandpass filters and then run the tuner on them to get several peaks. Not sure if that will be faster though when I have to use multiple tuners..? will see!

PaulStoffregen
12-28-2015, 11:17 PM
sure! I hope people find it useful.

I've merged this into the audio library. I took some small liberties, mainly to renaming the fit the library and Arduino naming conventions.

One quick question: internally the library has a "periodicity" variable. Maybe that was meant to be "probability"?

PaulStoffregen
12-29-2015, 12:12 AM
I made a first attempt at documentation. Here it is:

http://www.pjrc.com/teensy/gui/?info=AudioAnalyzeGuitarTuner

EDIT: the object was renamed. The correct link is now: http://www.pjrc.com/teensy/gui/?info=AudioAnalyzeNoteFrequency

Hopefully I didn't butcher it too badly? Let me know if anything should be corrected or if you'd like to add anything?

duff
12-29-2015, 02:53 AM
I've merged this into the audio library. I took some small liberties, mainly to renaming the fit the library and Arduino naming conventions.

One quick question: internally the library has a "periodicity" variable. Maybe that was meant to be "probability"?

yes, this confusing but I look at it as the probability of the periodicity (http://www.slideshare.net/tahsinboss/periodic-vs-aperiodic-signal) of the signal not just a strict mathematical definition of probability. So for example if the signal is somewhat or vary aperiodic such as the frequency is changing with time or noisey, this would exhibit low or no periodicity which should looked at with skepticism of algorithms found frequency. So I think just using probability would be fine for the name of that variable.

I admit that my math is not so strong to define this correctly but from my reading, it should be fairly correct, hope that makes sense.



I made a first attempt at documentation. Here it is:

http://www.pjrc.com/teensy/gui/?info=AudioAnalyzeGuitarTuner

Hopefully I didn't butcher it too badly? Let me know if anything should be corrected or if you'd like to add anything?
Wow I haven't looked at the gui tool in a while, very nice! The description looks good, I don't think any changes needed, thanks for adding this.

omjanger
12-29-2015, 09:51 AM
This is great!! Will start testing and give feedback on the results...

Davidelvig
12-29-2015, 02:43 PM
Paul, Could you read my longer post above (12/26) and let me know if I'm on track?

I'm thinking of packaging an audio library object that wraps the fft1024, and exposes a reliable fundamental pitch, though I want to confirm that I'm on the right track in my underlying assumptions.

PaulStoffregen
12-29-2015, 03:04 PM
I believe you should try Duff's code first. Either grab his stand-alone library (https://github.com/duff2013/AudioTuner), or get the latest Audio library (https://github.com/PaulStoffregen/Audio) from Github.

Duff wrote a great example. Once you've installed the latest audio lib, open it from File > Examples > Audio > Analysis > GuitarTuneNotes. (edit, it's been renamed to NoteFrequency since this post was written) It plays and analyzes one of several recorded samples. First, get that running on your Teensy. Connect the DAC output pin to amplified computer speaker or a stereo with standard line-level input.

Just to be absolutely clear about "on the right track", my opinion is you should focus your effort on getting Duff's example running on your hardware ASAP. That may turn out to be a dead end for you, but I believe it's likely to be fruitful. Even if it's not perfect, I believe working to get something imperfect really running on hardware will do you far more good that more hours on theoretical analysis.

Once you have Duff's example working, perhaps a next step would be adding your own sound samples. You can use the wav2sketch program (https://github.com/PaulStoffregen/Audio/tree/master/extras/wav2sketch) to convert short sound clips. Or you could connect a SD card and modify the program to play from SD instead of internal memory.

If you do any of this, I hope you'll report back here about how effective Duff's code using the YIN algorithm is when used with other instruments. Maybe you could even share some other test sounds, like the trumpet, trombone and tuba you mentioned? I'd love to add more realistic test samples for other types of instruments to that example sketch, if the sound samples can be freely shared.

duff
12-29-2015, 10:11 PM
Duff wrote a great example. Once you've installed the latest audio lib, open it from File > Examples > Audio > Analysis > GuitarTuneNotes. It plays and analyzes one of several recorded samples. First, get that running on your Teensy. Connect the DAC output pin to amplified computer speaker or a stereo with standard line-level input.

Just a note: the full samples where to large to fit in ram so I cut them off abrutly so maybe I should look into making them have a more graceful transistion at the end, you might notice some wierd results toward the end of the sample.

here is some pics of my tuner, It has SPI - PGA for dynamic gain control, a OLED and I plan to add a LIPO charger circuitry in the next revision, also designing of encloser in SolidWorks and use a 3d printer at our local hacker space in Little Rock.

5927592859295930

Davidelvig
12-29-2015, 10:31 PM
OK, I've modified my test app, replacing my FFT pitch detector with the Collin Duffy's AudioTuner.

Code is at https://github.com/Davidelvig/AudioTunerTest
(first foray into Github)

Physical setup is as in these pics
59325931

Audio Lib GUI as in this pic
5933

Pitch recognition works very well:
- in the tested range of about 40 Hz - 4000 Hz
- at a time cost of about 70ms per pitch detection cycle.

Sine waves are best.
Singing voice into the mic (mine) is pretty good as well.
Buzzed trumpet mouthpiece into the mic is not quite as accurate, but still pretty good.

The fundamental pitch is recognized if it is material, and if the next harmonic is not too big.
The fundamental need not be the biggest peak.

The test app will let you play with relative ratios of harmonics.

It works well, though perhaps too slow for my real-time use.

I may move on to test with other waveforms.

So, am I thinking correctly that:
- this works well for pitch recognition in the 20-4000 Hz range
- it will cost 70ms per pitch detection
?

duff
12-29-2015, 11:21 PM
Whats the lowest fundamental freq. you need? You can change the number blocks to process to speed it up at the cost of not being able detect lower frequencies. In the .h file at the top you will see where I define the number blocks the default I set was 24 so you can play around with that. Also the initialize function can be lowered to make for a deeper search for the fundamental try .1 or .05. This will add extra latency but not to much. One thing I did is cut some corners in the search algorithm to lower the CPU usage cause I wanted to be able to run a FFT, Peak, and bunch of filters but that requires some advanced programming to get it right so I'm not inclined to change that but its possibility in the future.

So to recap some theory, this algorithm needs to capture at least two cycles of the signal for it to do its work. The 70ms comes from having to find B0(flat) (30.87Hz) note on a bass. So to estimate how many blocks you need remember one block is approximately 2.9ms of the signal and freq is 1/time so you work out how many blocks with that info keeping in mind the nyquist rate thingy:) Well thats for a perfect world you might need more cycles if your signal is noisy or has lots of harmonics. I see your using a mic, is it quality mic? Some mics can shape the signal also in not such good ways, if you have an oscope check out you signal to make sure its clean. Also look at using some low pass filters.

I would say 4000Hz is not going to work so well, the range I designed this for is standard tuning notes of guitar and bass, 30.87Hz to 329.6Hz but it should be good up to 1000Hz with they way it written now because down sampling i did. I'm sure there are many improvemnts that could be made I just couldn't spend more time on it once I got it working good. I'm alway open to suggestions though.

PaulStoffregen
12-30-2015, 12:51 AM
Perhaps the analysis time could be made configurable? Maybe only in the begin() function would be enough? The AUDIO_GUITARTUNER_BLOCKS define could establish an upper limit, but maybe begin() could optionally allow less of the memory to be actually used for people who need faster response and don't need to detect all the way down to 31 Hz?

Davidelvig
12-30-2015, 12:58 AM
Lowest trumpet freq: 160 or so.
Trombone: 80 Hz
Tuba: 40 Hz

I cut the AUDIO_BLOCKS to 12 from 24 in AudioTuner.h
Now I can reliably detect 80Hz every 50 ms.
That's probably low and fast enough!
Good stuff.

I have always seen good recognition up though 4096Hz.
Mysteries, eh?

Perhaps you could consider an optional constructor, that would accept a number of AUDIO_BLOCKS.
Perhaps it's really a method call, such as setAudioBlocks(int blocks) or setFrequencyRange(LOW | MEDIUM | HIGH)

I can see that it would need to change the three lines where the #define is used to allocate memory on instantiation.
I can use this as is, with my local modification - I'll need to learn the impact of local modifications when updates come.

I'll also keep up with my FFT alternative.
It appears to have the advantage in speed (11ms cycle time), at the cost of not-so-low frequency (160Hz min).
I may try decimation to reduce my bin size - perhaps getting low enough.
I may also be able to play with my bin assessment to get a good-enough pitch at lower freqs.

Thanks to you and Paul for getting the YIN thing going!

Very cool!

duff
12-30-2015, 04:14 AM
Perhaps the analysis time could be made configurable? Maybe only in the begin() function would be enough? The AUDIO_GUITARTUNER_BLOCKS define could establish an upper limit, but maybe begin() could optionally allow less of the memory to be actually used for people who need faster response and don't need to detect all the way down to 31 Hz?
I see why for realtime stuff so I'll look into that when I start that 1Wamp project since I will want that also, there are some other optimizations I'm looking into also to lower the CPU usage since its pretty high now.


Lowest trumpet freq: 160 or so.
Trombone: 80 Hz
Tuba: 40 Hz

I cut the AUDIO_BLOCKS to 12 from 24 in AudioTuner.h
Now I can reliably detect 80Hz every 50 ms.
That's probably low and fast enough!
Good stuff.

I have always seen good recognition up though 4096Hz.
Mysteries, eh?

Perhaps you could consider an optional constructor, that would accept a number of AUDIO_BLOCKS.
Perhaps it's really a method call, such as setAudioBlocks(int blocks) or setFrequencyRange(LOW | MEDIUM | HIGH)

I can see that it would need to change the three lines where the #define is used to allocate memory on instantiation.
I can use this as is, with my local modification - I'll need to learn the impact of local modifications when updates come.

i'll take this into account thanks.

PaulStoffregen
12-30-2015, 08:26 PM
I've been wondering if we should rename this to something less guitar specific? Any ideas?

Once Teensyduino 1.27 is fully released (within the next few days), with this in the audio lib, changing the name will probably not be practical. Now's the last chance to reconsider what the name ought to be long-term.....

defragster
12-30-2015, 09:20 PM
Similar seeming apps offers this thesaurus medley - and feature creep roadmap - and they are String limited it seems::

Tuna Pitch - tune guitar, bass, banjo, cello, or other musical instrument
Description :: https://itunes.apple.com/us/app/tuna-pitch-tune-guitar-bass/id313492710?mt=8
Stay in tune with this chromatic tuner and pitch pipe for your musical instrument. It tunes not only with guitars, bass guitars, and banjos, but also many other instruments and even with your voice (just sing into it!). The built-in pitch pipe lets you play a reference tone for tuning by ear.

The tuner requires microphone access, which can be granted in the Settings app, in Privacy > Microphone. For best results, use a headset or microphone separate from the device.



PerfectTune - the Ultimate String Tuner (https://itunes.apple.com/us/app/perfecttune-ultimate-string/id333616755?mt=8) :: Description

PerfectTune is the ultimate tuner for stringed instruments. Not only does it feature a lightning fast pitch detection system, that is unparalleled by any other tuner on the AppStore, it also comes with an innovative multi-string layout that lets you tune your instrument quickly to a great variety of tunings.

PerfectTune features optimized algorithms for Guitar, Bass, Banjo, Violin, Cello, Ukelele and other stringed instruments, and for each these you can choose from a range of relevant tunings. There's dropped and open tunings for guitar, period tunings for lute, support for 5 and 6-string bass guitars and various types of banjo.

duff
12-30-2015, 09:29 PM
I've been wondering if we should rename this to something less guitar specific? Any ideas?

Once Teensyduino 1.27 is fully released (within the next few days), with this in the audio lib, changing the name will probably not be practical. Now's the last chance to reconsider what the name ought to be long-term.....
How about frequencyDetect or fundamentalDetect or just Yin?

I'm thinking the AudioLibrary version needs to be of broader scope anyway. I hoping some of DSP gurus can look at this and speed it up also. I know one library uses some cool FFT tricks to speed it up but its beyond me right now. But by all means hack away at it, i'll just keep my github version for specifically guitar tuning for now. Really right now the Snooze library is getting some t.l.c from me so but i'll get back to this soon.

omjanger
12-30-2015, 09:33 PM
I've been wondering if we should rename this to something less guitar specific? Any ideas?

Once Teensyduino 1.27 is fully released (within the next few days), with this in the audio lib, changing the name will probably not be practical. Now's the last chance to reconsider what the name ought to be long-term.....
I suggest Frequency detector or tone detector...

duff
12-30-2015, 09:38 PM
Similar seeming apps offers this thesaurus medley - and feature creep roadmap - and they are String limited it seems::
The changes paul and david proposed would make it more functional for sure like these apps. But I'm not sure it has to be geared just for tuning?



I suggest Frequency detector or tone detector...

To keep in spirt with the current naming scheme it should have "analyze_" in front of the file name. There is already analyze_tonedetect.h and cpp so the class name should probably follow the same too - "AudioAnalyze"xxxx also?

PaulStoffregen
12-30-2015, 09:53 PM
Is it a safe assumption (for naming purposes) to assume this is mostly intended for detecting the frequency of musical notes?

duff
12-30-2015, 10:07 PM
Is it a safe assumption (for naming purposes) to assume this is mostly intended for detecting the frequency of musical notes?
Yes

I just meant by "broader scope" is that it's not just used for tuning guitars. I know one person that uses it for converting what notes he plays to midi.

PaulStoffregen
12-30-2015, 10:34 PM
How would you feel about naming it AudioAnalyzeNoteFrequency?

The object class would be AudioAnalyzeNoteFrequency, the file name would be analyze_notefreq.cpp, and the short default name in the design tool would be either "notefrequency" or just "notefreq".

Really, I'm open to suggestions here. I'm also note terribly worried about leaving it named AudioAnalyzeGuitarTuner. But after 1.27 releases, the name really needs to stay fixed.

duff
12-30-2015, 11:00 PM
I'm sold, AudioAnalyzeNoteFrequency works and go a head pick between the other two, I'm not sure if you want short or long name forms but both are clear to what they do to me.

PaulStoffregen
12-30-2015, 11:06 PM
not sure if you want short or long name forms but both are clear to what they do to me.

Every object has a default short name in the design tool. When you drag the object onto the canvas, it starts with that name plus an incrementing number appended.

Davidelvig
12-30-2015, 11:26 PM
How about AudioAnalyzeYIN.... or just YIN.
That said, there is a "market" for a lower and higher pitch version of this - differing only in their number of AUDIO_BLOCKS, and being slower-lower and faster-higher.
In the spirit of FFT256 and FFT1024, how about

AudioAnalyzeYINLow YINLow
AudioAnalyzeYINHigh YINHigh


The "YIN" part is what got me.

With time, a "adjustable" YIN would flex for speed and pitch range.

PaulStoffregen
12-30-2015, 11:39 PM
Generally I've been trying to go with human readable names. How widely known is "YIN"?

Davidelvig
12-30-2015, 11:50 PM
Not as well-know as FFT, but I've found YIN mentioned more than once in web searches related to pitch detection.
There's one source paper that I'm aware of, but I been guided to it from 2-3 tracks.
It's autocorrelation plus corrections...
Anyone else heard of Yin before this thread?

omjanger
12-31-2015, 07:45 AM
Then I would rather have AudioAnalyzeAutocorrelation instead of Yin. Autocorrelation is a known term in many fields. But I think your suggestion Paul is very good and describing in this case. Go for it!

Davidelvig
12-31-2015, 08:27 AM
One last thought before you choose an object name, Paul.
Because of my need for speed (rapid cycles) I've polished up my FFT frequency finder.
Now it's in sketch code. I may try to make a pluggable audio library object out of it later.
It will likely be a skosh less precise in frequency matching, and not have good freq finding below 100 Hz, though it is much quicker.
As an audio library object, it would be another flavor of tuner, freq finder, note finder.
(In the event that influences your naming)
I know these object names need to stick, once published.

Thanks, all, for the great discussion!

lychrel
12-31-2015, 10:35 PM
Oh my gosh! This is incredibly helpful; and I've never seen a forum response this quick or in-depth! This is quite the community.
I have my college applications due tomorrow, so I won't be able to answer a bunch of the questions that my original lack of detail warranted. I'll get back on here as soon as I'm not sweating a 24-hour deadline. Again, thanks so much, guys!

lychrel
01-01-2016, 12:50 AM
The key point is you absolutely must digitally filter the signal before you discard 21 of every 22 samples. The digital filtering is what causes every 22nd sample to properly represent your signal. If you just take every 22nd sample without filter filtering, you'll get absolutely horrible results (assuming your signal is common audio or other content with higher frequencies present).

If I have a single-order analog low-pass filter (at around 1 kHz) on the signal before I pass it to the Teensy, would that obviate using a digital filter, or would you recommend it anyway?

Davidelvig
01-01-2016, 06:25 AM
Is your application music?
What speed do you need between samples?
If you can tolerate 70 ms per sample, duff's guitar tuner would be simple and accurate, down to 20Hz.

How accurate are you wanting to be?
How low (in Hz) do you need to detect?

And Happy New Year!

PaulStoffregen
01-03-2016, 02:05 AM
If I have a single-order analog low-pass filter (at around 1 kHz) on the signal before I pass it to the Teensy, would that obviate using a digital filter

No. A single order filter isn't even close to what you'd need. Maybe a 6th or 8th order filter would be in the right range. Maybe...

lychrel
01-18-2016, 07:14 PM
Okay. I think I'll investigate both FFT decimation per Mr. Stoffregen's suggestion and using the guitar tuner code. I'll just have to see what works better. Again, thanks!

Wow. I had no idea a single order filter would be such a bad idea. I guess digital really is the way to go, then. I don't want to physically Make an eighth-order low-pass filter haha.

Davidelvig
01-19-2016, 04:01 AM
@lychrel, I don't know that I got a response...
How precise (in percent, or in Hz) do you need to be, and in what low and high range (Hz)?

lychrel
01-22-2016, 11:19 PM
@lychrel, I don't know that I got a response...
How precise (in percent, or in Hz) do you need to be, and in what low and high range (Hz)?

I'd like to be, essentially, as precise as possible. (Definitely more precise than 47-Hz bins, though I guess I could interpolate...)

My range is 0 to 1 kHz.

PaulStoffregen
01-23-2016, 01:27 AM
I'd like to be, essentially, as precise as possible.


Have you run the File > Examples > Audio > Analysis > NoteFrequency example yet? How about with the your own sounds instead of Duff's guitar?

lychrel
01-23-2016, 09:53 PM
Have you run the File > Examples > Audio > Analysis > NoteFrequency example yet? How about with the your own sounds instead of Duff's guitar?

Actually, I got stuck running it because of a "no serial port name defined" error. I trouble-shot from this (https://forum.pjrc.com/threads/31518-Can-t-communicate-with-Teensy-3-2-through-Teensyduino?p=88073&viewfull=1#post88073)post, and that got rid of the error, but I still couldn't get any serial output.

Davidelvig
02-15-2016, 05:24 AM
@lychrel, I process the fft1024 results (a set of 512 bins, each representing a 43Hz chunk of the spectrum.)
With sign-wave input, I can extract pitch within a percent or two.

Signal complexity and cleanliness will affect performance.

Notefreq delivers great results as well. Usually more accurate, and slower.

Still having trouble getting serial debugging working?

Are you on a Mac?

PaulStoffregen
02-15-2016, 02:33 PM
but I still couldn't get any serial output.

Teensy becomes a serial devices a few seconds *after* the upload completes.

Before you upload again, turn off the "auto" mode in the Teensy Loader window. Then press the button on your Teensy. You'll see the Teensy Loader will detect the board, but programming won't happen because auto mode is off. During this time, Teensy will *NOT* be a serial device.

While Teensy is in this mode, click Arduino's Tools > Ports menu. Make a mental note (or a screenshot) of the ports. Those are the ones which are *not* Teensy. Windows is the hardest, since they all look the same with only the number being different.

Then click Upload again. After Teensy Loader completes the programming and you see "reboot ok", wait a few seconds. Click the Tools > Ports menu. You should see a new port appear (on Macs, you sometimes get 2 of them, one with "tty" and one with "cu"... either works from Arduino, but some other software only works with the "cu" one). Select the new port that you know is Teensy. Then open the serial monitor.

dbaylies
08-05-2016, 09:06 PM
Hey everyone!

I'm trying to implement Paul's second suggestion in post #3 of this thread, except with a decimation of two. I've filtered the incoming data well enough, but I'm having trouble using the arm_cfft_radix4_q15() function in my sketch. I expect this has to do with my copying and pasting from .h/.cpp files without fully understanding the code. I hope someone can help me out!

Here's my arduino sketch:


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// Found a suggestion by Paul in a forum to do this
extern "C" {
#include <../Audio/utility/sqrt_integer.h>
#include <../Audio/utility/dspinst.h>
}

// Filter the incoming audio from the mic
and pass it to the queue (also, play it through the mic)
AudioInputI2S i2s1; //xy=83,98
AudioFilterBiquad biquad1; //xy=273,100
AudioRecordQueue queue1; //xy=448,100
AudioOutputI2S i2s2; //xy=449,189
AudioConnection patchCord1(i2s1, 0, biquad1, 0);
AudioConnection patchCord2(biquad1, queue1);
AudioConnection patchCord3(biquad1, 0, i2s2, 0);
AudioConnection patchCord4(biquad1, 0, i2s2, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=355,391



void setup() {
AudioMemory(60);

sgtl5000_1.enable(); // Enable the audio shield
sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
sgtl5000_1.volume(0.5);

// Butterworth filter, 12 db/octave
biquad1.setLowpass(0, 800, 0.707);

queue1.begin();

}

int i;
// initialize the buffer that will be passed to arm_cfft_radix4_q15()
int buffer2[1024];
// used for filling up buffer2
int count = 0;

void loop() {
if (queue1.available() >= 2) {
byte buffer[512];
// Fetch 2 blocks from the audio library and copy
// into a 512 byte buffer. The Arduino SD library
// is most efficient when full 512 byte sector size
// writes are used.
memcpy(buffer, queue1.readBuffer(), 256);
queue1.freeBuffer();
memcpy(buffer + 256, queue1.readBuffer(), 256);
queue1.freeBuffer();

// Copy every 2nd int from the buffer into new buffer
for (i = 0; i < 512; i++) {
// check if index is a multiple of 2
if ((i % 2) == 0) {
// if it is, copy the number into the new buffer, using new counting system
buffer2[count] = buffer[i];
count = count + 1;
}
}

// Once new buffer fills, run it through arm_cfft_radix4_q15()
if (count == 1024) {
arm_cfft_radix4_q15(&fft_inst, buffer);
for (int i=0; i < 512; i++) {
uint32_t tmp = *((uint32_t *)buffer + i); // real & imag
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
output[i] = sqrt_uint32_approx(magsq);
}
}
}
}

This yields the error:


BiquadFilter_ManualFFT_Test: In function 'void loop()':
BiquadFilter_ManualFFT_Test:69: error: 'fft_inst' was not declared in this scope
arm_cfft_radix4_q15(&fft_inst, buffer);
^
BiquadFilter_ManualFFT_Test:73: error: 'output' was not declared in this scope
output[i] = sqrt_uint32_approx(magsq);
^
'fft_inst' was not declared in this scope

What must I defined for "fft_inst" and "output" in order to avoid these errors and get my code to work? I've been unable to find the origin of "fft_inst" in the github Audio Library, or understand it's function. I've checked the CMSIS documentation (https://www.keil.com/pack/doc/CMSIS/DSP/html/group___complex_f_f_t.html#ga8d66cdac41b8bf6cefdb8 95456eee84a) but I've found little help there.

Should I simply initialize "output" with


int output[];

?

Thanks in advance!

David

DerekR
08-05-2016, 11:40 PM
I don't see any call to the initialization function
arm_cfft_radix4_init_q15 ( arm_cfft_radix4_instance_q15 * S, uint16_t fftLen, uint8_t ifftFlag, uint8_t bitReverseFlag )
to establish an instance of the the arm_cfft_radix4_q15 and set up the FFT parameters.

For example, in the class declaration in analyze_fft1024.h you will find in the initialization:
arm_cfft_radix4_init_q15(&fft_inst, 1024, 0, 1);
and then in the private declarations at the bottom:
arm_cfft_radix4_instance_q15 fft_inst;

You need to do something similar in your code:



blah
arm_cfft_radix4_instance_q15 myFFT; // declare myFFT
blah

void setup(){
blah
blah
arm_cfft_radix4_init_q15(&myFFT, 1024, 0, 1); // initialize
blah
}

void loop(){
blah
arm_cfft_radix4_q15(&myFFT, buffer); // do it to it
blah
}

DerekR
08-06-2016, 02:24 AM
To add to my previous reply, here is a development sketch I put together for test purposes last month. Note that it uses a lot of my own library functions (not included), but you should get the idea of initializing the arm_math ffts..


// Prototyping sketch for quadrature spectrum analyzer using complex "grabber"
// Creates a pair of quadrature sinusoids, grabs complex blocks of 256 samples, windows and uses
// arm_math fft, reorders the data, computes the log_2 of magsq, smooths the results with first-order IIR filter,
// and displays spectrum -22.05kHz to +22.05kHz on graphics board.
// Derek Rowell July 2016
//
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "grabber_complex_256.h"
#include "sdrMath.h"
const int myInput = AUDIO_INPUT_LINEIN;
//
AudioInputI2S audioInput;
AudioSynthWaveformSine I_input1; // quadrature sinusoid 1
AudioSynthWaveformSine Q_input1;
AudioSynthWaveformSine I_input2; // quadrature sinusoid 2
AudioSynthWaveformSine Q_input2;
AudioMixer4 mixer1;
AudioMixer4 mixer2;
AudioGrabberComplex256 myData;
//
AudioConnection c1(I_input1, 0, mixer1, 0);
AudioConnection c2(I_input2, 0, mixer1, 1);
AudioConnection c3(Q_input1, 0, mixer2, 0);
AudioConnection c4(Q_input2, 0, mixer2, 1);
AudioConnection c5(mixer1, 0, myData, 0);
AudioConnection c6(mixer2, 0, myData, 1);
AudioControlSGTL5000 audioShield;
//
arm_cfft_radix2_instance_q15 myFFT;
int16_t FFTbuffer[512];
int16_t log2Mag[256];

//----------------------------------------------------------------------------
void setup() {
Serial.begin(57600);
delay(2000);
audioShield.enable();
AudioMemory(12);
audioShield.volume(0.5);
audioShield.inputSelect(myInput);
//
uint16_t Frequency1 = 19000; float Amplitude1 = 0.6;
uint16_t Frequency2 = 18000; float Amplitude2 = 0.6/8.0; // log difference should be 3
AudioNoInterrupts(); // Set up a pair of quadrature sinusoids
mixer1.gain(0, 1.0); mixer1.gain(1, 1.0);
mixer2.gain(0, 1.0); mixer2.gain(1, 1.0);
I_input1.frequency(Frequency1); I_input1.amplitude(Amplitude1); I_input1.phase(45);
Q_input1.frequency(Frequency1); Q_input1.amplitude(Amplitude1); Q_input1.phase(315);
I_input2.frequency(Frequency2); I_input2.amplitude(Amplitude2); I_input2.phase(0);
Q_input2.frequency(Frequency2); Q_input2.amplitude(Amplitude2); Q_input2.phase(90);
AudioInterrupts();
//
arm_cfft_radix2_init_q15(&myFFT, 256, 0, 1); // Initialize the complex FFT function
windowComplex256_16(FFTbuffer, HAMMING); // Create a Hamming data window
}

//----------------------------------------------------------------------------------
void loop() {
myData.grab(FFTbuffer); // "grab" a 256 complex sample buffer
windowComplex256_16(FFTbuffer, APPLY); // apply a Hamming window function
arm_cfft_radix2_q15(&myFFT, FFTbuffer); // Use arm_math complex FFT
for (int i=0; i<256; i++) {
int16_t Re = FFTbuffer[2*i];
int16_t Im = FFTbuffer[2*i+1];
uint32_t magsq = (Re*Re + Im*Im)>>12;
log2Mag[i] = fastLog2_32(magsq)>>1; // log(x^2) = 2log(x)
}
FFTshift_16(log2Mag, 256); // Reorder the +ve and -ve frequencies
smoothVector(log2Mag, 256, (uint16_t)(0.8*16384));
displaySpectrum(log2Mag, 256);
delay(10);
}

dbaylies
08-08-2016, 12:31 AM
Thanks Derek, that's much appreciated.

I put in the two initializing functions, and the errors have disappeared. However, I'm still not getting anything out of my serial moniter!

Do I need to apply a window to the data before performing the fft, as is done in analyze_fft1024.cpp? I've thrown some lines of code from analyze_fft1024.cpp into mine, but the compiler doesn't recognize "window".

This error confuses me, as window is defined in analyze_fft1024.h, which is included in Audio.h. And for that matter, the fft initializations that you brought up are also included in analyze_fft1024.h!

What's going on here? Is analyze_fft1024.h really being included in my sketch?

Thanks again - I've included my current code and error message.


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// Found a suggestion by Paul in a forum to do this
extern "C" {
#include <../Audio/utility/sqrt_integer.h>
#include <../Audio/utility/dspinst.h>
}

// Basically: Filter the incoming audio and pass it to the queue (also, play it through the mic)
AudioInputI2S i2s1; //xy=83,98
AudioFilterBiquad biquad1; //xy=273,100
AudioRecordQueue queue1; //xy=448,100
AudioOutputI2S i2s2; //xy=449,189
AudioConnection patchCord1(i2s1, 0, biquad1, 0);
AudioConnection patchCord2(biquad1, queue1);
AudioConnection patchCord3(biquad1, 0, i2s2, 0);
AudioConnection patchCord4(biquad1, 0, i2s2, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=355,391


// Establish an instance of the fft function
arm_cfft_radix4_instance_q15 fft_inst;

// window function
static void apply_window_to_fft_buffer(void *buffer, const void *window)
{
int16_t *buf = (int16_t *)buffer;
const int16_t *win = (int16_t *)window;;
for (int i=0; i < 1024; i++) {
int32_t val = *buf * *win++;
// *buf = signed_saturate_rshift(val, 16, 15);
*buf = val >> 15;
buf += 2;
}
}

void setup() {
AudioMemory(60);

sgtl5000_1.enable(); // Enable the audio shield
sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
sgtl5000_1.volume(0.5);

// Butterworth filter, 12 db/octave
biquad1.setLowpass(0, 800, 0.707);

queue1.begin();

//define fft params
arm_cfft_radix4_init_q15(&fft_inst, 1024, 0, 1);

}

int i;
// initialize the buffer that will be passed to arm_cfft_radix4_q15()
short int buffer2[1024];
// initialize output vector for fft results
int output[512];
// float for storing and printing individual fft results
float n;

void loop() {
// used for filling up buffer2
int count = 0;
if (queue1.available() >= 2) {
byte buffer[512];
// Fetch 2 blocks from the audio library and copy
// into a 512 byte buffer. The Arduino SD library
// is most efficient when full 512 byte sector size
// writes are used.
memcpy(buffer, queue1.readBuffer(), 256);
queue1.freeBuffer();
memcpy(buffer + 256, queue1.readBuffer(), 256);
queue1.freeBuffer();
// Copy every 2nd int from the buffer into new buffer
for (i = 0; i < 512; i++) {
// check if index is a multiple of 2
if ((i % 2) == 0) {
// if it is, copy the number into the new buffer, using new counting system
buffer2[count] = buffer[i];
count = count + 1;
}
}
// Once new buffer fills, run it through arm_cfft_radix4_q15()
if (count == 1024) {

// appy fft window
if (window) apply_window_to_fft_buffer(&fft_inst, window);

// perform fft operation
arm_cfft_radix4_q15(&fft_inst, buffer2);
for (i = 0; i < 512; i++) {
uint32_t tmp = *((uint32_t *)buffer2 + i); // real & imag
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
output[i] = sqrt_uint32_approx(magsq);
}
// Print FFT output
Serial.print("FFT: ");
for (i = 0; i < 512; i++) {
n = output[i];
if (n >= 0.01) {
Serial.print(n);
Serial.print(" ");
} else {
Serial.print(" - "); // don't print "0.00"
}
}
Serial.println();
}
}
}


BiquadFilter_ManualFFT_Test: In function 'void loop()':
BiquadFilter_ManualFFT_Test:91: error: 'window' was not declared in this scope
if (window) apply_window_to_fft_buffer(&fft_inst, window);
^
'window' was not declared in this scope

DerekR
08-08-2016, 02:20 PM
I'm sitting on my boat, with a small screen laptop, so it's a bit awkward to do a detailed analysis of your code. Nevertheless,I can see a number of issues:
1) You do not define a window function anywhere. Which function do you want to use : Hanning, Hamming, etc? And where is it defined? That's why you are getting the error message. Go back to the .h and .cpp files to see how they are defined.
You do not need a window function - the fft function works perfectly well without it - suggest you debug without one. Are you familiar with the purpose (and compromise) of windowing to minimize spectral leakage, and the rational for choosing one function over another. I usually just use a Hamming window unless I have a reason to choose another.

2) For a 1024 length FFT you need 8 data blocks (8x128 samples) in a buffer of length 2048. I don't see anywhere that you are doing this.

3) I don't understand your code to insert your data into the even-odd structure of the input fft buffer. There is a much simpler way to do it. It doesn't seem to work as I read it, and it seems to be the wrong length. How about something like:
for (int i = 0; i<1024; i++){
buffer[2*i] = sample[i];
buffer[2*i+1] = 0;
}

4) The line
AudioConnection patchCord2(biquad1, queue1);
needs input/output specification
AudioConnection patchCord2(biquad1, 0, queue1, 0);



Do you need a length 1024 FFT? Do you have a reason for this data resolution?

It seems that your sketch does exactly the same thing as the (working) prototype sketch in my previous post - what are you going to to with the FFT when you get it?

DerekR
08-09-2016, 01:36 AM
Now that I'm home, I remembered that you are trying to do decimation by two before the 1024 FFT. But this means you will need 16 audio blocks per FFT, because now you will only get 64 samples/block! You will still need a length 2048 buffer for the FFT data.

Aside: I am currently writing decimation by 2 and 4 audio library functions, with 8th-order Chebyshev II filters, and which will transmit partially filled blocks, for processor load reduction in communication quality speech processing (<5kHz).

Davidelvig
08-09-2016, 04:26 AM
I'll test these FFT-plus-decimation objects when you have them. I'll be interested to see the differences in freq defection accuracy and speed. DerekR, will you continue the reuse-the-last-half-of-the-buffer approach? It should theoretically be twice as precise and half as fast.

Nitnelav
08-09-2016, 12:50 PM
Hi,
I'm writing a FFT class with a .decimateSignal(int M) function too (M can be 2, 4, 8, 16 ,32) with cascaded arm_fir_decimate_q15 calls for the anti-aliases filters...
Maybe we should share !

DerekR
08-09-2016, 02:27 PM
Suffering from congenital laziness, what I am working on is simply to produce a modified version the biquad filter audio function/class which has a set of fixed Chebyshev coefficients (derived from MATLAB's fdatool) and to decimate the output block before transmitting it as a partially filled block. (I personally don't have any use for decimation by more than 4, because the samples per block becomes way too small :-) )

My application is time-domain processing, not FFT analysis, so I'm also looking at interpolation filters in case I need to reconstitute the original sampling rate.

I am currently questioning whether the processing overhead in the decimation process is justified by any potential saving in processing time in subsequent filtering operations.

Nitnelav
08-09-2016, 02:59 PM
Ok, I'll start a thread with a proof of concept and maybe we can build on that ?
I just have a bit of cleaning to do...

[edit]
... done here : https://forum.pjrc.com/threads/35951-FFT256-with-signal-decimation?p=111886#post111886

dbaylies
08-25-2016, 07:20 PM
Thanks again for the help, I've successfully decimated now.

For posterity, the issue was solely an error in buffer sizing. I just talked myself through the number of bytes in each buffer and how those buffers are used, and the problem became apparent.

Working on getting better pitch detection now, as well as how to deal with the 50ms delay that comes with the decimation. This is too high a delay for live musical applications.

DerekR
08-25-2016, 09:23 PM
Take a look at my latest thread "A New Accurate FFT Interpolator for Frequency Estimation" (https://forum.pjrc.com/threads/36358-A-New-Accurate-FFT-Interpolator-for-Frequency-Estimation?p=113231#post113231http://) that could well eliminate your need for decimation. It gives accurate sub-Hz results.

dbaylies
08-25-2016, 09:32 PM
DerekR, that looks very useful! And I like the "eureka!" tone of your post, even if you did reinvent the wheel. :)

I'll parse your work ASAP.

Jake
08-26-2016, 03:53 PM
Just getting back to fft algorithms, as a long time user of the FFT, but having never coded the algorithm, it seems the code of choice is FFTW downloadable at http://www.fftw.org/. This is the routine used in matlab and octave and is open source. It allows an arbitrary number of points in the FFT, and as long as the number is factorable, there is a speed increase as compared to the DFT. The more factorable, the better. Power of 2 is obviously the fastest. My guess is that the code is too big for the teensy and thus the traditional power of 2 algorithm is better for memory usage. Paul has probably gone through all these trade-offs prior to coding the audio library.

bmillier
10-02-2016, 07:51 PM
I'd like to use this as a guitar tuner as part of a bigger project (on T3.2) . That is, I would only want the notefreq routine to run when I was using the tuner function, and not taking up any processor time otherwise. Ideally, that would be a notefreq.end() function. I tried gating the notefreq input using a mixer, but when I set mixer gain =0, the YIN routine is still running and using 80% of CPU time (comparable to when it is actually measuring an actual guitar signal).
Is there any way of doing this?
Thanks.

duff
10-02-2016, 08:08 PM
I'd like to use this as a guitar tuner as part of a bigger project (on T3.2) . That is, I would only want the notefreq routine to run when I was using the tuner function, and not taking up any processor time otherwise. Ideally, that would be a notefreq.end() function. I tried gating the notefreq input using a mixer, but when I set mixer gain =0, the YIN routine is still running and using 80% of CPU time (comparable to when it is actually measuring an actual guitar signal).
Is there any way of doing this?
Thanks.
I have made a tuner using the noteFreq and it works good but your right about the cpu usage being too high. If your comfortable with editng the source files you can do this to get the cpu usage down:

In analyze_notefreq.cpp edit line 107, 113, 119 125 too:


x += 16;

bmillier
10-02-2016, 08:16 PM
Thanks for the response. I don't mind that the CPU usage is high when I am actually using it, as I want the best accuracy. But, I would like to "turn it off" the rest of the time (i.e. when I wasn't actually tuning the guitar which is most of the time) . I don't think your suggestion addresses this, am I correct?

duff
10-02-2016, 08:24 PM
No, there is no way to "turn it off" per se, so yes you are correct. I don't think any of the Audio Objects have an end or stop function, maybe they do? I think Paul said something about in a future version he will implement a way to add and subtract Audio Objects in real time but when I don't know. If you study the how these Audio Objects work you could probably implement your "end" function yourself though.

bmillier
10-02-2016, 09:58 PM
I was afraid of this. From what I've done so far, the functions that have both input and output have to be able to pass the data through regardless of whether the function is needed or not. In the case of the flange routine, (which I just corrected an error in and got it working properly) you can pass it "0" parameters, in which case it quickly executes the function without doing much processing. I was hoping for something like this in the notefreq routine, especially since it doesn't have to pass thru any data, just crunch some numbers and return a single result. I guess I'll check out the code and see if I can modify it so that it can exit without doing anything, dependant upon the value of a variable. Off the top of my head, maybe setting the threshold variable to "0" might be a good place to start.
Thanks for your comments/time.

bmillier
10-12-2016, 05:51 PM
@duff I figured out an easy way to disable the notefreq function when not needed, and I also used your suggestion re stepping through the data at 16 sample increments instead of 4. That reduced CPU from 80% to 40%. This, along with figuring out how to disable the other effects I was using, allow me to either 1) run the guitar tuner or 2) run all of the other effects, the rest of the time.
Thanks for the 4 -> 16 tip.

duff
10-13-2016, 01:41 AM
@duff I figured out an easy way to disable the notefreq function when not needed, and I also used your suggestion re stepping through the data at 16 sample increments instead of 4. That reduced CPU from 80% to 40%. This, along with figuring out how to disable the other effects I was using, allow me to either 1) run the guitar tuner or 2) run all of the other effects, the rest of the time.
Thanks for the 4 -> 16 tip.
Good to hear!:D

Wicky21
09-06-2017, 05:32 PM
Hello Duffy,

I'm going to analyze the fundamental frequency of an sound track. I have two .wav files which have attached with the Arduino code. one .wav has low level sound pitch and other has higher level of sound pitch. But both tracks were same with two levels of sound pitches.
But my problem it will perfectly identify fundamental frequency of the high level sound pitch track. But for the same low level sound pitch track it won't give any output. Any solutions?
(Analyze fundamental around 350Hz-500Hz. Not interested about below 300Hz )

Davidelvig
09-07-2017, 02:05 AM
I loaded your two WAV files onto Audacity. If you don't have it, I'd say get it. It's free.
Here is the Audacity FFT analysis on a section of each of your two waves:
1148611487.
FE55LW is first, then FE55M.

These screen shots are for 1024-sample segment of the wav, using the menu item Analyze\Plot Spectrum...
You need to highlight a 1024-sample interval first.
Note the "Peak" field below the second graph is not the fundamental. I moved the mouse before I captured the screen.

With Teensy, you can detect pitches pretty accurately in this pitch range with the AudioAnalyzeFFT1024 object, and then some post processing on the resulting bin contents - finding the peaks. I use a method that looks for positive to negative slope changes - above a minimum value, then correct for peak asymmetry.

The fundamental is pretty clear visually in these samples, though it's a pretty rich set of harmonics (and it sounds like it when played... like a hiss.)

By the way, what are these sounds?

Wicky21
09-21-2017, 06:10 AM
Somebody please tell me in Teensy Audio library "analyze_fft1024.cpp", What is the output of the below lines?


for (int i=0; i < 512; i++) {
uint32_t tmp = *((uint32_t *)buffer + i); // real & imag
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
output[i] = sqrt_uint32_approx(magsq);
}

uint32_t tmp = return Complex value of the particular bin?
uint32_t magsq = ? (no idea)
output[i] = return the absolute value ???

And i need to know in MATLAB fft() function returns a complex value. Is that similar to "uint32_t tmp" variable ?

DerekR
09-22-2017, 03:10 PM
It simply is computing the magnitude of the FFT. All FFT's are a relationship between "complex" mathematical time and frequency domains. That is they require complex numbers for the time signal and the spectrum output. There are FFTs, such as the Teensy's, that appear take real data in and out, but they just hide the actual complex code underneath. The code you are looking at is converting the complex spectrum to a magnitude spectrum:
mag = sqrt(real*real + imag*imag)

In the code example "tmp" is not a variable. If you read the code, "tmp" is a POINTER to a uint32_t variable, but it gets even better: its a pointer to a pair of int16_t variables ( the real and imaginary parts of the FFT), which have been stored in memory in adjacent pairs as per the arm_math complex number storage convention, allowing the maq squared value to be computed in a single instruction. Fantastic!!!

The multiply_16tx16t_add_16bx16b() instruction is described in Paul's /utility/dspinst.h in the audio library, BUT don't even think about playing with this stuff unless you REALLY, REALLY understand what you are doing. Failure is guaranteed :D

And of course MATLAB returns complex values - variables in MATLAB are all implicitly both complex and matrices/vectors. That's the beauty of it! When you say y = fft(x), then y is a complex vector of the same size of x. And you could simply say mag = abs(fft(x)) to get a real vector of magnitudes.

Wicky21
09-23-2017, 12:08 PM
Derek Good explanation as always.
So "uint32_t magsq" is that output value similar to MATLAB "y"(y=fft(x)) complex vector one element? I need to know from where each bin complex value can be find? Also sqrt_uint32_approx(magsq) output, is that similar to MATLAB mag(mag = abs(fft(x))) value???

Davidelvig
09-28-2017, 01:22 AM
@Wicky21
I've run your 2 wave files through my FFTFreq object with the sketch file as follows:

#include <Audio.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputAnalog adc1; //xy=91,270
AudioPlaySdWav playSdWav1; //xy=107,321
AudioMixer4 mixer1; //xy=293,333
AudioAnalyzeFFT1024 FFT; //xy=465,331
AudioConnection patchCord1(playSdWav1, 0, mixer1, 0);
AudioConnection patchCord2(playSdWav1, 1, mixer1, 1);
AudioConnection patchCord3(mixer1, FFT);
// GUItool: end automatically generated code

#include <FFTFreq.h>
FFTFreq _fft(&FFT);

void setup() {
Serial.begin(9600);
delay(500);
Serial.println("Started");
AudioMemory(8);
mixer1.gain(0,25);
mixer1.gain(1,25);
if (!(SD.begin(BUILTIN_SDCARD))) {
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
}

float lastFreq = 0;
unsigned long loopCount;
String fileName1 = "FE55M.wav";
String fileName2 = "FE55LW.wav";
bool file1 = true;
void loop() {
String fn = file1 ? fileName1 : fileName2;
if (!playSdWav1.isPlaying()) {
Serial.printf("\n************************************************ **\nFinished %s\tDelaying 1 second...\n", fn.c_str());
delay (1000);
fn = file1 ? fileName1 : fileName2;
playSdWav1.play(fn.c_str());
Serial.printf("Starting %s\tDelaying 200ms...\n**************************************** **********", fn.c_str());
delay(200);
file1 = !file1;
}

float freq = _fft.getFFTFreq();
float prob = _fft.getFFTProb();
int freqScalingFactor = 4;
if ((freq > 0) && (freq != lastFreq)) {
Serial.printf("\n%s at %dms\tLoop:%5d\t%4.1fHz\t%3d%s\t", fn.c_str(), playSdWav1.positionMillis(), loopCount % 100000, freq, (int)(prob*100), "%");
int count = freq/freqScalingFactor;
int countUp = 0;
while (count--) {
if ((countUp % (100/freqScalingFactor)) == 0) Serial.print(countUp * freqScalingFactor);
else Serial.print('_');
countUp++;
}
if ((prob == 1.0) && (playSdWav1.positionMillis() > 1000) && (playSdWav1.positionMillis() < 1100)) {
_fft.graphFFTBins(true);
delay(100);
}
lastFreq = freq;
}
loopCount++;
}

I did not include the code referenced in FFTFreq.h... it extracts fundamental frequency from an FFT bin-set as fast as possible, by finding peaks (changes in bin-to-bin slope from + to -) above a certain minimum threshold.

The output of this sketch on a Teensy 3.6 with uSD card holding your files is as follows:

Started

**************************************************
Finished FE55M.wav Delaying 1 second...
Starting FE55M.wav Delaying 200ms...
**************************************************
FE55M.wav at 200ms Loop: 0 347.1Hz 100% 0________________________100______________________ __200________________________300__________
FE55LW.wav at 232ms Loop: 7037 321.8Hz 85% 0________________________100______________________ __200________________________300____
FE55LW.wav at 255ms Loop:11990 104.0Hz 85% 0________________________100
FE55LW.wav at 266ms Loop:14462 236.6Hz 100% 0________________________100______________________ __200________
FE55LW.wav at 290ms Loop:19512 318.8Hz 85% 0________________________100______________________ __200________________________300___
FE55LW.wav at 313ms Loop:24462 301.6Hz 100% 0________________________100______________________ __200________________________
FE55LW.wav at 336ms Loop:29421 215.0Hz 100% 0________________________100______________________ __200__
FE55LW.wav at 348ms Loop:31913 104.5Hz 85% 0________________________100
FE55LW.wav at 359ms Loop:34358 212.5Hz 100% 0________________________100______________________ __200__
FE55LW.wav at 382ms Loop:39079 109.4Hz 85% 0________________________100_
FE55LW.wav at 406ms Loop:44003 111.7Hz 100% 0________________________100_
FE55LW.wav at 417ms Loop:46447 278.9Hz 100% 0________________________100______________________ __200__________________
FE55LW.wav at 452ms Loop:53954 121.5Hz 87% 0________________________100____
FE55LW.wav at 464ms Loop:56389 324.1Hz 85% 0________________________100______________________ __200________________________300_____
FE55LW.wav at 522ms Loop:68764 313.5Hz 85% 0________________________100______________________ __200________________________300__
FE55LW.wav at 591ms Loop:83571 417.2Hz 88% 0________________________100______________________ __200________________________300__________________ ______400___
FE55LW.wav at 603ms Loop:85972 127.0Hz 87% 0________________________100_____
FE55LW.wav at 638ms Loop:93451 311.7Hz 85% 0________________________100______________________ __200________________________300_
FE55LW.wav at 673ms Loop: 833 180.4Hz 100% 0________________________100___________________
FE55LW.wav at 707ms Loop: 8412 103.5Hz 100% 0________________________
FE55LW.wav at 777ms Loop:23394 102.3Hz 85% 0________________________
FE55LW.wav at 789ms Loop:25858 338.8Hz 85% 0________________________100______________________ __200________________________300________
FE55LW.wav at 800ms Loop:28302 147.1Hz 80% 0________________________100__________
FE55LW.wav at 835ms Loop:35718 119.7Hz 87% 0________________________100___
FE55LW.wav at 847ms Loop:38150 297.4Hz 100% 0________________________100______________________ __200_______________________
FE55LW.wav at 870ms Loop:43186 103.8Hz 85% 0________________________
FE55LW.wav at 893ms Loop:48179 113.7Hz 100% 0________________________100__
FE55LW.wav at 974ms Loop:65919 186.9Hz 100% 0________________________100____________________
FE55LW.wav at 998ms Loop:70869 137.5Hz 100% 0________________________100________
FE55LW.wav at 1090ms Loop:90985 119.4Hz 100% 0________________________100___


*
* *
* * *
* * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3 2 5 4 3 9 7 5 10 9 14 16 7 9 9 3 13 9 2 10 19 7 7 8 16 14 8 5 5 4 22 26 8 3 8 5 4 4 8 10 9 3 3 3 4 6 4 3 2 2 2 2 2 3 4 2 2 2 2 3 3 2 2 2 2 3 4 4 3 2 3 3 2 2 2 2 3 2
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 43 86 129 172 215 258 301 344 387 431 474 517 560 603 646 689 732 775 818 862 905 948 991 1034 1077 1120 1163 1206 1249 1293 1336 1379 1422 1465 1508 1551 1594 1637 1680 1724 1767 1810 1853 1896 1939 1982 2025 2068 2111 2155 2198 2241 2284 2327 2370 2413 2456 2499 2542 2586 2629 2672 2715 2758 2801 2844 2887 2930 2973 3017 3060 3103 3146 3189 3232 3275 3318 3361 3404 3448 3491 3534 3577 3620 3663 3706 3749 3792 3835 3878 3922 3965 4008 4051 4094 4137 4180 4223 4266 4310 4353 4396 4439 4482 4525 4568 4611 4654 4697 4741 4784 4827 4870 4913 4956 4999 5042 5085 5128 5172 5215 5258 5301 5344 5387 5430 5473 5516 5559 5603 5646 5689 5732 5775 5818 5861 5904 5947 5990 6034 6077 6120 6163 6206 6249 6292 6335 6378 6421 6465 6508 6551 6594 6637 6680 6723 6766 6809 6852 6896 6939 6982 7025 7068 7111 7154 7197 7240 7283 7326 7370 7413 7456 7499 7542 7585 7628 7671 7714 7757 7801 7844 7887 7930 7973 8016 8059 8102 8145 8188 8232 8275 8318 8361 8404 8447 8490 8533 8576 8620 8663 8706 8749 8792 8835 8878 8921 8964 9007 9051 9094 9137 9180 9223 9266 9309 9352 9395 9438 9482 9525 9568 9611 9654 9697 9740 9783 9826 9869 9913 9956 9999 10042 10085 10128 10171 10214 10257 10300 10344 10387 10430 10473 10516 10559 10602 10645 10688 10731 10775 10818 10861 10904 10947 10990 11033 11076 11119 11162 11206 11249 11292 11335 11378 11421 11464 11507 11550 11593 11637 11680 11723 11766 11809 11852 11895 11938 11981 12024 12068 12111 12154 12197 12240 12283 12326 12369 12412 12455 12499 12542 12585 12628 12671 12714 12757 12800 12843 12886 12930 12973 13016 13059 13102 13145 13188 13231 13274 13317 13361 13404 13447 13490 13533 13576 13619 13662 13705 13748 13792 13835 13878 13921 13964 14007 14050 14093 14136 14179 14222 14266 14309 14352 14395 14438 14481 14524 14567 14610 14653 14697 14740 14783 14826 14869 14912 14955 14998 15041 15084 15128 15171 15214 15257 15300 15343 15386 15429 15472 15515 15559 15602 15645 15688 15731 15774 15817 15860 15903 15946 15990 16033 16076 16119 16162 16205 16248 16291 16334 16377 16421 16464 16507 16550 16593 16636 16679 16722 16765 16809 16852 16895 16938 16981 17024 17067 17110 17153 17196 17240 17283 17326 17369 17412 17455 17498 17541 17584 17627 17671 17714 17757 17800 17843 17886 17929 17972 18015 18058 18102 18145 18188 18231 18274 18317 18360 18403 18446 18489 18533 18576 18619 18662 18705 18748 18791 18834 18877 18920 18964 19007 19050 19093 19136 19179 19222 19265 19308 19351 19395 19438 19481 19524 19567 19610 19653 19696 19739 19782 19826 19869 19912 19955 19998 20041 20084 20127 20170 20213 20257 20300 20343 20386 20429 20472 20515 20558 20601 20644 20688 20731 20774 20817 20860 20903 20946 20989 21032 21075 21119 21162 21205 21248 21291 21334 21377 21420 21463 21506 21550 21593 21636 21679 21722 21765 21808 21851 21894 21937
0 3 2 5 4 1 1 3 9 7 5 10 9 14 16 7 9 9 3 13 9 2 10 19 7 7 8 16 14 8 5 5 0 4 1 22 26 8 3 8 5 4 4 8 10 9 3 1 3 3 1 4 6 4 3 1 2 2 2 1 2 2 3 4 1 1 1 0 1 1 1 1 2 2 2 2 1 3 3 2 2 2 2 0 1 3 4 4 3 1 1 2 0 3 3 2 0 1 1 0 2 2 2 3 1 1 1 0 1 1 0 1 0 0 1 0 2 1 1 1 0 1 1 0 0 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
2.769864 8.434231 11.386441

FE55LW.wav at 1404ms Loop:27645 101.1Hz 100% 0________________________
FE55LW.wav at 1462ms Loop:40156 114.8Hz 85% 0________________________100__
FE55LW.wav at 1659ms Loop:82226 384.4Hz 87% 0________________________100______________________ __200________________________300__________________ __
FE55LW.wav at 1775ms Loop: 7486 394.2Hz 88% 0________________________100______________________ __200________________________300__________________ ____
FE55LW.wav at 1856ms Loop:24720 177.0Hz 100% 0________________________100__________________
FE55LW.wav at 1938ms Loop:42339 107.8Hz 100% 0________________________100
FE55LW.wav at 1949ms Loop:44801 129.0Hz 100% 0________________________100______
FE55LW.wav at 1961ms Loop:47306 276.0Hz 100% 0________________________100______________________ __200_________________
FE55LW.wav at 1984ms Loop:52334 110.3Hz 100% 0________________________100_
FE55LW.wav at 2030ms Loop:62199 299.7Hz 100% 0________________________100______________________ __200_______________________
FE55LW.wav at 2228ms Loop: 4964 110.4Hz 85% 0________________________100_
FE55LW.wav at 2251ms Loop: 9945 238.5Hz 100% 0________________________100______________________ __200________
FE55LW.wav at 2309ms Loop:22457 184.0Hz 100% 0________________________100____________________
FE55LW.wav at 2344ms Loop:30013 221.2Hz 100% 0________________________100______________________ __200____
FE55LW.wav at 2402ms Loop:42355 274.8Hz 100% 0________________________100______________________ __200_________________
FE55LW.wav at 2460ms Loop:54742 175.6Hz 100% 0________________________100_________________
FE55LW.wav at 2622ms Loop:89765 133.5Hz 100% 0________________________100_______
FE55LW.wav at 2785ms Loop:24641 321.5Hz 85% 0________________________100______________________ __200________________________300____
FE55LW.wav at 2808ms Loop:29646 134.8Hz 100% 0________________________100_______
FE55LW.wav at 2831ms Loop:34693 110.1Hz 85% 0________________________100_
FE55LW.wav at 2866ms Loop:42093 108.2Hz 100% 0________________________100_
FE55LW.wav at 2959ms Loop:61842 180.3Hz 100% 0________________________100___________________
FE55LW.wav at 2982ms Loop:66794 104.6Hz 85% 0________________________100
FE55LW.wav at 3005ms Loop:71777 279.2Hz 100% 0________________________100______________________ __200__________________
FE55LW.wav at 3052ms Loop:81643 277.6Hz 100% 0________________________100______________________ __200__________________
FE55LW.wav at 3075ms Loop:86594 277.5Hz 100% 0________________________100______________________ __200__________________
FE55LW.wav at 3098ms Loop:91617 297.7Hz 100% 0________________________100______________________ __200_______________________
FE55LW.wav at 3191ms Loop:11406 384.3Hz 87% 0________________________100______________________ __200________________________300__________________ __
FE55LW.wav at 3203ms Loop:13826 229.4Hz 100% 0________________________100______________________ __200______
FE55LW.wav at 3214ms Loop:16331 306.6Hz 85% 0________________________100______________________ __200________________________300
FE55LW.wav at 3342ms Loop:43419 103.1Hz 100% 0________________________
FE55LW.wav at 3411ms Loop:58589 104.5Hz 100% 0________________________100
FE55LW.wav at 3516ms Loop:81172 189.1Hz 100% 0________________________100_____________________
FE55LW.wav at 3528ms Loop:83672 117.1Hz 87% 0________________________100___
FE55LW.wav at 3667ms Loop:13524 266.7Hz 100% 0________________________100______________________ __200_______________
FE55LW.wav at 3690ms Loop:18430 272.7Hz 100% 0________________________100______________________ __200_________________
FE55LW.wav at 3702ms Loop:20925 337.6Hz 85% 0________________________100______________________ __200________________________300________
FE55LW.wav at 3818ms Loop:45944 104.8Hz 85% 0________________________100
**************************************************
Finished FE55LW.wav Delaying 1 second...
Starting FE55LW.wav Delaying 200ms...
**************************************************
FE55M.wav at 235ms Loop: 2799 215.5Hz 100% 0________________________100______________________ __200__
FE55M.wav at 246ms Loop: 5329 258.5Hz 100% 0________________________100______________________ __200_____________
FE55M.wav at 269ms Loop:10422 331.0Hz 85% 0________________________100______________________ __200________________________300______
FE55M.wav at 293ms Loop:15477 113.8Hz 100% 0________________________100__
FE55M.wav at 304ms Loop:18011 274.6Hz 100% 0________________________100______________________ __200_________________
FE55M.wav at 327ms Loop:23104 139.7Hz 100% 0________________________100________
FE55M.wav at 432ms Loop:45550 282.5Hz 100% 0________________________100______________________ __200___________________
FE55M.wav at 478ms Loop:55678 104.3Hz 85% 0________________________100
FE55M.wav at 490ms Loop:58156 190.8Hz 100% 0________________________100_____________________
FE55M.wav at 548ms Loop:70577 220.0Hz 100% 0________________________100______________________ __200___
FE55M.wav at 559ms Loop:73106 277.0Hz 100% 0________________________100______________________ __200__________________
FE55M.wav at 571ms Loop:75634 130.5Hz 100% 0________________________100______
FE55M.wav at 606ms Loop:83192 322.9Hz 85% 0________________________100______________________ __200________________________300____
FE55M.wav at 815ms Loop:28279 110.8Hz 85% 0________________________100_
FE55M.wav at 826ms Loop:30752 121.5Hz 87% 0________________________100____
FE55M.wav at 884ms Loop:43279 178.9Hz 100% 0________________________100__________________
FE55M.wav at 896ms Loop:45797 109.7Hz 85% 0________________________100_
FE55M.wav at 931ms Loop:53303 337.6Hz 100% 0________________________100______________________ __200________________________300________
FE55M.wav at 966ms Loop:60742 135.4Hz 100% 0________________________100_______
FE55M.wav at 1058ms Loop:80996 245.1Hz 100% 0________________________100______________________ __200__________




* * *
* * * * *
* * * * * * * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 2 3 5 3 9 12 4 13 18 13 9 7 16 7 11 18 11 6 13 6 7 9 8 7 6 13 12 4 4 3 7 9 10 14 16 7 6 13 14 3 7 5 6 2 2 3 3 2 2 2 2 2 3 3 2 2 2 2 3 2 2 2 2 2 2 2
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 43 86 129 172 215 258 301 344 387 431 474 517 560 603 646 689 732 775 818 862 905 948 991 1034 1077 1120 1163 1206 1249 1293 1336 1379 1422 1465 1508 1551 1594 1637 1680 1724 1767 1810 1853 1896 1939 1982 2025 2068 2111 2155 2198 2241 2284 2327 2370 2413 2456 2499 2542 2586 2629 2672 2715 2758 2801 2844 2887 2930 2973 3017 3060 3103 3146 3189 3232 3275 3318 3361 3404 3448 3491 3534 3577 3620 3663 3706 3749 3792 3835 3878 3922 3965 4008 4051 4094 4137 4180 4223 4266 4310 4353 4396 4439 4482 4525 4568 4611 4654 4697 4741 4784 4827 4870 4913 4956 4999 5042 5085 5128 5172 5215 5258 5301 5344 5387 5430 5473 5516 5559 5603 5646 5689 5732 5775 5818 5861 5904 5947 5990 6034 6077 6120 6163 6206 6249 6292 6335 6378 6421 6465 6508 6551 6594 6637 6680 6723 6766 6809 6852 6896 6939 6982 7025 7068 7111 7154 7197 7240 7283 7326 7370 7413 7456 7499 7542 7585 7628 7671 7714 7757 7801 7844 7887 7930 7973 8016 8059 8102 8145 8188 8232 8275 8318 8361 8404 8447 8490 8533 8576 8620 8663 8706 8749 8792 8835 8878 8921 8964 9007 9051 9094 9137 9180 9223 9266 9309 9352 9395 9438 9482 9525 9568 9611 9654 9697 9740 9783 9826 9869 9913 9956 9999 10042 10085 10128 10171 10214 10257 10300 10344 10387 10430 10473 10516 10559 10602 10645 10688 10731 10775 10818 10861 10904 10947 10990 11033 11076 11119 11162 11206 11249 11292 11335 11378 11421 11464 11507 11550 11593 11637 11680 11723 11766 11809 11852 11895 11938 11981 12024 12068 12111 12154 12197 12240 12283 12326 12369 12412 12455 12499 12542 12585 12628 12671 12714 12757 12800 12843 12886 12930 12973 13016 13059 13102 13145 13188 13231 13274 13317 13361 13404 13447 13490 13533 13576 13619 13662 13705 13748 13792 13835 13878 13921 13964 14007 14050 14093 14136 14179 14222 14266 14309 14352 14395 14438 14481 14524 14567 14610 14653 14697 14740 14783 14826 14869 14912 14955 14998 15041 15084 15128 15171 15214 15257 15300 15343 15386 15429 15472 15515 15559 15602 15645 15688 15731 15774 15817 15860 15903 15946 15990 16033 16076 16119 16162 16205 16248 16291 16334 16377 16421 16464 16507 16550 16593 16636 16679 16722 16765 16809 16852 16895 16938 16981 17024 17067 17110 17153 17196 17240 17283 17326 17369 17412 17455 17498 17541 17584 17627 17671 17714 17757 17800 17843 17886 17929 17972 18015 18058 18102 18145 18188 18231 18274 18317 18360 18403 18446 18489 18533 18576 18619 18662 18705 18748 18791 18834 18877 18920 18964 19007 19050 19093 19136 19179 19222 19265 19308 19351 19395 19438 19481 19524 19567 19610 19653 19696 19739 19782 19826 19869 19912 19955 19998 20041 20084 20127 20170 20213 20257 20300 20343 20386 20429 20472 20515 20558 20601 20644 20688 20731 20774 20817 20860 20903 20946 20989 21032 21075 21119 21162 21205 21248 21291 21334 21377 21420 21463 21506 21550 21593 21636 21679 21722 21765 21808 21851 21894 21937
0 2 2 1 3 5 3 9 12 4 13 18 13 9 7 16 7 11 18 11 6 13 6 7 9 8 7 6 13 12 4 4 3 7 9 10 14 16 7 6 13 14 3 7 5 6 2 2 1 3 3 2 1 2 2 0 1 2 2 1 3 3 1 0 0 1 1 2 2 2 2 3 2 0 1 0 1 2 1 0 0 1 1 1 1 1 2 2 0 2 2 1 0 0 1 1 1 1 1 2 0 1 1 0 1 0 1 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
5.686398 8.299888 11.204308

FE55M.wav at 1233ms Loop:88020 275.2Hz 100% 0________________________100______________________ __200_________________
FE55M.wav at 1372ms Loop:18120 180.7Hz 100% 0________________________100___________________
FE55M.wav at 1383ms Loop:20651 241.1Hz 100% 0________________________100______________________ __200_________
FE55M.wav at 1465ms Loop:38259 213.8Hz 100% 0________________________100______________________ __200__
FE55M.wav at 1476ms Loop:40781 105.7Hz 85% 0________________________100
FE55M.wav at 1488ms Loop:43263 105.0Hz 100% 0________________________100
FE55M.wav at 1499ms Loop:45795 112.3Hz 85% 0________________________100__
FE55M.wav at 1523ms Loop:50788 221.8Hz 100% 0________________________100______________________ __200____
FE55M.wav at 1592ms Loop:65882 243.5Hz 100% 0________________________100______________________ __200_________
FE55M.wav at 1627ms Loop:73377 172.3Hz 100% 0________________________100_________________
FE55M.wav at 1662ms Loop:80958 126.6Hz 87% 0________________________100_____
FE55M.wav at 1708ms Loop:90962 106.3Hz 85% 0________________________100
FE55M.wav at 1743ms Loop:98364 281.0Hz 100% 0________________________100______________________ __200___________________
FE55M.wav at 1801ms Loop:10832 109.8Hz 100% 0________________________100_
FE55M.wav at 1824ms Loop:15868 102.7Hz 100% 0________________________
FE55M.wav at 1906ms Loop:33493 343.0Hz 85% 0________________________100______________________ __200________________________300_________
FE55M.wav at 1952ms Loop:43480 313.4Hz 85% 0________________________100______________________ __200________________________300__
FE55M.wav at 1975ms Loop:48516 111.5Hz 85% 0________________________100_
FE55M.wav at 1999ms Loop:53503 114.1Hz 85% 0________________________100__
FE55M.wav at 2080ms Loop:71026 112.6Hz 100% 0________________________100__
FE55M.wav at 2207ms Loop:98807 105.9Hz 100% 0________________________100
FE55M.wav at 2265ms Loop:11308 321.9Hz 85% 0________________________100______________________ __200________________________300____
FE55M.wav at 2335ms Loop:26296 318.1Hz 85% 0________________________100______________________ __200________________________300___
FE55M.wav at 2347ms Loop:28772 182.8Hz 100% 0________________________100___________________
FE55M.wav at 2405ms Loop:41306 192.4Hz 100% 0________________________100______________________
FE55M.wav at 2416ms Loop:43835 106.0Hz 100% 0________________________100
FE55M.wav at 2428ms Loop:46328 107.0Hz 85% 0________________________100
FE55M.wav at 2440ms Loop:48821 160.1Hz 100% 0________________________100______________
FE55M.wav at 2463ms Loop:53827 189.5Hz 100% 0________________________100_____________________
FE55M.wav at 2498ms Loop:61445 313.7Hz 85% 0________________________100______________________ __200________________________300__
FE55M.wav at 2509ms Loop:63931 370.8Hz 87% 0________________________100______________________ __200________________________300________________
FE55M.wav at 2567ms Loop:76598 152.4Hz 100% 0________________________100____________
FE55M.wav at 2614ms Loop:86718 232.3Hz 100% 0________________________100______________________ __200_______
FE55M.wav at 2637ms Loop:91796 251.3Hz 100% 0________________________100______________________ __200___________
FE55M.wav at 2648ms Loop:94315 148.4Hz 100% 0________________________100___________
FE55M.wav at 2753ms Loop:17217 158.0Hz 80% 0________________________100_____________
FE55M.wav at 2776ms Loop:22170 218.7Hz 100% 0________________________100______________________ __200___
FE55M.wav at 2799ms Loop:27256 317.0Hz 85% 0________________________100______________________ __200________________________300___
FE55M.wav at 2811ms Loop:29739 233.5Hz 100% 0________________________100______________________ __200_______
FE55M.wav at 2869ms Loop:42037 169.3Hz 100% 0________________________100________________
FE55M.wav at 2892ms Loop:47072 322.5Hz 85% 0________________________100______________________ __200________________________300____
FE55M.wav at 2904ms Loop:49555 281.8Hz 100% 0________________________100______________________ __200___________________
FE55M.wav at 2939ms Loop:57193 307.0Hz 85% 0________________________100______________________ __200________________________300
FE55M.wav at 2950ms Loop:59658 247.0Hz 100% 0________________________100______________________ __200__________
FE55M.wav at 3008ms Loop:72328 272.9Hz 100% 0________________________100______________________ __200_________________
FE55M.wav at 3031ms Loop:77386 238.2Hz 100% 0________________________100______________________ __200________
FE55M.wav at 3055ms Loop:82457 239.1Hz 100% 0________________________100______________________ __200________
FE55M.wav at 3089ms Loop:89987 299.0Hz 100% 0________________________100______________________ __200_______________________
FE55M.wav at 3124ms Loop:97608 124.8Hz 87% 0________________________100_____
FE55M.wav at 3182ms Loop:10084 227.3Hz 100% 0________________________100______________________ __200_____
FE55M.wav at 3240ms Loop:22695 105.1Hz 100% 0________________________100
FE55M.wav at 3287ms Loop:32847 111.8Hz 100% 0________________________100_
FE55M.wav at 3356ms Loop:47830 168.9Hz 100% 0________________________100________________
FE55M.wav at 3368ms Loop:50352 210.2Hz 100% 0________________________100______________________ __200_
FE55M.wav at 3391ms Loop:55429 134.1Hz 100% 0________________________100_______
FE55M.wav at 3414ms Loop:60386 231.1Hz 100% 0________________________100______________________ __200______
FE55M.wav at 3449ms Loop:68024 232.4Hz 100% 0________________________100______________________ __200_______
FE55M.wav at 3461ms Loop:70550 105.6Hz 85% 0________________________100
FE55M.wav at 3507ms Loop:80654 226.8Hz 100% 0________________________100______________________ __200_____
FE55M.wav at 3565ms Loop:93344 226.2Hz 100% 0________________________100______________________ __200_____
FE55M.wav at 3600ms Loop: 841 278.7Hz 100% 0________________________100______________________ __200__________________
FE55M.wav at 3612ms Loop: 3372 274.9Hz 100% 0________________________100______________________ __200_________________
FE55M.wav at 3623ms Loop: 5902 233.1Hz 100% 0________________________100______________________ __200_______
FE55M.wav at 3646ms Loop:10995 306.4Hz 85% 0________________________100______________________ __200________________________300
FE55M.wav at 3658ms Loop:13464 236.5Hz 100% 0________________________100______________________ __200________
FE55M.wav at 3693ms Loop:20972 314.6Hz 85% 0________________________100______________________ __200________________________300__
FE55M.wav at 3705ms Loop:23449 117.1Hz 100% 0________________________100___
FE55M.wav at 3728ms Loop:28399 104.2Hz 85% 0________________________100
FE55M.wav at 3751ms Loop:33321 111.4Hz 85% 0________________________100_
FE55M.wav at 3809ms Loop:45876 309.4Hz 85% 0________________________100______________________ __200________________________300_
**************************************************
Finished FE55M.wav Delaying 1 second...



When I compare this output with the Audacity Analyze\Plot Spectrum option for particular moments in the files, I don't get good agreement, though it's impossible to match the sampled intervals precisely. Also, the fundamental is often visually hard-to-determine from the Audacity plot - since the first hump is broad and stepping upward... without any downslope between the upward steps.

Take a look at the output of the sketch and see if the detected fundamentals are "true" of the mosquito buzz in your view.
The algorithm in the FFTFreq object can be tweaked (or replaced) to detect peaks differently.
I created the algorithm based on the visual appearance of lots of sampled spectra (using Audacity) for human voice and trumpet. The mosquito buzz is somewhat different.

I'll look forward to your analysis.

Wicky21
09-28-2017, 04:19 AM
Dave FE55M.wav fundamental is quite correct. Both of the .wav files are same buzz but with two different sound levels. So FE55LW.wav fundamental also should come around the same as FE55M.wav. I know its hard to achieve the fundamental of FE55LW.wav

Davidelvig
09-28-2017, 04:57 AM
Let me run the files again and increase the gain on the quieter file.
I’ll also send the results in a narrower format that fits the forum’s margins.
So, what is the frequency range that I should be seeing?

Anyone: does one of the forum editor tags (other than CODE) use no line wrapping?

Wicky21
09-28-2017, 05:20 AM
I have a MATLAB code which i use to analyze the .wav files. Simply it will give the peaks by analyzing the spectrogram of the .wav file. According to that MATLAB code,

FE55M.wav has fundamental around 380Hz. But doesn't matter it can vary between 350Hz-480Hz.
FE55LW.wav fundamental is hard to analyze from MATLAB code either.

Frequency below 350Hz and frequency above 480Hz is not possible.

Davidelvig
09-28-2017, 12:22 PM
OK, I put the recognition "floor' at 300Hz, and now the plot looks like this.


**************************************************
FE55M.wav ( 269ms) 331 Hz o___________o___________o___________o____
FE55M.wav ( 606ms) 323 Hz o___________o___________o___________o___
FE55M.wav ( 931ms) 338 Hz o___________o___________o___________o_____
FE55M.wav (1070ms) 304 Hz o___________o___________o___________o_
FE55M.wav (1093ms) 316 Hz o___________o___________o___________o__
FE55M.wav (1105ms) 310 Hz o___________o___________o___________o_
FE55M.wav (1163ms) 316 Hz o___________o___________o___________o__
FE55M.wav (1906ms) 343 Hz o___________o___________o___________o_____
FE55M.wav (1952ms) 313 Hz o___________o___________o___________o__
FE55M.wav (2265ms) 322 Hz o___________o___________o___________o___
FE55M.wav (2335ms) 318 Hz o___________o___________o___________o__
FE55M.wav (2498ms) 314 Hz o___________o___________o___________o__
FE55M.wav (2509ms) 371 Hz o___________o___________o___________o_________
FE55M.wav (2753ms) 330 Hz o___________o___________o___________o____
FE55M.wav (2799ms) 317 Hz o___________o___________o___________o__
FE55M.wav (2834ms) 300 Hz o___________o___________o___________o
FE55M.wav (2892ms) 322 Hz o___________o___________o___________o___
FE55M.wav (2939ms) 307 Hz o___________o___________o___________o_
FE55M.wav (3693ms) 315 Hz o___________o___________o___________o__
FE55M.wav (3809ms) 309 Hz o___________o___________o___________o_
**************************************************
Starting FE55LW.wav Delaying 200ms...
**************************************************
FE55LW.wav ( 200ms) 347 Hz o___________o___________o___________o______
FE55LW.wav ( 232ms) 322 Hz o___________o___________o___________o___
FE55LW.wav ( 290ms) 319 Hz o___________o___________o___________o__
FE55LW.wav ( 313ms) 302 Hz o___________o___________o___________o
FE55LW.wav ( 464ms) 324 Hz o___________o___________o___________o___
FE55LW.wav ( 522ms) 314 Hz o___________o___________o___________o__
FE55LW.wav ( 557ms) 332 Hz o___________o___________o___________o____
FE55LW.wav ( 591ms) 417 Hz o___________o___________o___________o___________o_ __
FE55LW.wav ( 638ms) 312 Hz o___________o___________o___________o_
FE55LW.wav ( 789ms) 339 Hz o___________o___________o___________o_____
FE55LW.wav (1659ms) 384 Hz o___________o___________o___________o___________
FE55LW.wav (2019ms) 308 Hz o___________o___________o___________o_
FE55LW.wav (2785ms) 321 Hz o___________o___________o___________o___
FE55LW.wav (3017ms) 308 Hz o___________o___________o___________o_
FE55LW.wav (3040ms) 301 Hz o___________o___________o___________o
FE55LW.wav (3133ms) 310 Hz o___________o___________o___________o_
FE55LW.wav (3191ms) 384 Hz o___________o___________o___________o___________
FE55LW.wav (3214ms) 307 Hz o___________o___________o___________o_
FE55LW.wav (3702ms) 338 Hz o___________o___________o___________o_____


Now it only finds a frequency (above 300Hz) every 50-500 ms.

Still not getting your expected 380Hz range very often

Wicky21
09-28-2017, 12:44 PM
Check out the below .wav which has the same buzz with louder sound level. It should give the correct fundamental. So some how Mid level and low level sound should compare with the Louder .wav
Also rather than print set of fundamentals, What if you can take some weighted average and print a single value? because its hard to come up to a conclusion. Also i guess you can take some probability value right. So can weight each fundamentals.
Anyway try with attach .wav file and post the results. It should give the correct fundamental. because from Audacity also can analyze the fundamental clearly.

Davidelvig
09-28-2017, 12:55 PM
Similar results:


FE55L.wav ( 200ms) 347 Hz o___________o___________o___________o______
FE55L.wav ( 232ms) 322 Hz o___________o___________o___________o___
FE55L.wav ( 290ms) 319 Hz o___________o___________o___________o__
FE55L.wav ( 313ms) 302 Hz o___________o___________o___________o
FE55L.wav ( 464ms) 324 Hz o___________o___________o___________o___
FE55L.wav ( 522ms) 314 Hz o___________o___________o___________o__
FE55L.wav ( 557ms) 332 Hz o___________o___________o___________o____
FE55L.wav ( 591ms) 417 Hz o___________o___________o___________o___________o_ __
FE55L.wav ( 638ms) 312 Hz o___________o___________o___________o_
FE55L.wav ( 789ms) 339 Hz o___________o___________o___________o_____
FE55L.wav (1659ms) 384 Hz o___________o___________o___________o___________
FE55L.wav (2019ms) 308 Hz o___________o___________o___________o_
FE55L.wav (2785ms) 321 Hz o___________o___________o___________o___
FE55L.wav (3017ms) 308 Hz o___________o___________o___________o_
FE55L.wav (3040ms) 301 Hz o___________o___________o___________o
FE55L.wav (3133ms) 310 Hz o___________o___________o___________o_
FE55L.wav (3191ms) 384 Hz o___________o___________o___________o___________
FE55L.wav (3214ms) 307 Hz o___________o___________o___________o_
FE55L.wav (3702ms) 338 Hz o___________o___________o___________o_____

What are you trying to interpret from the mosquito buzz, and particularly the frequency?

Wicky21
09-28-2017, 01:05 PM
I'm trying to analyze the different type mosquito species wing beat tones. Trying to figure out is there any pattern.

Davidelvig
09-28-2017, 01:22 PM
Ok.
Let me know if I can help further.
Its been good to exercise my FFTFreq library with a different use.

With a Teensy 3.2 or higher, and a mic from NEUTRONNED, you could run a continuous display of pitches, or perhaps log them to the SD on a Teensy 3.6 for later analysis.

Maybe you can detect when a mosquito is about to bite by changes in pitch!
Or a different pitch for the mosquito carrying malaria!
:D

Wicky21
09-28-2017, 01:28 PM
Lol. Sure. Im searching the most efficient and correct algorithm which give correct frequencies. Otherwise results were wrong and im in a trouble :D

Natefp
06-09-2018, 07:59 PM
Davidelvig, would you be willing to share your FFTFreq library? I'm trying to detect frequencies faster than the YIN algorithm and would be interested in your work.

Davidelvig
06-12-2018, 03:50 AM
I'd want to do a little cleanup before sharing.
Have you tried speeding up the YIN algorithm (the AudioAnalyzeNoteFrequency object in the Audio library) by changing the header file analyze_notefreq.h?

Change the value of:
#define AUDIO_GUITARTUNER_BLOCKS 24
to
#define AUDIO_GUITARTUNER_BLOCKS 8

You'll lose very low frequency detection, but speed up processing a great deal.

Natefp
06-12-2018, 08:14 AM
Thank you. Changing the number of blocks did make a big difference on the update rate. Here are some rough numbers I measured:



Blocks
Updates/s


24
14


16
20


12
28


8
42


6
56


4
84


3
112


2
182



I ran it both on the Teensy 3.2 and Teensy 3.6 and they both gave similar updates. I would have thought that faster hardware would give me more updates / second, but no. Is it strictly based on lowest detectable frequency?

I'm attempting to make a project that allows someone to sing into the mic, have their song recorded (pitch and timing) and be able to play it back in a different instrument. Another variation would be to play notes sung in real time over headphones in a different instrument.

I'll be looking forward to looking at your FFTFreq library, when it's ready.

Natefp
06-12-2018, 06:42 PM
@bmillier, what was your easy way to disable the notefreq function when not needed? I'm doing the same thing, integrating the guitar tuner into a bigger project.

Davidelvig
06-13-2018, 06:08 PM
@natefp

I'd check the frequency detection ability at various block counts.
At 8 blocks, I can get reliable pitch detection at 180Hz or more (male singling voice)
I'll bet that fewer blocks leads to a higher minimum.

With 8 blocks, at 42 updates/second, that's about 24ms per update.
That will feel pretty responsive for vocal use.

The FFT pitch detection is no real magic.
I do this, in general:
- copy the first 50 (or so) bins to an array
- detect the meaningful peaks by slope changes, and ignoring minimal bin values (this is fuzzy and based on your input strength)
- interpolate the frequency of the presumed actual peak between the size and position of the bin-based peaks (spaced at 43Hz each).
- lastly, if the ratio of the 1st and second peak frequencies are a ratio of 2:3, I calculate the fundamental as 1/2 of peak 1.

There's lots of variables that you can apply:
- minimum peak value vs input volume (signal strength)
- ignore bins 0 & 1 (I do)
- ignore bins above expected range (50 bins * 43 Hz/bin = 2150 Hz)
- choice between the tallest peaks vs first two meaningful peaks (I use the first peaks now... I tried "tallest peaks" with little added value, and lost of complexity)

In the end, a Teensy Audio FFT1024 can fire every 12ms. About half of the time needed to actually collect 1024 samples, though Paul cleverly reuses the last half of each previous sampling to release a new sample set in half the time.

With these steps, and a good bin interpolation function, I get pitch detection that matches with an iPhone tuner app within a few Hz most of the time.
I've not check with an oscilloscope.

Good luck!

duff
06-13-2018, 10:01 PM
@bmillier, what was your easy way to disable the notefreq function when not needed? I'm doing the same thing, integrating the guitar tuner into a bigger project.
I have a newer version on GitHub (https://github.com/duff2013/AudioTuner) that has disable function implemented and also it can decimate the incoming signal and low pass filter it (for aliasing) to help with processor usage. You can decimate 2, 4, 8 which corresponds to ~22050, ~11025 and ~5512.5Hz sample rates. You have to add in the coefficients for the filter though, the examples show this though they are probably not great filter coefficients but worked well for my tests.

Natefp
06-22-2018, 06:31 PM
Thank you both. The updated version seems to be much better at recognizing singing as well.

dogweather
09-07-2019, 03:12 AM
Everyone, thanks for this discussion! I followed a link from the example code to this forum thread. I had no idea there'd be such great sample code. I bought a Teensy 3.2 after seeing the Adafruit FFT: Fun with Fourier Transforms (https://learn.adafruit.com/fft-fun-with-fourier-transforms/overview-1) project.

My goal is to create a conference-badge style instrument tuner in time for Defcon 28 (2020). I've decided to keep it simple and detect just one tone, like Concert A4 (440 Hz). (hackaday page (https://hackaday.io/project/167265-dc28-jam-con-badge)) So this code is right on point!

I'm definitely going to try to use it to tune a double bass, and so the conversation about the tuba was helpful. The fundamental frequency of its low E is also 41-ish Hz, but I can tune the instrument by using a tune on e.g. a harmonic played on the A string.

dogweather
09-07-2019, 03:22 AM
Oh, I forgot to mention: I'm also going to try an entirely different method: using AI / machine learning. Now that TensorFlow Light enables some TensorFlow models to run on Arduino-compatible chips, this opens the possibility of "training" a model which can recognize an A4 440Hz being played on various instruments.