Reactアプリの続きです。前回はTone.jsを使ってビートプレイヤーを作りました。今回も音楽アプリでコードプレイヤーを作りました。以前CodePenで作成したコードプレイヤーをReact/Next環境に移植し、新機能を追加しました。それではいきましょう!
【目次】
※参考:前回記事
【React & Tone.js】ビートプレイヤーを作った(ビートとBPMを変更可能) - クモのようにコツコツと
※参考:【React】ReactでWebアプリを作るシリーズまとめ
qiita.com
作ったもの
コードプレイヤー
下記のような用途に活用できる。
- 「根音」で鍵盤から単音を鳴らせる
- 「三和音」「四和音」「五和音」で和音を鳴らせる
- 和音の構成音を鍵盤の色や文字情報で調べることができる
- 和音の種類を変えると同じキーでの構成音の違いを比較できる
作ったアプリはこちら
※参考:コードプレイヤー
アプリの使い方
コードタイプ一覧
ソースコード
※参考:GitHub - ryo-i/chord-player: Tone.jsを使ったコードプレイヤー
このアプリで実現したかったこと
以前、こちらのTone.jsを使ってCodePen上に作ったコードプレイヤー
※参考:【Tone.js】和音楽器に五和音(テンションコード)他を追加する - クモのようにコツコツと
※参考:【Web Audio】Tone.jsを習得するためにやったことまとめ(随時更新) - Qiita
これをReact/Next環境で再現したい。前回の配色ジェネレーターと同じく、Nextスターターキットの環境をベースに作成する。
※参考:GitHub - ryo-i/next-app-started
※参考:【React】ReactでWebアプリを作るシリーズまとめ(随時更新) - Qiita
ソースコード
上記の挙動を実現しているソースコードはこちら。
※参考:GitHub - ryo-i/chord-player: Tone.jsを使ったコードプレイヤー
tone.jsのインストール
前回のビートプレイヤーと同じ手順。下記のコマンド
$ npm i tone
※参考:Tone.js
データ:data.json
inner
キーの中にコードプレイヤー用のデータ(設定値)を入れている。
"inner": { "keys": // 鍵盤の名称 , "keyButtons": // 鍵盤タグの値 , "scale": // スケール(音程)の一覧 , "chordTypes": // コードタイプの一覧 , "chordTypeButtons": // コードタイプ設定タグの値 },
CodePen版で直に配列で書いていた設定値をこちらに移動した。また、タグの数が多いのでループで生成するためにタグの設定値もこちらに追加した。
keys
キーは鍵盤の名称。このキーを元にコードの構成音を取得する。
"keys": [ ["C4"], ["C#4"], ["D4"], ["D#4"], ["E4"], ["F4"], ["F#4"], ["G4"], ["G#4"], ["A4"], ["A#4"], ["B4"], ["C5"] ],
keyButtons
キーは鍵盤タグの値。この値をループで読み込んでJSXの鍵盤タグを生成する。
"keyButtons": [ {"value": "C4" , "className": "w_key", "keyName": "C", "onClick": true}, {"value": "C#4" , "className": "b_key" , "keyName": "C#", "onClick": true}, // 中略 ],
scale
はスケール(音程)の一覧。コード構成音の番号に該当する音程をここから取得する。
"scale": [ "null", "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", "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6" ],
chordTypes
キーはコードタイプの一覧。コードタイプを変更した時にこの情報をページに表示したり、鍵盤を押したときのコード構成音の音程を取得する。
"chordTypes": [ { "chordValue": "ルート", "chordName": "(R)", "chordKeys": [1] }, { "chordValue": "パワーコード", "chordName": "5", "chordKeys": [1,8] }, // 中略 ],
chordTypeButtons
キーはコードタイプ設定タグの値。この値をループで読み込んでJSXのコードタイプ設定タグを生成する。
"chordTypeButtons": { "root": [ {"id": "chord_R", "value": "ルート", "cohrdTypeName": "(R)", "defaultChecked": true}, {"id": "chord_5", "value": "パワーコード", "cohrdTypeName": "5"} ], "triad": [ {"id": "chord_M", "value": "メジャー", "cohrdTypeName": "(M)"}, {"id": "chord_m", "value": "マイナー", "cohrdTypeName": "m"}, // 中略 ], "seventh": [ {"id": "chord_M7", "value": "メジャー・セブンス", "cohrdTypeName": "M7"}, {"id": "chord_7", "value": "セブンス", "cohrdTypeName": "7"}, // 中略 ], "tension": [ {"id": "chord_M9", "value": "メジャー・ナインス", "cohrdTypeName": "M9"}, {"id": "chord_9", "value": "ナインス", "cohrdTypeName": "9"}, // 中略 ] }
※参考:chord-player/data.json at main · ryo-i/chord-player · GitHub
Inner.tsx(全体)
Inner.tsxはコードプレイヤーの本体にあたるファイル
※参考:chord-player/Inner.tsx at main · ryo-i/chord-player · GitHub
全体的な構成はこうなっている。
import React, { useState, useEffect, useRef } from 'react'; import styled from 'styled-components'; import { inner } from '../data/data.json'; import * as Tone from 'tone'; // CSS in JS // 中略 const CoadPlayer = styled.div` // 中略 `; // Component function Inner() { // 中略(フック、Tone.js、JSXを設定) } export default Inner;
react
とフックuseState
,useEffect
,useRef
をインポート- CSS in JS
styled-components
をインポート - 設定データ
data.json
からinner
キーをインポート - Tone.js
tone
をインポート - 関数
Inner()
でInnerコンポーネントを設定(中でフック、Tone.js、JSXを設定) - Innerコンポーネントをエクスポート
CoadPlayerコンポーネント(CSS in jS)
変数keyWidth
で鍵盤タグの幅を設定(後でcalc()
の中で鍵盤数を掛けて鍵盤全体の幅を設定)
const keyWidth = '37px'
CoadPlayer
コンポーネントにstyled-components
によるCSS in JSの設定を書いている。div
タグになる。
const CoadPlayer = styled.div` color: #fff; #key { max-width: calc(${keyWidth} * 21); margin: 0 auto; overflow-x: scroll; .key_inner { background: #333; width: calc(${keyWidth} * 21); display: block; padding: 0 0 10px; position: relative; button { width: ${keyWidth}; text-align: center; display: inline-block; } // 中略 } `;
CodePenのCSSをSass(SCSS)的な書き方で整理する。
また、ビートプレイヤーの時と同じく全体的に黒背景ベースのスタイルにした。
Innerコンポーネント(全体)
Innerコンポーネントでフック、Tone.js、JSXを設定している。全体的な構成はこうなっている。
// Component function Inner() { // Hooks const [synth, setSynth] = useState(null); // 他、中略 // オブジェクト設定 interface chordTypes { // 中略 }; interface keyButtons { // 中略 }; interface chordTypeButtons { // 中略 }; // シンセ設定 useEffect(() => { // 中略 },[]); // 鍵盤リセット const resetKey = (): void => { // 中略 }; // 鍵盤カレント const currentKey = (currentChord): void => { // 中略 }; // 最新のコード取得 const getChord = (key: string, chords: string[][]): string[] => { // 中略 }; // 鍵盤の構成音のテキスト取得 const chordKeysText = (chord: string[]): string[] => { // 中略 }; // 鍵盤クリックイベント const clickKey = (e: React.MouseEvent<HTMLButtonElement>): void => { // 中略 }; // コードタイプ取得 // 中略 }; //コード取得 const getChords = (chordTypes: chordTypes): string[][] => { // 中略 }; // コード構成音変更 const changeChordInterval = (currentChords: string[][]): void => { // 中略 }; //コードタイプ変更イベント const chordTypeSelect = (e: React.ChangeEvent<HTMLInputElement>): void => { // 中略 } // JSX return ( // 中略 ); }
フック設定
冒頭でフック設定をしている。
// Hooks const [synth, setSynth] = useState(null); const [chords, setChords] = useState(inner.keys); const [chord, setChord] = useState(['-']); const [rootKey, setRootKey] = useState('-'); const [chordsInterval, setChordInterval] = useState('-'); const [chordValue, setChordValue] = useState(inner.chordTypes[0].chordValue); const [chordName, setChordName] = useState(inner.chordTypes[0].chordName); const [chordKeys, setChordKeys] = useState(inner.chordTypes[0].chordKeys.join(',')); const keyElement = useRef<HTMLInputElement>(null);
synth
はシンセ設定。初期値はnullchords
はコード音の設定。初期値はinner
(データ)のkeys
キーchord
はコードタイプの設定。初期値は['-']
(配列の中に文字列の-
が一つ)rootKey
はルート音設定。初期値は文字列の-
chordsInterval
はコードの音程設定。初期値は文字列の-
chordValue
はコードの日本語名。初期値はinner
の配列chordTypes
の一つ目のchordValue
chordName
はコードの記号名。初期値はinner
の配列chordTypes
の一つ目のchordName
chordKeys
はコード構成音の番号。初期値はinner
の配列chordTypes
の一つ目のchordKeys
をjoin(',')
でカンマ区切りの文字列にしているkeyElement
はuseRef
でinputタグを取得。初期値はnull
配列をカンマ区切りにするメソッドjoin()
※参考:[JavaScript] 配列→カンマ区切り文字列にする – コピペで使える JavaScript逆引きリファレンス
ちなみにカンマ区切りの文字を配列にするメソッドはsplit()
※参考:[JavaScript] カンマ区切り文字列→配列にする – コピペで使える JavaScript逆引きリファレンス
useRef
の型の書き方
※参考:TypeScriptのもとでuseRefを使うときに知るべきRefObjectとMutableRefObjectについて
useState
の型は任意
※参考:TypeScriptで使うReact hooks | JS Challenge
オブジェクト型設定
今回、TypeScriptの型推論だけでなく自分でもなるべく型をつけていこうと思い、オブジェクト(連想配列)のキーにもinterface
でオブジェクト型も設定した。
TypeScript型設定の参考
※参考:TypeScriptの型入門 - Qiita
※参考:TypeScript超入門(2):構文を理解する - Qiita
chordTypes
、keyButtons
、chordTypeButtons
// オブジェクト設定 interface chordTypes { chordValue: string; chordName: string; chordKeys: number[]; }; interface keyButtons { value: string; className: string; onClick: boolean; keyName: string; }; interface chordTypeButtons { id: string; value: string; cohrdTypeName: string; defaultChecked: boolean; };
シンセ設定
シンセ音の設定を初期に一回だけ実行する。これをしないと鍵盤を押すたびにシンセ音が重なってだんだん音が濁る現象が起こった。
※参考:鍵盤を連打するとだんだん音が濁る · Issue #6 · ryo-i/chord-player · GitHub
// シンセ設定 useEffect(() => { setSynth(new Tone.PolySynth().toDestination()); },[]);
いろいろ調べた結果、フックsynth
のuseState()
初期値をnull
にしつつ、下記のuseEffect()
(第二引数が空の配列でページ読み込み時に1回だけ実行)でsetSynth()
で設定する方法がうまくいった。
こちらの方法が参考になった!
※参考:JavaScript - Reactで関数を1回だけ実行する
鍵盤リセット
今回、CodePen版にはなかった新機能として和音の構成音にあたる鍵盤の色を変えた。
そのために、別の鍵盤を押したり、コードタイプを実行した時に、このresetKey()
関数で鍵盤色を変えるclassを削除して、リセットしている。
// 鍵盤リセット const resetKey = (): void => { const keyElements: HTMLCollection = keyElement.current.children; for (let i = 0; i < keyElements.length; i++) { if (keyElements[i].classList.contains('current')) { keyElements[i].classList.remove('current'); } } };
関数の戻り値(return
)の型はカッコのあとにつけるのだが、戻り値がない関数はvoid
をつける。
keyElement
はuseRef
でcurrent.children
プロパティによって子要素を取得できる。
useRefのドキュメント、読んでもいまいちわかりづらかったが
※参考:フック API リファレンス – React
こちらの記事がわかりやすかった
※参考:【useRef】React hookが便利すぎる
class名の存在チェックはcontains、削除はremove
※参考:Element.classList - Web API | MDN
鍵盤カレント
先ほどのresetKey()
関数の後に、currentKey()
関数で鍵盤リセットの後に、鍵盤の色を変更するclass名を追加する。
// 鍵盤カレント const currentKey = (currentChord): void => { const keyElements: HTMLCollection = keyElement.current.children; for (let i = 0; i < keyElements.length; i++) { const getKeyElement: HTMLButtonElement = keyElements[i] as HTMLButtonElement; const keyText: string = getKeyElement.value; if (currentChord.includes(keyText)) { keyElements[i].classList.add('current'); } } };
keyElement
の子要素のinputタグの中でvalueが引数のcurrentChord
と一致する鍵盤にclass名current
を追加する。
最新のコード取得
鍵盤を押したりコードタイプを変更したときに、その時のルート音の音程に該当するコード構成音を取得する。
// 最新のコード取得 const getChord = (key: string, chords: string[][]): string[] => { let getCurrentChord: string[]; for (let i = 0 ; i < chords.length; i++) { if (chords[i].indexOf(key) === 0) { getCurrentChord = chords[i]; } } return getCurrentChord; };
配列内の存在チェックの方法はいろいろあるが、今回は配列の1番目との一致を調べたかったのでindexOf()を使った
※参考:JavaScriptで指定した要素が配列に存在するかチェックする方法を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン
鍵盤の構成音のテキスト取得
これも新機能で、ルート音に該当するコード構成音の音程を取得して画面に表示する。
// 鍵盤の構成音のテキスト取得 const chordKeysText = (chord: string[]): string[] => { let chordKeysText: string[] = []; for (let i = 0; i < chord.length; i++) { const getChortText: string = chord[i].slice(0, -1); chordKeysText.push(getChortText); } return chordKeysText; };
chord
の末尾には4
など何オクターブ目かを表す数値がついている。この数値はページに表示する際には不要なので、slice()
で末尾の一文字を削除した。
※参考:JavaScriptで先頭、末尾の文字を削除する方法
鍵盤クリックイベント
鍵盤を押した時の処理
// 鍵盤クリックイベント const clickKey = (e: React.MouseEvent<HTMLButtonElement>): void => { const eventTarget: HTMLButtonElement = e.target as HTMLButtonElement; const KeyValue: string = eventTarget.value; const getCurrentChord: string[] = getChord(KeyValue, chords); setChord(getCurrentChord); resetKey(); currentKey(getCurrentChord); const getChordsIntervalsArray: string[] = chordKeysText(getCurrentChord); const getRootkey: string = getChordsIntervalsArray[0]; const getChordsIntervals: string = getChordsIntervalsArray.join(', '); setRootKey(getRootkey); setChordInterval(getChordsIntervals); synth.triggerAttackRelease(getCurrentChord, 0.4); };
- 引数が押した鍵盤のbuttonタグの情報
- その内容を元に
getChord()
を実行しsetChord()
にセット - 鍵盤色を
resetKey()
とcurrentKey()
で変更 chordKeysText()
でコード音程のテキストを取得し、setRootKey()
にルート音、setChordInterval()
にコード音程をセットsynth.triggerAttackRelease()
でシンセ音を鳴らす。
TypeScriptのイベント系の型
※参考:any型で諦めない React.EventCallback - Qiita
※参考:React + TypeScript: ReactでTypeScriptを使うとき基本として知っておきたいこと - Qiita
「Property 'value' does not exist on type 'EventTarget'.」エラーには「event.target as HTMLElement」と書いたら解決した。
※参考:javascript - Property 'value' does not exist on type EventTarget in TypeScript - Stack Overflow
コードタイプ取得
ここからはコードタイプ変更関係の処理になる。
引数のvalueとマッチするデータのchordTypes
の情報を返す処理
// コードタイプ取得 const getChordTypes = (getChordValue: string): chordTypes => { let getchordTypes: chordTypes; for (let i = 0; i < inner.chordTypes.length; i++) { if (inner.chordTypes[i].chordValue === getChordValue) { getchordTypes = inner.chordTypes[i]; } } return getchordTypes; };
戻り値の型は先ほどオブジェクト型で設定したchordTypes
コード取得
引数のchordTypes
を元にデータのスケールからコードの音程を配列に追加して返す処理
// コード取得 const getChords = (chordTypes: chordTypes): string[][] => { let getChords: string[][] = []; for (let i = 0 ; i < inner.keys.length; i++) { getChords.push([]); for (var j = 0; j < chordTypes['chordKeys'].length; j++){ const key: string = inner.scale[i+chordTypes['chordKeys'][j]]; getChords[i].push(key); } } return getChords; };
多次元配列の型はstring[][]
など[]
を2つ重ねる
※参考:TypeScriptの複雑な型 - asterisks
コード構成音変更
コード構成音が変わる時の鍵盤のカレントclassと表示されるテキストのフック変更
// コード構成音変更 const changeChordInterval = (currentChords: string[][]): void => { const getRoot: string = String(chord[0]); const getCurrentChord: string[] = getChord(getRoot, currentChords); resetKey(); currentKey(getCurrentChord); const getChordsIntervalsArray: string[] = chordKeysText(getCurrentChord); const getChordsIntervals: string = getChordsIntervalsArray.join(', '); setChordInterval(getChordsIntervals); };
- 引数の
currentChords
を元にgetChord()
で最新のコード取得して該当する鍵盤にclass名を追加 chordKeysText()
で鍵盤の構成音のテキスト取得し、join()
で半角カンマ区切りの文字列に結合してsetChordInterval()
にセット
コードタイプ変更イベント
コードタイプ変更のinputタグを押した時のイベント
// コードタイプ変更イベント const chordTypeSelect = (e: React.ChangeEvent<HTMLInputElement>): void => { const getChordValue: string = e.target.value; const getCurrentChordTypes: chordTypes = getChordTypes(getChordValue); setChordValue(getCurrentChordTypes.chordValue); setChordName(getCurrentChordTypes.chordName); setChordKeys(getCurrentChordTypes.chordKeys.join(', ')); const getCurrentChords: string[][] = getChords(getCurrentChordTypes); setChords(getCurrentChords); if (rootKey !== '-') { changeChordInterval(getCurrentChords); } }
- 引数
e
は押されたコードタイプ変更のinputタグ e
のvalueを元にgetChordTypes()
でコードタイプを取得し、その中の値をsetChordValue()
、setChordName()
、setChordKeys()
にセットgetChords()
でコードの音程情報を取得しsetChords()
にセット- ルートのフック
rootKey
が-
(初期値)でなければchangeChordInterval()
でコード構成音を変更
JSX設定
JSXタグをレンダリング処理
// JSX return ( <> <CoadPlayer> <div id="key"> <div className="key_inner" ref={keyElement}> {inner.keyButtons.map((val: keyButtons) => <button key={val.value} value={val.value} className={val.className} onClick={val.onClick ? clickKey : null}>{val.keyName}</button> )} </div> </div> <div id="chord_type"> <section id="chord_text"> <h2 id="chord_type">{chordValue}</h2> <p id="chord_keys">構成音: {chordKeys}</p> <p id="chord_name">{rootKey}{chordName}: {chordsInterval}</p> </section> <div id="chord_types"> <dl id="root"> <dt>根音</dt> <dd> {inner.chordTypeButtons.root.map((val: chordTypeButtons) => <label key={val.id}><input key={val.id} type="radio" id={val.id} name="chord_type" value={val.value} onChange={chordTypeSelect} defaultChecked={val.defaultChecked || null} />{val.cohrdTypeName}</label> )} </dd> </dl> <dl id="triad"> <dt>三和音</dt> <dd> {inner.chordTypeButtons.triad.map((val: chordTypeButtons) => <label key={val.id}><input key={val.id} type="radio" id={val.id} name="chord_type" value={val.value} onChange={chordTypeSelect} defaultChecked={val.defaultChecked || null} />{val.cohrdTypeName}</label> )} </dd> </dl> <dl id="seventh"> <dt>四和音</dt> <dd> {inner.chordTypeButtons.seventh.map((val: chordTypeButtons) => <label key={val.id}><input key={val.id} type="radio" id={val.id} name="chord_type" value={val.value} onChange={chordTypeSelect} defaultChecked={val.defaultChecked || null} />{val.cohrdTypeName}</label> )} </dd> </dl> <dl id="tension"> <dt>五和音</dt> <dd> {inner.chordTypeButtons.tension.map((val: chordTypeButtons) => <label key={val.id}><input key={val.id} type="radio" id={val.id} name="chord_type" value={val.value} onChange={chordTypeSelect} defaultChecked={val.defaultChecked || null} />{val.cohrdTypeName}</label> )} </dd> </dl> </div> </div> </CoadPlayer> </> ); }
CoadPlayer
コンポーネントはCSS in JS. key_inner
タグのref
属性の値keyElement
が子要素タグの取得処理に使われる- データの
inner.keyButtons
からmap()
ループで鍵盤のbuttonタグを生成 onClick
イベントはonClick
キーがtrueならclickKey
を、なければnull
(音が鳴らない鍵盤)#chord_types
タグ以下ではデータinner.chordTypeButtons
以下からタイプによってroot
、triad
、seventh
、tension
オブジェクトからmap()
ループでコードタイプ変更のinputタグを生成onChange
イベントにはchordTypeSelect
を設定defaultChecked
設定はval.defaultChecked
があれば設定、なければnull
似たような構成のタグが多かったのでmap()
でループして生成
※参考:【React】ループの書き方(for文エラー回避、配列、map()) - クモのようにコツコツと
マップの引数val
には冒頭で設定したオブジェクト型keyButtons
(鍵盤)、chordTypeButtons
(コードタイプ設定)を設定している
条件によってあったりなかったりする属性は三項演算子や論理演算子||
で分岐処理
※参考:【React】条件分岐の書き方(if文エラー回避、論理演算子、三項演算子) - クモのようにコツコツと
鍵盤のref
属性は子要素取得するためにuseRef
と連携して使う
※参考:Ref と DOM – React
リポジトリ名の変更→Vercelに反映
CodePen版の中にあるコードの綴りがかなりの部分が「cord」になっており、ただしくは「chord」なので置換する必要があった。
和音(わおん、英語: chord(コード)、独: Akkord)
※参考:和音 - Wikipedia
そしてリポジトリ名自体も「cord」になっていたので、リポジトリ名の変更を行なった。
リポジトリの変更方法
※参考:[https://docs.github.com/ja/github/administering-a-repository/managing-repository-settings/renaming-a-repository
GitHubのリポジトリ名変更方法 - Qiita]
まずGitHub上のリポジトリ名をリネイムし、次にローカルのリポジトリURLを変更
$ git remote set-url origin https://github.com/ryo-i/chord-player
configファイルのremote "origin"のurlが変わった。
リポジトリ名変更後にVercelへのデプロイがどうなるか
※参考:GibHub でリポジトリの名前を変更する · GitHub
Vercelのプロジェクト内にProject Settingsというページがあり、そこでプロジェクト名を「chord-player」に変更してみるが、下記のデプロイエラーになった
Failed to assign a domain to your deployment due to the following error: Aliasing the Deployment Timed Out
「次のエラーのため、デプロイメントにドメインを割り当てることができませんでした。 展開のエイリアスがタイムアウトしました」
Vercelのプロジェクト内のSettingsのdomainsでドメインも変更してみたがうまくいかず。
デプロイがうまくされないため、新プロジェクトを作ることにする。旧プロジェクトは「-bk」付きに再度リネイムした。
Vercelで新規プロジェクトを作成してデプロイうまくいったが、「-bk」付きの方も二重でデプロイされる。こちらのGitHubとの連携は解除したところ、新プロジェクトのみにデプロイされるようになった!
※参考:リポジトリ名を修正(cord-player -> chord-player) · Issue #4 · ryo-i/chord-player · GitHub
今後の課題
本アプリで聴けるコードは根音(ルート)の上に和音の構成音を重ねる配置だが、実際の音楽のアレンジでは和音の構成音を前後のオクターブに配置する「ボイシング」が行われることが多い。
コードチェンジの移動をなるべく少なくするために1オクターブ内で構成音の配置を変えるクローズドボイシング、両手を使って構成音が1オクターブ以上に広がった響きにするオープンボイシングがある。
ボイシングの種類やそれを実現する処理の検討はまだ検討できていないため、今後の課題としたい。
あと、今は「ジャーン」と和音を同時に鳴らしていますが、この他に「タ・ラ・ラ・ラーン、ジャーン」みたいな一音ずつ聴けるモードが必要なのか検討している。
今のメニュー数がSPの1画面に収まっているので、機能追加によってボタンが増えするのも考えものかという気持ちもある。。
最後に
ということでコードプレイヤーが完成しました。今回は、CodePen時代にはなかった新機能として、鍵盤にコード構成音の色をつけたり、ルート音が変わったときの音程をテキストで表示することができました。これによって、構成音が視覚的にもわかりやすくなたっと思います!
ピアノの白鍵黒鍵はCメジャースケールがベースになっているため、ルートが変わると黒鍵白鍵の組み合わせが変わるところに難しさを感じていました。このアプリでコードの理解のわかりやすさにつながるといいなと思います。
次は音楽編の第三弾、「スケールプレイヤー」を作りたく思います!このコードプレイヤーをベースに改造すれば作れそうな気がしています。スケールもキーが変わると白鍵黒鍵の組み合わせが変わるため、そこが視覚的にわかるといいなと思います。
スケールプレイヤーができると音楽の三要素「メロディ、ハーモニー、リズム」が揃うことになります♪それではまた!
※参考:【React】ReactでWebアプリを作るシリーズまとめ
qiita.com