对于playback

snd_pcm_begin

snd_pcm_commit,

貌似 commit给的frame才会使得alsa去把数据填充

转自 http://magodo.github.io/

ALSA - PCM接口

Apr 14, 2016


Table of Content


本文大部分内容纯属个人基于ALSA官网的”PCM Interface“的理解,如有理解错误的地方,欢迎邮件告诉我 :)

0. PCM

Pulse-code modulation(PCM)

将模拟信号表示为数字信号的一种方法,它是计算机,CD,数字电话以及其他数字音频设备对数字音频信号的标准表示法。在一个PCM流当中,模拟信号是按照一定间隔(采样周期)进行采集,每个采样点的采样值则被近似地量化为最接近的一个值(由采样深度决定)。

Linear pulse-code-modulation(LPCM)

PCM中的一种类型,这种类型的量化值与模拟信号强度(响度)是呈线性关系。

Non-linear pulse-code-modulation

LPCM中的一种类型PCM不同,它的量化值相对与模拟信号强度呈非线性关系。例如:u-law, A-law等。

一个PCM流的质量取决与两方面:

  • 采样频率
  • 采样深度(bit depth)

在ALSA的PCM接口当中,PCM泛指所有离散时间内的离散采样音频信号。

1. 概述

1.1 PCM设备的两种类型

PCM设备(内核level)可以简单地分为”输出(playback)”和”输入(capture)”两类。其中的方向是站在ALSA应用的角度而言,即:

  • PCM输出设备接收从ALSA应用传入的数据,再到mixer(CTL设备),然后路由至输出socket,外接的物理声卡设备(或者路由到别的PCM输入设备)
  • PCM输入设备将数据通过socket从物理声卡设备(或者经由mixer从别的PCM输出设备)读入,传入ALSA应用

    (注意:这里的PCM输入/输出设备指的是ALSA中的逻辑设备,它可以是物理的声卡设备,也可以是Plugin)

1.2 PCM设备和ALSA应用的ring buffer

具体参见这里

声卡设备在硬件层面有一个ring buffer,同时在alsa的kernel space,也维护一个ring buffer:

  • playback 当ALSA设备进入RUNNING状态,每当声卡传出一个period之后(花费1个period_time的时间),会向kernel发送硬件中断,于是kernel将另一个period的数据由自身维护的ring buffer传输至硬件声卡的buffer(瞬间);
  • capture 当ALSA设备进入RUNNING状态,声卡设备会不断地采集到数据,每采集一个period的数据后,声卡设备会发送硬件中断到kernel,kernel将数据由硬件buffer传输至自己维护的ring buffer中.

如下图所示:

作为alsa lib用户,我们只需要关心alsa-lib中的ring buffer。它由两个指针维护:

  1. 对于 playback:

    • 当前硬件正在读的sample (START)
    • 上一次应用程序写的sample (END)
  2. 对于 capture:
    • 当前硬件正在写的sample (END)
    • 上一次应用程序读的sample (START)

(参考Wiki-ring buffer)

1.2.1 ring buffer - 单位(period, frame, sample)

buffer的size可以通过ALSA library的API进行修改。如果buffer设的太大,那么一次数据的传输需要的延迟会增加。为了解决这个问题,ALSA将buffer分为一系列的period(在OSS/Free语境中称为fragment),然后以period为单位进行数据的传输。

因此,在buffer里有以下几个单位:

  • period 一个period当中存储多个多个frame
  • frame 一个frame中存储一个或多个同一时间采集的sample。多个ADC/DAC用于同一时间采集/转换多个sample,那么这几个同时间被处理的sample组成一个frame。通常,如果一个设别有N个channel,那么它的一个frame等于N个sample
  • sample 每一个采样得到的数值称为一个sample,它可能是多个字节的,大端或者小端,浮点数或者整数,有符号或者无符号

它们的关系如下图所示:

1.2.2 ring buffer - 访问方式(interleaved, non-interleaved)

