C# 实时声音频率图绘制

C# 实时声音频率图绘制

在这里插入图片描述

采集PCM音频数据

音频原来自麦克风

音频源来自录音文件

处理PCM音频数据

使用 FftSharp.FFT 将PCM数据进行傅里叶变换

安装FftSharp框架

在Nuget包管理器中搜索FftSharp并安装在这里插入图片描述

傅里叶变换

将采集到的PCM数据进行傅里叶变换

      // 傅里叶变换System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(audio);double[] ys = FftSharp.FFT.Magnitude(spectrum);

绘制频率图

采用自定义控件的方式来绘制频率图,核心代码如下:

        protected override void OnRender(DrawingContext drawingContext){// 渲染数据DrawFrequency(drawingContext, this.ActualWidth, this.ActualHeight);}private void DrawFrequency(DrawingContext drawingContext, double imageWidth, double imageHeight){double width = imageWidth - MarginLeft - 1;double height = imageHeight - MarginBottom - 1;var itemWidth = width / 4;var itemHeight = height / 4;drawingContext.DrawRectangle(this.Background, null, new Rect(0, 0, imageWidth, imageHeight));// 画方框drawingContext.DrawRect(this.Foreground, 1 + MarginLeft, 1, width, height);// 画竖线for (int i = 1; i < 4; i++){var left = i * itemWidth;drawingContext.DrawLine(this.Foreground, left + MarginLeft, 0, left + MarginLeft, height);// 画文本var freq = MaxFrequency * (i * 0.25);drawingContext.DrawText(FormatUtil.Frequency(freq), this.TextBrush, left + MarginLeft, height + MarginBottom / 2 + 2, 13);}drawingContext.DrawText(FormatUtil.Frequency(0), this.TextBrush, MarginLeft + 10, height + MarginBottom / 2 + 2, 13);drawingContext.DrawText(FormatUtil.Frequency(MaxFrequency), this.TextBrush, width + MarginLeft - 16, height + MarginBottom / 2 + 2, 13);// 画横线for (int i = 1; i < 4; i++){var top = i * itemHeight;drawingContext.DrawLine(this.Foreground, MarginLeft + 1, top, 10 + MarginLeft, top);// 画文本drawingContext.DrawText(Convert.ToString(20 - i * 5), this.TextBrush, MarginLeft / 2, top, 13);}// 画折线if (dataPoints.Count > 0){DrawPointPath(drawingContext, width, height);}}private void DrawPointPath(DrawingContext drawingContext, double width, double height){var itemHeight = height / 20;var itemWidth = width / dataPoints.Count;var amplitude = (mMaxVolume - mMinVolume) * 2;// 开始绘制路径StreamGeometry geometry = new StreamGeometry();using (StreamGeometryContext context = geometry.Open()){// 将路径移动到第一个数据点var x = (1 - dataPoints[0] / amplitude) * 20;context.BeginFigure(new Point(MarginLeft + 1, x * itemHeight), false, false);// 添加线段连接每个数据点for (int i = 1; i < dataPoints.Count; i++){x = (1 - dataPoints[i] / amplitude) * 20;context.LineTo(new Point(i * itemWidth + MarginLeft + 1, x * itemHeight), true, false);}}// 绘制路径drawingContext.DrawGeometry(null, pen, geometry);}

其他拓展类

FormatUtil

internal class FormatUtil{public static string Frequency(double freq){if (freq < 1000){return string.Format("{0}Hz", (int)freq);}else{var value = Math.Floor(freq / 1000);return string.Format("{0}kHz", value);}}}

DrawingContextExt

public static class DrawingContextExt
{public static void DrawRect(this DrawingContext drawingContext, Brush color, double x, double y, double w, double h){drawingContext.DrawRectangle(null, new Pen(color, 1), new System.Windows.Rect(x, y, w, h));}public static void DrawLine(this DrawingContext drawingContext, Brush color, double x, double y, double x2, double y2){drawingContext.DrawLine(new Pen(color, 1), new Point(x, y), new Point(x2, y2));}public static void FillEllipse(this DrawingContext drawingContext, Brush brush, double x, double y, double w, double h){var radiusX = w / 2;var radiusY = h / 2;drawingContext.DrawEllipse(brush, null, new Point(x - radiusX, y - radiusY), radiusX, radiusY);}public static void DrawText(this DrawingContext drawingContext, string data, Brush brush, double x, double y, double emSize = 10){// 创建FormattedText对象以设置文字的样式、位置和对齐方式FormattedText formattedText = new FormattedText(data,System.Globalization.CultureInfo.CurrentCulture,FlowDirection.LeftToRight,new Typeface("Arial"),emSize, brush);// 设置文字在 (50, 50) 的位置水平和垂直居中// 计算绘制点的坐标,使文本居中绘制Point drawPoint = new Point(x - formattedText.Width / 2, y - formattedText.Height / 2);// 绘制文字drawingContext.DrawText(formattedText, drawPoint);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/847953.shtml

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

相关文章

新手如何正确使用代理IP,一篇文章学会,包含实战案例

前言 一、代理IP1.1 什么是代理IP&#xff1f;1.2 代理ip分类1.3 代理IP的作用和优势 二、更换代理IP的方法2.1 重启路由器或光猫2.2 用拨号 vps 重拨更换动态IP代理。2.3 使用浏览器更换IP 三、IPIDEA代理的优势四、提取代理IP4.1 提取步骤4.2 浏览器使用代理IP 五、使用代理I…

Python实现连连看5

3 游戏初始化 当点击图5显示的菜单项“新游戏->开始游戏”后&#xff0c;游戏会显示图2所示的界面&#xff0c;开始游戏。 以上游戏初始化的过程可分为导入图片、生成图片地图以及根据地图显示图片三个步骤。 3.1 导入图片 图2所示的图片资源保存在“\res\pics”文件夹中…

NGINX 部署项目时,遇到刷新页面导致 404

步骤一:准备项目 首先,确保你的项目已经构建完毕,并且所有静态文件(如 index.html, js, css 文件等)都位于一个目录下,例如 /path/to/your/project。 步骤二:编辑 NGINX 配置文件 打开 NGINX 配置文件 你需要打开 NGINX 的配置文件。通常,这个文件位于 /etc/nginx/ngin…

CSS(盒子模型,定位,浮动,扩展)

CSS 盒子模型&#xff1a;外边距&#xff1a;内边距&#xff1a;水平居中&#xff1a; 定位&#xff1a;相对定位&#xff1a;绝对定位&#xff1a;固定定位&#xff1a; 浮动&#xff1a;扩展&#xff1a; 盒子模型&#xff1a; 盒子模型(Box Model) 规定了元素框处理元素内容…

Java核心: 使用instrumentation

在上一篇Java核心: 注解处理器我们提到&#xff0c;通过实现AbstractProcessor&#xff0c;并调用javac -processor能够生成代码来实现特殊逻辑。不过它存在两个明显的问题: 只能新增源文件来扩展逻辑&#xff0c;无法修改现有的类或方法必须有一个单独的编译过程&#xff0c;…

3毛钱的QC协议芯片TYPE-C USB快充接口物理层IC

前言&#xff1a; 现在基本使TYPE-C打天下了。很多产品和TYPEC息息相关&#xff0c;如笔记本的电源接口&#xff0c;手机更不用说了&#xff0c;甚至电烙铁也使TYPE-C接口的了&#xff0c;很多涉及采用TYPE-C接口的快充接口&#xff0c;简单的可以用电阻欺骗快充头&#xff0c…

什么是it运维工单系统?有哪些应用价值?

it运维工单系统是一个智能化的it运维服务管理系统&#xff0c;可以为企业和服务提供商提供高效的it运维服务管理&#xff0c;它可以自动分配任务、优化工作流程并跟踪工作进展&#xff0c;从而大大提高it运维工作效率和客户满意度。 一、it运维工单系统是什么&#xff1f; it…

100000开发的系统,执意重构钱多执念?

收到一位客户询盘&#xff0c;要重做自己的系统&#xff0c;原因&#xff1a;嫌弃基于PHP做的系统服务器消耗大。咨询了好几拨人&#xff0c;觉得外包公司贵&#xff0c;个人程序员又不靠谱&#xff0c;总之一门心思要重构。 现状&#xff1a; 1、系统研发耗费100000。 2、目…

Java编程常见问题汇总五

系列文章目录 文章目录 系列文章目录前言一、捕获不可能出现的异常二、transient的误用三、不必要的初始化四、最好用静态final定义Log变量五、选择错误的类加载器 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分…

docker create rm export exec命令详解

容器生命周期管理命令教程-3 1. 创建容器 docker create&#xff1a;创建一个新的容器但不启动它。 docker create -it --name mycontainer ubuntu bash通常使用 docker run(详细可看上一篇关于run命令的详细介绍) 2. 删除容器 docker rm&#xff1a;删除一个或多个容器。 d…

【ArrayList】不推荐使用Vector来实现线程安全的原因

在Java中&#xff0c;不推荐使用Vector来实现线程安全的ArrayList&#xff0c;主要原因有以下几点&#xff1a; 同步机制效率低下&#xff1a; Vector的所有方法都被同步&#xff08;synchronized&#xff09;了&#xff0c;这意味着每次对Vector的操作都会获取并释放锁。这种方…

嵌入式C与C语言

简介 嵌入式 C"这个概念更多地是指 C 语言在嵌入式系统中的应用,而不是 C 语言本身的语法和特性。 语言标准 无论是普通 C 还是嵌入式 C,它们都遵循相同的 C 语言标准,如 C89、C99 或 C11 等。 从语言规范的角度来看,两者没有本质的区别。 编程模型 嵌入式 C 更多地关…

【python】 ModuleNotFoundError: No module named datasets

成功解决“ModuleNotFoundError: No module named datasets”错误的全面指南 在Python编程中&#xff0c;遇到ModuleNotFoundError: No module named datasets这样的错误通常意味着Python解释器无法找到名为datasets的模块。datasets是一个流行的Python库&#xff0c;常用于加载…

spring 事务失效的几种场景

一、背景 在 springBoot 开发过程中&#xff0c;我们一般都是在业务方法上添加 Transactional 注解来让 spring 替我们管理事务&#xff0c;但在某些特定的场景下&#xff0c;添加完注解之后&#xff0c;事务是不生效的&#xff0c;接下来详细介绍下。 二、方法不是 public 2…

[leetcode hot 150]第一百三十六题,只出现一次的数字

题目&#xff1a; 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 根据题目关于空间、…

心链9----组队功能开发以及请求参数包装类和包装类实现

心链 — 伙伴匹配系统 组队功能开发 需求分析 理想的应用场景 我要跟别人一起参加竞赛或者做项目&#xff0c;可以发起队伍或者加入别人的队伍 用户可以 创建 一个队伍&#xff0c;设置队伍的人数、队伍名称&#xff08;标题&#xff09;、描述、超时时间 P0 队长、剩余的人数…

漏电保护器的工作原理

漏电保护器上的每月按一次按钮&#xff0c;如果按下后开关立即跳闸&#xff0c;则证明漏保功能良好该按钮的工作原理是模拟线路漏电&#xff0c;在漏保内部存在一个零序互感器&#xff0c;零序互感器分别测量零线和火线的电流值&#xff0c;如果二者数值不相等&#xff0c;零火…

【WEB系列】过滤器Filter

Filter&#xff0c;过滤器&#xff0c;属于Servlet规范&#xff0c;并不是Spring独有的。其作用从命名上也可以看出一二&#xff0c;拦截一个请求&#xff0c;做一些业务逻辑操作&#xff0c;然后可以决定请求是否可以继续往下分发&#xff0c;落到其他的Filter或者对应的Servl…

海报在线制作系统

文章转载自&#xff1a;FastAdmin海报在线制作系统 - 源码1688 应用介绍 介绍 新机构海报是一款基于FastAdminThinkPHP开发的一款新机构海报。 采用JavaScript vue canvas技术&#xff0c;实现在线一键制作海报&#xff0c;生成海报。 功能特性 1、自由创作 2、一键制作…

Django使用正则表达式

本书1-7章样章及配套资源下载链接: https://pan.baidu.com/s/1OGmhHxEMf2ZdozkUnDkAkA?pwdnanc 源码、PPT课件、教学视频等&#xff0c;可以从前言给出的下载信息下载&#xff0c;大家可以评估一下。 在Django框架的新版本&#xff08;v2.0 &#xff09;中&#xff0c;URLc…