html Javascript プログラミング 楽器

ギターのコードトーンやスケールをフレット上に表示するWebアプリを作った

フレット上の音を覚えたい

ギターはいくらやってもちっとも上手くならないのですが、それでも上手くなりたいと言う気持ちだけはあるので練習に役立つアプリをつくりました。

指定したコードやスケールの構成音をギターのフレット上に表示してくれるアプリです。

本来、ギターのコードやスケールなんてひとつ覚えたら横にずらすだけなんですが、これが何年やっても覚えられない。

例えば「FコードのサードのA」というのが指板全体でぱっと分かるのが理想です。しかし才能のない人は、なかなかそうはいきません。そんなことをコツコツ練習するためのアプリです。

最初はコードだけを考えていたのですが、作ってる途中でコードのデータを増やせばスケールにも使えると気がついて、あとからスケール表示機能も追加しました。

ちなみにコードフォームの表示機能はないです。コードフォームは普通に検索すればいくらでも出てきます。

使い方は簡単

アプリは以下のGithub Pagesで公開しています。

https://ryjkmr.github.io/guitar-fletboard-display-chord-scale/

ソースコードはこちら。

https://github.com/ryjkmr/guitar-fletboard-display-chord-scale

基本の使い方

使い方はとても簡単です。

左上の欄に見たいコードのルートを半角アルファベットで入力します。 小文字の入力しても自動的に大文字に変換されます。

フラットは小文字のB="b"で代用します。#は普通に入力できます。

入力と同時にフレット上の表示が更新されます。

右側のラジオボタンでコードの種類やスケールを選びます。選ぶとすぐに更新されます。

見たい弦やフレットだけを表示する

弦やフレット毎の表示/非表示機能をつけてあります。

特定のポジションや弦だけを練習したいときに便利です。

フレット番号の横にあるチェックボックスをオンオフするとそのフレットの表示が切り替わります。

1番左のオールと書いてあるスイッチは全部のフレットの表示/非表示を切り替えます。

弦の左に付いているチェックボックスは各弦の表示/非表示を切り替えます。1、2弦を非表示にすればベース用になります。

度数表記と音名表記を切り替え

フレット下のラジオボタンでルートに対する度数で表示するか、音名(AとかEb)とかで表示するかを選べます。

ダウンロードとキープ機能

「Download as PNG」 ボタンを押すとフレットの絵をPNGファイルをダウンロードできます。

「Keep this flet board below」 ボタンを押すとフレットの絵をページの下にキープできます。複数追加できるので、いくつかのコードやスケールを同時に見たいときに便利です。

リロードすると消えてしまいますので、繰り返し使いたいモノはブラウザの機能でプリントやPDF保存をすると良いでしょう。

いきあたりばったりで作ったアプリですが、自分のニーズに合わせてあるので、結構便利に使っています。

ギターフレットはtableで作成

ここからはアプリで使ったコードの説明を残しておきたいと思います。

ギターのフレットボードはtableで作っています。tdタグひとつが各弦のひとつのフレットに相当します。

html
          <tr>
            <td class="str-num "><input type="checkbox" value="1" class="str-on-off" checked>1</td>
            <td class="flet-cell str1 pos-0 E"></td>
            <td class="flet-cell str1 pos-1 F"></td>
            <td class="flet-cell str1 pos-2 Fs Gb"></td>
            <td class="flet-cell str1 pos-3 G "></td>
            <td class="flet-cell str1 pos-4 Gs Ab"></td>
            <td class="flet-cell str1 pos-5 A "></td>
            <td class="flet-cell str1 pos-6 As Bb"></td>
            <td class="flet-cell str1 pos-7 B "></td>
            <td class="flet-cell str1 pos-8 C"></td>
            <td class="flet-cell str1 pos-9 Cs Db "></td>
            <td class="flet-cell str1 pos-10 D"></td>
            <td class="flet-cell str1 pos-11 Ds Eb"></td>
            <td class="flet-cell str1 pos-12 E "></td>
            <td class="flet-cell str1 pos-13 F"></td>
            <td class="flet-cell str1 pos-14 Fs Gb"></td>
            <td class="flet-cell str1 pos-15 G "></td>
            <td class="flet-cell str1 pos-16 Gs Ab"></td>
          </tr>
          <tr>
              つづく......

各tdタグにはそのフレットの音程をクラスとして登録してあります。

