audio.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. --- 模块功能:音频播放.
  2. -- 支持MP3、amr文件播放;
  3. -- 支持本地TTS播放、通话中TTS播放到对端(需要使用支持TTS功能的core软件)
  4. -- @module audio
  5. -- @author openLuat
  6. -- @license MIT
  7. -- @copyright openLuat
  8. -- @release 2018.3.19
  9. require "common"
  10. require "misc"
  11. require "utils"
  12. module(..., package.seeall)
  13. local req = ril.request
  14. --音频播放的协程ID
  15. local taskID
  16. --sPriority:当前播放的音频优先级
  17. --sType:当前播放的音频类型
  18. --sPath:当前播放的音频数据信息
  19. --sVol:当前播放音量
  20. --sCb:当前播放结束或者出错的回调函数
  21. --sDup:当前播放的音频是否需要重复播放
  22. --sDupInterval:如果sDup为true,此值表示重复播放的间隔(单位毫秒),默认无间隔
  23. --sStrategy:优先级相同时的播放策略,0(表示继续播放正在播放的音频,忽略请求播放的新音频),1(表示停止正在播放的音频,播放请求播放的新音频)
  24. local sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval,sStrategy
  25. local function update(priority,type,path,vol,cb,dup,dupInterval)
  26. print("audio.update",sPriority,priority,type,path,vol,cb,dup,dupInterval)
  27. if sPriority then
  28. if priority>sPriority or (priority==sPriority and sStrategy==1) then
  29. print("audio.update1",priority,type,path,vol,cb,dup,dupInterval)
  30. --此处第三个参数传入table是因为publish接口无法处理nil后面的参数
  31. sys.publish("AUDIO_PLAY_END","NEW",{pri=priority,typ=type,pth=path,vl=vol,c=cb,dp=dup,dpIntval=dupInterval})
  32. else
  33. return false
  34. end
  35. else
  36. sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval = priority,type,path,vol,cb,dup,dupInterval
  37. if vol then setVolume(vol) end
  38. end
  39. return true
  40. end
  41. local function playEnd(result)
  42. log.info("audio.playEnd",result,sCb)
  43. local cb = sCb
  44. sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval = nil
  45. if cb then cb(result) end
  46. end
  47. local function isTtsApi()
  48. return tonumber((rtos.get_version()):match("Luat_V(%d+)_"))>=29
  49. end
  50. local function taskAudio()
  51. local playFnc =
  52. {
  53. FILE = audiocore.play,
  54. TTS = function(text)
  55. if isTtsApi() then
  56. audiocore.openTTS()
  57. local _,result = sys.waitUntil("TTS_OPEN_IND")
  58. if not result then return false end
  59. audiocore.playTTS(common.utf8ToUcs2(text))
  60. else
  61. req("AT+QTTS=1") req(string.format("AT+QTTS=%d,\"%s\"",2,string.toHex(common.utf8ToUcs2(text))))
  62. end
  63. end,
  64. TTSCC = function(text) req("AT+QTTS=1") req(string.format("AT+QTTS=%d,\"%s\"",4,string.toHex(common.utf8ToUcs2(text)))) end,
  65. RECORD = function(id) f,d=record.getSize() req("AT+AUDREC=1,0,2," .. id .. "," .. d*1000)end,
  66. }
  67. local stopFnc =
  68. {
  69. FILE = audiocore.stop,
  70. TTS = function(text)
  71. if isTtsApi() then
  72. audiocore.stopTTS()
  73. sys.waitUntil("TTS_STOP_IND")
  74. audiocore.closeTTS()
  75. sys.waitUntil("TTS_CLOSE_IND")
  76. else
  77. req("AT+QTTS=3") sys.waitUntil("AUDIO_STOP_END")
  78. end
  79. end,
  80. TTSCC = function() req("AT+QTTS=3") sys.waitUntil("AUDIO_STOP_END") end,
  81. RECORD = function(id) f,d=record.getSize() req("AT+AUDREC=1,0,3," .. id .. "," .. d*1000) sys.waitUntil("AUDIO_STOP_END") end,
  82. }
  83. while true do
  84. log.info("audio.taskAudio begin",sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval)
  85. --检查参数
  86. if not playFnc[sType] then
  87. playEnd(3)
  88. if sType==nil then break end
  89. end
  90. --开始播放
  91. if playFnc[sType](sPath)==false then
  92. playEnd(1)
  93. if sType==nil then break end
  94. end
  95. --挂起播放,等待播放成功、播放失败或者有新的播放请求激活协程
  96. local _,msg,param = sys.waitUntil("AUDIO_PLAY_END")
  97. log.info("audio.taskAudio resume msg",msg)
  98. if msg=="SUCCESS" then
  99. if sDup then
  100. if sType=="TTS" and isTtsApi() then
  101. stopFnc[sType](sPath)
  102. end
  103. if sDupInterval and sDupInterval>0 then
  104. sys.waitUntil("AUDIO_PLAY_END",sDupInterval)
  105. if sType==nil then break end
  106. end
  107. else
  108. stopFnc[sType](sPath)
  109. playEnd(0)
  110. if sType==nil then break end
  111. end
  112. elseif msg=="NEW" then
  113. stopFnc[sType](sPath)
  114. playEnd(4)
  115. --if sType==nil then break end
  116. update(param.pri,param.typ,param.pth,param.vl,param.c,param.dp,param.dpIntval)
  117. elseif msg=="STOP" then
  118. if param=="TTS" and isTtsApi() then
  119. stopFnc[param]()
  120. end
  121. playEnd(5)
  122. break
  123. else
  124. stopFnc[sType](sPath)
  125. playEnd(1)
  126. if sType==nil then break end
  127. end
  128. end
  129. end
  130. --[[
  131. 函数名:urc
  132. 功能 :本功能模块内“注册的底层core通过虚拟串口主动上报的通知”的处理
  133. 参数 :
  134. data:通知的完整字符串信息
  135. prefix:通知的前缀
  136. 返回值:无
  137. ]]
  138. local function urc(data,prefix)
  139. if prefix == "+QTTS" then
  140. local flag = string.match(data,": *(%d)",string.len(prefix)+1)
  141. --停止播放tts
  142. if flag=="0" --[[or flag == "1"]] then
  143. sys.publish("AUDIO_PLAY_END","SUCCESS")
  144. end
  145. end
  146. end
  147. --[[
  148. 函数名:rsp
  149. 功能 :本功能模块内“通过虚拟串口发送到底层core软件的AT命令”的应答处理
  150. 参数 :
  151. cmd:此应答对应的AT命令
  152. success:AT命令执行结果,true或者false
  153. response:AT命令的应答中的执行结果字符串
  154. intermediate:AT命令的应答中的中间信息
  155. 返回值:无
  156. ]]
  157. local function rsp(cmd,success,response,intermediate)
  158. local prefix = string.match(cmd,"AT(%+%u+%?*)")
  159. if prefix == "+QTTS" then
  160. local action = string.match(cmd,"QTTS=(%d)")
  161. if not success then
  162. if action=="1" or action=="2" then
  163. sys.publish("AUDIO_PLAY_END","ERROR")
  164. end
  165. end
  166. if action=="3" then
  167. sys.publish("AUDIO_STOP_END")
  168. end
  169. end
  170. end
  171. ril.regUrc("+QTTS",urc)
  172. ril.regRsp("+QTTS",rsp,0)
  173. local function audioMsg(msg)
  174. sys.publish("AUDIO_PLAY_END",msg.play_end_ind==true and "SUCCESS" or "ERROR")
  175. end
  176. local function ttsMsg(msg)
  177. log.info("audio.ttsMsg",msg.type,msg.result)
  178. local tag = {[0]="CLOSE", [1]="OPEN", [2]="PLAY", [3]="STOP"}
  179. if msg.type==2 then
  180. sys.publish("AUDIO_PLAY_END",msg.result and "SUCCESS" or "ERROR")
  181. else
  182. if tag[msg.type] then sys.publish("TTS_"..tag[msg.type].."_IND",msg.result) end
  183. end
  184. end
  185. --注册core上报的rtos.MSG_AUDIO消息的处理函数
  186. rtos.on(rtos.MSG_AUDIO,audioMsg)
  187. if isTtsApi() then
  188. rtos.on(rtos.MSG_TTS,ttsMsg)
  189. end
  190. --- 播放音频
  191. -- @number priority,音频优先级,数值越大,优先级越高
  192. -- @string type,音频类型,目前仅支持"FILE"、"TTS"、"TTSCC","RECORD"
  193. -- @string path,音频文件路径,跟typ有关
  194. -- typ为"FILE"时:表示音频文件路径
  195. -- typ为"TTS"时:表示要播放的UTF8编码格式的数据
  196. -- typ为"TTSCC"时:表示要播放给通话对端的UTF8编码格式的数据
  197. -- typ为"RECORD"时:表示要播放的录音id
  198. -- @number[opt=4] vol,播放音量,取值范围0到7,0为静音
  199. -- @function[opt=nil] cbFnc,音频播放结束时的回调函数,回调函数的调用形式如下:
  200. -- cbFnc(result)
  201. -- result表示播放结果:
  202. -- 0-播放成功结束;
  203. -- 1-播放出错
  204. -- 2-播放优先级不够,没有播放
  205. -- 3-传入的参数出错,没有播放
  206. -- 4-被新的播放请求中止
  207. -- 5-调用audio.stop接口主动停止
  208. -- @bool[opt=nil] dup,是否循环播放,true循环,false或者nil不循环
  209. -- @number[opt=0] dupInterval,循环播放间隔(单位毫秒),dup为true时,此值才有意义
  210. -- @return result,bool或者nil类型,同步调用成功返回true,否则返回false
  211. -- @usage audio.play(0,"FILE","/ldata/call.mp3")
  212. -- @usage audio.play(0,"FILE","/ldata/call.mp3",7)
  213. -- @usage audio.play(0,"FILE","/ldata/call.mp3",7,cbFnc)
  214. -- @usage 更多用法参考demo/audio/testAudio.lua
  215. function play(priority,type,path,vol,cbFnc,dup,dupInterval)
  216. if not update(priority,type,path,vol or 4,cbFnc,dup,dupInterval or 0) then return false end
  217. if not sType or not taskID or coroutine.status(taskID)=="dead" then
  218. taskID = sys.taskInit(taskAudio)
  219. end
  220. return true
  221. end
  222. --- 停止音频播放
  223. -- @return nil
  224. -- @usage audio.stop()
  225. function stop()
  226. if sType then
  227. if sType=="FILE" then
  228. audiocore.stop()
  229. elseif (sType=="TTS" and not isTtsApi()) or sType=="TTSCC" then
  230. req("AT+QTTS=3")
  231. elseif sType=="RECORD" then
  232. f,d=record.getSize() req("AT+AUDREC=1,0,3," .. sPath .. "," .. d*1000)
  233. end
  234. local typ = sType
  235. sPriority,sType,sPath,sVol,sCb,sDup,sDupInterval = nil
  236. sys.publish("AUDIO_PLAY_END","STOP",typ)
  237. end
  238. end
  239. --- 设置喇叭音量等级
  240. -- @number vol,音量值为0-7,0为静音
  241. -- @return bool result,设置成功返回true,失败返回false
  242. -- @usage audio.setVolume(7)
  243. function setVolume(vol)
  244. return audiocore.setvol(vol)
  245. end
  246. --- 设置麦克音量等级
  247. -- @number vol,音量值为0-15,0为静音
  248. -- @return bool result,设置成功返回true,失败返回false
  249. -- @usage audio.setMicVolume(14)
  250. function setMicVolume(vol)
  251. return audiocore.setmicvol(vol)
  252. end
  253. --- 设置优先级相同时的播放策略
  254. -- @number strategy,优先级相同时的播放策略;0:表示继续播放正在播放的音频,忽略请求播放的新音频;1:表示停止正在播放的音频,播放请求播放的新音频
  255. -- @return nil
  256. -- @usage audio.setStrategy(0)
  257. -- @usage audio.setStrategy(1)
  258. function setStrategy(strategy)
  259. sStrategy=strategy
  260. end
  261. --默认音频通道设置为LOUDSPEAKER,因为目前的模块只支持LOUDSPEAKER通道
  262. audiocore.setchannel(audiocore.LOUDSPEAKER)
  263. --默认音量等级设置为4级,4级是中间等级,最低为0级,最高为7级
  264. setVolume(4)
  265. --默认MIC音量等级设置为1级,最低为0级,最高为15级
  266. setMicVolume(1)