リムナンテスは愉快な気分

徒然なるままに、言語、数学、音楽、プログラミング、時々人生についての記事を書きます

【Tone.js】絶対和音感を鍛えるアプリで四和音を出題する

前々回と前回はこちら。

limnanthaceae.hatenablog.com
limnanthaceae.hatenablog.com


今回の完成形はこんな感じ。

f:id:frecafloros:20210430231052p:plain

チェックリストの作成

出題する和音の種類を選択できるようにします。
の前に和音のピッチクラスを再整理します。

// chord の pitch class
const chordTypes = [
	// triad
	{'chordName': '', 'chordKeys': [0,4,7]},
	{'chordName': 'm', 'chordKeys': [0,3,7]},
	{'chordName': 'dim', 'chordKeys': [0,3,6]},
	{'chordName': 'aug', 'chordKeys': [0,4,8]},
	{'chordName': 'sus4', 'chordKeys': [0,5,7]},
	{'chordName': 'sus2', 'chordKeys': [0,2,7]},
	// tetrad
	{'chordName': '7', 'chordKeys': [0,4,7,10]},
	{'chordName': 'm7', 'chordKeys': [0,3,7,10]},
	{'chordName': 'M7', 'chordKeys': [0,4,7,11]},
	{'chordName': 'mM7', 'chordKeys': [0,3,7,11]},
	{'chordName': 'm7b5', 'chordKeys': [0,3,6,10]},
	{'chordName': 'dim7', 'chordKeys': [0,3,6,9]},
	{'chordName': 'aug7', 'chordKeys': [0,4,8,10]},
	{'chordName': 'augM7', 'chordKeys': [0,4,8,11]},
	{'chordName': '6', 'chordKeys': [0,4,7,9]},
	{'chordName': 'm6', 'chordKeys': [0,3,7,9]},
	{'chordName': 'add9', 'chordKeys': [0,2,4,7]},
	//pentad
	{'chordName': '9', 'chordKeys': [0,2,4,7,10]},
	{'chordName': 'm9', 'chordKeys': [0,2,3,7,10]},
	{'chordName': '7(b9)', 'chordKeys': [0,1,4,7,10]},
	{'chordName': 'm7(b9)', 'chordKeys': [0,1,3,7,10]},
	{'chordName': '7(#9)', 'chordKeys': [0,3,4,7,10]},
	{'chordName': 'm7(11)', 'chordKeys': [0,3,5,7,10]},
	{'chordName': '7(#11)', 'chordKeys': [0,4,6,7,10]},
	{'chordName': '7(13)', 'chordKeys': [0,4,7,9,10]},
	{'chordName': 'm7(13)', 'chordKeys': [0,3,7,9,10]},
]

思いつく限りを放り込みました。triad=三和音、tetrad=四和音、pentad=五和音です(古典音楽理論でtetradとかpentadとか言わないと思いますがtriadと揃えて造語しました)。本当は11とか13とか六和音とかありますが、ちょっと面倒なので今後機会があれば実装することにします。

htmlのチェックボックスの方では、上のリストに対応する値を設定します。

