MusicMaster 0.0.1.20101110 → 1.0.0.20120307

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.
Files changed (49) hide show
  1. data/AUTHORS +4 -1
  2. data/ChangeLog +28 -0
  3. data/LICENSE +1 -1
  4. data/README +2 -5
  5. data/ReleaseInfo +8 -8
  6. data/bin/Calibrate.rb +55 -0
  7. data/bin/Clean.rb +55 -0
  8. data/bin/DBConvert.rb +3 -1
  9. data/bin/Deliver.rb +73 -42
  10. data/bin/Mix.rb +39 -78
  11. data/bin/Process.rb +55 -0
  12. data/bin/Record.rb +63 -116
  13. data/bin/{Album.rb → old/Album.rb} +18 -18
  14. data/bin/{AnalyzeAlbum.rb → old/AnalyzeAlbum.rb} +11 -11
  15. data/bin/{DeliverAlbum.rb → old/DeliverAlbum.rb} +12 -12
  16. data/bin/{Fct2Wave.rb → old/Fct2Wave.rb} +11 -11
  17. data/{album.conf.rb.example → bin/old/album.conf.rb.example} +0 -0
  18. data/lib/MusicMaster/FilesNamer.rb +253 -0
  19. data/lib/MusicMaster/Formats/MP3.rb +50 -0
  20. data/lib/MusicMaster/Formats/Test.rb +44 -0
  21. data/lib/MusicMaster/Formats/Wave.rb +80 -0
  22. data/lib/MusicMaster/Hash.rb +20 -0
  23. data/lib/MusicMaster/Launcher.rb +241 -0
  24. data/lib/MusicMaster/Processes/ApplyVolumeFct.rb +4 -8
  25. data/lib/MusicMaster/Processes/Compressor.rb +50 -47
  26. data/lib/MusicMaster/Processes/Custom.rb +3 -3
  27. data/lib/MusicMaster/Processes/{AddSilence.rb → Cut.rb} +8 -4
  28. data/lib/MusicMaster/Processes/CutFirstSignal.rb +6 -3
  29. data/lib/MusicMaster/Processes/DCShifter.rb +30 -0
  30. data/lib/MusicMaster/Processes/GVerb.rb +3 -2
  31. data/lib/MusicMaster/Processes/Normalize.rb +7 -6
  32. data/lib/MusicMaster/Processes/SilenceInserter.rb +31 -0
  33. data/lib/MusicMaster/Processes/Test.rb +39 -0
  34. data/lib/MusicMaster/Processes/VolCorrection.rb +3 -3
  35. data/lib/MusicMaster/RakeProcesses.rb +1014 -0
  36. data/lib/MusicMaster/Symbol.rb +12 -0
  37. data/lib/MusicMaster/Task.rb +29 -0
  38. data/lib/MusicMaster/Utils.rb +467 -0
  39. data/lib/MusicMaster/old/Common.rb +101 -0
  40. data/musicmaster.conf.rb.example +42 -59
  41. data/record.conf.rb.example +374 -30
  42. metadata +91 -41
  43. data/TODO +0 -3
  44. data/bin/Master.rb +0 -60
  45. data/bin/PrepareMix.rb +0 -422
  46. data/lib/MusicMaster/Common.rb +0 -197
  47. data/lib/MusicMaster/ConfLoader.rb +0 -56
  48. data/lib/MusicMaster/musicmaster.conf.rb +0 -96
  49. data/master.conf.rb.example +0 -13
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: MusicMaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.20101110
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ - 20120307
10
+ version: 1.0.0.20120307
5
11
  platform: ruby
6
12
  authors:
7
13
  - Muriel Salvan
@@ -9,69 +15,109 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-11-10 00:00:00 +01:00
18
+ date: 2012-03-07 00:00:00 +01:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
- name: WaveSwissKnife
22
+ name: rake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 0
31
+ - 9
32
+ version: "0.9"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rUtilAnts
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 1
45
+ - 0
46
+ version: "1.0"
17
47
  type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: WaveSwissKnife
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
20
54
  requirements:
21
55
  - - ">="
22
56
  - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ - 0
