本文转载自:http://blog.csdn.net/beyondioi/article/details/6994548

Alsa是Linux高级音频接口。面对众多的音频设备,Alsa为Linux音频开发人员提供了一套标准的访问机制,使得音频开发变得十分容易。不信?下面我们就利用它编写一个简单的录音/播音程序,不过这需要你有一定的计算机语言基础。

一个典型的音频程序应该具有以下结构:

打开音频设备

为设备设置读写参数

向音频设备读/写音频数据

关闭设备

Alsa库为我们实现这些操作提供了丰富的接口。

首先让我们封装一个打开音频设备的函数:

snd_pcm_t *pcm_handle;

bool device_open(int mode){

if (snd_pcm_open (&pcm_handle, “default” , mode , 0) < 0)

return false;

return true;

}

snd_pcm_open是Alsa库提供的打开设备调用函数,这里我们指定打开缺省的音频设备,并根据参数mode将设备置为录音或是播放状态,如果设备打开成功,pcm_handle便指向该设备句柄,我们用全局变量保存起来,方便以后使用。

第二步是设置参数,参数设置不当将会导致音频设备无法正常工作。在设置参数前,我们需要了解一下各个参数的含义以及一些基本概念。

样本长度(sample):样本是记录音频数据最基本的单位,常见的有8位和16位。

通道数(channel):该参数为1表示单声道,2则是立体声。

桢(frame):桢记录了一个声音单元,其长度为样本长度与通道数的乘积。

采样率(rate):每秒钟采样次数,该次数是针对桢而言。

周期(period):音频设备一次处理所需要的桢数,对于音频设备的数据访问以及音频数据的存储,都是以此为单位。

交错模式(interleaved):是一种音频数据的记录方式,在交错模式下,数据以连续桢的形式存放,即首先记录完桢1的左声道样本和右声道样本(假设为立体声格式),再开始桢2的记录。而在非交错模式下,首先记录的是一个周期内所有桢的左声道样本,再记录右声道样本,数据是以连续通道的方式存储。不过多数情况下,我们只需要使用交错模式就可以了。

明白了各参数含义及关系后,我们开始设置参数:

int bit_per_sample; //样本长度(bit)

int period_size; //周期长度(桢数)

int chunk_byte; //周期长度(字节数)

snd_pcm_hw_params_t *params; //定义参数变量

bool device_setparams(){

snd_pcm_hw_params_t *hw_params;

snd_pcm_hw_params_malloc (&hw_params); //为参数变量分配空间

 snd_pcm_hw_params_malloc (¶ms);

snd_pcm_hw_params_any ( pcm_handle, hw_params ); //参数初始化

snd_pcm_hw_params_set_access ( pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); //设置为交错模式

snd_pcm_hw_params_set_format( pcm_handle, hw_params, SND_FORMAT_S16_LE); //使用用16位样本

snd_pcm_hw_params_set_rate_near( pcm_handle, hw_params, 44100, 0); //设置采样率为44.1KHz

snd_pcm_hw_params_set_channels( pcm_handle, hw_params, 2); //设置为立体声

snd_pcm_hw_params_get_period_size( hw_params, &period_size); //获取周期长度

bit_per_sample = snd_pcm_hw_format_physical_width( hw_params.format ); //获取样本长度

chunk_byte = period_size * bit_per_sample * hw_params.channels / 8; //计算周期长度(字节数(bytes) = 每周期的桢数 * 样本长度(bit) * 通道数 / 8 )

snd_pcm_hw_params( pcm_handle, hw_params); //设置参数

params = hw_params; //保存参数,方便以后使用

snd_pcm_hw_params_free( hw_params); //释放参数变量空间

return true;

}

这里先使用了Alsa提供的一系列snd_pcm_hw_params_set_函数为参数变量赋值。最后才通过snd_pcm_hw_params将参数传递给设备。需要说明的是正式的开发中需要处理参数设置失败的情况,这里仅做为示例程序而未作考虑。

