正如标题中所提到的,我试图创建一个基于jQuery / JavaScript的节拍器以及HTML <audio />
标签来播放声音。
它有效&#34;好的&#34;,但在我看来,setInterval
方法的工作不够准确。我在这里搜索了一些帖子,但是因为我是jQuery和JavaScript的新手,我还没有找到一个可行的解决方案。同样的&#34;打开新标签和setInterval停止或滞后&#34; - 问题。我试图用stop(true,true)
阻止它,但它没有像我预期的那样工作。
我希望节拍器在背景中运行&#34;#34;打开新标签并在那里做某事时不改变速度。我也想要一个精确的节拍器;)
我的测试环境位于http://nie-wieder.net/metronom/test.html
目前,JS-Code和HTML-markup都在test.html源代码中,所以你可以看一下。
此外,我使用的是(我认为)js-code:
$(document).ready(function() {
//vars
var intervalReference = 0;
var currentCount = 1;
var countIncrement = .5;
var smin = 10;
var smax =240;
var svalue = 120;
//soundchkbox
$(".sndchck").attr("disabled", true);
//preload sound
$.ajax({
url: "snd/tick.ogg",
success: function() {
$(".sndchck").removeAttr("disabled");
}
});
// tick event
var met = $("#bpm").slider({
value: 120,
min: smin,
max: smax,
step: 1,
change: function( event, ui ) {
var delay = (1000*60/ui.value)/2
clearInterval(intervalReference);
//seems to be the Problem for me
intervalReference = setInterval(function(){
var $cur_sd = $('#sub_div_'+currentCount);
$cur_sd
.stop(true,true)
.animate({opacity: 1},15,
function() {
//Play HTML5 Sound
if($('#sound_check:checked').val()){
$('#tick')
.stop(true,true)
.trigger("play");
}
$(this).
stop(true,true).
animate({opacity:0});
}
);
currentCount += countIncrement;
if(currentCount > 4.5) currentCount = 1
}, delay);
createMusicTag(ui);
}
});
});
任何帮助都会很棒,我现在没有想法。
答案 0 :(得分:5)
setInterval不准确。您可以尝试做的是:
var timestamp = (new Date()).getTime();
function run() {
var now = (new Date()).getTime();
if( now - timestamp >= 1000 ) {
console.log( 'tick' );
timestamp = now;
}
setTimeout(run, 10);
}
run();
这将(每百分之一秒)将'timestamp'与当前时间进行比较,以查看diff是否为秒或更长(偏差为0.01秒),如果是log'tick'并重置当前时间戳。
http://jsfiddle.net/rlemon/UqbwT/
这是需要时间准确(imo)的最佳方法。
更新:如果更改setTimeout时间设置...您的偏差会减少。 http://jsfiddle.net/rlemon/UqbwT/1/
第二次更新:在回顾这篇文章之后,我认为必须有更准确的方法在javascript中使用计时器..所以通过一些研究我得到了this文章。我建议你阅读它。
答案 1 :(得分:1)
在尝试将 requestAnimationFrame
和 setTimeout
哄骗到鼓机应用程序的准确计时并失败后(一个 3 岁的孩子可以保持比我的代码更好的节奏),我放弃并切换到 { {3}},它立即为我提供了准确的音频计时。
这是一个最小的例子:
const scheduleBeep = time => {
const osc = audioContext.createOscillator();
osc.connect(audioContext.destination);
osc.frequency.value = 300;
osc.start(time);
osc.stop(time + 0.1);
};
window.AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
let timeBetweenSteps = 60 / 120;
let nextStepTime;
let interval;
const lookahead = 0.1;
const timeoutDelay = 30;
const schedule = () => {
while (nextStepTime < audioContext.currentTime + lookahead) {
nextStepTime += timeBetweenSteps;
scheduleBeep(nextStepTime);
}
};
document.querySelector("button")
.addEventListener("click", evt => {
clearInterval(interval);
if (evt.target.textContent === "Run") {
evt.target.textContent = "Stop";
nextStepTime = audioContext.currentTime;
interval = setInterval(schedule, timeoutDelay);
}
else {
evt.target.textContent = "Run";
}
})
;
<button>Run</button>
还有一个稍微复杂一点的例子,它添加了一个声音文件和一个 BPM 滑块:
<body>
<form class="metronome">
<button class="run">Run</button>
<input class="bpm" type="range" min="60" max="500" value="120">
<span class="bpm-readout">120</span>
</form>
<script>
const url = "https://upload.wikimedia.org/wikipedia/commons/e/e5/Abadie.jo-Marteau-1.ogg";
const $ = document.querySelector.bind(document);
const metroEls = {
run: $(".metronome .run"),
bpm: $(".metronome .bpm"),
bpmReadout: $(".metronome .bpm-readout"),
};
window.AudioContext =
window.AudioContext || window.webkitAudioContext
;
const audioContext = new AudioContext();
(async () => {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext
.decodeAudioData(arrayBuffer)
;
const scheduleSample = time => {
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start(time);
};
let timeBetweenSteps = 60 / 120;
let nextStepTime;
let interval;
const lookahead = 0.1;
const timeoutDelay = 30;
const schedule = () => {
while (nextStepTime <
audioContext.currentTime + lookahead) {
nextStepTime += timeBetweenSteps;
scheduleSample(nextStepTime);
}
};
metroEls.run.addEventListener("click", () => {
if (metroEls.run.textContent === "Run") {
metroEls.run.textContent = "Stop";
nextStepTime = audioContext.currentTime;
clearInterval(interval);
interval = setInterval(schedule, timeoutDelay);
}
else {
metroEls.run.textContent = "Run";
clearInterval(interval);
}
});
metroEls.bpm.addEventListener("change", e => {
timeBetweenSteps = 60 / e.target.value;
metroEls.bpmReadout.innerText = e.target.value;
});
})();
</script>
</body>
有用的资源: