Понимание того, почему звуковые каналы меняются при за循环е звука

Вопрос или проблема

Я пишу пользовательский аудиосмешиватель на C++, который использует 16-битный PCM с плавающей запятой, частоту дискретизации 44100 Гц и 2 канала.

Проблема, с которой я столкнулся, заключается в том, что каждый раз, когда мне нужно зациклить звуки (то есть, когда я достиг конца буфера PCM), я сбрасываю индексы выборки на 0, но я заметил, что при воспроизведении стереозвука с активным только одним каналом звуковые каналы по какой-то причине меняются местами при зацикливании. Левые образцы на самом деле воспроизводятся справа, а правые — слева. Они постоянно меняются местами каждый раз при зацикливании!

Код:

// это вызывается постоянно в аудиопотоке:
if (BuffersPlaying() < MAX_SOUND_BUFFERS /* 2 */)
 for (size_t i = 0; i < NUM_SAMPLES_PER_BUFFER /* 1024 */ * NUM_CHANNELS; ++i)
 {
     int mixedSamples = 0;
     for (size_t k = 0; k < sounds.size(); ++k)
     {
         Sound* sound = sounds[k];
         if (!sound->playing)
             continue;

         uint pcmSize = waveBuffers[sound->waveIndex].size / sizeof(int16);
         int16* pcmData = waveBuffers[sound->waveIndex].data;

         if (sound->sampleIndex < (int)pcmSize)
         {
             mixedSamples += (int)(pcmData[sound->sampleIndex] * soundsVolume);
             sound->sampleIndex++;
         }
         else if (sound->loop)
         {
             sound->sampleIndex = 0;

             mixedSamples += (int)(pcmData[sound->sampleIndex] * soundsVolume);
         }
         else
         {
             sound->playing = false;
             sound->sampleIndex = 0;
         }
     }
     soundBuffers[currentBuffer][i] = (int16)std::clamp((int)(mixedSamples * audioVolume), -32768, 32767);
}
QueueCurrentBuffer(); // загружает текущие аудиобуферы для воспроизведения аудио API
currentBuffer++;
currentBuffer %= MAX_SOUND_BUFFERS;

Однако я заметил, что в else if (sound->loop), если я установлю sound->sampleIndex в NUM_CHANNELS - 1 вместо 0, это действительно исправляет проблему, но я не понимаю, почему. Почему это так?

Я использую XAudio2 для воспроизведения звуковых буферов.

Ответ или решение

Понимание проблемы смены каналов звука при зацикливании

В процессе разработки пользовательского аудиосмешивателя на C++, вы столкнулись с проблемой обмена звуковыми каналами при зацикливании звуковых файлов. Чтобы разобраться в этом феномене, давайте проанализируем ваш код и основные принципы работы с аудио в контексте PCM.

Анализ проблемы

Ваш код обрабатывает PCM данные формата 16-битный знак, с частотой дискретизации 44100 Гц и двухканальным стереозвуком. Проблема начинается в этом фрагменте:

else if (sound->loop)
{
    sound->sampleIndex = 0;

    mixedSamples += (int)(pcmData[sound->sampleIndex] * soundsVolume);
}

Когда вы обнуляете sampleIndex в начале буфера, вы фактически всегда берете первый элемент PCM, который соответствует левому каналу звука. Так как вы работаете с стереозвуком (два канала), первый элемент будет представлять левый канал, второй – правый и так далее. Это приводит к тому, что при зацикливании звука вы постоянно берете «левый» элемент, в то время как записи на самом деле содержат данные для обоих каналов, что вызывает ненадлежащее воспроизведение.

Решение с изменением индекса

Вы упомянули, что установка sound->sampleIndex в NUM_CHANNELS - 1 устраняет проблему. Давайте рассмотрим, почему это работает.

При использовании двухканальной записи PCM, индексы образцов в массиве pcmData распределены следующим образом:

  • pcmData[0] — левый канал первого образца
  • pcmData[1] — правый канал первого образца
  • pcmData[2] — левый канал второго образца
  • pcmData[3] — правый канал второго образца
    и так далее.

Когда вы устанавливаете sampleIndex в NUM_CHANNELS - 1 (то есть 1 для двухканального звука), вы начинаете с правого канала последнего образца, что может временно решить проблему канального смещения. Однако это не является корректным долгосрочным решением.

Рекомендации по исправлению

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

else if (sound->loop)
{
    sound->sampleIndex = (sound->sampleIndex + NUM_CHANNELS) % (pcmSize + NUM_CHANNELS);
}

Заключение

Правильная обработка стереозвука требует внимательного управления индексами образцов при циклическом воспроизведении. Убедитесь, что всегда правильно рассчитываете индекс для доступа к PCM данным с учетом количества каналов. Это предотвращает проблемы воспроизведения и обеспечивает качественный вывод звука, создавая более профессиональный и эффективный аудиомикшер.

Оцените материал
Добавить комментарий

Капча загружается...