Efeitos sonoros em JavaScript / HTML5

316

Estou usando o HTML5 para programar jogos; o obstáculo que encontrei agora é como tocar efeitos sonoros.

Os requisitos específicos são poucos em número:

  • Toque e misture vários sons,
  • Reproduza a mesma amostra várias vezes, possivelmente sobreposições,
  • Interrompa a reprodução de uma amostra a qualquer momento,
  • De preferência, reproduza arquivos WAV contendo PCM bruto (de baixa qualidade), mas é possível convertê-los, é claro.

Minha primeira abordagem foi usar o <audio>elemento HTML5 e definir todos os efeitos sonoros na minha página. O Firefox reproduz os arquivos WAV de maneira pêssego, mas chamar #playvárias vezes não reproduz a amostra várias vezes. Pelo meu entendimento da especificação HTML5, o <audio>elemento também rastreia o estado de reprodução, o que explica o porquê.

Meu pensamento imediato foi clonar os elementos de áudio, então criei a seguinte pequena biblioteca JavaScript para fazer isso por mim (depende do 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;
      };
    });
  }
};

Então agora eu posso fazer Snd.boom();no console Firebug e jogar snd/boom.wav, mas ainda não consigo reproduzir o mesmo exemplo várias vezes. Parece que o<audio> elemento é realmente mais um recurso de streaming do que algo para reproduzir efeitos sonoros.

Existe uma maneira inteligente de fazer isso acontecer que eu estou perdendo, de preferência usando apenas HTML5 e JavaScript?

Devo também mencionar que, meu ambiente de teste é o Firefox 3.5 no Ubuntu 9.10. Os outros navegadores que experimentei - Opera, Midori, Chromium, Epiphany - produziram resultados variados. Alguns não jogam nada e outros lançam exceções.

Stéphan Kochen
fonte
Hah! Também estou portando um jogo antigo do Macintosh para HTML5. Importa-se de revelar qual você está clonando?
um nerd pago
1
É o Projeto ARASHI, um clone da tempestade.
Stéphan Kochen
O que se entende por "mistura" de sons?
Mads Skjern
2
Você terminou? Podemos ver isso?
25312 enyo
4
Infelizmente não. Eu tenho um talento especial para não terminar projetos de tempo livre. :( Há um repositório git para ele, mas eu não tenho tocado em anos: github.com/stephank/arashi-js
Stéphan Kochen

Respostas:

448

AudioObjetos HTML5

Você não precisa se preocupar com <audio>elementos. O HTML 5 permite acessar Audioobjetos diretamente:

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

Não há suporte para mixar na versão atual das especificações.

Para reproduzir o mesmo som várias vezes, crie várias instâncias do Audioobjeto. Você também pode definir snd.currentTime=0o objeto depois que ele terminar de tocar.


Como o construtor JS não suporta <source>elementos de fallback , você deve usar

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

para testar se o navegador suporta o Ogg Vorbis.


Se estiver escrevendo um jogo ou um aplicativo de música (mais do que apenas um player), convém usar a API de áudio da Web mais avançada , agora suportada pela maioria dos navegadores .

Kornel
fonte
18
Audioobjetos são armazenados automaticamente. Eles são projetados de maneira análoga a Imageobjetos, de modo que as técnicas de pré-carregamento de imagens da velha escola também funcionam com áudio.
Kornel
5
Isso também funciona no iOS. Observe que você precisa usar algum tipo de ação do usuário (por exemplo, um clique em um botão) para iniciar o som. Você não pode simplesmente fazer um snd.play()em window.load ().
Husky
1
Ainda estou usando a <audio>tag porque é mais controlável. Mas de qualquer forma obrigado pela informação!
precisa saber é o seguinte
2
Eu preferiria esse método se não fosse pelo fato de os elementos <audio> html5 permitirem várias fontes, portanto, se um navegador não suportar mp3, você poderá usar o ogg / wav. Seria necessário algum truque em javascript para fazer o mesmo.
Joelmdev 9/02
2
No iOS 6, a reprodução automática é suportada: você pode iniciar sons com um simples snd.play () em window.load ().
Pietro Polsinelli
36

API WebAudio da W3C

A partir de julho de 2012, a API WebAudio agora é suportada no Chrome e, pelo menos em parte, suportada no Firefox, e deve ser adicionada ao IOS a partir da versão 6.

Embora seja robusto o suficiente para ser usado programaticamente em tarefas básicas, o elemento Audio nunca foi concebido para fornecer suporte completo a áudio para jogos, etc. Foi desenvolvido para permitir que uma única mídia seja incorporada em uma página, semelhante a um img tag. Existem muitos problemas ao tentar usar a tag de áudio para jogos:

  • Os deslizamentos de tempo são comuns aos elementos de áudio
  • Você precisa de um elemento de áudio para cada instância de um som
  • Os eventos de carregamento não são totalmente confiáveis, ainda
  • Sem controles de volume comuns, sem desbotamento, sem filtros / efeitos

Usei este artigo Introdução ao WebAudio para iniciar a API do WebAudio. O Estudo de Caso do FieldRunners WebAudio também é uma boa leitura.

Chris Jaynes
fonte
Essa é uma resposta melhor que a aceita, pois se concentra na solução correta para o problema (WebAudio API) e nos motivos pelos quais os elementos de áudio não são uma boa solução, em vez de tentar criar uma solução ruim com os elementos de áudio
Anomalia
29

howler.js

Para a criação de jogos, uma das melhores soluções é usar uma biblioteca que resolva os muitos problemas que enfrentamos ao escrever código para a Web, como o howler.js . O howler.js abstrai a excelente (mas de baixo nível) API de áudio da Web em uma estrutura fácil de usar. Ele tentará retornar ao elemento de áudio HTML5 se a API de áudio da Web estiver indisponível.

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

Outra ótima biblioteca é o wad.js. , que é especialmente útil para produzir áudio sintetizado, como músicas e efeitos. Por exemplo:

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}
})