Ring buffer 有三种访问方式:

  1. Interleaved access

     C0 C1 C2 C3 C0 C1 C2 C3 ....
    
  2. Non-interleaved access

     C0 C0 C0 C0 ................ C1 C1 C1 C1 ............. C2 C2 C2 C2 ............... C3 C3 C3 C3 ...........   注意:对于这种访问模式,每个通道有单独的ring buffer!
    
  3. Complex access

具体参见 PCM Ring Buffer

2. ALSA 设备打开 和 数据传输

2.1 阻塞和非阻塞打开设备

PCM设备可以以阻塞或非阻塞两种模式打开:

  1. 打开设备时,如果该设备当前正在被其他应用使用:

    • 阻塞:则调用的进程会被阻塞
    • 非阻塞:立即返回 -EBUSY给调用进程
  2. 打开设备的mode也会影响到它的标准I/O数据传输。当应用调用PCM API对该设备进行操作(例如: playback/capture)如果该设备没被其他应用使用,但是ring buffer为满(对于playback)/空(对于capture):

    • 阻塞:则调用进程会被阻塞
    • 非阻塞:立即返回 -EAGAIN给调用进程

    该阻塞模式模式可以通过snd_pcm_nonblock函数改变。

2.2 数据传输

2.2.1 读写传输(read/write)

可以用于传输interleaved/non-interleaved的数据,并且有阻塞和非阻塞两种模式

2.2.2 直接读写传输(mmap)

可以用于传输interleaved/non-interleaved/complex的数据。应用程序的调用顺序如下:

  • capture

    1. snd_pcm_start(): 启动设备
    2. snd_pcm_avail_update(): 获得当前ring buffer中已读到的数据有多少
    3. (optional) snd_pcm_wait(): 等待ring buffer 中的数据达到设置的avail_min
    4. (optional) snd_pcm_avail_update(): 如果之前调用了snd_pcm_wait, 那么需要在这里再次调用snd_pcm_avail_update
    5. snd_pcm_mmap_begin(): 获得一片一定大小的内存,这片区域内是当前可被读取的数据. 其中的frame参数是[in,out], in代表你想读写的frame数,out代表实际读写的frame数
    6. 读取数据
    7. snd_pcm_mmap_commit(): 告诉alsa driver之前那片区域已经读取完毕,将那片内存标记为not ready.这个函数的返回值表示ring buffer中应用指针实际被更新的frame数,该数值如果和你实际读写的数值不同,则有两种情况:
      • 返回值 < 0: 代表进入了某种错误状态(包括xrun)
      • 返回值 >=0 但是 != 希望读写的值:代表在commit的时候(i.e. 更新指针的时候)发生了xrun
    8. 重复2-6
  • playback

    1. snd_pcm_avail_update(): 获得当前ring buffer中可以写入的数据有多少. playback设备在打开后整个buffer size的frame都是available的,因此这里会返回buffer size
    2. snd_pcm_mmap_begin(): 获得内存进行写入
    3. 写入数据
    4. snd_pcm_mmap_commit(): 告诉alsa driver之前那片区域已经写入完毕,将那片内存标记为not ready(直到那片区域的数据已经传给声卡).这个函数的返回值表示ring buffer中应用指针实际被更新的frame数,该数值如果和你实际读写的数值不同,则有两种情况:
      • 返回值 < 0: 代表进入了某种错误状态(包括xrun)
      • 返回值 >=0 但是 != 希望读写的值:代表在commit的时候(i.e. 更新指针的时候)发生了xrun
    5. snd_pcm_start(): 启动设备。 注意,如果之前没有commit过就启动设备,返回错误(-EPIPE), 设备处于PREPARED状态
    6. snd_pcm_avail_update()
    7. (optional)snd_pcm_wait()
    8. (optional) snd_pcm_avail_update(): 如果之前调用了snd_pcm_wait, 那么需要在这里再次调用snd_pcm_avail_update
    9. snd_pcm_mmap_begin()
    10. 写入数据
    11. snd_pcm_mmap_commit()
    12. 重复6-11

注意:

  1. 比较早的ALSA版本中,如果当前设备处于resample状态,则snd_pcm_wait可能break,具体请参考这里

2.2.2.1 snd_pcm_avail_update vs snd_pcm_avail

