こういうのを作ってます。一応今回で完結。
前回記事を見る場合はこちらから:
limnanthaceae.hatenablog.com
シリーズの最初から見る場合はこちらから:
limnanthaceae.hatenablog.com
せっかくp5.jsを使っているので、もう少し動的なあれこれを実装します。
その前にレイアウト変更
スマホで閲覧しても見やすいような配置に変更します。
constellation→chord→config→buttonの順番でしたが、configを前に持ってきてconfig→constellation→chord→buttonの順に。各要素が縦に並ぶようにして、constellation→chord→buttonが一画面に収まるようにします(configからスクロールしないと操作できないというのは些か面倒ではあるので折りたたみ式とかにするかも)。PCではconfigが左半分、それ以外が右半分という表示になります。
wrapを使うと、画面の横幅が小さくなった時に自動的に縦配列になります。
.container{ display: flex; flex-wrap: wrap; justify-content: center; }
ついでなので、「全選択」とblack adder chord(イキスギコード)*1も追加。
See the Pen
chordtrainer-10 by frecafloros (@frecafloros)
on CodePen.
ボタン改良
「解答」ボタンと「次の問題」ボタンを統合します。こいつらは元々交互に押されるものであって2つもいらないので。
本当はjQueryを使うのが簡単に実装できる方法だとは思いますが、今回はcssとjavascriptで制御します。
css側からテキストを制御するために、:before
や:after
*2といった「擬似要素」と呼ばれるものを活用します。普通にcssのcontent
で指定するだけでは文字列の書き換えをすることが(多分)できないのですが、擬似要素で要素もどきを作ることで文字列の書き換えができるようになります。
:before
や:after
は要素の前後に内容を追加することができるもので、class名:before
で要素の直前、class名:after
で要素の直後に内容を追加します。
「次の問題」のclassをqa-next
、「解答」のclassをqa-answer
として、次のように指定します。
<div class="obj-qa"> <button class="qa-next" id="btn-qa"> </button> </div>
#btn-qa{ width: 80px; } /* 次の問題ボタン */ .qa-next{ font-size: 0px; } .qa-next:after{ font-size: 14px; content: '次の問題'; } /* 解答ボタン */ .qa-answer{ font-size: 0px; } .qa-answer:after{ font-size: 14px; content: '解答'; }
続いて、「次の問題」→「解答」→「次の問題」→…の(テキストと押したときの効果の)切り替えは要素のclassをjsで書き換えることにより実装します。ボタンを押すたびに関数の実行、qa-next
classとqa-answer
classの入れ替えを実施します。
// qaボタン制御 let buttonQA = ()=>{ const buttonQA = document.getElementById("btn-qa"); let buttonQAClass = document.getElementsByClassName("qa-next"); if(buttonQAClass.length == 0){ // 現在のclassがqa-answer buttonAnswer(); // 解答の表示 buttonQA.classList.remove("qa-answer"); buttonQA.classList.add("qa-next"); }else{ // 現在のclassがqa-next buttonNext(); // 出題 buttonQA.classList.remove("qa-next"); buttonQA.classList.add("qa-answer"); } }
getElementsByClassName
は指定したclassの要素を抽出してくる関数で、要素をリスト形式で管理します。今回はフラグの代わりとして使っていて、リストの長さが0(=qa-next
のクラスとなっている要素が無い=今のクラスがqa-answer
)かそうでないか(=今のクラスがqa-next
)で処理を分岐させています。
ボタンのデザインも少し変更したものがこちら。
See the Pen
chordtrainer-11 by frecafloros (@frecafloros)
on CodePen.
サウンドビジュアライズ
本題。解答表示後、和音の可視化をします。
音の振幅値をとるためにp5.soundを導入します。これはp5.jsの拡張ライブラリです。(本当はTone.jsでやりたかったけど振幅値をリアルタイムで取れなかったのでp5側でやります。)
振幅値とるの無理だったので決めうちで実装。後述。
解答表示する前に可視化されると盛大なネタバレになってしまうので、表示したい時に表示できるようにしたい。
loop()
とnoLoop()
を使います。
まずsetup()
の時点ではリアルタイムで描画しないのでnoLoop()
で止めておきます。
draw()
が実質メインループになりますので、ここで描画処理させます。今回はメインループの外に関数drawConstellation();
とsoundVisualizer()
を作りました。一定時間が経過するとnoLoop()
を実行してループが停止します。あんまりメインループにループ停止書かないほうがいい気はする。
// chord constellationの描画 function setup(){ createCanvas(canvasWidth, canvasHeight).parent('chord-constellation'); textAlign(CENTER,CENTER); textSize(16); textFont('Optima'); translate(canvasWidth/2, canvasHeight/2); const r = 80; for(let i=0;i<12;i++){ text(pitch2keyname[i], r*sin(i*TAU/12), -1*r*cos(i*TAU/12)); } // 描画停止 noLoop(); } // サウンドビジュアル描画(メインループ?) function draw(){ // user gesture onclickSound.onclick = buttonSound; onclickQA.onclick = buttonQA; if(t<60){ // 描画 drawConstellation(); soundVisualizer(); // buttonQAを非活性化 buttonQAId.setAttribute("disabled", true); }else{ // 描画停止 noLoop(); // buttonQAを活性化 buttonQAId.removeAttribute("disabled"); } }
constellation の描画中に次の問題に行かれると次の問題の constellation を描画されてしまうので、constellation 描画中は「次の問題」ボタンを非活性にしています。非活性化はsetAttribute("disabled", true)
、活性化はremoveAttribute("disabled")
。"disabled"という属性をつけたり剥いだりして実装しています。
soundVisualizer()
では、和音構成音に対応した円弧を描画します。
function soundVisualizer(){ t = t + 1; // 円弧の size 設定 if(t<4){ // Attack size = rVisual/4*t; }else if(t<10){ // Decay size = rVisual - rVisual/4*(t-4)/6; }else{ // Release size = rVisual*3/4 - rVisual*3/4*(t-10)/50; } //円弧を描く colorMode(HSB); for(let i=0;i<chord.length;i++){ tmpTone = (rootPc + chordTypes[chordNum]['chordKeys'][i])%12; fill(30*tmpTone,60,85,0.4); stroke(30*tmpTone,60,85); tmpRad = tmpTone*TAU/12 - HALF_PI; arc(canvasWidth/2,canvasHeight/2,size,size,tmpRad-0.2,tmpRad+0.2,PIE); } }
上半分で、円弧のサイズを数式的に決め打ちしてます。最初に勢いよく大きくなって、少ししぼんだらゆっくり消えゆく感じ。
下半分で実際に円弧を描画しています。
音に沿って色相を動かしたかったので、HSBで色を指定しています。colorMode(HSB)
をすると、fill
やstroke
の色指定がHSBになります。
下はデモです。
See the Pen
chordtrainer-12 by frecafloros (@frecafloros)
on CodePen.
一応やりたいことはこれでだいたい済んだので、あとはアプリで和音感を鍛える修行します。
おわり。