如何通過OpenHarmony的音頻模塊實現錄音變速功能?

簡介

OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)是由開放原子開源基金會孵化及運營的開源項目,是面向全場景、全連接、全智能時代的智能物聯網操作系統。

多媒體子系統是OpenHarmony系統中的核心子系統,為系統提供了相機、音頻和視頻等多媒體功能。多媒體子系統的音頻模塊、音頻錄音功能可以提供兩套接口,一是由ohos.multimedia.media提供的AudioRecorder接口,能夠直接設置錄音保存的文件路徑,在錄制結束以后自動生成對應的錄音文件,代碼編寫比較簡單;二是由ohos.multimedia.audio提供的AudioCapturer接口,能夠獲得錄音過程中的PCM數據,并對數據進行處理。由于Capturer接口對于原始數據的處理更加靈活,今天就和大家介紹通過Capturer接口實現錄音變速的功能的方法。

效果展示

通過Capturer接口實現音頻的錄制,在錄制過程中對PCM數據進行重采樣實現聲音的快放和慢放。

首先設置錄音加速或者錄音減速,設置完成以后點擊“錄音開始”按鈕進行錄音,點擊“錄音結束”按鈕停止錄音,再通過點擊“播放開始”對錄音的音頻進行播放,播放的音頻是設置后的加速或者減速效果。

目錄結構

調用流程

1.Start的框架層調用流程

2. Read的框架層調用流程

源碼分析

1.首先看一下頁面的布局,主要分為四個模塊:

(1)設置錄音加速

<div style="text-color: aqua;margin-bottom: 20fp;"><text style="font-size: 30fp;">設置錄音加速:</text>
</div><div class="container"><button class="first" type="capsule" onclick="set_5_4">1.25倍速</button><button class="first" type="capsule" onclick="set_6_4">1.5倍速</button>
</div><div class="container"><button class="first" type="capsule" onclick="set_7_4">1.75倍速</button><button class="first" type="capsule" onclick="set_8_4">2倍速</button>
</div>

(2)設置錄音減速

<div style="text-color: aqua;margin-bottom: 20fp;margin-top: 20fp;"><text style="font-size: 30fp;">設置錄音減速:</text>
</div><div class="container"><button class="first" type="capsule" onclick="set_3_4">0.75倍速</button><button class="first" type="capsule" onclick="set_2_4">0.5倍速</button>
</div>

(3)錄音

<div style="text-color: aqua;margin-bottom: 20fp;margin-top: 20fp;"><text style="font-size: 30fp;">錄音:</text>
</div><div class="container"><button class="first" type="capsule" onclick="record">錄音開始</button><button class="first" type="capsule" onclick="recordstop">錄音結束</button>
</div>

(4)播放

<div style="text-color: aqua;margin-bottom: 20fp;margin-top: 20fp;"><text style="font-size: 30fp;">播放:</text>
</div><div class="container"><button class="first" type="capsule" onclick="play">播放開始</button><button class="first" type="capsule" onclick="playstop">播放結束</button>
</div><div class="container"><video if="{{ display }}" id="{{ videoId }}"class="video"src="{{url}}"autoplay="{{ autoplay }}"controls="{{ controlShow }}"muted="false"onseeked="seeked"onprepared="prepared"></video>
</div>

2.邏輯代碼在JS中:

(1)首先通過AudioCapturer接口獲取到PCM數據,再通過調用AudioCapturer的start接口來啟動錄音流程。

globalThis.capturer.start().then(function () {console.log("gyf start");globalThis.capturer.getBufferSize((err, bufferSize) => {if (err) {console.error('gyf getBufferSize error');} else {console.log("gyf bufferSize = " + bufferSize);globalThis.getBuf(bufferSize);}});
});

(2)啟動成功以后,getBuf會調用到getData函數,getData函數通過AudioCapturer的read方法來讀取數據,成功讀取到數據以后,通過handleBuffer函數對數據進行處理。handleBuffer函數的參數arrayBuffer就是通過read方法讀取出來的pcm數據,在handleBuffer中對數據進行了快速播放或者慢速播放的處理。

//循環調用read,進行數據的讀取
handleBuffer(arrayBuffer) {console.log("gyf handleBuffer");let result = new Uint8Array(arrayBuffer);console.log("gyf handleBuffer ================== " + result);let outData = this.test(result, up, down);fileio.writeSync(globalThis.fd, outData.buffer);globalThis.capturer.read(globalThis.bufSize, true).then(this.handleBuffer);
},getData(bufSize) {console.log("gyf getData");globalThis.capturer.read(bufSize, true).then(this.handleBuffer);
},getBuf(bufSize) {console.log("gyf getBuf");this.getData(bufSize);
},

