北京合众恒跃科技有限公司
直播中

EPTmachine

8年用户 1030经验值
擅长:可编程逻辑 嵌入式技术 存储技术 接口/总线/驱动 控制/MCU
私信 关注

【HZ-RK3568开发板免费体验】2、利用开发板上音频外设播放音频

HZ-RK3576有用于音频播放的Jack Audio接口。连接外部音箱或者耳机即可进行音频的播放功能。ALSA(Advanced Linux Sound Architecture)属于Linux内核的音频的子系统,在用户空间试用alsa-utils工具可以查看音频系统的组成、设置音频参数以及播放音频文件。同时,也可以通过编程调用alsa的API实现自定义应用。

1、ALSA编程接口

ALSA编程接口可以分为以下几类:

  • Information interface,提供音频设备信息以及用于控制音频设备的信息;
  • Control interface,调节音量以及其他声卡支持的控制功能;
  • Mixer interface,用于多个应用程序共享音频设备,这是ALSA的主要功能之一;
  • PCM interface,通过配置机制定义虚拟设备和硬件设备,是数字音频应用中的常用接口;
  • Raw MIDI interface,用于控制MIDI的底层接口,直接处理MIDI事件;
  • Sequencer interface,用于应用程控制MIDI接口;
  • Timer interface,用于使用声卡硬件中的定时器,同步不同的声卡事件;

合众恒跃提供的开发SDK中的buildroot/dl/alas-lib/文件夹中有alsa-lib的源码,也可以从Github上拉取最新的alsa-lib的release代码。

alsa-lib_package.png

tar -vxjf alsa-lib-1.2.7.2.tar.bz2

进入解压出来的文件夹,对源码进行配置

cd alsa-lib-1.2.7.2
./configure --host=aarch64-buildroot-linux-gnu --prefix=/home/hzhy/HZHY/RK3568/kits/alsa-lib --with-configdir=/usr/share/alsalib

--host指定编译使用的交叉编译器,--prefix指定存放编译好的lib文件,--with-configdir指定系统配置文件夹。完成上述操作后,执行编译和安装指令

make 

sudo make install

在执行sudo make install时会出现下列错误

make_install_error.png

出现问题的原因是在root用户下无法找到工具链中的编译器,需要在root用户下配置工具链,执行buildroot输出工具链中的environment_setup脚本即可,配置后重新执行make install

su
source /<SDK路径>/buildroot/output/host/environment_setup
make install

安装完成后在--prefix指定的文件夹中lib文件夹中,有一个pkgconfig文件夹,其中的文件是用于管理alsa-lib库的。在PKG_CONFIG_PATH环境变量中添加这一pkgconfig的路径。这样在makefile中就可以引用alsa-lib库。

PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/home/hzhy/HZHY/RK3568/kits/alsa-lib/lib/pkgconfig
export PKG_CONFIG_PATH

2、获取音频设备信息

获取音频设备信息由多个步骤构成,首先需要确定硬件声卡,该操作通过使用Control Interface实现。主要使用以下各函数:

  • snd_card_next
  • snd_ctl_open
  • snd_ctl_pcm_next_device
  • snd_ctl_card_info_get_id
  • snd_ctl_card_info_get_name

声卡由整数编号0开始识别。使用snd_card_next获取下一个声卡设备编号,第一个声卡设备使用-1作为函数输入获得。传递声卡名称(比如hw:0,hw:1)到snd_ctl_open来打卡声卡,打开的声卡使用handle进行管理。进而,handle用于调用函数来向结构体snd_ctl_card_info中填充声卡信息,结构中的成员信息通过使用比如snd_ctl_card_info_get_name来提取相关信息。比如:

card 0: PCH(HDA Intel PCH)

进一步获取相关的信息,需要使用声卡相关的PCM接口来获取。用于存储PCM信息的结构体为snd_pcm_info_t。调用的函数为snd_pcm_info_get_idsnd_pcm_info_get_name

以下为发现和显示声卡硬件信息的代码 device_hardware.c

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <locale.h>

// used by gettext for i18n, not needed here
#define _(STR) STR

static char *command;
#define error(...) do {\
      fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
      fprintf(stderr, __VA_ARGS__); \
      putc('\n', stderr); \
} while (0)

