C++結合ffmpeg獲取聲音的分貝值

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔

文章目錄

  • 前言
  • 一、分貝是什么?
    • 1.功率量
    • 2.場量
  • 二、實際操作
    • 1.分析wav文件
    • 2.讀取麥克風
  • 總結


前言

最近面對一個需求,就是需要傳遞聲音文件到模型里推理完成語音轉文字,問題是我們使用的是麥克風啊,由于這個特殊屬性就需要有一個合理的方法來判斷聲音的開始,聲音的結束和聲音的長度。像科大訊飛這樣的庫已經有這個功能了,如果遇到沒有這個功能的怎么辦,還得靠自己。

方法其實有很多,我們這里使用根據分貝來判斷,首先就需要獲取到分貝。


一、分貝是什么?

分貝(decibel)是量度兩個相同單位之數量比例的單位,常用dB表示。“分”(deci-)指十分之一,個位是“貝”或“貝爾”(bel,紀念發明家亞歷山大·格拉漢姆·貝爾),但一般只用分貝。

計算分貝的方法有功率量場量兩種方式,功率量這種方式我還沒有完全研究透,我先闡述下概念,等我研究透了再補充具體代碼。我們這里先介紹場量這種方式,再給出具體的代碼。

1.功率量

功率量(power quantity)是功率值或者直接與功率值成比例的其它量,如能量密度音強發光強度等。

考慮功率或者強度(intensity)時,其比值可以表示為分貝,這是通過把測量值參考量值之比計算基于10的對數,再乘以10。因此功率值P1與另一個功率值P0之比用分貝表示為LdB:

在這里插入圖片描述
兩個功率值的比值基于10的對數,就是貝爾(bel)值。兩個功率值之比的分貝值是貝爾值的1/10倍(或者說,1個分貝是十分之一貝爾)。P1與P0必須度量同一個數值類型,具有相同的單位。如果在上式中P1 = P0,那么LdB = 0。如果P1大于P0,那么LdB是正的;如果P1小于P0,那么LdB是負的。

重新安排上式可得到計算P1的公式,依據P0與LdB:

在這里插入圖片描述

因為貝爾是10倍的分貝,對應的使用貝爾(LB)的公式為:

在這里插入圖片描述

2.場量

場量(field quantity)是諸如電壓電流聲壓電場強度速度電荷密度等量值,其平方值在一個線性系統中與功率成比例。

考慮到場(field)幅值(amplitude)時,通常使用A1(度量到的幅值)的平方A0(參考幅值)的平方之比。這是因為對于大多數應用,功率與幅值的平方成比例,并期望對同一應用采取功率計算的分貝與用場的幅值計算的分貝相等。因此使用下述場量的分貝定義:

在這里插入圖片描述

上述公式可寫成:

在這里插入圖片描述

總結:拿到聲音的幅值和參考幅值以10為底求對數,然后再乘以20就是最終的分貝數了。為了方便闡述這里以單聲道、16000HZ采樣率、16bits小端為示例。

注意:以下所有的都是基于上面的參數來闡述的,不懂得請先補習下知識,要不然后面得就完全看不懂了。

二、實際操作

由于讀取麥克風和分析文件完全不一樣,這里就分開講。

1.分析wav文件

先說下RIFF,這個是分析文件必備的知識。

RIFF,全稱Resource Interchange File Format(資源交換文件格式),是一種元文件格式(meta-format),設計用于高效地存儲和交換多媒體數據,如音頻、視頻、圖像以及相關元數據。該格式由Microsoft和IBM于1991年聯合推出,主要用于當時的Windows 3.1操作系統,并成為其默認的多媒體文件格式。

就是說我們以.wav文件為示例,這種文件都是符合RIFF規范的。下面的圖片是.wav文件典型的存儲格式:
在這里插入圖片描述

