0

I'm using the $timeout angular function to call tick() each 512 ms in order to play datas which are in my audio queue. I'm using this to perform a live audio stream. Sometimes there are some cuts in the sounds and I really need to maintain a delta of one second between emitting and receiving sound. So I want to delete some audio datas in my queue corresponding to the duration of each cuts.

Do you know if there is a way to listen to those cuts on the audioContext.destination like :

audioContext.destination.oncuts = function(duration) {
    audioQueue.read(duration);
});

Here is my tick and audioQueue functions :

var tick = function() {
    $scope.soundclock = Date.now();
    $timeout(tick, $scope.tickInterval);
    if(startStream && isFocused) {
        if(isChrome === true || isOpera === true || isIE === true || isFirefox === true) {
            if(audioQueue.length()>=size) {
                float32 = audioQueue.read(size);
                source = audioContext.createBufferSource();
                audioBuffer = audioContext.createBuffer(1, size, sampleRate);
                data = audioBuffer.getChannelData(0);
                for(var i=0; i<size;i++) {
                    data[i] = float32[i];
                }
                source.buffer = audioBuffer;
                source.connect(audioContext.destination);
                source.start(0);
            }
        }
        if(isSafari === true) {
            if(audioQueue.length()>=size) {
                float32 = audioQueue.read(size);
                source = audioContext.createBufferSource();
                audioBuffer = audioContext.createBuffer(1, size, sampleRate);
                data = audioBuffer.getChannelData(0);
                for(var j=0; j<size;j++) {
                    data[j] = float32[j];
                }
                source.buffer = audioBuffer;
                source.connect(audioContext.destination);
                source.noteOn(0);
            }
        }
    }
};

var audioQueue = {
    buffer: new Float32Array(0),

    write: function(newAudio){
        currentQLength = this.buffer.length;
        newBuffer = new Float32Array(currentQLength+newAudio.length);
        d = Date.now() - date;
        console.log('Queued '+newBuffer.length+' samples. ');
        date = Date.now();
        newBuffer.set(this.buffer, 0);
        newBuffer.set(newAudio, currentQLength);
        this.buffer = newBuffer;
    },

    read: function(nSamples){
        samplesToPlay = this.buffer.subarray(0, nSamples);
        this.buffer = this.buffer.subarray(nSamples, this.buffer.length);
        console.log('Queue at '+this.buffer.length+' samples. ');
        return samplesToPlay;
    },

    length: function(){
        return this.buffer.length;
    }
};
guicontat
  • 105
  • 1
  • 11
  • Is it safe to feed audio buffers with `$timeout` which uses `window.timeout` to do its work? I'd be surprised. It's not a very good timer and it's entirely uncoupled from the actual audio. Doesn't the WebAudio api have a callback for such an important job? – spender Nov 16 '15 at 15:11
  • Yes, it has one, I can use audioContext.currentime property. But as I explain it to @Kaiido, there is another dimension to my problem : data could arrive with a low debit and so my audioContext has not enough data to read. When it happens, some cuts arise and that's why I'm looking for a way to detect cuts ! – guicontat Nov 18 '15 at 09:58

1 Answers1

1

You need to not rely on Javascript timers (which are, for audio purposes, horribly inaccurate) and schedule your ticks ahead of time. Check out http://www.html5rocks.com/en/tutorials/audio/scheduling/, which I wrote a while ago about scheduling timers.

spender
  • 117,338
  • 33
  • 229
  • 351
cwilso
  • 13,610
  • 1
  • 30
  • 35
  • I think you should include a little bit more directly in your answer (at least talk about WebAudioAPI's timing). Like it is now it just sounds like "don't do do it, go in this other castle, you'll find your answer". – Kaiido Nov 17 '15 at 06:30
  • That's because I wrote a many-page article, with diagrams and code examples, that many people have told me they found useful (it was referred to by at least three presentations at the Web Audio Conference last year). It's not a trivial issue. The only short version really is "Javascript timers are horribly inaccurate for audio purposes" - if you try to schedule a tick every 512ms, each tick could easily be off by up to 50ms (if JS garbage collection fires, e.g.) - which is going to be a very rough rhythm. – cwilso Nov 17 '15 at 14:17
  • 1/2 @cwilso, I wasn't questioning the quality of your article here, and If I made you think I was, or just if I seemed rude to you, please accept my apologies. What I was trying to explain you, is that her on stackoverflow, we do think that answers should stand on there own, without the need to go to an other domain to fully understand it. Check this meta post [about it](http://meta.stackexchange.com/questions/225370/your-answer-is-in-another-castle-when-is-an-answer-not-an-answer). If you do think that the many-pages and all the diagrams are necessary, then the question should probably be... – Kaiido Nov 18 '15 at 01:23
  • 2/2 ...closed as "too broad". Now, I don't think it need to be since you partially gave the right answer : "don't use Javascript Timers ..." what I would add : "WebAudioAPI provides an internal clock for each AudioContext, accessible thanks to the `audioCtx.currentTime` property. For more info about it, you can read my article...". This way, we don't need to read the "many-pages" and we still have the solution inside the answer. – Kaiido Nov 18 '15 at 01:23
  • Thanks @Kaiido for your help, i will test audioCtx.currentTime as soon as possible. But there is another dimension to my problem : datas could arrive with a low debit and so my audioContext has not enough data to read. When it happens, some cuts arise and that's why I'm looking for a way to detect cuts ! – guicontat Nov 18 '15 at 09:57
  • @Kaiido, I wasn't offended - but this question needs a lot of backgrounding to really answer. I gave the best high-level response I could come up with - "don't use Javascript timers" - but the actual answer, i.e. how to do this, is a quite long answer. I don't think it's "the question is too broad", it's "the answer is too broad." – cwilso Nov 18 '15 at 18:27
  • @cwilso from the "too broad" close reason : "There are either too many possible answers, or **good answers would be too long for this format**. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs." – Kaiido Nov 19 '15 at 03:51