-
Notifications
You must be signed in to change notification settings - Fork 1
/
synth-mini-EQ.h
111 lines (84 loc) · 2.89 KB
/
synth-mini-EQ.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
FM. BISON hybrid FM synthesis -- Mini EQ (3-band).
(C) njdewit technologies (visualizers.nl) & bipolaraudio.nl
MIT license applies, please see https://en.wikipedia.org/wiki/MIT_License or LICENSE in the project root!
FIXME:
- Turn into (semi-)3-band full cut EQ
*/
#pragma once
#include "3rdparty/filters/Biquad.h"
#include "synth-global.h"
#include "synth-interpolated-parameter.h"
namespace SFM
{
// Use the peak filter to tweak Q to a sort of satisfactory shape: https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/
constexpr float kMidQ = kNormalGainAtCutoff; // Higher is steeper!
// Centre frequencies (I hope, also, looked at https://blog.landr.com/eq-basics-everything-musicians-need-know-eq/ for ref.)
constexpr float kLoHz = 80.f;
constexpr float kMidHz = 1200.f;
constexpr float kHiHz = 4000.f;
class MiniEQ
{
public:
MiniEQ(unsigned sampleRate, bool withMid) :
m_withMid(withMid)
, m_bassFc(kLoHz/sampleRate)
, m_trebleFc(kHiHz/sampleRate)
, m_midFc(kMidHz/sampleRate)
, m_bassdB(0.f, sampleRate, kDefParameterLatency, 0.f, 1.f)
, m_trebledB(0.f, sampleRate, kDefParameterLatency, 0.f, 1.f)
, m_middB(0.f, sampleRate, kDefParameterLatency, 0.f, 1.f)
{
SetBiquads();
}
~MiniEQ() {}
void SetTargetdBs(float bassdB, float trebledB, float middB = 0.f);
SFM_INLINE void Apply(float &sampleL, float &sampleR)
{
SetBiquads();
if (true == m_withMid)
{
m_midPeak.process(sampleL, sampleR); // First push or pull MID freq.
}
float loL = sampleL, loR = sampleR;
m_bassShelf.process(loL, loR);
float hiL = sampleL, hiR = sampleR;
m_trebleShelf.process(hiL, hiR);
// Not sure if this is right at all, I'll keep the ticket open, but it does the trick for now
sampleL = (loL+hiL)*kNormalGainAtCutoff;
sampleR = (loR+hiR)*kNormalGainAtCutoff;
}
// Code duplication, but what are we going to do about it outside of a huge overhaul?
SFM_INLINE float ApplyMono(float sample)
{
SetBiquads();
if (true == m_withMid)
m_midPeak.processMono(sample);
float LO = sample;
m_bassShelf.processMono(LO);
float HI = sample;
m_trebleShelf.processMono(HI);
return (LO+HI)*kNormalGainAtCutoff;
}
private:
const bool m_withMid;
const float m_bassFc;
const float m_trebleFc;
const float m_midFc;
Biquad m_bassShelf;
Biquad m_trebleShelf;
Biquad m_midPeak;
InterpolatedParameter<kLinInterpolate, false> m_bassdB;
InterpolatedParameter<kLinInterpolate, false> m_trebledB;
InterpolatedParameter<kLinInterpolate, false> m_middB;
// Only called if necessary
SFM_INLINE void SetBiquads()
{
m_bassShelf.setBiquad(bq_type_lowshelf, m_bassFc, 0.f, m_bassdB.Sample()); // Bass
m_trebleShelf.setBiquad(bq_type_highshelf, m_trebleFc, 0.f, m_trebledB.Sample()); // Treble
// Mid?
if (true == m_withMid)
m_midPeak.setBiquad(bq_type_peak, m_midFc, kMidQ, m_middB.Sample());
}
};
}