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