webaudio tutorial spec sound examples javascript audio web-audio

javascript - tutorial - API de audio web, deje de sonar graciosamente



web audio api tutorial (1)

Aaah, sí, sí, sí! Finalmente encontré un montón de cosas, finalmente me molesté en leer "todo" en el documento (en diagonal). Y déjame decirte que esta api es un diamante en bruto. De todos modos, en realidad tienen lo que yo quería con Audio param :

La interfaz AudioParam representa un parámetro relacionado con el audio, generalmente un parámetro de un AudioNode (como GainNode.gain). Un AudioParam se puede establecer en un valor específico o un cambio en el valor, y se puede programar para que ocurra en un momento específico y siguiendo un patrón específico.

Tiene una función linearRampToValueAtTime ()

¡Y hasta tienen un ejemplo con lo que pregunté!

// create audio context var AudioContext = window.AudioContext || window.webkitAudioContext; var audioCtx = new AudioContext(); // set basic variables for example var myAudio = document.querySelector(''audio''); var pre = document.querySelector(''pre''); var myScript = document.querySelector(''script''); pre.innerHTML = myScript.innerHTML; var linearRampPlus = document.querySelector(''.linear-ramp-plus''); var linearRampMinus = document.querySelector(''.linear-ramp-minus''); // Create a MediaElementAudioSourceNode // Feed the HTMLMediaElement into it var source = audioCtx.createMediaElementSource(myAudio); // Create a gain node and set it''s gain value to 0.5 var gainNode = audioCtx.createGain(); // connect the AudioBufferSourceNode to the gainNode // and the gainNode to the destination gainNode.gain.setValueAtTime(0, audioCtx.currentTime); source.connect(gainNode); gainNode.connect(audioCtx.destination); // set buttons to do something onclick linearRampPlus.onclick = function() { gainNode.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 2); } linearRampMinus.onclick = function() { gainNode.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 2); }

Ejemplo de trabajo aquí

También tienen diferentes tipos de temporizaciones, como la rampa exponencial en lugar de la lineal, que creo que encajaría mejor en este escenario.

La aplicación de audio web proporciona el método .stop() para detener un sonido. Quiero que mi sonido disminuya en volumen antes de parar. Para hacerlo, utilicé un nodo de ganancia. Sin embargo, estoy enfrentando problemas extraños con esto, donde algunos sonidos simplemente no funcionan y no puedo entender por qué.

Aquí hay una versión simplificada de lo que hago:

https://jsfiddle.net/01p1t09n/1/

Escuchará que si elimina la línea con setTimeout() que reproduce cada sonido. Cuando setTimeout no se reproducen todos los sonidos. Lo que realmente me confunde es que uso push y shift consecuencia para encontrar la fuente correcta del sonido, sin embargo, parece que es otro que deja de tocar. La única forma en que puedo ver que esto sucede es si AudioContext.decodeAudioData no es síncrono. Solo prueba el jsfiddle para tener una mejor comprensión y ponte tus auriculares obviamente.

Aquí está el código de jsfiddle:

let url = "https://raw.githubusercontent.com/gleitz/midi-js-soundfonts/gh-pages/MusyngKite/acoustic_guitar_steel-mp3/A4.mp3"; let soundContainer = {}; let notesMap = {"A4": [] }; let _AudioContext_ = AudioContext || webkitAudioContext; let audioContext = new _AudioContext_(); var oReq = new XMLHttpRequest(); oReq.open("GET", url, true); oReq.responseType = "arraybuffer"; oReq.onload = function (oEvent) { var arrayBuffer = oReq.response; makeLoop(arrayBuffer); }; oReq.send(null); function makeLoop(arrayBuffer){ soundContainer["A4"] = arrayBuffer; let currentTime = audioContext.currentTime; for(let i = 0; i < 10; i++){ //playing at same intervals play("A4", currentTime + i * 0.5); setTimeout( () => stop("A4"), 500 + i * 500); //remove this line you will hear all the sounds. } } function play(notePlayed, start) { audioContext.decodeAudioData(soundContainer[notePlayed], (buffer) => { let source; let gainNode; source = audioContext.createBufferSource(); gainNode = audioContext.createGain(); // pushing notes in note map notesMap[notePlayed].push({ source, gainNode }); source.buffer = buffer; source.connect(gainNode); gainNode.connect(audioContext.destination); gainNode.gain.value = 1; source.start(start); }); } function stop(notePlayed){ let note = notesMap[notePlayed].shift(); note.source.stop(); }

Esto es solo para explicar por qué lo hago así, puedes omitirlo, es solo para explicar por qué no uso stop ()

La razón por la que estoy haciendo todo esto es porque quiero detener el sonido con gracia, por lo que si hay una posibilidad de hacerlo sin usar setTimeout, con gusto lo tomaría.

Básicamente, tengo un mapa en la parte superior que contiene mis sonidos (notas como A1, A # 1, B1, ...).

soundMap = {"A": [], "lot": [], "of": [], "sounds": []};

y un play() fct donde puebla las matrices una vez que toco los sonidos:

play(sound) { // sound is just { soundName, velocity, start} let source; let gainNode; // sound container is just a map from soundname to the sound data. this.audioContext.decodeAudioData(this.soundContainer[sound.soundName], (buffer) => { source = this.audioContext.createBufferSource(); gainNode = this.audioContext.createGain(); gainNode.gain.value = sound.velocity; // pushing sound in sound map this.soundMap[sound.soundName].push({ source, gainNode }); source.buffer = buffer; source.connect(gainNode); gainNode.connect(this.audioContext.destination); source.start(sound.start); }); }

Y ahora la parte que detiene los sonidos:

stop(sound){ //remember above, soundMap is a map from "soundName" to {gain, source} let dasound = this.soundMap[sound.soundName].shift(); let gain = dasound.gainNode.gain.value - 0.1; // we lower the gain via incremental values to not have the sound stop abruptly let i = 0; for(; gain > 0; i++, gain -= 0.1){ // watchout funky syntax ((gain, i) => { setTimeout(() => dasound.gainNode.gain.value = gain, 50 * i ); })(gain, i) } // we stop the source after the gain is set at 0. stop is in sec setTimeout(() => note.source.stop(), i * 50); }