Som para jogos

Outra biblioteca semelhante ao Wad.js. é o " Sound for Games ", que tem mais foco na produção de efeitos, ao mesmo tempo em que fornece um conjunto semelhante de funcionalidades por meio de uma API relativamente distinta (e talvez mais 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?]
  );
}

Resumo

Cada uma dessas bibliotecas merece uma olhada, se você precisa reproduzir um único arquivo de som ou criar seu próprio editor de música, gerador de efeitos ou videogame baseado em html.

d13
fonte
Não posso comentar sobre o desenvolvimento do jogo, mas estou usando isso para obter um pequeno feedback de áudio em uma interface e, para isso, funciona como um deleite absoluto. Rápido, fácil e simples.
Rid Iculous
Estrutura muito boa, funciona bem em dispositivos móveis também. Você tem que provocá-lo com touchstart ... mas uma vez feito isso funciona fora da caixa :)
Philip
9

Você também pode usar isso para detectar o áudio HTML 5 em alguns casos:

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

Função de detecção de HTML 5 JS

function supportsAudio()
{
    var a = document.createElement('audio'); 
    return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
}
Roubar
fonte
Obrigado por observar isso. Eu aprendi esse método com o has.js, mas acho que ele tem alguns problemas no Firefox e, aparentemente, também no Opera, de acordo com o código-fonte has.js. (ref: github.com/phiggins42/has.js/issues/48 github.com/phiggins42/has.js/blob/master/detect/audio.js#L19 )
Stéphan Kochen
5

Aqui está um método para possibilitar a reprodução do mesmo som simultaneamente. Combine com o preloader, e está tudo pronto. Isso funciona com o Firefox 17.0.1, pelo menos, ainda não o testou com mais nada.

// 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();
}

Ligue isso a uma tecla do teclado e aproveite:

player("step");
F-3000
fonte
Não. Não acho que criar um novo objeto para cada reprodução individual de som seja uma solução. É uma solução rápida e suja, mas isso pode ser feito melhor.
Pawel 26/05
@Pawel True, que isso NÃO deve ser usado como método principal de reprodução de sons, mas, pelo que sei, o princípio no exemplo é a única maneira de reproduzir o mesmo som mais de uma vez simultaneamente, ou conforme a OP o expressou, "reproduções sobrepostas". (Sem a necessidade de plugins de navegador.) Meu implemento real no meu projeto atual é muito mais sofisticado do que o código de exemplo que eu publiquei.
F-3000
4

Parece que o que você quer são sons multicanais. Suponhamos que você tenha 4 canais (como em jogos antigos de 16 bits), ainda não comecei a jogar com o recurso de áudio HTML5, mas você não precisa apenas de 4 elementos <audio> e do ciclo usado tocar o próximo efeito sonoro? Você já tentou isso? O que acontece? Se funcionar: para reproduzir mais sons simultaneamente, basta adicionar mais elementos <audio>.

Eu fiz isso antes sem o elemento <audio> HTML5, usando um pequeno objeto Flash do http://flash-mp3-player.net/ - escrevi um teste de música ( http://webdeavour.appspot.com/ ) e usou-o para reproduzir clipes de música quando o usuário clicou no botão da pergunta. Inicialmente, eu tinha um jogador por pergunta e era possível reproduzi-los por cima um do outro, então mudei para que houvesse apenas um jogador, que eu apontei para clipes musicais diferentes.

Lee Kowalkowski
fonte
Esse é um pensamento interessante, para usar o & lt; audio & gt; elementos como canais. Quando coloco dois elementos em uma página, posso reproduzi-los simultaneamente. Parece que a clonagem que estou fazendo não faz o que eu espero, porque no meu código original, a reprodução de dois exemplos também funciona. Apenas não duas vezes a mesma amostra. Vou ter que experimentar isso um pouco mais e voltarei aos resultados!
Stéphan Kochen
Sim, há possivelmente uma razão sutil por que é chamado clone e não cópia. Talvez o clone ainda esteja compartilhando algo com o original que os impede de tocar simultaneamente.
21411 Lee Kowalkowski
Para referência, a reutilização do mesmo elemento <audio> não funciona, pelo menos no Firefox. Você pode definir o atributo 'src', mas ele não carregará uma amostra diferente.
Stéphan Kochen
4

Dê uma olhada no site jai (-> mirror ) (interface de áudio javascript). Observando sua origem, eles parecem estar chamando play()repetidamente e mencionam que sua biblioteca pode ser apropriada para uso em jogos baseados em HTML5.

Você pode disparar vários eventos de áudio simultaneamente, que podem ser usados ​​para criar jogos Javascript ou fazer uma voz falar sobre alguma música de fundo

Raul Agrait
fonte
3

Para reproduzir a mesma amostra várias vezes, não seria possível fazer algo assim:

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

( eé o elemento de áudio)

Talvez eu tenha entendido completamente o seu problema. Deseja que o efeito sonoro seja reproduzido várias vezes ao mesmo tempo? Então isso está completamente errado.

adamse
fonte
Está correto, preciso reproduzir a mesma amostra várias vezes ao mesmo tempo.
Stéphan Kochen
Sim, isso reproduz a mesma instância várias vezes, mas para as pessoas que olham para esta resposta, observe que isso não causa "reproduções sobrepostas", conforme o OP.
Patrick Roberts
3

Aqui está uma ideia. Carregue todo o seu áudio para uma determinada classe de sons em um único elemento de áudio individual, em que os dados src são todas as suas amostras em um arquivo de áudio contíguo (provavelmente você quer um pouco de silêncio entre eles para que você possa capturar e cortar as amostras com um tempo limite com menos risco de sangramento para a próxima amostra). Em seguida, procure a amostra e toque-a quando necessário.

Se você precisar de mais de um destes para reproduzir, poderá criar um elemento de áudio adicional com o mesmo src para que seja armazenado em cache. Agora, você efetivamente tem várias "faixas". Você pode utilizar grupos de faixas com seu esquema de alocação de recursos favorito, como Round Robin etc.

