WaveSwissKnife 0.2.0.20120302
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +4 -0
- data/ChangeLog +31 -0
- data/Credits +3 -0
- data/LICENSE +31 -0
- data/README +15 -0
- data/ReleaseInfo +8 -0
- data/bin/WSK.rb +14 -0
- data/ext/WSK/AnalyzeUtils/AnalyzeUtils.c +272 -0
- data/ext/WSK/AnalyzeUtils/extconf.rb +7 -0
- data/ext/WSK/ArithmUtils/ArithmUtils.c +862 -0
- data/ext/WSK/ArithmUtils/extconf.rb +15 -0
- data/ext/WSK/CommonBuild.rb +29 -0
- data/ext/WSK/FFTUtils/FFTUtils.c +662 -0
- data/ext/WSK/FFTUtils/extconf.rb +15 -0
- data/ext/WSK/FunctionUtils/FunctionUtils.c +182 -0
- data/ext/WSK/FunctionUtils/extconf.rb +15 -0
- data/ext/WSK/SilentUtils/SilentUtils.c +431 -0
- data/ext/WSK/SilentUtils/extconf.rb +7 -0
- data/ext/WSK/VolumeUtils/VolumeUtils.c +494 -0
- data/ext/WSK/VolumeUtils/extconf.rb +15 -0
- data/external/CommonUtils/build.rb +28 -0
- data/external/CommonUtils/include/CommonUtils.h +177 -0
- data/external/CommonUtils/src/CommonUtils.c +639 -0
- data/lib/WSK/Actions/Analyze.rb +176 -0
- data/lib/WSK/Actions/ApplyMap.desc.rb +15 -0
- data/lib/WSK/Actions/ApplyMap.rb +57 -0
- data/lib/WSK/Actions/ApplyVolumeFct.desc.rb +30 -0
- data/lib/WSK/Actions/ApplyVolumeFct.rb +72 -0
- data/lib/WSK/Actions/Compare.desc.rb +25 -0
- data/lib/WSK/Actions/Compare.rb +238 -0
- data/lib/WSK/Actions/ConstantCompare.desc.rb +20 -0
- data/lib/WSK/Actions/ConstantCompare.rb +61 -0
- data/lib/WSK/Actions/Cut.desc.rb +20 -0
- data/lib/WSK/Actions/Cut.rb +60 -0
- data/lib/WSK/Actions/CutFirstSignal.desc.rb +25 -0
- data/lib/WSK/Actions/CutFirstSignal.rb +72 -0
- data/lib/WSK/Actions/DCShifter.desc.rb +15 -0
- data/lib/WSK/Actions/DCShifter.rb +67 -0
- data/lib/WSK/Actions/DrawFct.desc.rb +20 -0
- data/lib/WSK/Actions/DrawFct.rb +59 -0
- data/lib/WSK/Actions/FFT.rb +104 -0
- data/lib/WSK/Actions/GenAllValues.rb +67 -0
- data/lib/WSK/Actions/GenConstant.desc.rb +20 -0
- data/lib/WSK/Actions/GenConstant.rb +56 -0
- data/lib/WSK/Actions/GenSawtooth.rb +57 -0
- data/lib/WSK/Actions/GenSine.desc.rb +20 -0
- data/lib/WSK/Actions/GenSine.rb +73 -0
- data/lib/WSK/Actions/Identity.rb +43 -0
- data/lib/WSK/Actions/Mix.desc.rb +15 -0
- data/lib/WSK/Actions/Mix.rb +149 -0
- data/lib/WSK/Actions/Multiply.desc.rb +15 -0
- data/lib/WSK/Actions/Multiply.rb +73 -0
- data/lib/WSK/Actions/NoiseGate.desc.rb +35 -0
- data/lib/WSK/Actions/NoiseGate.rb +129 -0
- data/lib/WSK/Actions/SilenceInserter.desc.rb +20 -0
- data/lib/WSK/Actions/SilenceInserter.rb +87 -0
- data/lib/WSK/Actions/SilenceRemover.desc.rb +30 -0
- data/lib/WSK/Actions/SilenceRemover.rb +74 -0
- data/lib/WSK/Actions/VolumeProfile.desc.rb +35 -0
- data/lib/WSK/Actions/VolumeProfile.rb +63 -0
- data/lib/WSK/Common.rb +292 -0
- data/lib/WSK/FFT.rb +527 -0
- data/lib/WSK/Functions.rb +770 -0
- data/lib/WSK/Launcher.rb +216 -0
- data/lib/WSK/Maps.rb +29 -0
- data/lib/WSK/Model/CachedBufferReader.rb +151 -0
- data/lib/WSK/Model/Header.rb +133 -0
- data/lib/WSK/Model/InputData.rb +193 -0
- data/lib/WSK/Model/RawReader.rb +78 -0
- data/lib/WSK/Model/WaveReader.rb +91 -0
- data/lib/WSK/OutputInterfaces/DirectStream.rb +146 -0
- data/lib/WSK/RIFFReader.rb +60 -0
- metadata +155 -0
data/lib/WSK/Launcher.rb
ADDED
@@ -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
|