Juce实现动态压缩曲线绘制

动态范围压缩算法(Dynamic Range Compression,DRC)是将音频信号的动态范围映射到一个较小的范围内的过程,即降低较高的峰值的信号电平,而不处理较安静的部分。DRC被广泛用于音频录制、制作工作、降噪、广播和现场表演等应用中。

1.压缩算法的参数

  • **Threshold:**定义了开始压缩的音量。任何超过阈值的信号都将被压缩。例如当 Threshold = -10db,当信号音量超过 -10db 时,它将被压缩。

  • **Ratio:**控制超过 Threshold 的信号的压缩比率。例如当 Threshold = -10,input = -5,此时信号超过 Threshold 有 5db,它将被压缩,那么压缩多少呢?这就由 Ratio 控制,当 Ratio = 5 时,信号增量从原来的 5db 被抑制为 1db,当 Ratio = 2 时,则抑制为 2.5db,以此类推。

    在这里插入图片描述

  • Attack Time: Attack Time 和 Release Time 在一定程度上控制 Compressor ”灵敏度“。 Attack Time 定义了一旦信号超过 Threshold,Compressor 将增益降低到期望水平所需要的时间。

  • Release Time : 定义了一旦信号低于 Threshold,将增益恢复至正常水平需要的时间。

  • **Make-up Gain:**Compressor 降低信号的增益,因此可以施加一个额外的增益使得输入信号与输出信号的响度水平相当。

  • **Knee Width:**它控制了压缩曲线的特性(如下图),曲线是尖锐的拐角,还是想膝盖一样有弧度的曲线。

在这里插入图片描述

2.计算公式

Gain Computer:
Gain Computer 根据输入信号的电平(音量)来计算得到需要的增益。这个阶段涉及到了 Threshold(T)、Ratio(R)、Knee Width(W) 三个参数。一旦输入信号电平超过 T,那么它会根据 R 进行衰减,计算公式如下:
在这里插入图片描述
为了让 Compressor 有更加平滑的变化曲线,我们增加了 Knee Width(参考 Knee Width 参数说明图),这中模式我们称为 “Soft Knee”,其计算公式为:
在这里插入图片描述