(3)快速播放或者慢速播放是通過up和down兩個方法的組合來實現的,down方法的原理是對PCM數據進行插值處理,在相鄰兩點間插入down個采樣點,up方法的原理是間隔抽取,間隔up個點進行抽取采樣。

up(data, up) {if (1 == up) {return data;}let length = data.byteLength;let upLength = Math.round(length / up);var upData = new Uint8Array(upLength);for (var i = 0, j = 0; i < length; ) {if (j >= upLength) {break;}upData[j] = data[i];i += up;j++;}return upData;
},down(data, down) {if (1 == down) {return data;}let length = data.byteLength;let downLength = Math.round(length * down);var downData = new Uint8Array(downLength);for (var i = 0, j = 0; i < length - 1; ) {for (var k = 0; k < down; k++) {downData[j] = data[i];j++;}i++;}return downData;
},

(4)將down和up的方法組合調用,來實現1.25倍、1.5倍、1.75倍、2倍、0.75倍、0.5倍的速度播放。

test(data, up, down) {let downData = this.down(data, down);let upData = this.up(downData, up);return upData;
},

(5)播放wav格式的音頻文件,采集獲取PCM數據,需要我們根據設置的參數對pcm數據進行添加wav的頭部信息,通過創建AudioCapturer實例的時候設置采集音頻的參數,如采樣率、通道數、采樣格式等。

//音頻采集初始化
var audioStreamInfo = {samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_8000,channels: audio.AudioChannel.CHANNEL_1,sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_U8,encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
}var audioCapturerInfo = {source: audio.SourceType.SOURCE_TYPE_MIC,capturerFlags: 1
}var audioCapturerOptions = {streamInfo: audioStreamInfo,capturerInfo: audioCapturerInfo
}
let that = this;audio.createAudioCapturer(audioCapturerOptions,(err, data) => {if (err) {console.error(`gyf AudioCapturer Created : Error: ${err.message}`);}else {console.info('gyf AudioCapturer Created : Success : SUCCESS');that.capturer = data;}
});

(6)根據這些參數設置的信息需要對wav文件寫入文件頭,頭信息一般包含44個字節,里面需要設置三個chunk的信息(RIFF chunk、fmt chunk、data chunk),具體的信息可以查看官網的介紹WAV文件格式介紹:

//假設數據為1000秒鐘的時間(8000 * 1000)
encodeWAV() {var dataLen = 8000000;var sampleRate = 8000;var sampleBits = 8;var buffer = new ArrayBuffer(44);var data = new DataView(buffer);var channelCount = 1;   // 單聲道var offset = 0;// 資源交換文件標識符this.writeString(data, offset, 'RIFF'); offset += 4;// 下個地址開始到文件尾總字節數,即文件大小-8data.setUint32(offset, 36 + dataLen, true); offset += 4;// WAV文件標志this.writeString(data, offset, 'WAVE'); offset += 4;// 波形格式標志this.writeString(data, offset, 'fmt '); offset += 4;// 過濾字節,一般為 0x10 = 16data.setUint32(offset, 16, true); offset += 4;// 格式類別 (PCM形式采樣數據)data.setUint16(offset, 1, true); offset += 2;// 通道數data.setUint16(offset, channelCount, true); offset += 2;// 采樣率,每秒樣本數,表示每個通道的播放速度data.setUint32(offset, sampleRate, true); offset += 4;// 波形數據傳輸率 (每秒平均字節數) 單聲道×每秒數據位數×每樣本數據位/8data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;// 快數據調整數 采樣一次占用字節數 單聲道×每樣本的數據位數/8data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;// 每樣本數據位數data.setUint16(offset, sampleBits, true); offset += 2;// 數據標識符this.writeString(data, offset, 'data'); offset += 4;// 采樣數據總數,即數據總大小-44data.setUint32(offset, dataLen, true); offset += 4;return data;
},

總結

本文介紹了通過使用OpenHarmony音頻模塊的AudioCapturer接口實現錄音功能。AudioCapturer接口對于原始數據的處理非常靈活,能夠對采集的數據進行插值/抽值的重采樣處理,并將處理后的音頻處理保存至本地文件。由于本地文件使用的是WAV格式,故在寫數據前需要對WAV文件進行頭部信息的添加,這些信息可以根據創建AudioCapturer時設置的參數來進行設置,以此保證頭部信息的準確性,最后再通過應用層的video組件對音頻數據進行播放。

為了幫助到大家能夠更有效的學習OpenHarmony 開發的內容,下面特別準備了一些相關的參考學習資料:

