Использование собственного планировщика для времени аудиопотока
В приложениях обработки звука многие события синхронизируются по времени аудиопотока (которое в первом приближении представляет собой просто число потребляемых выборок, умноженное на коэффициент), а не системное время, часто называемое временем стены в этом сценарии. Например, часто желательно синхронизировать будущие события на основе времени потока. Например, можно запланировать таймер для приема записи, если в течение определенного времени не было голосовой активности; Время потока - лучшее представление времени для этой цели, чем время настенных часов.
Я думаю об использовании HistoricalScheduler
для сроков такой деятельности. Насколько я понимаю, он выполняет все запланированные действия в том же потоке, который вызвал AdvanceBy/To
, Кроме того, все приложение находится под нашим контролем, в том смысле, что нет сторонних компонентов - по крайней мере, таких, которые будут использовать RX.
Вопрос 1: Правильно ли это, хорошо или невероятно глупо? Я понимаю RX в уме, не имею опыта с ним, интуитивное чувство, если хотите.
Поток доставляется через обратные вызовы событий.NET. Поток, который доставляет потоковые буферы (и вызывает событие), лучше блокировать как можно меньше. Аудио передается в бесконечный буфер конечной длины, и эта фактическая буферизация будет происходить в наблюдателе для этого события, наблюдающего в выделенном потоке, и именно здесь будет увеличиваться время планировщика. По сути это сводится к
IDisposable _sub;
// The historic scheduler that reflects the time of
// the last sample in the endless buffer.
HistoricalScheduler _hisch = new HistoricalScheduler();
// This must be disposed of.
EventLoopScheduler _eventLoopSch = new EventLoopScheduler();
public IScheduler StreamTimeScheduler => _hisch;
// Subscribing to the stream.
void Initialize() {
_sub = Observable.FromEventPattern<TEventHandler, TEventArgs>(
h => ReceivedAudio += h,
h => ReceivedAudio -= h)
.ObserveOn(_eventLoopSch)
.Subscribe(b => Append(b));
}
// The function that puts samples into the endless buffer.
void Append(byte[] sample) {
// ...put the sample into the array...
_hisch.AdvanceBy(TimeSpan.FromSeconds(sample.Length / _avgBytesPerSecond));
}
Вопрос 2: Я чувствую странное продвижение планировщика из функции наблюдения, как в примере выше. Является ли это приемлемой практикой, или она сразу пахнет рыбой (или, может быть, это нечто среднее между двумя крайностями)?
Другое возможное осложнение заключается в том, что поток в конечном итоге перестает генерировать данные, как и время потока.
Вопрос 3: Каковы, если таковые имеются, последствия остановки времени в планировщике, так что некоторые запланированные действия никогда не будут выполняться и останутся в очереди навсегда? Будет ли просто удаление ссылки на планировщик причиной утечки одноразового ресурса?
А также время потока отличается от времени настенных часов.
Вопрос 4: Что, если таковые имеются, последствия различных планировщиков, обеспечивающих непоследовательное понятие Now
?
Мое лучшее предположение здесь "вообще нет", так как HistoricScheduler
в других сценариях мирно сосуществуют с планировщиками реального времени, но я не уверен, что достаточно хорошо понимаю эту часть RX, чтобы не задавать этот вопрос.