3.juce代码实现动态压缩曲线

  • 1.声明所需要的参数,及绘制区域

    int   attackTime;
    int   releasTime;
    int   makeUpGain;
    int   threshold;
    float knee;
    float ratio;Slider                LmtRatioSlider;
    Slider                LmtThresholdSlider;
    Slider                LmtKneeSlider;
    Slider                LmtAttackTimeSlider;
    Slider                LmtReleaseTimeSlider;
    Slider                LmtMakeUpGainSlider;Label                LabelRatioSlider;
    Label                LabelThresholdSlider;
    Label                LabelKneeSlider;
    Label                LabelAttackTimeSlider;
    Label                LabelReleaseTimeSlider;
    Label                LabelMakeUpGainSlider;
    
  • 2.在构造函数中创建并MakeVisible

    setSize (600, 400);addAndMakeVisible(LmtRatioSlider);
    LmtRatioSlider.setRange(30, 270, 1);
    LmtRatioSlider.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
    LmtRatioSlider.setTextBoxStyle(Slider::TextBoxBelow, false, 40, 15);
    LmtRatioSlider.setValue(ratio);LmtRatioSlider.setRotaryParameters({ 3.925f, 8.635f, true });
    LmtRatioSlider.setColour(Slider::rotarySliderOutlineColourId,Colour(0xFF00CAFF));
    LmtRatioSlider.setColour(Slider::rotarySliderFillColourId,Colour(16, 153, 212));
    LmtRatioSlider.addListener(this);addAndMakeVisible(LmtKneeSlider);
    LmtKneeSlider.setRange(0, 1, 0.01);
    LmtKneeSlider.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
    LmtKneeSlider.setTextBoxStyle(Slider::TextBoxBelow, false, 40, 15);
    LmtKneeSlider.setValue(knee);LmtKneeSlider.setRotaryParameters({ 3.925f, 8.635f, true });
    LmtKneeSlider.setColour(Slider::rotarySliderOutlineColourId,Colour(0xFF00CAFF));
    LmtKneeSlider.setColour(Slider::rotarySliderFillColourId,Colour(16, 153, 212));
    LmtKneeSlider.addListener(this);addAndMakeVisible(LmtThresholdSlider);
    LmtThresholdSlider.setRange(-60, 0, 1.0);
    LmtThresholdSlider.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
    LmtThresholdSlider.setTextBoxStyle(Slider::TextBoxBelow, true, 40, 15);
    LmtThresholdSlider.setValue(threshold);
    LmtThresholdSlider.setRotaryParameters({ 3.925f, 8.635f, true });
    LmtThresholdSlider.setColour(Slider::rotarySliderOutlineColourId,Colour(0xFF00CAFF));
    LmtThresholdSlider.setColour(Slider::rotarySliderFillColourId,Colour(16, 153, 212));
    LmtThresholdSlider.addListener(this);addAndMakeVisible(LmtAttackTimeSlider);
    LmtAttackTimeSlider.setRange(30, 270, 0.1);
    LmtAttackTimeSlider.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
    LmtAttackTimeSlider.setTextBoxStyle(Slider::TextBoxBelow, false, 40, 15);
    LmtAttackTimeSlider.setValue(attackTime);LmtAttackTimeSlider.setRotaryParameters({ 3.925f, 8.635f, true });
    LmtAttackTimeSlider.setColour(Slider::rotarySliderOutlineColourId,Colour(0xFF00CAFF));
    LmtAttackTimeSlider.setColour(Slider::rotarySliderFillColourId,Colour(16, 153, 212));
    LmtAttackTimeSlider.addListener(this);addAndMakeVisible(LmtReleaseTimeSlider);
    LmtReleaseTimeSlider.setRange(30, 270, 0.1);
    LmtReleaseTimeSlider.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
    LmtReleaseTimeSlider.setTextBoxStyle(Slider::TextBoxBelow, false, 40, 15);
    LmtReleaseTimeSlider.setValue(releasTime);
    LmtReleaseTimeSlider.setRotaryParameters({ 3.925f, 8.635f, true });
    LmtReleaseTimeSlider.setColour(Slider::rotarySliderOutlineColourId,Colour(0xFF00CAFF));
    LmtReleaseTimeSlider.setColour(Slider::rotarySliderFillColourId,Colour(16, 153, 212));
    LmtReleaseTimeSlider.addListener(this);addAndMakeVisible(LmtMakeUpGainSlider);
    LmtMakeUpGainSlider.setRange(0, 24, 1);
    LmtMakeUpGainSlider.setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
    LmtMakeUpGainSlider.setTextBoxStyle(Slider::TextBoxBelow, false, 40, 15);
    LmtMakeUpGainSlider.setValue(makeUpGain);
    LmtMakeUpGainSlider.setColour(Slider::rotarySliderOutlineColourId,Colour(0xFF00CAFF));
    LmtMakeUpGainSlider.setColour(Slider::rotarySliderFillColourId,Colour(16, 153, 212));
    LmtMakeUpGainSlider.addListener(this);addAndMakeVisible(&LabelRatioSlider);
    LabelRatioSlider.setColour(Label::textColourId, Colour(195, 203, 206));
    LabelRatioSlider.setText("Ratio", dontSendNotification);addAndMakeVisible(&LabelThresholdSlider);
    LabelThresholdSlider.setColour(Label::textColourId, Colour(195, 203, 206));
    LabelThresholdSlider.setText("Threshold", dontSendNotification);addAndMakeVisible(&LabelKneeSlider);
    LabelKneeSlider.setColour(Label::textColourId, Colour(195, 203, 206));
    LabelKneeSlider.setText("Knee",dontSendNotification);addAndMakeVisible(&LabelAttackTimeSlider);
    LabelAttackTimeSlider.setColour(Label::textColourId, Colour(195, 203, 206));
    LabelAttackTimeSlider.setText("Attack",dontSendNotification);addAndMakeVisible(&LabelReleaseTimeSlider);
    LabelReleaseTimeSlider.setColour(Label::textColourId, Colour(195, 203, 206));
    LabelReleaseTimeSlider.setText("Release", dontSendNotification);addAndMakeVisible(&LabelMakeUpGainSlider);
    LabelMakeUpGainSlider.setColour(Label::textColourId, Colour(195, 203, 206));
    LabelMakeUpGainSlider.setText("Makeup", dontSendNotification);area.setBounds(40, 30, 111, 110);
    
  • 3.在Resize函数中放置

    auto area = getLocalBounds();auto slider_area = area.removeFromBottom(100);
    slider_area.removeFromBottom(10);
    int width = (slider_area.getWidth() - 80) / 6;slider_area.removeFromLeft(10);
    LmtAttackTimeSlider.setBounds(slider_area.removeFromLeft(width)/*.withTrimmedTop(10)*/);
    slider_area.removeFromLeft(10);
    LmtReleaseTimeSlider.setBounds(slider_area.removeFromLeft(width)/*.withTrimmedTop(10)*/);
    slider_area.removeFromLeft(10);
    LmtKneeSlider.setBounds(slider_area.removeFromLeft(width)/*.withTrimmedTop(10)*/);
    slider_area.removeFromLeft(10);
    LmtRatioSlider.setBounds(slider_area.removeFromLeft(width)/*.withTrimmedTop(10)*/);
    slider_area.removeFromLeft(10);
    LmtThresholdSlider.setBounds(slider_area.removeFromLeft(width)/*.withTrimmedTop(10)*/);
    slider_area.removeFromLeft(10);
    LmtMakeUpGainSlider.setBounds(slider_area.removeFromLeft(width)/*.withTrimmedTop(10)*/);area.removeFromBottom(10);
    auto label_area = area.removeFromBottom(20);label_area.removeFromLeft(10);
    LabelAttackTimeSlider.setBounds(label_area.removeFromLeft(width).withSizeKeepingCentre(width,20));
    label_area.removeFromLeft(10);
    LabelReleaseTimeSlider.setBounds(label_area.removeFromLeft(width).withSizeKeepingCentre(width, 20));
    label_area.removeFromLeft(10);
    LabelKneeSlider.setBounds(label_area.removeFromLeft(width).withSizeKeepingCentre(width, 20));
    label_area.removeFromLeft(10);
    LabelRatioSlider.setBounds(label_area.removeFromLeft(width).withSizeKeepingCentre(width, 20));
    label_area.removeFromLeft(10);
    LabelThresholdSlider.setBounds(label_area.removeFromLeft(width).withSizeKeepingCentre(width, 20));
    label_area.removeFromLeft(10);
    LabelMakeUpGainSlider.setBounds(label_area.removeFromLeft(width).withSizeKeepingCentre(width, 20));
    
  • 4.在paint函数中绘制

    const float cornerSize = 4.0f;g.setColour(Colour(24, 25, 29));
    g.fillRect(area.getX(), area.getY(), area.getWidth(), area.getHeight());float mGain[601];
    for (int i = 0; i < 601; i++)
    {mGain[i] = drc_plot_calc(i * 0.1f - 60.0f, threshold, knee * 100, ratio, makeUpGain);
    }g.setColour(Colours::orange);
    int scopeSize = 600;
    float width = area.getWidth();
    float height = area.getHeight();
    // 绘制波形
    for (int i = 1; i < scopeSize; ++i)
    {if (mGain[i] > 0){break;}g.drawLine({ (float)jmap(i - 1, 0, scopeSize, 41,(int)width + 41),jmap((float)jlimit(-60.0f,0.0f,mGain[i - 1]), -60.0f, 0.0f, height + 30.0f, 0.0f + 30.0f),(float)jmap(i,     0, scopeSize, 41 , (int)width + 41),jmap((float)jlimit(-60.0f,0.0f,mGain[i]), -60.0f, 0.0f, height + 30.0f, 0.0f + 30.0f) });
    }g.setColour(Colour(46, 141, 159));
    g.setFont(10.0f);
    

    4.重点算法实现函数

    绘制动态压缩曲线按公式实现的函数

    float MainComponent::drc_plot_calc(float in, float T, float W, float R, float gain)
    {float dGain = 0.0;if (0 == W){if (in >= T){dGain = (T + (in - T) / R);}else{dGain = in;}}else{if (in > (T + W / 2)){dGain = (T + (in - T) / R);}else if (in < (T - W / 2)){dGain = in;}else{dGain = in + (1 / R - 1) * (in - T + W / 2) * (in - T + W / 2) / (2 * W);}}dGain = dGain + gain;return dGain;
    }
    

    5.绘制界面展示

    通过调节slider参数,曲线根据slider滑动展示。

      else if (in < (T - W / 2)){dGain = in;}else{dGain = in + (1 / R - 1) * (in - T + W / 2) * (in - T + W / 2) / (2 * W);}
    

    }
    dGain = dGain + gain;
    return dGain;
    }

    
    ### 5.绘制界面展示通过调节slider参数,曲线根据slider滑动展示。

