PortAudio详解
整理者:赤子玄心
QQ:280604597
Email:280604597@qq.com
大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的
1 简介
PortAudio是一个免费、跨平台、开源的音频I/O库。看到I/O可能就想到了文件,但是PortAudio操作的I/O不是文件,而是音频设备。它能够简化C/C++的音频程序的设计实现,能够运行在Windows、Macintosh OS X和UNIX之上(Linux的各种版本也不在话下)。使用PortAudio可以在不同的平台上迁移应用程序,比如你可以把你基于PortAudio的应用程序发展一个Android版本啊。
PortAudio的API非常简单,通过一个一个简单的回调函数或者阻塞的读/写接口来录制或者播放声音。PortAudio自带了很多示例程序,比如播放正弦波形的音频信号,处理音频输入,录制回放音频,列举音频设备。
PortAudio使用的是自己定义的License,关键点在于:
1、允许在你的项目或程序中免费使用PortAudio,商业软件也是免费使用的。
2、可以不开源你的源码。
3、禁止删除PortAudio中的版权信息。
4、如果你修复了PortAudio中的bug,请通知社区。
5、如果你的程序因为PortAudio而崩溃,我们不负任何责任。
PortAudio的最新版本是V19。本文讨论的就是该版本。
2 PortAudio V19库编译
2.1 准备
PortAudio官网:
PortAudio库API英文详解:
http://www.portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a443ad16338191af364e3be988014cbbe
DirectX SDK下载:
http://www.microsoft.com/en-us/download/details.aspx?id=6812
PortAudio下载:
http://portaudio.com/docs/v19-doxydocs/compile_windows.html
ASIO SDK下载:
http://download.csdn.net/detail/linyiqinggood/6778175
PortAudio是采集和播放音频的开源库,可以用于Linux和windows,在windows下依赖DirectX库(Directshow)用于驱动声卡,所以必须安装DirectX sdk库。
安装DirectX sdk库参考:
http://blog.sina.com.cn/s/blog_b5c2c06f01016cu5.html
portaudioV19的安装集合包下载地址:
http://download.csdn.net/detail/yanmy2012/4655561
2.2 编译方式
Windows编译参考http://portaudio.com/docs/v19-doxydocs/compile_windows.html
其他的编译方式http://portaudio.com/docs/v19-doxydocs/pages.html
由于V19 相对于V18改了非常多的地方,所以只编译V19的库,例程只有较新的才能在V19中编译通过,有些会显示找不到类型和函数申明。
2.3 编译过程
下面列出的步骤来建立PortAudio成一个dll和lib文件。得到的DLL文件可能包含所有五个目前的win32 PortAudio的API:MME,DirectSound的,WASAPI,WDM / KS和ASIO,根据下面的步骤9中设置的预处理器定义。PortAudio可以被编译使用Visual C + + Express版是由微软免费提供。如果你有一个C + +开发环境,只需下载并安装。这些指令已经成功观察到使用Visual Studio 2010和。
1) PortAudio为Windows需要的文件dsound.h的和dsconf.h。下载并安装DirectX SDK 获得这些文件。如果你安装了DirectX SDK!的DirectSound的库和头文件的自动加到 Visual C + +中。
如果你得到一个错误说缺少dsound.h,或dsconf.h的,你可以添加这些路径。或者,您可以复制dsound.h和dsconf.h,到portaudio\。还应该有一个名为“dsound.lib”C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib文件。
2) 支持ASIO,下载ASIO SDK在http://www.steinberg.net/en/company/developer.html。 SDK是免费的,但您将需要与斯坦伯格建立一个开发者帐号,所以我提供了下载文件,这个也是从网上收回来的。复制整个ASIOSDK2的到src文件夹\hostapi\ASIO\。重命名它,从ASIOSDK2到ASIOSDK。
3) 如果您的Visual Studio 6.0,7.0(VC.NET/2001)或7.1(VC.2003),打开portaudio.dsp并转换如果需要的话。
4) 如果你有Visual Studio 2005中的Visual C + + 2008 Express Edition或Visual Studio 2010中,双击portaudio.sln的文件位于在build \ MSVC \。这样做将打开Visual Studio或Visual C + +。点击“Finish”如果出现一个向导。sln文件中包含四种配置:Win32和Win64的发布和调试的变种。
对于Visual Studio 2005中的Visual C + + 2008 Express版或Visual Studio 2010中
5) 打开项目 - > portaudio“属性”,在树视图中选择“配置属性”。
6) 选择“配置”中的“配置”组合框。选择“所有平台”
7) 设置几个选项:
C/C++—〉优化—〉省略框架指针= YES
C/C++—〉代码生成—〉运行时库/MT
可选:C/C++—〉代码生成—〉浮点模型=快速
注:对于大多数用户来说,它是没有必要显式地设置结构成员对齐,默认情况下应该正常工作。然而,一些语言要求,例如,4字节对齐。如果您有portaudio.h结构的成员没有被正确读取或写入的问题,可能有必要显式地设置这个值由C/C++—〉代码生成—〉结构成员对齐,将其设置为一个适当的值(四是共同的价值)。如果你的编译器是可配置的,你应该确保它被设置为使用相同的结构成员对齐值所使用的PortAudio构建。
当你设置完这些参数后点击“确定”。
预处理器定义
由于预处理器定义是不同的,每个配置和平台,你需要编辑这些单独为每个配置/平台组合,你想修改的“配置”和“平台”组合框。
8) 为了抑制的PortAudio运行调试控制台输出项目—〉属性—〉配置属性—〉C/C++—〉预处理器。在该领域的预处理器定义,找到PA_ENABLE_DEBUG_OUTPUT并删除它。控制台将不输出调试信息。
9) 你需要明确地定义你想使用的音频API的预处理器定义。对于Windows提供的API定义是:
PA_USE_ASIO
PA_USE_DS(DirectSound的)
PA_USE_WMME(MME)
PA_USE_WASAPI
PA_USE_WDMKS
PA_USE_SKELETON
对于每个这样的,值为0表示不应列入这个API的支持。值1表示应该包括这个API的支持。
设置预处理器定义时,build是配置每个平台的过程。按照这些说明build你感兴趣的每一个配置/平台组合。
以上宏在“项目—〉属性—〉配置属性—〉C/C++—〉预处理器—〉预处理器定义”中定义的。
10) 从“生成”菜单上单击“生成” - >“生成解决方案”。对于32位编译的dll文件创建的这个过程(portaudio_x86.dll)中可以找到的目录生成\ MSVC \ WIN32 \发布。64位编译的DLL文件被称为portaudio_x64.dll,被发现在目录中生成\ MSVC \ X64 \发布。
11) 现在,任何项目需要portaudio可以与portaudio_x86.lib(或_x64),包括您可能要添加/删除一些DLL 相关的头(portaudio.h,和/或pa_asio.h,pa_x86_plain_converters.h),入口点。现在,这6个项目是不是从portaudio.h:
最后会生成portaudio_x86.lib和portaudio_x86.dll,各两个分别是Debug和Release的,不同配置下使用不同dll和lib。
安装完之后就是测试,源代码文件夹test文件夹下有测试代码,由于V19 相对于V18改了非常多的地方,所以只编译V19的库,例程只有较新的才能在V19中编译通过,有些会显示找不到类型和函数申明。
patest_record.c代码,运行过程中会有5秒的嘟嘟声(由的算法产生的float点型级别的声波),这样就证明了已经安装成功。
#include "portaudio.h"
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (1024)
#define NUM_SECONDS (5)
#define NUM_CHANNELS (2)
#define DITHER_FLAG (0)
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE (0.0f)
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE (128)
#define PRINTF_S_FORMAT "%d"
#endif
typedef struct
{
int frameIndex;
int maxFrameIndex;
SAMPLE *recordedSamples;
}
paTestData;
static int recordCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void) outputBuffer;
(void) timeInfo;
(void) statusFlags;
(void) userData;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = paComplete;
}
else
{
framesToCalc = framesPerBuffer;
finished = paContinue;
}
if( inputBuffer == NULL )
{
for( i=0; i
{
*wptr++ = SAMPLE_SILENCE;
if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE;
}
}
else
{
for( i=0; i
{
*wptr++ = *rptr++;
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;
}
}
data->frameIndex += framesToCalc;
return finished;
}
static int playCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
SAMPLE *wptr = (SAMPLE*)outputBuffer;
unsigned int i;
int finished;
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
(void) inputBuffer;
(void) timeInfo;
(void) statusFlags;
(void) userData;
if( framesLeft < framesPerBuffer )
{
for( i=0; i
{
*wptr++ = *rptr++;
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;
}
for( ; i
{
*wptr++ = 0;
if( NUM_CHANNELS == 2 ) *wptr++ = 0;
}
data->frameIndex += framesLeft;
finished = paComplete;
}
else
{
for( i=0; i
{
*wptr++ = *rptr++;
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;
}
data->frameIndex += framesPerBuffer;
finished = paContinue;
}
return finished;
}
int main(void);
int main(void)
{
PaStreamParameters inputParameters,
outputParameters;
PaStream* stream;
PaError err = paNoError;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, val;
double average;
printf("patest_record.c\n"); fflush(stdout);
data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE;
data.frameIndex = 0;
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes );
if( data.recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
goto done;
}
for( i=0; i
err = Pa_Initialize();
if( err != paNoError ) goto done;
inputParameters.device = Pa_GetDefaultInputDevice();
inputParameters.channelCount = 2;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
recordCallback,
&data );
if( err != paNoError ) goto done;
err = Pa_StartStream( stream );
if( err != paNoError ) goto done;
pr