6

タイトルで述べたように、私は<audio />サウンドを再生するためにHTMLタグと一緒にjQuery/JavaScriptベースのメトロノームを作成しようとしています。

「大丈夫」ですが、このsetInterval方法は十分に正確に機能していないようです。ここでいくつかのスレッドを検索しましたが、jQueryとJavaScriptの両方に慣れておらず、実用的な解決策が見つかりませんでした。「新しいタブを開いてsetIntervalが停止または遅れる」についても同じです-問題。でそれを防ごうとしましstop(true,true)たが、思った通りに動きませんでした。

新しいタブを開いてそこで何かをするときにテンポを変更せずに、メトロノームを「バックグラウンド」で実行したいと思います。また、私は確かに正確なメトロノームが欲しいです;)

これが私のテスト環境です: http: //nie-wieder.net/metronom/test.html

現時点では、JS-CodeとHTML-markupはすべてtest.htmlソースに含まれているため、ここで確認できます。

また、私が使用している(私が思うに)関係するjsコードは次のとおりです。

$(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);
            }
        });
});

どんな助けでも素晴らしいでしょう、私は今のところアイデアがありません。

4

2 に答える 2

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();

これにより、(100分の1秒ごとに)「タイムスタンプ」が現在の時刻と比較され、差分が1秒以上(偏差は0.01秒)であるかどうか、ログが「ティック」であるかどうかが確認され、現在のタイムスタンプがリセットされます。

http://jsfiddle.net/rlemon/UqbwT/

これは、正確な時間(imo)が必要なものへの最良のアプローチです。

更新:setTimeout時間設定を変更すると、偏差が少なくなります。http://jsfiddle.net/rlemon/UqbwT/1/

2番目の更新:この投稿を確認した後、javascriptでタイマーを使用するより正確な方法があるはずだと思いました。少し調べてみると、この記事に出くわしました。私はあなたがそれを読むことをお勧めします。

于 2012-04-18T14:36:26.750 に答える
1

requestAnimationFrameドラムマシンアプリの正確なタイミングを調整しようとしてsetTimeout失敗した後(3歳の場合、コードよりもテンポを維持できる可能性があります)、あきらめてWeb Audio APIに切り替えました。これにより、目的に合った正確なオーディオタイミングが即座に提供されました。

最小限の例を次に示します。

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>

役立つリソース:

于 2021-07-05T00:20:21.177 に答える