MusicMaster 0.0.1.20101110

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,197 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ require 'fileutils'
7
+ require 'rational'
8
+
9
+ module MusicMaster
10
+
11
+ # Call WSK
12
+ #
13
+ # Parameters:
14
+ # * *iInputFile* (_String_): The input file
15
+ # * *iOutputFile* (_String_): The output file
16
+ # * *iAction* (_String_): The action
17
+ # * *iParams* (_String_): Action parameters [optional = '']
18
+ def self.wsk(iInputFile, iOutputFile, iAction, iParams = '')
19
+ logInfo ''
20
+ logInfo "========== Processing #{iInputFile} ==#{iAction}==> #{iOutputFile} | #{iParams} ..."
21
+ system("#{$MusicMasterConf[:WSKCmdLine]} --input \"#{iInputFile}\" --output \"#{iOutputFile}\" --action #{iAction} -- #{iParams}")
22
+ lErrorCode = $?.exitstatus
23
+ if (lErrorCode == 0)
24
+ logInfo "========== Processing #{iInputFile} ==#{iAction}==> #{iOutputFile} | #{iParams} ... OK"
25
+ else
26
+ logErr "========== Processing #{iInputFile} ==#{iAction}==> #{iOutputFile} | #{iParams} ... ERROR #{lErrorCode}"
27
+ raise RuntimeError, "Processing #{iInputFile} ==#{iAction}==> #{iOutputFile} | #{iParams} ... ERROR #{lErrorCode}"
28
+ end
29
+ logInfo ''
30
+ end
31
+
32
+ # Parse plugins
33
+ def self.parsePlugins
34
+ require 'rUtilAnts/Plugins'
35
+ RUtilAnts::Plugins::initializePlugins
36
+ lLibDir = File.expand_path(File.dirname(__FILE__))
37
+ parsePluginsFromDir('Processes', "#{lLibDir}/Processes", 'MusicMaster::Processes')
38
+ end
39
+
40
+ # Apply given record effects on a Wave file.
41
+ # It modifies the given Wave file.
42
+ # It saves original and intermediate Wave files before modifications.
43
+ #
44
+ # Parameters:
45
+ # * *iEffects* (<em>list<map<Symbol,Object>></em>): List of effects to apply
46
+ # * *iFileName* (_String_): File name to apply effects to
47
+ # * *iDir* (_String_): The directory where temporary files are stored
48
+ def self.applyProcesses(iEffects, iFileName, iDir)
49
+ lFileNameNoExt = File.basename(iFileName[0..-5])
50
+ iEffects.each_with_index do |iEffectInfo, iIdxEffect|
51
+ begin
52
+ accessPlugin('Processes', iEffectInfo[:Name]) do |ioActionPlugin|
53
+ # Save the file before using the plugin
54
+ lSave = true
55
+ lSaveFileName = "#{iDir}/#{lFileNameNoExt}.Before_#{iIdxEffect}_#{iEffectInfo[:Name]}.wav"
56
+ if (File.exists?(lSaveFileName))
57
+ puts "!!! File #{lSaveFileName} already exists. Overwrite and apply effect ? [y='yes']"
58
+ lSave = ($stdin.gets.chomp == 'y')
59
+ end
60
+ if (lSave)
61
+ logInfo "Saving file #{iFileName} to #{lSaveFileName} ..."
62
+ FileUtils::mv(iFileName, lSaveFileName)
63
+ logInfo "===== Apply Effect #{iEffectInfo[:Name]} to #{iFileName} ====="
64
+ ioActionPlugin.execute(lSaveFileName, iFileName, iDir, iEffectInfo.clone.delete_if{|iKey, iValue| next (iKey == :Name)})
65
+ end
66
+ end
67
+ rescue Exception
68
+ logErr "An error occurred while processing #{iFileName} with process #{iEffectInfo[:Name]}: #{$!}."
69
+ raise
70
+ end
71
+ end
72
+ end
73
+
74
+ # Read record configuration.
75
+ # Perform basic checks on it.
76
+ #
77
+ # Parameters:
78
+ # * *iConfFile* (_String_): Configuration file
79
+ # Return:
80
+ # * _Exception_: Error, or nil in case of success
81
+ # * <em>map<Symbol,Object></em>: The configuration
82
+ def self.readRecordConf(iConfFile)
83
+ rError = nil
84
+ rConf = nil
85
+
86
+ if (!File.exists?(iConfFile))
87
+ rError = RuntimeError.new("Missing configuration file: #{iConfFile}")
88
+ else
89
+ File.open(iConfFile, 'r') do |iFile|
90
+ rConf = eval(iFile.read)
91
+ end
92
+ # Check that all tracks are assigned somewhere, just once
93
+ lLstTracks = nil
94
+ if (rConf[:Patches] != nil)
95
+ lLstTracks = rConf[:Patches].keys.clone
96
+ else
97
+ lLstTracks = []
98
+ end
99
+ if (rConf[:Performs] != nil)
100
+ rConf[:Performs].each do |iLstPerform|
101
+ lLstTracks.concat(iLstPerform)
102
+ end
103
+ end
104
+ lAssignedTracks = {}
105
+ lLstTracks.each do |iIdxTrack|
106
+ if (lAssignedTracks.has_key?(iIdxTrack))
107
+ rError = RuntimeError.new("Track #{iIdxTrack} is recorded twice.")
108
+ break
109
+ else
110
+ lAssignedTracks[iIdxTrack] = nil
111
+ end
112
+ end
113
+ if (rError == nil)
114
+ if (rConf[:Patches] != nil)
115
+ rConf[:Patches].each do |iIdxTrack, iTrackConf|
116
+ if ((iTrackConf.has_key?(:VolCorrection)) and
117
+ (iTrackConf.has_key?(:VolCompareCuts)))
118
+ rError = RuntimeError.new("Patch track #{iIdxTrack} has both :VolCorrection and :VolCompareCuts values defined.")
119
+ elsif ((!iTrackConf.has_key?(:VolCorrection)) and
120
+ (!iTrackConf.has_key?(:VolCompareCuts)))
121
+ rError = RuntimeError.new("Patch track #{iIdxTrack} has neither :VolCorrection nor :VolCompareCuts values defined.")
122
+ end
123
+ end
124
+ end
125
+ if (rError == nil)
126
+ lAssignedTracks.size.times do |iIdxTrack|
127
+ if (!lAssignedTracks.has_key?(iIdxTrack+1))
128
+ logWarn "Track #{iIdxTrack+1} is never recorded."
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ return rError, rConf
136
+ end
137
+
138
+ # Convert a Wave file to another
139
+ #
140
+ # Parameters:
141
+ # * *iSrcFile* (_String_): Source WAVE file
142
+ # * *iDstFile* (_String_): Destination WAVE file
143
+ # * *iParams* (<em>map<Symbol,Object></em>): The parameters:
144
+ # ** *:SampleRate* (_Integer_): The new sample rate in Hz
145
+ # ** *:BitDepth* (_Integer_): The new bit depth (only for Wave) [optional = nil]
146
+ # ** *:Dither* (_Boolean_): Do we apply dither (only for Wave) ? [optional = false]
147
+ # ** *:BitRate* (_Integer_): Bit rate in kbps (only for MP3) [optional = 320]
148
+ # ** *:FileFormat* (_Symbol_); File format. Here are the possible values: [optional = :Wave]
149
+ # *** *:Wave*: Uncompressed PCM Wave file
150
+ # *** *:MP3*: MP3 file
151
+ def self.src(iSrcFile, iDstFile, iParams)
152
+ if ((iParams[:FileFormat] != nil) and
153
+ (iParams[:FileFormat] == :MP3))
154
+ # MP3 conversion
155
+ lTranslatedParams = []
156
+ iParams.each do |iParam, iValue|
157
+ case iParam
158
+ when :SampleRate
159
+ lTranslatedParams << "Sample rate: #{iValue} Hz"
160
+ when :BitRate
161
+ lTranslatedParams << "Bit rate: #{iValue} kbps"
162
+ when :FileFormat
163
+ # Nothing to do
164
+ else
165
+ logErr "Unknown MP3 parameter: #{iParam} (value #{iValue.inspect}). Ignoring it."
166
+ end
167
+ end
168
+ puts "Convert file #{iSrcFile} into file #{iDstFile} in MP3 format with following parameters: #{lTranslatedParams.join(', ')}"
169
+ puts 'Press Enter when done.'
170
+ $stdin.gets
171
+ else
172
+ # Wave conversion
173
+ lTranslatedParams = [ '--profile standard', '--twopass' ]
174
+ iParams.each do |iParam, iValue|
175
+ case iParam
176
+ when :SampleRate
177
+ lTranslatedParams << "--rate #{iValue}"
178
+ when :BitDepth
179
+ lTranslatedParams << "--bits #{iValue}"
180
+ when :Dither
181
+ if (iValue == true)
182
+ lTranslatedParams << '--dither 4'
183
+ end
184
+ when :FileFormat
185
+ # Nothing to do
186
+ else
187
+ logErr "Unknown Wave parameter: #{iParam} (value #{iValue.inspect}). Ignoring it."
188
+ end
189
+ end
190
+ FileUtils::mkdir_p(File.dirname(iDstFile))
191
+ lCmd = "#{$MusicMasterConf[:SRCCmdLine]} #{lTranslatedParams.join(' ')} \"#{iSrcFile}\" \"#{iDstFile}\""
192
+ logInfo "=> #{lCmd}"
193
+ system(lCmd)
194
+ end
195
+ end
196
+
197
+ end
@@ -0,0 +1,56 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ # This file is required by MusicMaster binaries to load the MusicMaster configuration.
7
+ # This sets the $MusicMasterConf object that can then be accessed from anywhere.
8
+
9
+ module MusicMaster
10
+
11
+ def self.loadConf
12
+ $MusicMasterConf = nil
13
+ lConfSource = nil
14
+ # 1. Find from the environment
15
+ lConfigFileName = ENV['MUSICMASTER_CONF_PATH']
16
+ if (lConfigFileName == nil)
17
+ # 2. Find from the MusicMaster directory
18
+ lConfigFileName = "#{File.dirname(__FILE__)}/musicmaster.conf.rb"
19
+ if (File.exists?(lConfigFileName))
20
+ lConfSource = 'MusicMaster package local libraries'
21
+ else
22
+ # 3. Find from the current directory
23
+ lConfigFileName = "musicmaster.conf.rb"
24
+ if (File.exists?(lConfigFileName))
25
+ lConfSource = 'current directory'
26
+ else
27
+ # 4. Failure
28
+ end
29
+ end
30
+ else
31
+ lConfSource = 'MUSICMASTER_CONF_PATH environment variable'
32
+ end
33
+
34
+ # Check the configuration
35
+ if (lConfSource == nil)
36
+ logErr "No MusicMaster configuration file could be found. You can set it by setting MUSICMASTER_CONF_PATH environment variable, or create a musicmaster.conf.rb file either in #{File.dirname(__FILE__)} or the current directory."
37
+ else
38
+ if (File.exists?(lConfigFileName))
39
+ File.open(lConfigFileName, 'r') do |iFile|
40
+ begin
41
+ $MusicMasterConf = eval(iFile.read)
42
+ rescue Exception
43
+ logErr "Invalid configuration file #{lConfigFileName} specified in #{lConfSource}: #{$!}"
44
+ $MusicMasterConf = nil
45
+ end
46
+ end
47
+ else
48
+ logErr "Missing file #{lConfigFileName}, specified in #{lConfSource}"
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ MusicMaster::loadConf
@@ -0,0 +1,27 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module MusicMaster
7
+
8
+ module Processes
9
+
10
+ class AddSilence
11
+
12
+ # Execute the process
13
+ #
14
+ # Parameters:
15
+ # * *iInputFileName* (_String_): File name we want to apply effects to
16
+ # * *iOutputFileName* (_String_): File name to write
17
+ # * *iTempDir* (_String_): Temporary directory that can be used
18
+ # * *iParams* (<em>map<Symbol,Object></em>): Parameters
19
+ def execute(iInputFileName, iOutputFileName, iTempDir, iParams)
20
+ MusicMaster::wsk(iInputFileName, iOutputFileName, 'SilenceInserter', "--begin \"#{iParams[:Begin]}\" --end \"#{iParams[:End]}\"")
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,46 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ require 'WSK/Common'
7
+
8
+ module MusicMaster
9
+
10
+ module Processes
11
+
12
+ class ApplyVolumeFct
13
+
14
+ # Parameters of this process:
15
+ # * *:Function* (<em>map<Symbol,Object></em>): The function definition
16
+ # * *:Begin* (_String_): Position to apply volume transformation from. Can be specified as a sample number or a float seconds (ie. 12.3s).
17
+ # * *:End* (_String_): Position to apply volume transformation to. Can be specified as a sample number or a float seconds (ie. 12.3s). -1 means to the end of file.
18
+ # * *:DBUnits* (_Boolean_): Are the units of the function in DB scale ? (else they are in a ratio scale).
19
+
20
+ include WSK::Common
21
+
22
+ # Execute the process
23
+ #
24
+ # Parameters:
25
+ # * *iInputFileName* (_String_): File name we want to apply effects to
26
+ # * *iOutputFileName* (_String_): File name to write
27
+ # * *iTempDir* (_String_): Temporary directory that can be used
28
+ # * *iParams* (<em>map<Symbol,Object></em>): Parameters
29
+ def execute(iInputFileName, iOutputFileName, iTempDir, iParams)
30
+ # Create the file that will store the Function for WSK
31
+ lFunctionFile = "#{iTempDir}/#{File.basename(iInputFileName)[0..-5]}.fct.rb"
32
+ lFunction = WSK::Functions::Function.new
33
+ lFunction.set(iParams[:Function])
34
+ lFunction.writeToFile(lFunctionFile)
35
+ lStrUnitDB = '0'
36
+ if (iParams[:DBUnits])
37
+ lStrUnitDB = '1'
38
+ end
39
+ MusicMaster::wsk(iInputFileName, iOutputFileName, 'ApplyVolumeFct', "--function \"#{lFunctionFile}\" --begin \"#{iParams[:Begin]}\" --end \"#{iParams[:End]}\" --unitdb #{lStrUnitDB}")
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,246 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ require 'WSK/Common'
7
+
8
+ module MusicMaster
9
+
10
+ module Processes
11
+
12
+ class Compressor
13
+
14
+ # Parameters of this process:
15
+ # * *:DBUnits* (_Boolean_): Are units in DB format ? [optional = false]
16
+ # * *:Threshold* (_Float_): The threshold below which there is no compression (in DB if :DBUnit is true, else in a [0..1] scale)
17
+ # * *:Ratio* (_Float_): Compression ratio to apply above threshold.
18
+ # * *:AttackDuration* (_String_): The attack duration (either in seconds or in samples)
19
+ # * *:AttackDamping* (_String_): The attack damping value in the duration previously defined (in DB if :DBUnit is true, else in a [0..1] scale). The compressor will never attack more than :AttackDamping values during a duration of :AttackDuration.
20
+ # * *:AttackLookAhead* (_Boolean_): Is the attack to be forecast before it happens ?
21
+ # * *:ReleaseDuration* (_String_): The release duration (either in seconds or in samples)
22
+ # * *:ReleaseDamping* (_String_): The release damping value in the duration previously defined (in DB if :DBUnit is true, else in a [0..1] scale). The compressor will never release more than :ReleaseDamping values during a duration of :ReleaseDuration.
23
+ # * *:ReleaseLookAhead* (_Boolean_): Is the attack to be forecast before it happens ?
24
+ # * *:MinChangeDuration* (_String_): The minimal duration a change in volume should have (either in seconds or in samples)
25
+ # * *:RMSRatio* (_Float_): Ratio of RMS vs Peak level measurement used when profiling Wave files volumes. 0.0 = Use only Peak level. 1.0 = Use only RMS level. Other values in-between will produce a mix of both.
26
+
27
+ include WSK::Common
28
+
29
+ # -Infinity
30
+ MINUS_INFINITY = -1.0/0.0
31
+
32
+ # Execute the process
33
+ #
34
+ # Parameters:
35
+ # * *iInputFileName* (_String_): File name we want to apply effects to
36
+ # * *iOutputFileName* (_String_): File name to write
37
+ # * *iTempDir* (_String_): Temporary directory that can be used
38
+ # * *iParams* (<em>map<Symbol,Object></em>): Parameters
39
+ def execute(iInputFileName, iOutputFileName, iTempDir, iParams)
40
+ # Check parameters
41
+ lDBUnits = iParams[:DBUnits]
42
+ if (lDBUnits == nil)
43
+ lDBUnits = false
44
+ end
45
+ lParamsOK = true
46
+ if (lDBUnits)
47
+ # Threshold must be < 0
48
+ if (iParams[:Threshold] >= 0)
49
+ logErr "Threshold (#{iParams[:Threshold]}db) has to be < 0db"
50
+ lParamsOK = false
51
+ end
52
+ else
53
+ # Threshold must be < 1
54
+ if (iParams[:Threshold] >= 1)
55
+ logErr "Threshold (#{iParams[:Threshold]}) has to be < 1"
56
+ lParamsOK = false
57
+ end
58
+ # Threshold must be > 0
59
+ if (iParams[:Threshold] <= 0)
60
+ logErr "Threshold (#{iParams[:Threshold]}) has to be > 0"
61
+ lParamsOK = false
62
+ end
63
+ end
64
+ # Ratio must be > 1
65
+ if (iParams[:Ratio] <= 1)
66
+ logErr "Ratio (#{iParams[:Ratio]}) has to be > 1"
67
+ lParamsOK = false
68
+ end
69
+ if (lParamsOK)
70
+ # Get the volume profile of the Wave file
71
+ lTempVolProfileFile = "#{iTempDir}/#{File.basename(iInputFileName)[0..-5]}.ProfileFct.rb"
72
+ if (File.exists?(lTempVolProfileFile))
73
+ logWarn "File #{lTempVolProfileFile} already exists. Will not overwrite it."
74
+ else
75
+ lTempWaveFile = "#{iTempDir}/Dummy.wav"
76
+ # Get the volume profile
77
+ MusicMaster::wsk(iInputFileName, lTempWaveFile, 'VolumeProfile', "--function \"#{lTempVolProfileFile}\" --begin 0 --end -1 --interval \"#{$MusicMasterConf[:Compressor][:Interval]}\" --rmsratio #{iParams[:RMSRatio]}")
78
+ File::unlink(lTempWaveFile)
79
+ end
80
+
81
+ # Get the file header for various measures later
82
+ lHeader = nil
83
+ File.open(iInputFileName, 'rb') do |iFile|
84
+ lError, lHeader = readHeader(iFile)
85
+ if (lError != nil)
86
+ logErr "An error occurred while reading header: #{lError}"
87
+ end
88
+ end
89
+
90
+ # Create the Compressor's function based on the parameters
91
+ # Minimal value represented in DB.
92
+ # This value will be used to replace -Infinity
93
+ lMinimalDBValue = nil
94
+ lCompressorFunction = WSK::Functions::Function.new
95
+ lBDThreshold = Rational(iParams[:Threshold])
96
+ if (lDBUnits)
97
+ # The minimal DB value is the smallest ratio possible for RMS values of this file (1/2^(BPS-1)) converted in DB and minus 1 to not mix it with the ratio 1/2^(BPS-1)
98
+ lMinimalDBValue = lCompressorFunction.valueVal2db(Rational(1), Rational(2)**(lHeader.NbrBitsPerSample-1)) - 1
99
+ lCompressorFunction.set( {
100
+ :FunctionType => WSK::Functions::FCTTYPE_PIECEWISE_LINEAR,
101
+ :Points => [
102
+ [lMinimalDBValue, lMinimalDBValue],
103
+ [lBDThreshold, lBDThreshold],
104
+ [0, lBDThreshold - lBDThreshold/Rational(iParams[:Ratio]) ]
105
+ ]
106
+ } )
107
+ else
108
+ lCompressorFunction.set( {
109
+ :FunctionType => WSK::Functions::FCTTYPE_PIECEWISE_LINEAR,
110
+ :Points => [
111
+ [0, 0],
112
+ [lBDThreshold, lBDThreshold],
113
+ [1, lBDThreshold + (1-lBDThreshold)/Rational(iParams[:Ratio]) ]
114
+ ]
115
+ } )
116
+ end
117
+ logInfo "Compressor transfer function: #{lCompressorFunction.functionData[:Points].map{ |p| next [ sprintf('%.2f', p[0]), sprintf('%.2f', p[1]) ] }.inspect}"
118
+
119
+ # Compute the volume transformation function based on the profile function and the Compressor's parameters
120
+ lTempVolTransformFile = "#{iTempDir}/#{File.basename(iInputFileName)[0..-5]}.VolumeFct.rb"
121
+ if (File.exists?(lTempVolTransformFile))
122
+ logWarn "File #{lTempVolTransformFile} already exists. Will not overwrite it."
123
+ else
124
+ # Read the Profile function
125
+ logInfo 'Create volume profile function ...'
126
+ lProfileFunction = WSK::Functions::Function.new
127
+ lProfileFunction.readFromFile(lTempVolProfileFile)
128
+ if (lDBUnits)
129
+ # Convert the Profile function in DB units
130
+ lProfileFunction.convertToDB(Rational(1))
131
+ # Replace -Infinity with lMinimalDBValue
132
+ lProfileFunction.functionData[:Points].each do |ioPoint|
133
+ if (ioPoint[1] == MINUS_INFINITY)
134
+ ioPoint[1] = lMinimalDBValue
135
+ end
136
+ end
137
+ end
138
+
139
+ #dumpDebugFct(iInputFileName, lProfileFunction, 'ProfileDB', lDBUnits, iTempDir)
140
+
141
+ # Clone the profile function before applying the map
142
+ lNewProfileFunction = WSK::Functions::Function.new
143
+ lNewProfileFunction.set(lProfileFunction.functionData.clone)
144
+
145
+ # Transform the Profile function with the Compressor function
146
+ logInfo 'Apply compressor transfer function ...'
147
+ lNewProfileFunction.applyMapFunction(lCompressorFunction)
148
+
149
+ #dumpDebugFct(iInputFileName, lNewProfileFunction, 'NewProfileDB', lDBUnits, iTempDir)
150
+
151
+ # The difference of the functions will give the volume transformation profile
152
+ logInfo 'Compute differing function ...'
153
+ lDiffProfileFunction = WSK::Functions::Function.new
154
+ lDiffProfileFunction.set(lNewProfileFunction.functionData.clone)
155
+ if (lDBUnits)
156
+ # The volume transformation will be a DB difference
157
+ lDiffProfileFunction.substractFunction(lProfileFunction)
158
+ else
159
+ # The volume transformation will be a ratio
160
+ lDiffProfileFunction.divideByFunction(lProfileFunction)
161
+ end
162
+
163
+ #dumpDebugFct(iInputFileName, lDiffProfileFunction, 'RawDiffProfileDB', lDBUnits, iTempDir)
164
+
165
+ # Apply damping for attack and release times
166
+ logInfo 'Damp differing function with attack and release ...'
167
+ lAttackDuration = Rational(readDuration(iParams[:AttackDuration], lHeader.SampleRate))
168
+ lAttackSlope = iParams[:AttackDamping].to_f.to_r/lAttackDuration
169
+ lReleaseDuration = Rational(readDuration(iParams[:ReleaseDuration], lHeader.SampleRate))
170
+ lReleaseSlope = iParams[:ReleaseDamping].to_f.to_r/lReleaseDuration
171
+ if (lDBUnits)
172
+ lAttackSlope = -lAttackSlope
173
+ lReleaseSlope = -lReleaseSlope
174
+ end
175
+ # Take care of look-aheads
176
+ if ((iParams[:AttackLookAhead] == true) or
177
+ (iParams[:ReleaseLookAhead] == true))
178
+ # Look-Aheads are implemented by applying damping on the reverted function
179
+ lDiffProfileFunction.invertAbscisses
180
+ if (iParams[:AttackLookAhead] == false)
181
+ lDiffProfileFunction.applyDamping(nil, -lReleaseSlope)
182
+ elsif (iParams[:ReleaseLookAhead] == false)
183
+ lDiffProfileFunction.applyDamping(lAttackSlope, nil)
184
+ else
185
+ lDiffProfileFunction.applyDamping(lAttackSlope, -lReleaseSlope)
186
+ end
187
+ lDiffProfileFunction.invertAbscisses
188
+ end
189
+ if (iParams[:AttackLookAhead] == true)
190
+ if (iParams[:ReleaseLookAhead] == false)
191
+ lDiffProfileFunction.applyDamping(lReleaseSlope, nil)
192
+ end
193
+ elsif (iParams[:ReleaseLookAhead] == true)
194
+ if (iParams[:AttackLookAhead] == false)
195
+ lDiffProfileFunction.applyDamping(nil, -lAttackSlope)
196
+ end
197
+ else
198
+ lDiffProfileFunction.applyDamping(lReleaseSlope, -lAttackSlope)
199
+ end
200
+
201
+ #dumpDebugFct(iInputFileName, lDiffProfileFunction, 'DampedDiffProfileDB', lDBUnits, iTempDir)
202
+
203
+ # Eliminate glitches in the function.
204
+ # This is done by deleting intermediate abscisses that are too close to each other
205
+
206
+ logInfo 'Smooth differing function ...'
207
+ lDiffProfileFunction.removeNoiseAbscisses(Rational(readDuration(iParams[:MinChangeDuration], lHeader.SampleRate)))
208
+
209
+ #dumpDebugFct(iInputFileName, lDiffProfileFunction, 'SmoothedDiffProfileDB', lDBUnits, iTempDir)
210
+
211
+ # Save the volume transformation file
212
+ lDiffProfileFunction.writeToFile(lTempVolTransformFile)
213
+ end
214
+
215
+ # Apply the volume transformation to the Wave file
216
+ lStrUnitDB = 0
217
+ if (lDBUnits)
218
+ lStrUnitDB = 1
219
+ end
220
+ MusicMaster::wsk(iInputFileName, iOutputFileName, 'ApplyVolumeFct', "--function \"#{lTempVolTransformFile}\" --begin 0 --end -1 --unitdb #{lStrUnitDB}")
221
+ end
222
+ end
223
+
224
+ # Dump a function into a Wave file.
225
+ # This is used for debugging purposes only.
226
+ #
227
+ # Parameters:
228
+ # * *iInputFileName* (_String_): Name of the input file
229
+ # * *iFunction* (<em>WSK::Functions::Function</em>): The function to dump
230
+ # * *iName* (_String_): Name given to this function
231
+ # * *iDBUnits* (_Boolean_): Is the function in DB units ?
232
+ # * *iTempDir* (_String_): Temporary directory to use
233
+ def dumpDebugFct(iInputFileName, iFunction, iName, iDBUnits, iTempDir)
234
+ lBaseFileName = File.basename(iInputFileName)[0..-5]
235
+ # Clone the function to round it first
236
+ lRoundedFunction = WSK::Functions::Function.new
237
+ lRoundedFunction.set(iFunction.functionData.clone)
238
+ lRoundedFunction.writeToFile("#{iTempDir}/_#{lBaseFileName}_#{iName}.fct.rb", :Floats => true)
239
+ MusicMaster::wsk(iInputFileName, "#{iTempDir}/_#{lBaseFileName}_#{iName}.wav", 'DrawFct', "--function \"#{iTempDir}/_#{lBaseFileName}_#{iName}.fct.rb\" --unitdb #{iDBUnits ? '1' : '0'}")
240
+ end
241
+
242
+ end
243
+
244
+ end
245
+
246
+ end
@@ -0,0 +1,31 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module MusicMaster
7
+
8
+ module Processes
9
+
10
+ class Custom
11
+
12
+ # Execute the process
13
+ #
14
+ # Parameters:
15
+ # * *iInputFileName* (_String_): File name we want to apply effects to
16
+ # * *iOutputFileName* (_String_): File name to write
17
+ # * *iTempDir* (_String_): Temporary directory that can be used
18
+ # * *iParams* (<em>map<Symbol,Object></em>): Parameters
19
+ def execute(iInputFileName, iOutputFileName, iTempDir, iParams)
20
+ logInfo "Copying #{iInputFileName} => #{iOutputFileName} ..."
21
+ FileUtils::cp(iInputFileName, iOutputFileName)
22
+ puts "Apply custom process on file #{iOutputFileName}. Parameters: #{iParams.inspect}"
23
+ puts 'Press Enter when done ...'
24
+ $stdin.gets
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module MusicMaster
7
+
8
+ module Processes
9
+
10
+ class CutFirstSignal
11
+
12
+ # Execute the process
13
+ #
14
+ # Parameters:
15
+ # * *iInputFileName* (_String_): File name we want to apply effects to
16
+ # * *iOutputFileName* (_String_): File name to write
17
+ # * *iTempDir* (_String_): Temporary directory that can be used
18
+ # * *iParams* (<em>map<Symbol,Object></em>): Parameters
19
+ def execute(iInputFileName, iOutputFileName, iTempDir, iParams)
20
+ MusicMaster::wsk(iInputFileName, iOutputFileName, 'CutFirstSignal', "--silencethreshold 0 --noisefft none --silencemin \"#{$MusicMasterConf[:NoiseGate][:SilenceMin]}\"")
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,30 @@
1
+ #--
2
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
3
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
4
+ #++
5
+
6
+ module MusicMaster
7
+
8
+ module Processes
9
+
10
+ class GVerb
11
+
12
+ # Execute the process
13
+ #
14
+ # Parameters:
15
+ # * *iInputFileName* (_String_): File name we want to apply effects to
16
+ # * *iOutputFileName* (_String_): File name to write
17
+ # * *iTempDir* (_String_): Temporary directory that can be used
18
+ # * *iParams* (<em>map<Symbol,Object></em>): Parameters
19
+ def execute(iInputFileName, iOutputFileName, iTempDir, iParams)
20
+ puts "===> Apply GVerb from Audacity to file #{iInputFileName} and write file #{iOutputFileName}"
21
+ puts "===> Parameters: #{iParams.inspect}"
22
+ puts 'Press Enter when done.'
23
+ $stdin.gets
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end