Change sample rate for Audio Adaptor Board for Teensy 3.0 & 3.1 FFT

Status
Not open for further replies.

GeppettoLab

Active member
Hi,
I'm working on the Audio Adaptor Board for Teensy 3.1 using the FFT example of the Audio Library, to perform a frequency analysis of an handmade Kantele .
I'm trying to detect every single tone of this instrument, trought an 1024 FFT in the range of 40/4000 Hz.
The example works great but my problem is that with 512 bins of 21,53 Hz width, I cannot obtain the accuracy that i need.
I would try to modify the sample rate of the ADC from 44.1KHz to an half or less, I've searched for some variable and I found in the AudioStream library (specifically in the AudioStream.h file) the variable AUDIO_SAMPLE_RATE defined at 44117.64706.
Code:
#ifndef AudioStream_h
#define AudioStream_h

#include "Arduino.h"

#define AUDIO_BLOCK_SAMPLES  128
#define AUDIO_SAMPLE_RATE    44117.64706
#define AUDIO_SAMPLE_RATE_EXACT 44117.64706 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256

from this comment I suppose that this variable is in some way dependent from the CPU SPEED that I can modify from the Teensyduino IDE. But how?
I didn't find anything about this..

I start now some experiment modifying the variable and the CPU SPEED, but I will like to understand if I'm on the right way or not.
Can someone help me?
Thanks in advance
G
 
Last edited:
Ok..
I've tried in the afternoon, and nothing change. I've modified the AUDIO_SAMPLE_RATE variable and the CPU SPEED without results.
I could try to decimate the audio sample after the sampling. I know that it a waste of computing but I can't see another solution..

I will try to modify the library.. I'm not so pratical but I will post all my results.
Thanks!
 
However, you will find FFT has a fundamental trade-off between update speed and frequency resolution. If you decrease the same rate, or increase the number of FFT bins, the result is more resolution, but you get the results less often.

1024 bins at 44117 Hz is 23.2 milliseconds. The audio library FFT1024 object gives you 50% overlap, so you get new data every 11.6 ms, which represents the windowed average over the prior 23.2 ms.

As slower update rates, the period of time represented by one FFT output becomes longer, to the point where it probably is not very useful for most musical material.
 
As slower update rates, the period of time represented by one FFT output becomes longer, to the point where it probably is not very useful for most musical material.

ok, but my real problem is to obtain a better resolution on the low frequency. In this case 21Hz of bin width is too big
In the range from 200 and 500 Hz some tone differs from the others of 13Hz and I cannot directly recognize it, cause it falls in more then one bin.
Surely I can check for the armonics, but the resonance of my instruments create different problems..
Can I compute an FFT on 2048 instead?

---
I have another doubt.. I sample at 44100, than from nyqvist I check the frequency from 0 to 22000. then each bin is 22000/1024= 21Hz width..
but using a tone generator with a mic and the audio board, a 440HZ frequency falls in the 11th and 12th bin. Are they the representation of 440Hz? for my computing are the representation of 220Hz.
Where am I doing wrong?
 
I have another doubt.. I sample at 44100, than from nyqvist I check the frequency from 0 to 22000. then each bin is 22000/1024= 21Hz width..
but using a tone generator with a mic and the audio board, a 440HZ frequency falls in the 11th and 12th bin. Are they the representation of 440Hz? for my computing are the representation of 220Hz.
Where am I doing wrong?

