1
+ #pragma once
2
+
3
+ #include " AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT
4
+ #include " AudioTools/CoreAudio/AudioOutput.h"
5
+ #include " AudioTools/CoreAudio/StreamCopy.h"
6
+
7
+ namespace audio_tools {
8
+
9
+ /* *
10
+ * @brief Common configuration for FFT effects
11
+ * @ingroup transform
12
+ * @author phil schatzmann
13
+ */
14
+ struct FFTEffectConfig : public AudioInfo {
15
+ int length = 1024 ;
16
+ int stride = 512 ;
17
+ WindowFunction *window_function = &buffered_window;
18
+
19
+ protected:
20
+ Hann hann;
21
+ BufferedWindow buffered_window{&hann};
22
+ };
23
+
24
+ /* **
25
+ * @brief Abstract class for common Logic for FFT based effects. The effect is
26
+ * applied after the fft to the frequency domain before executing the ifft.
27
+ * @ingroup transform
28
+ * @author phil schatzmann
29
+ */
30
+
31
+ class FFTEffect : public AudioOutput {
32
+ public:
33
+ FFTEffect (Print &out) {
34
+ p_out = &out;
35
+ fft_cfg.ref = this ;
36
+ }
37
+
38
+ FFTEffectConfig defaultConfig () {
39
+ FFTEffectConfig c;
40
+ return c;
41
+ }
42
+
43
+ bool begin (FFTEffectConfig info) {
44
+ setAudioInfo (info);
45
+ fft_cfg.length = info.length ;
46
+ fft_cfg.stride = info.stride ;
47
+ fft_cfg.window_function = info.window_function ;
48
+ return begin ();
49
+ }
50
+
51
+ bool begin () override {
52
+ // copy result to output
53
+ copier.begin (*p_out, fft);
54
+
55
+ // setup fft
56
+ fft_cfg.copyFrom (audioInfo ());
57
+ fft_cfg.length = 1024 ;
58
+ fft_cfg.stride = 512 ;
59
+ fft_cfg.window_function = &buffered;
60
+ fft_cfg.callback = effect_callback;
61
+ return fft.begin (fft_cfg);
62
+ }
63
+
64
+ size_t write (const uint8_t *data, size_t len) override {
65
+ return fft.write (data, len);
66
+ }
67
+
68
+ protected:
69
+ Print *p_out = nullptr ;
70
+ AudioRealFFT fft;
71
+ AudioFFTConfig fft_cfg{fft.defaultConfig ()};
72
+ Hann hann;
73
+ BufferedWindow buffered{&hann};
74
+ StreamCopy copier;
75
+
76
+ virtual void effect (AudioFFTBase &fft) = 0;
77
+
78
+ static void effect_callback (AudioFFTBase &fft) {
79
+ FFTEffect *ref = (FFTEffect *)fft.config ().ref ;
80
+ // execute effect
81
+ ref->effect (fft);
82
+ // write ifft to output
83
+ ref->processOutput ();
84
+ }
85
+
86
+ void processOutput () { while (copier.copy ()); }
87
+ };
88
+
89
+ /* *
90
+ * @brief Apply Robotize FFT Effect on frequency domain data. See
91
+ * https://learn.bela.io/tutorials/c-plus-plus-for-real-time-audio-programming/phase-vocoder-part-3/
92
+ * @ingroup transform
93
+ * @author phil schatzmann
94
+ */
95
+ class FFTRobotize : public FFTEffect {
96
+ friend FFTEffect;
97
+
98
+ public:
99
+ FFTRobotize (AudioStream &out) : FFTEffect(out) { addNotifyAudioChange (out); };
100
+ FFTRobotize (AudioOutput &out) : FFTEffect(out) { addNotifyAudioChange (out); };
101
+ FFTRobotize (Print &out) : FFTEffect(out) {};
102
+
103
+ protected:
104
+ // / Robotise the output
105
+ void effect (AudioFFTBase &fft) {
106
+ FFTBin bin;
107
+ for (int n = 0 ; n < fft.size (); n++) {
108
+ float amplitude = fft.magnitudeFast (n);
109
+ // update new bin value
110
+ bin.real = amplitude;
111
+ bin.img = 0 ;
112
+ fft.setBin (n, bin);
113
+ }
114
+ }
115
+ };
116
+
117
+ /* *
118
+ * @brief Apply Robotize FFT Effect on frequency domain data. See
119
+ * https://learn.bela.io/tutorials/c-plus-plus-for-real-time-audio-programming/phase-vocoder-part-3/
120
+ * @ingroup transform
121
+ * @author phil schatzmann
122
+ */
123
+ class FFTWhisper : public FFTEffect {
124
+ friend FFTEffect;
125
+
126
+ public:
127
+ FFTWhisper (AudioStream &out) : FFTEffect(out) { addNotifyAudioChange (out); };
128
+ FFTWhisper (AudioOutput &out) : FFTEffect(out) { addNotifyAudioChange (out); };
129
+ FFTWhisper (Print &out) : FFTEffect(out) {};
130
+
131
+ protected:
132
+ // / Robotise the output
133
+ void effect (AudioFFTBase &fft) {
134
+ FFTBin bin;
135
+ for (int n = 0 ; n < fft.size (); n++) {
136
+ float amplitude = fft.magnitudeFast (n);
137
+ float phase = rand () / (float )RAND_MAX * 2 .f * M_PI;
138
+
139
+ // update new bin value
140
+ bin.real = cosf (phase) * amplitude;
141
+ bin.img = sinf (phase) * amplitude;
142
+ fft.setBin (n, bin);
143
+ }
144
+ }
145
+ };
146
+
147
+ /* *
148
+ * @brief Pitch Shift FFT Effect Configuration
149
+ * @ingroup transform
150
+ * @author phil schatzmann
151
+ */
152
+
153
+ struct FFTPitchShiftConfig : public FFTEffectConfig {
154
+ int shift = 1 ;
155
+ };
156
+
157
+ /* *
158
+ * @brief Apply Pitch Shift FFT Effect on frequency domain data: we just move
159
+ * the bins up or down
160
+ * @ingroup transform
161
+ * @author phil schatzmann
162
+ */
163
+ class FFTPitchShift : public FFTEffect {
164
+ friend FFTEffect;
165
+
166
+ public:
167
+ FFTPitchShift (AudioStream &out) : FFTEffect(out) {
168
+ addNotifyAudioChange (out);
169
+ };
170
+ FFTPitchShift (AudioOutput &out) : FFTEffect(out) {
171
+ addNotifyAudioChange (out);
172
+ };
173
+ FFTPitchShift (Print &out) : FFTEffect(out) {};
174
+
175
+ FFTPitchShiftConfig defaultConfig () {
176
+ FFTPitchShiftConfig result;
177
+ return result;
178
+ }
179
+
180
+ bool begin (FFTPitchShiftConfig psConfig) {
181
+ setShift (psConfig.shift );
182
+ return FFTEffect::begin (psConfig);
183
+ }
184
+
185
+ // / defines how many bins should be shifted up (>0) or down (<0);
186
+ void setShift (int bins) { shift = bins; }
187
+
188
+ protected:
189
+ int shift = 1 ;
190
+
191
+ // / Pitch Shift
192
+ void effect (AudioFFTBase &fft) {
193
+ FFTBin bin;
194
+ int max = fft.size ();
195
+
196
+ if (shift < 0 ) {
197
+ // copy bins: left shift
198
+ for (int n = -shift; n < max; n++) {
199
+ int to_bin = n + shift;
200
+ fft.getBin (n, bin);
201
+ fft.setBin (to_bin, bin);
202
+ }
203
+ // clear tail
204
+ bin.clear ();
205
+ for (int n = max + shift; n < max; n++) {
206
+ fft.setBin (n, bin);
207
+ }
208
+ } else if (shift > 0 ) {
209
+ // copy bins: right shift
210
+ for (int n = max - shift; n <= 0 ; n--) {
211
+ int to_bin = n + shift;
212
+ fft.getBin (n, bin);
213
+ fft.setBin (to_bin, bin);
214
+ }
215
+ // clear head
216
+ bin.clear ();
217
+ for (int n = 0 ; n < shift; n++) {
218
+ fft.setBin (n, bin);
219
+ }
220
+ }
221
+ }
222
+ };
223
+
224
+ } // namespace audio_tools
0 commit comments