Как разделить многоголосные партитуры lilypond на несколько миди-выходов (например, для хоровой практики)

Потратив достаточно много времени на изучение lilypond и написание партитур для нескольких голосов, я часто сталкивался с проблемой создания отдельных голосовых файлов midi/mp3 из партитуры. Я был удивлен, обнаружив, что это еще не функция или что ни у кого не было простого способа сделать это, поскольку отдельные голосовые mp3-файлы являются ценным ресурсом для изучения многоголосного произведения!

Хотя можно просто временно удалить другие голоса из партитуры, скомпилировать, переименовать миди, изменить на другой голос, скомпилировать, переименовать и т. д. Это очень быстро становится очень утомительным. Кроме того, я считаю полезным, чтобы другие голоса по-прежнему звучали на низком уровне громкости на заднем плане, чтобы иметь ощущение гармонии.

Поэтому мне нужен способ генерировать эти отдельные голоса из стандартной партитуры с несколькими голосами.

Вот стандартный сценарий lilypond для 4-голосного примера, который можно использовать в качестве тестового файла:

\version "2.18.2"

global = {
  \key c \major
  \time 4/4
  \tempo "Allegro" 2 = 60
}

% Lyrics
verseBass = \lyricmode {Ho Ho Hoo, Ho Ho Hoo !
}
verseTenor = \lyricmode {Ha Ha Haa, Ha Ha Haa !
}
verseAlto = \lyricmode {Huu Hu Hu, Hu Hu Huu !
}
verseSoprano = \lyricmode {Hi Hi Hii, Hi Hi Hii !
}