2.2.2.2 snd_pcm_mmap_begin()

这个API的返回参数的物理意义会根据不同的access type而不同,如下图所示:

注意:这里的 offset 的单位实际上是 areas[x].step的个数。

因此,想要得到某个channel的当前period起始地址的话,可以使用类似如下的代码,适用于两种access type:

unsigned char* getMmapBeginAddress(const snd_pcm_channel_area_t area, snd_pcm_uframes_t offset)
{
    return (unsigned char*)(area.addr) + offset*(area.step/8) + area.first/8;
}

在得到了起始地址之后,如果要往里面填数据(例如填“0”)的话不能直接使用memset之类的API直接操作,而是要根据不同的access type来操作。这里提供一段片段,应该适用于两种access type:

// pcm_mmap_begin(&areas, &offset, &frames);

unsigned int byte_per_step = areas[0].step / 8;

for (unsigned i = 0; i != n_channel; ++i)
{
    unsigned char* start_address = getMmapBeginAddress(areas[i], offset);

    for (unsigned int j = 0; j != frames; ++j)
    {
        memset(start_address + (byte_per_step * j), 0, (m_pALSACfg->bitsPerSample)/8);
    }
}

// pcm_mmap_commit(offset, frames);

3. ALSA应用能看到的PCM设备状态迁移图

调用snd_pcm_state可以获取当前PCM设备的状态

SND_PCM_STATE_OPEN

表示PCM设备处于打开状态。

进入原因:

  1. snd_pcm_open调用成功
  2. snd_pcm_hw_params调用失败,目的是强制ALSA应用设置正确的硬件参数

SND_PCM_STATE_SETUP

表示PCM设备已经被正确地设置了硬件参数。此时,它正在等到snd_pcm_prepare来使设备对设置的操作(playback/capture)做准备。

进入原因:

  1. snd_pcm_hw_params调用成功
  2. snd_pcm_drop

SND_PCM_STATE_PREPARED

表示设备已经就绪。此时,ALSA应用可以调用snd_pcm_start,读或者写来进行操作。

进入原因:

  1. snd_pcm_prepare调用成功

SND_PCM_STATE_RUNNING

表示设备正在操作,即正在处理采样的数据。这个过程可以被snd_pcm_dropsnd_pcm_drain停止。

进入原因:

  1. snd_pcm_start调用成功
  2. 操作为playback,并且ALSA应用的frame超过了软件参数中设置的start threshold
  3. 操作为capture,并且ALSA应用要求读的frame超过了软件参数中设置的start threshold

SND_PCM_STATE_XRUN

表示设备overrun(capture)或者underrun(playback). 其中,前者表示ALSA应用没有及时将PCM设备的ring buffer中的数据读走,导致ring buffer满了;后者表示ALSA应用没有及时往PCM设备ring buffer里传数据,导致ring buffer空了。

进入这种情况后,建议调用snd_pcm_recover来恢复,也可以通过snd_pcm_prepare/snd_pcm_drop/snd_pcm_drain来离开此状态

进入原因:

  1. overrun/underrun发生
  2. PCM设备buffer中的frame数小于stop threshold

SND_PCM_STATE_DRAINING

表示capture设备正在等待ALSA应用将ring buffer中的数据读走。

对于playback模式的设备调用了snd_pcm_drain是不会进入这个状态的。

进入原因:

  1. 设备为capture模式,并且调用了snd_pcm_drain

SND_PCM_STATE_PAUSED

表示支持pause(可以通过snd_pcm_hw_params_can_pause来确定)的设备处于停止状态。

进入原因:

  1. 支持pause的设备调用snd_pcm_pause

SND_PCM_STATE_SUSPENDED

表示由于电源管理系统,使设备进入一种挂起状态。

对于支持resume的设备,建议调用snd_pcm_resume来离开该状态,直接进入SND_PCM_STATE_PREPARE状态(可以调用snd_pcm_hw_params_can_resume来确定)。对于不支持resume的设备,可以调用snd_pcm_prepare/snd_pcm_drop/snd_pcm_drain来离开该状态。

进入原因:

  1. 设备由于电源管理系统而进入该状态

