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.
- 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
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: MusicMaster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
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:
|
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:
|
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
|
-
|
19
|
-
|
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
|
-
|
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:
|
65
|
+
email: muriel@x-aeon.com
|
27
66
|
executables:
|
28
|
-
-
|
29
|
-
-
|
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
|
-
-
|
72
|
+
- Process.rb
|
37
73
|
- Record.rb
|
38
74
|
extensions: []
|
39
75
|
|
40
76
|
extra_rdoc_files: []
|
41
77
|
|
42
78
|
files:
|
43
|
-
-
|
44
|
-
-
|
45
|
-
-
|
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/
|
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
|
-
-
|
72
|
-
-
|
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.
|
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
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
|