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,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 GenSawtooth
11
+
12
+ # Get the number of samples that will be written.
13
+ # This is called before execute, as it is needed to write the output file.
14
+ # It is possible to give a majoration: it will be padded with silence.
15
+ #
16
+ # Parameters::
17
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
18
+ # Return::
19
+ # * _Integer_: The number of samples to be written
20
+ def get_nbr_samples(iInputData)
21
+ return iInputData.Header.SampleRate
22
+ end
23
+
24
+ # Execute
25
+ #
26
+ # Parameters::
27
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
28
+ # * *oOutputData* (_Object_): The output data to fill
29
+ # Return::
30
+ # * _Exception_: An error, or nil if success
31
+ def execute(iInputData, oOutputData)
32
+ # Compute values used to create sawtooth
33
+ lMaxValue = 2**(iInputData.Header.NbrBitsPerSample-1)-1
34
+ lMinValue = -2**(iInputData.Header.NbrBitsPerSample-1)
35
+ lMiddleSample = iInputData.Header.SampleRate/2
36
+ # Create buffer
37
+ lBuffer = []
38
+ iInputData.Header.SampleRate.times do |iIdxSample|
39
+ iInputData.Header.NbrChannels.times do |iIdxChannel|
40
+ if (iIdxSample < lMiddleSample)
41
+ lBuffer << (iIdxSample*lMaxValue)/lMiddleSample
42
+ else
43
+ lBuffer << lMinValue+(lMiddleSample-iIdxSample)*lMinValue/(iInputData.Header.SampleRate-lMiddleSample)
44
+ end
45
+ end
46
+ end
47
+ # Write buffer
48
+ oOutputData.pushBuffer(lBuffer)
49
+
50
+ return nil
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ 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
+ :Frequency => [
10
+ '--frequency <Frequency>', Integer,
11
+ '<Frequency>: Frequency of the sine wave (in Hz)',
12
+ 'Specify the frequency of the generated sine wave.'
13
+ ],
14
+ :NbrSamples => [
15
+ '--nbrsamples <NbrSamples>', Integer,
16
+ '<NbrSamples>: Number of samples used to write this value',
17
+ 'Specify the number of samples during the value will be written.'
18
+ ]
19
+ }
20
+ }
@@ -0,0 +1,73 @@
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 GenSine
11
+
12
+ # Get the number of samples that will be written.
13
+ # This is called before execute, as it is needed to write the output file.
14
+ # It is possible to give a majoration: it will be padded with silence.
15
+ #
16
+ # Parameters::
17
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
18
+ # Return::
19
+ # * _Integer_: The number of samples to be written
20
+ def get_nbr_samples(iInputData)
21
+ return @NbrSamples
22
+ end
23
+
24
+ # Execute
25
+ #
26
+ # Parameters::
27
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
28
+ # * *oOutputData* (_Object_): The output data to fill
29
+ # Return::
30
+ # * _Exception_: An error, or nil if success
31
+ def execute(iInputData, oOutputData)
32
+ # Compute values used to create sawtooth
33
+ lMaxValue = 2**(iInputData.Header.NbrBitsPerSample-1)-1
34
+ # Compute the number of complete periods to put in the samples we want
35
+ lNbrSamplesPeriod = iInputData.Header.SampleRate/@Frequency
36
+ lNbrPeriods = @NbrSamples/lNbrSamplesPeriod
37
+ lBuffer = nil
38
+ if (lNbrPeriods > 0)
39
+ # Generate a buffer with a omplete period in it
40
+ lBuffer = []
41
+ lNbrSamplesPeriod.times do |iIdx|
42
+ lBuffer.concat( [(Math.sin((2*Math::PI*iIdx)/lNbrSamplesPeriod)*lMaxValue).round] * iInputData.Header.NbrChannels )
43
+ end
44
+ # Write them
45
+ lNbrPeriods.times do |iIdx|
46
+ oOutputData.pushBuffer(lBuffer)
47
+ end
48
+ end
49
+ lRemainingSamples = @NbrSamples % lNbrSamplesPeriod
50
+ if (lRemainingSamples > 0)
51
+ # Add the remaining part of the buffer
52
+ if (lBuffer == nil)
53
+ # Generate a part of the buffer
54
+ lBuffer = []
55
+ lRemainingSamples.times do |iIdx|
56
+ lBuffer.concat( [(Math.sin((2*Math::PI*iIdx)/lNbrSamplesPeriod)*lMaxValue).round] * iInputData.Header.NbrChannels )
57
+ end
58
+ # Write it
59
+ oOutputData.pushBuffer(lBuffer)
60
+ else
61
+ # Write a part of the already generated buffer
62
+ oOutputData.pushBuffer(lBuffer[0..iInputData.Header.NbrChannels*lRemainingSamples-1])
63
+ end
64
+ end
65
+
66
+ return nil
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,43 @@
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 Identity
11
+
12
+ # Get the number of samples that will be written.
13
+ # This is called before execute, as it is needed to write the output file.
14
+ # It is possible to give a majoration: it will be padded with silence.
15
+ #
16
+ # Parameters::
17
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
18
+ # Return::
19
+ # * _Integer_: The number of samples to be written
20
+ def get_nbr_samples(iInputData)
21
+ return iInputData.NbrSamples
22
+ end
23
+
24
+ # Execute
25
+ #
26
+ # Parameters::
27
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
28
+ # * *oOutputData* (_Object_): The output data to fill
29
+ # Return::
30
+ # * _Exception_: An error, or nil if success
31
+ def execute(iInputData, oOutputData)
32
+ iInputData.each_raw_buffer do |iInputRawBuffer, iNbrSamples, iNbrChannels|
33
+ oOutputData.pushRawBuffer(iInputRawBuffer)
34
+ end
35
+
36
+ return nil
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ 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
+ :MixFiles => [
10
+ '--files <FilesList>', String,
11
+ '<FilesList>: List of files to mix with the input file, with floating coefficients, separated with | (example: File1.wav|2|File2.wav|1.4|File3.wav|-1)',
12
+ 'Specify the list of files to mix along with their coefficient. The input file has the coefficient 1. The coefficient can be negative to invert the file while mixing.'
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,149 @@
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 Mix
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
+ @NbrSamples = iInputData.NbrSamples
24
+
25
+ # Decode the files list
26
+ # list< [ String, Float ] >
27
+ @LstFiles = []
28
+ lLstParams = @MixFiles.split('|')
29
+ if (lLstParams.size % 2 != 0)
30
+ raise RuntimeError, 'Invalid mix parameters. Example: File1.wav|1|File2.wav|0.4'
31
+ else
32
+ (lLstParams.size/2).times do |iIdxFile|
33
+ lFileName = lLstParams[iIdxFile*2]
34
+ lCoeff = lLstParams[iIdxFile*2+1].to_f
35
+ if (lCoeff == 0)
36
+ log_warn "File #{lFileName} has a null coefficient. It won't be part of the mix."
37
+ else
38
+ # Check if the file exists
39
+ if (File.exists?(lFileName))
40
+ # Check the file's header
41
+ lError = accessInputWaveFile(lFileName) do |iInputHeader2, iInputData2|
42
+ rSubError = nil
43
+ # Check that headers are the same
44
+ if (iInputHeader2 != iInputData.Header)
45
+ rSubError = RuntimeError.new("Mismatch headers with file #{lFileName}: First input file: #{iInputData.Header.inspect} Mix file: #{iInputHeader2.inspect}")
46
+ end
47
+ # OK, keep this file
48
+ @LstFiles << [ lFileName, lCoeff ]
49
+ if (iInputData2.NbrSamples > @NbrSamples)
50
+ @NbrSamples = iInputData2.NbrSamples
51
+ end
52
+ next rSubError
53
+ end
54
+ if (lError != nil)
55
+ raise lError
56
+ end
57
+ else
58
+ raise RuntimeError, "Missing file: #{lFileName}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ return @NbrSamples
65
+ end
66
+
67
+ # Execute
68
+ #
69
+ # Parameters::
70
+ # * *iInputData* (<em>WSK::Model::InputData</em>): The input data
71
+ # * *oOutputData* (_Object_): The output data to fill
72
+ # Return::
73
+ # * _Exception_: An error, or nil if success
74
+ def execute(iInputData, oOutputData)
75
+ rError = nil
76
+
77
+ # Store the list of opened files, and initialize it with the input data (first file)
78
+ # list< [ IO, InputData, Coeff, Buffer, NbrSamplesInBuffer ] >
79
+ lLstOpenedFiles = [ [ nil, iInputData, 1.0, nil, nil ] ]
80
+ @LstFiles.each do |iFileInfo|
81
+ iFileName, iCoeff = iFileInfo
82
+ lFileHandle = File.open(iFileName, 'rb')
83
+ rError, lHeader, lInputData = getWaveFileAccesses(lFileHandle)
84
+ if (rError == nil)
85
+ lLstOpenedFiles << [ lFileHandle, lInputData, iCoeff, nil, nil ]
86
+ else
87
+ break
88
+ end
89
+ end
90
+ if (rError == nil)
91
+ require 'WSK/ArithmUtils/ArithmUtils'
92
+ lArithmUtils = WSK::ArithmUtils::ArithmUtils.new
93
+ # Loop until we meet the maximal number of samples
94
+ # !!! We assume that buffers have the same size when read
95
+ # Initialize buffers
96
+ lLstOpenedFiles.each do |ioFileInfo|
97
+ lFileHandle, lInputData, lCoeff, lBuffer = ioFileInfo
98
+ lInputData.each_raw_buffer do |iRawBuffer, iNbrSamples, iNbrChannels|
99
+ break
100
+ end
101
+ lRawBuffer, lNbrSamples, lNbrChannels2 = lInputData.get_current_raw_buffer
102
+ ioFileInfo[3] = lRawBuffer
103
+ ioFileInfo[4] = lNbrSamples
104
+ end
105
+ # Sort the list based on the number of samples of each file.
106
+ # This is a prerequisite of the C function mixing.
107
+ lLstOpenedFiles.sort! do |iOF1, iOF2|
108
+ next (iOF2[1].NbrSamples <=> iOF1[1].NbrSamples)
109
+ end
110
+ lLstRemainingOpenedFiles = lLstOpenedFiles.clone
111
+ lNbrSamplesProcessed = 0
112
+ while (!lLstRemainingOpenedFiles.empty?)
113
+ # Mix all buffers
114
+ lMixRawBuffer, lNbrSamplesWritten = lArithmUtils.mixBuffers(lLstRemainingOpenedFiles, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels)
115
+ # Remove the ones that don't have data anymore
116
+ lLstRemainingOpenedFiles.delete_if do |ioFileInfo|
117
+ lFileHandle, lInputData, lCoeff, lRawBuffer = ioFileInfo
118
+ rToBeDeleted = false
119
+ # Set the next buffer of this file
120
+ if (lNbrSamplesProcessed + lNbrSamplesWritten >= lInputData.NbrSamples)
121
+ # Close the handle if it is not the main input
122
+ if (lFileHandle != nil)
123
+ lFileHandle.close
124
+ end
125
+ rToBeDeleted = true
126
+ else
127
+ # Read next Buffer
128
+ lInputData.each_raw_buffer(lNbrSamplesProcessed + lNbrSamplesWritten) do |iRawBuffer, iNbrSamples, iNbrChannels|
129
+ break
130
+ end
131
+ lRawBuffer, lNbrSamples, lNbrChannels2 = lInputData.get_current_raw_buffer
132
+ ioFileInfo[3] = lRawBuffer
133
+ ioFileInfo[4] = lNbrSamples
134
+ end
135
+ next rToBeDeleted
136
+ end
137
+ oOutputData.pushRawBuffer(lMixRawBuffer)
138
+ lNbrSamplesProcessed += lNbrSamplesWritten
139
+ end
140
+ end
141
+
142
+ return rError
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ 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
+ :Coeff => [
10
+ '--coeff <Coeff>', String,
11
+ '<Coeff>: Coefficient to apply in the form X/Y (ie. 4/3) or in db (ie. -3db)',
12
+ 'Specify the multiplying coefficient'
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,73 @@
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 Multiply
11
+
12
+ include WSK::Maps
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
+ rError = nil
35
+
36
+ lCoeff = nil
37
+ lMatch = @Coeff.match(/^(.*)db$/)
38
+ if (lMatch == nil)
39
+ lMatch = @Coeff.match(/^(\d*)\/(\d*)$/)
40
+ if (lMatch == nil)
41
+ log_err "Incorrect coefficient: #{@Coeff} is not in the form X/Y or in the form Xdb"
42
+ rError = RuntimeError.new("Incorrect coefficient: #{@Coeff} is not in the form X/Y or in the form Xdb")
43
+ else
44
+ lNum, lDenom = lMatch[1..2].map { |iStrValue| iStrValue.to_i }
45
+ lCoeff = Rational(lNum, lDenom)
46
+ end
47
+ else
48
+ lCoeff = 2**(lMatch[1].to_f/6)
49
+ end
50
+
51
+ if (rError == nil)
52
+ lMaxValue = 2**(iInputData.Header.NbrBitsPerSample-1) - 1
53
+ lMinValue = -2**(iInputData.Header.NbrBitsPerSample-1)
54
+ lFunction = {
55
+ :FunctionType => WSK::Functions::FCTTYPE_PIECEWISE_LINEAR,
56
+ :MinValue => lMinValue,
57
+ :MaxValue => lMaxValue,
58
+ :Points => {
59
+ lMinValue => (lMinValue*lCoeff).to_i,
60
+ lMaxValue => (lMaxValue*lCoeff).to_i
61
+ }
62
+ }
63
+ apply_map_functions(iInputData, oOutputData, [lFunction]*iInputData.Header.NbrChannels)
64
+ end
65
+
66
+ return rError
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,35 @@
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
+ :SilenceThreshold => [
10
+ '--silencethreshold <SilenceThreshold>', String,
11
+ '<SilenceThreshold>: Threshold to use to identify silent parts [default = 0]. It is possible to specify several values, for each channel, sperated with | (ie. 34|35). It is also possible to specify a range instead of a threshold with , (ie. -128,126 or -128,126|-127,132)',
12
+ 'Specify the silence threshold'
13
+ ],
14
+ :Attack => [
15
+ '--attack <AttackDuration>', String,
16
+ '<AttackDuration>: Attack duration in samples or in float seconds (ie. 234 or 25.3s).',
17
+ 'Specify the attack duration after the silence. This will fadein the noise before the non-silent part.'
18
+ ],
19
+ :Release => [
20
+ '--release <ReleaseDuration>', String,
21
+ '<ReleaseDuration>: Release duration in samples or in float seconds (ie. 234 or 25.3s).',
22
+ 'Specify the release duration before the silence. This will fadeout the noise after the non-silent part.'
23
+ ],
24
+ :SilenceMin => [
25
+ '--silencemin <SilenceDuration>', String,
26
+ '<SilenceDuration>: Silence duration in samples or in float seconds (ie. 234 or 25.3s).',
27
+ 'Specify the minimum duration a silent part must have to be interpreted as a silence.'
28
+ ],
29
+ :NoiseFFTFileName => [
30
+ '--noisefft <FFTFile>', String,
31
+ '<FFTFile>: File containing the FFT profile of the reference noise.',
32
+ 'This is used to compare potential noise profile with the real noise profile.'
33
+ ]
34
+ }
35
+ }
@@ -0,0 +1,129 @@
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 NoiseGate
11
+
12
+ include WSK::Common
13
+ include WSK::FFT
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
+ lSilenceThresholds = readThresholds(@SilenceThreshold, iInputData.Header.NbrChannels)
36
+ lAttackDuration = readDuration(@Attack, iInputData.Header.SampleRate)
37
+ lReleaseDuration = readDuration(@Release, iInputData.Header.SampleRate)
38
+ lSilenceDuration = readDuration(@SilenceMin, iInputData.Header.SampleRate)
39
+ lNoiseFFTMaxDistance, lNoiseFFTProfile = readFFTProfile(@NoiseFFTFileName)
40
+ # Create a map of the non silent parts
41
+ # list< [ Integer, Integer ] >
42
+ # list< [ IdxBeginNonSilentSample, IdxEndNonSilentSample ] >
43
+ lNonSilentParts = []
44
+ lIdxSample = 0
45
+ while (lIdxSample != nil)
46
+ lIdxNextSilence, lSilenceLength, lIdxNextBeyondThresholds = getNextSilentSample(iInputData, lIdxSample, lSilenceThresholds, lSilenceDuration, lNoiseFFTProfile, lNoiseFFTMaxDistance, false)
47
+ if (lIdxNextSilence == nil)
48
+ lNonSilentParts << [lIdxSample, iInputData.NbrSamples-1]
49
+ else
50
+ lNonSilentParts << [lIdxSample, lIdxNextSilence-1]
51
+ end
52
+ lIdxSample = lIdxNextBeyondThresholds
53
+ end
54
+ lStrNonSilentParts = lNonSilentParts.map { |iNonSilentInfo| "[#{iNonSilentInfo[0]/iInputData.Header.SampleRate}s - #{iNonSilentInfo[1]/iInputData.Header.SampleRate}s]" }
55
+ log_info "#{lNonSilentParts.size} non silent parts: #{lStrNonSilentParts[0..9].join(', ')}"
56
+ lStrDbgNonSilentParts = lNonSilentParts.map { |iNonSilentInfo| "[#{iNonSilentInfo[0]} - #{iNonSilentInfo[1]}]" }
57
+ log_debug "#{lNonSilentParts.size} non silent parts: #{lStrDbgNonSilentParts[0..9].join(', ')}"
58
+ # Now we write the non-silent parts, spaced with nulled parts, with fadeins and fadeouts around.
59
+ lNextSampleToWrite = 0
60
+ lNonSilentParts.each do |iNonSilentInfo|
61
+ iIdxBegin, iIdxEnd = iNonSilentInfo
62
+ # Compute the fadein buffer
63
+ lIdxBeginFadeIn = iIdxBegin - lAttackDuration
64
+ if (lIdxBeginFadeIn < 0)
65
+ lIdxBeginFadeIn = 0
66
+ end
67
+ # Write a blank buffer if needed
68
+ if (lIdxBeginFadeIn > lNextSampleToWrite)
69
+ log_debug "Write #{lIdxBeginFadeIn - lNextSampleToWrite} samples of silence"
70
+ oOutputData.pushRawBuffer("\000" * (((lIdxBeginFadeIn - lNextSampleToWrite)*iInputData.Header.NbrChannels*iInputData.Header.NbrBitsPerSample)/8))
71
+ end
72
+ lFadeInSize = iIdxBegin-lIdxBeginFadeIn
73
+ if (lFadeInSize > 0)
74
+ lBuffer = []
75
+ lIdxFadeSample = 0
76
+ iInputData.each(lIdxBeginFadeIn) do |iChannelValues|
77
+ if (lIdxFadeSample == lFadeInSize)
78
+ break
79
+ end
80
+ lBuffer.concat(iChannelValues.map { |iValue| (iValue*lIdxFadeSample)/lFadeInSize })
81
+ lIdxFadeSample += 1
82
+ end
83
+ log_debug "Write #{lBuffer.size/iInputData.Header.NbrChannels} samples of fadein."
84
+ oOutputData.pushBuffer(lBuffer)
85
+ else
86
+ log_debug 'Ignore empty fadein.'
87
+ end
88
+ # Write the file
89
+ log_debug "Write #{iIdxEnd-iIdxBegin+1} samples of audio."
90
+ iInputData.each_raw_buffer(iIdxBegin, iIdxEnd) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
91
+ oOutputData.pushRawBuffer(iInputRawBuffer)
92
+ end
93
+ # Write the fadeout buffer
94
+ lIdxEndFadeOut = iIdxEnd + lReleaseDuration
95
+ if (lIdxEndFadeOut >= iInputData.NbrSamples)
96
+ lIdxEndFadeOut = iInputData.NbrSamples - 1
97
+ end
98
+ lFadeOutSize = lIdxEndFadeOut-iIdxEnd
99
+ if (lFadeOutSize > 0)
100
+ lBuffer = []
101
+ lIdxFadeSample = 0
102
+ iInputData.each(iIdxEnd+1) do |iChannelValues|
103
+ if (lIdxFadeSample == lFadeOutSize)
104
+ break
105
+ end
106
+ lBuffer.concat(iChannelValues.map { |iValue| (iValue*(lFadeOutSize-lIdxFadeSample))/lFadeOutSize })
107
+ lIdxFadeSample += 1
108
+ end
109
+ log_debug "Write #{lBuffer.size/iInputData.Header.NbrChannels} samples of fadeout."
110
+ oOutputData.pushBuffer(lBuffer)
111
+ else
112
+ log_debug 'Ignore empty fadeout.'
113
+ end
114
+ lNextSampleToWrite = lIdxEndFadeOut + 1
115
+ end
116
+ # If there is remaining silence, write it
117
+ if (lNextSampleToWrite < iInputData.NbrSamples)
118
+ log_debug "Write #{iInputData.NbrSamples - lNextSampleToWrite} samples of last silence"
119
+ oOutputData.pushRawBuffer("\000" * (((iInputData.NbrSamples - lNextSampleToWrite)*iInputData.Header.NbrChannels*iInputData.Header.NbrBitsPerSample)/8))
120
+ end
121
+
122
+ return nil
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
129
+ 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
+ :BeginSilenceLength => [
10
+ '--begin <SilenceLength>', String,
11
+ '<SilenceLength>: Length of silence to insert in samples or in float seconds (ie. 234 or 25.3s)',
12
+ 'Specify the number of samples to insert at the beginning of the audio data'
13
+ ],
14
+ :EndSilenceLength => [
15
+ '--end <SilenceLength>', String,
16
+ '<SilenceLength>: Length of silence to insert in samples or in float seconds (ie. 234 or 25.3s)',
17
+ 'Specify the number of samples to insert at the end of the audio data'
18
+ ]
19
+ }
20
+ }