SND_PCM_STATE_DISCONNECTED

表示设备不再连接系统,该状态下不再接受任何I/O调用。

不完整的状态迁移图

4. 错误码

  1. -EPIPE 表示设备处于 xrun (underrun for playback or overrun for capture).

  2. -ESTRPIPE 表示设备处于 suspended. 这时候需要循环调用snd_pcm_resume() 直到它返回非-EAGAIN的error( setup )或者0 ( prepare ).

  3. -EBADFD 表示设备处于一个错误的状态,这意味着应用和alsa lib之间的握手协议已经错乱了.

  4. -ENOTTY, -ENODEV 表示设备已经被物理地移除了.

5. HW/SW 参数

5.1 HW 参数

ALSA PCM设备对于硬件参数,使用了一种”逐步限制”的机制(refinement system) - snd_pcm_hw_params_t. 应用程序在一开始设定所有支持的HW参数空间,然后通过设置其中的某几个参数为定值,直到其他参数都被限定为止。最后,将设定好了的参数空间install给该设备(snd_pcm_hw_params)。之后,HW参数就不得再改变了。

5.1.1 API

对于某个HW参数,会有以下几种形式的API可供使用:

  • snd_pcm_hw_params_get_xxx 获得某个硬件参数的大约值。有些API中带dir参数(out),文档中说是表明实际的硬件参数值与该大约值的大小关系,但是实际测试发现该值不是0就是1,所以建议在get的API中不要依赖该值;

  • snd_pcm_hw_params_set_xxx 设置某个硬件参数,如果当前硬件在当前的参数空间设置下不支持该值,则设置的实际值会与期望值不同。dir(in),用于指定当期望值不被支持的时候,硬件会选用比该值小的实际值还是大的实际值。(期望值 –(dir)–> 实际值)

  • snd_pcm_hw_params_set_xxx_near 设置某个硬件参数,如果当前硬件在当前的参数空间设置下不支持该值,则设置的实际值会与期望值不同。dir(in),用于指定当期望值不被支持的时候,会选用比该值小的实际值还是大的实际值。同时,实际被设置的值也会被转换为大约值传出来,这个大约值可能由于类型关系而于实际值有所区别(例如rate的实际值是个浮点数,而大约值却是个ulong)。(期望值 –(dir)–> 实际值 –(round)–> 大约值)

    注意:有时候并不是完全这样的,例如在我的环境下如果设置rate为5005,会有以下结果:

    set_dir expect actual approx
    1 5005 5004.x 5005
    -1 5005 5005.x 5004

    这正好相反…(黑人问号脸) 所以这个dir参数还是不用为妙!

  • snd_pcm_hw_params_test_xxx 测试在硬件的当前参数空间内,某个硬件参数是否可以被设置
  • snd_pcm_hw_params_can_xxx 测试当前硬件是否支持某种功能
  • snd_pcm_xxx_name 返回某个硬件参数的名字(包括:type, stream, access, format,…)

5.1.2 rate/buffer/period的单位

  • rate 的单位是 frame/s

  • period time 的单位是us
  • period size 的单位是frame
  • period bytes 的单位是byte

  • buffer time 的单位是us
  • buffer size 的单位是frame
  • buffer bytes 的单位是byte

5.1.3 periods vs buffer size vs buffer time

  1. buffer size/time指定了ring buffer的大小,它一定是period size/time的整数倍;
  2. periods实际也是用于指定buffer的大小的.

额外地,如果指定了periods而没有指定buffer size,则实际的buffer size将会位于:[periods*period_size, buffer_size max]

5.1.4 access type

  1. 选择不同的传递API(r/w or direct), 记得使用相应的access type, 否则会返回-EBADF
  2. 不是所有设备都满足NONINTERLEAVED/INTERLEAVED,例如我的声卡只支持INTERLEAVED,因此最好在设置前用test API判断下

5.1.5 format

  1. 物理长度和有效长度的关系 在使用read/write API的时候对于buffer的读写要使用物理长度
  2. LE 和 BE
  3. 浮点数类型可以通过与int一起作为一个Union来简化多字节的操作

5.2 SW 参数

