2015-10-19 96 views
3

硬編碼音頻效果是一個重要的工具,但有時您想要的是通過潛在不同的非常非線性或甚至圖靈完成函數在樣本級別不斷重新定義聲音,因此唯一的方法是編寫數字,範圍從-1到1,直接進入緩衝區。它經常說,在JavaScript中生成實時聲音樣本有計時問題。因爲它的實時聲音取決於其他遊戲控制,比如頻率與鼠標位置或相機看到的成比例,我需要知道預計算和計劃播放多少緩衝區,每次JavaScript再次運行,也許在間隔幾毫秒的定時器或某些事件上(以及哪些事件可以做到這一點?)。Web Audio API如何獲取已播放緩衝區的數量並安排下一個緩衝區在連續播放後立即播放?

我在我的http://sourceforge.net/projects/jsoundcard軟件中完成了這項工作,該軟件保留了麥克風緩衝區中剩餘數據以及揚聲器緩衝區中排隊的數據的統計數據,並使用該軟件連續調整消費麥克風幀與生成揚聲器幀的比率,大部分介於0.99和1.01之間,採用線性插值的方式,所以當一個緩衝區變得稍大時,它會縮小得更快,而且對於Java函數中定義的任何可能的聲音效果,揚聲器和麥克風之間的延遲會迅速縮小。您獲得較少的延遲,因爲它觀察時間並通過統計調整緩衝區的使用。

我想用Web Audio API,如果可能的話在javascript級別,或者如果需要,我會考慮將一些JSoundCard代碼移植到該項目,如果他們的計算聲音的方式是兼容的話。

我應該怎麼做才能訪問已經使用了多少緩衝區?

如何安排另一個緩衝區在播放緩衝區結束時立即播放?

我需要知道在播放第一個緩衝區時剩下多少空間來選擇添加多少緩衝區,因爲時間上的小錯誤會導致嚴重的延遲。

繼承人的JavaScript代碼(即在Chrome,火狐和Opera在Windows 7中工作)來生成一個一次性的聲音(其持續上升的頻率)開始在特定的時間:

var audConType = window.AudioContext || window.webkitAudioContext; 
var context = new audConType(); 
var around = 0; 
var aroundSpeed = .1; 
var soundFunc = function(amplitudeIn){ //ignore amplitudeIn for now 
    aroundSpeed += .000002; 
    return Math.sin(around += aroundSpeed); 
}; 
var channels = 1; 
var howManyFrames = 50000; 
var rate = 44100; 
var buffer = context.createBuffer(channels, howManyFrames, rate); 
for(var channel=0; channel<channels; channel++){ 
    var chanBuf = buffer.getChannelData(channel); 
    for (var f=0; f<howManyFrames; f++){ 
     chanBuf[f] = soundFunc(0); 
    } 
} 
var source = context.createBufferSource(); 
source.buffer = buffer; 
source.connect(context.destination); 
source.start(); 

編輯,新代碼後cwilso的迴應:

ScriptProcessorNode確實允許我使用JavaScript代碼處理麥克風示例,並將其發送給Firefox中的揚聲器(爲什麼不是Chrome和Opera呢?),但是它們延遲1/3秒,這使得它不切實際現場音樂表演或遊戲中的事件。有沒有辦法設置麥克風緩衝區大小?

