正在播放带有PortAudio和sndfile的立体声.wav文件,输出是模糊的,并且调低/放慢速度

问题描述 投票:0回答:1

我一直在编写一些代码,以使用PortAudio和sndfile在c ++中播放立体声.wav文件,但是输出声音模糊不清(音调降低对我来说不是什么大问题,但它可能是问题)。几乎看起来像是在用模糊的方式播放部分垃圾数据,但是我不相信我正在使用任何变量,这可能发生在我不首先清除数据的地方。我尝试遵循一些PortAudios示例进行立体声播放,但是由于输入来自.wav文件而不是生成的文件,因此我无法完美地遵循它。我还编译并运行了一些PortAudio示例(使用立体声),并且工作正常。我不确定问题出在哪里。

Audio.h

struct AudioFile {
  SNDFILE* file = nullptr;
  SF_INFO  info;
  int      buffer_size = 512;
  int      readHead = 0;
  sf_count_t count = 1;
};

/*
 Class for handling basic audio functions
*/
class Audio {
 protected:
 public:
  /// Constructor
  Audio();
  /// Destructor
  ~Audio();
  /// Load an audio file
  AudioFile loadFile(const char* path);
  /// Play an audio file
  void playFile(AudioFile* file);
};

Audio.cpp

/// Audio constructor
Audio::Audio() {
  PaError err = Pa_Initialize();
  if (err != paNoError) std::cerr << "PAError: " << err << std::endl;

#ifdef DEBUG
  std::cout << "Initialising PortAudio" << std::endl;
  std::cout << "----------------------" << std::endl;
  std::cout << "Version: " << Pa_GetVersion << std::endl;
  std::cout << "Devices:" << std::endl;
  std::cout << "----------------------" << std::endl;

  int numDevices = Pa_GetDeviceCount();
  for (int i=0; i < numDevices; i++) {
    auto deviceInfo = Pa_GetDeviceInfo(i);
    std::cout << "Name: " << deviceInfo->name << std::endl;
    std::cout << "HostApi: " << deviceInfo->hostApi << std::endl;
    std::cout << "SampleRate: " << deviceInfo->defaultSampleRate << std::endl;
    std::cout << "InputChannels: " << deviceInfo->maxInputChannels << std::endl;
    std::cout << "OutputChannels: " << deviceInfo->maxOutputChannels << std::endl;
    std::cout << "----------------------" << std::endl;
  }
#endif
}

Audio::~Audio() {
  PaError err = Pa_Terminate();
  if (err != paNoError) std::cerr << "PAError: " << err << std::endl;
}

/* Loads an audiofile */
AudioFile Audio::loadFile(const char* path) {
  AudioFile file;
  ::memset(&file.info, 0, sizeof(file.info));
  file.file = sf_open(path, SFM_READ, &file.info);
  return file;
}

static int patestCallback(const void* inputBuffer, void* outputBuffer,
                          unsigned long                   framesPerBuffer,
                          const PaStreamCallbackTimeInfo* timeInfo,
                          PaStreamCallbackFlags statusFlags, void* userData) {
  /// Prevent warnings
  (void)inputBuffer;
  (void)timeInfo;
  (void)statusFlags;

  /// an AudioFile gets passed as userData
  AudioFile* file = (AudioFile*)userData;
  float*            out  = (float*)outputBuffer;

  sf_seek(file->file, file->readHead, SF_SEEK_SET);

  auto data   = std::make_unique<float[]>(framesPerBuffer);
  file->count = sf_read_float(file->file, data.get(), framesPerBuffer);

  for (int i = 0; i < framesPerBuffer; i++) { 
      *out++ = data[i];
  }

  file->readHead += file->buffer_size;

  if (file->count > 0) return paContinue;
  else return paComplete;
}

void Audio::playFile(AudioFile* file) {
  PaStream*          stream = nullptr;
  PaStreamParameters params;
  params.device       = Pa_GetDefaultOutputDevice();
  params.channelCount = file->info.channels;
  params.sampleFormat = paFloat32;
  params.suggestedLatency =
    Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency;
  params.hostApiSpecificStreamInfo = nullptr;

  /// Check if params work
  PaError err = Pa_IsFormatSupported(nullptr, &params, file->info.samplerate);
  if (err != paFormatIsSupported) {
    std::cerr << "PAError: " << Pa_GetErrorText(err) << std::endl;
    return;
  }

  err = Pa_OpenStream(&stream, nullptr, &params, file->info.samplerate,
                      file->buffer_size * params.channelCount, paClipOff,
                      &patestCallback, file);
  if (err != paNoError) std::cerr << "PAError: " << Pa_GetErrorText(err) << std::endl;

  err = Pa_StartStream(stream);
  if (err != paNoError)
    std::cerr << "PAError: " << Pa_GetErrorText(err) << std::endl;

  /// wait until file finishes playing
  while (file->count > 0) {}

  err = Pa_StopStream(stream);
  if (err != paNoError)
    std::cerr << "PAError: " << Pa_GetErrorText(err) << std::endl;

  err = Pa_CloseStream(stream);
  if (err != paNoError)
    std::cerr << "PAError: " << Pa_GetErrorText(err) << std::endl;

}

我也尝试过不使用data指针(使用此指针确实会产生更清晰但仍模糊的声音),然后按值将音频文件传递到playFile函数中。任何帮助表示赞赏。

c++ audio portaudio libsndfile
1个回答
0
投票

最终弄清楚了,这里有一个主要问题:

err = Pa_OpenStream(&stream, nullptr, &params, file->info.samplerate,
                      file->buffer_size * params.channelCount, paClipOff,
                      &patestCallback, file);

[我给Pa_OpenStream的buffersize *通道数,但是我应该一直给它提供buffersize,然后直接在回调函数中对framesPerBuffer进行通道调整:

static int patestCallback(const void* inputBuffer, void* outputBuffer,
                          unsigned long                   framesPerBuffer,
                          const PaStreamCallbackTimeInfo* timeInfo,
                          PaStreamCallbackFlags statusFlags, void* userData) {
  /// Prevent warnings
  (void)inputBuffer;
  (void)timeInfo;
  (void)statusFlags;

  /// an AudioFile gets passed as userData
  velox::AudioFile* file = (velox::AudioFile*)userData;
  float*            out  = (float*)outputBuffer;

  sf_seek(file->file, file->readHead, SF_SEEK_SET);

  auto data = std::make_unique<float[]>(framesPerBuffer * file->info.channels);
  file->count = sf_read_float(file->file, data.get(),
                              framesPerBuffer * file->info.channels);

  for (int i = 0; i < framesPerBuffer * file->info.channels; i++) { 
      *out++ = data[i];
  }

  file->readHead += file->buffer_size;

  if (file->count > 0) return paContinue;
  else return paComplete;
}

此更改固定了音调和模糊度,这在事后看来很有意义。

© www.soinside.com 2019 - 2024. All rights reserved.