wav文件主要包含三個chunk:riff chunk、format chunk、data chunk,這三個是缺一不可的。我簡單寫了個解析wav的算法我覺得還不完善,所以暫時不放出來了,因為ffmpeg已經自帶解析算法了,也不需要再單獨寫一個,感興趣的可以私下研究下。這里我用ffmpeg的代碼來演示:

main.cpp

#include <iostream>
#include <cstdio>extern "C" {
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
}void read_file_ffmpeg(const char *filename) {av_register_all();avformat_network_init();AVFormatContext *formatContext = nullptr;if (avformat_open_input(&formatContext, filename, nullptr, nullptr) != 0) {fprintf(stderr, "Could not open file.\n");return;}if (avformat_find_stream_info(formatContext, nullptr) < 0) {fprintf(stderr, "Failed to retrieve stream info.\n");return;}int audioStreamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);if (audioStreamIndex < 0) {fprintf(stderr, "No audio stream found.\n");return;}AVCodecContext *codecContext = formatContext->streams[audioStreamIndex]->codec;AVCodec *codec = avcodec_find_decoder(codecContext->codec_id);if (!codec) {fprintf(stderr, "Codec not found.\n");return;}if (avcodec_open2(codecContext, codec, nullptr) < 0) {fprintf(stderr, "Could not open codec.\n");return;}AVPacket packet;AVFrame *frame = av_frame_alloc();//check max dbdouble max_db = 0;while (av_read_frame(formatContext, &packet) >= 0) {if (packet.stream_index == audioStreamIndex) {int ret;// 發送數據包到解碼器ret = avcodec_send_packet(codecContext, &packet);if (ret < 0) {fprintf(stderr, "Error sending a packet for decoding.\n");break;}// 獲取解碼后的幀while (true) {ret = avcodec_receive_frame(codecContext, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {fprintf(stderr, "Error during decoding.\n");break;}// 解碼成功,處理frame->data和frame->linesize中的音頻數據
//                std::cout << frame->linesize[0] << std::endl;uint8_t *sampleValues = frame->data[0];for (int k = 0; k < frame->linesize[0]; k = k + 2) {uint16_t u_sample = sampleValues[k] | (sampleValues[k + 1] << 8);auto sample = (int16_t) u_sample;
//                    std::cout << sample << ',';double dB = 20 * log10(abs(sample));std::cout << dB << ',';if (dB > max_db) {max_db = dB;}
//                        if (dB > DB_SAMPLE) {
//                            std::cout << "people talk" << std::endl;
//                        }}std::cout << "---------------" << std::endl;}}av_packet_unref(&packet);}std::cout << "max_db: " << max_db << std::endl;av_frame_free(&frame);avcodec_close(codecContext);avformat_close_input(&formatContext);
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(read_microphone)set(CMAKE_CXX_STANDARD 11)
#set(CMAKE_CXX_COMPILER /usr/bin/g++-11)
#set(CMAKE_C_COMPILER /usr/bin/gcc-11)find_package(PkgConfig REQUIRED)pkg_check_modules(ffmpeg_lib REQUIRED IMPORTED_TARGET libavformat libavutil libavdevice libavcodec)
add_executable(ffmpeg_bin main.cpp)
target_link_libraries(ffmpeg_bin PkgConfig::ffmpeg_lib)

執行成功了輸出分貝值:

25.1055,20,20,18.0618,25.1055,6.0206,16.902,23.5218,33.442,31.8213,33.0643,34.4855,35.8478,33.9794,31.8213,33.6248,34.8073,36.2583,34.8073,35.563,32.2

這個結果的參考幅值是1而不是32767,我查了下幅值計算分貝是沒有標準參考值的,因參考值的不同結果也會不同。不過這并不影響我們對結果的判斷。

注意:一定要用我說的那種wav文件,因為市面上主流的語音識別都是基于16000hz,單聲道,16bits小端處理的。雙聲道除了增加了性能消耗和復雜度幾乎沒有特別的意義,而且過高的采樣率并不利于人聲識別,反而增加背景噪聲。

