WaveSwissKnife 0.2.0.20120302

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/AUTHORS +4 -0
  2. data/ChangeLog +31 -0
  3. data/Credits +3 -0
  4. data/LICENSE +31 -0
  5. data/README +15 -0
  6. data/ReleaseInfo +8 -0
  7. data/bin/WSK.rb +14 -0
  8. data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
  9. data/ext/WSK/AnalyzeUtils/extconf.rb +7 -0
  10. data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
  11. data/ext/WSK/ArithmUtils/extconf.rb +15 -0
  12. data/ext/WSK/CommonBuild.rb +29 -0
  13. data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
  14. data/ext/WSK/FFTUtils/extconf.rb +15 -0
  15. data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
  16. data/ext/WSK/FunctionUtils/extconf.rb +15 -0
  17. data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
  18. data/ext/WSK/SilentUtils/extconf.rb +7 -0
  19. data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
  20. data/ext/WSK/VolumeUtils/extconf.rb +15 -0
  21. data/external/CommonUtils/build.rb +28 -0
  22. data/external/CommonUtils/include/CommonUtils.h +177 -0
  23. data/external/CommonUtils/src/CommonUtils.c +639 -0
  24. data/lib/WSK/Actions/Analyze.rb +176 -0
  25. data/lib/WSK/Actions/ApplyMap.desc.rb +15 -0
  26. data/lib/WSK/Actions/ApplyMap.rb +57 -0
  27. data/lib/WSK/Actions/ApplyVolumeFct.desc.rb +30 -0
  28. data/lib/WSK/Actions/ApplyVolumeFct.rb +72 -0
  29. data/lib/WSK/Actions/Compare.desc.rb +25 -0
  30. data/lib/WSK/Actions/Compare.rb +238 -0
  31. data/lib/WSK/Actions/ConstantCompare.desc.rb +20 -0
  32. data/lib/WSK/Actions/ConstantCompare.rb +61 -0
  33. data/lib/WSK/Actions/Cut.desc.rb +20 -0
  34. data/lib/WSK/Actions/Cut.rb +60 -0
  35. data/lib/WSK/Actions/CutFirstSignal.desc.rb +25 -0
  36. data/lib/WSK/Actions/CutFirstSignal.rb +72 -0
  37. data/lib/WSK/Actions/DCShifter.desc.rb +15 -0
  38. data/lib/WSK/Actions/DCShifter.rb +67 -0
  39. data/lib/WSK/Actions/DrawFct.desc.rb +20 -0
  40. data/lib/WSK/Actions/DrawFct.rb +59 -0
  41. data/lib/WSK/Actions/FFT.rb +104 -0
  42. data/lib/WSK/Actions/GenAllValues.rb +67 -0
  43. data/lib/WSK/Actions/GenConstant.desc.rb +20 -0
  44. data/lib/WSK/Actions/GenConstant.rb +56 -0
  45. data/lib/WSK/Actions/GenSawtooth.rb +57 -0
  46. data/lib/WSK/Actions/GenSine.desc.rb +20 -0
  47. data/lib/WSK/Actions/GenSine.rb +73 -0
  48. data/lib/WSK/Actions/Identity.rb +43 -0
  49. data/lib/WSK/Actions/Mix.desc.rb +15 -0
  50. data/lib/WSK/Actions/Mix.rb +149 -0
  51. data/lib/WSK/Actions/Multiply.desc.rb +15 -0
  52. data/lib/WSK/Actions/Multiply.rb +73 -0
  53. data/lib/WSK/Actions/NoiseGate.desc.rb +35 -0
  54. data/lib/WSK/Actions/NoiseGate.rb +129 -0
  55. data/lib/WSK/Actions/SilenceInserter.desc.rb +20 -0
  56. data/lib/WSK/Actions/SilenceInserter.rb +87 -0
  57. data/lib/WSK/Actions/SilenceRemover.desc.rb +30 -0
  58. data/lib/WSK/Actions/SilenceRemover.rb +74 -0
  59. data/lib/WSK/Actions/VolumeProfile.desc.rb +35 -0
  60. data/lib/WSK/Actions/VolumeProfile.rb +63 -0
  61. data/lib/WSK/Common.rb +292 -0
  62. data/lib/WSK/FFT.rb +527 -0
  63. data/lib/WSK/Functions.rb +770 -0
  64. data/lib/WSK/Launcher.rb +216 -0
  65. data/lib/WSK/Maps.rb +29 -0
  66. data/lib/WSK/Model/CachedBufferReader.rb +151 -0
  67. data/lib/WSK/Model/Header.rb +133 -0
  68. data/lib/WSK/Model/InputData.rb +193 -0
  69. data/lib/WSK/Model/RawReader.rb +78 -0
  70. data/lib/WSK/Model/WaveReader.rb +91 -0
  71. data/lib/WSK/OutputInterfaces/DirectStream.rb +146 -0
  72. data/lib/WSK/RIFFReader.rb +60 -0
  73. metadata +155 -0
