Просмотр исходного кода

Merge branch 'master' of gitee.com:openLuat/LuatOS

Wendal Chen 1 год назад
Родитель
Сommit
0e32ee6d67
2 измененных файлов с 276 добавлено и 10 удалено
  1. 266 7
      components/multimedia/luat_lib_multimedia_audio.c
  2. 10 3
      luat/include/luat_audio.h

+ 266 - 7
components/multimedia/luat_lib_multimedia_audio.c

@@ -20,8 +20,9 @@
 #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) {
@@ -32,7 +33,32 @@ int l_multimedia_raw_handler(lua_State *L, void* ptr) {
         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_pushlightuserdata(L, g_s_record.record_buffer[(int)msg->ptr]);
+                lua_call(L, 3, 0);
+            }else{
+                lua_call(L, 2, 0);
+                if (msg->arg1 == LUAT_MULTIMEDIA_CB_RECORD_DONE){
+                    luaL_unref(L,LUA_REGISTRYINDEX, g_s_record.zbuff_ref[0]);
+                    if (g_s_record.record_buffer[0]->addr){
+                        luat_heap_opt_free(g_s_record.record_buffer[0]->type,g_s_record.record_buffer[0]->addr);
+                        g_s_record.record_buffer[0]->addr = NULL;
+                        g_s_record.record_buffer[0]->len = 0;
+                        g_s_record.record_buffer[0]->used = 0;
+                    }
+                    luaL_unref(L,LUA_REGISTRYINDEX, g_s_record.zbuff_ref[1]);
+                    if (g_s_record.record_buffer[1]->addr){
+                        luat_heap_opt_free(g_s_record.record_buffer[1]->type,g_s_record.record_buffer[1]->addr);
+                        g_s_record.record_buffer[1]->addr = NULL;
+                        g_s_record.record_buffer[1]->len = 0;
+                        g_s_record.record_buffer[1]->used = 0;
+                    }
+                }
+            }
+#else
             lua_call(L, 2, 0);
+#endif
         }
     }
     lua_pushinteger(L, 0);
