summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour/dsp_filter.h
blob: b7be41095056f78e59710563bd6a574c77c4b49b (plain)
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*
 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */
#ifndef _dsp_filter_h_
#define _dsp_filter_h_

#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <glib.h>
#include <glibmm.h>
#include <fftw3.h>

#include "pbd/malign.h"

#include "ardour/buffer_set.h"
#include "ardour/chan_mapping.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"

namespace ARDOUR { namespace DSP {

	/** C/C++ Shared Memory
	 *
	 * A convenience class representing a C array of float[] or int32_t[]
	 * data values. This is useful for lua scripts to perform DSP operations
	 * directly using C/C++ with CPU Hardware acceleration.
	 *
	 * Access to this memory area is always 4 byte aligned. The data
	 * is interpreted either as float or as int.
	 *
	 * This memory area can also be shared between different instances
	 * or the same lua plugin (DSP, GUI).
	 *
	 * Since memory allocation is not realtime safe it should be
	 * allocated during dsp_init() or dsp_configure().
	 * The memory is free()ed automatically when the lua instance is
	 * destroyed.
	 */
	class DspShm {
		public:
			DspShm (size_t s = 0)
				: _data (0)
				, _size (0)
			{
				assert (sizeof(float) == sizeof (int32_t));
				assert (sizeof(float) == sizeof (int));
				allocate (s);
			}

			~DspShm () {
				cache_aligned_free (_data);
			}

			/** [re] allocate memory in host's memory space
			 *
			 * @param s size, total number of float or integer elements to store.
			 */
			void allocate (size_t s) {
				if (s == _size) { return; }
				cache_aligned_free (_data);
				cache_aligned_malloc ((void**) &_data, sizeof (float) * s);
				if (_data) { _size = s; }
			}

			/** clear memory (set to zero) */
			void clear () {
				memset (_data, 0, sizeof(float) * _size);
			}

			/** access memory as float array
			 *
			 * @param off offset in shared memory region
			 * @returns float[]
			 */
			float* to_float (size_t off) {
				if (off >= _size) { return 0; }
				return &(((float*)_data)[off]);
			}

			/** access memory as integer array
			 *
			 * @param off offset in shared memory region
			 * @returns int_32_t[]
			 */
			int32_t* to_int (size_t off) {
				if (off >= _size) { return 0; }
				return &(((int32_t*)_data)[off]);
			}

			/** atomically set integer at offset
			 *
			 * This involves a memory barrier. This call
			 * is intended for buffers which are
			 * shared with another instance.
			 *
			 * @param off offset in shared memory region
			 * @param val value to set
			 */
			void atomic_set_int (size_t off, int32_t val) {
				g_atomic_int_set (&(((int32_t*)_data)[off]), val);
			}

			/** atomically read integer at offset
			 *
			 * This involves a memory barrier. This call
			 * is intended for buffers which are
			 * shared with another instance.
			 *
			 * @param off offset in shared memory region
			 * @returns value at offset
			 */
			int32_t atomic_get_int (size_t off) {
				return g_atomic_int_get (&(((int32_t*)_data)[off]));
			}

		private:
			void* _data;
			size_t _size;
	};

	/** lua wrapper to memset() */
	void memset (float *data, const float val, const uint32_t n_samples);
	/** matrix multiply
	 * multiply every sample of `data' with the corresponding sample at `mult'.
	 *
	 * @param data multiplicand
	 * @param mult multiplicand
	 * @param n_samples number of samples in data and mmult
	 */
	void mmult (float *data, float *mult, const uint32_t n_samples);
	/** calculate peaks
	 *
	 * @param data data to analyze
	 * @param min result, minimum value found in range
	 * @param max result, max value found in range
	 * @param n_samples number of samples to analyze
	 */
	void peaks (const float *data, float &min, float &max, uint32_t n_samples);

	/** non-linear power-scale meter deflection
	 *
	 * @param power signal power (dB)
	 * @returns deflected value
	 */
	float log_meter (float power);
	/** non-linear power-scale meter deflection
	 *
	 * @param coeff signal value
	 * @returns deflected value
	 */
	float log_meter_coeff (float coeff);