在这里插入图片描述
完整代码地址:https://download.csdn.net/download/huangyifei_1111/91250409

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

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

相关文章

技术视界 | OpenLoong 控制框架:打造通用人形机器人智能系统的中枢基座

在人形机器人向通用性、智能化方向加速演进的当下&#xff0c;控制系统的角色正在发生根本变化&#xff1a;它不再只是底层驱动的接口适配层&#xff0c;也不只是策略调用的转译引擎&#xff0c;而是成为连接具身模型、异构本体与多样化任务的“中枢神经系统”。 在 2025 年张…

IOS 蓝牙连接

最近做一个硬件设备&#xff0c;写IOS相应的数据连接/分析代码时&#xff1b;发现一个问题&#xff0c;如果是开机&#xff0c;每次都能连接上。连接断开后&#xff0c;发现再也扫描不到了。通过第三方工具LightBlue&#xff0c;发现信号是-127。 此时进入设置查看蓝牙设备&am…

【硬核数学 · LLM篇】3.1 Transformer之心:自注意力机制的线性代数解构《从零构建机器学习、深度学习到LLM的数学认知》

我们已经完成了对机器学习和深度学习核心数学理论的全面探索。我们从第一阶段的经典机器学习理论&#xff0c;走到了第二阶段的深度学习“黑盒”内部&#xff0c;用线性代数、微积分、概率论、优化理论等一系列数学工具&#xff0c;将神经网络的每一个部件都拆解得淋漓尽致。 …