% Music
voiceBass = \relative c {\global c4 b f'2 d4 g, c2 \bar "|."}
voiceTenor = \relative c' {\global g4 b c2 b4 f g2}
voiceAlto = \relative c' {\global e2 d4 g g b, e2}
voiceSoprano = \relative c'' {\global c8e d4 a2 d4 b c2}

% Score stucture
voicePartBass = \new Staff \with {
  instrumentName = "Bass"
  shortInstrumentName = "B"
  midiInstrument = "violin" 
} {
  
  \clef bass \voiceBass   
} \addlyrics { \verseBass }

voicePartTenor = \new Staff \with {
  instrumentName = "Tenor"
  shortInstrumentName = "T"
  midiInstrument =  "violin" 
} { 
  
  \clef "G_8" \voiceTenor
} \addlyrics {\verseTenor}

voicePartAlto = \new Staff \with {
  instrumentName = "Alto"
  shortInstrumentName = "A"
  midiInstrument = "violin" 
} {
  
  \voiceAlto   
} \addlyrics { \verseAlto }

voicePartSoprano = \new Staff \with {
  instrumentName = "Soprano"
  shortInstrumentName = "S"
  midiInstrument =  "violin" 
} { 
  
  \voiceSoprano
} \addlyrics {\verseSoprano}


\score {
    <<
      \new ChoirStaff << % choirstaff adds the fancy join on voice staves
        \voicePartSoprano
        \voicePartAlto
        \voicePartTenor 
        \voicePartBass
      >>
    >>
    \layout {}
    \midi {}
}

Какие выходы

введите описание изображения здесь

И миди файл со всем.

Ответы (4)

TL;DR: вы добавляете несколько тегов, и в конце есть bash-скрипт, который автоматизирует генерацию отдельной части midi (только для пользователей Unix, извините: P)

Вариант midiMaximumVolume_

В своем стремлении к многоголосному разделению я узнал об опцииmidiMaximumVolume , которую можно установить в Staffсреде. Установив для этой переменной значение от 0 до 1, можно уменьшить (или полностью подавить) громкость нотоносца, в котором она находится.

Это должно быть установлено для каждого голоса, поэтому это примерно так же утомительно, как удаление музыкальных переменных других частей, а затем вам все равно придется выполнять махинации с компиляцией / переименованием.

Однако это уже большой шаг к автоматизации разделения голосов, поскольку это единственный параметр, на который можно ориентироваться (вместо любых пользовательских \myvoiceпеременных, которые можно использовать...)

Кроме того, это позволяет использовать другие голоса на низком уровне громкости в фоновом режиме, установив для них меньшую громкость (например, основной голос на 1, другие на 0,3).

Представляем систему тегов

Итак, идея состоит в том, чтобы пометить каждую часть в ее нотоносце именем и тегом, чтобы мы могли легко определить, где ввести параметр громкости, а также имя для каждой части (в основном для именования файлов). Я пошел с чем-то вроде этого:

%#%part:<part_name>

Который подходит к комментарию lilypond. Он должен быть размещен в том месте, где будет установлен параметр громкости. Используя приведенный выше пример, это будет выглядеть так:

% 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 ly2partmidi(в конце поста).

Теперь вы можете получить функцию и запустить


        
Created with Raphaël 2.1.0 T A B

который генерирует test_<part_name1>.midi, test_<part_name2>.midiи т. д. за один раз!

Чтобы модулировать громкость (или тишину) голосов:

$ ly2partmidi <file.ly> <mainvolume> <othervolume>

например. чтобы полностью удалить другие голоса ly2partmidi <file.ly> 1 0или оставить все голоса, кроме основного 0 1, по умолчанию используется значение 1 0.3.

Если мы правильно настроим test.lyвышеуказанный файл и запустим функцию, она сгенерирует файлы test_Soprano.midi, и , как и test_Alto.midiожидалось .test_Tenor.miditest_Bass.midi

Вы также можете добавить туда шаг midi2mp3, если хотите.

Баш-функция:


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блоков в один файл. Каждый из этих блоков будет набирать музыку в какой-либо файл, если \layoutв нем есть блок, и будет помещать MIDI в какой-либо файл, если в нем есть \midiблок. Таким образом, вы можете добавить еще один блок оценки, который будет выглядеть следующим образом:

\score {
    \new Staff \with {
        midiInstrument = "saxophone"
        midiMinimumVolume = #1
        midiMaximumVolume = #1
    } \new Voice \soprano
    \new Staff \with {
        midiInstrument = "acoustic grand"
        midiMinimumVolume = #0.4
        midiMaximumVolume = #0.4
    } \new Voice \alto
    % etc for \tenor and \bass
    % NO \layout block!!
    \midi {}
}

Это создаст файл your-filename-0.midi, в котором ваше сопрано играет громкий саксофон, а остальные партии - тихое пианино. Вы не поместили \layoutв него блок, так что он не будет набирать музыку, просто сделает MIDI. Промойте и повторите для остальных трех голосов.

Конечно, вы можете дополнительно автоматизировать это. Я использую эту функцию (которую я спер где-то много лет назад, не помню где):

rehearsalMidi = #
(define-music-function
 (parser location name midiInstrument lyrics) (string? string? ly:music?)
 #{
     \unfoldRepeats <<
         \new Staff = "sopran" \new Voice = "sopran" { s1*0\f \sopran }
         \new Staff = "alt" \new Voice = "alt" { s1*0\f \alt }
         \new Staff = "tenor" \new Voice = "tenor" { s1*0\f \tenor }
         \new Staff = "bas" \new Voice = "bas" { s1*0\f \bas }
         \context Staff = $name {
             \set Score.midiMinimumVolume = #0.5
             \set Score.midiMaximumVolume = #0.5
             \set Score.tempoWholesPerMinute = #(ly:make-moment 100 4)
             \set Staff.midiMinimumVolume = #1.0
             \set Staff.midiMaximumVolume = #1.0
             \set Staff.midiInstrument = $midiInstrument
         }
         \new Lyrics \with {
             alignBelowContext = $name
         } \lyricsto $name $lyrics
     >>
 #})

Теперь я просто добавляю такие блоки,

\book {
    \bookOutputSuffix "sopran"
    \score {
        \rehearsalMidi "sopran" "soprano sax" \slovaI
        \midi { }
    }
}

по одному на каждый голос. Три аргумента \rehearsalMidi: голос, чья партия, инструмент, на котором эта партия должна быть сыграна, и лирика к ней. Поскольку в чешском языке мы называем четыре голоса сопраном , альтом , тенором и басом , я использую эти имена для своих голосов, но вы, несомненно, сможете изменить эту функцию, чтобы она соответствовала вашим потребностям. И просто сделайте имена файлов более красивыми, например , и т book. д. вместо и т. д.bookOutputSuffixmy-filename-sopran.midimy-filename-alt.midimy-filename-0.midi

Кстати, когда вы напишете это один раз, вы можете скопировать и вставить его во всю набранную вами хоровую музыку (что я и делаю уже несколько лет :)).

Вау, это тоже очень хороший подход! У меня недостаточно опыта в языке lilypond, чтобы создавать такие функции, как у вас, поэтому я выбрал «внешний» подход к автоматизации и оставил lilypond только для музыки.
Однако у меня есть вопрос о вашей функции: если он не набирает музыку, какой смысл добавлять тексты?
@RoB, эта функция - просто «генератор исходного кода», она просто записывает больший блок исходного кода. Ничего сложного. // Слова каким-то образом встраиваются в MIDI. Я предполагаю, что некоторые программы могут затем воспользоваться этим (особенно программы, которые «преобразовывают» MIDI обратно в партитуру).

Какими бы интересными ни были эти ответы, есть хороший аргумент в пользу решения с низким кодом, которое не требует внешних сценариев Bash, программ C или определения новых функций. Он использует две основные функции, встроенные в LilyPond: имена выходных файлов ( https://lilypond.org/doc/v2.22/Documentation/notation/output-file-names ) и включает ( https://lilypond.org/doc/ v2.22/Документация/обозначение/включая-lilypond-файлы ).

Рассмотрим следующую иерархию каталогов:

┌─ base
├─┬─ music
│ └─── example.ily
├─┬─ score
│ └─┬─ audio.ily
│   ├─ example.ily
│   └─ printed.ily
├─┬─ words
│ └─── example.ily
└─── compile.ly

Содержимое вашей базовой папки состоит всего из трех каталогов ( music, words, score) и одного файла компиляции ( compile.ly).

Внутри ваших папок musicи wordsнаходятся просто .ilyфайлы, содержащие, соответственно, вашу музыку и переменные слова.

Внутри вашей scoreпапки находится один .ilyфайл, который использует два \includeоператора для ссылки на два других .ilyфайла:

  1. a \bookи \scoreблок, который приводит только к печатной музыке ( printed.ily)
  2. несколько \bookи \scoreблоков, которые приводят только к аудиофайлам ( audio.ily)

Как это выглядит?

compile.ly

\version "2.22.0"
\include "./music/example.ily"
\include "./words/example.ily"
\include "./score/example.ily"

music/example.ily

sop = \relative c' {
  c4 d e f
  e d e2
}
alt = \relative c' {
  e4 f g a
  g fis g2
}
ten = \relative c' {
  c2 b
  a g
}
bas = \relative c {
  a2 e'
  a, a
}

