当前位置: 首页 > news >正文

音频动态压缩算法曲线实现

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.dtcms.com/a/265479.html

相关文章:

  • C++【成员变量、成员函数、this指针】
  • OSPF高级特性之FRR
  • Vue 项目在哪里加载「字典数据」最好
  • 基于 alpine 构建 .net 的基础镜像
  • 开源模型应用落地-让AI更懂你的每一次交互-Mem0集成Qdrant、Neo4j与Streamlit的创新实践(四)
  • Zookeeper 客户端 .net访问框架 ZookeeperNetEx项目开发编译
  • 开源 C# .net mvc 开发(六)特殊控制控制台、周期、邮件编程
  • 深度实战:Ubuntu服务器宕机排查全记录
  • 月付物理服务器租用平台-青蛙云
  • 基于 govaluate 的监控系统中,如何设计灵活可扩展的自定义表达式函数体系
  • npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree
  • Python Set() 完全指南:从入门到精通
  • R语言开发记录,一
  • 前端-HTML-day1
  • Rust Web 全栈开发(二):构建 HTTP Server
  • 主流分布式中间件及其选型
  • locate命令的原理是啥
  • OpenCV CUDA模块设备层-----在GPU 上高效地执行两个 uint 类型值的最大值比较函数vmax2()
  • Frida:配置自动补全 in VSCode
  • 搭建VirtualBox-6+vagrant_2+docker+mysql5.7的步骤
  • 客户案例 | 某新能源车企依托Atlassian工具链+龙智定制开发服务,打造符合ASPICE标准的研发管理体系
  • 云原生系统DOCKER中容器系统搭建
  • Python字符与ASCII转换方法
  • Ubuntu Gnome 安装和卸载 WhiteSur-gtk-theme 类 Mac 主题的正确方法
  • vue2+elementui使用compressorjs压缩上传的图片
  • Euler2203安装.NetCore6.0环境操作步骤
  • python安装虚拟环境
  • Python 物联网(IoT)与边缘计算开发实战(1)
  • 优雅草蜻蜓R实时音视频会议系统云原生私有化部署方案深度解析-优雅草卓伊凡|贝贝|clam|麻子|夜辰
  • Docker 容器资源限制