近期有較多用戶咨詢通話錄音功能的實現方案及相關參考案例。
AirUI 可視化方案目前仍在優化中,具備輕量化、低成本、高可靠性等特性,在工業場景具備一定應用價值。
針對用戶對通話錄音功能的需求,本文提供一套可直接落地的實現方案,支持自動接聽與自動錄音,適用于暫不使用可視化 UI 的開發場景參考。
一、方案功能
基于LuatOS開發,適配多種型號核心板,可實現低成本通話留痕,適用于客服系統、會議記錄、遠程問診、話務工單追溯等場景。
主要功能特色如下:
自動接聽: 來電響鈴2聲后自動接聽,無需手動操作。
自動錄音: 通話接通后自動開始錄音,對方掛斷后自動停止。
SD卡存儲: 錄音文件以PCM格式保存到SD卡,支持自動掛載和空間檢測。
數據優化: 只保存上行數據,避免下行數據造成的回聲問題。
跨模組兼容: Air780EHM、Air780EGH、Air780EHV、Air8000系列核心板,均可通過外掛音頻配件板和SD卡存儲配件板實現。
簡而言之:一套方案搞定多個硬件型號,極大提升開發效率。
二、主要硬件準備
方案一:Air8000/Air780EHV開發板
開發板提供了豐富的音頻接口資源,可通過開發板上的音頻接口進行連接和測試。
如Air8000/Air780EHV開發板:

方案二:核心板+配件板
如果沒有Air780EHV和Air8000系列Turnkey開發板,那么可使用Air780EHM、Air780EGH、Air780EHV、Air8000系列核心板,通過外掛音頻配件板和存儲配件板來實現通話錄音功能。
AirAUDIO_1010音頻配件板: 負責音頻輸入輸出;
AirMICROSD_1010存儲配件板: 提供SD卡存儲功能。
連接小貼士: 如果搭配AirAUDIO_1010擴展板測試,需將擴展板中PA開關撥到OFF,讓軟件控制PA,避免pop音。
本文以Air780EGH為例:
接線方式參照下方圖表對應連接(注意不同型號核心板具體引腳號差異):


