main.lua 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. -- LuaTools需要PROJECT和VERSION这两个信息
  2. PROJECT = "record"
  3. VERSION = "1.0.0"
  4. --[[]
  5. 运行环境:Air780EHV核心板+AirAUDIO_1000配件板
  6. 最后修改时间:2025-6-17
  7. 使用了如下IO口:
  8. [3, "MIC+", " PIN3脚, 用于麦克风正极"],
  9. [4, "MIC-", " PIN4脚, 用于麦克风负极"],
  10. [5, "spk+", " PIN5脚, 用于喇叭正极"],
  11. [6, "spk-", " PIN6脚, 用于喇叭负极"],
  12. [20, "AudioPA_EN", " PIN20脚, 用于PA使能脚"],
  13. 3.3V
  14. GND
  15. 执行逻辑为:
  16. 设置i2s和音频参数,提供了两种录音方式,录音到文件区或者录音到内存,然后播放,然后
  17. ]]
  18. -- sys库是标配
  19. _G.sys = require("sys")
  20. _G.sysplus = require("sysplus")
  21. --代码提供了2种方式录音,对应recordmode的值
  22. --1:直接录音到文件
  23. --2:录音到内存,然后保存到文件
  24. local recordmode = 1
  25. --代码提供了2种方式对录音文件做处理
  26. --1:发送到服务器
  27. --2:发送到串口
  28. local recordhandle = 1
  29. local taskName = "task_audio"
  30. local MSG_MD = "moreData" -- 播放缓存有空余
  31. local MSG_PD = "playDone" -- 播放完成所有数据
  32. -- amr数据存放buffer,尽可能地给大一些
  33. amr_buff = zbuff.create(20 * 1024)
  34. -- 创建一个amr的encoder
  35. encoder = nil
  36. pcm_buff0 = zbuff.create(16000)
  37. pcm_buff1 = zbuff.create(16000)
  38. audio.on(0,function(id, event, point)
  39. -- 使用play来播放文件时只有播放完成回调
  40. if event == audio.RECORD_DATA then -- 录音数据
  41. if point == 0 then
  42. log.info("buff", point, pcm_buff0:used())
  43. codec.encode(encoder, pcm_buff0, amr_buff)
  44. else
  45. log.info("buff", point, pcm_buff1:used())
  46. codec.encode(encoder, pcm_buff1, amr_buff)
  47. end
  48. elseif event == audio.RECORD_DONE then -- 录音完成
  49. sys.publish("AUDIO_RECORD_DONE")
  50. else
  51. local succ, stop, file_cnt = audio.getError(0)
  52. if not succ then
  53. if stop then
  54. log.info("用户停止播放")
  55. else
  56. log.info("第", file_cnt, "个文件解码失败")
  57. end
  58. end
  59. -- log.info("播放完成一个音频")
  60. sysplus.sendMsg(taskName, MSG_PD)
  61. end
  62. end
  63. )
  64. ---- MultipartForm上传文件
  65. -- url string 请求URL地址
  66. -- filename string 上传服务器的文件名
  67. -- filePath string 待上传文件的路径
  68. local function postMultipartFormData(url, filename, filePath)
  69. local boundary = "----WebKitFormBoundary" .. os.time()
  70. local req_headers = {
  71. ["Content-Type"] = "multipart/form-data; boundary=" .. boundary
  72. }
  73. local body = {}
  74. table.insert(
  75. body,
  76. "--" .. boundary .. '\r\nContent-Disposition: form-data; name="file"; filename="' .. filename .. '"\r\n\r\n'
  77. )
  78. table.insert(body, io.readFile(filePath))
  79. table.insert(body, "\r\n")
  80. table.insert(body, "--" .. boundary .. "--\r\n")
  81. body = table.concat(body)
  82. log.info("headers: ", "\r\n" .. json.encode(req_headers), type(body))
  83. log.info("body: " .. body:len() .. "\r\n" .. body)
  84. local code, headers, body = http.request("POST", url, req_headers, body).wait()
  85. log.info("http.post", code, headers, body)
  86. end
  87. function audio_setup()
  88. local i2c_id = 0 -- i2c_id 0
  89. local pa_pin = gpio.AUDIOPA_EN -- 喇叭pa功放脚
  90. local power_pin = 20 -- es8311电源脚
  91. local i2s_id = 0 -- i2s_id 0
  92. local i2s_mode = 0 -- i2s模式 0 主机 1 从机
  93. local i2s_sample_rate = 16000 -- 采样率
  94. local i2s_bits_per_sample = 16 -- 数据位数
  95. local i2s_channel_format = i2s.MONO_R -- 声道, 0 左声道, 1 右声道, 2 立体声
  96. local i2s_communication_format = i2s.MODE_LSB -- 格式, 可选MODE_I2S, MODE_LSB, MODE_MSB
  97. local i2s_channel_bits = 16 -- 声道的BCLK数量
  98. local multimedia_id = 0 -- 音频通道 0
  99. local pa_on_level = 1 -- PA打开电平 1 高电平 0 低电平
  100. local power_delay = 3 -- 在DAC启动前插入的冗余时间,单位100ms
  101. local pa_delay = 100 -- 在DAC启动后,延迟多长时间打开PA,单位1ms
  102. local power_on_level = 1 -- 电源控制IO的电平,默认拉高
  103. local power_time_delay = 100 -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms
  104. local voice_vol = 80 -- 喇叭音量
  105. local mic_vol = 80 -- 麦克风音量
  106. gpio.setup(power_pin, 1, gpio.PULLUP) -- 设置ES83111电源脚
  107. gpio.setup(pa_pin, 1, gpio.PULLUP) -- 设置功放PA脚
  108. sys.wait(200)
  109. i2c.setup(i2c_id, i2c.FAST) -- 设置i2c
  110. i2s.setup(
  111. i2s_id,
  112. i2s_mode,
  113. i2s_sample_rate,
  114. i2s_bits_per_sample,
  115. i2s_channel_format,
  116. i2s_communication_format,
  117. i2s_channel_bits
  118. ) -- 设置i2s
  119. audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
  120. audio.setBus(
  121. multimedia_id,
  122. audio.BUS_I2S,
  123. {
  124. chip = "es8311",
  125. i2cid = i2c_id,
  126. i2sid = i2s_id
  127. }
  128. ) -- 通道0的硬件输出通道设置为I2S
  129. audio.vol(multimedia_id, voice_vol)
  130. audio.micVol(multimedia_id, mic_vol)
  131. sys.publish("AUDIO_READY")
  132. end
  133. -- 配置好audio外设
  134. sys.taskInit(audio_setup)
  135. local function audio_task()
  136. sys.waitUntil("AUDIO_READY")
  137. sys.wait(5000)
  138. local result
  139. -- 下面为录音demo,根据适配情况选择性开启
  140. local recordPath = "/record.amr"
  141. if recordmode == 1 then
  142. -- -- 直接录音到文件
  143. err = audio.record(0, audio.AMR, 5, 7, recordPath)
  144. sys.waitUntil("AUDIO_RECORD_DONE")
  145. log.info("record", "录音结束")
  146. elseif recordmode == 2 then
  147. -- 录音到内存自行编码
  148. encoder = codec.create(codec.AMR, false, 7)
  149. log.info("encoder", encoder)
  150. log.info("开始录音")
  151. err = audio.record(0, audio.AMR, 5, 7, nil, nil, pcm_buff0, pcm_buff1)
  152. sys.waitUntil("AUDIO_RECORD_DONE")
  153. log.info("record", "录音结束")
  154. os.remove(recordPath)
  155. io.writeFile(recordPath, "#!AMR\n")
  156. io.writeFile(recordPath, amr_buff:query(), "a+b")
  157. end
  158. result = audio.play(0, {recordPath})
  159. if result then
  160. -- 等待音频通道的回调消息,或者切换歌曲的消息
  161. while true do
  162. msg = sysplus.waitMsg(taskName, nil)
  163. if type(msg) == "table" then
  164. if msg[1] == MSG_PD then
  165. log.info("播放结束")
  166. break
  167. end
  168. else
  169. log.error(type(msg), msg)
  170. end
  171. end
  172. else
  173. log.debug("解码失败!")
  174. sys.wait(1000)
  175. end
  176. -- 下面的演示是将音频文件发送到服务器上,如有需要,可以将下面代码注释打开,这里的url是合宙的文件上传测试服务器,上传的文件到http://tools.openluat.com/tools/device-upload-test查看
  177. if recordhandle == 1 then
  178. local timeTable = os.date("*t", os.time())
  179. local nowTime =
  180. string.format(
  181. "%4d%02d%02d_%02d%02d%02d",
  182. timeTable.year,
  183. timeTable.month,
  184. timeTable.day,
  185. timeTable.hour,
  186. timeTable.min,
  187. timeTable.sec
  188. )
  189. local filename = mobile.imei() .. "_" .. nowTime .. ".amr"
  190. postMultipartFormData("http://tools.openluat.com/api/site/device_upload_file", filename, recordPath)
  191. elseif recordhandle == 2 then
  192. -- 该方法为从串口1,把录音数据传给串口1
  193. uart.setup(1, 115200) -- 开启串口1
  194. uart.write(1, io.readFile(recordPath)) -- 向串口发送录音文件
  195. end
  196. end
  197. sysplus.taskInitEx(audio_task, taskName)
  198. -- 用户代码已结束---------------------------------------------
  199. -- 结尾总是这一句
  200. sys.run()
  201. -- sys.run()之后后面不要加任何语句!!!!!