|
|
@@ -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
|