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,176 @@
|
|
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
|
+
module WSK
|
7
|
+
|
8
|
+
module Actions
|
9
|
+
|
10
|
+
class Analyze
|
11
|
+
|
12
|
+
include WSK::Common
|
13
|
+
|
14
|
+
# Get the number of samples that will be written.
|
15
|
+
# This is called before execute, as it is needed to write the output file.
|
16
|
+
# It is possible to give a majoration: it will be padded with silence.
|
17
|
+
#
|
18
|
+
# Parameters:
|
19
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
20
|
+
# Return:
|
21
|
+
# * _Integer_: The number of samples to be written
|
22
|
+
def getNbrSamples(iInputData)
|
23
|
+
return 0
|
24
|
+
end
|
25
|
+
|
26
|
+
# Execute
|
27
|
+
#
|
28
|
+
# Parameters:
|
29
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
30
|
+
# * *oOutputData* (_Object_): The output data to fill
|
31
|
+
# Return:
|
32
|
+
# * _Exception_: An error, or nil if success
|
33
|
+
def execute(iInputData, oOutputData)
|
34
|
+
lMaxValue = 2**(iInputData.Header.NbrBitsPerSample-1)-1
|
35
|
+
lMinValue = 2**(iInputData.Header.NbrBitsPerSample-1)
|
36
|
+
|
37
|
+
# Require C extension
|
38
|
+
require 'WSK/AnalyzeUtils/AnalyzeUtils'
|
39
|
+
lAnalyzeUtils = AnalyzeUtils::AnalyzeUtils.new
|
40
|
+
# Initialize computing arrays
|
41
|
+
lCMaxValues = lAnalyzeUtils.init64bitsArray(iInputData.Header.NbrChannels, 0);
|
42
|
+
lCMinValues = lAnalyzeUtils.init64bitsArray(iInputData.Header.NbrChannels, lMinValue);
|
43
|
+
lCSumValues = lAnalyzeUtils.init64bitsArray(iInputData.Header.NbrChannels, 0);
|
44
|
+
lCAbsSumValues = lAnalyzeUtils.init64bitsArray(iInputData.Header.NbrChannels, 0);
|
45
|
+
lCSquareSumValues = lAnalyzeUtils.init128bitsArray(iInputData.Header.NbrChannels);
|
46
|
+
# Gather data
|
47
|
+
lNbrSamplesProcessed = 0
|
48
|
+
iInputData.eachRawBuffer do |iRawBuffer, iNbrSamples, iNbrChannels|
|
49
|
+
lAnalyzeUtils.completeAnalyze(iRawBuffer, iInputData.Header.NbrBitsPerSample, iNbrSamples, iNbrChannels, lCMaxValues, lCMinValues, lCSumValues, lCAbsSumValues, lCSquareSumValues)
|
50
|
+
lNbrSamplesProcessed += iNbrSamples
|
51
|
+
$stdout.write("#{(lNbrSamplesProcessed*100)/iInputData.NbrSamples} %\015")
|
52
|
+
$stdout.flush
|
53
|
+
end
|
54
|
+
# Get the Ruby arrays from the C ones
|
55
|
+
lMaxValues = lAnalyzeUtils.getRuby64bitsArray(lCMaxValues, iInputData.Header.NbrChannels);
|
56
|
+
lMinValues = lAnalyzeUtils.getRuby64bitsArray(lCMinValues, iInputData.Header.NbrChannels);
|
57
|
+
lSumValues = lAnalyzeUtils.getRuby64bitsArray(lCSumValues, iInputData.Header.NbrChannels);
|
58
|
+
lAbsSumValues = lAnalyzeUtils.getRuby64bitsArray(lCAbsSumValues, iInputData.Header.NbrChannels);
|
59
|
+
lSquareSumValues = lAnalyzeUtils.getRuby128bitsArray(lCSquareSumValues, iInputData.Header.NbrChannels);
|
60
|
+
# Compute values in DB scale also
|
61
|
+
lDBMinValues = []
|
62
|
+
lMinValues.each do |iValue|
|
63
|
+
lDBMinValues << val2db(iValue, lMinValue)[0]
|
64
|
+
end
|
65
|
+
lDBMaxValues = []
|
66
|
+
lMaxValues.each do |iValue|
|
67
|
+
lDBMaxValues << val2db(iValue, lMaxValue)[0]
|
68
|
+
end
|
69
|
+
lMoyValues = []
|
70
|
+
lDBMoyValues = []
|
71
|
+
lSumValues.each do |iValue|
|
72
|
+
lMoyValue = Float(iValue)/Float(iInputData.NbrSamples)
|
73
|
+
lMoyValues << lMoyValue
|
74
|
+
lDBMoyValues << val2db(lMoyValue, lMinValue)[0]
|
75
|
+
end
|
76
|
+
lAbsMoyValues = []
|
77
|
+
lDBAbsMoyValues = []
|
78
|
+
lAbsSumValues.each do |iValue|
|
79
|
+
lAbsMoyValue = Float(iValue)/Float(iInputData.NbrSamples)
|
80
|
+
lAbsMoyValues << lAbsMoyValue
|
81
|
+
lDBAbsMoyValues << val2db(lAbsMoyValue, lMinValue)[0]
|
82
|
+
end
|
83
|
+
lAbsMaxValues = []
|
84
|
+
lDBAbsMaxValues = []
|
85
|
+
lMinValues.each_with_index do |iValue, iIdx|
|
86
|
+
lAbsMinValue = iValue.abs
|
87
|
+
lAbsMaxValue = lMaxValues[iIdx].abs
|
88
|
+
lAbsMax = nil
|
89
|
+
if (lAbsMinValue > lAbsMaxValue)
|
90
|
+
lAbsMax = lAbsMinValue
|
91
|
+
else
|
92
|
+
lAbsMax = lAbsMaxValue
|
93
|
+
end
|
94
|
+
lAbsMaxValues << lAbsMax
|
95
|
+
lDBAbsMaxValues << val2db(lAbsMax, lMinValue)[0]
|
96
|
+
end
|
97
|
+
lAbsMaxValue = lAbsMaxValues.sort[-1]
|
98
|
+
lAbsMaxValueDB, lAbsMaxValuePC = val2db(lAbsMaxValue, lMinValue)
|
99
|
+
lAbsMoyValue = 0
|
100
|
+
lAbsMoyValues.each do |iValue|
|
101
|
+
lAbsMoyValue += iValue
|
102
|
+
end
|
103
|
+
lAbsMoyValue /= lAbsMoyValues.size
|
104
|
+
lAbsMoyValueDB, lAbsMoyValuePC = val2db(lAbsMoyValue, lMinValue)
|
105
|
+
lRMSValues = []
|
106
|
+
lDBRMSValues = []
|
107
|
+
lSquareSumValues.each do |iValue|
|
108
|
+
lRMSValue = Math.sqrt(iValue/iInputData.NbrSamples).round
|
109
|
+
lRMSValues << lRMSValue
|
110
|
+
lDBRMSValues << val2db(lRMSValue, lMinValue)[0]
|
111
|
+
end
|
112
|
+
lResult = {
|
113
|
+
:MinValues => lMinValues,
|
114
|
+
:MaxValues => lMaxValues,
|
115
|
+
:MoyValues => lMoyValues,
|
116
|
+
:DBMinValues => lDBMinValues,
|
117
|
+
:DBMaxValues => lDBMaxValues,
|
118
|
+
:DBMoyValues => lDBMoyValues,
|
119
|
+
:AbsMaxValues => lAbsMaxValues,
|
120
|
+
:AbsMoyValues => lAbsMoyValues,
|
121
|
+
:DBAbsMaxValues => lDBAbsMaxValues,
|
122
|
+
:DBAbsMoyValues => lDBAbsMoyValues,
|
123
|
+
:SampleRate => iInputData.Header.SampleRate,
|
124
|
+
:SampleSize => iInputData.Header.NbrBitsPerSample,
|
125
|
+
:NbrChannels => iInputData.Header.NbrChannels,
|
126
|
+
:DataRate => ((iInputData.Header.NbrChannels*iInputData.Header.NbrBitsPerSample)/8)*iInputData.Header.SampleRate,
|
127
|
+
:NbrDataSamples => iInputData.NbrSamples,
|
128
|
+
:DataLength => Float(iInputData.NbrSamples)/Float(iInputData.Header.SampleRate),
|
129
|
+
:AbsMaxValue => lAbsMaxValue,
|
130
|
+
:DBAbsMaxValue => lAbsMaxValueDB,
|
131
|
+
:PCAbsMaxValue => lAbsMaxValuePC,
|
132
|
+
:AbsMoyValue => lAbsMoyValue,
|
133
|
+
:DBAbsMoyValue => lAbsMoyValueDB,
|
134
|
+
:PCAbsMoyValue => lAbsMoyValuePC,
|
135
|
+
:MaxPossibleValue => lMaxValue,
|
136
|
+
:MinPossibleValue => lMinValue,
|
137
|
+
:RMSValues => lRMSValues,
|
138
|
+
:DBRMSValues => lDBRMSValues
|
139
|
+
}
|
140
|
+
# Display
|
141
|
+
logInfo "Min values: #{lResult[:MinValues].join(', ')}"
|
142
|
+
logInfo "Min values (db): #{lResult[:DBMinValues].join(', ')}"
|
143
|
+
logInfo "Max values: #{lResult[:MaxValues].join(', ')}"
|
144
|
+
logInfo "Max values (db): #{lResult[:DBMaxValues].join(', ')}"
|
145
|
+
logInfo "Moy values (DC offset): #{lResult[:MoyValues].join(', ')}"
|
146
|
+
logInfo "Moy values (DC offset) (db): #{lResult[:DBMoyValues].join(', ')}"
|
147
|
+
logInfo "RMS values: #{lResult[:RMSValues].join(', ')}"
|
148
|
+
logInfo "RMS values (db): #{lResult[:DBRMSValues].join(', ')}"
|
149
|
+
logInfo "Abs Max values: #{lResult[:AbsMaxValues].join(', ')}"
|
150
|
+
logInfo "Abs Max values (db): #{lResult[:DBAbsMaxValues].join(', ')}"
|
151
|
+
logInfo "Abs Moy values: #{lResult[:AbsMoyValues].join(', ')}"
|
152
|
+
logInfo "Abs Moy values (db): #{lResult[:DBAbsMoyValues].join(', ')}"
|
153
|
+
logInfo ''
|
154
|
+
logInfo 'Header:'
|
155
|
+
logInfo "Sample rate: #{lResult[:SampleRate]}"
|
156
|
+
logInfo "Sample size (bits): #{lResult[:SampleSize]}"
|
157
|
+
logInfo "Number of channels: #{lResult[:NbrChannels]}"
|
158
|
+
logInfo "Data rate: #{lResult[:DataRate]} bytes/sec"
|
159
|
+
logInfo ''
|
160
|
+
logInfo 'Data:'
|
161
|
+
logInfo "Number of data samples: #{lResult[:NbrDataSamples]} (#{lResult[:DataLength]} secs)"
|
162
|
+
logInfo "Maximal absolute value: #{lResult[:AbsMaxValue]} (#{lResult[:DBAbsMaxValue]} db) (#{lResult[:PCAbsMaxValue]} %)"
|
163
|
+
logInfo "Mean absolute value: #{lResult[:AbsMoyValue]} (#{lResult[:DBAbsMoyValue]} db) (#{lResult[:PCAbsMoyValue]} %)"
|
164
|
+
# Write a result file
|
165
|
+
File.open('analyze.result', 'wb') do |oFile|
|
166
|
+
oFile.write(Marshal.dump(lResult))
|
167
|
+
end
|
168
|
+
|
169
|
+
return nil
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
@@ -0,0 +1,15 @@
|
|
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
|
+
{
|
7
|
+
:OutputInterface => 'DirectStream',
|
8
|
+
:Options => {
|
9
|
+
:MapFileName => [
|
10
|
+
'--transformmap <MapFileName>', String,
|
11
|
+
'<MapFileName>: Name of the map file corresponding to the transform',
|
12
|
+
'Specify the file to take the transformation from.'
|
13
|
+
]
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,57 @@
|
|
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
|
+
module WSK
|
7
|
+
|
8
|
+
module Actions
|
9
|
+
|
10
|
+
class ApplyMap
|
11
|
+
|
12
|
+
include WSK::Common
|
13
|
+
|
14
|
+
# Get the number of samples that will be written.
|
15
|
+
# This is called before execute, as it is needed to write the output file.
|
16
|
+
# It is possible to give a majoration: it will be padded with silence.
|
17
|
+
#
|
18
|
+
# Parameters:
|
19
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
20
|
+
# Return:
|
21
|
+
# * _Integer_: The number of samples to be written
|
22
|
+
def getNbrSamples(iInputData)
|
23
|
+
return iInputData.NbrSamples
|
24
|
+
end
|
25
|
+
|
26
|
+
# Execute
|
27
|
+
#
|
28
|
+
# Parameters:
|
29
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
30
|
+
# * *oOutputData* (_Object_): The output data to fill
|
31
|
+
# Return:
|
32
|
+
# * _Exception_: An error, or nil if success
|
33
|
+
def execute(iInputData, oOutputData)
|
34
|
+
lTransformMap = nil
|
35
|
+
File.open(@MapFileName, 'rb') do |iFile|
|
36
|
+
lTransformMap = Marshal.load(iFile.read)
|
37
|
+
end
|
38
|
+
iInputData.eachBuffer do |iBuffer, iNbrSamples, iNbrChannels|
|
39
|
+
lTransformedBuffer = iBuffer.map do |iValue|
|
40
|
+
if (lTransformMap[iValue] == nil)
|
41
|
+
logWarn "Unknown value from the transform map: #{iValue}. Keeping it."
|
42
|
+
next iValue
|
43
|
+
else
|
44
|
+
next lTransformMap[iValue]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
oOutputData.pushBuffer(lTransformedBuffer)
|
48
|
+
end
|
49
|
+
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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
|
+
{
|
7
|
+
:OutputInterface => 'DirectStream',
|
8
|
+
:Options => {
|
9
|
+
:FctFileName => [
|
10
|
+
'--function <FunctionFileName>', String,
|
11
|
+
'<FunctionFileName>: File containing the function definition',
|
12
|
+
'Specify the function to apply'
|
13
|
+
],
|
14
|
+
:Begin => [
|
15
|
+
'--begin <BeginPos>', String,
|
16
|
+
'<BeginPos>: Position to apply volume transformation from. Can be specified as a sample number or a float seconds (ie. 12.3s).',
|
17
|
+
'Specify the first sample that will have the function applied'
|
18
|
+
],
|
19
|
+
:End => [
|
20
|
+
'--end <EndPos>', String,
|
21
|
+
'<EndPos>: Position to apply volume transformation to. Can be specified as a sample number or a float seconds (ie. 12.3s). -1 means to the end of file.',
|
22
|
+
'Specify the last sample that will have the function applied'
|
23
|
+
],
|
24
|
+
:UnitDB => [
|
25
|
+
'--unitdb <Switch>', Integer,
|
26
|
+
'<Switch>: 0 means that units used in the function are ratios. 1 means that units used in the functions are db.',
|
27
|
+
'Specify the unit used in the function'
|
28
|
+
]
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,72 @@
|
|
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
|
+
module WSK
|
7
|
+
|
8
|
+
module Actions
|
9
|
+
|
10
|
+
class ApplyVolumeFct
|
11
|
+
|
12
|
+
include WSK::Common
|
13
|
+
include WSK::Functions
|
14
|
+
|
15
|
+
# Get the number of samples that will be written.
|
16
|
+
# This is called before execute, as it is needed to write the output file.
|
17
|
+
# It is possible to give a majoration: it will be padded with silence.
|
18
|
+
#
|
19
|
+
# Parameters:
|
20
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
21
|
+
# Return:
|
22
|
+
# * _Integer_: The number of samples to be written
|
23
|
+
def getNbrSamples(iInputData)
|
24
|
+
return iInputData.NbrSamples
|
25
|
+
end
|
26
|
+
|
27
|
+
# Execute
|
28
|
+
#
|
29
|
+
# Parameters:
|
30
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
31
|
+
# * *oOutputData* (_Object_): The output data to fill
|
32
|
+
# Return:
|
33
|
+
# * _Exception_: An error, or nil if success
|
34
|
+
def execute(iInputData, oOutputData)
|
35
|
+
rError = nil
|
36
|
+
|
37
|
+
lIdxBegin = readDuration(@Begin, iInputData.Header.SampleRate)
|
38
|
+
lIdxEnd = readDuration(@End, iInputData.Header.SampleRate)
|
39
|
+
if (lIdxEnd == -1)
|
40
|
+
lIdxEnd = iInputData.NbrSamples - 1
|
41
|
+
end
|
42
|
+
if (lIdxEnd >= iInputData.NbrSamples)
|
43
|
+
rError = RuntimeError.new("Transformation ends at #{lIdxEnd}, superceeding last sample (#{iInputData.NbrSamples-1})")
|
44
|
+
else
|
45
|
+
lFunction = WSK::Functions::Function.new
|
46
|
+
begin
|
47
|
+
lFunction.readFromFile(@FctFileName)
|
48
|
+
rescue Exception
|
49
|
+
rError = $!
|
50
|
+
end
|
51
|
+
if (rError == nil)
|
52
|
+
# First, write samples before
|
53
|
+
iInputData.eachRawBuffer(0, lIdxBegin-1) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
|
54
|
+
oOutputData.pushRawBuffer(iInputRawBuffer)
|
55
|
+
end
|
56
|
+
# Then apply volume transformation
|
57
|
+
lFunction.applyOnVolume(iInputData, oOutputData, lIdxBegin, lIdxEnd, (@UnitDB == 1))
|
58
|
+
# Last, write samples after
|
59
|
+
iInputData.eachRawBuffer(lIdxEnd+1) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
|
60
|
+
oOutputData.pushRawBuffer(iInputRawBuffer)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
return rError
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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
|
+
{
|
7
|
+
:OutputInterface => 'DirectStream',
|
8
|
+
:Options => {
|
9
|
+
:InputFileName2 => [
|
10
|
+
'--inputfile2 <FileName>', String,
|
11
|
+
'<FileName>: Second file name to compare with.',
|
12
|
+
'Specify the file to compare with. The resulting file will be (inputfile - inputfile2)*Coefficient.'
|
13
|
+
],
|
14
|
+
:Coeff => [
|
15
|
+
'--coeff <Coefficient>', Integer,
|
16
|
+
'<Coefficient>: Coefficient to multiply the differences [default = 1]',
|
17
|
+
'Specify the multiplying coefficient.'
|
18
|
+
],
|
19
|
+
:GenMap => [
|
20
|
+
'--genmap <Switch>', Integer,
|
21
|
+
'<Switch>: 0 or 1, turning off or on the map generation [default = 0]',
|
22
|
+
'If 1, a file (named Output.map) will be created, storing the distortion for each encountered value.'
|
23
|
+
]
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,238 @@
|
|
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
|
+
module WSK
|
7
|
+
|
8
|
+
module Actions
|
9
|
+
|
10
|
+
class Compare
|
11
|
+
|
12
|
+
include WSK::Common
|
13
|
+
include WSK::Maps
|
14
|
+
|
15
|
+
# Get the number of samples that will be written.
|
16
|
+
# This is called before execute, as it is needed to write the output file.
|
17
|
+
# It is possible to give a majoration: it will be padded with silence.
|
18
|
+
#
|
19
|
+
# Parameters:
|
20
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
21
|
+
# Return:
|
22
|
+
# * _Integer_: The number of samples to be written
|
23
|
+
def getNbrSamples(iInputData)
|
24
|
+
rNbrSamples = iInputData.NbrSamples
|
25
|
+
|
26
|
+
# Get the second input file
|
27
|
+
lError = accessInputWaveFile(@InputFileName2) do |iInputHeader2, iInputData2|
|
28
|
+
rSubError = nil
|
29
|
+
# First check that headers are the same
|
30
|
+
if (iInputHeader2 != iInputData.Header)
|
31
|
+
rSubError = RuntimeError.new("Mismatch headers: First input file: #{iInputData.Header.inspect} Second input file: #{iInputHeader2.inspect}")
|
32
|
+
end
|
33
|
+
# Then we will return the maximal samples
|
34
|
+
if (iInputData2.NbrSamples > iInputData.NbrSamples)
|
35
|
+
logWarn "Second file has more samples (#{iInputData2.NbrSamples} > #{iInputData.NbrSamples})."
|
36
|
+
rNbrSamples = iInputData2.NbrSamples
|
37
|
+
elsif (iInputData2.NbrSamples < iInputData.NbrSamples)
|
38
|
+
logWarn "Second file has less samples (#{iInputData2.NbrSamples} < #{iInputData.NbrSamples})."
|
39
|
+
else
|
40
|
+
logInfo 'Files have the same number of samples.'
|
41
|
+
end
|
42
|
+
next rSubError
|
43
|
+
end
|
44
|
+
if (lError != nil)
|
45
|
+
raise lError
|
46
|
+
end
|
47
|
+
@TotalNbrSamples = rNbrSamples
|
48
|
+
|
49
|
+
return rNbrSamples
|
50
|
+
end
|
51
|
+
|
52
|
+
# Execute
|
53
|
+
#
|
54
|
+
# Parameters:
|
55
|
+
# * *iInputData* (<em>WSK::Model::InputData</em>): The input data
|
56
|
+
# * *oOutputData* (_Object_): The output data to fill
|
57
|
+
# Return:
|
58
|
+
# * _Exception_: An error, or nil if success
|
59
|
+
def execute(iInputData, oOutputData)
|
60
|
+
@NbrBitsPerSample = iInputData.Header.NbrBitsPerSample
|
61
|
+
@NbrChannels = iInputData.Header.NbrChannels
|
62
|
+
if (@GenMap == 1)
|
63
|
+
# We want to generate the map
|
64
|
+
@DistortionMap = [nil]*(2**@NbrBitsPerSample)
|
65
|
+
else
|
66
|
+
@DistortionMap = nil
|
67
|
+
end
|
68
|
+
# Measure the cumulative errors
|
69
|
+
@CumulativeErrors = 0
|
70
|
+
@MaxValue = 2**(@NbrBitsPerSample-1)-1
|
71
|
+
@MinValue = -2**(@NbrBitsPerSample-1)
|
72
|
+
# Get the second input file
|
73
|
+
rError = accessInputWaveFile(@InputFileName2) do |iInputHeader2, iInputData2|
|
74
|
+
# Loop on both files.
|
75
|
+
# !!! We count on the same buffer size for both files.
|
76
|
+
require 'WSK/ArithmUtils/ArithmUtils'
|
77
|
+
@ArithmUtils = WSK::ArithmUtils::ArithmUtils.new
|
78
|
+
# Initialize buffers
|
79
|
+
lNbrSamplesProcessed = 0
|
80
|
+
iInputData.eachRawBuffer do |iRawBuffer, iNbrSamples, iNbrChannels|
|
81
|
+
break
|
82
|
+
end
|
83
|
+
lRawBuffer1, lNbrSamples1, lNbrChannels = iInputData.getCurrentRawBuffer
|
84
|
+
iInputData2.eachRawBuffer do |iRawBuffer, iNbrSamples, iNbrChannels|
|
85
|
+
break
|
86
|
+
end
|
87
|
+
lRawBuffer2, lNbrSamples2, lNbrChannels = iInputData2.getCurrentRawBuffer
|
88
|
+
while ((lRawBuffer1 != nil) or
|
89
|
+
(lRawBuffer2 != nil))
|
90
|
+
if (lRawBuffer1 == nil)
|
91
|
+
oOutputData.pushRawBuffer(lRawBuffer2)
|
92
|
+
lNbrSamplesProcessed += lNbrSamples2
|
93
|
+
if (lNbrSamplesProcessed == @TotalNbrSamples)
|
94
|
+
lRawBuffer2 = nil
|
95
|
+
end
|
96
|
+
elsif (lRawBuffer2 == nil)
|
97
|
+
computeInverseMap
|
98
|
+
oOutputData.pushRawBuffer(@ArithmUtils.applyMap(@InverseMap, lRawBuffer1, @NbrBitsPerSample, lNbrSamples1))
|
99
|
+
lNbrSamplesProcessed += lNbrSamples1
|
100
|
+
if (lNbrSamplesProcessed == @TotalNbrSamples)
|
101
|
+
lRawBuffer1 = nil
|
102
|
+
end
|
103
|
+
elsif (lNbrSamples1 == lNbrSamples2)
|
104
|
+
lOutputBuffer, lCumulativeErrors = @ArithmUtils.compareBuffers(
|
105
|
+
lRawBuffer1,
|
106
|
+
lRawBuffer2,
|
107
|
+
@NbrBitsPerSample,
|
108
|
+
@NbrChannels,
|
109
|
+
lNbrSamples1,
|
110
|
+
@Coeff,
|
111
|
+
@DistortionMap
|
112
|
+
)
|
113
|
+
oOutputData.pushRawBuffer(lOutputBuffer)
|
114
|
+
@CumulativeErrors += lCumulativeErrors
|
115
|
+
lNbrSamplesProcessed += lNbrSamples1
|
116
|
+
if (lNbrSamplesProcessed == @TotalNbrSamples)
|
117
|
+
lRawBuffer1 = nil
|
118
|
+
lRawBuffer2 = nil
|
119
|
+
end
|
120
|
+
elsif (lNbrSamples1 > lNbrSamples2)
|
121
|
+
lOutputBuffer, lCumulativeErrors = @ArithmUtils.compareBuffers(
|
122
|
+
lRawBuffer1[0..lRawBuffer2.size-1],
|
123
|
+
lRawBuffer2,
|
124
|
+
@NbrBitsPerSample,
|
125
|
+
@NbrChannels,
|
126
|
+
lNbrSamples1,
|
127
|
+
@Coeff,
|
128
|
+
@DistortionMap
|
129
|
+
)
|
130
|
+
oOutputData.pushRawBuffer(lOutputBuffer)
|
131
|
+
@CumulativeErrors += lCumulativeErrors
|
132
|
+
# Write remaining buffer (-Buffer1)
|
133
|
+
computeInverseMap
|
134
|
+
oOutputData.pushRawBuffer(@ArithmUtils.applyMap(@InverseMap, lRawBuffer1[lRawBuffer2.size..-1], @NbrBitsPerSample, lNbrSamples2 - lNbrSamples1))
|
135
|
+
# Buffer2 is finished
|
136
|
+
lRawBuffer2 = nil
|
137
|
+
lNbrSamplesProcessed += lNbrSamples1
|
138
|
+
else
|
139
|
+
lOutputBuffer, lCumulativeErrors = @ArithmUtils.compareBuffers(
|
140
|
+
lRawBuffer1,
|
141
|
+
lRawBuffer2[0..lRawBuffer1.size-1],
|
142
|
+
@NbrBitsPerSample,
|
143
|
+
@NbrChannels,
|
144
|
+
lNbrSamples1,
|
145
|
+
@Coeff,
|
146
|
+
@DistortionMap
|
147
|
+
)
|
148
|
+
oOutputData.pushRawBuffer(lOutputBuffer)
|
149
|
+
@CumulativeErrors += lCumulativeErrors
|
150
|
+
# Write remaining buffer (Buffer2)
|
151
|
+
oOutputData.pushRawBuffer(lRawBuffer2[lRawBuffer1.size..-1])
|
152
|
+
# Buffer1 is finished
|
153
|
+
lRawBuffer1 = nil
|
154
|
+
lNbrSamplesProcessed += lNbrSamples2
|
155
|
+
end
|
156
|
+
# Read next buffers if they are not finished
|
157
|
+
if (lRawBuffer1 != nil)
|
158
|
+
iInputData.eachRawBuffer(lNbrSamplesProcessed) do |iRawBuffer, iNbrSamples, iNbrChannels|
|
159
|
+
break
|
160
|
+
end
|
161
|
+
lRawBuffer1, lNbrSamples1, lNbrChannels = iInputData.getCurrentRawBuffer
|
162
|
+
end
|
163
|
+
if (lRawBuffer2 != nil)
|
164
|
+
iInputData2.eachRawBuffer(lNbrSamplesProcessed) do |iRawBuffer, iNbrSamples, iNbrChannels|
|
165
|
+
break
|
166
|
+
end
|
167
|
+
lRawBuffer2, lNbrSamples2, lNbrChannels = iInputData2.getCurrentRawBuffer
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
if (@DistortionMap != nil)
|
172
|
+
# Write the distortion map
|
173
|
+
logInfo 'Generate distortion map in distortion.diffmap'
|
174
|
+
File.open('distortion.diffmap', 'wb') do |oFile|
|
175
|
+
oFile.write(Marshal.dump(@DistortionMap))
|
176
|
+
end
|
177
|
+
logInfo 'Generate invert map in invert.map'
|
178
|
+
# We want to spot the values that are missing, and the duplicate values
|
179
|
+
lInvertMap = [nil]*(2**@NbrBitsPerSample)
|
180
|
+
(@MinValue .. @MaxValue).each do |iValue|
|
181
|
+
if (@DistortionMap[iValue] == nil)
|
182
|
+
logWarn "Value #{iValue} was not part of the input file"
|
183
|
+
else
|
184
|
+
lRecordedValue = iValue + @DistortionMap[iValue]
|
185
|
+
if (lInvertMap[lRecordedValue] == nil)
|
186
|
+
lInvertMap[lRecordedValue] = iValue
|
187
|
+
else
|
188
|
+
if (iValue.abs < lInvertMap[lRecordedValue].abs)
|
189
|
+
logWarn "Recorded value #{lRecordedValue} is used for both input values #{iValue} and #{lInvertMap[lRecordedValue]}. Setting it to #{iValue}."
|
190
|
+
lInvertMap[lRecordedValue] = iValue
|
191
|
+
else
|
192
|
+
logWarn "Recorded value #{lRecordedValue} is used for both input values #{iValue} and #{lInvertMap[lRecordedValue]}. Keeping it to #{lInvertMap[lRecordedValue]}."
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
(@MinValue .. @MaxValue).each do |iValue|
|
198
|
+
if (lInvertMap[iValue] == nil)
|
199
|
+
logWarn "Missing value that has never been recorded: #{iValue}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
File.open('invert.map', 'wb') do |oFile|
|
203
|
+
oFile.write(Marshal.dump(lInvertMap))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
puts "Cumulative errors: #{@CumulativeErrors} (#{Float(@CumulativeErrors*100)/Float(iInputData.NbrSamples*(2**@NbrBitsPerSample))} %)"
|
207
|
+
|
208
|
+
return rError
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
# Compute the inverse map
|
214
|
+
def computeInverseMap
|
215
|
+
if (defined?(@InverseMap) == nil)
|
216
|
+
# Compute the function that will perform inversion
|
217
|
+
lMaxValue = 2**(@NbrBitsPerSample-1) - 1
|
218
|
+
lMinValue = -2**(@NbrBitsPerSample-1)
|
219
|
+
@InverseMap = @ArithmUtils.createMapFromFunctions(
|
220
|
+
@NbrBitsPerSample,
|
221
|
+
[ {
|
222
|
+
:FunctionType => WSK::Functions::FCTTYPE_PIECEWISE_LINEAR,
|
223
|
+
:MinValue => lMinValue,
|
224
|
+
:MaxValue => lMaxValue,
|
225
|
+
:Points => {
|
226
|
+
lMinValue => -lMinValue,
|
227
|
+
lMaxValue => -lMaxValue
|
228
|
+
}
|
229
|
+
} ] * @NbrChannels
|
230
|
+
)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
@@ -0,0 +1,20 @@
|
|
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
|
+
{
|
7
|
+
:OutputInterface => 'DirectStream',
|
8
|
+
:Options => {
|
9
|
+
:Value => [
|
10
|
+
'--value <Value>', Integer,
|
11
|
+
'<Value>: Constant value to compare with',
|
12
|
+
'Specify the value to compare with.'
|
13
|
+
],
|
14
|
+
:NbrSamples => [
|
15
|
+
'--nbrsamples <NbrSamples>', Integer,
|
16
|
+
'<NbrSamples>: Number of samples used to compare with',
|
17
|
+
'Specify the number of samples the file should have.'
|
18
|
+
]
|
19
|
+
}
|
20
|
+
}
|