Плавный переход значений в многопоточной среде

Как плавно и со скоростью, определенной другим потоком, перевести значение из x в y?

Позвольте мне попытаться объяснить с моей конкретной проблемой, поскольку это иллюстрирует это довольно хорошо.

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

Чтобы этого избежать, нужно плавно перевести громкость с текущего значения на ноль.

PortAudio, библиотека, которую я использую, обрабатывает аудио в фоновом потоке, который использует функцию обратного вызова, когда ему нужны данные или есть данные для меня. Буфер с образцами звука и значением громкости являются аргументами этого обратного вызова, поэтому я не могу контролировать, как часто они читаются.

Как вы добиваетесь перехода громкости при следующих обстоятельствах:

  • Отсутствует контроль приоритета потока в аудио-потоке (предположительно, один из самых высоких), поэтому нет контроля над частотой чтения.
  • На самом деле не может заблокировать обратный вызов, так как это может вызвать аудио ошибки.
  • Защита данных не нужна - не будет создавать заметных ошибок.

Благодарю.

РЕДАКТИРОВАТЬ:

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

В основном, поток данных может быть описан с помощью:

Loader (async producer) -> Buffer -> Player (async consumer)

Что добавляет трудности, так это то, что буфер работает как очередь (для будущей реализации безблокировочной очереди и целостности данных), поэтому я не могу дотронуться до аудиоданных, чтобы изменить их часть, чтобы они были переходными.

Это может быть недостаток дизайна, недосмотр или просто туннельное видение с моей стороны, так что это может быть изменено.

Кроме того, я хотел оставить буфер для обработки необработанных данных, так как объект Player отвечает за фактическое воспроизведение, и я чувствовал, что ответственность за управление громкостью.

Упрощенный обратный вызов может быть описан с помощью этого примера кода:

static int bufferCallback(const void *inputBuffer, void *outputBuffer,
              unsigned long framesPerBuffer,
              const PaStreamCallbackTimeInfo *timeInfo,
              PaStreamCallbackFlags statusFlags,
              void *userData)
{
    /* Cast data passed through stream to our structure. */
    simple_player<float> *data = (simple_player<float> *)userData;
    float *out = (float *)outputBuffer;
    unsigned int i;
    (void)inputBuffer; /* Prevent unused variable warning. */

    float into;
    i = 0;
    unsigned int more;
    while (i < framesPerBuffer)
    {
        more = data->buffer->get(into);
        if (!more)
        {
            return paContinue;
        }
        *out = clip::soft_clip(into*data->volume);
        out++;
        more = data->buffer->get(into);

        if (!more)
        {
            return paContinue;
        }
        *out = clip::soft_clip(into*data->volume);
        out++;
        i++;
    }
    return paContinue;
}

Примечание. После дальнейшего рассмотрения я не думаю, что смогу синхронизировать поток с потоком обработки PortAudio. Вместо этого я бы позволил самому обратному вызову изменить громкость, расширив проигрыватель и выполнив вычисления в обратном вызове, например:

static int bufferCallback(const void *inputBuffer, void *outputBuffer,
              unsigned long framesPerBuffer,
              const PaStreamCallbackTimeInfo *timeInfo,
              PaStreamCallbackFlags statusFlags,
              void *userData)
{
    /*Callback body*/
    if(data->volume!=data->target_volume)
    {
        data->volume+=data->volume_delta;
    }
    return paContinue;
}

0 ответов

Другие вопросы по тегам