/* @module audio @summary 多媒体-音频 @version 1.0 @date 2022.03.11 @demo multimedia @tag LUAT_USE_MEDIA */ #include "luat_base.h" #include "luat_msgbus.h" #include "luat_zbuff.h" #define LUAT_LOG_TAG "audio" #include "luat_log.h" #include "luat_multimedia.h" #include "luat_audio.h" #include "luat_mem.h" #define MAX_DEVICE_COUNT 2 #ifndef __BSP_COMMON_H__ #include "c_common.h" #endif #ifdef LUAT_USE_RECORD static luat_record_ctrl_t g_s_record = {0}; #endif static luat_multimedia_cb_t multimedia_cbs[MAX_DEVICE_COUNT]; int l_multimedia_raw_handler(lua_State *L, void* ptr) { (void)ptr; rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1); if (multimedia_cbs[msg->arg2].function_ref) { lua_geti(L, LUA_REGISTRYINDEX, multimedia_cbs[msg->arg2].function_ref); if (lua_isfunction(L, -1)) { lua_pushinteger(L, msg->arg2); lua_pushinteger(L, msg->arg1); #ifdef LUAT_USE_RECORD if (msg->arg1 == LUAT_MULTIMEDIA_CB_RECORD_DATA){ lua_pushinteger(L, (int)msg->ptr); lua_call(L, 3, 0); }else{ lua_call(L, 2, 0); } #else lua_call(L, 2, 0); #endif } } lua_pushinteger(L, 0); return 1; } /* 启动一个多媒体通道准备播放音频 @api audio.start(id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed) @int 多媒体播放通道号 @int 音频格式 @int 声音通道数 @int 采样频率 @int 采样位数 @boolean 是否有符号,默认true @return boolean 成功true, 失败false @usage audio.start(0, audio.PCM, 1, 16000, 16) */ static int l_audio_start_raw(lua_State *L){ int multimedia_id = luaL_checkinteger(L, 1); int audio_format = luaL_checkinteger(L, 2); int num_channels= luaL_checkinteger(L, 3); int sample_rate = luaL_checkinteger(L, 4); int bits_per_sample = luaL_checkinteger(L, 5); int is_signed = 1; if (lua_isboolean(L, 6)) { is_signed = lua_toboolean(L, 6); } lua_pushboolean(L, !luat_audio_start_raw(multimedia_id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed)); return 1; } #ifdef LUAT_USE_RECORD #ifdef LUAT_SUPPORT_AMR #include "interf_enc.h" #include "interf_dec.h" #endif #include "luat_fs.h" #define RECORD_ONCE_LEN 5 #ifdef LUAT_SUPPORT_AMR static void record_encode_amr(uint8_t *data, uint32_t len){ uint8_t outbuf[64]; int16_t *pcm = (int16_t *)data; uint32_t total_len = len >> 1; uint32_t done_len = 0; uint8_t out_len; uint32_t pcm_len = (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)?160:320; while ((total_len - done_len) >= pcm_len){ #ifdef LUAT_USE_INTER_AMR luat_audio_inter_amr_coder_encode(g_s_record.encoder_handler, &pcm[done_len], outbuf,&out_len); #else out_len = Encoder_Interface_Encode(g_s_record.encoder_handler, g_s_record.quailty , &pcm[done_len], outbuf, 0); #endif if (out_len <= 0){ LLOGD("encode error in %d,result %d", done_len, out_len); }else{ luat_fs_fwrite(outbuf, out_len, 1, g_s_record.fd); } done_len += pcm_len; } } static void record_stop_encode_amr(void){ #ifdef LUAT_USE_INTER_AMR luat_audio_inter_amr_coder_deinit(g_s_record.encoder_handler); #else Encoder_Interface_exit(g_s_record.encoder_handler); #endif g_s_record.encoder_handler = NULL; } #endif static void record_stop(uint8_t *data, uint32_t len); static void record_buffer_full(void) { rtos_msg_t msg = {0}; msg.handler = l_multimedia_raw_handler; msg.arg1 = LUAT_MULTIMEDIA_CB_RECORD_DATA; msg.arg2 = g_s_record.multimedia_id; msg.ptr = (void *)((uint32_t)g_s_record.record_buffer_index); luat_msgbus_put(&msg, 1); g_s_record.record_buffer_index = !g_s_record.record_buffer_index; g_s_record.record_buffer[g_s_record.record_buffer_index]->used = 0; } static void record_run(uint8_t *data, uint32_t len) { if (g_s_record.fd){ #ifdef LUAT_SUPPORT_AMR if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){ record_encode_amr(data, len); } else #endif { luat_fs_fwrite(data, len, 1, g_s_record.fd); } }else{ 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); g_s_record.record_buffer[g_s_record.record_buffer_index]->used += len; if (g_s_record.record_buffer[g_s_record.record_buffer_index]->used >= g_s_record.record_callback_level) { record_buffer_full(); } } if (g_s_record.record_time) { g_s_record.record_time_tmp++; if (g_s_record.record_time_tmp >= (g_s_record.record_time * 10) ) { record_stop(NULL, 0); } } } static int record_cb(uint8_t id ,luat_i2s_event_t event, uint8_t *rx_data, uint32_t rx_len, void *param) { switch(event) { case LUAT_I2S_EVENT_RX_DONE: luat_audio_run_callback_in_task(record_run, rx_data, rx_len); break; default: break; } return 0; } static void record_no_i2s_cb(uint8_t id, uint8_t *rx_data, uint32_t rx_len, void *param) { luat_audio_run_callback_in_task(record_run, rx_data, rx_len); } static void record_start(uint8_t *data, uint32_t len){ //需要保存文件,看情况打开编码功能 if (g_s_record.fd){ if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){ #ifdef LUAT_SUPPORT_AMR #ifdef LUAT_USE_INTER_AMR 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); #else g_s_record.encoder_handler = Encoder_Interface_init(g_s_record.quailty); #endif if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB){ luat_fs_fwrite("#!AMR\n", 6, 1, g_s_record.fd); }else{ luat_fs_fwrite("#!AMR-WB\n", 9, 1, g_s_record.fd); } #endif } } luat_audio_conf_t *audio = luat_audio_get_config(g_s_record.multimedia_id); if (LUAT_AUDIO_BUS_I2S == audio->bus_type) { luat_i2s_conf_t *i2s = luat_i2s_get_config(g_s_record.multimedia_id); g_s_record.bak_cb_rx_len = i2s->cb_rx_len; g_s_record.bak_is_full_duplex = i2s->is_full_duplex; g_s_record.bak_sample_rate = i2s->sample_rate; g_s_record.bak_luat_i2s_event_callback = i2s->luat_i2s_event_callback; i2s->is_full_duplex = 1; i2s->luat_i2s_event_callback = record_cb; switch(g_s_record.type) { case LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB: case LUAT_MULTIMEDIA_DATA_TYPE_PCM: i2s->cb_rx_len = 320 * RECORD_ONCE_LEN; i2s->sample_rate = 8000; break; case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB: i2s->cb_rx_len = 640 * RECORD_ONCE_LEN; i2s->sample_rate = 16000; break; default: if (g_s_record.type <= 32000) { i2s->cb_rx_len = g_s_record.type / 5; } else if (g_s_record.type < 80000) { i2s->cb_rx_len = g_s_record.type / 10; } else { i2s->cb_rx_len = g_s_record.type / 20; } i2s->sample_rate = g_s_record.type; break; } luat_audio_record_and_play(g_s_record.multimedia_id, i2s->sample_rate, NULL, 3200, 2); } else { //非I2S的录音device uint32_t sample_rate = 8000; if (g_s_record.type >= 8000) { sample_rate = g_s_record.type; } else { switch(g_s_record.type) { case LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB: sample_rate = 16000; break; } } luat_audio_setup_record_callback(g_s_record.multimedia_id, record_no_i2s_cb, &g_s_record); luat_audio_record_and_play(g_s_record.multimedia_id, sample_rate, NULL, 3200, 2); } } static void record_stop(uint8_t *data, uint32_t len){ rtos_msg_t msg = {0}; //关闭audio硬件功能 luat_audio_record_stop(g_s_record.multimedia_id); luat_audio_pm_request(g_s_record.multimedia_id, LUAT_AUDIO_PM_STANDBY); luat_audio_conf_t *audio = luat_audio_get_config(g_s_record.multimedia_id); if (LUAT_AUDIO_BUS_I2S == audio->bus_type) { //还原参数 luat_i2s_conf_t *i2s = luat_i2s_get_config(g_s_record.multimedia_id); i2s->cb_rx_len = g_s_record.bak_cb_rx_len; i2s->is_full_duplex = g_s_record.bak_is_full_duplex; i2s->sample_rate = g_s_record.bak_sample_rate; i2s->luat_i2s_event_callback = g_s_record.bak_luat_i2s_event_callback; } //录音存文件时,看情况关闭编码功能 if (g_s_record.fd) { if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){ #ifdef LUAT_SUPPORT_AMR record_stop_encode_amr(); #endif }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_PCM){ // 不需要特殊处理 } luat_fs_fclose(g_s_record.fd); g_s_record.fd = NULL; } //通知luat task清除zbuff数据,并回调用户 msg.handler = l_multimedia_raw_handler; msg.arg1 = LUAT_MULTIMEDIA_CB_RECORD_DONE; msg.arg2 = g_s_record.multimedia_id; g_s_record.record_time_tmp = 0; g_s_record.is_run = 0; g_s_record.record_buffer_index = 0; luat_msgbus_put(&msg, 1); } /** 录音 @api audio.record(id, record_type, record_time, amr_quailty, path, record_callback_time, buff0, buff1,channelCount) @int id 多媒体播放通道号 @int record_type 录音音频格式,支持 audio.AMR audio.PCM (部分平台支持audio.AMR_WB),或者直接输入采样率 @int record_time 录制时长 单位秒,可选,默认0即表示一直录制 @int amr_quailty 质量,audio.AMR下有效 @string path 录音文件路径,可选,不指定则不保存,可在audio.on回调函数中处理原始PCM数据 @int record_callback_time 不指定录音文件路径时,单次录音回调时长,单位是100ms。默认1,既100ms @zbuff 录音原始PCM数据缓存0,不填写录音文件路径才会用到 @zbuff 录音原始PCM数据缓存1,不填写录音文件路径才会用到 @channelCount 声道数量,只针对非I2S设备有效,1单声道录音 2立体声录音 默认单声道.I2S设备在I2S相关API里配置 @return boolean 成功返回true,否则返回false @usage err,info = audio.record(id, type, record_time, quailty, path) */ static int l_audio_record(lua_State *L){ size_t len; uint32_t record_buffer_len; g_s_record.multimedia_id = luaL_checkinteger(L, 1); g_s_record.type = luaL_optinteger(L, 2,LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB); g_s_record.record_time = luaL_optinteger(L, 3, 0); g_s_record.quailty = luaL_optinteger(L, 4, 0); if (g_s_record.fd || g_s_record.is_run) { LLOGE("record is running"); goto ERROR_OUT; } if (lua_isstring(L, 5)) { const char *path = luaL_checklstring(L, 5, &len); luat_fs_remove(path); g_s_record.fd = luat_fs_fopen(path, "wb+"); if(!g_s_record.fd){ LLOGE("open file %s failed", path); goto ERROR_OUT; } } else { if (!lua_isuserdata(L, 7) || !lua_isuserdata(L, 8)) { goto ERROR_OUT; } } record_buffer_len = luaL_optinteger(L, 6, 1); g_s_record.channelCnt = luaL_optinteger(L, 9, LUAT_RECORD_MONO); if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB||g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){ #ifdef LUAT_SUPPORT_AMR if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB){ record_buffer_len *= 320 * RECORD_ONCE_LEN; }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){ #ifdef LUAT_USE_INTER_AMR record_buffer_len *= 640 * RECORD_ONCE_LEN; #else LLOGE("not support 16k"); return 0; #endif } #else LLOGE("not support AMR"); return 0; #endif }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_PCM){ record_buffer_len *= 320 * RECORD_ONCE_LEN; }else if (g_s_record.type >= 8000 ){ record_buffer_len *= (g_s_record.type/5); } else { LLOGE("not support type %d", g_s_record.type); goto ERROR_OUT; } if (!g_s_record.fd) { g_s_record.record_callback_level = record_buffer_len; g_s_record.record_buffer[0] = ((luat_zbuff_t *)luaL_checkudata(L, 7, LUAT_ZBUFF_TYPE)); g_s_record.record_buffer[1] = ((luat_zbuff_t *)luaL_checkudata(L, 8, LUAT_ZBUFF_TYPE)); g_s_record.record_buffer[0]->used = 0; g_s_record.record_buffer[1]->used = 0; if (g_s_record.record_buffer[0]->len < record_buffer_len) { __zbuff_resize(g_s_record.record_buffer[0], record_buffer_len); } if (g_s_record.record_buffer[1]->len < record_buffer_len) { __zbuff_resize(g_s_record.record_buffer[1], record_buffer_len); } } g_s_record.is_run = 1; luat_audio_run_callback_in_task(record_start, NULL, 0); lua_pushboolean(L, 1); return 1; ERROR_OUT: lua_pushboolean(L, 0); return 1; } /** 录音停止 @api audio.recordStop(id) @int id 多媒体播放通道号 @return boolean 成功返回true,否则返回false @usage audio.recordStop(0) */ static int l_audio_record_stop(lua_State *L) { if (g_s_record.is_run) { luat_audio_run_callback_in_task(record_stop, NULL, 0); lua_pushboolean(L, 1); return 1; } else { LLOGE("record is not running"); return 0; } } #endif /** 往一个多媒体通道写入音频数据 @api audio.write(id, data) @string/zbuff 音频数据 @return boolean 成功返回true,否则返回false @usage audio.write(0, "xxxxxx") */ static int l_audio_write_raw(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } size_t len; const char *buf; if(lua_isuserdata(L, 2)) { luat_zbuff_t *buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE)); len = buff->used; buf = (const char *)(buff->addr); } else { buf = lua_tolstring(L, 2, &len);//取出字符串数据 } lua_pushboolean(L, !luat_audio_write_raw(multimedia_id, (uint8_t*)buf, len)); return 1; } /** 停止指定的多媒体通道 @api audio.stop(id) @int audio id,例如0 @return boolean 成功返回true,否则返回false @usage audio.stop(0) */ static int l_audio_stop_raw(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } lua_pushboolean(L, !luat_audio_stop_raw(multimedia_id)); return 1; } /** 暂停/恢复指定的多媒体通道 @api audio.pause(id, pause) @int audio id,例如0 @boolean onoff true 暂停,false 恢复 @return boolean 成功返回true,否则返回false @usage audio.pause(0, true) --暂停通道0 audio.pause(0, false) --恢复通道0 */ static int l_audio_pause_raw(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } lua_pushboolean(L, !luat_audio_pause_raw(multimedia_id, lua_toboolean(L, 2))); return 1; } /** 注册audio播放事件回调 @api audio.on(audio_id, func) @int audio id, audio 0写0, audio 1写1 @function 回调方法,回调时传入参数为1、int 通道ID 2、int 消息值,有audio.MORE_DATA,audio.DONE,audio.RECORD_DATA,audio.RECORD_DONE,3、RECORD_DATA后面跟数据存在哪个zbuff内,0或者1 @return nil 无返回值 @usage audio.on(0, function(audio_id, msg) log.info("msg", audio_id, msg) end) */ static int l_audio_raw_on(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } if (multimedia_cbs[multimedia_id].function_ref != 0) { luaL_unref(L, LUA_REGISTRYINDEX, multimedia_cbs[multimedia_id].function_ref); multimedia_cbs[multimedia_id].function_ref = 0; } if (lua_isfunction(L, 2)) { lua_pushvalue(L, 2); multimedia_cbs[multimedia_id].function_ref = luaL_ref(L, LUA_REGISTRYINDEX); } return 0; } /* 播放或者停止播放一个文件,播放完成后,会回调一个audio.DONE消息,可以用pause来暂停或者恢复,其他API不可用。考虑到读SD卡速度比较慢而拖累luavm进程的速度,所以尽量使用本API @api audio.play(id, path, errStop) @int 音频通道 @string/table 文件名,如果为空,则表示停止播放,如果是table,则表示连续播放多个文件,主要应用于云喇叭,目前只有Air780EXXX支持,并且会用到errStop参数 @boolean 是否在文件解码失败后停止解码,只有在连续播放多个文件时才有用,默认true,遇到解码错误自动停止 @return boolean 成功返回true,否则返回false @usage audio.play(0, "xxxxxx") --开始播放某个文件 audio.play(0) --停止播放某个文件 */ static int l_audio_play(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } size_t len = 0; int result = 0; const char *buf; uint8_t is_error_stop = 1; if (lua_istable(L, 2)) { size_t len = lua_rawlen(L, 2); //返回数组的长度 if (!len) { luat_audio_play_stop(multimedia_id); lua_pushboolean(L, 1); return 1; } uData_t *info = (uData_t *)luat_heap_malloc(len * sizeof(uData_t)); for (size_t i = 0; i < len; i++) { lua_rawgeti(L, 2, 1 + i); info[i].value.asBuffer.buffer = (void*)lua_tolstring(L, -1, &info[i].value.asBuffer.length); info[i].Type = UDATA_TYPE_OPAQUE; lua_pop(L, 1); //将刚刚获取的元素值从栈中弹出 } if (lua_isboolean(L, 3)) { is_error_stop = lua_toboolean(L, 3); } result = luat_audio_play_multi_files(multimedia_id, info, len, is_error_stop); lua_pushboolean(L, !result); luat_heap_free(info); } else if (LUA_TSTRING == (lua_type(L, (2)))) { buf = lua_tolstring(L, 2, &len);//取出字符串数据 result = luat_audio_play_file(multimedia_id, buf); lua_pushboolean(L, !result); } else { luat_audio_play_stop(multimedia_id); lua_pushboolean(L, 1); } return 1; } #ifdef LUAT_USE_TTS /* TTS播放或者停止 @api audio.tts(id, data) @int 音频通道 @string/zbuff 需要播放的内容 @return boolean 成功返回true,否则返回false @tag LUAT_USE_TTS @usage audio.tts(0, "测试一下") --开始播放 audio.tts(0) --停止播放 */ static int l_audio_play_tts(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } size_t len = 0; int result = 0; const char *buf; if (LUA_TSTRING == (lua_type(L, (2)))) { buf = lua_tolstring(L, 2, &len);//取出字符串数据 result = luat_audio_play_tts_text(multimedia_id, (void*)buf, len); lua_pushboolean(L, !result); } else if(lua_isuserdata(L, 2)) { luat_zbuff_t *buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE)); result = luat_audio_play_tts_text(multimedia_id, buff->addr, buff->used); lua_pushboolean(L, !result); } else { luat_audio_play_stop(multimedia_id); lua_pushboolean(L, 1); } return 1; } #endif /** 停止播放文件,和audio.play(id)是一样的作用 @api audio.playStop(id) @int audio id,例如0 @return boolean 成功返回true,否则返回false @usage audio.playStop(0) */ static int l_audio_play_stop(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } lua_pushboolean(L, !luat_audio_play_stop(multimedia_id)); return 1; } /** 检查当前文件是否已经播放结束 @api audio.isEnd(id) @int 音频通道 @return boolean 成功返回true,否则返回false @usage audio.isEnd(0) */ static int l_audio_play_wait_end(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } lua_pushboolean(L, luat_audio_is_finish(multimedia_id)); return 1; } /* 获取最近一次播放结果,不是所有平台都支持的,目前只有Air780EXXX支持 @api audio.getError(id) @int 音频通道 @return boolean 是否全部播放成功,true成功,false有文件播放失败 @return boolean 如果播放失败,是否是用户停止,true是,false不是 @return int 第几个文件失败了,从1开始 @usage local result, user_stop, file_no = audio.getError(0) */ static int l_audio_play_get_last_error(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } int result = luat_audio_play_get_last_error(multimedia_id); lua_pushboolean(L, 0 == result); lua_pushboolean(L, result < 0); lua_pushinteger(L, result > 0?result:0); return 3; } /* 配置一个音频通道的特性,比如实现自动控制PA开关。注意这个不是必须的,一般在调用play的时候才需要自动控制,其他情况比如你手动控制播放时,就可以自己控制PA开关 @api audio.config(id, paPin, onLevel, dacDelay, paDelay, dacPin, dacLevel, dacTimeDelay) @int 音频通道 @int PA控制IO @int PA打开时的电平 @int 在DAC启动前插入的冗余时间,单位100ms,一般用于外部DAC @int 在DAC启动后,延迟多长时间打开PA,单位1ms @int 外部dac电源控制IO,如果不填,则表示使用平台默认IO,比如Air780E使用DACEN脚,air105则不启用 @int 外部dac打开时,电源控制IO的电平,默认拉高 @int 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms,默认0ms @usage --下面的配置是Air780E云喇叭板的配置 audio.config(0, 25, 1, 6, 200) --PA控制脚是GPIO25,高电平打开 */ static int l_audio_config(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); if (multimedia_id >= MAX_DEVICE_COUNT) { LLOGE("multimedia_id %d is out of range", multimedia_id); return 0; } int pa_pin = luaL_optinteger(L, 2, -1); int level = luaL_optinteger(L, 3, 1); int dac_pre_delay = luaL_optinteger(L, 4, 5); int dac_last_delay = luaL_optinteger(L, 5, 200); int dac_power_pin = luaL_optinteger(L, 6, -1); int dac_power_level = luaL_optinteger(L, 7, 1); int pa_dac_delay = luaL_optinteger(L, 8, 0); if (pa_dac_delay < 0) pa_dac_delay = 0; if (dac_pre_delay < 0) dac_pre_delay = 0; if (dac_last_delay < 0) dac_last_delay = 0; luat_audio_config_pa(multimedia_id, pa_pin, level, (uint32_t)dac_pre_delay, (uint32_t)dac_last_delay); luat_audio_config_dac(multimedia_id, dac_power_pin, dac_power_level, (uint32_t)pa_dac_delay); return 0; } /* 配置一个音频通道的音量调节,直接将原始数据放大或者缩小,不是所有平台都支持,建议尽量用硬件方法去缩放 @api audio.vol(id, value) @int 音频通道 @int 音量,百分比,1%~1000%,默认100%,就是不调节 @return int 当前音量 @usage local result = audio.vol(0, 90) --通道0的音量调节到90%,result存放了调节后的音量水平,有可能仍然是100 */ static int l_audio_vol(lua_State *L) { int id = luaL_checkinteger(L, 1); int vol = luaL_optinteger(L, 2, 100); lua_pushinteger(L, luat_audio_vol(id, vol)); return 1; } /* 配置一个音频通道的mic音量调节 @api audio.micVol(id, value) @int 音频通道 @int mic音量,百分比,1%~100%,默认100%,就是不调节 @return int 当前mic音量 @usage local result = audio.vol(0, 90) --通道0的音量调节到90%,result存放了调节后的音量水平,有可能仍然是100 */ static int l_audio_mic_vol(lua_State *L) { int id = luaL_checkinteger(L, 1); int mic_vol = luaL_optinteger(L, 2, 100); lua_pushinteger(L, luat_audio_mic_vol(id, mic_vol)); return 1; } /* 配置一个音频通道的硬件输出总线,只有对应soc软硬件平台支持才设置对应类型 @api audio.setBus(id, bus_type) @int 音频通道,例如0 @int 总线类型, 例如 audio.BUS_SOFT_DAC, audio.BUS_I2S @table codec配置参数, 当总线类型为audio.BUS_I2S时生效,table中包括以下字段:
chip codec型号,当前支持"es8311"
i2cid codec的硬件i2c id
i2sid codec的硬件i2s id
voltage i2cid codec的电压,可选 codec.VDDA_3V3 codec.VDDA_1V8 @return nil 无返回值 @usage audio.setBus(0, audio.BUS_SOFT_DAC) --通道0的硬件输出通道设置为软件DAC audio.setBus(0, audio.BUS_I2S,{chip="es8311",i2cid=0,i2sid=0,voltage=codec.VDDA_3V3}) --通道0的硬件输出通道设置为I2S */ static int l_audio_set_output_bus(lua_State *L) { size_t len; int id = luaL_checkinteger(L, 1); luat_audio_conf_t* audio_conf = luat_audio_get_config(id); int tp = luaL_checkinteger(L, 2); int ret = luat_audio_set_bus_type(id,tp); if (audio_conf!=NULL && lua_istable(L,3) && tp==LUAT_AUDIO_BUS_I2S){ audio_conf->codec_conf.multimedia_id = id; audio_conf->bus_type = LUAT_AUDIO_BUS_I2S; audio_conf->codec_conf.codec_opts = &codec_opts_common; lua_pushstring(L, "chip"); if (LUA_TSTRING == lua_gettable(L, 3)) { const char *chip = luaL_checklstring(L, -1,&len); if(strcmp(chip,"es8311") == 0){ audio_conf->codec_conf.codec_opts = &codec_opts_es8311; } } lua_pop(L, 1); lua_pushstring(L, "i2cid"); if (LUA_TNUMBER == lua_gettable(L, 3)) { audio_conf->codec_conf.i2c_id = luaL_checknumber(L, -1); } lua_pop(L, 1); lua_pushstring(L, "i2sid"); if (LUA_TNUMBER == lua_gettable(L, 3)) { audio_conf->codec_conf.i2s_id = luaL_checknumber(L, -1); } lua_pop(L, 1); lua_pushstring(L, "voltage"); if (LUA_TNUMBER == lua_gettable(L, 3)) { audio_conf->voltage = luaL_checknumber(L, -1); } lua_pop(L, 1); } ret |= luat_audio_init(id, 0, 0); lua_pushboolean(L, !ret); return 1; } LUAT_WEAK void luat_audio_set_debug(uint8_t on_off) { (void)on_off; } /* 配置调试信息输出 @api audio.debug(on_off) @boolean true开 false关 @return @usage audio.debug(true) --开启调试信息输出 audio.debug(false) --关闭调试信息输出 */ static int l_audio_set_debug(lua_State *L) { luat_audio_set_debug(lua_toboolean(L, 1)); return 0; } /* audio 休眠控制(一般会自动调用不需要手动执行) @api audio.pm(id,pm_mode) @int 音频通道 @int 休眠模式 @return boolean true成功 @usage audio.pm(multimedia_id,audio.RESUME) */ static int l_audio_pm_request(lua_State *L) { uint8_t multimedia_id = (uint8_t)luaL_checkinteger(L, 1); luat_audio_pm_mode_t mode = (luat_audio_pm_mode_t)luaL_checkinteger(L, 2); int ret = luat_audio_pm_request(multimedia_id, mode); lua_pushboolean(L, !ret); return 1; } #include "rotable2.h" static const rotable_Reg_t reg_audio[] = { { "start" , ROREG_FUNC(l_audio_start_raw)}, { "write" , ROREG_FUNC(l_audio_write_raw)}, { "pause", ROREG_FUNC(l_audio_pause_raw)}, { "stop", ROREG_FUNC(l_audio_stop_raw)}, { "on", ROREG_FUNC(l_audio_raw_on)}, { "play", ROREG_FUNC(l_audio_play)}, #ifdef LUAT_USE_TTS { "tts", ROREG_FUNC(l_audio_play_tts)}, #endif { "playStop", ROREG_FUNC(l_audio_play_stop)}, { "isEnd", ROREG_FUNC(l_audio_play_wait_end)}, { "config", ROREG_FUNC(l_audio_config)}, { "vol", ROREG_FUNC(l_audio_vol)}, { "micVol", ROREG_FUNC(l_audio_mic_vol)}, { "getError", ROREG_FUNC(l_audio_play_get_last_error)}, { "setBus", ROREG_FUNC(l_audio_set_output_bus)}, { "debug", ROREG_FUNC(l_audio_set_debug)}, { "pm", ROREG_FUNC(l_audio_pm_request)}, #ifdef LUAT_USE_RECORD { "record", ROREG_FUNC(l_audio_record)}, { "recordStop", ROREG_FUNC(l_audio_record_stop)}, #endif //@const RESUME number PM模式 工作模式 { "RESUME", ROREG_INT(LUAT_AUDIO_PM_RESUME)}, //@const STANDBY number PM模式 待机模式,PA断电,codec待机状态,系统不能进低功耗状态,如果PA不可控,codec进入静音模式 { "STANDBY", ROREG_INT(LUAT_AUDIO_PM_STANDBY)}, //@const SHUTDOWN number PM模式 关机模式,PA断电,可配置的codec关机状态,不可配置的codec断电,系统能进低功耗状态 { "SHUTDOWN", ROREG_INT(LUAT_AUDIO_PM_SHUTDOWN)}, //@const POWEROFF number PM模式 断电模式,PA断电,codec断电,系统能进低功耗状态 { "POWEROFF", ROREG_INT(LUAT_AUDIO_PM_POWER_OFF)}, //@const PCM number PCM格式,即原始ADC数据 { "PCM", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_PCM)}, //@const MP3 number MP3格式 { "MP3", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_MP3)}, //@const WAV number WAV格式 { "WAV", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_WAV)}, //@const AMR number AMR_NB格式 { "AMR", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)}, //@const AMR_NB number AMR_NB格式 { "AMR_NB", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB)}, //@const AMR_WB number AMR_WB格式 { "AMR_WB", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB)}, //@const ULAW number G711 ulaw格式 { "ULAW", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_ULAW)}, //@const ALAW number G711 alaw格式 { "ALAW", ROREG_INT(LUAT_MULTIMEDIA_DATA_TYPE_ALAW)}, //@const MORE_DATA number audio.on回调函数传入参数的值,表示底层播放完一段数据,可以传入更多数据 { "MORE_DATA", ROREG_INT(LUAT_MULTIMEDIA_CB_AUDIO_NEED_DATA)}, //@const DONE number audio.on回调函数传入参数的值,表示底层播放完全部数据了 { "DONE", ROREG_INT(LUAT_MULTIMEDIA_CB_AUDIO_DONE)}, //@const RECORD_DATA number audio.on回调函数传入参数的值,表示录音数据 { "RECORD_DATA", ROREG_INT(LUAT_MULTIMEDIA_CB_RECORD_DATA)}, //@const RECORD_DONE number audio.on回调函数传入参数的值,表示录音完成 { "RECORD_DONE", ROREG_INT(LUAT_MULTIMEDIA_CB_RECORD_DONE)}, //@const BUS_DAC number 硬件输出总线,DAC类型 { "BUS_DAC", ROREG_INT(LUAT_AUDIO_BUS_DAC)}, //@const BUS_I2S number 硬件输出总线,I2S类型 { "BUS_I2S", ROREG_INT(LUAT_AUDIO_BUS_I2S)}, //@const BUS_SOFT_DAC number 硬件输出总线,软件模式DAC类型 { "BUS_SOFT_DAC", ROREG_INT(LUAT_AUDIO_BUS_SOFT_DAC)}, //@const VOLTAGE_1800 number 可配置的codec工作电压,1.8V { "VOLTAGE_1800", ROREG_INT(LUAT_AUDIO_VOLTAGE_1800)}, //@const VOLTAGE_3300 number 可配置的codec工作电压,3.3V { "VOLTAGE_3300", ROREG_INT(LUAT_AUDIO_VOLTAGE_3300)}, //@const RECORD_MONO number 录音使用单声道 { "RECORD_MONO", ROREG_INT(LUAT_RECORD_MONO)}, //@const RECORD_STEREO number 录音使用立体声 { "RECORD_STEREO", ROREG_INT(LUAT_RECORD_STEREO)}, { NULL, ROREG_INT(0)} }; LUAMOD_API int luaopen_multimedia_audio( lua_State *L ) { luat_newlib2(L, reg_audio); return 1; }