Skip to main content
added code spec -removed indent
Source Link
RoB
  • 181
  • 6
% 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} 

 
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
} 

% 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}
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
}
% 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} 

 
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
} 

Source Link
RoB
  • 181
  • 6

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
}