VexFlow 入門その1

提供:yonewiki

VexFlow 使い方へ戻る。

概要

 ひとつひとつの構成要素を知る前に入門用の楽譜をいくつか描いて全貌をつかもうとするのが最初です。公式サイトにもあるものと同じです。このサイトでは使い方を最後まで学習すると以下のようなスコアブックのような描画もできるようになります。

 

五線譜

 五線譜を描いてみます。目安になる線を引きました。左上の角の頂点が(x, y)対応で(1, 1)、そこから時計回りに(499, 1)→(499, 199)→(410, 100)→(10, 40)という線です。


 スクリプトは以下のとおり。

<div id="output"></div>
//2023-09Mid:<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
//2024-03Mid:Version
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.2.2/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave
} = Vex.Flow;

// Create an SVG renderer and attach it to the DIV element named "boo".
const div = document.getElementById('output');
const renderer = new Renderer(div, Renderer.Backends.SVG);

// Configure the rendering context.
renderer.resize(500, 200);
const context = renderer.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave = new Stave(10, 40, 400);

// Add a clef and time signature.
stave.addClef('treble').addTimeSignature('C');

// Connect it to the rendering context and draw!
stave.setContext(context).draw();
</script>

 最初に、div に 出力するタグを取得する。ここでは <div id="output"></div> のタグ全体要素を取得している。次にnew Renderer(div, Renderer.Backends.SVG); でSVGでの描画処理をしてくれるようになります。他にもRenderer.Backends.CANVASという指定もできます。自分はSVGバージョンが好みです。なんか問題報告が少ない。CANVASはスケールサイズ表示がおかしいとか言ってる人もいます。renderer.resize(500, 200); は描画エリアのサイズを指定しています。そして、この描画エリアに五線譜のオブジェクトとして構築した stave を配置します。


 New Stave(10, 40, 400)で、描画エリアの x = 10, y = 40の位置に幅 400 の五線譜を描きます。(410, 100)の頂点位置をみればわかりますが、きっちり 10+400 の大きさになっています。五線譜の真ん中の第三線が中央になっており縦 200 が 1 行にちょうど良いようです。楽譜部分の表示スケールを変更して小さくする予定なので、その時にまた、描画領域について考えてみたいと思います。


 stave.addClef('treble').addTimeSignature('4/4'); で treble ト音記号 4/4 つまり4分の4拍子という五線譜になります。


 自分は、持っているPCが12型で1920x1280を使っていますので、表示倍率を150%にしています。その関係で、実質は横サイズが1920の66%で、横は1280pxと変わらない表示領域です。したがって、Wikiの横幅も最大にして、1000pxくらいがちょうどいい限界の大きさです。しかし、そのサイズでは、4小節くらいを1行にするには楽譜が大きすぎるので、もうちょっと小さくしないといけないなとは思っていますが、その処理も、もうちょっと後でやるとします。続けて、実際の音符を配置してみます。


 拍子記号は以下のようなものが使えます。

シーケンス 意味
C Cを表示(4/4拍子)
C| Cの横中央に縦棒を表示(2/2拍子)
n1/n2 n2 分の n1 拍子

 

五線譜に音符を配置

 目安になる線を引きました。左上の角の頂点が(x, y)対応で(1, 1)、そこから時計回りに(499, 1)→(499, 199)→(360, 100)です。


スクリプトは以下のようなものです。


const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Formatter
} = Vex.Flow;

const div2 = document.getElementById('output2');
const renderer2 = new Renderer(div2, Renderer.Backends.SVG);

// Configure the rendering context.
renderer2.resize(500, 200);
const context2 = renderer2.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave2 = new Stave(10, 40, 400);

// Add a clef and time signature.
stave2.addClef('treble').addTimeSignature('C|');

// Connect it to the rendering context and draw!
stave2.setContext(context2).draw();

// Create the notes
const notes2 = [
  new StaveNote({
    keys: ['c/5'],
    duration: 'q'
  }),
  new StaveNote({
    keys: ['d/4'],
    duration: 'q'
  }),
  new StaveNote({
    keys: ['b/4'],
    duration: 'qr'
  }),
  new StaveNote({
    keys: ['c/4', 'e/4', 'g/4'],
    duration: 'q'
  }),
];

// Create a voice in 4/4 and add above notes
const voice2 = new Voice({ num_beats: 4, beat_value: 4 });
voice2.addTickables(notes2);

// Format and justify the notes to 400 pixels.
new Formatter().joinVoices([voice2]).format([voice2], 350);

