Hi

I see a lot of fft functions in the core code, but I cannot find any examples on how to FFT:iFFT a sample

thanks for pointing me to the right direction

Phil

Hi

I see a lot of fft functions in the core code, but I cannot find any examples on how to FFT:iFFT a sample

thanks for pointing me to the right direction

Phil

Hey Phil!

I hope all is well with you since we last talked.

Creating examples and guide on FFT is certainly on the list of tasks to do. Iâ€™m sorry for the lack of one as of right now.

no worries, but now I got bigger issues

section

`.data' will not fit in region`

FLASHâ€™

and my program is basically doing nothing but FFT->iFFT

```
#include "daisy_seed.h"
#include "daisysp.h"
#include "arm_math.h"
// Use the daisy namespace to prevent having to type
// daisy:: before all libdaisy functions
using namespace daisy;
using namespace daisysp;
// Declare a DaisySeed object called hardware
DaisySeed hardware;
Oscillator osc;
AdEnv env;
arm_rfft_fast_instance_f32* fftInstance;
float32_t *fftBuffer;
void AudioCallback(AudioHandle::InterleavingInputBuffer in,
AudioHandle::InterleavingOutputBuffer out,
size_t size)
{
float osc_out;
//Fill the block with samples
for(size_t i = 0; i < size; i += 2)
{
osc_out = osc.Process();
//Set the left and right outputs
out[i] = osc_out;
// out[i + 1] = osc_out;
out[i+1] = in[i+1];
arm_rfft_fast_f32(fftInstance,(float32_t*)&in[0],fftBuffer,0);
arm_rfft_fast_f32(fftInstance,fftBuffer,(float32_t*)&out[0],1);
}
}
int main(void)
{
// Declare a variable to store the state we want to set for the LED.
bool led_state;
led_state = true;
// Configure and Initialize the Daisy Seed
// These are separate to allow reconfiguration of any of the internal
// components before initialization.
hardware.Configure();
hardware.Init();
hardware.SetAudioBlockSize(4);
//How many samples we'll output per second
float samplerate = hardware.AudioSampleRate();
fftInstance = new arm_rfft_fast_instance_f32;
arm_status status = arm_rfft_fast_init_f32(fftInstance, hardware.AudioBlockSize());
fftBuffer = new float32_t[hardware.AudioBlockSize()];
//Set up oscillator
osc.Init(samplerate);
osc.SetWaveform(osc.WAVE_SIN);
osc.SetAmp(1.f);
osc.SetFreq(440);
hardware.StartAudio(AudioCallback);
// Loop forever
for(;;)
{
// Set the onboard LED
hardware.SetLed(led_state);
// Toggle the LED state for the next time around.
led_state = !led_state;
// Wait 500ms
System::Delay(500);
}
}
```

Hey Phil!

Iâ€™m sorry for the delay in response. I answered this in the other thread where you also mentioned it.

If you Google about CMSIS DSP flash Usage, youâ€™ll see some stuff about large tables being needlessly linked, and suggestions for how to reduce this.

In my opinion, CMSIS DSP is poorly documented, and not written to efficiently use flash. It might be great functionally, I donâ€™t know.

Iâ€™ve read some stuff suggesting using complex FFT, and stuffing the imaginary part with 0, to reduce storage requirements.

Interesting stuff from Teensy forum, years ago, but on this topic:

1 Like

funny that I stopped wasting my time with teensies becos of SNR issues

now I donâ€™t see how settings array values to zero at runtime would reduce the size of the storage requirement

The way the CMSIS constant tables are set up for real numbers is different from complex. Maybe itâ€™s non-intuitive, but it is true. Complex FFT of size 2048 easily fits in Daisy flash, the only down side is some wasted RAM for the 0 imaginary component.

SNR: sound/noise ratio is 50% on teensies

so they fool people with an amazing audio library

yet you have to interface the teensy with a ADC breakout board just to sample audio

and I2S is quiet hard to debug

The main thing I prefer about Daisy is the builtin codec, and patch_sm is very handy for eurorack modules.

On the other hand, I2S might be tricky, but in my experience with Teensy, using several different I2S devices, I2S just worked, with no debugging of I2S.

