Efectos de sonido en JavaScript / HTML5

316

Estoy usando HTML5 para programar juegos; El obstáculo con el que me he encontrado ahora es cómo reproducir efectos de sonido.

Los requisitos específicos son pocos en número:

  • Juega y mezcla múltiples sonidos,
  • Reproduzca la misma muestra varias veces, posiblemente reproducciones superpuestas,
  • Interrumpir la reproducción de una muestra en cualquier punto,
  • Preferiblemente reproduzco archivos WAV que contienen PCM sin procesar (de baja calidad), pero puedo convertirlos, por supuesto.

Mi primer enfoque fue usar el <audio>elemento HTML5 y definir todos los efectos de sonido en mi página. Firefox reproduce los archivos WAV simplemente a melocotón, pero llamar #playvarias veces realmente no reproduce la muestra varias veces. Desde mi comprensión de la especificación HTML5, el <audio>elemento también rastrea el estado de reproducción, por lo que eso explica por qué.

Mi pensamiento inmediato fue clonar los elementos de audio, así que creé la siguiente pequeña biblioteca de JavaScript para hacer eso por mí (depende de jQuery):

var Snd = {
  init: function() {
    $("audio").each(function() {
      var src = this.getAttribute('src');
      if (src.substring(0, 4) !== "snd/") { return; }
      // Cut out the basename (strip directory and extension)
      var name = src.substring(4, src.length - 4);
      // Create the helper function, which clones the audio object and plays it
      var Constructor = function() {};
      Constructor.prototype = this;
      Snd[name] = function() {
        var clone = new Constructor();
        clone.play();
        // Return the cloned element, so the caller can interrupt the sound effect
        return clone;
      };
    });
  }
};

Así que ahora puedo hacerlo Snd.boom();desde la consola Firebug y jugar snd/boom.wav, pero aún no puedo reproducir la misma muestra varias veces. Parece que el <audio>elemento es realmente más una función de transmisión en lugar de algo con lo que reproducir efectos de sonido.

¿Hay una manera inteligente de hacer que esto suceda que me falta, preferiblemente usando solo HTML5 y JavaScript?

También debo mencionar que mi entorno de prueba es Firefox 3.5 en Ubuntu 9.10. Los otros navegadores que he probado - Opera, Midori, Chromium, Epiphany - produjeron resultados variables. Algunos no juegan nada, y algunos lanzan excepciones.