// Render voice
voice2.draw(context2, stave2);


 変数名が最初の楽譜とダブるところは数字を付けました。一番最初の StaveNote, Voice, Formatterの部分は最初のと共通なので書き換えが必要です。あとの部分で2重で定義してもダメです。最初のがエラーになって、致命的エラーで何も描画処理がされなくなります。コード全体としては、音符を付ける部分は、五線譜に付け足すので、すべて、最初のコードより後ろに付け足す感じです。const notes2 以降が付け足した部分です。ここまで書いて言えるのは VexFlow はプログラミングに近いです。音楽や楽譜しか知らない人が、この方式で楽譜を描くのは難しいと感じると思います。ABC記述方式のモノが好まれる理由が少しわかります。ただVexflowの方が複雑な記述であるゆえに、複雑な楽譜が書ける言えるのかもしれない。まるで配列の中にひとつづつの音符を生成している感じで、ひとつの音符ごとに区切って new StaveNote() 関数に引数としてオブジェクト値(自分はこれをPerlの連想配列と似ているので連想配列とちょくちょく呼んでいます)を指定しています。プロパティ名にkeys: とduration: が指定されます。プロパティ名であることがわかりやすいように:までつけていますが名前自体は keys と duration です。keys プロパティ名に対応するものとして、配列が指定されるようです。値が一つしかなくてもです。そこに音の高さ相当する値、音階名と段階番号を付けます。実音記号のA(ラ), B(シ), C(ド), D(レ), E(ミ), F(ファ), G(ソ)に対応していて、/ で区切って段階の番号を付与します。


 ピアノと同じで88音の下から1, 2, … , 10, 11 まであります。ピアノ譜のト音記号の五線譜に下に一つ補助線を付けたドが C/4 に相当します。通常、ト音記号の五線譜には E/3 が最低音、 最高の音しても G/6 までくらいを使います。C/6 から 上の音での演奏が続くときは オクターブ記号を使って、その下に描くことがあります。ギターも同じです。ギターは4オクターブしかありません。が一番上の音付近を使うときは、オクターブ記号を置いて、一段階下げて表記します。どの楽器もピアノの88鍵盤には負けます。ギターの一番低い音でもE/3です。クラリネットの音域もギターに似ていますが、最高音はギターよりも狭い範囲が一般的です。最高音のあたりはかなりうまい人だと出せなくはない音ですが狙うのはスゴイ難しいです。ベースとピアノの最低音あたりはヘ音記号の五線譜に書かれることが普通です。ピアノはト音記号とヘ音記号の二つ同時に使うこともあります。音域やばいっす。バイオリンあたりはテノール記号(ハ音記号)というト音記号でもヘ音記号でもないものを使う場合があります。この3種類しかないと思います。あとは変則の五線譜として、通常の位置とは異なる部分に、記号を配置する手法があります。


 durationの値には文字列が設定されて、 q が四分音符、h が2分音符、w が全音符を表します。8, 16, 32, 64 がそれぞれ8分音符、 16分音符、 32分音符、64分音符に対応しますが、連符もありますので、ひとことで言えるほど簡単ではないです。後ろに r をつけると休符になります。rest の r だそうです。 voice2.draw(context2, stave2);


 このようにして作った notes2 を Voiceという単位に格納するため new Voice({ num_beats: 4, beat_value: 4 }); でプロパティ名 num_beats と beat_value を持ったオブジェクトを引数にしています。ともに 4 拍子の中に 4つの拍子をもった音だと定義されていて、その中に、notes2 を取り込むべく、voice2.addTickables(notes2); としています。


 そして、これらを400pxの五線譜の中に納まるように、new Formatter().joinVoices([voice2]).format([voice2], 350); としています。x = 350のところに最後の音符が収まっていることも確認できます。このように処理した voice2 を context2 の描画領域に stave2 の 五線譜に描画します。


 duration に設定できる値は以下のようなものです。

シーケンス 意味
w 全音符
h 2分音符
q 4分音符
8 8分音符
16 16分音符
32 32分音符

 

五線譜に音符を配置 2Voices


 スクリプトは以下のようなものです。

<div id="output3"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Formatter
} = Vex.Flow;
//******************ここまで共通**
// Create an SVG renderer and attach it to the DIV element named "boo".
const div3 = document.getElementById('output3');
const renderer3 = new Renderer(div3, Renderer.Backends.SVG);

// Configure the rendering context.
renderer3.resize(500, 200);
const context3 = renderer3.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave3 = new Stave(10, 40, 400);

// Add a clef and time signature.
stave3.addClef('treble').addTimeSignature('4/4');

// Connect it to the rendering context and draw!
stave3.setContext(context3).draw();