软件参数可以在任意时刻修改,包括RUNNING状态。

5.2.1 avail_min

默认值一般 == period size, 用于snd_pcm_wait, 当available的值大于avail_min时,该函数返回。

5.2.2 start threshold

PCM设备状态自动进入RUNNING的事件。注意:

  1. start_threshold默认值为 1 frame
  2. 只对readi/writei有效,对于 direct access 无效

具体地,

  • playback

    前提:设备处于 PREPARED 状态, start threshold < buffer size(i.e. start threshold enabled):

    • writei trigger: 写入后,如果ring buffer 中的数据 >= start_threshold, PCM设备进入 RUNNING
    • 手动 start (晦涩的部分 - -):
      • 如果ring buffer中没有数据,则手动start会使设备可能保持在 PREPARED 状态也可能进入 RUNNING 状态
      • 如果ring buffer中有数据,则手动start会使设备进入 RUNNING (此时相当于发生硬件中断,如果可传输的数据不足1个period,则会出现 XRUN )
  • capture

    前提:设备处于 PREPARED 状态, start threshold < buffer size(i.e. start threshold enabled):

    • readi trigger: 如果ALSA应用要求读的数据 >= start_threshold, PCM设备会进入 RUNNING 状态
    • 手动 start: 立即进入 RUNNING 状态

如果你希望手动地使Playback PCM设备进入RUNNING状态(snd_pcm_start), 则可以将该值设的比buffer size更大(e.g. MAX_INT),可是要注意,如果ring buffer中没有数据或者数据不多,则手动start可能使设备进入 XRUN 状态!

5.3 典型配置项

  1. 打开设备,确定设备类型(capture/playback)
  2. 设置硬件参数:
    1. 分配参数空间内存
    2. 获取最大参数空间
    3. 判断并设置access type
    4. 设置数据类型:
      1. format
      2. channel (near)
      3. rate (near)
    5. 设置硬件中断频率: period_size/period_time(near). 典型做法为:设置period_time, 然后由它来获取period_size,以备用
    6. 设置ring buffer大小: buffer_size/buffer_time/periods(near). 典型做法为:设置buffer_time/periods, 然后由它来获取buffer_size,以备用
    7. 设置
    8. (可选)dump hw info
    9. 释放分配的参数空间内存
  3. 设置软件参数:
    1. 分配参数空间内存
    2. 获取当前软件参数空间
    3. (可选)设置start threshold (默认为0 frame)
    4. (可选)设置avail_min (默认为 1 period size)
    5. (可选)设置period event
    6. 设置
    7. (可选)dump sw info
    8. 释放分配的参数空间内存

6. STREAM状态

这里的流的状态(status)不同于上面所提到PCM设备的状态(snd_pcm_state), 通过snd_pcm_status_get_state返回的snd_pcm_status_t结构体记录。其中包括:

  1. timestamp of trigger: snd_pcm_status_get_trigger_tstamp()
  2. timestamp of last pointer update: snd_pcm_status_get_tstamp()
  3. delay(sample): snd_pcm_status_get_delay()
  4. available count(sample): snd_pcm_status_get_avail()
  5. maximum available samples: snd_pcm_status_get_avail_max()
  6. ADC over-range count(sample): snd_pcm_status_get_overrange()

6.1 获得stream状态,更新r/w指针

7. STREAM同步

8. PCM 命名规范

9. 配置ALSA Device

9.1 物理声卡设备

ALSA世界中,物理的声卡设备由card, device, subdevice指定。

  • card 对应一张声卡设备,它可以由STR或者INT(zero-based index)表示。例如,我的Ubuntu-12.04-LTS上执行 $ aplay -l,可以得到card: 0 或者 Intel;
  • device 大部分的ALSA硬件访问都发生在这一层。它由INT(zero-based index)表示。同一声卡的不同device可以被独立地使用。通常,只要指定了__card__和__device__,就等于指定了音频信号的入/出口;
  • subdevice ALSA可以识别的最小单位。最常见的情况是,一个声卡设备的不同channel有单独的subdevice。如果传输单声道的音频,则可以在某个特定的subdevice中传输;而如果指定某个subdevice传输多声道数据,则该subdevice后面的subdevice也会被自动使用。