Doing a 1024 point FFT on 44.1 kHz sampling, you have a bin width of 44100/1024 = 43 Hz (512 positive and 512 negative frequencies. So a 440 Hz tone should fall in the 11/12th bin (positive frequency) and 1012-11/12th bin (negative frequency). This is independent of the fact that standard spectra only present 0 to Nyquist.

Concerning FFT size, as Paul says, there is a trade-off between frequency bin width and temporal resolution. This is not only signal processing but also physics (uncertainty relation), that you cannot simultaneously measure precisely frequency and time. In other words, any acoustic feature that is, say 10 ms long has a bandwidth of at least 100 Hz, or to get a tone precise at 1 Hz the tone must be at least 1 s long and constant (delta_f * delta_t >= 1).
 
http://forum.pjrc.com/threads/24793-...ll=1#post48465

This code needs tweaked to use the latest library code but gives much higher resolution - uses parabolic interpolation on the FFT.

Have you thought about using analyze_tonedetect in the audio library?

figure 7 in http://www.ni.com/white-paper/4844/en/ (referenced in quoted link) shows nicely the influence of windowing (suppressing sidelobes) at the expense of increasing the main lobe.

Yes, parabolic interpolation of FFT bins may increase the accuracy of determining the frequency of the tone, but the resolution (differentiating two adjacent tones) is determined by the main lobe of the window response (see above mentioned Fig. 7) There are indeed high resolution methods (e.g. autoregression, or MUSIC, etc) but their spectral estimation capability may not be very accurate, as they are more sensitive to noise.
 
Doing a 1024 point FFT on 44.1 kHz sampling, you have a bin width of 44100/1024 = 43 Hz (512 positive and 512 negative frequencies. So a 440 Hz tone should fall in the 11/12th bin (positive frequency) and 1012-11/12th bin (negative frequency). This is independent of the fact that standard spectra only present 0 to Nyquist.
here it's my mistake!

Maybe this will help? TL;DR = FFT is not very good for this....
http://stackoverflow.com/questions/1...-a-smart-phone
This is very helpful and interesting, I will use someone of this methods it to improve my software.
But now, for my project the fundamental is not so important i need some armonics and I think that FFT is still the best solution.

Concerning FFT size, as Paul says, there is a trade-off between frequency bin width and temporal resolution. This is not only signal processing but also physics (uncertainty relation), that you cannot simultaneously measure precisely frequency and time. In other words, any acoustic feature that is, say 10 ms long has a bandwidth of at least 100 Hz, or to get a tone precise at 1 Hz the tone must be at least 1 s long and constant (delta_f * delta_t >= 1)
I don't need a fast computation, probably I can try to use a wider buffer to compute a larger part of sound.

Should I simply increment the AUDIO_BLOCK_SAMPLES in the AudioStream library?
 
There is another potential application for a variable sample rate in the audio library.
I finished a prototype of a wavetable-based oscillator (parallel EEPROM + DAC, driven by teensy 3.1), similar to the Monowave.

Teensy 3.1 controls MIDI In, bank select, wave select, and a DS1077 to produce the right frequency (which is divided by a 4040 to send the 256 wave samples to the DAC).
It sounds really good, and plan to improve upon the design by using Paul's audio adapter (and the optional SPI EEPROM).
If anybody has already done something like this with Paul's audio adapter i would like to exchange notes.

Happy tinkering !
 
There is another potential application for a variable sample rate in the audio library.
I finished a prototype of a wavetable-based oscillator (parallel EEPROM + DAC, driven by teensy 3.1), similar to the Monowave.

Teensy 3.1 controls MIDI In, bank select, wave select, and a DS1077 to produce the right frequency (which is divided by a 4040 to send the 256 wave samples to the DAC).
It sounds really good, and plan to improve upon the design by using Paul's audio adapter (and the optional SPI EEPROM).
If anybody has already done something like this with Paul's audio adapter i would like to exchange notes.

Happy tinkering !

i think at best you could emulate a monowave. the audio adapter (that is, the codec on there) only supports a couple of (fixed) sample rates, like most or all modern audio codecs (ie 22.05 kHz, 24 kHz, 32 kHz, 44.1kHz, 48 kHz, 96 kHz and a few others); for PPG style / variable sample rate synthesis you might be better off trying with a SPI DAC, as the adafruit waveHC library did.
 
Should I simply increment the AUDIO_BLOCK_SAMPLES in the AudioStream library?

There no single, easy-to-edit location to change the audio library sample rate.

AUDIO_BLOCK_SAMPLES controls the number of samples processed in each block, not the sample rate. Maybe you're thinking of AUDIO_SAMPLE_RATE and AUDIO_SAMPLE_RATE_EXACT in AudioStream.h? Changing those is only the beginning of the work needed to change the library's sample rate.

Each input and output object is coded to implement 44117.64706 samples per second. The I2S objects use MCLK and LRCLK from the I2S hardware. The others generally use the PDB to generate their timing. Changing the definition in AudioStream.h does NOT cause all these to update. The library was designed to run at a fixed rate, so changing it is not so simple.


I finished a prototype of a wavetable-based oscillator (parallel EEPROM + DAC, driven by teensy 3.1), similar to the Monowave.

Teensy 3.1 controls MIDI In, bank select, wave select, and a DS1077 to produce the right frequency (which is divided by a 4040 to send the 256 wave samples to the DAC).
It sounds really good, and plan to improve upon the design by using Paul's audio adapter (and the optional SPI EEPROM).
If anybody has already done something like this with Paul's audio adapter i would like to exchange notes.

You probably should look at using the arbitraryWaveform() feature of the waveform synthesis object.

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

If you already have a 256 sample waveform, you can probably use this feature to play it at different speeds.

However, you MUST properly bandwidth limit your waveform when playing at higher frequencies. The waveform object doesn't do this for you. Perhaps a future version should do this?

If your waveform contains high frequencies and you increase its speed, you'll be increasing those frequencies too when you play it faster then 1 waveform sample for each 1 audio output sample. Any frequency you increase beyond 22.06 kHz aliases back to become horrible distortion.

I know this frequency aliasing concept can be difficult to understand. I've been bit by it. It is real. When you actually increase the sample rate, for example playing the 256 samples at 60 kHz, you're increasing the total digital bandwidth to 30 kHz, which automatically makes extra room for those higher frequencies in the original material to become inaudible ultrasonic content.

But when you play those 256 samples faster using the waveform synthesis object, it skips through the 256 samples and uses linear interpolation to map onto the libraries fixed 44.1 kHz sample rate. The total signal bandwidth remains fixed at 22.06 kHz. The linear interpolation does a pretty good job of giving you the right waveform output, but it does not handle the case where you push higher frequency content from the original 256 samples beyond 22.06 kHz.

The 256 samples you load MUST be low-pass filtered when you play them at higher speeds. If you play the waveform at 2X speed, it must not have any content above 11.03 kHz, because you're doubling its entire bandwidth. If you try to create any sound above 22.06 kHz, it will not magically become unheard ultrasonic signals. It will alias to become horrible distortion.
 
aliasing

>>You probably should look at using the arbitraryWaveform() feature of the waveform synthesis object.

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

>> If you already have a 256 sample waveform, you can probably use this feature to play it at different speeds.

>> However, you MUST properly bandwidth limit your waveform when playing at higher frequencies. The waveform >>object doesn't do this for you. Perhaps a future version should do this?

Hi Paul,
thank you for your quick reply - yes, I think I do understand aliasing.
Here is short summary of my thinking:
1) With variable sampling rate, most of the aliasing will be eliminated by using a good ol' analog filter.
2) With fixed sampling rate, using interpolation to generate different frequencies, in order to avoid aliasing I will need to store different versions (I was thinking one for every octave) of the same waveform, and band-limit that adequately. But now the amount of memory to store the waveforms could go up by 5x - which is why I am adding the optional SPI EEPROM to your audio shield :)
3) Or - and maybe that's what you are thinking - it would be possible to perform a convolution of the interpolated waveform with a good low pass FIR. This is equivalent to 1) in the sense that it will be done in real time - is this what you are planning to do with the Audio Library ?