Stéphan Kochen
fuente
Ja! También estoy portando un viejo juego de Macintosh a HTML5. ¿Te importa revelar cuál estás clonando?
un nerd pagado el
1
Es el Proyecto ARASHI, un clon de tempestad.
Stéphan Kochen
¿Qué se entiende por "mezclar" sonidos?
Mads Skjern
2
¿Lo terminaste? Podemos verlo?
enyo
44
Tristemente no. Tengo un don para no terminar proyectos de tiempo libre. :( Hay un repositorio git para él, pero no lo he tocado en años: github.com/stephank/arashi-js
Stéphan Kochen

Respuestas:

448

AudioObjetos HTML5

No necesita molestarse con los <audio>elementos. HTML 5 le permite acceder a Audioobjetos directamente:

var snd = new Audio("file.wav"); // buffers automatically when created
snd.play();

No hay soporte para mezclar en la versión actual de la especificación.

Para reproducir el mismo sonido varias veces, cree varias instancias del Audioobjeto. También puede establecer snd.currentTime=0el objeto después de que termine de reproducirse.


Dado que el constructor JS no admite <source>elementos de reserva , debe usar

(new Audio()).canPlayType("audio/ogg; codecs=vorbis")

para probar si el navegador es compatible con Ogg Vorbis.


Si está escribiendo un juego o una aplicación de música (más que un simple reproductor), querrá usar una API de audio web más avanzada , que ahora es compatible con la mayoría de los navegadores .

Kornel
fuente
18
AudioLos objetos son autobufferizados. Están diseñados de forma análoga a los Imageobjetos, por lo que las técnicas de precarga de imágenes de la vieja escuela también funcionan con audio.
Kornel
55
Esto también funciona en iOS. Sin embargo, tenga en cuenta que debe usar algún tipo de acción del usuario (por ejemplo, hacer clic en un botón) para iniciar el sonido. No puede simplemente hacer un snd.play()en window.load ().
Husky
1
Todavía estoy usando la <audio>etiqueta porque es más controlable. Pero de todos modos gracias por la información!
Derek 朕 會 功夫
2
Preferiría este método si no fuera por el hecho de que los elementos <audio> html5 permiten múltiples fuentes, por lo que si un navegador no admite mp3, puede recurrir a ogg / wav. Requeriría algunos trucos en JavaScript para lograr lo mismo.
joelmdev
2
En iOS 6 se admite la reproducción automática: puede iniciar sonidos con un simple snd.play () en window.load ().
Pietro Polsinelli
36

WebAudio API por W3C

A partir de julio de 2012, la API de WebAudio ahora es compatible con Chrome, y al menos parcialmente compatible con Firefox, y está programada para agregarse a iOS a partir de la versión 6.

Aunque es lo suficientemente robusto como para usarse programáticamente para tareas básicas, el elemento Audio nunca tuvo la intención de proporcionar soporte de audio completo para juegos, etc. Fue diseñado para permitir que una sola pieza de medios se incruste en una página, similar a un img etiqueta. Hay muchos problemas al intentar usar la etiqueta de audio para juegos:

  • Los deslizamientos de tiempo son comunes con los elementos de audio.
  • Necesita un elemento de audio para cada instancia de un sonido
  • Los eventos de carga aún no son totalmente confiables
  • Sin controles de volumen comunes, sin desvanecimiento, sin filtros / efectos

Utilicé este artículo Comenzando con WebAudio para comenzar con la API de WebAudio. El estudio de caso de FieldRunners WebAudio también es una buena lectura.

Chris Jaynes
fuente
Esta es una mejor respuesta que la aceptada, ya que se centra en la solución correcta del problema (API de WebAudio) y las razones por las que los elementos de audio no son una buena solución, en lugar de tratar de hackear una mala solución con elementos de audio
Anomalía
29

howler.js

Para la creación de juegos, una de las mejores soluciones es usar una biblioteca que resuelva los muchos problemas que enfrentamos al escribir código para la web, como howler.js . howler.js resume la gran (pero de bajo nivel) API de audio web en un marco fácil de usar. Intentará recurrir a HTML5 Audio Element si Web Audio API no está disponible.

var sound = new Howl({
  urls: ['sound.mp3', 'sound.ogg']
}).play();
// it also provides calls for spatial/3d audio effects (most browsers)
sound.pos3d(0.1,0.3,0.5);

wad.js

Otra gran biblioteca es wad.js , que es especialmente útil para producir audio de sintetizador, como música y efectos. Por ejemplo:

var saw = new Wad({source : 'sawtooth'})
saw.play({
    volume  : 0.8,
    wait    : 0,     // Time in seconds between calling play() and actually triggering the note.
    loop    : false, // This overrides the value for loop on the constructor, if it was set. 
    pitch   : 'A4',  // A4 is 440 hertz.
    label   : 'A',   // A label that identifies this note.
    env     : {hold : 9001},
    panning : [1, -1, 10],
    filter  : {frequency : 900},
    delay   : {delayTime : .8}
})

Sonido para juegos

Otra biblioteca similar a Wad.js es " Sound for Games ", se centra más en la producción de efectos, al tiempo que proporciona un conjunto similar de funcionalidades a través de una API relativamente distinta (y quizás más concisa):

function shootSound() {
  soundEffect(
    1046.5,           //frequency
    0,                //attack
    0.3,              //decay
    "sawtooth",       //waveform
    1,                //Volume
    -0.8,             //pan
    0,                //wait before playing
    1200,             //pitch bend amount
    false,            //reverse bend
    0,                //random pitch range
    25,               //dissonance
    [0.2, 0.2, 2000], //echo array: [delay, feedback, filter]
    undefined         //reverb array: [duration, decay, reverse?]
  );
}

Resumen

Vale la pena echar un vistazo a cada una de estas bibliotecas, ya sea que necesite reproducir un solo archivo de sonido, o tal vez crear su propio editor de música basado en html, generador de efectos o videojuego.

d13
fuente
No puedo comentar sobre el desarrollo del juego, pero estoy usando esto para algunos comentarios de audio menores en una interfaz y para eso funciona de maravilla. Rápido, fácil y simple.
Rid Iculous
Marco realmente agradable, funciona bien en dispositivos móviles también. Tienes que activarlo con touchstart ... pero una vez hecho, funciona de inmediato :)
Philip
9

También puede usar esto para detectar audio HTML 5 en algunos casos:

http://diveintohtml5.ep.io/everything.html

