Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I thought I was nearly there but can't quite get it over the line. #80

Open
DrJaymz opened this issue Mar 14, 2023 · 5 comments
Open

I thought I was nearly there but can't quite get it over the line. #80

DrJaymz opened this issue Mar 14, 2023 · 5 comments

Comments

@DrJaymz
Copy link

DrJaymz commented Mar 14, 2023

My goal was to work out the loudest amplitude fundamental from my samples and return its frequency. Using esp32.

Obviously before I start I got my I2S device recording and playing back - I can hear myself so thats working.

Then I needed to convert my data to double. Presumably so that it has positive and negative around 0 values.
I checked this for sanity and it also looks correct. i.e. you get a waveform centred on 0.

So then, it should just be a matter of run the FFT over my samples and use the FindMajorPeak but it fails on the FFT compute step.

I have 1024 samples at this point, block size = 1, sample rate is 22000 but should be enough to run compute and get something sensible.

TBH, I know what FFT does, I'm not great at working out how big the samples needed to be and I'm used to higher level languages where I don't need to be careful with the allocation and I don't need to get in to pointers; so these are probably where I'm getting it wrong if that helps?

data_offset = 0;
  InitI2SSpeakOrMic(MODE_MIC);
  size_t byte_read;
  while (1)
  {
    i2s_read(Speak_I2S_NUMBER, (char *)(microphonedata0 + data_offset),
             DATA_SIZE, &byte_read, (100 / portTICK_RATE_MS));
    data_offset += DATA_SIZE;
    if (data_offset == DATA_SIZE * BLOCKS)
      break;
  }

  // Can I play this back - Yes this plays it back ok.
  
  // size_t bytes_written;
  // InitI2SSpeakOrMic(MODE_SPK);
  // i2s_write(Speak_I2S_NUMBER, microphonedata0, data_offset, &bytes_written,
  //           portMAX_DELAY);

  // // Convert 8-bit unsigned int data to double for some reason

  double *micData = (double *)malloc((DATA_SIZE * BLOCKS) * sizeof(double));
  double *micDataImag = (double *)malloc((DATA_SIZE * BLOCKS) * sizeof(double));
  for (int i = 0; i < DATA_SIZE * BLOCKS; i++)
  {
    micData[i] = (double)(microphonedata0[i] - 128);
    micDataImag = 0;
  }

  // Apply Hamming window to microphone data
   FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);  // <-- crashes here LoadProhibited
   FFT.Compute(micData, micDataImag, DATA_SIZE * BLOCKS, FFT_FORWARD);

   double peak = FFT.MajorPeak(micData, DATA_SIZE * BLOCKS, SAMPLE_RATE);
   Serial.printf("Peak: %0.1f \n", peak);

   free(micData);
   free(micDataImag);

@DrJaymz
Copy link
Author

DrJaymz commented Mar 15, 2023

micDataImag = 0; <--- duh.
micDataImag[i] = 0;

@blaz-r
Copy link
Contributor

blaz-r commented Mar 18, 2023

Hello,
so with this fix, the code is working as it should now?

Also, if you are doing this in a loop, it might be worth initializing memory outside of it, so you don't allocate and free every iteration, as that is just wasting time. Also, you can use new in c++ and then delete[] to deallocate it, which is a bit nicer than malloc. Or you can init array on stack/data segment without malloc or new if you know the size beforehand, like: double data[size];

@DrJaymz
Copy link
Author

DrJaymz commented Mar 20, 2023

Well not really with that code. I found that it worked if I specified on the constructor as below but not as I showed it there;

double vReal[512];
double vImag[512];
arduinoFFT FFT = arduinoFFT(vReal, vImag, 512, 64000);

Then call:

  FFT.Compute(FFT_FORWARD);
  FFT.ComplexToMagnitude();
  double peak = FFT.MajorPeakParabola(); 

Then I get something sensible. I wasn't sure what FFT functions I needed to call before FFT.MajorPeakParabola();
But this seemed to work. No idea if I needed to remove DC or not beforehand, but I don't think it changes anything.

There were other problems with the I2S sampling. I just wanted to get to the point where the mic was being sampled via I2s continually and I could just drop in when I felt like it and grab 512 samples and get the most dominant frequency.
I have it working but its a bit messy to integrate with my existing code right now.

What I really need is a bandpass filter, which I think I will can do on the samples directly before passing into fft.

@kosme
Copy link
Owner

kosme commented Mar 20, 2023

@DrJaymz, I see a problem with the setup you describe. The sampling rate should be at least twice the highest expected frequency. Since you mention that you can hear yourself speaking, I infer you are dealing with audio. Therefore, the sampling frequency should be at least 44000.

@DrJaymz
Copy link
Author

DrJaymz commented Mar 21, 2023

The sampling frequency is 64000hz. I am using the m5stack core 2 built in spm1423 microphone. I tried 22000, then 44100 and 48000 and at 64000 I got the best sound. My window size is 512 samples.
The microphone is a PDM microphone not PCM, but I'm told that the I2S driver interface takes care of that weird format and all I need to do is just read. I couldn't find any useable documentation on any of that - but I found a demo someone else had done also using 64000 and used the same settings. Whether or not PDM leads to different effective sample rates than PCM I have no idea. But the fundamental frequency emitted from ardiuinoFFT when I use a 1kHz tone is 1.000Khz. So - guess it must be correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants