ALSA driver--PCM

在介绍PCM 之前,我们先给出创建PCM实例的框架。

  #include <sound/pcm.h>
  ....

  /* hardware definition */
  static struct snd_pcm_hardware snd_mychip_playback_hw = {
          .info = (SNDRV_PCM_INFO_MMAP |
                   SNDRV_PCM_INFO_INTERLEAVED |
                   SNDRV_PCM_INFO_BLOCK_TRANSFER |
                   SNDRV_PCM_INFO_MMAP_VALID),
          .formats =          SNDRV_PCM_FMTBIT_S16_LE,
          .rates =            SNDRV_PCM_RATE_8000_48000,
          .rate_min =         8000,
          .rate_max =         48000,
          .channels_min =     2,
          .channels_max =     2,
          .buffer_bytes_max = 32768,
          .period_bytes_min = 4096,
          .period_bytes_max = 32768,
          .periods_min =      1,
          .periods_max =      1024,
  };

  /* hardware definition */
  static struct snd_pcm_hardware snd_mychip_capture_hw = {
          .info = (SNDRV_PCM_INFO_MMAP |
                   SNDRV_PCM_INFO_INTERLEAVED |
                   SNDRV_PCM_INFO_BLOCK_TRANSFER |
                   SNDRV_PCM_INFO_MMAP_VALID),
          .formats =          SNDRV_PCM_FMTBIT_S16_LE,
          .rates =            SNDRV_PCM_RATE_8000_48000,
          .rate_min =         8000,
          .rate_max =         48000,
          .channels_min =     2,
          .channels_max =     2,
          .buffer_bytes_max = 32768,
          .period_bytes_min = 4096,
          .period_bytes_max = 32768,
          .periods_min =      1,
          .periods_max =      1024,
  };

  /* open callback */
  static int snd_mychip_playback_open(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          struct snd_pcm_runtime *runtime = substream->runtime;

          runtime->hw = snd_mychip_playback_hw;
          /* more hardware-initialization will be done here */
          ....
          return 0;
  }

  /* close callback */
  static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          /* the hardware-specific codes will be here */
          ....
          return 0;

  }

  /* open callback */
  static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          struct snd_pcm_runtime *runtime = substream->runtime;

          runtime->hw = snd_mychip_capture_hw;
          /* more hardware-initialization will be done here */
          ....
          return 0;
  }

  /* close callback */
  static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          /* the hardware-specific codes will be here */
          ....
          return 0;

  }

  /* hw_params callback */
  static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,
                               struct snd_pcm_hw_params *hw_params)
  {
          return snd_pcm_lib_malloc_pages(substream,
                                     params_buffer_bytes(hw_params));
  }

  /* hw_free callback */
  static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
  {
          return snd_pcm_lib_free_pages(substream);
  }

  /* prepare callback */
  static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          struct snd_pcm_runtime *runtime = substream->runtime;

          /* set up the hardware with the current configuration
           * for example...
           */
          mychip_set_sample_format(chip, runtime->format);
          mychip_set_sample_rate(chip, runtime->rate);
          mychip_set_channels(chip, runtime->channels);
          mychip_set_dma_setup(chip, runtime->dma_addr,
                               chip->buffer_size,
                               chip->period_size);
          return 0;
  }

  /* trigger callback */
  static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream,
                                    int cmd)
  {
          switch (cmd) {
          case SNDRV_PCM_TRIGGER_START:
                  /* do something to start the PCM engine */
                  ....
                  break;
          case SNDRV_PCM_TRIGGER_STOP:
                  /* do something to stop the PCM engine */
                  ....
                  break;
          default:
                  return -EINVAL;
          }
  }

  /* pointer callback */
  static snd_pcm_uframes_t
  snd_mychip_pcm_pointer(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          unsigned int current_ptr;

          /* get the current hardware pointer */
          current_ptr = mychip_get_hw_pointer(chip);
          return current_ptr;
  }

  /* operators */
  static struct snd_pcm_ops snd_mychip_playback_ops = {
          .open =        snd_mychip_playback_open,
          .close =       snd_mychip_playback_close,
          .ioctl =       snd_pcm_lib_ioctl,
          .hw_params =   snd_mychip_pcm_hw_params,
          .hw_free =     snd_mychip_pcm_hw_free,
          .prepare =     snd_mychip_pcm_prepare,
          .trigger =     snd_mychip_pcm_trigger,
          .pointer =     snd_mychip_pcm_pointer,
  };

  /* operators */
  static struct snd_pcm_ops snd_mychip_capture_ops = {
          .open =        snd_mychip_capture_open,
          .close =       snd_mychip_capture_close,
          .ioctl =       snd_pcm_lib_ioctl,
          .hw_params =   snd_mychip_pcm_hw_params,
          .hw_free =     snd_mychip_pcm_hw_free,
          .prepare =     snd_mychip_pcm_prepare,
          .trigger =     snd_mychip_pcm_trigger,
          .pointer =     snd_mychip_pcm_pointer,
  };

  /*
   *  definitions of capture are omitted here...
   */

  /* create a pcm device */
  static int snd_mychip_new_pcm(struct mychip *chip)
  {
          struct snd_pcm *pcm;
          int err;

          err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
          if (err < 0)
                  return err;
          pcm->private_data = chip;
          strcpy(pcm->name, "My Chip");
          chip->pcm = pcm;
          /* set operators */
          snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                          &snd_mychip_playback_ops);
          snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
                          &snd_mychip_capture_ops);
          /* pre-allocation of buffers */
          /* NOTE: this may fail */
          snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                                snd_dma_pci_data(chip->pci),
                                                64*1024, 64*1024);
          return 0;
  }

