TL;DR : You add some tags and there's a bash script at the end which automates the separate midi part generation (for Unix users only, sorry :P)
The midiMaximumVolume
option
In my quest for a multi-voice split, I found out about an option called
midiMaximumVolume
which can be set within a Staff
environment.
By setting this variable to a value between 0 and 1, it's possible to tone down (or entirely suppress) the volume of the staff it's placed within.
This needs to be set for each voice, so it's about as tedious as removing the music variables of other parts and then you still have to do the compiling/renaming shenanigans.
However it's already a great step towards automation of split-voices since it is a single parameter that can be targeted (instead of any custom \myvoice
variables one may use...)
Plus, this makes it possible to have the other voices at a low volume in the background by setting them to a lower volume (eg. main voice at 1, others at 0.3).
Introducing a tag system
So the idea is to tag each part in its staff with a name & tag so we can easily detect where to introduce the volume parameter as well as a name for each part (mainly for file-naming). I went with something like this :
%#%part:<part_name>
Which fits in a lilypond comment. It must be placed at the location where the volume option would be set. Using the example above, it would be like so :
% Score stucture
voicePartBass = \new Staff \with {
instrumentName = "Bass"
shortInstrumentName = "B"
midiInstrument = "violin"
} {
%#%part:Bass
\clef bass \voiceBass
} \addlyrics { \verseBass }
voicePartTenor = \new Staff \with {
instrumentName = "Tenor"
shortInstrumentName = "T"
midiInstrument = "violin"
} {
%#%part:Tenor
\clef "G_8" \voiceTenor
} \addlyrics {\verseTenor}
Bash magic
Now we have to target each tag and change it into the appropriate commands to use its voice as the main voice with the others in background, and repeat for all tags.
I made this into a bash function ly2partmidi
(at the end of the post).
You can now source the function and run
$ ly2partmidi test.ly
which generates test_<part_name1>.midi
, test_<part_name2>.midi
, etc. in one go !
To modulate the volume (or silence) voices :
$ ly2partmidi <file.ly> <mainvolume> <othervolume>
eg. to remove other voices completely ly2partmidi <file.ly> 1 0
or to have all but the main voice 0 1
, default is 1 0.3
.
If we set up the test.ly
file above properly, and run the function, it generates test_Soprano.midi
, test_Alto.midi
, test_Tenor.midi
and test_Bass.midi
files, as expected.
You can also add a midi2mp3 step in there, if you want.
The bash function :
ly2partmidi () {
lyfile=${1}
volumeMain=${2:-1} # level of the 'main' voice of a file
volumeOther=${3:-0.3} # level of the other voices (set to 0 if completely silent)
tagregex="^\s*%#%part:([a-zA-Z0-9_\-]+)"
setvolcmd="\\\set midiMaximumVolume = #"
# find the tags in the .ly file
mapfile -t fulltags < <( grep -Po ${tagregex} ${lyfile} )
tagnames=()
for t in ${fulltags[@]} ; do
t1=$(echo $t | sed -E "s/${tagregex}/\1/")
tagnames+=("$t1")
done
# get unique of each tag
fulltags=($(echo "${fulltags[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
tagnames=($(echo "${tagnames[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
# for each tag...
for i in ${!fulltags[@]}; do
ft=${fulltags[$i]}
t=${tagnames[$i]}
echo -e "\n--- Generating part [$t] ..."
echo -e "\tCopying lilypond script"
# make temp copy of .ly
t_lyfile=${lyfile/.ly/_$t.ly}
cp -f $lyfile $t_lyfile
echo -e "\tReplacing tags with midiVolume cmd"
# replace tags with the midiVolume cmds
sed -i -E "s/${ft}/${setvolcmd}${volumeMain}/g" $t_lyfile
sed -i -E "s/${tagregex}/${setvolcmd}${volumeOther}/g" $t_lyfile
echo -e "\tRunning lilypond engraving to generate midi"
# generate midi
lily_o=${t_lyfile/.ly/}
lilypond -s -dno-print-pages -o $lily_o $t_lyfile
rm $t_lyfile
done
}