0

I would like to read a wave file, and process them into fft. this is my current working code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sndfile.h>
#include <iostream>
#include <vector>

#include "fftw-3.3.8/api/fftw3.h"

using namespace std;

#define ARRAY_LEN(x)    ((int) (sizeof (x) / sizeof (x [0])))
#define MAX(x,y)        ((x) > (y) ? (x) : (y))
#define MIN(x,y)        ((x) < (y) ? (x) : (y))

vector<double> read_audio_vector(const char* filePath){
  SNDFILE *infile ;
  SF_INFO sfinfo ;
  double buffer [8192] = {};
  vector<double> output_buffer(8192, 0);
  sf_count_t count ;
  cout << "Reading from : " << filePath << endl;
 
  memset (&sfinfo, 0, sizeof (sfinfo)) ;
  if ((infile = sf_open (filePath, SFM_READ, &sfinfo)) == NULL)     {     
    printf ("Error : Not able to open input file '%s'\n", filePath);
    sf_close (infile);
    exit (1) ;
  } 

  count = sf_read_double (infile, buffer, ARRAY_LEN (buffer));
  for (int j=0; j<8192; ++j){
    output_buffer[j] = buffer[j];
  }
  
  sf_close (infile) ;
  return output_buffer;
}

vector<vector<double> > computeFullFFT_vector(int frameSize, int numFrames, vector<double> buffer ){
  vector<double> audioFrame(frameSize,0);
  vector<double> magnitudeSpectrum(frameSize/2,0);
  vector<vector<double> > Spectrogram(numFrames, vector<double>(frameSize/2));
  int startidx;
  for (int frameidx=0; frameidx<numFrames; ++frameidx){
    // Extract frame from buffer, with a hop of 128
    startidx=frameidx*128;
    for (int i = 0; i < frameSize; i++){
        audioFrame[i] = buffer[startidx+i];
    }
    // performFFT && Update -> Spectrogram
  }
  return Spectrogram;
}

int main (int argc, char ** argv) {
  // Init
    SNDFILE *infile ;
    SF_INFO sfinfo ;
    int frameSize = 256;

  // Read Audio
    cout << "\n==== Read Audio ===== \n";
    vector<double> x = read_audio_vector(argv[1]);

    cout << "--x.size() : " << x.size() << endl;
    int i;
    i=0;    cout << "x[" << i << "] : " << x[i] << endl;
    i=7999; cout << "x[" << i << "] : " << x[i] << endl;
    i=8000; cout << "x[" << i << "] : " << x[i] << endl;
    i=8191; cout << "x[" << i << "] : " << x[i] << endl;

  // Process FFT here
    int numFrames = (8192-frameSize)/128 + 1;
    vector<vector<double> > Spectrogram(numFrames, vector<double>(frameSize/2));
    Spectrogram = computeFullFFT_vector(frameSize, numFrames, x);

  cout << "Done" << endl;
  return 0 ;
}

However, the problem with is that i assumed and pre-allocated 8192 number of samples. In this case, i have only 1 second at 8kHz, meaning i only have 8000 samples. Hence you see these values

buffer[0] : 0.176361
buffer[7999] : 0.025177
buffer[8000] : 0
buffer[8191] : 0

As you can see, from index 8000 to 8191, these values are empty. So they are redundant. Why i set to 8192, is because i want to pre-allocate Spectrogram with numFrames, and to do that i need to know the number of samples.

Problem:

I want to make this code a general-purpose code, that accepts a wave file of any length, (1s, 10sec, 3minutes, etc), so this pre-allocation does not work anymore.

  1. Is there a way to find out the number of samples of the wave file, so i can change from a fixed 8192 to a variable number depending on the length of the wave files?

  2. Alternatively, can i read the wave file in chunks, but with hop length? Currently this doesn't work because it doesn't read them with hop length.

  int num_frames = 0;
  while ((count = sf_read_double (infile, buffer, ARRAY_LEN (buffer))) > 0) {
    for (int i = 0; i < 256; i++){
      buffer[i] *= 0.5;
    }
    num_frames++;
  }
  cout << "num_frames=" << num_frames; // this gives 32 frames, instead of the 63 frames that i desire

FYI : i compile with

g++ ./debug_tmp.cpp $(pkg-config --libs --cflags sndfile) ;
./a.out wav/test_1s.wav 
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
leonardltk1
  • 257
  • 1
  • 4
  • 18

1 Answers1

0
  1. To get the total number of samples of your .wav file, you need to look at the structure SF_INFO and especially the members frames and channels. The total number of samples will be the product of these two.

  2. Yes you can read your file by cutting it in chunks. Simply specify the length of your choice and pass it in third argument of sf_readf_double. Note that this argument means frames, and not samples. The return will be the actual amount of frames read (in case of you are at the end of the file, the number of frames actually read will be less that the number you asked.

Here is a code example in C where I show the total number of samples, then I reduce the volume of the audio by cutting in chunks with a voluntary weird number of my choice 147.

#include <stdio.h>
#include "sndfile.h"

#define MONO                        1
#define DATA_BLOCK_LENGTH           147 // Frames

int main(void) {
  int i;

  // Input file
  char *inFileName;
  SNDFILE *inFile;
  SF_INFO inFileInfo;
  inFileName = "audioFiles/whiteNoise.wav";

  // Get total number of samples
  inFile = sf_open(inFileName, SFM_READ, &inFileInfo);
  if(inFile == NULL)
    printf("Audio file error.\n");

  int nFrames = (int)inFileInfo.frames;

  // Print results
  printf("Total number of frames: %d\n", nFrames);
  printf("Number of channels: %d\n", inFileInfo.channels);
  printf("Total number of samples: %d", nFrames * inFileInfo.channels);

  // Output file
  char *outFileName;
  SNDFILE *outFile;
  SF_INFO outFileInfo;
  outFileName = "audioFiles/outWhiteNoise.wav";
  outFileInfo.frames        = inFileInfo.frames;
  outFileInfo.samplerate    = inFileInfo.samplerate;
  outFileInfo.channels      = inFileInfo.channels;
  outFileInfo.format        = inFileInfo.format;
  outFile = sf_open(outFileName, SFM_WRITE, &outFileInfo);

  // Process
  int inDataBuffer[DATA_BLOCK_LENGTH*MONO];
  int outDataBuffer[DATA_BLOCK_LENGTH*MONO];

  int nReadFrames;

  while(nFrames > 0) {
    nReadFrames   = sf_readf_int(inFile, inDataBuffer, DATA_BLOCK_LENGTH);

    for(i = 0; i < DATA_BLOCK_LENGTH; i++)
      outDataBuffer[i] = inDataBuffer[i] / 2;

    sf_writef_int(outFile, outDataBuffer, nReadFrames);

    nFrames       -= nReadFrames;
  }

  sf_close(inFile); sf_close(outFile);

  return 0;
}
alpereira7
  • 1,522
  • 3
  • 17
  • 30