9.2 ALSA设备

和上面提到的物理声卡设备不同,ALSA设备由字符串来指定,它们被定义在ALSA配置文件中(通常为: /usr/share/alsa/alsa.conf, 并在其中load额外的配置文件)。ALSA设备通常是plugin的封装(各种plugin也定义在在配置文件中)。

例如, /usr/share/alsa/alsa.conf 中,定义了ALSA设备”hw”, 它使用了Plugin:hw: (比较奇怪吧…看下面的注释)

  • ALSA device: “hw” 的定义:

      pcm.hw {                                    # pcm.ALSA设备名  这里"ALSA设备名为: hw"
          @args [ CARD DEV SUBDEV ]               # ALSA设备允许有输入参数,这里定义了三个(有默认值的)输入参数
          @args.CARD {
              type string
              default {
                  @func getenv
                  vars [
                      ALSA_PCM_CARD
                      ALSA_CARD
                  ]
                  default {
                      @func refer
                      name defaults.pcm.card
                  }
              }
          }
          @args.DEV {
              type integer
              default {
                  @func igetenv
                  vars [
                      ALSA_PCM_DEVICE
                  ]
                  default {
                      @func refer
                      name defaults.pcm.device
                  }
              }
          }
          @args.SUBDEV {
              type integer
              default {
                  @func refer
                  name defaults.pcm.subdevice
              }
          }
          type hw                                 # type后面跟的是plugin的名字。这里使用的Plugin为hw, 更多plugin请见:http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html
          card $CARD
          device $DEV
          subdevice $SUBDEV
          hint {
              show {
                  @func refer
                  name defaults.namehint.extended
              }
              description "Direct hardware device without any conversions"
          }
      }
    

9.3 ALSA plugin

ALSA提供了各种功能的plugin。以下记录了其中的某几个容易混淆的概念。在探索细节之前,先了解一下plugin的工作模式,如下所示:

其中,slave 也可能是另一个plug。

9.3.1 plug

这个plugin可以用于自动转换各种硬件参数,包括: format, channels, rate. 使用plug插件的ALSA设备定义格式如下:

pcm.name {
        type plug               # Automatic conversion PCM
        slave STR               # Slave name
        # or
        slave {                 # Slave definition
                pcm STR         # Slave PCM name
                # or
                pcm { }         # Slave PCM definition
                [format STR]    # Slave format (default nearest) or "unchanged"
                [channels INT]  # Slave channels (default nearest) or "unchanged"
                [rate INT]      # Slave rate (default nearest) or "unchanged"
        }
        route_policy STR        # route policy for automatic ttable generation
                                # STR can be 'default', 'average', 'copy', 'duplicate'
                                # average: result is average of input channels
                                # copy: only first channels are copied to destination
                                # duplicate: duplicate first set of channels
                                # default: copy policy, except for mono capture - sum
        ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
                CCHANNEL {
                        SCHANNEL REAL   # route value (0.0 - 1.0)
                }
        }
        rate_converter STR      # type of rate converter
        # or
        rate_converter [ STR1 STR2 ... ]
                                # type of rate converter
                                # default value is taken from defaults.pcm.rate_converter
}

这里我的理解只停留在slave中的format, channels和rate这几个配置项。以format为例,假设,当前我要向一个playback设备,输入一个8KHz采样率,5秒长度的1KHz的正弦波,则该文件有8K * 5 = 40K个frame。我希望该设备以16KHz的采样率往外传输音频。此时,我需要一个plugplugin的设备,如下所示:

pcm.my_plug{
    type plug
    slave.pcm "hw:0,0"
    slave.rate 16000
}

这里的slave.rate 16000表示,my_plug的输出为16KHz的数据,作为slave,也就是"hw:0,0"的输入。这里有个先决条件是,"hw:0,0"在当前硬件参数空间中是支持16KHz的(可以用alsacap来测试)。

注意:在代码中打开该设备后,配置hw参数的时候设置的rate应该是与文件一致:8KHz (基本原则是:文件采样率是多少就应该以多少的采样率来读)