static void device_list(snd_pcm_stream_t stream)
{
      snd_ctl_t *handle;
      int card, err, dev, idx;
      snd_ctl_card_info_t *info;
      snd_pcm_info_t *pcminfo;
      snd_ctl_card_info_alloca(&info);
      snd_pcm_info_alloca(&pcminfo);

      card = -1;
      if (snd_card_next(&card) < 0 || card < 0) {
            error(_("no soundcards found..."));
            return;
      }
      printf(_("**** List of %s Hardware Devices ****\n"),
             snd_pcm_stream_name(stream));
      while (card >= 0) {
            char name[32];
            sprintf(name, "hw:%d", card);
            if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
                  error("control open (%i): %s", card, snd_strerror(err));
                  goto next_card;
            }
            if ((err = snd_ctl_card_info(handle, info)) < 0) {
                  error("control hardware info (%i): %s", card, snd_strerror(err));
                  snd_ctl_close(handle);
                  goto next_card;
            }
            dev = -1;
            while (1) {
                  unsigned int count;
                  if (snd_ctl_pcm_next_device(handle, &dev)<0)
                        error("snd_ctl_pcm_next_device");
                  if (dev < 0)
                        break;
                  snd_pcm_info_set_device(pcminfo, dev);
                  snd_pcm_info_set_subdevice(pcminfo, 0);
                  snd_pcm_info_set_stream(pcminfo, stream);
                  if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
                        if (err != -ENOENT)
                              error("control digital audio info (%i): %s", card, snd_strerror(err));
                        continue;
                  }
                  printf(_("card %i: [%s,%i] %s [%s], device %i: %s [%s]\n"),
			 card, name, dev, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),
                        dev,
                        snd_pcm_info_get_id(pcminfo),
                        snd_pcm_info_get_name(pcminfo));
                  count = snd_pcm_info_get_subdevices_count(pcminfo);
                  printf( _("  Subdevices: %i/%i\n"),
                        snd_pcm_info_get_subdevices_avail(pcminfo), count);
                  for (idx = 0; idx < (int)count; idx++) {
                        snd_pcm_info_set_subdevice(pcminfo, idx);
                        if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
                              error("control digital audio playback info (%i): %s", card, snd_strerror(err));
                        } else {
                              printf(_("  Subdevice #%i: %s\n"),
                                    idx, snd_pcm_info_get_subdevice_name(pcminfo));
                        }
                  }
            }
            snd_ctl_close(handle);
      next_card:
            if (snd_card_next(&card) < 0) {
                  error("snd_card_next");
                  break;
            }
      }
}


int main (int argc, char *argv[])
{
  device_list(SND_PCM_STREAM_CAPTURE);
  device_list(SND_PCM_STREAM_PLAYBACK);
}

编写以下Makefile

LDLIBS = $(shell pkg-config --libs alsa) -lm

CFLAGS = -g $(shell pkg-config --cflags alsa)

SRC = device_hardware.c
EXE = device_hardware 

all: $(EXE)

clean:
	rm $(EXE)

导入buildroot构建的工具链的environment-setup配置,并执行Make指令,得到可执行文件,将可执行文件传输到开发板运行./device_hardware,可以得到以下的输出。

device_info.png

3、播放音频文件

开发板的音频输出接口外接音箱可以实现音频播放功能。以下为使用alsa-lib的函数播放音乐的代码。

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
	      
int main(int argc, char *argv[])
{
  int i;
  int err;
  int buf[128];
  snd_pcm_t *playback_handle;
  snd_pcm_hw_params_t *hw_params;
  FILE *fin;
  size_t nread;
  unsigned int rate = 44100;	

  if (argc != 3) {
    fprintf(stderr, "Usage: %s card file\n", argv[0]);
    exit(1);
  }

  if ((err = snd_pcm_open (&playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
    fprintf (stderr, "cannot open audio device %s (%s)\n", 
	     argv[1],
	     snd_strerror (err));
    exit (1);
  }
		   
  if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
    fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
				 
  if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) {
    fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
	
  if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
    fprintf (stderr, "cannot set access type (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
	
  if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
    fprintf (stderr, "cannot set sample format (%s)\n",
	     snd_strerror (err));
    exit (1);
  }


  if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &rate, 0)) < 0) {
    fprintf (stderr, "cannot set sample rate (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("Rate set to %d\n", rate);
	
  if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) < 0) {
    fprintf (stderr, "cannot set channel count (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
	
  if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0) {
    fprintf (stderr, "cannot set parameters (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
	
  snd_pcm_hw_params_free (hw_params);
	
  /*
  if ((err = snd_pcm_prepare (playback_handle)) < 0) {
    fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  */
	
  if ((fin = fopen(argv[2], "r")) == NULL) {
      fprintf(stderr, "Can't open %s for reading\n", argv[2]);
      exit(1);
  }

  while ((nread = fread(buf, sizeof(int), 128, fin)) > 0) {
    //printf("writing\n");
    if ((err = snd_pcm_writei (playback_handle, buf, nread)) != nread) {
      fprintf (stderr, "write to audio interface failed (%s)\n",
	       snd_strerror (err));
      snd_pcm_prepare(playback_handle);
    }
  }

  snd_pcm_drain(playback_handle);	
  snd_pcm_close (playback_handle);
  exit (0);
}

保存为playback.c,并编写以下Makefile文件

LDLIBS = $(shell pkg-config --libs alsa) -lm

CFLAGS = -g $(shell pkg-config --cflags alsa)

SRC = playback.c
EXE = playback

all: $(EXE)

clean:
	rm $(EXE)

执行Make得到可执行文件,传输到开发板执行./playback hw:0 music.wav,播放音乐文件的效果如视频所示。

alsa_playback

更多回帖

发帖
×
20
完善资料,
赚取积分