Плавный переход значений в многопоточной среде
Как плавно и со скоростью, определенной другим потоком, перевести значение из 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;
}