整个过程如下图所示:

需要注意的是,采样率转换后的文件并不是完全符合上面的公式的,使用plug插件的采样率转换功能以后,开始的一小部分和结束的一小部分音频的数据会是0.例如,我有一个8000Hz采样率双通道16位采样深度的5秒正弦波,其大小是:160,000 Byte. 在转换成44.1KHz采样率以后,理论值是:160,000 / 8000 * 441000 = 882,000 Byte. 而实际值是884,736 Byte. 通过audacity分析实际音频文件,发现开始的3ms和结束的12ms的数据都是0,整个文件的长度变为了5.015秒。多出的15ms的数据大小 = 0.015 * 44100 * 2 * 2 = 2646 byte ,约等于比理想状态多出来的部分了。

反过来,对于capture设备,设备中的rate代表采样的频率,设置设备hw参数的代码中配置的rate代表文件的采样率。

10. 实践

10.1 从Chrome浏览器中下载音乐

基本思想是:

  1. 如果Chrome直接通过ALSA接口播放,则有两种情况:
    1. 打开defaultALSA设备进行播放,需要做:
      1. 将 /usr/share/alsa/pulse-alsa.conf重命名,因为它会将defaultALSA设备设置为使用Plugin: pulse的设备;
      2. ~/.asoundrc加入:

         pcm.!default{
             type file
             slave {
                 pcm {
                     type hw
                     card 0
                     device 0
                 }
             }
             file "/tmp/a.wav"
         }
        
    2. 打开预定义的ALSA设备:hw(例如: hw:0,0):
      1. 在 /usr/share/alsa/alsa.conf中,修改pcm.hwALSA设备的定义。将以下部分:

           type hw
           card $CARD
           device $DEV
           subdevice $SUBDEV
           hint {
               show {
                   @func refer
                   name defaults.namehint.extended
               }
               description "Direct hardware device without any conversions"
           }
        

        修改为:

           type file              # 使用file plugin来代替本来的hw plugin
           slave.pcm {
               type hw
               card 0
               device 0
           }
           file "/tmp/a.wav"
        
  2. 如果Chrome使用的是PulseAudio接口,那么需要做别的处理.

于是,我进行了如下步骤:

  1. 判断浏览器是否使用pulseaudio播放还是使用ALSA接口播放
    1. 暂停PulseAudio(关机前记得回复):

       $ echo "autospawn = no" > ~/.pulse/client.conf
       $ pactl exit
      
    2. 打开Chrome,用网易云音乐播放歌曲,果然无法播放了. 因此,Chrome使用的是PulseAudio接口。

  2. 修改/usr/share/alsa/中的Plugin: pulse定义:

     #pcm.pulse {
     #    type pulse
     #    hint {
     #        show on
     #        description "PulseAudio Sound Server"
     #    }
     #}
    
     pcm.pulse {
         type file
         slave {
             pcm "hw:0,1"
         }
         file "/tmp/c.wav"
     }
    
  3. 然而,并无任何ruan用。。。未完待续。

引用

[1] PCM interface

[2] Introduction to Sound Programming with ALSA

[3] PCM Ring Buffer

[4] A close look at ALSA

[5] Linux AlSA sound notes

  • Zhaoting's Blog
  • 2015-2016
  • Made in Shanghai

