gpt4 book ai didi

javascript - 同步音频和 Canvas

转载 作者:行者123 更新时间:2023-12-02 15:11:06 25 4
gpt4 key购买 nike

问题

我正在尝试对 Canvas 的帧进行计时,以便与后台播放的音频同步。
当我使用 window.requestAnimationFrame 时,您会在不同的浏览器/计算机上在一秒钟内获得不稳定的帧数。我还尝试了 setInterval(function(){//code }, 1); ,它比 window.requestAnimationFrame 更接近完美,但它仍然没有完成工作正确。

<小时/>

这是我的代码的工作原理。

setInterval 每毫秒触发一次,然后检查应在 x 时间生成哪些对象,并且它还会清除并更新 Canvas 。另外,我使用毫秒来计算对象的速度,以便它们在正确的 x 时间到达 Canvas 上的位置。

有更好的方法吗?
我将在下面列出我的代码,以及我当前的 CodePen 的链接,以便您可以看到它的运行情况。 (点击 Canvas 区域即可开始动画背景中有音乐需要同步,但只有点击才会开始)

<小时/>

Sonar Song on CodePen

var Queues = [
[1, '#FFFFFF', 'line', true, [20,20], [80,80], 500, 950],
[2, '#FFFFFF', 'line', true, [80,80], [55,20], 950, 1200]
];

var playing = false;


$(document).click(function(){
if(playing == false) {
var audio = document.getElementById("audio");
audio.currentTime = 0;
audio.play();
apparatus();
playing = true;
} else {
var audio = document.getElementById("audio");
audio.pause();
playing = false;
$('body canvas').remove();
}

});

function apparatus() {

var canvas, context, toggle, time = 0, vidtime = 0;

var points = [];

init();
function init() {

canvas = document.createElement( 'canvas' );
context = canvas.getContext( '2d' );
canvas.width = $(document).width();
canvas.height = $(document).height();
document.body.appendChild( canvas );

}

var point = function(options) {
this.position = {}, this.end_position = {}, this.distance = {}, this.velocity = {}, this.time = {};
points.push(this);

// TIMING
this.time.start = options[6];
this.time.end = options[7];

// VECTORS
this.position.x = options[4][0] * (canvas.width / 100);
this.position.y = options[4][1] * (canvas.height / 100);
this.end_position.x = options[5][0] * (canvas.width / 100);
this.end_position.y = options[5][1] * (canvas.height / 100);

this.distance.x = Math.abs(this.position.x - this.end_position.x);
this.distance.y = Math.abs(this.position.y - this.end_position.y);

if(this.position.x > this.end_position.x) {
this.velocity.x = -Math.abs(this.distance.x / (this.time.end - this.time.start));
} else {
this.velocity.x = Math.abs(this.distance.x / (this.time.end - this.time.start));
}
if(this.position.y > this.end_position.y) {
this.velocity.y = -Math.abs(this.distance.y / (this.time.end - this.time.start));
} else {
this.velocity.y = Math.abs(this.distance.y / (this.time.end - this.time.start));
}

// STYLING
this.style = options[2];
this.color = options[1];

//-- STYLING / STYLE TYPES
if(this.style == 'line') {
}
}

point.prototype.draw = function() {
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;

context.fillStyle = this.color;
context.beginPath();
context.arc( this.position.x, this.position.y, 10, 0, Math.PI * 2, true );
context.closePath();
context.fill();
}

setInterval(function(){
time++;
console.log(time);
context.fillStyle = '#000000';
context.fillRect(0, 0, $(document).width(), $(document).height());
for(var i = 0; i < Queues.length; i++) {
if(time == Queues[i][6]) {
new point(Queues[i]);
}
if(time == Queues[i][7]) {
console.log('la');
}
}
for(var i = 0; i <= points.length; i++) {
if(points[i] != null) {
points[i].draw();
}
}
}, 1);
}

最佳答案

正在同步

您不能依赖 setInterval 或 setTimeout,因为它们只会尽可能接近请求的时间进行响应,因此对于同步动画和时间等内容非常不可靠。

您需要使用一致的时间进行同步。您还必须使您的代码适应迟到。

日期和 requestAnimationFrame

首先要抓紧时间。 JavaScript 通过 Date 对象提供以毫秒为单位的时间。

以下内容将返回自过去某个时间以来的当前时间(以毫秒为单位)(我不知道 1452798031458 毫秒前的时间)

var currentTime = new Date().valueOf();

或者,如果您使用 window.requestAnimationFrame,您将获得时间作为第一个参数

//  main update loop
function update(time){
// time is the millisecond time
requestAnimationFrame(update); // request new frame
}
update(new Date().valueOf);

如果您希望某件事在设定的时间发生,您必须考虑测试时间的机会之间的时间,因为这可能会有所不同。

定期事件时间

让我们尝试一个基于帧时间的计时事件。

首先使用一些变量来跟踪时间

var frameTime;     // time between calls to update
var lastFrameTime; // the time of the perviouse call to update
var eventTime; // the event start time that we want to stay in sync with
var timeInterval = 1000; // when we want sync events

然后开始我们想要保持同步的事件。

function startSound(){
sound.play(); // assuming you have the sound.
eventTime = new Date().valueOf(); // now
}

更新函数将在尽可能接近我们想要的时间的情况下执行正确的操作。

function update(time){
frameTime = time - lastFrameTime; // how long since the last call to update
var timeSinceEvent = time-eventTime; // get the time since the event started
var timeSinceLastSync = timeSinceEvent % timeInterval;

// Several options here so explained below

requestAnimationFrame(update); // request new frame
lastFrameTime = time; // remember the last frame time
}
startSound(); // start playing
lastFrameTime = new Date().valueOf;
update(new Date().valueOf);

现在你有一些选择。人类的感知不太擅长在 1/10 秒以下的任何时间分离视觉效果,人类的听觉非常擅长及时分离事件(当在录音中弹奏吉他时出现 10 毫秒的延迟时,我会感到恼火),所以你有考虑您要展示的内容。

视觉事件

视觉上是最简单的。您只需在同步时间之后尽可能接近地触发 snyc 事件即可。

if(timeSinceLastSync > timeInterval){
// do the visual event
eventTime += timeInterval; // update the event time to the time this
// event was to have happened.
}

这将在帧时间 1/60(分钟)秒内触发视觉事件。您会注意到我添加到了 eventTime。我没有得到当前时间,因为当前时间可能会相差 30 毫秒,如果我们添加哪怕一个毫秒的误差,它也会很快累积。

音频、高精度事件

对于音频事件,我们通过查看最后一帧时间来抢占事件时间

// make sure we are near the next event time. this is to avoid firing the event 
// twice. Once on the frame before and once on the frame after.
if((timeInterval - timeSinceLastSync) > timeInterval/2){
// will the time to the new sync event be less than half the last frame time
// or have we passed that time.
if((timeInterval - timeSinceLastSync) < frameTime || timeSinceLastSync > timeInterval ){
// fire the synced event.
eventTime += timeInterval; // update the event time to the time this
// event was or will happened.
}
}

我们在这里所做的是使用最后一帧完成所需的时间,并假设当前帧将花费相同的时间。然后,我们找到距离同步事件还有多长时间,如果该时间小于帧时间的一半,则触发该事件。或者我们可能已经过了同步事件时间。无论哪种方式,我们现在都需要触发该事件。

然后再次通过添加来更新当前同步事件时间。

此方法将使您尽可能接近您想要触发事件的时间。 (假设 60fps,则在 1/120 秒内)

改进

您可以对此进行改进。 JavaScript 是阻塞的,所以有些事情你需要等到更新函数结束。您还可以通过获取退出功能之前的时间来跟踪您在更新功能中花费的时间。然后,您将估计您请求触发的代码需要多长时间才会发生,并且可以将其包含在计算中。通常,帧之间的时间为 16 毫秒,但您的更新函数可能只运行 8 毫秒,因此会触发事件下一帧之前 8 毫秒。

性能和微秒

某些浏览器上还有一个微秒(1/1,000,000 秒)计时器,称为性能

// assume no performance API
var usePerformance = false
// at the start of you code check if its available
if(typeof performance === "function"){ // is performance available
usePerformance = true;
}

为了避免每次需要时都必须测试性能 API,请使用任一 API 创建单独的函数

var updateP = function(){
}
// normal update function
var updateN = function(){
}
// set the correct update function
var update;
if(usePerformance){
update = updateP;
}else{
update = updateN;
}

使用性能

使用性能 API

var now = performance.now(); // returns the time in ms with a fractional
// representing the microseconds.

事件之间的时间

var n = performance.now();
console.log(performance.now()-n); // returns 0.021 depending on the system speed

希望这有帮助。

关于javascript - 同步音频和 Canvas ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34797036/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com