其实要说在Linux系统下播放音乐,确实是一件让人非常抓狂的事情,抛开各种音频格式的商业授权不说,即使提供给你相应的解码库,能玩儿得转的人那又是少之又少。可能有些盆友说ubuntu这方面确实做得不错,一旦默认安装好,几乎不用装任何其他东西,常见的是音频文件都可以正常播放了。因为我天生就有股喜欢折腾的劲儿,所以关于ubuntu确实不怎么感冒,只能说萝卜白菜各有所爱吧。今天我们以wav文件(也就是上一篇博文所提到的PCM格式的音频文件)为例,看看在Linux下怎么播放它,顺便会简单介绍一下Linux系统的音频驱动框架的基础知识。
   说到Linux系统下的音频系统驱动框架,最常见的有OSS和ALSA。我们先来简单了解一下这两个框架,以及它们的历史渊源。
   OSS全称是Open Sound System,叫做开放式音频系统,最早是Unix系统上一种统一的音频接口。这种基于文件系统的统一访问方式,就意味着对声音的操作完全可以像对普通文件那样执行open,read,write和close等操作,这也正是得益于文件系统的强大有力支撑。OSS中,主要提供了一下几种音频设备的抽象设备文件:
   /dev/mixer:用来访问声卡中内置的混音器mixer,用于调整音量大小和选择音源;
    /dev/dsp、/dev/audio:读这个设备就相当于录音,写这个设备就相当于放音。/dev/dsp与/dev/audio的主要区别在于所采样的PCM编码方式的不同,/dev/audio使用的是μ律编码(存在这个设备文件的目的主要是为了与SunOS兼容,所以在非SunOS系统中尽量不要使用),而/dev/dsp使用8-bit(无符号)的线性编码;
   /dev/sequencer、/dev/sequencer2:主要用于访问声卡内置的,或者连接在MIDI接口的合成器synthesizer。
    还有其他的诸如/dev/adsp、/dev/dmmidi、/dev/midi等等,一些不常用的就先不管了。看一下我的CentOS 5.3内核版本2.6.21系统中的音频设备文件:

我们可以直接使用Unix/Linux的命令来放音和录音,例如,命令cat /dev/dsp >xyz 可用来录音,录音的结果放在xyz文件中;命令cat xyz >/dev/dsp播放声音文件xyz。当然,我们还可以通过open、close、read、write、ioctl等这些文件的操作函数直接控制这些设备,达到对声音应用程序级别的访问与控制。那么这么看来OSS应该还算比较完美了,Linux下的声音编程应该没有难度才对,怎么会说Linux下声音变成是一件很头疼的事儿呢?
    其实OSS自从诞生到OSSv3版及其之前,都是Linux的原始声音系统,并集成在内核代码里。当OSS被4Front Technologies收购后,于2002年OSSv4作为商业软件的出现时,它的命运就被我们接下来要介绍的ALSA给改写了。其实严格意义上来说,商业化不是导致OSS没落的根本原因,也有技术层面的因素在,比如OSS的混音功能。由于先天的设计缺陷,OSS对混音的支持非常糟糕,由于当时的声卡本身是支持多路输出的混合,所以OSS就偷懒了,将混音的任务交给了声卡,所以那个年代的程序猿们为了操作混音器,代码里充斥着大量的ioctl函数,现在看起来相当难受。
   ALSA全称是Advanced Linux Sound Architecture,叫做Linux系统下的高级音频架构,它主要为声卡提供的驱动组件,以替代原先的 OSS。这个项目最早始于1998年Gravis Ultrasound所开发的驱动,它一直作为一个单独的软件包开发,直到2002年他被引进入Linux内核的开发版本(2.5.4-2.5.5)。自从2.6版本开始ALSA成为Linux内核中默认的标准音频驱动程序集,而OSS则被标记为废弃。所以,现在看来OSS被ALSA替代,闭源和商业化都只是外因,内因还是其设计的缺陷。虽然2007年4Front又宣布OSSv4重新在GPL协议下重新开源,但已经人去楼空秋已暮了,现在ALSA对OSS的支持也比较好了,不知道OSS还能否王者归来。其实这些都不重要,对于开发者来说,简单、便捷、高效、实用才是王道,优美的框架结构,完善的文档支持强过口水战百倍。
目前ALSA已经成为Linux系统里主流的音频系统框架,在2.6.21的内核里已经看不到OSS的影子了。在内核设备驱动层面,ALSA提供了alsa-driver,同时在应用层,ALSA也为我们提供了alsa-lib,应用程序只要调用alsa-lib所提供的API,就可以完成对底层音频硬件的控制:

上图向我们展示了ALSA的一个简单的结构,用户空间的alsa-lib对应用程序提供统一的API接口,这样可以隐藏了驱动层的实现细节,简化了应用程序的实现难度。内核空间中,alsa-soc其实是对alsa-driver的进一步封装,针对嵌入式设备提供了一些列增强的功能,通常也被叫做ASoC,即Alsa-soc的缩写,像Android系统中底层就用了ASoC。想了解ALSA更多细节的盆友可以访问他们的官网:http://www.alsa-project.org/main/index.php/Main_Page    下面,我们首先看一下OSS下如何播放wav文件:

点击(此处)折叠或打开

  1. /*playsound.c*/
  2. #include stdio.h>
  3. #include stdlib.h>
  4. #include unistd.h>
  5. #include fcntl.h>
  6. #include sys/types.h>
  7. #include sys/stat.h>
  8. #include linux/soundcard.h>
  9. #define AUDIO_DEVICE "/dev/dsp"
  10. int play_sound(char *filename,int rate,int bits){
  11. struct stat stat_buf;
  12. unsigned char *buf = NULL;
  13. int result,arg,status,handler,fd;
  14. fd = open(filename,O_RDONLY);
  15. if(fd0)
  16. return -1;
  17. if(fstat(fd,&stat_buf))
  18. {
  19. close(fd);
  20. return -1;
  21. }
  22. if(!stat_buf.st_size)
  23. {
  24. close(fd);
  25. return -1;
  26. }
  27. buf=malloc(stat_buf.st_size);
  28. if(!buf){
  29. close(fd);
  30. return -1;
  31. }
  32. if(read(fd,buf,stat_buf.st_size)0){
  33. free(buf);
  34. close(fd);
  35. return -1;
  36. }
  37. handler = open(AUDIO_DEVICE,O_WRONLY);
  38. if(-1 == handler){
  39. return -1;
  40. }
  41. arg = rate*2;
  42. status = ioctl(handler,SOUND_PCM_WRITE_RATE,&arg);
  43. if(-1 == status)
  44. return -1;
  45. arg = bits;
  46. status = ioctl(handler,SOUND_PCM_WRITE_BITS,&arg);
  47. if(-1 == status)
  48. return -1;
  49. result = write(handler,buf,stat_buf.st_size);
  50. if(-1 == result)
  51. return -1;
  52. free(buf);
  53. close(fd);
  54. close(handler);
  55. return result;
  56. }
  57. int main(int argc,char** argv){
  58. play_sound(argv[1],atoi(argv[2]),atoi(argv[3]));
  59. return 0;
  60. }

因为只是演示用,所以错误判断就少了一些。另外,为了让我们的播放程序自动获得音频文件的参数,诸如采样率,量化精度等,我又提供了一个shell脚本player:

点击(此处)折叠或打开

  1. #!/bin/sh
  2. [ "$#" -eq 0 ] &
  3. BITS=`file $1 | cut -d' ' -f9`
  4. RATE=`file $1 | cut -d' ' -f12`
  5. echo "Playing...$(file $1)"
  6. ./playsound $1 $RATE $BITS

将上述C文件编译,然后,在命令行之./player 文件名,不出意外的话就可以听到声音了,只可惜没办法演示这个过程:

我的系统确实可以听到,但是声音比较小,如果你在命令行执行amixer的话,应该可以看到下面的输出信息:

我的声卡音量居然只有75%(因为我用的虚拟机),然后一句“amixer set Master 100%”命令下去,再重新播放声音,应该就很happy了。

其实大家可能有点疑惑,不是前面介绍了半天ALSA的好处了,怎么用OSS来示范,是不是专拣软柿子捏啊。再说了,现在很多人的系统几乎都不支持OSS了,上面的代码有毛用。其实我也很不甘心,所以又重新装了CentOS6.3的虚拟系统,用ALSA的API再来播一下wav看得行不,经过N个小时的折腾,皇天不负有心人---It's OK!(新手入门,大家来找BUG吧 :) )
   内核版本2.6.32,看一下/dev目录下确实没有dsp和mixer设备文件了,取而代之的/dev/snd目录。在centos5.3里我们也见到过这个目录,但当时还只是试用阶段,现在alsa已经完全扶正了:

播放代码如下:

点击(此处)折叠或打开

  1. #include stdio.h>
  2. #include stdlib.h>
  3. #include unistd.h>
  4. #include fcntl.h>
  5. #include sys/types.h>
  6. #include sys/stat.h>
  7. #include linux/soundcard.h>
  8. #include alsa/asoundlib.h>
  9. #define ALSA_MAX_BUF_SIZE 65535
  10. int play_sound(char* filename,int rate,int bits,int channel,int order)
  11. {
  12. long loops;
  13. int rc,size,dir;
  14. snd_pcm_t *handle;
  15. snd_pcm_hw_params_t *params;
  16. snd_pcm_uframes_t frames,periodsize;
  17. snd_mixer_t *mixer;
  18. snd_mixer_elem_t *pcm_element;
  19. char *buffer;
  20. unsigned int val;
  21. FILE *fp = fopen(filename,"rb");
  22. rc = snd_pcm_open(&handle,"default",SND_PCM_STREAM_PLAYBACK,0);
  23. snd_pcm_hw_params_alloca(&params);
  24. snd_pcm_hw_params_any(handle,params);
  25. snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED);
  26. switch(order){
  27. case 1:
  28. snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
  29. break;
  30. case 2:
  31. snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
  32. break;
  33. defualt:
  34. break;
  35. }
  36. snd_pcm_hw_params_set_channels(handle,params,channel);
  37. val = rate;
  38. snd_pcm_hw_params_set_rate_near(handle,params,&val,0);
  39. snd_pcm_hw_params_get_buffer_size_max(params,&frames);
  40. frames = frames ALSA_MAX_BUF_SIZE? frames:ALSA_MAX_BUF_SIZE;
  41. rc = snd_pcm_hw_params_set_buffer_size_near(handle,params,&frames);
  42. snd_pcm_hw_params_get_period_size_min(params,&periodsize,NULL);
  43. if(!periodsize){
  44. periodsize=size/4;
  45. }
  46. rc = snd_pcm_hw_params_set_period_size_near(handle,params,&periodsize,NULL);
  47. rc = snd_pcm_hw_params(handle,params);
  48. snd_mixer_open(&mixer,0);
  49. snd_mixer_attach(mixer,"default");
  50. snd_mixer_selem_register(mixer,NULL,NULL);
  51. snd_mixer_load(mixer);
  52. for(pcm_element = snd_mixer_first_elem(mixer);pcm_element;pcm_element=snd_mixer_elem_next(pcm_element))
  53. {
  54. if(snd_mixer_elem_get_type(pcm_element)==SND_MIXER_ELEM_SIMPLE && snd_mixer_selem_is_active(pcm_element))
  55. {
  56. if(!strcmp(snd_mixer_selem_get_name(pcm_element),"Master"))
  57. {
  58. snd_mixer_selem_set_playback_volume_range(pcm_element,0,100);
  59. snd_mixer_selem_set_playback_volume_all(pcm_element,(long)100);
  60. }
  61. }
  62. }
  63. buffer = (char*)malloc(size);
  64. while(1)
  65. {
  66. rc = fread(buffer,1,size,fp);
  67. if(0== rc)
  68. break;
  69. while((rc = snd_pcm_writei(handle,buffer,size))0)
  70. {
  71. usleep(200);
  72. if(-EPIPE == rc)
  73. snd_pcm_prepare(handle);
  74. else if(0 > rc)
  75. printf("error fomr writei\n");
  76. }
  77. }
  78. snd_pcm_drain(handle);
  79. snd_pcm_close(handle);
  80. free(buffer);
  81. snd_mixer_close(mixer);
  82. fclose(fp);
  83. return 0;
  84. }
  85. int main(int argc,char** argv){
  86. play_sound(argv[1],atoi(argv[2]),atoi(argv[3]),atoi(argv[4]),atoi(argv[5]));
  87. return 0;
  88. }

然后将player脚本也对应修改一下:

点击(此处)折叠或打开

  1. #!/bin/sh
  2. [ "$#" -eq 0 ] &
  3. ORDER=`file $1 | cut -d' ' -f3`
  4. BITS=`file $1 | cut -d' ' -f9`
  5. CHANNEL=`file $1 | cut -d' ' -f11`
  6. RATE=`file $1 | cut -d' ' -f12`
  7. #channel
  8. if [ "$CHANNEL" == "stereo" ]; then
  9. CHANNEL=2
  10. else
  11. CHANNEL=1
  12. fi
  13. #platform-byte-order
  14. if [ "$ORDER" == "(little-endian)" ]; then
  15. ORDER=1
  16. else
  17. ORDER=2
  18. fi
  19. echo "Playing...$(file $1)"
  20. ./playsound $1 $RATE $BITS $CHANNEL $ORDER

编译C文件时,由于我们用了alsa库,所以gcc的编译选项要加上-lasound才可以。如果播放时声音很小,可以用amixer来调节音量。如果不幸的是你系统里找不到amixer命令的话,就用yum install alsa-utils或者下载alsa源码来安装吧。
  附件是测试用的音频文件,另外,后面我会将完整支持OSS和ALSA两种架构的最终播放代码放在github上,有需要的盆友到时候可以拿去鼓捣鼓捣,今天就先到这里吧。
  附件:news.wav

