play_stream.lua 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. --[[
  2. @module play_stream
  3. @summary 流式播放
  4. @version 1.0
  5. @date 2025.09.08
  6. @author 梁健
  7. @usage
  8. 本文件为流式播放应用功能模块,核心业务逻辑为:
  9. 1、创建一个播放流式音频task(task_audio)
  10. 2、创建一个模拟获取流式音频的task(audio_get_data)
  11. 3、此task通过流式传输不断向audio_buff(zbuff)填入播放的音频
  12. 4、播放task 不断播放audio_buff(zbuff)的音频内容
  13. 5、使用powerkey 按键进行音量减小,点击boot 按键进行音量增加
  14. 本文件没有对外接口,直接在main.lua中require "play_stream"就可以加载运行;
  15. ]]
  16. exaudio = require("exaudio")
  17. local audio_buff -- 存储音频数据的zbuff,使用方法见https://docs.openluat.com/osapi/core/zbuff/
  18. local write_seek = 0 -- 写入zbuff 的指针位置
  19. local read_seek = 0 -- 驱动zbuff 的指针位置
  20. local zbuff_size = 61440 -- 申请内存的最大值,需要1024的倍数
  21. local read_size = 4096 -- 除了最后一包数据,读写zbuff 都要按照1024倍数进行
  22. local file = nil -- 文件句柄,打开文件后,将会被赋值
  23. -- 音频初始化设置参数,exaudio.setup 传入参数
  24. local audio_setup_param ={
  25. model= "es8311", -- dac类型,可填入"es8311","es8211"
  26. i2c_id = 0, -- i2c_id,可填入0,1 并使用pins 工具配置对应的管脚
  27. pa_ctrl = 162, -- 音频放大器电源控制管脚
  28. dac_ctrl = 164, -- 音频编解码芯片电源控制管脚
  29. }
  30. -- 当前音频快播完的时候,产生的回调,返回数据后,exaudio会将音频传入core,注意不可以在回调函数中加入耗时和延迟的操作
  31. local function audio_need_more_data()
  32. if read_seek >= write_seek then -- 读指针,赶上了写指针,播放完成
  33. log.info("播放完了")
  34. return nil
  35. else
  36. if write_seek - read_seek < read_size then --当写指针和读指针的位置小于4096 字节的时候,以差值读取
  37. read_size = write_seek - read_seek
  38. end
  39. audio_buff:seek(read_seek) -- 复位读指针位置
  40. local read_data = audio_buff:read(read_size) -- 读取zbuff位置
  41. log.info("获取音频",read_seek,#read_data)
  42. read_seek = read_seek + #read_data -- 写指针位置修改
  43. return read_data
  44. end
  45. end
  46. -- 播放完成回调
  47. local function play_end(event)
  48. if event == exaudio.PLAY_DONE then
  49. log.info("播放完成",exaudio.is_end())
  50. end
  51. end
  52. -- 流式播放音频播放的配置
  53. local audio_play_param ={
  54. type= 2, -- 播放类型,有0,播放文件,1.播放tts 2. 流式播放
  55. -- 如果是播放文件,支持mp3,amr,wav格式
  56. -- 如果是tts,内容格式见:https://wiki.luatos.com/chips/air780e/tts.html?highlight=tts
  57. -- 流式播放,仅支持PCM 格式音频,如果是流式播放,则sampling_rate, sampling_depth,signed_or_unsigned 必填写
  58. content = audio_need_more_data, -- 如果播放类型为0时,则填入string 是播放单个音频文件,如果是表则是播放多段音频文件。
  59. -- 如果播放tts 则填入要播放的内容。
  60. -- 如果为2,流式播放,则填入音频回调函数
  61. cbfnc = play_end, -- 播放完毕回调函数
  62. sampling_rate = 16000, -- 采样率,仅为流式播放起作用
  63. sampling_depth = 16, -- 采样位位深,仅流式播放的时候才有作用
  64. signed_or_unsigned = true -- PCM 的数据是否有符号,仅为流式播放起作用
  65. }
  66. ---------------------------------
  67. ---通过BOOT 按键增大音量---
  68. ---------------------------------
  69. local volume_number = 50
  70. local function add_volume()
  71. volume_number = volume_number + 20
  72. log.info("增大音量",volume_number)
  73. exaudio.vol(volume_number)
  74. end
  75. gpio.setup(0, add_volume, gpio.PULLDOWN, gpio.RISING)
  76. gpio.debounce(0, 200, 1)
  77. ---------------------------------
  78. ---通过POWERKEY按键减小音量-------
  79. ---------------------------------
  80. local function down_volume()
  81. volume_number = volume_number - 15
  82. log.info("减小音量",volume_number)
  83. exaudio.vol(volume_number)
  84. end
  85. gpio.setup(gpio.PWR_KEY, down_volume, gpio.PULLUP, gpio.FALLING)
  86. gpio.debounce(gpio.PWR_KEY, 200, 1) -- 防抖,防止频繁触发
  87. ---------------------------------
  88. ---------模拟获取音频task---------
  89. ---------------------------------
  90. local function audio_get_data()
  91. log.info("开始流式获取音频数据")
  92. file = io.open("/luadb/test.pcm", "rb") -- 模拟流式播放音源,实际的音频数据来源也可以来自网络或者本地存储
  93. audio_buff = zbuff.create(zbuff_size)
  94. while true do
  95. log.info("存入音频",write_seek)
  96. local read_data = file:read(read_size) -- 读取文件,模拟流式音频源
  97. if read_data == nil then
  98. file:close() -- 模拟音频获取完毕,关闭音频文件
  99. break
  100. end
  101. audio_buff:seek(write_seek) -- 复位写指针位置
  102. audio_buff:write(read_data) -- 模拟网页端获取音频数据
  103. write_seek = write_seek + #read_data -- 写指针位置修改
  104. sys.wait(100) -- 写数据需要留出事件给其他task 运行代码
  105. end
  106. end
  107. sys.taskInitEx(audio_get_data, "audio_get_data")
  108. ---------------------------------
  109. ------------通过主task------------
  110. ---------------------------------
  111. local taskName = "task_audio"
  112. local function audio_task()
  113. log.info("开始流式播报")
  114. if exaudio.setup(audio_setup_param) then
  115. exaudio.play_start(audio_play_param)
  116. end
  117. end
  118. sys.taskInitEx(audio_task, taskName)