MusicMaster 0.0.1.20101110

Sign up to get free protection for your applications and to get access to all the features.
data/bin/PrepareMix.rb ADDED
@@ -0,0 +1,422 @@
1
+ #!env ruby
2
+ #--
3
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
4
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
5
+ #++
6
+
7
+ require 'MusicMaster/Common'
8
+ require 'rUtilAnts/Plugins'
9
+ RUtilAnts::Plugins::initializePlugins
10
+ require 'rUtilAnts/Logging'
11
+ RUtilAnts::Logging::initializeLogging('', '')
12
+ require 'MusicMaster/ConfLoader'
13
+
14
+ module MusicMaster
15
+
16
+ # Get the configuration
17
+ #
18
+ # Parameters:
19
+ # * *iRecordConf* (<em>map<Symbol,Object></em>): The record configuration
20
+ # Return:
21
+ # * <em>map<Symbol,Object></em>: The configuration
22
+ def self.getConfig(iRecordConf)
23
+ rConfig = {
24
+ :MixFiles => []
25
+ }
26
+
27
+ # Check all needed files are present
28
+ lError = false
29
+ if (iRecordConf[:Patches] != nil)
30
+ iRecordConf[:Patches].each do |iIdxTrack, iTrackConf|
31
+ if (!File.exists?("Patch.#{iIdxTrack}.wav"))
32
+ logErr "Missing file Patch.#{iIdxTrack}.wav"
33
+ lError = true
34
+ end
35
+ if (!File.exists?("Patch.#{iIdxTrack}.Silence.wav"))
36
+ logErr "Missing file Patch.#{iIdxTrack}.Silence.wav"
37
+ lError = true
38
+ end
39
+ if (iTrackConf[:VolCorrection] == nil)
40
+ if (!File.exists?("Patch.#{iIdxTrack}.VolOriginal.wav"))
41
+ logErr "Missing file Patch.#{iIdxTrack}.VolOriginal.wav"
42
+ lError = true
43
+ end
44
+ if (!File.exists?("Patch.#{iIdxTrack}.VolReference.wav"))
45
+ logErr "Missing file Patch.#{iIdxTrack}.VolReference.wav"
46
+ lError = true
47
+ end
48
+ end
49
+ end
50
+ end
51
+ if (iRecordConf[:Performs] != nil)
52
+ iRecordConf[:Performs].each do |iLstPerform|
53
+ if (!File.exists?("Perform.#{iLstPerform.join(' ')}.wav"))
54
+ logErr "Missing file Perform.#{iLstPerform.join(' ')}.wav"
55
+ lError = true
56
+ end
57
+ end
58
+ end
59
+ if (!File.exists?('Perform.Silence.wav'))
60
+ logErr 'Missing file Perform.Silence.wav'
61
+ lError = true
62
+ end
63
+
64
+ if (lError)
65
+ logErr 'Some errors were found during files parsing. Aborting.'
66
+ else
67
+ # Generate the configuration
68
+ lPerformSilenceThresholds = getSilenceThresholds('Perform')
69
+ lLstStrPerformSilenceThresholds = []
70
+ lPerformSilenceThresholds.each do |iSilenceThresholdInfo|
71
+ lLstStrPerformSilenceThresholds << iSilenceThresholdInfo.join(',')
72
+ end
73
+ lPerformFFTProfile = genSilenceFFTProfile('Perform')
74
+ # Perform files
75
+ if (iRecordConf[:Performs] != nil)
76
+ iRecordConf[:Performs].each do |iLstPerform|
77
+ lFileName = "Perform.#{iLstPerform.join(' ')}.wav"
78
+ # Check if there is a DC offset to apply first
79
+ lAnalyze = analyzeFile(lFileName)
80
+ lDCOffsets = []
81
+ lOffset = false
82
+ lAnalyze[:MoyValues].each do |iMoyValue|
83
+ lDCOffset = iMoyValue.round
84
+ lDCOffsets << lDCOffset
85
+ if (lDCOffset != 0)
86
+ lOffset = true
87
+ end
88
+ end
89
+ lSilenceThresholdsWithDC = getDCThresholds(lDCOffsets, lPerformSilenceThresholds)
90
+ # Build the list of parameters to give to wsk
91
+ lLstStrSilenceThresholdsWithDC = []
92
+ lSilenceThresholdsWithDC.each do |iSilenceThresholdInfo|
93
+ lLstStrSilenceThresholdsWithDC << iSilenceThresholdInfo.join(',')
94
+ end
95
+ # The list of operations to perform
96
+ # list< [ String, String ] >
97
+ lOperations = [
98
+ [ 'SilenceRemover', "--silencethreshold \"#{lLstStrSilenceThresholdsWithDC.join('|')}\" --attack 0 --release #{$MusicMasterConf[:NoiseGate][:SilenceMin]} --noisefft \"#{lPerformFFTProfile}\"" ]
99
+ ]
100
+ if (lOffset)
101
+ logInfo "DC offset to correct: #{lDCOffsets.join(', ')}"
102
+ lOperations << [ 'DCShifter', "--offset \"#{lDCOffsets.map { |iValue| -iValue }.join('|')}\"" ]
103
+ else
104
+ logInfo 'No DC offset to correct'
105
+ end
106
+ lOperations << [ 'NoiseGate', "--silencethreshold \"#{lLstStrPerformSilenceThresholds.join('|')}\" --attack #{$MusicMasterConf[:NoiseGate][:Attack]} --release #{$MusicMasterConf[:NoiseGate][:Release]} --silencemin #{$MusicMasterConf[:NoiseGate][:SilenceMin]} --noisefft \"#{lPerformFFTProfile}\"" ]
107
+ # Register this file
108
+ rConfig[:MixFiles] << {
109
+ :FileName => lFileName,
110
+ :Operations => lOperations
111
+ }
112
+ end
113
+ end
114
+ # Patch files
115
+ if (iRecordConf[:Patches] != nil)
116
+ iRecordConf[:Patches].each do |iIdxTrack, iTrackConf|
117
+ # Measure the volume differences between Perform and Patch
118
+ lPatchSilenceThresholds = getSilenceThresholds("Patch.#{iIdxTrack}")
119
+ lLstStrPatchSilenceThresholds = []
120
+ lPatchSilenceThresholds.each do |iSilenceThresholdInfo|
121
+ lLstStrPatchSilenceThresholds << iSilenceThresholdInfo.join(',')
122
+ end
123
+ lPatchFFTProfile = genSilenceFFTProfile("Patch.#{iIdxTrack}")
124
+ # Check if there is a DC offset to apply first
125
+ lFileName = "Patch.#{iIdxTrack}.wav"
126
+ lAnalyze = analyzeFile(lFileName)
127
+ lDCOffsets = []
128
+ lOffset = false
129
+ lAnalyze[:MoyValues].each do |iMoyValue|
130
+ lDCOffset = iMoyValue.round
131
+ lDCOffsets << lDCOffset
132
+ if (lDCOffset != 0)
133
+ lOffset = true
134
+ end
135
+ end
136
+ lSilenceThresholdsWithDC = getDCThresholds(lDCOffsets, lPatchSilenceThresholds)
137
+ # Build the list of parameters to give to wsk
138
+ lLstStrSilenceThresholdsWithDC = []
139
+ lSilenceThresholdsWithDC.each do |iSilenceThresholdInfo|
140
+ lLstStrSilenceThresholdsWithDC << iSilenceThresholdInfo.join(',')
141
+ end
142
+ lVolCorrection = nil
143
+ if (iTrackConf[:VolCorrection] == nil)
144
+ # We use recorded previews to indicate volume corrections
145
+ lVolReference_Framed = genFramedWave("Patch.#{iIdxTrack}.VolReference.wav", lLstStrPerformSilenceThresholds, iTrackConf[:VolCompareCuts][0], iTrackConf[:VolCompareCuts][1], lPerformFFTProfile)
146
+ lVolOriginal_Framed = genFramedWave("Patch.#{iIdxTrack}.VolOriginal.wav", lLstStrPatchSilenceThresholds, iTrackConf[:VolCompareCuts][0], iTrackConf[:VolCompareCuts][1], lPatchFFTProfile)
147
+ lVolReference_Analyze = analyzeFile(lVolReference_Framed)
148
+ lVolOriginal_Analyze = analyzeFile(lVolOriginal_Framed)
149
+ lRMSReference = 0
150
+ lVolReference_Analyze[:RMSValues].each do |iValue|
151
+ lRMSReference += iValue
152
+ end
153
+ lRMSReference = lRMSReference / lVolReference_Analyze[:RMSValues].size
154
+ lRMSOriginal = 0
155
+ lVolOriginal_Analyze[:RMSValues].each do |iValue|
156
+ lRMSOriginal += iValue
157
+ end
158
+ lRMSOriginal = lRMSOriginal / lVolOriginal_Analyze[:RMSValues].size
159
+ # Show RMS difference
160
+ logInfo "Track #{iIdxTrack} - Reference sample RMS: #{lRMSReference} (#{lVolReference_Analyze[:RMSValues].join(', ')})"
161
+ logInfo "Track #{iIdxTrack} - Patch sample RMS: #{lRMSOriginal} (#{lVolOriginal_Analyze[:RMSValues].join(', ')})"
162
+ # If the Patch is louder, apply a volume reduction
163
+ if (lRMSOriginal != lRMSReference)
164
+ if (lRMSOriginal < lRMSReference)
165
+ # Here we are loosing quality: we need to increase the volume
166
+ lDBValue, lPCValue = val2db(lRMSReference-lRMSOriginal, lAnalyze[:MinPossibleValue].abs)
167
+ logWarn "Patch Track #{iIdxTrack} should be recorded louder (at least #{lDBValue} db <=> #{lPCValue} %)."
168
+ end
169
+ lVolCorrection = "#{lRMSReference}/#{lRMSOriginal}"
170
+ end
171
+ else
172
+ lVolCorrection = iTrackConf[:VolCorrection]
173
+ end
174
+ # The list of operations to perform
175
+ # list< [ String, String ] >
176
+ lOperations = [
177
+ [ 'SilenceRemover', "--silencethreshold \"#{lLstStrSilenceThresholdsWithDC.join('|')}\" --attack 0 --release #{$MusicMasterConf[:NoiseGate][:SilenceMin]} --noisefft \"#{lPatchFFTProfile}\"" ]
178
+ ]
179
+ if (lOffset)
180
+ logInfo "DC offset to correct: #{lDCOffsets.join(', ')}"
181
+ lOperations << [ 'DCShifter', "--offset \"#{lDCOffsets.map { |iValue| -iValue }.join('|')}\"" ]
182
+ else
183
+ logInfo 'No DC offset to correct'
184
+ end
185
+ lOperations << [ 'NoiseGate', "--silencethreshold \"#{lLstStrPatchSilenceThresholds.join('|')}\" --attack #{$MusicMasterConf[:NoiseGate][:Attack]} --release #{$MusicMasterConf[:NoiseGate][:Release]} --silencemin #{$MusicMasterConf[:NoiseGate][:SilenceMin]} --noisefft \"#{lPatchFFTProfile}\"" ]
186
+ if (lVolCorrection != nil)
187
+ lOperations << [ 'Multiply', "--coeff \"#{lVolCorrection}\"" ]
188
+ end
189
+ # Register this file
190
+ rConfig[:MixFiles] << {
191
+ :FileName => lFileName,
192
+ :Operations => lOperations
193
+ }
194
+ end
195
+ end
196
+ # Now treat additional WAV files
197
+ if (iRecordConf[:WaveFiles])
198
+ lLstWaveFiles = Dir.glob('Wave.*.wav')
199
+ if (lLstWaveFiles.empty?)
200
+ logWarn 'Record conf indicated some Wave files were present, but none was found.'
201
+ else
202
+ lLstWaveFiles.each do |iFileName|
203
+ rConfig[:MixFiles] << {
204
+ :FileName => iFileName,
205
+ :Operations => []
206
+ }
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ return rConfig
213
+ end
214
+
215
+ # Convert a value to its db notation and % notation
216
+ #
217
+ # Parameters:
218
+ # * *iValue* (_Integer_): The value
219
+ # * *iMaxValue* (_Integer_): The maximal possible value
220
+ # Return:
221
+ # * _Float_: Its corresponding db
222
+ # * _Float_: Its corresponding percentage
223
+ def self.val2db(iValue, iMaxValue)
224
+ if (iValue == 0)
225
+ return -1.0/0, 0.0
226
+ else
227
+ if (defined?(@Log2) == nil)
228
+ @Log2 = Math.log(2.0)
229
+ end
230
+ return -6*(Math.log(Float(iMaxValue))-Math.log(Float(iValue.abs)))/@Log2, (Float(iValue.abs)*100)/Float(iMaxValue)
231
+ end
232
+ end
233
+
234
+ # Get thresholds shifted by a DC offset.
235
+ #
236
+ # Parameters:
237
+ # * *iDCOffsets* (<em>list<Integer></em>): The DC offsets
238
+ # * *iThresholds* (<em>list<[Integer,Integer]></em>): The thresholds to shift
239
+ # Return:
240
+ # * <em>list<[Integer,Integer]></em>: The shifted thresholds
241
+ # * _Boolean_: Has an offset really been applied ?
242
+ def self.getDCThresholds(iDCOffsets, iThresholds)
243
+ rCorrectedSilenceThresholds = []
244
+
245
+ # Compute the silence thresholds with DC offset applied
246
+ iThresholds.each do |iThresholdInfo|
247
+ lCorrectedThresholdInfo = []
248
+ iThresholdInfo.each_with_index do |iValue, iIdxChannel|
249
+ lCorrectedThresholdInfo << iValue + iDCOffsets[iIdxChannel]
250
+ end
251
+ rCorrectedSilenceThresholds << lCorrectedThresholdInfo
252
+ end
253
+
254
+ return rCorrectedSilenceThresholds
255
+ end
256
+
257
+ # Analyze a given wav file
258
+ #
259
+ # Parameters:
260
+ # * *iWaveFile* (_String_): The wav file to analyze
261
+ # Return:
262
+ # * <em>map<Symbol,Object></em>: The analyze result
263
+ def self.analyzeFile(iWaveFile)
264
+ rResult = nil
265
+
266
+ lAnalyzeFile = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{File.basename(iWaveFile)}.analyze"
267
+ if (!File.exists?(lAnalyzeFile))
268
+ lDummyFile = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/Dummy.wav"
269
+ wsk(iWaveFile, lDummyFile, 'Analyze')
270
+ File.unlink(lDummyFile)
271
+ FileUtils::mv('analyze.result', lAnalyzeFile)
272
+ end
273
+ File.open(lAnalyzeFile, 'rb') do |iFile|
274
+ rResult = Marshal.load(iFile.read)
275
+ end
276
+
277
+ return rResult
278
+ end
279
+
280
+ # Get the silence threshold of a given record type.
281
+ # Remove the DC offset in the returned thresholds.
282
+ #
283
+ # Parameters:
284
+ # * *iRecordType* (_String_): The record type
285
+ # Return:
286
+ # * <em>list<[Integer,Integer]></em>: Silence thresholds, per channel
287
+ def self.getSilenceThresholds(iRecordType)
288
+ rSilenceThresholds = []
289
+
290
+ # Read the silence file
291
+ lSilenceThresholdFile = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{iRecordType}.Silence.threshold"
292
+ if (!File.exists?(lSilenceThresholdFile))
293
+ lResult = analyzeFile("#{iRecordType}.Silence.wav")
294
+ # Compute the DC offsets
295
+ lDCOffsets = lResult[:MoyValues].map { |iValue| iValue.round }
296
+ lSilenceThresholds = []
297
+ lResult[:MaxValues].each_with_index do |iMaxValue, iIdxChannel|
298
+ # Remove DC Offset
299
+ lCorrectedMinValue = lResult[:MinValues][iIdxChannel] - lDCOffsets[iIdxChannel]
300
+ lCorrectedMaxValue = iMaxValue - lDCOffsets[iIdxChannel]
301
+ # Compute the silence threshold by adding the margin
302
+ lSilenceThresholds << [(lCorrectedMinValue-lCorrectedMinValue.abs*$MusicMasterConf[:PrepareMix][:MarginSilenceThresholds]).to_i, (lCorrectedMaxValue+lCorrectedMaxValue.abs*$MusicMasterConf[:PrepareMix][:MarginSilenceThresholds]).to_i]
303
+ end
304
+ # Write them
305
+ File.open(lSilenceThresholdFile, 'wb') do |oFile|
306
+ oFile.write(Marshal.dump(lSilenceThresholds))
307
+ end
308
+ end
309
+ File.open(lSilenceThresholdFile, 'rb') do |iFile|
310
+ rSilenceThresholds = Marshal.load(iFile.read)
311
+ end
312
+ logInfo "#{iRecordType} silence thresholds: #{rSilenceThresholds.join(', ')}"
313
+
314
+ return rSilenceThresholds
315
+ end
316
+
317
+ # Generate the silence FFT profile of a record type.
318
+ #
319
+ # Parameters:
320
+ # * *iRecordType* (_String_): The record type
321
+ # Return:
322
+ # * _String_: Name of the file containing the FFT profile
323
+ def self.genSilenceFFTProfile(iRecordType)
324
+ rFFTProfileFileName = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{iRecordType}.Silence.fftprofile"
325
+
326
+ if (!File.exists?(rFFTProfileFileName))
327
+ lDummyFile = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/Dummy.wav"
328
+ wsk("#{iRecordType}.Silence.wav", lDummyFile, 'FFT')
329
+ File.unlink(lDummyFile)
330
+ FileUtils::mv('fft.result', rFFTProfileFileName)
331
+ end
332
+
333
+ return rFFTProfileFileName
334
+ end
335
+
336
+ # Generate the framed wave file corresponding to a specified wave file.
337
+ # Framed wave removes silence at the beginning and end of the file, and then cut a specific section of the file
338
+ # If the framed file already exists, it does nothing.
339
+ #
340
+ # Parameters:
341
+ # * *iInputFile* (_String_): The file we want to frame
342
+ # * *iStrSilenceThresholds* (_String_): The silence thresholds parameter
343
+ # * *iBeginCut* (_String_): The begin marker to cut
344
+ # * *iEndCut* (_String_): The end marker to cut
345
+ # * *iSilenceFFTProfile* (_String_): Name of the file containing the silence FFT profile
346
+ # Return:
347
+ # * _String_: Name of the framed file
348
+ def self.genFramedWave(iInputFile, iStrSilenceThresholds, iBeginCut, iEndCut, iSilenceFFTProfile)
349
+ lBaseName = File.basename(iInputFile)[0..-5]
350
+ lFileName_Framed = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{lBaseName}_Framed.wav"
351
+ if (File.exists?(lFileName_Framed))
352
+ logInfo "File #{lFileName_Framed} already exists. Skipping its generation."
353
+ else
354
+ wsk(iInputFile, lFileName_Framed, 'SilenceRemover', "--silencethreshold \"#{iStrSilenceThresholds}\" --attack 0 --release 0 --noisefft \"#{iSilenceFFTProfile}\"")
355
+ end
356
+ # Cut the specific region
357
+ lFileName_Cut = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{lBaseName}_Cut.wav"
358
+ if (File.exists?(lFileName_Cut))
359
+ logInfo "File #{lFileName_Cut} already exists. Skipping its generation."
360
+ else
361
+ wsk(lFileName_Framed, lFileName_Cut, 'Cut', "--begin \"#{iBeginCut}\" --end \"#{iEndCut}\"")
362
+ end
363
+ # Remove its DC offset if needed
364
+ lAnalyze = analyzeFile(lFileName_Cut)
365
+ lDCOffsets = []
366
+ lOffset = false
367
+ lAnalyze[:MoyValues].each do |iValue|
368
+ lDCOffset = iValue.round
369
+ lDCOffsets << lDCOffset
370
+ if (lDCOffset != 0)
371
+ lOffset = true
372
+ end
373
+ end
374
+ lFileName_DCOffset = nil
375
+ if (lOffset)
376
+ lFileName_DCOffset = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{lBaseName}_DCOffset.wav"
377
+ if (File.exists?(lFileName_DCOffset))
378
+ logInfo "File #{lFileName_DCOffset} already exists. Skipping its generation."
379
+ else
380
+ wsk(lFileName_Cut, lFileName_DCOffset, 'DCShifter', "--offset \"#{lDCOffsets.map { |iValue| -iValue }.join('|')}\"")
381
+ end
382
+ else
383
+ lFileName_DCOffset = lFileName_Cut
384
+ end
385
+ rFileName_NoiseGate = "#{$MusicMasterConf[:PrepareMix][:TempDir]}/#{lBaseName}_NoiseGate.wav"
386
+ if (File.exists?(rFileName_NoiseGate))
387
+ logInfo "File #{rFileName_NoiseGate} already exists. Skipping its generation."
388
+ else
389
+ wsk(lFileName_DCOffset, rFileName_NoiseGate, 'NoiseGate', "--silencethreshold \"#{iStrSilenceThresholds}\" --attack #{$MusicMasterConf[:NoiseGate][:Attack]} --release #{$MusicMasterConf[:NoiseGate][:Release]} --silencemin #{$MusicMasterConf[:NoiseGate][:SilenceMin]} --noisefft \"#{iSilenceFFTProfile}\"")
390
+ end
391
+
392
+ return rFileName_NoiseGate
393
+ end
394
+
395
+ end
396
+
397
+ rErrorCode = 0
398
+ lRecordConfFile, lConfigFile = ARGV[0..1]
399
+ if ((lRecordConfFile == nil) or
400
+ (lConfigFile == nil))
401
+ puts 'Usage: PrepareMix <RecordConfFile> <MixConfFile>'
402
+ rErrorCode = 1
403
+ elsif (File.exists?(lConfigFile))
404
+ logErr "File #{lConfigFile} already exist."
405
+ rErrorCode = 2
406
+ else
407
+ lError, lRecordConf = MusicMaster::readRecordConf(lRecordConfFile)
408
+ if (lError == nil)
409
+ FileUtils::mkdir_p($MusicMasterConf[:PrepareMix][:TempDir])
410
+ lConfig = MusicMaster::getConfig(lRecordConf)
411
+ require 'pp'
412
+ File.open(lConfigFile, 'w') do |oFile|
413
+ oFile.write(lConfig.pretty_inspect)
414
+ end
415
+ logInfo "===== config saved in #{lConfigFile}"
416
+ else
417
+ logErr "Error: #{lError}"
418
+ rErrorCode = 3
419
+ end
420
+ end
421
+
422
+ exit rErrorCode
data/bin/Record.rb ADDED
@@ -0,0 +1,145 @@
1
+ #!env ruby
2
+ #--
3
+ # Copyright (c) 2009-2010 Muriel Salvan (murielsalvan@users.sourceforge.net)
4
+ # Licensed under the terms specified in LICENSE file. No warranty is provided.
5
+ #++
6
+
7
+ require 'fileutils'
8
+ require 'optparse'
9
+ require 'rUtilAnts/Logging'
10
+ RUtilAnts::Logging::initializeLogging('', '')
11
+ require 'MusicMaster/Common'
12
+ require 'MusicMaster/ConfLoader'
13
+
14
+ module MusicMaster
15
+
16
+ class Record
17
+
18
+ # Constructor
19
+ def initialize
20
+ # List of perform partitions
21
+ # list< list< Integer > >
22
+ @LstPerforms = []
23
+ # List of patch tracks
24
+ # list< Integer >
25
+ @LstPatches = []
26
+ @WaveFiles = false
27
+ MusicMaster::parsePlugins
28
+ end
29
+
30
+ # Execute the recordings
31
+ #
32
+ # Parameters:
33
+ # * *iArgs* (<em>list<String></em>): The list of arguments
34
+ # Return:
35
+ # * _Integer_: The error code
36
+ def execute(iArgs)
37
+ rErrorCode = 0
38
+
39
+ lError = nil
40
+ # Read the arguments
41
+ if (iArgs.size != 1)
42
+ lError = RuntimeError.new('Usage: Record <ConfFile>')
43
+ else
44
+ # Read configuration
45
+ lError, lConf = MusicMaster::readRecordConf(iArgs[0])
46
+ if (lError == nil)
47
+ if (lConf[:Performs] != nil)
48
+ if (!lConf[:Performs].empty?)
49
+ # Record performances
50
+ lConf[:Performs].each do |iLstPerform|
51
+ puts "===== Record Perform partition #{iLstPerform.join(', ')} ====="
52
+ record("Perform.#{iLstPerform.join(' ')}.wav")
53
+ end
54
+ puts '===== Record Perform silence ====='
55
+ record('Perform.Silence.wav')
56
+ end
57
+ end
58
+ if (lConf[:Patches] != nil)
59
+ lConf[:Patches].each do |iIdxTrack, iTrackConf|
60
+ # If the track has already a volume correction to be applied, ignore this step
61
+ if (iTrackConf[:VolCorrection] == nil)
62
+ puts "===== Record Perform volume preview for track #{iIdxTrack} ====="
63
+ record("Patch.#{iIdxTrack}.VolReference.wav")
64
+ puts "===== Measure the volume cuts from file Patch.#{iIdxTrack}.VolReference.wav and set them in the record conf file ====="
65
+ puts 'Enter when done.'
66
+ $stdin.gets
67
+ end
68
+ end
69
+ # Now we don't need anymore the volume setting for Perform.
70
+ lConf[:Patches].each do |iIdxTrack, iTrackConf|
71
+ lTryAgain = true
72
+ while (lTryAgain)
73
+ puts "===== Record the Patch track #{iIdxTrack} ====="
74
+ record("Patch.#{iIdxTrack}.wav")
75
+ puts 'Is the file correct ? (No peak limit reached ?) \'y\'=yes.'
76
+ lTryAgain = ($stdin.gets.chomp != 'y')
77
+ end
78
+ if (iTrackConf[:Effects] != nil)
79
+ MusicMaster::applyProcesses(iTrackConf[:Effects], "Patch.#{iIdxTrack}.wav", $MusicMasterConf[:Record][:TempDir])
80
+ end
81
+ puts "===== Record Patch silence for track #{iIdxTrack} ====="
82
+ record("Patch.#{iIdxTrack}.Silence.wav")
83
+ if (iTrackConf[:Effects] != nil)
84
+ MusicMaster::applyProcesses(iTrackConf[:Effects], "Patch.#{iIdxTrack}.Silence.wav", $MusicMasterConf[:Record][:TempDir])
85
+ end
86
+ if (iTrackConf[:VolCorrection] == nil)
87
+ puts "===== Record Patch volume preview for track #{iIdxTrack} ====="
88
+ record("Patch.#{iIdxTrack}.VolOriginal.wav")
89
+ if (iTrackConf[:Effects] != nil)
90
+ MusicMaster::applyProcesses(iTrackConf[:Effects], "Patch.#{iIdxTrack}.VolOriginal.wav", $MusicMasterConf[:Record][:TempDir])
91
+ end
92
+ end
93
+ end
94
+ end
95
+ if (lConf[:WaveFiles])
96
+ puts '===== Generate Wave in Wave.*.wav files ====='
97
+ puts 'Press Enter to continue once done.'
98
+ $stdin.gets
99
+ end
100
+ logInfo 'Finished recording ok. Ready to use PrepareMix.rb and Mix.rb.'
101
+ end
102
+ end
103
+ if (lError != nil)
104
+ puts "!!! Error: #{lError}"
105
+ rErrorCode = 1
106
+ end
107
+
108
+ return rErrorCode
109
+ end
110
+
111
+ # Record into a given file
112
+ #
113
+ # Parameters:
114
+ # * *iFileName* (_String_): File name to record into
115
+ def record(iFileName)
116
+ lTryAgain = true
117
+ if (File.exists?(iFileName))
118
+ puts "File \"#{iFileName}\" already exists. Overwrite ? ['y' = yes]"
119
+ lTryAgain = ($stdin.gets.chomp == 'y')
120
+ end
121
+ while (lTryAgain)
122
+ puts "Record file \"#{iFileName}\""
123
+ puts 'Press Enter to continue once done. Type \'s\' to skip it.'
124
+ lSkip = ($stdin.gets.chomp == 's')
125
+ if (lSkip)
126
+ lTryAgain = false
127
+ else
128
+ # Get the last file that has been recorded
129
+ lFileName = $MusicMasterConf[:Record][:RecordedFileGetter].call
130
+ if (!File.exists?(lFileName))
131
+ logErr "File #{lFileName} does not exist. Could not get recorded file."
132
+ else
133
+ logInfo "Getting recorded file: #{lFileName} => #{iFileName}"
134
+ FileUtils::mv(lFileName, iFileName)
135
+ lTryAgain = false
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+
145
+ exit MusicMaster::Record.new.execute(ARGV)