OpenHarmony 開發環境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源碼解析》:https://qr18.cn/CgxrRy

  • 搭建開發環境
  • Windows 開發環境的搭建
  • Ubuntu 開發環境搭建
  • Linux 與 Windows 之間的文件共享
  • ……

系統架構分析:https://qr18.cn/CgxrRy

  • 構建子系統
  • 啟動流程
  • 子系統
  • 分布式任務調度子系統
  • 分布式通信子系統
  • 驅動子系統
  • ……

OpenHarmony 設備開發學習手冊:https://qr18.cn/CgxrRy

在這里插入圖片描述

OpenHarmony面試題(內含參考答案):https://qr18.cn/CgxrRy

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/bicheng/14827.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14827.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14827.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2024-05-24 問AI: 在深度學習中,什么是L1 正則化和L2 正則化?

文心一言 在深度學習和機器學習中&#xff0c;正則化是一種用于防止模型過擬合的技術。它通過向損失函數添加一個懲罰項來實現&#xff0c;這個懲罰項與模型的復雜度有關。正則化項可以看作是對模型參數的約束&#xff0c;使得模型在訓練時不僅關注于最小化訓練誤差&#xff0…

Java入門基礎學習筆記43——包

什么是包&#xff1f; 包是用來分門別類的管理各種不同程序的&#xff0c;類似文件夾&#xff0c;建包有利于程序的管理和維護。 建包的語法規則&#xff1a; package cn.ensource.javabean;public class Car() {} 在自己的程序中調用其他包下的程序的注意事項&#xff1a; 1…

動手學深度學習——層和塊

1. 層 層是一個將輸入數據轉換為輸出數據的神經網絡組件。每個層都會對輸入數據進行一定的操作&#xff0c;例如線性變換、非線性激活函數等&#xff0c;以產生輸出數據。 torch.nn模塊提供了各種預定義的層&#xff0c;如線性層、卷積層、池化層等&#xff0c; nn.Linear&a…

BLE學習筆記(0.0) —— 基礎概念(0)

前言 &#xff08;1&#xff09;本章節主要是對BLE技術進行簡單的介紹&#xff0c;熟悉藍牙技術的發展過程&#xff0c;了解相關術語方便后續的學習。 &#xff08;2&#xff09;為了防止單篇博客太長以至于看不下去&#xff0c;因此我基礎概念章節分為兩篇來寫。 &#xff08;…

直播回放| 機器人任務挑戰賽線上培訓資料合集

大賽培訓回顧 5月22日&#xff0c;卓翼飛思實驗室為全國各賽區精心組織的機器人任務挑戰賽&#xff08;無人協同系統&#xff09;線上培訓第三期順利落下帷幕&#xff0c;吸引300余人參與。本次培訓主要針對仿真平臺的基本使用&#xff0c;從仿真平臺獲取激光雷達/視覺數據&am…

Mysql教程(0):學習框架

1、Mysql簡介 MySQL 是一個開放源代碼的、免費的關系型數據庫管理系統。在 Web 開發領域&#xff0c;MySQL 是最流行、使用最廣泛的關系數據庫。MySql 分為社區版和商業版&#xff0c;社區版完全免費&#xff0c;并且幾乎能滿足全部的使用場景。由于 MySQL 是開源的&#xff0…

選擇排序,改進冒泡排序,快速排序的查找和計數排序

簡單選擇排序 數據結構:單鏈表 實現方法:n為鏈表長度, 第1趟先選出1到n-1個元素中的最小值和0號元素交換, 第2趟從2到n-1號元素選出最小值和1號元素交換, … 第n-2趟從n-2到n-1號元素中選出最小值和n-2號元素交換. 第n-1趟n-1號元素即為最小值。比較結束。 代碼:…

1075: 求最小生成樹(Prim算法)

解法&#xff1a; 總結起來&#xff0c;Prim算法的核心思想是從一個頂點開始&#xff0c;一步一步地選擇與當前最小生成樹相鄰的且權值最小的邊&#xff0c;直到覆蓋所有的頂點&#xff0c;形成一個最小生成樹。 #include<iostream> #include<vector> using names…

算法-跳馬

bfs類的應用題。 解法&#xff1a; 每一個點都可能作為匯集的那個點&#xff0c;因此采用遍歷的方式&#xff0c;對每個點進行處理&#xff0c;得出每個點的“所有馬跳到本點的最小步數和“&#xff0c;取最小值即可。 邏輯1&#xff1a;以該點作為源點出發&#xff0c;求處…

springboot基于Web前端技術的java養老院管理系統_utbl7

