【问题标题】:Audiocontext is not playing any soundAudiocontext 没有播放任何声音
【发布时间】:2020-08-19 20:20:57
【问题描述】:

作为这篇文章的后续:How to rapidly play multiple copies of a soundfile in javascript 我创建了一个小型演示页面来说明我的问题的核心。

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

我的初始方法使用克隆节点在 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>

【问题讨论】:

    标签: javascript


    【解决方案1】:

    好的,这是您想要的函数代码。此代码可以使用本地文件进行测试以排除所有类型的安全问题(包括 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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多