Función de detección de HTML 5 JS

function supportsAudio()
{
    var a = document.createElement('audio'); 
    return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
}
Robar
fuente
Gracias por notar esto. Aprendí este método de has.js, pero creo que tiene algunos problemas en Firefox, y aparentemente en Opera también de acuerdo con el código fuente de has.js. (referencias: github.com/phiggins42/has.js/issues/48 github.com/phiggins42/has.js/blob/master/detect/audio.js#L19 )
Stéphan Kochen
5

Aquí hay un método para hacer posible reproducir incluso el mismo sonido simultáneamente. Combine con el precargador, y ya está todo listo. Esto funciona con Firefox 17.0.1 al menos, aún no lo he probado con nada más.

// collection of sounds that are playing
var playing={};
// collection of sounds
var sounds={step:"knock.ogg",throw:"swing.ogg"};

// function that is used to play sounds
function player(x)
{
    var a,b;
    b=new Date();
    a=x+b.getTime();
    playing[a]=new Audio(sounds[x]);
    // with this we prevent playing-object from becoming a memory-monster:
    playing[a].onended=function(){delete playing[a]};
    playing[a].play();
}

Vincula esto a una tecla del teclado y disfruta:

player("step");
F-3000
fuente
No. No creo que crear un nuevo objeto para cada reproducción de sonido individual sea una solución en absoluto. Es una solución rápida y sucia, pero esto se puede hacer mejor.
Pawel
@Pawel Es cierto que esto NO debe usarse como método principal para reproducir sonidos, pero que yo sepa, el principio en el ejemplo es la única forma de reproducir el mismo sonido más de una vez simultáneamente, o como OP lo expresó, "reproducciones superpuestas". (Sin requerir complementos del navegador). Mi implemento real en mi proyecto actual es mucho más sofisticado que el código de ejemplo que publiqué.
F-3000
4

Parece que lo que quieres son sonidos multicanal. Supongamos que tiene 4 canales (como en los juegos de 16 bits realmente antiguos), todavía no he tenido la oportunidad de jugar con la función de audio HTML5, pero no solo necesita 4 elementos <audio> y ciclo que se usa tocar el próximo efecto de sonido? ¿Has intentado eso? ¿Lo que pasa? Si funciona: para reproducir más sonidos simultáneamente, solo agregue más elementos <audio>.

He hecho esto antes sin el elemento HTML5 <audio>, usando un pequeño objeto Flash de http://flash-mp3-player.net/ - Escribí un cuestionario de música ( http://webdeavour.appspot.com/ ) y lo usó para reproducir clips de música cuando el usuario hizo clic en el botón de la pregunta. Inicialmente tenía un jugador por pregunta, y era posible jugarlos uno encima del otro, así que lo cambié para que solo hubiera un jugador, lo que apunté a diferentes clips de música.

Lee Kowalkowski
fuente
Es un pensamiento interesante, usar & lt; audio & gt; elementos como canales. Cuando pongo dos elementos en una página, puedo reproducirlos simultáneamente. Parece que la clonación que estoy haciendo en realidad no hace lo que esperaba, porque en mi código original, reproducir dos muestras también funciona. Simplemente no dos veces la misma muestra. ¡Tendré que experimentar con esto un poco más y volveré a los resultados!
Stéphan Kochen
Sí, posiblemente haya una razón sutil por la que se llama clonar y no copiar. Quizás el clon todavía comparte algo con el original que les impide jugar simultáneamente.
Lee Kowalkowski
Como referencia, la reutilización del mismo elemento <audio> no funciona, al menos en Firefox. Puede establecer el atributo 'src', pero en realidad no cargará una muestra diferente.
Stéphan Kochen
4

Eche un vistazo al sitio jai (-> espejo ) (interfaz de audio javascript). Al mirar su fuente, parecen estar llamando play()repetidamente, y mencionan que su biblioteca podría ser apropiada para su uso en juegos basados ​​en HTML5.

Puede disparar múltiples eventos de audio simultáneamente, lo que podría usarse para crear juegos Javascript o tener una voz hablando sobre música de fondo

Raul Agrait
fuente
3

Para reproducir la misma muestra varias veces, ¿no sería posible hacer algo como esto:

e.pause(); // Perhaps optional
e.currentTime = 0;
e.play();

( ees el elemento de audio)

Tal vez entendí mal tu problema, ¿quieres que el efecto de sonido se reproduzca varias veces al mismo tiempo? Entonces esto está completamente mal.

adamse
fuente
Eso es correcto, necesito reproducir la misma muestra varias veces al mismo tiempo.
Stéphan Kochen
Sí, esto juega la misma instancia varias veces, pero para las personas que miran esta respuesta, solo tengan en cuenta que esto no causa "reproducciones superpuestas", como lo expresó el OP.
Patrick Roberts
3

Aquí hay una idea. Cargue todo su audio para una determinada clase de sonidos en un solo elemento de audio individual donde los datos src son todas sus muestras en un archivo de audio contiguo (probablemente desee un poco de silencio entre ellas para poder capturar y cortar las muestras con un tiempo de espera con menos riesgo de sangrado a la siguiente muestra). Luego, busque la muestra y reprodúzcala cuando sea necesario.

Si necesita más de uno de estos para reproducir, puede crear un elemento de audio adicional con el mismo src para que quede en caché. Ahora, efectivamente tiene múltiples "pistas". Puede utilizar grupos de pistas con su esquema de asignación de recursos favorito, como Round Robin, etc.

También puede especificar otras opciones como poner en cola los sonidos en una pista para reproducir cuando ese recurso esté disponible o cortar una muestra que se está reproduciendo actualmente.

digitalicarus
fuente
2

Recomendaría usar SoundJS , una biblioteca que he ayudado a desarrollar. Le permite escribir una única base de código que funciona en todas partes, con SoundJS seleccionando audio web, audio html o audio flash según corresponda.

Te permitirá hacer todo lo que quieras:

  • Juega y mezcla múltiples sonidos,
  • Reproduzca la misma muestra varias veces, posiblemente reproducciones superpuestas
  • Interrumpir la reproducción de una muestra en cualquier punto
  • reproducir archivos WAV que contengan (según el soporte del navegador)

Espero que ayude.

OJay
fuente
esa biblioteca es bastante impresionante, realmente cavo el generador sfx. hora de hacer una descarga
RozzA
1

No es posible hacer múltiples disparos con un solo <audio>elemento. Necesitas usar múltiples elementos para esto.

Eli Gray
fuente
1

Me encontré con esto mientras programaba un generador de tarjeta de caja de música. Comenzó con diferentes bibliotecas, pero cada vez hubo un problema técnico de alguna manera. El retraso en la implementación de audio normal fue malo, no hubo reproducciones múltiples ... finalmente terminó usando la biblioteca lowlag + soundmanager:

http://lowlag.alienbill.com/ y http://www.schillmania.com/projects/soundmanager2/

Puede consultar la implementación aquí: http://musicbox.grit.it/

Genere archivos wav + ogg para juegos en varios navegadores. Este reproductor de MusicBox funciona de manera receptiva en iPad, iPhone, Nexus, Mac, PC, ... funciona para mí.

Arena
fuente
Esa es una buena aplicación musicbox! ¿Tienes un repositorio git? ¡Podría usarlo con nuestros hijos!
icarito
0

Sé que esto es un truco total, pero pensé que debería agregar esta biblioteca de audio de código abierto de muestra que puse en Github hace un tiempo ...

https://github.com/run-time/jThump

Después de hacer clic en el enlace a continuación, escriba las teclas de la fila de inicio para reproducir un riff de blues (también escriba varias teclas al mismo tiempo, etc.)

Muestra usando la biblioteca jThump >> http://davealger.com/apps/jthump/

Básicamente funciona creando <iframe>elementos invisibles que cargan una página que reproduce un sonido en Ready ().

Esto ciertamente no es ideal pero podrías hacer +1 en esta solución basada solo en la creatividad (y en el hecho de que es de código abierto y funciona en cualquier navegador en el que lo haya probado) Espero que al menos alguien más busque algunas ideas.

:)

