MusicMaster 0.0.1.20101110

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.
@@ -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