3.普通用戶模塊包括&#xff1a;普通會員的注冊、養老院客房查詢、養老院留言查詢、預約老人基本信息登記、選擇房間、用戶繳費的功能。 4.數據信息能夠及時進行動態更新&#xff0c;增刪&#xff0c;用戶搜素方便&#xff0c;使用戶可以直接瀏覽相關信息&#xff0c;要考慮便于…

Vue3實戰筆記(35)—集成炫酷的粒子特效

文章目錄 前言一、vue3使用tsparticles二、使用步驟總結 前言 學習一個有趣炫酷的玩意開心一下。 tsparticles&#xff0c;可以方便的實現各種粒子特效。支持的語言框架也是相當的豐富. 官網&#xff1a;https://particles.js.org/ 一、vue3使用tsparticles 先來個vue3使用…

Go 語言逃逸分析:內存管理的關鍵

文章目錄 前言1 逃逸分析是什么&#xff1f;2 逃逸分析的基本思想是什么&#xff1f;3 逃逸分析的分配原則是什么&#xff1f;4 如何進行逃逸分析&#xff1f;5 逃逸分析案例5.1 變量在函數外存在引用5.2 引用類型的逃逸5.3 閉包捕獲變量5.4 變量占用內存較大 6 變量會逃逸到堆…

代碼隨想錄訓練營打卡第36天:動態規劃解決子序列問題

1.300最長遞增子序列 1.問題描述 找到其中最長嚴格遞增子序列的長度。 子序列 是由數組派生而來的序列&#xff0c;刪除&#xff08;或不刪除&#xff09;數組中的元素而不改變其余元素的順序。 2.問題轉換 從nums[0...i]的最長的遞增的子序列 3.解題思路 每一個位置的n…

經濟學問題

問題1 1916年&#xff0c;福特汽車公司以440美元的價格生產了50萬輛T型福特汽車。該公司當年盈利6000萬美元。亨利福特告訴一位報紙記者&#xff0c;他打算把T型車的價格降至360美元&#xff0c;他希望在這個價格上能賣出80萬輛汽車。福特說&#xff1a;“每輛車的利潤減少&am…

Flutter 中的 CupertinoPicker 小部件:全面指南

Flutter 中的 CupertinoPicker 小部件&#xff1a;全面指南 在Flutter中&#xff0c;CupertinoPicker是一個用于創建iOS風格的選擇器的組件&#xff0c;它允許用戶通過滾動來選擇一個值。CupertinoPicker可以用于選擇日期、時間或者任何可枚舉的值。本文將詳細介紹CupertinoPi…

C++多態詳解

目錄 一、多態的概念 二、多態的定義及實現 1.多態的構成條件 2.虛函數 3.虛函數的重寫 4.例題理解&#xff08;超級重要&#xff0c;強烈建議做一下&#xff09; 5.C11 override和 final 6.重載、覆蓋&#xff08;重寫&#xff09;、隱藏&#xff08;重定義&#xff0…

【yijiej】mysql報錯 之 報錯:Duplicate entry 字段 for key ‘表名.idx_字段’

一、問題操作 Mysql 進行insert 操作&#xff0c;報錯&#xff1a;Duplicate entry 字段 for key ‘表名.idx_字段’ 原因解析&#xff1a;idx 是做的索引鍵&#xff0c;是具有唯一性二、問題原因&#xff08;三種情況&#xff0c;當前我遇到的情況是第一種&#xff09; 1、當 …

零基礎代碼隨想錄【Day42】|| 1049. 最后一塊石頭的重量 II,494. 目標和,474.一和零

目錄 DAY42 1049.最后一塊石頭的重量II 解題思路&代碼 494.目標和 解題思路&代碼 474.一和零 解題思路&代碼 DAY42 1049.最后一塊石頭的重量II 力扣題目鏈接(opens new window) 題目難度&#xff1a;中等 有一堆石頭&#xff0c;每塊石頭的重量都是正整…

(Qt) 默認QtWidget應用包含什么?

文章目錄 ?前言?創建&#x1f6e0;?選擇一個模板&#x1f6e0;?Location&#x1f6e0;?構建系統&#x1f6e0;?Details&#x1f6e0;?Translation&#x1f6e0;?構建套件(Kit)&#x1f6e0;?匯總 ?項目??概要??構建步驟??清除步驟 ?Code&#x1f526;untitled…

【EasyX】快速入門——消息處理,音頻

1.消息處理 我們先看看什么是消息 1.1.獲取消息 想要獲取消息,就必須學會getmessage函數 1.1.1.getmessage函數 有兩個重載版本,它們的作用是一樣的 參數filter可以篩選我們需要的消息類型 我們看看參數filter的取值 當然我們可以使用位運算組合這些值 例如,我們…