WaveSwissKnife 0.2.0.20120302
Sign up to get free protection for your applications and to get access to all the features.
- 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,770 @@
|
|
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 'rational'
|
7
|
+
|
8
|
+
# Convert a float to a Rational
|
9
|
+
class Float
|
10
|
+
|
11
|
+
# Convert to a Rational
|
12
|
+
#
|
13
|
+
# Return::
|
14
|
+
# * _Rational_: Corresponding Rational
|
15
|
+
def to_r
|
16
|
+
rResult = nil
|
17
|
+
|
18
|
+
if (self.nan?)
|
19
|
+
rResult = Rational(0,0) # Div by zero error
|
20
|
+
elsif (self.infinite?)
|
21
|
+
rResult = Rational(self<0 ? -1 : 1,0) # Div by zero error
|
22
|
+
else
|
23
|
+
lSign, lExponent, lFloat = [self].pack("G").unpack("B*").first.unpack("AA11A52")
|
24
|
+
lSign = (-1)**lSign.to_i
|
25
|
+
lExponent = lExponent.to_i(2)
|
26
|
+
if ((lExponent.nonzero?) and
|
27
|
+
(lExponent<2047))
|
28
|
+
rResult = Rational(lSign)*Rational(2)**(lExponent-1023)*Rational("1#{lFloat}".to_i(2),0x10000000000000)
|
29
|
+
elsif (lExponent.zero?)
|
30
|
+
rResult = Rational(lSign)*Rational(2)**(-1024)*Rational("0#{lFloat}".to_i(2),0x10000000000000)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
return rResult
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
module WSK
|
40
|
+
|
41
|
+
module Functions
|
42
|
+
|
43
|
+
# Function type piecewise linear.
|
44
|
+
# Here are the possible attributes used by this type:
|
45
|
+
# *:MinValue* (_Rational_): Minimal value of the plots of the function [optional = Minimal value of points]
|
46
|
+
# *:MaxValue* (_Rational_): Maximal value of the plots of the function [optional = Maximal value of points]
|
47
|
+
# *:Points* (<em>map<Rational,Rational></em>): Coordinates of points indicating each linear part
|
48
|
+
FCTTYPE_PIECEWISE_LINEAR = 0
|
49
|
+
|
50
|
+
# Class implementing a mathematical function that can then be used in many contexts
|
51
|
+
class Function
|
52
|
+
|
53
|
+
include WSK::Common
|
54
|
+
|
55
|
+
# Constructor
|
56
|
+
def initialize
|
57
|
+
# The underlying Ruby function
|
58
|
+
@Function = nil
|
59
|
+
# The C libraries
|
60
|
+
@FunctionUtils = nil
|
61
|
+
@VolumeUtils = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Read from a file
|
65
|
+
#
|
66
|
+
# Parameters::
|
67
|
+
# * *iFileName* (_String_): File name
|
68
|
+
def read_from_file(iFileName)
|
69
|
+
lStrFunction = nil
|
70
|
+
if (File.exists?(iFileName))
|
71
|
+
File.open(iFileName, 'r') do |iFile|
|
72
|
+
lStrFunction = iFile.read
|
73
|
+
end
|
74
|
+
else
|
75
|
+
raise RuntimeError.new("Missing file #{iFileName} to load function.")
|
76
|
+
end
|
77
|
+
begin
|
78
|
+
@Function = eval(lStrFunction)
|
79
|
+
rescue Exception
|
80
|
+
raise RuntimeError.new("Invalid function specified in file #{iFileName}: #{$!}")
|
81
|
+
end
|
82
|
+
convertDataTypes
|
83
|
+
optimize
|
84
|
+
end
|
85
|
+
|
86
|
+
# Read a function from the volume of an input data
|
87
|
+
#
|
88
|
+
# Parameters::
|
89
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
90
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample beginning the volume reading
|
91
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample ending the volume reading
|
92
|
+
# * *iInterval* (_Integer_): The number of samples used as an interval in measuring the volume
|
93
|
+
# * *iRMSRatio* (_Float_): The ratio of RMS measure vs Peak measure (0.0 = only peak, 1.0 = only RMS)
|
94
|
+
def read_from_input_volume(iInputData, iIdxBeginSample, iIdxEndSample, iInterval, iRMSRatio)
|
95
|
+
@Function = {
|
96
|
+
:FunctionType => FCTTYPE_PIECEWISE_LINEAR,
|
97
|
+
:Points => []
|
98
|
+
}
|
99
|
+
# Profile
|
100
|
+
prepareVolumeUtils
|
101
|
+
lIdxCurrentSample = iIdxBeginSample
|
102
|
+
while (lIdxCurrentSample <= iIdxEndSample)
|
103
|
+
lIdxCurrentEndSample = lIdxCurrentSample + iInterval - 1
|
104
|
+
if (lIdxCurrentEndSample > iIdxEndSample)
|
105
|
+
lIdxCurrentEndSample = iIdxEndSample
|
106
|
+
end
|
107
|
+
lRawBuffer = ''
|
108
|
+
iInputData.each_raw_buffer(lIdxCurrentSample, lIdxCurrentEndSample, :nbr_samples_prefetch => iIdxEndSample-lIdxCurrentSample) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
|
109
|
+
lRawBuffer += iInputRawBuffer
|
110
|
+
end
|
111
|
+
# Profile this buffer
|
112
|
+
lChannelLevelValues = @VolumeUtils.measureLevel(lRawBuffer, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels, lIdxCurrentEndSample - lIdxCurrentSample + 1, iRMSRatio)
|
113
|
+
# Combine the channel levels based on the RMS ratio also
|
114
|
+
lMaxValue = Rational(0, 1)
|
115
|
+
lRMSValue = Rational(0, 1)
|
116
|
+
lChannelLevelValues.each do |iLevelValue|
|
117
|
+
if (iLevelValue > lMaxValue)
|
118
|
+
lMaxValue = iLevelValue
|
119
|
+
end
|
120
|
+
lRMSValue += iLevelValue*iLevelValue
|
121
|
+
end
|
122
|
+
lRMSValue = Math.sqrt(lRMSValue/lChannelLevelValues.size).to_r
|
123
|
+
lLevelValue = lRMSValue*(iRMSRatio.to_r) + lMaxValue*(Rational(1)-iRMSRatio.to_r)
|
124
|
+
#log_debug "[#{lIdxCurrentSample} - #{lIdxCurrentEndSample}] - Level: #{lLevelValue}"
|
125
|
+
# If intervals are of length 1, the function is exactly the profile: no need to make intermediate points
|
126
|
+
if (lIdxCurrentEndSample == lIdxCurrentSample)
|
127
|
+
@Function[:Points] << [ Rational(lIdxCurrentSample), lLevelValue]
|
128
|
+
lIdxCurrentSample += 1
|
129
|
+
else
|
130
|
+
# Complete the function
|
131
|
+
if (@Function[:Points].empty?)
|
132
|
+
# First points: add also the point 0
|
133
|
+
@Function[:Points] = [ [ Rational(0, 1), lLevelValue] ]
|
134
|
+
end
|
135
|
+
# Add a point to the function in the middle of this interval
|
136
|
+
lPointX = lIdxCurrentSample - iIdxBeginSample + Rational(lIdxCurrentEndSample - lIdxCurrentSample + 1, 2)
|
137
|
+
@Function[:Points] << [lPointX, lLevelValue]
|
138
|
+
# Increment the cursor
|
139
|
+
lIdxCurrentSample = lIdxCurrentEndSample + 1
|
140
|
+
if (lIdxCurrentSample == iIdxEndSample + 1)
|
141
|
+
# The last point: add the ending one
|
142
|
+
@Function[:Points] << [Rational(iIdxEndSample - iIdxBeginSample, 1), lLevelValue]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
$stdout.write("#{(lIdxCurrentSample*100)/(iIdxEndSample - iIdxBeginSample + 1)} %\015")
|
146
|
+
$stdout.flush
|
147
|
+
end
|
148
|
+
optimize
|
149
|
+
end
|
150
|
+
|
151
|
+
# Set directly a function from a hash
|
152
|
+
#
|
153
|
+
# Parameters::
|
154
|
+
# * *iHashFunction* (<em>map<Symbol,Object></em>): The hashed function
|
155
|
+
def set(iHashFunction)
|
156
|
+
@Function = iHashFunction
|
157
|
+
convertDataTypes
|
158
|
+
optimize
|
159
|
+
end
|
160
|
+
|
161
|
+
# Apply the function on the volume of a raw buffer
|
162
|
+
#
|
163
|
+
# Parameters::
|
164
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
165
|
+
# * *oOutputData* (<em>WSK::Model::DirectStream</em>): The output data
|
166
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample beginning the volume transformation
|
167
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample ending the volume transformation
|
168
|
+
# * *iUnitDB* (_Boolean_): Are function values to be interpreted as DB units ?
|
169
|
+
def apply_on_volume(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB)
|
170
|
+
prepareFunctionUtils
|
171
|
+
lCFunction = @FunctionUtils.createCFunction(@Function, iIdxBeginSample, iIdxEndSample)
|
172
|
+
lIdxBufferSample = iIdxBeginSample
|
173
|
+
iInputData.each_raw_buffer(iIdxBeginSample, iIdxEndSample) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
|
174
|
+
prepareVolumeUtils
|
175
|
+
oOutputData.pushRawBuffer(@VolumeUtils.applyVolumeFct(lCFunction, iInputRawBuffer, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels, iNbrSamples, lIdxBufferSample, iUnitDB))
|
176
|
+
lIdxBufferSample += iNbrSamples
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Draw the function into a raw buffer
|
181
|
+
#
|
182
|
+
# Parameters::
|
183
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
184
|
+
# * *oOutputData* (<em>WSK::Model::DirectStream</em>): The output data
|
185
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample beginning the volume transformation
|
186
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample ending the volume transformation
|
187
|
+
# * *iUnitDB* (_Boolean_): Are function values to be interpreted as DB units ?
|
188
|
+
# * *iMedianValue* (_Integer_): Median value to draw function ratio of 1.
|
189
|
+
def draw(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB, iMedianValue)
|
190
|
+
prepareFunctionUtils
|
191
|
+
lCFunction = @FunctionUtils.createCFunction(@Function, iIdxBeginSample, iIdxEndSample)
|
192
|
+
oOutputData.each_buffer(iIdxBeginSample, iIdxEndSample) do |iIdxBeginBufferSample, iIdxEndBufferSample|
|
193
|
+
prepareVolumeUtils
|
194
|
+
oOutputData.pushRawBuffer(@VolumeUtils.drawVolumeFct(lCFunction, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels, iIdxEndBufferSample-iIdxBeginBufferSample+1, iIdxBeginBufferSample, iUnitDB, iMedianValue))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Divide values by a given factor
|
199
|
+
#
|
200
|
+
# Parameters::
|
201
|
+
# * *iFactor* (_Rational_): Factor to divide by
|
202
|
+
def divide_by(iFactor)
|
203
|
+
case @Function[:FunctionType]
|
204
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
205
|
+
@Function[:Points].each do |ioPoint|
|
206
|
+
ioPoint[1] /= iFactor
|
207
|
+
end
|
208
|
+
else
|
209
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Convert the Y units in DB equivalent
|
214
|
+
#
|
215
|
+
# Parameters::
|
216
|
+
# * *iMaxYValue* (_Rational_): Maximal Y value
|
217
|
+
def convert_to_db(iMaxYValue)
|
218
|
+
case @Function[:FunctionType]
|
219
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
220
|
+
# Prepare variables for log computations
|
221
|
+
@Log2 = Math::log(2).to_r
|
222
|
+
@LogMax = value_log(iMaxYValue)
|
223
|
+
@Function[:Points].each do |ioPoint|
|
224
|
+
ioPoint[1] = value_val_2_db_Internal(ioPoint[1])
|
225
|
+
end
|
226
|
+
else
|
227
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Round values to a given precision
|
232
|
+
#
|
233
|
+
# Parameters::
|
234
|
+
# * *iPrecisionX* (_Rational_): The desired precision for X values (1000 will round to E-3)
|
235
|
+
# * *iPrecisionY* (_Rational_): The desired precision for Y values (1000 will round to E-3)
|
236
|
+
def round_to_precision(iPrecisionX, iPrecisionY)
|
237
|
+
case @Function[:FunctionType]
|
238
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
239
|
+
@Function[:Points] = @Function[:Points].map do |iPoint|
|
240
|
+
next [ (Rational((iPoint[0]*iPrecisionX).round, 1))/iPrecisionX, (Rational((iPoint[1]*iPrecisionY).round, 1))/iPrecisionY ]
|
241
|
+
end
|
242
|
+
else
|
243
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
244
|
+
end
|
245
|
+
optimize
|
246
|
+
end
|
247
|
+
|
248
|
+
# Apply damping.
|
249
|
+
#
|
250
|
+
# Parameters::
|
251
|
+
# * *iSlopeUp* (_Rational_): The maximal value of slope when increasing (should be > 0), or nil if none
|
252
|
+
# * *iSlopeDown* (_Rational_): The minimal value of slope when decreasing (should be < 0), or nil if none
|
253
|
+
def apply_damping(iSlopeUp, iSlopeDown)
|
254
|
+
if ((iSlopeUp != nil) and
|
255
|
+
(iSlopeUp <= 0))
|
256
|
+
log_err "Upward slope (#{iSlopeUp}) has to be > 0"
|
257
|
+
elsif ((iSlopeDown != nil) and
|
258
|
+
(iSlopeDown >= 0))
|
259
|
+
log_err "Downward slope (#{iSlopeDown}) has to be < 0"
|
260
|
+
else
|
261
|
+
case @Function[:FunctionType]
|
262
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
263
|
+
# Keep the first point
|
264
|
+
lNewPoints = [ @Function[:Points][0] ]
|
265
|
+
lIdxSegment = 0
|
266
|
+
while (lIdxSegment < @Function[:Points].size - 1)
|
267
|
+
# Compute the slope of this segment
|
268
|
+
#puts "lIdxSegment=#{lIdxSegment}/#{@Function[:Points].size} Points=[ [ #{sprintf('%.2f',@Function[:Points][lIdxSegment][0])}, #{sprintf('%.2f',@Function[:Points][lIdxSegment][1])} ], [ #{sprintf('%.2f',@Function[:Points][lIdxSegment+1][0])}, #{sprintf('%.2f',@Function[:Points][lIdxSegment+1][1])} ] ]"
|
269
|
+
lSegmentSlope = (@Function[:Points][lIdxSegment+1][1]-@Function[:Points][lIdxSegment][1])/(@Function[:Points][lIdxSegment+1][0]-@Function[:Points][lIdxSegment][0])
|
270
|
+
#puts "lIdxSegment=#{lIdxSegment}/#{@Function[:Points].size} Slope=#{sprintf('%.2f',lSegmentSlope)} (#{lSegmentSlope.precs.inspect})"
|
271
|
+
if (((lSegmentSlope > 0) and
|
272
|
+
(iSlopeUp != nil) and
|
273
|
+
(lSegmentSlope > iSlopeUp)) or
|
274
|
+
((lSegmentSlope < 0) and
|
275
|
+
(iSlopeDown != nil) and
|
276
|
+
(lSegmentSlope < iSlopeDown)))
|
277
|
+
# Choose the correct damping slope depending on the direction
|
278
|
+
lSlope = nil
|
279
|
+
if (lSegmentSlope > 0)
|
280
|
+
lSlope = iSlopeUp
|
281
|
+
else
|
282
|
+
lSlope = iSlopeDown
|
283
|
+
end
|
284
|
+
# We have to apply damping starting the beginning of this segment.
|
285
|
+
# Find the next intersection between the damped segment and our function.
|
286
|
+
# The abscisse of the intersection
|
287
|
+
lIntersectX = nil
|
288
|
+
# A constant for the next loop
|
289
|
+
lDampedSegmentOffsetY = @Function[:Points][lIdxSegment][1] - @Function[:Points][lIdxSegment][0]*lSlope
|
290
|
+
lIdxSegmentIntersect = lIdxSegment + 1
|
291
|
+
while (lIdxSegmentIntersect < @Function[:Points].size - 1)
|
292
|
+
# Find if there is an intersection
|
293
|
+
lSegmentIntersectDistX = @Function[:Points][lIdxSegmentIntersect+1][0] - @Function[:Points][lIdxSegmentIntersect][0]
|
294
|
+
lSegmentIntersectDistY = @Function[:Points][lIdxSegmentIntersect+1][1] - @Function[:Points][lIdxSegmentIntersect][1]
|
295
|
+
lIntersectX = ((lDampedSegmentOffsetY - @Function[:Points][lIdxSegmentIntersect][1])*lSegmentIntersectDistX + @Function[:Points][lIdxSegmentIntersect][0]*lSegmentIntersectDistY)/(lSegmentIntersectDistY - lSlope*lSegmentIntersectDistX)
|
296
|
+
# Is lIntersectX among our range ?
|
297
|
+
if ((lIntersectX >= @Function[:Points][lIdxSegmentIntersect][0]) and
|
298
|
+
(lIntersectX <= @Function[:Points][lIdxSegmentIntersect+1][0]))
|
299
|
+
# We have an intersection in the segment beginning at point n. lIdxSegmentIntersect, exactly at abscisse lIntersectX.
|
300
|
+
break
|
301
|
+
else
|
302
|
+
# Erase it as we will test for it after the loop
|
303
|
+
lIntersectX = nil
|
304
|
+
end
|
305
|
+
lIdxSegmentIntersect += 1
|
306
|
+
end
|
307
|
+
# Here, lIdxSegmentIntersect can point to the last point if no intersection was found
|
308
|
+
if (lIntersectX == nil)
|
309
|
+
# We could not find any intersection
|
310
|
+
# We consider adding a point following the damped slope till the end of the function
|
311
|
+
lIntersectX = @Function[:Points][-1][0]
|
312
|
+
end
|
313
|
+
# Add the intersecting point (could be the last one)
|
314
|
+
lIntersectPoint = [ lIntersectX, (lIntersectX - @Function[:Points][lIdxSegment][0])*lSlope + @Function[:Points][lIdxSegment][1] ]
|
315
|
+
#puts "lIntersectX=#{lIntersectX.to_f} @Function[:Points][lIdxSegment][0]=#{@Function[:Points][lIdxSegment][0].to_f} lSlope=#{lSlope.to_f} @Function[:Points][lIdxSegment][1]=#{@Function[:Points][lIdxSegment][1].to_f} lIntersectPoint[1]=#{lIntersectPoint[1].to_f}"
|
316
|
+
lNewPoints << lIntersectPoint
|
317
|
+
# Continue after this intersection (we create also the intersecting point on our old points by modifying them)
|
318
|
+
@Function[:Points][lIdxSegmentIntersect] = lIntersectPoint
|
319
|
+
lIdxSegment = lIdxSegmentIntersect
|
320
|
+
else
|
321
|
+
# The slope is ok, keep this segment as it is
|
322
|
+
lNewPoints << @Function[:Points][lIdxSegment+1]
|
323
|
+
lIdxSegment += 1
|
324
|
+
end
|
325
|
+
end
|
326
|
+
# Replace our points with new ones
|
327
|
+
@Function[:Points] = lNewPoints
|
328
|
+
else
|
329
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
optimize
|
333
|
+
end
|
334
|
+
|
335
|
+
# Invert the abscisses of a function
|
336
|
+
def invert_abscisses
|
337
|
+
case @Function[:FunctionType]
|
338
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
339
|
+
lNewPoints = []
|
340
|
+
lMinMaxX = @Function[:Points][0][0] + @Function[:Points][-1][0]
|
341
|
+
@Function[:Points].reverse_each do |iPoint|
|
342
|
+
lNewPoints << [lMinMaxX - iPoint[0], iPoint[1]]
|
343
|
+
end
|
344
|
+
@Function[:Points] = lNewPoints
|
345
|
+
else
|
346
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# Get the function bounds
|
351
|
+
#
|
352
|
+
# Return::
|
353
|
+
# * _Rational_: Minimal X
|
354
|
+
# * _Rational_: Minimal Y
|
355
|
+
# * _Rational_: Maximal X
|
356
|
+
# * _Rational_: Maximal Y
|
357
|
+
def get_bounds
|
358
|
+
rMinX = nil
|
359
|
+
rMinY = nil
|
360
|
+
rMaxX = nil
|
361
|
+
rMaxY = nil
|
362
|
+
|
363
|
+
case @Function[:FunctionType]
|
364
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
365
|
+
rMinX = @Function[:Points][0][0]
|
366
|
+
rMaxX = @Function[:Points][-1][0]
|
367
|
+
@Function[:Points].each do |iPoint|
|
368
|
+
if (rMinY == nil)
|
369
|
+
rMinY = iPoint[1]
|
370
|
+
rMaxY = iPoint[1]
|
371
|
+
else
|
372
|
+
if (rMinY > iPoint[1])
|
373
|
+
rMinY = iPoint[1]
|
374
|
+
end
|
375
|
+
if (rMaxY < iPoint[1])
|
376
|
+
rMaxY = iPoint[1]
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
else
|
381
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
382
|
+
end
|
383
|
+
|
384
|
+
return rMinX, rMinY, rMaxX, rMaxY
|
385
|
+
end
|
386
|
+
|
387
|
+
# Write the function to a file
|
388
|
+
#
|
389
|
+
# Parameters::
|
390
|
+
# * *iFileName* (_String_): File name to write
|
391
|
+
# * *iParams* (<em>map<Symbol,Object></em>): Additional parameters [optional = {}]:
|
392
|
+
# * *:floats* (_Boolean_): Do we write Float values ? [optional = false]
|
393
|
+
def write_to_file(iFileName, iParams = {})
|
394
|
+
lParams = {
|
395
|
+
# Default value
|
396
|
+
:floats => false
|
397
|
+
}.merge(iParams)
|
398
|
+
case @Function[:FunctionType]
|
399
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
400
|
+
require 'pp'
|
401
|
+
lData = @Function
|
402
|
+
if (lParams[:floats])
|
403
|
+
# Convert to Floats
|
404
|
+
lData[:Points].map! do |iPoint|
|
405
|
+
next [ iPoint[0].to_f, iPoint[1].to_f ]
|
406
|
+
end
|
407
|
+
end
|
408
|
+
File.open(iFileName, 'w') do |oFile|
|
409
|
+
oFile.write(lData.pretty_inspect)
|
410
|
+
end
|
411
|
+
else
|
412
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Apply a mapping function to this function.
|
417
|
+
#
|
418
|
+
# Parameters::
|
419
|
+
# * *iMapFunction* (_Function_): The mapping function
|
420
|
+
def apply_map_function(iMapFunction)
|
421
|
+
case @Function[:FunctionType]
|
422
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
423
|
+
case iMapFunction.function_data[:FunctionType]
|
424
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
425
|
+
# Both functions are piecewise linear
|
426
|
+
# Algorithm:
|
427
|
+
# * For each segment of our function:
|
428
|
+
# * We look at the segments from the map function.
|
429
|
+
# * For each found segment:
|
430
|
+
# *** We find at which abscisses this segment will change values
|
431
|
+
# *** We change the sub-segment between those abscisses
|
432
|
+
lPoints = @Function[:Points]
|
433
|
+
lMapPoints = iMapFunction.function_data[:Points]
|
434
|
+
lNewPoints = []
|
435
|
+
lIdxSegment = 0
|
436
|
+
while (lIdxSegment < lPoints.size-1)
|
437
|
+
lBeginX = lPoints[lIdxSegment][0]
|
438
|
+
lBeginY = lPoints[lIdxSegment][1]
|
439
|
+
lEndX = lPoints[lIdxSegment+1][0]
|
440
|
+
lEndY = lPoints[lIdxSegment+1][1]
|
441
|
+
# The direction in which we are going to look for the map segments
|
442
|
+
lIncMapSegment = nil
|
443
|
+
if (lEndY >= lBeginY)
|
444
|
+
lIncMapSegment = true
|
445
|
+
else
|
446
|
+
lIncMapSegment = false
|
447
|
+
end
|
448
|
+
# Find the map function's segment containing the beginning of our segment
|
449
|
+
lIdxMapSegment = 0
|
450
|
+
if (lBeginY == lMapPoints[-1][0])
|
451
|
+
lIdxMapSegment = lMapPoints.size - 2
|
452
|
+
else
|
453
|
+
while (lBeginY >= lMapPoints[lIdxMapSegment+1][0])
|
454
|
+
lIdxMapSegment += 1
|
455
|
+
end
|
456
|
+
end
|
457
|
+
# Compute the new value of our segment beginning
|
458
|
+
lNewBeginY = lMapPoints[lIdxMapSegment][1] + ((lMapPoints[lIdxMapSegment+1][1]-lMapPoints[lIdxMapSegment][1])*(lBeginY-lMapPoints[lIdxMapSegment][0]))/(lMapPoints[lIdxMapSegment+1][0]-lMapPoints[lIdxMapSegment][0])
|
459
|
+
lNewPoints << [ lBeginX, lNewBeginY ]
|
460
|
+
# Get the next map segments unless we reach our segment's end
|
461
|
+
# !!! Find the next map segment according to the direction
|
462
|
+
if (lIncMapSegment)
|
463
|
+
while (lEndY > lMapPoints[lIdxMapSegment+1][0])
|
464
|
+
# We have a new map segment to consider in our segment
|
465
|
+
# Find the absciss at which our Y coordinates get the value lMapPoints[lIdxMapSegment+1][0]
|
466
|
+
lNewSegmentX = lBeginX + ((lEndX-lBeginX)*(lMapPoints[lIdxMapSegment+1][0] - lBeginY))/(lEndY-lBeginY)
|
467
|
+
lNewPoints << [ lNewSegmentX, lMapPoints[lIdxMapSegment+1][1] ]
|
468
|
+
lIdxMapSegment += 1
|
469
|
+
end
|
470
|
+
# Our segment ends before next map segment
|
471
|
+
else
|
472
|
+
while (lEndY < lMapPoints[lIdxMapSegment][0])
|
473
|
+
# We have a new map segment to consider in our segment
|
474
|
+
# Find the absciss at which our Y coordinates get the value lMapPoints[lIdxMapSegment][0]
|
475
|
+
lNewSegmentX = lBeginX + ((lEndX-lBeginX)*(lMapPoints[lIdxMapSegment][0] - lBeginY))/(lEndY-lBeginY)
|
476
|
+
lNewPoints << [ lNewSegmentX, lMapPoints[lIdxMapSegment][1] ]
|
477
|
+
lIdxMapSegment -= 1
|
478
|
+
end
|
479
|
+
# Our segment ends before previous map segment
|
480
|
+
end
|
481
|
+
# Write the segment end if it is the last one (otherwise it will be written by the next iteration)
|
482
|
+
if (lIdxSegment == lPoints.size-2)
|
483
|
+
lNewEndY = lMapPoints[lIdxMapSegment][1] + ((lMapPoints[lIdxMapSegment+1][1]-lMapPoints[lIdxMapSegment][1])*(lEndY-lMapPoints[lIdxMapSegment][0]))/(lMapPoints[lIdxMapSegment+1][0]-lMapPoints[lIdxMapSegment][0])
|
484
|
+
lNewPoints << [ lEndX, lNewEndY ]
|
485
|
+
end
|
486
|
+
lIdxSegment += 1
|
487
|
+
end
|
488
|
+
# Replace with new points
|
489
|
+
@Function[:Points] = lNewPoints
|
490
|
+
else
|
491
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
492
|
+
end
|
493
|
+
else
|
494
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
495
|
+
end
|
496
|
+
optimize
|
497
|
+
end
|
498
|
+
|
499
|
+
# Remove intermediate abscisses that are too close to each other
|
500
|
+
#
|
501
|
+
# Parameters::
|
502
|
+
# * *iMinDistance* (_Rational_): Minimal distance for abscisses triplets to have
|
503
|
+
def remove_noise_abscisses(iMinDistance)
|
504
|
+
case @Function[:FunctionType]
|
505
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
506
|
+
lNewPoints = [ @Function[:Points][0] ]
|
507
|
+
lIdxPoint = 0
|
508
|
+
while (lIdxPoint < @Function[:Points].size - 1)
|
509
|
+
# Now we skip the next last point among iMinDistance range
|
510
|
+
lPointX = @Function[:Points][lIdxPoint][0]
|
511
|
+
lIdxOtherPoint = lIdxPoint + 1
|
512
|
+
while ((lIdxOtherPoint < @Function[:Points].size) and
|
513
|
+
(@Function[:Points][lIdxOtherPoint][0] - lPointX < iMinDistance))
|
514
|
+
lIdxOtherPoint += 1
|
515
|
+
end
|
516
|
+
# Either lIdxOtherPoint is beyond the end, or it points to the first point that is beyond iMinDistance
|
517
|
+
# We add the previous point if it is not already ours
|
518
|
+
if (lIdxOtherPoint-1 > lIdxPoint)
|
519
|
+
lNewPoints << @Function[:Points][lIdxOtherPoint-1]
|
520
|
+
# And we continue searching from this new added point
|
521
|
+
lIdxPoint = lIdxOtherPoint-1
|
522
|
+
else
|
523
|
+
# It is our point, continue on to the next one
|
524
|
+
lNewPoints << @Function[:Points][lIdxOtherPoint]
|
525
|
+
lIdxPoint = lIdxOtherPoint
|
526
|
+
end
|
527
|
+
end
|
528
|
+
@Function[:Points] = lNewPoints
|
529
|
+
else
|
530
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
531
|
+
end
|
532
|
+
optimize
|
533
|
+
end
|
534
|
+
|
535
|
+
# Substract a function to this function
|
536
|
+
#
|
537
|
+
# Parameters::
|
538
|
+
# * *iSubFunction* (_Function_): The function to substract
|
539
|
+
def substract_function(iSubFunction)
|
540
|
+
case @Function[:FunctionType]
|
541
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
542
|
+
case iSubFunction.function_data[:FunctionType]
|
543
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
544
|
+
lNewPoints = []
|
545
|
+
unionXWithFunction_PiecewiseLinear(iSubFunction) do |iX, iY, iOtherY|
|
546
|
+
if (iY == nil)
|
547
|
+
lNewPoints << [ iX, -iOtherY ]
|
548
|
+
elsif (iOtherY == nil)
|
549
|
+
lNewPoints << [ iX, iY ]
|
550
|
+
else
|
551
|
+
lNewPoints << [ iX, iY - iOtherY ]
|
552
|
+
end
|
553
|
+
end
|
554
|
+
# Replace with new points
|
555
|
+
@Function[:Points] = lNewPoints
|
556
|
+
else
|
557
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
558
|
+
end
|
559
|
+
else
|
560
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
561
|
+
end
|
562
|
+
optimize
|
563
|
+
end
|
564
|
+
|
565
|
+
# Divide this function by another function
|
566
|
+
#
|
567
|
+
# Parameters::
|
568
|
+
# * *iDivFunction* (_Function_): The function that divides
|
569
|
+
def divide_by_function(iDivFunction)
|
570
|
+
case @Function[:FunctionType]
|
571
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
572
|
+
case iDivFunction.function_data[:FunctionType]
|
573
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
574
|
+
lNewPoints = []
|
575
|
+
unionXWithFunction_PiecewiseLinear(iDivFunction) do |iX, iY, iOtherY|
|
576
|
+
if (iY == nil)
|
577
|
+
lNewPoints << [ iX, 0 ]
|
578
|
+
elsif (iOtherY == nil)
|
579
|
+
lNewPoints << [ iX, 0 ]
|
580
|
+
else
|
581
|
+
lNewPoints << [ iX, iY / iOtherY ]
|
582
|
+
end
|
583
|
+
end
|
584
|
+
# Replace with new points
|
585
|
+
@Function[:Points] = lNewPoints
|
586
|
+
else
|
587
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
588
|
+
end
|
589
|
+
else
|
590
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
591
|
+
end
|
592
|
+
optimize
|
593
|
+
end
|
594
|
+
|
595
|
+
# Get the internal function data
|
596
|
+
#
|
597
|
+
# Return::
|
598
|
+
# * <em>map<Symbol,Object></em>: The internal function data
|
599
|
+
def function_data
|
600
|
+
return @Function
|
601
|
+
end
|
602
|
+
|
603
|
+
# Compute the log of a function value.
|
604
|
+
#
|
605
|
+
# Parameters::
|
606
|
+
# * *iValue* (_Rational_): The value
|
607
|
+
def value_log(iValue)
|
608
|
+
return Math::log(iValue).to_r
|
609
|
+
end
|
610
|
+
|
611
|
+
# Compute a DB value out of a ratio using function values
|
612
|
+
#
|
613
|
+
# Parameters::
|
614
|
+
# * *iValue* (_Rational_): The value
|
615
|
+
# * *iMaxValue* (_Rational_): The maximal value
|
616
|
+
# Return::
|
617
|
+
# * _Rational_: Its corresponding db
|
618
|
+
def value_val_2_db(iValue, iMaxValue)
|
619
|
+
@Log2 = Math::log(2).to_r
|
620
|
+
@LogMax = value_log(iMaxValue)
|
621
|
+
|
622
|
+
return value_val_2_db_Internal(iValue)
|
623
|
+
end
|
624
|
+
|
625
|
+
private
|
626
|
+
|
627
|
+
# Optimize the function internal representation without modifying it.
|
628
|
+
def optimize
|
629
|
+
case @Function[:FunctionType]
|
630
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
631
|
+
# Join segments that have the same slope
|
632
|
+
lNewPoints = [ @Function[:Points][0] ]
|
633
|
+
lLastSlope = (@Function[:Points][1][1]-@Function[:Points][0][1])/(@Function[:Points][1][0]-@Function[:Points][0][0])
|
634
|
+
lIdxSegment = 1
|
635
|
+
while (lIdxSegment < @Function[:Points].size - 1)
|
636
|
+
# Compute this segment's slope
|
637
|
+
lSlope = (@Function[:Points][lIdxSegment+1][1]-@Function[:Points][lIdxSegment][1])/(@Function[:Points][lIdxSegment+1][0]-@Function[:Points][lIdxSegment][0])
|
638
|
+
if (lLastSlope != lSlope)
|
639
|
+
# We are changing slopes
|
640
|
+
lNewPoints << @Function[:Points][lIdxSegment]
|
641
|
+
lLastSlope = lSlope
|
642
|
+
end
|
643
|
+
lIdxSegment += 1
|
644
|
+
end
|
645
|
+
# Add last point
|
646
|
+
lNewPoints << @Function[:Points][-1]
|
647
|
+
# Change points
|
648
|
+
@Function[:Points] = lNewPoints
|
649
|
+
else
|
650
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
# Convert contained objects into the types used for function values
|
655
|
+
def convertDataTypes
|
656
|
+
case @Function[:FunctionType]
|
657
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
658
|
+
@Function[:Points] = @Function[:Points].map do |iPoint|
|
659
|
+
lNewPointX = nil
|
660
|
+
lNewPointY = nil
|
661
|
+
if (iPoint[0].is_a?(Rational))
|
662
|
+
lNewPointX = iPoint[0]
|
663
|
+
elsif (iPoint[0].is_a?(Float))
|
664
|
+
lNewPointX = iPoint[0].to_r
|
665
|
+
else
|
666
|
+
lNewPointX = Rational(iPoint[0])
|
667
|
+
end
|
668
|
+
if (iPoint[1].is_a?(Rational))
|
669
|
+
lNewPointY = iPoint[1]
|
670
|
+
elsif (iPoint[1].is_a?(Float))
|
671
|
+
lNewPointY = iPoint[1].to_r
|
672
|
+
else
|
673
|
+
lNewPointY = Rational(iPoint[1])
|
674
|
+
end
|
675
|
+
next [ lNewPointX, lNewPointY ]
|
676
|
+
end
|
677
|
+
else
|
678
|
+
log_err "Unknown function type: #{@Function[:FunctionType]}"
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
# Prepare the Function utils C library.
|
683
|
+
# This can be called several times.
|
684
|
+
def prepareFunctionUtils
|
685
|
+
if (@FunctionUtils == nil)
|
686
|
+
require 'WSK/FunctionUtils/FunctionUtils'
|
687
|
+
@FunctionUtils = WSK::FunctionUtils::FunctionUtils.new
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
# Prepare the Volume utils C library.
|
692
|
+
# This can be called several times.
|
693
|
+
def prepareVolumeUtils
|
694
|
+
if (@VolumeUtils == nil)
|
695
|
+
require 'WSK/VolumeUtils/VolumeUtils'
|
696
|
+
@VolumeUtils = WSK::VolumeUtils::VolumeUtils.new
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
# Find abscisses of both functions and the corresponding Y values
|
701
|
+
#
|
702
|
+
# Parameters::
|
703
|
+
# * *iOtherFunction* (_Function_): The other function
|
704
|
+
# * *CodeBlock*: Code called for each point found:
|
705
|
+
# * *iX* (_Rational_): The corresponding abscisse (can be nil if none)
|
706
|
+
# * *iY* (_Rational_): The corresponding Y value for this function (can be nil if none)
|
707
|
+
# * *iOtherY* (_Rational_): The corresponding Y value for the other function
|
708
|
+
def unionXWithFunction_PiecewiseLinear(iOtherFunction)
|
709
|
+
lPoints = @Function[:Points]
|
710
|
+
lOtherPoints = iOtherFunction.function_data[:Points]
|
711
|
+
# Get all the abscisses sorted
|
712
|
+
lXList = (lPoints.map { |iPoint| next iPoint[0] } + lOtherPoints.map { |iPoint| next iPoint[0] }).sort.uniq
|
713
|
+
# Read segments abscisse by abscisse
|
714
|
+
lIdxSegment = 0
|
715
|
+
lIdxOtherSegment = 0
|
716
|
+
lXList.each do |iX|
|
717
|
+
if (lPoints[lIdxSegment] == nil)
|
718
|
+
# No abscisse on lPoints for this iX
|
719
|
+
# Forcefully we have lOtherPoints[lIdxOtherSegment][0] == iX
|
720
|
+
yield(iX, nil, lOtherPoints[lIdxOtherSegment][1])
|
721
|
+
lIdxOtherSegment += 1
|
722
|
+
elsif (lOtherPoints[lIdxOtherSegment] == nil)
|
723
|
+
# No abscisse on lOtherPoints for this iX
|
724
|
+
# Forcefully we have lPoints[lIdxSegment][0] == iX
|
725
|
+
yield(iX, lPoints[lIdxSegment][1], nil)
|
726
|
+
lIdxSegment += 1
|
727
|
+
elsif (lPoints[lIdxSegment][0] == iX)
|
728
|
+
# lPoints has this abscisse
|
729
|
+
if (lOtherPoints[lIdxOtherSegment][0] == iX)
|
730
|
+
# If both functions have a point here, it's easy.
|
731
|
+
yield(iX, lPoints[lIdxSegment][1], lOtherPoints[lIdxOtherSegment][1])
|
732
|
+
lIdxOtherSegment += 1
|
733
|
+
else
|
734
|
+
# Compute the Y value for the other function
|
735
|
+
yield(iX, lPoints[lIdxSegment][1], lOtherPoints[lIdxOtherSegment-1][1] + ((lOtherPoints[lIdxOtherSegment][1] - lOtherPoints[lIdxOtherSegment-1][1])*(iX - lOtherPoints[lIdxOtherSegment-1][0]))/(lOtherPoints[lIdxOtherSegment][0] - lOtherPoints[lIdxOtherSegment-1][0]))
|
736
|
+
end
|
737
|
+
lIdxSegment += 1
|
738
|
+
else
|
739
|
+
# We have forcefully lOtherPoints[lIdxOtherSegment][0] == iX
|
740
|
+
# Compute the Y value for this function
|
741
|
+
yield(iX, lPoints[lIdxSegment-1][1] + ((lPoints[lIdxSegment][1] - lPoints[lIdxSegment-1][1])*(iX - lPoints[lIdxSegment-1][0]))/(lPoints[lIdxSegment][0] - lPoints[lIdxSegment-1][0]), lOtherPoints[lIdxOtherSegment][1])
|
742
|
+
lIdxOtherSegment += 1
|
743
|
+
end
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
# Convert a value to its db notation.
|
748
|
+
# Operate on the function values numbers.
|
749
|
+
# !!! Prerequisites: The following variables have to be set before calling this function
|
750
|
+
# * @Log2: Contains log(2)
|
751
|
+
# * @LogMax: Contains log(MaximalValue)
|
752
|
+
#
|
753
|
+
# Parameters::
|
754
|
+
# * *iValue* (_Rational_): The value
|
755
|
+
# Return::
|
756
|
+
# * _Rational_: Its corresponding db
|
757
|
+
def value_val_2_db_Internal(iValue)
|
758
|
+
if (iValue == 0)
|
759
|
+
# -Infinity
|
760
|
+
return -1.0/0.0
|
761
|
+
else
|
762
|
+
return -6*(@LogMax-value_log(iValue.abs))/@Log2
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
end
|
767
|
+
|
768
|
+
end
|
769
|
+
|
770
|
+
end
|