前提・実現したいこと
HTMLとJavascriptで音階当てゲームを作っています。
現在、実装しようとしている機能は、クイズ開始前に各音の確認をするために
クリック、又はキーボードで音を鳴らす仕組みです。
発生している問題・エラーメッセージ
Google Chromeで動作確認を行っているのですが、鍵盤のキーボードをクリックしても音が鳴りません。 Chromeの検証ツールで確認したところ、下記エラーが表示されましたが、対処方法が分かりません。 Uncaught TypeError: Failed to set the 'value' property on 'AudioParam': The provided float value is non-finite. 「指定された値(value)は有限ではない(無限)。」
該当のソースコード
HTML
1<!DOCTYPE html> 2<html> 3<head> 4<meta charset="UTF-8"> 5<title>音当てゲーム</title> 6<script></script> 7</head> 8<body onload="init()"> 9<p>音当てゲーム</p> 10<input type="button" id="test" value="テスト" onclick="testGame()"> 11<input type="button" id="start" value="スタート" onclick="initGame()"> 12<input type="button" id="stop" value="ストップ" onclick="stop2Game()"> 13<input type="button" value="ドレミを表示" onclick="showKeyboard()"> 14【第<span id="qNo">1</span>問:<span id="memo">白鍵のみ</span>】 15<span id="message"></span> 16<hr> 17volume:<input type="range" id="volume" value="0.5" min="0" max="1" 18step="0.1" onchange="setVolume()"> 19<span id="volumeText">[0.5]</span> 20<div id="piano"></div> 21</body> 22</html>
JavaScript
1var audioContext = new AudioContext(); //オーディオコンテキスト 2var oscillator = new Array(); 3var gain = new Array(); 4var gainNode; //Gainノード 5var qNo,answer,cnt,total; //問題番号、解答、正解数 6var status; //ステータス(wait/qustion/answer) 7var startTime,timer1,timer2; 8var minNotoNo = 72; 9 10var doremi = ["ド","ド#","レ","レ#","ミ","ファ","ファ#","ソ","ソ#","ラ","ラ#","シ"]; 11 12var keyboard = ["Z","S","X","D","C","V","G","B","H","N","J","M"]; 13 14function init(){ 15 16for(var i=0;i<keyboard.length;i++){ 17 var notoNo; 18 19 notoNo = i+minNotoNo; 20 21var key = document.createElement("button"); 22key.id = "key_" + i; 23key.onclick = hitKey; 24key.onmousedown = mouseKeyDown; 25key.onmouseup = mouseKeyUp; 26key.onmouseleave = mouseKeyUp; 27 28 29if(doremi[notoNo%12].indexOf("#") > 0){ 30 //黒鍵 31 key.classList.add("black"); 32 }else{ 33 //白鍵 34 key.classList.add("white"); 35 var baseKey = document.createElement("div"); 36 } 37 //ベースの鍵盤に追加 38 baseKey.appendChild(key); 39 //ベースの鍵盤をピアノに追加 40 document.getElementById("piano").appendChild(baseKey); 41 42 testGame(notoNo); 43 } 44 45 //Gainノードの作成 46 gainNode = audioContext.createGain(); 47 setVolume(); 48} 49 50function testGame(notoNo){ 51var audioContext = new AudioContext(); 52 53 oscillator[notoNo] = audioContext.createOscillator(); 54 gain[notoNo] = audioContext.createGain(); 55 56 var f = 440*Math.pow(2,(notoNo-69)/12); 57 58 //接続 59 oscillator[notoNo].connect(gain[notoNo]); 60 gain[notoNo].connect(audioContext.destination); 61 62 //周波数、ボリューム(無音)をセット 63 oscillator[notoNo].frequency.value = f; 64 gain[notoNo].gain.value = 0; 65 66 //無音で鳴らす 67 oscillator[notoNo].start(); 68 } 69 const eventName = typeof document.ontouchend !== 'undefined' ? 'touchend' : 'mouseup'; 70 document.addEventListener(eventName, initAudioContext); 71 function initAudioContext(){ 72 document.removeEventListener(eventName, initAudioContext); 73 // wake up AudioContext 74 audioContext.resume(); 75 } 76 77 78 function mouseKeyDown(event){ 79 //鍵盤のノートナンバー(4文字目以降の文字列)を取得 80 var notoNo = event.target.id.substr(4); 81 //音を鳴らす 82 playKey(notoNo); 83 } 84 85 function mouseKeyUp(event){ 86 //鍵盤のノートナンバー(4文字目以降の文字列)を取得 87 var notoNo = event.target.id.substr(4); 88 //音を止める 89 stopKey(notoNo); 90 } 91 92 document.onkeydown = function(event){ 93 //キーに割り当てられているショートカットをキャンセル 94 event.preventDefault(); 95 //押されたキーボードと対応する音を鳴らす 96 for(var i=0;i<keyboard.length;i++){ 97 if(event.key == keyboard[i].toLowerCase()) playKey(i+minNotoNo); 98 } 99 } 100 101 document.onkeyup = function(event){ 102 //放されたキーボードと対応する音を止める 103 for(var i=0;i<keyboard.length;i++){ 104 if(event.key == keyboard[i].toLowerCase()) stopKey(i+minNotoNo); 105 } 106 } 107 108 function playKey(notoNo){ 109 //音を鳴らす(ボリュームを上げる) 110 gain.value = document.getElementById("volume").value; 111 //押されている鍵盤にstrikeクラスを追加 112 document.getElementById("key_" + notoNo).classList.add("strike"); 113 } 114 115 function stopKey(notoNo){ 116 //無音にする 117 gain.value = 0; 118 //放された鍵盤からstrikeクラスを削除 119 document.getElementById("key_" + notoNo).classList.remove("strike"); 120 } 121 122function hitKey(event){ 123 if(status == "question"){ 124 //解答 125 cnt++; 126 status = "answer"; 127 //音階の生成 128 var noteNo = Number(event.target.id.split("_")[1]) + 72; 129 event.target.classList.add("red"); 130 //音階をセット 131 sound(noteNo); 132 } 133} 134 135function sound(noteNo){ 136 //oscillatorノードの作成 137 var oscillatorNode = audioContext.createOscillator(); 138 var f = 440 * Math.pow(2,(noteNo-69) / 12); 139 oscillatorNode.frequency.setValueAtTime(f,audioContext.currentTime); 140 //ノードの接続 141 oscillatorNode.connect(gainNode); 142 gainNode.connect(audioContext.destination); 143 //0.2秒間再生 144 oscillatorNode.start(audioContext.currentTime); 145 oscillatorNode.stop(audioContext.currentTime + 0.2); 146 if(status == "answer")oscillatorNode.onended = checkNote; 147} 148 149function setVolume(){ 150 //ボリュームをセット 151 var volume = document.getElementById("volume").value; 152 gainNode.gain.setValueAtTime(volume,audioContext.currentTime); 153 document.getElementById("volumeText").innerHTML = "[" + volume + "]"; 154} 155 156 157 158function checkNote(){ 159 //解答チェック 160 var message,key; 161 for(var i=0;i<doremi.length;i++){ 162 key = document.getElementById("key_" + i); 163 if(key.classList.contains("red")){ 164 if(key.id.split("_")[1] == answer){ 165 message = "正解![" + doremi[answer] + "]"; 166 if(cnt == 1)total++; 167 //5秒後に次の問題を表示 168 timer2 = setTimeout(function(){ 169 qNo++; 170 //次の問題/終了 171 if(qNo < 11){ 172 startGame(); 173 }else{ 174 stopGame(); 175 } 176 },5000); 177 }else{ 178 message = "不正解!!"; 179 status = "question"; 180 } 181 key.classList.remove("red"); 182 document.getElementById("message").innerText = message; 183 } 184 } 185} 186 187function initGame(){ 188 //初期化 189 total = 0; 190 qNo = 1; 191 //開始 192 document.getElementById("start").disabled = true; 193 startGame(); 194} 195 196function startGame(){ 197 //開始 198 cnt = 0; 199 status = "wait"; 200 document.getElementById("qNo").innerText = qNo; 201 document.getElementById("memo").innerText = "白鍵のみ"; 202 if(qNo > 5){ 203 document.getElementById("memo").innerText = "白鍵+黒鍵"; 204 } 205 startTime = Date.now(); 206 timer1 = setInterval(countTime,100); 207} 208 209function stop2Game(){ 210 //終了 211 status = "wait"; 212 var message = "終了 (正解数:" + total + "/" + qNo + ")" 213 document.getElementById("message").innerText = message; 214 document.getElementById("start").disabled = false; 215} 216 217function stopGame(){ 218 //終了 219 status = "wait"; 220 var message = "終了 (正解数:" + total + "/10)" 221 document.getElementById("message").innerText = message; 222 document.getElementById("start").disabled = false; 223} 224 225function countTime(){ 226 //カウントダウン 227 var time = 3 - Math.floor((Date.now() - startTime)/1000); 228 document.getElementById("message").innerHTML = "<b>" + time + "</b>"; 229 //問題 230 if(time == 0){ 231 status = "question"; 232 clearInterval(timer1); 233 document.getElementById("message").innerText = ""; 234 answer = Math.floor(Math.random() * 12); 235 //白鍵のみ調整 236 if((qNo < 6)&&(doremi[answer].indexOf("#") > 0)) answer++; 237 sound(answer + 72); 238 } 239} 240 241function showKeyboard(){ 242 var notoNo,keyText; 243 for(var i=0;i<doremi.length;i++){ 244 notoNo = i+minNotoNo; 245 keyText = "<div id='text"+notoNo+"'>"+doremi[notoNo%12]+"</div>"; 246 document.getElementById("key_" + i).innerHTML = keyText; 247 } 248}
CSS
1#piano {margin-top: 10px;} 2#piano div { 3 position: relative; 4 width: 80px; 5 height: 400px; 6 float: left; 7} 8#piano button {position: absolute;} 9.white{ 10 background-color: white; 11 top: 0px; 12 width: 80px; 13 height: 400px; 14 border-radius: 20px; 15 z-index: 0; 16} 17.black{ 18 background-color: black; 19 top: 0px; 20 left: 50px; 21 width: 60px; 22 height: 240px; 23 border-radius: 20px; 24 z-index: 1; 25} 26button div{ 27 position: absolute; 28 bottom: 0px; 29 left: 0px; 30 width: 100px; 31 font-size: 20px; 32} 33white div {color: black;} 34black div {color: gray;} 35.red{background-color: red;} 36.strike{background-color: yellow;} 37#memo {color: #0000CC;} 38#message {color: #CC0000;}
試したこと
エラーの該当箇所「oscillator[notoNo].frequency.value = f;」
(69行目)から変数のスペルミス等確認していますがよくわかりません。
補足情報(FW/ツールのバージョンなど)
Google Chrome最新バージョン:75.0.3770.142(Official Build) (64 ビット)
回答3件