これによりjqueryのクラス指定を使って、$('.Eb').text('●');

と書くだけでEbのフレット全てに●印を入れることができます。

枠線はborderで引き、CSSでcollapsを指定して枠線をくっつけています。

テーブルセルの真ん中に線を引く

弦をセルの中心に描くためには、CSSのlinear-gradientを使っています。途中で急激にグラデーションの色を黒くすることで、その部分が弦に見えるようにしています。

弦を描きたくないセルを除外するためにセレクタに :not を使っています。

フレットマークは丸く描く方法が思いつかなかったので、同じグラデーションで白ではなくグレーを背景にしたパターンを作って代用しています。

td:not(.flet-num, .pos-mark, .pos-0, .str-num) {
  background: linear-gradient(to bottom, white 0%, white 46%, black 47%, black 53%, white 54%, white 100%)
}

.pos-mark {
  background: linear-gradient(to bottom, lightgray 0%, lightgray 46%,
      black 47%, black 53%, lightgray 54%, lightgray 100%)
}

文字を白縁にする

セルに単純に文字を入れただけだと、文字と弦が重なって見づらいので、文字に白フチを付けています。

CSSでtext-shadowを4つつける簡易的な方法です。

.flet-cell {
  text-align: center;
  color: gray;
  font-size: xx-large;
  font-weight: bold;
  text-shadow:
    5px 5px 5px white, -5px -5px 5px white,
    -5px 5px 5px white, 5px -5px 5px white;
}

ラジオボタンを大きくする

ラジオボタンはfont-sizeでは大きくならないのでいつも困ります。

検索するといろいろな方法があるみたいですが、transform: scaleで大きくして、vertical-alignでだいたいの位置を合わせるのが簡単でした。

.radio-button {
  margin-left: 20px;
  margin-top: 20px;
  margin-right: 5px;
  transform: scale(2.5);
  vertical-align: 25%;
}

ルート、コードの種類から絶対音を計算して、対応フレットに任意の文字を表示する

