我正在做一个音频训练器作为一个学习练习。它可能是有用的音频技术员谁需要识别音频频率(例如。当在现场音乐会或配乐时敲响反馈时)。
几天前我贴出了卑微的开端。我得到了非常有用的反馈,并更进一步。下面是JSFiddle上的一个工作示例 & 以下是GitHub上的完整代码.如果有人喜欢在自己的浏览器中使用devtools,这里有一个联机工作示例。
在我继续增加更多功能之前,我想知道到目前为止你们对此的看法。欢迎任何反馈!
let toneContext = null;
let toneGenerator = null;
let toneAmplifier = null;
function startFrequencyTrainer(difficultyMode, previousFrequency) {
let frequencies = null;
let frequency = null;
// Create objects
toneContext = new(window.AudioContext || window.webkitAudioContext)();
toneAmplifier = toneContext.createGain();
// Pick a frequency
frequencies = getFrequencies(difficultyMode);
frequency = getNewFrequency(frequencies, previousFrequency);
return {
frequencies,
frequency
};
}
function stopFrequencyTrainer() {
toneContext.close();
}
function startToneGenerator(frequency, volumeControl, startTimer, stopTimer) {
// Create and configure the oscillator
toneGenerator = toneContext.createOscillator();
toneGenerator.type = 'sine'; // could be sine, square, sawtooth or triangle
toneGenerator.frequency.value = frequency;
// Connect toneGenerator -> toneAmplifier -> output
toneGenerator.connect(toneAmplifier);
toneAmplifier.connect(toneContext.destination);
// Set the gain volume
toneAmplifier.gain.value = volumeControl.value / 100;
// Fire up the toneGenerator
toneGenerator.start(toneContext.currentTime + startTimer);
toneGenerator.stop(toneContext.currentTime + startTimer + stopTimer);
}
function stopToneGenerator() {
if (toneGenerator) {
toneGenerator.disconnect();
}
}
function changeVolume(volumeControl) {
toneAmplifier.gain.value = volumeControl.value / 100;
}
function getFrequencies(difficultyMode) {
let frequencies = null;
if (difficultyMode === 'easy') {
frequencies = ["250", "800", "2500", "8000"];
} else if (difficultyMode === 'normal') {
frequencies = ["100", "200", "400", "800", "1600", "3150", "6300", "12500"];
} else if (difficultyMode === 'hard') {
frequencies = ["80", "125", "160", "250", "315", "500", "630", "1000", "1250", "2000", "2500", "4000", "5000", "8000", "10000", "16000"];
} else if (difficultyMode === 'pro') {
frequencies = ["20", "25", "31.5", "40", "50", "63", "80", "100", "125", "160", "200", "250", "315", "400", "500", "630", "800", "1000", "1250", "1600", "2000", "2500", "3150", "4000", "5000", "6300", "8000", "10000", "12500", "16000", "20000"];
}
return frequencies;
}
function getNewFrequency(frequencies, previousFrequency) {
let newFrequency = null;
newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];
// Avoid getting the same frequency twice in a row
while (newFrequency === previousFrequency) {
newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];
}
return newFrequency;
}
function frequencyFormatter(frequency) {
let frequencyFormatted = null;
if (frequency > 999) {
frequencyFormatted = frequency / 1000 + ' k';
} else {
frequencyFormatted = frequency + ' ';
}
return frequencyFormatted;
}<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:900" />
</head>
<body>
<div class="body">
<div class="title">
<h1>Frequency Trainer</h1>
</div>
<div class="controls">
<br />
<button type="button" id="start-button" class="control-button">Start</button>
<button type="button" id="stop-button" class="control-button">Stop</button>
<button type="button" id="next-button" class="control-button">Next</button><br />
<br />
Volume:<br />
<input type="range" id="volume-control" class="volume-control" min="0" max="20" value="2" step="0.1" /><br />
<br />
<button type="button" id="difficulty-easy" class="difficulty-button" data-difficulty="easy">Easy</button>
<button type="button" id="difficulty-normal" class="difficulty-button" data-difficulty="normal">Normal</button>
<button type="button" id="difficulty-hard" class="difficulty-button" data-difficulty="hard">Hard</button>
<button type="button" id="difficulty-pro" class="difficulty-button" data-difficulty="pro">Pro</button><br />
<br />
</div>
<div class="grid">
</div>
</div>
<script>
(function () {
let difficultyMode = 'easy'; // default difficulty mode
let frequencyTrainer = startFrequencyTrainer(difficultyMode, null);
let frequency = frequencyTrainer.frequency;
let frequencyContainers = null;
// Control buttons
let startButton = document.getElementById('start-button');
startButton.onclick = function () {
stopToneGenerator();
startToneGenerator(frequency, volumeControl, 0, 3);
};
let stopButton = document.getElementById('stop-button');
stopButton.onclick = function () {
stopToneGenerator();
};
let nextButton = document.getElementById('next-button');
nextButton.onclick = function () {
stopToneGenerator();
stopFrequencyTrainer();
frequency = startFrequencyTrainer(difficultyMode, frequency).frequency;
startToneGenerator(frequency, volumeControl, 0.05, 3);
};
let volumeControl = document.getElementById('volume-control');
volumeControl.oninput = function () {
changeVolume(volumeControl);
};
function fillFrequencyGrid(frequencies) {
let frequencyFormatted = null;
let frequencyGrid = document.getElementsByClassName('grid')[0];
frequencyGrid.innerHTML = '';
frequencies.forEach(function (frequency) {
frequencyFormatted = frequencyFormatter(frequency);
frequencyGrid.insertAdjacentHTML('beforeend', '<div class="frequency-container" data-frequency="' + frequency + '">' + frequencyFormatted + 'Hz</div>');
});
}
function makeFrequencyGridInteractive() {
frequencyContainers = document.getElementsByClassName('frequency-container');
Array.prototype.forEach.call(frequencyContainers, function (frequencyContainer) {
frequencyContainer.onclick = function () {
let frequencyChosen = frequencyContainer.getAttribute('data-frequency');
let frequencyChosenFormatted = frequencyFormatter(frequencyChosen);
stopToneGenerator();
if (frequencyChosen === frequency) {
if (window.confirm(frequencyChosenFormatted + 'Hz is correct!\nLet\'s try another one!')) {
stopFrequencyTrainer();
frequency = startFrequencyTrainer(difficultyMode, frequency).frequency;
startToneGenerator(frequency, volumeControl, 0.05, 3);
}
} else {
window.alert(frequencyChosenFormatted + 'Hz is not correct.\nPlease try again.');
startToneGenerator(frequency, volumeControl, 0.05, 3);
}
};
});
}
// Generate frequency grid
fillFrequencyGrid(frequencyTrainer.frequencies);
makeFrequencyGridInteractive();
// Difficulty buttons
let difficultyButtons = document.getElementsByClassName('difficulty-button');
Array.prototype.forEach.call(difficultyButtons, function (difficultyButton) {
difficultyButton.onclick = function () {
stopToneGenerator();
stopFrequencyTrainer();
difficultyMode = difficultyButton.getAttribute('data-difficulty');
frequencyTrainer = startFrequencyTrainer(difficultyMode, frequency);
frequency = frequencyTrainer.frequency;
fillFrequencyGrid(frequencyTrainer.frequencies);
makeFrequencyGridInteractive();
};
});
}());
</script>
</body>
</html>发布于 2018-09-18 15:52:50
这看起来是个整洁的小程序。我承认在阅读你的第一篇文章之前我还没有探索过音频API,作为一名音乐家,我喜欢看到技术连接音频和视觉元素。频率所包含的颜色是一个很好的数组。干得好!下面提到了一些可以帮助优化内存和编码风格的事情。反馈既来自我的经验,也来自于阅读就像这个文章。
使用事件委托,而不是手动设置onclick处理程序。
例如,与其将回调函数分配给每个频率按钮的onclick属性,不如对click事件使用EventTarget.addEventListener(),并检查event.target是否有类frequency-container:
frequencyGrid.addEventListener('click', function(event) {
const target = event.target;
if (target.classList.contains('frequency-container')) {
let frequencyChosen = target.getAttribute('data-frequency');它不仅允许在容器上设置一次单击处理程序,而且还可以避免内存泄漏(因为可以从DOM中删除频率)。有关更多信息,请参见多姆:为什么这是内存泄漏?的答案。
javascript部分中的许多函数在第一行都有'use strict';,而页面上的IIFE在开始时就有这个指令。为什么不让Javascript部分更像生活呢?
const作为默认值,而不是let对于任何不需要重新分配的变量,最好使用const而不是let。然后,当您确定重新分配是必要的,使用let。这样,覆盖价值的可能性就会更小。
在同一主题上,在分配实际值之前不久分配null值几乎没有意义--例如,startFrequencyTrainer()中有以下行:
设频率=空;让频率=空;//创建对象toneContext =新的(window.AudioContext欧元/ window.webkitAudioContext)();toneAmplifier = toneContext.createGain();//挑选频率= getFrequencies(difficultyMode);频率=getNewFrequency(频率,previousFrequency);
相反,frequencies和frequency在分配时可以声明为const。
// Create objects
toneContext = new(window.AudioContext || window.webkitAudioContext)();
toneAmplifier = toneContext.createGain();
// Pick a frequency
const frequencies = getFrequencies(difficultyMode);
const frequency = getNewFrequency(frequencies, previousFrequency);newFrequency在getNewFrequency()中的情况也是如此:
设newFrequency =空;newFrequency =频率Math.floor(Math.random()*frequencies.length);
这可以简化为单行,因为在赋值之前,值是null是没有意义的:
let newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];将扩展算子用于将HTMLElement集合放入数组中而不是使用Array.prototype.forEach.call。
像这样的线条:
设difficultyButtons =difficultyButtons函数(difficultyButton) {
可以更改为:
let difficultyButtons = document.getElementsByClassName('difficulty-button');
[...difficultyButtons].forEach( function (difficultyButton) {基于难度
频率选择
函数getFrequencies()没有任何问题,但是可以通过声明函数外部的映射来缩短它:
const frequenciesByDifficulty = {
'easy': ["250", "800", "2500", "8000"],
'normal': ["100", "200", "400", "800", "1600", "3150", "6300", "12500"],
'hard': ["80", "125", "160", "250", "315", "500", "630", "1000", "1250", "2000", "2500", "4000", "5000", "8000", "10000", "16000"],
'pro': ["20", "25", "31.5", "40", "50", "63", "80", "100", "125", "160", "200", "250", "315", "400", "500", "630", "800", "1000", "1250", "1600", "2000", "2500", "3150", "4000", "5000", "6300", "8000", "10000", "12500", "16000", "20000"]
};然后,该函数可以简单地从映射中查找频率:
function getFrequencies(difficultyMode) {
if (difficultyMode in frequenciesByDifficulty) {
return frequenciesByDifficulty[difficultyMode];
}
//fallback
return null;
}这样,就可以改变频率,而不必修改功能。
这可能只是个人偏好--函数formatterFunction()可以通过删除frequencyFormatted来简化,并在它们准备好后立即返回:
function frequencyFormatter(frequency) {
if (frequency > 999) {
return frequency / 1000 + ' k';
}
return frequency + ' ';
}如果您愿意,可以使用箭头函数(与所有函数一样)对其进行简化:
const frequencyFormatter = frequency => frequency > 999 ? frequency / 1000 + ' k' : frequency + ' ';使用部分函数
使用Function.bind()为oninput回调创建一个部分应用函数:
volumeControl.oninput = changeVolume.bind(null, volumeControl);见下面的片段。我没有把所有函数都更改为箭头函数,但是可以.
'use strict';
let toneContext = null;
let toneGenerator = null;
let toneAmplifier = null;
function startFrequencyTrainer(difficultyMode, previousFrequency) {
// Create objects
toneContext = new(window.AudioContext || window.webkitAudioContext)();
toneAmplifier = toneContext.createGain();
// Pick a frequency
const frequencies = getFrequencies(difficultyMode);
const frequency = getNewFrequency(frequencies, previousFrequency);
return {
frequencies,
frequency
};
}
function stopFrequencyTrainer() {
toneContext.close();
}
function startToneGenerator(frequency, volumeControl, startTimer, stopTimer) {
// Create and configure the oscillator
toneGenerator = toneContext.createOscillator();
toneGenerator.type = 'sine'; // could be sine, square, sawtooth or triangle
toneGenerator.frequency.value = frequency;
// Connect toneGenerator -> toneAmplifier -> output
toneGenerator.connect(toneAmplifier);
toneAmplifier.connect(toneContext.destination);
// Set the gain volume
toneAmplifier.gain.value = volumeControl.value / 100;
// Fire up the toneGenerator
toneGenerator.start(toneContext.currentTime + startTimer);
toneGenerator.stop(toneContext.currentTime + startTimer + stopTimer);
}
function stopToneGenerator() {
if (toneGenerator) {
toneGenerator.disconnect();
}
}
function changeVolume(volumeControl) {
toneAmplifier.gain.value = volumeControl.value / 100;
}
const frequenciesByDifficulty = {
'easy': ["250", "800", "2500", "8000"],
'normal': ["100", "200", "400", "800", "1600", "3150", "6300", "12500"],
'hard': ["80", "125", "160", "250", "315", "500", "630", "1000", "1250", "2000", "2500", "4000", "5000", "8000", "10000", "16000"],
'pro': ["20", "25", "31.5", "40", "50", "63", "80", "100", "125", "160", "200", "250", "315", "400", "500", "630", "800", "1000", "1250", "1600", "2000", "2500", "3150", "4000", "5000", "6300", "8000", "10000", "12500", "16000", "20000"]
};
function getFrequencies(difficultyMode) {
if (difficultyMode in frequenciesByDifficulty) {
return frequenciesByDifficulty[difficultyMode];
}
//fallback
return null;
}
function getNewFrequency(frequencies, previousFrequency) {
let newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];
// Avoid getting the same frequency twice in a row
while (newFrequency === previousFrequency) {
newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];
}
return newFrequency;
}
function frequencyFormatter(frequency) {
if (frequency > 999) {
return frequency / 1000 + ' k';
}
return frequency + ' ';
}
(function() {
'use strict';
let difficultyMode = 'easy'; // default difficulty mode
let frequencyTrainer = startFrequencyTrainer(difficultyMode);
let frequency = frequencyTrainer.frequency;
let frequencyContainers = null;
const frequencyGrid = document.getElementsByClassName('grid')[0];
const controls = document.getElementsByClassName('controls')[0];
// Control buttons
const startButton = document.getElementById('start-button');
startButton.onclick = function() {
stopToneGenerator();
startToneGenerator(frequency, volumeControl, 0, 3);
};
const stopButton = document.getElementById('stop-button');
stopButton.onclick = function() {
stopToneGenerator();
};
const nextButton = document.getElementById('next-button');
nextButton.onclick = function() {
stopToneGenerator();
stopFrequencyTrainer();
frequency = startFrequencyTrainer(difficultyMode, frequency).frequency;
startToneGenerator(frequency, volumeControl, 0.05, 3);
};
let volumeControl = document.getElementById('volume-control');
volumeControl.oninput = changeVolume.bind(null, volumeControl);
function fillFrequencyGrid(frequencies) {
let frequencyFormatted = null;
frequencyGrid.innerHTML = '';
frequencies.forEach(function(frequency) {
frequencyFormatted = frequencyFormatter(frequency);
frequencyGrid.insertAdjacentHTML('beforeend', '<div class="frequency-container" data-frequency="' + frequency + '">' + frequencyFormatted + 'Hz</div>');
});
}
frequencyGrid.addEventListener('click', function(event) {
const target = event.target;
if (target.classList.contains('frequency-container')) {
let frequencyChosen = target.getAttribute('data-frequency');
let frequencyChosenFormatted = frequencyFormatter(frequencyChosen);
stopToneGenerator();
if (frequencyChosen === frequency) {
if (window.confirm(frequencyChosenFormatted + 'Hz is correct!\nLet\'s try another one!')) {
stopFrequencyTrainer();
frequency = startFrequencyTrainer(difficultyMode, frequency).frequency;
startToneGenerator(frequency, volumeControl, 0.05, 3);
}
} else {
window.alert(frequencyChosenFormatted + 'Hz is not correct.\nPlease try again.');
startToneGenerator(frequency, volumeControl, 0.05, 3);
}
}
})
// Generate frequency grid
fillFrequencyGrid(frequencyTrainer.frequencies);
// Difficulty buttons
controls.addEventListener('click', event => {
if (event.target.classList.contains('difficulty-button')) {
event.stopPropagation();
stopToneGenerator();
stopFrequencyTrainer();
difficultyMode = event.target.getAttribute('data-difficulty');
frequencyTrainer = startFrequencyTrainer(difficultyMode, frequency);
frequency = frequencyTrainer.frequency;
fillFrequencyGrid(frequencyTrainer.frequencies);
}
}, false);
}());body {
font-family: 'Montserrat', sans-serif;
text-align: center;
padding-top: 10px;
}
h1 {
margin: 0 auto;
font-size: 30px;
text-decoration: underline;
}
h2 {
margin: 0;
font-size: 25px;
}
a {
color: #0000BB;
}
a:hover {
color: #000000;
}
button {
font-family: 'Montserrat', sans-serif;
text-align: center;
font-size: calc(10px + 1vw);
}
.body {
max-width: 1500px;
border: 1px solid black;
width: 95%;
margin: 0 auto;
}
.title {
padding: 10px 0 0 0;
margin: 0 auto;
width: 95%;
}
.content {
padding: 30px 0 0 0;
margin: 0 auto;
width: 95%;
}
.controls {
padding: 0;
margin: 0 auto;
width: 95%;
}
.volume-control {
padding: 0;
margin: 0 auto;
min-width: 200px;
width: 80%;
}
.footer {
padding: 20px 0 10px 0;
margin: 0 auto;
width: 95%;
}
.grid {
margin: 0 auto;
width: 95%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
}
.frequency-container {
margin: 2px;
border: 1px solid black;
padding: 0;
min-width: 80px;
min-height: 80px;
max-width: 300px;
max-height: 300px;
display: flex;
align-items: center;
justify-content: center;
font-size: calc(30px + 0.2vw);
text-shadow: 0 0 25px white;
}
.frequency-container:before {
content: '';
padding-top: 100%;
float: left;
}
[data-frequency="20"] {
background: #CC2828;
}
[data-frequency="25"] {
background: #CC3028;
}
[data-frequency="31.5"] {
background: #CC3928;
}
[data-frequency="40"] {
background: #CC4128;
}
[data-frequency="50"] {
background: #CC4928;
}
[data-frequency="63"] {
background: #CC5128;
}
[data-frequency="80"] {
background: #CC5928;
}
[data-frequency="100"] {
background: #CC6128;
}
[data-frequency="125"] {
background: #CC6A28;
}
[data-frequency="160"] {
background: #CC7228;
}
[data-frequency="200"] {
background: #CC7A28;
}
[data-frequency="250"] {
background: #CC8228;
}
[data-frequency="315"] {
background: #CC8A28;
}
[data-frequency="400"] {
background: #CC9228;
}
[data-frequency="500"] {
background: #CC9B28;
}
[data-frequency="630"] {
background: #CCAB28;
}
[data-frequency="800"] {
background: #CCBB28;
}
[data-frequency="1000"] {
background: #CCCC28;
}
[data-frequency="1250"] {
background: #BBCC28;
}
[data-frequency="1600"] {
background: #ABCC28;
}
[data-frequency="2000"] {
background: #9BCC28;
}
[data-frequency="2500"] {
background: #8ACC28;
}
[data-frequency="3150"] {
background: #7ACC28;
}
[data-frequency="4000"] {
background: #6ACC28;
}
[data-frequency="5000"] {
background: #59CC28;
}
[data-frequency="6300"] {
background: #49CC28;
}
[data-frequency="8000"] {
background: #39CC28;
}
[data-frequency="10000"] {
background: #28CC28;
}
[data-frequency="12500"] {
background: #28CC39;
}
[data-frequency="16000"] {
background: #28CC49;
}
[data-frequency="20000"] {
background: #28CC59;
}<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:900" />
<div class="body">
<div class="title">
<h1>Frequency Trainer</h1>
</div>
<div class="controls">
<br />
<button type="button" id="start-button" class="control-button">Start</button>
<button type="button" id="stop-button" class="control-button">Stop</button>
<button type="button" id="next-button" class="control-button">Next</button><br />
<br /> Volume:
<br />
<input type="range" id="volume-control" class="volume-control" min="0" max="20" value="2" step="0.1" /><br />
<br />
<button type="button" id="difficulty-easy" class="difficulty-button" data-difficulty="easy">Easy</button>
<button type="button" id="difficulty-normal" class="difficulty-button" data-difficulty="normal">Normal</button>
<button type="button" id="difficulty-hard" class="difficulty-button" data-difficulty="hard">Hard</button>
<button type="button" id="difficulty-pro" class="difficulty-button" data-difficulty="pro">Pro</button><br />
<br />
</div>
<div class="grid">
</div>
<div class="footer">
<a href="https://github.com/MaxVMH/frequency-trainer/tree/v.0.0.4-alpha">v.0.0.4</a>
</div>
</div>或者使用Arrow函数:
'use strict';
let toneContext = null;
let toneGenerator = null;
let toneAmplifier = null;
const startFrequencyTrainer = (difficultyMode, previousFrequency) => {
// Create objects
toneContext = new(window.AudioContext || window.webkitAudioContext)();
toneAmplifier = toneContext.createGain();
// Pick a frequency
const frequencies = getFrequencies(difficultyMode);
const frequency = getNewFrequency(frequencies, previousFrequency);
return {
frequencies,
frequency
};
}
const stopFrequencyTrainer = _ => toneContext.close();
const startToneGenerator = (frequency, volumeControl, startTimer, stopTimer) => {
// Create and configure the oscillator
toneGenerator = toneContext.createOscillator();
toneGenerator.type = 'sine'; // could be sine, square, sawtooth or triangle
toneGenerator.frequency.value = frequency;
// Connect toneGenerator -> toneAmplifier -> output
toneGenerator.connect(toneAmplifier);
toneAmplifier.connect(toneContext.destination);
// Set the gain volume
toneAmplifier.gain.value = volumeControl.value / 100;
// Fire up the toneGenerator
toneGenerator.start(toneContext.currentTime + startTimer);
toneGenerator.stop(toneContext.currentTime + startTimer + stopTimer);
}
const stopToneGenerator = _ => {
if (toneGenerator) {
toneGenerator.disconnect();
}
}
const changeVolume = volumeControl => toneAmplifier.gain.value = volumeControl.value / 100;
const frequenciesByDifficulty = {
'easy': ["250", "800", "2500", "8000"],
'normal': ["100", "200", "400", "800", "1600", "3150", "6300", "12500"],
'hard': ["80", "125", "160", "250", "315", "500", "630", "1000", "1250", "2000", "2500", "4000", "5000", "8000", "10000", "16000"],
'pro': ["20", "25", "31.5", "40", "50", "63", "80", "100", "125", "160", "200", "250", "315", "400", "500", "630", "800", "1000", "1250", "1600", "2000", "2500", "3150", "4000", "5000", "6300", "8000", "10000", "12500", "16000", "20000"]
};
const getFrequencies = difficultyMode => {
if (difficultyMode in frequenciesByDifficulty) {
return frequenciesByDifficulty[difficultyMode];
}
//fallback
return null;
}
const getNewFrequency = (frequencies, previousFrequency) => {
let newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];
// Avoid getting the same frequency twice in a row
while (newFrequency === previousFrequency) {
newFrequency = frequencies[Math.floor(Math.random() * frequencies.length)];
}
return newFrequency;
}
const frequencyFormatter = frequency => {
if (frequency > 999) {
return frequency / 1000 + ' k';
}
return frequency + ' ';
}
(function() {
'use strict';
let difficultyMode = 'easy'; // default difficulty mode
let frequencyTrainer = startFrequencyTrainer(difficultyMode);
let frequency = frequencyTrainer.frequency;
let frequencyContainers = null;
const frequencyGrid = document.getElementsByClassName('grid')[0];
const controls = document.getElementsByClassName('controls')[0];
// Control buttons
const startButton = document.getElementById('start-button');
startButton.onclick = function() {
stopToneGenerator();
startToneGenerator(frequency, volumeControl, 0, 3);
};
const stopButton = document.getElementById('stop-button');
stopButton.onclick = function() {
stopToneGenerator();
};
const nextButton = document.getElementById('next-button');
nextButton.onclick = function() {
stopToneGenerator();
stopFrequencyTrainer();
frequency = startFrequencyTrainer(difficultyMode, frequency).frequency;
startToneGenerator(frequency, volumeControl, 0.05, 3);
};
let volumeControl = document.getElementById('volume-control');
volumeControl.oninput = changeVolume.bind(null, volumeControl);
const fillFrequencyGrid = frequencies => {
frequencyGrid.innerHTML = '';
frequencies.forEach(function(frequency) {
const frequencyFormatted = frequencyFormatter(frequency);
frequencyGrid.insertAdjacentHTML('beforeend', '<div class="frequency-container" data-frequency="' + frequency + '">' + frequencyFormatted + 'Hz</div>');
});
};
frequencyGrid.addEventListener('click', event => {
const target = event.target;
if (target.classList.contains('frequency-container')) {
event.stopPropagation();
let frequencyChosen = target.getAttribute('data-frequency');
let frequencyChosenFormatted = frequencyFormatter(frequencyChosen);
stopToneGenerator();
if (frequencyChosen === frequency) {
if (window.confirm(frequencyChosenFormatted + 'Hz is correct!\nLet\'s try another one!')) {
stopFrequencyTrainer();
frequency = startFrequencyTrainer(difficultyMode, frequency).frequency;
startToneGenerator(frequency, volumeControl, 0.05, 3);
}
} else {
window.alert(frequencyChosenFormatted + 'Hz is not correct.\nPlease try again.');
startToneGenerator(frequency, volumeControl, 0.05, 3);
}
}
}, true);
// Generate frequency grid
fillFrequencyGrid(frequencyTrainer.frequencies);
controls.addEventListener('click', event => {
if (event.target.classList.contains('difficulty-button')) {
event.stopPropagation();
stopToneGenerator();
stopFrequencyTrainer();
difficultyMode = event.target.getAttribute('data-difficulty');
frequencyTrainer = startFrequencyTrainer(difficultyMode, frequency);
frequency = frequencyTrainer.frequency;
fillFrequencyGrid(frequencyTrainer.frequencies);
}
}, false);
}());body {
font-family: 'Montserrat', sans-serif;
text-align: center;
padding-top: 10px;
}
h1 {
margin: 0 auto;
font-size: 30px;
text-decoration: underline;
}
h2 {
margin: 0;
font-size: 25px;
}
a {
color: #0000BB;
}
a:hover {
color: #000000;
}
button {
font-family: 'Montserrat', sans-serif;
text-align: center;
font-size: calc(10px + 1vw);
}
.body {
max-width: 1500px;
border: 1px solid black;
width: 95%;
margin: 0 auto;
}
.title {
padding: 10px 0 0 0;
margin: 0 auto;
width: 95%;
}
.content {
padding: 30px 0 0 0;
margin: 0 auto;
width: 95%;
}
.controls {
padding: 0;
margin: 0 auto;
width: 95%;
}
.volume-control {
padding: 0;
margin: 0 auto;
min-width: 200px;
width: 80%;
}
.footer {
padding: 20px 0 10px 0;
margin: 0 auto;
width: 95%;
}
.grid {
margin: 0 auto;
width: 95%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
}
.frequency-container {
margin: 2px;
border: 1px solid black;
padding: 0;
min-width: 80px;
min-height: 80px;
max-width: 300px;
max-height: 300px;
display: flex;
align-items: center;
justify-content: center;
font-size: calc(30px + 0.2vw);
text-shadow: 0 0 25px white;
}
.frequency-container:before {
content: '';
padding-top: 100%;
float: left;
}
[data-frequency="20"] {
background: #CC2828;
}
[data-frequency="25"] {
background: #CC3028;
}
[data-frequency="31.5"] {
background: #CC3928;
}
[data-frequency="40"] {
background: #CC4128;
}
[data-frequency="50"] {
background: #CC4928;
}
[data-frequency="63"] {
background: #CC5128;
}
[data-frequency="80"] {
background: #CC5928;
}
[data-frequency="100"] {
background: #CC6128;
}
[data-frequency="125"] {
background: #CC6A28;
}
[data-frequency="160"] {
background: #CC7228;
}
[data-frequency="200"] {
background: #CC7A28;
}
[data-frequency="250"] {
background: #CC8228;
}
[data-frequency="315"] {
background: #CC8A28;
}
[data-frequency="400"] {
background: #CC9228;
}
[data-frequency="500"] {
background: #CC9B28;
}
[data-frequency="630"] {
background: #CCAB28;
}
[data-frequency="800"] {
background: #CCBB28;
}
[data-frequency="1000"] {
background: #CCCC28;
}
[data-frequency="1250"] {
background: #BBCC28;
}
[data-frequency="1600"] {
background: #ABCC28;
}
[data-frequency="2000"] {
background: #9BCC28;
}
[data-frequency="2500"] {
background: #8ACC28;
}
[data-frequency="3150"] {
background: #7ACC28;
}
[data-frequency="4000"] {
background: #6ACC28;
}
[data-frequency="5000"] {
background: #59CC28;
}
[data-frequency="6300"] {
background: #49CC28;
}
[data-frequency="8000"] {
background: #39CC28;
}
[data-frequency="10000"] {
background: #28CC28;
}
[data-frequency="12500"] {
background: #28CC39;
}
[data-frequency="16000"] {
background: #28CC49;
}
[data-frequency="20000"] {
background: #28CC59;
}<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:900" />
<div class="body">
<div class="title">
<h1>Frequency Trainer</h1>
</div>
<div class="controls">
<br />
<button type="button" id="start-button" class="control-button">Start</button>
<button type="button" id="stop-button" class="control-button">Stop</button>
<button type="button" id="next-button" class="control-button">Next</button><br />
<br /> Volume:
<br />
<input type="range" id="volume-control" class="volume-control" min="0" max="20" value="2" step="0.1" /><br />
<br />
<button type="button" id="difficulty-easy" class="difficulty-button" data-difficulty="easy">Easy</button>
<button type="button" id="difficulty-normal" class="difficulty-button" data-difficulty="normal">Normal</button>
<button type="button" id="difficulty-hard" class="difficulty-button" data-difficulty="hard">Hard</button>
<button type="button" id="difficulty-pro" class="difficulty-button" data-difficulty="pro">Pro</button><br />
<br />
</div>
<div class="grid">
</div>
<div class="footer">
<a href="https://github.com/MaxVMH/frequency-trainer/tree/v.0.0.4-alpha">v.0.0.4</a>
</div>
</div>https://codereview.stackexchange.com/questions/203680
复制相似问题