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.rhkb.cn/news/343367.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CTFHUB-技能树-web-信息泄露

目录 1.目录遍历 2.PHPINFO 3.备份文件下载 3.1 网站源码 3.2 bak文件 3.3 vim缓存 3.4 .DS_Store 4.Git泄露 4.1 Log 4.2 Stash 4.3 Index 5.SVN泄露 6.HG泄露 1.目录遍历 这个没什么好讲的&#xff0c;进去直接点击找flag,然后在下面目录翻&#xff0c;就找到了 …

【Vue】路由介绍

一、引入 思考 单页面应用程序&#xff0c;之所以开发效率高&#xff0c;性能好&#xff0c;用户体验好 最大的原因就是&#xff1a;页面按需更新 比如当点击【发现音乐】和【关注】时&#xff0c;只是更新下面部分内容&#xff0c;对于头部是不更新的 要按需更新&#xff…

mysql 8 linux7,8安装教程

选择自己对应的linux版本 cat /etc/os-release //查看自己linux系统版本 1.mysql下载地址 MySQL :: Download MySQL Community Server (Archived Versions) 拉到下面找到 选择自己linux指定的版本&#xff0c;否则会很麻烦 cat /etc/os-release //查看系统版本 2.查…

抱抱脸上第一的开原模型Qwen2-72B;腾讯开源人像照片生成视频的模型;Facebook开源翻译模型;智谱 AI 推出的最新一代预训练模型

✨ 1: Qwen2 Qwen2 是一种多语言预训练和指令调优的语言模型&#xff0c;支持128K上下文长度并在多项基准测试中表现优异。 Qwen2&#xff08;全称“Qwen Qwen”&#xff0c;简称Qwen&#xff09;是一个先进的大语言模型家族&#xff0c;在其前身Qwen1.5的基础上进行了重大提…

读取文件

自学python如何成为大佬(目录):自学python如何成为大佬(目录)_利用python语言智能手机的默认语言实战一-CSDN博客 在Python中打开文件后&#xff0c;除了可以向其写入或追加内容&#xff0c;还可以读取文件中的内容。读取文件内容主要分为以下几种情况&#xff1a; 1 读取指…

【人工智能】流行且重要的智能算法整理

✍&#x1f3fb;记录学习过程中的输出&#xff0c;坚持每天学习一点点~ ❤️希望能给大家提供帮助~欢迎点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;指点&#x1f64f; 小记&#xff1a; 今天在看之前写的文档时&#xff0c;发现有人工智能十大算法的内容&#xf…

wireshark 二次开发

一、 Windows 准备 1、源代码下载 Git&#xff1a;https://github.com/wireshark/wireshark 2、 准备Visual C 要编译wireshark&#xff0c;开发电脑上应该安装了Visual Studio并包括了Visual C&#xff0c;请至少安装Visual Studio 2010以减少不必要的麻烦。 visual studio …

【ffmpeg】本地格式转换 mp4转wav||裁剪mp4

个人感受&#xff1a;太爽了&#xff01;&#xff01;&#xff01;&#xff08;可能用惯了转换网站和无良的转换软件&#xff09; ———— 使用FFmpeg把mp4文件转换为WAV文件 - 简书 (jianshu.com) FFMPEG 视频分割和合并 - 简书 (jianshu.com) ———— 示例 ffmpeg -i …

具有 MOSFET 的电压到电流 (V-I) 转换器电路

设计说明 该单电源、低侧、V-I 转换器向可以连接到比运算放大器电源电压更高的电压的负载提供经过良好调节的电流。该 电路接受介于 0V 和 2V 之间的输入电压&#xff0c;将其转换为介于 0mA 和 100mA 之间的电流。通过将低侧电流检测电 阻 R3 上的压降反馈到运算放大器的反相…

C语言 指针——函数指针的典型应用:通用排序

目录 编程实现升序和降序排序 如果不使用函数指针编程… 使用函数指针编写一个通用的排序函数 小结 编程实现升序和降序排序 如果不使用函数指针编程… 使用函数指针编写一个通用的排序函数 小结 正确理解指针的概念  指针是一种特殊的数据类型  指针类型的变量&am…

