portaudio使用笔记《转》
原文链接:https://blog.csdn.net/gg_simida/article/details/77185755
介绍
PortAudio是一个免费、跨平台、开源的音频I/O库。看到I/O可能就想到了文件,但是PortAudio操作的I/O不是文件,而是音频设备。它能够简化C/C++的音频程序的设计实现,能够运行在Windows、Macintosh OS X和UNIX之上(Linux的各种版本也不在话下)。使用PortAudio可以在不同的平台上迁移应用程序,比如你可以把你基于PortAudio的应用程序发展一个Android版本啊。
PortAudio的API非常简单,通过一个一个简单的回调函数或者阻塞的读/写接口来录制或者播放声音。PortAudio自带了很多示例程序,比如播放正弦波形的音频信号,处理音频输入,录制回放音频,列举音频设备。
PortAudio的最新版本是V19。本文讨论的就是该版本。
范例参考
下载安装
源码下载路径
VS上编译的步骤
ubuntu下直接使用apt-get install portaudio19-dev即可
使用流程
编写一个PortAudio应用,只需要掌握回调函数即可:
- 编写一个回调函数,PortAudio在进行音频处理的时候自动调用。
- 初始化PA库,并为音频I/O打开一个流(stream)。
- 启动流,PA会在幕后调用回调函数。
- 在回调函数中你可以从inputBuffer读取音频数据,或者将音频数据写入到outputBuffer。
- 回调函数返回1,或者调用相应的函数来停止流。
- 关闭流,然后终止PA。
除了回调函数,PA还支持阻塞I/O模型,但并不是所有的功能都得到支持。所以推荐使用回调函数。
总流程图

