SAF
Loading...
Searching...
No Matches
decorrelator.c
Go to the documentation of this file.
1/*
2 * Copyright 2020 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
27
29(
30 void ** const phDecor
31)
32{
34 *phDecor = (void*)pData;
35
36 /* default user parameters */
37 pData->nChannels = 1;
38 pData->enableTransientDucker = 0;
39 pData->decorAmount = 1.0f;
40 pData->compensateLevel = 0;
41
42 /* afSTFT stuff */
43 pData->fs = 48000.0f;
44 pData->hSTFT = NULL;
45 pData->InputFrameTD = (float**)malloc2d(MAX_NUM_CHANNELS, DECORRELATOR_FRAME_SIZE, sizeof(float));
46 pData->OutputFrameTD = (float**)malloc2d(MAX_NUM_CHANNELS, DECORRELATOR_FRAME_SIZE, sizeof(float));
47 pData->InputFrameTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_CHANNELS, TIME_SLOTS, sizeof(float_complex));
48 pData->OutputFrameTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_CHANNELS, TIME_SLOTS, sizeof(float_complex));
49 pData->transientFrameTF = (float_complex***)malloc3d(HYBRID_BANDS, MAX_NUM_CHANNELS, TIME_SLOTS, sizeof(float_complex));
50
51 /* codec data */
52 pData->hDecor = NULL;
53 pData->hDucker = NULL;
54 pData->new_nChannels = pData->nChannels;
55 pData->progressBar0_1 = 0.0f;
57 strcpy(pData->progressBarText,"");
58
59 /* flags */
62}
63
65(
66 void ** const phDecor
67)
68{
69 decorrelator_data *pData = (decorrelator_data*)(*phDecor);
70
71 if (pData != NULL) {
72 /* not safe to free memory during intialisation/processing loop */
73 while (pData->codecStatus == CODEC_STATUS_INITIALISING ||
75 SAF_SLEEP(10);
76 }
77
78 /* free afSTFT and buffers */
79 if(pData->hSTFT!=NULL)
80 afSTFT_destroy(&(pData->hSTFT));
81 free(pData->InputFrameTD);
82 free(pData->OutputFrameTD);
83 free(pData->InputFrameTF);
84 free(pData->OutputFrameTF);
85 free(pData->transientFrameTF);
86 free(pData->progressBarText);
87
90
91 free(pData);
92 pData = NULL;
93 *phDecor = NULL;
94 }
95}
96
98(
99 void * const hDecor,
100 int sampleRate
101)
102{
103 decorrelator_data *pData = (decorrelator_data*)(hDecor);
104
105 /* define frequency vector */
106 pData->fs = sampleRate;
107 afSTFT_getCentreFreqs(pData->hSTFT, (float)sampleRate, HYBRID_BANDS, pData->freqVector);
108 if(pData->hDecor!=NULL)
110}
111
113(
114 void* const hDecor
115)
116{
117 decorrelator_data *pData = (decorrelator_data*)(hDecor);
118 int nChannels;
119
121 return; /* re-init not required, or already happening */
122 while (pData->procStatus == PROC_STATUS_ONGOING){
123 /* re-init required, but we need to wait for the current processing loop to end */
124 pData->codecStatus = CODEC_STATUS_INITIALISING; /* indicate that we want to init */
125 SAF_SLEEP(10);
126 }
127
128 /* for progress bar */
130 strcpy(pData->progressBarText,"Preparing Decorrelators");
131 pData->progressBar0_1 = 0.0f;
132
133 /* (Re)Initialise afSTFT */
134 nChannels = pData->new_nChannels;
135 if(pData->hSTFT==NULL)
136 afSTFT_create(&(pData->hSTFT), nChannels, nChannels, HOP_SIZE, 0, 1, AFSTFT_BANDS_CH_TIME);
137 else if(pData->nChannels != nChannels) {/* Or change the number of channels */
138 afSTFT_channelChange(pData->hSTFT, nChannels, nChannels);
140 }
141 pData->nChannels = nChannels;
142
143 /* Init transient ducker */
145 transientDucker_create(&(pData->hDucker), nChannels, HYBRID_BANDS);
146
147 /* Init decorrelator */
148 const int orders[4] = {20, 15, 6, 3}; /* 20th order up to 700Hz, 15th->2.4kHz, 6th->4kHz, 3rd->12kHz, NONE(only delays)->Nyquist */
149 const float freqCutoffs[4] = {600.0f, 2.4e3f, 4.0e3f, 12e3f};
150 //const float freqCutoffs[4] = {900.0f, 6.8e3f, 12e3f, 16e3f};
151 const int maxDelay = 8;
153 latticeDecorrelator_create(&(pData->hDecor), pData->fs, HOP_SIZE, pData->freqVector, HYBRID_BANDS, pData->nChannels, (int*)orders, (float*)freqCutoffs, 4, maxDelay, 0, 0.75f);
154
155 /* done! */
156 strcpy(pData->progressBarText,"Done!");
157 pData->progressBar0_1 = 1.0f;
159}
160
162(
163 void * const hDecor,
164 const float *const * inputs,
165 float* const* const outputs,
166 int nInputs,
167 int nOutputs,
168 int nSamples
169)
170{
171 decorrelator_data *pData = (decorrelator_data*)(hDecor);
172 int ch, i, band, enableTransientDucker, compensateLevel;
173 float decorAmount;
174
175 /* local copies of user parameters */
176 int nCH;
177 nCH = pData->nChannels;
178 decorAmount = pData->decorAmount;
179 enableTransientDucker = pData->enableTransientDucker;
180 compensateLevel = pData->compensateLevel;
181
182 /* Process frame */
183 if (nSamples == DECORRELATOR_FRAME_SIZE && (pData->codecStatus == CODEC_STATUS_INITIALISED) ) {
185
186 /* Load time-domain data */
187 for(i=0; i < SAF_MIN(nCH, nInputs); i++)
189 for(; i<nCH; i++)
190 memset(pData->InputFrameTD[i], 0, DECORRELATOR_FRAME_SIZE * sizeof(float)); /* fill remaining channels with zeros */
191
192 /* Apply time-frequency transform (TFT) */
194
195 /* Apply decorrelation */
196 if(enableTransientDucker){
197 /* remove transients */
198 transientDucker_apply(pData->hDucker, pData->InputFrameTF, TIME_SLOTS, 0.95f, 0.995f, pData->OutputFrameTF, pData->transientFrameTF);
199 /* decorrelate only the residual */
201 }
202 else
204
205 /* Optionally compensate for the level (as they channels wll no longer sum coherently) */
206 if(compensateLevel){
207 for(band=0; band<HYBRID_BANDS; band++)
208 cblas_sscal(/*re+im*/2*nCH*TIME_SLOTS, 0.75f*(float)nCH/(sqrtf((float)nCH)), (float*)FLATTEN2D(pData->OutputFrameTF[band]), 1);
209 }
210
211 /* re-introduce the transient part */
212 if(enableTransientDucker){
213 //scalec = cmplxf(1.0f, 0.0f);//!compensateLevel ? cmplxf(1.25f*(sqrtf((float)nCH)/(float)nCH), 0.0f) : cmplxf(1.0f, 0.0f);
214 for(band=0; band<HYBRID_BANDS; band++)
215 cblas_saxpy(/*re+im*/2*nCH*TIME_SLOTS, 1.0f, (float*)FLATTEN2D(pData->transientFrameTF[band]), 1, (float*)FLATTEN2D(pData->OutputFrameTF[band]), 1);
216 }
217
218 /* Mix thedecorrelated audio with the input non-decorrelated audio */
219 for(band=0; band<HYBRID_BANDS; band++){
220 cblas_sscal(/*re+im*/2*nCH*TIME_SLOTS, decorAmount, (float*)FLATTEN2D(pData->OutputFrameTF[band]), 1);
221 cblas_saxpy(/*re+im*/2*nCH*TIME_SLOTS, 1.0f-decorAmount, (float*)FLATTEN2D(pData->InputFrameTF[band]), 1, (float*)FLATTEN2D(pData->OutputFrameTF[band]), 1);
222 }
223
224 /* inverse-TFT */
226
227 /* Copy to output buffer */
228 for (ch = 0; ch < SAF_MIN(nCH, nOutputs); ch++)
229 utility_svvcopy(pData->OutputFrameTD[ch], DECORRELATOR_FRAME_SIZE, outputs[ch]);
230 for (; ch < nOutputs; ch++)
231 memset(outputs[ch], 0, DECORRELATOR_FRAME_SIZE*sizeof(float));
232 }
233 else
234 for (ch=0; ch < nOutputs; ch++)
235 memset(outputs[ch],0, DECORRELATOR_FRAME_SIZE*sizeof(float));
236
238}
239
240
241/* Set Functions */
242
247
248void decorrelator_setNumberOfChannels(void* const hDecor, int newValue )
249{
250 decorrelator_data *pData = (decorrelator_data*)(hDecor);
251
252 if(newValue!=pData->new_nChannels){
253 pData->new_nChannels = newValue;
255 }
256}
257
258void decorrelator_setDecorrelationAmount(void* const hDecor, float newValue)
259{
260 decorrelator_data *pData = (decorrelator_data*)(hDecor);
261 pData->decorAmount = SAF_CLAMP(newValue, 0.0f, 1.0f);
262}
263
264void decorrelator_setLevelCompensationFlag(void* const hDecor, int newValue)
265{
266 decorrelator_data *pData = (decorrelator_data*)(hDecor);
267 saf_assert(newValue==0 || newValue==1, "newValue is a bool");
268 pData->compensateLevel = newValue;
269}
270
271void decorrelator_setTransientBypassFlag(void* const hDecor, int newValue)
272{
273 decorrelator_data *pData = (decorrelator_data*)(hDecor);
274 saf_assert(newValue==0 || newValue==1, "newValue is a bool");
275 pData->enableTransientDucker = newValue;
276}
277
278/* Get Functions */
279
284
286{
287 decorrelator_data *pData = (decorrelator_data*)(hDecor);
288 return pData->codecStatus;
289}
290
291float decorrelator_getProgressBar0_1(void* const hDecor)
292{
293 decorrelator_data *pData = (decorrelator_data*)(hDecor);
294 return pData->progressBar0_1;
295}
296
297void decorrelator_getProgressBarText(void* const hDecor, char* text)
298{
299 decorrelator_data *pData = (decorrelator_data*)(hDecor);
300 memcpy(text, pData->progressBarText, PROGRESSBARTEXT_CHAR_LENGTH*sizeof(char));
301}
302
303int decorrelator_getNumberOfChannels(void* const hDecor)
304{
305 decorrelator_data *pData = (decorrelator_data*)(hDecor);
306 return pData->new_nChannels;
307}
308
309float decorrelator_getDecorrelationAmount(void* const hDecor)
310{
311 decorrelator_data *pData = (decorrelator_data*)(hDecor);
312 return pData->decorAmount;
313}
314
316{
317 decorrelator_data *pData = (decorrelator_data*)(hDecor);
318 return pData->compensateLevel;
319}
320
322{
323 decorrelator_data *pData = (decorrelator_data*)(hDecor);
324 return pData->enableTransientDucker;
325}
326
327int decorrelator_getDAWsamplerate(void* const hDecor)
328{
329 decorrelator_data *pData = (decorrelator_data*)(hDecor);
330 return pData->fs;
331}
332
334{
335 return 12*HOP_SIZE;
336}
#define MAX_NUM_CHANNELS
Maximum number of input/output channels supported.
Definition _common.h:230
#define PROGRESSBARTEXT_CHAR_LENGTH
Length of progress bar string.
Definition _common.h:227
@ PROC_STATUS_ONGOING
Codec is processing input audio, and should not be reinitialised at this time.
Definition _common.h:220
@ PROC_STATUS_NOT_ONGOING
Codec is not processing input audio, and may be reinitialised if needed.
Definition _common.h:222
CODEC_STATUS
Current status of the codec.
Definition _common.h:201
@ CODEC_STATUS_NOT_INITIALISED
Codec has not yet been initialised, or the codec configuration has changed.
Definition _common.h:204
@ CODEC_STATUS_INITIALISED
Codec is initialised and ready to process input audio.
Definition _common.h:202
@ CODEC_STATUS_INITIALISING
Codec is currently being initialised, input audio should not be processed.
Definition _common.h:207
void afSTFT_clearBuffers(void *const hSTFT)
Flushes time-domain buffers with zeros.
Definition afSTFTlib.c:519
void afSTFT_create(void **const phSTFT, int nCHin, int nCHout, int hopsize, int lowDelayMode, int hybridmode, AFSTFT_FDDATA_FORMAT format)
Creates an instance of afSTFT.
Definition afSTFTlib.c:143
void afSTFT_backward_knownDimensions(void *const hSTFT, float_complex ***dataFD, int framesize, int dataFD_nCH, int dataFD_nHops, float **dataTD)
Performs backward afSTFT transform (dataFD dimensions are known)
Definition afSTFTlib.c:391
void afSTFT_forward_knownDimensions(void *const hSTFT, float **dataTD, int framesize, int dataFD_nCH, int dataFD_nHops, float_complex ***dataFD)
Performs forward afSTFT transform (dataFD dimensions are known)
Definition afSTFTlib.c:268
void afSTFT_getCentreFreqs(void *const hSTFT, float fs, int nBands, float *freqVector)
Returns current frequency vector.
Definition afSTFTlib.c:546
void afSTFT_destroy(void **const phSTFT)
Destroys an instance of afSTFT.
Definition afSTFTlib.c:199
void afSTFT_channelChange(void *const hSTFT, int new_nCHin, int new_nCHout)
Re-allocates memory to support a change in the number of input/output channels.
Definition afSTFTlib.c:477
@ AFSTFT_BANDS_CH_TIME
nBands x nChannels x nTimeHops
Definition afSTFTlib.h:80
#define TIME_SLOTS
Number of STFT timeslots.
#define HOP_SIZE
STFT hop size.
#define HYBRID_BANDS
Number of frequency bands.
int decorrelator_getLevelCompensationFlag(void *const hDecor)
Returns whether to apply level compensation (0 or 1)
void decorrelator_setDecorrelationAmount(void *const hDecor, float newValue)
Sets the decorrelation amount [0..1].
int decorrelator_getFrameSize(void)
Returns the processing framesize (i.e., number of samples processed with every _process() call )
void decorrelator_refreshParams(void *const hDecor)
Sets intialisation flags to 1, so as to re-initialise all settings/variables (as decorrelator is curr...
int decorrelator_getDAWsamplerate(void *const hDecor)
Returns the DAW/Host sample rate.
int decorrelator_getTransientBypassFlag(void *const hDecor)
Returns whether to bypass decorrelating the transients (0 or 1)
void decorrelator_setLevelCompensationFlag(void *const hDecor, int newValue)
Sets whether to apply level compensation (0 or 1)
void decorrelator_destroy(void **const phDecor)
Destroys an instance of decorrelator.
float decorrelator_getProgressBar0_1(void *const hDecor)
(Optional) Returns current intialisation/processing progress, between 0..1
void decorrelator_getProgressBarText(void *const hDecor, char *text)
(Optional) Returns current intialisation/processing progress text
void decorrelator_setTransientBypassFlag(void *const hDecor, int newValue)
Sets whether to bypass decorrelating the transients (0 or 1)
void decorrelator_init(void *const hDecor, int sampleRate)
Initialises decorrelator with default settings, and samplerate.
void decorrelator_process(void *const hDecor, const float *const *inputs, float *const *const outputs, int nInputs, int nOutputs, int nSamples)
Decorrelates the input signals.
void decorrelator_setNumberOfChannels(void *const hDecor, int newValue)
Sets the number of input/output channels.
CODEC_STATUS decorrelator_getCodecStatus(void *const hDecor)
Returns current codec status, see CODEC_STATUS enum.
int decorrelator_getNumberOfChannels(void *const hDecor)
Returns the number of input/output channels.
void decorrelator_create(void **const phDecor)
Creates an instance of decorrelator.
float decorrelator_getDecorrelationAmount(void *const hDecor)
Returns the decorrelation amount [0..1].
int decorrelator_getProcessingDelay()
Returns the processing delay in samples (may be used for delay compensation features)
void decorrelator_initCodec(void *const hDecor)
Intialises the codec variables, based on current global/user parameters.
void decorrelator_setCodecStatus(void *const hDecor, CODEC_STATUS newStatus)
Sets codec status.
A multi-channel decorrelator.
#define DECORRELATOR_FRAME_SIZE
Framesize, in time-domain samples.
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
#define SAF_CLAMP(a, min, max)
Ensures value "a" is clamped between the "min" and "max" values.
void transientDucker_destroy(void **phDucker)
Destroys an instance of the transient ducker.
void transientDucker_apply(void *hDucker, float_complex ***inFrame, int nTimeSlots, float alpha, float beta, float_complex ***residualFrame, float_complex ***transientFrame)
Applies the transient ducker, returning either the "ducked" input frame, or the transient part of the...
void utility_svvcopy(const float *a, const int len, float *c)
Single-precision, vector-vector copy, i.e.
void latticeDecorrelator_reset(void *hDecor)
Sets the internal buffers to zero.
void latticeDecorrelator_apply(void *hDecor, float_complex ***inFrame, int nTimeSlots, float_complex ***decorFrame)
Applies the lattice all-pass-filter-based multi-channel signal decorrelator.
#define SAF_MIN(a, b)
Returns the minimum of the two values.
void latticeDecorrelator_create(void **phDecor, float fs, int hopsize, float *freqVector, int nBands, int nCH, int *orders, float *freqCutoffs, int nCutoffs, int maxDelay, int lookupOffset, float enComp_coeff)
Creates an instance of the lattice all-pass-filter-based multi-channel signal decorrelator.
void latticeDecorrelator_destroy(void **phDecor)
Destroys an instance of the lattice all-pass-filter-based multi-channel signal decorrelator.
void transientDucker_create(void **phDucker, int nCH, int nBands)
Creates an instance of the transient ducker/extractor.
void ** malloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:89
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void *** malloc3d(size_t dim1, size_t dim2, size_t dim3, size_t data_size)
3-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:151
#define FLATTEN2D(A)
Use this macro when passing a 2-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:65
Main structure for decorrelator.
int fs
host sampling rate
float freqVector[HYBRID_BANDS]
frequency vector for time-frequency transform, in Hz
int enableTransientDucker
1: transient extractor is enabled, 0: disabled
float ** InputFrameTD
Input time-domain signals; MAX_NUM_CHANNELS x DECORRELATOR_FRAME_SIZE.
int new_nChannels
New number of input/output channels (current value will be replaced by this after next re-init)
float progressBar0_1
Current (re)initialisation progress, between [0..1].
CODEC_STATUS codecStatus
see CODEC_STATUS
float_complex *** InputFrameTF
Input time-frequency domain signals; HYBRID_BANDS x MAX_NUM_CHANNELS x TIME_SLOTS.
float decorAmount
The mix between decorrelated signals and the input signals [0..1], 1: fully decorrelated 0: bypassed.
float_complex *** transientFrameTF
Transient time-frequency domain signals; HYBRID_BANDS x MAX_NUM_CHANNELS x TIME_SLOTS.
void * hSTFT
afSTFT handle
char * progressBarText
Current (re)initialisation step, string.
float ** OutputFrameTD
Output time-domain signals; MAX_NUM_CHANNELS x DECORRELATOR_FRAME_SIZE.
void * hDucker
Transient extractor/Ducker handle.
int nChannels
Current number of input/output channels.
int compensateLevel
1: apply a sqrt(nChannels)/nChannels scaling on the output signals, 0: disabled
float_complex *** OutputFrameTF
Output time-frequency domain signals; HYBRID_BANDS x MAX_NUM_CHANNELS x TIME_SLOTS.
PROC_STATUS procStatus
see PROC_STATUS
void * hDecor
Decorrelator handle.