1.创建pcm实例

ALSA driver为我们提供接口snd_pcm_new来创建PCM实例。但是我们最好是写一个如上述snd_mychip_new_pcm的函数来来对构建pcm实例的过程进行封装。

/**
* snd_pcm_new - create a new PCM instance
* @card: the card instance
* @id: the id string
* @device: the device index (zero based)
* @playback_count: the number of substreams for playback
* @capture_count: the number of substreams for capture
* @rpcm: the pointer to store the new pcm instance
*/
int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm)

第三个参数表示新创建的PCM实例的index(0,1,2,3).可以在一个card上创建多个PCM 实例。每一个PCM又可以包含多个substream.如果芯片支持多路播放,那么将有多个substream.每次open/close都作用于某个substream.在创建PCM的substream时就指定了number(0~playback_count).当App在调用alsa lib API:snd_pcm_open时,alsa core通过snd_pcm_attach_substream函数来open一个空闲的substream.

2.设置PCM的操作函数

创建完PCM函数后,就可设置PCM 的操作函数。

  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
                  &snd_mychip_playback_ops);
  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
                  &snd_mychip_capture_ops);

操作函数即我们写driver时需要实现的功能,以供alsa-core调用。ALSA PCM的操作函数包括:

  static struct snd_pcm_ops snd_mychip_playback_ops = {
          .open =        snd_mychip_pcm_open,
          .close =       snd_mychip_pcm_close,
          .ioctl =       snd_pcm_lib_ioctl,
          .hw_params =   snd_mychip_pcm_hw_params,
          .hw_free =     snd_mychip_pcm_hw_free,
          .prepare =     snd_mychip_pcm_prepare,
          .trigger =     snd_mychip_pcm_trigger,
          .pointer =     snd_mychip_pcm_pointer,
  };

每个函数都包含一个snd_pcm_substream 的指针,指向当前操作的substream.

在上面的例子中,每个操作函数里面都包含如下宏调用:

其中返回的是substream->private_data,sustream的private_data是pcm->private_data的一份拷贝。拷贝动作是在snd_pcm_open时调用的snd_pcm_attach_substream中进行。一般来说pcm的private_data是芯片专用数据,当然我们也可以overwrite以保存别的数据。

2.1 open

当open PCM的一路substream时,open函数就会被调用。

  static int snd_xxx_open(struct snd_pcm_substream *substream)
  {
          struct mychip *chip = snd_pcm_substream_chip(substream);
          struct snd_pcm_runtime *runtime = substream->runtime;

          runtime->hw = snd_mychip_playback_hw;
          return 0;
  }

