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.
Files changed (88) hide show
  1. data/AUTHORS +1 -0
  2. data/ChangeLog +5 -0
  3. data/Credits +3 -0
  4. data/LICENSE +31 -0
  5. data/README +18 -0
  6. data/ReleaseInfo +8 -0
  7. data/TODO +2 -0
  8. data/bin/WSK.rb +14 -0
  9. data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
  10. data/ext/WSK/AnalyzeUtils/AnalyzeUtils.o +0 -0
  11. data/ext/WSK/AnalyzeUtils/AnalyzeUtils.so +0 -0
  12. data/ext/WSK/AnalyzeUtils/Makefile +149 -0
  13. data/ext/WSK/AnalyzeUtils/build.rb +18 -0
  14. data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
  15. data/ext/WSK/ArithmUtils/ArithmUtils.o +0 -0
  16. data/ext/WSK/ArithmUtils/ArithmUtils.so +0 -0
  17. data/ext/WSK/ArithmUtils/Makefile +149 -0
  18. data/ext/WSK/ArithmUtils/build.rb +20 -0
  19. data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
  20. data/ext/WSK/FFTUtils/FFTUtils.o +0 -0
  21. data/ext/WSK/FFTUtils/FFTUtils.so +0 -0
  22. data/ext/WSK/FFTUtils/Makefile +149 -0
  23. data/ext/WSK/FFTUtils/build.rb +20 -0
  24. data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
  25. data/ext/WSK/FunctionUtils/FunctionUtils.o +0 -0
  26. data/ext/WSK/FunctionUtils/FunctionUtils.so +0 -0
  27. data/ext/WSK/FunctionUtils/Makefile +149 -0
  28. data/ext/WSK/FunctionUtils/build.rb +20 -0
  29. data/ext/WSK/SilentUtils/Makefile +149 -0
  30. data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
  31. data/ext/WSK/SilentUtils/SilentUtils.o +0 -0
  32. data/ext/WSK/SilentUtils/SilentUtils.so +0 -0
  33. data/ext/WSK/SilentUtils/build.rb +18 -0
  34. data/ext/WSK/VolumeUtils/Makefile +149 -0
  35. data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
  36. data/ext/WSK/VolumeUtils/VolumeUtils.o +0 -0
  37. data/ext/WSK/VolumeUtils/VolumeUtils.so +0 -0
  38. data/ext/WSK/VolumeUtils/build.rb +20 -0
  39. data/lib/WSK/Actions/Analyze.rb +176 -0
  40. data/lib/WSK/Actions/ApplyMap.desc.rb +15 -0
  41. data/lib/WSK/Actions/ApplyMap.rb +57 -0
  42. data/lib/WSK/Actions/ApplyVolumeFct.desc.rb +30 -0
  43. data/lib/WSK/Actions/ApplyVolumeFct.rb +72 -0
  44. data/lib/WSK/Actions/Compare.desc.rb +25 -0
  45. data/lib/WSK/Actions/Compare.rb +238 -0
  46. data/lib/WSK/Actions/ConstantCompare.desc.rb +20 -0
  47. data/lib/WSK/Actions/ConstantCompare.rb +61 -0
  48. data/lib/WSK/Actions/Cut.desc.rb +20 -0
  49. data/lib/WSK/Actions/Cut.rb +60 -0
  50. data/lib/WSK/Actions/CutFirstSignal.desc.rb +25 -0
  51. data/lib/WSK/Actions/CutFirstSignal.rb +72 -0
  52. data/lib/WSK/Actions/DCShifter.desc.rb +15 -0
  53. data/lib/WSK/Actions/DCShifter.rb +67 -0
  54. data/lib/WSK/Actions/DrawFct.desc.rb +20 -0
  55. data/lib/WSK/Actions/DrawFct.rb +59 -0
  56. data/lib/WSK/Actions/FFT.rb +104 -0
  57. data/lib/WSK/Actions/GenAllValues.rb +67 -0
  58. data/lib/WSK/Actions/GenConstant.desc.rb +20 -0
  59. data/lib/WSK/Actions/GenConstant.rb +56 -0
  60. data/lib/WSK/Actions/GenSawtooth.rb +57 -0
  61. data/lib/WSK/Actions/GenSine.desc.rb +20 -0
  62. data/lib/WSK/Actions/GenSine.rb +73 -0
  63. data/lib/WSK/Actions/Identity.rb +43 -0
  64. data/lib/WSK/Actions/Mix.desc.rb +15 -0
  65. data/lib/WSK/Actions/Mix.rb +149 -0
  66. data/lib/WSK/Actions/Multiply.desc.rb +15 -0
  67. data/lib/WSK/Actions/Multiply.rb +73 -0
  68. data/lib/WSK/Actions/NoiseGate.desc.rb +35 -0
  69. data/lib/WSK/Actions/NoiseGate.rb +129 -0
  70. data/lib/WSK/Actions/SilenceInserter.desc.rb +20 -0
  71. data/lib/WSK/Actions/SilenceInserter.rb +87 -0
  72. data/lib/WSK/Actions/SilenceRemover.desc.rb +30 -0
  73. data/lib/WSK/Actions/SilenceRemover.rb +74 -0
  74. data/lib/WSK/Actions/VolumeProfile.desc.rb +35 -0
  75. data/lib/WSK/Actions/VolumeProfile.rb +63 -0
  76. data/lib/WSK/Common.rb +292 -0
  77. data/lib/WSK/FFT.rb +527 -0
  78. data/lib/WSK/Functions.rb +770 -0
  79. data/lib/WSK/Launcher.rb +216 -0
  80. data/lib/WSK/Maps.rb +29 -0
  81. data/lib/WSK/Model/CachedBufferReader.rb +151 -0
  82. data/lib/WSK/Model/Header.rb +133 -0
  83. data/lib/WSK/Model/InputData.rb +193 -0
  84. data/lib/WSK/Model/RawReader.rb +78 -0
  85. data/lib/WSK/Model/WaveReader.rb +91 -0
  86. data/lib/WSK/OutputInterfaces/DirectStream.rb +146 -0
  87. data/lib/WSK/RIFFReader.rb +60 -0
  88. 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