DaveAlger
fuente
¿Por qué alguien votó en contra de esto? Parece una opción viable para mí.
jtrick
es una solución bastante hacky, pero he usado esta biblioteca para hacer un ejemplo de un juego de lienzo html 5 para que pueda funcionar de todos
modos
0

La respuesta seleccionada funcionará en todo excepto IE. Escribí un tutorial sobre cómo hacerlo funcionar cross browser = http://www.andy-howard.com/how-to-play-sounds-cross-browser-incincluido-ie/index.html

Aquí está la función que escribí;

function playSomeSounds(soundPath)
 {

 var trident = !!navigator.userAgent.match(/Trident\/7.0/);
 var net = !!navigator.userAgent.match(/.NET4.0E/);
 var IE11 = trident && net
 var IEold = ( navigator.userAgent.match(/MSIE/i) ? true : false );
 if(IE11 || IEold){
 document.all.sound.src = soundPath;
 }
 else
 {
 var snd = new Audio(soundPath); // buffers automatically when created
 snd.play();
 }
 };

También debe agregar la siguiente etiqueta a la página html:

<bgsound id="sound">

Finalmente puede llamar a la función y simplemente pasar por la ruta aquí:

playSomeSounds("sounds/welcome.wav");
Andrew Junior Howard
fuente
0