设置好参数后便可以开始录音了。录音过程实际上就是从音频设备中读取数据信息并保存。

char *wave_buf;

int wave_buf_len;

bool device_capture( int dtime /*录音长度(单位:秒)*/){

 wave_buf_len = dtime * params.rate * bit_per_sample * params.channels / 8 ; //计算音频数据长度(秒数 * 采样率 * 桢长度)

 char *data = wave_buf = (char*)malloc( wave_buf_len ); //分配空间

int r = 0;

while ( data ?C wave_buf <= wave_buf_len ?C chunk_size ){

r = snd_pcm_readi( pcm_handle, data , chunk_size);

if ( r>0 ) data += r * chunk_byte;

else

return false

}

return true;

}

形参dtime用来确定录音时间,根据录音时间分配数据空间,再调用snd_pcm_readi从音频设备读取音频数据,存放到wave_buf中。

同样的原理,我们再添加一个播放函数,向音频设备写入数据:

bool device_play(){

char *data = wave_buf;

int r = 0;

while ( data ?C wave_buf <= wave_buf_len ?C chunk_size ){

r = snd_pcm_writei( pcm_handle, data , chunk_size);

if ( r>0 ) data += r * chunk_byte;

else

return false

}

return true;

}

最后我们给这个示例程序加上main函数

#include

bool device_open( int mode);

bool device_setparams();

bool device_capture( int dtime );

bool device_play();

char *wave_buf;

int wave_buf_len;

int bit_per_sample;

int period_size;

int chunk_byte;

int chunk_size;

snd_pcm_hw_params_t *params;

