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

fix: 完善audio 的扩展库文件

梁健 6 месяцев назад
Родитель
Сommit
f135dc2e38

+ 360 - 245
module/Air8000/demo/audio/exaudio.lua

@@ -1,31 +1,28 @@
 --[[
 @module exaudio
 @summary exaudio扩展库
-@version 1.0
-@date    2025.08.08
+@version 1.1
+@date    2025.09.01
 @author  梁健
 @usage
 ]]
 local exaudio = {}
 
-local i2s_id = 0            -- i2s_id 0
-local i2s_mode = 0          -- i2s模式 0 主机 1 从机
-local i2s_sample_rate = 16000   -- 采样率
-local i2s_channel_format = i2s.MONO_R   -- 声道, 0 左声道, 1 右声道, 2 立体声
-local i2s_communication_format = i2s.MODE_LSB   -- 格式, 可选MODE_I2S, MODE_LSB, MODE_MSB
-local i2s_channel_bits = 16     -- 声道的BCLK数量
-local multimedia_id = 0         -- 音频通道 0
-local voice_vol = 55
-local mic_vol = 80
-local pcm_buff0 = nil
-local pcm_buff1 = nil
-local power_on_level = 1
-local MSG_PD = "playDone"   -- 播放完成所有数据
--- ES8311 的 I2C 地址和芯片 ID 寄存器
-local ES8311_ADDR = 0x18  -- 7位地址,根据实际情况调整
-local CHIP_ID_REG = 0x00  -- 芯片 ID 寄存器地址,需查阅数据手册确认
+-- 常量定义
+local I2S_ID = 0
+local I2S_MODE = 0          -- 0:主机 1:从机
+local I2S_SAMPLE_RATE = 16000
+local I2S_CHANNEL_FORMAT = i2s.MONO_R  -- 0:左声道, 1:右声道, 2:立体声
+local I2S_COMM_FORMAT = i2s.MODE_LSB   -- 可选MODE_I2S, MODE_LSB, MODE_MSB
+local I2S_CHANNEL_BITS = 16
+local MULTIMEDIA_ID = 0
+local MSG_PLAY_DONE = "playDone"
+local ES8311_ADDR = 0x18    -- 7位地址
+local CHIP_ID_REG = 0x00    -- 芯片ID寄存器地址
+
+-- 模块常量
 exaudio.PLAY_DONE = 1
-exaudio.RECODE_DONE = 1
+exaudio.RECORD_DONE = 1
 exaudio.AMR_NB = 0
 exaudio.AMR_WB = 1
 exaudio.PCM_8000 = 2
@@ -33,346 +30,464 @@ exaudio.PCM_16000 = 3
 exaudio.PCM_24000 = 4
 exaudio.PCM_32000 = 5
 