// Create the notes
const notes3 = [
  new StaveNote({
    keys: ['c/5'],
    duration: 'q'
  }),
  new StaveNote({
    keys: ['d/4'],
    duration: 'q'
  }),
  new StaveNote({
    keys: ['b/4'],
    duration: 'qr'
  }),
  new StaveNote({
    keys: ['c/4', 'e/4', 'g/4'],
    duration: 'q'
  }),
];

const notes32 = [new StaveNote({
  keys: ['c/4'],
  duration: 'w'
})];

// Create a voice in 4/4 and add above notes
const voices3 = [
  new Voice({
    num_beats: 4,
    beat_value: 4
  }).addTickables(notes3),
  new Voice({
    num_beats: 4,
    beat_value: 4
  }).addTickables(notes32),
];

// Format and justify the notes to 400 pixels.
new Formatter().joinVoices(voices3).format(voices3, 350);

// Render voices.
voices3.forEach(function(v) {
  v.draw(context3, stave3);
});


 和音を除いて、通常、一人では演奏できないような記譜法を2Voicesと呼んでいます。和音を引けない楽器ならそれだけで2Voices 3Voicesとも言えますが、この例のように1小節の中を休符と音符とで分かち合うのを1Voiceとした考え方を保有する記譜の場合は、特別な記述が必要です。別の音という意味付けになります。スコア譜なんかではよく2Voiceや3Voiceの記述が入ります。アレンジするうえで、音を足していくことはよくあるためです。ただしスコア譜では、一小節を短くして |1Voice(2Voice)|のように記譜している方式もよくあります。パートを増やせばいいじゃんと思いますが、限られたスペースにちょっとした音のためだけにパートを増やすことは通常は、実施しません。


 const notes32の部分の定義のように、音符の定義を別で実施します。Voiceを生成するときに、2つの音符をまとめるときに配列に個別のオブジェクトとして定義します。そして作られた配列2の大きさのVoiceをFormatter関数を呼び出して、そのメンバ関数 joinVoices で結合します。joinVoices 関数は返り値が formatter 形式で .format も Formatter のメンバ関数です。そして 350px の中に描画するようになります。そして、2要素の配列の Voices それぞれを繰り返し処理によって、引数の描画エリア context3 五線譜 stave3 に音符を配置して描画します。


五線譜に音符を配置 付点臨時記号


 スクリプトは以下のようなものです。

<div id="output4"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Accidental,
  Formatter,
  Dot
} = Vex.Flow;
//******************ここまで共通**
</script>
<script>
// Create an SVG renderer and attach it to the DIV element named "boo".
const div4 = document.getElementById('output4');
const renderer4 = new Renderer(div4, Renderer.Backends.SVG);

// Configure the rendering context.
renderer4.resize(500, 200);
const context4 = renderer4.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave4 = new Stave(10, 40, 400);

// Add a clef and time signature.
stave4.addClef('treble').addTimeSignature('4/4');

// Connect it to the rendering context and draw!
stave4.setContext(context4).draw();

// Create the notes
const notes4 = [
  dotted(new StaveNote({
    keys: ['c##/5'],
    duration: '8d'
  }).addModifier(new Accidental('##'))),
  new StaveNote({
    keys: ['db/4'],
    duration: '16'
  }).addModifier(new Accidental('b')),
  dotted(new StaveNote({
    keys: ['b/4', 'd/5', 'f/5'],
    duration: 'h'
  }).addModifier(new Accidental('#'),0),[0, 2]),
  dotted(new StaveNote({
    keys: ['c/4', 'e/4', 'g/4'],
    duration: '8'
  })),
];

Formatter.FormatAndDraw(context4, stave4, notes4);

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;
}
</script>


 付点を付けるにはDot.buildAndAttach([ staveNoteオブジェクト ], { index: 数値 }) といった関数を実行する必要があります。例では何度も呼び出すのが面倒なので、ユーザ関数を定義しています。公式では、特定のひとつの音符にしか、付点をつけることができないユーザ関数になっていたので、引数に数値単独の値を付与するところに、数値配列に、何番目の音符に付点をつけるかを指定するようなものに変更しています。Formatter.FormatAndDraw(context4, stave4, notes4); だけでも五線譜に音符を描画できるという便利な一面も紹介してくれています。


 臨時記号をつける処理をしているが、keys: ['c##/5']のように指定しても描画には反映されない。描画に影響しているのは、.addModifier(new Accidental('#'), 0) という部分です。一つ目の引数で Accidental オブジェクトのコンストラクタ引数に臨時記号の種類を与えています。##が二重シャープ。#がシャープ。bがフラット。bbが二重フラットです。今回は使いませんでしたが、ナチュラルはnです。.addModifier の2つめの引数はコードになっているときの、何番目の音に適用するかを指定しています。


 改めてまとめると、臨時記号の種類は以下のようになっています。

