「VexFlow 連桁」の版間の差分

提供:yonewiki
 
(同じ利用者による、間の11版が非表示)
50行目: 50行目:
   Formatter.FormatAndDraw(ctx, stave2, notes2);
   Formatter.FormatAndDraw(ctx, stave2, notes2);


   const stave3 = new Stave(710, 50, 450).setEndBarType(Barline.type.DOUBLE).setContext(ctx).draw();
   const stave3 = new Stave(710, 50, 450).setEndBarType(Barline.type.REPEAT_END).setContext(ctx).draw();
   const notes3_1 = [
   const notes3_1 = [
     new StaveNote({ keys: ["c/4"], duration: "8" }),  
     new StaveNote({ keys: ["c/4"], duration: "8" }),  
76行目: 76行目:
   const beams3 = [new Beam(notes3_1), new Beam(notes3_2), new Beam(notes3_3), new Beam(notes3_4)];
   const beams3 = [new Beam(notes3_1), new Beam(notes3_2), new Beam(notes3_3), new Beam(notes3_4)];


//  beams3.forEach((b) => b.setContext(ctx).draw());
  stave3.setContext(ctx).draw();
 
   notes3.forEach((StaveNote) => StaveNote.setStave(stave3));
   notes3.forEach((StaveNote) => StaveNote.setStave(stave3));
   const voice3 = new Voice({beat_value: 4, num_beats: 4}).setMode(3).addTickables(notes3);
   const voice3 = new Voice({beat_value: 4, num_beats: 4}).setMode(3).addTickables(notes3);
   new Formatter().joinVoices([voice3]).formatToStave([voice3], stave3);
   new Formatter().joinVoices([voice3]).formatToStave([voice3], stave3);
  voice3.setContext(ctx).draw();
  beams3.forEach((beam) => beam.setContext(ctx).draw());
})();
</yjavascript>
<syntaxhighlight lang="JavaScript" line start="1">
<div id="yonet202302_Output01"></div>
<script>
(function(){
  const {
    Factory,
    Stave,
    StaveNote,
    Formatter,
    Voice,
    Barline,
    Accidental,
    Beam,
  } = Vex.Flow;
  const f = new Factory({ renderer: { elementId: 'yonet202302_Output01', width: 1500, height: 195 } });
  const ctx = f.getContext();
  const stave1 = new Stave(10, 50, 350).setContext(ctx).draw();
  stave1.addClef("treble").setContext(ctx).draw();
  stave1.addTimeSignature('C').setContext(ctx).draw();
  const notes1 = [
    new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "1" })
    .addModifier(new Accidental('b'), 1),
  ];
  Formatter.FormatAndDraw(ctx, stave1, notes1);
  const stave2 = new Stave(360, 50, 350).setContext(ctx).draw();
  const notes2 = [
    new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "2" })
    .addModifier(new Accidental('n'), 1),
    new StaveNote({ keys: ["d/4", "f/4", "a/4"], duration: "4" })
    .addModifier(new Accidental('#'), 2),
    new StaveNote({ keys: ["e/4", "g/4", "b/4"], duration: "4" })
    .addModifier(new Accidental('##'), 2),
  ];
  Formatter.FormatAndDraw(ctx, stave2, notes2);
  const stave3 = new Stave(710, 50, 450).setEndBarType(Barline.type.REPEAT_END).setContext(ctx).draw();
  const notes3_1 = [
    new StaveNote({ keys: ["c/4"], duration: "8" }),
    new StaveNote({ keys: ["d/4"], duration: "8" }),
  ];
  const notes3_2 = [
    new StaveNote({ keys: ["e/4"], duration: "16" }),
    new StaveNote({ keys: ["f/4"], duration: "16" }),
    new StaveNote({ keys: ["g/4"], duration: "16" }),
    new StaveNote({ keys: ["a/4"], duration: "16" }),
  ];
  const notes3_3 = [
    new StaveNote({ keys: ["c/4"], duration: "8" }),
    new StaveNote({ keys: ["d/4"], duration: "8" }),
  ];
  const notes3_4 = [
    new StaveNote({ keys: ["e/4"], duration: "16" }),
    new StaveNote({ keys: ["f/4"], duration: "16" }),
    new StaveNote({ keys: ["g/4"], duration: "16" }),
    new StaveNote({ keys: ["a/4"], duration: "16" }),
  ];
  const notes3 = notes3_1.concat(notes3_2).concat(notes3_3).concat(notes3_4);
  const beams3 = [new Beam(notes3_1), new Beam(notes3_2), new Beam(notes3_3), new Beam(notes3_4)];


   stave3.setContext(ctx).draw();
   stave3.setContext(ctx).draw();
  notes3.forEach((StaveNote) => StaveNote.setStave(stave3));
  const voice3 = new Voice({beat_value: 4, num_beats: 4}).setMode(3).addTickables(notes3);
  new Formatter().joinVoices([voice3]).formatToStave([voice3], stave3);
   voice3.setContext(ctx).draw();
   voice3.setContext(ctx).draw();
   beams3.setContext(ctx).draw();
 
   beams3.forEach((beam) => beam.setContext(ctx).draw());


})();
</script>
</syntaxhighlight>
 これまで一小節分の音符をひとつの配列にまとめていましたが、連桁を描くにはより細かい単位での配列にしなければならず、ひとつの連桁ごとにグループにした配列に細分化しなければなりません。そして、細分化したものを繋げたものを、一小節のnotesとしています。3小節目のデータなので、自身で定義する変数名には基本的に3という接尾句をつけています。3小節目の1つめの連桁を 3_1 のようにします。
 notes3_1 ~ notes3_4 までを定義し、繋げたものを notes3 としました。定義は39~42行目がnotes3_1、43~48行目がnotes3_2、49~52行目がnotes3_3、53~58行目がnotes3_4です。
 notes3にまとめているのが、60行目です。配列を結合するのは、javascriptの関数です。VexFlow独自のものではありません。このように随所にプログラミングの技術が必要となるのがVexFlowの悪い点でしょう。
 notes3_1.concat(notes3_2) で notes3_1 の後ろに notes3_2 が結合されます。例では、その後ろにもさらに .concat関数を連続させて、同じことを繰り返しています。そして const notes3 = …; で notes3 を構築しています。
 連桁の描画をそれぞれの notes に対して実施します。62行目が記述された時点で、もともとの棒がいったん消えます。そして、new Beam(3_1) ~ new Beam(3_4)を配列にした、beams3を作ります。
 beams3の全ての要素に対して処理をするため beams3.forEach() という関数を使います。引数に、個別の値がひとつづつ付与されて繰り返し処理がされるものと考えてください。そして、その引数を使った関数をさらに定義しているのが、forEach の引数の中身となります。(Beam) => Beam.XXXXX) という表記で、各要素の値の.xxxxという関数を実行するという意味でここでは.setContext(ctx).draw()を実行して、連桁部分をデバイスコンテキストctxのエリアに描画しています。連桁の位置はVoiceの描画によって変動するので、先に Voice の処理を済ませる必要があります。
 Beamオブジェクトを使うので、最初の宣言で12行目のように記述するのを忘れないようにしてください。
 なんだかややこしいですが、これが連桁の描き方の基本です。他にもいろいろ方法はありますので、そのあたりは慣れてきてから取り組むといいでしょう。描画する順番は、5線譜(Stave)、StaveNote&Voice、Beamの順となります。
