当前位置: 首页 > news >正文

alsa 编程

ALSA(Advanced Linux Sound Architecture)是由内核驱动,标准的API库和一系列实用程序组成.因为涉及到版权和BUG的问题Linux 2.6内核抛弃了旧的OSS,ALSA作为声音编程的生力军被作为了合并到了内核中.

 

数字音频基础:

音频是由电器设备(麦克风等)将空气的变化转化成的电信号.模数转换器(A/D)将模拟电压转化成一系列不连续的值称之为采样,然后将采样值送往数模转化器(D/A)从而将声音还原.采样的频率是影响数字声音质量的一个关键因素,由Nyquist采样定理知,采样的频率至少是信号中最高频率的2倍方能的还原原始信号.

ALSA基础知识:

 ALSA由许多声卡的声卡驱动程序组成,同时它也提供一个称为libasound的API库。应用程序开发者应该使用libasound而不是内核中的ALSA接口。因为libasound提供最高级并且编程方便的编程接口。并且提供一个设备逻辑命名功能,这样开发者甚至不需要知道类似设备文件这样的低层接口。相反,OSS/Free驱动是在内核系统调用级上编程,它要求开发者提供设备文件名并且利用ioctrl来实现相应的功能。为了向后兼容,ALSA提供内核模块来模拟OSS,这样之前的许多在OSS基础上开发的应用程序不需要任何改动就可以在ALSA上运行。另外,libaoss库也可以模拟OSS,而它不需要内核模块。ALSA包含插件功能,使用插件可以扩展新的声卡驱动,包括完全用软件实现的虚拟声卡。ALSA提供一系列基于命令行的工具集,比如混音器(mixer),音频文件播放器(aplay),以及控制特定声卡特定属性的工具.

 

ALSA接口:

控制接口:用来管理已注册的声卡并检查其可用的设备

 

PCM接口:用来管理数字音频的录音和回放,这是一个用的最广泛的接口,我们将在下文中着重介绍.

 

原始 MIDI 接口:支持标准MIDI(Musical Instrument Digital Interface),提供了访问声卡MIDI的接口.

 

时间接口:用来声卡的计时声音事件的同步

 

Sequencer接口:高级MIDI和声音合成接口,可以处理更多的MIDI协议

 

混音接口:用来声卡设备的信号处理和音量,建立在控制接口之上

 

设备命名:

 API库使用逻辑设备名而不是设备文件。设备名字可以是真实的硬件名字也可以是插件名字。硬件名字使用hw:i,j这样的格式。其中i是卡号,j是这块声卡上的设备号。第一个声音设备是hw:0,0.这个别名默认引用第一块声音设备并且在本文示例中一直会被用到。插件使用另外的唯一名字。比如plughw:,表示一个插件,这个插件不提供对硬件设备的访问,而是提供像采样率转换这样的软件特性,硬件本身并不支持这样的特性。

 

声音缓存和数据传输:

  每个声卡都有一个硬件缓存区来保存记录下来的样本。当缓存区足够满时,声卡将产生一个中断。内核声卡驱动然后使用直接内存(DMA)访问通道将样本传送到内存中的应用程序缓存区。类似地,对于回放,任何应用程序使用DMA将自己的缓存区数据传送到声卡的硬件缓存区中。

      这样硬件缓存区是环缓存。也就是说当数据到达缓存区末尾时将重新回到缓存区的起始位置。ALSA维护一个指针来指向硬件缓存以及应用程序缓存区中数据操作的当前位置。从内核外部看,我们只对应用程序的缓存区感兴趣,所以本文只讨论应用程序缓存区。

 

      应用程序缓存区的大小可以通过ALSA库函数调用来控制。缓存区可以很大,一次传输操作可能会导致不可接受的延迟,我们把它称为延时(latency)。为了解决这个问题,ALSA将缓存区拆分成一系列周期(period)(OSS/Free中叫片断fragments).ALSA以period为单元来传送数据。

 

      一个周期(period)存储一些帧(frames)。每一帧包含时间上一个点所抓取的样本。对于立体声设备,一个帧会包含两个信道上的样本。图1展示了分解过程:一个缓存区分解成周期,然后是帧,然后是样本。图中包含一些假定的数值。图中左右信道信息被交替地存储在一个帧内。这称为交错(interleaved)模式。在非交错模式中,一个信道的所有样本数据存储在另外一个信道的数据之后。

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

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

 

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

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

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

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

 

