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.mzph.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;满足他们在车间自动化和连接…

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…

华为端云一体化开发 (起步1.0)(HarmonyOS学习第七课)

官方文献&#xff1a; 为丰富HarmonyOS对云端开发的支持、实现端云联动&#xff0c;DevEco Studio推出了云开发功能&#xff0c;开发者在创建工程时选择云开发模板&#xff0c;即可在DevEco Studio内同时完成HarmonyOS应用/元服务的端侧与云侧开发&#xff0c;体验端云一体化协…

论文代码解读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;确保所有数据都…

「动态规划」如何求地下城游戏中,最低初始健康点数是多少?

174. 地下城游戏https://leetcode.cn/problems/dungeon-game/description/ 恶魔们抓住了公主并将她关在了地下城dungeon的右下角。地下城是由m x n个房间组成的二维网格。我们英勇的骑士最初被安置在左上角的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公主。骑士…

【Text2SQL 论文】C3:使用 ChatGPT 实现 zero-shot Text2SQL

论文&#xff1a;C3: Zero-shot Text-to-SQL with ChatGPT ⭐⭐⭐⭐ arXiv:2307.07306&#xff0c;浙大 Code&#xff1a;C3SQL | GitHub 一、论文速读 使用 ChatGPT 来解决 Text2SQL 任务时&#xff0c;few-shots ICL 的 setting 需要输入大量的 tokens&#xff0c;这有点昂贵…

MacOS M系列芯片一键配置多个不同版本的JDK

第一步&#xff1a;下载JDK。 官网下载地址&#xff1a;Java Archive | Oracle 选择自己想要下载的版本&#xff0c;一般来说下载一个jdk8和一个jdk11就够用了。 M系列芯片选择这两个&#xff0c;第一个是压缩包&#xff0c;第二个是dmg可以安装的。 第二步&#xff1a;编辑…

eclipse插件开发(二)RCP第三方库的引入方式

RCP第三方库的引入 最近在RCP开发过程中遇到JSON串与对象互转的问题&#xff0c;如何像spring开发模式一样引入第三方库呢&#xff1f;eclipse插件开发中用到p2库&#xff0c;但也支持maven库的引入。关键在于.target这个关键文件。 .target 文件用于定义一个目标平台&#x…

民主测评要做些什么?

民主测评&#xff0c;作为一种重要的民主管理工具&#xff0c;旨在通过广泛征求群众意见&#xff0c;对特定对象或事项进行客观、公正的评价。它不仅是推动民主参与、民主监督的重要手段&#xff0c;也是提升治理效能、促进社会和谐的有效途径。以下将详细介绍民主测评的主要过…

如何以非交互方式将参数传递给交互式脚本

文章目录 问题回答1. 使用 Here Document2. 使用 echo 管道传递3. 使用文件描述符4. 使用 expect 工具 参考 问题 我有一个 Bash 脚本&#xff0c;它使用 read 命令以交互方式读取命令参数&#xff0c;例如 yes/no 选项。是否有一种方法可以在非交互式脚本中调用这个脚本&…