@@ -42,7 +68,7 @@ int l_multimedia_raw_handler(lua_State *L, void* ptr) {
 /*
 启动一个多媒体通道准备播放音频
 @api audio.start(id, audio_format, num_channels, sample_rate, bits_per_sample, is_signed)
-@int 多媒体播放通道号,0或者1
+@int 多媒体播放通道号
 @int 音频格式
 @int 声音通道数
 @int 采样频率
@@ -52,7 +78,6 @@ int l_multimedia_raw_handler(lua_State *L, void* ptr) {
 @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);
@@ -67,6 +92,224 @@ static int l_audio_start_raw(lua_State *L){
 	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	10	   //单声道 8K录音单次10个编码块,总共200ms回调 320B 20ms,amr编码要求,20ms一个块
+
+#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;
+
+	while ((total_len - done_len) >= 160){
+#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 += 160;
+	}
+}
+
+static void record_stop_encode_amr(void){
+	luat_audio_record_stop(g_s_record.multimedia_id);
+	luat_audio_pm_request(g_s_record.multimedia_id, LUAT_AUDIO_PM_STANDBY);
+#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;
+    if (g_s_record.fd){
+        luat_fs_fclose(g_s_record.fd);
+        g_s_record.fd = NULL;
+    }
+	luat_i2s_conf_t *i2s = luat_i2s_get_config(g_s_record.multimedia_id);
+    memcpy(i2s, &g_s_record.i2s_back, sizeof(luat_i2s_conf_t));
+}
+#endif
+
+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_rtos_event_send(g_s_record.task_handle, LUAT_I2S_EVENT_RX_DONE, (uint32_t)rx_data, rx_len, 0, 0);
+		break;
+	case LUAT_I2S_EVENT_TRANSFER_DONE:
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static void record_task(void *arg)
+{
+    luat_event_t event;
+    rtos_msg_t msg = {0};
+    msg.handler = l_multimedia_raw_handler;
+	luat_i2s_conf_t *i2s = luat_i2s_get_config(g_s_record.multimedia_id);
+    memcpy(&g_s_record.i2s_back, i2s, sizeof(luat_i2s_conf_t));
+    i2s->cb_rx_len = g_s_record.record_buffer[g_s_record.record_buffer_index]->len;
+	i2s->is_full_duplex = 1;
+	i2s->luat_i2s_event_callback = record_cb;
+    if (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB){
+        i2s->sample_rate = 8000;
+    }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_WB){
+        i2s->sample_rate = 16000;
+    }
+    
+    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
+            luat_fs_fwrite("#!AMR\n", 6, 1, g_s_record.fd);
+#endif
+        }
+    }
+
+    luat_audio_record_and_play(g_s_record.multimedia_id, i2s->sample_rate, NULL, 3200, 2);
+
+    while (1)
+    {
+        luat_rtos_event_recv(g_s_record.task_handle, 0, &event, NULL, LUAT_WAIT_FOREVER);
+        switch(event.id){
+        case LUAT_I2S_EVENT_RX_DONE:
+            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.fd){
+                    record_encode_amr((uint8_t *)event.param1, event.param2);
+                }else{
+                    memcpy(g_s_record.record_buffer[g_s_record.record_buffer_index]->addr, (uint8_t *)event.param1, event.param2);
+                    g_s_record.record_buffer[g_s_record.record_buffer_index]->used = event.param2;
+                    
+                    msg.arg1 = LUAT_MULTIMEDIA_CB_RECORD_DATA;
+                    msg.arg2 = g_s_record.multimedia_id;
+                    msg.ptr = 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_time_tmp++;
+                if (g_s_record.record_time_tmp >= (g_s_record.record_time * (g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_AMR_NB?5:10) ))	//8K 5秒 16K 10秒
+                {
+                    record_stop_encode_amr();
+                    msg.arg1 = LUAT_MULTIMEDIA_CB_RECORD_DONE;
+                    msg.arg2 = g_s_record.multimedia_id;
+                    luat_msgbus_put(&msg, 1);
+                    goto end;
+                }
+#endif
+            }
+            break;
+        }
+    }
+end:
+    g_s_record.record_time_tmp = 0;
+    g_s_record.is_run = 0;
+    g_s_record.record_buffer_index = 0;
+    luat_rtos_task_delete(g_s_record.task_handle);
+}
+
+/**
+录音
+@api audio.record(id, record_type, record_time, amr_quailty, path)
+@int id             多媒体播放通道号
+@int record_type    录音文件音频格式,支持 audio.AMR audio.PCM 
+@int record_time    录制时长 单位秒
+@int amr_quailty    质量,audio.AMR下有效
+@string path        录音文件路径,可选,不指定则不保存,可在audio.on回调函数中处理原始PCM数据
+@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_checkinteger(L, 3);
+    g_s_record.quailty = luaL_optinteger(L, 4, 0);
+    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);
+            return 0;
+        }
+    }
+    if (g_s_record.is_run){
+        LLOGE("record is running");
+        return 0;
+    }
+
+    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
+    }
+    
+    g_s_record.record_buffer[0] = lua_newuserdata(L, sizeof(luat_zbuff_t));
+    g_s_record.record_buffer[0]->type = LUAT_HEAP_SRAM;
+    g_s_record.record_buffer[0]->len = record_buffer_len;
+    g_s_record.record_buffer[0]->addr = luat_heap_opt_malloc(LUAT_HEAP_SRAM,g_s_record.record_buffer[0]->len);
+    lua_pushlightuserdata(L, g_s_record.record_buffer[0]);
+    g_s_record.zbuff_ref[0] = luaL_ref(L, LUA_REGISTRYINDEX);
+    lua_pop(L, 1);
+
+    g_s_record.record_buffer[1] = lua_newuserdata(L, sizeof(luat_zbuff_t));
+    g_s_record.record_buffer[1]->type = LUAT_HEAP_SRAM;
+    g_s_record.record_buffer[1]->len = record_buffer_len;
+    g_s_record.record_buffer[1]->addr = luat_heap_opt_malloc(LUAT_HEAP_SRAM,g_s_record.record_buffer[1]->len);
+    lua_pushlightuserdata(L, g_s_record.record_buffer[1]);
+    g_s_record.zbuff_ref[1] = luaL_ref(L, LUA_REGISTRYINDEX);
+    lua_pop(L, 1);
+
+#else
+    LLOGE("not support AMR");
+    return 0;
+#endif
+    }else if(g_s_record.type==LUAT_MULTIMEDIA_DATA_TYPE_PCM){
+        // 不需要特殊处理
+    }else{
+        LLOGE("not support %d", g_s_record.type);
+        return 0;
+    }
+
+    luat_rtos_task_create(&g_s_record.task_handle, 8*1024, 100, "record_task", record_task, NULL, 0);
+    g_s_record.is_run = 1;
+    lua_pushboolean(L, 1);
+    return 1;
+}
+
+#endif
+
 /**
 往一个多媒体通道写入音频数据
 @api audio.write(id, data)
@@ -125,7 +368,7 @@ static int l_audio_pause_raw(lua_State *L) {
 
 /**
 注册audio播放事件回调
-@api    audio.on(id, event, func)
+@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
 @return nil 无返回值
@@ -307,7 +550,7 @@ audio.config(0, 25, 1, 6, 200)	--PA控制脚是GPIO25,高电平打开,Air780
 */
 static int l_audio_config(lua_State *L) {
     int id = luaL_checkinteger(L, 1);
-    int pa_pin = luaL_optinteger(L, 2, 255);
+    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);
@@ -455,7 +698,9 @@ static const rotable_Reg_t reg_audio[] =
 	{ "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)},
+#endif
 	//@const RESUME number PM模式 工作模式
     { "RESUME",         ROREG_INT(LUAT_AUDIO_PM_RESUME)},
     //@const STANDBY number PM模式 待机模式,PA断电,codec待机状态,系统不能进低功耗状态,如果PA不可控,codec进入静音模式
@@ -466,10 +711,24 @@ static const rotable_Reg_t reg_audio[] =
     { "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 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类型

+ 10 - 3
luat/include/luat_audio.h

@@ -73,20 +73,27 @@ typedef enum{
 	LUAT_AUDIO_BUS_SOFT_DAC
 }luat_audio_bus_type_t;
 
+#ifdef LUAT_USE_RECORD
+#include "luat_i2s.h"
 typedef struct{
 	luat_rtos_task_handle task_handle;
 	FILE* fd;
+	luat_i2s_conf_t i2s_back;
     uint8_t multimedia_id;
 	uint8_t quailty;
 	uint8_t type;
+	uint8_t is_run;
 	uint16_t record_time;
 	uint16_t record_time_tmp;
 	void* encoder_handler;
-	luat_zbuff_t * record_buffer;
-	int zbuff_ref;
-	uint8_t is_run;
+	luat_zbuff_t * record_buffer[2];
+	int record_buffer_index;
+	int zbuff_ref[2];
+	
 }luat_record_ctrl_t;
 
+#endif
+
 /**
  * @brief 设置音频硬件输出类型,后续初始化都会根据类型做不同处理,所以要首先使用此函数设置类型!!!
  *