words/example.ily

sop_words = \lyricmode {
  so -- pra -- no
  words they are here
}
alt_words = \lyricmode {
  al -- to words be
  here yo ho
}
ten_words = \lyricmode {
  ten -- or
  words -- ies
}
bas_words = \lyricmode {
  gol -- lum
  gol -- lum
}

score/example.ily

\include "./printed.ily"
\include "./audio.ily"

score/printed.ily

\book {
  \bookOutputName "my_printed_score"
  \score {
    <<
      \new ChoirStaff <<
        \new Voice = "sop" \sop
        \new Lyrics \lyricsto sop \sop_words
        \new Voice = "alt" \alt
        \new Lyrics \lyricsto alt \alt_words
        \new Voice = "ten" { \clef "treble_8" \ten }
        \new Lyrics \lyricsto ten \ten_words
        \new Voice = "bas" { \clef bass \bas }
        \new Lyrics \lyricsto bas \bas_words
      >>
    >>
    \layout {}
  }
}

score/audio.ily

\book {
  \bookOutputName "midi_sop"
  \score {
    \new Staff \sop
    \midi {}
  }
}
\book {
  \bookOutputName "midi_alt"
  \score {
    \new Staff \alt
    \midi {}
  }
}
\book {
  \bookOutputName "midi_ten"
  \score {
    \new Staff \ten
    \midi {}
  }
}
\book {
  \bookOutputName "midi_bas"
  \score {
    \new Staff \bas
    \midi {}
  }
}

Как я уже сказал выше, две функции, которые имеют значение здесь, это bookOutputNameи include.

Используя bookOutputName, вы можете создать несколько файлов — в этом случае один будет содержать только музыку сопрано, другой — только музыку альта и т. д. Конечно, при желании вы можете добавить другие инструменты и контролировать их громкость. являются и их стерео позиции (левое ухо / правое ухо), среди других вариантов:

\book {
  \bookOutputName "midi_sop_bas"
  \score {
   <<
    \new Staff \with {
      midiMinimumVolume = #0.8
      midiPanPosition = #-1
    } \sop
    \new Staff \with {
      midiMaximumVolume = #0.5
      midiPanPosition = #1
    } \bas
   >>
    \midi {}
  }
}

(Причиной вызова \includeявляется закомментировать %файл \include "./printed.ily", когда вы довольны тем, как выглядит музыка, и хотите только настроить то, как она звучит. Компиляция только MIDI без блока макета часто на несколько порядков быстрее.)

В этом случае все, что вам нужно сделать сейчас, это добавить еще несколько блоков \book, \scoreупорядочить и структурировать их так, как вы хотите слышать свои MIDI-файлы, а затем, если вы так склонны, использовать их в \includeутверждениях (или не делайте), чтобы контролировать, какие из них компилируются.

Нанчо Альварес , наборщик LilyPond партитур Томаса Луиса де Виктории ( Partituras de Tomás Luis de Victoria ), написал программу на C, которая разбивает MIDI-дорожки на отдельные MIDI-файлы: splitmidi.c .