查看: 626|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-19 21:11
  • 签到天数: 66 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2023-12-17 11:31:03 | 显示全部楼层 |阅读模式
    分享到:
    1.ALSA概述
    ALSA(Advanced Linux Sound Architecture)是Linux操作系统的音频子系统,负责处理声音和音频。作为Linux内核的一部分,它提供了全面的音频支持,允许应用程序与音频硬件进行通信,实现音频录制、播放和处理。ALSA的关键特点包括多硬件支持,可适用于内置声卡、USB音频设备等各种硬件。它专注于提供低延迟音频处理,因此适用于实时音频应用,如音乐制作和游戏。此外,ALSA具有模块化设计,可轻松添加或替换音频驱动程序和组件,以满足不同需求。它还提供用户空间工具和库,用于配置和操作音频设备。尽管最初设计为Linux,但一些ALSA组件已移植到其他操作系统,实现跨**音频支持。总之,ALSA是Linux上强大的音频架构,为各种音频应用提供了可靠的音频处理功能。
    1.png
    2.lsa-lib库
    alsa-lib 是一套 Linux 应用层的 C 语言函数库,为音频应用程序开发提供了一套统一、标准
    的接口,应用程序只需调用这一套 API 即可完成对底层声卡设备的操控,譬如播放与录音。
    用户空间的 alsa-lib对应用程序提供了统一的API 接口,这样可以隐藏驱动层的实现细节,简化了应用
    程序的实现难度、无需应用程序开发人员直接去读写音频设备节点。
    在 Linux 内核设备驱动层、基于 ALSA 音频驱动框架注册的sound 设备会在/dev/snd 目录下生成相应的:
    2.png
    3.硬件连接
    如下所示为板子的外设接口示意图,引出了音频接口,通过耳机接口引出,需要将耳机线连接到Audio接口。电路原理图设计:
    3.png
    耳机接口:
    4.png
    实物连接:
    5.jpg
    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进行音乐播放,并打印出音频格式信息,运行程序,开始播放音乐。
    6.png
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

    手机版|小黑屋|与非网

    GMT+8, 2024-5-20 19:57 , Processed in 0.115854 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.