SAF
Loading...
Searching...
No Matches
safmex_qmf.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
25#include "safmex.h"
26
27/* ===================================================================== */
28/* Config */
29/* ===================================================================== */
30
31#define NUM_INPUT_ARGS_CREATE ( 7 )
32const MEX_DATA_TYPES inputDataTypes_create[NUM_INPUT_ARGS_CREATE] = {
33 SM_INT32,
34 SM_INT32,
35 SM_INT32,
36 SM_INT32,
40};
41#define NUM_INPUT_ARGS_FWD ( 1 )
42#define NUM_OUTPUT_ARGS_FWD ( 1 )
43#define NUM_INPUT_ARGS_BKWD ( 1 )
44#define NUM_OUTPUT_ARGS_BKWD ( 1 )
45const MEX_DATA_TYPES inputDataTypes_fwd[NUM_INPUT_ARGS_FWD] = {
47};
48const MEX_DATA_TYPES inputDataTypes_bkwd[NUM_INPUT_ARGS_BKWD] = {
50};
51const MEX_DATA_TYPES outputDataTypes_fwd[NUM_OUTPUT_ARGS_FWD] = {
53};
54const MEX_DATA_TYPES outputDataTypes_bkwd[NUM_OUTPUT_ARGS_BKWD] = {
56};
57
58
59/* ===================================================================== */
60/* Vars */
61/* ===================================================================== */
62
63/* user arguments */
64int nCHin; /* Number of input channels */
65int nCHout; /* Number of output channels */
66int hopsize; /* Hop size, in samples */
67int blocksize; /* time domain samples to process at a time */
68int hybridmode; /* 0: disabled, 1: hybrid-filtering enabled */
69int formatFlag; /* 0: nBands x nCH x time, 1: time x nCH x nBands */
70float fs; /* sampling rate */
71
72/* internal parameters */
73void* hQMF = NULL; /* qmf handle */
74QMF_FDDATA_FORMAT format; /* enum cast of "formatFlag" */
75float* freqVector = NULL;
76int nBands;
77int procDelay;
78int timeSlots;
79float** dataTD_in = NULL;
80float** dataTD_out = NULL;
81float_complex*** dataFD_in = NULL;
82float_complex*** dataFD_out = NULL;
83
84
85/* ===================================================================== */
86/* MEX Wrapper */
87/* ===================================================================== */
88
89void mexFunction
90(
91 int nlhs, /* Number of output argments */
92 mxArray *plhs[], /* Pointers for output arguments */
93 int nrhs, /* Number of input argments */
94 const mxArray *prhs[] /* Pointers for input arguments */
95)
96{
97 /* mex variables */
98 int nDims;
99 int *pDims = NULL;
100
101 /* DESTROY */
102 if(nrhs == 0){
103 if(hQMF!=NULL){
104 mexPrintf("Destroying QMF filterbank.\n");
105 qmf_destroy(&hQMF);
106 free(freqVector); freqVector = NULL;
107 free(dataTD_in); dataTD_in = NULL;
108 free(dataFD_in); dataFD_in = NULL;
109 free(dataTD_out); dataTD_out = NULL;
110 free(dataFD_out); dataFD_out = NULL;
111 hQMF = NULL;
112 }
113 else
114 mexPrintf("QMF filterbank is already dead!\n");
115 }
116
117 /* CREATE */
118 else if(nrhs==NUM_INPUT_ARGS_CREATE && (nlhs==0 || nlhs==1 || nlhs==2)){
119 if(hQMF!=NULL)
120 mexErrMsgIdAndTxt("MyToolbox:inputError","safmex_qmf is already initialised! First destroy it if you want to change its configuration.");
121
122 /* Check input argument datatypes are as expected */
123 checkArgDataTypes((mxArray**)prhs, (MEX_DATA_TYPES*)inputDataTypes_create, NUM_INPUT_ARGS_CREATE);
124
125 /* Copy user arguments */
126 nCHin = (int)mxGetScalar(prhs[0]);
127 nCHout = (int)mxGetScalar(prhs[1]);
128 hopsize = (int)mxGetScalar(prhs[2]);
129 blocksize = (int)mxGetScalar(prhs[3]);
130 hybridmode = (int)mxGetScalar(prhs[4]);
131 formatFlag = (int)mxGetScalar(prhs[5]);
132 fs = (float)mxGetScalar(prhs[6]);
133 switch(formatFlag){
134 case 0: format = QMF_BANDS_CH_TIME; break;
135 case 1: format = QMF_TIME_CH_BANDS; break;
136 default:
137 mexErrMsgIdAndTxt("MyToolbox:inputError","the value of the fifth argument should be 0 or 1");
138 }
139
140 /* Extra checks */
141 if( !(hybridmode==0 || hybridmode==1) )
142 mexErrMsgIdAndTxt("MyToolbox:inputError","'hybridmode' should be 0 (disabled) or 1 (enabled)");
143 if( !(formatFlag==0 || formatFlag==1) )
144 mexErrMsgIdAndTxt("MyToolbox:inputError","'formatFlag' should be 0 (bands x channels x time) or 1 (time x channels x bands)");
145 if( !(hopsize==4 || hopsize==8 || hopsize==16 || hopsize==32 || hopsize==64 || hopsize==128) )
146 mexErrMsgIdAndTxt("MyToolbox:inputError","the 'hopsize' should be 4, 8, 16, 32, 64, or 128");
147 if( blocksize % hopsize != 0)
148 mexErrMsgIdAndTxt("MyToolbox:inputError","'blocksize' must be a multiple of 'hopsize'");
149
150 /* Create an instance of the qmf filterbank */
151 timeSlots = blocksize/hopsize;
152 qmf_create(&hQMF, nCHin, nCHout, hopsize, hybridmode, format);
153 nBands = qmf_getNBands(hQMF);
154 procDelay = qmf_getProcDelay(hQMF);
155 freqVector = malloc1d(nBands*sizeof(float));
156 qmf_getCentreFreqs(hQMF, fs, nBands, freqVector);
157
158 /* Allocate buffers */
159 dataTD_in = (float**)malloc2d(nCHin, blocksize, sizeof(float));
160 dataTD_out = (float**)malloc2d(nCHout, blocksize, sizeof(float));
161 switch(formatFlag){
162 case 0:
163 dataFD_in = (float_complex***)malloc3d(nBands, nCHin, timeSlots, sizeof(float_complex));
164 dataFD_out = (float_complex***)malloc3d(nBands, nCHout, timeSlots, sizeof(float_complex));
165 break;
166 case 1:
167 dataFD_in = (float_complex***)malloc3d(timeSlots, nCHin, nBands, sizeof(float_complex));
168 dataFD_out = (float_complex***)malloc3d(timeSlots, nCHout, nBands, sizeof(float_complex));
169 break;
170 }
171
172 /* (optional) output frequency vector and processing delay */
173 if(nlhs>0){
174 nDims = 2;
175 pDims = realloc1d(pDims, 2*sizeof(int));
176 pDims[0] = nBands;
177 pDims[1] = 1;
178 SAFsingle2MEXdouble(freqVector, nDims, pDims, &plhs[0]);
179 }
180 if(nlhs>1){
181 plhs[1] = mxCreateDoubleScalar(procDelay);
182 }
183
184 /* Mainly just for debugging... */
185 mexPrintf("Creating QMF filterbank:");
186 snprintf(message, MSG_STR_LENGTH, " %d input channels,", nCHin); mexPrintf(message);
187 snprintf(message, MSG_STR_LENGTH, " %d output channels,", nCHout); mexPrintf(message);
188 snprintf(message, MSG_STR_LENGTH, " %d hopsize,", hopsize); mexPrintf(message);
189 snprintf(message, MSG_STR_LENGTH, " %d blocksize,", blocksize); mexPrintf(message);
190 if(hybridmode)
191 mexPrintf(" hybrid mode enabled,");
192 else
193 mexPrintf(" hybrid mode disabled,");
194 if(formatFlag)
195 mexPrintf(" format: time x channels x bands.\n");
196 else
197 mexPrintf(" format: bands x channels x time.\n");
198 }
199
200 /* TRANSFORM */
201 else if(nrhs == 1 && nlhs == 1){
202 if(hQMF==NULL)
203 mexErrMsgIdAndTxt("MyToolbox:inputError","safmex_qmf is uninitialised!");
204
205 /* Find dimensionality of input */
206 mwSize nDims_mx;
207 const mwSize *pDims_mx;
208 nDims_mx = mxGetNumberOfDimensions(prhs[0]);
209 pDims_mx = mxGetDimensions(prhs[0]);
210
211 /* FORWARD */
212 if(!mxIsComplex(prhs[0])){
213 /* Check input argument datatypes are as expected */
214 checkArgDataTypes((mxArray**)prhs, (MEX_DATA_TYPES*)inputDataTypes_fwd, NUM_INPUT_ARGS_FWD);
215
216 /* extra checks */
217 if( !(pDims_mx[0] == (mwSize)nCHin) ){
218 snprintf(message, MSG_STR_LENGTH, "Was expecting %d input channels.", nCHin);
219 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
220 }
221 if( !(pDims_mx[1] == (mwSize)blocksize) ){
222 snprintf(message, MSG_STR_LENGTH, "Was expecting a block size of %d samples.", blocksize);
223 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
224 }
225
226 /* QMF analysis */
227 MEXdouble2SAFsingle(prhs[0], &FLATTEN2D(dataTD_in), &nDims, &pDims);
228 qmf_analysis(hQMF, dataTD_in, blocksize, dataFD_in);
229
230 /* output */
231 nDims = 3;
232 pDims = realloc1d(pDims, nDims*sizeof(int));
233 switch(formatFlag){
234 case 0: pDims[0] = nBands; pDims[1] = nCHin; pDims[2] = timeSlots; break;
235 case 1: pDims[0] = timeSlots; pDims[1] = nCHin; pDims[2] = nBands; break;
236 }
237 SAFsingle2MEXdouble_complex(FLATTEN3D(dataFD_in), nDims, pDims, &plhs[0]);
238
239 /* Check output argument datatypes are as expected */
240 checkArgDataTypes((mxArray**)plhs, (MEX_DATA_TYPES*)outputDataTypes_fwd, NUM_OUTPUT_ARGS_FWD);
241 }
242
243 /* BACKWARD */
244 else if(mxIsComplex(prhs[0])){
245 /* Check input argument datatypes are as expected */
246 checkArgDataTypes((mxArray**)prhs, (MEX_DATA_TYPES*)inputDataTypes_bkwd, NUM_INPUT_ARGS_BKWD);
247
248 /* extra checks */
249 if( !(pDims_mx[0] == (mwSize)nBands) && formatFlag==0 ){
250 snprintf(message, MSG_STR_LENGTH, "Was expecting %d bands.", nBands);
251 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
252 }
253 if( !(pDims_mx[0] == (mwSize)timeSlots) && formatFlag==1 ){
254 snprintf(message, MSG_STR_LENGTH, "Was expecting %d down-sampled time indices.", timeSlots);
255 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
256 }
257 if( !(pDims_mx[1] == (mwSize)nCHout) ){
258 snprintf(message, MSG_STR_LENGTH, "Was expecting %d input channels.", nCHout);
259 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
260 }
261 if( !(pDims_mx[2] == (mwSize)timeSlots) && formatFlag==0 ){
262 snprintf(message, MSG_STR_LENGTH, "Was expecting %d down-sampled time indices.", timeSlots);
263 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
264 }
265 if( !(pDims_mx[2] == (mwSize)nBands) && formatFlag==1 ){
266 snprintf(message, MSG_STR_LENGTH, "Was expecting %d bands.", nBands);
267 mexErrMsgIdAndTxt("MyToolbox:inputError", message);
268 }
269
270 /* QMF synthesis */
271 MEXdouble2SAFsingle_complex(prhs[0], &FLATTEN3D(dataFD_out), &nDims, &pDims);
272 qmf_synthesis(hQMF, dataFD_out, blocksize, dataTD_out);
273
274 /* output */
275 nDims = 2;
276 pDims = realloc1d(pDims, nDims*sizeof(int));
277 pDims[0] = nCHout;
278 pDims[1] = blocksize;
279 SAFsingle2MEXdouble(FLATTEN2D(dataTD_out), nDims, pDims, &plhs[0]);
280
281 /* Check output argument datatypes are as expected */
282 checkArgDataTypes((mxArray**)plhs, (MEX_DATA_TYPES*)outputDataTypes_bkwd, NUM_OUTPUT_ARGS_BKWD);
283 }
284 else
285 mexErrMsgIdAndTxt("MyToolbox:inputError","Unrecognised input/output configuration, refer to help instructions.");
286 }
287
288 /* ERROR */
289 else
290 mexErrMsgIdAndTxt("MyToolbox:inputError","Unrecognised input/output configuration, refer to help instructions.");
291}
void qmf_getCentreFreqs(void *const hQMF, float fs, int nBands, float *centreFreq)
Computes the QMF/hybrid-QMF centre frequencies.
QMF_FDDATA_FORMAT
Options for how the frequency domain data is permuted when using qmf.
int qmf_getProcDelay(void *const hQMF)
Returns the processing delay in samples.
void qmf_synthesis(void *const hQMF, float_complex ***dataFD, int framesize, float **dataTD)
Performs QMF synthesis of the input frequency-domain signals.
void qmf_analysis(void *const hQMF, float **dataTD, int framesize, float_complex ***dataFD)
Performs QMF analysis of the input time-domain signals.
void qmf_destroy(void **const phQMF)
Destroys an instance of the qmf filterbank.
int qmf_getNBands(void *const hQMF)
Returns the number of frequency bands.
void qmf_create(void **const phQMF, int nCHin, int nCHout, int hopsize, int hybridmode, QMF_FDDATA_FORMAT format)
Creates an instance of the qmf filterbank.
@ QMF_BANDS_CH_TIME
nBands x nChannels x nTimeHops
@ QMF_TIME_CH_BANDS
nTimeHops x nChannels x nBands
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 * realloc1d(void *ptr, size_t dim1_data_size)
1-D realloc (same as realloc, but with error checking)
Definition md_malloc.c:79
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
#define FLATTEN3D(A)
Use this macro when passing a 3-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:72
Main include header for safmex.
void MEXdouble2SAFsingle(const mxArray *in, float **out, int *nDims, int **pDims)
Convert a mex double-precision array into single precision array for SAF.
Definition safmex.h:151
#define MSG_STR_LENGTH
Warning/error message character length.
Definition safmex.h:28
char message[MSG_STR_LENGTH]
Current warning/error message.
Definition safmex.h:29
MEX_DATA_TYPES
Supported SAF/MEX data conversions.
Definition safmex.h:33
@ SM_DOUBLE_REAL
Scalar, real valued; 1 x 1.
Definition safmex.h:37
@ SM_INT32
Integer; 1 x 1.
Definition safmex.h:34
@ SM_DOUBLE_REAL_1D_OR_2D
Real 2-D matrix or 1-D vector; N x M | N x 1.
Definition safmex.h:41
@ SM_DOUBLE_COMPLEX_3D
Complex 3-D matrix; N x M x K.
Definition safmex.h:46
void SAFsingle2MEXdouble(float *in, int nDims, int *dims, mxArray **out)
Convert a single precision array used by SAF to mex double-precision array.
Definition safmex.h:309
void MEXdouble2SAFsingle_complex(const mxArray *in, float_complex **out, int *nDims, int **pDims)
Convert a mex double-precision array into single precision array for SAF (complex-valued)
Definition safmex.h:199
void SAFsingle2MEXdouble_complex(float_complex *in, int nDims, int *dims, mxArray **out)
Convert a single precision array used by SAF to mex double-precision array (complex valued)
Definition safmex.h:343
void checkArgDataTypes(mxArray **hData, MEX_DATA_TYPES *dataTypes, int nArgs)
Helper function to check the format of the input/output arguments are as expected.
Definition safmex.h:64