var volume = .7; 
var maxMicrophoneAmplitude = 1; 
var soundFunc = function(ins, outs){ 
    var microphoneAmplitude = ins[0]; 
    var estimatedFramesPerSecond = 44100; 
    var decaySeconds = .1; 
    var decay = 1/(estimatedFramesPerSecond*decaySeconds); 
    maxMicrophoneAmplitude = Math.max(maxMicrophoneAmplitude, microphoneAmplitude)*(1-decay); 
    microphoneAmplitude/maxMicrophoneAmplitude * volume 
    outs[0] = microphoneAmplitude/maxMicrophoneAmplitude * volume; 
}; 
soundFunc.inSize = 1; 
soundFunc.outSize = 1; 
var inputNodes = {}; 
window.onload = function(){ 
    var context = new AudioContext(); 
    if(!navigator.getUserMedia && navigator['mozGetUserMedia']){ 
     navigator.getUserMedia = navigator['mozGetUserMedia']; 
    } 
    if(!navigator.getUserMedia && navigator['webkitGetUserMedia']){ 
     navigator.getUserMedia = navigator['webkitGetUserMedia']; 
    } 
    if(!navigator.getUserMedia && navigator['msGetUserMedia']){ 
     navigator.getUserMedia = navigator['msGetUserMedia']; 
    } 
    var framesPerBuffer = 1024; 
    var scriptNode = context.createScriptProcessor(framesPerBuffer, 1, 1); 
    scriptNode.onaudioprocess = function(audioProcessingEvent){ 
     var inputBuffer = audioProcessingEvent.inputBuffer; 
     var outputBuffer = audioProcessingEvent.outputBuffer; 
     var chansIn = []; 
     var chansOut = []; 
     for(var c=0; c<inputBuffer.numberOfChannels; c++){ 
      chansIn[c] = inputBuffer.getChannelData(c); 
     } 
     for(var c=0; c<outputBuffer.numberOfChannels; c++){ 
      chansOut[c] = outputBuffer.getChannelData(c); 
     } 
     var ins = new Float32Array(inputBuffer.numberOfChannels); 
     var outs = new Float32Array(outputBuffer.numberOfChannels); 
     for(var f=0; f<framesPerBuffer; f++){ 
      for(var c=0; c<inputBuffer.numberOfChannels; c++){ 
       ins[c] = chansIn[c][f]; 
      } 
      soundFunc(ins, outs); 
      for(var c=0; c<outputBuffer.numberOfChannels; c++){ 
       chansOut[c][f] = outs[c]; 
      } 
     } 

    } 
    if(navigator.getUserMedia){ 
     navigator.getUserMedia(
      {'audio':true}, 
      function(stream){ 
       var input = context.createMediaStreamSource(stream); 
       //http://stackoverflow.com/questions/22860468/html5-microphone-capture-stops-after-5-seconds-in-firefox says to save a reference to avoid sound ending after 5 seconds 
       inputNodes.mic = input; 
       input.connect(scriptNode); 
       scriptNode.connect(context.destination); 
       input.start(); 
      }, 
      function(e){ alert('Error capturing audio. e='+e); } 
     ); 
    }else alert('getUserMedia not supported in this browser.'); 
}; 

回答

1

要做到這一點,最好的辦法是不使用AudioBuffers,但使用ScriptProcessor(目前,這將由AudioWorkers被替換,但他們還沒有在任何地方實現)。這將使您以動態的方式完全控制音頻位。

+0

對於我來說,緩衝工作完美無暇,當計劃開始和停止後彼此。它們允許任意數量的音頻被緩衝而沒有任何延遲(只是生成緩衝區的時間),而處理器具有16k採樣的限制,儘管存在如此小的緩衝區大小,但具有可怕的延遲。緩衝區也可以立即停止並重新啓動。當用戶界面長時間受阻時(例如,當你想關閉此頁面的彈出窗口處於活動狀態時),處理器會造成可怕的噪音。緩衝器工作得很好我不知道我甚至會在最終實現時使用audioworker。 – Esailija

+0

引用原始問題:「有時候你想要的是通過潛在的不同的非常非線性或甚至圖靈完成函數在樣本水平上不斷重新定義聲音,因此唯一的方法就是在範圍內寫入數字 - 1到1,直接進入緩衝區。「這聽起來很像AudioWorkers的目標場景。 (AudioWorker將修復ScriptProcessor的實際問題,就像UI阻塞問題一樣。)如果緩衝區適用於您,請使用它們;對它們進行適當的調度通常是有問題的,這就是爲什麼我不會首先指出該解決方案的原因。 – cwilso