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

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

【Tone.js】絶対和音感を鍛えるアプリを作る

コード進行を耳コピしたいのに音がわからない…ということはありませんか?
というわけで和音を聞き取る特訓ができるアプリを作ります。tone.jsというライブラリを使うとブラウザ上で音を出すことができるようですので、これを使います。

基本設計

  1. スタートボタンを押すと和音が流れる
  2. 答え合わせボタンを押すと和音のコード名が表示される


というシンプルな構成にしようと思います。あんまり理想が高すぎると完成しないので。

1つ目の「スタートボタンを押すと和音が流れる」は、あらかじめ用意しておいた和音リストからランダムに選択し、和音を流す。2つ目の「答え合わせボタンを押すと和音のコード名が表示される」は、'Cm'や'Eaugadd9'といったようなコード名が最低限、将来的にはピッチクラス、構成音名、半音圏上の図示ができるようになればいいなぁ、くらいの気持ちです。

単音を鳴らす

Tone.jsはWeb Audio APIというやつを簡単に操作できるように作成されたJSのライブラリらしいです。

早速Tone.jsをインポートします。htmlのヘッダーに以下の記述を入れるとTone.jsが使えるようになります。

<script>https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.15/Tone.js</script>

手始めに単音を鳴らします。練習として、ボタンを押すと「ラ」の音(220Hz)が八分音符の長さだけ流れるようにします。

let buttonStart = () => {
	const synth = new Tone.Synth().toDestination();
	synth.triggerAttackRelease("A3", "8n");	
};

デフォルトは多分120BPM(=1分間に四分音符が120拍の速さ)だと思います。

new Tone.Synth().toDestination()シンセサイザーを生成します。以降このシンセ(=オブジェクト)に対して操作をするような感じ。
triggerAttackRelease()は音を鳴らし(Attack)て止める(Release)までの一連の動作をやってくれます。第一引数はA3やC4などの音高を指定、第二引数は音の長さを指定します。音の長さは四分音符なら4n、八分音符なら8n、といった勝手です。

スタートボタンを押すとA3(220Hz)の音が流れるはずです。(スマホ等の場合はResultを押してから)


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


和音を鳴らす

和音を鳴らします。ただのSynth()はmonosynthっぽいので代わりにPolySynth()を使います。PolySynth()以外で和音出せるのかは謎。

原理的には、前段の"A3"の代わりに["A3", "C4", "E4"]のようなコード構成音の配列を入れることで和音を鳴らします。

let buttonStart = () => {
	const synth = new Tone.PolySynth().toDestination();
	synth.triggerAttackRelease(["A3", "C4", "E4"], "8n");	
};

これでAmの和音が鳴る。

ついでに、今後の拡張性を考えて、根音(のピッチクラス)とコードの種類からコード構成音を自動生成できるようにします。

// scale
const scale = [
	'C3', 'C#3', 'D3', 'D#3', 'E3', 'F3', 'F#3', 'G3', 'G#3', 'A3', 'A#3', 'B3',
	'C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4',
	'C5', 'C#5', 'D5', 'D#5', 'E5', 'F5', 'F#5', 'G5', 'G#5', 'A5', 'A#5', 'B5'
];

// chord の pitch class
const chordTypes = [
	{'chordName':'M', 'chordKeys': [0,4,7]},
	{'chordName':'m', 'chordKeys': [0,3,7]},
	{'chordName':'dim', 'chordKeys': [0,3,6]},
	{'chordName':'aug', 'chordKeys': [0,4,8]}
];

まずスケールですが、転回形を今後加えることを考えて一応3オクターブ分用意します。根音はC3からB3までのどれかを選択する感じで。コードのタイプはとりあえず三和音のメジャー、マイナー、dim、augを用意します。もろもろの都合により、辞書のリストになってます。そのうち増やす。

和音の配列を生成するために、ルート音とコード音からスケールの音を拾ってきてスタックしていきます。

// 和音生成
chord = [];
for (let j=0;j<chordTypes[2]['chordKeys'].length; j++){
	let pitch = scale[6+chordTypes[2]['chordKeys'][j]];
	chord.push(pitch);
}

上の例では、スケールの6番目(F#3)から+0番目、+4番目、+6番目(dimの配列)を拾ってきて音のリストを作っています。したがって完成したリストは["F#3", "A3", "C4"]となるはず。

下のデモでスタートボタンを押すとF#dimが鳴るはずです。


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

ランダムに和音を鳴らす

絶対和音感を鍛えたいので、ランダムに和音が出題されてほしいです。なのでボタンをクリックしたときにどの和音を鳴らすかを乱数で決めます。

JavaScriptの乱数生成は[0, 1)を生成する関数しかない?ようなので、ランダムに整数値を吐き出す関数を作ります。

// 乱数生成器
let randomInt = (min, max) => {
	// 返値はmin以上max未満の整数
	min = Math.ceil(min);
	max = Math.floor(max);
	return Math.floor(Math.random() * (max - min) + min);
};

この関数はmin以上max未満の整数値をランダムで返します。
根音は12種類のうちから、コードは4種類のうちから1つずつ選びます。

スタートボタンを押すと和音がランダムに鳴るようになっていると思います。


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


コードネームを表示する

「答えを表示」で今鳴っている和音のコードネームを表示させます。といっても前段で生成した乱数を元にテキスト化するだけです。

テキストをhtmlに流し込むにはgetElementById()を使って場所の指定をします。また、流し込むテキストの内容はinnerHTMLに代入する形で実現できます。

// chord name表示
targetName = document.getElementById("chordname-txt");
targetName.innerHTML = pitch2keyname[rootPc] + chordTypes[chordNum]['chordName'];

rootPcは根音のピッチクラス(0から11までの整数値)、chordNumは和音の構成を選択する値です。例えばchordNum=0はメジャーコードに対応しています。

下のデモでは、「次の問題」でまた別の和音をランダムに選択し鳴らします。「答えを表示」を押すと、CmF#dimといったような形で和音のコードネームが表示されるはずです。選ばれた和音は「和音再生」で何度も聞くことができます。


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


とりあえず素体は出来たので、(細かいところが少し気にはなりますが)四和音などに拡張する前にUIをどうにかしたいですね。



続き

limnanthaceae.hatenablog.com