音程を扱うプログラミングでちょっとややこしいのが以下のような点です。

  • 相対音程と絶対音程がある(Cコードの5度はGみたいな話)
  • 1つの音に複数の呼び方がある(例:D#とEbは同じ音)
  • リアルな音名と画面に表示したい文字は違う(CコードのCは"●"、Gは⑤と表示したい)

このアプリケーションでは、以下のようなオブジェクトや配列を辞書的に使ってこうした変換を行っています。

もっと効率的な方法があるかもしれませんが、こういうのは自分にとって分かりやすいことが大事だったりします。


変数 key:htmlのinput要素から取得。ルート音が英字で入る(例:"E")
オブジェクト:key_to_num keyに対応する音程の数値を取り出す。key_to_num("A")は"0"
const key_to_num = { A: 0, As: 1, Bb: 1, B: 2, C: 3, Cs: 4, Db: 4, D: 5, Ds: 6, Eb: 6, E: 7, F: 8, Fs: 9, Gb: 9, G: 10, Gs: 11, Ab: 11 }

変数 chord_type:htmlのラジオボタンから取得。コードの種類の名称が英字で入る(例:m7)
オブジェクト:CHORD_DATA コードの種類に対応する構成音が英字で記録されている。CHORD_DATA["m7"]で構成音の配列が取り出せる
const CHORD_DATA = {
    m7: ["root", "flat3", "fifth", "flat7"],,,,,,,,

オブジェクト interval_to_num:英字で書かれたコードの構成音を数値に変換。interval_to_num["flat3"]で"3"が返る
const interval_to_num = {
  root: 0, flat2: 1, flat9: 1, second: 2, nineth: 2, flat3: 3, sharp9: 3, third: 4, eleventh: 5, fourth: 5,
  sharp11, flat5: 6, fifth: 7, sharp5: 8, flat13: 8, flat6: 8, thirteenth: 9, sixth: 9, sharp13: 10, sharp6: 10, flat7: 10, maj7: 11
};

key_to_numとinterval_to_numの結果を足すと絶対音程が決まる(要オーバーフロー処理)

配列 key_sharpとkey_flat:数値で書かれた音程を#表記か♭表記の英字に変換する key_sharp[0]は"A"が返る
ここで得られた結果と同じクラス名を持つセルに書き込む
G#はクラス名として使えないのでGsとしている。

オブジェクト CHORD_CHARACTOR:コードの種類からセルに書き込む文字列を配列として取得。CHORD_CHARACTOR["m7"]で ["●", "♭3", "⑤", "♭7"]が返る

CHORD_CHARACTOR = {
    M: ["●", "③", "⑤"],
    M7: ["●", "③", "⑤", "7"],,,,,,

ダウンロードやキープ時に使うコード名はラジオボタンのカスタムデータ属性から取得しているが、これはあまりよくない。
コードやスケール情報はすべてJSONで書き、ラジオボタンなどはそこから生成した方が拡張性が上がる(やるとは言ってない)

HTML要素を画像化してダウンロード、ページに追加してキープ

作成したコードやスケールの図を画像化するためには「html2canvas」という素晴らしいライブラリを使っています。

https://html2canvas.hertzen.com

このライブラリは、以下のような簡単なコードで任意のHTML要素をcanvas要素に変換できます。

html2canvas(document.body).then(function(canvas) {
  // やりたい処理    
  document.body.appendChild(canvas);
});

// PNGでダウンロードする

    html2canvas(document.querySelector("#guitar")).then(canvas => {
      let downloadEle = document.createElement("a");
      downloadEle.href = canvas.toDataURL("image/png");
      const filename = key + chord_Name + ".png";
      downloadEle.download = filename;
      downloadEle.click();
    });

//キープ
    html2canvas(document.querySelector("#guitar")).then(canvas => {
      let d = document.createElement("div");
      let h1 = document.createElement("h1"); //コード名を書く場所を追加
      h1.textContent = key + chord_Name;
      d.appendChild(h1);
      d.appendChild(canvas);
      d.classList.add("keeped-flet");
      $('#keep_area').append(d);
    });

全体のコード

すべてのコードは以下のような感じです。サーバーに置かなくてもローカルアプリとして動きます。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Guitar Code & Scale Tone Viewer</title>
  <link rel="stylesheet" type="text/css" href="style.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

  <!-- https://html2canvas.hertzen.com/ -->
  <script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>

  <script src="./script.js"></script>

  <!-- 
  copyright Ryoji Kimura https://ryjkmr.com
  The MIT License (MIT) 
-->
</head>

<body>
  <div id="flet-wrapper">
    <h1 class="title">Guitar Code & Scale Tone Viewer <span class="credit"><a href="https://ryjkmr.com/">by
          ryjkmr.com/</a></span></h1>
    <div class="control">
      <input type="text" id="key-input" class="input" value="A">
      <label for="7"><input type="radio" class="radio-button select-chord-type" id="7" name="chord-type" value="_7" data-chordname="7" checked>
      7</label>
      <label for="m7"><input type="radio" class="radio-button select-chord-type" id="m7" name="chord-type"  data-chordname="m7" value="m7">
      m7</label>
      <label for="M7"><input type="radio" class="radio-button select-chord-type" id="M7" name="chord-type"  data-chordname="M7" value="M7">
      M7</label>
      <label for="m7b5"><input type="radio" class="radio-button select-chord-type" id="m7b5" name="chord-type" value="m7b5" data-chordname="m7b5" >
      m7♭5</label>
      <label for="M"><input type="radio" class="radio-button select-chord-type" id="M" name="chord-type" value="M" data-chordname="" >
      M</label>
      <label for="m"><input type="radio" class="radio-button select-chord-type" id="m" name="chord-type" value="m" data-chordname="m" >
      m</label>
      <label for="dim"><input type="radio" class="radio-button select-chord-type" id="dim" name="chord-type" value="dim"  data-chordname="dim" >
      dim</label>
      <label for="root"><input type="radio" class="radio-button select-chord-type" id="root" name="chord-type" value="root" data-chordname="" >
      root</label>
      <br>
      <label for="maj_scale"><input type="radio" class="radio-button select-chord-type" id="maj_scale" name="chord-type" value="maj_scale"  data-chordname=" Major Scale"  >
      Major Scale</label>
      <label for="maj_penta"><input type="radio" class="radio-button select-chord-type" id="maj_penta" name="chord-type" value="maj_penta"  data-chordname=" Major Pentatonic" >
      Major Pentatonic</label>
      <label for="min_penta" style="display: inline-block;"><input type="radio" class="radio-button select-chord-type" id="min_penta" name="chord-type" value="min_penta" data-chordname=" minor Pentatonic" >
      Minor Pentatonic</label>
    </div>

    <div id="guitar">
      <table>
        <p id="ChordName"></p>
        <tbody>
          <tr>
            <td class="str-num "><input type="checkbox" value="1" class="str-on-off" checked>1</td>
            <td class="flet-cell str1 pos-0 E"></td>
            <td class="flet-cell str1 pos-1 F"></td>
            <td class="flet-cell str1 pos-2 Fs Gb"></td>
            <td class="flet-cell str1 pos-3 G "></td>
            <td class="flet-cell str1 pos-4 Gs Ab"></td>
            <td class="flet-cell str1 pos-5 A "></td>
            <td class="flet-cell str1 pos-6 As Bb"></td>
            <td class="flet-cell str1 pos-7 B "></td>
            <td class="flet-cell str1 pos-8 C"></td>
            <td class="flet-cell str1 pos-9 Cs Db "></td>
            <td class="flet-cell str1 pos-10 D"></td>
            <td class="flet-cell str1 pos-11 Ds Eb"></td>
            <td class="flet-cell str1 pos-12 E "></td>
            <td class="flet-cell str1 pos-13 F"></td>
            <td class="flet-cell str1 pos-14 Fs Gb"></td>
            <td class="flet-cell str1 pos-15 G "></td>
            <td class="flet-cell str1 pos-16 Gs Ab"></td>
          </tr>
          <tr>
            <td class="str-num "><input type="checkbox" value="2" class="str-on-off" checked>2</td>
            <td class="flet-cell str2 pos-0 B"></td>
            <td class="flet-cell str2 pos-1 C"></td>
            <td class="flet-cell str2 pos-2 Cs Db"></td>
            <td class="flet-cell str2 pos-3 D "></td>
            <td class="flet-cell str2 pos-4 Ds Eb"></td>
            <td class="flet-cell str2 pos-5 E "></td>
            <td class="flet-cell str2 pos-6 F"></td>
            <td class="flet-cell str2 pos-7 Fs Gb "></td>
            <td class="flet-cell str2 pos-8 G"></td>
            <td class="flet-cell str2 pos-9 Gs Ab "></td>
            <td class="flet-cell str2 pos-10 A"></td>
            <td class="flet-cell str2 pos-11 As Bb"></td>
            <td class="flet-cell str2 pos-12 B pos-mark"></td>
            <td class="flet-cell str2 pos-13 C"></td>
            <td class="flet-cell str2 pos-14 Cs Db"></td>
            <td class="flet-cell str2 pos-15 D "></td>
            <td class="flet-cell str2 pos-16 Ds Eb"></td>
          </tr>
          <tr>
            <td class="str-num "><input type="checkbox" value="3" class="str-on-off" checked>3</td>
            <td class="flet-cell str3 pos-0 G"></td>
            <td class="flet-cell str3 pos-1 Gs Ab"></td>
            <td class="flet-cell str3 pos-2 A"></td>
            <td class="flet-cell str3 pos-3 As Bb pos-mark"></td>
            <td class="flet-cell str3 pos-4 B"></td>
            <td class="flet-cell str3 pos-5 C pos-mark"></td>
            <td class="flet-cell str3 pos-6 Cs Db"></td>
            <td class="flet-cell str3 pos-7 D pos-mark"></td>
            <td class="flet-cell str3 pos-8 Ds Eb"></td>
            <td class="flet-cell str3 pos-9 E pos-mark"></td>
            <td class="flet-cell str3 pos-10 F"></td>
            <td class="flet-cell str3 pos-11 Fs Gb"></td>
            <td class="flet-cell str3 pos-12 G "></td>
            <td class="flet-cell str3 pos-13 Gs Ab"></td>
            <td class="flet-cell str3 pos-14 A"></td>
            <td class="flet-cell str3 pos-15 As Bb pos-mark"></td>
            <td class="flet-cell str3 pos-16 B"></td>
          </tr>
          <tr>
            <td class="str-num "><input type="checkbox" value="4" class="str-on-off" checked>4</td>
            <td class="flet-cell str4 pos-0 D"></td>
            <td class="flet-cell str4 pos-1 Ds Eb"></td>
            <td class="flet-cell str4 pos-2 E"></td>
            <td class="flet-cell str4 pos-3 F pos-mark"></td>
            <td class="flet-cell str4 pos-4 Fs Gb"></td>
            <td class="flet-cell str4 pos-5 G pos-mark"></td>
            <td class="flet-cell str4 pos-6 Gs Ab"></td>
            <td class="flet-cell str4 pos-7 A pos-mark"></td>
            <td class="flet-cell str4 pos-8 As Bb"></td>
            <td class="flet-cell str4 pos-9 B pos-mark"></td>
            <td class="flet-cell str4 pos-10 C"></td>
            <td class="flet-cell str4 pos-11 Cs Db"></td>
            <td class="flet-cell str4 pos-12 D "></td>
            <td class="flet-cell str4 pos-13 Ds Eb"></td>
            <td class="flet-cell str4 pos-14 E"></td>
            <td class="flet-cell str4 pos-15 F pos-mark"></td>
            <td class="flet-cell str4 pos-16 Fs Gb"></td>
          </tr>
          <tr>
            <td class="str-num "><input type="checkbox" value="5" class="str-on-off" checked>5</td>
            <td class="flet-cell str5 pos-0 A"></td>
            <td class="flet-cell str5 pos-1 As Bb"></td>
            <td class="flet-cell str5 pos-2 B"></td>
            <td class="flet-cell str5 pos-3 C "></td>
            <td class="flet-cell str5 pos-4 Cs Db"></td>
            <td class="flet-cell str5 pos-5 D "></td>
            <td class="flet-cell str5 pos-6 Ds Eb"></td>
            <td class="flet-cell str5 pos-7 E "></td>
            <td class="flet-cell str5 pos-8 F"></td>
            <td class="flet-cell str5 pos-9 Fs Gb "></td>
            <td class="flet-cell str5 pos-10 G"></td>
            <td class="flet-cell str5 pos-11 Gs Ab"></td>
            <td class="flet-cell str5 pos-12 A pos-mark"></td>
            <td class="flet-cell str5 pos-13 As Bb"></td>
            <td class="flet-cell str5 pos-14 B"></td>
            <td class="flet-cell str5 pos-15 C "></td>
            <td class="flet-cell str5 pos-16 Cs Db"></td>
          </tr>
          <tr>
            <td class="str-num "><input type="checkbox" value="6" class="str-on-off" checked>6</td>
            <td class="flet-cell str6 pos-0 E"></td>
            <td class="flet-cell str6 pos-1 F"></td>
            <td class="flet-cell str6 pos-2 Fs Gb"></td>
            <td class="flet-cell str6 pos-3 G "></td>
            <td class="flet-cell str6 pos-4 Gs Ab"></td>
            <td class="flet-cell str6 pos-5 A "></td>
            <td class="flet-cell str6 pos-6 As Bb"></td>
            <td class="flet-cell str6 pos-7 B "></td>
            <td class="flet-cell str6 pos-8 C"></td>
            <td class="flet-cell str6 pos-9 Cs Db "></td>
            <td class="flet-cell str6 pos-10 D"></td>
            <td class="flet-cell str6 pos-11 Ds Eb"></td>
            <td class="flet-cell str6 pos-12 E "></td>
            <td class="flet-cell str6 pos-13 F"></td>
            <td class="flet-cell str6 pos-14 Fs Gb"></td>
            <td class="flet-cell str6 pos-15 G "></td>
            <td class="flet-cell str6 pos-16 Gs Ab"></td>
          </tr>
          <tr>
            <td class="str-num "><input type="checkbox" value="" class="all-on-off" checked>all</td>
            <td class="flet-num even pos-open"><input type="checkbox" value="0" class="flet-on-off" checked>0</td>
            <td class="flet-num odd"><input type="checkbox" value="1" class="flet-on-off" checked>1</td>
            <td class="flet-num even"><input type="checkbox" value="2" class="flet-on-off" checked>2 </td>
            <td class="flet-num odd"><input type="checkbox" value="3" class="flet-on-off" checked>3 </td>
            <td class="flet-num even"><input type="checkbox" value="4" class="flet-on-off" checked>4 </td>
            <td class="flet-num odd"><input type="checkbox" value="5" class="flet-on-off" checked>5 </td>
            <td class="flet-num even"><input type="checkbox" value="6" class="flet-on-off" checked>6 </td>
            <td class="flet-num odd"><input type="checkbox" value="7" class="flet-on-off" checked>7 </td>
            <td class="flet-num even"><input type="checkbox" value="8" class="flet-on-off" checked>8 </td>
            <td class="flet-num odd"><input type="checkbox" value="9" class="flet-on-off" checked>9 </td>
            <td class="flet-num even"><input type="checkbox" value="10" class="flet-on-off" checked>10</td>
            <td class="flet-num odd"><input type="checkbox" value="11" class="flet-on-off" checked>11</td>
            <td class="flet-num even"><input type="checkbox" value="12" class="flet-on-off" checked>12</td>
            <td class="flet-num odd"><input type="checkbox" value="13" class="flet-on-off" checked>13</td>
            <td class="flet-num even"><input type="checkbox" value="14" class="flet-on-off" checked>14</td>
            <td class="flet-num odd"><input type="checkbox" value="15" class="flet-on-off" checked>15</td>
            <td class="flet-num even"><input type="checkbox" value="16" class="flet-on-off" checked>16</td>
          </tr>
        </tbody>
      </table>
    </div>
    <div class="control">
      <label for="dosuu"><input type="radio" class="radio-button select-display-type" id="dosuu" name="disp-type" value="dosuu" checked>
      度数表示</label>
      <label for="tone-name"><input type="radio" class="radio-button select-display-type" id="tone-name" name="disp-type" value="onmei">
      音名表示</label>
    </div>
    <div class="control">
      <button id="download" class="btn_below">Download as PNG</button>
      <button id="keep" class="btn_below">Keep this flet board below</button>
    </div>
    <div id="keep_area">
    </div>
  </div>

</body>

</html>
$(function () {

  //コードとスケールの音程を格納
  const CHORD_DATA = {
    M: ["root", "third", "fifth"],
    M7: ["root", "third", "fifth", "maj7"],
    _7: ["root", "third", "fifth", "flat7"],//数字は要素名の先頭にできないので_7としている
    m: ["root", "flat3", "fifth"],
    m7: ["root", "flat3", "fifth", "flat7"],
    m7b5: ["root", "flat3", "flat5", "flat7"],
    dim: ["root", "flat3", "flat5", "sixth"],
    maj_scale: ["root", "nineth", "third", "fourth", "fifth", "sixth", "maj7"],
    min_penta: ["root", "flat3", "fourth", "fifth", "flat7"],
    maj_penta: ["root", "nineth", "third", "fifth", "sixth"],
    root: ["root"],
  };

  //表示用の文字を格納
  const CHORD_CHARACTOR = {
    M: ["●", "③", "⑤"],
    M7: ["●", "③", "⑤", "7"],
    _7: ["●", "③", "⑤", "♭7"],//数字は要素名の先頭にできないので_7としている
    m: ["●", "♭3", "⑤"],
    m7: ["●", "♭3", "⑤", "♭7"],
    m7b5: ["●", "♭3", "♭5", "♭7"],
    dim: ["●", "♭3", "♭5", "6"],
    maj_scale: ["●", "②", "③", "④", "⑤", "⑥", "⑦"],
    min_penta: ["●", "♭3", "④", "⑤", "♭7"],
    maj_penta: ["●", "②", "③", "⑤", "⑥"],
    root: ["●"],
  };

  //ルートを数値に変換する辞書
  const key_to_num = { A: 0, As: 1, Bb: 1, B: 2, C: 3, Cs: 4, Db: 4, D: 5, Ds: 6, Eb: 6, E: 7, F: 8, Fs: 9, Gb: 9, G: 10, Gs: 11, Ab: 11 }

  //音名から相対音程を表す数値(半音=1)への変換辞書
  const interval_to_num = {
    root: 0, flat2: 1, flat9: 1, second: 2, nineth: 2, flat3: 3, sharp9: 3, third: 4, eleventh: 5, fourth: 5,
    sharp11: 6, flat5: 6, fifth: 7, sharp5: 8, flat13: 8, flat6: 8, thirteenth: 9, sixth: 9, sharp13: 10, sharp6: 10, flat7: 10, maj7: 11
  };

  //#表記と♭表記の使い分け用の辞書。G#をGsと書くのはクラス名に#が使えないため。フラットは小文字のBで代用しているので問題ない
  const key_sharp = ["A", "As", "B", "C", "Cs", "D", "Ds", "E", "F", "Fs", "G", "Gs"];
  const key_flat = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"];

  /*
    コードやスケールを表示するまでの変換過程
    key → key_to_numでキー(ルート音)を数値に変換
    CHORD_DATA → inter_val_to_num で相対音程の数値に変換 → キーの数値と足し算して絶対音程の数値に変換
      → key_sharp か key_flatを使って、絶対音名に変換
      → 絶対音名でクラス指定したテーブルセルにCHORD_CHARACTORから取得した表記を記入する
  */

  let key = "A";//初期値。ルート音
  let chord_type = "_7";//初期値。
  let disp_type = "dosuu";//初期値 度数表記か絶対値表記かを指定
  let chord_Name = "7";//初期値。表示名

  generateTab($('#key-input').val().replace("#", "s"), chord_type, disp_type);

  function generateTab(key, chord_type, disp_type) {

    if (!key) return;
    //clear all tab
    $(".flet-cell").text("");

    const key_num = key_to_num[key];//ルート音を数値に変換。A=0
    console.log("key_num", key_num);
    const chord_tone = [];
    for (let tone of CHORD_DATA[chord_type]) { //コードデータから、root,thirdといった相対音程の情報を取り出して

      let temp = interval_to_num[tone] + key_num;//数値に変換。key_numを足して絶対音に変換
      if (temp > 11) temp -= 12;//音階は0〜11までなのでオーバーフロー処理
      console.log(tone, temp);
      if (key.slice(1.2) === "b" || key === "F") { //♭キーとFの時は♭表記にする
        chord_tone.push(key_flat[temp]);//コードトーンを格納
      } else {
        chord_tone.push(key_sharp[temp]);//コードトーンを格納(絶対音名が入る "A","C#","E"など)
      }
    }

    //トライアドとセブンスに色を付けるためのClassをクリア
    $(".flet-cell").removeClass("root");
    $(".flet-cell").removeClass("third");
    $(".flet-cell").removeClass("fifth");
    $(".flet-cell").removeClass("seventh");

    for (let tone in chord_tone) {

      switch (disp_type) {

        //度数表記か音名表記かで処理を分けている
        case "dosuu":
          $("." + chord_tone[tone]).text(CHORD_CHARACTOR[chord_type][tone]);//音名Classを持つtdに度数を入れる
          break;
        case "onmei":
          $("." + chord_tone[tone]).text(chord_tone[tone].replace("s", "#"));//音名Classを持つtdに絶対音名を入れる
          break;
      }

      //トライアドとセブンスを色分けするためのクラスを付加
      switch (CHORD_DATA[chord_type][tone]) {
        case "root":
          $("." + chord_tone[tone]).addClass("root");
          break;
        case "third":
          $("." + chord_tone[tone]).addClass("third");
          break;
        case "flat3":
          $("." + chord_tone[tone]).addClass("third");
          break;
        case "fifth":
          $("." + chord_tone[tone]).addClass("fifth");
          break;
        case "flat5":
          $("." + chord_tone[tone]).addClass("fifth");
          break;
        case "maj7":
          $("." + chord_tone[tone]).addClass("seventh");
          break;
        case "flat7":
          $("." + chord_tone[tone]).addClass("seventh");
          break;
      }
    }
  }

  //不要なフレット表示のオン・オフ
  $('.flet-on-off').click(function () {
    const className = ".pos-" + $(this).val();
    if ($(this).prop('checked')) {
      $(className).css('visibility', 'visible');

    } else {
      $(className).css('visibility', 'hidden');
    };
  });

  //不要な弦表示のオンオフ
  $('.str-on-off').click(function () {
    const className = ".str" + $(this).val();
    if ($(this).prop('checked')) {
      $(className).css('visibility', 'visible');

    } else {
      $(className).css('visibility', 'hidden');
    };
  });


  //全フレットの一括オン・オフ
  $('.all-on-off').click(function () {
    if ($(this).prop('checked')) {
      $('.flet-cell').css('visibility', 'visible');
      $('.flet-on-off').prop('checked', true);
    } else {
      $('.flet-cell').css('visibility', 'hidden');
      $('.flet-on-off').prop('checked', false);
    };
  });

  //キー入力のチェックと自動補正
  $('#key-input').keyup(function () {
    let input = $(this).val();
    input = input.slice(0, 1).toUpperCase() + input.slice(1, 2);
    $(this).val(input);
    let temp1 = input.slice(0, 1);
    if (!temp1.match(/^[A-G]$/)) {
      $(this).val("");
      return;
    }
    if (input.length === 1) {
      key = $(this).val();
      generateTab(key, chord_type, disp_type);
    }
    temp1 = input.slice(1, 2);
    if (temp1.match(/^[b#]$/)) {
      key = $(this).val().replace("#", "s");
      generateTab(key, chord_type, disp_type);
    } else {
      $(this).val(input.slice(0, 1));
    }
  });

  //コードタイプを選択したらタブを作成
  $('.select-chord-type').change(function () {
    chord_type = $(this).val();
    chord_Name = $(this).data("chordname");
    console.log(chord_Name);
    key = $('#key-input').val().replace("#", "s");
    generateTab(key, chord_type, disp_type);
  });

  //表示形式を選択したらタブを作成
  $('.select-display-type').change(function () {
    disp_type = $(this).val();
    key = $('#key-input').val().replace("#", "s");
    generateTab(key, chord_type, disp_type);
  });

  //PNGでダウンロード
  $('#download').click(function () {
    $('#ChordName').text(key.replace("s", "#")  + chord_Name);//左上にコード(スケール名)を一時的に表示
    html2canvas(document.querySelector("#guitar")).then(canvas => {
      let downloadEle = document.createElement("a");
      downloadEle.href = canvas.toDataURL("image/png");
      const filename = key + chord_Name + ".png";
      downloadEle.download = filename;
      downloadEle.click();
      $('#ChordName').text("");
    });
  });

  //今のフレット図を画像化してキープ
  $('#keep').click(function () {
    html2canvas(document.querySelector("#guitar")).then(canvas => {
      let d = document.createElement("div");
      let h1 = document.createElement("h1");
      h1.textContent = key.replace("s", "#") + chord_Name;
      d.appendChild(h1);
      d.appendChild(canvas);
      d.classList.add("keeped-flet");
      $('#keep_area').append(d);
    });
  });

});
#flet-wrapper {
  margin-top: 50px;
  margin-left: 5%;
  width: 80%;
}

table {
  width: 100%;
}

body {
  font-family: sans-serif;
}

table, td, th {
  border-right: 2px solid gray;
  border-collapse: collapse;
  margin-bottom: 10px;
}



td, th {
  padding: 3px;
  width: 60px;
  height: 50px;
}


td:not(.flet-num, .pos-mark, .pos-0, .str-num) {
  background: linear-gradient(to bottom, white 0%, white 46%, black 47%, black 53%, white 54%, white 100%)
}

.pos-mark {
  background: linear-gradient(to bottom, lightgray 0%, lightgray 46%,
      black 47%, black 53%, lightgray 54%, lightgray 100%)
}


.even {
  background-color: #707070;
}

.odd {
  background-color: #1d1d1d;
}

.flet-num {
  height: 25px;
  text-align: center;
  color: white;
  font-size: large;
}

.flet-cell {
  text-align: center;
  color: gray;
  font-size: xx-large;
  font-weight: bold;
  text-shadow:
    5px 5px 5px white, -5px -5px 5px white,
    -5px 5px 5px white, 5px -5px 5px white;

}

.pos-open, .pos-0, .str-num {
  width: 30px;
  text-align: right;
  border-right: 7px slid black;
}

.pos-mark {
  background-color: #818181;
}

.str1 {
  border-top: 3px solid gray;
}

.root {
  color: black;
  font-weight: bold;
}


.third {
  color: red;
}

.fifth {
  color: blue;
}

.seventh {
  color: green;
}

.control {
  font-size: xx-large;
  margin-left: 50px;
  margin-bottom: 50px;
}

label {
  display: inline-block;
}

.radio-button {
  margin-left: 20px;
  margin-top: 20px;
  margin-right: 5px;
  transform: scale(2.5);
  vertical-align: 25%;
}

#key-input {
  width: 2em;
  font-size: 200%;
}

.btn_below {
  margin: 10px;
  font-size: xx-large;
}

#ChordName {
  margin-left: 50px;
  font-size: xx-large;
}

.keeped-flet {
  margin-top: 30px;
}

.credit {
  font-size: medium;
}

-html, Javascript, プログラミング, 楽器