在open函数内,至少应该初始化此substream的runtime->hw结构体.snd_mychip_playback_hw是预先定义的硬件描述。

				
时间: 2024-10-10 21:18:51

ALSA driver--PCM的相关文章

ALSA driver --PCM device 创建过程

前面已经写过PCM device的创建框架,我们现在来看看PCM device是如何创建的. 在调用snd_pcm_new时就会创建一个snd_pcm类型的PCM device. struct snd_pcm { struct snd_card *card;//PCM device 说挂载的声卡 struct list_head list;//一个Card可能有多个PCM device,PCM device列表 int device; /* device number *///PCM devic

Alsa中PCM参数设置

1) PCM设备的句柄.2) 指定同时可供回放或截获的PCM流的方向3) 提供一些关于我们想要使用的设置选项的信息,比如缓冲区大小,采样率,PCM数据格式等4) 检查硬件是否支持设置选项.   4.1) 初始化PCM变量   4.2) 分配hwparams结构   4.3) 打开PCM设备   4.4) 以声卡的全部设置选项空间来初始化hwparams结构   4.5) 指定访问类型,采样格式,采样率,声道号码,周期数目以及周期大小        a) 访问类型 :指定了哪一个多声道数据储存在缓

嵌入式开发之davinci--- 8148/8168/8127 中的alsa音频pcm g711 和aac 音频格式

(1)alsa pcm (2)g711 (3)aac (4) --------------author:pkf -------------------time:2-4 ---------------------------qq:1327706646 (1)alsa pcm (2)g711 G.711 G.711 也称为PCM(脉冲编码调制),是国际电信联盟订定出来的一套语音压缩标准,主要用于电话.它主要用脉冲编码调制对音频采样,采样率为8k每秒.它利用一个 64Kbps 未压缩通道传输语音讯号.

ALSA driver--Asoc

ALSA Asoc框架如下图: Asoc分为machine,platform,codec三大模块.关于machine,platform,codec的介绍参考自这里http://blog.csdn.net/droidphone/article/details/7165482 Machine  是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入.输出设备也

Linux ALSA 音频系统:物理链路篇

1. Overview 硬件平台及软件版本: Kernel - 3.4.5 SoC - Samsung exynos CODEC - WM8994 Machine - goni_wm8994 Userspace - tinyalsa Linux ALSA 音频系统架构大致如下: +--------+ +--------+ +--------+ |tinyplay| |tinycap | |tinymix | +--------+ +--------+ +--------+ | ^ ^ V | V

Linux ALSA 音频系统:逻辑设备篇

6. 声卡和 PCM 设备的建立过程 前面几章分析了 Codec.Platform.Machine 驱动的组成部分及其注册过程,这三者都是物理设备相关的,大家应该对音频物理链路有了一定的认知.接着分析音频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备. PCM 逻辑设备,我们又习惯称之为 PCM 中间层或 pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝:往下是触发 codec.platform.machine 的操作函

ALSA lib基本概念

1.channel 通道,即我们熟知的声道数.左/右声道,5.1channel等等 2.sample A sample is a single value that describes the amplitude of the audio signal at a single point in time, on a single channel. sample即一次采样,通常的sample bit指的是一个channnel上,一次采样的bit数(常见的sample bit 8/16/24/32b

交叉编译音频(无视频)版linphone

环境:Ubuntu10.04.fscl mx53 linphone3.6.1 依赖库:alsa-lib-1.0.18   ncurses-5.9   readline-5.1   libosip2-3.6.0   libeXosip2-3.6.0    libogg-1.1.4  speex-1.2rc1  libxml2-2.7.6 安装configure依赖工具: sudo apt-get install libtool automake autoconf 设置环境变量: export PR

如何在Ubuntu安装*.exe文件

下载及安装 若你使用 Debian 或者 Ubuntu 之类的发行版,只需要一个命令即可完成安装: apt-get install wine 若你使用的为其他发行版,请访问 http://winehq.org/site/download 查询安装方法[]Wine 的配置 运行 winecfg 即可,里面有详尽的配置. 注意:Wine 1.1.4 以上版本的 winecfg 已经是中文界面了.[]Wine 中文乱码的解决 Wine 1.1.4 以上版本的中文支持得到了极大改善.如果您在使用较低版本