多媒体技术基础之---Come on!来点儿音乐吧的更多相关文章

  1. Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路

     Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路 先看一看我的代码运行结果. 代码运行起来初始化状态: 点击开始按钮,唱片机的机械臂匀速接近唱片磁盘,同时唱片磁盘也 ...

  2. 我在 Gitee 上发现了一个简洁又好用的网络音乐播放器!

    这几天无聊的时候我想听听歌,但我想要找一个简单快速的网络音乐播放器来用用.这时我在 Gitee 上看见一个看上去不错的开源项目 -- Hi音乐. 项目链接:https://gitee.com/hi-j ...

  3. 自律训练法 John Sehorz

    自律训练法,系1932年由德国精神医学医师John Sehorz所创立.他研究人们在催眠催眠状态下,所呈现的生理状态,如:沉重与温暖感.. ,因而,John Sehorz改以「逆向操作」之方式,由自我 ...

  4. AngularJS的学习--TodoMVC的分析

    最近一段时间一直在看AngularJS,趁着一点时间总结一下. 官网地址:http://angularjs.org/ 先推荐几个教程 1. AngularJS入门教程 比较基础,是官方Tutorial ...

  5. 看AngularJS

    最近一段时间一直在看AngularJS,趁着一点时间总结一下. 官网地址:http://angularjs.org/ 先推荐几个教程 1. AngularJS入门教程 比较基础,是官方Tutorial ...

  6. 音频分析框架pyAudioAnalysis文档

    “ pyAudioAnalysis是一个非常好用且强大的音频分析开源工具,能实现音频的特征提取.分类和回归模型的训练和执行,以及其他一些实用的功能.此外,本文档并非直译,也有部分比较简略,可以结合源码 ...

  7. HashMap之equals和hashCode小陷阱

    先以一段代码开始这篇blog. 01 public class Name { 02   03   private String first; //first name 04   private Str ...

  8. Wwise音频解决方案概述

    Wwise(Wave Works Interactive Sound Engine,Wwise基础知识,wiki)是Audiokinetic公司提供的跨平台游戏音频解决方案,有着高效完整工作流和工具链 ...

  9. 【十天自制软渲染器】DAY 01:图形学学习建议与环境搭建

    推荐直接阅读博客原文,更新更及时,阅读体验更佳 「十天自制软渲染器」这个标题我承认标题党了.在对图形学一无所知的情况下想十天自制一个软渲染器,就好似一节课没上过却试图一个晚上看完<30 天精通 ...

随机推荐

  1. input 只读不能修改

    方法1: onfocus=this.blur() <input type="text" name="input1" value="中国" ...

  2. JavaScript引用方法说明

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 使用新浪云(SAE)实现基于mySql和微信公众平台的关键字请求响应服务

    本例是作者初次尝试微信公众平台开发之作,实现传统的关键字请求响应功能.即:用户发关键字,公众号通过关键字进行检索, 在mysql数据库中读取与关键字相关的信息,并返回给用户.本例在微信订阅号(开发者模 ...

  4. ORACLE创建表之前判断表是否存在与SQL Server 对比使用

    在SQL Server 数据库中,我们在创建表之前删除表,有if exit()这样的语句,但是在oracle中却没有.如果直接使用drop table那么如果表不存在会报错,导致后续语句无法运行.因此 ...

  5. study topics

    永远不变的东西,原理 study roadmap: 1.user space: tizen power manager => suspend/resume or runtime? android ...

  6. 三、Distributing Maya Plugins

    For example, a fully implemented render utility node will have at least three files: the plug-in fil ...

  7. MSBuild简单介绍

    背景 托博客园的福,上周六,有家开发医疗行业系统的初创公司联系我,说在博客园上看到我关于WPF的几篇文章,邀请我去他们那里交流WPF相关的技术知识和心得体会.作为非大拿的我自然是受宠若惊,但对方好意相 ...

  8. PHP中使用Session配合Javascript实现文件上传进度条功能

    Web应用中常需要提供文件上传的功能.典型的场景包括用户头像上传.相册图片上传等.当需要上传的文件比较大的时候,提供一个显示上传进度的进度条就很有必要了. 在PHP .4以前,实现这样的进度条并不容易 ...

  9. NHibernate系列文章十九:NHibernate关系之多对多关系(附程序下载)

    摘要 NHibernate的多对多关系映射由many-to-many定义. 从这里下载本文的代码NHibernate Demo 1.修改数据库 添加Product表 添加ProductOrder表 数 ...

  10. ARCGIS对谷歌影像进行投影转换

    相信有不少同学会有这样的困扰,通过软件下载的谷歌遥感影像,直接用ARCGIS等专业软件打开之后发现,遥感影像有拉伸的情况,这是什么原因呢.那是因为,通过软件下载下来的遥感影像的投影信息包含的是经纬度信 ...