luat_lib_multimedia_audio.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. /*
  2. @module audio
  3. @summary 多媒体-音频
  4. @version 1.0
  5. @date 2022.03.11
  6. @demo multimedia
  7. @tag LUAT_USE_MEDIA
  8. */
  9. #include "luat_base.h"
  10. #include "luat_msgbus.h"
  11. #include "luat_zbuff.h"
  12. #define LUAT_LOG_TAG "audio"
  13. #include "luat_log.h"
  14. #include "luat_multimedia.h"
  15. #include "luat_audio.h"
  16. #include "luat_mem.h"
  17. #ifndef __BSP_COMMON_H__
  18. #include "c_common.h"
  19. #endif
  20. #ifdef LUAT_USE_RECORD
  21. static luat_record_ctrl_t g_s_record = {0};
  22. #endif
  23. static luat_multimedia_cb_t multimedia_cbs[MAX_DEVICE_COUNT];
  24. int l_multimedia_raw_handler(lua_State *L, void* ptr) {
  25. (void)ptr;
  26. rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
  27. if (multimedia_cbs[msg->arg2].function_ref) {
  28. lua_geti(L, LUA_REGISTRYINDEX, multimedia_cbs[msg->arg2].function_ref);
  29. if (lua_isfunction(L, -1)) {
  30. lua_pushinteger(L, msg->arg2);
  31. lua_pushinteger(L, msg->arg1);
  32. #ifdef LUAT_USE_RECORD
  33. if (msg->arg1 == LUAT_MULTIMEDIA_CB_RECORD_DATA){
  34. lua_pushinteger(L, (int)msg->ptr);
  35. lua_call(L, 3, 0);
  36. }else{
  37. lua_call(L, 2, 0);
  38. }
  39. #else
  40. lua_call(L, 2, 0);
  41. #endif
  42. }
  43. }
  44. lua_pushinteger(L, 0);
  45. return 1;
  46. }
  47. /*
  48. 启动一个多媒体通道准备播放音频
  49. @api audio.start(id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed)
  50. @int 多媒体播放通道号
  51. @int 音频格式
  52. @int 声音通道数
  53. @int 采样频率
  54. @int 采样位数
  55. @boolean 是否有符号,默认true
  56. @return boolean 成功true, 失败false
  57. @usage
  58. audio.start(0, audio.PCM, 1, 16000, 16)
  59. */
  60. static int l_audio_start_raw(lua_State *L){
  61. int multimedia_id = luaL_checkinteger(L, 1);
  62. int audio_format = luaL_checkinteger(L, 2);
  63. int num_channels= luaL_checkinteger(L, 3);
  64. int sample_rate = luaL_checkinteger(L, 4);
  65. int bits_per_sample = luaL_checkinteger(L, 5);
  66. int is_signed = 1;
  67. if (lua_isboolean(L, 6))
  68. {
  69. is_signed = lua_toboolean(L, 6);
  70. }
  71. lua_pushboolean(L, !luat_audio_start_raw(multimedia_id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed));
  72. return 1;
  73. }
  74. #ifdef LUAT_USE_RECORD
  75. #ifdef LUAT_SUPPORT_AMR
  76. #include "interf_enc.h"
  77. #include "interf_dec.h"
  78. #endif
  79. #include "luat_fs.h"
  80. #define RECORD_ONCE_LEN 5
  81. #ifdef LUAT_SUPPORT_AMR
  82. static void record_encode_amr(uint8_t *data, uint32_t len){
  83. uint8_t outbuf[64];
  84. int16_t *pcm = (int16_t *)data;
  85. uint32_t total_len = len >> 1;
  86. uint32_t done_len = 0;
  87. uint8_t out_len;
  88. uint32_t pcm_len = (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)?160:320;
  89. while ((total_len - done_len) >= pcm_len){
  90. #ifdef LUAT_USE_INTER_AMR
  91. luat_audio_inter_amr_coder_encode(g_s_record.encoder_handler, &pcm[done_len], outbuf,&out_len);
  92. #else
  93. out_len = Encoder_Interface_Encode(g_s_record.encoder_handler, g_s_record.quailty , &pcm[done_len], outbuf, 0);
  94. #endif
  95. if (out_len <= 0){
  96. LLOGD("encode error in %d,result %d", done_len, out_len);
  97. }else{
  98. luat_fs_fwrite(outbuf, out_len, 1, g_s_record.fd);
  99. }
  100. done_len += pcm_len;
  101. }
  102. }
  103. static void record_stop_encode_amr(void){
  104. #ifdef LUAT_USE_INTER_AMR
  105. luat_audio_inter_amr_coder_deinit(g_s_record.encoder_handler);
  106. #else
  107. Encoder_Interface_exit(g_s_record.encoder_handler);
  108. #endif
  109. g_s_record.encoder_handler = NULL;
  110. }
  111. #endif
  112. static void record_stop(uint8_t *data, uint32_t len);
  113. static void record_buffer_full(void)
  114. {
  115. rtos_msg_t msg = {0};
  116. msg.handler = l_multimedia_raw_handler;
  117. msg.arg1 = LUAT_MULTIMEDIA_CB_RECORD_DATA;
  118. msg.arg2 = g_s_record.multimedia_id;
  119. msg.ptr = (void *)((uint32_t)g_s_record.record_buffer_index);
  120. luat_msgbus_put(&msg, 1);
  121. g_s_record.record_buffer_index = !g_s_record.record_buffer_index;
  122. g_s_record.record_buffer[g_s_record.record_buffer_index]->used = 0;
  123. }
  124. static void record_run(uint8_t *data, uint32_t len)
  125. {
  126. if (g_s_record.fd){
  127. #ifdef LUAT_SUPPORT_AMR
  128. if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){
  129. record_encode_amr(data, len);
  130. }
  131. else
  132. #endif
  133. {
  134. luat_fs_fwrite(data, len, 1, g_s_record.fd);
  135. }
  136. }else{
  137. memcpy(g_s_record.record_buffer[g_s_record.record_buffer_index]->addr + g_s_record.record_buffer[g_s_record.record_buffer_index]->used, data, len);
  138. g_s_record.record_buffer[g_s_record.record_buffer_index]->used += len;
  139. if (g_s_record.record_buffer[g_s_record.record_buffer_index]->used >= g_s_record.record_callback_level)
  140. {
  141. record_buffer_full();
  142. }
  143. }
  144. if (g_s_record.record_time)
  145. {
  146. g_s_record.record_time_tmp++;
  147. if (g_s_record.record_time_tmp >= (g_s_record.record_time * 10) )
  148. {
  149. record_stop(NULL, 0);
  150. }
  151. }
  152. }
  153. static int record_cb(uint8_t id ,luat_i2s_event_t event, uint8_t *rx_data, uint32_t rx_len, void *param)
  154. {
  155. switch(event)
  156. {
  157. case LUAT_I2S_EVENT_RX_DONE:
  158. luat_audio_run_callback_in_task(record_run, rx_data, rx_len);
  159. break;
  160. default:
  161. break;
  162. }
  163. return 0;
  164. }
  165. static void record_no_i2s_cb(uint8_t id, uint8_t *rx_data, uint32_t rx_len, void *param)
  166. {
  167. luat_audio_run_callback_in_task(record_run, rx_data, rx_len);
  168. }
  169. static void record_start(uint8_t *data, uint32_t len){
  170. //需要保存文件,看情况打开编码功能
  171. if (g_s_record.fd){
  172. if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){
  173. #ifdef LUAT_SUPPORT_AMR
  174. #ifdef LUAT_USE_INTER_AMR
  175. g_s_record.encoder_handler = luat_audio_inter_amr_coder_init(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB?0:1, g_s_record.quailty);
  176. #else
  177. g_s_record.encoder_handler = Encoder_Interface_init(g_s_record.quailty);
  178. #endif
  179. if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB){
  180. luat_fs_fwrite("#!AMR\n", 6, 1, g_s_record.fd);
  181. }else{
  182. luat_fs_fwrite("#!AMR-WB\n", 9, 1, g_s_record.fd);
  183. }
  184. #endif
  185. }
  186. }
  187. luat_audio_conf_t *audio = luat_audio_get_config(g_s_record.multimedia_id);
  188. if (LUAT_AUDIO_BUS_I2S == audio->bus_type)
  189. {
  190. luat_i2s_conf_t *i2s = luat_i2s_get_config(g_s_record.multimedia_id);
  191. g_s_record.bak_cb_rx_len = i2s->cb_rx_len;
  192. g_s_record.bak_is_full_duplex = i2s->is_full_duplex;
  193. g_s_record.bak_sample_rate = i2s->sample_rate;
  194. g_s_record.bak_luat_i2s_event_callback = i2s->luat_i2s_event_callback;
  195. i2s->is_full_duplex = 1;
  196. i2s->luat_i2s_event_callback = record_cb;
  197. switch(g_s_record.type)
  198. {
  199. case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB:
  200. case LUAT_MULTIMEDIA_DATA_TYPE_PCM:
  201. i2s->cb_rx_len = 320 * RECORD_ONCE_LEN;
  202. i2s->sample_rate = 8000;
  203. break;
  204. case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
  205. i2s->cb_rx_len = 640 * RECORD_ONCE_LEN;
  206. i2s->sample_rate = 16000;
  207. break;
  208. default:
  209. if (g_s_record.type <= 32000)
  210. {
  211. i2s->cb_rx_len = g_s_record.type / 5;
  212. }
  213. else if (g_s_record.type < 80000)
  214. {
  215. i2s->cb_rx_len = g_s_record.type / 10;
  216. }
  217. else
  218. {
  219. i2s->cb_rx_len = g_s_record.type / 20;
  220. }
  221. i2s->sample_rate = g_s_record.type;
  222. break;
  223. }
  224. luat_audio_record_and_play(g_s_record.multimedia_id, i2s->sample_rate, NULL, 3200, 2);
  225. } else { //非I2S的录音device
  226. uint32_t sample_rate = 8000;
  227. if (g_s_record.type >= 8000)
  228. {
  229. sample_rate = g_s_record.type;
  230. }
  231. else
  232. {
  233. switch(g_s_record.type)
  234. {
  235. case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB:
  236. sample_rate = 16000;
  237. break;
  238. }
  239. }
  240. luat_audio_setup_record_callback(g_s_record.multimedia_id, record_no_i2s_cb, &g_s_record);
  241. luat_audio_record_and_play(g_s_record.multimedia_id, sample_rate, NULL, 3200, 2);
  242. }
  243. }
  244. static void record_stop(uint8_t *data, uint32_t len){
  245. rtos_msg_t msg = {0};
  246. //关闭audio硬件功能
  247. luat_audio_record_stop(g_s_record.multimedia_id);
  248. luat_audio_pm_request(g_s_record.multimedia_id, LUAT_AUDIO_PM_STANDBY);
  249. luat_audio_conf_t *audio = luat_audio_get_config(g_s_record.multimedia_id);
  250. if (LUAT_AUDIO_BUS_I2S == audio->bus_type)
  251. {
  252. //还原参数
  253. luat_i2s_conf_t *i2s = luat_i2s_get_config(g_s_record.multimedia_id);
  254. i2s->cb_rx_len = g_s_record.bak_cb_rx_len;
  255. i2s->is_full_duplex = g_s_record.bak_is_full_duplex;
  256. i2s->sample_rate = g_s_record.bak_sample_rate;
  257. i2s->luat_i2s_event_callback = g_s_record.bak_luat_i2s_event_callback;
  258. }
  259. //录音存文件时,看情况关闭编码功能
  260. if (g_s_record.fd) {
  261. if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){
  262. #ifdef LUAT_SUPPORT_AMR
  263. record_stop_encode_amr();
  264. #endif
  265. }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_PCM){
  266. // 不需要特殊处理
  267. }
  268. luat_fs_fclose(g_s_record.fd);
  269. g_s_record.fd = NULL;
  270. }
  271. //通知luat task清除zbuff数据,并回调用户
  272. msg.handler = l_multimedia_raw_handler;
  273. msg.arg1 = LUAT_MULTIMEDIA_CB_RECORD_DONE;
  274. msg.arg2 = g_s_record.multimedia_id;
  275. g_s_record.record_time_tmp = 0;
  276. g_s_record.is_run = 0;
  277. g_s_record.record_buffer_index = 0;
  278. luat_msgbus_put(&msg, 1);
  279. }
  280. /**
  281. 录音
  282. @api audio.record(id, record_type, record_time, amr_quailty, path, record_callback_time, buff0, buff1,channelCount)
  283. @int id 多媒体播放通道号
  284. @int record_type 录音音频格式,支持 audio.AMR audio.PCM (部分平台支持audio.AMR_WB),或者直接输入采样率
  285. @int record_time 录制时长 单位秒,可选,默认0即表示一直录制
  286. @int amr_quailty 质量,audio.AMR下有效
  287. @string path 录音文件路径,可选,不指定则不保存,可在audio.on回调函数中处理原始PCM数据
  288. @int record_callback_time 不指定录音文件路径时,单次录音回调时长,单位是100ms。默认1,既100ms
  289. @zbuff 录音原始PCM数据缓存0,不填写录音文件路径才会用到
  290. @zbuff 录音原始PCM数据缓存1,不填写录音文件路径才会用到
  291. @channelCount 声道数量,只针对非I2S设备有效,1单声道录音 2立体声录音 默认单声道.I2S设备在I2S相关API里配置
  292. @return boolean 成功返回true,否则返回false
  293. @usage
  294. err,info = audio.record(id, type, record_time, quailty, path)
  295. */
  296. static int l_audio_record(lua_State *L){
  297. size_t len;
  298. uint32_t record_buffer_len;
  299. g_s_record.multimedia_id = luaL_checkinteger(L, 1);
  300. g_s_record.type = luaL_optinteger(L, 2,LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB);
  301. g_s_record.record_time = luaL_optinteger(L, 3, 0);
  302. g_s_record.quailty = luaL_optinteger(L, 4, 0);
  303. if (g_s_record.fd || g_s_record.is_run) {
  304. LLOGE("record is running");
  305. goto ERROR_OUT;
  306. }
  307. if (lua_isstring(L, 5)) {
  308. const char *path = luaL_checklstring(L, 5, &len);
  309. luat_fs_remove(path);
  310. g_s_record.fd = luat_fs_fopen(path, "wb+");
  311. if(!g_s_record.fd){
  312. LLOGE("open file %s failed", path);
  313. goto ERROR_OUT;
  314. }
  315. } else {
  316. if (!lua_isuserdata(L, 7) || !lua_isuserdata(L, 8))
  317. {
  318. goto ERROR_OUT;
  319. }
  320. }
  321. record_buffer_len = luaL_optinteger(L, 6, 1);
  322. g_s_record.channelCnt = luaL_optinteger(L, 9, LUAT_RECORD_MONO);
  323. if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){
  324. #ifdef LUAT_SUPPORT_AMR
  325. if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB){
  326. record_buffer_len *= 320 * RECORD_ONCE_LEN;
  327. }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){
  328. #ifdef LUAT_USE_INTER_AMR
  329. record_buffer_len *= 640 * RECORD_ONCE_LEN;
  330. #else
  331. LLOGE("not support 16k");
  332. return 0;
  333. #endif
  334. }
  335. #else
  336. LLOGE("not support AMR");
  337. return 0;
  338. #endif
  339. }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_PCM){
  340. record_buffer_len *= 320 * RECORD_ONCE_LEN;
  341. }else if (g_s_record.type >= 8000 ){
  342. record_buffer_len *= (g_s_record.type/5);
  343. } else
  344. {
  345. LLOGE("not support type %d", g_s_record.type);
  346. goto ERROR_OUT;
  347. }
  348. if (!g_s_record.fd)
  349. {
  350. g_s_record.record_callback_level = record_buffer_len;
  351. g_s_record.record_buffer[0] = ((luat_zbuff_t *)luaL_checkudata(L, 7, LUAT_ZBUFF_TYPE));
  352. g_s_record.record_buffer[1] = ((luat_zbuff_t *)luaL_checkudata(L, 8, LUAT_ZBUFF_TYPE));
  353. g_s_record.record_buffer[0]->used = 0;
  354. g_s_record.record_buffer[1]->used = 0;
  355. if (g_s_record.record_buffer[0]->len < record_buffer_len)
  356. {
  357. __zbuff_resize(g_s_record.record_buffer[0], record_buffer_len);
  358. }
  359. if (g_s_record.record_buffer[1]->len < record_buffer_len)
  360. {
  361. __zbuff_resize(g_s_record.record_buffer[1], record_buffer_len);
  362. }
  363. }
  364. g_s_record.is_run = 1;
  365. luat_audio_run_callback_in_task(record_start, NULL, 0);
  366. lua_pushboolean(L, 1);
  367. return 1;
  368. ERROR_OUT:
  369. lua_pushboolean(L, 0);
  370. return 1;
  371. }
  372. /**
  373. 录音停止
  374. @api audio.recordStop(id)
  375. @int id 多媒体播放通道号
  376. @return boolean 成功返回true,否则返回false
  377. @usage
  378. audio.recordStop(0)
  379. */
  380. static int l_audio_record_stop(lua_State *L) {
  381. if (g_s_record.is_run) {
  382. luat_audio_run_callback_in_task(record_stop, NULL, 0);
  383. lua_pushboolean(L, 1);
  384. return 1;
  385. } else {
  386. LLOGE("record is not running");
  387. return 0;
  388. }
  389. }
  390. #endif
  391. /**
  392. 往一个多媒体通道写入音频数据
  393. @api audio.write(id, data)
  394. @string or zbuff 音频数据
  395. @return boolean 成功返回true,否则返回false
  396. @usage
  397. audio.write(0, "xxxxxx")
  398. */
  399. static int l_audio_write_raw(lua_State *L) {
  400. int multimedia_id = luaL_checkinteger(L, 1);
  401. size_t len;
  402. const char *buf;
  403. if(lua_isuserdata(L, 2))
  404. {
  405. luat_zbuff_t *buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE));
  406. len = buff->used;
  407. buf = (const char *)(buff->addr);
  408. }
  409. else
  410. {
  411. buf = lua_tolstring(L, 2, &len);//取出字符串数据
  412. }
  413. lua_pushboolean(L, !luat_audio_write_raw(multimedia_id, (uint8_t*)buf, len));
  414. return 1;
  415. }
  416. /**
  417. 停止指定的多媒体通道
  418. @api audio.stop(id)
  419. @int audio id,例如0
  420. @return boolean 成功返回true,否则返回false
  421. @usage
  422. audio.stop(0)
  423. */
  424. static int l_audio_stop_raw(lua_State *L) {
  425. int id = luaL_checkinteger(L, 1);
  426. lua_pushboolean(L, !luat_audio_stop_raw(id));
  427. return 1;
  428. }
  429. /**
  430. 暂停/恢复指定的多媒体通道
  431. @api audio.pause(id, pause)
  432. @int audio id,例如0
  433. @boolean onoff true 暂停,false 恢复
  434. @return boolean 成功返回true,否则返回false
  435. @usage
  436. audio.pause(0, true) --暂停通道0
  437. audio.pause(0, false) --恢复通道0
  438. */
  439. static int l_audio_pause_raw(lua_State *L) {
  440. int id = luaL_checkinteger(L, 1);
  441. lua_pushboolean(L, !luat_audio_pause_raw(id, lua_toboolean(L, 2)));
  442. return 1;
  443. }
  444. /**
  445. 注册audio播放事件回调
  446. @api audio.on(audio_id, func)
  447. @int audio id, audio 0写0, audio 1写1
  448. @function 回调方法,回调时传入参数为1、int 通道ID 2、int 消息值,有audio.MORE_DATA,audio.DONE,audio.RECORD_DATA,audio.RECORD_DONE,3、RECORD_DATA后面跟数据存在哪个zbuff内,0或者1
  449. @return nil 无返回值
  450. @usage
  451. audio.on(0, function(audio_id, msg)
  452. log.info("msg", audio_id, msg)
  453. end)
  454. */
  455. static int l_audio_raw_on(lua_State *L) {
  456. int multimedia_id = luaL_checkinteger(L, 1);
  457. if (multimedia_cbs[multimedia_id].function_ref != 0) {
  458. luaL_unref(L, LUA_REGISTRYINDEX, multimedia_cbs[multimedia_id].function_ref);
  459. multimedia_cbs[multimedia_id].function_ref = 0;
  460. }
  461. if (lua_isfunction(L, 2)) {
  462. lua_pushvalue(L, 2);
  463. multimedia_cbs[multimedia_id].function_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  464. }
  465. return 0;
  466. }
  467. /*
  468. 播放或者停止播放一个文件,播放完成后,会回调一个audio.DONE消息,可以用pause来暂停或者恢复,其他API不可用。考虑到读SD卡速度比较慢而拖累luavm进程的速度,所以尽量使用本API
  469. @api audio.play(id, path, errStop)
  470. @int 音频通道
  471. @string/table 文件名,如果为空,则表示停止播放,如果是table,则表示连续播放多个文件,主要应用于云喇叭,目前只有Air780EXXX支持,并且会用到errStop参数
  472. @boolean 是否在文件解码失败后停止解码,只有在连续播放多个文件时才有用,默认true,遇到解码错误自动停止
  473. @return boolean 成功返回true,否则返回false
  474. @usage
  475. audio.play(0, "xxxxxx") --开始播放某个文件
  476. audio.play(0) --停止播放某个文件
  477. */
  478. static int l_audio_play(lua_State *L) {
  479. int multimedia_id = luaL_checkinteger(L, 1);
  480. size_t len = 0;
  481. int result = 0;
  482. const char *buf;
  483. uint8_t is_error_stop = 1;
  484. if (lua_istable(L, 2))
  485. {
  486. size_t len = lua_rawlen(L, 2); //返回数组的长度
  487. if (!len)
  488. {
  489. luat_audio_play_stop(multimedia_id);
  490. lua_pushboolean(L, 1);
  491. return 1;
  492. }
  493. uData_t *info = (uData_t *)luat_heap_malloc(len * sizeof(uData_t));
  494. for (size_t i = 0; i < len; i++)
  495. {
  496. lua_rawgeti(L, 2, 1 + i);
  497. info[i].value.asBuffer.buffer = (void*)lua_tolstring(L, -1, &info[i].value.asBuffer.length);
  498. info[i].Type = UDATA_TYPE_OPAQUE;
  499. lua_pop(L, 1); //将刚刚获取的元素值从栈中弹出
  500. }
  501. if (lua_isboolean(L, 3))
  502. {
  503. is_error_stop = lua_toboolean(L, 3);
  504. }
  505. result = luat_audio_play_multi_files(multimedia_id, info, len, is_error_stop);
  506. lua_pushboolean(L, !result);
  507. luat_heap_free(info);
  508. }
  509. else if (LUA_TSTRING == (lua_type(L, (2))))
  510. {
  511. buf = lua_tolstring(L, 2, &len);//取出字符串数据
  512. result = luat_audio_play_file(multimedia_id, buf);
  513. lua_pushboolean(L, !result);
  514. }
  515. else
  516. {
  517. luat_audio_play_stop(multimedia_id);
  518. lua_pushboolean(L, 1);
  519. }
  520. return 1;
  521. }
  522. #ifdef LUAT_USE_TTS
  523. /*
  524. TTS播放或者停止
  525. @api audio.tts(id, data)
  526. @int 音频通道
  527. @string/zbuff 需要播放的内容
  528. @return boolean 成功返回true,否则返回false
  529. @tag LUAT_USE_TTS
  530. @usage
  531. audio.tts(0, "测试一下") --开始播放
  532. audio.tts(0) --停止播放
  533. -- Air780E的TTS功能详细说明
  534. -- https://wiki.luatos.com/chips/air780e/tts.html
  535. */
  536. static int l_audio_play_tts(lua_State *L) {
  537. int multimedia_id = luaL_checkinteger(L, 1);
  538. size_t len = 0;
  539. int result = 0;
  540. const char *buf;
  541. if (LUA_TSTRING == (lua_type(L, (2))))
  542. {
  543. buf = lua_tolstring(L, 2, &len);//取出字符串数据
  544. result = luat_audio_play_tts_text(multimedia_id, (void*)buf, len);
  545. lua_pushboolean(L, !result);
  546. }
  547. else if(lua_isuserdata(L, 2))
  548. {
  549. luat_zbuff_t *buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE));
  550. result = luat_audio_play_tts_text(multimedia_id, buff->addr, buff->used);
  551. lua_pushboolean(L, !result);
  552. }
  553. else
  554. {
  555. luat_audio_play_stop(multimedia_id);
  556. lua_pushboolean(L, 1);
  557. }
  558. return 1;
  559. }
  560. #endif
  561. /**
  562. 停止播放文件,和audio.play(id)是一样的作用
  563. @api audio.playStop(id)
  564. @int audio id,例如0
  565. @return boolean 成功返回true,否则返回false
  566. @usage
  567. audio.playStop(0)
  568. */
  569. static int l_audio_play_stop(lua_State *L) {
  570. lua_pushboolean(L, !luat_audio_play_stop(luaL_checkinteger(L, 1)));
  571. return 1;
  572. }
  573. /**
  574. 检查当前文件是否已经播放结束
  575. @api audio.isEnd(id)
  576. @int 音频通道
  577. @return boolean 成功返回true,否则返回false
  578. @usage
  579. audio.isEnd(0)
  580. */
  581. static int l_audio_play_wait_end(lua_State *L) {
  582. int multimedia_id = luaL_checkinteger(L, 1);
  583. lua_pushboolean(L, luat_audio_is_finish(multimedia_id));
  584. return 1;
  585. }
  586. /*
  587. 获取最近一次播放结果,不是所有平台都支持的,目前只有Air780EXXX支持
  588. @api audio.getError(id)
  589. @int 音频通道
  590. @return boolean 是否全部播放成功,true成功,false有文件播放失败
  591. @return boolean 如果播放失败,是否是用户停止,true是,false不是
  592. @return int 第几个文件失败了,从1开始
  593. @usage
  594. local result, user_stop, file_no = audio.getError(0)
  595. */
  596. static int l_audio_play_get_last_error(lua_State *L) {
  597. int multimedia_id = luaL_checkinteger(L, 1);
  598. int result = luat_audio_play_get_last_error(multimedia_id);
  599. lua_pushboolean(L, 0 == result);
  600. lua_pushboolean(L, result < 0);
  601. lua_pushinteger(L, result > 0?result:0);
  602. return 3;
  603. }
  604. /*
  605. 配置一个音频通道的特性,比如实现自动控制PA开关。注意这个不是必须的,一般在调用play的时候才需要自动控制,其他情况比如你手动控制播放时,就可以自己控制PA开关
  606. @api audio.config(id, paPin, onLevel, dacDelay, paDelay, dacPin, dacLevel, dacTimeDelay)
  607. @int 音频通道
  608. @int PA控制IO
  609. @int PA打开时的电平
  610. @int 在DAC启动前插入的冗余时间,单位100ms,一般用于外部DAC
  611. @int 在DAC启动后,延迟多长时间打开PA,单位1ms
  612. @int 外部dac电源控制IO,如果不填,则表示使用平台默认IO,比如Air780E使用DACEN脚,air105则不启用
  613. @int 外部dac打开时,电源控制IO的电平,默认拉高
  614. @int 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms,默认0ms
  615. @usage
  616. audio.config(0, pin.PC0, 1) --PA控制脚是PC0,高电平打开,air105用这个配置就可以用了
  617. audio.config(0, 25, 1, 6, 200) --PA控制脚是GPIO25,高电平打开,Air780E云喇叭板用这个配置就可以用了
  618. */
  619. static int l_audio_config(lua_State *L) {
  620. int id = luaL_checkinteger(L, 1);
  621. int pa_pin = luaL_optinteger(L, 2, -1);
  622. int level = luaL_optinteger(L, 3, 1);
  623. int dac_pre_delay = luaL_optinteger(L, 4, 5);
  624. int dac_last_delay = luaL_optinteger(L, 5, 200);
  625. int dac_power_pin = luaL_optinteger(L, 6, -1);
  626. int dac_power_level = luaL_optinteger(L, 7, 1);
  627. int pa_dac_delay = luaL_optinteger(L, 8, 0);
  628. if (pa_dac_delay < 0)
  629. pa_dac_delay = 0;
  630. if (dac_pre_delay < 0)
  631. dac_pre_delay = 0;
  632. if (dac_last_delay < 0)
  633. dac_last_delay = 0;
  634. luat_audio_config_pa(id, pa_pin, level, (uint32_t)dac_pre_delay, (uint32_t)dac_last_delay);
  635. luat_audio_config_dac(id, dac_power_pin, dac_power_level, (uint32_t)pa_dac_delay);
  636. return 0;
  637. }
  638. /*
  639. 配置一个音频通道的音量调节,直接将原始数据放大或者缩小,不是所有平台都支持,建议尽量用硬件方法去缩放
  640. @api audio.vol(id, value)
  641. @int 音频通道
  642. @int 音量,百分比,1%~1000%,默认100%,就是不调节
  643. @return int 当前音量
  644. @usage
  645. local result = audio.vol(0, 90) --通道0的音量调节到90%,result存放了调节后的音量水平,有可能仍然是100
  646. */
  647. static int l_audio_vol(lua_State *L) {
  648. int id = luaL_checkinteger(L, 1);
  649. int vol = luaL_optinteger(L, 2, 100);
  650. lua_pushinteger(L, luat_audio_vol(id, vol));
  651. return 1;
  652. }
  653. /*
  654. 配置一个音频通道的mic音量调节
  655. @api audio.micVol(id, value)
  656. @int 音频通道
  657. @int mic音量,百分比,1%~100%,默认100%,就是不调节
  658. @return int 当前mic音量
  659. @usage
  660. local result = audio.vol(0, 90) --通道0的音量调节到90%,result存放了调节后的音量水平,有可能仍然是100
  661. */
  662. static int l_audio_mic_vol(lua_State *L) {
  663. int id = luaL_checkinteger(L, 1);
  664. int mic_vol = luaL_optinteger(L, 2, 100);
  665. lua_pushinteger(L, luat_audio_mic_vol(id, mic_vol));
  666. return 1;
  667. }
  668. /*
  669. 配置一个音频通道的硬件输出总线,只有对应soc软硬件平台支持才设置对应类型
  670. @api audio.setBus(id, bus_type)
  671. @int 音频通道,例如0
  672. @int 总线类型, 例如 audio.BUS_SOFT_DAC
  673. @int 硬件id, 例如 总线类型为audio.BUS_I2S时,硬件id即为i2s codec的i2c id
  674. @return nil 无返回值
  675. @usage
  676. audio.setBus(0, audio.BUS_SOFT_DAC) --通道0的硬件输出通道设置为软件DAC
  677. audio.setBus(0, audio.BUS_I2S) --通道0的硬件输出通道设置为I2S
  678. */
  679. static int l_audio_set_output_bus(lua_State *L) {
  680. size_t len;
  681. int id = luaL_checkinteger(L, 1);
  682. luat_audio_conf_t* audio_conf = luat_audio_get_config(id);
  683. int tp = luaL_checkinteger(L, 2);
  684. int ret = luat_audio_set_bus_type(id,tp);
  685. if (audio_conf!=NULL && lua_istable(L,3) && tp==LUAT_AUDIO_BUS_I2S){
  686. audio_conf->codec_conf.multimedia_id = id;
  687. audio_conf->bus_type = LUAT_AUDIO_BUS_I2S;
  688. audio_conf->codec_conf.codec_opts = &codec_opts_common;
  689. lua_pushstring(L, "chip");
  690. if (LUA_TSTRING == lua_gettable(L, 3)) {
  691. const char *chip = luaL_checklstring(L, -1,&len);
  692. if(strcmp(chip,"es8311") == 0){
  693. audio_conf->codec_conf.codec_opts = &codec_opts_es8311;
  694. }
  695. }
  696. lua_pop(L, 1);
  697. lua_pushstring(L, "i2cid");
  698. if (LUA_TNUMBER == lua_gettable(L, 3)) {
  699. audio_conf->codec_conf.i2c_id = luaL_checknumber(L, -1);
  700. }
  701. lua_pop(L, 1);
  702. lua_pushstring(L, "i2sid");
  703. if (LUA_TNUMBER == lua_gettable(L, 3)) {
  704. audio_conf->codec_conf.i2s_id = luaL_checknumber(L, -1);
  705. }
  706. lua_pop(L, 1);
  707. lua_pushstring(L, "voltage");
  708. if (LUA_TNUMBER == lua_gettable(L, 3)) {
  709. audio_conf->voltage = luaL_checknumber(L, -1);
  710. }
  711. lua_pop(L, 1);
  712. }
  713. ret |= luat_audio_init(id, 0, 0);
  714. lua_pushboolean(L, !ret);
  715. return 1;
  716. }
  717. LUAT_WEAK void luat_audio_set_debug(uint8_t on_off)
  718. {
  719. (void)on_off;
  720. }
  721. /*
  722. 配置调试信息输出
  723. @api audio.debug(on_off)
  724. @boolean true开 false关
  725. @return
  726. @usage
  727. audio.debug(true) --开启调试信息输出
  728. audio.debug(false) --关闭调试信息输出
  729. */
  730. static int l_audio_set_debug(lua_State *L) {
  731. luat_audio_set_debug(lua_toboolean(L, 1));
  732. return 0;
  733. }
  734. /*
  735. audio 休眠控制(一般会自动调用不需要手动执行)
  736. @api audio.pm(id,pm_mode)
  737. @int 音频通道
  738. @int 休眠模式
  739. @return boolean true成功
  740. @usage
  741. audio.pm(multimedia_id,audio.RESUME)
  742. */
  743. static int l_audio_pm_request(lua_State *L) {
  744. lua_pushboolean(L, !luat_audio_pm_request(luaL_checkinteger(L, 1),luaL_checkinteger(L, 2)));
  745. return 1;
  746. }
  747. #include "rotable2.h"
  748. static const rotable_Reg_t reg_audio[] =
  749. {
  750. { "start" , ROREG_FUNC(l_audio_start_raw)},
  751. { "write" , ROREG_FUNC(l_audio_write_raw)},
  752. { "pause", ROREG_FUNC(l_audio_pause_raw)},
  753. { "stop", ROREG_FUNC(l_audio_stop_raw)},
  754. { "on", ROREG_FUNC(l_audio_raw_on)},
  755. { "play", ROREG_FUNC(l_audio_play)},
  756. #ifdef LUAT_USE_TTS
  757. { "tts", ROREG_FUNC(l_audio_play_tts)},
  758. #endif
  759. { "playStop", ROREG_FUNC(l_audio_play_stop)},
  760. { "isEnd", ROREG_FUNC(l_audio_play_wait_end)},
  761. { "config", ROREG_FUNC(l_audio_config)},
  762. { "vol", ROREG_FUNC(l_audio_vol)},
  763. { "micVol", ROREG_FUNC(l_audio_mic_vol)},
  764. { "getError", ROREG_FUNC(l_audio_play_get_last_error)},
  765. { "setBus", ROREG_FUNC(l_audio_set_output_bus)},
  766. { "debug", ROREG_FUNC(l_audio_set_debug)},
  767. { "pm", ROREG_FUNC(l_audio_pm_request)},
  768. #ifdef LUAT_USE_RECORD
  769. { "record", ROREG_FUNC(l_audio_record)},
  770. { "recordStop", ROREG_FUNC(l_audio_record_stop)},
  771. #endif
  772. //@const RESUME number PM模式 工作模式
  773. { "RESUME", ROREG_INT(LUAT_AUDIO_PM_RESUME)},
  774. //@const STANDBY number PM模式 待机模式,PA断电,codec待机状态,系统不能进低功耗状态,如果PA不可控,codec进入静音模式
  775. { "STANDBY", ROREG_INT(LUAT_AUDIO_PM_STANDBY)},
  776. //@const SHUTDOWN number PM模式 关机模式,PA断电,可配置的codec关机状态,不可配置的codec断电,系统能进低功耗状态
  777. { "SHUTDOWN", ROREG_INT(LUAT_AUDIO_PM_SHUTDOWN)},
  778. //@const POWEROFF number PM模式 断电模式,PA断电,codec断电,系统能进低功耗状态
  779. { "POWEROFF", ROREG_INT(LUAT_AUDIO_PM_POWER_OFF)},
  780. //@const PCM number PCM格式,即原始ADC数据
  781. { "PCM", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_PCM)},
  782. //@const MP3 number MP3格式
  783. { "MP3", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_MP3)},
  784. //@const WAV number WAV格式
  785. { "WAV", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_WAV)},
  786. //@const AMR number AMR_NB格式
  787. { "AMR", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)},
  788. //@const AMR_NB number AMR_NB格式
  789. { "AMR_NB", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)},
  790. //@const AMR_WB number AMR_WB格式
  791. { "AMR_WB", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB)},
  792. //@const ULAW number G711 ulaw格式
  793. { "ULAW", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_ULAW)},
  794. //@const ALAW number G711 alaw格式
  795. { "ALAW", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_ALAW)},
  796. //@const MORE_DATA number audio.on回调函数传入参数的值,表示底层播放完一段数据,可以传入更多数据
  797. { "MORE_DATA", ROREG_INT(LUAT_MULTIMEDIA_CB_AUDIO_NEED_DATA)},
  798. //@const DONE number audio.on回调函数传入参数的值,表示底层播放完全部数据了
  799. { "DONE", ROREG_INT(LUAT_MULTIMEDIA_CB_AUDIO_DONE)},
  800. //@const RECORD_DATA number audio.on回调函数传入参数的值,表示录音数据
  801. { "RECORD_DATA", ROREG_INT(LUAT_MULTIMEDIA_CB_RECORD_DATA)},
  802. //@const RECORD_DONE number audio.on回调函数传入参数的值,表示录音完成
  803. { "RECORD_DONE", ROREG_INT(LUAT_MULTIMEDIA_CB_RECORD_DONE)},
  804. //@const BUS_DAC number 硬件输出总线,DAC类型
  805. { "BUS_DAC", ROREG_INT(LUAT_AUDIO_BUS_DAC)},
  806. //@const BUS_I2S number 硬件输出总线,I2S类型
  807. { "BUS_I2S", ROREG_INT(LUAT_AUDIO_BUS_I2S)},
  808. //@const BUS_SOFT_DAC number 硬件输出总线,软件模式DAC类型
  809. { "BUS_SOFT_DAC", ROREG_INT(LUAT_AUDIO_BUS_SOFT_DAC)},
  810. //@const VOLTAGE_1800 number 可配置的codec工作电压,1.8V
  811. { "VOLTAGE_1800", ROREG_INT(LUAT_AUDIO_VOLTAGE_1800)},
  812. //@const VOLTAGE_3300 number 可配置的codec工作电压,3.3V
  813. { "VOLTAGE_3300", ROREG_INT(LUAT_AUDIO_VOLTAGE_3300)},
  814. //@const RECORD_MONO number 录音使用单声道
  815. { "RECORD_MONO", ROREG_INT(LUAT_RECORD_MONO)},
  816. //@const RECORD_STEREO number 录音使用立体声
  817. { "RECORD_STEREO", ROREG_INT(LUAT_RECORD_STEREO)},
  818. { NULL, ROREG_INT(0)}
  819. };
  820. LUAMOD_API int luaopen_multimedia_audio( lua_State *L ) {
  821. luat_newlib2(L, reg_audio);
  822. return 1;
  823. }