录音
一、录音概述
录音技术是通过麦克风将声波振动转换为模拟电信号,经模数转换(ADC)采样、量化后编码为数字音频(如 WAV、MP3 格式),并存储于磁带、光盘或闪存介质中的过程,核心环节包括声学采集(指向性麦克风)、信号放大、降噪处理(如 DSP 算法)、动态范围压缩及编解码优化,现代技术进一步融合高保真(Hi-Res)、空间音频(3D 录音)与云端同步能力,广泛应用于音乐制作、通信、安防及多媒体领域。
二、演示功能概述
本篇文章演示的内容为:Air8000 整机开发板,接上喇叭,录制音频,然后播放,播放完之后把录音文件通过串口 1 发送出去
三、准备硬件环境
参考:Air8000 硬件环境清单,准备好硬件环境,本篇文档使用的是 Air8000 整机开发板,如下图所示:
四、准备软件环境
烧录工具:Luatools 工具
Air8000 烧录需要的固件和脚本文件:
LuatOS 运行所需要的 lib 文件:
使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件
五、代码 API 和代码解析
5.1 代码 API
mcu.altfun(type, sn, pad_index, alt_fun, is_input)
IO 外设功能复用选择,注意普通 MCU 通常是以 GPIO 号为唯一 ID 号,但是专用 SOC,比如 CAT1 的,可能以 PAD 号或者模块 pin 脚号(pin.xxx 后续支持)为唯一 ID 号。本函数不是所有平台适用
参数
传入值类型 | 解释 |
int | 外设类型,目前有mcu.UART,mcu.I2C,mcu.SPI,mcu.PWM,mcu.GPIO,mcu.I2S,mcu.LCD,mcu.CAM,具体需要看平台 |
int | 总线序号,0~N,如果是mcu.GPIO,则是GPIO号。具体看平台的IOMUX复用表 |
int | 唯一ID号,如果留空不写,则表示清除配置,使用平台的默认配置。具体看平台的IOMUX复用表 |
int | 复用功能序号,0~N。具体看平台的IOMUX复用表 |
boolean | 是否是输入功能,true是,留空是false |
返回值
无
例子
mcu.altfun(mcu.I2C, 0, 13, 2, 0) --复用pin80为i2c0_scl
mcu.altfun(mcu.I2C, 0, 14, 2, 0) --复用pin81为i2c0_sda
audio.config(id, paPin, onLevel, dacDelay, paDelay, dacPin, dacLevel, dacTimeDelay)
配置一个音频通道的特性,比如实现自动控制 PA 开关。注意这个不是必须的,一般在调用 play 的时候才需要自动控制,其他情况比如你手动控制播放时,就可以自己控制 PA 开关
参数
传入值类型 | 解释 |
int | 音频通道 |
int | PA控制IO |
int | PA打开时的电平 |
int | 在DAC启动前插入的冗余时间,单位100ms,一般用于外部DAC |
int | 在DAC启动后,延迟多长时间打开PA,单位1ms |
int | 外部dac电源控制IO |
int | 外部dac打开时,电源控制IO的电平,默认拉高 |
int | 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms,默认0ms |
返回值
无
例子
audio.config(0, pin.PC0, 1) --PA控制脚是PC0,高电平打开
audio.config(0, 25, 1, 6, 200) --PA控制脚是GPIO25,高电平打开
codec.create(type, isDecoder, quality)
创建编解码用的 codec
参数
传入值类型 | 解释 |
int | 多媒体类型,目前支持codec.MP3 codec.AMR |
boolean | 是否是解码器,true解码器,false编码器,默认true,是解码器 |
int | 编码等级,部分bsp有内部编解码器,可能需要提前输入编解码等级,不知道的就填7 |
返回值
返回值类型 | 解释 |
userdata | 成功返回一个数据结构,否则返回nil |
例子
-- 创建解码器
local decoder = codec.create(codec.MP3)--创建一个mp3的decoder
-- 创建编码器
local encoder = codec.create(codec.AMR, false)--创建一个amr的encoder
-- 创建编码器
local encoder = codec.create(codec.AMR_WB, false, 8)--创建一个amr-wb的encoder,编码等级默认8
codec.encode(coder, in_buffer, out_buffer, mode)
编码音频数据,由于 flash 和 ram 空间一般比较有限,除了部分 bsp 有内部 amr 编码功能,目前只支持 amr-nb 编码
参数
传入值类型 | 解释 |
userdata | codec.create创建的编解码用的coder |
zbuff | 输入的数据,zbuff形式,从0到used |
zbuff | 输出的数据,zbuff形式,自动添加到buff的尾部,如果空间大小不足,会自动扩展,但是会额外消耗时间,甚至会失败,所以尽量一开始就给足空间 |
int | amr_nb的编码等级 0~7(即 MR475~MR122)值越大消耗的空间越多,音质越高,默认0 amr_wb的编码等级 0~8,值越大消耗的空间越多,音质越高,默认0 |
返回值
返回值类型 | 解释 |
boolean | 成功返回true,失败返回false |
例子
codec.encode(amr_coder, inbuf, outbuf)
i2s.setup(id, mode, sample, bitw, channel, format, framebit)
初始化 i2s
参数
传入值类型 | 解释 |
int | i2s通道号,与具体设备有关 |
int | 模式, 0 主机 1 从机 |
int | 采样率,默认44100. 可选 |
int | 数据位数,默认16, 可以是8的倍数 |
int | 声道, 0 左声道, 1 右声道, 2 立体声. 可选 |
int | 格式, 可选MODE_I2S, MODE_LSB, MODE_MSB |
int | 1个声道的BCLK数量, 可选16和32 |
返回值
返回值类型 | 解释 |
boolean | 成功与否 |
int | 底层返回值 |
例子
-- 以默认参数初始化i2s
i2s.setup(0)
-- 以详细参数初始化i2s, 示例为默认值
i2s.setup(0, 0, 44100, 16, 0, 0, 16)
codec.release(coder)
释放编解码用的 coder
参数
传入值类型 | 解释 |
coder | codec.create创建的编解码用的coder |
返回值
无
例子
codec.release(coder)
audio.vol(id, value)
配置一个音频通道的音量调节,直接将原始数据放大或者缩小,不是所有平台都支持,建议尽量用硬件方法去缩放
参数
传入值类型 | 解释 |
int | 音频通道 |
int | 音量,百分比,1%~1000%,默认100%,就是不调节 |
返回值
返回值类型 | 解释 |
int | 当前音量 |
例子
local result = audio.vol(0, 90) --通道0的音量调节到90%,result存放了调节后的音量水平,有可能仍然是100
audio.micVol(id, value)
配置一个音频通道的 mic 音量调节
参数
传入值类型 | 解释 |
int | 音频通道 |
int | mic音量,百分比,1%~100%,默认100%,就是不调节 |
返回值
返回值类型 | 解释 |
int | 当前mic音量 |
例子
local result = audio.vol(0, 90) --通道0的音量调节到90%,result存放了调节后的音量水平,有可能仍然是100
audio.getError(id)
获取最近一次播放结果
参数
传入值类型 | 解释 |
int | 音频通道 |
返回值
返回值类型 | 解释 |
boolean | 是否全部播放成功,true成功,false有文件播放失败 |
boolean | 如果播放失败,是否是用户停止,true是,false不是 |
int | 第几个文件失败了,从1开始 |
例子
local result, user_stop, file_no = audio.getError(0)
audio.play(id, path, errStop)
播放或者停止播放一个文件,播放完成后,会回调一个 audio.DONE 消息,可以用 pause 来暂停或者恢复,其他 API 不可用。考虑到读 SD 卡速度比较慢而拖累 luavm 进程的速度,所以尽量使用本 API
参数
传入值类型 | 解释 |
int | 音频通道 |
string/table | 文件名,如果为空,则表示停止播放,如果是table,则表示连续播放多个文件,主要应用于云喇叭 |
boolean | 是否在文件解码失败后停止解码,只有在连续播放多个文件时才有用,默认true,遇到解码错误自动停止 |
返回值
返回值类型 | 解释 |
boolean | 成功返回true,否则返回false |
例子
audio.play(0, "xxxxxx") --开始播放某个文件
audio.setBus(id, bus_type)
配置一个音频通道的硬件输出总线,只有对应 soc 软硬件平台支持才设置对应类型
参数
传入值类型 | 解释 |
int | 音频通道,例如0 |
int | 总线类型, 例如 audio.BUS_SOFT_DAC |
int | 硬件id, 例如 总线类型为audio.BUS_I2S时,硬件id即为i2s codec的i2c id |
返回值
返回值类型 | 解释 |
nil | 无返回值 |
例子
audio.setBus(0, audio.BUS_SOFT_DAC) --通道0的硬件输出通道设置为软件DAC
audio.setBus(0, audio.BUS_I2S) --通道0的硬件输出通道设置为I2S
audio.on(audio_id, func)
注册 audio 播放事件回调
参数
传入值类型 | 解释 |
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 |
常量 | 类型 | 解释 |
audio.MORE_DATA | number | audio.on回调函数传入参数的值,表示底层播放完一段数据,可以传入更多数据 |
audio.DONE | number | audio.on回调函数传入参数的值,表示底层播放完全部数据了 |
audio.RECORD_DATA | number | audio.on回调函数传入参数的值,表示录音数据 |
audio.RECORD_DONE | number | audio.on回调函数传入参数的值,表示录音完成 |
返回值
返回值类型 | 解释 |
nil | 无返回值 |
例子
audio.on(0, function(audio_id, msg)
log.info("msg", audio_id, msg)
end)
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里配置 |
返回值
返回值类型 | 解释 |
boolean | 成功返回true,否则返回false |
例子
err,info = audio.record(id, type, record_time, quailty, path)
5.2 代码解析
1.复用 pin80 和 pin81 为 i2c0
mcu.altfun(mcu.I2C, 0, 13, 2, 0)
mcu.altfun(mcu.I2C, 0, 14, 2, 0)
2.设置 audio.on 的回调函数,根据 event 对录音数据的 point 存在对应的 buff 里面,再根据 event 判断是否为录音完成,录音完成通过 sys.publish 发送内部消息,激活录音完成之后的等待,然后根据播放结果,返回对应内容
local taskName = "task_audio"
local MSG_MD = "moreData" -- 播放缓存有空余
local MSG_PD = "playDone" -- 播放完成所有数据
-- amr数据存放buffer,尽可能地给大一些
amr_buff = zbuff.create(20 * 1024)
--创建一个amr的encoder
encoder = nil
pcm_buff0 = zbuff.create(16000)
pcm_buff1 = zbuff.create(16000)
audio.on(0, function(id, event, point)
--使用play来播放文件时只有播放完成回调
if event == audio.RECORD_DATA then -- 录音数据
if point == 0 then
log.info("buff", point, pcm_buff0:used())
codec.encode(encoder, pcm_buff0, amr_buff)
else
log.info("buff", point, pcm_buff1:used())
codec.encode(encoder, pcm_buff1, amr_buff)
end
elseif event == audio.RECORD_DONE then -- 录音完成
sys.publish("AUDIO_RECORD_DONE")
else
local succ,stop,file_cnt = audio.getError(0)
if not succ then
if stop then
log.info("用户停止播放")
else
log.info("第", file_cnt, "个文件解码失败")
end
end
-- log.info("播放完成一个音频")
sysplus.sendMsg(taskName, MSG_PD)
end
end)
3.通过 HTTP 的 post 方式,利用 multipart/form-data 的方式,把录音文件传到服务器上。
---- MultipartForm上传文件
-- url string 请求URL地址
-- filename string 上传服务器的文件名
-- filePath string 待上传文件的路径
local function postMultipartFormData(url, filename, filePath)
local boundary = "----WebKitFormBoundary"..os.time()
local req_headers = {
["Content-Type"] = "multipart/form-data; boundary=" .. boundary,
}
local body = {}
table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\"file\"; filename=\"".. filename .."\"\r\n\r\n")
table.insert(body, io.readFile(filePath))
table.insert(body, "\r\n")
table.insert(body, "--"..boundary.."--\r\n")
body = table.concat(body)
log.info("headers: ", "\r\n" .. json.encode(req_headers), type(body))
log.info("body: " .. body:len() .. "\r\n" .. body)
local code, headers, body = http.request("POST",url,
req_headers,
body
).wait()
log.info("http.post", code, headers, body)
end
-- 下面的演示是将音频文件发送到服务器上,如有需要,可以将下面代码注释打开,这里的url是合宙的文件上传测试服务器,上传的文件到http://tools.openluat.com/tools/device-upload-test查看
--[[
local timeTable = os.date("*t", os.time())
local nowTime = string.format("%4d%02d%02d_%02d%02d%02d", timeTable.year, timeTable.month, timeTable.day, timeTable.hour, timeTable.min, timeTable.sec)
local filename = mobile.imei() .. "_" .. nowTime .. ".amr"
postMultipartFormData("http://tools.openluat.com/api/site/device_upload_file", filename, recordPath)
]]
4.设置 i2s 和 audio 的一些参数,控制 8311 上电,然后设置 i2c,i2s,audio 的一些基础配置。
function audio_setup()
local i2c_id = 0 -- i2c_id 0
local pa_pin = 16 -- 喇叭pa功放脚
local power_pin = 8 -- es8311电源脚
local i2s_id = 0 -- i2s_id 0
local i2s_mode = 0 -- i2s模式 0 主机 1 从机
local i2s_sample_rate = 16000 -- 采样率
local i2s_bits_per_sample = 16 -- 数据位数
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 pa_on_level = 1 -- PA打开电平 1 高电平 0 低电平
local power_delay = 3 -- 在DAC启动前插入的冗余时间,单位100ms
local pa_delay = 100 -- 在DAC启动后,延迟多长时间打开PA,单位1ms
local power_on_level = 1 -- 电源控制IO的电平,默认拉高
local power_time_delay = 100 -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms
local voice_vol = 70 -- 喇叭音量
local mic_vol = 80 -- 麦克风音量
pm.power(pm.LDO_CTL, false) --开发板上ES8311由LDO_CTL控制上下电
sys.wait(100)
pm.power(pm.LDO_CTL, true) --开发板上ES8311由LDO_CTL控制上下电
gpio.setup(8, 1)
i2c.setup(i2c_id,i2c.FAST) --设置i2c
i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits) --设置i2s
audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311",i2cid = i2c_id , i2sid = i2s_id, voltage = audio.VOLTAGE_1800}) --通道0的硬件输出通道设置为I2S
audio.vol(multimedia_id, voice_vol)
audio.micVol(multimedia_id, mic_vol)
sys.publish("AUDIO_READY")
end
-- 配置好audio外设
sys.taskInit(audio_setup)
5.本函数主要有两种录音的方式,第一种是直接录音到文件里面,然后等待录音完成的激活,然后播放音频,等待播放完成的激活,第二种方式是录音到内存,通过数据处理的方式保存成文件,等待录音完成的激活,然后播放音频,等待播放完成的激活,最后有两个对录音文件的扩展,第一个是上面已经讲解过的:利用 http 的方式把文件传到服务器上。第二个是:把录音文件通过串口 1,传到串口上。
local function audio_task()
sys.waitUntil("AUDIO_READY")
sys.wait(5000)
local result
--下面为录音demo,根据适配情况选择性开启
local recordPath = "/record.amr"
-- -- 直接录音到文件
-- err = audio.record(0, audio.AMR, 5, 7, recordPath)
-- sys.waitUntil("AUDIO_RECORD_DONE")
-- log.info("record","录音结束")
-- result = audio.play(0, {recordPath})
-- if result then
-- --等待音频通道的回调消息,或者切换歌曲的消息
-- while true do
-- msg = sysplus.waitMsg(taskName, nil)
-- if type(msg) == 'table' then
-- if msg[1] == MSG_PD then
-- log.info("播放结束")
-- break
-- end
-- else
-- log.error(type(msg), msg)
-- end
-- end
-- else
-- log.debug("解码失败!")
-- sys.wait(1000)
-- end
-- -- 录音到内存自行编码
encoder = codec.create(codec.AMR, false, 7)
log.info("encoder",encoder)
log.info("开始录音")
err = audio.record(0, audio.AMR, 5, 7, nil,nil, pcm_buff0, pcm_buff1)
sys.waitUntil("AUDIO_RECORD_DONE")
log.info("record","录音结束")
os.remove(recordPath)
io.writeFile(recordPath, "#!AMR\n")
io.writeFile(recordPath, amr_buff:query(), "a+b")
result = audio.play(0, {recordPath})
if result then
--等待音频通道的回调消息,或者切换歌曲的消息
while true do
msg = sysplus.waitMsg(taskName, nil)
if type(msg) == 'table' then
if msg[1] == MSG_PD then
log.info("播放结束")
break
end
else
log.error(type(msg), msg)
end
end
else
log.debug("解码失败!")
sys.wait(1000)
end
-- 下面的演示是将音频文件发送到服务器上,如有需要,可以将下面代码注释打开,这里的url是合宙的文件上传测试服务器,上传的文件到http://tools.openluat.com/tools/device-upload-test查看
--[[
local timeTable = os.date("*t", os.time())
local nowTime = string.format("%4d%02d%02d_%02d%02d%02d", timeTable.year, timeTable.month, timeTable.day, timeTable.hour, timeTable.min, timeTable.sec)
local filename = mobile.imei() .. "_" .. nowTime .. ".amr"
postMultipartFormData("http://tools.openluat.com/api/site/device_upload_file", filename, recordPath)
]]
--该方法为从串口1,把录音数据传给串口1
-- uart.setup(1, 115200) -- 开启串口1
-- uart.write(1, io.readFile(recordPath)) -- 向串口发送录音文件
end
六、运行结果展示
6.1 完整代码
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "record"
VERSION = "1.0.0"
-- sys库是标配
_G.sys = require("sys")
_G.sysplus = require("sysplus")
gpio.setup(24, 1, gpio.PULLUP) -- i2c工作的电压域
mcu.altfun(mcu.I2C, 0, 13, 2, 0)
mcu.altfun(mcu.I2C, 0, 14, 2, 0)
local taskName = "task_audio"
local MSG_MD = "moreData" -- 播放缓存有空余
local MSG_PD = "playDone" -- 播放完成所有数据
-- amr数据存放buffer,尽可能地给大一些
amr_buff = zbuff.create(20 * 1024)
--创建一个amr的encoder
encoder = nil
pcm_buff0 = zbuff.create(16000)
pcm_buff1 = zbuff.create(16000)
audio.on(0, function(id, event, point)
--使用play来播放文件时只有播放完成回调
if event == audio.RECORD_DATA then -- 录音数据
if point == 0 then
log.info("buff", point, pcm_buff0:used())
codec.encode(encoder, pcm_buff0, amr_buff)
else
log.info("buff", point, pcm_buff1:used())
codec.encode(encoder, pcm_buff1, amr_buff)
end
elseif event == audio.RECORD_DONE then -- 录音完成
sys.publish("AUDIO_RECORD_DONE")
else
local succ,stop,file_cnt = audio.getError(0)
if not succ then
if stop then
log.info("用户停止播放")
else
log.info("第", file_cnt, "个文件解码失败")
end
end
-- log.info("播放完成一个音频")
sysplus.sendMsg(taskName, MSG_PD)
end
end)
---- MultipartForm上传文件
-- url string 请求URL地址
-- filename string 上传服务器的文件名
-- filePath string 待上传文件的路径
local function postMultipartFormData(url, filename, filePath)
local boundary = "----WebKitFormBoundary"..os.time()
local req_headers = {
["Content-Type"] = "multipart/form-data; boundary=" .. boundary,
}
local body = {}
table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\"file\"; filename=\"".. filename .."\"\r\n\r\n")
table.insert(body, io.readFile(filePath))
table.insert(body, "\r\n")
table.insert(body, "--"..boundary.."--\r\n")
body = table.concat(body)
log.info("headers: ", "\r\n" .. json.encode(req_headers), type(body))
log.info("body: " .. body:len() .. "\r\n" .. body)
local code, headers, body = http.request("POST",url,
req_headers,
body
).wait()
log.info("http.post", code, headers, body)
end
function audio_setup()
local i2c_id = 0 -- i2c_id 0
local pa_pin = 16 -- 喇叭pa功放脚
local power_pin = 8 -- es8311电源脚
local i2s_id = 0 -- i2s_id 0
local i2s_mode = 0 -- i2s模式 0 主机 1 从机
local i2s_sample_rate = 16000 -- 采样率
local i2s_bits_per_sample = 16 -- 数据位数
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 pa_on_level = 1 -- PA打开电平 1 高电平 0 低电平
local power_delay = 3 -- 在DAC启动前插入的冗余时间,单位100ms
local pa_delay = 100 -- 在DAC启动后,延迟多长时间打开PA,单位1ms
local power_on_level = 1 -- 电源控制IO的电平,默认拉高
local power_time_delay = 100 -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms
local voice_vol = 70 -- 喇叭音量
local mic_vol = 80 -- 麦克风音量
pm.power(pm.LDO_CTL, false) --开发板上ES8311由LDO_CTL控制上下电
sys.wait(100)
pm.power(pm.LDO_CTL, true) --开发板上ES8311由LDO_CTL控制上下电
gpio.setup(8, 1)
i2c.setup(i2c_id,i2c.FAST) --设置i2c
i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits) --设置i2s
audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311",i2cid = i2c_id , i2sid = i2s_id, voltage = audio.VOLTAGE_1800}) --通道0的硬件输出通道设置为I2S
audio.vol(multimedia_id, voice_vol)
audio.micVol(multimedia_id, mic_vol)
sys.publish("AUDIO_READY")
end
-- 配置好audio外设
sys.taskInit(audio_setup)
local function audio_task()
sys.waitUntil("AUDIO_READY")
sys.wait(5000)
local result
--下面为录音demo,根据适配情况选择性开启
local recordPath = "/record.amr"
-- -- 直接录音到文件
-- err = audio.record(0, audio.AMR, 5, 7, recordPath)
-- sys.waitUntil("AUDIO_RECORD_DONE")
-- log.info("record","录音结束")
-- result = audio.play(0, {recordPath})
-- if result then
-- --等待音频通道的回调消息,或者切换歌曲的消息
-- while true do
-- msg = sysplus.waitMsg(taskName, nil)
-- if type(msg) == 'table' then
-- if msg[1] == MSG_PD then
-- log.info("播放结束")
-- break
-- end
-- else
-- log.error(type(msg), msg)
-- end
-- end
-- else
-- log.debug("解码失败!")
-- sys.wait(1000)
-- end
-- -- 录音到内存自行编码
encoder = codec.create(codec.AMR, false, 7)
log.info("encoder",encoder)
log.info("开始录音")
err = audio.record(0, audio.AMR, 5, 7, nil,nil, pcm_buff0, pcm_buff1)
sys.waitUntil("AUDIO_RECORD_DONE")
log.info("record","录音结束")
os.remove(recordPath)
io.writeFile(recordPath, "#!AMR\n")
io.writeFile(recordPath, amr_buff:query(), "a+b")
result = audio.play(0, {recordPath})
if result then
--等待音频通道的回调消息,或者切换歌曲的消息
while true do
msg = sysplus.waitMsg(taskName, nil)
if type(msg) == 'table' then
if msg[1] == MSG_PD then
log.info("播放结束")
break
end
else
log.error(type(msg), msg)
end
end
else
log.debug("解码失败!")
sys.wait(1000)
end
-- 下面的演示是将音频文件发送到服务器上,如有需要,可以将下面代码注释打开,这里的url是合宙的文件上传测试服务器,上传的文件到http://tools.openluat.com/tools/device-upload-test查看
--[[
local timeTable = os.date("*t", os.time())
local nowTime = string.format("%4d%02d%02d_%02d%02d%02d", timeTable.year, timeTable.month, timeTable.day, timeTable.hour, timeTable.min, timeTable.sec)
local filename = mobile.imei() .. "_" .. nowTime .. ".amr"
postMultipartFormData("http://tools.openluat.com/api/site/device_upload_file", filename, recordPath)
]]
--该方法为从串口1,把录音数据传给串口1
-- uart.setup(1, 115200) -- 开启串口1
-- uart.write(1, io.readFile(recordPath)) -- 向串口发送录音文件
end
sysplus.taskInitEx(audio_task, taskName, task_cb)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
6.2 结果演示
七、总结
本文演示如何在 Air8000 整机板外挂 es8311,提供了两种录音方式,1:录音到文件,2:录音到内存,然后编码存储到文件。然后提供了两种扩展的应用方式 1:把录音文件发送到服务器,2:把录音文件发送到串口。
八、功耗数据
此功耗测试数据的场景为飞行模式下,单纯录音:录音功耗下载地址:点我,下载完整压缩文件包
九、常见问题
后续在此扩展补充,敬请期待......