2.讀取麥克風

同樣使用ffmpeg,只不過讀出來的數據不是wav,而是pcm格式,不用對數據進行特別的解碼,直接拿來用即可。

main.cpp

#include <iostream>
#include <cstdio>
#include <fstream>extern "C" {
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
}/*** @author arnold* @brief use alsa and default device* */
void read_microphone() {//    av_register_all();//ffmpeg 3.x versionavdevice_register_all();AVFormatContext *fmt_ctx = nullptr;AVInputFormat *input_fmt = av_find_input_format("alsa"); // 音頻設備的輸入格式,如alsa、pulse等const char *dev_name = "default"; // microphone device nameAVDictionary *format_opts = nullptr;//set stream format optionsav_dict_set(&format_opts, "sample_rate", "16000", 0);//set audio sampleav_dict_set(&format_opts, "channels", "1", 0);//set audio channelav_dict_set(&format_opts, "fragment_size", "256", 0);//set audio fragment size// open audio deviceif (avformat_open_input(&fmt_ctx, dev_name, input_fmt, &format_opts) != 0) {printf("can't open input device!\n");if (format_opts)av_dict_free(&format_opts);return;}if (format_opts)av_dict_free(&format_opts);//Output Info---printf("---------------- File Information ---------------\n");av_dump_format(fmt_ctx, 0, dev_name, 0);printf("-------------------------------------------------\n");// find audio stream infoif (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {printf("can't get audio stream info!\n");return;}int audio_stream_idx = -1;// find audio stream indexfor (int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {audio_stream_idx = i;break;}}if (audio_stream_idx == -1) {printf("can't find audio stream index!\n");return;}//write pcm data
//    std::ofstream ofs("../audio/test.pcm");AVPacket packet;while (av_read_frame(fmt_ctx, &packet) >= 0) {if (packet.stream_index == audio_stream_idx) {std::cout << "packet size: " << packet.size << std::endl;std::cout << "packet duration: " << packet.duration << std::endl;
//            ofs.write((char *) packet.data, packet.size);
//            ofs.flush();for (int i = 0; i < packet.size; i = i + 2) {uint16_t u_sample = packet.data[i] | (packet.data[i + 1] << 8);auto sample = (int16_t) u_sample;
//                std::cout << sample << ',';double dB = 20 * log10(abs(sample));if (dB > 60){std::cout << dB << ',';}}std::cout << std::endl;}av_packet_unref(&packet);}avformat_close_input(&fmt_ctx);
//    ofs.close();
}int main() {read_microphone();return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(read_microphone)set(CMAKE_CXX_STANDARD 11)
#set(CMAKE_CXX_COMPILER /usr/bin/g++-11)
#set(CMAKE_C_COMPILER /usr/bin/gcc-11)find_package(PkgConfig REQUIRED)pkg_check_modules(ffmpeg_lib REQUIRED IMPORTED_TARGET libavformat libavutil libavdevice libavcodec)
add_executable(ffmpeg_bin main.cpp)
target_link_libraries(ffmpeg_bin PkgConfig::ffmpeg_lib)

總結

1、代碼完全基于單聲道音頻,沒對多聲道進行處理,理論上除了參考值不同對多聲道音頻也是能處理的
2、注意出來的分貝值不一定等于分貝計測出來的,分貝計很可能是基于功率強度或聲壓強度來來測試結果,我們依據的是幅值,原理還是不一樣的。

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

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

相關文章

鏈表的回文結構OJ

鏈表的回文結構_牛客題霸_牛客網對于一個鏈表&#xff0c;請設計一個時間復雜度為O(n),額外空間復雜度為O(1)的算法&#xff0c;判斷其是否為。題目來自【牛客題霸】https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId49&&tqId29370&rp1&a…

CodeMeter助力Hilscher,推動實現全球智能制造連接解決方案

Hilscher的旗艦店為開放工業4.0聯盟&#xff08;OI4&#xff09;社區提供了應用商店的便捷和開放性&#xff0c;將這一概念引入工業領域。該商店依托CodeMeter的許可證管理和加密保護&#xff0c;為工業用戶提供了豐富的應用和解決方案庫&#xff0c;滿足他們在車間自動化和連接…

2020年06月C語言二級真題

計算矩陣邊緣元素之和 題目描述 輸入一個整數矩陣&#xff0c;計算位于矩陣邊緣的元素之和。 所謂矩陣邊緣的元素&#xff0c;就是第一行和最后一行的元素以及第一列和最后一列的元素。 輸入格式 第一行分別為矩陣的行數n和列數m&#xff0c;兩者之間以一個空格分開。 接下來輸…

WPF中讀取Excel文件的內容

演示效果 實現方案 1.首先導入需要的Dll(這部分可能需要你自己搜一下) Epplus.dll Excel.dll ICSharpCode.SharpZipLib.dll 2.在你的解決方案的的依賴項->添加引用->瀏覽->選擇1中的這幾個Dll點擊確定。(添加依賴) 3.然后看代碼內容 附上源碼 using Excel; usi…

計網復習資料

一、選擇題&#xff08;每題2分&#xff0c;共40分&#xff09; 1. Internet 網絡本質上屬于&#xff08; &#xff09;網絡。 A.電路交換 B.報文交換 C.分組交換 D.虛電路 2.在 OSI 參考模型中,自下而上第一個提供端到端服務的是( )。 A.數據鏈路層 B.傳輸…

Thinkphp使用Elasticsearch查詢

在Thinkphp中調用ES&#xff0c;如果自己手寫json格式的query肯定是很麻煩的。我這里使用的是ONGR ElasticsearchDSL 構建 ES 查詢。ongr ElasticsearchDSL 的開源項目地址&#xff1a;GitHub - ongr-io/ElasticsearchDSL: Query DSL library for Elasticsearch。ONGR Elastics…

100V 15A TO-252 N溝道MOS管 HC070N10L 惠海

MOS管的工作原理是基于在P型半導體與N型半導體之間形成的PN結&#xff0c;通過改變柵極電壓來調整溝道內載流子的數量&#xff0c;從而改變溝道電阻和源極與漏極之間的電流大小。由于MOS管具有輸入電阻高、噪聲小、功耗低等優點&#xff0c;它們在大規模和超大規模集成電路中得…

package.json中resolutions的使用場景

文章目錄 用途配置示例使用方法注意事項和peerDependencies有什么不同peerDependenciesresolutions 總結 ?創作者&#xff1a;全棧弄潮兒 &#x1f3e1; 個人主頁&#xff1a; 全棧弄潮兒的個人主頁 &#x1f3d9;? 個人社區&#xff0c;歡迎你的加入&#xff1a;全棧弄潮兒的…

git【工具軟件】分布式版本控制工具軟件

一、Git 的介紹 git軟件的作用&#xff1a;管理軟件開發項目中的源代碼文件。 常用功能&#xff1a; 倉庫管理、文件管理、分支管理、標簽管理、遠程操作 功能指令&#xff1a; add&#xff0c;commit&#xff0c;log&#xff0c;branch&#xff0c;tag&#xff0c;remote…

Ubuntu Linux LTS 24.04 AMD64 桌面版安裝記錄

下載iso aria2c -x 4 -s 12 "https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/24.04/ubuntu-24.04-desktop-amd64.iso" "https://mirrors.163.com/ubuntu-releases/24.04/ubuntu-24.04-desktop-amd64.iso" "https://mirrors.zju.edu.cn/ubuntu…

[pyradiomics][python]pyradiomics所有whl文件下載地址匯總

源碼地址&#xff1a;https://github.com/AIM-Harvard/pyradiomics pyradiomics是一個開源的Python軟件包&#xff0c;專門用于從醫學影像中提取高通量的定量特征&#xff0c;這些特征被稱為影像組學(Radiomics)特征。以下是關于pyradiomics的詳細介紹&#xff1a; 一、基本概…

華為端云一體化開發 (起步1.0)(HarmonyOS學習第七課)

官方文獻&#xff1a; 為豐富HarmonyOS對云端開發的支持、實現端云聯動&#xff0c;DevEco Studio推出了云開發功能&#xff0c;開發者在創建工程時選擇云開發模板&#xff0c;即可在DevEco Studio內同時完成HarmonyOS應用/元服務的端側與云側開發&#xff0c;體驗端云一體化協…

大數據面試題第二期*6

題1、Namenode掛了怎么辦? 方法一&#xff1a;將SecondaryNameNode中數據拷貝到namenode存儲數據的目錄。 方法二&#xff1a;使用importCheckpoint選項啟動namenode守護進程&#xff0c;從而將SecondaryNameNode中數據拷貝到namenode目錄中。 題2、Hadoop 的namenode 宕機怎么…

論文代碼解讀STPGNN

1.前言 本次代碼文章來自于《2024-AAAI-Spatio-Temporal Pivotal Graph Neural Networks for Traffic Flow Forecasting》&#xff0c;基本模型結構如下圖所示&#xff1a; 文章講解視頻鏈接 代碼開源鏈接 接下來就開始代碼解讀了。 2.代碼解讀 class nconv(nn.Module):de…

NDIS Filter開發-網絡數據的傳輸

和NIC小端口驅動不同的是&#xff0c;無需考慮網絡數據具體是如何傳輸的&#xff0c;只需要針對NBL進行處理即可。Filter驅動程序可以啟動發送請求和接收指示&#xff0c;或“過濾”其他驅動程序的請求和指示。Filter模塊堆疊在微型端口適配器上。 驅動程序堆棧中的Filter模塊…

谷粒商城實戰(033 業務-秒殺功能4-高并發問題解決方案sentinel 1)

Java項目《谷粒商城》架構師級Java項目實戰&#xff0c;對標阿里P6-P7&#xff0c;全網最強 總時長 104:45:00 共408P 此文章包含第326p-第p331的內容 關注的問題 sentinel&#xff08;哨兵&#xff09; sentinel來實現熔斷、降級、限流等操作 騰訊開源的tendis&#xff0c…

ctfshow web

【nl】難了 <?php show_source(__FILE__); error_reporting(0); if(strlen($_GET[1])<4){echo shell_exec($_GET[1]); } else{echo "hack!!!"; } ?> //by Firebasky //by Firebasky ?1>nl //先寫個文件 ?1*>b //這樣子會把所有文件名寫在b里…

JSON 無法序列化

JSON 無法序列化通常出現在嘗試將某些類型的數據轉換為 JSON 字符串時&#xff0c;這些數據類型可能包含不可序列化的內容。 JSON 序列化器通常無法處理特定類型的數據&#xff0c;例如日期時間對象、自定義類實例等。在將數據轉換為 JSON 字符串之前&#xff0c;確保所有數據都…

clickhouse學習筆記(三)常見表引擎

目錄 一、 MergeTree系列引擎 1、MergeTree 數據TTL &#xff08;1&#xff09; 列級別 TTL &#xff08;2&#xff09; 表級別 TTL 存儲策略 2、ReplacingMergeTree 3、CollapsingMergeTree 4、VersionedCollapsingMergeTree 5、SummingMergeTree 6、AggregatingMe…

「動態規劃」如何求地下城游戲中,最低初始健康點數是多少?

174. 地下城游戲https://leetcode.cn/problems/dungeon-game/description/ 惡魔們抓住了公主并將她關在了地下城dungeon的右下角。地下城是由m x n個房間組成的二維網格。我們英勇的騎士最初被安置在左上角的房間里&#xff0c;他必須穿過地下城并通過對抗惡魔來拯救公主。騎士…