Audiocontext 不播放任何声音

2024-02-17

作为这篇文章的后续:如何在 JavaScript 中快速播放声音文件的多个副本 https://stackoverflow.com/questions/61453760/how-to-rapidly-play-multiple-copies-of-a-soundfile-in-javascript/61457101?noredirect=1#comment108765135_61457101我创建了一个小型演示页面来说明问题的核心。

我的目标是在用户按住按钮时一遍又一遍地快速播放相同的音频文件,而不会使浏览器崩溃;-)(小提琴中的第二行)

我最初的方法使用克隆节点在 DOM 中创建多个音频对象。这实际上在 Chrome 和 Edge 中工作得很好,但 safari 和 Firefox 过了一段时间就会遇到问题。这会导致音频不同步,并且在用户释放按钮后音频继续启动。

因此 Codebreaker007 建议改用音频上下文,这导致了一些不同的问题。 Chrome 回复:

AudioContext 不允许启动。它必须在用户在页面上做出手势后恢复(或创建)。

Chrome 和 Firefox 不播放音频文件。然后我按照谷歌指南进行操作,至少错误消息消失了,但仍然没有声音。使用 Chrome 的网络音频插件,我可以在某一时刻看到音频节点正在正确创建。我怎样才能让它们听得见?如何启动音频上下文?

我想我已经非常接近解决方案了,所以让我们一起解决这个问题。

 <!doctype html>
<html class="h-100" lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>Test</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script>
     AudioContext:true;


    var clickingBuffer = null;
    var timer
    var inStart = 0
    var inStop = 0
    var timer = null
    var type = ""
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    var context = new AudioContext();

// =========== Variant 1: Clone Node ========== //

var sound = new Audio("https://sounds4email.com/wav/hellobaby.mp3");
sound.preload = 'auto';
sound.load();

function triggersound(){
    console.log("triggerSound")
    var click=sound.cloneNode();
    click.volume=1;
    click.play();
}

// =========== Variant 2: AudioContext  ========== //


function loadClickSound(url) {
    console.log("loading sound")
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    // Decode asynchronously
    request.onload = function() {
        context.decodeAudioData(request.response, function(buffer) {
            if (!buffer) {
                console.log('Error decoding file data: ' + url);
                return;
            }
        clickingBuffer = buffer;
        });
    request.onerror = function() {
        console.log('BufferLoader: XHR error');        
        };
    console.og("sound buffered")
    request.send();
    };
}

function playSound(buffer, time, volume) {   
    console.log("playSound")          
    context.resume().then(() => {
        var source = context.createBufferSource();   // creates a sound source
        source.buffer = buffer;                     // tell the source which sound to play
        source.connect(context.destination);          // connect the source to the context's destination (the speakers)
        var gainNode = context.createGain();          // Create a gain node
        source.connect(gainNode);                     // Connect the source to the gain node
        gainNode.connect(context.destination);        // Connect the gain node to the destination
        gainNode.gain.value = volume;                  // Set the volume
        source.start(time);                           // play the source at the deisred time 0=now  
         console.log('Playback resumed successfully');
  }); 

}




// =========== RAPID FIRE ========== //


function stop() {
    console.log("stop")
    inStop = 1
}


// Initializing the spinning sequence. Blocking other user interaction
function start(tp) {
    type = tp
   console.log("active")
   context.resume().then(() => {
    console.log('Playback resumed successfully');
    if (null === timer) {
       timer = setInterval(timerCallback, 200)
       inStart = 1
       }
  });



}
/**
 * Timer callback
 */
 function timerCallback() {
 console.log(type + " " + inStart + " " + inStop)
  if (inStart) {
      if(type==="node"){
         triggersound()

      } else if(type==="context"){
        playSound(clickingBuffer, 0, 1)

      }
  }
  if (inStop) {
    inStop = 0
    clearTimeout(timer)
    timer = null
    console.log("stopped")
    }
}



// =========== DOC READY ========== //
$( document ).ready(function() {
    console.log( "ready!" );

    // You call with in your document ready
    loadClickSound("https://sounds4email.com/wav/hellobaby.mp3");
    // Fix up prefixi

});

// =================================================================================//
</script>
</head>

<body class="d-flex flex-column align-content-center align-items-center justify-content-center w-100 h-100" >
    <div class="row p-1 w-100">
        <div class="col">
            Click once:
        </div>

    <button id="clickNode" style="width: 100px; height: 100px;" class="col m-1" onclick="triggersound()">
        Clone Node
    </button>
    <button id="clickContext" style="width: 100px; height: 100px;" class="col m-1" onclick="playSound(clickingBuffer, 0, 1)">
        Audio Context
    </button>
