PortAudio详解(2015年12月1日更新)

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官网:

http://www.portaudio.com/

 

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值