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
@@ -0,0 +1,193 @@
|
|
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 'WSK/Model/RawReader.rb'
|
7
|
+
require 'WSK/Model/WaveReader.rb'
|
8
|
+
|
9
|
+
module WSK
|
10
|
+
|
11
|
+
module Model
|
12
|
+
|
13
|
+
class InputData
|
14
|
+
|
15
|
+
# Number of samples in the data
|
16
|
+
# Integer
|
17
|
+
attr_reader :NbrSamples
|
18
|
+
|
19
|
+
# Header of the input data
|
20
|
+
# WSK::Model::Header
|
21
|
+
attr_reader :Header
|
22
|
+
|
23
|
+
# Constructor
|
24
|
+
#
|
25
|
+
# Parameters::
|
26
|
+
# * *iFile* (_IO_): The file descriptor. Don't use it externally as long as it is used by this class.
|
27
|
+
# * *iHeader* (<em>WSK::Model::Header</em>): Corresponding file header
|
28
|
+
def initialize(iFile, iHeader)
|
29
|
+
@File, @Header = iFile, iHeader
|
30
|
+
@NbrSamples = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check that data seems coherent, and initialize the cursor
|
34
|
+
#
|
35
|
+
# Return::
|
36
|
+
# * _Exception_: Error, or nil in case of success
|
37
|
+
def init_cursor
|
38
|
+
rError = nil
|
39
|
+
|
40
|
+
# Size of a sample
|
41
|
+
# Integer
|
42
|
+
lSampleSize = (@Header.NbrChannels*@Header.NbrBitsPerSample)/8
|
43
|
+
# Read the size of the data
|
44
|
+
rError, lDataSize = RIFFReader.new(@File).setFilePos('data')
|
45
|
+
if (rError == nil)
|
46
|
+
# Check that the data size is coherent
|
47
|
+
if (lDataSize % lSampleSize == 0)
|
48
|
+
@NbrSamples = lDataSize / lSampleSize
|
49
|
+
@RawReader = RawReader.new(@File, @File.pos, lSampleSize, @NbrSamples)
|
50
|
+
@WaveReader = WaveReader.new(@RawReader, @Header)
|
51
|
+
log_debug "Number of samples: #{@NbrSamples}"
|
52
|
+
else
|
53
|
+
rError = RuntimeError.new("Data size (#{lDataSize} should be a multiple of #{lSampleSize} according to header.")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
return rError
|
58
|
+
end
|
59
|
+
|
60
|
+
# Iterate through the samples
|
61
|
+
#
|
62
|
+
# Parameters::
|
63
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample to begin with [optional = 0]
|
64
|
+
# * *CodeBlock*: The code called for each iteration:
|
65
|
+
# * *iInputSampleData* (<em>list<Integer></em>): The list of values (1 per channel)
|
66
|
+
def each(iIdxBeginSample = 0)
|
67
|
+
each_buffer(iIdxBeginSample) do |iBuffer, iNbrSamples|
|
68
|
+
iBuffer.size.times do |iIdxSample|
|
69
|
+
yield(iBuffer[iIdxSample*@Header.NbrChannels..(iIdxSample+1)*@Header.NbrChannels-1])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Iterate through the buffers. This is far more efficient than iterating over samples.
|
75
|
+
#
|
76
|
+
# Parameters::
|
77
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample to begin with [optional = 0]
|
78
|
+
# * *CodeBlock*: The code called for each iteration:
|
79
|
+
# * *iInputBuffer* (<em>list<Integer></em>): The list of channel values
|
80
|
+
# * *iNbrSamples* (_Integer_): The number of samples in this buffer
|
81
|
+
# * *iNbrChannels* (_Integer_): The number of channels in this buffer
|
82
|
+
def each_buffer(iIdxBeginSample = 0)
|
83
|
+
@WaveReader.each_buffer(iIdxBeginSample) do |iBuffer, iNbrSamples|
|
84
|
+
yield(iBuffer, iNbrSamples, @Header.NbrChannels)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Get the current buffer.
|
89
|
+
# !!! This must be called only if the buffer was previously initialized (call getSampleSata to do so).
|
90
|
+
#
|
91
|
+
# Return::
|
92
|
+
# * <em>list<Integer></em>: The list of channel values
|
93
|
+
# * _Integer_: The number of samples in this buffer
|
94
|
+
# * _Integer_: The number of channels in this buffer
|
95
|
+
def get_current_buffer
|
96
|
+
rBuffer, lIdxStartSample, lIdxEndSample = @WaveReader.get_current_buffer
|
97
|
+
|
98
|
+
return rBuffer, lIdxEndSample-lIdxStartSample+1, @Header.NbrChannels
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get the current raw buffer.
|
102
|
+
# !!! This must be called only if the buffer was previously initialized (call each_raw_buffer to do so).
|
103
|
+
#
|
104
|
+
# Return::
|
105
|
+
# * _String_: The raw buffer
|
106
|
+
# * _Integer_: The number of samples in this buffer
|
107
|
+
# * _Integer_: The number of channels in this buffer
|
108
|
+
def get_current_raw_buffer
|
109
|
+
rBuffer, lIdxStartSample, lIdxEndSample = @RawReader.get_current_buffer
|
110
|
+
|
111
|
+
return rBuffer, lIdxEndSample-lIdxStartSample+1, @Header.NbrChannels
|
112
|
+
end
|
113
|
+
|
114
|
+
# Iterate through the buffers in the reverse order. This is far more efficient than iterating over samples.
|
115
|
+
#
|
116
|
+
# Parameters::
|
117
|
+
# * *iIdxEndSample* (_Integer_): Index of the first sample to begin with [optional = @NbrSamples-1]
|
118
|
+
# * *CodeBlock*: The code called for each iteration:
|
119
|
+
# * *iInputBuffer* (<em>list<Integer></em>): The list of channel values
|
120
|
+
# * *iNbrSamples* (_Integer_): The number of samples in this buffer
|
121
|
+
# * *iNbrChannels* (_Integer_): The number of channels in this buffer
|
122
|
+
def each_reverse_buffer(iIdxEndSample = @NbrSamples-1)
|
123
|
+
@WaveReader.each_reverse_buffer(0, iIdxEndSample) do |iBuffer, iNbrSamples|
|
124
|
+
yield(iBuffer, iNbrSamples, @Header.NbrChannels)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Iterate through the buffers in raw mode (strings read directly without unpacking).
|
129
|
+
# This is far more efficient than iterating over samples or unpacked buffers.
|
130
|
+
#
|
131
|
+
# Parameters::
|
132
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample to begin with [optional = 0]
|
133
|
+
# * *iIdxLastSample* (_Integer_): Index of the last sample to end with [optional = @NbrSamples-1]
|
134
|
+
# * *iOptions* (<em>map<Symbol,Object></em>): Additional options. See CachedBufferReader for documentation. [optional = {}]
|
135
|
+
# * *CodeBlock*: The code called for each iteration:
|
136
|
+
# * *iInputRawBuffer* (_String_): The raw buffer
|
137
|
+
# * *iNbrSamples* (_Integer_): The number of samples in this buffer
|
138
|
+
# * *iNbrChannels* (_Integer_): The number of channels in this buffer
|
139
|
+
def each_raw_buffer(iIdxBeginSample = 0, iIdxLastSample = @NbrSamples-1, iOptions = {})
|
140
|
+
@RawReader.each_buffer(iIdxBeginSample, iIdxLastSample, iOptions) do |iBuffer, iNbrSamples|
|
141
|
+
yield(iBuffer, iNbrSamples, @Header.NbrChannels)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Iterate through the buffers in the reverse order in raw mode (strings read directly without unpacking).
|
146
|
+
# This is far more efficient than iterating over samples or unpacked buffers.
|
147
|
+
#
|
148
|
+
# Parameters::
|
149
|
+
# * *iIdxBeginSample* (_Integer_): Index of the first sample to begin with [optional = 0]
|
150
|
+
# * *iIdxLastSample* (_Integer_): Index of the last sample to end with [optional = @NbrSamples-1]
|
151
|
+
# * *iOptions* (<em>map<Symbol,Object></em>): Additional options. See CachedBufferReader for documentation. [optional = {}]
|
152
|
+
# * *CodeBlock*: The code called for each iteration:
|
153
|
+
# * *iInputRawBuffer* (_String_): The raw buffer
|
154
|
+
# * *iNbrSamples* (_Integer_): The number of samples in this buffer
|
155
|
+
# * *iNbrChannels* (_Integer_): The number of channels in this buffer
|
156
|
+
def each_reverse_raw_buffer(iIdxBeginSample = 0, iIdxLastSample = @NbrSamples-1, iOptions = {})
|
157
|
+
@RawReader.each_reverse_buffer(iIdxBeginSample, iIdxLastSample, iOptions) do |iBuffer, iNbrSamples|
|
158
|
+
yield(iBuffer, iNbrSamples, @Header.NbrChannels)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Get a sample's data
|
163
|
+
#
|
164
|
+
# Parameters::
|
165
|
+
# * *iIdxSample* (_Integer_): Index of the sample to retrieve
|
166
|
+
# * *iOptions* (<em>map<Symbol,Object></em>): Additional options [optional = {}]
|
167
|
+
# * *:reverse_buffer* (_Boolean_): Do we load the previous buffer containing this sample if needed ? [optional = false]
|
168
|
+
# Return::
|
169
|
+
# * <em>list<Integer></em>: The list of values (1 per channel), or nil in case of error
|
170
|
+
def get_sample_data(iIdxSample, iOptions = {})
|
171
|
+
rSampleData = nil
|
172
|
+
|
173
|
+
lReverseBuffer = (iOptions[:reverse_buffer] != nil) ? iOptions[:reverse_buffer] : false
|
174
|
+
if (lReverseBuffer)
|
175
|
+
@WaveReader.each_reverse_buffer(0, iIdxSample) do |iBuffer, iNbrSamples|
|
176
|
+
rSampleData = iBuffer[-@Header.NbrChannels..-1]
|
177
|
+
break
|
178
|
+
end
|
179
|
+
else
|
180
|
+
@WaveReader.each_buffer(iIdxSample) do |iBuffer, iNbrSamples|
|
181
|
+
rSampleData = iBuffer[0..@Header.NbrChannels-1]
|
182
|
+
break
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
return rSampleData
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,78 @@
|
|
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 'WSK/Model/CachedBufferReader'
|
7
|
+
|
8
|
+
module WSK
|
9
|
+
|
10
|
+
module Model
|
11
|
+
|
12
|
+
# Implement a RAW file reader using cached buffer reader.
|
13
|
+
# Buffers returned are of type String.
|
14
|
+
class RawReader < CachedBufferReader
|
15
|
+
|
16
|
+
# Buffer size.
|
17
|
+
# It is expressed in bytes.
|
18
|
+
# Integer
|
19
|
+
BUFFER_SIZE = 8388608
|
20
|
+
|
21
|
+
# Constructor
|
22
|
+
#
|
23
|
+
# Parameters::
|
24
|
+
# * *iFile* (_IO_): The file descriptor. Don't use it externally as long as it is used by this class.
|
25
|
+
# * *iFirstSampleFilePos* (_Integer_): Position in the file of the first sample
|
26
|
+
# * *iSampleSize* (_Integer_): Size of a single sample to read
|
27
|
+
# * *iNbrSamples* (_Integer_): Total number of samples
|
28
|
+
def initialize(iFile, iFirstSampleFilePos, iSampleSize, iNbrSamples)
|
29
|
+
@File, @FirstSampleFilePos, @SampleSize, @NbrSamples = iFile, iFirstSampleFilePos, iSampleSize, iNbrSamples
|
30
|
+
super()
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the number of samples read per buffer
|
34
|
+
#
|
35
|
+
# Return::
|
36
|
+
# * _Integer_: Nnumber of samples in 1 buffer
|
37
|
+
def get_nbr_samples_per_buffer
|
38
|
+
return BUFFER_SIZE/@SampleSize
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the total number of samples
|
42
|
+
#
|
43
|
+
# Return::
|
44
|
+
# * _Integer_: Total number of samples
|
45
|
+
def get_nbr_samples
|
46
|
+
return @NbrSamples
|
47
|
+
end
|
48
|
+
|
49
|
+
# Read a buffer
|
50
|
+
#
|
51
|
+
# Parameters::
|
52
|
+
# * *iIdxStartSample* (_Integer_): Index of the first sample to begin with
|
53
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample to end with
|
54
|
+
# Return::
|
55
|
+
# * _Object_: The corresponding buffer
|
56
|
+
def read_buffer(iIdxStartSample, iIdxEndSample)
|
57
|
+
@File.seek(@FirstSampleFilePos + iIdxStartSample*@SampleSize)
|
58
|
+
log_debug "Raw read samples [#{iIdxStartSample} - #{iIdxEndSample}]"
|
59
|
+
return @File.read((iIdxEndSample-iIdxStartSample+1)*@SampleSize)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Extract a sub-buffer for the given index range
|
63
|
+
#
|
64
|
+
# Parameters::
|
65
|
+
# * *iBuffer* (_Object_): The buffer to extract from
|
66
|
+
# * *iIdxStartSample* (_Integer_): Index of the first sample to begin with
|
67
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample to end with
|
68
|
+
# Return::
|
69
|
+
# * _Object_: The sub buffer
|
70
|
+
def extract_sub_buffer(iBuffer, iIdxStartSample, iIdxEndSample)
|
71
|
+
return iBuffer[iIdxStartSample*@SampleSize..(iIdxEndSample+1)*@SampleSize-1]
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,91 @@
|
|
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 'WSK/Model/CachedBufferReader'
|
7
|
+
|
8
|
+
module WSK
|
9
|
+
|
10
|
+
module Model
|
11
|
+
|
12
|
+
# Implement a Wave file reader using a raw buffer reader.
|
13
|
+
# Buffers returned are of type list<Integer>.
|
14
|
+
class WaveReader < CachedBufferReader
|
15
|
+
|
16
|
+
# Number of channel samples per buffer.
|
17
|
+
# Integer
|
18
|
+
NBR_CHANNEL_SAMPLES_PER_BUFFER = 2097152
|
19
|
+
|
20
|
+
# Constructor
|
21
|
+
#
|
22
|
+
# Parameters::
|
23
|
+
# * *iRawReader* (_CachedBufferReader_): The reader that provides raw data
|
24
|
+
# * *iHeader* (<em>WSK::Model::Header</em>): Corresponding file header
|
25
|
+
def initialize(iRawReader, iHeader)
|
26
|
+
@RawReader, @Header = iRawReader, iHeader
|
27
|
+
super()
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the number of samples read per buffer
|
31
|
+
#
|
32
|
+
# Return::
|
33
|
+
# * _Integer_: Nnumber of samples in 1 buffer
|
34
|
+
def get_nbr_samples_per_buffer
|
35
|
+
return NBR_CHANNEL_SAMPLES_PER_BUFFER/@Header.NbrChannels
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the total number of samples
|
39
|
+
#
|
40
|
+
# Return::
|
41
|
+
# * _Integer_: Total number of samples
|
42
|
+
def get_nbr_samples
|
43
|
+
return @RawReader.get_nbr_samples
|
44
|
+
end
|
45
|
+
|
46
|
+
# Read a buffer
|
47
|
+
#
|
48
|
+
# Parameters::
|
49
|
+
# * *iIdxStartSample* (_Integer_): Index of the first sample to begin with
|
50
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample to end with
|
51
|
+
# Return::
|
52
|
+
# * _Object_: The corresponding buffer
|
53
|
+
def read_buffer(iIdxStartSample, iIdxEndSample)
|
54
|
+
lRawBuffer = nil
|
55
|
+
lNbrSamplesToRead = iIdxEndSample - iIdxStartSample + 1
|
56
|
+
@RawReader.each_buffer(iIdxStartSample, iIdxEndSample) do |iBuffer, iNbrSamples|
|
57
|
+
if (lRawBuffer == nil)
|
58
|
+
if (lNbrSamplesToRead == iNbrSamples)
|
59
|
+
# We have the buffer directly. No copy.
|
60
|
+
lRawBuffer = iBuffer
|
61
|
+
else
|
62
|
+
# We will need to concatenate other buffers. Clone it.
|
63
|
+
lRawBuffer = iBuffer.clone
|
64
|
+
end
|
65
|
+
else
|
66
|
+
# Concatenate
|
67
|
+
lRawBuffer.concat(iBuffer)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
log_debug "Decode samples [#{iIdxStartSample} - #{iIdxEndSample}]"
|
71
|
+
|
72
|
+
return @Header.getDecodedSamples(lRawBuffer, lNbrSamplesToRead)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Extract a sub-buffer for the given index range
|
76
|
+
#
|
77
|
+
# Parameters::
|
78
|
+
# * *iBuffer* (_Object_): The buffer to extract from
|
79
|
+
# * *iIdxStartSample* (_Integer_): Index of the first sample to begin with
|
80
|
+
# * *iIdxEndSample* (_Integer_): Index of the last sample to end with
|
81
|
+
# Return::
|
82
|
+
# * _Object_: The sub buffer
|
83
|
+
def extract_sub_buffer(iBuffer, iIdxStartSample, iIdxEndSample)
|
84
|
+
return iBuffer[iIdxStartSample*@Header.NbrChannels..(iIdxEndSample+1)*@Header.NbrChannels-1]
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,146 @@
|
|
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 OutputInterfaces
|
9
|
+
|
10
|
+
class DirectStream
|
11
|
+
|
12
|
+
# Here we define the buffer size.
|
13
|
+
# The buffer will be used to store contiguous audio data in RAM.
|
14
|
+
# Each OutputData object will never use more than this size.
|
15
|
+
# It is expressed in bytes.
|
16
|
+
# Integer
|
17
|
+
BUFFER_SIZE = 2097152
|
18
|
+
|
19
|
+
# Initialize the plugin
|
20
|
+
#
|
21
|
+
# Parameters::
|
22
|
+
# * *oFile* (_IO_): The file descriptor. Don't use it externally as long as it is used by this class.
|
23
|
+
# * *iHeader* (<em>WSK::Model::Header</em>): Corresponding file header
|
24
|
+
# * *iNbrOutputDataSamples* (_Integer_): The number of output data samples
|
25
|
+
# Return::
|
26
|
+
# * _Exception_: An error, or nil in case of success
|
27
|
+
def initInterface(oFile, iHeader, iNbrOutputDataSamples)
|
28
|
+
rError = nil
|
29
|
+
|
30
|
+
@File, @Header, @NbrSamples = oFile, iHeader, iNbrOutputDataSamples
|
31
|
+
@NbrSamplesWritten = 0
|
32
|
+
# Size of a sample
|
33
|
+
# Integer
|
34
|
+
@SampleSize = (@Header.NbrChannels*@Header.NbrBitsPerSample)/8
|
35
|
+
# Compute the number of samples to store in the buffer
|
36
|
+
@NbrSamplesPerBuffer = BUFFER_SIZE/@SampleSize
|
37
|
+
# The position of the last written sample in the buffer
|
38
|
+
# Integer
|
39
|
+
@IdxCurrentBufferSample = 0
|
40
|
+
# The buffer itself, list of channels values
|
41
|
+
# list< Integer >
|
42
|
+
@Buffer = []
|
43
|
+
|
44
|
+
return rError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Finalize writing
|
48
|
+
#
|
49
|
+
# Return::
|
50
|
+
# * _Integer_: The number of samples written
|
51
|
+
def finalize
|
52
|
+
if (!@Buffer.empty?)
|
53
|
+
flushBuffer
|
54
|
+
end
|
55
|
+
|
56
|
+
return @NbrSamplesWritten
|
57
|
+
end
|
58
|
+
|
59
|
+
# Add a sample data
|
60
|
+
#
|
61
|
+
# Parameters::
|
62
|
+
# * *iSampleData* (<em>list<Integer></em>): The list of channel values for this sample
|
63
|
+
def pushSample(iSampleData)
|
64
|
+
# Write data in the buffer
|
65
|
+
@Buffer += iSampleData
|
66
|
+
@IdxCurrentBufferSample += 1
|
67
|
+
if (@IdxCurrentBufferSample == @NbrSamplesPerBuffer)
|
68
|
+
# We have to flush the buffer
|
69
|
+
flushBuffer
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add a buffer
|
74
|
+
#
|
75
|
+
# Parameters::
|
76
|
+
# * *iBuffer* (<em>list<Integer></em>): The list of channel values for this buffer
|
77
|
+
def pushBuffer(iBuffer)
|
78
|
+
# Write data in the current buffer
|
79
|
+
@Buffer += iBuffer
|
80
|
+
@IdxCurrentBufferSample += iBuffer.size/@Header.NbrChannels
|
81
|
+
if (@IdxCurrentBufferSample >= @NbrSamplesPerBuffer)
|
82
|
+
# We have to flush the buffer
|
83
|
+
flushBuffer
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Add a raw buffer
|
88
|
+
#
|
89
|
+
# Parameters::
|
90
|
+
# * *iRawBuffer* (_String_): The raw buffer
|
91
|
+
def pushRawBuffer(iRawBuffer)
|
92
|
+
# First, flush eventually remaining buffer to encode
|
93
|
+
if (!@Buffer.empty?)
|
94
|
+
flushBuffer
|
95
|
+
end
|
96
|
+
# Then write our raw buffer
|
97
|
+
@File.write(iRawBuffer)
|
98
|
+
updateProgress(iRawBuffer.size/@SampleSize)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Loop on a range of samples split into buffers
|
102
|
+
#
|
103
|
+
# Parameters::
|
104
|
+
# * *iIdxBeginSample* (_Integer_): The beginning sample
|
105
|
+
# * *iIdxEndSample* (_Integer_): The ending sample
|
106
|
+
# * _CodeBlock_: The code called for each buffer:
|
107
|
+
# * *iIdxBeginBufferSample* (_Integer_): The beginning of this buffer's sample
|
108
|
+
# * *iIdxEndBufferSample* (_Integer_): The ending of this buffer's sample
|
109
|
+
def each_buffer(iIdxBeginSample, iIdxEndSample)
|
110
|
+
lIdxBeginBufferSample = iIdxBeginSample
|
111
|
+
while (lIdxBeginBufferSample <= iIdxEndSample)
|
112
|
+
lIdxEndBufferSample = lIdxBeginBufferSample + @NbrSamplesPerBuffer - 1
|
113
|
+
if (lIdxEndBufferSample > iIdxEndSample)
|
114
|
+
lIdxEndBufferSample = iIdxEndSample
|
115
|
+
end
|
116
|
+
yield(lIdxBeginBufferSample, lIdxEndBufferSample)
|
117
|
+
lIdxBeginBufferSample = lIdxEndBufferSample + 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Write the buffer to the disk
|
124
|
+
def flushBuffer
|
125
|
+
# Write it
|
126
|
+
@File.write(@Header.getEncodedString(@Buffer))
|
127
|
+
updateProgress(@Buffer.size/@Header.NbrChannels)
|
128
|
+
@IdxCurrentBufferSample = 0
|
129
|
+
@Buffer = []
|
130
|
+
end
|
131
|
+
|
132
|
+
# Add a samples' number to the progression
|
133
|
+
#
|
134
|
+
# Parameters::
|
135
|
+
# * *iNbrSamples* (_Integer_): Number of samples
|
136
|
+
def updateProgress(iNbrSamples)
|
137
|
+
@NbrSamplesWritten += iNbrSamples
|
138
|
+
$stdout.write("[#{(@NbrSamplesWritten*100)/@NbrSamples}%]\015")
|
139
|
+
$stdout.flush
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,60 @@
|
|
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
|
+
# Class reading RIFF files
|
9
|
+
class RIFFReader
|
10
|
+
|
11
|
+
# Constructor
|
12
|
+
#
|
13
|
+
# Parameters::
|
14
|
+
# * *iFile* (_IO_): File to read
|
15
|
+
def initialize(iFile)
|
16
|
+
@File = iFile
|
17
|
+
end
|
18
|
+
|
19
|
+
# Position the file on the data associated to a given RIFF name
|
20
|
+
#
|
21
|
+
# Parameters::
|
22
|
+
# * *iRIFFName* (_String_): The RIFF name
|
23
|
+
# Return::
|
24
|
+
# * _Exception_: An error, or nil in case of success
|
25
|
+
# * _Integer_: The RIFF chunk size
|
26
|
+
def setFilePos(iRIFFName)
|
27
|
+
rError = nil
|
28
|
+
rSize = nil
|
29
|
+
|
30
|
+
# Loop through the names until we find ours
|
31
|
+
@File.seek(12)
|
32
|
+
lRIFFName, rSize = @File.read(8).unpack('a4V')
|
33
|
+
lCurrentPos = 20
|
34
|
+
while (lRIFFName != nil)
|
35
|
+
if (lRIFFName == iRIFFName)
|
36
|
+
# We are positioned correctly
|
37
|
+
lRIFFName = nil
|
38
|
+
log_debug "Found RIFF chunk #{iRIFFName} of size #{rSize}"
|
39
|
+
else
|
40
|
+
log_debug "Skip RIFF chunk #{lRIFFName} of size #{rSize}"
|
41
|
+
# Go to the next chunk
|
42
|
+
@File.seek(lCurrentPos + rSize)
|
43
|
+
lData = @File.read(8)
|
44
|
+
lCurrentPos += rSize + 8
|
45
|
+
if (lData == nil)
|
46
|
+
# End of the file
|
47
|
+
rError = RuntimeError.new("End of file met: no RIFF #{iRIFFName} chunk found.")
|
48
|
+
lRIFFName = nil
|
49
|
+
else
|
50
|
+
lRIFFName, rSize = lData.unpack('a4V')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
return rError, rSize
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|