luat_lib_multimedia_audio.c 32 KB

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