Как слушать, когда audioContext.destination не воспроизводит звук в Web Audio API

Я использую угловую функцию $timeout для вызова tick() каждые 512 мс, чтобы воспроизводить данные, которые находятся в моей аудиоочереди. Я использую это для воспроизведения живого аудиопотока. Иногда в звуках есть некоторые сокращения, и мне действительно нужно поддерживать дельту в одну секунду между излучением и получением звука. Поэтому я хочу удалить некоторые аудиоданные в своей очереди, соответствующие продолжительности каждого сокращения.

Знаете ли вы, есть ли способ прослушать эти фрагменты в audioContext.destination, например:

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

Вот мои функции tick и audioQueue:

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;
    }
};

person guicontat    schedule 16.11.2015    source источник
comment
Безопасно ли загружать аудиобуферы с помощью $timeout, который использует window.timeout для выполнения своей работы? Я был бы удивлен. Это не очень хороший таймер, и он совершенно не связан с реальным звуком. Разве API WebAudio не имеет обратного вызова для такой важной работы?   -  person spender    schedule 16.11.2015
comment
Да, он есть, я могу использовать свойство audioContext.currentime. Но, как я объясняю @Kaiido, у моей проблемы есть еще одно измерение: данные могут поступать с низким дебетом, и поэтому в моем audioContext недостаточно данных для чтения. Когда это происходит, возникают некоторые порезы, и поэтому я ищу способ обнаружить порезы!   -  person guicontat    schedule 18.11.2015


Ответы (1)


Вам не нужно полагаться на таймеры Javascript (которые для аудио ужасно неточны) и планировать свои тики заранее. Посетите http://www.html5rocks.com/en/tutorials/audio/scheduling/, который я недавно писал о таймерах планирования.

person cwilso    schedule 16.11.2015
comment
Я думаю, вы должны включить немного больше прямо в свой ответ (по крайней мере, поговорить о сроках WebAudioAPI). Как и сейчас, это просто звучит так, как будто не делай этого, иди в этот другой замок, ты найдешь свой ответ. - person Kaiido; 17.11.2015
comment
Это потому, что я написал многостраничную статью с диаграммами и примерами кода, которую многие люди считают полезной (на нее ссылались как минимум в трех презентациях на конференции Web Audio в прошлом году). Это не тривиальный вопрос. Единственная короткая версия на самом деле заключается в том, что таймеры Javascript ужасно неточны для звуковых целей - если вы попытаетесь запланировать тик каждые 512 мс, каждый тик может легко отключиться на 50 мс (например, если сработает сборка мусора JS) - что будет очень грубый ритм. - person cwilso; 17.11.2015
comment
1/2 @cwilso, я не ставил под сомнение качество вашей статьи здесь, и если я заставил вас так подумать или просто показался вам грубым, примите мои извинения. Что я пытался вам объяснить, так это то, что она в stackoverflow, мы действительно думаем, что ответы должны стоять сами по себе, без необходимости переходить в другой домен, чтобы полностью понять это. Проверьте этот мета-пост об этом. Если вы считаете, что много страниц и все диаграммы необходимы, то вопрос, вероятно, должен быть... - person Kaiido; 18.11.2015
comment
2/2 ...закрыт как слишком широкий. Теперь я не думаю, что это должно быть так, поскольку вы частично дали правильный ответ: не используйте таймеры Javascript ... что я хотел бы добавить: WebAudioAPI предоставляет внутренние часы для каждого AudioContext, доступные благодаря audioCtx.currentTime свойство. Для получения дополнительной информации об этом вы можете прочитать мою статью.... Таким образом, нам не нужно читать много страниц, и у нас все еще есть решение внутри ответа. - person Kaiido; 18.11.2015
comment
Спасибо @Kaiido за вашу помощь, я как можно скорее протестирую audioCtx.currentTime. Но у моей проблемы есть еще одно измерение: данные могут поступать с низким дебетом, поэтому в моем аудиоконтексте недостаточно данных для чтения. Когда это происходит, возникают некоторые порезы, и поэтому я ищу способ обнаружить порезы! - person guicontat; 18.11.2015
comment
@Kaiido, я не обиделся, но для ответа на этот вопрос нужно много знаний. Я дал лучший высокоуровневый ответ, который я мог придумать - не используйте таймеры Javascript, - но фактический ответ, то есть как это сделать, является довольно длинным ответом. Я не думаю, что вопрос слишком широк, это ответ слишком широк. - person cwilso; 18.11.2015
comment
@cwilso из-за слишком широкой причины закрытия: либо слишком много возможных ответов, либо хорошие ответы были бы слишком длинными для этого формата. Пожалуйста, добавьте детали, чтобы сузить набор ответов или выделить проблему, на которую можно ответить в нескольких абзацах. - person Kaiido; 19.11.2015