Receives messages via WebSocket, processes audio and text messages. Handles binary audio chunks, extracting metadata (timestamp, flags) and putting the audio PCM data with metadata into the `incoming_chunks` queue. Applies back-pressure if the queue is full. Parses text message
(ws: WebSocket, app: FastAPI, incoming_chunks: asyncio.Queue, callbacks: 'TranscriptionCallbacks')
| 230 | # -------------------------------------------------------------------- |
| 231 | |
| 232 | async def process_incoming_data(ws: WebSocket, app: FastAPI, incoming_chunks: asyncio.Queue, callbacks: 'TranscriptionCallbacks') -> None: |
| 233 | """ |
| 234 | Receives messages via WebSocket, processes audio and text messages. |
| 235 | |
| 236 | Handles binary audio chunks, extracting metadata (timestamp, flags) and |
| 237 | putting the audio PCM data with metadata into the `incoming_chunks` queue. |
| 238 | Applies back-pressure if the queue is full. |
| 239 | Parses text messages (assumed JSON) and triggers actions based on message type |
| 240 | (e.g., updates client TTS state via `callbacks`, clears history, sets speed). |
| 241 | |
| 242 | Args: |
| 243 | ws: The WebSocket connection instance. |
| 244 | app: The FastAPI application instance (for accessing global state if needed). |
| 245 | incoming_chunks: An asyncio queue to put processed audio metadata dictionaries into. |
| 246 | callbacks: The TranscriptionCallbacks instance for this connection to manage state. |
| 247 | """ |
| 248 | try: |
| 249 | while True: |
| 250 | msg = await ws.receive() |
| 251 | if "bytes" in msg and msg["bytes"]: |
| 252 | raw = msg["bytes"] |
| 253 | |
| 254 | # Ensure we have at least an 8‑byte header: 4 bytes timestamp_ms + 4 bytes flags |
| 255 | if len(raw) < 8: |
| 256 | logger.warning("🖥️⚠️ Received packet too short for 8‑byte header.") |
| 257 | continue |
| 258 | |
| 259 | # Unpack big‑endian uint32 timestamp (ms) and uint32 flags |
| 260 | timestamp_ms, flags = struct.unpack("!II", raw[:8]) |
| 261 | client_sent_ns = timestamp_ms * 1_000_000 |
| 262 | |
| 263 | # Build metadata using fixed fields |
| 264 | metadata = { |
| 265 | "client_sent_ms": timestamp_ms, |
| 266 | "client_sent": client_sent_ns, |
| 267 | "client_sent_formatted": format_timestamp_ns(client_sent_ns), |
| 268 | "isTTSPlaying": bool(flags & 1), |
| 269 | } |
| 270 | |
| 271 | # Record server receive time |
| 272 | server_ns = time.time_ns() |
| 273 | metadata["server_received"] = server_ns |
| 274 | metadata["server_received_formatted"] = format_timestamp_ns(server_ns) |
| 275 | |
| 276 | # The rest of the payload is raw PCM bytes |
| 277 | metadata["pcm"] = raw[8:] |
| 278 | |
| 279 | # Check queue size before putting data |
| 280 | current_qsize = incoming_chunks.qsize() |
| 281 | if current_qsize < MAX_AUDIO_QUEUE_SIZE: |
| 282 | # Now put only the metadata dict (containing PCM audio) into the processing queue. |
| 283 | await incoming_chunks.put(metadata) |
| 284 | else: |
| 285 | # Queue is full, drop the chunk and log a warning |
| 286 | logger.warning( |
| 287 | f"🖥️⚠️ Audio queue full ({current_qsize}/{MAX_AUDIO_QUEUE_SIZE}); dropping chunk. Possible lag." |
| 288 | ) |
| 289 |
no test coverage detected