シーケンス 意味
n ナチュラル
# シャープ(半音上げる)
## ダブルシャープ(半音2回上げる)
b フラット(半音下げる)
bb ダブルフラット(半音2回下げる)


 Dot.…によって付点をつけるだけで、長さに不整合が起こっても記譜に問題は起こりません。どちらかというと一小節の中に納まるように配置されるかは、durationの組み合わせのほうが重要です。


 今回はト音記号 treble からヘ音記号 bass に変えました。他にも以下のような記号があります。

シーケンス 意味
treble ト音記号 𝄞
bass ヘ音記号 𝄢
alto ハ音記号(アルト) 𝄡
percussion 打楽器記号
tenor ハ音記号(テナー) 𝄡一つ上にシフト
soprano ハ音記号(ソプラノ) 𝄡二つ下にシフト
mezzo-soprano ハ音記号(メゾソプラノ) 𝄡一つ下にシフト
baritone-c ハ音記号(バリトン) 𝄡二つ上にシフト
baritone-f ヘ音記号(バリトン) 𝄢一つ下にシフト
subbass ヘ音記号(サブバス) 𝄢一つ上にシフト
french ト音記号(フレンチ) 𝄞一つ下にシフト

 

五線譜に音符を配置 連桁(れんこう)・ビーム・梁 new Beam()による方法


 スクリプトは以下のようなものです。

<div id="output5"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Accidental,
  Beam,
  Formatter,
  Dot
} = Vex.Flow;
//******************ここまで共通**
</script>
<script>
// Create an SVG renderer and attach it to the DIV element named "boo".
const div5 = document.getElementById('output5');
const renderer5 = new Renderer(div5, Renderer.Backends.SVG);

// Configure the rendering context.
renderer5.resize(500, 200);
const context5 = renderer5.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave5 = new Stave(10, 40, 400);

// Add a clef and time signature.
stave5.addClef('treble').addTimeSignature('4/4');

// Connect it to the rendering context and draw!
stave5.setContext(context5).draw();

// Create the notes
const notes51 = [
    dotted(
        new StaveNote({
            keys: ["e##/5"],
            duration: "8d",
        }).addModifier(new Accidental("##"))
    ),
    new StaveNote({
        keys: ["b/4"],
        duration: "16",
    }).addModifier(new Accidental("b")),
];

const notes52 = [
    new StaveNote({
        keys: ["c/4"],
        duration: "8",
    }),
    new StaveNote({
        keys: ["d/4"],
        duration: "16",
    }),
    new StaveNote({
        keys: ["e/4"],
        duration: "16",
    }).addModifier(new Accidental("b")),
];

const notes53 = [
    new StaveNote({
        keys: ["d/4"],
        duration: "16",
    }),
    new StaveNote({
        keys: ["e/4"],
        duration: "16",
    }).addModifier(new Accidental("#")),
    new StaveNote({
        keys: ["g/4"],
        duration: "32",
    }),
    new StaveNote({
        keys: ["a/4"],
        duration: "32",
    }),
    new StaveNote({
        keys: ["g/4"],
        duration: "16",
    }),
];

const notes54 = [
    new StaveNote({
        keys: ["d/4"],
        duration: "q",
    }),
];

const allNotes5 = notes51.concat(notes52).concat(notes53).concat(notes54);

// Create the beams for the first three groups.
// This hides the normal stems and flags.
const beams5 = [new Beam(notes51), new Beam(notes52), new Beam(notes53)];

Formatter.FormatAndDraw(context5, stave5, allNotes5);

// Draw the beams and stems.
beams5.forEach((b) => {
    b.setContext(context5).draw();
});

//********前の記譜と共通の関数です。2重定義しないように注意******
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;
}
//********ここまで
</script>


 音符の旗を描画せずにつなぐのを連桁といいます。表紙のひと固まりを繋ぎ演奏しやすいような表記にしています。8分音符の場合4つをつないで、2分音符のかたまりにしたりもします。例の楽譜では3拍目が16分音符で四分音符1つ分のひとかたまりになっています。16分音符の3つ目が32分なっています。キッチリ演奏しようとすると32分はめちゃくちゃ短いのでアタックからの立ち上がりが早い楽器でかつ技術力が要求される奏法になってきます。そんな演奏面の話はどうでもよいでしょうか。


 連桁は、Beam というシーケンスによって描画されます。Notesを連桁にしたい塊でひとつになるように作ると、const beams5 = [new Beam(notes51), new Beam(notes52), new Beam(notes53)];という記法によって、連桁のかたまりごとに配列にすることができます。このそれぞれの要素について関数、new Beam(notes51).setContext(context5).draw();のような操作によって蓮桁の描画準備がされます。小節の全体は const allNotes5 = notes51.concat(notes52).concat(notes53).concat(notes54); のように結合されたものを.FormatAndDraw(context5, stave5, allNotes5);のようにして描画されます。

 

