2016-05-26 53 views
2

我有一個實時的恆定波形數據源,每秒鐘以恆定的採樣率爲我提供第二個單通道音頻。目前,我打他們這樣說:WebAudio - 無縫播放音頻塊的序列

// data : Float32Array, context: AudioContext 
function audioChunkReceived (context, data, sample_rate) { 
    var audioBuffer = context.createBuffer(2, data.length, sample_rate); 
    audioBuffer.getChannelData(0).set(data); 
    var source = context.createBufferSource(); // creates a sound source 
    source.buffer = audioBuffer; 
    source.connect(context.destination); 
    source.start(0); 
} 

音頻播放罰款,但與正在播放的連續區塊之間的明顯停頓(如預期)。我想擺脫他們,我明白我將不得不介紹一些緩衝。

問題:

  • 是否有一個JS庫,能爲我做到這一點? (我正在尋找它們的過程中)
  • 如果沒有圖書館可以做到這一點,我該怎麼做呢?
  • 檢測播放在一個源中完成時是否有另一個準備好立即播放? (使用AudioBufferSourceNode.onended事件處理程序)
  • 創建一個大緩衝區並一個接一個地複製我的音頻塊,並使用AudioBufferSourceNode.start AudioBufferSourceNode.stop函數控制流程?
  • 有什麼不同?
+0

找到的項目:https://github.com/scottstensland/websockets-streaming-audio它通過WebSockets的流波形文件的話,聽起來體面:http://websockets-streaming-audio.herokuapp.com/ – xmichaelx

回答

1

我已經在TypeScript中編寫了一個小類,它現在用作緩衝區。它有緩衝區大小定義爲控制它可以容納多少塊。它很短並且具有自描述性,所以我將它粘貼在這裏。有很多需要改進的地方,所以我們歡迎任何想法。

(可以快速利用它轉換爲JS:https://www.typescriptlang.org/play/

class SoundBuffer { 
    private chunks : Array<AudioBufferSourceNode> = []; 
    private isPlaying: boolean = false; 
    private startTime: number = 0; 
    private lastChunkOffset: number = 0; 

    constructor(public ctx:AudioContext, public sampleRate:number,public bufferSize:number = 6, private debug = true) { } 

    private createChunk(chunk:Float32Array) { 
     var audioBuffer = this.ctx.createBuffer(2, chunk.length, this.sampleRate); 
     audioBuffer.getChannelData(0).set(chunk); 
     var source = this.ctx.createBufferSource(); 
     source.buffer = audioBuffer; 
     source.connect(this.ctx.destination); 
     source.onended = (e:Event) => { 
      this.chunks.splice(this.chunks.indexOf(source),1); 
      if (this.chunks.length == 0) { 
       this.isPlaying = false; 
       this.startTime = 0; 
       this.lastChunkOffset = 0; 
      } 
     }; 

     return source; 
    } 

    private log(data:string) { 
     if (this.debug) { 
      console.log(new Date().toUTCString() + " : " + data); 
     } 
    } 

    public addChunk(data: Float32Array) { 
     if (this.isPlaying && (this.chunks.length > this.bufferSize)) { 
      this.log("chunk discarded"); 
      return; // throw away 
     } else if (this.isPlaying && (this.chunks.length <= this.bufferSize)) { // schedule & add right now 
      this.log("chunk accepted"); 
      let chunk = this.createChunk(data); 
      chunk.start(this.startTime + this.lastChunkOffset); 
      this.lastChunkOffset += chunk.buffer.duration; 
      this.chunks.push(chunk); 
     } else if ((this.chunks.length < (this.bufferSize/2)) && !this.isPlaying) { // add & don't schedule 
      this.log("chunk queued"); 
      let chunk = this.createChunk(data); 
      this.chunks.push(chunk); 
     } else { // add & schedule entire buffer 
      this.log("queued chunks scheduled"); 
      this.isPlaying = true; 
      let chunk = this.createChunk(data); 
      this.chunks.push(chunk); 
      this.startTime = this.ctx.currentTime; 
      this.lastChunkOffset = 0; 
      for (let i = 0;i<this.chunks.length;i++) { 
       let chunk = this.chunks[i]; 
       chunk.start(this.startTime + this.lastChunkOffset); 
       this.lastChunkOffset += chunk.buffer.duration; 
      } 
     } 
    } 
} 
3

你不顯示怎麼audioChunkReceived,但要獲得無縫的播放,你必須確保你的數據,你想之前要播放它,之前上一個停止播放。

一旦你有了這個,你可以通過調用start(t)來安排最新的塊開始播放,其中t是前一個塊的結束時間。

但是,如果緩衝採樣率與context.sampleRate不同,則可能無法順利播放,因爲需要將緩衝區轉換爲上下文速率。

+0

謝謝你,我已經使用你的建議,拼湊出簡單的解決方案。你可以在另一個答案中查看它。如果我有更多的業力,我會upvote你的答案:) – xmichaelx

+0

很高興你得到它的工作! –