Use ChannelStreamer to handle multiple live audio sources (e.g., microphone and system audio) in a single streaming session.
Key Concepts:
- Mixing: The SDK mixes multiple PCM channels into a single mono stream over one WebSocket. It keeps channels aligned by consuming the shortest buffer; if a source ends, you must call
mixer.close_channel(name) to pad it with silence and prevent stalling. - Attribution:
ChannelStreamer delivers an enriched DualChannelTurnEvent. This allows you to identify the physical source of words via turn.channel and word.channel. - Independence: Channel attribution (
word.channel) is independent of speaker diarization (word.speaker). - Async Support:
AsyncChannelStreamer provides an asyncio-native interface with await mixer.stream(...) and await mixer.close_channel(...).
Workflow:
- Initialize
RealTimeTranscriber. - Create a
ChannelStreamer instance, declaring named channels and the session sample rate. - Register handlers on the mixer (e.g.,
mixer.on(RealTimeEvents.Turn, handler)). - Connect the client.
- Feed PCM data to each channel using
mixer.stream(channel_name, pcm_data). - Call
mixer.flush() to push trailing audio.
from assemblyai.streaming.v3 import (
ChannelStreamer, RealTimeTranscriber, RealTimeTranscriberOptions,
RealTimeEvents, RealTimeParameters,
)
def on_turn(client, event): # event is a DualChannelTurnEvent
print(f"[{event.channel}] {event.transcript}")
for w in event.words:
print(f" {w.text!r} -> channel={w.channel} speaker={w.speaker}")
client = RealTimeTranscriber(RealTimeTranscriberOptions(api_key="<YOUR_API_KEY>"))
# Declare the channels and the session sample rate (must be pcm_s16le).
mixer = ChannelStreamer(client, channels=["mic", "system"], sample_rate=16000)
# Register handlers on the mixer: Turn handlers receive the enriched event,
# other events (Begin/Error/…) are forwarded to the client.
mixer.on(RealTimeEvents.Turn, on_turn)
client.connect(RealTimeParameters(
sample_rate=16000, speech_model="universal-3-5-pro", speaker_labels=True,
))
# Feed each source separately — e.g. from two capture callbacks. Send
# continuous PCM for every channel (silence as zeros), at the same rate.
mixer.stream("mic", mic_pcm)
mixer.stream("system", system_pcm)
mixer.flush() # push trailing buffered audio
client.disconnect(terminate=True)