import asyncio import websockets import json import librosa import numpy as np import os import argparse import sys async def stream_file(audio_path, uri): if not os.path.exists(audio_path): print(f"Error: {audio_path} not found.") return print(f"Loading and resampling {audio_path} to 16kHz...") y, sr = librosa.load(audio_path, sr=16000) audio_int16 = (y * 32767).astype(np.int16) audio_bytes = audio_int16.tobytes() print(f"Connecting to server at {uri}...") try: async with websockets.connect(uri) as websocket: print("Connected successfully. Starting file stream...") chunk_samples = 1024 chunk_bytes_size = chunk_samples * 2 async def send_audio(): for idx in range(0, len(audio_bytes), chunk_bytes_size): chunk = audio_bytes[idx:idx + chunk_bytes_size] await websocket.send(chunk) await asyncio.sleep(chunk_samples / 16000) await websocket.send(b"") print("Finished streaming all audio bytes. Waiting for final transcript...") async def receive_transcripts(): try: async for message in websocket: data = json.loads(message) if "text" in data: print(f"\n[ASR Stream Result]: {data['text']}") except websockets.exceptions.ConnectionClosed: print("\nConnection closed by server.") await asyncio.gather(send_audio(), receive_transcripts()) except Exception as e: print(f"Error: {e}") async def stream_mic(uri): try: import sounddevice as sd except ImportError: print("Error: sounddevice library is required for live recording. Run: pip install sounddevice") return print(f"Connecting to server at {uri}...") try: async with websockets.connect(uri) as websocket: print("Connected successfully!") print("Recording from microphone... SPEAK YORUBA NOW! Press Ctrl+C to stop.") audio_queue = asyncio.Queue() loop = asyncio.get_running_loop() def callback(indata, frames, time, status): if status: print(f"Status check: {status}", file=sys.stderr) loop.call_soon_threadsafe(audio_queue.put_nowait, indata.copy()) # Start the sounddevice InputStream (16kHz, mono, int16) stream = sd.InputStream( samplerate=16000, channels=1, dtype='int16', blocksize=1024, callback=callback ) with stream: async def send_mic_audio(): try: while True: indata = await audio_queue.get() await websocket.send(indata.tobytes()) except asyncio.CancelledError: await websocket.send(b"") print("Microphone recording stopped.") async def receive_transcripts(): try: async for message in websocket: data = json.loads(message) if "text" in data: print(f"\n[ASR Stream Result]: {data['text']}") except websockets.exceptions.ConnectionClosed: print("\nConnection closed by server.") send_task = asyncio.create_task(send_mic_audio()) receive_task = asyncio.create_task(receive_transcripts()) await asyncio.gather(send_task, receive_task, return_exceptions=True) except Exception as e: print(f"Microphone stream error: {e}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Yoruba Streaming ASR Client") parser.add_argument("--mic", action="store_true", help="Use live microphone instead of a file") parser.add_argument("--file", type=str, default="test_unfinetuned_yo.wav", help="Audio file path to stream (default: test_unfinetuned_yo.wav)") parser.add_argument("--uri", type=str, default="ws://localhost:8000/stream", help="WebSocket URI (default: ws://localhost:8000/stream)") args = parser.parse_args() if args.mic: asyncio.run(stream_mic(args.uri)) else: asyncio.run(stream_file(args.file, args.uri))