三、開源示例與教程
基于LuatOS開發的通話錄音示例已上傳Gitee開源倉庫,即便是新接觸LuatOS開發的朋友,也可以根據合宙資料中心提供配套實操教程快速上手。
核心功能模塊包括SD卡掛載、通話狀態機、錄音數據回調等,完整示例代碼詳見源碼倉庫最新文件。
--[[
錄音功能特性:
- 錄音文件保存為PCM格式:/sd/record_call.pcm
- 只保存上行數據(包含本地聲音和網絡回聲)
- 下行數據自動跳過,避免重復存儲
- 支持SD卡自動掛載和空間檢測
-- ====================== 錄音功能 ======================
-- 創建音頻數據緩沖區
local up1 = zbuff.create(BUFFER_SIZE,0) -- 上行數據保存區1
local up2 = zbuff.create(BUFFER_SIZE,0) -- 上行數據保存區2
local down1 = zbuff.create(BUFFER_SIZE,0) -- 下行數據保存區1
local down2 = zbuff.create(BUFFER_SIZE,0) -- 下行數據保存區2
-- 打開錄音文件
local function open_record_file()
-- 先掛載SD卡
if not mount_sd_card() then
log.error("錄音文件", "SD卡掛載失敗,無法進行錄音")
return false
end
log.info("錄音文件", "SD卡掛載成功,錄音文件將保存到SD卡")
-- 關閉已打開的文件
if record_file then
record_file:close()
record_file = nil
end
-- 刪除舊錄音文件
if io.exists(RECORD_FILE_PATH) then
os.remove(RECORD_FILE_PATH)
log.info("錄音文件", "刪除舊錄音文件:", RECORD_FILE_PATH)
end
-- 創建錄音文件
record_file = io.open(RECORD_FILE_PATH, "wb")
if record_file then
log.info("錄音文件", "創建錄音文件成功:", RECORD_FILE_PATH)
record_start_time = mcu.ticks()
is_recording_to_file = true
return true
else
log.error("錄音文件", "創建錄音文件失敗:", RECORD_FILE_PATH)
return false
end
end
-- 關閉錄音文件
local function close_record_file()
if record_file then
record_file:close()
record_file = nil
local file_size = io.fileSize(RECORD_FILE_PATH)
record_duration = (mcu.ticks() - record_start_time) / 1000 -- 轉換為秒
log.info("錄音文件", "錄音完成", "文件大小:", file_size, "字節", "錄音時長:", string.format("%.1f", record_duration), "秒", "路徑:", RECORD_FILE_PATH)
is_recording_to_file = false
record_start_time = 0
record_duration = 0
end
end
-- 寫入錄音數據到文件
local function write_record_data(buff, is_downlink)
if not record_file or not is_recording_to_file then
return false
end
-- 保存數據
if not is_downlink then
local data_size = buff:used()
if data_size > 0 then
local start_time = mcu.ticks()
-- 寫入數據到文件
record_file:write(buff:query())
local end_time = mcu.ticks()
local write_time = end_time - start_time
local write_speed = data_size / (write_time / 1000) -- 字節/秒
log.info("錄音寫入",
"數據大小:", data_size, "字節,",
"寫入耗時:", string.format("%.2f", write_time), "ms,",
"寫入速度:", string.format("%.2f", write_speed / 1024), "KB/s")
return true
end
else
-- 下行數據不保存,只記錄日志
-- 寫入下行數據會導致文件內有回聲
local data_size = buff:used()
if data_size > 0 then
log.info("錄音寫入", "下行數據跳過", "數據大小:", data_size, "字節")
end
end
return false
end
-- 音頻數據回調函數
local function recordCallback(is_dl, point)
if is_dl then
log.info("錄音", "下行數據,位于緩存", point+1, "緩存1數據量", down1:used(), "緩存2數據量", down2:used())
-- 處理下行數據
if point == 0 then
write_record_data(down1, true)
down1:del() -- 清空緩沖區
else
write_record_data(down2, true)
down2:del() -- 清空緩沖區
end
else
log.info("錄音", "上行數據,位于緩存", point+1, "緩存1數據量", up1:used(), "緩存2數據量", up2:used())
-- 處理上行數據
if point == 0 then
write_record_data(up1, false)
up1:del() -- 清空緩沖區
else
write_record_data(up2, false)
up2:del() -- 清空緩沖區
end
end
log.info("通話質量", cc.quality())
end
-- 啟用通話錄音
local function enableRecording()
cc.record(true, up1, up2, down1, down2)
cc.on("record", recordCallback)
log.info("cc_app", "通話錄音已啟用")
end
-- 開始通話錄音到文件
local function start_call_recording()
if open_record_file() then
log.info("通話錄音", "開始錄音到文件:", RECORD_FILE_PATH)
return true
else
log.error("通話錄音", "無法開始錄音到文件,請檢查SD卡")
return false
end
end
-- 停止通話錄音到文件
local function stop_call_recording()
close_record_file()
log.info("通話錄音", "停止錄音到文件")
end
-- 獲取所有緩沖區
local function getRecordingBuffers()
return {
up1 = up1,
up2 = up2,
down1 = down1,
down2 = down2
}
end
-- 獲取錄音文件信息
local function get_record_file_info()
if io.exists(RECORD_FILE_PATH) then
local file_size = io.fileSize(RECORD_FILE_PATH)
return {
path = RECORD_FILE_PATH,
size = file_size,
duration = record_duration,
exists = true
}
else
return {
path = RECORD_FILE_PATH,
size = 0,
duration = 0,
exists = false
}
end
end
-- 呼入自動接聽,等待對方掛斷
local function handle_scenario(status)
if status == "INCOMINGCALL" then
-- 獲取來電號碼
caller_number = cc.lastNum() or "未知號碼"
call_counter = call_counter + 1
log.info("收到來電,號碼:", caller_number, "響鈴次數:", call_counter)
-- 響鈴2聲后自動接聽
if call_counter >= 2 then
log.info("自動接聽來電")
cc.accept(0)
call_counter = 0 -- 重置計數器
end
elseif status == "SPEECH_START" then
-- 語音通話真正開始
log.info("電話已接通,電話號碼:", caller_number)
-- 開始通話錄音到文件
start_call_recording()
elseif status == "DISCONNECTED" then
-- 對方掛斷通話
log.info("通話結束對方掛斷")
-- 停止通話錄音到文件
stop_call_recording()
call_counter = 0 -- 重置計數器
end
end
-- ====================== 主事件處理器 ======================
sys.subscribe("CC_IND", function(status)
log.info("CC狀態", status)
handle_scenario(status)
-- 需要處理的通用狀態
if status == "READY" then
sys.publish("CC_READY") -- 發布系統就緒事件
elseif status == "HANGUP_CALL_DONE" or status == "MAKE_CALL_FAILED" or status == "DISCONNECTED" then
exaudio.pm(audio.SHUTDOWN) --主動進入低功耗模式
end
end)
-- ====================== 電話系統初始化 ======================
local function init_cc()
-- 先嘗試掛載SD卡
mount_sd_card()
-- 初始化音頻設備
audio_drv.initAudioDevice()
-- 等待電話系統就緒
sys.waitUntil("CC_READY")
-- 初始化電話功能
cc.init(audio_drv.getMultimediaId())
-- 啟用通話錄音(錄音功能在cc_app中)
enableRecording()
log.info("cc_app", "電話系統初始化完成")
end
-- 啟動初始化任務
sys.taskInit(init_cc)
四、使用注意事項
必須插入SD卡才能使用錄音功能,因為錄音文件較大無法存入內存;緩沖區大小必須是640的倍數,否則可能導致錄音異常。
錄音文件保存在SD卡的/sd/record_call.pcm路徑下,可以通過讀卡器在電腦上查看;錄音文件為原始PCM格式,需要使用專用播放器(如Audacity )播放。

審核編輯 黃宇
-
開源
+關注
關注
3文章
4299瀏覽量
46397 -
LuatOS
+關注
關注
0文章
168瀏覽量
2738
發布評論請先 登錄
WiFi 7 工業遠距離無線新標桿 ——Wallystech 全場景解決方案
通話錄音功能實現:自動接聽 + 自動錄音開源方案
LuatOS中Modbus RTU通信開發指南
LuatOS框架的使用(上)
LuatOS-Air轉LuatOS常見故障排查手冊
五大技術路徑,重塑格局:唯創電子錄音芯片方案引領智能錄音新紀元
數字工牌是什么?數字工牌錄音方案設計
2025年新錄音芯片方案解決錄音設備三大難題
多封裝長錄音:WT2003H系列MP3錄音芯片滿足全場景音頻需求
教程來啦!LuatOS中的消息通信機制詳解及其應用場景
揭秘LuatOS Task:多任務管理的“智能中樞”
唯創錄音芯片五大技術方案,重塑智能錄音市場新格局:自動識別環境噪音、智能調節錄音參數、實現多路并
多種錄音筆錄音芯片方案推薦
芯資訊|廣州唯創電子語音芯片錄音采樣率解析:為多元場景打造聲音解決方案
工業場景通話錄音:LuatOS 開源方案
評論