2012-07-16 29 views
8

A menudo leo que no es posible pausar/reanudar archivos de audio con el Web Audio API.
Pero ahora vi un example donde realmente hicieron posible detenerlo y reanudarlo. Traté de averiguar cómo lo hicieron. Pensé que tal vez source.looping = false era la clave, pero no lo era.
Por ahora mi audio siempre está reproduciéndose desde el principio.Web Audio API reanudar desde la pausa

Este es mi código actual

var context = new (window.AudioContext || window.webkitAudioContext)(); 

function AudioPlayer() { 
    this.source = context.createBufferSource(); 
    this.analyser = context.createAnalyser(); 
    this.stopped = true; 
} 

AudioPlayer.prototype.setBuffer = function(buffer) { 
    this.source.buffer = buffer; 
    this.source.looping = false; 
}; 

AudioPlayer.prototype.play = function() { 
    this.source.connect(this.analyser); 
    this.analyser.connect(context.destination); 

    this.source.noteOn(0); 
    this.stopped = false; 
}; 

AudioPlayer.prototype.stop = function() { 
    this.analyser.disconnect(); 
    this.source.disconnect(); 
    this.stopped = true; 
}; 

¿Alguien sabe qué hacer, para conseguir que funcione?

Respuesta

5

Sin gastar cualquier momento el control de la fuente de tu ejemplo, yo diría que usted desea utilizar el método de la noteGrainOn AudioBufferSourceNode (https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#methodsandparams-AudioBufferSourceNode)

Sólo un seguimiento de hasta qué punto en el búfer que tenía cuando llamó a noteOff, y luego notóGrainOn desde allí al reanudar en un nuevo AudioBufferSourceNode.

¿Eso tenía sentido?

EDITAR: Consulte los comentarios a continuación para conocer las API actualizadas.

+0

Bueno, él no usa 'noteGrainOn()' en el ejemplo (aún no puede entender cómo lo hace). Pero de todos modos, esto funciona como un hechizo, así que lo tomaré como una respuesta aceptada. Gracias :) –

+1

noteGrainOn() ahora se ha renombrado en la especificación, ya que tiene noteOn() - ambos son ahora play(), con o sin un argumento de compensación de grano. Sin embargo, aún no he visto esto respaldado. – LeeGee

+1

es en realidad 'start' (con o sin argumentos' start (howSoonRelativeToCurrentTime, fromSecondInBuffer, forDuration) ') y parece funcionar en Chrome Canary –

15

La respuesta de Oskar y el comentario de ayke son muy útiles, pero me faltaba un ejemplo de código. Así que escribí uno: http://jsfiddle.net/v3syS/2/ Espero que ayude.

var url = 'http://thelab.thingsinjars.com/web-audio-tutorial/hello.mp3'; 

var ctx = new webkitAudioContext(); 
var buffer; 
var sourceNode; 

var startedAt; 
var pausedAt; 
var paused; 

function load(url) { 
    var request = new XMLHttpRequest(); 
    request.open('GET', url, true); 
    request.responseType = 'arraybuffer'; 
    request.onload = function() { 
     ctx.decodeAudioData(request.response, onBufferLoad, onBufferError); 
    }; 
    request.send(); 
}; 

function play() { 
    sourceNode = ctx.createBufferSource(); 
    sourceNode.connect(ctx.destination); 
    sourceNode.buffer = buffer; 
    paused = false; 

    if (pausedAt) { 
     startedAt = Date.now() - pausedAt; 
     sourceNode.start(0, pausedAt/1000); 
    } 
    else { 
     startedAt = Date.now(); 
     sourceNode.start(0); 
    } 
}; 

function stop() { 
    sourceNode.stop(0); 
    pausedAt = Date.now() - startedAt; 
    paused = true; 
}; 

function onBufferLoad(b) { 
    buffer = b; 
    play(); 
}; 

function onBufferError(e) { 
    console.log('onBufferError', e); 
}; 

document.getElementById("toggle").onclick = function() { 
    if (paused) play(); 
    else stop(); 
}; 

load(url); 
0

La falta de una función de pausa integrada en la API de WebAudio me parece un gran descuido. Posiblemente, en el futuro será posible hacerlo utilizando el MediaElementSource planificado, que le permitirá conectar un elemento (que admite la pausa) a Web Audio. Por ahora, la mayoría de las soluciones parecen basarse en recordar el tiempo de reproducción (tal como se describe en la respuesta de imbrizi). Tal solución tiene problemas cuando se repiten los sonidos (¿el ciclo de implementación es discontinuo o no?), Y cuando se permite cambiar dinámicamente la velocidad de reproducción de los sonidos (ya que ambos afectan el tiempo). Otra solución, igualmente hack-ish y técnicamente incorrecto, pero mucho más simple que puede utilizar es:

source.playbackRate = paused?0.0000001:1; 

Desafortunadamente, 0 no es un valor válido para playbackRate (lo que de hecho una pausa en el sonido). Sin embargo, para muchos propósitos prácticos, un valor muy bajo, como 0.000001, es lo suficientemente cercano y no producirá ningún resultado audible.

