使用portaudio和sndfile播放WAV文件

我是关键

我已经编写了一个使用portaudio和sndfile播放声音文件的功能。不幸的是,声音质量太差了。声音更像嘶嘶声。以下是我正在使用的函数的源代码。

#define _GLIBCXX_USE_C99_MATH 1
#include "PlaySound_config.h"
#include <boost/predef.h>

#if !defined(USE_PORTAUDIO) // {
#  define USE_PORTAUDIO 0
#  if (! BOOST_OS_CYGWIN && ! BOOST_OS_WINDOWS) // {
#    undef USE_PORTAUDIO
#    define USE_PORTAUDIO 1
#  endif // }
#endif // }

#if (PLAY_SOUND_HAVE_PORTAUDIO_H && PLAY_SOUND_HAVE_SNDFILE_H && PLAY_SOUND_HAVE_SNDFILE_HH && USE_PORTAUDIO) // {

#if (PLAY_SOUND_HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#include <portaudio.h>
#include <sndfile.hh>

#include <cmath>
#include <fstream>
#include <iostream>
#include <vector>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>

#include "PlaySound.h"
#include "PlaySoundStrings.h"

void SoundWarning(const std::string& message)
{
    std::cerr << message << std::endl;
}

bool PlaySoundFile(const std::string& soundFile, unsigned long /* volume */)
{
    const int MAX_CHANNELS = 1;
    const double SAMPLE_RATE = 11025.0;
    const unsigned long FRAMES_PER_BUFFER = 1024;
    const size_t BUFFER_LEN = 1024;
    using boost::format;
    using boost::io::group;
    std::string message;
    if (soundFile.empty())
    {
        errno = EINVAL;
        message = playSoundStrings[error_invalid_argument];
        SoundWarning(message);
        return false;
    }
    boost::filesystem::path soundFilePath(soundFile);
    if (! boost::filesystem::exists(soundFilePath))
    {
        errno = EINVAL;
        message = str(format(playSoundStrings[error_file_does_not_exist]) % soundFile.c_str());
        SoundWarning(message);
        return false;
    }
    PaError paError = Pa_Initialize();
    if (paError != paNoError)
    {
        message = str(format(playSoundStrings[error_pa_initialize_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        return false;
    }
    SNDFILE* sndFile;
    SF_INFO sfInfo;
    sndFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo);
    if (! sndFile)
    {
        message = str(format(playSoundStrings[error_sf_open_failed]) % soundFile.c_str() % sf_strerror(nullptr));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    if (sfInfo.channels > MAX_CHANNELS)
    {
        message = str(format(playSoundStrings[error_too_many_channels]) % sfInfo.channels % MAX_CHANNELS);
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    PaStream* stream = nullptr;
    PaStreamParameters paStreamParameters;
    paStreamParameters.device = Pa_GetDefaultOutputDevice();
    paStreamParameters.channelCount = sfInfo.channels;
    paStreamParameters.sampleFormat = paInt16;
    paStreamParameters.suggestedLatency = Pa_GetDeviceInfo(paStreamParameters.device)->defaultLowOutputLatency;
    paStreamParameters.hostApiSpecificStreamInfo = nullptr;
    paError = Pa_OpenStream(
        &stream, nullptr, &paStreamParameters,
        SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff,
        nullptr, nullptr);
    if (paError != paNoError || ! stream)
    {
        message = str(format(playSoundStrings[error_pa_open_stream_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    paError = Pa_StartStream(stream);
    if (paError != paNoError)
    {
        message = str(format(playSoundStrings[error_pa_start_stream_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    sf_count_t readCount = 0;
    double data[BUFFER_LEN];
    while ((readCount = sf_read_double(sndFile, data, BUFFER_LEN)))
    {
        paError = Pa_WriteStream(stream, data, BUFFER_LEN);
        if (paError != paNoError)
        {
            message = str(format(playSoundStrings[error_pa_write_stream_failed]) % Pa_GetErrorText(paError));
            SoundWarning(message);
            break;
        }
    }
    paError = Pa_CloseStream(stream);
    if (paError != paNoError)
    {
        message = str(format(playSoundStrings[error_pa_close_stream_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    Pa_Terminate();
    return true;
}

我在文章“什么是轻量级跨平台WAV播放库”中看到了一些示例代码但样本不完整。看来它只会播放文件的前五秒。我想播放整个文件。

知道我在做什么错吗?

此代码是我的PlaySound项目的一部分

我是关键

我在代码的原始版本中犯了几个错误。第一个是在我初始化PaStreamParameters结构的sampleFormat成员的行中。

在我的原始代码中,我按如下方式初始化了该成员。

paStreamParameters.sampleFormat = paInt16;

我应该按如下所示对其进行初始化。

paStreamParameters.sampleFormat = paInt32;

我的下一个错误是在调用Pa_OpenStream函数。我将sampleRate参数设置为硬编码常量,在这种情况下为11025.0。我应该将其设置为SF_INFO结构的samplerate成员的值。

我的第三个错误是使用sf_read_double函数从声音文件中读取。我最终发现了几个工作示例,包括sndfile-play应用程序,而是使用sf_read_float函数。

我的第四个错误是,我没有缩放从声音文件读取的数据,然后再将其传递给Pa_WriteStream函数。我在sndfile-play应用程序的源代码中找到了用于缩放数据的代码。

对于任何有兴趣的人,我的源代码的最终版本如下。

#define _GLIBCXX_USE_C99_MATH 1
#include "PlaySound_config.h"
#include <boost/predef.h>

#if !defined(USE_PORTAUDIO) // {
#  define USE_PORTAUDIO 0
#  if (! BOOST_OS_CYGWIN && ! BOOST_OS_WINDOWS) // {
#    undef USE_PORTAUDIO
#    define USE_PORTAUDIO 1
#  endif // }
#endif // }

#if (PLAY_SOUND_HAVE_PORTAUDIO_H && PLAY_SOUND_HAVE_SNDFILE_H && PLAY_SOUND_HAVE_SNDFILE_HH && USE_PORTAUDIO) // {

#if (PLAY_SOUND_HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <portaudio.h>
#include <sndfile.hh>

#include <cmath>
#include <fstream>
#include <iostream>
#include <vector>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>

#include "PlaySound.h"
#include "PlaySoundStrings.h"

void SoundWarning(const std::string& message)
{
    std::cerr << message << std::endl;
}

bool PlaySoundFile(const std::string& soundFile, unsigned long /* volume */)
{
    const int MAX_CHANNELS = 1;
    const size_t BUFFER_LEN = 1024;
    using boost::format;
    using boost::io::group;
    std::string message;
    if (soundFile.empty())
    {
        errno = EINVAL;
        message = playSoundStrings[error_invalid_argument];
        SoundWarning(message);
        return false;
    }
    boost::filesystem::path soundFilePath(soundFile);
    if (! boost::filesystem::exists(soundFilePath))
    {
        errno = EINVAL;
        message = str(format(playSoundStrings[error_file_does_not_exist]) % soundFile.c_str());
        SoundWarning(message);
        return false;
    }
    PaError paError = Pa_Initialize();
    if (paError != paNoError)
    {
        message = str(format(playSoundStrings[error_pa_initialize_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        return false;
    }
    SNDFILE* sndFile = nullptr;
    SF_INFO sfInfo;
    ::memset(&sfInfo, 0, sizeof(sfInfo));
    sndFile = sf_open(soundFile.c_str(), SFM_READ, &sfInfo);
    if (! sndFile)
    {
        message = str(format(playSoundStrings[error_sf_open_failed]) % soundFile.c_str() % sf_strerror(nullptr));
        SoundWarning(message);
           Pa_Terminate();
        return false;
    }
    if (sfInfo.channels > MAX_CHANNELS)
    {
        message = str(format(playSoundStrings[error_too_many_channels]) % sfInfo.channels % MAX_CHANNELS);
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    PaStream* stream = nullptr;
    PaStreamParameters paStreamParameters;
    paStreamParameters.device = Pa_GetDefaultOutputDevice();
    paStreamParameters.channelCount = sfInfo.channels;
    paStreamParameters.sampleFormat = paInt32;
    paStreamParameters.suggestedLatency = Pa_GetDeviceInfo(paStreamParameters.device)->defaultLowOutputLatency;
    paStreamParameters.hostApiSpecificStreamInfo = nullptr;
    paError = Pa_OpenStream(
        &stream, nullptr, &paStreamParameters,
        sfInfo.samplerate, paFramesPerBufferUnspecified, paClipOff,
        nullptr, nullptr);
    if (paError != paNoError || ! stream)
    {
        message = str(format(playSoundStrings[error_pa_open_stream_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    paError = Pa_StartStream(stream);
    if (paError != paNoError)
    {
        message = str(format(playSoundStrings[error_pa_start_stream_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    int subFormat = sfInfo.format & SF_FORMAT_SUBMASK;
    double scale = 1.0;
    if (subFormat == SF_FORMAT_FLOAT || subFormat == SF_FORMAT_DOUBLE)
    {
        sf_command(sndFile, SFC_CALC_SIGNAL_MAX, &scale, sizeof(scale));
        if (scale < 1e-10)
        {
            scale = 1.0;
        }
        else
        {
            scale = 32700.0 / scale;
        }
    }
    sf_count_t readCount = 0;
    float data[BUFFER_LEN];
    ::memset(data, 0, sizeof(data));
    while ((readCount = sf_read_float(sndFile, data, BUFFER_LEN)))
    {
        if (subFormat == SF_FORMAT_FLOAT || subFormat == SF_FORMAT_DOUBLE)
        {
            int m = 0;
            for (m = 0 ; m < readCount ; ++m)
            {
                data[m] *= scale;
            }
        }
        paError = Pa_WriteStream(stream, data, BUFFER_LEN);
        if (paError != paNoError)
        {
            message = str(format(playSoundStrings[error_pa_write_stream_failed]) % Pa_GetErrorText(paError));
            SoundWarning(message);
            break;
        }
        ::memset(data, 0, sizeof(data));
    }
    paError = Pa_CloseStream(stream);
    if (paError != paNoError)
    {
        message = str(format(playSoundStrings[error_pa_close_stream_failed]) % Pa_GetErrorText(paError));
        SoundWarning(message);
        Pa_Terminate();
        return false;
    }
    Pa_Terminate();
    return true;
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用Java播放WAV文件

来自分类Dev

如何使用Java播放WAV文件?

来自分类Dev

Android:如何使用MediaPlayer播放WAV文件

来自分类Dev

如何使用python无限播放“.wav”文件

来自分类Dev

从数组播放Wav文件

来自分类Dev

播放.wav文件

来自分类Dev

如何使用C#从Properties.Resources播放WAV文件

来自分类Dev

使用Naudio或Win API同时播放多个.wav文件

来自分类Dev

使用gstreamer api播放.wav文件时出错

来自分类Dev

AVAudioPlayer无法播放使用ExtAudioFileWriteAsync写入的音频(.wav)文件

来自分类Dev

不使用库播放 MP3/WAV 文件?

来自分类Dev

在Java中播放.wav文件

来自分类Dev

在Java中播放.wav文件

来自分类Dev

播放.wav文件x秒

来自分类Dev

播放wav文件python 3

来自分类Dev

ALSA应用程序可在Raspberry Pi上读取和播放WAV文件

来自分类Dev

录制然后播放WAV文件:错误(1、2147483648)和(-38、0)(API 23 Runtime Permission?)

来自分类Dev

如何安装和设置使用PortAudio的环境?

来自分类Dev

增加/减少WAV文件Python的播放速度

来自分类Dev

用NAudio处理后播放wav文件

来自分类Dev

停止在MATLAB GUI中播放wav文件

来自分类Dev

在Delphi中播放PCM Wav文件

来自分类Dev

在Chrome中播放WAV文件失败

来自分类Dev

播放时如何绘制WAV文件

来自分类Dev

停止在MATLAB GUI中播放wav文件

来自分类Dev

Android播放音频文件(.wav)

来自分类Dev

在Chrome中播放WAV文件失败

来自分类Dev

在Google Home中播放简短的WAV文件

来自分类Dev

播放使用JavaScript用base64编码的.wav声音文件