五線譜に音符を配置 連桁(れんこう)・ビーム・梁 Beam.generateBeams()による方法


 スクリプトは以下のようなものです。

<div id="output6"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Accidental,
  Beam,
  Formatter,
  Dot
} = Vex.Flow;
//******************ここまで共通**
</script>
<script>
// Create an SVG renderer and attach it to the DIV element named "boo".
const div6 = document.getElementById("output6");
const renderer6 = new Renderer(div6, Renderer.Backends.SVG);

// Configure the rendering context.
renderer6.resize(500, 500);
const context6 = renderer6.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave6 = new Stave(10, 40, 400);

// Add a clef and time signature.
stave6.addClef("treble").addTimeSignature("4/4");

// Connect it to the rendering context and draw!
stave6.setContext(context6).draw();

const notes6 = [
    dotted(new StaveNote({ keys: ["e##/5"], duration: "8d" }).addModifier(new Accidental("##"))),
    new StaveNote({ keys: ["b/4"], duration: "16" }).addModifier(new Accidental("b")),
    new StaveNote({ keys: ["c/4"], duration: "8" }),
    new StaveNote({ keys: ["d/4"], duration: "16" }),
    new StaveNote({ keys: ["e/4"], duration: "16" }).addModifier(new Accidental("b")),
    new StaveNote({ keys: ["d/4"], duration: "16" }),
    new StaveNote({ keys: ["e/4"], duration: "16" }).addModifier(new Accidental("#")),
    new StaveNote({ keys: ["g/4"], duration: "32" }),
    new StaveNote({ keys: ["a/4"], duration: "32" }),
    new StaveNote({ keys: ["g/4"], duration: "16" }),
    new StaveNote({ keys: ["d/4"], duration: "q" }),
];

const beams6 = Beam.generateBeams(notes6);
Formatter.FormatAndDraw(context6, stave6, notes6);
beams6.forEach((b) => {
    b.setContext(context6).draw();
});
//********前の記譜と共通の関数です。2重定義しないように注意******
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;
}
//********ここまで
</script>


 notesで一小節分をまとめて定義した後、const beams6 = Beam.generateBeams(notes6); とするだけです。自動なので、思い通りになっていない可能性もありますが、8分音符4つは最大で二分音符にそれ以下は四分音符単位で連桁してくれるようです。連桁に含まれるので、ここまでの知識では連符には対応できないです。3連とか5/6/7/9/10/11/12…連とかは違う表記が必要です。また別の項目で確認するつもりなので、それまでは我慢です。自分もまだ知らないし。知らんけどじゃないし。本当に知らないし。本当に知らんけど。なんだけど。


 連符って演奏上においては最難関だね。3連でも、2分三連はさらにムズイ。2分三連くらいの長さになると、適当だとさすがにだめだし、あってないことを多い。ビート分割して、感じないとね。2分三連の中抜きみたいに1個あるいは2個だけ鳴らすとか、ありえないくらいむずかしい。心で刻むのつらいな。演奏家としては向いてないんだろうな。

 

五線譜に音符を配置 タイ


 スクリプトは以下のようなものです。

<div id="output7"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Accidental,
  Beam,
  Formatter,
  Dot,
  StaveTie
} = Vex.Flow;
//******************ここまで共通**
</script>
<script>
// Create an SVG renderer and attach it to the DIV element named "boo".
const div7 = document.getElementById("output7");
const renderer7 = new Renderer(div7, Renderer.Backends.SVG);

// Configure the rendering context.
renderer7.resize(500, 200);
const context7 = renderer7.getContext();

// Create a stave of width 400 at position 10, 40 on the canvas.
const stave7 = new Stave(10, 40, 400);

// Add a clef and time signature.
stave7.addClef("treble").addTimeSignature("4/4");

// Connect it to the rendering context and draw!
stave7.setContext(context7).draw();

const notes7 = [
    dotted(
        new StaveNote({
            keys: ["e##/5"],
            duration: "8d",
        }).addModifier(new Accidental("##"))
    ),
    new StaveNote({
        keys: ["b/4"],
        duration: "16",
    }).addModifier(new Accidental("b")),
    new StaveNote({
        keys: ["c/4"],
        duration: "8",
    }),
    new StaveNote({
        keys: ["d/4"],
        duration: "16",
    }),
    new StaveNote({
        keys: ["d/4"],
        duration: "16",
    }),
    new StaveNote({
        keys: ["d/4", "A/3"],
        duration: "q",
    }),
    new StaveNote({
        keys: ["d/4", "A/3"],
        duration: "q",
    }),
];

