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,216 @@
1
+ #--
2
+ # Copyright (c) 2009 - 2012 Muriel Salvan (muriel@x-aeon.com)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ require 'optparse'
7
+ require 'date'
8
+
9
+ module WSK
10
+
11
+ class Launcher
12
+
13
+ include WSK::Common
14
+
15
+ # Constructor
16
+ def initialize
17
+ # Options set by the command line parser
18
+ @InputFileName = nil
19
+ @OutputFileName = nil
20
+ @Action = nil
21
+ @DisplayHelp = false
22
+ @Debug = false
23
+ parsePlugins
24
+
25
+ # The command line parser
26
+ @Options = OptionParser.new
27
+ @Options.banner = 'WSK.rb [--help] [--debug] --input <InputFile> --output <OutputFile> --action <ActionName> -- <ActionOptions>'
28
+ @Options.on( '--input <InputFile>', String,
29
+ '<InputFile>: WAVE file name to use as input',
30
+ 'Specify input file name') do |iArg|
31
+ @InputFileName = iArg
32
+ end
33
+ @Options.on( '--output <OutputFile>', String,
34
+ '<OutputFile>: WAVE file name to use as output',
35
+ 'Specify output file name') do |iArg|
36
+ @OutputFileName = iArg
37
+ end
38
+ @Options.on( '--action <ActionName>', String,
39
+ "<ActionName>: Name of the action to process. Available Actions: #{get_plugins_names('Actions').sort.join(', ')}",
40
+ 'Specify the Action to execute') do |iArg|
41
+ @Action = iArg
42
+ end
43
+ @Options.on( '--help',
44
+ 'Display help') do
45
+ @DisplayHelp = true
46
+ end
47
+ @Options.on( '--debug',
48
+ 'Activate debug logs') do
49
+ @Debug = true
50
+ end
51
+ end
52
+
53
+ # Execute command line arguments
54
+ #
55
+ # Parameters::
56
+ # * *iArgs* (<em>list<String></em>): Command line arguments
57
+ # Return::
58
+ # * _Integer_: The error code to return to the terminal
59
+ def execute(iArgs)
60
+ rResult = 0
61
+
62
+ lBeginTime = DateTime.now
63
+ lError = nil
64
+ lActionArgs = nil
65
+ begin
66
+ # Split the arguments
67
+ lMainArgs, lActionArgs = splitParameters(iArgs)
68
+ lRemainingArgs = @Options.parse(lMainArgs)
69
+ if (!lRemainingArgs.empty?)
70
+ lError = RuntimeError.new("Unknown arguments: #{lRemainingArgs.join(' ')}")
71
+ end
72
+ rescue Exception
73
+ lError = $!
74
+ end
75
+ if (lError == nil)
76
+ if (@DisplayHelp)
77
+ puts @Options
78
+ else
79
+ if (@Debug)
80
+ activate_log_debug(true)
81
+ end
82
+ # Check mandatory arguments were given
83
+ if (@InputFileName == nil)
84
+ lError = RuntimeError.new('Missing --input option. Please specify an input file.')
85
+ elsif (@OutputFileName == nil)
86
+ lError = RuntimeError.new('Missing --output option. Please specify an output file.')
87
+ elsif (@Action == nil)
88
+ lError = RuntimeError.new('Missing --action option. Please specify an action to perform.')
89
+ elsif (!File.exists?(@InputFileName))
90
+ lError = RuntimeError.new("Missing input file #{@InputFileName}")
91
+ elsif (File.exists?(@OutputFileName))
92
+ lError = RuntimeError.new("Output file #{@OutputFileName} already exists.")
93
+ else
94
+ # Access the Action
95
+ access_plugin('Actions', @Action) do |ioActionPlugin|
96
+ lDesc = ioActionPlugin.pluginDescription
97
+ # Check the output interface required by this plugin
98
+ lOutputInterfaceName = lDesc[:OutputInterface]
99
+ if (lOutputInterfaceName == nil)
100
+ lOutputInterfaceName = 'DirectStream'
101
+ end
102
+ # Initialize the variables if options are specified
103
+ if (lDesc[:Options] == nil)
104
+ if (!lActionArgs.empty?)
105
+ lError = RuntimeError.new("Unknown Action arguments: #{lActionArgs.join(' ')}. Normally no parameter was expected.")
106
+ end
107
+ else
108
+ # Check options
109
+ lPluginOptions = OptionParser.new
110
+ # Variables to instantiate
111
+ lVariables = {}
112
+ lDesc[:Options].each do |iVariable, iOptionInfo|
113
+ # Set the variable correctly when the option is encountered
114
+ lPluginOptions.on(*iOptionInfo) do |iArg|
115
+ lVariables[iVariable] = iArg
116
+ end
117
+ end
118
+ if (lActionArgs.empty?)
119
+ lError = RuntimeError.new("Action was expecting arguments: #{lPluginOptions.to_s}. Please specify them after -- separator.")
120
+ else
121
+ # Parse Action's options
122
+ begin
123
+ lRemainingActionArgs = lPluginOptions.parse(lActionArgs)
124
+ if (!lRemainingActionArgs.empty?)
125
+ lError = RuntimeError.new("Unknown Action arguments: #{lRemainingActionArgs.join(' ')}. Expected signature: #{lPluginOptions.to_s}")
126
+ end
127
+ rescue Exception
128
+ lError = $!
129
+ end
130
+ # Instantiate variables if needed
131
+ if (lError == nil)
132
+ instantiateVars(ioActionPlugin, lVariables)
133
+ end
134
+ end
135
+ end
136
+ # Plugin is initialized
137
+ if (lError == nil)
138
+ # Access the output interface plugin
139
+ access_plugin('OutputInterfaces', lOutputInterfaceName) do |ioOutputPlugin|
140
+ # Access the input file
141
+ lError = accessInputWaveFile(@InputFileName) do |iInputHeader, iInputData|
142
+ lInputSubError = nil
143
+
144
+ # Get the maximal output data samples
145
+ lNbrOutputDataSamples = ioActionPlugin.get_nbr_samples(iInputData)
146
+ log_debug "Number of samples to be written: #{lNbrOutputDataSamples}"
147
+
148
+ # Access the output file
149
+ lInputSubError = accessOutputWaveFile(@OutputFileName, iInputHeader, ioOutputPlugin, lNbrOutputDataSamples) do
150
+ # Execute
151
+ log_info "Execute Action #{@Action}, reading #{@InputFileName} and writing #{@OutputFileName} using #{lOutputInterfaceName} output interface."
152
+ next ioActionPlugin.execute(iInputData, ioOutputPlugin)
153
+ end
154
+
155
+ next lInputSubError
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ if (lError != nil)
165
+ log_err "Error encountered: #{lError}"
166
+ rResult = 1
167
+ end
168
+ log_info "Elapsed milliseconds: #{((DateTime.now-lBeginTime)*86400000).to_i}"
169
+
170
+ return rResult
171
+ end
172
+
173
+ private
174
+
175
+ # Split parameters, before and after the first -- encountered
176
+ #
177
+ # Parameters::
178
+ # * *iParameters* (<em>list<String></em>): The parameters
179
+ # Return::
180
+ # * <em>list<String></em>: The first part
181
+ # * <em>list<String></em>: The second part
182
+ def splitParameters(iParameters)
183
+ rFirstPart = iParameters
184
+ rSecondPart = []
185
+
186
+ lIdxSeparator = iParameters.index('--')
187
+ if (lIdxSeparator != nil)
188
+ if (lIdxSeparator == 0)
189
+ rFirstPart = []
190
+ else
191
+ rFirstPart = iParameters[0..lIdxSeparator-1]
192
+ end
193
+ if (lIdxSeparator == iParameters.size-1)
194
+ rSecondPart = []
195
+ else
196
+ rSecondPart = iParameters[lIdxSeparator+1..-1]
197
+ end
198
+ end
199
+
200
+ return rFirstPart, rSecondPart
201
+ end
202
+
203
+ # Store a map of variable names and their corresponding values as instance variables of a given class
204
+ #
205
+ # Parameters::
206
+ # * *ioObject* (_Object_): Object where we want to instantiate those variables
207
+ # * *iVars* (<em>map<Symbol,Object></em>): The map of variables and their values
208
+ def instantiateVars(ioObject, iVars)
209
+ iVars.each do |iVariable, iValue|
210
+ ioObject.instance_variable_set("@#{iVariable}".to_sym, iValue)
211
+ end
212
+ end
213
+
214
+ end
215
+
216
+ end
data/lib/WSK/Maps.rb ADDED
@@ -0,0 +1,29 @@
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 Maps
9
+
10
+ # Apply map functions to an input data, writing into an output data
11
+ #
12
+ # Parameters::
13
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
14
+ # * *oOutputData* (_Object_): The output data to fill
15
+ # * *iFunctions* (<em>list<map<Symbol,Object>></em>): The functions to apply, per channel
16
+ def apply_map_functions(iInputData, oOutputData, iFunctions)
17
+ require 'WSK/ArithmUtils/ArithmUtils'
18
+ lArithmUtils = ArithmUtils::ArithmUtils.new
19
+ # Create the map corresponding to the functions
20
+ lMap = lArithmUtils.createMapFromFunctions(iInputData.Header.NbrBitsPerSample, iFunctions)
21
+ # Apply the map
22
+ iInputData.each_raw_buffer do |iInputRawBuffer, iNbrSamples, iNbrChannels|
23
+ oOutputData.pushRawBuffer(lArithmUtils.applyMap(lMap, iInputRawBuffer, iInputData.Header.NbrBitsPerSample, iNbrSamples))
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,151 @@
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 Model
9
+
10
+ # Class to be inherited to define the following virtual methods:
11
+ # * read_buffer(iIdxStart, iIdxEnd) -> Buffer
12
+ # * extract_sub_buffer(iBuffer, iIdxStart, iIdxEnd) -> Buffer
13
+ # * get_nbr_samples_per_buffer -> Integer (number of samples in 1 buffer)
14
+ # * get_nbr_samples -> Integer (total number of samples)
15
+ class CachedBufferReader
16
+
17
+ # Constructor
18
+ def initialize
19
+ @NbrSamples = get_nbr_samples
20
+ @NbrSamplesPerBuffer = get_nbr_samples_per_buffer
21
+ # The position of the first sample of the buffer
22
+ # Integer
23
+ @IdxStartBufferSample = nil
24
+ # The position of the last sample of the buffer
25
+ # Integer
26
+ @IdxEndBufferSample = nil
27
+ # The buffer itself
28
+ @Buffer = nil
29
+ end
30
+
31
+ # Iterate through the buffers.
32
+ #
33
+ # Parameters::
34
+ # * *iIdxStartSample* (_Integer_): Index of the first sample to begin with [optional = 0]
35
+ # * *iIdxEndSample* (_Integer_): Index of the last sample to end with [optional = @NbrSamples-1]
36
+ # * *iOptions* (<em>map<Symbol,Object></em>): Additional options [optional = {}]:
37
+ # * *:nbr_samples_prefetch* (_Integer_): Specify a number of samples to effectively read if the data needs to be accessed. This number will always be minored by the number of samples to read and majored by the number of samples per buffer. [optional = 0]
38
+ # * *CodeBlock*: The code called for each iteration:
39
+ # * *iBuffer* (_String_): The buffer
40
+ # * *iNbrSamples* (_Integer_): The number of samples in this buffer
41
+ def each_buffer(iIdxStartSample = 0, iIdxEndSample = @NbrSamples-1, iOptions = {})
42
+ lNbrSamplesPrefetch = iOptions[:nbr_samples_prefetch]
43
+ if (lNbrSamplesPrefetch == nil)
44
+ lNbrSamplesPrefetch = 0
45
+ end
46
+ lIdxFirstSample = iIdxStartSample
47
+ while (lIdxFirstSample <= iIdxEndSample)
48
+ lIdxLastSample = lIdxFirstSample+@NbrSamplesPerBuffer
49
+ if (lIdxLastSample > iIdxEndSample)
50
+ lIdxLastSample = iIdxEndSample
51
+ end
52
+ # Compute the last sample to prefetch
53
+ lIdxLastSamplePrefetch = lIdxFirstSample + lNbrSamplesPrefetch
54
+ if (lIdxLastSamplePrefetch < lIdxLastSample)
55
+ lIdxLastSamplePrefetch = lIdxLastSample
56
+ elsif (lIdxLastSamplePrefetch > lIdxFirstSample+@NbrSamplesPerBuffer)
57
+ lIdxLastSamplePrefetch = lIdxFirstSample+@NbrSamplesPerBuffer
58
+ end
59
+ prepare_buffer(lIdxFirstSample, lIdxLastSample, lIdxFirstSample, lIdxLastSamplePrefetch)
60
+ # Check if we need to return a sub-copy of the buffer
61
+ lBuffer = []
62
+ if ((lIdxFirstSample == @IdxStartBufferSample) and
63
+ (lIdxLastSample == @IdxEndBufferSample))
64
+ lBuffer = @Buffer
65
+ else
66
+ lBuffer = extract_sub_buffer(@Buffer, lIdxFirstSample - @IdxStartBufferSample, lIdxLastSample - @IdxStartBufferSample)
67
+ end
68
+ # Call client code
69
+ yield(lBuffer, lIdxLastSample - lIdxFirstSample + 1)
70
+ lIdxFirstSample = lIdxLastSample+1
71
+ end
72
+ end
73
+
74
+ # Iterate through the buffers in reverse order.
75
+ #
76
+ # Parameters::
77
+ # * *iIdxStartSample* (_Integer_): Index of the first sample to begin with [optional = 0]
78
+ # * *iIdxEndSample* (_Integer_): Index of the last sample to end with [optional = @NbrSamples-1]
79
+ # * *iOptions* (<em>map<Symbol,Object></em>): Additional options [optional = {}]:
80
+ # * *:nbr_samples_prefetch* (_Integer_): Specify a number of samples to effectively read if the data needs to be accessed. This number will always be minored by the number of samples to read and majored by the number of samples per buffer. [optional = 0]
81
+ # * *CodeBlock*: The code called for each iteration:
82
+ # * *iBuffer* (_String_): The buffer
83
+ # * *iNbrSamples* (_Integer_): The number of samples in this buffer
84
+ def each_reverse_buffer(iIdxStartSample = 0, iIdxEndSample = @NbrSamples-1, iOptions = {})
85
+ lNbrSamplesPrefetch = iOptions[:nbr_samples_prefetch]
86
+ if (lNbrSamplesPrefetch == nil)
87
+ lNbrSamplesPrefetch = 0
88
+ end
89
+ lIdxLastSample = iIdxEndSample
90
+ while (lIdxLastSample >= iIdxStartSample)
91
+ lIdxFirstSample = lIdxLastSample-@NbrSamplesPerBuffer
92
+ if (lIdxFirstSample < iIdxStartSample)
93
+ lIdxFirstSample = iIdxStartSample
94
+ end
95
+ # Compute the first sample to prefetch
96
+ lIdxFirstSamplePrefetch = lIdxLastSample - lNbrSamplesPrefetch
97
+ if (lIdxFirstSamplePrefetch > lIdxFirstSample)
98
+ lIdxFirstSamplePrefetch = lIdxFirstSample
99
+ elsif (lIdxFirstSamplePrefetch < lIdxLastSample-@NbrSamplesPerBuffer)
100
+ lIdxFirstSamplePrefetch = lIdxLastSample-@NbrSamplesPerBuffer
101
+ end
102
+ prepare_buffer(lIdxFirstSample, lIdxLastSample, lIdxFirstSamplePrefetch, lIdxLastSample)
103
+ # Check if we need to return a sub-copy of the buffer
104
+ lBuffer = []
105
+ if ((lIdxFirstSample == @IdxStartBufferSample) and
106
+ (lIdxLastSample == @IdxEndBufferSample))
107
+ lBuffer = @Buffer
108
+ else
109
+ lBuffer = extract_sub_buffer(@Buffer, lIdxFirstSample - @IdxStartBufferSample, lIdxLastSample - @IdxStartBufferSample)
110
+ end
111
+ # Call client code
112
+ yield(lBuffer, lIdxLastSample - lIdxFirstSample + 1)
113
+ lIdxLastSample = lIdxFirstSample-1
114
+ end
115
+ end
116
+
117
+ # Ensure that a given samples range is loaded in the buffer.
118
+ # Use the caching mechanism if needed.
119
+ # The buffer might contain more samples than desired.
120
+ #
121
+ # Parameters::
122
+ # * *iIdxStartSample* (_Integer_): Index of the first sample to begin with [optional = 0]
123
+ # * *iIdxEndSample* (_Integer_): Index of the last sample to end with [optional = @NbrSamples-1]
124
+ # * *iIdxStartSamplePrefetch* (_Integer_): Specify the first sample to effectively read if the data needs to be accessed. [optional = iIdxStartSample]
125
+ # * *iIdxEndSamplePrefetch* (_Integer_): Specify the last sample to effectively read if the data needs to be accessed. [optional = iIdxEndSample]
126
+ def prepare_buffer(iIdxStartSample, iIdxEndSample, iIdxStartSamplePrefetch = iIdxStartSample, iIdxEndSamplePrefetch = iIdxEndSample)
127
+ if ((@Buffer == nil) or
128
+ (iIdxStartSample < @IdxStartBufferSample) or
129
+ (iIdxEndSample > @IdxEndBufferSample))
130
+ # Read all from the data
131
+ @Buffer = read_buffer(iIdxStartSamplePrefetch, iIdxEndSamplePrefetch)
132
+ @IdxStartBufferSample = iIdxStartSamplePrefetch
133
+ @IdxEndBufferSample = iIdxEndSamplePrefetch
134
+ end
135
+ end
136
+
137
+ # Get the current buffer
138
+ #
139
+ # Return::
140
+ # * _Object_: The current buffer
141
+ # * _Integer_: The first sample of the buffer
142
+ # * _Integer_: The last sample of the buffer
143
+ def get_current_buffer
144
+ return @Buffer, @IdxStartBufferSample, @IdxEndBufferSample
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,133 @@
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 Model
9
+
10
+ class Header
11
+
12
+ # Audio format
13
+ # PCM = 1 (i.e. Linear quantization)
14
+ # Values other than 1 indicate some form of compression.
15
+ # Integer
16
+ attr_reader :AudioFormat
17
+
18
+ # Number of channels
19
+ # Integer
20
+ attr_reader :NbrChannels
21
+
22
+ # Sample Rate
23
+ # Integer
24
+ attr_reader :SampleRate
25
+
26
+ # Bits per sample (per channel)
27
+ # Integer
28
+ attr_reader :NbrBitsPerSample
29
+
30
+ # Constructor
31
+ #
32
+ # Parameters::
33
+ # * *iAudioFormat* (_Integer_): Audio format
34
+ # * *iNbrChannels* (_Integer_): Number of channels
35
+ # * *iSampleRate* (_Integer_): Sample rate
36
+ # * *iNbrBitsPerSample* (_Integer_): Number of bits per channel's sample
37
+ def initialize(iAudioFormat, iNbrChannels, iSampleRate, iNbrBitsPerSample)
38
+ @AudioFormat, @NbrChannels, @SampleRate, @NbrBitsPerSample = iAudioFormat, iNbrChannels, iSampleRate, iNbrBitsPerSample
39
+ # The pack/unpack formula to use
40
+ lStrChannelPackFormula = ''
41
+ case @NbrBitsPerSample
42
+ when 8
43
+ lStrChannelPackFormula = 'C'
44
+ when 16
45
+ lStrChannelPackFormula = 's'
46
+ when 24
47
+ lStrChannelPackFormula = 'Cs'
48
+ when 32
49
+ lStrChannelPackFormula = 'l'
50
+ else
51
+ raise RuntimeError.new("#{@NbrBitsPerSample} bits PCM data not supported.")
52
+ end
53
+ @StrSamplePackFormula = lStrChannelPackFormula*@NbrChannels
54
+ end
55
+
56
+ # Get decoded samples from an encoded PCM string.
57
+ #
58
+ # Parameters::
59
+ # * *iEncodedString* (_String_): The encoded string
60
+ # * *iNbrSamplesToDecode* (_String_): Number of samples to decode
61
+ # Return::
62
+ # * <em>list<Integer></em>: The list of samples (there will be iNbrSamplesToDecode*@NbrChannels values)
63
+ def getDecodedSamples(iEncodedString, iNbrSamplesToDecode)
64
+ rSamples = iEncodedString.unpack(@StrSamplePackFormula*iNbrSamplesToDecode)
65
+
66
+ if (@NbrBitsPerSample == 8)
67
+ # Values are read unsigned. Shift them.
68
+ rSamples.map! do |iChannelValue|
69
+ next iChannelValue-128
70
+ end
71
+ elsif (@NbrBitsPerSample == 24)
72
+ # Each channel value has been decoded into 2 integers. We have to sum them.
73
+ lRealSamples = []
74
+ (iNbrSamplesToDecode*@NbrChannels).times do |iIdxChannelSample|
75
+ lIdxSamples = iIdxChannelSample*2
76
+ lRealSamples[iIdxChannelSample] = rSamples[lIdxSamples] + rSamples[lIdxSamples+1] * 256
77
+ end
78
+ rSamples = lRealSamples
79
+ end
80
+
81
+ return rSamples
82
+ end
83
+
84
+ # Get encoded PCM string from decoded samples
85
+ #
86
+ # Parameters::
87
+ # * *iChannelSamples* (<em>list<Integer></em>): The list of samples to encode
88
+ # Return::
89
+ # * _String_: Encoded PCM samples
90
+ def getEncodedString(iChannelSamples)
91
+ lRealChannelSamples = nil
92
+ if (@NbrBitsPerSample == 8)
93
+ # Values have to be stored unsigned. Shift them.
94
+ lRealChannelSamples = []
95
+ iChannelSamples.each do |iChannelValue|
96
+ lRealChannelSamples << iChannelValue+128
97
+ end
98
+ elsif (@NbrBitsPerSample == 24)
99
+ # Each channel must be split into 2 integer values before encoding.
100
+ lRealChannelSamples = []
101
+ iChannelSamples.size.times do |iIdxChannelSample|
102
+ lIdxReal = iIdxChannelSample*2
103
+ lRealChannelSamples[lIdxReal] = iChannelSamples[iIdxChannelSample] & 255
104
+ lRealChannelSamples[lIdxReal+1] = iChannelSamples[iIdxChannelSample] / 256
105
+ end
106
+ else
107
+ lRealChannelSamples = iChannelSamples
108
+ end
109
+
110
+ return lRealChannelSamples.pack(@StrSamplePackFormula*(iChannelSamples.size/@NbrChannels))
111
+ end
112
+
113
+ # Compare with a different object
114
+ #
115
+ # Parameters::
116
+ # * *iOther* (_Object_): Another object
117
+ # Return::
118
+ # * _Boolean_: Are the objects equal ?
119
+ def ==(iOther)
120
+ return ((iOther.object_id == self.object_id) or
121
+ ((iOther.is_a?(WSK::Model::Header)) and
122
+ (iOther.AudioFormat == @AudioFormat) and
123
+ (iOther.NbrChannels == @NbrChannels) and
124
+ (iOther.SampleRate == @SampleRate) and
125
+ (iOther.NbrBitsPerSample == @NbrBitsPerSample)))
126
+
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+
133
+ end