flutter封装vlcplayer的控制器

import dart:async;import package:flutter_vlc_player/flutter_vlc_player.dart; import package:flutter/material.dart;class GlobalVlcController extends ChangeNotifier {//设置单例/*static final GlobalVlcController _instance GlobalVlcController._internal();fact…

SEO-滥用元机器人、规范或 hreflang 标签

&#x1f9f1; 一、滥用 Meta Robots 标签 ❌ 常见问题&#xff1a; 问题描述设置了 noindex 不该屏蔽的页面比如产品页、分类页被意外 noindex&#xff0c;导致不被收录设置 nofollow 导致内链失效所有链接都被 nofollow&#xff0c;影响爬虫抓取路径在 <meta> 标签和…

笨方法学python -练习14

程序&#xff1a; from sys import argv script, user_name argv prompt > print(f"Hi {user_name}, Im the {script} script.") print("Id like to ask you a few questions.") print(f"Do you like me {user_name}?") likes in…

Frida:配置自动补全 in VSCode

1. 前言 编写 frida JavaScript 脚本是一件 very 普遍的事情在 Android Reverse 中。为了方便编写&#xff0c;配置相关的环境使其能够自动补全是很关键的&#xff0c;即通过类名就能够获取该类的所有对外接口信息&#xff0c;这是面向对象编程的核心优势&#xff0c;可惜我没…

FPGA矩阵算法实现

简介 现如今设计上对速度的要求越来越高&#xff0c;而矩阵相乘含有大量的乘法和加法计算&#xff0c;造成计算时间长从而影响性能&#xff0c;本章节利用FPGA实现浮点型矩阵运算&#xff0c;可在极短时间内完成矩阵运算。 知识介绍 矩阵计算公式如下&#xff1a; 需要保证A的…

C#可空类型详解:从基础到高级应用

C#可空类型详解&#xff1a;从基础到高级应用 在C#编程中&#xff0c;可空类型是一个非常重要的概念&#xff0c;它允许我们为值类型&#xff08;如int、bool、DateTime等&#xff09;分配null值&#xff0c;从而增强了代码的表达能力和灵活性。本文将详细介绍C#中可空类型的各…

Elasticsearch:异常检测入门

在我之前的文章里&#xff0c;我有讲述很多有关使用机器学习来针对数据做异常监测的文章。你可以在 “开发者上手指南” 里的 “机器学习” 章节中找到。在今天的练习中&#xff0c;我将使用最新的 Elastic Stack 9.0.2 来展示如何在 Elasticsearch 中使用机器学习的方法来进行…

ARuler3.1.3 | 高级版测量应用,利用AR技术测量所有

ARuler是一款非常便捷的测量应用程序&#xff0c;专为需要精确测量的用户设计。它不仅具备强大的3D测量功能&#xff0c;还利用增强现实&#xff08;AR&#xff09;技术&#xff0c;为用户提供多种测量选项&#xff0c;包括角度、长度、宽度、高度、面积和体积等。无论是日常生…

MapReduce分布式计算框架:从原理到实战

大家好&#xff01;今天我们来聊聊大数据处理领域的一个重要框架——MapReduce。作为Google提出的经典分布式计算模型&#xff0c;MapReduce极大地简化了海量数据的处理流程。无论你是大数据新手还是有一定经验的开发者&#xff0c;这篇文章都会让你对MapReduce有更深入的理解。…

Redis 7 及更高版本的脚本化方案

一、背景与动机 传统的 Redis 脚本机制依赖于客户端加载 EVAL 脚本&#xff0c;存在以下局限&#xff1a; 网络与编译开销 每次调用都要传输脚本源码或重新加载 SHA1。缓存失效风险 重启、主从切换、SCRIPT FLUSH 后脚本缓存丢失&#xff0c;事务易失败。调试与运维困难 SHA1…

Java项目:基于SSM框架实现的云端学习管理系统【ssm+B/S架构+源码+数据库+毕业论文】

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对课程学习信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差…

【压力测试之_Jmeter链接Oracle数据库链接】

Oracle数据库链接 欢迎来到挖坑避坑课堂链接数据库 欢迎来到挖坑避坑课堂 之前性能测试都是业务之类的&#xff0c;数据库压测很少涉及&#xff0c;就会出现很多各式各样的问题&#xff0c;首要问题就是Jmeter链接数据库的问题&#xff0c;本篇主要讲解Jmeter链接Oracle数据库…

Appium与Appium Inspector配置教程

一、连接设备 首先将手机的开发者模式打开&#xff0c;不同手机的开启方法不同&#xff0c;这里演示的测试机为vivoS1&#xff0c;其他机型的开启方法大家可以自行AI搜索。 1.手机授权 &#xff08;1&#xff09;点击手机的【设置】选项 &#xff08;2&#xff09;打开手机…

【web出海】深度拆解 FLUX.1 kontext:这不仅是AI绘画的革命,更是 MicroSaaS 创业者的黄金机遇

前言 近日&#xff0c;Black Forest Labs 发布的 FLUX.1 Kontext 模型在AI圈掀起了波澜。它不仅仅是又一个文生图工具&#xff0c;其独特的“在情境中&#xff08;in-context&#xff09;”编辑、惊人的角色一致性、精准的局部修改和强大的文字渲染能力&#xff0c;标志着一个技…

Git 安装闭坑指南(仅 Windows 环境)

&#x1f4bb; Git 安装闭坑指南&#xff08;仅 Windows 环境&#xff09; 适用人群&#xff1a;刚开始用 Git 的 Windows 用户&#xff1b;重新配置开发环境的程序员&#xff1b;不想踩坑的团队小伙伴 目标&#xff1a;快速、稳定地安装 Git&#xff0c;在各种常见场景下避免“…

2025年4月SCI-吕佩尔狐优化算法Rüppell’s fox optimizer-附Matlab免费代码

引言 本期介绍一种新的元启发式算法——吕佩尔狐优化算法Rppell’s fox optimizer&#xff0c;RFO。RFO的灵感来自于吕佩尔狐狸在白天和晚上自然而聪明的集体觅食行为。优化器利用吕佩尔狐敏锐的视觉、听觉和嗅觉对其各种主要觅食活动进行数学模拟&#xff0c;在优化过程中兼顾…

SwiftUI 中的模糊效果详解:.blur、.material、UIVisualEffectView

模糊效果&#xff08;Blur Effect&#xff09;是 iOS 用户界面设计的重要组成部分&#xff0c;它被广泛应用于系统控制中心、通知背景、弹窗蒙版等场景&#xff0c;营造出“毛玻璃”的视觉层次感。 本文将深入解析 SwiftUI 中实现模糊效果的三种主流方式&#xff1a;.blur(radi…