ALSA 学习小记的更多相关文章

  1. mongodb入门学习小记

    Mongodb 简单入门(个人学习小记) 1.安装并注册成服务:(示例) E:\DevTools\mongodb3.2.6\bin>mongod.exe --bind_ip 127.0.0.1 ...

  2. javascript学习小记(一)

    大四了,课少了许多,突然之间就不知道学什么啦.整天在宿舍混着日子,很想学习就是感觉没有一点头绪,昨天看了电影激战.这种纠结的情绪让我都有点喘不上气啦!一点要找点事情干了,所以决定找个东西开始学习.那就 ...

  3. js 正则学习小记之匹配字符串

    原文:js 正则学习小记之匹配字符串 今天看了第5章几个例子,有点收获,记录下来当作回顾也当作分享. 关于匹配字符串问题,有很多种类型,今天讨论 js 代码里的字符串匹配.(因为我想学完之后写个语法高 ...

  4. js 正则学习小记之左最长规则

    原文:js 正则学习小记之左最长规则 昨天我在判断正则引擎用到的方法是用 /nfa|nfa not/ 去匹配 "nfa not",得到的结果是 'nfa'.其实我们的本意是想得到整 ...

  5. js 正则学习小记之NFA引擎

    原文:js 正则学习小记之NFA引擎 之前一直认为自己正则还不错,在看 次碳酸钴,Barret Lee 等大神都把正则玩的出神入化后发现我只是个战五渣.  求抱大腿,求大神调教. 之前大致有个印象,正 ...

  6. js 正则学习小记之匹配字符串优化篇

    原文:js 正则学习小记之匹配字符串优化篇 昨天在<js 正则学习小记之匹配字符串>谈到 个字符,除了第一个 个,只有 个转义( 个字符),所以 次,只有 次成功.这 次匹配失败,需要回溯 ...

  7. CSS学习小记

    搜狗主页页面CSS学习小记 1.边框的处理   要形成上图所示的布局效果,即,点选后,导航下面的边框不显示而其他的边框形成平滑的形状.相对于把导航的下面边框取消然后用空白覆盖掉下面搜索栏的边框比较而言 ...

  8. Gcd&Exgcd算法学习小记

    Preface 对于许多数论问题,都需要涉及到Gcd,求解Gcd,常常使用欧几里得算法,以前也只是背下来,没有真正了解并证明过. 对于许多求解问题,可以列出贝祖方程:ax+by=Gcd(a,b),用E ...

  9. logstash 学习小记

    logstash 学习小记 标签(空格分隔): 日志收集 Introduce Logstash is a tool for managing events and logs. You can use ...

随机推荐

  1. About SCCM 2012 UDA(User Device Affinity)

    Two useful articles about UDA: http://fbinotto.blogspot.com/2011/09/sccm-2012-user-device-affinity.h ...

  2. LintCode Edit Distance

    LintCode Edit Distance Given two words word1 and word2, find the minimum number of steps required to ...

  3. c# 引用百度地图

    <script type="text/javascript"> //创建和初始化地图函数 var map = new BMap.Map("home" ...

  4. 【Cocos2d-x 3.x】 事件处理机制源码分析

    在游戏中,触摸是最基本的,必不可少的.Cocos2d-x 3.x中定义了一系列事件,同时也定义了负责监听这些事件的监听器,另外,cocos定义了事件分发类,用来将事件派发出去以便可以实现相应的事件. ...

  5. [1]开发准备-使用C#.NET开发基于本地数据缓存的PC客户端

    小记:本人是PHPer,对C#.NET的开发只能说看得懂,也写得了功能略简单的PC客户端程序,下面的是本人开发一款名叫“理财速记”的PC客户端软件的全过程记录,期间包括比较繁琐的C#.NET资料查询等 ...

  6. 《C#编程宝典:十年典藏版》阅读笔记(1)

    1.运行时错误,使用Checked块语句进行异常检查与抛出异常. 2.值类型使用线程堆栈保存数据,数据大小大概为1M左右,引用类型使用托管堆保存数据,可以无限分配空间,因为有一个GC垃圾回收机制存在, ...

  7. [C++] Running time and Integer to String

    std::string num2str(int64_t p_vint, int8_t p_radix) { char str[48] = { 0 }; int64_t temp = 0; int64_ ...

  8. 1.13 linux笔记

    free -m 看内存脚本 rpm -ivh 显示过程 rpm -U --upgraderpm -F --freshenrpm -e --eraserpm -e koren find / -name ...

  9. Linux-Rsync服务器/客户端搭建实战

    一.需求 每晚汇总各机器的操作日志,同步到主服务器进行日志分析. 二.基础知识 rsync 分为服务器端.客户端,服务器端搭建比客户端辛苦一些(也是很简单). rsync 服务器是指以 deamon ...

  10. mysql操作记录

    use mysql;select host,user,password from user; grant all privileges on *.* to root@'%' identified by ...