零基础入门多媒体音频(6)-alsa(2)

这篇具有很好参考价值的文章主要介绍了零基础入门多媒体音频(6)-alsa(2)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

PCM接口
ALSA的PCM中间层非常有用,每个驱动只需要实现底层的功能来访问硬件。要使用PCM层,你需要先引用 <sound/pcm.h>头文件。此外,如果你要使用和hw_param相关的函数,<sound/pcm_params.h>也是必须的。

每个声卡设备最多拥有4个PCM实例。一个PCM实例对应一个PCM设备文件。实例数量的约束来自linux设备序号可用的位大小。当64bit的设备序号开始使用时,我们就可以支持更多的PCM实例。

PCM实例包含PCM回放和录制流。每个PCM流包含一个或更多的PCM子流。一些声卡支持多路回放功能。比如,emu10k1 支持32路立体声播放子流。这种情况下,每次打开动作,一路空闲的子流会被自动选择并打开。如果只有一路子流存在并且已经打开过,那么后续的打开动作将会阻塞或者返回错误码EAGAIN 。但在驱动中不需要关注这些细节。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)
{
        /* the hardware-specific codes will be here */
        ....
        return 0;
}

/* hw_free callback */
static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
{
        /* the hardware-specific codes will be here */
        ....
        return 0;
}

/* 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,
        .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,
        .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_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
                                       &chip->pci->dev,
                                       64*1024, 64*1024);
        return 0;
}

调用snd_pcm_new()函数可以获得PCM实例。可以为PCM定义一个构造函数,比如说:


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;
        ...
        return 0;
}

snd_pcm_new()函数包含六个参数。第一个参数时PCM要绑定的声卡。也就是上篇文章提到并获得的snd_card实例。第二个参数是ID字符串。第三个参数是新PCM的索引。第四个和第五个参数是播放和录制子流的个数。当没有播放和录制子流可用的时候,对应的参数传0.

如果芯片支持多路播放或者录制子流,你可以指定更大的数。但在open,close,回调等函数内部要做好处理。当你需要知道在处理哪个子流时,你可以从每个回调函数的参数中获取,参考下面的示例:
struct snd_pcm_substream *substream;
int index = substream->number;

当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);

一个典型的操作结构体如下:

static struct snd_pcm_ops snd_mychip_playback_ops = {
        .open =        snd_mychip_pcm_open,
        .close =       snd_mychip_pcm_close,
        .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,
};


操作结构体的包含了所有回调函数。
设置完操作结构体后,可以预分配缓冲区(buffer)并设置管理分配模式。执行下面的代码即可。
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
                               &chip->pci->dev,
                               64*1024, 64*1024);


这段代码会默认会分配最多64kB的buffer。另外,pcm->info_flags还可以包含更多的信息。 <sound/asound.h>中定义的SNDRV_PCM_INFO_XXX类型的宏都可以添加到flag中。

运行时指针---------PCM的主要信息
当PCM子流被打开,一个PCM运行时就被创建好并分配给子流。通过 substream->runtime就可以访问了。你要用来控制PCM的信息都可以通过运行时指针获得。Hw_params和sw_params配置的拷贝,缓冲区指针。Mmap记录。,自旋锁等。

<sound/pcm.h>文件定义了运行时实例。下面是截取的一部分代码。

struct _snd_pcm_runtime {
        /* -- Status -- */
        struct snd_pcm_substream *trigger_master;
        snd_timestamp_t trigger_tstamp;       /* trigger timestamp */
        int overrange;
        snd_pcm_uframes_t avail_max;
        snd_pcm_uframes_t hw_ptr_base;        /* Position at buffer restart */
        snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/

        /* -- HW params -- */
        snd_pcm_access_t access;      /* access mode */
        snd_pcm_format_t format;      /* SNDRV_PCM_FORMAT_* */
        snd_pcm_subformat_t subformat;        /* subformat */
        unsigned int rate;            /* rate in Hz */
        unsigned int channels;                /* channels */
        snd_pcm_uframes_t period_size;        /* period size */
        unsigned int periods;         /* periods */
        snd_pcm_uframes_t buffer_size;        /* buffer size */
        unsigned int tick_time;               /* tick time */
        snd_pcm_uframes_t min_align;  /* Min alignment for the format */
        size_t byte_align;
        unsigned int frame_bits;
        unsigned int sample_bits;
        unsigned int info;
        unsigned int rate_num;
        unsigned int rate_den;

        /* -- SW params -- */
        struct timespec tstamp_mode;  /* mmap timestamp is updated */
        unsigned int period_step;
        unsigned int sleep_min;               /* min ticks to sleep */
        snd_pcm_uframes_t start_threshold;
        /*
         * The following two thresholds alleviate playback buffer underruns; when
         * hw_avail drops below the threshold, the respective action is triggered:
         */
        snd_pcm_uframes_t stop_threshold;     /* - stop playback */
        snd_pcm_uframes_t silence_threshold;  /* - pre-fill buffer with silence */
        snd_pcm_uframes_t silence_size;       /* max size of silence pre-fill; when >= boundary,
                                               * fill played area with silence immediately */
        snd_pcm_uframes_t boundary;   /* pointers wrap point */

        /* internal data of auto-silencer */
        snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
        snd_pcm_uframes_t silence_filled; /* size filled with silence */

        snd_pcm_sync_id_t sync;               /* hardware synchronization ID */

        /* -- mmap -- */
        volatile struct snd_pcm_mmap_status *status;
        volatile struct snd_pcm_mmap_control *control;
        atomic_t mmap_count;

        /* -- locking / scheduling -- */
        spinlock_t lock;
        wait_queue_head_t sleep;
        struct timer_list tick_timer;
        struct fasync_struct *fasync;

        /* -- private section -- */
        void *private_data;
        void (*private_free)(struct snd_pcm_runtime *runtime);

        /* -- hardware description -- */
        struct snd_pcm_hardware hw;
        struct snd_pcm_hw_constraints hw_constraints;

        /* -- timer -- */
        unsigned int timer_resolution;        /* timer resolution */

        /* -- DMA -- */
        unsigned char *dma_area;      /* DMA area */
        dma_addr_t dma_addr;          /* physical bus address (not accessible from main CPU) */
        size_t dma_bytes;             /* size of DMA area */

        struct snd_dma_buffer *dma_buffer_p;  /* allocated buffer */

