使用 libsndfile 读取具有跳跃长度的波形文件,或识别文件中的样本数

Posted

技术标签:

【中文标题】使用 libsndfile 读取具有跳跃长度的波形文件,或识别文件中的样本数【英文标题】:reading wave file with hop lengths using libsndfile, or to identify the number of samples in the file 【发布时间】:2020-07-15 12:03:09 【问题描述】:

我想读取一个波形文件,并将它们处理成 fft。这是我当前的工作代码:

#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 ;

但是,问题在于我假设并预先分配了 8192 个样本。 在这种情况下,我在 8kHz 时只有 1 秒,这意味着我只有 8000 个样本。 因此,您会看到这些值

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

如您所见,从索引 8000 到 8191,这些值为空。所以它们是多余的。 为什么我设置为 8192,是因为我想预先分配 SpectrogramnumFrames,为此我需要知道样本的数量。

问题:

我想让这段代码成为通用代码,它接受任意长度的波形文件(1 秒、10 秒、3 分钟等),因此这种预分配不再起作用。

    有没有办法找出波形文件的样本数,所以我可以根据波形文件的长度从固定的 8192 更改为可变数?

    或者,我可以分块读取波形文件,但有跳跃长度吗? 目前这不起作用,因为它不读取它们的跳数。

  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

仅供参考:我编译用

g++ ./debug_tmp.cpp $(pkg-config --libs --cflags sndfile) ;
./a.out wav/test_1s.wav 

【问题讨论】:

【参考方案1】:

    要获得.wav 文件的样本总数,您需要查看结构SF_INFO,尤其是成员frameschannels。样本总数将是这两者的乘积。

    是的,您可以通过将文件切成块来读取文件。只需指定您选择的长度并将其传递给sf_readf_double 的第三个参数。请注意,此参数表示帧,而不是样本。返回将是实际读取的帧数(如果您位于文件末尾,则实际读取的帧数将少于您要求的数量。

这是一个 C 代码示例,其中显示了样本总数,然后我通过使用我自愿选择的奇怪数字 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;

【讨论】:

以上是关于使用 libsndfile 读取具有跳跃长度的波形文件,或识别文件中的样本数的主要内容,如果未能解决你的问题,请参考以下文章

在 C 中使用 libsndfile 读取 .wav 文件

在 C++ 中使用 libsndfile 从 WAV 文件中提取原始音频数据

如何从 MATLAB 的 audioread 等 libsndfile 库中读取数组格式的音频文件

qt中使用libsndfile实时播放声音c++

libsndfile sf_readf_short 最大帧数

如何使用 libsndfile 在音频文件中打印静音?