== '''異なる音の長さ同士における連桁''' ==
 前の項目では、同じ音分同士を繋げましたが、次は異なる長さのものを繋げてみましょう。連符については後で考えますので、8分16分、32分とその付点ありの音のみで考えましょう。表示は複雑ですが、記述としては容易に想像がつく変更です。付点が同時につかわれたり、間に32分が入ったりするだけです。
<div id="yonet202302_Output02"></div>
<yjavascript></script>
<script>
(function(){
  const {
    Factory,
    Stave,
    StaveNote,
    Formatter,
    Voice,
    Barline,
    Accidental,
    Beam,
    Dot,
  } = Vex.Flow;
  const f = new Factory({ renderer: { elementId: 'yonet202302_Output02', width: 1500, height: 195 } });
  const ctx = f.getContext();
  const stave1 = new Stave(10, 50, 350).setContext(ctx).draw();
  stave1.addClef("treble").setContext(ctx).draw();
  stave1.addTimeSignature('C').setContext(ctx).draw();
  const notes1 = [
    new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "1" })
    .addModifier(new Accidental('b'), 1),
  ];
  Formatter.FormatAndDraw(ctx, stave1, notes1);
  const stave2 = new Stave(360, 50, 350).setContext(ctx).draw();
  const notes2 = [
    new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "2" })
    .addModifier(new Accidental('n'), 1),
    new StaveNote({ keys: ["d/4", "f/4", "a/4"], duration: "4" })
    .addModifier(new Accidental('#'), 2),
    new StaveNote({ keys: ["e/4", "g/4", "b/4"], duration: "4" })
    .addModifier(new Accidental('##'), 2),
  ];
  Formatter.FormatAndDraw(ctx, stave2, notes2);
  const stave3 = new Stave(710, 50, 450).setEndBarType(Barline.type.REPEAT_END).setContext(ctx).draw();
  const notes3_1 = [
    dotted(new StaveNote({ keys: ["c/4"], duration: "8d" })),
    new StaveNote({ keys: ["d/4"], duration: "16" }),
  ];
  const notes3_2 = [
    dotted(new StaveNote({ keys: ["e/4"], duration: "16d" })),
    new StaveNote({ keys: ["f/4"], duration: "32" }),
    dotted(new StaveNote({ keys: ["g/4"], duration: "16d" })),
    new StaveNote({ keys: ["a/4"], duration: "32" }),
  ];
  const notes3_3 = [
    new StaveNote({ keys: ["c/4"], duration: "16" }),
    new StaveNote({ keys: ["d/4"], duration: "8" }),
    new StaveNote({ keys: ["d/4"], duration: "16" }),
  ];
  const notes3_4 = [
    new StaveNote({ keys: ["e/4"], duration: "16" }),
    new StaveNote({ keys: ["f/4"], duration: "16" }),
    new StaveNote({ keys: ["g/4"], duration: "32" }),
    new StaveNote({ keys: ["g/4"], duration: "32" }),
    new StaveNote({ keys: ["a/4"], duration: "16" }),
  ];
  const notes3 = notes3_1.concat(notes3_2).concat(notes3_3).concat(notes3_4);
  const beams3 = [new Beam(notes3_1), new Beam(notes3_2), new Beam(notes3_3), new Beam(notes3_4)];
  stave3.setContext(ctx).draw();
  notes3.forEach((StaveNote) => StaveNote.setStave(stave3));
  const voice3 = new Voice({beat_value: 4, num_beats: 4}).setMode(3).addTickables(notes3);
  new Formatter().joinVoices([voice3]).formatToStave([voice3], stave3);
  voice3.setContext(ctx).draw();
  beams3.forEach((beam) => beam.setContext(ctx).draw());
  function dotted(staveNote, noteIndex = -1) {
    if (noteIndex < 0) {
      Dot.buildAndAttach([staveNote], {
        all: true
      });
    }
    else {
      for(noteIndexValue of noteIndex){
        Dot.buildAndAttach([staveNote], {
          index: noteIndexValue
        });
      }
    }
    return staveNote;
  }
})();
})();
</yjavascript>
</yjavascript>