Anyway, both systems have their pros and cons.

hereâ€™s an example which displays the frequency of audio input, using cfft of size 2048:

```
// Using FFT size of 2048 needs 16K of sample buffer, and only gets a resolution of
// 47Hz.
#include "daisysp.h"
#include "../../kxmx_bluemchen/src/kxmx_bluemchen.h"
#include <string.h>
#include "arm_math.h"
#include "arm_const_structs.h"
using namespace kxmx;
using namespace daisy;
using namespace daisysp;
Bluemchen hw;
// OLED is 64x32
void UpdateOled(float fl)
{
hw.display.Fill(false);
std::string str = std::to_string(static_cast<int>(fl));
char *cstr = &str[0];
hw.display.SetCursor(0, 0);
// Font_16x26 4 digits
// Font_11x18 5 digits BEST for this
// Font_6x8 10 digits
hw.display.WriteString(cstr, Font_11x18, true);
hw.display.Update();
}
#define FFT_SIZE 2048
#define FFT_BUF_SIZE (2*FFT_SIZE)
float fft_buf[FFT_BUF_SIZE];
float output[FFT_SIZE];
// global, so do_fft() knows when to run
int fft_buf_index = 0;
void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
{
for(size_t i = 0; i < size; i++)
{
if (fft_buf_index < FFT_BUF_SIZE) {
fft_buf[fft_buf_index++] = in[0][i];
fft_buf[fft_buf_index++] = 0.f;
}
out[0][i] = in[0][i];
out[1][i] = in[1][i];
}
}
float do_fft(float *input, float *output)
{
float32_t maxValue;
uint32_t index = 0;
int Fmax = hw.AudioSampleRate() / 2; // 24000
uint32_t Nbins = FFT_SIZE / 2;
/* Process the data through the CFFT/CIFFT module */
/* this must match FFT_SIZE, and 2048 is as high as Daisy can fit */
arm_cfft_f32(&arm_cfft_sR_f32_len2048, input, 0 /*ifftFlag*/, 1/*doBitReverse*/);
/* Process the data through the Complex Magnitude Module for
calculating the magnitude at each bin */
arm_cmplx_mag_f32(input, output, FFT_SIZE /*fftSize*/);
/* Calculates maxValue and returns corresponding BIN value */
arm_max_f32(output, FFT_SIZE /*fftSize*/, &maxValue, &index);
/*
to calculate the freq of the selected bin
N (Bins) = FFT Size/2
FR = Fmax/N(Bins)
*/
// error test
if (index >= Nbins)
return -1.f;
// good result
return (float) index * ((float)Fmax / Nbins);
}
int main(void)
{
hw.Init();
hw.StartAdc();
hw.StartAudio(AudioCallback);
while (1)
{
float f;
static float old_f = -1.f;
if (fft_buf_index == FFT_BUF_SIZE) {
// do FFT
f = do_fft(fft_buf, output);
// start collecting samples again
fft_buf_index = 0;
} else {
continue;
}
// if frequency value has changed, update display
if ((f > 0.f) && (f != old_f)) {
UpdateOled(f);
old_f = f;
}
}
}
```

1 Like

Hello --as a way to finally get FFT to work on the daisy I copied your code to try (and switched it to the patch)

I put (i think) all the relevant files in the makefile (see this post)

and i changed the name of that one file to a lower case S. (so the asm file would work)

BUT now I am getting these new errors

/Users/cricketbee/desktop/daisyexamples/patch/blank/blank.cpp:71: undefined reference to `arm_cmplx_mag_f32' /Applications/ARM/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /Users/cricketbee/desktop/daisyexamples/patch/blank/blank.cpp:74: undefined reference to `

arm_max_f32â€™

```
I have those in my makefile
```

TARGET = Blank

CPP_SOURCES = blank.cpp

OPT = -Os

LIBDAISY_DIR = â€¦/â€¦/libDaisy

DAISYSP_DIR = â€¦/â€¦/DaisySP

ASM_SOURCES =

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.s

