回答

收藏

[评测分享] 【更适合初学者的开发板ELF 1】+ 7.音频播放测试

#板卡评测 #板卡评测 1988 人阅读 | 0 人回复 | 2023-12-17

1.ALSA概述
ALSA(Advanced Linux Sound Architecture)是Linux操作系统的音频子系统,负责处理声音和音频。作为Linux内核的一部分,它提供了全面的音频支持,允许应用程序与音频硬件进行通信,实现音频录制、播放和处理。ALSA的关键特点包括多硬件支持,可适用于内置声卡、USB音频设备等各种硬件。它专注于提供低延迟音频处理,因此适用于实时音频应用,如音乐制作和游戏。此外,ALSA具有模块化设计,可轻松添加或替换音频驱动程序和组件,以满足不同需求。它还提供用户空间工具和库,用于配置和操作音频设备。尽管最初设计为Linux,但一些ALSA组件已移植到其他操作系统,实现跨**音频支持。总之,ALSA是Linux上强大的音频架构,为各种音频应用提供了可靠的音频处理功能。

2.lsa-lib库
alsa-lib 是一套 Linux 应用层的 C 语言函数库,为音频应用程序开发提供了一套统一、标准
的接口,应用程序只需调用这一套 API 即可完成对底层声卡设备的操控,譬如播放与录音。
用户空间的 alsa-lib对应用程序提供了统一的API 接口,这样可以隐藏驱动层的实现细节,简化了应用
程序的实现难度、无需应用程序开发人员直接去读写音频设备节点。
在 Linux 内核设备驱动层、基于 ALSA 音频驱动框架注册的sound 设备会在/dev/snd 目录下生成相应的:

3.硬件连接
如下所示为板子的外设接口示意图,引出了音频接口,通过耳机接口引出,需要将耳机线连接到Audio接口。电路原理图设计:

耳机接口:

实物连接:

4.应用编程测试
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <alsa/asoundlib.h>
  6. #define PCM_PLAYBACK_DEV "hw:0,0"
  7. typedef struct WAV_RIFF {
  8. char ChunkID[4]; /* "RIFF" */
  9. u_int32_t ChunkSize;
  10. char Format[4]; /* "WAVE" */
  11. } __attribute__ ((packed)) RIFF_t;
  12. typedef struct WAV_FMT {
  13. char Subchunk1ID[4]; /* "fmt " */
  14. u_int32_t Subchunk1Size; /* 16 for PCM */
  15. u_int16_t AudioFormat; /* PCM = 1*/
  16. u_int16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */
  17. u_int32_t SampleRate; /* 8000, 44100, etc. */
  18. u_int32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
  19. u_int16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */
  20. u_int16_t BitsPerSample; /* 8bits, 16bits, etc. */
  21. } __attribute__ ((packed)) FMT_t;
  22. static FMT_t wav_fmt;
  23. typedef struct WAV_DATA {
  24. char Subchunk2ID[4]; /* "data" */
  25. u_int32_t Subchunk2Size; /* data size */
  26. } __attribute__ ((packed)) DATA_t;

  27. static snd_pcm_t *pcm = NULL; //pcm 句柄
  28. static unsigned int buf_bytes; //应用程序缓冲区的大小(字节为单位)
  29. static void *buf = NULL; //指向应用程序缓冲区的指针
  30. static int fd = -1; //指向 WAV 音频文件的文件描述符
  31. static snd_pcm_uframes_t period_size = 1024; //周期大小(单位: 帧)
  32. static unsigned int periods = 4; //周期数(设备驱动层 buffer 的大小)
  33. static int snd_pcm_init(void)
  34. {
  35. snd_pcm_hw_params_t *hwparams = NULL;
  36. int ret;
  37. /* 打开 PCM 设备 */
  38. ret = snd_pcm_open(&pcm, PCM_PLAYBACK_DEV, SND_PCM_STREAM_PLAYBACK, 0);
  39. if (0 > ret) {
  40. fprintf(stderr, "snd_pcm_open error: %s: %s\n",
  41. PCM_PLAYBACK_DEV, snd_strerror(ret));
  42. return -1;
  43. }
  44. /* 实例化 hwparams 对象 */
  45. snd_pcm_hw_params_malloc(&hwparams);
  46. /* 获取 PCM 设备当前硬件配置,对 hwparams 进行初始化 */
  47. ret = snd_pcm_hw_params_any(pcm, hwparams);
  48. if (0 > ret) {
  49. fprintf(stderr, "snd_pcm_hw_params_any error: %s\n", snd_strerror(ret));
  50. goto err2;
  51. }
  52. /**************
  53. 设置参数
  54. ***************/
  55. /* 设置访问类型: 交错模式 */
  56. ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
  57. if (0 > ret) {
  58. fprintf(stderr, "snd_pcm_hw_params_set_access error: %s\n", snd_strerror(ret));
  59. goto err2;
  60. }
  61. /* 设置数据格式: 有符号 16 位、小端模式 */
  62. ret = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
  63. if (0 > ret) {
  64. fprintf(stderr, "snd_pcm_hw_params_set_format error: %s\n", snd_strerror(ret));
  65. goto err2;
  66. }
  67. /* 设置采样率 */
  68. ret = snd_pcm_hw_params_set_rate(pcm, hwparams, wav_fmt.SampleRate, 0);
  69. if (0 > ret) {
  70. fprintf(stderr, "snd_pcm_hw_params_set_rate error: %s\n", snd_strerror(ret));
  71. goto err2;
  72. }
  73. /* 设置声道数: 双声道 */
  74. ret = snd_pcm_hw_params_set_channels(pcm, hwparams, wav_fmt.NumChannels);
  75. if (0 > ret) {
  76. fprintf(stderr, "snd_pcm_hw_params_set_channels error: %s\n", snd_strerror(ret));
  77. goto err2;
  78. }
  79. /* 设置周期大小: period_size */
  80. ret = snd_pcm_hw_params_set_period_size(pcm, hwparams, period_size, 0);
  81. if (0 > ret) {
  82. fprintf(stderr, "snd_pcm_hw_params_set_period_size error: %s\n", snd_strerror(ret));
  83. goto err2;
  84. }
  85. /* 设置周期数(驱动层 buffer 的大小): periods */
  86. ret = snd_pcm_hw_params_set_periods(pcm, hwparams, periods, 0);
  87. if (0 > ret) {
  88. fprintf(stderr, "snd_pcm_hw_params_set_periods error: %s\n", snd_strerror(ret));
  89. goto err2;
  90. }
  91. /* 使配置生效 */
  92. ret = snd_pcm_hw_params(pcm, hwparams);
  93. snd_pcm_hw_params_free(hwparams); //释放 hwparams 对象占用的内存
  94. if (0 > ret) {
  95. fprintf(stderr, "snd_pcm_hw_params error: %s\n", snd_strerror(ret));
  96. goto err1;
  97. }
  98. buf_bytes = period_size * wav_fmt.BlockAlign; //变量赋值,一个周期的字节大小
  99. return 0;
  100. err2:
  101. snd_pcm_hw_params_free(hwparams);
  102. err1:
  103. snd_pcm_close(pcm); //关闭 pcm 设备
  104. return -1;
  105. }
  106. static int open_wav_file(const char *file)
  107. {
  108. RIFF_t wav_riff;
  109. DATA_t wav_data;
  110. int ret;
  111. fd = open(file, O_RDONLY);
  112. if (0 > fd) {
  113. fprintf(stderr, "open error: %s: %s\n", file, strerror(errno));
  114. return -1;
  115. }
  116. ret = read(fd, &wav_riff, sizeof(RIFF_t));
  117. if (sizeof(RIFF_t) != ret) {
  118. if (0 > ret)
  119. perror("read error");
  120. else
  121. fprintf(stderr, "check error: %s\n", file);
  122. close(fd);
  123. return -1;
  124. }
  125. if (strncmp("RIFF", wav_riff.ChunkID, 4) ||//校验
  126. strncmp("WAVE", wav_riff.Format, 4)) {
  127. fprintf(stderr, "check error: %s\n", file);
  128. close(fd);
  129. return -1;
  130. }
  131. ret = read(fd, &wav_fmt, sizeof(FMT_t));
  132. if (sizeof(FMT_t) != ret) {
  133. if (0 > ret)
  134. perror("read error");
  135. else
  136. fprintf(stderr, "check error: %s\n", file);
  137. close(fd);
  138. return -1;
  139. }
  140. if (strncmp("fmt ", wav_fmt.Subchunk1ID, 4)) {//校验
  141. fprintf(stderr, "check error: %s\n", file);
  142. close(fd);
  143. return -1;
  144. }
  145. printf("<<<<音频文件格式信息>>>>\n\n");
  146. printf(" file name: %s\n", file);
  147. printf(" Subchunk1Size: %u\n", wav_fmt.Subchunk1Size);
  148. printf(" AudioFormat: %u\n", wav_fmt.AudioFormat);
  149. printf(" NumChannels: %u\n", wav_fmt.NumChannels);
  150. printf(" SampleRate: %u\n", wav_fmt.SampleRate);
  151. printf(" ByteRate: %u\n", wav_fmt.ByteRate);
  152. printf(" BlockAlign: %u\n", wav_fmt.BlockAlign);
  153. printf(" BitsPerSample: %u\n\n", wav_fmt.BitsPerSample);
  154. if (0 > lseek(fd, sizeof(RIFF_t) + 8 + wav_fmt.Subchunk1Size,
  155. SEEK_SET)) {
  156. perror("lseek error");
  157. close(fd);
  158. return -1;
  159. }
  160. while(sizeof(DATA_t) == read(fd, &wav_data, sizeof(DATA_t))) {
  161. if (!strncmp("data", wav_data.Subchunk2ID, 4))
  162. return 0;
  163. if (0 > lseek(fd, wav_data.Subchunk2Size, SEEK_CUR)) {
  164. perror("lseek error");
  165. close(fd);
  166. return -1;
  167. }
  168. }
  169. fprintf(stderr, "check error: %s\n", file);
  170. return -1;
  171. }
  172. int main(int argc, char *argv[])
  173. {
  174. int ret;
  175. if (2 != argc) {
  176. fprintf(stderr, "Usage: %s <audio_file>\n", argv[0]);
  177. exit(EXIT_FAILURE);
  178. }
  179. if (open_wav_file(argv[1]))
  180. exit(EXIT_FAILURE);
  181. if (snd_pcm_init())
  182. goto err1;
  183. buf = malloc(buf_bytes);
  184. if (NULL == buf) {
  185. perror("malloc error");
  186. goto err2;
  187. }
  188. While(1) {
  189. memset(buf, 0x00, buf_bytes);
  190. ret = read(fd, buf, buf_bytes);
  191. if (0 >= ret)
  192. goto err3;
  193. ret = snd_pcm_writei(pcm, buf, period_size);
  194. if (0 > ret) {
  195. fprintf(stderr, "snd_pcm_writei error: %s\n", snd_strerror(ret));
  196. goto err3;
  197. }
  198. else if (ret < period_size) {
  199. if (0 > lseek(fd, (ret-period_size) * wav_fmt.BlockAlign, SEEK_CUR)) {
  200. perror("lseek error");
  201. goto err3;
  202. }
  203. }
  204. }
  205. err3:
  206. free(buf); //释放内存
  207. err2:
  208. snd_pcm_close(pcm); //关闭 pcm 设备
  209. err1:
  210. close(fd); //关闭打开的音频文件
  211. exit(EXIT_FAILURE);
  212. }
复制代码
将编译好的可执行文件添加到开发板,并添加可执行文件,将需要播放的音频同样放在当前路径下,我这里是将下载的音乐通过格式化工厂进行格式转换,重命名为test.wav。然后输入./pcm_playback  ./test.wav进行音乐播放,并打印出音频格式信息,运行程序,开始播放音乐。

分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

1326 积分
16 主题
+ 关注
热门推荐
关闭

站长推荐上一条 /3 下一条