zoukankan      html  css  js  c++  java
  • ubuntu alsa

    今天要在linux下搞音频编程,在网上查阅了一下资料,网上很多资料都是在linux下直接对/dev/dsp进行编程的,因为在以往的linux系统中,我们是可以通过cat  xxx.wav /dev/dsp 来很容易的播放一个音频文件,在应用程序中,也可以直接操作/dev/dsp,实现声音的播放:打开->写入(实际上就能播放)->关闭。

    然而在换成了ubuntu-12.04 LST后,我发现/dev中根本找不到dsp,之前直接操作/dev/dsp的程序都无法正常运行,而是 can't find /dev/dsp.


    经过我再三查找,发现现在基本上都不用dsp了,大多数人都在用ALSA音频编程(其实我也是个菜鸟)。首先介绍一下一些关于ALSA编程的知识:

     


    1、GNU/Linux 系统下三大主流声卡驱动程序集:
    Linux 有三个主流的声卡驱动程序集:OSS/Lite(也称为OSS/Free)、OSS/Full
    (商业软件)、ALSA(自由软件)。
     
    OSS/Lite 是现在linux内核中自带的声卡驱动程序集,最初由 Hannu Savolainen
    开发。后来 Hannu 跑去开发 Open Sound System(也就是上面所说的OSS/Full)。
    由于 Hannu 的“逃跑”,RH 资助 Alan Cox 增强 Hannu 开发的驱动程序,并使它们
    完全模块化。现在 Alan Cox 是内核声卡驱程集的维护人。OSS/Lite 从kernel-2.0开
    始并入内核,现在大家使用的声卡驱程默认都是OSS/Lite中的。
     
    OSS/Full 是由 4Front Technologies 开发并销售的商业软件。它可以驱动很多
    声卡并且可以用在很多 UNIX 系统中。OSS/Full 完全兼容以前基于 OSS/Lite 开发
    的应用程序。作为一个商业软件,你虽然可以使用它,但是你得不到它的源代码,并且
    你必须为此而付钱。
     
    ALSA 是linux内核的下一代标准声卡驱动集。开始时 Jaroslav Kysela 等人为
    Gravis UltraSound Card 开发驱动程序,后来该计划改名为 ALSA「先进的linux音频
    体系」,因为他们认为 ALSA 比原来内核中的 OSS/Lite 驱动程序集更优秀,完全可以
    代替 OSS/Lite。他们是对的,ALSA 支持的声卡比 OSS/Lite 多,完全兼容以前基于
    OSS 开发的程序,SMP(多处理器) 与 线程安全设计,并且从 2.5 分支的内核开始,
    ALSA 的驱动程序集开始并入内核,大家可以在今年的 2.6 版本的内核中看到使用它们。
     
    2、为什么要使用 ALSA 开发音频程序
    首先,ALSA 是 linux 以后声卡驱动程序的标准,OSS/Lite 迟早会从内核中去除。
    开发基于 ALSA 的音频程序可以保证以后的兼容。
    其次,我们简单比较一下开发基于 OSS 与 ALSA 的方法。
    OSS 向应用程序提供了一系列的系统接口。开发基于 OSS 的应用程序需要使用
    open,close,ioctl,read,write 等低级系统调用来完成音频设备的控制、音频流的输入
    输出。
    而 ALSA 则为应用程序开发人员提供了一个优秀的音频库。利用该音频库,开发
    人员可以方便快捷地开发出自己的应用程序,细节则留给音频库内部处理。当然 ALSA
    也提供了类似于 OSS 的系统接口,不过 ALSA 的开发者建议应用程序开发者使用音频
    库而不是驱程API。
     
    3、那么我该从何开始呢
    第一步当然是安装 ALSA 驱动程序与音频库。
    当前 ALSA 有两个分支,一个是以前的0.5版本,一个是现在的0.9。ALSA的开发者
    已经不支持0.5版本了,所以我们要使用0.9。大家可以在 ALSA 的主页
    www.alsa-project.org 上下载安装。这个页面上的信息对大家安装很有用:
    www.alsa-project.org/alsa-doc ,建议先浏览一下。
     
    第二步是参考文档与例子。
    在 ALSA 的文档页面上有两篇为应用程序开发者提供的文章:
    Howto use the ALSA API [ http://www.op.net/~pbd/alsa-audio.html ]
    当然,还有音频库API的在线参考:
     
     要是你已经完成以上几步的话,那么你就应该开始开发了。
    4. ALSA设备文件结构

    我们从alsa在Linux中的设备文件结构开始我们的alsa之旅. 看看我的电脑中的alsa驱动的设备文件结构:

    $ cd /dev/snd
    $ ls -l
    
    crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0
    crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0
    crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c
    crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p
    crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p
    crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq
    crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer
    $

        我们可以看到以下设备文件:

        controlC0 -->                 用于声卡的控制,例如通道选择,混音,麦克风的控制等
        midiC0D0  -->                用于播放midi音频
        pcmC0D0c --〉               用于录音的pcm设备
        pcmC0D0p --〉               用于播放的pcm设备
        seq  --〉                        音序器
        timer --〉                       定时器

    其中,C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。从上面的列表可以看出,我的声卡下挂了6个设备,根据声卡的实际能力,驱动实际上可以挂上更多种类的设备,在include/sound/core.h中,定义了以下设备类型:

    #define SNDRV_DEV_TOPLEVEL  ((__force snd_device_type_t) 0) 
    #define SNDRV_DEV_CONTROL ((__force snd_device_type_t) 1)
    #define SNDRV_DEV_LOWLEVEL_PRE ((__force snd_device_type_t) 2)
    #define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)
    #define SNDRV_DEV_PCM ((__force snd_device_type_t) 0x1001)
    #define SNDRV_DEV_RAWMIDI ((__force snd_device_type_t) 0x1002)
    #define SNDRV_DEV_TIMER ((__force snd_device_type_t) 0x1003)
    #define SNDRV_DEV_SEQUENCER ((__force snd_device_type_t) 0x1004)
    #define SNDRV_DEV_HWDEP ((__force snd_device_type_t) 0x1005)
    #define SNDRV_DEV_INFO ((__force snd_device_type_t) 0x1006)
    #define SNDRV_DEV_BUS ((__force snd_device_type_t) 0x1007)
    #define SNDRV_DEV_CODEC ((__force snd_device_type_t) 0x1008)
    #define SNDRV_DEV_JACK ((__force snd_device_type_t) 0x1009)
    #define SNDRV_DEV_LOWLEVEL ((__force snd_device_type_t) 0x2000)  

     通常,我们更关心的是pcm和control这两种设备。

    5.一些例子,这些例子在官方文档也有,请自行查阅

    1.)显示一些PCM的类型和格式:
     #include <iostream>
    #include <alsa/asoundlib.h>
     
    int main()
    {
           std::cout << "ALSA library version: " << SND_LIB_VERSION_STR << std::endl;
     
           std::cout << "PCM stream types: " << std::endl;
     
           for (int val=0; val <= SND_PCM_STREAM_LAST; ++val)
                  std::cout << snd_pcm_stream_name((snd_pcm_stream_t)val) << std::endl;
           std::cout << std::endl;
     
           std::cout << "PCM access types: " << std::endl;
           for (int val=0; val <= SND_PCM_ACCESS_LAST; ++val)
                  std::cout << snd_pcm_access_name((snd_pcm_access_t)val) << std::endl;
           std::cout << std::endl;
     
           std::cout << "PCM subformats: " << std::endl;
           for (int val=0; val <= SND_PCM_SUBFORMAT_LAST; ++val)
                  std::cout << snd_pcm_subformat_name((snd_pcm_subformat_t)val) << " (" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << std::endl;
           std::cout << std::endl;
     
           std::cout << "PCM states: " << std::endl;
           for (int val=0; val <= SND_PCM_STATE_LAST; ++val)
                  std::cout << snd_pcm_state_name((snd_pcm_state_t)val) << std::endl;
           std::cout << std::endl;
     
     
           std::cout << "PCM formats: " << std::endl;
           for (int val=0; val <= SND_PCM_FORMAT_LAST; ++val)
                  std::cout << snd_pcm_format_name((snd_pcm_format_t)val) << " (" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << std::endl;
           std::cout << std::endl;
     
    }
    2.)打开PCM设备和设置参数
     #include <iostream>
    #include <alsa/asoundlib.h>
     
    int main()
    {
           int                               rc;
           snd_pcm_t*                         handle;
           snd_pcm_hw_params_t*      params;
           unsigned int                  val, val2;
           int                               dir;
           snd_pcm_uframes_t             frames;
     
           if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
           {
                  std::cerr << "unable to open pcm devices: " << snd_strerror(rc) << std::endl;
                  exit(1);
           }
     
           snd_pcm_hw_params_alloca(&params);
     
           snd_pcm_hw_params_any(handle, params);
     
           snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
     
           snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
     
           snd_pcm_hw_params_set_channels(handle, params, 2);
     
           val = 44100;
     
           snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
     
           if ( (rc = snd_pcm_hw_params(handle, params)) < 0)
           {
                  std::cerr << "unable to set hw parameters: " << snd_strerror(rc) << std::endl;
                  exit(1);
           }
     
           std::cout << "PCM handle name = " << snd_pcm_name(handle) << std::endl;
     
           std::cout << "PCM state = " << snd_pcm_state_name(snd_pcm_state(handle)) << std::endl;
     
           snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *)&val);
     
           std::cout << "access type = " << snd_pcm_access_name((snd_pcm_access_t)val) << std::endl;
     
           snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)(&val));
     
           std::cout << "format = '" << snd_pcm_format_name((snd_pcm_format_t)val) << "' (" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << std::endl;
     
          snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
          std::cout << "subformat = '" <<
        snd_pcm_subformat_name((snd_pcm_subformat_t)val) << "' (" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << std::endl;
     
          snd_pcm_hw_params_get_channels(params, &val);
          std::cout << "channels = " << val << std::endl;
     
          snd_pcm_hw_params_get_rate(params, &val, &dir);
          std::cout << "rate = " << val << " bps" << std::endl;
     
           snd_pcm_hw_params_get_period_time(params, &val, &dir);
          std::cout << "period time = " << val << " us" << std::endl;
     
          snd_pcm_hw_params_get_period_size(params, &frames, &dir);
          std::cout << "period size = " << static_cast<int>(frames) << " frames" << std::endl;
     
           snd_pcm_hw_params_get_buffer_time(params, &val, &dir);
          std::cout << "buffer time = " << val << " us" << std::endl;
     
           snd_pcm_hw_params_get_buffer_size(params, (snd_pcm_uframes_t *) &val);
          std::cout << "buffer size = " << val << " frames" << std::endl;
     
          snd_pcm_hw_params_get_periods(params, &val, &dir);
          std::cout << "periods per buffer = " << val << " frames" << std::endl;
     
           snd_pcm_hw_params_get_rate_numden(params, &val, &val2);
          std::cout << "exact rate = " << val/val2 << " bps" << std::endl;
     
          val = snd_pcm_hw_params_get_sbits(params);
          std::cout << "significant bits = " << val << std::endl;
     
          snd_pcm_hw_params_get_tick_time(params, &val, &dir);
          std::cout << "tick time = " << val << " us" << std::endl;
     
          val = snd_pcm_hw_params_is_batch(params);
          std::cout << "is batch = " << val << std::endl;
     
          val = snd_pcm_hw_params_is_block_transfer(params);
          std::cout << "is block transfer = " << val << std::endl;
     
          val = snd_pcm_hw_params_is_double(params);
          std::cout << "is double = " << val << std::endl;
     
           val = snd_pcm_hw_params_is_half_duplex(params);
          std::cout << "is half duplex = " << val << std::endl;
     
           val = snd_pcm_hw_params_is_joint_duplex(params);
          std::cout << "is joint duplex = " << val << std::endl;
     
           val = snd_pcm_hw_params_can_overrange(params);
          std::cout << "can overrange = " << val << std::endl;
     
          val = snd_pcm_hw_params_can_mmap_sample_resolution(params);
          std::cout << "can mmap = " << val << std::endl;
     
          val = snd_pcm_hw_params_can_pause(params);
          std::cout << "can pause = " << val << std::endl;
     
           val = snd_pcm_hw_params_can_resume(params);
          std::cout << "can resume = " << val << std::endl;
     
          val = snd_pcm_hw_params_can_sync_start(params);
          std::cout << "can sync start = " << val << std::endl;
     
          snd_pcm_close(handle);
     
          return 0;
    }
    3.)一个简单的声音播放程序
     #include <iostream>
    #include <alsa/asoundlib.h>
     
    int main()
    {
           long                             loops;
           int                               rc;
           int                                       size;
           snd_pcm_t*                         handle;
           snd_pcm_hw_params_t*      params;
           unsigned int                  val;
           int                               dir;
           snd_pcm_uframes_t             frames;
           char*                                  buffer;
     
           if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
           {
                  std::cerr << "unable to open pcm device: " << snd_strerror(rc) << std::endl;
                  exit(1);
           }
     
           snd_pcm_hw_params_alloca(&params);
     
           snd_pcm_hw_params_any(handle, params);
     
           snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
     
           snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
     
           snd_pcm_hw_params_set_channels(handle, params, 2);
     
           val = 44100;
     
           snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
     
           frames = 32;
           snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
     
           if ( (rc = snd_pcm_hw_params(handle, params)) < 0)
           {
                  std::cerr << "unable to set hw paramseters: " << snd_strerror(rc) << std::endl;
                  exit(1);
           }
     
           snd_pcm_hw_params_get_period_size(params, &frames, &dir);
           size = frames * 4;
           buffer = new char[size];
     
           snd_pcm_hw_params_get_period_time(params, &val, &dir);
     
           loops = 5000000 / val;
     
           while (loops > 0) {
                  loops--;
                  if ( (rc = read(0, buffer, size)) == 0)
                  {
                         std::cerr << "end of file on input" << std::endl;
                         break;
                  }
                  else if (rc != size)
                         std::cerr << "short read: read " << rc << " bytes" << std::endl;
     
                  if ( (rc = snd_pcm_writei(handle, buffer, frames)) == -EPIPE)
                  {
                         std::cerr << "underrun occurred" << std::endl;
                         snd_pcm_prepare(handle);
                  }
                  else if (rc < 0)
                         std::cerr << "error from writei: " << snd_strerror(rc) << std::endl;
                  else if (rc != (int)frames)
                         std::cerr << "short write, write " << rc << " frames" << std::endl;
           }
     
           snd_pcm_drain(handle);
           snd_pcm_close(handle);
           free(buffer);
     
           return 0;
    }
    4.)一个简单的记录声音的程序
     #include <iostream>
    #include <alsa/asoundlib.h>
     
    int main()
    {
           long                             loops;
           int                                       rc;
           int                                       size;
           snd_pcm_t*                         handle;
           snd_pcm_hw_params_t*      params;
           unsigned int                  val;
           int                                       dir;
           snd_pcm_uframes_t             frames;
           char*                                  buffer;
     
           if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)
           {
                  std::cerr << "unable to open pcm device: " << snd_strerror(rc) << std::endl;
                  exit(1);
           }
     
           snd_pcm_hw_params_alloca(&params);
     
           snd_pcm_hw_params_any(handle, params);
     
           snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
     
           snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
     
           snd_pcm_hw_params_set_channels(handle, params, 2);
     
           val = 44100;
           snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
     
           if ( (rc = snd_pcm_hw_params(handle, params)) < 0)
           {
                  std::cerr << "unable to set hw parameters: " << snd_strerror(rc) << std::endl;
                  exit(1);
           }
     
           snd_pcm_hw_params_get_period_size(params, &frames, &dir);
     
           size = frames * 4;
           buffer = new char[size];
     
           snd_pcm_hw_params_get_period_time(params, &val, &dir);
     
           loops = 5000000 / val;
     
           while (loops > 0)
           {
                  loops --;
                  rc = snd_pcm_readi(handle, buffer, frames);
                  if (rc == -EPIPE)
                  {
                         std::cerr << "overrun occurred" << std::endl;
                         snd_pcm_prepare(handle);
                  }
                  else if (rc < 0)
                         std::cerr << "error from read: " << snd_strerror(rc) << std::endl;
                  else if ( rc != (int)frames)
                         std::cerr << "short read, read " << rc << " frames" << std::endl;
                  rc = write(1, buffer, size);
                  if (rc != size)
                         std::cerr << "short write: wrote " << rc << " bytes" << std::endl;
           }
     
           snd_pcm_drain(handle);
           snd_pcm_close(handle);
           free(buffer);
     
           return 0;
    }

     注意:编译的时候记得加上参数,g++ xxx.cpp -o xxx -lasound;如果编译时出现如下错误:alsa/asoundlib.h: No such file or directory

    缺少一个库:

    apt-get install libasound2-dev

    OK!


     
  • 相关阅读:
    PHP :cookie用法
    php数组变化
    js引入
    python中wx模块的具体使用方法
    python随机模块random的22种函数(小结)
    python 数据库查询返回list或tuple实例
    Python使用Excel将数据写入多个sheet
    使用Python实现将多表分批次从数据库导出到Excel
    Python基础学习笔记之数据类型
    python Django 反向访问器的外键冲突解决
  • 原文地址:https://www.cnblogs.com/zxouxuewei/p/6381662.html
Copyright © 2011-2022 走看看