#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
        /* -- OSS things -- */
        struct snd_pcm_oss_runtime oss;
#endif
};

每个声卡驱动的回调函数,这些数据都是制度的。只有PCM中间层可以修改更新他们。唯一的例外时硬件描述中的DMA buffer信息以及私有数据。此外,如果你使用标准管理buffer分配模式,你不需要设置DMA buffer信息。

硬件描述信息
硬件描述符(结构体snd_pcm_hardware)定义了基础的硬件配置信息。首先,你需要在PCM open的回调函数里面定义它。运行时实例持有的是一份这个描述的拷贝,而不是指针。在open回调函数里面。如果你需要,你可以修改这份拷贝的描述符 (runtime->hw)。比如,在一些芯片模式下,最大声道数支持1,依然可以使用同样的硬件描述符并且在后续的代码中修改channels_max。

struct snd_pcm_runtime *runtime = substream->runtime;
...
runtime->hw = snd_mychip_playback_hw; /* common definition */
if (chip->model == VERY_OLD_ONE)
        runtime->hw.channels_max = 1;

我们的硬件描述符通常是下面这个样子的。
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,
};

Info字段保存PCM的类型和能力。<sound/asound.h> 文件中SNDRV_PCM_INFO_XXX类型的宏定义了info字段的类型。是否支持mmap和支持什么交织类型必须指定。SNDRV_PCM_INFO_MMAP 标志为表示驱动支持MMAP,SNDRV_PCM_INFO_INTERLEAVED 标志位表示支持交织PCM,SNDRV_PCM_INFO_NONINTERLEAVED 表示支持非交织PCM。如果交织和非交织都支持,则可以两者都设置。
上面的实例代码中, OSS mmap mode指定了BLOCK_TRANSFER 和MMAP_VALID 。这两个标志位一般都是同时指定的。只有当驱动真正支持mmap的时候才能设置MMAP_VALID 。

SNDRV_PCM_INFO_PAUSE 和SNDRV_PCM_INFO_RESUME表示PCM支持pause/resume。如果设置了这两个标志位,那么在trigger 要做响应的处理。文章来源地址https://www.toymoban.com/news/detail-851392.html

