Web Audio API: макет для достижения панорамирования для произвольного числа источников - PullRequest
0 голосов
/ 03 сентября 2018

Я пытаюсь добиться управляемого панорамирования для любого количества одновременных веб-источников звука. Сами источники моно. Я работаю в Javascript с веб-аудио API (https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API).

В настоящее время проблема, с которой я сталкиваюсь, заключается в том, что я пытаюсь использовать многоканальный выход (по одному для каждого источника), но интерпретация канала переопределяет мои попытки панорамирования (см. https://developer.mozilla.org/en-US/docs/Web/API/AudioNode/channelInterpretation), заставляет меня думать, что я делаю что-то неправильно архитектурно.

Я бы хотел оставить здесь вещи в основном на концептуальном уровне, поскольку я считаю, что именно в этом заключается моя проблема.

Текущая настройка

Мой подход к этому состоит в том, чтобы один узел обрабатывал всю обработку для каждого источника, называемого здесь «scriptNode». Создается количество каналов, равное количеству аудиоисточников, и также создается такое же равное количество узлов панорамирования. График выглядит так:

The bundle size (the '=' segments) is the number of channels, set to be equal to the number of sources.

scriptNode == splitter =+-- panner1 --+= merger == destination
                        \-- panner. --/
                        \-- panner. --/
                        \-- pannerN --/

Некоторые ошибки, я вызываю эту функцию для установки scriptNode:

scriptNode = firstPart.audioCtx.createScriptProcessor(2048, 0, numParts);

Где numParts - количество источников. Я также устанавливаю scriptCode channelCountMode в «явный» и channelInterpretation в «ораторы». Одна из этих настроек может оказаться важной, но я не смог ничего найти, пытаясь поиграть с настройками.

Проблема

Когда я на самом деле тестирую свой код с этой архитектурой, я получу следующее поведение в зависимости от количества выбранных частей. Ползунки панорамирования привязаны к значениям «панорамирования» узла панорамирования для каждого соответствующего источника.

  • numParts = 1: монофонический выход, панорамирование с помощью ползунка не влияет на громкость выходного сигнала (сильнее к середине). Я полагаю, что это побочный продукт микширования моно из паннера.
  • numParts = 2: стереофонический выход, один жесткий левый, один жесткий правый. Панорамирование обоих каналов с помощью ползунка ничего не делает.
  • numParts = 3: То же, что и = 2, но третий канал молчит.
  • numParts = 4: Аналогично = 2, теперь все каналы снова работают, они панорамируются в порядке L / R / L / R. Повторное панорамирование с помощью ползунка ничего не делает.

Такое поведение, кажется, соответствует описанию channelInterpretation, но я хочу, чтобы панорамирование работало для каждого источника отдельно, независимо от количества каналов, которые я использую. И я все еще хотел бы использовать каналы, потому что каждый из моих источников ожидает записи в монобуфер.

Могу ли я сделать архитектурную настройку, чтобы сохранить эту многоканальную стратегию и добиться того, что я ищу?

Фрагменты кода

Соответствующие части текущего кода, основанные на моих утверждениях выше, наряду с попытками исправить проблему. Изменить: Благодаря комментариям ниже, мне удалось найти проблему. Я вызвал однострочное исправление, чтобы этот код можно было использовать позже.

Функция обработки звука. Только первый синтезатор (источник) устанавливает этот обратный вызов:

function customAudioProcessCallback( audioProcessingEvent )
{
    // First synth - process audio for all synths!

    const outputBuffer = audioProcessingEvent.outputBuffer;

    for ( var i = 0; i < numParts; i++ ) {

    // Each part writes to one channel.

    synthParts[ i ].synthesize(outputBuffer.getChannelData( i ), outputBuffer.length);

    }
}

Соответствующий фрагмент функции воспроизведения:

function play()
{
    const contextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext);
    synthParts[ 0 ].audioCtx = new contextClass();

    synthParts[ 0 ].scriptNode = synthParts[ 0 ].audioCtx.createScriptProcessor ? synthParts[ 0 ].audioCtx.createScriptProcessor(2048, 0, numParts+1) : synthParts[ 0 ].audioCtx.createJavaScriptNode(2048, 0, numParts+1); // 2048, 0 input channels, ? outputs
    synthParts[ 0 ].scriptNode.onaudioprocess = customAudioProcessCallback;
    synthParts[ 0 ].scriptNode.channelCountMode = 'explicit';
    synthParts[ 0 ].scriptNode.channelInterpretation = 'speakers';

    // Set up splitter and panners for all channels
    var splitter = synthParts[ 0 ].audioCtx.createChannelSplitter( numParts+1 );

    for ( var i = 0; i < numParts; i++ ) {

        panList[ i ] = synthParts[ 0 ].audioCtx.createStereoPanner();
        panList[ i ].pan = panValues[ i ];

    }

    // Connection:
    // scriptNode -- splitter -+-- panner1 --+- destination
    //                         \-- panner. --/
    //                         \-- pannerN --/

    synthParts[ 0 ].scriptNode.connect(splitter);

    for ( var i = 0; i < numParts; i++ ) {

        splitter.connect( panList[ i ], i);

        // This line used to read: 
        //    panList[ i ].connect( synthParts[ 0 ].audioCtx.destination, 0, i );
        // However, this was connecting multiple parts to the input of the audio context destination, which is limited to 1 input. The correct call is below.
        panList[ i ].connect( synthParts[ 0 ].audioCtx.destination );

    }
}

1 Ответ

0 голосов
/ 03 сентября 2018

A PannerNode всегда производит стереофонический выход. Когда вы подключаете выход панорамирования к одному из входов слияния, стереофонический выход паннера микшируется с понижением до моно, эффективно удаляя большую часть эффекта панорамирования.

Некоторая информация отсутствует, но я не понимаю, зачем вам слияние. Вы можете отправлять стереофонический выход из каждой панели напрямую в пункт назначения. Место назначения будет соответствующим образом микшировать стереофонический выход из каждого панорамирующего устройства, сохраняя эффект панорамирования.

...