这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » openwrt 音频开发

共3条 1/1 1 跳转至

openwrt 音频开发

高工
2018-12-28 09:15:19     打赏

1、Linux 音频架构图

音视频的好坏 直接影响 产品体验




2、音频架构图层次说明

openWRT 采用 ALSA 层次图,如下


Application: 上层应用 主要调用alsa-lib 中的接口 实现业务逻辑。使用alsa-util中aplay,arecord,amixer,speaker-test进行相关测试。

HAL层 : 移植alsa-lib 和 alsa-utils. 在openwrt中 feeds 自带了alsa-lib 1.1.01[简介]

kernel: 按照ALSA 驱动层进行适配。[简介]

3、Application层

openwrt中应用启动需要自定义启动脚本,放在目录/etc/init.d 目录下。


4、启动脚本顺序定义

05 defconfig //加载默认参数


10 boot //启动


39 usb // 加载usbfs


40 network // 设置网卡参数


45 firewall // 防火墙


50 dropbear // sshd server


50 cron // …


50 telnet // 如果没有修改root密码,则启动telnet server


60 dnsmasq // DHCP 和 DNS 服务端


95 done // …


96 led // 指示灯


97 watchdog // …


99 sysctl // 最后,进行必要的内核参数调整


5、启动脚本书写

按照官方Wiki脚本,进行自定制。


6、HAL层

这一层不需要做太多改动,需要配置feeds选择自己需要的版本即可,

具体的接口查询,可以到alsa-project查询。


7、kernel

根据ALSA驱动进行相关分析。


machine

/* SoC machine */

struct snd_soc_card {

      char *name;

      ...

      int (*probe)(struct platform_device *pdev);

      int (*remove)(struct platform_device *pdev);

      

      /* the pre and post PM functions are used to do any PM work before and

       * after the codec and DAIs do any PM work. */

      int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);

      int (*suspend_post)(struct platform_device *pdev, pm_message_t state);

      int (*resume_pre)(struct platform_device *pdev);

      int (*resume_post)(struct platform_device *pdev);

      ...

 

      /* CPU <--> Codec DAI links  */

      struct snd_soc_dai_link *dai_link;

      int num_links;

      ...

};

probe/remove 可选,主要侦测machine。


suspend/resume 在codec,DAIs和DMA suspend resume 会相应的触发,也是可选的


machine DAI口配置,配置对应的结构体


/*  digital audio interface glue - connects codec <--> CPU */

static struct snd_soc_dai_link corgi_dai = {

.name = "WM8731",

.stream_name = "WM8731",

.cpu_dai_name = "pxa-is2-dai",

.codec_dai_name = "wm8731-hifi",

.platform_name = "pxa-pcm-audio",

.codec_name = "wm8713-codec.0-001a",

.init = corgi_wm8731_init,

.ops = &corgi_ops,

};

 

/* audio machine driver */

static struct snd_soc_card snd_soc_corgi = {

      .name = "Corgi",

      .dai_link = &corgi_dai,

      .num_links = 1,

};

plarform

DMA 驱动,Soc DAI 驱动


/* SoC audio ops */ 

 

struct snd_soc_ops { 

int (*startup)(struct snd_pcm_substream *);

void (*shutdown)(struct snd_pcm_substream *);

int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *);

int (*prepare)(struct snd_pcm_substream *);

int (*trigger)(struct snd_pcm_substream *, int); 

};

//平台驱动通过DMA接口关联起来

struct snd_soc_platform_driver { 

char *name;

int (*probe)(struct platform_device *pdev);

int (*remove)(struct platform_device *pdev);

int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); 

int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);

/* pcm creation and destruction */

int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *); 

void (*pcm_free)(struct snd_pcm *);

/*

* For platform caused delay reporting.

* Optional.

*/ 

snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *, struct snd_soc_dai *);

/* platform stream ops */

struct snd_pcm_ops *pcm_ops;

};

codec

每个codec codec 驱动必须具备以下功能


codec DAI 与 PCM 配置

使用I2C 或者SPI控制IO

mixer audio 控制

codec 音频操作

DAPM 描述

DAPM 事件处理

可选

DAC mute处理

// DAI PCM配置

static struct snd_soc_dai_ops wm8731_dai_ops = {

.prepare = wm8731_pcm_prepare,

.hw_params = wm8731_hw_params,

.shutdown = wm8731_shutdown,

.digital_mute = wm8731_mute,

.set_sysclk = wm8731_set_dai_sysclk,

.set_fmt = wm8731_set_dai_fmt, };