+0

+10 para un hack rápido :-) – Sam

+0

Esta no es una gran idea, ya que evitará que el navegador limpie los búferes de audio no utilizados. Es un poco torpe trabajar con la API tal como está, pero está diseñada de esa manera por razones de rendimiento y eficiencia de la memoria. Una pequeña biblioteca contenedora como en la pregunta de PO es un mejor enfoque. –

0

ACTUALIZACIÓN: Esto solo es válido para Chrome. Firefox (v29) aún no implementa la propiedad MediaElementAudioSourceNode.mediaElement.

Asumiendo que ya tiene la referencia AudioContext y la fuente de los medios de comunicación (por ejemplo a través de AudioContext.createMediaElementSource() llamada de método), puede llamar MediaElement.play() y MediaElement.pause() de su fuente, por ejemplo,

source.mediaElement.pause(); 
source.mediaElement.play(); 

Sin necesidad de hacks ni soluciones alternativas, es compatible. Si está trabajando con una etiqueta <audio> como fuente, no debe llamar a pausa directamente en el elemento de audio en su JavaScript, que detendrá la reproducción.

9

En los navegadores actuales (Chrome 43, Firefox 40) ahora hay métodos 'curriculum vitae' disponibles para AudioContext 'suspender' y:

var audioCtx = new AudioContext(); 
susresBtn.onclick = function() { 
    if(audioCtx.state === 'running') { 
    audioCtx.suspend().then(function() { 
     susresBtn.textContent = 'Resume context'; 
    }); 
    } else if(audioCtx.state === 'suspended') { 
    audioCtx.resume().then(function() { 
     susresBtn.textContent = 'Suspend context'; 
    }); 
    } 
} 

(código de ejemplo modificado de https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend)

3

En realidad la web -Audio API puede hacer la pausa y jugar la tarea por ti. Se conoce el estado actual del contexto de audio (en ejecución o en suspensión), por lo que se puede hacer esto de esta manera fácil:

susresBtn.onclick = function() { 
 
    if(audioCtx.state === 'running') { 
 
    audioCtx.suspend() 
 
    } else if(audioCtx.state === 'suspended') { 
 
    audioCtx.resume() 
 
    } 
 
}

Espero que esto puede ayudar.

0

En 2017, usar ctx.currentTime funciona bien para hacer un seguimiento del punto en la canción. El siguiente código utiliza un botón (songStartPause) que alterna entre un botón de pausa play &. Utilicé variables globales por simplicidad. La variable musicStartPoint realiza un seguimiento de la hora en la que estás en la canción. La aplicación de música realiza un seguimiento del tiempo en segundos.

Indica tu musicStartPoint inicial a 0 (principio de la canción)

var ctx = new webkitAudioContext(); 
var buff, src; 
var musicLoaded = false; 
var musicStartPoint = 0; 
var songOnTime, songEndTime; 
var songOn = false; 

songStartPause.onclick = function() { 

    if(!songOn) { 
    if(!musicLoaded) { 
     loadAndPlay(); 
     musicLoaded = true; 
    } else { 
     play(); 
    } 

    songOn = true; 
    songStartPause.innerHTML = "||" //a fancy Pause symbol 

    } else { 
    songOn = false; 
    src.stop(); 
    setPausePoint(); 
    songStartPause.innerHTML = ">" //a fancy Play symbol 
    } 
} 

Uso ctx.currentTime para restar el tiempo de la canción termina de cuando empezó, y anexar este período de tiempo para muy lejos que eras en la canción inicialmente.

function setPausePoint() { 
    songEndTime = ctx.currentTime; 
    musicStartPoint += (songEndTime - songOnTime); 
} 

Funciones de carga/reproducción.

function loadAndPlay() { 
    var req = new XMLHttpRequest(); 
    req.open("GET", "//mymusic.com/unity.mp3") 
    req.responseType = "arraybuffer"; 
    req.onload = function() { 
    ctx.decodeAudioData(req.response, function(buffer) { 
     buff = buffer; 
     play(); 
    }) 
    } 
    req.send(); 
} 

function createBuffer() { 
     src = ctx.createBufferSource(); 
     src.buffer = buff; 
    } 


function connectNodes() { 
    src.connect(ctx.destination); 
} 

Por último, la función de reproducción dice la canción que empezar por el musicStartPoint especificada (y para jugar de inmediato), y también establece la variable songOnTime.

function play(){ 
    createBuffer() 
    connectNodes(); 
    songOnTime = ctx.currentTime; 
    src.start(0, musicStartPoint); 
} 

* Nota al margen: Sé que puede tener un aspecto más limpio para establecer songOnTime en la función de clic, pero calculo que tiene sentido para agarrar el código de tiempo lo más cerca posible a src.start, al igual que cómo nos agarra el tiempo de pausa lo más cerca posible de src.stop.

Cuestiones relacionadas