int main( int , char** ){

//录音

if (!device_open(SND_PCM_STREAM_CAPTURE ) return 1;

if (!device_setparams()) return 2;

if (!device_capture( 3 )) return 3; //录制3秒

snd_pcm_close( pcm_handle );

//播放

if (!device_open( SND_PCM_STREAM_PLAYBACK ) return 4;

if (!device_setparams()) return 5;

if (!device_play()) return 6;

snd_pcm_close( pcm_handle );

return 0;

}

这样,我们便完成了一个具有录音,播音的功能的音频程序,因为使用了alsa库,如果你使用的是gcc编译器,最后链接时记得要带上参数——lasound 。

限于篇幅,Alsa接口提供的强大功能不仅于此,有兴趣的读者可以参阅ALSA HOWTO,那上面你一定能够发现Alsa的强大之处。

使用ALSA编写自己的音频程序【转】的更多相关文章

  1. 搭建java开发环境、使用eclipse编写第一个java程序

    搭建java开发环境.使用eclipse编写第一个java程序 一.Java 开发环境的搭建 1.首先安装java SDK(简称JDK). 点击可执行文件 jdk-6u24-windows-i586. ...

  2. Java基础-接口中国特色社会主义的体制中有这样的现象:地方省政府要坚持党的领导和按 照国务院的指示进行安全生产。请编写一个java应用程序描述上述的体制现象。 要求如下: (1)该应用程序中有一个“党中央”接口:CentralPartyCommittee,该接口中 有个“坚持党的领导”方法:void partyLeader() (2)该应用程序中有一个“国务院”抽象类:StateCouncil,

    36.中国特色社会主义的体制中有这样的现象:地方省政府要坚持党的领导和按 照国务院的指示进行安全生产.请编写一个java应用程序描述上述的体制现象. 要求如下: (1)该应用程序中有一个“党中央”接口 ...

  3. Java基础-继承-编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight。小车类Car是Vehicle的子类,其中包含的属性有载人数 loader。卡车类Truck是Car类的子类,其中包含的属性有载重量payload。每个 类都有构造方法和输出相关数据的方法。最后,写一个测试类来测试这些类的功 能。

    #29.编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight.小车类Car是Vehicle的子类,其中包含的属性有载人数 loader.卡车类T ...

  4. java基础,继承类题目:编写一个Java应用程序,该程序包括3个类:Monkey类、People类和主类 E

    21.编写一个Java应用程序,该程序包括3个类:Monkey类.People类和主类 E.要求: (1) Monkey类中有个构造方法:Monkey (String s),并且有个public vo ...

  5. 编写高性能Web应用程序的10个技巧

    这篇文章讨论了: ·一般ASP.NET性能的秘密 ·能提高ASP.NET表现的有用的技巧和窍门 ·在ASP.NET中使用数据库的建议 ·ASP.NET中的缓存和后台处理 使用ASP.NET编写一个We ...

  6. Xamarin iOS编写第一个应用程序创建工程

    Xamarin iOS编写第一个应用程序创建工程 在Xcode以及Xamarin安装好后,就可以在Xamarin Studio中编写程序了.本节将主要讲解在Xamarin Studio中如何进行工程的 ...

  7. 编写简单的Mapreduce程序并部署在Hadoop2.2.0上运行

    今天主要来说说怎么在Hadoop2.2.0分布式上面运行写好的 Mapreduce 程序. 可以在eclipse写好程序,export或用fatjar打包成jar文件. 先给出这个程序所依赖的Mave ...

  8. 【ZZ】C 语言中的指针和内存泄漏 & 编写高效的C程序与C代码优化

    C 语言中的指针和内存泄漏 http://www.ibm.com/developerworks/cn/aix/library/au-toughgame/ 本文讨论了几种在使用动态内存分配时可以避免的陷 ...

  9. 使用Playground编写第一个Swift程序

    从控制台输出“HelloWorld”是我学习C语言的第一步,也是我人生中非常重要的一步.多年后的今天,我仍希望以HelloWorld作为第一步,与大家共同开启一个神奇.瑰丽的世界——Swift编程. ...

随机推荐

  1. Android基础TOP4:Tost的使用

    Activity: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xm ...

  2. [ Java ] [ UT ] [ Mock ] [ JUnit ] 單元測試的撰寫

    最近新的專案用到很多的單元測試,對於單元測試有多了一歇的了解. 先寫下大綱,後面分篇寫出總結心得. 1. 單元測試要隔離對外部的關聯 2. Mock, spy 的用法時機差異 3. JUnit 4, ...

  3. 转载:使用FileReader对象的readAsDataURL方法来读取图像文件

    文章转载自:http://blog.okbase.net/jquery2000/archive/1296.html: FileReader对象的readAsDataURL方法可以将读取到的文件编码成D ...

  4. 【译】x86程序员手册05 - 2.3寄存器

    2.3 Registers 寄存器 The 80386 contains a total of sixteen registers that are of interest to the applic ...

  5. java web项目和java项目的区别(看清IDE本质)

    想必大家在使用MyEclipse时对这两个概念不去深究.只知道是Java EE类的基本都是Web项目,而Java应用程序就是Java项目.而且很多人都愿意使用MyEclipse作为开发工具,且不说大家 ...

  6. (转)Hibernate的一级缓存

    http://blog.csdn.net/yerenyuan_pku/article/details/70148567 Hibernate的一级缓存 Hibernate的一级缓存就是指Session缓 ...

  7. iOS UIWebView 访问https绕过证书验证的方法

    @implementation NSURLRequest (NSURLRequestWithIgnoreSSL) + (BOOL)allowsAnyHTTPSCertificateForHost:(N ...

  8. 一台电脑同时使用多个Git账号

    参照 https://my.oschina.net/u/3578363/blog/2209781

  9. UpdateData

    UpdateData 类似于C语言的scanf printf函数 管理控件与关联变量之间的数据更新. updatedata(true)把界面输入的数值更新到关联变量中,updatedata(false ...

  10. SQL With As 用法Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介

    Sql 四大排名函数(ROW_NUMBER.RANK.DENSE_RANK.NTILE)简介   排名函数是Sql Server2005新增的功能,下面简单介绍一下他们各自的用法和区别.我们新建一张O ...