-
Notifications
You must be signed in to change notification settings - Fork 7
/
transverb.h
234 lines (183 loc) · 8.02 KB
/
transverb.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
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*------------------------------------------------------------------------
Copyright (C) 2001-2022 Tom Murphy 7 and Sophia Poirier
This file is part of Transverb.
Transverb is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Transverb is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Transverb. If not, see <http:https://www.gnu.org/licenses/>.
To contact the author, use the contact form at http:https://destroyfx.org/
------------------------------------------------------------------------*/
#pragma once
#include <array>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <optional>
#include <vector>
#include "dfxplugin.h"
#include "dfxsmoothedvalue.h"
#include "iirfilter.h"
#include "transverb-base.h"
class TransverbDSP final : public DfxPluginCore {
public:
explicit TransverbDSP(DfxPlugin* inDfxPlugin);
void process(float const* inAudio, float* outAudio, unsigned long inNumFrames) override;
void reset() override;
void processparameters() override;
private:
static constexpr int kAudioSmoothingDur_samples = 42;
static constexpr double kHighpassFilterCutoff = 39.;
static constexpr size_t kNumFIRTaps = 23;
static constexpr double kFIRSpeedThreshold = 5.;
static constexpr double kUnitySpeed = 1.;
enum class FilterMode { None, Highpass, LowpassIIR, LowpassFIR };
struct Head {
dfx::SmoothedValue<double> speed;
dfx::SmoothedValue<float> mix, feed;
double read = 0.;
std::optional<double> targetdist;
double distspeedfactor = 1.;
std::vector<float> buf;
dfx::IIRFilter filter;
std::array<float, kNumFIRTaps> firCoefficients {};
bool speedHasChanged = false;
int smoothcount = 0;
float smoothstep = 0.f;
float lastdelayval = 0.f;
void reset();
};
static constexpr float interpolateHermite(float const* data, double readaddress, int arraysize, int writeaddress);
// uses only the fractional portion of the address
static constexpr float interpolateLinear(float value1, float value2, double address)
{
auto const posFract = static_cast<float>(std::fmod(address, 1.));
return (value1 * (1.0f - posFract)) + (value2 * posFract);
}
static constexpr float interpolateLinear(float const* data, double readaddress, int arraysize/*, int writeaddress*/);
// negative input values are bumped into non-negative range by incremements of modulo
static constexpr int mod_bipolar(int value, int modulo);
static inline double fmod_bipolar(double value, double modulo);
// distance in samples of a read position from the current write position
double getdist(double read) const;
void processdist(double distnormalized, Head& head);
// these store the parameter values
int bsize = 0;
dfx::SmoothedValue<float> drymix;
int distchangemode {};
int writer = 0;
std::array<Head, dfx::TV::kNumDelays> heads;
int const MAXBUF; // the size of the audio buffer (dependent on sampling rate)
bool firstrendersincereset = false;
std::vector<float>& buftemp;
std::vector<float> const firCoefficientsWindow;
};
class Transverb final : public DfxPlugin {
public:
explicit Transverb(TARGET_API_BASE_INSTANCE_TYPE inInstance);
void dfx_PostConstructor() override;
bool loadpreset(long index) override; // overridden to support the random preset
void randomizeparameters() override;
long dfx_GetPropertyInfo(dfx::PropertyID inPropertyID, dfx::Scope inScope, unsigned long inItemIndex,
size_t& outDataSize, dfx::PropertyFlags& outFlags) override;
long dfx_GetProperty(dfx::PropertyID inPropertyID, dfx::Scope inScope, unsigned long inItemIndex,
void* outData) override;
long dfx_SetProperty(dfx::PropertyID inPropertyID, dfx::Scope inScope, unsigned long inItemIndex,
void const* inData, size_t inDataSize) override;
auto& getscratchbuffer() noexcept {
return buftemp;
}
protected:
size_t settings_sizeOfExtendedData() const noexcept override;
void settings_saveExtendedData(void* outData, bool isPreset) override;
void settings_restoreExtendedData(void const* inData, size_t storedExtendedDataSize,
long dataVersion, bool isPreset) override;
void settings_doChunkRestoreSetParameterStuff(long tag, float value, long dataVersion, long presetNum) override;
private:
static constexpr long kNumPresets = 16;
void initPresets();
auto& speedModeStateFromPropertyID(dfx::PropertyID inPropertyID) {
return speedModeStates.at(dfx::TV::speedModePropertyIDToIndex(inPropertyID));
}
std::array<int32_t, dfx::TV::kNumDelays> speedModeStates {};
std::vector<float> buftemp; // shared between all DSP cores (memory optimization)
};
constexpr int TransverbDSP::mod_bipolar(int value, int modulo) {
assert(modulo > 0);
while (value < 0) {
value += modulo;
}
return value % modulo;
}
inline double TransverbDSP::fmod_bipolar(double value, double modulo) {
assert(modulo > 0.);
while (value < 0.) {
value += modulo;
}
return std::fmod(value, modulo);
}
constexpr float TransverbDSP::interpolateHermite(float const* data, double readaddress,
int arraysize, int writeaddress) {
int posMinus1 = 0, posPlus1 = 0, posPlus2 = 0;
auto const pos = static_cast<int>(readaddress);
auto const posFract = static_cast<float>(readaddress - static_cast<double>(pos));
// because the readers and writer are not necessarily aligned,
// upcoming or previous samples could be discontiguous, in which case
// just "interpolate" with repeated samples
switch (mod_bipolar(writeaddress - pos, arraysize)) {
case 0: // the previous sample is bogus
posMinus1 = pos;
posPlus1 = (pos + 1) % arraysize;
posPlus2 = (pos + 2) % arraysize;
break;
case 1: // the next 2 samples are bogus
posMinus1 = (pos == 0) ? (arraysize - 1) : (pos - 1);
posPlus1 = posPlus2 = pos;
break;
case 2: // the sample 2 steps ahead is bogus
posMinus1 = (pos == 0) ? (arraysize - 1) : (pos - 1);
posPlus1 = posPlus2 = (pos + 1) % arraysize;
break;
default: // everything's cool
posMinus1 = (pos == 0) ? (arraysize - 1) : (pos - 1);
posPlus1 = (pos + 1) % arraysize;
posPlus2 = (pos + 2) % arraysize;
break;
}
float const a = ((3.0f * (data[pos] - data[posPlus1])) -
data[posMinus1] + data[posPlus2]) * 0.5f;
float const b = (2.0f * data[posPlus1]) + data[posMinus1] -
(2.5f * data[pos]) - (data[posPlus2] * 0.5f);
float const c = (data[posPlus1] - data[posMinus1]) * 0.5f;
return (((a * posFract) + b) * posFract + c) * posFract + data[pos];
}
/*
constexpr float TransverbDSP::interpolateHermitePostLowpass(float const* data, float address) {
auto const pos = static_cast<int>(address);
float const posFract = address - static_cast<float>(pos);
float const a = ((3.0f * (data[1] - data[2])) -
data[0] + data[3]) * 0.5f;
float const b = (2.0f * data[2]) + data[0] -
(2.5f * data[1]) - (data[3] * 0.5f);
float const c = (data[2] - data[0]) * 0.5f;
return (((a * posFract) + b) * posFract + c) * posFract + data[1];
}
*/
constexpr float TransverbDSP::interpolateLinear(float const* data, double readaddress,
int arraysize/*, int writeaddress*/) {
auto const pos = static_cast<int>(readaddress);
#if 0
if (mod_bipolar(writeaddress - pos, arraysize) == 1) {
// the upcoming sample is not contiguous because
// the write head is about to write to it
return data[pos];
}
#endif
auto const posPlus1 = (pos + 1) % arraysize;
return interpolateLinear(data[pos], data[posPlus1], readaddress);
}