WaveSwissKnife 0.2.0.20120302

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }