クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【Tone.js】コード音が読みやすくなるようにリファクタリング

久々にTone.js再開!前回はコード切り替えボタンと鍵盤を連携しました。これからコードを増やしていくにあたり、今の書き方だとコード名とコード音の把握が分かりにくくなりそうな気がします。今回はコードのリファクタリングをメインにしてみたく思います。それでは行きましょう!

【目次】

※参考:【Tone.js】コード切り替えボタンと鍵盤の連携 - クモのようにコツコツと

※参考:Tone.jsを習得するためにやった事まとめ qiita.com

前回のおさらい

前回作ったもの。

See the Pen tone.js-12cord- 10 by イイダリョウ (@i_ryo) on CodePen.

上の切り替えボタンを押すとボタンの下のコード名の表記が変わる。そして鍵盤の和音がメジャー、マイナーに切り替わる。

※参考:【Tone.js】コード切り替えボタンと鍵盤の連携 - クモのようにコツコツと

コードタイプの書き換え

コードタイプ名を連想配列に変更

コードタイプのところは配列になっている。

//コードタイプ
var codeTypes = [
  [1,5,8],[1,4,8]
];
  • 変数codeTypesの中に配列。値は2つ。値も配列になっている
  • 1番目の値の配列はメジャーでルート音(1)に5音上、8音上を重ねる
  • 2番目の値の配列はマイナーでルート音(1)に4音上、8音上を重ねる

今は2種類だから1番目、2番目でいいのだが、このままだとコード種類を増やしたときに「○番目」と数えるのが直感的ではない。

配列の中を連想配列に書き換えてみる。

//コードタイプ
var codeTypes = [
    {'codeName': 'M',
        'codeKeys': [1,5,8]},
    {'codeName': 'm',
        'codeKeys': [1,4,8]}
];
  • 変数codeTypesの中に配列。値は2つ。値は連想配列に変更
  • 1番目の値のcodeNameキーの値=メジャー(M)、
    codeKeysキーの値=ルート音(1)に5音上、8音上を重ねる
  • 2番目の値のcodeNameキーの値=マイナー(m)、
    codeKeysキーの値=ルート音(1)に4音上、8音上を重ねる

Console.logで検証

ちゃんと変数が読めるかConsole.logで検証する。

//検証
console.log(codeTypes[1]['codeKeys'].length); //3
console.log(codeTypes[1]['codeKeys'][2]); //8
  • 変数codeTypesの配列1番目のcodeKeysキーの値の数をコンソールに表示→3つ
  • 変数codeTypesの配列1番目のcodeKeysキーの値の3番目の値をコンソールに表示→8

配列1番目はメジャーで和音の数は3つ。和音の3番目(0からカウントなので2)は8

ちゃんと読めているようだ。

和音のfor文の書き換え

和音を作るfor文を書き換える。元のコードはこちら。

//和音
for (var h = 0 ; h < codeTypes.length; h++ ) {
  chords.push( [] );
  for (var i = 0 ; i < Key.length; i++ ) {
    chords[h].push( [] );
    for (var  j = 0; j < codeTypes[h].length; j++){
      var nmb = scale[twelveCode[i]+codeTypes[h][j]];
      chords[h][i].push(nmb); 
    }
  }
}

for文は3重のループになっている。このうちCodeTypeが出てくるのは1番目と3番目。しかし、1番目codeTypes.lengthcodeTypes直下の配列数(コードタイプ数)でメジャー、マイナーの2種類なので変わらない。変わるのは3番目のループの部分。ここにcodeKeysキーを反映する。

変更後のコード