	void process_map (BufferSet* bufs,
	                  const ChanMapping& in,
	                  const ChanMapping& out,
	                  pframes_t nframes, framecnt_t offset,
	                  const DataType&);

	/** 1st order Low Pass filter */
	class LIBARDOUR_API LowPass {
		public:
			/** instantiate a LPF
			 *
			 * @param samplerate samplerate
			 * @param freq cut-off frequency
			 */
			LowPass (double samplerate, float freq);
			/** process audio data
			 *
			 * @param data pointer to audio-data
			 * @param n_samples number of samples to process
			 */
			void proc (float *data, const uint32_t n_samples);
			/** filter control data
			 *
			 * This is useful for parameter smoothing.
			 *
			 * @param data pointer to control-data array
			 * @param val target value
			 * @param array length
			 */
			void ctrl (float *data, const float val, const uint32_t n_samples);
			/** update filter cut-off frequency
			 *
			 * @param freq cut-off frequency
			 */
			void set_cutoff (float freq);
			/** reset filter state */
			void reset () { _z =  0.f; }
		private:
			float _rate;
			float _z;
			float _a;
	};

	/** Biquad Filter */
	class LIBARDOUR_API Biquad {
		public:
			enum Type {
				LowPass,
				HighPass,
				BandPassSkirt,
				BandPass0dB,
				Notch,
				AllPass,
				Peaking,
				LowShelf,
				HighShelf
			};

			/** Instantiate Biquad Filter
			 *
			 * @param samplerate Samplerate
			 */
			Biquad (double samplerate);
			Biquad (const Biquad &other);

			/** process audio data
			 *
			 * @param data pointer to audio-data
			 * @param n_samples number of samples to process
			 */
			void run (float *data, const uint32_t n_samples);
			/** setup filter, compute coefficients
			 *
			 * @param t filter type (LowPass, HighPass, etc)
			 * @param freq filter frequency
			 * @param Q filter quality
			 * @param gain filter gain
			 */
			void compute (Type t, double freq, double Q, double gain);

			/** setup filter, set coefficients directly */
			void configure (double a1, double a2, double b0, double b1, double b2);

			/** filter transfer function (filter response for spectrum visualization)
			 * @param freq frequency
			 * @return gain at given frequency in dB (clamped to -120..+120)
			 */
			float dB_at_freq (float freq) const;

			/** reset filter state */
			void reset () { _z1 = _z2 = 0.0; }
		private:
			double _rate;
			float  _z1, _z2;
			double _a1, _a2;
			double _b0, _b1, _b2;
	};

	class LIBARDOUR_API FFTSpectrum {
		public:
			FFTSpectrum (uint32_t window_size, double rate);
			~FFTSpectrum ();

			/** set data to be analyzed and pre-process with hanning window
			 * n_samples + offset must not be larger than the configured window_size
			 *
			 * @param data raw audio data
			 * @param n_samples number of samples to write to analysis buffer
			 * @param offset destination offset
			 */
			void set_data_hann (float const * const data, const uint32_t n_samples, const uint32_t offset = 0);

			/** process current data in buffer */
			void execute ();

			/** query
			 * @param bin the frequency bin 0 .. window_size / 2
			 * @param norm gain factor (set equal to @bin for 1/f normalization)
			 * @return signal power at given bin (in dBFS)
			 */
			float power_at_bin (const uint32_t bin, const float norm = 1.f) const;

			float freq_at_bin (const uint32_t bin) const {
				return bin * _fft_freq_per_bin;
			}

		private:
			static Glib::Threads::Mutex fft_planner_lock;
			float* hann_window;

			void init (uint32_t window_size, double rate);
			void reset ();

			uint32_t _fft_window_size;
			uint32_t _fft_data_size;
			double   _fft_freq_per_bin;

			float* _fft_data_in;
			float* _fft_data_out;
			float* _fft_power;

			fftwf_plan _fftplan;
	};

} } /* namespace */
#endif