const beams7 = Beam.generateBeams(notes7);
Formatter.FormatAndDraw(context7, stave7, notes7);
beams7.forEach(function (b) {
    b.setContext(context7).draw();
});

const ties7 = [
    new StaveTie({
        first_note: notes7[4],
        last_note: notes7[5],
        first_indices: [0],
        last_indices: [0],
    }),
    new StaveTie({
        first_note: notes7[5],
        last_note: notes7[6],
        first_indices: [1],
        last_indices: [1],
    }),
];

ties7.forEach((t) => {
    t.setContext(context7).draw();
});
//********前の記譜と共通の関数です。2重定義しないように注意******
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;
}
//********ここまで
</script>


 タイは、notes に対してconst ties7 = [ new StaveTie({ first_note: notes7[4], last_note: notes7[5], first_indices: [0], last_indices: [0],}) ] という具合に StaveTie() の引数にオブジェクトを定義していて、主に4つのプロパティを設定します。first_noteがタイの始まり、last_noteがタイの終わりの音符です。和音を構成していない場合は必ず 0 ですが、first_indicesとlast_indicesにどの和音に対してタイを描画するかを指定しています。高さの異なる音に対して指定した場合はスラーになりそうですが、本当にそうなるかどうか確かめてみました。そしたら、やっぱりスラーと同じ役割になりそうでした。ついでに和音に2番目の音、配列番号1番に対してタイを描画する例は最後のA/3に対しての描画が参考になるでしょう。スラーについて言及したドキュメントもありそうなので、はっきりしたことはそれを確かめてみてからですね。何か不都合があるかもしれないし。タイでスラーを描画するのはね。どうなんだろうね。


 あー。いっけん大丈夫そうだけど、駄目だ。スラーは旗の根元からとか自由に描くこともありました。タイは音符で接続するだけで大丈夫な法則があったんですね。なるほど。気が付かなかった。やるなVexFlow。楽譜をじっくり眺めないと気付かないことだな。どうやらslurというキーワードではなく、その柔軟さを必要とするため、curveという名前で実現しているっぽい。またその時がきたら説明できると思います。VexFlow作者も大変だな。かなり完璧だけど、できないこともあるんだろうね。ここまで網羅されていると、できないことがあってもしゃーねぇな。


 ここまで書いて思ったんだけど、(function(){ コード記述部 })();の中に書けば、変数を名前空間で分け隔てることができるんじゃねと思えてきた。変数に番号とかつけてるのってカッコ悪いのかな。次の記事で試してみよう。

 

タブ譜


 スクリプトは以下のようなものです。

<div id="output8"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Renderer,
  Stave,
  StaveNote,
  Voice,
  Accidental,
  Beam,
  Formatter,
  Dot,
  StaveTie,
  TabStave, 
  TabNote, 
  Bend, 
  Vibrato
} = Vex.Flow;
//******************ここまで共通**
</script>
<script>
// Create an SVG renderer and attach it to the DIV element with id="output".
const div8 = document.getElementById("output8");
const renderer8 = new Renderer(div8, Renderer.Backends.SVG);

// Configure the rendering context.
renderer8.resize(500, 300);
const context8 = renderer8.getContext();

// Create a tab stave of width 400 at position 10, 40 on the canvas.
const stave8 = new TabStave(10, 40, 400);
stave8.addClef("tab").setContext(context8).draw();

const notes8 = [
    // A single note
    new TabNote({
        positions: [{ str: 3, fret: 7 }],
        duration: "q",
    }),

    // A chord with the note on the 3rd string bent
    new TabNote({
        positions: [
            { str: 2, fret: 10 },
            { str: 3, fret: 9 },
        ],
        duration: "q",
    }).addModifier(new Bend("Full"), 1),

    // A single note with a harsh vibrato
    new TabNote({
        positions: [{ str: 2, fret: 5 }],
        duration: "h",
    }).addModifier(new Vibrato().setHarsh(true).setVibratoWidth(70), 0),
];