API详解
第一步:编写回调函数
首先引入PA的头文件:
#include "portaudio.h"
回调函数会在两种情况下被PA调用:一是PA获取到了音频数据,二是PA需要音频数据作为输出时。
回调函数是一个神奇的地方,因为一些系统在一个特殊的线程中处理回调函数,甚至是通过中断来处理,这不同于程序中的其他代码。如果你想音频能够按时到达Speaker,就得保证回调函数能够快速地执行。不同的平台上,什么 样的操作是安全的,什么样的操作是不安全的,是不一样的。一个通用飞准则就是,不要做内存的分配释放操作、读写文件、printf,或者其他依赖于OS的不能在一定时间内返回的操作,也包括可能导致上下文切换的操作。
回调函数的原型如下:
typedef int PaStreamCallback( const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) ;
第二步:初始化PA
在调用任何PA函数之前,必须先调用Pa_Initialize()。它回扫描当前可用的音频设备,以便随后查询使用。像其他的PA函数一样,如果发生错误,它返回一个错误类型。你可以通过Pa_GetErrorText( err )得到出错信息。
if( err != paNoError ) printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) );
当你使用完PA后,记得终止它:
err = Pa_Terminate(); if( err != paNoError ) printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) );
第三步:打开流
随后就是打开一个输入流。跟打开一个文件很类似。可以指定是否需要音频输入/输出、音道数目、音频格式、采样率等。打开缺省的输入流,意味着打开缺省的音频设备,不需要去遍历并选择一个音频设备。
#define SAMPLE_RATE (44100)
static paTestData data; .....
PaStream *stream;
PaError err; /* Open an audio I/O stream. */
err = Pa_OpenDefaultStream( &stream, 0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
256, /* frames per buffer, i.e. the number of sample frames that PortAudio will request from the callback. Many apps may want to use paFramesPerBufferUnspecified, which tells PortAudio to pick the best,possibly changing, buffer size.*/
patestCallback, /* this is your callback function */
&data ); /*This is a pointer that will be passed to your callback*/
if( err != paNoError )
goto error;
上面的代码打开一个输出流,对于本示例,并不需要有输入的音频流,所以打开一个输出流就足够了。PA当然也支持打开一个输入流来录制,或者同时打开输入流和输出流,同时进行录制和回放,实时地音频处理也可以做到。
同时读/写时,需要注意下面几点:
- 一些平台只允许在同一个设备上同时进行读写。
- 虽然可以同时打开多个流,但是很难同步。
- 一些平台不支持在一个设备上同时打开多个流。
- PA对多个流的测试不足。
- PA调用应该在同一个线程中,或者用户负责同步。
第四步:启动、停止、终止流
PA在启动流之前是不能播放声音的,只有在调用Pa_StartStream()之后,PA才会调用用户的回调函数进行音频处理。
err = Pa_StartStream( stream ); if( err != paNoError ) goto error;
可以通过传给回调函数的用户数据块来与回调函数交互,其他交互的方法有:全局变量,IPC。需要注意是是,回调函数可能在中断中调用,与用户代码难以同步。因此要尽量避免一些很容易被破坏的复杂数据结构,比如双向链表,避免使用锁,如果信号量。这些可能导致回调函数被阻塞住,会丢掉音频数据。在一些平台上,还存在死锁的问题。
PA会一直调用回调函数直到停止流,可以通过好几种方式停止流。有的时候我们可能想等待一段时间,PA提供了函数Pa_Sleep()来让调用方睡眠指定毫秒,但它可能会等待超过指定的时间,在时间精度要求很高的场合,不推荐使用。
/* Sleep for several seconds. / Pa_Sleep(NUM_SECONDS1000);
最简单停止流的办法是调用Pa_StopStream():
err = Pa_StopStream( stream );
if( err != paNoError )
goto error;
Pa_StopStream()会保证用户在回调函数中处理的缓冲数据得到播放,这可能导致一些延迟。替代的办法是调用Pa_AbortStream()。在一些平台上,终止一个流要快一些,但是可能导致回调函数已经处理的数据不能被播放。
另一个停止流的办法是回调函数返回paComplete或者paAbort。paComplete保证最后一个缓冲区被播放,paAbort则会尽快停止。如果采用此方法,你需要在再次开始流之前调用Pa_StopStream()。
第五步:关闭流和终止PA
当处理完一个流后,应该关闭流,释放资源。
err = Pa_CloseStream( stream ); if( err != paNoError ) goto error;
如果你忘记了,一定不要忘记终止PA:
err = Pa_Terminate( ); if( err != paNoError ) goto error;
其它实用函数
PA提供两个函数来得到PA的版本,这在使用动态链接库的时候非常有用:
int Pa_GetVersion (void) const char * Pa_GetVersionText (void)
根据错误码得到出错信息:
const char * Pa_GetErrorText (PaError errorCode)
PA流有三个状态:活动的、停止的、回调停止的。如果一个流是回调停止的,应该先停止流然后再重新启动流:
PaError Pa_IsStreamStopped (PaStream *stream) PaError Pa_IsStreamActive (PaStream *stream)
获取流的信息,比如延迟、采样率:
const PaStreamInfo * Pa_GetStreamInfo (PaStream *stream)
用以同步的时间戳:
PaTime Pa_GetStreamTime (PaStream *stream)
回调函数使用的CPU数目:
double Pa_GetStreamCpuLoad (PaStream *stream)
指定格式的采样大小:
PaError Pa_GetSampleSize (PaSampleFormat format)
枚举查询音频设备
如果需要显式指定音频设备,我们就需要查询系统的音频设备。查询之前需要先初始化PA库:
int numDevices;
numDevices = Pa_GetDeviceCount();
if( numDevices < 0 )
{
printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
err = numDevices; goto error;
}
如果你想获取每个设备的信息,直接做一个循环即可:
const PaDeviceInfo *deviceInfo; for( i=0; i<numDevices; i++ )
{
deviceInfo = Pa_GetDeviceInfo( i ); ...
}
PaDeviceInfo结构体包含了丰富的设备信息,比如设备名、缺省延迟等。它有如下成员:
/** A structure providing information and capabilities of PortAudio devices.
Devices may support input, output or both input and output.
*/
typedef struct PaDeviceInfo
{
int structVersion; /* this is struct version 2 */
const char *name;
PaHostApiIndex hostApi; /**< note this is a host API index, not a type id*/
int maxInputChannels;
int maxOutputChannels;
/** Default latency values for interactive performance. */
PaTime defaultLowInputLatency;
PaTime defaultLowOutputLatency;
/** Default latency values for robust non-interactive applications (eg. playing sound files). */
PaTime defaultHighInputLatency;
PaTime defaultHighOutputLatency;
double defaultSampleRate;
} PaDeviceInfo;
但是这些信息中没有设备的采用率,如何才能得到呢?没有采样率的原因在于设备的多样性,音频设备支持的采用率差异很大,有些支持一定返回的采样率,有些只是支持一系列采样点,一些设备支持的采样率,另外的设备不能不支持。因此我们在使用设备之前,可以先测试设备的能力。
const PaStreamParameters *inputParameters;
const PaStreamParameters *outputParameters;
double desiredSampleRate;
...
PaError err;
err = Pa_IsFormatSupported( inputParameters, outputParameters, desiredSampleRate );
if( err == paFormatIsSupported )
{
printf( "Hooray!\n");
}
else
{
printf("Too Bad.\n");
}
在获得一个设备配置之后,或者你想测试下配置是否被设备支持,可以将信息填到PaStreamParameters结构体中,然后尝试打开流:
阻塞式读/写
PA的阻塞式用法,存在性能问题。但它作为V19引入的新特性,与第三方系统的兼容更加自然些,也比较容易理解。有兴趣的可以参考http://portaudio.com/docs/v19-doxydocs/blocking_read_write.html。
作者:C_GO流媒体后台开发
链接:https://www.jianshu.com/p/c8bab4b3041c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
portaudio使用笔记《转》的更多相关文章
- git-简单流程(学习笔记)
这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- NET Core-学习笔记(三)
这里将要和大家分享的是学习总结第三篇:首先感慨一下这周跟随netcore官网学习是遇到的一些问题: a.官网的英文版教程使用的部分nuget包和我当时安装的最新包版本不一致,所以没法按照教材上给出的列 ...
- springMVC学习笔记--知识点总结1
以下是学习springmvc框架时的笔记整理: 结果跳转方式 1.设置ModelAndView,根据view的名称,和视图渲染器跳转到指定的页面. 比如jsp的视图渲染器是如下配置的: <!-- ...
- 读书笔记汇总 - SQL必知必会(第4版)
本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
随机推荐
- iframe子页面获取父页面的点击事件
子页面a.php代码如下: <a href="ad.php" id="pic_ad" target="_blank"><i ...
- 英语insuraunce保险insuraunce单词
English Alternative forms insuraunce Etymology From the older form ensurance, see also assurance. Pr ...
- android黑白屏的问题
你会很奇怪,为什么有些app启动时,会出现一会儿的黑屏或者白屏才进入Activity的界面显示,但是有些app却不会如QQ手机端,的确这里要做处理一下.这里先了解一下为什么会出现这样的现象,其实很简单 ...
- 揭秘MySQL主从数据不一致
前言: 目前MySQL数据库最常用的是主从架构,大多数高可用架构也是通过主从架构演变而来.但是主从架构运行时间长久后容易出现数据不一致的情况,比如因从库可写造成的误操作或者复制bug等,本篇文章将会详 ...
- win2003下安装python3.4 + pyspider
昨天尝试了在win2003下安装python2.7.这个是文章地址:https://www.cnblogs.com/alpiny/p/11706606.html 但是程序跑了一晚上,发现有一点问题,是 ...
- document.forms使用
定义:document.forms返回form表单的集合,包含了当前DOM结构中所有的form表单. 语法: . 获取当前DOM结构中的第一个form表单. document.forms[] . 获取 ...
- webpack4.0报WARNING in configuration警告
在进行webpack打包工作时,先进行如下步骤 1). 安装webpack:推荐全局命令 cnpm install webpack -g 查看webpack版本 webpack -v 2) . 此时 ...
- Unity 渲染教程(二):着色器基础
转载:https://www.jianshu.com/p/7db167704056 这是关于渲染基础的系列教程的第二部分.这个渲染基础的系列教程的第一部分是有关矩阵的内容.在这篇文章中我们将编写我们的 ...
- JMeter的执行顺序
JMeter执行顺序如下:1.配置元件2.前置处理器3.定时器4.采样器5.后置处理器(除非服务器响应为空)6.断言(除非服务器响应为空)7.监听器(除非服务器响应为空) 只有当作用域内存在采样器时, ...
- 项目Beta冲刺(团队)——05.29(7/7)
项目Beta冲刺(团队)--05.29(7/7) 格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Beta冲刺(团队) 团队名称:为了交项目干杯 作业目标:记录Beta敏捷冲刺第7 ...