MusicMaster 0.0.1.20101110 → 1.0.0.20120307
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +4 -1
- data/ChangeLog +28 -0
- data/LICENSE +1 -1
- data/README +2 -5
- data/ReleaseInfo +8 -8
- data/bin/Calibrate.rb +55 -0
- data/bin/Clean.rb +55 -0
- data/bin/DBConvert.rb +3 -1
- data/bin/Deliver.rb +73 -42
- data/bin/Mix.rb +39 -78
- data/bin/Process.rb +55 -0
- data/bin/Record.rb +63 -116
- data/bin/{Album.rb → old/Album.rb} +18 -18
- data/bin/{AnalyzeAlbum.rb → old/AnalyzeAlbum.rb} +11 -11
- data/bin/{DeliverAlbum.rb → old/DeliverAlbum.rb} +12 -12
- data/bin/{Fct2Wave.rb → old/Fct2Wave.rb} +11 -11
- data/{album.conf.rb.example → bin/old/album.conf.rb.example} +0 -0
- data/lib/MusicMaster/FilesNamer.rb +253 -0
- data/lib/MusicMaster/Formats/MP3.rb +50 -0
- data/lib/MusicMaster/Formats/Test.rb +44 -0
- data/lib/MusicMaster/Formats/Wave.rb +80 -0
- data/lib/MusicMaster/Hash.rb +20 -0
- data/lib/MusicMaster/Launcher.rb +241 -0
- data/lib/MusicMaster/Processes/ApplyVolumeFct.rb +4 -8
- data/lib/MusicMaster/Processes/Compressor.rb +50 -47
- data/lib/MusicMaster/Processes/Custom.rb +3 -3
- data/lib/MusicMaster/Processes/{AddSilence.rb → Cut.rb} +8 -4
- data/lib/MusicMaster/Processes/CutFirstSignal.rb +6 -3
- data/lib/MusicMaster/Processes/DCShifter.rb +30 -0
- data/lib/MusicMaster/Processes/GVerb.rb +3 -2
- data/lib/MusicMaster/Processes/Normalize.rb +7 -6
- data/lib/MusicMaster/Processes/SilenceInserter.rb +31 -0
- data/lib/MusicMaster/Processes/Test.rb +39 -0
- data/lib/MusicMaster/Processes/VolCorrection.rb +3 -3
- data/lib/MusicMaster/RakeProcesses.rb +1014 -0
- data/lib/MusicMaster/Symbol.rb +12 -0
- data/lib/MusicMaster/Task.rb +29 -0
- data/lib/MusicMaster/Utils.rb +467 -0
- data/lib/MusicMaster/old/Common.rb +101 -0
- data/musicmaster.conf.rb.example +42 -59
- data/record.conf.rb.example +374 -30
- metadata +91 -41
- data/TODO +0 -3
- data/bin/Master.rb +0 -60
- data/bin/PrepareMix.rb +0 -422
- data/lib/MusicMaster/Common.rb +0 -197
- data/lib/MusicMaster/ConfLoader.rb +0 -56
- data/lib/MusicMaster/musicmaster.conf.rb +0 -96
- data/master.conf.rb.example +0 -13
@@ -0,0 +1,253 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 - 2012 Muriel Salvan (muriel@x-aeon.com)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'MusicMaster/Hash'
|
7
|
+
|
8
|
+
module MusicMaster
|
9
|
+
|
10
|
+
module FilesNamer
|
11
|
+
|
12
|
+
# Get the directory in which files are recorded
|
13
|
+
#
|
14
|
+
# Return::
|
15
|
+
# * _String_: Directory to record files to
|
16
|
+
def getRecordedDir
|
17
|
+
return @MusicMasterConf[:Directories][:Record]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get the directory in which static audio files are stored
|
21
|
+
#
|
22
|
+
# Return::
|
23
|
+
# * _String_: Directory to store static audio files to
|
24
|
+
def getWaveDir
|
25
|
+
return @MusicMasterConf[:Directories][:Wave]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get the directory in which recorded files are analyzed
|
29
|
+
#
|
30
|
+
# Return::
|
31
|
+
# * _String_: Directory to store analysis results of recorded files to
|
32
|
+
def getAnalyzedRecordedDir
|
33
|
+
return @MusicMasterConf[:Directories][:AnalyzeRecord]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the directory in which files are cleaned
|
37
|
+
#
|
38
|
+
# Return::
|
39
|
+
# * _String_: Directory to clean files to
|
40
|
+
def getCleanedDir
|
41
|
+
return @MusicMasterConf[:Directories][:Clean]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the directory in which files are calibrated
|
45
|
+
#
|
46
|
+
# Return::
|
47
|
+
# * _String_: Directory to calibrate files to
|
48
|
+
def getCalibratedDir
|
49
|
+
return @MusicMasterConf[:Directories][:Calibrate]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get the directory in which Wave files are processed
|
53
|
+
#
|
54
|
+
# Return::
|
55
|
+
# * _String_: Directory to process files to
|
56
|
+
def getProcessesWaveDir
|
57
|
+
return @MusicMasterConf[:Directories][:ProcessWave]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the directory in which recorded files are processed
|
61
|
+
#
|
62
|
+
# Return::
|
63
|
+
# * _String_: Directory to process files to
|
64
|
+
def getProcessesRecordDir
|
65
|
+
return @MusicMasterConf[:Directories][:ProcessRecord]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get the directory in which mix files are processed
|
69
|
+
#
|
70
|
+
# Return::
|
71
|
+
# * _String_: Directory to mix files to
|
72
|
+
def getMixDir
|
73
|
+
return @MusicMasterConf[:Directories][:Mix]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get the directory in which final mix files are linked
|
77
|
+
#
|
78
|
+
# Return::
|
79
|
+
# * _String_: Directory storing links to final mix files
|
80
|
+
def getFinalMixDir
|
81
|
+
return @MusicMasterConf[:Directories][:FinalMix]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the directory in which files are delivered
|
85
|
+
#
|
86
|
+
# Return::
|
87
|
+
# * _String_: Directory to deliver files to
|
88
|
+
def getDeliverDir
|
89
|
+
return @MusicMasterConf[:Directories][:Deliver]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get the recorded file name of a given list of tracks on a given environment
|
93
|
+
#
|
94
|
+
# Parameters::
|
95
|
+
# * *iEnv* (_Symbol_): The environment
|
96
|
+
# * *iLstTracks* (<em>list<Integer></em>): The list of tracks being recorded
|
97
|
+
# Return::
|
98
|
+
# * _String_: Name of the Wave file
|
99
|
+
def getRecordedFileName(iEnv, iLstTracks)
|
100
|
+
return "#{getRecordedDir}/#{iEnv}.#{iLstTracks.sort.join('.')}.wav"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Get the recorded silence file name on a given recording environment
|
104
|
+
#
|
105
|
+
# Parameters::
|
106
|
+
# * *iEnv* (_Symbol_): The environment
|
107
|
+
# Return::
|
108
|
+
# * _String_: Name of the Wave file
|
109
|
+
def getRecordedSilenceFileName(iEnv)
|
110
|
+
return "#{getRecordedDir}/#{iEnv}.Silence.wav"
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get the recorded calibration file name, recording from a recording environment in order to be compared later with a reference environment.
|
114
|
+
#
|
115
|
+
# Parameters::
|
116
|
+
# * *iEnvReference* (_Symbol_): The reference environment
|
117
|
+
# * *iEnvRecording* (_Symbol_): The recording environment
|
118
|
+
# Return::
|
119
|
+
# * _String_: Name of the Wave file
|
120
|
+
def getRecordedCalibrationFileName(iEnvReference, iEnvRecording)
|
121
|
+
return "#{getRecordedDir}/Calibration.#{iEnvRecording}.#{iEnvReference}.wav"
|
122
|
+
end
|
123
|
+
|
124
|
+
# Get the calibrated recorded file name
|
125
|
+
#
|
126
|
+
# Parameters::
|
127
|
+
# * *iRecordedBaseName* (_String_): Base name of the recorded track
|
128
|
+
# Return::
|
129
|
+
# * _String_: Name of the Wave file
|
130
|
+
def getCalibratedFileName(iRecordedBaseName)
|
131
|
+
return "#{getCalibratedDir}/#{iRecordedBaseName}.Calibrated.wav"
|
132
|
+
end
|
133
|
+
|
134
|
+
# Get the name of a source wave file
|
135
|
+
#
|
136
|
+
# Parameters::
|
137
|
+
# * *iFileName* (_String_): Name of the Wave file used to generate this source wave file
|
138
|
+
# Return::
|
139
|
+
# * _String_: Name of the Wave file
|
140
|
+
def getWaveSourceFileName(iFileName)
|
141
|
+
if (File.exists?(iFileName))
|
142
|
+
# Use the original one
|
143
|
+
return iFileName
|
144
|
+
else
|
145
|
+
# We will generate a new one
|
146
|
+
return "#{getWaveDir}/#{File.basename(iFileName)}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Get the name of an analysis file taken from a recorded file
|
151
|
+
#
|
152
|
+
# Parameters::
|
153
|
+
# * *iBaseName* (_String_): Base name of the recorded file (without extension)
|
154
|
+
# Return::
|
155
|
+
# * _String_: The analysis file name
|
156
|
+
def getRecordedAnalysisFileName(iBaseName)
|
157
|
+
return "#{getAnalyzedRecordedDir}/#{iBaseName}.analyze"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Get the name of a FFT profike file taken from a recorded file
|
161
|
+
#
|
162
|
+
# Parameters::
|
163
|
+
# * *iBaseName* (_String_): Base name of the recorded file (without extension)
|
164
|
+
# Return::
|
165
|
+
# * _String_: The FFT profile file name
|
166
|
+
def getRecordedFFTProfileFileName(iBaseName)
|
167
|
+
return "#{getAnalyzedRecordedDir}/#{iBaseName}.fftprofile"
|
168
|
+
end
|
169
|
+
|
170
|
+
# Get the name of the file generated after removing silences from it.
|
171
|
+
#
|
172
|
+
# Parameters::
|
173
|
+
# * *iBaseName* (_String_): Base name of the file
|
174
|
+
# Return::
|
175
|
+
# * _String_: The generated file name
|
176
|
+
def getSilenceRemovedFileName(iBaseName)
|
177
|
+
return "#{getCleanedDir}/#{iBaseName}.01.SilenceRemover.wav"
|
178
|
+
end
|
179
|
+
|
180
|
+
# Get the name of the file generated after applying a cut from it.
|
181
|
+
#
|
182
|
+
# Parameters::
|
183
|
+
# * *iBaseName* (_String_): Base name of the file
|
184
|
+
# * *iCutInfo* (<em>[String,String]</em>): The cut information, used to extract only a part of the file (begin and end markers, in seconds or samples)
|
185
|
+
# Return::
|
186
|
+
# * _String_: The generated file name
|
187
|
+
def getCutFileName(iBaseName, iCutInfo)
|
188
|
+
return "#{getCleanedDir}/#{iBaseName}.02.Cut.#{iCutInfo.join('_')}.wav"
|
189
|
+
end
|
190
|
+
|
191
|
+
# Get the name of the file generated after applying a DC remover from it.
|
192
|
+
#
|
193
|
+
# Parameters::
|
194
|
+
# * *iBaseName* (_String_): Base name of the file
|
195
|
+
# Return::
|
196
|
+
# * _String_: The generated file name
|
197
|
+
def getDCRemovedFileName(iBaseName)
|
198
|
+
return "#{getCleanedDir}/#{iBaseName}.03.DCShifter.wav"
|
199
|
+
end
|
200
|
+
|
201
|
+
# Get the name of the file generated after applying a noise gate from it.
|
202
|
+
#
|
203
|
+
# Parameters::
|
204
|
+
# * *iBaseName* (_String_): Base name of the file
|
205
|
+
# Return::
|
206
|
+
# * _String_: The generated file name
|
207
|
+
def getNoiseGateFileName(iBaseName)
|
208
|
+
return "#{getCleanedDir}/#{iBaseName}.04.NoiseGate.wav"
|
209
|
+
end
|
210
|
+
|
211
|
+
# Get the name of a file to processed
|
212
|
+
#
|
213
|
+
# Parameters::
|
214
|
+
# * *iDir* (_String_): Directory where to store the processed file
|
215
|
+
# * *iBaseName* (_String_): Base name of the processed file source
|
216
|
+
# * *iIdxProcess* (_Integer_): Index of the process
|
217
|
+
# * *iProcessName* (_String_): Name of the process to apply
|
218
|
+
# * *iProcessParams* (<em>map<Symbol,Object></em>): Process parameters
|
219
|
+
def getProcessedFileName(iDir, iBaseName, iIdxProcess, iProcessName, iProcessParams)
|
220
|
+
# If the base name contains already an ID, integrate it in the new ID
|
221
|
+
lMatch = iBaseName.match(/^(.*)\.([[:xdigit:]]{32,32})$/)
|
222
|
+
if (lMatch == nil)
|
223
|
+
return "#{iDir}/#{iBaseName}.#{iIdxProcess}.#{iProcessName}.#{iProcessParams.unique_id}.wav"
|
224
|
+
else
|
225
|
+
lNewBaseName = lMatch[1]
|
226
|
+
lNewProcessParams = {
|
227
|
+
:__InheritedID__ => lMatch[2]
|
228
|
+
}.merge(iProcessParams)
|
229
|
+
return "#{iDir}/#{lNewBaseName}.#{iIdxProcess}.#{iProcessName}.#{lNewProcessParams.unique_id}.wav"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Get the name of a file to be mixed
|
234
|
+
#
|
235
|
+
# Parameters::
|
236
|
+
# * *iDir* (_String_): Directory where to store the mixed file
|
237
|
+
# * *iMixName* (_String_): Name of the mix
|
238
|
+
# * *iMixTracksConf* (<em>map<Symbol,Object></em>): Mix tracks' parameters
|
239
|
+
def getMixFileName(iDir, iMixName, iMixTracksConf)
|
240
|
+
return "#{iDir}/#{iMixName}.#{iMixTracksConf.unique_id}.wav"
|
241
|
+
end
|
242
|
+
|
243
|
+
# Get the name of a final mix file (the symbolic link)
|
244
|
+
#
|
245
|
+
# Parameters::
|
246
|
+
# * *iMixName* (_String_): Name of the mix
|
247
|
+
def getFinalMixFileName(iMixName)
|
248
|
+
return "#{getFinalMixDir}/#{iMixName}.wav"
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2012 Muriel Salvan (muriel@x-aeon.com)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module MusicMaster
|
7
|
+
|
8
|
+
module Formats
|
9
|
+
|
10
|
+
class MP3
|
11
|
+
|
12
|
+
# Give the file extension
|
13
|
+
#
|
14
|
+
# Return::
|
15
|
+
# * _String_: The file extension (without .)
|
16
|
+
def getFileExt
|
17
|
+
return 'mp3'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Deliver a file.
|
21
|
+
# The delivered file can be a shortcut to the source one.
|
22
|
+
#
|
23
|
+
# Parameters::
|
24
|
+
# * *iSrcFileName* (_String_): The source file to deliver from
|
25
|
+
# * *iDstFileName* (_String_): The destination file to be delivered
|
26
|
+
# * *iFormatConf* (<em>map<Symbol,Object></em>): The format configuration
|
27
|
+
# * *iMetadata* (<em>map<Symbol,Object></em>): The metadata that can be used while delivering the file
|
28
|
+
def deliver(iSrcFileName, iDstFileName, iFormatConf, iMetadata)
|
29
|
+
# TODO: Implement it using an external tool, and make regression testing
|
30
|
+
lTranslatedParams = []
|
31
|
+
iParams.each do |iParam, iValue|
|
32
|
+
case iParam
|
33
|
+
when :SampleRate
|
34
|
+
lTranslatedParams << "Sample rate: #{iValue} Hz"
|
35
|
+
when :BitRate
|
36
|
+
lTranslatedParams << "Bit rate: #{iValue} kbps"
|
37
|
+
else
|
38
|
+
log_warn "Unknown MP3 format parameter: #{iParam} (value #{iValue.inspect}). Ignoring it."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
puts "Convert file #{iSrcFileName} into file #{iDstFileName} in MP3 format with following parameters: #{lTranslatedParams.join(', ')}"
|
42
|
+
puts 'Press Enter when done.'
|
43
|
+
$stdin.gets
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2012 Muriel Salvan (muriel@x-aeon.com)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module MusicMaster
|
7
|
+
|
8
|
+
module Formats
|
9
|
+
|
10
|
+
class Test
|
11
|
+
|
12
|
+
# Give the file extension
|
13
|
+
#
|
14
|
+
# Return::
|
15
|
+
# * _String_: The file extension (without .)
|
16
|
+
def getFileExt
|
17
|
+
return 'test.rb'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Deliver a file.
|
21
|
+
# The delivered file can be a shortcut to the source one.
|
22
|
+
#
|
23
|
+
# Parameters::
|
24
|
+
# * *iSrcFileName* (_String_): The source file to deliver from
|
25
|
+
# * *iDstFileName* (_String_): The destination file to be delivered
|
26
|
+
# * *iFormatConf* (<em>map<Symbol,Object></em>): The format configuration
|
27
|
+
# * *iMetadata* (<em>map<Symbol,Object></em>): The metadata that can be used while delivering the file
|
28
|
+
def deliver(iSrcFileName, iDstFileName, iFormatConf, iMetadata)
|
29
|
+
log_debug "Deliver for test purposes file #{iDstFileName}"
|
30
|
+
File.open(iDstFileName, 'w') do |oFile|
|
31
|
+
oFile.write({
|
32
|
+
:SrcFileName => iSrcFileName,
|
33
|
+
:DstFileName => iDstFileName,
|
34
|
+
:FormatConf => iFormatConf,
|
35
|
+
:Metadata => iMetadata
|
36
|
+
}.inspect)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2012 Muriel Salvan (muriel@x-aeon.com)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module MusicMaster
|
7
|
+
|
8
|
+
module Formats
|
9
|
+
|
10
|
+
# Wave file format.
|
11
|
+
# Take the following parameters:
|
12
|
+
# * *:Dither* (_Boolean_): Do we apply dither while converting ?
|
13
|
+
# * *:SampleRate* (_Integer_): Sample rate in Hz (ie 44100, 192000 ...)
|
14
|
+
# * *:BitDepth* (_Integer_): Number of bits used to encode 1 sample on 1 channel (ie 8, 16, 24...)
|
15
|
+
class Wave
|
16
|
+
|
17
|
+
# Give the file extension
|
18
|
+
#
|
19
|
+
# Return::
|
20
|
+
# * _String_: The file extension (without .)
|
21
|
+
def getFileExt
|
22
|
+
return 'wav'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Deliver a file.
|
26
|
+
# The delivered file can be a shortcut to the source one.
|
27
|
+
#
|
28
|
+
# Parameters::
|
29
|
+
# * *iSrcFileName* (_String_): The source file to deliver from
|
30
|
+
# * *iDstFileName* (_String_): The destination file to be delivered
|
31
|
+
# * *iFormatConf* (<em>map<Symbol,Object></em>): The format configuration
|
32
|
+
# * *iMetadata* (<em>map<Symbol,Object></em>): The metadata that can be used while delivering the file
|
33
|
+
def deliver(iSrcFileName, iDstFileName, iFormatConf, iMetadata)
|
34
|
+
# Check if we can just make a shortcut
|
35
|
+
lShortcut = true
|
36
|
+
if (!iFormatConf.empty?)
|
37
|
+
if (iFormatConf[:Dither])
|
38
|
+
lShortcut = false
|
39
|
+
else
|
40
|
+
# Need to get the source file attributes (sample rate and bit depth)
|
41
|
+
require 'WSK/Common'
|
42
|
+
self.class.module_eval('include WSK::Common')
|
43
|
+
accessInputWaveFile(iSrcFileName) do |iHeader, iInputData|
|
44
|
+
lShortcut = !(((iFormatConf[:SampleRate] != nil) and
|
45
|
+
(iFormatConf[:SampleRate] != iHeader.SampleRate)) or
|
46
|
+
((iFormatConf[:BitDepth] != nil) and
|
47
|
+
(iFormatConf[:BitDepth] != iHeader.NbrBitsPerSample)))
|
48
|
+
next nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
if (lShortcut)
|
53
|
+
# Just create a shortcut
|
54
|
+
createShortcut(iSrcFileName, iDstFileName)
|
55
|
+
else
|
56
|
+
# We need to convert the Wave file: call SSRC
|
57
|
+
lTranslatedParams = [ '--profile standard', '--twopass' ]
|
58
|
+
iFormatConf.each do |iParam, iValue|
|
59
|
+
case iParam
|
60
|
+
when :SampleRate
|
61
|
+
lTranslatedParams << "--rate #{iValue}"
|
62
|
+
when :BitDepth
|
63
|
+
lTranslatedParams << "--bits #{iValue}"
|
64
|
+
when :Dither
|
65
|
+
lTranslatedParams << '--dither 4' if (iValue == true)
|
66
|
+
else
|
67
|
+
log_warn "Unknown Wave format parameter: #{iParam} (value #{iValue.inspect}). Ignoring it."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
lCmd = "#{@MusicMasterConf[:Formats]['Wave'][:SRCCmdLine]} #{lTranslatedParams.sort.join(' ')} \"#{iSrcFileName}\" \"#{iDstFileName}\""
|
71
|
+
log_info "=> #{lCmd}"
|
72
|
+
raise "Error while executing SSRC command \"#{lCmd}\": error code #{$?.exitstatus}" if (!system(lCmd)) or ($?.exitstatus != 0)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2012 Muriel Salvan (muriel@x-aeon.com)
|
4
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'yaml'
|
8
|
+
require 'digest/md5'
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
|
12
|
+
# Get a unique ID that will always be the same for any Hash having the same content
|
13
|
+
#
|
14
|
+
# Return::
|
15
|
+
# * _String_: The unique ID
|
16
|
+
def unique_id
|
17
|
+
return Digest::MD5.hexdigest(self.to_a.sort.to_yaml)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2012 Muriel Salvan (muriel@x-aeon.com)
|
3
|
+
# Licensed under the terms specified in LICENSE file. No warranty is provided.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'fileutils'
|
7
|
+
require 'optparse'
|
8
|
+
require 'rUtilAnts/Logging'
|
9
|
+
RUtilAnts::Logging::install_logger_on_object(:no_dialogs => true)
|
10
|
+
require 'MusicMaster/Utils'
|
11
|
+
require 'MusicMaster/FilesNamer'
|
12
|
+
require 'MusicMaster/RakeProcesses'
|
13
|
+
|
14
|
+
module MusicMaster
|
15
|
+
|
16
|
+
class Launcher
|
17
|
+
|
18
|
+
include MusicMaster::Utils
|
19
|
+
|
20
|
+
# Constructor
|
21
|
+
def initialize
|
22
|
+
parsePlugins
|
23
|
+
@DisplayHelp = false
|
24
|
+
@Debug = false
|
25
|
+
|
26
|
+
require 'optparse'
|
27
|
+
@Options = OptionParser.new
|
28
|
+
@Options.banner = "#{$0} [--help] [--debug] #{getOptionsBanner} <ConfigFile>"
|
29
|
+
@Options.on( '--help',
|
30
|
+
'Display help') do
|
31
|
+
@DisplayHelp = true
|
32
|
+
end
|
33
|
+
@Options.on( '--debug',
|
34
|
+
'Activate debug logs') do
|
35
|
+
@Debug = true
|
36
|
+
end
|
37
|
+
completeOptionParser(@Options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Execute the process
|
41
|
+
#
|
42
|
+
# Parameters::
|
43
|
+
# * *iArgs* (<em>list<String></em>): The list of arguments
|
44
|
+
# Return::
|
45
|
+
# * _Integer_: The error code
|
46
|
+
def execute(iArgs)
|
47
|
+
rErrorCode = 0
|
48
|
+
|
49
|
+
lError = nil
|
50
|
+
lConfFileName = nil
|
51
|
+
begin
|
52
|
+
lRemainingArgs = @Options.parse(iArgs)
|
53
|
+
if ((lRemainingArgs.size != 1) and
|
54
|
+
(!@DisplayHelp))
|
55
|
+
lError = RuntimeError.new("Please specify 1 config file (specified: \"#{lRemainingArgs.join(' ')}\")")
|
56
|
+
end
|
57
|
+
lConfFileName = lRemainingArgs.first
|
58
|
+
rescue Exception
|
59
|
+
lError = $!
|
60
|
+
end
|
61
|
+
if (lError == nil)
|
62
|
+
if (@DisplayHelp)
|
63
|
+
puts @Options
|
64
|
+
else
|
65
|
+
if (@Debug)
|
66
|
+
activate_log_debug(true)
|
67
|
+
end
|
68
|
+
# Read the MusicMaster configuration
|
69
|
+
begin
|
70
|
+
@MusicMasterConf = get_musicmaster_conf
|
71
|
+
rescue Exception
|
72
|
+
lError = $!
|
73
|
+
end
|
74
|
+
if (lError == nil)
|
75
|
+
# Read configuration
|
76
|
+
lError, lRecordConf = readConf(lConfFileName)
|
77
|
+
if (lError == nil)
|
78
|
+
# Check the conf. This is dependent on the process
|
79
|
+
lError = checkConf(lRecordConf)
|
80
|
+
if (lError == nil)
|
81
|
+
@RecordConf = lRecordConf
|
82
|
+
initialize_Utils
|
83
|
+
begin
|
84
|
+
lRakeTarget = getRakeTarget
|
85
|
+
rescue
|
86
|
+
lError = $!
|
87
|
+
end
|
88
|
+
if (lError == nil)
|
89
|
+
if debug_activated?
|
90
|
+
Rake::application.options.trace = true
|
91
|
+
displayRakeTasks
|
92
|
+
end
|
93
|
+
begin
|
94
|
+
Rake::Task[lRakeTarget].invoke
|
95
|
+
rescue
|
96
|
+
lError = $!
|
97
|
+
end
|
98
|
+
log_info 'Processed finished successfully.' if (lError == nil)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
if (lError != nil)
|
106
|
+
log_err "#{lError}#{(debug_activated? and lError.backtrace) ? "\n#{lError.backtrace.join("\n")}" : ''}"
|
107
|
+
rErrorCode = 1
|
108
|
+
end
|
109
|
+
|
110
|
+
return rErrorCode
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
# Give additional command line options banner
|
116
|
+
#
|
117
|
+
# Return::
|
118
|
+
# * _String_: Options banner
|
119
|
+
def getOptionsBanner
|
120
|
+
return ''
|
121
|
+
end
|
122
|
+
|
123
|
+
# Complete options with the specific ones of this binary
|
124
|
+
#
|
125
|
+
# Parameters::
|
126
|
+
# * *ioOptionParser* (_OptionParser_): The options parser to complete
|
127
|
+
def completeOptionParser(ioOptionParser)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Check configuration.
|
131
|
+
#
|
132
|
+
# Parameters::
|
133
|
+
# * *iConf* (<em>map<Symbol,Object></em>): The configuration
|
134
|
+
# Return::
|
135
|
+
# * _Exception_: Error, or nil in case of success
|
136
|
+
def checkConf(iConf)
|
137
|
+
return nil
|
138
|
+
end
|
139
|
+
|
140
|
+
# Initialize Rake processes and return the task to be built
|
141
|
+
#
|
142
|
+
# Return::
|
143
|
+
# * _Symbol_: Rake target to execute
|
144
|
+
def getRakeTarget
|
145
|
+
return nil
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
include FilesNamer
|
151
|
+
include RakeProcesses
|
152
|
+
include Utils
|
153
|
+
|
154
|
+
# Get the global MusicMaster configuration
|
155
|
+
# Read it from:
|
156
|
+
# 1. The environment variable MUSICMASTER_CONF_PATH
|
157
|
+
# 2. The installed MusicMaster directory
|
158
|
+
# 3. The current directory
|
159
|
+
#
|
160
|
+
# Return::
|
161
|
+
# * <em>map<Symbol,Object></em>: The MusicMaster configuration
|
162
|
+
def get_musicmaster_conf
|
163
|
+
rMusicMasterConf = nil
|
164
|
+
|
165
|
+
lConfSource = nil
|
166
|
+
# 1. Find from the environment
|
167
|
+
lConfigFileName = ENV['MUSICMASTER_CONF_PATH']
|
168
|
+
if (lConfigFileName == nil)
|
169
|
+
# 2. Find from the MusicMaster directory
|
170
|
+
lConfigFileName = "#{File.dirname(__FILE__)}/musicmaster.conf.rb"
|
171
|
+
if (File.exists?(lConfigFileName))
|
172
|
+
lConfSource = 'MusicMaster package local libraries'
|
173
|
+
else
|
174
|
+
# 3. Find from the current directory
|
175
|
+
lConfigFileName = "musicmaster.conf.rb"
|
176
|
+
if (File.exists?(lConfigFileName))
|
177
|
+
lConfSource = 'current directory'
|
178
|
+
else
|
179
|
+
# 4. Failure
|
180
|
+
end
|
181
|
+
end
|
182
|
+
else
|
183
|
+
lConfSource = 'MUSICMASTER_CONF_PATH environment variable'
|
184
|
+
end
|
185
|
+
|
186
|
+
# Check the configuration
|
187
|
+
if (lConfSource == nil)
|
188
|
+
raise "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."
|
189
|
+
else
|
190
|
+
if (File.exists?(lConfigFileName))
|
191
|
+
File.open(lConfigFileName, 'r') do |iFile|
|
192
|
+
begin
|
193
|
+
rMusicMasterConf = eval(iFile.read)
|
194
|
+
rescue Exception
|
195
|
+
raise "Invalid configuration file #{lConfigFileName} specified in #{lConfSource}: #{$!}"
|
196
|
+
rMusicMasterConf = nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
else
|
200
|
+
raise "Missing file #{lConfigFileName}, specified in #{lConfSource}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
return rMusicMasterConf
|
205
|
+
end
|
206
|
+
|
207
|
+
# Parse plugins
|
208
|
+
def parsePlugins
|
209
|
+
require 'rUtilAnts/Plugins'
|
210
|
+
RUtilAnts::Plugins::install_plugins_on_object
|
211
|
+
lLibDir = File.expand_path(File.dirname(__FILE__))
|
212
|
+
parse_plugins_from_dir('Processes', "#{lLibDir}/Processes", 'MusicMaster::Processes')
|
213
|
+
parse_plugins_from_dir('Formats', "#{lLibDir}/Formats", 'MusicMaster::Formats')
|
214
|
+
end
|
215
|
+
|
216
|
+
# Read configuration.
|
217
|
+
# Perform basic checks on it, independent of the process reading it.
|
218
|
+
#
|
219
|
+
# Parameters::
|
220
|
+
# * *iConfFile* (_String_): Configuration file
|
221
|
+
# Return::
|
222
|
+
# * _Exception_: Error, or nil in case of success
|
223
|
+
# * <em>map<Symbol,Object></em>: The configuration
|
224
|
+
def readConf(iConfFile)
|
225
|
+
rError = nil
|
226
|
+
rConf = nil
|
227
|
+
|
228
|
+
if (!File.exists?(iConfFile))
|
229
|
+
rError = RuntimeError.new("Missing configuration file: #{iConfFile}")
|
230
|
+
else
|
231
|
+
File.open(iConfFile, 'r') do |iFile|
|
232
|
+
rConf = eval(iFile.read)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
return rError, rConf
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|