Formatter.FormatAndDraw(context8, stave8, notes8);
</script>


 タブの文字フォントが公式の例と違うな。淋しい。どれが何音符かわからないダサい。2分音符には〇と棒が、全音符には〇が、四分音符いかにも旗は必要だと思う。あとタブは指番号だけでなくフレットの一部抜粋でコードの図が記入できた方がいいんじゃね。あとこんな大げさなFullとかいうチョーキングってダサくない。W.C と Cho. または C やH.Cでいいよ。あと数字は手書きフォントのがいいね。総じていえばシンコーミュージックがやっぱさえてると思う。シンコーミュージックのは綺麗なスコアだけど内容がダメダメっていうね。何かしらデメリットあるわ。どの立場から言ってるのかとお思いでしょうけど。なんでスコア譜ってあんなにダメダメなんだろうね。お金出して買ってる人もいるのに。ここに。耳コピができないというややる気もない子がここにいるのに。スコア譜命なんだけど。Youtubeのスコア出してる神レベルの人だけが最強だとおもう。あれは弾いていて本当に楽だし、現実的な運指になってるし、弾き心地最高。ロックを感じれる。時代が味方してくれている。あとは市販のスコア譜がもっと魂のはいったものになってほしい。弾きにくいとこをちょっと考えるくらいは自分でできるけど、なんか違うんだよなが解決できない。何が違うんだろうな。差分を埋め合わせる技術力が付かない。悲しい。という愚痴。あと音作りもよくわからないんだよな。でかい音出して練習できないからな。あと、ずっとヘッドフォンでやってると耳おかしくなるから生音だけで練習する時間も結構あって、あれはあれで淋しい。運指の練習にはなるけど、最後の弾き方の練習はやっぱヘッドフォンでやるしかなくて、結局、いい音作りできなくて、折れるということを繰り返してる。悔しいな。小さい才能の引き出し方がまったくわからないから、全く開花しないのな。もんくばっかりしか思いつかないもん。やだよな。こんな志向の奴の音楽なんて聴きたくないよな。わかるよ。だから、部屋にこもってめいわくかけないくらいのことしか思いつかない。つまんない。結局そこに落ち着く。つまんない。でも音楽もクラリネットもギターも大好き!これだけは譲れない。でも、どうすんのコレって感じだね。ハハハ。


 何が変わったかっていうと、Stave が TabStaveに変わった。Notes が TabNote に、Keysが positions になって、[{ str: 3, fret: 7 }] っていう指定に変わった。str が弦番号ね。1弦が細い方、6弦が太い方。なので、弦番号の数字の小ささ大きさが弦の幅に比例していると覚えておきましょう。そしたら、どっちが1弦だっけ問題はなくなるね。fret がタブ譜に書かれる数字そのもの、あとでこれを手書き調のフォントに変える方法を考えてみたいと思います。あとは数字を〇で囲ったりできるんかな。無理かな。旗つけたり。やえもするとフレット図いれたり。


 音符の装飾は、addModifier()で同じなんだけど、臨時記号の#のようなAccidentalではなく、new Bend("Full"), 1のような指定でチョーキング。new Vibrato().setHarsh(true).setVibratoWidth(70) で、長さ70のビブラート。setHarsh(true)でカクカクのビブラート。falseなら丸みを帯びた波線です。falseのほうが好きだな。Harshはヒドイとかっていう意味だそうで荒々しくをON OFFみたいな感じですね。その他についてはまた後ほどですね。ビブラートは通常の楽譜でも描画できるべきなので、五線譜の方でもできるかためしておきますか。いや、試してみたら簡単にはいかないな。同じではないのだな。五線譜上に表示させるのはまた考えないとだめですね。なんか方法はありそう。

 

小節線


 スクリプトは以下のようなものです。

<div id="output9"></div>
//******************ここから複数の楽譜表示における共通のスクリプト**
<script src="https://cdn.jsdelivr.net/npm/vexflow@4.1.0/build/cjs/vexflow.js"></script>
<script>
const {
  Stave, 
  StaveNote, 
  Beam, 
  Formatter, 
  Renderer
} = Vex.Flow;
//******************ここまで共通**
</script>
<script>
// Create an SVG renderer and attach it to the DIV element with id="output".
const div9 = document.getElementById("output9");
const renderer9 = new Renderer(div9, Renderer.Backends.SVG);

// Configure the rendering context.
renderer9.resize(890, 130);
const context9 = renderer9.getContext();

// Measure 1
const staveMeasure91 = new Stave(10, 0, 470);
//staveMeasure91.addClef("treble").addTimeSignature("4/4").setContext(context9).draw();
staveMeasure91.addClef("treble");
staveMeasure91.addTimeSignature("4/4");
staveMeasure91.setContext(context9).draw();

const notesMeasure91 = [
  new StaveNote({ keys: ["c/4"], duration: "q" }), 
  new StaveNote({ keys: ["d/4"], duration: "q" }), 
  new StaveNote({ keys: ["b/4"], duration: "qr" }), 
  new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "q" })
];