60
+ - 1
23
61
  version: 0.0.1
24
- version:
62
+ type: :runtime
63
+ version_requirements: *id003
25
64
  description: Command line tool handling steps to deliver music album masters from recordings. Handle Track Mixing, Track Mastering, Track Master Delivery, Album Mastering and Album Master Delivery. Easy-to-use configuration files drive the complete processes.
26
- email: murielsalvan@users.sourceforge.net
65
+ email: muriel@x-aeon.com
27
66
  executables:
28
- - Album.rb
29
- - AnalyzeAlbum.rb
67
+ - Calibrate.rb
68
+ - Clean.rb
30
69
  - DBConvert.rb
31
70
  - Deliver.rb
32
- - DeliverAlbum.rb
33
- - Fct2Wave.rb
34
- - Master.rb
35
71
  - Mix.rb
36
- - PrepareMix.rb
72
+ - Process.rb
37
73
  - Record.rb
38
74
  extensions: []
39
75
 
40
76
  extra_rdoc_files: []
41
77
 
42
78
  files:
43
- - lib/MusicMaster/Common.rb
44
- - lib/MusicMaster/ConfLoader.rb
45
- - lib/MusicMaster/musicmaster.conf.rb
46
- - lib/MusicMaster/Processes/GVerb.rb
47
- - lib/MusicMaster/Processes/CutFirstSignal.rb
48
- - lib/MusicMaster/Processes/Normalize.rb
49
- - lib/MusicMaster/Processes/VolCorrection.rb
50
- - lib/MusicMaster/Processes/Custom.rb
51
- - lib/MusicMaster/Processes/AddSilence.rb
52
- - lib/MusicMaster/Processes/ApplyVolumeFct.rb
53
- - lib/MusicMaster/Processes/Compressor.rb
79
+ - AUTHORS
80
+ - bin/Calibrate.rb
81
+ - bin/Clean.rb
54
82
  - bin/DBConvert.rb
55
83
  - bin/Deliver.rb
56
- - bin/DeliverAlbum.rb
57
84
  - bin/Mix.rb
58
- - bin/PrepareMix.rb
85
+ - bin/old/album.conf.rb.example
86
+ - bin/old/Album.rb
87
+ - bin/old/AnalyzeAlbum.rb
88
+ - bin/old/DeliverAlbum.rb
89
+ - bin/old/Fct2Wave.rb
90
+ - bin/Process.rb
59
91
  - bin/Record.rb
60
- - bin/AnalyzeAlbum.rb
61
- - bin/Master.rb
62
- - bin/Fct2Wave.rb
63
- - bin/Album.rb
64
- - ReleaseInfo
65
- - README
66
- - LICENSE
67
- - AUTHORS
68
- - Credits
69
- - TODO
70
92
  - ChangeLog
71
- - album.conf.rb.example
72
- - master.conf.rb.example
93
+ - Credits
94
+ - lib/MusicMaster/FilesNamer.rb
95
+ - lib/MusicMaster/Formats/MP3.rb
96
+ - lib/MusicMaster/Formats/Test.rb
97
+ - lib/MusicMaster/Formats/Wave.rb
98
+ - lib/MusicMaster/Hash.rb
99
+ - lib/MusicMaster/Launcher.rb
100
+ - lib/MusicMaster/old/Common.rb
101
+ - lib/MusicMaster/Processes/ApplyVolumeFct.rb
102
+ - lib/MusicMaster/Processes/Compressor.rb
103
+ - lib/MusicMaster/Processes/Custom.rb
104
+ - lib/MusicMaster/Processes/Cut.rb
105
+ - lib/MusicMaster/Processes/CutFirstSignal.rb
106
+ - lib/MusicMaster/Processes/DCShifter.rb
107
+ - lib/MusicMaster/Processes/GVerb.rb
108
+ - lib/MusicMaster/Processes/Normalize.rb
109
+ - lib/MusicMaster/Processes/SilenceInserter.rb
110
+ - lib/MusicMaster/Processes/Test.rb
111
+ - lib/MusicMaster/Processes/VolCorrection.rb
112
+ - lib/MusicMaster/RakeProcesses.rb
113
+ - lib/MusicMaster/Symbol.rb
114
+ - lib/MusicMaster/Task.rb
115
+ - lib/MusicMaster/Utils.rb
116
+ - LICENSE
73
117
  - musicmaster.conf.rb.example