<div class="config">
	<dl>
		<dt><label><input type="checkbox" id="checkTriad">三和音:</label></dt>
		<dd>
			<label><input type="checkbox" name="chordval" value=0>(M)</label>
			<label><input type="checkbox" name="chordval" value=1>m</label>
			<label><input type="checkbox" name="chordval" value=2>dim</label>
			<label><input type="checkbox" name="chordval" value=3>aug</label>
			<label><input type="checkbox" name="chordval" value=4>sus4</label>
			<label><input type="checkbox" name="chordval" value=5>sus2</label>
		</dd>
	</dl>
	<dl>
		<dt><label><input type="checkbox" id="checkTetrad">四和音:</label></dt>
		<dd>
			<label><input type="checkbox" name="chordval" value=6>7</label>
			<label><input type="checkbox" name="chordval" value=7>m7</label>
			<label><input type="checkbox" name="chordval" value=8>M7</label>
			<label><input type="checkbox" name="chordval" value=9>mM7</label>
			<label><input type="checkbox" name="chordval" value=10>m7b5</label>
			<label><input type="checkbox" name="chordval" value=11>dim7</label>
			<label><input type="checkbox" name="chordval" value=12>aug7</label>
			<label><input type="checkbox" name="chordval" value=13>augM7</label>
			<label><input type="checkbox" name="chordval" value=14>6</label>
			<label><input type="checkbox" name="chordval" value=15>m6</label>
			<label><input type="checkbox" name="chordval" value=16>add9</label>
		</dd>
	</dl>
	<dl>
		<dt><label><input type="checkbox" id="checkPentad">五和音:</label></dt>
		<dd>
			<label><input type="checkbox" name="chordval" value=17>9</label>
			<label><input type="checkbox" name="chordval" value=18>m9</label>
			<label><input type="checkbox" name="chordval" value=19>7(b9)</label>
			<label><input type="checkbox" name="chordval" value=20>m7(b9)</label>
			<label><input type="checkbox" name="chordval" value=21>7(#9)</label>
			<label><input type="checkbox" name="chordval" value=22>m7(11)</label>
			<label><input type="checkbox" name="chordval" value=23>7(#11)</label>
			<label><input type="checkbox" name="chordval" value=24>7(13)</label>
			<label><input type="checkbox" name="chordval" value=25>m7(13)</label>
		</dd>
	</dl>
</div>

全選択できるように、「三和音」・「四和音」・「五和音」の横にもチェックボックスをつけておきます。例えば「三和音」をクリックすると三和音の全てにチェックが入るような仕様にする予定。

リスト型で作ってますが、cssの設定はdl、dt、ddに対して

dl{
	display: flex;
	margin: 0;
}

dt{
	font-weight: bold;
	width: 30%;
}

dd{
	margin: 0;
	display: inline-block;
	width: 70%;
}

です。dt(三和音・四和音・五和音)とdd(m、m7、etc.)の描画エリアの比が3:7になるようにしています。



See the Pen
chordtrainer-08
by frecafloros (@frecafloros)
on CodePen.


今はただの飾りですが。

単にチェックボックスをつけるだけなら<input type="checkbox"...>だけで十分なのですが、これだとチェックボックスからテキストが分離して改行されてしまう場合があります。これを回避するために<label>タグを使っています。<label>タグを使う利点は主に2つで、テキスト回り込みを回避することに加えて、テキストをクリックすることでもチェックが入れられることです。

<label>タグ自体のcss

label{
	white-space: nowrap;
}

で指定します。これでテキストが改行されなくなります。

選んだ種類の和音をランダムに選ぶ

どの和音がチェックされているかを取得→値をリストにスタック→乱数出す→スタックしたリストの値を使ってコード参照

の手順でやっていこうかなと思います。

その前に、三四五和音のチェックボックスの処理から行います。チェックされていたら該当の和音全部チェック入れる、チェックが外れたら全部消す、という処理をまず始めます。

三和音のところの要素はgetElementByIdで、(M)やmなどの具体的な和音はnameでグループ化しているのでgetElementsByNameで探すことができます。addEventListenerを使うとチェックボックスがon/offになるタイミングを感知することができるのでこいつを使います。チェックボックスはonがtrueでoffがfalseになっているので、下のようにfor文を回して処理していきます。

const checkTriad = document.getElementById("checkTriad");
const chordval = document.getElementsByName("chordval");

checkTriad.addEventListener("click", ()=>{
	if(checkTriad.checked){
		for(let i=0;i<=5;i++){
			chordval[i].checked = true;
		}
	}else{
		for(let i=0;i<=5;i++){
			chordval[i].checked = false;
		}
	}
});

四和音、五和音も同様に。(全選択した後にどこか一つの和音のチェックボックスを切っても、三・四・五和音のチェックボックスがonのままになってしまいますがいつか直します。)(よく考えたらonchangeでいいような…?)


続いて、「次の問題」ボタンが押されたら、onになっているチェックボックスを検知して値(value)をリストにぶっ込みます。値を取得してcheckChordTypes配列にひたすらプッシュ。

// checkbox 検知
checkChordTypes = [];
for(let i=0;i<chordval.length;i++){
	if(chordval[i].checked == true){
		checkChordTypes.push(chordval[i].value);
	}
}

そうするとチェックされたところの値だけをぶっこんだcheckChordTypes = [0,2,3,5,7]のような配列ができるので、この配列のindexを乱数回して指定します。例えば乱数で3が出たらcheckChordTypes[3]つまり5が選ばれた和音の種類、という算段。(一つも選択されてないときのエラー判定書いてないのでこの時多分バグる。あとで抽選しないように直す。)

chordNum = checkChordTypes[randomInt(0,checkChordTypes.length)];



See the Pen
chordtrainer-09
by frecafloros (@frecafloros)
on CodePen.


ひとまず実装したい機能は全部積めたので、ここらで終了にしたいと思います。
気が向いた時にバグ取り、ビジュアル面の強化、24平均律の和音などに手を出していこうかなと考えています。