C_SOURCES =

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_rfft_fast_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_rfft_fast_init_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/CommonTables/arm_common_tables.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/CommonTables/arm_const_structs.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_cfft_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_cfft_radix8_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_cfft_radix4_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_cfft_radix4_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/ComplexMathFunctions/arm_cmplx_mag_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/ComplexMathFunctions/arm_max_f32.c

C_INCLUDES +=

-I$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Include

SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core

include $(SYSTEM_FILES_DIR)/Makefile

```
thanks for any help
```

Hereâ€™s what I have in the Makefile for the CMSIS stuff:

C_SOURCES =

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_cfft_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_cfft_radix8_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/ComplexMathFunctions/arm_cmplx_mag_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/CommonTables/arm_const_structs.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/CommonTables/arm_common_tables.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/StatisticsFunctions/arm_max_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_rfft_fast_init_f32.c

$(LIBDAISY_DIR)/Drivers/CMSIS/DSP/Source/TransformFunctions/arm_rfft_fast_f32.c

as a way to finally get FFT to work on the daisy

please create your own post, it is not good practice to highjack other people threads, especially if **you** encounter compilation issues that concerns **your** setup

I am getting spammed on my post for topics that do not concern me

thx for your comprehension

if people were doing the same, a whole site would need to be dedicated to one single thread

oh sorry about that. I actually thought the opposite was true especially since there are so few threads about FFT on this forum. I thought it would be good to put it here so that you would see it as I was hoping you might have had some sucess with your FFT project. And since I was responding to not being able to getting the code running that TelePlayer posted in this thread (see above) It seemed to make the most sense to post it here.

Do you think it would be better to make a new thread for his code?

thanks for your input

For anyone whoâ€™s trying to use Ă‰milie Gilletâ€™s `shy_fft.h`

header these days, here are some tips (learned from much trial and error):

- Although the processing classes are all
`template <typename T>`

, I can only get them to work with single-precision floats. If anyone has managed to get double-precision arithmetic working, please let me know! (Or, if someone sees why the implementation obviously doesnâ€™t support it, thatâ€™d be interesting tooâ€¦) - A
`ShyFFT<T, N>`

instance must be initialized (by calling member function`Init`

) before anything will work. - The FFT and iFFT are performed are called by invoking the
`Direct`

and`Inverse`

methods of the class. You only need to supply pointers to the buffers of size N where samples will be read from and written to. - The FFT / iFFT is not normalized, meaning composing
`Inverse`

with`Direct`

will give you back what you started with, multiplied by N, the FFT size. Multiplying a signal by 2048 is 66 dB of gainâ€¦ beware headphone users. - The FFT of a length-N signal is always a length-N complex signal. When the input is real, that complex output signal has some symmetry, so all the information lives in the first N / 2 bins. Real FFT functions return those N / 2 complex numbers as an array of N real numbers, but there isnâ€™t a standard convention for how those numbers should be arranged. Ă‰milieâ€™s method packs the output of
`Direct`

as`{real[0], real[1], ..., real[N / 2 - 1], imag[0], imag[1], ..., imag[N / 2 - 1]}`

. The input to`Inverse`

expects the same format. (By comparison, the CMSIS real FFT function packs frequency-domain data like`{real[0], imag[0], ..., real[N / 2 - 1], imag[N / 2 - 1]}`

.)

7 Likes

Iâ€™m not as proficient as many here clearly are at programming, so +1 for a simple and clearly annotated FFT example please. I would pay good money tbh.

Woah, point number 5 is exactly what Iâ€™d been trying to figure out for some time, so I really appreciate the writeup!

Iâ€™d been working on a very â€śsimpleâ€ť wrapper class for shy_fft to make it easier to do things with (mostly tested with converting to input > fft > ifft > output, and came up wit hthis (again, itâ€™s very poorly implemented and havenâ€™t picked it back up in a while).

I did some preliminary work to try and get (I)FFT into Heavy, but it needs some DSP experts to finalize and get working:

Thereâ€™s some issues for how these objects are typically used inside Pd, which may not make this particularly useful though.

Hereâ€™s a working C++ example for the Seed!

5 Likes

Thanks so much.

Iâ€™m only accustomed to using Arduino, so I have no idea what any of this makefile stuff is about.