WaveSwissKnife 0.2.0.20120302

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/AUTHORS +4 -0
  2. data/ChangeLog +31 -0
  3. data/Credits +3 -0
  4. data/LICENSE +31 -0
  5. data/README +15 -0
  6. data/ReleaseInfo +8 -0
  7. data/bin/WSK.rb +14 -0
  8. data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
  9. data/ext/WSK/AnalyzeUtils/extconf.rb +7 -0
  10. data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
  11. data/ext/WSK/ArithmUtils/extconf.rb +15 -0
  12. data/ext/WSK/CommonBuild.rb +29 -0
  13. data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
  14. data/ext/WSK/FFTUtils/extconf.rb +15 -0
  15. data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
  16. data/ext/WSK/FunctionUtils/extconf.rb +15 -0
  17. data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
  18. data/ext/WSK/SilentUtils/extconf.rb +7 -0
  19. data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
  20. data/ext/WSK/VolumeUtils/extconf.rb +15 -0
  21. data/external/CommonUtils/build.rb +28 -0
  22. data/external/CommonUtils/include/CommonUtils.h +177 -0
  23. data/external/CommonUtils/src/CommonUtils.c +639 -0
  24. data/lib/WSK/Actions/Analyze.rb +176 -0
  25. data/lib/WSK/Actions/ApplyMap.desc.rb +15 -0
  26. data/lib/WSK/Actions/ApplyMap.rb +57 -0
  27. data/lib/WSK/Actions/ApplyVolumeFct.desc.rb +30 -0
  28. data/lib/WSK/Actions/ApplyVolumeFct.rb +72 -0
  29. data/lib/WSK/Actions/Compare.desc.rb +25 -0
  30. data/lib/WSK/Actions/Compare.rb +238 -0
  31. data/lib/WSK/Actions/ConstantCompare.desc.rb +20 -0
  32. data/lib/WSK/Actions/ConstantCompare.rb +61 -0
  33. data/lib/WSK/Actions/Cut.desc.rb +20 -0
  34. data/lib/WSK/Actions/Cut.rb +60 -0
  35. data/lib/WSK/Actions/CutFirstSignal.desc.rb +25 -0
  36. data/lib/WSK/Actions/CutFirstSignal.rb +72 -0
  37. data/lib/WSK/Actions/DCShifter.desc.rb +15 -0
  38. data/lib/WSK/Actions/DCShifter.rb +67 -0
  39. data/lib/WSK/Actions/DrawFct.desc.rb +20 -0
  40. data/lib/WSK/Actions/DrawFct.rb +59 -0
  41. data/lib/WSK/Actions/FFT.rb +104 -0
  42. data/lib/WSK/Actions/GenAllValues.rb +67 -0
  43. data/lib/WSK/Actions/GenConstant.desc.rb +20 -0
  44. data/lib/WSK/Actions/GenConstant.rb +56 -0
  45. data/lib/WSK/Actions/GenSawtooth.rb +57 -0
  46. data/lib/WSK/Actions/GenSine.desc.rb +20 -0
  47. data/lib/WSK/Actions/GenSine.rb +73 -0
  48. data/lib/WSK/Actions/Identity.rb +43 -0
  49. data/lib/WSK/Actions/Mix.desc.rb +15 -0
  50. data/lib/WSK/Actions/Mix.rb +149 -0
  51. data/lib/WSK/Actions/Multiply.desc.rb +15 -0
  52. data/lib/WSK/Actions/Multiply.rb +73 -0
  53. data/lib/WSK/Actions/NoiseGate.desc.rb +35 -0
  54. data/lib/WSK/Actions/NoiseGate.rb +129 -0
  55. data/lib/WSK/Actions/SilenceInserter.desc.rb +20 -0
  56. data/lib/WSK/Actions/SilenceInserter.rb +87 -0
  57. data/lib/WSK/Actions/SilenceRemover.desc.rb +30 -0
  58. data/lib/WSK/Actions/SilenceRemover.rb +74 -0
  59. data/lib/WSK/Actions/VolumeProfile.desc.rb +35 -0
  60. data/lib/WSK/Actions/VolumeProfile.rb +63 -0
  61. data/lib/WSK/Common.rb +292 -0
  62. data/lib/WSK/FFT.rb +527 -0
  63. data/lib/WSK/Functions.rb +770 -0
  64. data/lib/WSK/Launcher.rb +216 -0
  65. data/lib/WSK/Maps.rb +29 -0
  66. data/lib/WSK/Model/CachedBufferReader.rb +151 -0
  67. data/lib/WSK/Model/Header.rb +133 -0
  68. data/lib/WSK/Model/InputData.rb +193 -0
  69. data/lib/WSK/Model/RawReader.rb +78 -0
  70. data/lib/WSK/Model/WaveReader.rb +91 -0
  71. data/lib/WSK/OutputInterfaces/DirectStream.rb +146 -0
  72. data/lib/WSK/RIFFReader.rb +60 -0
  73. metadata +155 -0
