2016-05-26 7 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); 
} 

Audio играет хорошо, но с заметными паузами между последовательными кусками воспроизводимого (как и ожидалось). Я бы хотел избавиться от них, и я понимаю, что мне нужно будет ввести какую-то буферизацию.

Вопросы:

  • Есть библиотека 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, который теперь служит в качестве буфера. Он имеет bufferSize, определенный для управления количеством кусков, которые он может удерживать. Он короткий и самоописательный, поэтому я вставляю его здесь. Есть много улучшений, поэтому любые идеи приветствуются.

(вы можете быстро преобразовать его в 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

Спасибо, я использовал ваши советы и собрал простые решения. Вы можете просмотреть его в другом ответе. Если бы у меня было больше кармы, я бы поднял ваш ответ :) – xmichaelx

+0

Рад, что у вас это работает! –