2016-05-26 25 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

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