-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsubwooferplugin.cpp
154 lines (128 loc) · 3.61 KB
/
subwooferplugin.cpp
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "subwooferplugin.h"
#include <qmath.h>
#include <QSettings>
static BiquadSCoefs coefs[STAGE_LENGTH] = {
{1.0, 1.931851652578, 1.0, 0.0, 0.0, 1.0},
{1.0, 1.414213562373, 1.0, 0.0, 0.0, 1.0},
{1.0, 0.517638090205, 1.0, 0.0, 0.0, 1.0}
};
static void biquadBilinearDesign(BiquadZCoefs *out, const BiquadSCoefs *in, float omega)
{
const float t = 2.0 * tan(omega * M_PI / 2);
const float v = (4.0 * in->a + 2.0 * in->b * t + in->c * t * t);
out->a2 = (4.0 * in->a - 2.0 * in->b * t + in->c * t * t) / v;
out->a1 = (2.0 * in->c * t * t - 8.0 * in->a) / v;
out->a0 = 1.0;
out->b2 = (4.0 * in->d - 2.0 * in->e * t + in->f * t * t) / v * in->c / in->f;
out->b1 = (2.0 * in->f * t * t - 8.0 * in->d) / v * in->c / in->f;
out->b0 = (4 * in->d + in->f * t * t + 2.0 * in->e * t) / v * in->c / in->f;
}
static void biquadReset(BiquadBuffer *buf)
{
buf->x1 = buf->x2 = buf->y1 = buf->y2 = 0;
}
static float biquadProcess(BiquadBuffer *buf, const BiquadZCoefs *in, float x)
{
const float center = x * in->b0 + in->b1 * buf->x1 + in->b2 * buf->x2;
const float y = in->a0 * center - in->a1 * buf->y1 - in->a2 * buf->y2;
buf->x2 = buf->x1;
buf->x1 = x;
buf->y2 = buf->y1;
buf->y1 = y;
return y;
}
static void butterworth6Design(BiquadZCoefs *out, int samplerate, int cutout)
{
const float omega = cutout * 1.0 / samplerate;
for(int i = 0; i < STAGE_LENGTH; ++i)
{
biquadBilinearDesign(out + i, coefs + i, omega);
}
}
static float butterworth6Process(BiquadBuffer *buf, const BiquadZCoefs *in, float x)
{
float y = biquadProcess(&buf[0], &in[0], x);
for(int i = 1; i < STAGE_LENGTH; ++i)
{
y = biquadProcess(&buf[i], &in[i], y);
}
return y;
}
static float shaitan(float x)
{
return 2.5 * atan(0.9 * x) + 2.5 * sqrt(1 - pow(0.9 * x, 2)) - 2.5;
}
SubwooferPlugin *SubwooferPlugin::m_instance = nullptr;
SubwooferPlugin::SubwooferPlugin()
: Effect()
{
m_instance = this;
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QSettings settings;
#else
QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
#endif
setLevel(settings.value("Subwoofer/level", 10).toUInt());
m_cutoff = settings.value("Subwoofer/cutoff", 250).toUInt();
}
SubwooferPlugin::~SubwooferPlugin()
{
m_instance = nullptr;
}
void SubwooferPlugin::applyEffect(Buffer *b)
{
if(channels() != 2)
{
return;
}
m_mutex.lock();
const float level = m_level / 10.f;
float *data = b->data;
for(size_t i = 0; i < b->samples; i += 2)
{
const float x0 = data[i];
const float x1 = data[i + 1];
const float lf0 = butterworth6Process(m_buffer[0], m_coefs, x0);
const float lf1 = butterworth6Process(m_buffer[1], m_coefs, x1);
const float lfs = shaitan((lf0 + lf1) / 2.0 * level) / level;
data[i] = x0 - lf0 + lfs;
data[i + 1] = x1 - lf1 + lfs;
}
m_mutex.unlock();
}
void SubwooferPlugin::configure(quint32 freq, ChannelMap map)
{
if(sampleRate() != freq)
{
Effect::configure(freq, map);
reset();
}
}
void SubwooferPlugin::reset()
{
for(int i = 0; i < 2; ++i)
{
for(int j = 0; j < 3; ++j)
{
biquadReset(&m_buffer[i][j]);
}
}
butterworth6Design(m_coefs, sampleRate(), m_cutoff);
}
void SubwooferPlugin::setLevel(float level)
{
m_mutex.lock();
m_level = level;
m_mutex.unlock();
}
void SubwooferPlugin::setCutOff(int value)
{
m_mutex.lock();
m_cutoff = value;
reset();
m_mutex.unlock();
}
SubwooferPlugin* SubwooferPlugin::instance()
{
return m_instance;
}