// Helper function to justify and draw a 4/4 voice
Formatter.FormatAndDraw(context9, staveMeasure91, notesMeasure91);

// Measure 2 - second measure is placed adjacent to first measure.
const staveMeasure92 = new Stave(staveMeasure91.width + staveMeasure91.x, 0, 400);

const notesMeasure92_part1 = [
  new StaveNote({ keys: ["c/4"], duration: "8" }), 
  new StaveNote({ keys: ["d/4"], duration: "8" }), 
  new StaveNote({ keys: ["b/4"], duration: "8" }), 
  new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "8" })
];

const notesMeasure92_part2 = [
  new StaveNote({ keys: ["c/4"], duration: "8" }), 
  new StaveNote({ keys: ["d/4"], duration: "8" }), 
  new StaveNote({ keys: ["b/4"], duration: "8" }), 
  new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "8" })
];

// Create the beams for 8th notes in second measure.
const beam91 = new Beam(notesMeasure92_part1);
const beam92 = new Beam(notesMeasure92_part2);

const notesMeasure92 = notesMeasure92_part1.concat(notesMeasure92_part2);

staveMeasure92.setContext(context9).draw();
Formatter.FormatAndDraw(context9, staveMeasure92, notesMeasure92);

// Render beams
beam91.setContext(context9).draw();
beam92.setContext(context9).draw();
</script>


 2小節の楽譜にしました。まずは描画エリアを広くしないといけないので、横890にしました。1小節目は470。2小節目を400としてみました。記譜する内容はもっとも短い部分で8分音符なので、1小節あたり400は余裕があります。1小節目にはト音記号と拍子記号をおくので70ほど大きくします。


 したがって、const staveMeasure91 = new Stave(10, 0, 470); が1小節目。2小節目の始まりは10ではないので、staveMeasure91.width + staveMeasure91.xとして、const staveMeasure92 = new Stave(470 + 10, 0, 400); という内容で作られます。staveMeasure91.width が 470 で、staveMeasure91.width が 10 です。細かい計算があっていないと乱れるってことだな。abcjsとかはこんなこと気にしなくても自動で小節線が引かれますそのかわり、今回のように余裕を持っていひくとかバランスよくするとか、細かい指示は自動ではできません。結局、VexFlowくらい細かい指示をしないと思い通りにはならないですね。描く人の気持ちを込めれるのがVexFlowだな。AI?まだまだ無理だな。はやくAIに楽譜を描かせればいいのに。MIDI情報だけで完璧に描けるようになるんじゃね。シンコーミュージックファンなので、シンコーの楽譜で学習した奴がいいな。シンコーミュージックに潜入してどうやって描画してるのか見てみたいくらいだよ。入社したいわー。1年くらいかな。コピー譜つくる才能が無くて放り出されそうだけど。編集作業の一員くらいならやれるでしょ。コピする人の下で働くみたいなね。そんな奴いらんか。しかも1年とかなめたこといってるやつ採用するわけねぇな。シンコーミュージックは好きなんだけどな。間違ってますねコレとか偉そうにギター弾きながらコピしているひとにグサグサいってみたりね。すぐ解雇だな。どんだけシンコー好きやねんって感じだね。


 2小節目は8分音符四つを繰り返すので、notesMeasure92_part1とpart2を作ってますね。全く同じだから、notesMeasure92_part1.concat(notesMeasure92_part1);で繰り返せないのかな。だめか無限ループみたいになるわな。手前でコピーだから const notesMeasure92_part2 = notesMeasure92_part1;ってすれば、省略できんのかもな。やってみるか。はい。ダメでしたー。意味わからん。できそうなのにできない。なんか違うんだな。8分音符を4つのやつどうしを繋ぐときは、const notesMeasure92 = notesMeasure92_part1.concat(notesMeasure92_part2); とします。これは前までにやった 4つのNotesを繋ぐときに使ったときと同じことです。自動でやる方法なら notesMeasure92_part1 とか2つ作らず1つに8音まとめてから、const beam9 = Beam.generateBeams(notesMeasure92)とかだな。そんな感じで2小節は足し算が必要になるという地味な方法でした。横には4小節くらいを描くのがせいぜいなので普通はそれほど複雑にはならないでしょうね。ページとか縦という概念を覚えないといけないのでしょう。ページは描画領域を分けるのでしょう。タブレット端末とかを楽譜にして演奏する目的なら横に8小節くらいとかで、縦にふやしていく感じのパート譜を作れば便利そうだね。改行部分を柔軟に設定するのはできないな。柔軟にしようとしたら、これまたJavaScriptで自力で制御するんでしょう。そこまでやれる人いたら、使いこなしが凄そうだな。

 

VexFlow 使い方へ戻る。