WaveSwissKnife 0.0.1.20101110-x86-cygwin
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +1 -0
- data/ChangeLog +5 -0
- data/Credits +3 -0
- data/LICENSE +31 -0
- data/README +18 -0
- data/ReleaseInfo +8 -0
- data/TODO +2 -0
- data/bin/WSK.rb +14 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.o +0 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.so +0 -0
- data/ext/WSK/AnalyzeUtils/Makefile +149 -0
- data/ext/WSK/AnalyzeUtils/build.rb +18 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.o +0 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.so +0 -0
- data/ext/WSK/ArithmUtils/Makefile +149 -0
- data/ext/WSK/ArithmUtils/build.rb +20 -0
- data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
- data/ext/WSK/FFTUtils/FFTUtils.o +0 -0
- data/ext/WSK/FFTUtils/FFTUtils.so +0 -0
- data/ext/WSK/FFTUtils/Makefile +149 -0
- data/ext/WSK/FFTUtils/build.rb +20 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.o +0 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.so +0 -0
- data/ext/WSK/FunctionUtils/Makefile +149 -0
- data/ext/WSK/FunctionUtils/build.rb +20 -0
- data/ext/WSK/SilentUtils/Makefile +149 -0
- data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
- data/ext/WSK/SilentUtils/SilentUtils.o +0 -0
- data/ext/WSK/SilentUtils/SilentUtils.so +0 -0
- data/ext/WSK/SilentUtils/build.rb +18 -0
- data/ext/WSK/VolumeUtils/Makefile +149 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.o +0 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.so +0 -0
- data/ext/WSK/VolumeUtils/build.rb +20 -0
- data/lib/WSK/Actions/Analyze.rb +176 -0
- data/lib/WSK/Actions/ApplyMap.desc.rb +15 -0
- data/lib/WSK/Actions/ApplyMap.rb +57 -0
- data/lib/WSK/Actions/ApplyVolumeFct.desc.rb +30 -0
- data/lib/WSK/Actions/ApplyVolumeFct.rb +72 -0
- data/lib/WSK/Actions/Compare.desc.rb +25 -0
- data/lib/WSK/Actions/Compare.rb +238 -0
- data/lib/WSK/Actions/ConstantCompare.desc.rb +20 -0
- data/lib/WSK/Actions/ConstantCompare.rb +61 -0
- data/lib/WSK/Actions/Cut.desc.rb +20 -0
- data/lib/WSK/Actions/Cut.rb +60 -0
- data/lib/WSK/Actions/CutFirstSignal.desc.rb +25 -0
- data/lib/WSK/Actions/CutFirstSignal.rb +72 -0
- data/lib/WSK/Actions/DCShifter.desc.rb +15 -0
- data/lib/WSK/Actions/DCShifter.rb +67 -0
- data/lib/WSK/Actions/DrawFct.desc.rb +20 -0
- data/lib/WSK/Actions/DrawFct.rb +59 -0
- data/lib/WSK/Actions/FFT.rb +104 -0
- data/lib/WSK/Actions/GenAllValues.rb +67 -0
- data/lib/WSK/Actions/GenConstant.desc.rb +20 -0
- data/lib/WSK/Actions/GenConstant.rb +56 -0
- data/lib/WSK/Actions/GenSawtooth.rb +57 -0
- data/lib/WSK/Actions/GenSine.desc.rb +20 -0
- data/lib/WSK/Actions/GenSine.rb +73 -0
- data/lib/WSK/Actions/Identity.rb +43 -0
- data/lib/WSK/Actions/Mix.desc.rb +15 -0
- data/lib/WSK/Actions/Mix.rb +149 -0
- data/lib/WSK/Actions/Multiply.desc.rb +15 -0
- data/lib/WSK/Actions/Multiply.rb +73 -0
- data/lib/WSK/Actions/NoiseGate.desc.rb +35 -0
- data/lib/WSK/Actions/NoiseGate.rb +129 -0
- data/lib/WSK/Actions/SilenceInserter.desc.rb +20 -0
- data/lib/WSK/Actions/SilenceInserter.rb +87 -0
- data/lib/WSK/Actions/SilenceRemover.desc.rb +30 -0
- data/lib/WSK/Actions/SilenceRemover.rb +74 -0
- data/lib/WSK/Actions/VolumeProfile.desc.rb +35 -0
- data/lib/WSK/Actions/VolumeProfile.rb +63 -0
- data/lib/WSK/Common.rb +292 -0
- data/lib/WSK/FFT.rb +527 -0
- data/lib/WSK/Functions.rb +770 -0
- data/lib/WSK/Launcher.rb +216 -0
- data/lib/WSK/Maps.rb +29 -0
- data/lib/WSK/Model/CachedBufferReader.rb +151 -0
- data/lib/WSK/Model/Header.rb +133 -0
- data/lib/WSK/Model/InputData.rb +193 -0
- data/lib/WSK/Model/RawReader.rb +78 -0
- data/lib/WSK/Model/WaveReader.rb +91 -0
- data/lib/WSK/OutputInterfaces/DirectStream.rb +146 -0
- data/lib/WSK/RIFFReader.rb +60 -0
- metadata +151 -0
@@ -0,0 +1,662 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
* Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
**/
|
5
|
+
|
6
|
+
#include "ruby.h"
|
7
|
+
#include <math.h>
|
8
|
+
#include <stdio.h>
|
9
|
+
#include <CommonUtils.h>
|
10
|
+
|
11
|
+
#include <gmp.h>
|
12
|
+
|
13
|
+
// Type used to compute FFT calculations
|
14
|
+
typedef long long int tFFTValue;
|
15
|
+
|
16
|
+
// Struct used to convey data among iterators in the completeSumCosSin method
|
17
|
+
typedef struct {
|
18
|
+
int nbrFreq;
|
19
|
+
double* w;
|
20
|
+
tFFTValue* sumCos;
|
21
|
+
tFFTValue* sumSin;
|
22
|
+
int* ptrIdxSum;
|
23
|
+
int nbrChannels;
|
24
|
+
double** cosCache;
|
25
|
+
double** sinCache;
|
26
|
+
} tCompleteSumCosSinStruct;
|
27
|
+
|
28
|
+
// Struct that contains the trigo cache
|
29
|
+
typedef struct {
|
30
|
+
int nbrFreq;
|
31
|
+
double** cosCache;
|
32
|
+
double** sinCache;
|
33
|
+
} tTrigoCache;
|
34
|
+
|
35
|
+
// Struct that contains a C FFT profile
|
36
|
+
typedef struct {
|
37
|
+
int nbrFreq;
|
38
|
+
int nbrChannels;
|
39
|
+
mpf_t** profile;
|
40
|
+
mpf_t maxFFTValue;
|
41
|
+
} tFFTProfile;
|
42
|
+
|
43
|
+
/** Create a ruby object storing the Wi coefficients used to compute the sin and cos sums
|
44
|
+
*
|
45
|
+
* Parameters:
|
46
|
+
* * *iSelf* (_FFTUtils_): The object containing this method
|
47
|
+
* * *iValIdxFirstFreq* (_Integer_): First frequency index to generate the array of Wi
|
48
|
+
* * *iValIdxLastFreq* (_Integer_): Last frequency index to generate the array of Wi
|
49
|
+
* * *iValSampleRate* (_Integer_): The sample rate
|
50
|
+
* Return:
|
51
|
+
* * _Object_: The container of Wi series
|
52
|
+
**/
|
53
|
+
static VALUE fftutils_createWi(
|
54
|
+
VALUE iSelf,
|
55
|
+
VALUE iValIdxFirstFreq,
|
56
|
+
VALUE iValIdxLastFreq,
|
57
|
+
VALUE iValSampleRate) {
|
58
|
+
int lIdxFirstFreq = FIX2INT(iValIdxFirstFreq);
|
59
|
+
int lIdxLastFreq = FIX2INT(iValIdxLastFreq);
|
60
|
+
int lSampleRate = FIX2INT(iValSampleRate);
|
61
|
+
|
62
|
+
// For each frequency index i, we have
|
63
|
+
// Fi = Sum(t=0..N-1, Xt * cos( Wi * t ) )^2 + Sum(t=0..N-1, Xt * sin( Wi * t ) )^2
|
64
|
+
// With N = Number of samples, Xt the sample number t, and Wi = -2*Pi*440*2^(i/12)/S, with S = sample rate
|
65
|
+
double * lW = ALLOC_N(double, lIdxLastFreq-lIdxFirstFreq+1);
|
66
|
+
// Define the common multipler (-880*PI)
|
67
|
+
double lCommonMultiplier = -3520.0*atan2(1.0, 1.0);
|
68
|
+
|
69
|
+
int lIdxFreq;
|
70
|
+
double lDblSampleRate = (double)lSampleRate;
|
71
|
+
for (lIdxFreq = lIdxFirstFreq; lIdxFreq < lIdxLastFreq + 1; ++lIdxFreq) {
|
72
|
+
lW[lIdxFreq-lIdxFirstFreq] = (lCommonMultiplier*(pow(2.0,(((double)lIdxFreq)/12.0))))/lDblSampleRate;
|
73
|
+
}
|
74
|
+
|
75
|
+
// Encapsulate it
|
76
|
+
return Data_Wrap_Struct(rb_cObject, NULL, free, lW);
|
77
|
+
}
|
78
|
+
|
79
|
+
/** Create empty arrays of tFFTValues to be used for sin and cos sums
|
80
|
+
*
|
81
|
+
* Parameters:
|
82
|
+
* * *iSelf* (_FFTUtils_): Self
|
83
|
+
* * *iValNbrFreq* (_Integer_): Number of frequencies to store
|
84
|
+
* * *iValNbrChannels* (_Integer_): Number of channels
|
85
|
+
* Return:
|
86
|
+
* * _Object_: An encapsulated array for computation
|
87
|
+
**/
|
88
|
+
static VALUE fftutils_initSumArray(
|
89
|
+
VALUE iSelf,
|
90
|
+
VALUE iValNbrFreq,
|
91
|
+
VALUE iValNbrChannels) {
|
92
|
+
VALUE rValContainer;
|
93
|
+
int lNbrFreq = FIX2INT(iValNbrFreq);
|
94
|
+
int lNbrChannels = FIX2INT(iValNbrChannels);
|
95
|
+
|
96
|
+
tFFTValue * lSumArray = ALLOC_N(tFFTValue, lNbrFreq*lNbrChannels);
|
97
|
+
// Fill it with 0
|
98
|
+
memset(lSumArray, 0, lNbrFreq*lNbrChannels*sizeof(tFFTValue));
|
99
|
+
|
100
|
+
// Encapsulate it
|
101
|
+
rValContainer = Data_Wrap_Struct(rb_cObject, NULL, free, lSumArray);
|
102
|
+
|
103
|
+
return rValContainer;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Process a value read from an input buffer for the CompleteSumCosSin function.
|
108
|
+
*
|
109
|
+
* Parameters:
|
110
|
+
* * *iValue* (<em>const tSampleValue</em>): The value being read
|
111
|
+
* * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
|
112
|
+
* * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
|
113
|
+
* * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tNextSilentStruct*</em>.
|
114
|
+
* Return:
|
115
|
+
* * _int_: The return code:
|
116
|
+
* ** 0: Continue iteration
|
117
|
+
* ** 1: Break all iterations
|
118
|
+
* ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
|
119
|
+
*/
|
120
|
+
int fftutils_processValue_CompleteSumCosSin(
|
121
|
+
const tSampleValue iValue,
|
122
|
+
const tSampleIndex iIdxSample,
|
123
|
+
const int iIdxChannel,
|
124
|
+
void* iPtrArgs) {
|
125
|
+
// Interpret parameters
|
126
|
+
tCompleteSumCosSinStruct* lPtrVariables = (tCompleteSumCosSinStruct*)iPtrArgs;
|
127
|
+
|
128
|
+
if (iIdxChannel == 0) {
|
129
|
+
*(lPtrVariables->ptrIdxSum) = 0;
|
130
|
+
}
|
131
|
+
long double lTrigoValue;
|
132
|
+
int lIdxW;
|
133
|
+
for (lIdxW = 0; lIdxW < lPtrVariables->nbrFreq; ++lIdxW) {
|
134
|
+
lTrigoValue = ((long double)lPtrVariables->w[lIdxW]) * ((long double)iIdxSample);
|
135
|
+
lPtrVariables->sumCos[*(lPtrVariables->ptrIdxSum)] += (tFFTValue)(iValue*cos(lTrigoValue));
|
136
|
+
lPtrVariables->sumSin[*(lPtrVariables->ptrIdxSum)] += (tFFTValue)(iValue*sin(lTrigoValue));
|
137
|
+
++(*(lPtrVariables->ptrIdxSum));
|
138
|
+
}
|
139
|
+
|
140
|
+
return 0;
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Process a value read from an input buffer for the CompleteSumCosSin function.
|
145
|
+
* Use the trigo cache.
|
146
|
+
*
|
147
|
+
* Parameters:
|
148
|
+
* * *iValue* (<em>const tSampleValue</em>): The value being read
|
149
|
+
* * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
|
150
|
+
* * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
|
151
|
+
* * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tNextSilentStruct*</em>.
|
152
|
+
* Return:
|
153
|
+
* * _int_: The return code:
|
154
|
+
* ** 0: Continue iteration
|
155
|
+
* ** 1: Break all iterations
|
156
|
+
* ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
|
157
|
+
*/
|
158
|
+
int fftutils_processValue_CompleteSumCosSinWithCache(
|
159
|
+
const tSampleValue iValue,
|
160
|
+
const tSampleIndex iIdxSample,
|
161
|
+
const int iIdxChannel,
|
162
|
+
void* iPtrArgs) {
|
163
|
+
// Interpret parameters
|
164
|
+
tCompleteSumCosSinStruct* lPtrVariables = (tCompleteSumCosSinStruct*)iPtrArgs;
|
165
|
+
|
166
|
+
if (iIdxChannel == 0) {
|
167
|
+
*(lPtrVariables->ptrIdxSum) = 0;
|
168
|
+
}
|
169
|
+
int lIdxW;
|
170
|
+
for (lIdxW = 0; lIdxW < lPtrVariables->nbrFreq; ++lIdxW) {
|
171
|
+
lPtrVariables->sumCos[*(lPtrVariables->ptrIdxSum)] += (tFFTValue)(iValue*lPtrVariables->cosCache[lIdxW][iIdxSample]);
|
172
|
+
lPtrVariables->sumSin[*(lPtrVariables->ptrIdxSum)] += (tFFTValue)(iValue*lPtrVariables->sinCache[lIdxW][iIdxSample]);
|
173
|
+
++(*(lPtrVariables->ptrIdxSum));
|
174
|
+
}
|
175
|
+
|
176
|
+
return 0;
|
177
|
+
}
|
178
|
+
|
179
|
+
/** Complete the cosinus et sinus sums to compute the FFT
|
180
|
+
*
|
181
|
+
* Parameters:
|
182
|
+
* * *iSelf* (_FFTUtils_): Self
|
183
|
+
* * *iValInputRawBuffer* (_String_): The input raw buffer
|
184
|
+
* * *iValIdxSample* (_Integer_): The current sample index (to be used when several buffers are used for the same FFT)
|
185
|
+
* * *iValNbrBitsPerSample* (_Integer_): The number of bits per sample
|
186
|
+
* * *iValNbrSamples* (_Integer_): The number of samples
|
187
|
+
* * *iValNbrChannels* (_Integer_): The number of channels
|
188
|
+
* * *iValNbrFreq* (_Integer_): The number of frequencies to compute (size of array contained in iValW)
|
189
|
+
* * *iValW* (_Object_): Container of the Wi (should be initialized with createWi), or nil if none. Either use W or TrigoCache.
|
190
|
+
* * *iValTrigoCache* (_Object_): Container of the trigo cache (should be initialized with initTrigoCache), or nil if none. Either use W or TrigoCache.
|
191
|
+
* * *ioValSumCos* (_Object_): Container of the cos sums (should be initialized with initSumArray)
|
192
|
+
* * *ioValSumSin* (_Object_): Container of the sin sums (should be initialized with initSumArray)
|
193
|
+
**/
|
194
|
+
static VALUE fftutils_completeSumCosSin(
|
195
|
+
VALUE iSelf,
|
196
|
+
VALUE iValInputRawBuffer,
|
197
|
+
VALUE iValIdxSample,
|
198
|
+
VALUE iValNbrBitsPerSample,
|
199
|
+
VALUE iValNbrSamples,
|
200
|
+
VALUE iValNbrChannels,
|
201
|
+
VALUE iValNbrFreq,
|
202
|
+
VALUE iValW,
|
203
|
+
VALUE iValTrigoCache,
|
204
|
+
VALUE ioValSumCos,
|
205
|
+
VALUE ioValSumSin) {
|
206
|
+
// Translate Ruby objects
|
207
|
+
tSampleIndex iNbrSamples = FIX2LONG(iValNbrSamples);
|
208
|
+
int iNbrChannels = FIX2INT(iValNbrChannels);
|
209
|
+
int iNbrFreq = FIX2INT(iValNbrFreq);
|
210
|
+
int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
|
211
|
+
char* lPtrRawBuffer = RSTRING(iValInputRawBuffer)->ptr;
|
212
|
+
tSampleIndex iIdxSample = FIX2LONG(iValIdxSample);
|
213
|
+
// Get the lW array
|
214
|
+
double * lW = NULL;
|
215
|
+
if (iValW != Qnil) {
|
216
|
+
Data_Get_Struct(iValW, double, lW);
|
217
|
+
}
|
218
|
+
// Get the trigo cache
|
219
|
+
tTrigoCache* lPtrTrigoCache = NULL;
|
220
|
+
if (iValTrigoCache != Qnil) {
|
221
|
+
Data_Get_Struct(iValTrigoCache, tTrigoCache, lPtrTrigoCache);
|
222
|
+
}
|
223
|
+
// Get the cos and sin sum arrays
|
224
|
+
tFFTValue * lSumCos;
|
225
|
+
tFFTValue * lSumSin;
|
226
|
+
Data_Get_Struct(ioValSumCos, tFFTValue, lSumCos);
|
227
|
+
Data_Get_Struct(ioValSumSin, tFFTValue, lSumSin);
|
228
|
+
|
229
|
+
// Parse the data. This is done differently depending on the data structure
|
230
|
+
// Define variables outside the loops to not allocate and initialize heap size for nothing
|
231
|
+
int lIdxSum = 0;
|
232
|
+
|
233
|
+
// Set variables to give to the process
|
234
|
+
tCompleteSumCosSinStruct lProcessVariables;
|
235
|
+
lProcessVariables.nbrFreq = iNbrFreq;
|
236
|
+
lProcessVariables.w = lW;
|
237
|
+
lProcessVariables.sumCos = lSumCos;
|
238
|
+
lProcessVariables.sumSin = lSumSin;
|
239
|
+
lProcessVariables.ptrIdxSum = &lIdxSum;
|
240
|
+
lProcessVariables.nbrChannels = iNbrChannels;
|
241
|
+
if (lPtrTrigoCache == NULL) {
|
242
|
+
// Iterate through the raw buffer
|
243
|
+
commonutils_iterateThroughRawBuffer(
|
244
|
+
lPtrRawBuffer,
|
245
|
+
iNbrBitsPerSample,
|
246
|
+
iNbrChannels,
|
247
|
+
iNbrSamples,
|
248
|
+
iIdxSample,
|
249
|
+
&fftutils_processValue_CompleteSumCosSin,
|
250
|
+
&lProcessVariables
|
251
|
+
);
|
252
|
+
} else {
|
253
|
+
lProcessVariables.cosCache = lPtrTrigoCache->cosCache;
|
254
|
+
lProcessVariables.sinCache = lPtrTrigoCache->sinCache;
|
255
|
+
// Iterate through the raw buffer by using the cache
|
256
|
+
commonutils_iterateThroughRawBuffer(
|
257
|
+
lPtrRawBuffer,
|
258
|
+
iNbrBitsPerSample,
|
259
|
+
iNbrChannels,
|
260
|
+
iNbrSamples,
|
261
|
+
iIdxSample,
|
262
|
+
&fftutils_processValue_CompleteSumCosSinWithCache,
|
263
|
+
&lProcessVariables
|
264
|
+
);
|
265
|
+
}
|
266
|
+
|
267
|
+
return Qnil;
|
268
|
+
}
|
269
|
+
|
270
|
+
/** Compute the final FFT coefficients in Ruby integers, per channel and per frequency.
|
271
|
+
* Use previously computed cos and sin sum arrays.
|
272
|
+
*
|
273
|
+
* Parameters:
|
274
|
+
* * *iSelf* (_FFTUtils_): Self
|
275
|
+
* * *iValNbrChannels* (_Integer_): The number of channels
|
276
|
+
* * *iValNbrFreq* (_Integer_): The number of frequencies to compute (size of array contained in iValW)
|
277
|
+
* * *iValSumCos* (_Object_): Container of the cos sums (should be initialized with initSumArray)
|
278
|
+
* * *iValSumSin* (_Object_): Container of the sin sums (should be initialized with initSumArray)
|
279
|
+
* Return:
|
280
|
+
* * <em>list<list<Integer>></em>: List of FFT coefficients, per channel, per frequency
|
281
|
+
**/
|
282
|
+
static VALUE fftutils_computeFFT(
|
283
|
+
VALUE iSelf,
|
284
|
+
VALUE iValNbrChannels,
|
285
|
+
VALUE iValNbrFreq,
|
286
|
+
VALUE iValSumCos,
|
287
|
+
VALUE iValSumSin) {
|
288
|
+
// Translate Ruby objects
|
289
|
+
int lNbrChannels = FIX2INT(iValNbrChannels);
|
290
|
+
int lNbrFreq = FIX2INT(iValNbrFreq);
|
291
|
+
// Get the cos and sin sum arrays
|
292
|
+
tFFTValue * lSumCos;
|
293
|
+
tFFTValue * lSumSin;
|
294
|
+
Data_Get_Struct(iValSumCos, tFFTValue, lSumCos);
|
295
|
+
Data_Get_Struct(iValSumSin, tFFTValue, lSumSin);
|
296
|
+
// The C-array of the final result
|
297
|
+
VALUE lValFFT[lNbrFreq];
|
298
|
+
|
299
|
+
int lIdxFreq;
|
300
|
+
int lIdxChannel;
|
301
|
+
int lIdxSum;
|
302
|
+
// Buffer that stores string representation of tFFTValue for Ruby RBigNum
|
303
|
+
char lStrValue[128];
|
304
|
+
// The bignums to put in the result
|
305
|
+
VALUE lValChannelFFTs[lNbrChannels];
|
306
|
+
mpz_t lSinSin;
|
307
|
+
mpz_init(lSinSin);
|
308
|
+
mpz_t lFFTCoeff;
|
309
|
+
mpz_init(lFFTCoeff);
|
310
|
+
// Put back the cos and sin values in the result, summing their square values
|
311
|
+
for (lIdxFreq = 0; lIdxFreq < lNbrFreq; ++lIdxFreq) {
|
312
|
+
lIdxSum = lIdxFreq;
|
313
|
+
for (lIdxChannel = 0; lIdxChannel < lNbrChannels; ++lIdxChannel) {
|
314
|
+
// Initialize MPZ with char* as they don't accept long long int.
|
315
|
+
sprintf(lStrValue, "%lld", lSumSin[lIdxSum]);
|
316
|
+
mpz_set_str(lSinSin, lStrValue, 10);
|
317
|
+
mpz_mul(lSinSin, lSinSin, lSinSin);
|
318
|
+
sprintf(lStrValue, "%lld", lSumCos[lIdxSum]);
|
319
|
+
mpz_set_str(lFFTCoeff, lStrValue, 10);
|
320
|
+
mpz_mul(lFFTCoeff, lFFTCoeff, lFFTCoeff);
|
321
|
+
mpz_add(lFFTCoeff, lFFTCoeff, lSinSin);
|
322
|
+
lValChannelFFTs[lIdxChannel] = rb_cstr2inum(mpz_get_str(lStrValue, 16, lFFTCoeff), 16);
|
323
|
+
lIdxSum += lNbrFreq;
|
324
|
+
}
|
325
|
+
lValFFT[lIdxFreq] = rb_ary_new4(lNbrChannels, lValChannelFFTs);
|
326
|
+
}
|
327
|
+
mpz_clear(lFFTCoeff);
|
328
|
+
mpz_clear(lSinSin);
|
329
|
+
|
330
|
+
return rb_ary_new4(lNbrFreq, lValFFT);
|
331
|
+
}
|
332
|
+
/* To be used if GMP library is absent.
|
333
|
+
static VALUE fftutils_computeFFT(
|
334
|
+
VALUE iSelf,
|
335
|
+
VALUE iValNbrChannels,
|
336
|
+
VALUE iValNbrFreq,
|
337
|
+
VALUE iValSumCos,
|
338
|
+
VALUE iValSumSin) {
|
339
|
+
// Translate Ruby objects
|
340
|
+
int lNbrChannels = FIX2INT(iValNbrChannels);
|
341
|
+
int lNbrFreq = FIX2INT(iValNbrFreq);
|
342
|
+
// Get the cos and sin sum arrays
|
343
|
+
tFFTValue * lSumCos;
|
344
|
+
tFFTValue * lSumSin;
|
345
|
+
Data_Get_Struct(iValSumCos, tFFTValue, lSumCos);
|
346
|
+
Data_Get_Struct(iValSumSin, tFFTValue, lSumSin);
|
347
|
+
// The C-array of the final result
|
348
|
+
VALUE lValFFT[lNbrFreq];
|
349
|
+
ID lPlusID = rb_intern("+");
|
350
|
+
ID lMultiplyID = rb_intern("*");
|
351
|
+
|
352
|
+
int lIdxFreq;
|
353
|
+
int lIdxChannel;
|
354
|
+
int lIdxSum;
|
355
|
+
// Buffer that stores string representation of tFFTValue for Ruby RBigNum
|
356
|
+
char lStrValue[128];
|
357
|
+
// RBigNums that will store temprary arithmetic results
|
358
|
+
VALUE lValCos;
|
359
|
+
VALUE lValSin;
|
360
|
+
VALUE lValCosCos;
|
361
|
+
VALUE lValSinSin;
|
362
|
+
// The bignums to put in the result
|
363
|
+
VALUE lValChannelFFTs[lNbrChannels];
|
364
|
+
// Put back the cos and sin values in the result, summing their square values
|
365
|
+
for (lIdxFreq = 0; lIdxFreq < lNbrFreq; ++lIdxFreq) {
|
366
|
+
lIdxSum = lIdxFreq;
|
367
|
+
for (lIdxChannel = 0; lIdxChannel < lNbrChannels; ++lIdxChannel) {
|
368
|
+
// Initialize Ruby objects for the arithmetic, as C will not treat numbers greater than 64 bits.
|
369
|
+
sprintf(lStrValue, "%lld", lSumCos[lIdxSum]);
|
370
|
+
lValCos = rb_cstr2inum(lStrValue, 10);
|
371
|
+
sprintf(lStrValue, "%lld", lSumSin[lIdxSum]);
|
372
|
+
lValSin = rb_cstr2inum(lStrValue, 10);
|
373
|
+
lValCosCos = rb_funcall(lValCos, lMultiplyID, 1, lValCos);
|
374
|
+
lValSinSin = rb_funcall(lValSin, lMultiplyID, 1, lValSin);
|
375
|
+
lValChannelFFTs[lIdxChannel] = rb_funcall(lValCosCos, lPlusID, 1, lValSinSin);
|
376
|
+
lIdxSum += lNbrFreq;
|
377
|
+
}
|
378
|
+
lValFFT[lIdxFreq] = rb_ary_new4(lNbrChannels, lValChannelFFTs);
|
379
|
+
}
|
380
|
+
|
381
|
+
return rb_ary_new4(lNbrFreq, lValFFT);
|
382
|
+
}
|
383
|
+
*/
|
384
|
+
|
385
|
+
/**
|
386
|
+
* Free a trigonometric cache.
|
387
|
+
* This method is called by Ruby GC.
|
388
|
+
*
|
389
|
+
* Parameters:
|
390
|
+
* * *iPtrTrigoCache* (<em>void*</em>): The trigo cache to free (in fact a <em>tTrigoCache*</em>)
|
391
|
+
*/
|
392
|
+
static void fftutils_freeTrigoCache(void* iPtrTrigoCache) {
|
393
|
+
tTrigoCache* lPtrTrigoCache = (tTrigoCache*)iPtrTrigoCache;
|
394
|
+
|
395
|
+
int lIdxW;
|
396
|
+
for (lIdxW = 0; lIdxW < lPtrTrigoCache->nbrFreq; ++lIdxW) {
|
397
|
+
// Free it
|
398
|
+
free(lPtrTrigoCache->cosCache[lIdxW]);
|
399
|
+
free(lPtrTrigoCache->sinCache[lIdxW]);
|
400
|
+
}
|
401
|
+
free(lPtrTrigoCache->cosCache);
|
402
|
+
free(lPtrTrigoCache->sinCache);
|
403
|
+
}
|
404
|
+
|
405
|
+
/**
|
406
|
+
* Create a cache of trigonometric values that will be then used in completeSumCosSin method
|
407
|
+
*
|
408
|
+
* Parameters:
|
409
|
+
* * *iSelf* (_FFTUtils_): Self
|
410
|
+
* * *iValW* (_Object_): Container of the W coefficients (initialized using createWi)
|
411
|
+
* * *iValNbrFreq* (_Integer_): The number of frequencies in the W coefficients
|
412
|
+
* * *iValNbrSamples* (_Integer_): Number of samples for which we create the cache
|
413
|
+
* Return:
|
414
|
+
* * _Object_: Container of the trigonometric cache
|
415
|
+
*/
|
416
|
+
static VALUE fftutils_initTrigoCache(
|
417
|
+
VALUE iSelf,
|
418
|
+
VALUE iValW,
|
419
|
+
VALUE iValNbrFreq,
|
420
|
+
VALUE iValNbrSamples) {
|
421
|
+
// Translate parameters in C types
|
422
|
+
int iNbrFreq = FIX2INT(iValNbrFreq);
|
423
|
+
tSampleIndex iNbrSamples = FIX2LONG(iValNbrSamples);
|
424
|
+
// Get the lW array
|
425
|
+
double * lW;
|
426
|
+
Data_Get_Struct(iValW, double, lW);
|
427
|
+
|
428
|
+
// Create the cache
|
429
|
+
int lIdxW;
|
430
|
+
tSampleIndex lIdxSample;
|
431
|
+
double* lTmpSamplesValuesCos;
|
432
|
+
double* lTmpSamplesValuesSin;
|
433
|
+
double lTrigoValue;
|
434
|
+
tTrigoCache* lPtrTrigoCache = ALLOC(tTrigoCache);
|
435
|
+
lPtrTrigoCache->cosCache = ALLOC_N(double*, iNbrFreq);
|
436
|
+
lPtrTrigoCache->sinCache = ALLOC_N(double*, iNbrFreq);
|
437
|
+
lPtrTrigoCache->nbrFreq = iNbrFreq;
|
438
|
+
for (lIdxW = 0; lIdxW < iNbrFreq; ++lIdxW) {
|
439
|
+
// Allocate the double array storing values for each sample
|
440
|
+
lTmpSamplesValuesCos = ALLOC_N(double, iNbrSamples);
|
441
|
+
lTmpSamplesValuesSin = ALLOC_N(double, iNbrSamples);
|
442
|
+
// Fill it
|
443
|
+
for (lIdxSample = 0; lIdxSample < iNbrSamples; ++lIdxSample) {
|
444
|
+
lTrigoValue = lIdxSample*lW[lIdxW];
|
445
|
+
lTmpSamplesValuesCos[lIdxSample] = cos(lTrigoValue);
|
446
|
+
lTmpSamplesValuesSin[lIdxSample] = sin(lTrigoValue);
|
447
|
+
}
|
448
|
+
// Store it
|
449
|
+
lPtrTrigoCache->cosCache[lIdxW] = lTmpSamplesValuesCos;
|
450
|
+
lPtrTrigoCache->sinCache[lIdxW] = lTmpSamplesValuesSin;
|
451
|
+
}
|
452
|
+
|
453
|
+
// Encapsulate it in a Ruby object
|
454
|
+
return Data_Wrap_Struct(rb_cObject, NULL, fftutils_freeTrigoCache, lPtrTrigoCache);
|
455
|
+
}
|
456
|
+
|
457
|
+
/**
|
458
|
+
* Initialize an MPF number based on a Ruby's fixnum.
|
459
|
+
*
|
460
|
+
* Parameters:
|
461
|
+
* * *ioMPF* (<em>mpf_t</em>): The mpf to initialize
|
462
|
+
* * *iValInt* (_Integer_): The Ruby integer
|
463
|
+
* Return:
|
464
|
+
* * _int_: The result code of the set
|
465
|
+
*/
|
466
|
+
inline int initMPF(
|
467
|
+
mpf_t ioMPF,
|
468
|
+
VALUE iValInt) {
|
469
|
+
return mpf_init_set_str(ioMPF, RSTRING(rb_big2str(iValInt, 16))->ptr, 16);
|
470
|
+
}
|
471
|
+
|
472
|
+
/**
|
473
|
+
* Get a Ruby integer based on an MPF storing an integer value.
|
474
|
+
* Prerequisite: The MPF value must be truncated before calling this function.
|
475
|
+
*
|
476
|
+
* Parameters:
|
477
|
+
* * *iMPF* (<em>mpf_t</em>): The mpf to read
|
478
|
+
* Return:
|
479
|
+
* * _Integer_: The Ruby integer
|
480
|
+
*/
|
481
|
+
#define MAX_NUMBER_DIGITS 256
|
482
|
+
VALUE mpf2RubyInt(
|
483
|
+
mpf_t iMPF) {
|
484
|
+
// The buffer where it will be written
|
485
|
+
char lStrNumber[MAX_NUMBER_DIGITS];
|
486
|
+
// The exponent part. Used to add trailing 0s.
|
487
|
+
mp_exp_t lExp;
|
488
|
+
// Fill the string with the mantissa part
|
489
|
+
mpf_get_str(lStrNumber, &lExp, 16, MAX_NUMBER_DIGITS, iMPF);
|
490
|
+
int lStrSize = strlen(lStrNumber);
|
491
|
+
if (lExp-lStrSize > 0) {
|
492
|
+
// We need to add (lExp-lStrSize) trailing 0s.
|
493
|
+
char* lPtrStrNumber = lStrNumber + lStrSize;
|
494
|
+
memset(lPtrStrNumber, '0', lExp-lStrSize);
|
495
|
+
lPtrStrNumber[lExp-lStrSize] = 0;
|
496
|
+
}
|
497
|
+
|
498
|
+
return rb_cstr2inum(lStrNumber, 16);
|
499
|
+
}
|
500
|
+
|
501
|
+
/**
|
502
|
+
* Free an FFT profile.
|
503
|
+
* This method is called by Ruby GC.
|
504
|
+
*
|
505
|
+
* Parameters:
|
506
|
+
* * *iPtrFFTProfile* (<em>void*</em>): The FFT profile to free (in fact a <em>tFFTProfile*</em>)
|
507
|
+
*/
|
508
|
+
static void fftutils_freeFFTProfile(void* iPtrFFTProfile) {
|
509
|
+
tFFTProfile* lPtrFFTProfile = (tFFTProfile*)iPtrFFTProfile;
|
510
|
+
|
511
|
+
int lIdxFreq;
|
512
|
+
int lIdxChannel;
|
513
|
+
mpf_t* lPtrChannelValues;
|
514
|
+
for (lIdxFreq = 0; lIdxFreq < lPtrFFTProfile->nbrFreq; ++lIdxFreq) {
|
515
|
+
lPtrChannelValues = lPtrFFTProfile->profile[lIdxFreq];
|
516
|
+
for (lIdxChannel = 0; lIdxChannel < lPtrFFTProfile->nbrChannels; ++lIdxChannel) {
|
517
|
+
mpf_clear(lPtrChannelValues[lIdxChannel]);
|
518
|
+
}
|
519
|
+
// Free it
|
520
|
+
free(lPtrFFTProfile->profile[lIdxFreq]);
|
521
|
+
}
|
522
|
+
mpf_clear(lPtrFFTProfile->maxFFTValue);
|
523
|
+
free(lPtrFFTProfile->profile);
|
524
|
+
}
|
525
|
+
|
526
|
+
/**
|
527
|
+
* Initialize a C object storing a profile
|
528
|
+
*
|
529
|
+
* Parameters:
|
530
|
+
* * *iSelf* (_FFTUtils_): Self
|
531
|
+
* * *iValFFTProfile* (<em>[Integer,Integer,list<list<Integer>>]</em>): FFT Profile
|
532
|
+
* Return:
|
533
|
+
* * _Object_: Object storing a C FFT Profile, to be used with other C functions
|
534
|
+
*/
|
535
|
+
static VALUE fftutils_createCFFTProfile(
|
536
|
+
VALUE iSelf,
|
537
|
+
VALUE iValFFTProfile) {
|
538
|
+
// The C profile
|
539
|
+
tFFTProfile* lPtrFFTProfile = ALLOC(tFFTProfile);
|
540
|
+
int lNbrBitsPerSample = FIX2INT(rb_ary_entry(iValFFTProfile, 0));
|
541
|
+
tSampleIndex lNbrSamples = FIX2LONG(rb_ary_entry(iValFFTProfile, 1));
|
542
|
+
VALUE lValFFTCoeffs = rb_ary_entry(iValFFTProfile, 2);
|
543
|
+
lPtrFFTProfile->nbrFreq = RARRAY(lValFFTCoeffs)->len;
|
544
|
+
lPtrFFTProfile->nbrChannels = RARRAY(rb_ary_entry(lValFFTCoeffs, 0))->len;
|
545
|
+
|
546
|
+
// Compute the maximal values
|
547
|
+
mpf_init_set_ui(lPtrFFTProfile->maxFFTValue, 1 << (lNbrBitsPerSample-1));
|
548
|
+
mpf_mul_ui(lPtrFFTProfile->maxFFTValue, lPtrFFTProfile->maxFFTValue, lNbrSamples);
|
549
|
+
mpf_mul(lPtrFFTProfile->maxFFTValue, lPtrFFTProfile->maxFFTValue, lPtrFFTProfile->maxFFTValue);
|
550
|
+
mpf_add(lPtrFFTProfile->maxFFTValue, lPtrFFTProfile->maxFFTValue, lPtrFFTProfile->maxFFTValue);
|
551
|
+
|
552
|
+
// Fill the C structure
|
553
|
+
lPtrFFTProfile->profile = ALLOC_N(mpf_t*, lPtrFFTProfile->nbrFreq);
|
554
|
+
mpf_t* lPtrChannelValues;
|
555
|
+
VALUE lValChannelValues;
|
556
|
+
int lConvertResult = 0;
|
557
|
+
int lIdxFreq;
|
558
|
+
int lIdxChannel;
|
559
|
+
for (lIdxFreq = 0; lIdxFreq < lPtrFFTProfile->nbrFreq; ++lIdxFreq) {
|
560
|
+
lValChannelValues = rb_ary_entry(lValFFTCoeffs, lIdxFreq);
|
561
|
+
lPtrChannelValues = ALLOC_N(mpf_t, lPtrFFTProfile->nbrChannels);
|
562
|
+
for (lIdxChannel = 0; lIdxChannel < lPtrFFTProfile->nbrChannels; ++lIdxChannel) {
|
563
|
+
lConvertResult += initMPF(lPtrChannelValues[lIdxChannel], rb_ary_entry(lValChannelValues, lIdxChannel));
|
564
|
+
}
|
565
|
+
lPtrFFTProfile->profile[lIdxFreq] = lPtrChannelValues;
|
566
|
+
}
|
567
|
+
if (lConvertResult != 0) {
|
568
|
+
// Errors occured
|
569
|
+
char lLogMessage[256];
|
570
|
+
sprintf(lLogMessage, "%d errors occurred while creating the C FFT profile.", -lConvertResult);
|
571
|
+
rb_funcall(iSelf, rb_intern("logErr"), 1, rb_str_new2(lLogMessage));
|
572
|
+
}
|
573
|
+
|
574
|
+
// Encapsulate it in a Ruby object
|
575
|
+
return Data_Wrap_Struct(rb_cObject, NULL, fftutils_freeFFTProfile, lPtrFFTProfile);
|
576
|
+
}
|
577
|
+
|
578
|
+
/**
|
579
|
+
* Compare 2 FFT profiles and measure their distance.
|
580
|
+
* Here is an FFT profile structure:
|
581
|
+
* [ Integer, Integer, list<list<Integer>> ]
|
582
|
+
* [ NbrBitsPerSample, NbrSamples, FFTValues ]
|
583
|
+
* FFTValues are declined per channel, per frequency index.
|
584
|
+
* Bits per sample and number of samples are taken into account to relatively compare the profiles.
|
585
|
+
*
|
586
|
+
* Parameters:
|
587
|
+
* * *iSelf* (_FFTUtils_): Self
|
588
|
+
* * *iValProfile1* (_Object_): Profile 1, initialized by createCFFTProfile.
|
589
|
+
* * *iValProfile2* (_Object_): Profile 2, initialized by createCFFTProfile.
|
590
|
+
* * *iValScale* (_Integer_): The scale used to compute values
|
591
|
+
* Return:
|
592
|
+
* * _Integer_: Distance (Profile 2 - Profile 1).
|
593
|
+
*/
|
594
|
+
static VALUE fftutils_distFFTProfiles(
|
595
|
+
VALUE iSelf,
|
596
|
+
VALUE iValProfile1,
|
597
|
+
VALUE iValProfile2,
|
598
|
+
VALUE iValScale) {
|
599
|
+
// Translate parameters in C types
|
600
|
+
mpf_t iScale;
|
601
|
+
initMPF(iScale, iValScale);
|
602
|
+
// Get the FFT Profiles
|
603
|
+
tFFTProfile* lPtrFFTProfile1;
|
604
|
+
Data_Get_Struct(iValProfile1, tFFTProfile, lPtrFFTProfile1);
|
605
|
+
tFFTProfile* lPtrFFTProfile2;
|
606
|
+
Data_Get_Struct(iValProfile2, tFFTProfile, lPtrFFTProfile2);
|
607
|
+
|
608
|
+
// Return the max of the distances of each frequency coefficient
|
609
|
+
mpf_t lMaxDist;
|
610
|
+
mpf_init_set_ui(lMaxDist, 0);
|
611
|
+
|
612
|
+
int lIdxFreq;
|
613
|
+
int lIdxChannel;
|
614
|
+
mpf_t* lPtrChannelValues1;
|
615
|
+
mpf_t* lPtrChannelValues2;
|
616
|
+
mpf_t lDist;
|
617
|
+
mpf_init(lDist);
|
618
|
+
mpf_t lDist2;
|
619
|
+
mpf_init(lDist2);
|
620
|
+
|
621
|
+
for (lIdxFreq = 0; lIdxFreq < lPtrFFTProfile1->nbrFreq; ++lIdxFreq) {
|
622
|
+
lPtrChannelValues1 = lPtrFFTProfile1->profile[lIdxFreq];
|
623
|
+
lPtrChannelValues2 = lPtrFFTProfile2->profile[lIdxFreq];
|
624
|
+
for (lIdxChannel = 0; lIdxChannel < lPtrFFTProfile1->nbrChannels; ++lIdxChannel) {
|
625
|
+
// Compute iFFT2Value - iFFT1Value, on a scale of iScale
|
626
|
+
mpf_div(lDist, lPtrChannelValues2[lIdxChannel], lPtrFFTProfile2->maxFFTValue);
|
627
|
+
mpf_div(lDist2, lPtrChannelValues1[lIdxChannel], lPtrFFTProfile1->maxFFTValue);
|
628
|
+
mpf_sub(lDist, lDist, lDist2);
|
629
|
+
if (mpf_cmp(lDist, lMaxDist) > 0) {
|
630
|
+
mpf_set(lMaxDist, lDist);
|
631
|
+
}
|
632
|
+
}
|
633
|
+
}
|
634
|
+
// Apply the scale
|
635
|
+
mpf_mul(lMaxDist, lMaxDist, iScale);
|
636
|
+
mpf_trunc(lMaxDist, lMaxDist);
|
637
|
+
// Get the Ruby result
|
638
|
+
VALUE rValDistance = mpf2RubyInt(lMaxDist);
|
639
|
+
|
640
|
+
// Clean memory
|
641
|
+
mpf_clear(lDist2);
|
642
|
+
mpf_clear(lDist);
|
643
|
+
mpf_clear(lMaxDist);
|
644
|
+
mpf_clear(iScale);
|
645
|
+
|
646
|
+
return rValDistance;
|
647
|
+
}
|
648
|
+
|
649
|
+
// Initialize the module
|
650
|
+
void Init_FFTUtils() {
|
651
|
+
VALUE lWSKModule = rb_define_module("WSK");
|
652
|
+
VALUE lFFTUtilsModule = rb_define_module_under(lWSKModule, "FFTUtils");
|
653
|
+
VALUE lFFTUtilsClass = rb_define_class_under(lFFTUtilsModule, "FFTUtils", rb_cObject);
|
654
|
+
|
655
|
+
rb_define_method(lFFTUtilsClass, "completeSumCosSin", fftutils_completeSumCosSin, 10);
|
656
|
+
rb_define_method(lFFTUtilsClass, "createWi", fftutils_createWi, 3);
|
657
|
+
rb_define_method(lFFTUtilsClass, "initSumArray", fftutils_initSumArray, 2);
|
658
|
+
rb_define_method(lFFTUtilsClass, "initTrigoCache", fftutils_initTrigoCache, 3);
|
659
|
+
rb_define_method(lFFTUtilsClass, "computeFFT", fftutils_computeFFT, 4);
|
660
|
+
rb_define_method(lFFTUtilsClass, "createCFFTProfile", fftutils_createCFFTProfile, 1);
|
661
|
+
rb_define_method(lFFTUtilsClass, "distFFTProfiles", fftutils_distFFTProfiles, 3);
|
662
|
+
}
|
Binary file
|
Binary file
|