Cheers,
 
But now the amount of memory to store the waveforms could go up by 5x - which is why I am adding the optional SPI EEPROM to your audio shield :)

Each copy is only 512 bytes. Just use "const" and they'll go into only flash memory. 10 copies is only 5K out of 256K.
 
Would porting this work?

HTML:
https://github.com/adafruit/WaveHC

I'm trying to get this lib to work with the Teensy audio card so that I can change samplerates on the fly using a pot.

So far, no such luck.
 
Hi there, I'm working on a audio project as a part of a final B.S. project in mechatronics engineering and using a teensy as a test platform. Same as the OP I need better resolution per bin for FFT diagnosis, and was able to lower the kHz manually by changing :
#define AUDIO_SAMPLE_RATE 17045.45454
#define AUDIO_SAMPLE_RATE_EXACT 17045.45454 // 96 MHz * 1 (MCLK_MULT) / 22 (MCLK_DIV) / 256 - MCLK_MULT and MCLK_DIV defined in Output_i2s.cpp
- in audiostream.h, and
#define MCLK_MULT 1
#define MCLK_DIV 22
- in output_i2s.cpp

I was pleasantly surprised that's the only thing you need to change in regards to FFT and I2SInputs at least, as the I2S clock is what's used to emulate the kHz speed, so everything else falls into place easily. I didn't check elsewhere, but if someone is not getting the proper kHz, I'm guessing a quick search for MCLK in the directory will show you other files it might be defined in.
 
Thank you, I tried it with 8 kHz sample rate and it works:
audiostream.h:
#elif F_CPU == 180000000
#define MCLK_MULT 1 //16
#define MCLK_DIV 87 // 255 // 180.000.000*16/2.048.000; 2.048.000/256=8kHz sample rate
#define MCLK_SRC 0

output_i2s.cpp:
#ifndef AUDIO_SAMPLE_RATE_EXACT
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define AUDIO_SAMPLE_RATE_EXACT 8081.89655 //44117.64706 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256
#elif defined(__MKL26Z64__)
#define AUDIO_SAMPLE_RATE_EXACT 8081.89655 //22058.82353 // 48 MHz / 2176, or 96 MHz * 1 / 17 / 256
 
Thank you, I tried it with 8 kHz sample rate and it works:
audiostream.h:
#elif F_CPU == 180000000
#define MCLK_MULT 1 //16
#define MCLK_DIV 87 // 255 // 180.000.000*16/2.048.000; 2.048.000/256=8kHz sample rate
#define MCLK_SRC 0

output_i2s.cpp:
#ifndef AUDIO_SAMPLE_RATE_EXACT
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define AUDIO_SAMPLE_RATE_EXACT 8081.89655 //44117.64706 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256
#elif defined(__MKL26Z64__)
#define AUDIO_SAMPLE_RATE_EXACT 8081.89655 //22058.82353 // 48 MHz / 2176, or 96 MHz * 1 / 17 / 256

I think you inverted the name of the file.

Hi there, I'm working on a audio project as a part of a final B.S. project in mechatronics engineering and using a teensy as a test platform. Same as the OP I need better resolution per bin for FFT diagnosis, and was able to lower the kHz manually by changing :
#define AUDIO_SAMPLE_RATE 17045.45454
#define AUDIO_SAMPLE_RATE_EXACT 17045.45454 // 96 MHz * 1 (MCLK_MULT) / 22 (MCLK_DIV) / 256 - MCLK_MULT and MCLK_DIV defined in Output_i2s.cpp
- in audiostream.h, and
#define MCLK_MULT 1
#define MCLK_DIV 22
- in output_i2s.cpp



I changed the files but the results is the semo, 43Hz of bin width

How did you test the software?
I use the fft example with a mic and produce a 430Hz freq, that results in the 11th bin...
 
Status
Not open for further replies.
Back
Top