</div>

<div class="row p-1 w-100">

    <div class="col">
        Press and hold:
    </div>
    <button id="autoNode" style="width: 100px; height: 100px;" class="col m-1" onmousedown="start('node')" onmouseup="stop()">
       Auto Clone Node
    </button>
    <button id="autoContext" style="width: 100px; height: 100px;" class="col m-1" onmousedown="start('context')" onmouseup="stop()">
       Auto Audio Context
    </button>
</div>
</body>
</html>

好的,这是您想要的函数代码。该代码可以使用本地文件进行测试,以排除所有类型的安全问题(包括 xhr 代码)。它使用纯 JS ES5,并已在不同操作系统上使用 firefox 和 chrome 进行了测试。请将其放入audio_test.html中以验证该功能。警告一句,不要混合 html 标签和 java 脚本函数调用,使用事件监听器,就像我在代码中演示的那样。停止按钮功能只是一个开始,在播放后重新锁定需要额外的代码,我没有打扰来写。 不要尝试持续创建缓冲区,因为这会填满内存并使浏览器/操作系统崩溃。如果你想要重叠的声音,这意味着使用缓冲区数组,但这将是另一个问题。

<!DOCTYPE html>
<!-- Author: codebreaker007 @ stackoverflow -->
<html class="h-100" lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->

  <title>Web Audio API: load + play</title>
</head>
<body>
  <p>Example of using the Web Audio API to load a sound file and </br>play once, play continous on mousedown or stop play
  and start playing on user-click.</p>
  Tested in Firefox and Chrome</p>
  <input type="file" accept="audio/*" value="">
  <button id="playonce" disabled=disabled>Play once</button>
  <button id="playstop" disabled=disabled>Stop play</button>
  <button id="playcont" disabled=disabled>Play cont</button>
<script>
    /* global AudioContext:true,
*/
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
var source = null;
var clickingBuffer = null;
var mouseIsDown = false;
var buttonPo = document.getElementById("playonce");
var buttonPs = document.getElementById("playstop");
var buttonPc = document.getElementById("playcont");

if (document.readyState!="loading") docReady();
/* Modern browsers */
else document.addEventListener("DOMContentLoaded", docReady);
function docReady() {

    buttonPo.addEventListener("click", function(e){
        playSound(clickingBuffer, 0, 0, 0.8);
        buttonPs.disabled = false;
    });
    buttonPs.addEventListener("click", function(e){
    if (source) {
        source.stop(0);
      }
       buttonPs.disabled = true;
    });

    buttonPc.addEventListener("mousedown", function(e){
        playSound(clickingBuffer, 1, 0, 1);
        //  while(mouseIsDown)   playSound(clickingBuffer, 0, 1);
    });
    buttonPc.addEventListener("mouseup", function(e){
       if (source) {
        source.stop(0);
        }
    });
}

function playSound(buffer2play, isLoop, time, volume) { 
  console.log("playsound called");
   source = context.createBufferSource();   // creates a sound source
  source.buffer = buffer2play;                     // tell the source which sound to play
  if (isLoop) source.loop = true;
  else  source.loop = false;
  source.connect(context.destination);          // connect the source to the context's destination (the speakers)
  var gainNode = context.createGain();          // Create a gain node
  source.connect(gainNode);                     // Connect the source to the gain node
  gainNode.connect(context.destination);        // Connect the gain node to the destination
  gainNode.gain.value = volume;                  // Set the volume
  source.start(time);                           // play the source at the deisred time 0=now  
  console.log("playSound");    
 }

function initSound(arrayBuffer) {
  context.decodeAudioData(arrayBuffer, function(buffer) {
    // clickingBuffer is global to reuse the decoded audio later.
    clickingBuffer = buffer;
// Test routine activate buttons    
     buttonPo.disabled = false;
     buttonPc.disabled = false;
  }, function(e) {
    console.log('Error decoding file', e);
  }); 
}

// User selects file, read it as an ArrayBuffer and pass to the API.
var fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', function(e) {  
  var reader = new FileReader();
  reader.onload = function(e) {
    initSound(this.result);
  };
  reader.readAsArrayBuffer(this.files[0]);
}, false);

// Load file from a URL as an ArrayBuffer.
// Example: loading via xhr2: loadSoundFile('sounds/test.mp3');
function loadClickSound(url) {
    console.log("loading sound");
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    // Decode asynchronously
    request.onload = function() {
    // Decode asynchronously
    initSound(this.response); // this.response is an ArrayBuffer.
  };
  xhr.send();
}

</script>
</body>
</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Audiocontext 不播放任何声音 的相关文章

随机推荐