118
+ - README
74
119
  - record.conf.rb.example
120
+ - ReleaseInfo
75
121
  has_rdoc: true
76
122
  homepage: http://musicmaster.sourceforge.net/
77
123
  licenses: []
@@ -82,21 +128,25 @@ rdoc_options: []
82
128
  require_paths:
83
129
  - lib
84
130
  required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
85
132
  requirements:
86
133
  - - ">="
87
134
  - !ruby/object:Gem::Version
135
+ segments:
136
+ - 0
88
137
  version: "0"
89
- version:
90
138
  required_rubygems_version: !ruby/object:Gem::Requirement
139
+ none: false
91
140
  requirements:
92
141
  - - ">="
93
142
  - !ruby/object:Gem::Version
143
+ segments:
144
+ - 0
94
145
  version: "0"
95
- version:
96
146
  requirements: []
97
147
 
98
148
  rubyforge_project: musicmaster
99
- rubygems_version: 1.3.5
149
+ rubygems_version: 1.3.7
100
150
  signing_key:
101
151
  specification_version: 3
102
152
  summary: Command line tool helping recording, mixing and mastering music tracks and albums.
data/TODO DELETED
@@ -1,3 +0,0 @@
1
- * Add a Makefile generation procedure to ensure that modifying files will regenerate correct albums.
2
- * Implement Functions with inheritance for specialization.
3
- * Implement a Multiband compressor.
data/bin/Master.rb DELETED
@@ -1,60 +0,0 @@
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 'MusicMaster/Common'
9
- require 'rUtilAnts/Logging'
10
- RUtilAnts::Logging::initializeLogging('', '')
11
- require 'MusicMaster/ConfLoader'
12
-
13
- module MusicMaster
14
-
15
- # Execute the mastering
16
- #
17
- # Parameters:
18
- # * *iConf* (<em>map<Symbol,Object></em>): Configuration of the master
19
- # * *iWaveFile* (_String_): Wave file to master
20
- # Return:
21
- # * _String_: Name of the Wave file containing the result
22
- def self.execute(iConf, iWaveFile)
23
- rWaveFileToProcess = "#{$MusicMasterConf[:Master][:Dir]}/#{File.basename(iWaveFile)}"
24
-
25
- logInfo 'Copying Master file ...'
26
- FileUtils::cp(iWaveFile, rWaveFileToProcess)
27
- # Execute each step of the mastering to the wave file
28
- if (iConf[:Mastering] != nil)
29
- self.applyProcesses(iConf[:Mastering], rWaveFileToProcess, $MusicMasterConf[:Master][:Dir])
30
- end
31
-
32
- return rWaveFileToProcess
33
- end
34
-
35
- end
36
-
37
- rErrorCode = 0
38
- lConfFile, lWaveFile = ARGV[0..1]
39
- if ((lConfFile == nil) or
40
- (lWaveFile == nil))
41
- logErr 'Usage: Master <ConfFile> <WaveFile>'
42
- rErrorCode = 1
43
- elsif (!File.exists?(lConfFile))
44
- logErr "File #{lConfFile} does not exist."
45
- rErrorCode = 2
46
- elsif (!File.exists?(lWaveFile))
47
- logErr "File #{lWaveFile} does not exist."
48
- rErrorCode = 3
49
- else
50
- MusicMaster::parsePlugins
51
- FileUtils::mkdir_p($MusicMasterConf[:Master][:Dir])
52
- lConf = nil
53
- File.open(lConfFile, 'r') do |iFile|
54
- lConf = eval(iFile.read)
55
- end
56
- lFinalWave = MusicMaster::execute(lConf, lWaveFile)
57
- logInfo "===== Mastering finished in #{lFinalWave}"
58
- end
59
-
60
- exit rErrorCode
data/bin/PrepareMix.rb DELETED
@@ -1,422 +0,0 @@
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