struct snd_soc_dai_driver wm8731_dai = { .name = "wm8731-hifi",

.playback = { .stream_name = "Playback",

.channels_min = 1,

.channels_max = 2,

.rates = WM8731_RATES,

.formats = WM8731_FORMATS,},

.capture = { .stream_name = "Capture",

.channels_min = 1,

.channels_max = 2,

.rates = WM8731_RATES,

.formats = WM8731_FORMATS,}, .ops = &wm8731_dai_ops,

.symmetric_rates = 1, }; 

// i2c 控制 读写


i2c_write

i2c_read

// mixer audio control


codec 说有mixer与control都定义在soc.h中


 #define SOC_SINGLE(xname, reg, shift, mask, invert)

定义单一控制器:-

  xname  控制名称e.g. "Playback Volume"

  reg = codec register

  shift = control bit(s) offset in register

  mask = control bit size(s) e.g. mask of 7 = 3 bits

  invert = the control is inverted 是否反转

 

其他宏包括: -

    #define SOC_DOUBLE (xname ,reg ,shift_left ,shift_right ,mask ,invert )

立体声控制

    #define SOC_DOUBLE_R (xname ,reg_left ,reg_right ,shift ,mask ,invert )

一个跨越2个寄存器的立体声控制

    #define SOC_ENUM_SINGLE (xreg ,xshift ,xmask ,xtexts )

   xreg = 寄存器

   XSHIFT = 控制位(小号)偏移在寄存器 

   xmask = 控制位(小号)大小

   xtexts = 指向描述每个设置的字符串数组的指针

//audio 控制操作


/* SoC audio ops */ 

struct snd_soc_ops { 

int (*startup)(struct snd_pcm_substream *);

void (*shutdown)(struct snd_pcm_substream *);

int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free (struct snd_pcm_substream *);

int (*prepare)(struct snd_pcm_substream *); 

};

//DAPM

//DAPM handler 参考


8、openWRT 音频应用

上面一节介绍了音频框架。在openWRT音频业务主要VOIP通过 ,上/下行通过模块如下:




9、调试技术

链路上应用层UDP截取文件

知道UDP 下的G711A 对应的buffer


           // 应用层将buffer写到文件 代码块

            static struct file *file_g711a= NULL;

            int writelen = 0;

            if (NULL == fp)

            {

                file_g711a= file_open("/test1", O_RDWR | O_CREAT, 0777);

                 if(file_g711a == NULL){

          printf("g711a file  = NULL");

        }else{

            printf("g711a file open ok");

          }   

            }

if(file_g711a != NULL){

            writelen = fwrite(g711a_buffer, 1, sizeof(g711a_buffer), file_g711a);

            }

            printk("writelen:%d\n",writelen);

10、链路上 alsa 截取文件

转换成PCM后,将对应的buffer 写道文件


           // 应用层将buffer写到文件 代码块

            static struct file *file_pcm= NULL;

            int writelen = 0;

            if (NULL == fp)

            {

                file_pcm= file_open("/test.pcm", O_RDWR | O_CREAT, 0777);

                 if(file_pcm == NULL){

          printf("pcm file  = NULL");

        }else{

            printf("pcm file open ok");

          }   

            }

if(file_pcm != NULL){

            writelen = fwrite(pcm_buffer, 1, sizeof(pcm_buffer), file_pcm);

            }

            printk("writelen:%d\n",writelen);

链路上kernel 截取文件

在PCM与DMA通信的函数中 将buffer 写到文件中


           // kernel 将buffer写到文件 代码块

            static struct file *fp = NULL;

            mm_segment_t fs;

            static loff_t pos = 0;

            printk("hello enter\n");

            if (NULL == fp)

            {

                fp = filp_open("/test.pcm", O_RDWR | O_CREAT, 0777);

                if (IS_ERR(fp))

                {

                    printk("create file error\n");

                    return -1;

                }

            }

            fs = get_fs();

            set_fs(KERNEL_DS);

            int writelen = vfs_write(fp, buf, size*4, &pos);

            pos += size*4;

            printk("writelen:%d pos:%d\n",writelen, pos);






管理员
2018-12-31 09:25:56     打赏
2楼

谢谢楼主分享


院士
2019-01-04 09:04:45     打赏
3楼

openwrt 音频开发


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]