Используйте часы Web Audio API для выполнения обратных вызовов или событий
Недавно я начал работать над фреймворком, который будет интенсивно использовать API Web Audio. Он предназначен для использования в качестве звукового движка для браузерных игр и интерактивных веб-сайтов.
Для некоторых функций мне нужно убедиться, что я могу выполнять обратные вызовы и генерировать события именно тогда, когда запланировано воспроизведение аудиофайлов. Я написал 3 типа часовых механизмов для экспериментов, но так как setTimeout()
а также setInterval()
не совсем точны, я действительно хотел работать с часами Web Audio API. Я нашел хитрый способ добиться этого, но мне нужно ваше мнение по этому поводу, потому что я не знаю, является ли это очень хорошей идеей или есть намного лучшие решения.
Хитрость, которую я использую сейчас, состоит в том, чтобы воспроизвести тихий файл WAV всего с 1 образцом и прикрепить обратные вызовы к onended. Пока это работает довольно хорошо. Отклонение составляет около 0-5 мс в Chrome и 0-10 мс в Firefox. С помощью setTimeout()
приводил к отклонениям от 30 до 50 мс, а при использовании настраиваемого самокорректирующегося setInterval отклонения были очень непредсказуемыми.
Код:
// Init hacky hacky clock in class Clock
schedule (startTime, callback = () => {}) {
let silence = this.Experience.AudioEngine.playSound(this.silence, startTime);
silence.onended = () => {
startTime *= 1000;
let callbackTime = this.Experience.AudioEngine.getCurrentTime() * 1000,
deviation = callbackTime-startTime;
this.totalDeviation += deviation;
console.log(`Starttime: ${startTime}ms, callbacktime: ${callbackTime}ms, deviation: ${deviation}ms`);
callback();
};
}
Пример того, где я использую это:
startMetronome (bpm, timeSignature = '4/4', measuresAmount) {
let timeSignatureSplit = timeSignature.split('/'),
beatsAmountPerMeasure = timeSignatureSplit[0],
noteValue = timeSignatureSplit[1],
noteValueTime = (60/bpm) * (4/noteValue),
beatsAmount = measuresAmount * beatsAmountPerMeasure,
startTime = null,
time = 0;
this.Experience.AudioEngine.waitUntilPreloadingFinished(this.Experience.Clock.silence, () => {
for (let beats = 0; beats < beatsAmount; beats++) {
if (beats == 0) startTime = this.Experience.AudioEngine.getCurrentTime(); // gets current time from AudioContext
time = startTime + beats * noteValueTime;
this.Experience.Clock.schedule(time, () => {
// Callback
}, clockType);
}
// Print Clock scheduling test results
this.Experience.Clock.schedule(time+0.1, () => {
console.log(`Total deviation over ${measuresAmount} measures at ${bpm}bpm ${timeSignature}: ${this.Experience.Clock.totalDeviation}ms`);
});
});
}
Я открыт для предложений. Если вы знаете лучший способ, пожалуйста, дайте мне знать! Кроме того, так как это просто фрагменты кода, которые являются частью классов с множеством методов, дайте мне знать, если есть неясности.
Спасибо!