一个典型的应用程序:

一个典型的声音程序使用PCM的程序通常类似下面的伪代码:

 

打开回放或录音接口

设置硬件参数(访问模式,数据格式,信道数,采样率,等等)

while(有数据要被处理)

{

   读PCM数据(录音)或 写PCM数据(回放)

}

关闭接口

下面我们介绍声音回放和,录音的例子程序作为对以上内容的总结

/*

alsa_pcm_playback.c

从标准的声音输入设备中声音并写到默认的PCM设备中

*/

 

#define ALSA_PCM_NEW_HW_PARAMS_API

 

#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;

  

  /* 打开PCM设备用来回放 */

  rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_PLAYBACK, 0);

  if (rc < 0)

 {

    fprintf(stderr, 

            "unable to open pcm device: %s\n",

            snd_strerror(rc));

    exit(1);

  }

  

  /* Allocate a hardware parameters object. */

  snd_pcm_hw_params_alloca(&params);

 

  /* Fill it in with default values. */

  snd_pcm_hw_params_any(handle, params);

 

  /* Set the desired hardware parameters. */

 

  /* Interleaved mode */

  snd_pcm_hw_params_set_access(handle, params,

                      SND_PCM_ACCESS_RW_INTERLEAVED);

 

  /* Signed 16-bit little-endian format */

  snd_pcm_hw_params_set_format(handle, params,

                              SND_PCM_FORMAT_S16_LE);

 

  /* Two channels (stereo) */

  snd_pcm_hw_params_set_channels(handle, params, 2);

 

  /* 44100 bits/second sampling rate (CD quality) */

  val = 44100;

  snd_pcm_hw_params_set_rate_near(handle, params, 

                                  &val, &dir);

 

  /* Set period size to 32 frames. */

  frames = 32;

  snd_pcm_hw_params_set_period_size_near(handle,

                              params, &frames, &dir);

 

  /* Write the parameters to the driver */

  rc = snd_pcm_hw_params(handle, params);

  if (rc < 0) {

    fprintf(stderr, 

            "unable to set hw parameters: %s\n",

            snd_strerror(rc));

    exit(1);

  }

 

  /* Use a buffer large enough to hold one period */

  snd_pcm_hw_params_get_period_size(params, &frames,

                                    &dir);

  size = frames * 4; /* 2 bytes/sample, 2 channels */

  buffer = (char *) malloc(size);

 

  /* We want to loop for 5 seconds */

  snd_pcm_hw_params_get_period_time(params, 

                                    &val, &dir);

  /* 5 seconds in microseconds divided by 

   * period time */

  loops = 5000000 / val;

 

  while (loops > 0) {

    loops--;

    rc = read(0, buffer, size);

    if (rc == 0) {

      fprintf(stderr, "end of file on input\n");

      break;

    } else if (rc != size) {

      fprintf(stderr,

              "short read: read %d bytes\n", rc);

    }

    rc = snd_pcm_writei(handle, buffer, frames);

    if (rc == -EPIPE) {

      /* EPIPE means underrun */

      fprintf(stderr, "underrun occurred\n");

      snd_pcm_prepare(handle);

    } else if (rc < 0) {

      fprintf(stderr,

              "error from writei: %s\n",

              snd_strerror(rc));

    }  else if (rc != (int)frames) {

      fprintf(stderr, 

              "short write, write %d frames\n", rc);

    }

  }

 

  snd_pcm_drain(handle);

  snd_pcm_close(handle);

  free(buffer);

  

  return 0;

}

 

/*

 

Example 4 - Simple sound recording

alsa_pcm_record.c

读PCM设备到标准的输出中

This example reads from the default PCM device

and writes to standard output for 5 seconds of data.

 

*/

 

/* Use the newer ALSA API */

#define ALSA_PCM_NEW_HW_PARAMS_API

 