【多模态】37、TextSquare | 借助 Gemini-Pro 通过四个步骤来生成高质量的文本问答数据

文章目录 一、背景二、方法2.1 Square-10M2.2 模型结构2.3 使用 Square-10M 进行有监督微调 三、效果3.1 实验设置3.2 Benchmark 测评 论文&#xff1a;TextSquare: Scaling up Text-Centric Visual Instruction Tuning 代码&#xff1a;暂无 出处&#xff1a;字节 | 华中科技…

自动驾驶仿真(高速道路)LaneKeeping

前言 A high-level decision agent trained by deep reinforcement learning (DRL) performs quantitative interpretation of behavioral planning performed in an autonomous driving (AD) highway simulation. The framework relies on the calculation of SHAP values an…

WPF国际化的最佳实践

WPF国际化的最佳实践 1.创建项目资源文件 如果你的项目没有Properties文件夹和Resources.resx文件&#xff0c;可以通过右键项目-资源-常规-添加创建或打开程序集资源 2.添加国际化字符串 打开Resources.resx文件&#xff0c;添加需要翻译的文本字符&#xff0c;并将访问修…

java版B/S架构UWB人员定位系统源码spring boot+vue技术架构uwb定位装置-工业级UWB室内定位系统源码

java版B/S架构UWB人员定位系统源码spring bootvue技术架构uwb定位装置-工业级UWB室内定位系统源码 本套系统运用UWB定位技术&#xff0c;开发的高精度人员定位系统&#xff0c;通过独特的射频处理&#xff0c;配合先进的位置算法&#xff0c;可以有效计算复杂环境下的人员与物…

怎么避免电脑磁盘数据泄露?磁盘数据保护方法介绍

电脑磁盘是电脑存储数据的基础&#xff0c;而为了避免磁盘数据泄露&#xff0c;我们需要保护电脑磁盘。下面我们就来了解一下磁盘数据保护的方法。 磁盘加密 磁盘加密可以通过专业的加密算法来加密保护磁盘数据&#xff0c;避免电脑磁盘数据泄露。在这里小编推荐使用文件夹只读…

Springboot注意点

1.Usermapper里加param注解 2.RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody 是Spring框架中用于处理HTTP请求的两个不同的注 get请求一般用url传参数&#xff0c;所以参数名和参数的值就在ur…

Windows系统下DOS命令

Windows系统下DOS命令 1. 与文件操作相关1.1 mkdir&#xff0c;md命令1.2 rmdir、rd命令1.3 dir命令1.4 start命令1.5 echo命令1.6 type命令1.7 copy命令1.8 move命令1.9 copy和move的区别1.10 del命令1.11 rename命令1.12 attrib命令1.13 fsutil命令1.14 assoc命令 2. 与网络相…

如何在MySQL中实现upsert:如果不存在则插入?

目录 1 使用 REPLACE 2 使用 INSERT ... ON DUPLICATE KEY UPDATE 使用 INSERT IGNORE 有效会导致 MySQL 在尝试执行语句时忽略执行错误 INSERT 。这意味着 包含 索引或 字段 INSERT IGNORE 中重复值的语句 不会 产生错误&#xff0c;而只是完全忽略该特定 命令。其明显目的是…

centos官方yum源不可用 解决方案(随手记)

昨天用yum安装软件的时候&#xff0c;就报错了 [rootop01 ~]# yum install -y net-tools CentOS Stream 8 - AppStream 73 B/s | 38 B 00:00 Error: Failed to download metadata for repo appstream: Cannot prepare internal mirrorlis…

Ubuntu 22.04.4 LTS安装cmake-3.29.5

一、下载源码 wget https://github.com/Kitware/CMake/releases/download/v3.29.5/cmake-3.29.5.tar.gz tar -xzvf cmake-3.29.5.tar.gz 二、编译 运行./bootstrap。 如果出现下列问题&#xff1a; -- Could NOT find OpenSSL, try to set the path to OpenSSL root folder …