Você também pode especificar outras opções, como enfileirar sons em uma faixa para reproduzir quando esse recurso estiver disponível ou cortar uma amostra atualmente em execução.

digitalicarus
fonte
2

Eu recomendaria usar o SoundJS , uma biblioteca que eu ajudo a desenvolver. Ele permite que você escreva uma única base de código que funcione em qualquer lugar, com o SoundJS escolhendo o áudio da web, o html ou o flash, conforme apropriado.

Isso permitirá que você faça tudo o que deseja:

  • Toque e misture vários sons,
  • Reproduza a mesma amostra várias vezes, possivelmente sobrepostas
  • Interromper a reprodução de uma amostra a qualquer momento
  • reproduzir arquivos WAV contendo (dependendo do suporte do navegador)

Espero que ajude.

OJay
fonte
essa biblioteca é incrível, eu realmente gosto do gerador sfx. tempo para fazer um web Saiko de download para qualquer pessoa interessada
ROZZA
1

Não é possível executar a reprodução múltipla com um único <audio>elemento. Você precisa usar vários elementos para isso.

Eli Gray
fonte
1

Eu me deparei com isso enquanto programava um gerador de cartão de música. Começou com bibliotecas diferentes, mas sempre que havia uma falha de alguma forma. O atraso na implementação de áudio normal foi ruim, não houve várias reproduções ... acabou usando a biblioteca lowlag + soundmanager:

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

Você pode conferir a implementação aqui: http://musicbox.grit.it/

Eu criei arquivos wav + ogg para reproduções multi-navegador. Este musicbox player funciona responsivo no ipad, iphone, Nexus, mac, pc, ... funciona para mim.

Grit
fonte
Esse é um ótimo aplicativo de musicbox! Você tem um repositório git? Eu posso usá-lo com nossos filhos!
Icarito 01/05/19
0

Eu sei que isso é um hack total, mas pensei em adicionar essa biblioteca de áudio de código aberto de exemplo que eu coloquei no github há algum tempo ...

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

Depois de clicar no link abaixo, digite as teclas da linha inicial para tocar blues (também digite várias teclas ao mesmo tempo etc.)

Exemplo usando a biblioteca jThump >> http://davealger.com/apps/jthump/

Basicamente, funciona criando <iframe>elementos invisíveis que carregam uma página que reproduz um som emReady ().

Certamente isso não é o ideal, mas você pode marcar essa solução com +1 apenas com base na criatividade (e no fato de que ela é de código aberto e funciona em qualquer navegador em que eu tentei). Espero que isso dê a alguém que procure pelo menos algumas idéias.

:)

DaveAlger
fonte
Por que alguém votou negativamente nisso? Parece uma opção viável para mim.
jtrick
é uma solução bastante hacky, mas eu usei essa biblioteca para criar um exemplo de jogo de tela html 5 para que ele funcione não obstante
DaveAlger
0

A resposta selecionada funcionará em tudo, exceto no IE. Eu escrevi um tutorial sobre como fazê-lo funcionar em vários navegadores = http://www.andy-howard.com/how-to-play-sounds-cross-browser-including-ie/index.html

Aqui está a função que escrevi;

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();
 }
 };

Você também precisa adicionar a seguinte tag à página html:

<bgsound id="sound">

Finalmente, você pode chamar a função e simplesmente passar pelo caminho aqui:

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

Você sempre pode tentar AudioContextter suporte limitado, mas faz parte do rascunho da API de áudio da web . Pode valer a pena se você planeja lançar algo no futuro. E se você está apenas programando para o Chrome e Firefox, é de ouro.

haelmic
fonte
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>

user1024
fonte
0

A API de áudio da Web é a ferramenta certa para este trabalho. Há pouco trabalho envolvido no carregamento e reprodução de arquivos de sons. Felizmente, existem muitas bibliotecas por aí que simplificam o trabalho. Estar interessado em sons, também criei uma biblioteca chamada musquito Você também pode conferir.

Atualmente, ele suporta apenas efeitos sonoros desbotados e estou trabalhando em outras coisas, como espacialização em 3D.

VJAI
fonte