//和音
for (var h = 0 ; h < codeTypes.length; h++ ) {
  chords.push( [] );
  for (var i = 0 ; i < Key.length; i++ ) {
    chords[h].push( [] );
    for (var  j = 0; j < codeTypes[h]['codeKeys'].length; j++){        
      var nmb = scale[twelveCode[i]+codeTypes[h]['codeKeys'][j]];
      chords[h][i].push(nmb); 
    }
  }
}
  • for文の条件の2番目、codeTypes[h].lengthをcodeTypes[h]['codeKeys'].lengthに変更
  • 変数nmbcodeTypes[h][j]codeTypes[h]['codeKeys'][j]に変更

codeTypes[h]のあとに['codeKeys']を追記するとこで「変数codeTypesの配列h番目のcodeKeysキーの」という意味になる。for文の条件はcodeKeysキーの値の数、変数nmbcodeKeysキーのj番目、という意味になる。

できたのがこちら!

See the Pen tone.js-12cord- 11 by イイダリョウ (@i_ryo) on CodePen.

見た目も動きもまったく変わらないのだが、キーの種類はわかりやすくなってコードは読みやすくなった!

なお、コード名を「Cメジャー」「Eマイナー」などわかりやすいように鍵盤の表記はローマ字(C〜)に変更した。

和音の書き換え

ルート音と12コードを連想配列でまとめる

同じようなやり方で和音の部分の書き換えられそう。

元のコード

//ルート音
var root = {C4: 0, Cs4: 1, D4: 2, Ds4: 3, E4: 4, F4: 5, Fs4: 6, G4: 7, Gs4: 8, A4: 9, As4: 10, B4: 11, C5: 12};

//12コード
var twelveCode =[root['C4'],root['Cs4'],root['D4'],root['Ds4'],root['E4'],root['F4'],root['Fs4'],root['G4'],root['Gs4'],root['A4'],root['As4'],root['B4'],root['C5']]
  • 変数rootは連想配列。キー名にC4などの鍵盤名、値に0からカウントを始める数字
  • 変数twelveCodeは配列。配列rootのキー名の値を入れる

これは下のfor文をキー名ではなくi番目と数字で指定するために、わざわざrootに数字を入れている。

似たような内容が2つの場所に分かれているのがわかりにくので合体させてみる。

//ルート音
var root =[
    {'rootName':'C4', 'rootNmb': 0},
    {'rootName':'Cs4', 'rootNmb': 1},
    {'rootName':'D4', 'rootNmb': 2},
    {'rootName':'Ds4', 'rootNmb': 3},
    {'rootName':'E4', 'rootNmb': 4},
    {'rootName':'F4', 'rootNmb': 5},
    {'rootName':'Fs4', 'rootNmb': 6},
    {'rootName':'G4', 'rootNmb': 7},
    {'rootName':'Gs4', 'rootNmb': 8},
    {'rootName':'A4', 'rootNmb': 9},
    {'rootName':'As4', 'rootNmb': 10},
    {'rootName':'B4', 'rootNmb': 11},
    {'rootName':'C5', 'rootNmb': 12}
]
  • 変数rootの中は配列。配列の値は連想配列。
  • 連想配列の中はrootNameキー(ルート音)とrootNmbキー(番号)

コンソールで検証

//検証
console.log(root.length);  //13
console.log(root[3]['rootNmb']); //3
  • 配列rootの数→13
  • 配列rootの4番目のrootNmbキーの番号→3

うむ、想定通りに読めてる。

for文の書き換え

和音のfor文を書き換える。

元のコート

//和音
for (var h = 0 ; h < codeTypes.length; h++ ) {
  chords.push( [] );
  for (var i = 0 ; i < Key.length; i++ ) {
    chords[h].push( [] );
    for (var  j = 0; j < codeTypes[h]['codeKeys'].length; j++){        
      var nmb = scale[twelveCode[i]+codeTypes[h]['codeKeys'][j]];
      chords[h][i].push(nmb); 
    }
  }
}

多重ループの3番目のfor文、変数nmbの中にtwelveCodeがある。ここを書き換える。

変更後

