vocal_helper.fanout module
vocal_helper.fanout
Fork one queue into several, so a producer never has to know who is listening.
Why the pipeline needed this
Pipeline wires its raw-audio queue to exactly one consumer, the voice-activity stage.
That was enough while every diarizer in the package worked on voiced segments, and it stops
being enough the moment one works on the audio itself: DiartDiarStage
consumes PCM directly, and an end-of-stream batch diarizer wants the whole stream. Neither can
be attached without a second reader, and making the source aware of them would put the
knowledge of who consumes what back into the thing that should know least.
Ported from operator-assistance-streaming/streaming/multiplex.py, which is being absorbed.
Measuring it
on_wait reports how long each put blocked, per sink. That number is the difference
between “the model is slow” and “a consumer is starving everyone”, and it is what
vocal_helper.live_metrics.LiveMetrics.record_queue_wait() was built to receive.
- vocal_helper.fanout.LOSSLESS = 'lossless'
wait for room. Nothing is lost, and this sink can pace the whole fan-out.
- Type:
Sink policy
- vocal_helper.fanout.LOSSY = 'lossy'
when full, drop the OLDEST pending item to make room. For consumers that show the present rather than record it — a stale frame on a live meter is worth less than a stalled session, and dropping the oldest keeps what is displayed as current as possible.
- Type:
Sink policy
- class vocal_helper.fanout.Sink(queue, *, policy='lossless', name='')[source]
Bases:
objectOne destination of a fan-out: a queue plus what to do when it is full.
- Parameters:
queue (asyncio.Queue) – Where items go. Its
maxsizeis what makes the policy matter at all; an unbounded queue never blocks and never drops, it just grows until the process dies.name (str, optional) – Used in
on_waitreports, so a queue-wait figure can be attributed to a consumer rather than to “somewhere in the pipeline”.
- dropped
Items dropped because this sink was full. Non-zero is information, not an error — it says a lossy consumer could not keep up, which is exactly what it opted into.
- async vocal_helper.fanout.fanout(source, sinks, *, on_wait=None)[source]
Pull from source and forward every item to every sink until the stream ends.
The terminating
Nonesentinel always reaches every sink, whatever its policy: losing an end-of-stream marker is not a late frame, it is a consumer that waits forever. On a lossy sink it makes room for itself the same way data does, rather than blocking on a queue nobody is draining — the first version awaited it unconditionally and deadlocked. On cancellation the sentinel is pushed too, so no downstream stage is left hanging on a session that was torn down.- Parameters:
source (asyncio.Queue) – The producer’s queue. A bare queue in
sinksis wrapped as a losslessSink, so the simple call stays simple.on_wait (callable, optional) –
(sink_name, seconds)for every put that had to wait. Feed it toLiveMetrics.record_queue_wait()to find out which consumer is pacing the rest.
- Return type:
None