#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;

  

  /* Open PCM device for recording (capture). */

  rc = snd_pcm_open(&handle, "default",

                    SND_PCM_STREAM_CAPTURE, 0);

  if (rc < 0) {

    fprintf(stderr, 

            "unable to open pcm device: %s\n",

            snd_strerror(rc));

    exit(1);

  }

  

  /* Allocate a hardware parameters object. */

  snd_pcm_hw_params_alloca(&params);

 

  /* Fill it in with default values. */

  snd_pcm_hw_params_any(handle, params);

 

  /* Set the desired hardware parameters. */

 

  /* Interleaved mode */

  snd_pcm_hw_params_set_access(handle, params,

                      SND_PCM_ACCESS_RW_INTERLEAVED);

 

  /* Signed 16-bit little-endian format */

  snd_pcm_hw_params_set_format(handle, params,

                              SND_PCM_FORMAT_S16_LE);

 

  /* Two channels (stereo) */

  snd_pcm_hw_params_set_channels(handle, params, 2);

 

  /* 44100 bits/second sampling rate (CD quality) */

  val = 44100;

  snd_pcm_hw_params_set_rate_near(handle, params, 

                                  &val, &dir);

 

  /* Set period size to 32 frames. */

  frames = 32;

  snd_pcm_hw_params_set_period_size_near(handle, 

                              params, &frames, &dir);

 

  /* Write the parameters to the driver */

  rc = snd_pcm_hw_params(handle, params);

  if (rc < 0) {

    fprintf(stderr,

            "unable to set hw parameters: %s\n",

            snd_strerror(rc));

    exit(1);

  }

 

  /* Use a buffer large enough to hold one period */

  snd_pcm_hw_params_get_period_size(params,

                                      &frames, &dir);

  size = frames * 4; /* 2 bytes/sample, 2 channels */

  buffer = (char *) malloc(size);

 

  /* We want to loop for 5 seconds */

  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) {

      /* EPIPE means overrun */

      fprintf(stderr, "overrun occurred\n");

      snd_pcm_prepare(handle);

    } else if (rc < 0) {

      fprintf(stderr,

              "error from read: %s\n", 

              snd_strerror(rc));

    } else if (rc != (int)frames) {

      fprintf(stderr, "short read, read %d frames\n", rc);

    }

    rc = write(1, buffer, size);

    if (rc != size)

      fprintf(stderr, 

              "short write: wrote %d bytes\n", rc);

  }

 

  snd_pcm_drain(handle);

  snd_pcm_close(handle);

  free(buffer);

 

  return 0;

}

 

./listing4 > sound.raw

./listing3 < sound.raw

 

 

http://blog.csdn.net/spygg/article/details/7824750

转载于:https://www.cnblogs.com/Ph-one/p/6823001.html

相关文章:

  • fopen
  • RIFF和WAVE音频文件格式
  • wav音频文件头解析
  • fseek
  • signal( SIGINT, SigIntHandler )
  • Linux下直接读写物理地址内存
  • 数学2考点
  • 2018考研全年备考规划,你还在等什么?
  • 复旦软件工程专业课
  • src源dst目标
  • linux 中mmap的用法
  • platform_set_drvdata 和 platform_get_drvdata
  • 内核Alsa之pcm
  • MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
  • 双链表的初始化,建立,插入,查找,删除。
  • 《深入 React 技术栈》
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • C++类的相互关联
  • css布局,左右固定中间自适应实现
  • ES学习笔记(12)--Symbol
  • HTTP--网络协议分层,http历史(二)
  • JavaScript 奇技淫巧
  • Javascript设计模式学习之Observer(观察者)模式
  • js写一个简单的选项卡
  • MYSQL 的 IF 函数
  • nodejs:开发并发布一个nodejs包
  • opencv python Meanshift 和 Camshift
  • React-flux杂记
  • SQLServer插入数据
  • STAR法则
  • Vue.js 移动端适配之 vw 解决方案
  • 大型网站性能监测、分析与优化常见问题QA
  • 来,膜拜下android roadmap,强大的执行力
  • 面试遇到的一些题
  • 少走弯路,给Java 1~5 年程序员的建议
  • 微信开源mars源码分析1—上层samples分析
  • 详解移动APP与web APP的区别
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 学习Vue.js的五个小例子
  • 移动端解决方案学习记录
  • 自制字幕遮挡器
  • (function(){})()的分步解析
  • (Oracle)SQL优化技巧(一):分页查询
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转载)Linux网络编程入门
  • .NET BackgroundWorker
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • @angular/cli项目构建--Dynamic.Form
  • @RunWith注解作用
  • []新浪博客如何插入代码(其他博客应该也可以)
  • [④ADRV902x]: Digital Filter Configuration(发射端)
  • [bzoj1006]: [HNOI2008]神奇的国度(最大势算法)