@@ -0,0 +1,862 @@
1
+ /**
2
+ * Copyright (c) 2009 - 2012 Muriel Salvan (muriel@x-aeon.com)
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
+ #include <gmp.h>
11
+
12
+ // Struct used to store a map
13
+ typedef struct {
14
+ // Number of channels
15
+ int nbrChannels;
16
+ // Are there some values that can exceed the values range ? 0 = No 1 = Yes.
17
+ int possibleExceedValues;
18
+ // The map
19
+ tSampleValue** map;
20
+ } tMap;
21
+
22
+ // Struct used to convey data among iterators in the applyMap method
23
+ typedef struct {
24
+ unsigned int offsetIdxMap;
25
+ tSampleValue** map;
26
+ } tApplyMapStruct;
27
+
28
+ // Struct used to store info about a buffer
29
+ typedef struct {
30
+ // All buffers point on the same data at the beginning of the iterations
31
+ unsigned char* buffer_8bits;
32
+ signed short int* buffer_16bits;
33
+ t24bits* buffer_24bits;
34
+ tSampleIndex nbrBufferSamples;
35
+ long double coeff;
36
+ } tBufferInfo;
37
+
38
+ // Struct used to convey data among iterators in the Mix method
39
+ typedef struct {
40
+ tBufferInfo* lstBuffers;
41
+ int nbrActiveBuffers;
42
+ long double mainCoeff;
43
+ // Temporary attributes
44
+ long double tmpValue;
45
+ int idxBuffer;
46
+ } tMixStruct;
47
+
48
+ // Struct used to convey data among iterators in the Compare method
49
+ typedef struct {
50
+ unsigned char* buffer2_8bits;
51
+ signed short int* buffer2_16bits;
52
+ t24bits* buffer2_24bits;
53
+ long double coeffDiff;
54
+ tSampleValue* map;
55
+ tSampleValue mapOffset;
56
+ VALUE self;
57
+ mpz_t cumulativeErrors;
58
+ } tCompareStruct;
59
+
60
+ /**
61
+ * Free a map.
62
+ * This method is called by Ruby GC.
63
+ *
64
+ * Parameters::
65
+ * * *iPtrMap* (<em>void*</em>): The trigo cache to free (in fact a <em>tMap*</em>)
66
+ */
67
+ static void arithmutils_freeMap(void* iPtrMap) {
68
+ tMap* lPtrMap = (tMap*)iPtrMap;
69
+
70
+ int lIdxChannel;
71
+ for (lIdxChannel = 0; lIdxChannel < lPtrMap->nbrChannels; ++lIdxChannel) {
72
+ // Free it
73
+ free(lPtrMap->map[lIdxChannel]);
74
+ }
75
+ free(lPtrMap->map);
76
+ }
77
+
78
+ /**
79
+ * Fill a channel map with a given function of type Piecewise Linear
80
+ *
81
+ * Parameters::
82
+ * * *iNbrBitsPerSample* (<em>const int</em>): Number of bits per sample
83
+ * * *oPtrChannelMap* (<em>int*</em>): The channel map to fill
84
+ * * *iValFunction* (<em>map<Symbol,Object></em>): The function to apply
85
+ * Return::
86
+ * * _int_: Result code:
87
+ * ** *0*: The map never exceeds limits
88
+ * ** *1*: The map can exceed limits
89
+ * ** *2*: An error occurred
90
+ */
91
+ static int arithmutils_fillMap_PiecewiseLinear(
92
+ const int iNbrBitsPerSample,
93
+ int* oPtrChannelMap,
94
+ VALUE iValFunction) {
95
+ int rResultCode = 0;
96
+
97
+ // Read points in a sorted list of couples [x,y]
98
+ VALUE lValSortedPoints = rb_funcall(rb_funcall(rb_hash_aref(iValFunction, ID2SYM(rb_intern("Points"))), rb_intern("to_a"), 0), rb_intern("sort"), 0);
99
+ int lNbrPoints = RARRAY(lValSortedPoints)->len;
100
+ // Read the scale min and max values
101
+ long long int lMinScale = FIX2INT(rb_hash_aref(iValFunction, ID2SYM(rb_intern("MinValue"))));
102
+ long long int lMaxScale = FIX2INT(rb_hash_aref(iValFunction, ID2SYM(rb_intern("MaxValue"))));
103
+ long long int lDiffScale = lMaxScale - lMinScale;
104
+
105
+ // Compute the limits
106
+ long long int lMaxValue = (1 << (iNbrBitsPerSample-1)) - 1;
107
+ long long int lMinValue = -(1 << (iNbrBitsPerSample-1));
108
+ long long int lDiffValue = lMaxValue - lMinValue;
109
+ int lIdxMapOffset = 1 << (iNbrBitsPerSample-1);
110
+
111
+ // Variables to be used in loops
112
+ int lIdxPoint;
113
+ VALUE lValPreviousPoint;
114
+ long double lPreviousPointX;
115
+ long double lPreviousPointY;
116
+ VALUE lValNextPoint;
117
+ long double lNextPointX;
118
+ long double lNextPointY;
119
+ long double lDiffX;
120
+ long double lDiffY;
121
+ int lIdxValue;
122
+ long long int lNewValue;
123
+
124
+ // Loop on each points pair
125
+ for (lIdxPoint = 0; lIdxPoint < lNbrPoints-1; ++lIdxPoint) {
126
+ // Compute coordinates at the scale
127
+ lValPreviousPoint = rb_ary_entry(lValSortedPoints, lIdxPoint);
128
+ lPreviousPointX = lMinValue + (lDiffValue*(FIX2LONG(rb_ary_entry(lValPreviousPoint, 0)) - lMinScale))/lDiffScale;
129
+ lPreviousPointY = lMinValue + (lDiffValue*(FIX2LONG(rb_ary_entry(lValPreviousPoint, 1)) - lMinScale))/lDiffScale;
130
+ lValNextPoint = rb_ary_entry(lValSortedPoints, lIdxPoint+1);
131
+ lNextPointX = lMinValue + (lDiffValue*(FIX2LONG(rb_ary_entry(lValNextPoint, 0)) - lMinScale))/lDiffScale;
132
+ lNextPointY = lMinValue + (lDiffValue*(FIX2LONG(rb_ary_entry(lValNextPoint, 1)) - lMinScale))/lDiffScale;
133
+ lDiffX = lNextPointX - lPreviousPointX;
134
+ lDiffY = lNextPointY - lPreviousPointY;
135
+ /*
136
+ printf("NOSCALE lPreviousPoint=%ld,%ld lNextPoint=%ld,%ld\n", FIX2LONG(rb_ary_entry(lValPreviousPoint, 0)), FIX2LONG(rb_ary_entry(lValPreviousPoint, 1)), FIX2LONG(rb_ary_entry(lValNextPoint, 0)), FIX2LONG(rb_ary_entry(lValNextPoint, 1)));
137
+ printf("NOSCALE lMinScale=%lld lDiffScale=%lld\n", lMinScale, lDiffScale);
138
+ printf("lPreviousPoint=%Lf,%Lf lNextPoint=%Lf,%Lf\n", lPreviousPointX, lPreviousPointY, lNextPointX, lNextPointY);
139
+ printf("lMinValue=%lld lDiffValue=%lld\n", lMinValue, lDiffValue);
140
+ */
141
+ // Fill the part of the channel map between these 2 points
142
+ for (lIdxValue = lPreviousPointX; lIdxValue <= lNextPointX; ++lIdxValue) {
143
+ lNewValue = lPreviousPointY + round((lDiffY*(((long double)lIdxValue) - lPreviousPointX))/lDiffX);
144
+ /*
145
+ if (abs(lIdxValue) <= 10) {
146
+ printf("lIdxValue=%d lNewValue=%lld\n", lIdxValue, lNewValue);
147
+ }
148
+ */
149
+ if ((lNewValue > lMaxValue) ||
150
+ (lNewValue < lMinValue)) {
151
+ rResultCode = 1;
152
+ }
153
+ oPtrChannelMap[lIdxValue+lIdxMapOffset] = lNewValue;
154
+ }
155
+ }
156
+
157
+ return rResultCode;
158
+ }
159
+
160
+ /**
161
+ * Fill a channel map with a given function
162
+ *
163
+ * Parameters::
164
+ * * *iSelf* (_Object_): Calling object
165
+ * * *iNbrBitsPerSample* (<em>const int</em>): Number of bits per sample
166
+ * * *oPtrChannelMap* (<em>int*</em>): The channel map to fill
167
+ * * *iValFunction* (<em>map<Symbol,Object></em>): The function to apply
168
+ * Return::
169
+ * * _int_: Result code:
170
+ * ** *0*: The map never exceeds limits
171
+ * ** *1*: The map can exceed limits
172
+ * ** *2*: An error occurred
173
+ */
174
+ static int arithmutils_fillMapWithFunction(
175
+ VALUE iSelf,
176
+ const int iNbrBitsPerSample,
177
+ int* oPtrChannelMap,
178
+ VALUE iValFunction) {
179
+ int rResultCode = 0;
180
+
181
+ // Retrieve the function type
182
+ int lFunctionType = FIX2INT(rb_hash_aref(iValFunction, ID2SYM(rb_intern("FunctionType"))));
183
+ // Call the relevant method based on the type
184
+ switch (lFunctionType) {
185
+ case FCTTYPE_PIECEWISE_LINEAR:
186
+ rResultCode = arithmutils_fillMap_PiecewiseLinear(iNbrBitsPerSample, oPtrChannelMap, iValFunction);
187
+ break;
188
+ default: ; // The ; is here to make gcc compile: variables declarations are forbidden after a label.
189
+ char lLogMessage[256];
190
+ sprintf(lLogMessage, "Unknown function type %d", lFunctionType);
191
+ rb_funcall(iSelf, rb_intern("log_warn"), 1, rb_str_new2(lLogMessage));
192
+ rResultCode = 2;
193
+ break;
194
+ }
195
+
196
+ return rResultCode;
197
+ }
198
+
199
+ /**
200
+ * Create a map from a list of functions.
201
+ *
202
+ * Parameters::
203
+ * * *iSelf* (_FFT_): Self
204
+ * * *iValNbrBitsPerSample* (_Integer_): Number of bits per sample
205
+ * * *iValFunctions* (<em>list<map<Symbol,Object>></em>): List of functions, per channel
206
+ * Return::
207
+ * * _Object_: Container of the map
208
+ **/
209
+ static VALUE arithmutils_createMapFromFunctions(
210
+ VALUE iSelf,
211
+ VALUE iValNbrBitsPerSample,
212
+ VALUE iValFunctions) {
213
+ // Translate Ruby objects
214
+ int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
215
+ int lNbrChannels = RARRAY(iValFunctions)->len;
216
+
217
+ int lIdxChannel;
218
+ int* lPtrChannelMap;
219
+ int lNbrDifferentValues = 1 << iNbrBitsPerSample;
220
+ // The map
221
+ tMap* lPtrMap = ALLOC(tMap);
222
+ lPtrMap->nbrChannels = lNbrChannels;
223
+ lPtrMap->possibleExceedValues = 0;
224
+ lPtrMap->map = ALLOC_N(int*, lNbrChannels);
225
+ for (lIdxChannel = 0; lIdxChannel < lNbrChannels; ++lIdxChannel) {
226
+ lPtrChannelMap = ALLOC_N(int, lNbrDifferentValues);
227
+ if (arithmutils_fillMapWithFunction(iSelf, iNbrBitsPerSample, lPtrChannelMap, rb_ary_entry(iValFunctions, lIdxChannel)) == 1) {
228
+ lPtrMap->possibleExceedValues = 1;
229
+ }
230
+ lPtrMap->map[lIdxChannel] = lPtrChannelMap;
231
+ }
232
+
233
+ return Data_Wrap_Struct(rb_cObject, NULL, arithmutils_freeMap, lPtrMap);
234
+ }
235
+
236
+ /**
237
+ * Process a value read from an input buffer for the applyMap function.
238
+ *
239
+ * Parameters::
240
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
241
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
242
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
243
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tApplyMapStruct*</em>.
244
+ * Return::
245
+ * * _int_: The return code:
246
+ * ** 0: Continue iteration
247
+ * ** 1: Break all iterations
248
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
249
+ */
250
+ int arithmutils_processValue_applyMap(
251
+ const tSampleValue iValue,
252
+ tSampleValue* oPtrValue,
253
+ const tSampleIndex iIdxSample,
254
+ const int iIdxChannel,
255
+ void* iPtrArgs) {
256
+
257
+ (*oPtrValue) = ((tApplyMapStruct*)iPtrArgs)->map[iIdxChannel][iValue + ((tApplyMapStruct*)iPtrArgs)->offsetIdxMap];
258
+
259
+ return 0;
260
+ }
261
+
262
+ /**
263
+ * Apply a map on an input buffer, and outputs a result buffer.
264
+ *
265
+ * Parameters::
266
+ * * *iSelf* (_FFT_): Self
267
+ * * *iValMap* (_Object_): The container of the map
268
+ * * *iValInputBuffer* (_String_): The input buffer
269
+ * * *iValNbrSamples* (_Integer_): Number of samples from the buffer
270
+ * Return::
271
+ * * _String_: Output buffer
272
+ **/
273
+ static VALUE arithmutils_applyMap(
274
+ VALUE iSelf,
275
+ VALUE iValMap,
276
+ VALUE iValInputBuffer,
277
+ VALUE iValNbrBitsPerSample,
278
+ VALUE iValNbrSamples) {
279
+ // Translate Ruby objects
280
+ tSampleIndex iNbrSamples = FIX2LONG(iValNbrSamples);
281
+ int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
282
+ // Get the map
283
+ tMap* lPtrMap;
284
+ Data_Get_Struct(iValMap, tMap, lPtrMap);
285
+
286
+ // Get the input buffer
287
+ char* lPtrRawBuffer = RSTRING_PTR(iValInputBuffer);
288
+ int lBufferCharSize = RSTRING(iValInputBuffer)->len;
289
+ // Allocate the output buffer
290
+ char* lPtrOutputBuffer = ALLOC_N(char, lBufferCharSize);
291
+
292
+ // Create parameters to give the process
293
+ tApplyMapStruct lProcessParams;
294
+ lProcessParams.offsetIdxMap = 1 << (iNbrBitsPerSample-1);
295
+ lProcessParams.map = lPtrMap->map;
296
+
297
+ // Iterate through the raw buffer
298
+ commonutils_iterateThroughRawBufferOutput(
299
+ iSelf,
300
+ lPtrRawBuffer,
301
+ lPtrOutputBuffer,
302
+ iNbrBitsPerSample,
303
+ lPtrMap->nbrChannels,
304
+ iNbrSamples,
305
+ 0,
306
+ lPtrMap->possibleExceedValues,
307
+ &arithmutils_processValue_applyMap,
308
+ &lProcessParams
309
+ );
310
+
311
+ VALUE rValOutputBuffer = rb_str_new(lPtrOutputBuffer, lBufferCharSize);
312
+
313
+ free(lPtrOutputBuffer);
314
+
315
+ return rValOutputBuffer;
316
+ }
317
+
318
+ /**
319
+ * Process a value read from an input buffer for the mix function.
320
+ * Optimized for 8 bits samples.
321
+ *
322
+ * Parameters::
323
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
324
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
325
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
326
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tMixStruct*</em>.
327
+ * Return::
328
+ * * _int_: The return code:
329
+ * ** 0: Continue iteration
330
+ * ** 1: Break all iterations
331
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
332
+ */
333
+ int arithmutils_processValue_mix_8bits(
334
+ const tSampleValue iValue,
335
+ tSampleValue* oPtrValue,
336
+ const tSampleIndex iIdxSample,
337
+ const int iIdxChannel,
338
+ void* iPtrArgs) {
339
+ tMixStruct* lPtrParams = (tMixStruct*)iPtrArgs;
340
+
341
+ if (lPtrParams->nbrActiveBuffers > 0) {
342
+ // Check if we hit the limit of the last active buffer (the one having the least samples)
343
+ if (iIdxSample == lPtrParams->lstBuffers[lPtrParams->nbrActiveBuffers-1].nbrBufferSamples) {
344
+ // We have to check the buffers that are to be removed
345
+ lPtrParams->idxBuffer = lPtrParams->nbrActiveBuffers-1;
346
+ while ((lPtrParams->idxBuffer >= 0) &&
347
+ (lPtrParams->lstBuffers[lPtrParams->idxBuffer].nbrBufferSamples == iIdxSample )) {
348
+ --lPtrParams->nbrActiveBuffers;
349
+ --lPtrParams->idxBuffer;
350
+ }
351
+ }
352
+ // We have to mix several buffers
353
+ lPtrParams->tmpValue = ((long double)iValue)*lPtrParams->mainCoeff;
354
+ for (lPtrParams->idxBuffer = 0; lPtrParams->idxBuffer < lPtrParams->nbrActiveBuffers; ++lPtrParams->idxBuffer) {
355
+ lPtrParams->tmpValue += (((long double)*(lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_8bits))-128)*lPtrParams->lstBuffers[lPtrParams->idxBuffer].coeff;
356
+ ++lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_8bits;
357
+ }
358
+ // Export the result
359
+ (*oPtrValue) = round(lPtrParams->tmpValue);
360
+ } else {
361
+ // There is only the main buffer remaining
362
+ (*oPtrValue) = round(((long double)iValue)*lPtrParams->mainCoeff);
363
+ }
364
+
365
+ return 0;
366
+ }
367
+
368
+ /**
369
+ * Process a value read from an input buffer for the mix function.
370
+ * Optimized for 16 bits samples.
371
+ *
372
+ * Parameters::
373
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
374
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
375
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
376
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tMixStruct*</em>.
377
+ * Return::
378
+ * * _int_: The return code:
379
+ * ** 0: Continue iteration
380
+ * ** 1: Break all iterations
381
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
382
+ */
383
+ int arithmutils_processValue_mix_16bits(
384
+ const tSampleValue iValue,
385
+ tSampleValue* oPtrValue,
386
+ const tSampleIndex iIdxSample,
387
+ const int iIdxChannel,
388
+ void* iPtrArgs) {
389
+ tMixStruct* lPtrParams = (tMixStruct*)iPtrArgs;
390
+
391
+ if (lPtrParams->nbrActiveBuffers > 0) {
392
+ // Check if we hit the limit of the last active buffer (the one having the least samples)
393
+ if (iIdxSample == lPtrParams->lstBuffers[lPtrParams->nbrActiveBuffers-1].nbrBufferSamples) {
394
+ // We have to check the buffers that are to be removed
395
+ lPtrParams->idxBuffer = lPtrParams->nbrActiveBuffers-1;
396
+ while ((lPtrParams->idxBuffer >= 0) &&
397
+ (lPtrParams->lstBuffers[lPtrParams->idxBuffer].nbrBufferSamples == iIdxSample )) {
398
+ --lPtrParams->nbrActiveBuffers;
399
+ --lPtrParams->idxBuffer;
400
+ }
401
+ }
402
+ // We have to mix several buffers
403
+ lPtrParams->tmpValue = ((long double)iValue)*lPtrParams->mainCoeff;
404
+ for (lPtrParams->idxBuffer = 0; lPtrParams->idxBuffer < lPtrParams->nbrActiveBuffers; ++lPtrParams->idxBuffer) {
405
+ lPtrParams->tmpValue += ((long double)*(lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_16bits))*lPtrParams->lstBuffers[lPtrParams->idxBuffer].coeff;
406
+ ++lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_16bits;
407
+ }
408
+ // Export the result
409
+ (*oPtrValue) = round(lPtrParams->tmpValue);
410
+ } else {
411
+ // There is only the main buffer remaining
412
+ (*oPtrValue) = round(((long double)iValue)*lPtrParams->mainCoeff);
413
+ }
414
+
415
+ return 0;
416
+ }
417
+
418
+ /**
419
+ * Process a value read from an input buffer for the mix function.
420
+ * Optimized for 8 bits samples.
421
+ *
422
+ * Parameters::
423
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
424
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
425
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
426
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tMixStruct*</em>.
427
+ * Return::
428
+ * * _int_: The return code:
429
+ * ** 0: Continue iteration
430
+ * ** 1: Break all iterations
431
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
432
+ */
433
+ int arithmutils_processValue_mix_24bits(
434
+ const tSampleValue iValue,
435
+ tSampleValue* oPtrValue,
436
+ const tSampleIndex iIdxSample,
437
+ const int iIdxChannel,
438
+ void* iPtrArgs) {
439
+ tMixStruct* lPtrParams = (tMixStruct*)iPtrArgs;
440
+
441
+ if (lPtrParams->nbrActiveBuffers > 0) {
442
+ // Check if we hit the limit of the last active buffer (the one having the least samples)
443
+ if (iIdxSample == lPtrParams->lstBuffers[lPtrParams->nbrActiveBuffers-1].nbrBufferSamples) {
444
+ // We have to check the buffers that are to be removed
445
+ lPtrParams->idxBuffer = lPtrParams->nbrActiveBuffers-1;
446
+ while ((lPtrParams->idxBuffer >= 0) &&
447
+ (lPtrParams->lstBuffers[lPtrParams->idxBuffer].nbrBufferSamples == iIdxSample )) {
448
+ --lPtrParams->nbrActiveBuffers;
449
+ --lPtrParams->idxBuffer;
450
+ }
451
+ }
452
+ // We have to mix several buffers
453
+ lPtrParams->tmpValue = ((long double)iValue)*lPtrParams->mainCoeff;
454
+ for (lPtrParams->idxBuffer = 0; lPtrParams->idxBuffer < lPtrParams->nbrActiveBuffers; ++lPtrParams->idxBuffer) {
455
+ lPtrParams->tmpValue += ((long double)(lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_24bits->value))*lPtrParams->lstBuffers[lPtrParams->idxBuffer].coeff;
456
+ lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_24bits = (t24bits*)(((int)lPtrParams->lstBuffers[lPtrParams->idxBuffer].buffer_24bits)+3);
457
+ }
458
+ // Export the result
459
+ (*oPtrValue) = round(lPtrParams->tmpValue);
460
+ } else {
461
+ // There is only the main buffer remaining
462
+ (*oPtrValue) = round(((long double)iValue)*lPtrParams->mainCoeff);
463
+ }
464
+
465
+ return 0;
466
+ }
467
+
468
+ /**
469
+ * Mix a list of buffers.
470
+ * Prerequisite: The list of buffers have to be sorted, from the one having the more samples to the one having the less.
471
+ *
472
+ * Parameters::
473
+ * * *iSelf* (_FFT_): Self
474
+ * * *iValBuffers* (<em>list<list<Object>></em>): The list of buffers and their associated info (see Mix.rb for details)
475
+ * * *iValNbrBitsPerSample* (_Integer_): Number of bits per sample
476
+ * * *iValNbrChannels* (_Integer_): Number of channels
477
+ * Return::
478
+ * * _String_: Output buffer
479
+ * * _Integer_: Number of samples written
480
+ **/
481
+ static VALUE arithmutils_mixBuffers(
482
+ VALUE iSelf,
483
+ VALUE iValBuffers,
484
+ VALUE iValNbrBitsPerSample,
485
+ VALUE iValNbrChannels) {
486
+ // Translate Ruby objects
487
+ int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
488
+ int iNbrChannels = FIX2INT(iValNbrChannels);
489
+
490
+ // Create the list of additional buffers to consider
491
+ // This list is sorted from the one having the most samples to the one having the least samples
492
+ int lNbrBuffers = RARRAY(iValBuffers)->len;
493
+ tBufferInfo lPtrAdditionalBuffers[lNbrBuffers-1];
494
+ int lIdxBuffer;
495
+ VALUE lValBufferInfo;
496
+ char* lPtrBuffer;
497
+ for (lIdxBuffer = 0; lIdxBuffer < lNbrBuffers-1 ; ++lIdxBuffer) {
498
+ lValBufferInfo = rb_ary_entry(iValBuffers, lIdxBuffer+1);
499
+ lPtrBuffer = RSTRING_PTR(rb_ary_entry(lValBufferInfo, 3));
500
+ lPtrAdditionalBuffers[lIdxBuffer].coeff = NUM2DBL(rb_ary_entry(lValBufferInfo, 2));
501
+ lPtrAdditionalBuffers[lIdxBuffer].buffer_8bits = (unsigned char*)lPtrBuffer;
502
+ lPtrAdditionalBuffers[lIdxBuffer].buffer_16bits = (signed short int*)lPtrBuffer;
503
+ lPtrAdditionalBuffers[lIdxBuffer].buffer_24bits = (t24bits*)lPtrBuffer;
504
+ lPtrAdditionalBuffers[lIdxBuffer].nbrBufferSamples = FIX2INT(rb_ary_entry(lValBufferInfo, 4));
505
+ }
506
+
507
+ // Get the first buffer: the one that has the most samples
508
+ VALUE lValFirstBufferInfo = rb_ary_entry(iValBuffers, 0);
509
+ VALUE lValFirstBuffer = rb_ary_entry(lValFirstBufferInfo, 3);
510
+ char* lPtrFirstBuffer = RSTRING_PTR(lValFirstBuffer);
511
+ int lBufferCharSize = RSTRING(lValFirstBuffer)->len;
512
+ tSampleIndex lNbrSamples = FIX2INT(rb_ary_entry(lValFirstBufferInfo, 4));
513
+
514
+ // Allocate the output buffer
515
+ char* lPtrOutputBuffer = ALLOC_N(char, lBufferCharSize);
516
+
517
+ // Create variables to give to the iteration
518
+ tMixStruct lProcessParams;
519
+ lProcessParams.lstBuffers = lPtrAdditionalBuffers;
520
+ lProcessParams.nbrActiveBuffers = lNbrBuffers - 1;
521
+ lProcessParams.mainCoeff = NUM2DBL(rb_ary_entry(lValFirstBufferInfo, 2));
522
+
523
+ // Iterate through the raw buffer
524
+ if (iNbrBitsPerSample == 8) {
525
+ commonutils_iterateThroughRawBufferOutput(
526
+ iSelf,
527
+ lPtrFirstBuffer,
528
+ lPtrOutputBuffer,
529
+ iNbrBitsPerSample,
530
+ iNbrChannels,
531
+ lNbrSamples,
532
+ 0,
533
+ 1,
534
+ &arithmutils_processValue_mix_8bits,
535
+ &lProcessParams
536
+ );
537
+ } else if (iNbrBitsPerSample == 16) {
538
+ commonutils_iterateThroughRawBufferOutput(
539
+ iSelf,
540
+ lPtrFirstBuffer,
541
+ lPtrOutputBuffer,
542
+ iNbrBitsPerSample,
543
+ iNbrChannels,
544
+ lNbrSamples,
545
+ 0,
546
+ 1,
547
+ &arithmutils_processValue_mix_16bits,
548
+ &lProcessParams
549
+ );
550
+ } else {
551
+ // If it is not 24 bits, the method will throw an exception. So we are safe.
552
+ commonutils_iterateThroughRawBufferOutput(
553
+ iSelf,
554
+ lPtrFirstBuffer,
555
+ lPtrOutputBuffer,
556
+ iNbrBitsPerSample,
557
+ iNbrChannels,
558
+ lNbrSamples,
559
+ 0,
560
+ 1,
561
+ &arithmutils_processValue_mix_24bits,
562
+ &lProcessParams
563
+ );
564
+ }
565
+
566
+ VALUE rValOutputBuffer = rb_str_new(lPtrOutputBuffer, lBufferCharSize);
567
+
568
+ free(lPtrOutputBuffer);
569
+
570
+ return rb_ary_new3(2, rValOutputBuffer, LONG2FIX(lNbrSamples));
571
+ }
572
+
573
+ // The value that represents nil in the maps
574
+ static tSampleValue gImpossibleValue;
575
+ static ID gID_log_warn;
576
+
577
+ /**
578
+ * Process a value read from an input buffer for the compare function.
579
+ * Optimized for 8 bits samples.
580
+ *
581
+ * Parameters::
582
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
583
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
584
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
585
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tMixStruct*</em>.
586
+ * Return::
587
+ * * _int_: The return code:
588
+ * ** 0: Continue iteration
589
+ * ** 1: Break all iterations
590
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
591
+ */
592
+ int arithmutils_processValue_compare_8bits(
593
+ const tSampleValue iValue,
594
+ tSampleValue* oPtrValue,
595
+ const tSampleIndex iIdxSample,
596
+ const int iIdxChannel,
597
+ void* iPtrArgs) {
598
+ tCompareStruct* lPtrParams = (tCompareStruct*)iPtrArgs;
599
+
600
+ tSampleValue lValue2 = (*lPtrParams->buffer2_8bits)-128;
601
+ if (lPtrParams->map != NULL) {
602
+ // Complete the map
603
+ if (lPtrParams->map[lPtrParams->mapOffset+iValue] == gImpossibleValue) {
604
+ lPtrParams->map[lPtrParams->mapOffset+iValue] = lValue2;
605
+ } else if (lPtrParams->map[lPtrParams->mapOffset+iValue] != lValue2) {
606
+ char lMessage[256];
607
+ sprintf(lMessage, "Distortion for input value %d was found both %d and %d", iValue, lPtrParams->map[lPtrParams->mapOffset+iValue], lValue2);
608
+ rb_funcall(lPtrParams->self, gID_log_warn, 1, rb_str_new2(lMessage));
609
+ }
610
+ }
611
+ *oPtrValue = (tSampleValue)(((long double)(lValue2-iValue))*lPtrParams->coeffDiff);
612
+ mpz_add_ui(lPtrParams->cumulativeErrors, lPtrParams->cumulativeErrors, abs(lValue2-iValue));
613
+ ++lPtrParams->buffer2_8bits;
614
+
615
+ return 0;
616
+ }
617
+
618
+ /**
619
+ * Process a value read from an input buffer for the compare function.
620
+ * Optimized for 16 bits samples.
621
+ *
622
+ * Parameters::
623
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
624
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
625
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
626
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tMixStruct*</em>.
627
+ * Return::
628
+ * * _int_: The return code:
629
+ * ** 0: Continue iteration
630
+ * ** 1: Break all iterations
631
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
632
+ */
633
+ int arithmutils_processValue_compare_16bits(
634
+ const tSampleValue iValue,
635
+ tSampleValue* oPtrValue,
636
+ const tSampleIndex iIdxSample,
637
+ const int iIdxChannel,
638
+ void* iPtrArgs) {
639
+ tCompareStruct* lPtrParams = (tCompareStruct*)iPtrArgs;
640
+
641
+ tSampleValue lValue2 = *lPtrParams->buffer2_16bits;
642
+ if (lPtrParams->map != NULL) {
643
+ // Complete the map
644
+ if (lPtrParams->map[lPtrParams->mapOffset+iValue] == gImpossibleValue) {
645
+ lPtrParams->map[lPtrParams->mapOffset+iValue] = lValue2;
646
+ } else if (lPtrParams->map[lPtrParams->mapOffset+iValue] != lValue2) {
647
+ char lMessage[256];
648
+ sprintf(lMessage, "Distortion for input value %d was found both %d and %d", iValue, lPtrParams->map[lPtrParams->mapOffset+iValue], lValue2);
649
+ rb_funcall(lPtrParams->self, gID_log_warn, 1, rb_str_new2(lMessage));
650
+ }
651
+ }
652
+ *oPtrValue = (tSampleValue)(((long double)(lValue2-iValue))*lPtrParams->coeffDiff);
653
+ mpz_add_ui(lPtrParams->cumulativeErrors, lPtrParams->cumulativeErrors, abs(lValue2-iValue));
654
+ ++lPtrParams->buffer2_16bits;
655
+
656
+ return 0;
657
+ }
658
+
659
+ /**
660
+ * Process a value read from an input buffer for the compare function.
661
+ * Optimized for 8 bits samples.
662
+ *
663
+ * Parameters::
664
+ * * *iValue* (<em>const tSampleValue</em>): The value being read
665
+ * * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
666
+ * * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
667
+ * * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tMixStruct*</em>.
668
+ * Return::
669
+ * * _int_: The return code:
670
+ * ** 0: Continue iteration
671
+ * ** 1: Break all iterations
672
+ * ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
673
+ */
674
+ int arithmutils_processValue_compare_24bits(
675
+ const tSampleValue iValue,
676
+ tSampleValue* oPtrValue,
677
+ const tSampleIndex iIdxSample,
678
+ const int iIdxChannel,
679
+ void* iPtrArgs) {
680
+ tCompareStruct* lPtrParams = (tCompareStruct*)iPtrArgs;
681
+
682
+ tSampleValue lValue2 = lPtrParams->buffer2_24bits->value;
683
+ if (lPtrParams->map != NULL) {
684
+ // Complete the map
685
+ if (lPtrParams->map[lPtrParams->mapOffset+iValue] == gImpossibleValue) {
686
+ lPtrParams->map[lPtrParams->mapOffset+iValue] = lValue2;
687
+ } else if (lPtrParams->map[lPtrParams->mapOffset+iValue] != lValue2) {
688
+ char lMessage[256];
689
+ sprintf(lMessage, "Distortion for input value %d was found both %d and %d", iValue, lPtrParams->map[lPtrParams->mapOffset+iValue], lValue2);
690
+ rb_funcall(lPtrParams->self, gID_log_warn, 1, rb_str_new2(lMessage));
691
+ }
692
+ }
693
+ *oPtrValue = (tSampleValue)(((long double)(lValue2-iValue))*lPtrParams->coeffDiff);
694
+ mpz_add_ui(lPtrParams->cumulativeErrors, lPtrParams->cumulativeErrors, abs(lValue2-iValue));
695
+ lPtrParams->buffer2_24bits = (t24bits*)(((int)lPtrParams->buffer2_24bits)+3);
696
+
697
+ return 0;
698
+ }
699
+
700
+ /**
701
+ * Get a Ruby integer based on an MPZ storing an integer value.
702
+ *
703
+ * Parameters::
704
+ * * *iMPZ* (<em>mpz_t</em>): The mpz to read
705
+ * Return::
706
+ * * _Integer_: The Ruby integer
707
+ */
708
+ #define MAX_NUMBER_DIGITS 256
709
+ VALUE mpz2RubyInt(
710
+ mpz_t iMPZ) {
711
+ // The buffer where it will be written
712
+ char lStrNumber[MAX_NUMBER_DIGITS];
713
+ mpz_get_str(lStrNumber, 16, iMPZ);
714
+
715
+ return rb_cstr2inum(lStrNumber, 16);
716
+ }
717
+
718
+ /**
719
+ * Compare 2 buffers.
720
+ * Write the difference in an output buffer (Buffer2 - Buffer1).
721
+ * Buffers must have the same size.
722
+ *
723
+ * Parameters::
724
+ * * *iSelf* (_FFT_): Self
725
+ * * *iValBuffer1* (<em>_String_</em>): First buffer
726
+ * * *iValBuffer2* (<em>_String_</em>): Second buffer
727
+ * * *iValNbrBitsPerSample* (_Integer_): Number of bits per sample
728
+ * * *iValNbrChannels* (_Integer_): Number of channels
729
+ * * *iValNbrSamples* (_Integer_): Number of samples in buffers
730
+ * * *iValCoeffDiff* (_Float_): The coefficient to apply on the differences written to the output
731
+ * * *ioValMap* (</em>list<Integer></em>): Map of differences in sample values (can be nil if we don't want to compute it)
732
+ * Return::
733
+ * * _String_: Output buffer
734
+ * * _Integer_: Cumulative errors in this buffer
735
+ **/
736
+ static VALUE arithmutils_compareBuffers(
737
+ VALUE iSelf,
738
+ VALUE iValBuffer1,
739
+ VALUE iValBuffer2,
740
+ VALUE iValNbrBitsPerSample,
741
+ VALUE iValNbrChannels,
742
+ VALUE iValNbrSamples,
743
+ VALUE iValCoeffDiff,
744
+ VALUE ioValMap) {
745
+ // Translate Ruby objects
746
+ int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
747
+ int iNbrChannels = FIX2INT(iValNbrChannels);
748
+ int iNbrSamples = FIX2INT(iValNbrSamples);
749
+ long double iCoeffDiff = NUM2DBL(iValCoeffDiff);
750
+ char* lPtrBuffer1 = RSTRING_PTR(iValBuffer1);
751
+ char* lPtrBuffer2 = RSTRING_PTR(iValBuffer2);
752
+ int lBufferCharSize = RSTRING(iValBuffer1)->len;
753
+ int lNbrSampleValues = 0;
754
+ // Allocate the output buffer
755
+ char* lPtrOutputBuffer = ALLOC_N(char, lBufferCharSize);
756
+
757
+ // Create variables to give to the iteration
758
+ tCompareStruct lProcessParams;
759
+ lProcessParams.coeffDiff = iCoeffDiff;
760
+ lProcessParams.self = iSelf;
761
+ if (ioValMap == Qnil) {
762
+ lProcessParams.map = NULL;
763
+ } else {
764
+ // Create the internal map
765
+ // Define the impossible value
766
+ gImpossibleValue = pow(2, iNbrBitsPerSample-1) + 1;
767
+ lNbrSampleValues = RARRAY(ioValMap)->len;
768
+ tSampleValue lMap[lNbrSampleValues];
769
+ VALUE lValMapValue;
770
+ int lIdxSampleValue;
771
+ for (lIdxSampleValue = 0; lIdxSampleValue < lNbrSampleValues; ++lIdxSampleValue) {
772
+ lValMapValue = rb_ary_entry(ioValMap, lIdxSampleValue);
773
+ if (lValMapValue == Qnil) {
774
+ lMap[lIdxSampleValue] = gImpossibleValue;
775
+ } else {
776
+ lMap[lIdxSampleValue] = FIX2LONG(lValMapValue);
777
+ }
778
+ }
779
+ lProcessParams.map = lMap;
780
+ lProcessParams.mapOffset = pow(2, iNbrBitsPerSample-1);
781
+ }
782
+ lProcessParams.buffer2_8bits = (unsigned char*)lPtrBuffer2;
783
+ lProcessParams.buffer2_16bits = (signed short int*)lPtrBuffer2;
784
+ lProcessParams.buffer2_24bits = (t24bits*)lPtrBuffer2;
785
+ mpz_init(lProcessParams.cumulativeErrors);
786
+
787
+ // Iterate through the raw buffer
788
+ if (iNbrBitsPerSample == 8) {
789
+ commonutils_iterateThroughRawBufferOutput(
790
+ iSelf,
791
+ lPtrBuffer1,
792
+ lPtrOutputBuffer,
793
+ iNbrBitsPerSample,
794
+ iNbrChannels,
795
+ iNbrSamples,
796
+ 0,
797
+ 1,
798
+ &arithmutils_processValue_compare_8bits,
799
+ &lProcessParams
800
+ );
801
+ } else if (iNbrBitsPerSample == 16) {
802
+ commonutils_iterateThroughRawBufferOutput(
803
+ iSelf,
804
+ lPtrBuffer1,
805
+ lPtrOutputBuffer,
806
+ iNbrBitsPerSample,
807
+ iNbrChannels,
808
+ iNbrSamples,
809
+ 0,
810
+ 1,
811
+ &arithmutils_processValue_compare_16bits,
812
+ &lProcessParams
813
+ );
814
+ } else {
815
+ // If it is not 24 bits, the method will throw an exception. So we are safe.
816
+ commonutils_iterateThroughRawBufferOutput(
817
+ iSelf,
818
+ lPtrBuffer1,
819
+ lPtrOutputBuffer,
820
+ iNbrBitsPerSample,
821
+ iNbrChannels,
822
+ iNbrSamples,
823
+ 0,
824
+ 1,
825
+ &arithmutils_processValue_compare_24bits,
826
+ &lProcessParams
827
+ );
828
+ }
829
+
830
+ if (lProcessParams.map != NULL) {
831
+ // Modify the array in parameter
832
+ int lIdxSampleValue;
833
+ for (lIdxSampleValue = 0; lIdxSampleValue < lNbrSampleValues; ++lIdxSampleValue) {
834
+ if (lProcessParams.map[lIdxSampleValue] == gImpossibleValue) {
835
+ rb_ary_store(ioValMap, lIdxSampleValue, Qnil);
836
+ } else {
837
+ rb_ary_store(ioValMap, lIdxSampleValue, LONG2FIX(lProcessParams.map[lIdxSampleValue]));
838
+ }
839
+ }
840
+ }
841
+ VALUE rValCumulativeErrors = mpz2RubyInt(lProcessParams.cumulativeErrors);
842
+ mpz_clear(lProcessParams.cumulativeErrors);
843
+
844
+ VALUE rValOutputBuffer = rb_str_new(lPtrOutputBuffer, lBufferCharSize);
845
+
846
+ free(lPtrOutputBuffer);
847
+
848
+ return rb_ary_new3(2, rValOutputBuffer, rValCumulativeErrors);
849
+ }
850
+
851
+ // Initialize the module
852
+ void Init_ArithmUtils() {
853
+ VALUE lWSKModule = rb_define_module("WSK");
854
+ VALUE lArithmUtilsModule = rb_define_module_under(lWSKModule, "ArithmUtils");
855
+ VALUE lArithmUtilsClass = rb_define_class_under(lArithmUtilsModule, "ArithmUtils", rb_cObject);
856
+
857
+ rb_define_method(lArithmUtilsClass, "createMapFromFunctions", arithmutils_createMapFromFunctions, 2);
858
+ rb_define_method(lArithmUtilsClass, "applyMap", arithmutils_applyMap, 4);
859
+ rb_define_method(lArithmUtilsClass, "mixBuffers", arithmutils_mixBuffers, 3);
860
+ rb_define_method(lArithmUtilsClass, "compareBuffers", arithmutils_compareBuffers, 7);
861
+ gID_log_warn = rb_intern("log_warn");
862
+ }