-local audio_setup_param ={
-    model= "es8311",          -- dac类型,可填入"es8311","es8211"
-    i2c_id = 0,          -- i2c_id,可填入0,1 并使用pins 工具配置对应的管脚
-    pa_ctrl = 0,         -- 音频放大器电源控制管脚
-    dac_ctrl = 0,        --  音频编解码芯片电源控制管脚
-    dac_delay = 3,        -- 在DAC启动前插入的冗余时间,单位100ms
-    pa_delay = 100,      -- 在DAC启动后,延迟多长时间打开PA,单位1ms
-    dac_time_delay = 600,    -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms
-    bits_per_sample = 16,  -- codec 采样位数
-    pa_on_level = 1,           -- PA打开电平 1 高电平 0 低电平        
+-- 默认配置参数
+local audio_setup_param = {
+    model = "es8311",         -- dac类型: "es8311","es8211"
+    i2c_id = 0,               -- i2c_id: 0,1
+    pa_ctrl = 0,              -- 音频放大器电源控制管脚
+    dac_ctrl = 0,             -- 音频编解码芯片电源控制管脚
+    dac_delay = 3,            -- DAC启动前冗余时间(100ms)
+    pa_delay = 100,           -- DAC启动后延迟打开PA的时间(ms)
+    dac_time_delay = 600,     -- 播放完毕后PA与DAC关闭间隔(ms)
+    bits_per_sample = 16,     -- 采样位数
+    pa_on_level = 1           -- PA打开电平 1:高 0:低        
 }
- 
-
-local audio_play_param ={
-    type= 0,                -- 播放类型,有0,播放文件,1.播放tts 2. 流式播放
-                            -- 如果是播放文件,支持mp3,amr,wav格式
-                            -- 如果是tts,内容格式见:https://wiki.luatos.com/chips/air780e/tts.html?highlight=tts
-                            -- 流式播放,仅支持PCM 格式音频,如果是流式播放,则Sampling_Rate, Sampling_Depth,Signed_or_Unsigned 必填写
-    content = nil,          -- 如果播放类型为0时,则填入string 是播放单个音频文件,如果是表则是播放多段音频文件。
-                            -- 如果播放tts 则填入要播放的内容。
-                            -- 如果为2,流式播放,则填入音频回调函数
-    cbFnc = nil,            -- 播放完毕回调函数
-                            --  0-播放成功结束
-                            --  1-播放出错
-                            --  2-播放优先级不够,没有播放
-                            --  3-传入的参数出错,没有播放
-                            --  4-被新的播放请求中止
-                            --  5-调用audio.stop接口主动停止
-    priority = 0,           -- 音频优先级,数值越大,优先级越高
-                            -- 优先级高的播放请求会终止优先级低的播放
-                            -- 相同优先级的播放请求,播放策略参考:audio.setStrategy接口
-    sampling_Rate = 16000,  -- 采样率,仅为流式播放起作用
-    sampling_Depth =  16,   -- 采样位位深,仅流式播放的时候才有作用
-    signed_or_Unsigned = true  -- PCM 的数据是否有符号,仅为流式播放起作用
+
+local audio_play_param = {
+    type = 0,                 -- 0:文件 1:TTS 2:流式
+    content = nil,            -- 内容/回调函数
+    cbFnc = nil,              -- 播放完毕回调
+    priority = 0,             -- 优先级(数值越大越高)
+    sampling_Rate = 16000,    -- 采样率(仅流式)
+    sampling_Depth = 16,      -- 采样位深(仅流式)
+    signed_or_Unsigned = true -- PCM是否有符号(仅流式)
 }
 
+local audio_record_param = {
+    format = 0,               -- 录制格式
+    time = 5,                 -- 录制时间(秒)
+    path = nil,               -- 文件路径或流式回调
+    cbFnc = nil               -- 录音完毕回调
+}
 
+-- 内部变量
+local pcm_buff0 = nil
+local pcm_buff1 = nil
+local voice_vol = 55
+local mic_vol = 80
 
-local audio_record_param ={
-    format= 0,              -- 录制格式,有exaudio.AMR,exaudio.AMR_WB,exaudio.PCM_8000,exaudio.PCM_16000,exaudio.PCM_24000,exaudio.PCM_32000
-    time = 5,               -- 录制时间,单位(秒)
-    path = nil,             -- 如果填入的是字符串,则表示是文件路径,录音会传输到这个路径里
-                            -- 如果填入的是函数,则表示是流式录音,录音的数据会传输到此函数内
-    cbFnc = nil,            -- 录音完毕回调函数 
-}
+-- 工具函数:参数检查
+local function check_param(param, expected_type, name)
+    if type(param) ~= expected_type then
+        log.error(string.format("参数错误: %s 应为 %s 类型", name, expected_type))
+        return false
+    end
+    return true
+end
 
-local function audio_callback(id, event,point)
-    log.info("audio_callback,event:",event,audio.MORE_DATA,audio.DONE,audio.RECORD_DATA,audio.RECORD_DONE)
-    if event  == audio.MORE_DATA then
-        audio_play_param.content()
-    elseif event  ==  audio.DONE  then
-        if audio_play_param.cbFnc ~= nil and  type(audio_play_param.cbFnc) == "function" then
+-- 音频回调处理
+local function audio_callback(id, event, point)
+    -- log.info("audio_callback", "event:", event, 
+    --         "MORE_DATA:", audio.MORE_DATA, 
+    --         "DONE:", audio.DONE,
+    --         "RECORD_DATA:", audio.RECORD_DATA,
+    --         "RECORD_DONE:", audio.RECORD_DONE)
+
+    if event == audio.MORE_DATA then
+        if type(audio_play_param.content) == "function" then
+            audio_play_param.content()
+        end
+        
+    elseif event == audio.DONE then
+        if type(audio_play_param.cbFnc) == "function" then
             audio_play_param.cbFnc(exaudio.PLAY_DONE)
         end
-        sys.publish(MSG_PD)
-    elseif event  ==  audio.RECORD_DATA then
-        if audio_record_param.path ~= nil and  type(audio_record_param.path) == "function" then
-            if point == 0 then
-                audio_record_param.path(pcm_buff0,pcm_buff0:used())
-            else
-                audio_record_param.path(pcm_buff1,pcm_buff1:used())
-            end
+        sys.publish(MSG_PLAY_DONE)
+        
+    elseif event == audio.RECORD_DATA then
+        if type(audio_record_param.path) == "function" then
+            local buff, len = point == 0 and pcm_buff0 or pcm_buff1,
+                             point == 0 and pcm_buff0:used() or pcm_buff1:used()
+            audio_record_param.path(buff, len)
         end
-    elseif event  ==  audio.RECORD_DONE then
-        if audio_record_param.cbFnc ~= nil and  type(audio_record_param.cbFnc) == "function" then
-            audio_record_param.cbFnc(exaudio.RECODE_DONE)
+        
+    elseif event == audio.RECORD_DONE then
+        if type(audio_record_param.cbFnc) == "function" then
+            audio_record_param.cbFnc(exaudio.RECORD_DONE)
         end
     end
 end
+
+-- 读取ES8311芯片ID
 local function read_es8311_id()
-    -- 发送要读取的寄存器地址
-    i2c.send(audio_setup_param.i2c_id, ES8311_ADDR, CHIP_ID_REG)
-    -- 读取 1 个字节的数据
+
+
+    -- 发送读取请求
+    local send_ok = i2c.send(audio_setup_param.i2c_id, ES8311_ADDR, CHIP_ID_REG)
+    if not send_ok then
+        log.error("发送芯片ID读取请求失败")
+        return false
+    end
+
+    -- 读取数据
     local data = i2c.recv(audio_setup_param.i2c_id, ES8311_ADDR, 1)
-    
     if data and #data == 1 then
-        -- 将读取到的字节转换为十六进制
         return true
-    else
-        return false
     end
+
+    log.error("读取ES8311芯片ID失败")
+    return false
 end
 
+-- 音频硬件初始化
 local function audio_setup()
-    local result,data
     sys.wait(100)
 
-    i2c.setup(audio_setup_param.i2c_id,i2c.FAST)
-    
-
-    result,data= i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, audio_setup_param.bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
-    if not result then     --  设置音频参数错误
-        return result
+    -- I2C配置
+    if not i2c.setup(audio_setup_param.i2c_id, i2c.FAST) then
+        log.error("I2C初始化失败")
+        return false
     end
-
-    audio.config(multimedia_id, audio_setup_param.pa_ctrl, audio_setup_param.pa_on_level, audio_setup_param.dac_delay, audio_setup_param.pa_delay, audio_setup_param.dac_ctrl, power_on_level, audio_setup_param.dac_time_delay)
-    audio.setBus(multimedia_id, audio.BUS_I2S,{chip = audio_setup_param.model,i2cid = audio_setup_param.i2c_id , i2sid = i2s_id, voltage = audio.VOLTAGE_1800})	--通道0的硬件输出通道设置为I2S
-
-    audio.vol(multimedia_id, voice_vol)
-    audio.micVol(multimedia_id, mic_vol)
-    audio.pm(multimedia_id,audio.RESUME)
+    -- 初始化I2S
+    local result, data = i2s.setup(
+        I2S_ID, 
+        I2S_MODE, 
+        I2S_SAMPLE_RATE, 
+        audio_setup_param.bits_per_sample, 
+        I2S_CHANNEL_FORMAT, 
+        I2S_COMM_FORMAT,
+        I2S_CHANNEL_BITS
+    )
+
+    if not result then
+        log.error("I2S设置失败")
+        return false
+    end
+    -- 配置音频通道
+    audio.config(
+        MULTIMEDIA_ID, 
+        audio_setup_param.pa_ctrl, 
+        audio_setup_param.pa_on_level, 
+        audio_setup_param.dac_delay, 
+        audio_setup_param.pa_delay, 
+        audio_setup_param.dac_ctrl, 
+        1,  -- power_on_level
+        audio_setup_param.dac_time_delay
+    )
+    -- 设置总线
+    audio.setBus(
+        MULTIMEDIA_ID, 
+        audio.BUS_I2S,
+        {
+            chip = audio_setup_param.model,
+            i2cid = audio_setup_param.i2c_id,
+            i2sid = I2S_ID,
+            voltage = audio.VOLTAGE_1800
+        }
+    )
+
+  
+    -- 设置音量
+    audio.vol(MULTIMEDIA_ID, voice_vol)
+    audio.micVol(MULTIMEDIA_ID, mic_vol)
+    audio.pm(MULTIMEDIA_ID, audio.RESUME)
+    
     sys.wait(100)
-    if not read_es8311_id() then
-        log.error("编解码芯片(8311)通讯失败,请检查硬件")
+    -- 检查芯片连接
+    if audio_setup_param.model == "es8311" and not read_es8311_id() then
+        log.error("ES8311通讯失败,请检查硬件")
         return false
     end
 
-    
-    audio.on(multimedia_id, audio_callback)
-    return result
+    -- 注册回调
+    audio.on(MULTIMEDIA_ID, audio_callback)
+    return true
 end
 
-
-
-
+-- 模块接口:初始化
 function exaudio.setup(audioConfigs)
-    if audioConfigs.model == nil  and (audioConfigs.model ~= "es8311"  or audioConfigs.model ~= "es8211") then   -- codec 名称必现写
-        log.error("没有填写codec 名称,或者不是es8311,es8211")
-        return false      
-    else
-        audio_setup_param.model = audioConfigs.model
+    -- 检查必要参数
+    if not audioConfigs or type(audioConfigs) ~= "table" then
+        log.error("配置参数必须为table类型")
+        return false
     end
-
-    if audioConfigs.model  == "es8311"  then
-        if  ( audioConfigs.i2c_id == nil  or type(audioConfigs.i2c_id) ~= "number"  )then   
-             -- 如果是8311 ,则必须写出i2c 地址
-            log.error("当前使用8311 codec, 但没有填写i2c id")
+    -- 检查codec型号
+    if not audioConfigs.model or 
+       (audioConfigs.model ~= "es8311" and audioConfigs.model ~= "es8211") then
+        log.error("请指定正确的codec型号(es8311或es8211)")
+        return false
+    end
+    audio_setup_param.model = audioConfigs.model
+    -- 针对ES8311的特殊检查
+    if audioConfigs.model == "es8311" then
+        if not check_param(audioConfigs.i2c_id, "number", "i2c_id") then
             return false
-        else
-            audio_setup_param.i2c_id = audioConfigs.i2c_id
         end
+        audio_setup_param.i2c_id = audioConfigs.i2c_id
     end
 
-    if audioConfigs.pa_ctrl == nil then   -- pa_ctrl PA 的控制管脚必须填写
-        log.error("pa 控制为空")
+    -- 检查功率放大器控制管脚
+    if audioConfigs.pa_ctrl == nil then
+        log.error("pa_ctrl(功率放大器控制管脚)为必填项")
         return false
-    else
-        audio_setup_param.pa_ctrl = audioConfigs.pa_ctrl
-    end
-
-
-    if audioConfigs.dac_ctrl ~= nil  and type(audioConfigs.dac_ctrl) == "number" then   -- 编解码芯片控制管脚不需要必填
-        audio_setup_param.dac_ctrl = audioConfigs.dac_ctrl
-    end
-
-    if audioConfigs.dac_delay ~= nil  and type(audioConfigs.dac_delay) == "number" then   -- 在DAC启动前插入的冗余时间 ,非必填
-        audio_setup_param.dac_delay = audioConfigs.dac_delay
-    end
-
-    if audioConfigs.pa_delay ~= nil  and type(audioConfigs.pa_delay) == "number" then   -- 在DAC启动后,延迟多长时间打开PA ,非必填
-        audio_setup_param.pa_delay = audioConfigs.padelay
-    end
-
-
-    if audioConfigs.dac_time_delay ~= nil  and type(audioConfigs.dac_time_delay) == "number" then   
-        audio_setup_param.dac_time_delay = audioConfigs.dac_time_delay
     end
-
-    if audioConfigs.bits_per_sample ~= nil  and type(audioConfigs.bits_per_sample) == "number" then   
-        audio_setup_param.bits_per_sample = audioConfigs.bits_per_sample
-    else
-        audio_setup_param.bits_per_sample = 16
-    end
-    
-    if audioConfigs.pa_on_level ~= nil  and type(audioConfigs.pa_on_level) == "number" then  
-        audio_setup_param.pa_on_level = audioConfigs.pa_on_level
-    else
-        audio_setup_param.pa_on_level = 1  
+    audio_setup_param.pa_ctrl = audioConfigs.pa_ctrl
+
+    -- 处理可选参数
+    local optional_params = {
+        {name = "dac_ctrl", type = "number"},
+        {name = "dac_delay", type = "number"},
+        {name = "pa_delay", type = "number"},
+        {name = "dac_time_delay", type = "number"},
+        {name = "bits_per_sample", type = "number"},
+        {name = "pa_on_level", type = "number"}
+    }
+
+    for _, param in ipairs(optional_params) do
+        if audioConfigs[param.name] ~= nil then
+            if check_param(audioConfigs[param.name], param.type, param.name) then
+                audio_setup_param[param.name] = audioConfigs[param.name]
+            else
+                return false
+            end
+        end
     end
 
-    return audio_setup()    -- 返回初始化结果
+    -- 确保采样位数有默认值
+    audio_setup_param.bits_per_sample = audio_setup_param.bits_per_sample or 16
+    return audio_setup()
 end
 
+-- 模块接口:写入音频数据块
 function exaudio.write_datablock(data)
-    -- local block_lens = 256
-    -- if #data % block_lens  ~= 0 then
-    --     log.info("write_datablock0",#data,block_lens - #data % block_lens)
-    --     data = data .. string.rep("\255", block_lens - #data % block_lens) 
-    --     log.info("write_datablock1",#data)
-    -- end
-    audio.write(multimedia_id,data)    
+    if not data or type(data) ~= "string" then
+        log.error("写入的数据必须为字符串类型")
+        return false
+    end
+    return audio.write(MULTIMEDIA_ID, data)
 end
 
+-- 模块接口:开始播放
 function exaudio.play_start(playConfigs)
-    if playConfigs.type == nil  and type(playConfigs.type)  ~= "number" then   
-        log.error("type 必须填写,也必须为数值,0:播放文件,1: 播放TTS,2:流式播放")
-        return false      
-    else
-        audio_play_param.type = playConfigs.type
+    if not playConfigs or type(playConfigs) ~= "table" then
+        log.error("播放配置必须为table类型")
+        return false
     end
 
-    if playConfigs.priority ~= nil and type(playConfigs.priority) == "number" then  -- 如果当前的播放优先级比历史优先级高,则停止之前的播放
-        if playConfigs.priority >= audio_play_param.priority then   
-            audio.play(multimedia_id)
-            sys.waitUntil(MSG_PD)
-            audio_play_param.priority = playConfigs.priority
+    -- 检查播放类型
+    if not check_param(playConfigs.type, "number", "type") then
+        log.error("type必须为数值(0:文件,1:TTS,2:流式)")
+        return false
+    end
+    audio_play_param.type = playConfigs.type
+
+    -- 处理优先级
+    if playConfigs.priority ~= nil then
+        if check_param(playConfigs.priority, "number", "priority") then
+            if playConfigs.priority >= audio_play_param.priority then
+                audio.play(MULTIMEDIA_ID)
+                sys.waitUntil(MSG_PLAY_DONE)
+                audio_play_param.priority = playConfigs.priority
+            end
+        else
+            return false
         end
     end
 
-    if audio_play_param.type == 0 then    --  播放文件处理
-        if playConfigs.content == nil then
-            log.error("当type 为0 时,playConfigs.content 只能为string(播放单文件),或者table(多文件)")
-            return false      
+    -- 处理不同播放类型
+    local play_type = audio_play_param.type
+    if play_type == 0 then  -- 文件播放
+        if not playConfigs.content then
+            log.error("文件播放需要指定content(文件路径或路径表)")
+            return false
         end
-        
-        if(type(playConfigs.content) == "table") then      -- 当播放多文件时候
-            for _, path in ipairs(playConfigs.content) do  
-                if type(path) ~= "string" then             -- 检查多文件是否都是string
-                    log.error("当type 为0 时,playConfigs.content 为table 时,table 内元素必须为string 类型")
-                    return false     
+
+        local content_type = type(playConfigs.content)
+        if content_type == "table" then
+            for _, path in ipairs(playConfigs.content) do
+                if type(path) ~= "string" then
+                    log.error("播放列表元素必须为字符串路径")
+                    return false
                 end
             end
-        elseif  (type(playConfigs.content) ~= "string") then
-            log.error("当type 为0 时,playConfigs.content 类型必须为table(播放多文件),或者string(播放单文件)")
-            return false    
+        elseif content_type ~= "string" then
+            log.error("文件播放content必须为字符串或路径表")
+            return false
         end
+
         audio_play_param.content = playConfigs.content
-        audio.play(multimedia_id,audio_play_param.content)
-    end
+        audio.play(MULTIMEDIA_ID, audio_play_param.content)
 
-    if audio_play_param.type == 1 then        -- 播放tts 处理
-        if playConfigs.content == nil or type(playConfigs.content) ~= "string"  then
-            log.error("当type 为1 时,表示播放tts,playConfigs.content 必须为string 类型")
-            return false    
+    elseif play_type == 1 then  -- TTS播放
+        if not check_param(playConfigs.content, "string", "content") then
+            log.error("TTS播放content必须为字符串")
+            return false
         end
         audio_play_param.content = playConfigs.content
-        audio.tts(multimedia_id,audio_play_param.content)
-    end
-    if audio_play_param.type == 2 then        -- 流式播放处理
-        if playConfigs.content == nil or type(playConfigs.content) ~= "function"  then
-            log.error("当type 为2 时,表示流式播放,playConfigs.content 必须为function 类型")
-            return false    
+        audio.tts(MULTIMEDIA_ID, audio_play_param.content)
+
+    elseif play_type == 2 then  -- 流式播放
+        if not check_param(playConfigs.content, "function", "content") then
+            log.error("流式播放content必须为回调函数")
+            return false
         end
-        if playConfigs.sampling_Rate == nil or type(playConfigs.sampling_Rate) ~= "number"  then
-            log.error("当type 为2 时,表示流式播放,sampling_Rate(采样率)必须为int 类型")
-            return false    
+        if not check_param(playConfigs.sampling_Rate, "number", "sampling_Rate") then
+            return false
         end
-        if playConfigs.sampling_Depth == nil or type(playConfigs.sampling_Depth) ~= "number"  then
-            log.error("当type 为2 时,表示流式播放,sampling_Depth(采样位深)必须为int 类型")
-            return false    
+        if not check_param(playConfigs.sampling_Depth, "number", "sampling_Depth") then
+            return false
         end
-        if playConfigs.signed_or_Unsigned == nil and type(playConfigs.signed_or_Unsigned) == "boolean"  then
+
+        audio_play_param.content = playConfigs.content
+        audio_play_param.sampling_Rate = playConfigs.sampling_Rate
+        audio_play_param.sampling_Depth = playConfigs.sampling_Depth
+        
+        if playConfigs.signed_or_Unsigned ~= nil then
             audio_play_param.signed_or_Unsigned = playConfigs.signed_or_Unsigned
         end
-        audio_play_param.content = playConfigs.content
-        audio.start(multimedia_id, audio.PCM, 1, playConfigs.sampling_Rate, playConfigs.sampling_Depth, audio_play_param.signed_or_Unsigned)
-        audio.write(0, string.rep("\0", 512))  -- 流式播放前需要先发一下数据
-    end
 
+        audio.start(
+            MULTIMEDIA_ID, 
+            audio.PCM, 
+            1, 
+            playConfigs.sampling_Rate, 
+            playConfigs.sampling_Depth, 
+            audio_play_param.signed_or_Unsigned
+        )
+        -- 发送初始数据
+        audio.write(MULTIMEDIA_ID, string.rep("\0", 512))
+    end
 
-    if playConfigs.cbFnc ~= nil and type(playConfigs.cbFnc) == "function" then -- 如果填了回调函数,则保存回调韩函数,播放完毕调用回调函数
-        audio_play_param.cbFnc = playConfigs.cbFnc
+    -- 处理回调函数
+    if playConfigs.cbFnc ~= nil then
+        if check_param(playConfigs.cbFnc, "function", "cbFnc") then
+            audio_play_param.cbFnc = playConfigs.cbFnc
+        else
+            return false
+        end
     else
         audio_play_param.cbFnc = nil
     end
-    return true    
+
+    return true
 end
 
+-- 模块接口:停止播放
 function exaudio.play_stop()
-    return audio.stop(multimedia_id)
+    return audio.stop(MULTIMEDIA_ID)
 end
 
+-- 模块接口:检查播放是否结束
 function exaudio.isEnd()
     return audio.isEnd()
 end
 
+-- 模块接口:获取错误信息
 function exaudio.getError()
     return audio.getError()
 end
 
+-- 模块接口:开始录音
 function exaudio.record_start(recodConfigs)
-    local record_type ,amr_quailty,recod_format
-    if recodConfigs.format == nil and type(recodConfigs.format) ~= "number" and recodConfigs.format > 5  then   -- 录音格式必须填写
-        log.error("必须填写录制的音频格式(format),格式内容有exaudio.AMR_NB,exaudio.AMR_WB,exaudio.PCM_8000,exaudio.PCM_16000,exaudio.PCM_24000,exaudio.PCM_32000")
-        audio_record_param.format = recodConfigs.format
-        return false      
+    if not recodConfigs or type(recodConfigs) ~= "table" then
+        log.error("录音配置必须为table类型")
+        return false
     end
-    if recodConfigs.time ~= nil and type(recodConfigs.time) == "number"  then   -- 录音时间
-        audio_record_param.time  = recodConfigs.time
-    else
-        audio_record_param.time = 0
+    -- 检查录音格式
+    if recodConfigs.format == nil or type(recodConfigs.format) ~= "number" or recodConfigs.format > 5 then
+        log.error("请指定正确的录音格式")
+        return false
     end
-    if recodConfigs.path ~= nil then
-        audio_record_param.path  = recodConfigs.path
-        if type(recodConfigs.path) == "string"  then   -- 录音保存位置
-            record_type = recodConfigs.path
-        elseif type(recodConfigs.path) == "function"  then
-            record_type = nil
+    audio_record_param.format = recodConfigs.format
+
+    -- 处理录音时间
+    if recodConfigs.time ~= nil then
+        if check_param(recodConfigs.time, "number", "time") then
+            audio_record_param.time = recodConfigs.time
+        else
+            return false
         end
     else
-        log.error("必须填入路径,或者用于流式录音的回调函数")
-        return  false
+        audio_record_param.time = 0
     end
 
-    
-    
-    if recodConfigs.format  == exaudio.AMR_NB  then
+    -- 处理存储路径/回调
+    if not recodConfigs.path then
+        log.error("必须指定录音路径或流式回调函数")
+        return false
+    end
+    audio_record_param.path = recodConfigs.path
+
+    -- 转换录音格式
+    local recod_format, amr_quailty
+    if audio_record_param.format == exaudio.AMR_NB then
         recod_format = audio.AMR_NB
         amr_quailty = 7
-    elseif recodConfigs.format  == exaudio.AMR_WB then
+    elseif audio_record_param.format == exaudio.AMR_WB then
         recod_format = audio.AMR_WB
         amr_quailty = 8
-    elseif recodConfigs.format  == exaudio.PCM_8000 then
+    elseif audio_record_param.format == exaudio.PCM_8000 then
         recod_format = 8000
-    elseif recodConfigs.format  == exaudio.PCM_16000 then
+    elseif audio_record_param.format == exaudio.PCM_16000 then
         recod_format = 16000
-    elseif recodConfigs.format  == exaudio.PCM_24000 then
+    elseif audio_record_param.format == exaudio.PCM_24000 then
         recod_format = 24000
-    elseif recodConfigs.format  == exaudio.PCM_32000 then
+    elseif audio_record_param.format == exaudio.PCM_32000 then
         recod_format = 32000
     end
 
-    if recodConfigs.cbFnc ~= nil and type(recodConfigs.cbFnc) == "function" then -- 如果填了回调函数,则保存回调韩函数,播放完毕调用回调函数
-        audio_record_param.cbFnc = recodConfigs.cbFnc
+    -- 处理回调函数
+    if recodConfigs.cbFnc ~= nil then
+        if check_param(recodConfigs.cbFnc, "function", "cbFnc") then
+            audio_record_param.cbFnc = recodConfigs.cbFnc
+        else
+            return false
+        end
     else
         audio_record_param.cbFnc = nil
     end
-
-    if record_type == "string" then
-        return audio.record(multimedia_id, recod_format, audio_record_param.time, amr_quailty, record_type)
-    else
-        if pcm_buff0 == nil  and pcm_buff1 == nil then
+    -- 开始录音
+    local path_type = type(audio_record_param.path)
+    if path_type == "string" then
+        return audio.record(
+            MULTIMEDIA_ID, 
+            recod_format, 
+            audio_record_param.time, 
+            amr_quailty, 
+            audio_record_param.path
+        )
+    elseif path_type == "function" then
+        -- 初始化缓冲区
+        if not pcm_buff0 or not pcm_buff1 then
             pcm_buff0 = zbuff.create(16000)
             pcm_buff1 = zbuff.create(16000)
         end
-        return audio.record(multimedia_id, recod_format, audio_record_param.time, amr_quailty, record_type, 3,pcm_buff0,pcm_buff1)
+        return audio.record(
+            MULTIMEDIA_ID, 
+            recod_format, 
+            audio_record_param.time, 
+            amr_quailty, 
+            nil, 
+            3,
+            pcm_buff0,
+            pcm_buff1
+        )
     end
- 
+    log.error("录音路径必须为字符串或函数")
+    return false
 end
 
+-- 模块接口:停止录音
 function exaudio.record_stop()
-    audio.recordStop(multimedia_id)
+    audio.recordStop(MULTIMEDIA_ID)
 end
 
+-- 模块接口:设置音量
 function exaudio.vol(number)
-    audio.vol(multimedia_id, number)
+    if check_param(number, "number", "音量值") then
+        voice_vol = number
+        return audio.vol(MULTIMEDIA_ID, number)
+    end
+    return false
 end
 
+-- 模块接口:设置麦克风音量
 function exaudio.micVol(number)
-    udio.micVol(multimedia_id, number)
+    if check_param(number, "number", "麦克风音量值") then
+        mic_vol = number
+        return audio.micVol(MULTIMEDIA_ID, number)  
+    end
+    return false
 end
 
 return exaudio

+ 1 - 1
module/Air8000/demo/audio/main.lua

@@ -15,7 +15,7 @@ _G.sys = require("sys")
 -- require "play_tts"      -- 播放tts
 -- require "play_steam"        -- 流式播放
 require "record_file"        -- 录音到文件
--- require "record_steam"        -- 流式录音
+-- require "record_steam"        -- 流式录音   
 
 
 sys.timerLoopStart(function()

+ 1 - 1
module/Air8000/demo/audio/play_file.lua

@@ -4,7 +4,7 @@ local audio_setup_param ={
     model= "es8311",          -- 音频编解码类型,可填入"es8311","es8211"
     i2c_id = 0,          -- i2c_id,可填入0,1 并使用pins 工具配置对应的管脚
     pa_ctrl = 162,         -- 音频放大器电源控制管脚
-    dac_ctrl = 165,        --  音频编解码芯片电源控制管脚
+    dac_ctrl = 164,        --  音频编解码芯片电源控制管脚
     -- dac_delay = 3,        -- 在DAC启动前插入的冗余时间,单位100ms,控制pop 音
     -- pa_delay = 100,      -- 在DAC启动后,延迟多长时间打开PA,单位1ms,控制pop 音
     -- dac_time_delay = 600,    -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms,控制pop 音

+ 1 - 1
module/Air8000/demo/audio/play_tts.lua

@@ -3,7 +3,7 @@ exaudio = require("exaudio")
 local audio_setup_param ={
     model= "es8311",          -- dac类型,可填入"es8311","es8211"
     i2c_id = 0,          -- i2c_id,可填入0,1 并使用pins 工具配置对应的管脚
-    pa_ctrl = 21,         -- 音频放大器电源控制管脚
+    pa_ctrl = 162,         -- 音频放大器电源控制管脚
     dac_ctrl = 164,        --  音频编解码芯片电源控制管脚 
     -- dac_delay = 3,        -- 在DAC启动前插入的冗余时间,单位100ms
     -- pa_delay = 100,      -- 在DAC启动后,延迟多长时间打开PA,单位1ms

+ 0 - 0
module/Air8000/demo/audio/readme.md


+ 4 - 8
module/Air8000/demo/audio/record_file.lua

@@ -6,22 +6,19 @@ local audio_setup_param ={
     pa_ctrl = 162,         -- 音频放大器电源控制管脚
     dac_ctrl = 164,        --  音频编解码芯片电源控制管脚
     bits_per_sample = 16  -- 录音的位深,可选8,16,24 位,但是当选择音频格式为AMR_NB,自动调节为8位,当音频格式为AMR_WB,自动调节为16位
-    -- dac_delay = 3,        -- 在DAC启动前插入的冗余时间,单位100ms,控制pop 音
-    -- pa_delay = 100,      -- 在DAC启动后,延迟多长时间打开PA,单位1ms,控制pop 音
-    -- dac_time_delay = 600,    -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms,控制pop 音
-    -- pa_on_level = 1,           -- PA打开电平 1 高电平 0 低电平        
 }
 local recordPath = "/record.amr"
 
 local function record_end(event)
-    if event == exaudio.RECODE_DONE then
+    if event == exaudio.RECORD_DONE then
         log.info("录音完成")
+        log.info("录音后文件大小",io.fileSize(recordPath))
     end
 end 
 
 
 local audio_record_param ={
-    format= exaudio.AMR_NB,    -- 录制格式,有exaudio.AMR_NB,exaudio.AMR_WB,exaudio.PCM_8000,exaudio.PCM_16000,exaudio.PCM_24000,exaudio.PCM_32000
+    format= exaudio.AMR_WB,    -- 录制格式,有exaudio.AMR_NB,exaudio.AMR_WB,exaudio.PCM_8000,exaudio.PCM_16000,exaudio.PCM_24000,exaudio.PCM_32000
     time = 5,               -- 录制时间,单位(秒)
     path = recordPath,             -- 如果填入的是字符串,则表示是文件路径,录音会传输到这个路径里
                                       -- 如果填入的是函数,则表示是流式录音,录音的数据会传输到此函数内,返回的是zbuf地址,以及数据长度
@@ -32,11 +29,10 @@ local audio_record_param ={
 
 local taskName = "task_audio"
 local function audio_task()
+    sys.wait(100)
     log.info("开始录制音频到文件")
     if exaudio.setup(audio_setup_param) then
         exaudio.record_start(audio_record_param)
-        sys.wait(7000)
-        log.info("录音后文件大小",io.fileSize(recordPath))
     end
 end
 

+ 1 - 5
module/Air8000/demo/audio/record_steam.lua

@@ -6,17 +6,13 @@ local audio_setup_param ={
     pa_ctrl = 162,         -- 音频放大器电源控制管脚
     dac_ctrl = 164,        --  音频编解码芯片电源控制管脚
     bits_per_sample = 16  -- 录音的位深,可选8,16,24 位,但是当选择音频格式为AMR_NB,自动调节为8位,当音频格式为AMR_WB,自动调节为16位
-    -- dac_delay = 3,        -- 在DAC启动前插入的冗余时间,单位100ms,控制pop 音
-    -- pa_delay = 100,      -- 在DAC启动后,延迟多长时间打开PA,单位1ms,控制pop 音
-    -- dac_time_delay = 600,    -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms,控制pop 音
-    -- pa_on_level = 1,           -- PA打开电平 1 高电平 0 低电平        
 }
 
 local function  recode_data_callback(addr,data_len)
     log.info("收到音频流,地址为:",addr,"有效数据长度为:",data_len)
 end
 local function record_end(event)
-    if event == exaudio.RECODE_DONE then
+    if event == exaudio.RECORD_DONE then
         log.info("录音完成")
     end
 end