<syntaxhighlight lang="JavaScript" line start="1">
</syntaxhighlight>


 
 


[[VexFlow 使い方]]に戻る。
[[VexFlow 使い方]]に戻る。

2023年2月10日 (金) 22:37時点における最新版

VexFlow 使い方に戻る。

概要

 音符の旗同士を繋ぐ連桁(れんこう:英 Beem ビーム)について、記述します。連桁?はしげた?みなさんはなんと呼んでいるのでしょうか?自分も連桁とは思っていませんでしたが、そうなのだからそうなんだろう。音符の丸いところは符頭(たま)、丸いところから伸びた棒が符幹(ぼう)、8分音符より短いやつらにつけられる符尾(はた)と専門用語いっぱいあります。読み方はちがいますよ。ふとう、ふかん、ふび、ですね。

 

単純な連桁

 連桁には複雑なものもありますが、まずは単純な8分音符4つや2つを繋げるもの、つまり同じ音分同士を繋ぐというものをやってみましょう。16分音符同士とかです。


<div id="yonet202302_Output01"></div>
<script>
(function(){
  const {
    Factory,
    Stave,
    StaveNote,
    Formatter,
    Voice,
    Barline,
    Accidental,
    Beam,
  } = Vex.Flow;

  const f = new Factory({ renderer: { elementId: 'yonet202302_Output01', width: 1500, height: 195 } });
  const ctx = f.getContext();
  const stave1 = new Stave(10, 50, 350).setContext(ctx).draw();
  stave1.addClef("treble").setContext(ctx).draw();
  stave1.addTimeSignature('C').setContext(ctx).draw();

  const notes1 = [
    new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "1" })
    .addModifier(new Accidental('b'), 1), 
  ];
  Formatter.FormatAndDraw(ctx, stave1, notes1);

  const stave2 = new Stave(360, 50, 350).setContext(ctx).draw();
  const notes2 = [
    new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "2" }) 
    .addModifier(new Accidental('n'), 1), 
    new StaveNote({ keys: ["d/4", "f/4", "a/4"], duration: "4" })
    .addModifier(new Accidental('#'), 2), 
    new StaveNote({ keys: ["e/4", "g/4", "b/4"], duration: "4" })
    .addModifier(new Accidental('##'), 2), 
  ];
  Formatter.FormatAndDraw(ctx, stave2, notes2);

  const stave3 = new Stave(710, 50, 450).setEndBarType(Barline.type.REPEAT_END).setContext(ctx).draw();
  const notes3_1 = [
    new StaveNote({ keys: ["c/4"], duration: "8" }), 
    new StaveNote({ keys: ["d/4"], duration: "8" }), 
  ];
  const notes3_2 = [
    new StaveNote({ keys: ["e/4"], duration: "16" }), 
    new StaveNote({ keys: ["f/4"], duration: "16" }), 
    new StaveNote({ keys: ["g/4"], duration: "16" }), 
    new StaveNote({ keys: ["a/4"], duration: "16" }), 
  ];
  const notes3_3 = [
    new StaveNote({ keys: ["c/4"], duration: "8" }), 
    new StaveNote({ keys: ["d/4"], duration: "8" }), 
  ];
  const notes3_4 = [
    new StaveNote({ keys: ["e/4"], duration: "16" }), 
    new StaveNote({ keys: ["f/4"], duration: "16" }), 
    new StaveNote({ keys: ["g/4"], duration: "16" }), 
    new StaveNote({ keys: ["a/4"], duration: "16" }), 
  ];

  const notes3 = notes3_1.concat(notes3_2).concat(notes3_3).concat(notes3_4);

  const beams3 = [new Beam(notes3_1), new Beam(notes3_2), new Beam(notes3_3), new Beam(notes3_4)];

  stave3.setContext(ctx).draw();

  notes3.forEach((StaveNote) => StaveNote.setStave(stave3));
  const voice3 = new Voice({beat_value: 4, num_beats: 4}).setMode(3).addTickables(notes3);
  new Formatter().joinVoices([voice3]).formatToStave([voice3], stave3);
  voice3.setContext(ctx).draw();

  beams3.forEach((beam) => beam.setContext(ctx).draw());

})();
</script>

 これまで一小節分の音符をひとつの配列にまとめていましたが、連桁を描くにはより細かい単位での配列にしなければならず、ひとつの連桁ごとにグループにした配列に細分化しなければなりません。そして、細分化したものを繋げたものを、一小節のnotesとしています。3小節目のデータなので、自身で定義する変数名には基本的に3という接尾句をつけています。3小節目の1つめの連桁を 3_1 のようにします。


 notes3_1 ~ notes3_4 までを定義し、繋げたものを notes3 としました。定義は39~42行目がnotes3_1、43~48行目がnotes3_2、49~52行目がnotes3_3、53~58行目がnotes3_4です。


 notes3にまとめているのが、60行目です。配列を結合するのは、javascriptの関数です。VexFlow独自のものではありません。このように随所にプログラミングの技術が必要となるのがVexFlowの悪い点でしょう。


 notes3_1.concat(notes3_2) で notes3_1 の後ろに notes3_2 が結合されます。例では、その後ろにもさらに .concat関数を連続させて、同じことを繰り返しています。そして const notes3 = …; で notes3 を構築しています。


 連桁の描画をそれぞれの notes に対して実施します。62行目が記述された時点で、もともとの棒がいったん消えます。そして、new Beam(3_1) ~ new Beam(3_4)を配列にした、beams3を作ります。


 beams3の全ての要素に対して処理をするため beams3.forEach() という関数を使います。引数に、個別の値がひとつづつ付与されて繰り返し処理がされるものと考えてください。そして、その引数を使った関数をさらに定義しているのが、forEach の引数の中身となります。(Beam) => Beam.XXXXX) という表記で、各要素の値の.xxxxという関数を実行するという意味でここでは.setContext(ctx).draw()を実行して、連桁部分をデバイスコンテキストctxのエリアに描画しています。連桁の位置はVoiceの描画によって変動するので、先に Voice の処理を済ませる必要があります。


 Beamオブジェクトを使うので、最初の宣言で12行目のように記述するのを忘れないようにしてください。


 なんだかややこしいですが、これが連桁の描き方の基本です。他にもいろいろ方法はありますので、そのあたりは慣れてきてから取り組むといいでしょう。描画する順番は、5線譜(Stave)、StaveNote&Voice、Beamの順となります。


異なる音の長さ同士における連桁

 前の項目では、同じ音分同士を繋げましたが、次は異なる長さのものを繋げてみましょう。連符については後で考えますので、8分16分、32分とその付点ありの音のみで考えましょう。表示は複雑ですが、記述としては容易に想像がつく変更です。付点が同時につかわれたり、間に32分が入ったりするだけです。


 

VexFlow 使い方に戻る。