ならば

音とかで遊んでいたログ

普通奏者、快速奏者

「この奏者は『急行』、四分音符より長い音を演奏します」
みたいな。


作り方

ここからは技術的な話。

この画像はLilyPondのソースファイルから生成した楽譜の画像そのままで、生成後は全く手を加えていない。○の列は、LilyPond内部の音楽表現では歌詞として扱われるようになっている。ソースファイルには各○の描画コマンドを一個一個書いているわけではなくて、LilyPondの処理系*1にメロディとスキップする音の長さを与えて自動生成させた。

ソースファイル。
前半に歌詞の自動生成に使うSchemeの関数定義がある。後半が普通の(?)楽譜作成用のコード。

\version "2.16.2"

LOCAL = #(list "普通" (ly:make-duration 5) #'darkgreen)
RAPID = #(list "快速" (ly:make-duration 3) #'blue)
EXPRESS = #(list "急行" (ly:make-duration 2) #'green) 
LIMITED = #(list "特急" (ly:make-duration 1) #'red)
CAGE = #(list "4'33''" (ly:make-duration 0) #'black) 

%% from http://comments.gmane.org/gmane.comp.gnu.lilypond.general/64060
#(define (ly:duration=? dur1 dur2)
   (not (or (ly:duration<? dur1 dur2)
            (ly:duration<? dur2 dur1))))

#(define (stop-mark col)
   "draw a stop mark"
   (markup #:vspace 1.5
     #:raise 1.5
     #:with-color col (#:draw-circle 1.4 0.8 #f)))

#(define (make-a-name name col)
   "make a tag name"
   (list
    (make-music 'ContextSpeccedMusic
      'context-type 'Bottom
      'element (make-music 'PropertySet
                 'value (markup
                         #:line
                         (#:with-color col name))
                 'symbol 'vocalName))
    (make-music 'ContextSpeccedMusic
      'context-type 'Bottom
      'element (make-music 'PropertySet
                 'value 3
                 'symbol 'fontSize))))

#(define (make-a-word dur1 dur2 col)
   "make a stop mark with 'col in verse if 'dur1 > 'dur2"
   (make-music
    'LyricEvent
    'duration
    dur1
    'text
    (if (ly:duration<? dur2 dur1)
        (stop-mark col)
        (stop-mark #'white))))

makeVerse = #(define-music-function
              (parse location music list) 
              (ly:music? list?)
              "make a verse"
              (make-music 'SequentialMusic 'elements
                (append
                 (make-a-name (car list) (caddr list))
                 (map (lambda (x)
                        (make-a-word x (cadr list) (caddr list)))
                   (extract-all-durations music)))))

%% from http://lists.gnu.org/archive/html/lilypond-user/2012-03/msg00642.html
#(define (extract-all-durations music)
   (map! (lambda (m) (ly:music-property m 'duration))
     (extract-music music
       (lambda (m) (ly:duration?
                    (ly:music-property m 'duration))))))


\paper {
  #(set-paper-size "a4")
  markup-system-spacing #'minimum-distance = 12
  system-system-spacing #'minimum-distance = 38
}

\header {
  title = "線路は16小節続くよ"
  tagline = ##f
}

melody = \relative c'' {
  g4. d8 g8. d16 g8. a16 | b2 g | c4. c8 g4 a | b2. r4 | \break
  g4. d8 g8. d16 g8. a16 | b2 g | a4. a8 a4 b | a2. r4 | \break
  a4. a8 gis8. a16 b8. a16 | g!2 d | c'4 c g a | b2. r4 | \break
  e,4. fis8 g8. fis16 g8. e16 | d4. d8 g2 | b4. c8 b4 a | g2. r4|
  \bar "|."
}

<<
  \new Voice {
    \tempo 4 = 120
    \key g \major
    \melody
  }
  \new Lyrics \lyricmode { \makeVerse \melody \LOCAL }
  \new Lyrics \lyricmode { \makeVerse \melody \RAPID }
  \new Lyrics \lyricmode { \makeVerse \melody \EXPRESS }
  \new Lyrics \lyricmode { \makeVerse \melody \LIMITED }
  \new Lyrics \lyricmode { \makeVerse \melody \CAGE }
>>


LilyPond内部では音楽はSchemeのリストとして表現されている。
ある音楽構造がどのようなリストで表現されるか、というのは \displayMusic を使うと分かる。
左上がLilyPondのコード、左下が \displayMusic の出力、右が対応する楽譜。


逆にSchemeのS式をLilyPondのソースファイルに書いて楽譜を生成することもできる。


というわけで、冒頭の画像を生成するソースファイルは大まかには次のステップで作成した。

  1. 実現したい楽譜(歌詞の部分に○を表示した楽譜)の短い例を \markup コマンドを使って書く*2
  2. \displayMusic で1の内部表現を知る
  3. 2の内部表現を生成するSchemeの関数を作る
  4. メロディから各音の長さを抽出する関数を作る
    (実際はネットでそれらしきものを検索したらソースコードのコメントに書いてあるサイトに見つかったのでひっぱってきた)
  5. メロディから4の関数を使って得られる結果をもとに、3の関数を使って歌詞を作るように処理をまとめる

1から3の流れは公式ドキュメントに例示されている。
Extending LilyPond: 1.3.3 Doubling a note with slurs (example)

大まかにはこの通りだが、実際にはLilyPondもSchemeScheme以前に関数型言語の考え方もよく分かっていないので試行錯誤しかしていない気がする。何ひとつ分かってないではないか*3


備忘録

今回知ったLilyPondとの戯れ方をメモしておく。

Q:
「普通」、「急行」のように、歌詞の前に名前を付けるにはどうしたらいいのか?
A:
vocalName で設定する。歌詞の番号の場合は stanza で、これらは表示される位置も異なる。
Q:
休符にも歌詞を割り当てるにはどうしたらいいのか?
A:
\lyricmode で歌詞を作成し、語句にメロディとは別に長さを指定する。
\addlyrics や \lyricsto だと、メロディのリズムが強制的に当てはめられて、この場合は休符はスキップされる。
Q:
楽譜のタイトルと五線譜のスペースを調整するにはどうしたらいいのか? 五線譜と五線譜の間のスペースは?
A:
\paper の中で設定可能
Q:
調号を指定しているのに、音符にも -is や -es を付けないと変化記号が表示されないのはなぜか?
A:
そういう仕様。公式ドキュメントでも注記されている。

調号は 譜刻される 臨時記号にだけ影響を与え、音符の ピッチ には影響を与えません!

2.1.2 臨時記号と調号


Q:
変化記号のナチュラルを表示するにはどうしたらいいのか?
A:
g! のように "!" を付ける。
補足:
楽譜の記法としては不要だが注意喚起のためにあえて表示したい場合に使う。冒頭の楽譜の10小節目のgが例。

*1:実際はそこから呼び出されるSchemeの処理系

*2:歌詞は普通は単なる文字列だが、\markup を使うとお絵かきもできる

*3:LilyPondとはたまに戯れたくなるのだが、その頃には大体戯れ方を忘れている