Synchronize audios with HTML5 and Javascript

5

I want to merge two audios into one to synchronize them with HTML5 on the client side. I've seen that with Web Audio Api you can do many things, but I have not been able to find how to do it.

I have the link to two audio files (.mp3, .wav ...), what I want is to synchronize these two audio files, as if they were a voice and a song. I do not want to join them one after the other, I want to synchronize them.

I would like to do everything on the client side using HTML5, without using a server. Is it possible to do it?

    
asked by Miguel Bastida 22.07.2016 в 11:39
source

3 answers

4

If you want to synchronize the audios and sound at the same time, the idea would be for your program to use the event canplaythrough and do the following:

  • Defines a variable that will serve as a counter for the audios loaded.
  • Create the two audios on the without autoplay page.
  • Put an event handler canplaythrough in the audios.
  • When the audio is finished loading, the controller will trigger, which will:
    • Add +1 to the audio counter loaded.
    • Have all audios been loaded?
      • If yes, play the audios at the same time.

Why use the event canplaythrough instead of loadeddata or canplay ? The canplaythrough event is launched when enough data has been loaded to know that the entire melody can be played without buffering. Something that could cause the audios to desynchronize.

Here I leave a commented example of two different audios (although quite dissonant, sorry) that sound simultaneously synchronized:

// contador de audios cargados
var cargados = 0;

// controlador del evento canplaythrough
function sincronizarAudio() {
  // aumentamos el número de audios que se han cargado
  cargados++;
  
  // si se han cargado los dos audios que queremos sincronizar
  if (cargados == 2) {
    // le damos play a los dos
    audio1.play();
    audio2.play();
  }
}

// crea un elemento audio
var audio1 = document.createElement("audio");
// asigna la fuente del sonido
audio1.setAttribute("src", "http://www.w3schools.com/tags/horse.ogg");
// opcional: especifica que se vean los controles
audio1.setAttribute("controls", "controls");
// añade el controlador del evento canplaythrough
audio1.oncanplaythrough = sincronizarAudio;
// añade el elemento al cuerpo de la página
document.querySelector("body").appendChild(audio1);

// lo mismo de arriba pero con otro audio/video
var audio2 = document.createElement("audio");
audio2.setAttribute("src", "http://www.w3schools.com/tags/movie.ogg");
audio2.setAttribute("controls", "controls");
audio2.oncanplaythrough = sincronizarAudio;
document.querySelector("body").appendChild(audio2);

If you also want to synchronize when pause / play, you can add event handlers for pause and play that would apply the method pause() and play() respectively to all synchronized audios .

In the following example I used two slightly longer audios (although of different duration, which can cause problems) so you can see how when pausing / playing in one of them the other also pauses / plays.

var cargados = 0;

function sincronizarAudio() {
  cargados++;

  if (cargados == 2) {
    audio1.play();
    audio2.play();
  }
}

function sincronizarPlay() {
  audio1.play();
  audio2.play();
}

function sincronizarPause() {
  audio1.pause();
  audio2.pause();
}

var audio1 = document.createElement("audio");
audio1.setAttribute("src", "http://www.w3schools.com/tags/mov_bbb.ogg");
audio1.setAttribute("controls", "controls");
audio1.oncanplaythrough = sincronizarAudio;
audio1.onplay = sincronizarPlay;
audio1.onpause = sincronizarPause;
document.querySelector("body").appendChild(audio1);

var audio2 = document.createElement("audio");
audio2.setAttribute("src", "http://www.w3schools.com/tags/movie.ogg");
audio2.setAttribute("controls", "controls");
audio2.oncanplaythrough = sincronizarAudio;
audio2.onplay = sincronizarPlay;
audio2.onpause = sincronizarPause;
document.querySelector("body").appendChild(audio2);
    
answered by 24.07.2016 / 23:59
source
1

I see it's a long-standing question, but definitely whoever walks by here should take a minute to review Howler.js that allows access to Web Audio Api features and degrade them to HTML5 audio in older browsers. Synchronizing audios is a task implemented in the core of howler, loop without pauses, audio sprite, etc.

Update: I liked Álvaro's solution and I asked myself how the synchronicity between both audios is maintained over time, and with longer audios. So I generated this snippet to test. It turns out a very good synchronization! Unfortunately with the HTML5 API you can not put an audio left and another% right , you can not do seamless loops and Web Audio API

var cargados = 0;

function sincronizarAudio() {
  cargados++;

  if (cargados == 2) {
    audio1.play();
    audio2.play();
  }
}

function sincronizarPlay() {
  audio1.play();
  audio2.play();
}

function sincronizarPause() {
  audio1.pause();
  audio2.pause();
}

var audio1 = document.createElement("audio");
audio1.setAttribute("src", "http://www.music.helsinki.fi/tmt/opetus/uusmedia/esim/a2002011001-e02-128k.ogg");
audio1.setAttribute("controls", "controls");

var audio2 = document.createElement("audio");
audio2.setAttribute("src", "http://www.music.helsinki.fi/tmt/opetus/uusmedia/esim/a2002011001-e02-128k.ogg");
audio2.setAttribute("controls", "controls");

var tiempo1 = document.getElementById('t1');
var tiempo2 = document.getElementById('t2');
var diferencia = document.getElementById('d');

audio1.addEventListener("timeupdate",function(){
        tiempo1.value = this.currentTime;
        diferencia.value = Math.abs(this.currentTime-audio2.currentTime);
});
audio2.addEventListener("timeupdate",function(){
        tiempo2.value = this.currentTime;
        diferencia.value = Math.abs(this.currentTime-audio1.currentTime);
});

audio1.oncanplaythrough = sincronizarAudio;
audio1.onplay = sincronizarPlay;
audio1.onpause = sincronizarPause;
document.querySelector("body").appendChild(audio1);
audio2.oncanplaythrough = sincronizarAudio;
audio2.onplay = sincronizarPlay;
audio2.onpause = sincronizarPause;
document.querySelector("body").appendChild(audio2);
<div>
  Tiempo audio 1: <input type=text id=t1>
</div>
<div>
  Tiempo audio 1: <input type=text id=t2>
</div>
<div>
  Diferencia: <input type=text id=d>
</div>
    
answered by 10.05.2018 в 00:57
0

I have not worked with audio for a long time, but I remember that the way to achieve this was with buffers audio , otherwise you'll see that as the sounds reproduce they will be unbalanced little by little.

Working with audio can be a complex task in many cases, especially if you do not have an idea of how sound works (as it is my case). I recommend you look at this article from html5rocks in which they explain a lot well as working with audio in HTML5 / JS and if I remember correctly there is an example that would be useful for what you need.

    
answered by 24.08.2016 в 10:18