VexFlow 連符
概要
連符を描いてみたいと思います。tupletというのが連符のキーワードになります。
3連符
<div id="output"></div>
<script>
(function(){
// This approach to importing classes works in CJS contexts (i.e., a regular <script src="..."> tag).
const { Stave, StaveNote, Beam, Formatter, Renderer, Tuplet } = Vex;
// Create an SVG renderer and attach it to the DIV element with id="output".
const div = document.getElementById("output");
const renderer = new Renderer(div, Renderer.Backends.SVG);
// Configure the rendering context.
renderer.resize(890, 130);
const context = renderer.getContext();
// Measure 1
const staveMeasure1 = new Stave(10, 0, 470);
staveMeasure1.addClef("treble").setContext(context).draw();
const notesMeasure1 = [
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(context, staveMeasure1, notesMeasure1);
// Measure 2 - second measure is placed adjacent to first measure.
const staveMeasure2 = new Stave(staveMeasure1.width + staveMeasure1.x, 0, 400);
const notesMeasure2_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 notesMeasure2_part2 = [
new StaveNote({ keys: ["c/4"], duration: "8" }),
new StaveNote({ keys: ["c/4"], duration: "8" }),
new StaveNote({ keys: ["c/4"], duration: "8" }),
];
const notesMeasure2_part3 = [
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 beam1 = new Beam(notesMeasure2_part1);
const beam2 = new Beam(notesMeasure2_part2);
const tuplet2 = new Tuplet(notesMeasure2_part2);
const beam3 = new Beam(notesMeasure2_part3);
const notesMeasure2 = notesMeasure2_part1.concat(notesMeasure2_part2).concat(notesMeasure2_part3);
staveMeasure2.setContext(context).draw();
Formatter.FormatAndDraw(context, staveMeasure2, notesMeasure2);
// Render beams
beam1.setContext(context).draw();
beam2.setContext(context).draw();
tuplet2.setContext(context).draw();
beam3.setContext(context).draw();
})();
</script>
12連符(間違えている例)
描けばいいってもんじゃないらしい。連符にも楽典の法則はあって、12:2だと後ろの2が音符の何個分を12個に割るのかという意味なので、32分音符の2個分は16分音符に相当するものを12連にするという意味になるらしい。12:8なら正しいことになる。かといってBeamの音を8分(1本線)とか16分(2本線)にかえていいかというとそれもダメです。8連以上の連符の場合は元の音1つ分にあたる4分音符の3段階下の音で表記することに決まっています。12:2の後ろの:2が表示されていなければ、それでも良いのですが、表示されてしまう書き方になっているみたい。困ったな。パート譜を作るならもうちょっと詰めて描画するべきだろうね。スコアの体で1行2小節の描き方をしてしまった。ちなみにダフニスとクロエ第二組曲夜明けの第三バイオリンの最初の12連符を描きました。吹奏楽でやったんで、Bbクラリネットに移行したときの譜面になっています。四分音符が1分当たり50の速さで演奏します。疲れるバックサウンドだな。ラヴェルはサディストだな。いいんだよなでも。川のせせらぎのように聞こえるもんな。許す。けど、あなたのこの指示のせいで、青春の4か月を奪ったよ。このパターンを20種類くらい練習しないといけなかったな。この調子が3分以上つづく曲ってバカだろコレ。みたいなね。それで、自分は次々とパート(つまり第三バイオリンから第一バイオリンにさせられたり、やっぱ二をやれとか、いや四がいなくなったから四たのむわとかなってバスクラリネットが不足してるからやってみるかとかね)を変えさせられて、バスクラリネットもやったし、こんなの吹けるわけねぇ。ってなったのを通り越して、全部を知り尽くした気がしてた夏だったな。結局、コンクールでれなかったし。なかなか感慨深い曲だわ。良すぎるよな。この曲。DTMに打ち込んでせせらぎをMAX付近で聴くダフニスとクロエの夜明けもいいんだよな。神々の遊びだよ。難易度はディオニソスの祭りとダフニスとクロエの2強が最強説だよ。これを知ったら他のすべての曲なんてちょろいって思える。まぁ折角ちょろくても、結局大した演奏できないんだけどベースがあれだからな。下手だから。
<div id="output2"></div>
<script>
(function(){
// This approach to importing classes works in CJS contexts (i.e., a regular <script src="..."> tag).
const { Stave, StaveNote, Beam, Formatter, Renderer, Tuplet, Voice } = Vex;
// Create an SVG renderer and attach it to the DIV element with id="output".
const div = document.getElementById("output2");
const renderer = new Renderer(div, Renderer.Backends.SVG);
// Configure the rendering context.
renderer.resize(1695, 250);
const context = renderer.getContext();
// Measure 1
const staveMeasure1 = new Stave(10, 50, 880);
staveMeasure1.addClef("treble").addKeySignature('E').addTimeSignature('4/4').setContext(context).draw();
const notesMeasure1 = [
new StaveNote({ keys: ["d/5"], duration: "wr" }),
];
// Helper function to justify and draw a 4/4 voice
Formatter.FormatAndDraw(context, staveMeasure1, notesMeasure1);
// Measure 2 - second measure is placed adjacent to first measure.
const staveMeasure2 = new Stave(staveMeasure1.width + staveMeasure1.x, 50, 800);
const notesMeasure2_part1 = [
new StaveNote({ keys: ["G#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["D#/5"], duration: "32" }),
new StaveNote({ keys: ["A/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["G#/4"], duration: "32" }),
new StaveNote({ keys: ["B/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
];
//ドA レB ミC# ファD ソE ラF# シ(G#→b→G) ドA
//ド(C→#→C#) レD ミE ファ(F→#→F#) ソG ラA シB ドC
//ド(Bb→#→B) レ(C→#→C#) ミD ファ(Eb→#→E) ソ(F→#→F#) ラG シA ドB(Bb→#→B)
const notesMeasure2_part2 = [
new StaveNote({ keys: ["G#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["D#/5"], duration: "32" }),
new StaveNote({ keys: ["A/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["G#/4"], duration: "32" }),
new StaveNote({ keys: ["B/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
];
const notesMeasure2_part3 = [
new StaveNote({ keys: ["G#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["D#/5"], duration: "32" }),
new StaveNote({ keys: ["A/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["G#/4"], duration: "32" }),
new StaveNote({ keys: ["B/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
];
const notesMeasure2_part4 = [
new StaveNote({ keys: ["G#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["D#/5"], duration: "32" }),
new StaveNote({ keys: ["A/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["G#/4"], duration: "32" }),
new StaveNote({ keys: ["B/4"], duration: "32" }),
new StaveNote({ keys: ["C#/5"], duration: "32" }),
new StaveNote({ keys: ["E/5"], duration: "32" }),
new StaveNote({ keys: ["F#/5"], duration: "32" }),
];
// Create the beams for 8th notes in second measure.
const beam1 = new Beam(notesMeasure2_part1);
const tuplet1 = new Tuplet(notesMeasure2_part1);
const beam2 = new Beam(notesMeasure2_part2);
const tuplet2 = new Tuplet(notesMeasure2_part2);
const beam3 = new Beam(notesMeasure2_part3);
const tuplet3 = new Tuplet(notesMeasure2_part3);
const beam4 = new Beam(notesMeasure2_part4);
const tuplet4 = new Tuplet(notesMeasure2_part4);
const voice = new Voice({ time: { num_beats: 1, beat_value: 4 } });
voice.setStrict(true);
voice.addTickables(notesMeasure2_part1);
//new Formatter().joinVoices([voice]).formatToStave([voice], staveMeasure2);
const notesMeasure2 = notesMeasure2_part1.concat(notesMeasure2_part2).concat(notesMeasure2_part3).concat(notesMeasure2_part4);
staveMeasure2.setContext(context).draw();
Formatter.FormatAndDraw(context, staveMeasure2, notesMeasure2);
// Render beams
beam1.setContext(context).draw();
tuplet1.setContext(context).draw();
beam2.setContext(context).draw();
tuplet2.setContext(context).draw();
beam3.setContext(context).draw();
tuplet3.setContext(context).draw();
beam4.setContext(context).draw();
tuplet4.setContext(context).draw();
})();
</script>
テスト
まぁな、わかんね。この後ろに3:2とかってなるセンスがわからん。でも意味としては8分の5の4拍で3連符だから3:2なんだろうね。8分の5の1拍で3連が3:1か。どういう意味。いや連符のこういう表記って理解してなかったわ。ちょっと勉強してこよかな。楽典か。この年になって楽典みるとはおもわんかったわ。(離席…)んー楽典を読んで来たった。3連符は、割りたい音のひとつ短い音符を使って書き、4~7までは、ふたつ短い音符を、8以降は、みっつ短い音符で16以降は4つ短い音符を使うとあります。なんだよ3:2って、書いてねぇよそんなこと。でも4分音符を3連するってことは2分音符相当を3つに割るってこと。やっぱり8分の4が2分音符相当なので、あってるわ。8分音符で3連は4分音符相当なはずだから8分の2だから8分の6になるわ。8分の5じゃねぇわ。でも、この3:2や3:1がこのルールを捻じ曲げることが出来てるってことなのかもしれない。もうちょっと楽典あさるか。(離席…)ほうほう。うしろのやつは、表記されてる音符の何個分にって意味らしい。なので最初の3連は4分音符2個分を3連で。という意味です。後ろの3連は8分音符1つ分を3連にって意味になるね。なので正しい表記ですね。ひとつ前に12連符は間違えてるってことだな。32分音符2つ分に12個って意味になるからね。というわけで、Beatをしっかりフォーマットしないと、あり得ない譜面になるってことだな。むずかしい。
(function(){
const set = (key) => (value) => (object) => {
object[key] = value;
return object;
};
const setStemDirection = set('stem_direction');
// This approach to importing classes works in CJS contexts (i.e., a regular <script src="..."> tag).
const { Factory, Stave, StaveNote, Beam, Formatter, Renderer, Tuplet, Voice, Stem } = Vex;
const setStemUp = setStemDirection(Stem.UP);
const setStemDown = setStemDirection(Stem.DOWN);
const f = new Factory({ renderer: { elementId: 'output3', width: 490, height: 200 } });
// Create an SVG renderer and attach it to the DIV element with id="output".
const stave = f.Stave({ x: 10, y: 40, width: 470 }).addTimeSignature('5/8');
const notes = [
{ keys: ['f/4'], duration: '4' },
{ keys: ['c/4'], duration: '4' },
{ keys: ['d/4'], duration: '4' },
{ keys: ['d/5'], duration: '8' },
{ keys: ['g/5'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes.slice(3, 6),
});
f.Tuplet({
notes: notes.slice(0, 3),
options: {
location: Tuplet.LOCATION_BOTTOM,
ratioed: true,
},
});
f.Tuplet({
notes: notes.slice(3, 6),
options: {
location: Tuplet.LOCATION_BOTTOM,
notes_occupied: 1,
},
});
const voice = f
.Voice({ time: { num_beats: 5, beat_value: 8 } })
.setStrict(true)
.addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
})();
12連符 再挑戦
先ほどは、ちょっと駄目だったけど、VoiceのBeatっていう概念を取り込むと改善されるのか?
(function(){
const set = (key) => (value) => (object) => {
object[key] = value;
return object;
};
const setStemDirection = set('stem_direction');
// This approach to importing classes works in CJS contexts (i.e., a regular <script src="..."> tag).
const { Factory, Stave, StaveNote, Beam, Formatter, Renderer, Tuplet, Voice, Stem } = Vex;
const setStemUp = setStemDirection(Stem.UP);
const setStemDown = setStemDirection(Stem.DOWN);
const f = new Factory({ renderer: { elementId: 'output3', width: 490, height: 200 } });
// Create an SVG renderer and attach it to the DIV element with id="output".
const stave = f.Stave({ x: 10, y: 40, width: 470 }).addTimeSignature('4/4');
const notes = [
{ keys: ['d/5'], duration: 'wr' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
const stave2 = f.Stave({ x: stave.width + stave.x, y: 40, width: 400 }).addTimeSignature('4/4');
const notes2_1 = [
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
const notes2_2 = [
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
const notes2_3 = [
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
const notes2_4 = [
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
{ keys: ['d/5'], duration: '32' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes2_1.slice(0, 12),
});
f.Tuplet({
notes: notes2_1.slice(0, 12),
options: {
location: Tuplet.LOCATION_BOTTOM,
// ratioed: true,
},
});
f.Beam({
notes: notes2_2.slice(0, 12),
});
f.Tuplet({
notes: notes2_2.slice(0, 12),
options: {
location: Tuplet.LOCATION_BOTTOM,
// ratioed: true,
},
});
f.Beam({
notes: notes2_3.slice(0, 12),
});
f.Tuplet({
notes: notes2_3.slice(0, 12),
options: {
location: Tuplet.LOCATION_BOTTOM,
// ratioed: true,
},
});
f.Beam({
notes: notes2_4.slice(0, 12),
});
f.Tuplet({
notes: notes2_4.slice(0, 12),
options: {
location: Tuplet.LOCATION_BOTTOM,
// ratioed: true,
},
});
// f.Tuplet({
// notes: notes2.slice(3, 6),
// options: {
// location: Tuplet.LOCATION_BOTTOM,
// notes_occupied: 1,
// },
// });
const notes2 = notes2_1.concat(notes2_2).concat(notes2_3).concat(notes2_4);
const voice2 = f
.Voice({ time: { num_beats: 4, beat_value: 4 } })
.setStrict(true)
.addTickables(notes2);
new Formatter().joinVoices([voice2]).formatToStave([voice2], stave2);
f.draw();
})();