Clarify audio queue, capture clock and PCM reserve contracts
This commit is contained in:
1 parent
799a71d361
commit
eab9223f39
3 files changed
+39
-21
No files matched your search
@@ -1,24 +1,28 @@
|
||||
/* Ограниченная FIFO MPSC: несколько producers, ровно один consumer. reset — только при остановленных участниках.
|
||||
* Резервирование и публикация разделены sequence слота; ещё не опубликованная голова блокирует чтение следующих элементов.
|
||||
* Обычные данные используют Capacity-8 мест, control может занять всю ёмкость; приоритета обхода FIFO у команд нет.
|
||||
* T должен допускать конструирование по умолчанию и копирующее присваивание без исключений; стоимость копирования задаёт T. */
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
/* Ограниченная очередь с несколькими producers. Producer не меняет позицию consumer.
|
||||
* Резервирование и публикация разделены sequence каждого слота; callback не ждёт занятый слот.
|
||||
* reset допустим только до запуска всех участников. Последние 8 слотов зарезервированы для команд. */
|
||||
template<class T, size_t Capacity> class AudioEventQueue {
|
||||
static_assert(Capacity > 8);
|
||||
struct Slot { std::atomic<uint64_t> sequence{0}; T value{}; };
|
||||
std::array<Slot, Capacity> m_slots;
|
||||
alignas(64) std::atomic<uint64_t> m_write{0};
|
||||
alignas(64) std::atomic<uint64_t> m_read{0};
|
||||
struct Slot { std::atomic<uint64_t> sequence{0}; T value{}; }; // Номер оборота/готовность публикации и собственная копия T.
|
||||
std::array<Slot, Capacity> m_slots; // Фиксированные слоты, без динамического роста.
|
||||
alignas(64) std::atomic<uint64_t> m_write{0}; // Следующая позиция резервирования, общая для producers.
|
||||
alignas(64) std::atomic<uint64_t> m_read{0}; // Следующая позиция чтения; изменяет только consumer.
|
||||
public:
|
||||
AudioEventQueue() { reset(); }
|
||||
/* Сбрасывает позиции/sequence без очистки сохранённых T; разрешён до запуска либо после завершения всех участников. */
|
||||
void reset() {
|
||||
m_write = 0; m_read = 0;
|
||||
for (size_t i = 0; i < Capacity; ++i) m_slots[i].sequence = i;
|
||||
}
|
||||
/* Копирует value в зарезервированный слот и публикует; true — принято, false — ёмкость/слот недоступны либо конкуренция.
|
||||
* Не ждёт освобождения слота, не более восьми попыток резервирования; false не означает строго полную очередь. */
|
||||
bool push(const T& value, bool control = false) {
|
||||
uint64_t position = m_write.load(std::memory_order_relaxed);
|
||||
for (int attempt = 0; attempt < 8; ++attempt) {
|
||||
@@ -37,6 +41,8 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* Только consumer: true копирует следующий опубликованный элемент; false оставляет value неизменным.
|
||||
* false возможен и при size()>0, если producer головы ещё выполняет копирование. */
|
||||
bool pop(T& value) {
|
||||
uint64_t position = m_read.load(std::memory_order_relaxed);
|
||||
auto& slot = m_slots[position % Capacity];
|
||||
@@ -46,6 +52,7 @@ public:
|
||||
m_read.store(position + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
/* Приблизительный снимок 0..Capacity, включая резервирования до публикации; не гарантия успеха pop/push. */
|
||||
size_t size() const {
|
||||
uint64_t read = m_read.load(std::memory_order_acquire);
|
||||
uint64_t count = m_write.load(std::memory_order_acquire) - read;
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
/* Capture worker переводит непрерывную позицию PCM 48кГц в монотонное время, плавно уточняя привязку по backend.
|
||||
* sequence — индекс PCM-фрейма на канал, observedUs — наблюдаемое время первого фрейма в микросекундах.
|
||||
* Сам не обнаруживает разрывы: владелец вызывает reset при потере sequence, смене route или скачке времени. */
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
// Позиция PCM задаёт непрерывное время; backend уточняет привязку без скачков на каждом обновлении latency.
|
||||
// Единственный владелец — capture worker. Настоящий разрыв sequence/route/time вызывает reset у владельца.
|
||||
struct VoiceCaptureClock {
|
||||
bool initialized = false;
|
||||
uint64_t originSequence = 0, previousSequence = 0;
|
||||
int64_t originUs = 0, errorUs = 0, adjustmentUs = 0;
|
||||
double phaseUs = 0;
|
||||
bool initialized = false; // Получена первая пара sequence/observedUs.
|
||||
uint64_t originSequence = 0, previousSequence = 0; // Начальная и последняя позиции в непрерывном отрезке PCM.
|
||||
int64_t originUs = 0, errorUs = 0, adjustmentUs = 0; // Начальное время, ошибка перед коррекцией и последняя коррекция, мкс.
|
||||
double phaseUs = 0; // Накопленная плавная поправка к номинальной шкале 48кГц, мкс.
|
||||
|
||||
/* Начинает новый независимый отрезок, очищая привязку и диагностические метрики. */
|
||||
void reset() { *this = {}; }
|
||||
|
||||
/* sequence должен не убывать и не переполняться до следующего reset. Первый вызов возвращает observedUs.
|
||||
* Далее возвращает originUs + номинальное время PCM + фазовую поправку: фильтр с постоянной 2с,
|
||||
* изменение поправки не более 0.5% прошедшего PCM-времени (100мкс на 20мс); errorUs/adjustmentUs обновляются. */
|
||||
int64_t map(uint64_t sequence, int64_t observedUs) {
|
||||
if (!initialized) {
|
||||
initialized = true; originSequence = previousSequence = sequence; originUs = observedUs;
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
/* Политика резерва готового PCM desktop, единственный владелец — RX worker. Сам PCM/очередь не хранит.
|
||||
* Резерв 1..4 блока по 960 фреймов/20мс (48кГц); частично сыгранный блок находится сверх этого резерва.
|
||||
* bufferMs=20/40/60/80 проверяет VoiceAudioIo::start, конструктор этого класса не валидирует аргумент.
|
||||
* Четыре места данных доступны в AudioEventQueue<Playback,12>; у звонка sample вызывается по progress вывода. */
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
/* Резерв готового PCM desktop: bufferMs = 20..80, кратно 20 (проверяет VoiceAudioIo::start).
|
||||
* Размер callback и настроенный запас задают нижнюю границу; недостача добавляет
|
||||
* один блок, 10 секунд стабильного вывода убирают один. Частично сыгранный блок — сверх резерва.
|
||||
* Только RX worker; время монотонное. Четыре PCM-слота доступны в AudioEventQueue<Playback, 12>. */
|
||||
class VoicePlaybackBuffer {
|
||||
public:
|
||||
/* Сохраняет настроенный минимум bufferMs/20 и сбрасывает адаптацию. */
|
||||
explicit VoicePlaybackBuffer(unsigned bufferMs = 20) : minimumBlocks(bufferMs / 20) { reset(); }
|
||||
unsigned blocks = 1;
|
||||
unsigned callbackFrames = 960;
|
||||
unsigned blocks = 1; // Текущая цель полной очереди, от настроенного минимума до четырёх блоков.
|
||||
unsigned callbackFrames = 960; // Размер последнего callback из sample, фреймов на канал.
|
||||
/* Возвращает настроенный минимум и 10с пробный интервал, забывая историю starvation и callback. */
|
||||
void reset() { blocks = minimumBlocks; callbackFrames = 960; stableSince = 0; lastGrowth = 0; lastSample = 0; probeUs = 10000000; }
|
||||
/* nowUs — монотонные мкс; frames — полный размер выходного callback, missing — нехватка хотя бы одного PCM-фрейма.
|
||||
* Минимум — max(настройка, ceil(frames/960), ограниченное 1..4). При missing рост на блок не чаще 500мс;
|
||||
* каждая такая попытка удваивает интервал снижения с 10с до 300с. Стабильный вывод убирает по одному блоку
|
||||
* после этого интервала; пауза sample >1с сбрасывает стабильность, но не адаптированный интервал. */
|
||||
void sample(int64_t nowUs, unsigned frames, bool missing) {
|
||||
callbackFrames = frames;
|
||||
unsigned minimum = std::max(minimumBlocks, std::clamp((frames + 959) / 960, 1u, 4u));
|
||||
@@ -29,6 +35,6 @@ public:
|
||||
}
|
||||
}
|
||||
private:
|
||||
unsigned minimumBlocks;
|
||||
int64_t stableSince = 0, lastGrowth = 0, lastSample = 0, probeUs = 10000000;
|
||||
unsigned minimumBlocks; // Настроенный минимум, не уменьшается адаптацией.
|
||||
int64_t stableSince = 0, lastGrowth = 0, lastSample = 0, probeUs = 10000000; // Начало стабильности/рост/опрос/интервал снижения, мкс.
|
||||
};
|
||||
Reference in new issue
Block a user