//和音
for (var h = 0 ; h < codeTypes.length; h++ ) {
  chords.push( [] );
  for (var i = 0 ; i < Key.length; i++ ) {
    chords[h].push( [] );
    for (var  j = 0; j < codeTypes[h]['codeKeys'].length; j++){        
      var nmb = scale[root[i]['rootNmb']+codeTypes[h]['codeKeys'][j]];
      chords[h][i].push(nmb); 
    }
  }
}
  • twelveCode[I]root[i]['rootNmb']に変更

さあどうだ?

See the Pen tone.js-12cord- 12 by イイダリョウ (@i_ryo) on CodePen.

やた!なった!

つーか鍵盤(Key)の数でよかった…

そこでふと気づいたのだが、そもそものfor文のi番目は変数Keyの数lengthではないか? もしかしてKeyの数(i)を入れるだけでいい?とよぎった…。

//和音
for (var h = 0 ; h < codeTypes.length; h++ ) {
  chords.push( [] );
  for (var i = 0 ; i < Key.length; i++ ) {
    chords[h].push( [] );
    for (var  j = 0; j < codeTypes[h]['codeKeys'].length; j++){        
      var nmb = scale[i+codeTypes[h]['codeKeys'][j]];
      chords[h][i].push(nmb); 
    }
  }
}

変数nmbroot[i]['rootNmb']iに変更

恐る恐る実行…

See the Pen tone.js-12cord- 13 by イイダリョウ (@i_ryo) on CodePen.

うお!音がなった!なんだ、鍵盤数(i)を入れればいいだけだったんだ!

ということで、変数rootはまるまる削除したw

最終的なコード

最新状態はこんなん。

//DOM
var Key = document.querySelectorAll('#piano li');
var codeTypeText = document.querySelector('#code_type_text');
var codeType = document.getElementsByName("code_type");

//音階
var scale = [
  //休符
  'null', 
  //1オクターブ目
  'C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4',
  //2オクターブ目
  'C5', 'C#5', 'D5', 'D#5', 'E5', 'F5', 'F#5', 'G5', 'G#5', 'A5', 'A#5', 'B5'];

//コードタイプ
var codeTypes = [
    {'codeName': 'M',
        'codeKeys': [1,5,8]},
    {'codeName': 'm',
        'codeKeys': [1,4,8]}
];

//和音入れ場
var chords = [];

//和音
for (var h = 0 ; h < codeTypes.length; h++ ) {
  chords.push( [] );
  for (var i = 0 ; i < Key.length; i++ ) {
    chords[h].push( [] );
    for (var  j = 0; j < codeTypes[h]['codeKeys'].length; j++){        
      var nmb = scale[i+codeTypes[h]['codeKeys'][j]];
      chords[h][i].push(nmb); 
    }
  }
}

//コードタイプ設定
  function codeTypeSelect() {
    for(var i = 0; i < codeType.length; i++){
      if(codeType[i].checked) {
        var CodeTypeValue = codeType[i].value;
        codeTypeText.innerHTML = CodeTypeValue;
           return chords[i];
      }
    }
  }

codeTypeSelect();

//シンセ生成
var synth = new Tone.PolySynth().toMaster();

//イベントリスナ
for (var i = 0; i < Key.length; i++) {
(function(i) {
  Key[i].addEventListener('click', function () { 
  //チェックされているコードタイプを確認
  var seletcCords = codeTypeSelect();
  //メジャーコードが4分音符の長さ鳴る
  synth.triggerAttackRelease(seletcCords[i], '4n');
  }, false);
 })(i);
}

結局、ルート音のコードはまるまる必要ないということに気づいた旅だったw

最後に

f:id:idr_zz:20191117193022j:plain

ということで、今回はコードのリファクタリングのみ!外観は鍵盤名をローマ字にしたくらい。次回はいよいよ、コードの種類をいろいろ増やしていきたいと思います!(セブンスとかナインスとか)それではまた!


※参考:Tone.jsを習得するためにやった事まとめ qiita.com