Skip to main content

Overview

This tutorial demonstrates how to process multiple concurrent audio streams using the Sanas SDK with dummy audio data. You’ll learn how to:
  • Share a single SDK instance across streams
  • Manage per-stream processors and state
  • Process multiple simultaneous audio streams
Use cases: Contact centers, conference platforms, multi-user applications, gaming voice chat servers.

Prerequisites

Sanas SDK is installed and configured. See Quick Start.

Handle Multiple Streams

To handle multiple audio streams, we’ll use a ThreadPoolExecutor to run each call in its own thread, with a shared SDK instance and a separate AudioProcessor per stream.
import sanas_remote_sdk
import threading
import time
from concurrent.futures import ThreadPoolExecutor

def sleep_until(target_time):
    """Sleep until target time with precision"""
    sleep_time = target_time - time.time()
    if sleep_time > 0.001:
        time.sleep(sleep_time - 0.001)
    while time.time() < target_time:
        pass

def process_call(call_id, sdk):
    """Process audio in a separate thread"""
    call_ready = threading.Event()

    def state_callback(state, reason):
        if state == sanas_remote_sdk.ProcessorState.READY:
            print(f"Call {call_id} state: READY")
            call_ready.set()
        elif state == sanas_remote_sdk.ProcessorState.FAILED:
            print(f"Call {call_id} state: FAILED")
        elif state == sanas_remote_sdk.ProcessorState.DISCONNECTED:
            print(f"Call {call_id} state: DISCONNECTED")
        elif state == sanas_remote_sdk.ProcessorState.INITIALIZING:
            print(f"Call {call_id} state: INITIALIZING")

    # Create processor for this stream
    audio_params = sanas_remote_sdk.AudioParams()
    audio_params.modelName = "desired_sanas_model"
    audio_params.sampleRate = 16000

    processor, result = sdk.CreateAudioProcessor(audio_params, state_callback)

    if result == sanas_remote_sdk.CreateProcessorResult.SUCCESS:
        if call_ready.wait(timeout=10):
            print(f"Call {call_id} processing audio...")

            first_chunk_time = time.time()

            # Process 1500 chunks (30 seconds of audio at 8kHz)
            for i in range(1500):
                ideal_time = first_chunk_time + (i * 0.02)
                sleep_until(ideal_time)

                input_samples = [0.1] * 160  # Simulated audio data (e.g., from network stream)
                output_samples = processor.ProcessSamples(input_samples)

        sdk.DestroyAudioProcessor(processor)

# Initialize SDK once - shared across all streams
sdk = sanas_remote_sdk.CreateRemoteSDK()
init_params = sanas_remote_sdk.InitParams()
init_params.remoteEndpoint = "your_server_ip"
init_params.accountId = "your_account_id"
init_params.accountSecret = "your_account_secret"
init_params.secureMedia = False
sdk.Initialize(init_params)

# Process 4 concurrent audio streams
with ThreadPoolExecutor(max_workers=4) as executor:
    futures = [executor.submit(process_call, i + 1, sdk) for i in range(4)]
    for future in futures:
        future.result()

sdk.Shutdown()
This example uses a ThreadPoolExecutor for demonstration purposes. In production, use any concurrency model (threads, asyncio, etc.) - each stream just needs its own AudioProcessor and must maintain precise 20ms timing between chunks.

Key Components

ComponentScopePurpose
SDK InstanceGlobalShared connection to the Sanas server
Audio ProcessorPer-threadIndependent audio processing
Threading EventPer-threadState synchronization
Thread PoolGlobalManages concurrent execution

References

RemoteSDK

SDK lifecycle methods including CreateAudioProcessor and DestroyAudioProcessor.

AudioProcessor

ProcessSamples and GetState methods for audio processing.

Need Help?

Email Support

support@sanas.aiResponse time: 1 business day

Support Portal

Raise a support ticket for urgent issues