@@ -0,0 +1,176 @@
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
+ 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 get_nbr_samples(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.each_raw_buffer 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
+ log_info "Min values: #{lResult[:MinValues].join(', ')}"
142
+ log_info "Min values (db): #{lResult[:DBMinValues].join(', ')}"
143
+ log_info "Max values: #{lResult[:MaxValues].join(', ')}"
144
+ log_info "Max values (db): #{lResult[:DBMaxValues].join(', ')}"
145
+ log_info "Moy values (DC offset): #{lResult[:MoyValues].join(', ')}"
146
+ log_info "Moy values (DC offset) (db): #{lResult[:DBMoyValues].join(', ')}"
147
+ log_info "RMS values: #{lResult[:RMSValues].join(', ')}"
148
+ log_info "RMS values (db): #{lResult[:DBRMSValues].join(', ')}"
149
+ log_info "Abs Max values: #{lResult[:AbsMaxValues].join(', ')}"
150
+ log_info "Abs Max values (db): #{lResult[:DBAbsMaxValues].join(', ')}"
151
+ log_info "Abs Moy values: #{lResult[:AbsMoyValues].join(', ')}"
152
+ log_info "Abs Moy values (db): #{lResult[:DBAbsMoyValues].join(', ')}"
153
+ log_info ''
154
+ log_info 'Header:'
155
+ log_info "Sample rate: #{lResult[:SampleRate]}"
156
+ log_info "Sample size (bits): #{lResult[:SampleSize]}"
157
+ log_info "Number of channels: #{lResult[:NbrChannels]}"
158
+ log_info "Data rate: #{lResult[:DataRate]} bytes/sec"
159
+ log_info ''
160
+ log_info 'Data:'
161
+ log_info "Number of data samples: #{lResult[:NbrDataSamples]} (#{lResult[:DataLength]} secs)"
162
+ log_info "Maximal absolute value: #{lResult[:AbsMaxValue]} (#{lResult[:DBAbsMaxValue]} db) (#{lResult[:PCAbsMaxValue]} %)"
163
+ log_info "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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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 get_nbr_samples(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.each_buffer do |iBuffer, iNbrSamples, iNbrChannels|
39
+ lTransformedBuffer = iBuffer.map do |iValue|
40
+ if (lTransformMap[iValue] == nil)
41
+ log_warn "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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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 get_nbr_samples(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.read_from_file(@FctFileName)
48
+ rescue Exception
49
+ rError = $!
50
+ end
51
+ if (rError == nil)
52
+ # First, write samples before
53
+ iInputData.each_raw_buffer(0, lIdxBegin-1) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
54
+ oOutputData.pushRawBuffer(iInputRawBuffer)
55
+ end
56
+ # Then apply volume transformation
57
+ lFunction.apply_on_volume(iInputData, oOutputData, lIdxBegin, lIdxEnd, (@UnitDB == 1))
58
+ # Last, write samples after
59
+ iInputData.each_raw_buffer(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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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 get_nbr_samples(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
+ log_warn "Second file has more samples (#{iInputData2.NbrSamples} > #{iInputData.NbrSamples})."
36
+ rNbrSamples = iInputData2.NbrSamples
37
+ elsif (iInputData2.NbrSamples < iInputData.NbrSamples)
38
+ log_warn "Second file has less samples (#{iInputData2.NbrSamples} < #{iInputData.NbrSamples})."
39
+ else
40
+ log_info '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.each_raw_buffer do |iRawBuffer, iNbrSamples, iNbrChannels|
81
+ break
82
+ end
83
+ lRawBuffer1, lNbrSamples1, lNbrChannels = iInputData.get_current_raw_buffer
84
+ iInputData2.each_raw_buffer do |iRawBuffer, iNbrSamples, iNbrChannels|
85
+ break
86
+ end
87
+ lRawBuffer2, lNbrSamples2, lNbrChannels = iInputData2.get_current_raw_buffer
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.each_raw_buffer(lNbrSamplesProcessed) do |iRawBuffer, iNbrSamples, iNbrChannels|
159
+ break
160
+ end
161
+ lRawBuffer1, lNbrSamples1, lNbrChannels = iInputData.get_current_raw_buffer
162
+ end
163
+ if (lRawBuffer2 != nil)
164
+ iInputData2.each_raw_buffer(lNbrSamplesProcessed) do |iRawBuffer, iNbrSamples, iNbrChannels|
165
+ break
166
+ end
167
+ lRawBuffer2, lNbrSamples2, lNbrChannels = iInputData2.get_current_raw_buffer
168
+ end
169
+ end
170
+ end
171
+ if (@DistortionMap != nil)
172
+ # Write the distortion map
173
+ log_info 'Generate distortion map in distortion.diffmap'
174
+ File.open('distortion.diffmap', 'wb') do |oFile|
175
+ oFile.write(Marshal.dump(@DistortionMap))
176
+ end
177
+ log_info '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
+ log_warn "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
+ log_warn "Recorded value #{lRecordedValue} is used for both input values #{iValue} and #{lInvertMap[lRecordedValue]}. Setting it to #{iValue}."
190
+ lInvertMap[lRecordedValue] = iValue
191
+ else
192
+ log_warn "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
+ log_warn "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 - 2012 Muriel Salvan (muriel@x-aeon.com)
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
+ }