WaveSwissKnife 0.2.0.20120302
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 +4 -0
- data/ChangeLog +31 -0
- data/Credits +3 -0
- data/LICENSE +31 -0
- data/README +15 -0
- data/ReleaseInfo +8 -0
- data/bin/WSK.rb +14 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
- data/ext/WSK/AnalyzeUtils/extconf.rb +7 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
- data/ext/WSK/ArithmUtils/extconf.rb +15 -0
- data/ext/WSK/CommonBuild.rb +29 -0
- data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
- data/ext/WSK/FFTUtils/extconf.rb +15 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
- data/ext/WSK/FunctionUtils/extconf.rb +15 -0
- data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
- data/ext/WSK/SilentUtils/extconf.rb +7 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
- data/ext/WSK/VolumeUtils/extconf.rb +15 -0
- data/external/CommonUtils/build.rb +28 -0
- data/external/CommonUtils/include/CommonUtils.h +177 -0
- data/external/CommonUtils/src/CommonUtils.c +639 -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 +155 -0
@@ -0,0 +1,15 @@
|
|
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
|
+
require "#{File.dirname(__FILE__)}/../CommonBuild"
|
7
|
+
# TODO (Cygwin): Adding -L/usr/local/lib is due to some Cygwin installs that do not include it with gcc
|
8
|
+
$LDFLAGS += ' -L/usr/local/lib '
|
9
|
+
begin
|
10
|
+
have_library('gmp')
|
11
|
+
rescue Exception
|
12
|
+
puts "\n\n!!! Missing library gmp in this system. Please install it from http://gmplib.org/\n\n"
|
13
|
+
raise
|
14
|
+
end
|
15
|
+
create_makefile('FFTUtils')
|
@@ -0,0 +1,182 @@
|
|
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
|
+
/**
|
13
|
+
* Free a C function.
|
14
|
+
* This method is called by Ruby GC.
|
15
|
+
*
|
16
|
+
* Parameters::
|
17
|
+
* * *iPtrCFct* (<em>void*</em>): The C function to free (in fact a <em>tFunction*</em>)
|
18
|
+
*/
|
19
|
+
static void volumeutils_freeCFct(void* iPtrCFct) {
|
20
|
+
tFunction* lPtrCFct = (tFunction*)iPtrCFct;
|
21
|
+
|
22
|
+
if (lPtrCFct->freeFct != NULL) {
|
23
|
+
(lPtrCFct->freeFct)(lPtrCFct->fctData);
|
24
|
+
}
|
25
|
+
free(lPtrCFct->fctData);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Free a C function of type Piecewiese Linear.
|
30
|
+
*
|
31
|
+
* Parameters::
|
32
|
+
* * *iPtrCFct* (<em>void*</em>): The C function to free (in fact a <em>tFunction_PiecewiseLinear*</em>)
|
33
|
+
*/
|
34
|
+
static void functionutils_freeCFct_PiecewiseLinear(void* iPtrCFct) {
|
35
|
+
tFunction_PiecewiseLinear* lPtrCFct = (tFunction_PiecewiseLinear*)iPtrCFct;
|
36
|
+
|
37
|
+
free(lPtrCFct->pointsX);
|
38
|
+
free(lPtrCFct->pointsY);
|
39
|
+
}
|
40
|
+
|
41
|
+
static int gID_to_s;
|
42
|
+
static int gID_numerator;
|
43
|
+
static int gID_denominator;
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Convert a Ruby function value into a long double
|
47
|
+
*
|
48
|
+
* Parameters::
|
49
|
+
* * *iValBD* (_Rational_): The value to convert
|
50
|
+
* Return::
|
51
|
+
* * <em>long double</em>: The equivalent long double
|
52
|
+
*/
|
53
|
+
inline long double value2ld(
|
54
|
+
VALUE iValBD) {
|
55
|
+
long double rResult;
|
56
|
+
|
57
|
+
mpf_t lDenominator;
|
58
|
+
mpf_init_set_str(lDenominator, RSTRING_PTR(rb_funcall(rb_funcall(iValBD, gID_denominator, 0), gID_to_s, 0)), 10);
|
59
|
+
mpf_t lDivResult;
|
60
|
+
mpf_init_set_str(lDivResult, RSTRING_PTR(rb_funcall(rb_funcall(iValBD, gID_numerator, 0), gID_to_s, 0)), 10);
|
61
|
+
mpf_div(lDivResult, lDivResult, lDenominator);
|
62
|
+
// TODO: Find a way to round correctly lDivResult. It is currently truncated.
|
63
|
+
rResult = mpf_get_d(lDivResult);
|
64
|
+
mpf_clear(lDivResult);
|
65
|
+
mpf_clear(lDenominator);
|
66
|
+
|
67
|
+
return rResult;
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Fill a C function with a given function of type Piecewise Linear
|
72
|
+
*
|
73
|
+
* Parameters::
|
74
|
+
* * *oPtrCFunction* (<em>tFunction*</em>): The C function to fill
|
75
|
+
* * *iValFunction* (<em>map<Symbol,Object></em>): The function to apply
|
76
|
+
* * *iIdxBeginSample* (<em>tSampleIndex</em>): First sample beginning the function
|
77
|
+
* * *iIdxEndSample* (<em>tSampleIndex</em>): Last sample ending the function
|
78
|
+
*/
|
79
|
+
static int functionutils_fillCFunction_PiecewiseLinear(
|
80
|
+
tFunction* oPtrCFunction,
|
81
|
+
VALUE iValFunction,
|
82
|
+
tSampleIndex iIdxBeginSample,
|
83
|
+
tSampleIndex iIdxEndSample) {
|
84
|
+
int rResultCode = 0;
|
85
|
+
|
86
|
+
// Create the basic structure
|
87
|
+
tFunction_PiecewiseLinear* lPtrFctData = ALLOC(tFunction_PiecewiseLinear);
|
88
|
+
oPtrCFunction->fctData = (void*)lPtrFctData;
|
89
|
+
oPtrCFunction->freeFct = functionutils_freeCFct_PiecewiseLinear;
|
90
|
+
|
91
|
+
// Fill it
|
92
|
+
// Read points in a sorted list of couples [x,y]
|
93
|
+
VALUE lValSortedPoints = rb_funcall(rb_funcall(rb_hash_aref(iValFunction, ID2SYM(rb_intern("Points"))), rb_intern("to_a"), 0), rb_intern("sort"), 0);
|
94
|
+
lPtrFctData->nbrPoints = RARRAY(lValSortedPoints)->len;
|
95
|
+
lPtrFctData->pointsX = ALLOC_N(tSampleIndex, lPtrFctData->nbrPoints);
|
96
|
+
lPtrFctData->pointsY = ALLOC_N(long double, lPtrFctData->nbrPoints);
|
97
|
+
|
98
|
+
// Get the X bounds
|
99
|
+
long double lMinX = value2ld(rb_ary_entry(rb_ary_entry(lValSortedPoints, 0), 0));
|
100
|
+
long double lDistX = value2ld(rb_ary_entry(rb_ary_entry(lValSortedPoints, lPtrFctData->nbrPoints-1), 0))-lMinX;
|
101
|
+
long double lDistSample = iIdxEndSample-iIdxBeginSample;
|
102
|
+
|
103
|
+
// Loop on each points pair
|
104
|
+
VALUE lValPoint;
|
105
|
+
tSampleIndex lPointX;
|
106
|
+
// Set the first point alone, as no check is performed about duplicates.
|
107
|
+
lValPoint = rb_ary_entry(lValSortedPoints, 0);
|
108
|
+
lPtrFctData->pointsX[0] = iIdxBeginSample+((tSampleIndex)((lDistSample*(value2ld(rb_ary_entry(lValPoint, 0))-lMinX))/lDistX));
|
109
|
+
lPtrFctData->pointsY[0] = value2ld(rb_ary_entry(lValPoint, 1));
|
110
|
+
/*
|
111
|
+
printf("Function point n.0: %lld,%LF\n", lPtrFctData->pointsX[0], lPtrFctData->pointsY[0]);
|
112
|
+
*/
|
113
|
+
// Loop on other points
|
114
|
+
int lIdxPoint;
|
115
|
+
int lIdxLastCPoint = 0;
|
116
|
+
for (lIdxPoint = 1; lIdxPoint < lPtrFctData->nbrPoints; ++lIdxPoint) {
|
117
|
+
lValPoint = rb_ary_entry(lValSortedPoints, lIdxPoint);
|
118
|
+
lPointX = iIdxBeginSample+((tSampleIndex)((lDistSample*(value2ld(rb_ary_entry(lValPoint, 0))-lMinX))/lDistX));
|
119
|
+
// If this abscisse is already set, change the already set one
|
120
|
+
if (lPtrFctData->pointsX[lIdxLastCPoint] != lPointX) {
|
121
|
+
++lIdxLastCPoint;
|
122
|
+
lPtrFctData->pointsX[lIdxLastCPoint] = lPointX;
|
123
|
+
}
|
124
|
+
lPtrFctData->pointsY[lIdxLastCPoint] = value2ld(rb_ary_entry(lValPoint, 1));
|
125
|
+
/*
|
126
|
+
printf("Function point n.%d: %lld,%LF\n", lIdxLastCPoint, lPtrFctData->pointsX[lIdxLastCPoint], lPtrFctData->pointsY[lIdxLastCPoint]);
|
127
|
+
*/
|
128
|
+
}
|
129
|
+
// It is possible that the count differs
|
130
|
+
lPtrFctData->nbrPoints = lIdxLastCPoint + 1;
|
131
|
+
|
132
|
+
return rResultCode;
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* Create a C function based on a given function to be applied on a given discrete range.
|
137
|
+
*
|
138
|
+
* Parameters::
|
139
|
+
* * *iSelf* (_Object_): Calling object
|
140
|
+
* * *iValFunction* (<em>map<Symbol,Object></em>): The function
|
141
|
+
* * *iValIdxBeginSample* (<em>map<Symbol,Object></em>): The first sample for this function
|
142
|
+
* * *iValIdxEndSample* (<em>map<Symbol,Object></em>): The last sample for this function
|
143
|
+
* Return::
|
144
|
+
* * _Object_: The Ruby object containing the C function
|
145
|
+
*/
|
146
|
+
static VALUE functionutils_createCFunction(
|
147
|
+
VALUE iSelf,
|
148
|
+
VALUE iValFunction,
|
149
|
+
VALUE iValIdxBeginSample,
|
150
|
+
VALUE iValIdxEndSample) {
|
151
|
+
tSampleIndex iIdxBeginSample = FIX2LONG(iValIdxBeginSample);
|
152
|
+
tSampleIndex iIdxEndSample = FIX2LONG(iValIdxEndSample);
|
153
|
+
|
154
|
+
tFunction* lPtrCFunction = ALLOC(tFunction);
|
155
|
+
// Retrieve the function type
|
156
|
+
lPtrCFunction->fctType = FIX2INT(rb_hash_aref(iValFunction, ID2SYM(rb_intern("FunctionType"))));
|
157
|
+
// Call the relevant method based on the type
|
158
|
+
switch (lPtrCFunction->fctType) {
|
159
|
+
case FCTTYPE_PIECEWISE_LINEAR:
|
160
|
+
functionutils_fillCFunction_PiecewiseLinear(lPtrCFunction, iValFunction, iIdxBeginSample, iIdxEndSample);
|
161
|
+
break;
|
162
|
+
default: ; // The ; is here to make gcc compile: variables declarations are forbidden after a label.
|
163
|
+
char lLogMessage[256];
|
164
|
+
sprintf(lLogMessage, "Unknown function type %d", lPtrCFunction->fctType);
|
165
|
+
rb_funcall(iSelf, rb_intern("log_err"), 1, rb_str_new2(lLogMessage));
|
166
|
+
break;
|
167
|
+
}
|
168
|
+
|
169
|
+
return Data_Wrap_Struct(rb_cObject, NULL, volumeutils_freeCFct, lPtrCFunction);
|
170
|
+
}
|
171
|
+
|
172
|
+
// Initialize the module
|
173
|
+
void Init_FunctionUtils() {
|
174
|
+
VALUE lWSKModule = rb_define_module("WSK");
|
175
|
+
VALUE lFunctionUtilsModule = rb_define_module_under(lWSKModule, "FunctionUtils");
|
176
|
+
VALUE lFunctionUtilsClass = rb_define_class_under(lFunctionUtilsModule, "FunctionUtils", rb_cObject);
|
177
|
+
|
178
|
+
rb_define_method(lFunctionUtilsClass, "createCFunction", functionutils_createCFunction, 3);
|
179
|
+
gID_to_s = rb_intern("to_s");
|
180
|
+
gID_numerator = rb_intern("numerator");
|
181
|
+
gID_denominator = rb_intern("denominator");
|
182
|
+
}
|
@@ -0,0 +1,15 @@
|
|
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
|
+
require "#{File.dirname(__FILE__)}/../CommonBuild"
|
7
|
+
# TODO (Cygwin): Adding -L/usr/local/lib is due to some Cygwin installs that do not include it with gcc
|
8
|
+
$LDFLAGS += ' -L/usr/local/lib '
|
9
|
+
begin
|
10
|
+
have_library('gmp')
|
11
|
+
rescue Exception
|
12
|
+
puts "\n\n!!! Missing library gmp in this system. Please install it from http://gmplib.org/\n\n"
|
13
|
+
raise
|
14
|
+
end
|
15
|
+
create_makefile('FunctionUtils')
|
@@ -0,0 +1,431 @@
|
|
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 <CommonUtils.h>
|
8
|
+
|
9
|
+
// Struct used to convey data among iterators in the getNextSilentSample method
|
10
|
+
typedef struct {
|
11
|
+
tSampleIndex* ptrIdxFirstSilentSample;
|
12
|
+
tThresholdInfo* ptrSilenceThresholds;
|
13
|
+
tSampleIndex* ptrIdxSilenceSample_Result;
|
14
|
+
tSampleIndex minSilenceSamples;
|
15
|
+
int nbrChannels;
|
16
|
+
} tFindSilentStruct;
|
17
|
+
|
18
|
+
// Struct used to convey data among iterators in the getFirstSampleBeyondThreshold method
|
19
|
+
typedef struct {
|
20
|
+
tSampleIndex* ptrIdxSample_Result;
|
21
|
+
tThresholdInfo* ptrThresholds;
|
22
|
+
int nbrChannels;
|
23
|
+
} tFirstSampleBeyondThresholdStruct;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Process a value read from an input buffer for the NextSilentSample function.
|
27
|
+
*
|
28
|
+
* Parameters::
|
29
|
+
* * *iValue* (<em>const tSampleValue</em>): The value being read
|
30
|
+
* * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
|
31
|
+
* * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
|
32
|
+
* * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tFindSilentStruct*</em>.
|
33
|
+
* Return::
|
34
|
+
* * _int_: The return code:
|
35
|
+
* ** 0: Continue iteration
|
36
|
+
* ** 1: Break all iterations
|
37
|
+
* ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
|
38
|
+
*/
|
39
|
+
int silentutils_processValue(
|
40
|
+
const tSampleValue iValue,
|
41
|
+
const tSampleIndex iIdxSample,
|
42
|
+
const int iIdxChannel,
|
43
|
+
void* iPtrArgs) {
|
44
|
+
int rResult = 0;
|
45
|
+
|
46
|
+
// Interpret parameters
|
47
|
+
tFindSilentStruct* lPtrVariables = (tFindSilentStruct*)iPtrArgs;
|
48
|
+
|
49
|
+
if ((iValue < (lPtrVariables->ptrSilenceThresholds)[iIdxChannel].min) ||
|
50
|
+
(iValue > (lPtrVariables->ptrSilenceThresholds)[iIdxChannel].max)) {
|
51
|
+
// This value is not silent
|
52
|
+
// If we were in silence that has not yet reached its minimal duration, cancel this last silence
|
53
|
+
*(lPtrVariables->ptrIdxFirstSilentSample) = -1;
|
54
|
+
// Don't even read other channels
|
55
|
+
rResult = 2;
|
56
|
+
} else {
|
57
|
+
// This value is silent
|
58
|
+
if ((*(lPtrVariables->ptrIdxFirstSilentSample)) == -1) {
|
59
|
+
// This is the first silent value we have
|
60
|
+
*(lPtrVariables->ptrIdxFirstSilentSample) = iIdxSample;
|
61
|
+
}
|
62
|
+
// Check if the minimal duration has been reached
|
63
|
+
if (iIdxSample - (*(lPtrVariables->ptrIdxFirstSilentSample)) + 1 >= lPtrVariables->minSilenceSamples) {
|
64
|
+
// We have found a silence according to thresholds.
|
65
|
+
*(lPtrVariables->ptrIdxSilenceSample_Result) = *(lPtrVariables->ptrIdxFirstSilentSample);
|
66
|
+
// Stop iterations
|
67
|
+
rResult = 1;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
return rResult;
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Process a value read from an input buffer for the NextSilentSample function.
|
76
|
+
* Do it in backwards search.
|
77
|
+
*
|
78
|
+
* Parameters::
|
79
|
+
* * *iValue* (<em>const tSampleValue</em>): The value being read
|
80
|
+
* * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
|
81
|
+
* * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
|
82
|
+
* * *iPtrArgs* (<em>void*</em>): additional arguments. In fact a <em>tFindSilentStruct*</em>.
|
83
|
+
* Return::
|
84
|
+
* * _int_: The return code:
|
85
|
+
* ** 0: Continue iteration
|
86
|
+
* ** 1: Break all iterations
|
87
|
+
* ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
|
88
|
+
*/
|
89
|
+
int silentutils_Reverse_processValue(
|
90
|
+
const tSampleValue iValue,
|
91
|
+
const tSampleIndex iIdxSample,
|
92
|
+
const int iIdxChannel,
|
93
|
+
void* iPtrArgs) {
|
94
|
+
int rResult = 0;
|
95
|
+
|
96
|
+
// Interpret parameters
|
97
|
+
tFindSilentStruct* lPtrVariables = (tFindSilentStruct*)iPtrArgs;
|
98
|
+
|
99
|
+
if ((iValue < (lPtrVariables->ptrSilenceThresholds)[iIdxChannel].min) ||
|
100
|
+
(iValue > (lPtrVariables->ptrSilenceThresholds)[iIdxChannel].max)) {
|
101
|
+
// This value is not silent
|
102
|
+
// If we were in silence that has not yet reached its minimal duration, cancel this last silence
|
103
|
+
*(lPtrVariables->ptrIdxFirstSilentSample) = -1;
|
104
|
+
// Don't even read other channels
|
105
|
+
rResult = 2;
|
106
|
+
} else {
|
107
|
+
// This value is silent
|
108
|
+
if ((*(lPtrVariables->ptrIdxFirstSilentSample)) == -1) {
|
109
|
+
// This is the first silent value we have
|
110
|
+
*(lPtrVariables->ptrIdxFirstSilentSample) = iIdxSample;
|
111
|
+
}
|
112
|
+
// Check if the minimal duration has been reached
|
113
|
+
if ((*(lPtrVariables->ptrIdxFirstSilentSample)) - iIdxSample + 1 >= lPtrVariables->minSilenceSamples) {
|
114
|
+
// We have found a silence according to thresholds.
|
115
|
+
*(lPtrVariables->ptrIdxSilenceSample_Result) = *(lPtrVariables->ptrIdxFirstSilentSample);
|
116
|
+
// Stop iterations
|
117
|
+
rResult = 1;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
return rResult;
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Code block called by getNextSilentSample in the each_raw_buffer loop.
|
126
|
+
* This is meant to be used with rb_iterate.
|
127
|
+
*
|
128
|
+
* Parameters::
|
129
|
+
* * *iValYieldArgs* (<em>list<Object></em>): The yield arguments:
|
130
|
+
* ** *iValInputRawBuffer* (_String_): The raw buffer
|
131
|
+
* ** *iValNbrSamples* (_Integer_): The number of samples in this buffer
|
132
|
+
* ** *iValNbrChannels* (_Integer_): The number of channels in this buffer
|
133
|
+
* * *iValIterateArgs* (<em>list<Object></em>): The iterate arguments:
|
134
|
+
* ** *iValNbrBitsPerSample* (_Integer_): Number of bits per sample
|
135
|
+
* ** *iValPtrIdxSample* (_Integer_): Address of the IdxSample variable
|
136
|
+
* ** *iValPtrIdxFirstSilentSample* (_Integer_): Address of the IdxFirstSilentSample variable
|
137
|
+
* ** *iValPtrSilenceThresholds* (_Integer_): Address of the SilenceThresholds variable
|
138
|
+
* ** *iValMinSilenceSamples* (_Integer_): Minimal silence samples
|
139
|
+
* ** *iValPtrIdxSilenceSample_Result* (_Integer_): Address of the IdxSilenceSample_Result variable
|
140
|
+
* ** *iValBackwardsSearch* (_Boolean_): Do we search backwards ?
|
141
|
+
*/
|
142
|
+
static VALUE silentutils_blockEachRawBuffer(
|
143
|
+
VALUE iValYieldArgs,
|
144
|
+
VALUE iValIterateArgs) {
|
145
|
+
// Read arguments
|
146
|
+
VALUE iValInputRawBuffer = rb_ary_entry(iValYieldArgs, 0);
|
147
|
+
VALUE iValNbrSamples = rb_ary_entry(iValYieldArgs, 1);
|
148
|
+
VALUE iValNbrChannels = rb_ary_entry(iValYieldArgs, 2);
|
149
|
+
VALUE iValNbrBitsPerSample = rb_ary_entry(iValIterateArgs, 0);
|
150
|
+
VALUE iValPtrIdxSample = rb_ary_entry(iValIterateArgs, 1);
|
151
|
+
VALUE iValPtrIdxFirstSilentSample = rb_ary_entry(iValIterateArgs, 2);
|
152
|
+
VALUE iValPtrSilenceThresholds = rb_ary_entry(iValIterateArgs, 3);
|
153
|
+
VALUE iValMinSilenceSamples = rb_ary_entry(iValIterateArgs, 4);
|
154
|
+
VALUE iValPtrIdxSilenceSample_Result = rb_ary_entry(iValIterateArgs, 5);
|
155
|
+
VALUE iValBackwardsSearch = rb_ary_entry(iValIterateArgs, 6);
|
156
|
+
// Translate parameters in C types
|
157
|
+
int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
|
158
|
+
tSampleIndex iNbrSamples = FIX2LONG(iValNbrSamples);
|
159
|
+
int iNbrChannels = FIX2INT(iValNbrChannels);
|
160
|
+
tSampleIndex* lPtrIdxSample = (tSampleIndex*)FIX2INT(iValPtrIdxSample);
|
161
|
+
tSampleIndex* lPtrIdxFirstSilentSample = (tSampleIndex*)FIX2INT(iValPtrIdxFirstSilentSample);
|
162
|
+
tThresholdInfo* lPtrSilenceThresholds = (tThresholdInfo*)FIX2INT(iValPtrSilenceThresholds);
|
163
|
+
tSampleIndex iMinSilenceSamples = FIX2LONG(iValMinSilenceSamples);
|
164
|
+
tSampleIndex* lPtrIdxSilenceSample_Result = (tSampleIndex*)FIX2INT(iValPtrIdxSilenceSample_Result);
|
165
|
+
|
166
|
+
// Get the real underlying raw buffer
|
167
|
+
char* lPtrRawBuffer = RSTRING_PTR(iValInputRawBuffer);
|
168
|
+
|
169
|
+
// Set variables to give to the process
|
170
|
+
tFindSilentStruct lProcessVariables;
|
171
|
+
lProcessVariables.ptrIdxFirstSilentSample = lPtrIdxFirstSilentSample;
|
172
|
+
lProcessVariables.ptrSilenceThresholds = lPtrSilenceThresholds;
|
173
|
+
lProcessVariables.ptrIdxSilenceSample_Result = lPtrIdxSilenceSample_Result;
|
174
|
+
lProcessVariables.minSilenceSamples = iMinSilenceSamples;
|
175
|
+
lProcessVariables.nbrChannels = iNbrChannels;
|
176
|
+
|
177
|
+
// Iterate through the raw buffer
|
178
|
+
if (iValBackwardsSearch == Qtrue) {
|
179
|
+
commonutils_iterateReverseThroughRawBuffer(
|
180
|
+
lPtrRawBuffer,
|
181
|
+
iNbrBitsPerSample,
|
182
|
+
iNbrChannels,
|
183
|
+
iNbrSamples,
|
184
|
+
*lPtrIdxSample,
|
185
|
+
&silentutils_Reverse_processValue,
|
186
|
+
&lProcessVariables
|
187
|
+
);
|
188
|
+
} else {
|
189
|
+
commonutils_iterateThroughRawBuffer(
|
190
|
+
lPtrRawBuffer,
|
191
|
+
iNbrBitsPerSample,
|
192
|
+
iNbrChannels,
|
193
|
+
iNbrSamples,
|
194
|
+
*lPtrIdxSample,
|
195
|
+
&silentutils_processValue,
|
196
|
+
&lProcessVariables
|
197
|
+
);
|
198
|
+
}
|
199
|
+
|
200
|
+
// If the result was found in this raw buffer, stop parsing everything
|
201
|
+
if (*lPtrIdxSilenceSample_Result != -1) {
|
202
|
+
rb_iter_break();
|
203
|
+
}
|
204
|
+
|
205
|
+
// Modify the sample index
|
206
|
+
if (iValBackwardsSearch == Qtrue) {
|
207
|
+
(*lPtrIdxSample) -= iNbrSamples;
|
208
|
+
} else {
|
209
|
+
(*lPtrIdxSample) += iNbrSamples;
|
210
|
+
}
|
211
|
+
|
212
|
+
return Qnil;
|
213
|
+
}
|
214
|
+
|
215
|
+
/**
|
216
|
+
* Get the next silent sample from an input buffer
|
217
|
+
*
|
218
|
+
* Parameters::
|
219
|
+
* * *iSelf* (_FFTUtils_): Self
|
220
|
+
* * *iValInputData* (<em>WSK::Model::InputData</em>): The input data
|
221
|
+
* * *iValIdxStartSample* (_Integer_): Index of the first sample to search from
|
222
|
+
* * *iValSilenceThresholds* (<em>list< [Integer,Integer] ></em>): The silence thresholds specifications
|
223
|
+
* * *iValMinSilenceSamples* (_Integer_): Number of samples minimum to identify a silence
|
224
|
+
* * *iValBackwardsSearch* (_Boolean_): Do we search backwards ?
|
225
|
+
* Return::
|
226
|
+
* * _Integer_: Index of the next silent sample, or nil if none
|
227
|
+
**/
|
228
|
+
static VALUE silentutils_getNextSilentInThresholds(
|
229
|
+
VALUE iSelf,
|
230
|
+
VALUE iValInputData,
|
231
|
+
VALUE iValIdxStartSample,
|
232
|
+
VALUE iValSilenceThresholds,
|
233
|
+
VALUE iValMinSilenceSamples,
|
234
|
+
VALUE iValBackwardsSearch) {
|
235
|
+
VALUE rValNextSilentSample = Qnil;
|
236
|
+
|
237
|
+
// Translate parameters in C types
|
238
|
+
tSampleIndex iIdxStartSample = FIX2LONG(iValIdxStartSample);
|
239
|
+
|
240
|
+
// The cursor of samples. Set it to the first sample we start from searching.
|
241
|
+
tSampleIndex lIdxSample = iIdxStartSample;
|
242
|
+
// Read some info from the Header
|
243
|
+
VALUE lValHeader = rb_funcall(iValInputData, rb_intern("Header"), 0);
|
244
|
+
VALUE lValNbrBitsPerSample = rb_funcall(lValHeader, rb_intern("NbrBitsPerSample"), 0);
|
245
|
+
VALUE lValNbrChannels = rb_funcall(lValHeader, rb_intern("NbrChannels"), 0);
|
246
|
+
// Decode the thresholds
|
247
|
+
int lNbrChannels = FIX2INT(lValNbrChannels);
|
248
|
+
tThresholdInfo lSilenceThresholds[lNbrChannels];
|
249
|
+
VALUE lTmpThresholds;
|
250
|
+
int lIdxChannel;
|
251
|
+
for(lIdxChannel = 0; lIdxChannel < lNbrChannels; ++lIdxChannel) {
|
252
|
+
lTmpThresholds = rb_ary_entry(iValSilenceThresholds, lIdxChannel);
|
253
|
+
lSilenceThresholds[lIdxChannel].min = FIX2INT(rb_ary_entry(lTmpThresholds, 0));
|
254
|
+
lSilenceThresholds[lIdxChannel].max = FIX2INT(rb_ary_entry(lTmpThresholds, 1));
|
255
|
+
}
|
256
|
+
|
257
|
+
// Index of the first silent sample encountered while parsing.
|
258
|
+
// Used to assert the minimal duration of the silence. -1 means we don't have one yet.
|
259
|
+
tSampleIndex lIdxFirstSilentSample;
|
260
|
+
// The result
|
261
|
+
tSampleIndex lIdxSilenceSample_Result;
|
262
|
+
|
263
|
+
// Encapsulate pointers to the data that will be used and modified by the iteration block
|
264
|
+
VALUE lValPtrIdxSample = INT2FIX(&lIdxSample);
|
265
|
+
VALUE lValPtrIdxFirstSilentSample = INT2FIX(&lIdxFirstSilentSample);
|
266
|
+
VALUE lValPtrSilenceThresholds = INT2FIX(&lSilenceThresholds);
|
267
|
+
VALUE lValPtrIdxSilenceSample_Result = INT2FIX(&lIdxSilenceSample_Result);
|
268
|
+
|
269
|
+
lIdxFirstSilentSample = -1;
|
270
|
+
lIdxSilenceSample_Result = -1;
|
271
|
+
|
272
|
+
// Parse the data, using thresholds matching only
|
273
|
+
if (iValBackwardsSearch == Qtrue) {
|
274
|
+
rb_iterate(
|
275
|
+
commonutils_callEachReverseRawBuffer,
|
276
|
+
rb_ary_new3(2,
|
277
|
+
iValInputData,
|
278
|
+
LONG2FIX(lIdxSample)),
|
279
|
+
silentutils_blockEachRawBuffer,
|
280
|
+
rb_ary_new3(7,
|
281
|
+
lValNbrBitsPerSample,
|
282
|
+
lValPtrIdxSample,
|
283
|
+
lValPtrIdxFirstSilentSample,
|
284
|
+
lValPtrSilenceThresholds,
|
285
|
+
iValMinSilenceSamples,
|
286
|
+
lValPtrIdxSilenceSample_Result,
|
287
|
+
iValBackwardsSearch)
|
288
|
+
);
|
289
|
+
} else {
|
290
|
+
rb_iterate(
|
291
|
+
commonutils_callEachRawBuffer,
|
292
|
+
rb_ary_new3(2,
|
293
|
+
iValInputData,
|
294
|
+
LONG2FIX(lIdxSample)),
|
295
|
+
silentutils_blockEachRawBuffer,
|
296
|
+
rb_ary_new3(7,
|
297
|
+
lValNbrBitsPerSample,
|
298
|
+
lValPtrIdxSample,
|
299
|
+
lValPtrIdxFirstSilentSample,
|
300
|
+
lValPtrSilenceThresholds,
|
301
|
+
iValMinSilenceSamples,
|
302
|
+
lValPtrIdxSilenceSample_Result,
|
303
|
+
iValBackwardsSearch)
|
304
|
+
);
|
305
|
+
}
|
306
|
+
|
307
|
+
if (lIdxSilenceSample_Result != -1) {
|
308
|
+
rValNextSilentSample = LONG2FIX(lIdxSilenceSample_Result);
|
309
|
+
}
|
310
|
+
|
311
|
+
return rValNextSilentSample;
|
312
|
+
}
|
313
|
+
|
314
|
+
/**
|
315
|
+
* Process a value read from an input buffer for the NextSilentSample function.
|
316
|
+
*
|
317
|
+
* Parameters::
|
318
|
+
* * *iValue* (<em>const tSampleValue</em>): The value being read
|
319
|
+
* * *iIdxSample* (<em>const tSampleIndex</em>): Index of this sample
|
320
|
+
* * *iIdxChannel* (<em>const int</em>): Channel corresponding to the value being read
|
321
|
+
* * *iPtrArgs* (<em>void*</em>): additional arguments. In fact an <em>tFirstSampleBeyondThresholdStruct*</em>.
|
322
|
+
* Return::
|
323
|
+
* * _int_: The return code:
|
324
|
+
* ** 0: Continue iteration
|
325
|
+
* ** 1: Break all iterations
|
326
|
+
* ** 2: Skip directly to the next sample (don't call us for other channels of this sample)
|
327
|
+
*/
|
328
|
+
int silentutils_sbt_processValue(
|
329
|
+
const tSampleValue iValue,
|
330
|
+
const tSampleIndex iIdxSample,
|
331
|
+
const int iIdxChannel,
|
332
|
+
void* iPtrArgs) {
|
333
|
+
int rResult = 0;
|
334
|
+
|
335
|
+
// Interpret parameters
|
336
|
+
tFirstSampleBeyondThresholdStruct* lPtrVariables = (tFirstSampleBeyondThresholdStruct*)iPtrArgs;
|
337
|
+
|
338
|
+
if ((iValue < (lPtrVariables->ptrThresholds)[iIdxChannel].min) ||
|
339
|
+
(iValue > (lPtrVariables->ptrThresholds)[iIdxChannel].max)) {
|
340
|
+
// This value is not silent
|
341
|
+
rResult = 1;
|
342
|
+
*(lPtrVariables->ptrIdxSample_Result) = iIdxSample;
|
343
|
+
}
|
344
|
+
|
345
|
+
return rResult;
|
346
|
+
}
|
347
|
+
|
348
|
+
/**
|
349
|
+
* Get the sample index that exceeds a threshold in a raw buffer.
|
350
|
+
*
|
351
|
+
* Parameters::
|
352
|
+
* * *iSelf* (_FFTUtils_): Self
|
353
|
+
* * *iValRawBuffer* (_String_): The raw buffer
|
354
|
+
* * *iValThresholds* (<em>list< [Integer,Integer] ></em>): The thresholds
|
355
|
+
* * *iValNbrBitsPerSample* (_Integer_): Number of bits per sample
|
356
|
+
* * *iValNbrChannels* (_Integer_): Number of channels
|
357
|
+
* * *iValNbrSamples* (_Integer_): Number of samples
|
358
|
+
* * *iValLastSample* (_Boolean_): Are we looking for the last sample beyond threshold ?
|
359
|
+
* Return::
|
360
|
+
* * _Integer_: Index of the first sample exceeding thresholds, or nil if none
|
361
|
+
*/
|
362
|
+
static VALUE silentutils_getSampleBeyondThresholds(
|
363
|
+
VALUE iSelf,
|
364
|
+
VALUE iValRawBuffer,
|
365
|
+
VALUE iValThresholds,
|
366
|
+
VALUE iValNbrBitsPerSample,
|
367
|
+
VALUE iValNbrChannels,
|
368
|
+
VALUE iValNbrSamples,
|
369
|
+
VALUE iValLastSample) {
|
370
|
+
VALUE rValIdxFirstSample = Qnil;
|
371
|
+
|
372
|
+
// Translate parameters in C types
|
373
|
+
tSampleIndex iNbrSamples = FIX2LONG(iValNbrSamples);
|
374
|
+
int iNbrChannels = FIX2INT(iValNbrChannels);
|
375
|
+
int iNbrBitsPerSample = FIX2INT(iValNbrBitsPerSample);
|
376
|
+
// Get the underlying char*
|
377
|
+
char* lPtrRawBuffer = RSTRING_PTR(iValRawBuffer);
|
378
|
+
// Decode the thresholds
|
379
|
+
tThresholdInfo lThresholds[iNbrChannels];
|
380
|
+
VALUE lTmpThresholds;
|
381
|
+
int lIdxChannel;
|
382
|
+
for(lIdxChannel = 0; lIdxChannel < iNbrChannels; ++lIdxChannel) {
|
383
|
+
lTmpThresholds = rb_ary_entry(iValThresholds, lIdxChannel);
|
384
|
+
lThresholds[lIdxChannel].min = FIX2INT(rb_ary_entry(lTmpThresholds, 0));
|
385
|
+
lThresholds[lIdxChannel].max = FIX2INT(rb_ary_entry(lTmpThresholds, 1));
|
386
|
+
}
|
387
|
+
// Set variables to give to the process
|
388
|
+
tSampleIndex lIdxSampleOut = -1;
|
389
|
+
tFirstSampleBeyondThresholdStruct lProcessVariables;
|
390
|
+
lProcessVariables.ptrThresholds = &lThresholds[0];
|
391
|
+
lProcessVariables.ptrIdxSample_Result = &lIdxSampleOut;
|
392
|
+
lProcessVariables.nbrChannels = iNbrChannels;
|
393
|
+
|
394
|
+
// Parse the buffer
|
395
|
+
if (iValLastSample == Qtrue) {
|
396
|
+
commonutils_iterateReverseThroughRawBuffer(
|
397
|
+
lPtrRawBuffer,
|
398
|
+
iNbrBitsPerSample,
|
399
|
+
iNbrChannels,
|
400
|
+
iNbrSamples,
|
401
|
+
iNbrSamples-1,
|
402
|
+
&silentutils_sbt_processValue,
|
403
|
+
&lProcessVariables
|
404
|
+
);
|
405
|
+
} else {
|
406
|
+
commonutils_iterateThroughRawBuffer(
|
407
|
+
lPtrRawBuffer,
|
408
|
+
iNbrBitsPerSample,
|
409
|
+
iNbrChannels,
|
410
|
+
iNbrSamples,
|
411
|
+
0,
|
412
|
+
&silentutils_sbt_processValue,
|
413
|
+
&lProcessVariables
|
414
|
+
);
|
415
|
+
}
|
416
|
+
if (lIdxSampleOut != -1) {
|
417
|
+
rValIdxFirstSample = LONG2FIX(lIdxSampleOut);
|
418
|
+
}
|
419
|
+
|
420
|
+
return rValIdxFirstSample;
|
421
|
+
}
|
422
|
+
|
423
|
+
// Initialize the module
|
424
|
+
void Init_SilentUtils() {
|
425
|
+
VALUE lWSKModule = rb_define_module("WSK");
|
426
|
+
VALUE lSilentUtilsModule = rb_define_module_under(lWSKModule, "SilentUtils");
|
427
|
+
VALUE lSilentUtilsClass = rb_define_class_under(lSilentUtilsModule, "SilentUtils", rb_cObject);
|
428
|
+
|
429
|
+
rb_define_method(lSilentUtilsClass, "getNextSilentInThresholds", silentutils_getNextSilentInThresholds, 5);
|
430
|
+
rb_define_method(lSilentUtilsClass, "getSampleBeyondThresholds", silentutils_getSampleBeyondThresholds, 6);
|
431
|
+
}
|