Siempre puede intentarlo AudioContext, tiene un soporte limitado, pero es parte del borrador de trabajo de la API de audio web . Puede valer la pena si planeas lanzar algo en el futuro. Y si solo estás programando para Chrome y Firefox, eres dorado.

haelmic
fuente
0

var AudioContextFunc = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContextFunc();
var player=new WebAudioFontPlayer();
var instrumVox,instrumApplause;
var drumClap,drumLowTom,drumHighTom,drumSnare,drumKick,drumCrash;
loadDrum(21,function(s){drumClap=s;});
loadDrum(30,function(s){drumLowTom=s;});
loadDrum(50,function(s){drumHighTom=s;});
loadDrum(15,function(s){drumSnare=s;});
loadDrum(5,function(s){drumKick=s;});
loadDrum(70,function(s){drumCrash=s;});
loadInstrument(594,function(s){instrumVox=s;});
loadInstrument(1367,function(s){instrumApplause=s;});
function loadDrum(n,callback){
  var info=player.loader.drumInfo(n);
  player.loader.startLoad(audioContext, info.url, info.variable);
  player.loader.waitLoad(function () {callback(window[info.variable])});
}
function loadInstrument(n,callback){
  var info=player.loader.instrumentInfo(n);
  player.loader.startLoad(audioContext, info.url, info.variable);
  player.loader.waitLoad(function () {callback(window[info.variable])});
}
function uhoh(){
  var when=audioContext.currentTime;
  var b=0.1;
  player.queueWaveTable(audioContext, audioContext.destination, instrumVox, when+b*0, 60, b*1);
  player.queueWaveTable(audioContext, audioContext.destination, instrumVox, when+b*3, 56, b*4);
}
function applause(){
  player.queueWaveTable(audioContext, audioContext.destination, instrumApplause, audioContext.currentTime, 54, 3);
}
function badumtss(){
  var when=audioContext.currentTime;
  var b=0.11;
  player.queueWaveTable(audioContext, audioContext.destination, drumSnare, when+b*0, drumSnare.zones[0].keyRangeLow, 3.5);
  player.queueWaveTable(audioContext, audioContext.destination, drumLowTom, when+b*0, drumLowTom.zones[0].keyRangeLow, 3.5);
  player.queueWaveTable(audioContext, audioContext.destination, drumSnare, when+b*1, drumSnare.zones[0].keyRangeLow, 3.5);
  player.queueWaveTable(audioContext, audioContext.destination, drumHighTom, when+b*1, drumHighTom.zones[0].keyRangeLow, 3.5);
  player.queueWaveTable(audioContext, audioContext.destination, drumSnare, when+b*3, drumSnare.zones[0].keyRangeLow, 3.5);
  player.queueWaveTable(audioContext, audioContext.destination, drumKick, when+b*3, drumKick.zones[0].keyRangeLow, 3.5);
  player.queueWaveTable(audioContext, audioContext.destination, drumCrash, when+b*3, drumCrash.zones[0].keyRangeLow, 3.5);
}
<script src='https://surikov.github.io/webaudiofont/npm/dist/WebAudioFontPlayer.js'></script>
<button onclick='badumtss();'>badumtss</button>
<button onclick='uhoh();'>uhoh</button>
<button onclick='applause();'>applause</button>
<br/><a href='https://github.com/surikov/webaudiofont'>More sounds</a>

usuario1024
fuente
0

Web Audio API es la herramienta adecuada para este trabajo. Hay un poco de trabajo involucrado en cargar archivos de sonido y reproducirlo. Afortunadamente, hay muchas bibliotecas que simplifican el trabajo. Al estar interesado en los sonidos también creé una biblioteca llamada mosquito , puedes ver eso también.

Actualmente solo admite efectos de sonido de desvanecimiento y estoy trabajando en otras cosas como la espacialización 3D.

VJAI
fuente