WaveSwissKnife 0.0.1.20101110-x86-cygwin
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +1 -0
- data/ChangeLog +5 -0
- data/Credits +3 -0
- data/LICENSE +31 -0
- data/README +18 -0
- data/ReleaseInfo +8 -0
- data/TODO +2 -0
- data/bin/WSK.rb +14 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.o +0 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.so +0 -0
- data/ext/WSK/AnalyzeUtils/Makefile +149 -0
- data/ext/WSK/AnalyzeUtils/build.rb +18 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.o +0 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.so +0 -0
- data/ext/WSK/ArithmUtils/Makefile +149 -0
- data/ext/WSK/ArithmUtils/build.rb +20 -0
- data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
- data/ext/WSK/FFTUtils/FFTUtils.o +0 -0
- data/ext/WSK/FFTUtils/FFTUtils.so +0 -0
- data/ext/WSK/FFTUtils/Makefile +149 -0
- data/ext/WSK/FFTUtils/build.rb +20 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.o +0 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.so +0 -0
- data/ext/WSK/FunctionUtils/Makefile +149 -0
- data/ext/WSK/FunctionUtils/build.rb +20 -0
- data/ext/WSK/SilentUtils/Makefile +149 -0
- data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
- data/ext/WSK/SilentUtils/SilentUtils.o +0 -0
- data/ext/WSK/SilentUtils/SilentUtils.so +0 -0
- data/ext/WSK/SilentUtils/build.rb +18 -0
- data/ext/WSK/VolumeUtils/Makefile +149 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.o +0 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.so +0 -0
- data/ext/WSK/VolumeUtils/build.rb +20 -0
- data/lib/WSK/Actions/Analyze.rb +176 -0
- data/lib/WSK/Actions/ApplyMap.desc.rb +15 -0
- data/lib/WSK/Actions/ApplyMap.rb +57 -0
- data/lib/WSK/Actions/ApplyVolumeFct.desc.rb +30 -0
- data/lib/WSK/Actions/ApplyVolumeFct.rb +72 -0
- data/lib/WSK/Actions/Compare.desc.rb +25 -0
- data/lib/WSK/Actions/Compare.rb +238 -0
- data/lib/WSK/Actions/ConstantCompare.desc.rb +20 -0
- data/lib/WSK/Actions/ConstantCompare.rb +61 -0
- data/lib/WSK/Actions/Cut.desc.rb +20 -0
- data/lib/WSK/Actions/Cut.rb +60 -0
- data/lib/WSK/Actions/CutFirstSignal.desc.rb +25 -0
- data/lib/WSK/Actions/CutFirstSignal.rb +72 -0
- data/lib/WSK/Actions/DCShifter.desc.rb +15 -0
- data/lib/WSK/Actions/DCShifter.rb +67 -0
- data/lib/WSK/Actions/DrawFct.desc.rb +20 -0
- data/lib/WSK/Actions/DrawFct.rb +59 -0
- data/lib/WSK/Actions/FFT.rb +104 -0
- data/lib/WSK/Actions/GenAllValues.rb +67 -0
- data/lib/WSK/Actions/GenConstant.desc.rb +20 -0
- data/lib/WSK/Actions/GenConstant.rb +56 -0
- data/lib/WSK/Actions/GenSawtooth.rb +57 -0
- data/lib/WSK/Actions/GenSine.desc.rb +20 -0
- data/lib/WSK/Actions/GenSine.rb +73 -0
- data/lib/WSK/Actions/Identity.rb +43 -0
- data/lib/WSK/Actions/Mix.desc.rb +15 -0
- data/lib/WSK/Actions/Mix.rb +149 -0
- data/lib/WSK/Actions/Multiply.desc.rb +15 -0
- data/lib/WSK/Actions/Multiply.rb +73 -0
- data/lib/WSK/Actions/NoiseGate.desc.rb +35 -0
- data/lib/WSK/Actions/NoiseGate.rb +129 -0
- data/lib/WSK/Actions/SilenceInserter.desc.rb +20 -0
- data/lib/WSK/Actions/SilenceInserter.rb +87 -0
- data/lib/WSK/Actions/SilenceRemover.desc.rb +30 -0
- data/lib/WSK/Actions/SilenceRemover.rb +74 -0
- data/lib/WSK/Actions/VolumeProfile.desc.rb +35 -0
- data/lib/WSK/Actions/VolumeProfile.rb +63 -0
- data/lib/WSK/Common.rb +292 -0
- data/lib/WSK/FFT.rb +527 -0
- data/lib/WSK/Functions.rb +770 -0
- data/lib/WSK/Launcher.rb +216 -0
- data/lib/WSK/Maps.rb +29 -0
- data/lib/WSK/Model/CachedBufferReader.rb +151 -0
- data/lib/WSK/Model/Header.rb +133 -0
- data/lib/WSK/Model/InputData.rb +193 -0
- data/lib/WSK/Model/RawReader.rb +78 -0
- data/lib/WSK/Model/WaveReader.rb +91 -0
- data/lib/WSK/OutputInterfaces/DirectStream.rb +146 -0
- data/lib/WSK/RIFFReader.rb +60 -0
- metadata +151 -0
@@ -0,0 +1,770 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
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 readFromFile(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 readFromInputVolume(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.eachRawBuffer(lIdxCurrentSample, lIdxCurrentEndSample, :NbrSamplesPrefetch => 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
|
+
#logDebug "[#{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 applyOnVolume(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB)
|
170
|
+
prepareFunctionUtils
|
171
|
+
lCFunction = @FunctionUtils.createCFunction(@Function, iIdxBeginSample, iIdxEndSample)
|
172
|
+
lIdxBufferSample = iIdxBeginSample
|
173
|
+
iInputData.eachRawBuffer(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.eachBuffer(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 divideBy(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
|
+
logErr "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 convertToDB(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 = valueLog(iMaxYValue)
|
223
|
+
@Function[:Points].each do |ioPoint|
|
224
|
+
ioPoint[1] = valueVal2db_Internal(ioPoint[1])
|
225
|
+
end
|
226
|
+
else
|
227
|
+
logErr "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 roundToPrecision(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
|
+
logErr "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 applyDamping(iSlopeUp, iSlopeDown)
|
254
|
+
if ((iSlopeUp != nil) and
|
255
|
+
(iSlopeUp <= 0))
|
256
|
+
logErr "Upward slope (#{iSlopeUp}) has to be > 0"
|
257
|
+
elsif ((iSlopeDown != nil) and
|
258
|
+
(iSlopeDown >= 0))
|
259
|
+
logErr "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
|
+
logErr "Unknown function type: #{@Function[:FunctionType]}"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
optimize
|
333
|
+
end
|
334
|
+
|
335
|
+
# Invert the abscisses of a function
|
336
|
+
def invertAbscisses
|
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
|
+
logErr "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 getBounds
|
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
|
+
logErr "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 writeToFile(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
|
+
logErr "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 applyMapFunction(iMapFunction)
|
421
|
+
case @Function[:FunctionType]
|
422
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
423
|
+
case iMapFunction.functionData[: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.functionData[: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
|
+
logErr "Unknown function type: #{@Function[:FunctionType]}"
|
492
|
+
end
|
493
|
+
else
|
494
|
+
logErr "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 removeNoiseAbscisses(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
|
+
logErr "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 substractFunction(iSubFunction)
|
540
|
+
case @Function[:FunctionType]
|
541
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
542
|
+
case iSubFunction.functionData[: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
|
+
logErr "Unknown function type: #{@Function[:FunctionType]}"
|
558
|
+
end
|
559
|
+
else
|
560
|
+
logErr "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 divideByFunction(iDivFunction)
|
570
|
+
case @Function[:FunctionType]
|
571
|
+
when FCTTYPE_PIECEWISE_LINEAR
|
572
|
+
case iDivFunction.functionData[: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
|
+
logErr "Unknown function type: #{@Function[:FunctionType]}"
|
588
|
+
end
|
589
|
+
else
|
590
|
+
logErr "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 functionData
|
600
|
+
return @Function
|
601
|
+
end
|
602
|
+
|
603
|
+
# Compute the log of a function value.
|
604
|
+
#
|
605
|
+
# Parameters:
|
606
|
+
# * *iValue* (_Rational_): The value
|
607
|
+
def valueLog(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 valueVal2db(iValue, iMaxValue)
|
619
|
+
@Log2 = Math::log(2).to_r
|
620
|
+
@LogMax = valueLog(iMaxValue)
|
621
|
+
|
622
|
+
return valueVal2db_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
|
+
logErr "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
|
+
logErr "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.functionData[: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 valueVal2db_Internal(iValue)
|
758
|
+
if (iValue == 0)
|
759
|
+
# -Infinity
|
760
|
+
return -1.0/0.0
|
761
|
+
else
|
762
|
+
return -6*(@LogMax-valueLog(iValue.abs))/@Log2
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
end
|
767
|
+
|
768
|
+
end
|
769
|
+
|
770
|
+
end
|