到了这里,关于零基础入门多媒体音频(6)-alsa(2)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 鸿蒙实战多媒体运用:【音频组件】

    音频组件用于实现音频相关的功能,包括音频播放,录制,音量管理和设备管理。 图 1  音频组件架构图 基本概念 采样 采样是指将连续时域上的模拟信号按照一定的时间间隔采样,获取到离散时域上离散信号的过程。 采样率 采样率为每秒从连续信号中提取并组成离散信号

    2024年03月10日
    浏览(84)
  • 多媒体库SDL以及实时音视频库WebRTC中的多线程问题实战详解

    目录 1、概述 2、开源跨平台多媒体库SDL介绍 3、开源音视频实时通信库WebRTC介绍

    2024年02月08日
    浏览(56)
  • 05 带音视频、多媒体、2D3D显示加速的嵌入式类芯片介绍

    作者 将狼才鲸 创建日期 2022-04-11 带硬件音视频编解码模块的芯片有两类: 一是不包含GPU(支持OpenGL ES、Open VG等协议),只带有图片编解码器、VPU视频编解码器和2D显示加速(多图层、打点、画线、画矩形、平移、缩放、旋转、替换、透明)的芯片。 二是包含完整的GPU,支持

    2023年04月08日
    浏览(129)
  • 鸿蒙HarmonyOS开发实战—多媒体开发(音频开发 一)

    HarmonyOS音频模块支持音频业务的开发,提供音频相关的功能,主要包括音频播放、音频采集、音量管理和短音播放等。 基本概念 采样 采样是指将连续时域上的模拟信号按照一定的时间间隔采样,获取到离散时域上离散信号的过程。 采样率 采样率为每秒从连续信号中提取并

    2024年01月24日
    浏览(51)
  • 【python】《多媒体技术与应用》实验报告「数字音频处理」

     《多媒体技术与应用》 实验报告 实验名称 数字视频处理 实验时间 2022/4/25 姓名 班级 计非201 学号 成绩 一.  实验目的 1. 掌握数字音频的读取与打开; 2. 掌握数字音频信号的频谱分析; 3. 验证 PCM 编码算法。 二.实验原理 声音是由物体振动而产生的,声波的三要素是频率

    2023年04月16日
    浏览(57)
  • Android多媒体功能开发(11)——使用AudioRecord类录制音频

    AudioRecord类优点是能录制到缓冲区,能够实现边录边播(AudioRecord + AudioTrack)以及对音频的实时处理(如QQ电话)。缺点是输出是PCM格式的原始采集数据,如果直接保存成音频文件,不能够被播放器播放,所以必须用代码实现数据编码以及压缩。 使用AudioRecord录音的基本步骤是

    2023年04月09日
    浏览(44)
  • (八)穿越多媒体奇境:探索Streamlit的图像、音频与视频魔法

    欢迎各位读者来到“最全Streamlit教程”专栏系列!如果您正在寻找一种简单而强大的方式来创建交互式数据应用程序,那么Streamlit无疑是您的最佳选择。作为该领域的热门框架,Streamlit让数据科学家、开发者和爱好者能够以前所未有的速度构建出引人入胜的数据可视化工具。

    2024年02月13日
    浏览(46)
  • HarmonyOS学习路之开发篇—多媒体开发(音频开发 二(1)

    接口说明 接口名 描述 AudioCapturer(AudioCapturerInfo audioCapturerInfo) throws IllegalArgumentException 构造函数,设置录音相关音频参数,使用默认录音设备。 AudioCapturer(AudioCapturerInfo audioCapturerInfo, AudioDeviceDescriptor devInfo) throws IllegalArgumentException 构造函数,设置录音相关音频参数并指定录音

    2024年04月23日
    浏览(56)
  • [前端笔记——多媒体与嵌入] 6.HTML 中的图片+视频+音频内容

    可以用 img 元素来把图片放到网页上。它是一个空元素(它不需要包含文本内容或闭合标签),最少只需要一个 src (一般读作其全称 * *source) * *来使其生效。src 属性包含了指向我们想要引入的图片的路径,可以是相对路径或绝对 URL,就像 a 元素的 href 属性一样。 属性是

    2023年04月25日
    浏览(57)
  • LuatOS-SOC接口文档(air780E)--audio - 多媒体音频

    常量 类型 解释 audio.PCM number PCM格式,即原始ADC数据 audio.MORE_DATA number audio.on回调函数传入参数的值,表示底层播放完一段数据,可以传入更多数据 audio.DONE number audio.on回调函数传入参数的值,表示底层播放完全部数据了 audio.BUS_DAC number 硬件输出总线,DAC类型 audio.BUS_I2S numb

    2024年02月07日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包