Spaces:
Sleeping
Sleeping
File size: 4,661 Bytes
b357a00 e021baf b357a00 e021baf b357a00 e021baf b357a00 1525833 b357a00 1525833 b357a00 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# app.py
import asyncio
import base64
import json
import logging
from collections import deque
from aiohttp import web
import numpy as np
import sys
import time
import uuid
# --- Configuration ---
HOST = "0.0.0.0"
PORT = 7860
# The target delay in seconds.
TARGET_DELAY_SECONDS = 2
# The client sends a chunk of data every 1 second (1000ms).
CHUNK_SEND_INTERVAL = 1.0
# Configure logging
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(message)s')
async def websocket_handler(request):
"""
Handles a single WebSocket connection for both sending and receiving audio.
"""
websocket = web.WebSocketResponse()
await websocket.prepare(request)
client_id = str(uuid.uuid4())
logging.info(f"[{time.strftime('%H:%M:%S')}] New client connected: {client_id}")
# Use a deque as a buffer to store audio chunks for the echo delay
internal_audio_buffer = deque()
# Calculate the number of chunks needed to fill the buffer
buffer_size = int(TARGET_DELAY_SECONDS / CHUNK_SEND_INTERVAL)
try:
# Main loop to continuously receive data from the client
async for message in websocket:
if message.type == web.WSMsgType.TEXT:
# The client sends a JSON string, so we need to parse it.
try:
message_object = json.loads(message.data)
logging.info(f"[{time.strftime('%H:%M:%S')}] Received chunk #{message_object['chunkNumber']}")
# Decode the base64 audio data back to a binary array
audio_data_base64 = message_object['audioData']
audio_bytes = base64.b64decode(audio_data_base64)
# Calculate loudness (RMS) of the audio chunk
audio_samples = np.frombuffer(audio_bytes, dtype=np.int16)
rms = np.sqrt(np.mean(np.square(audio_samples.astype(np.float64)))) if audio_samples.size > 0 else 0
message_object['loudness'] = float(rms)
# Add the new chunk to the buffer
internal_audio_buffer.append(message_object)
# If the buffer has enough chunks, pop the oldest one and send it back
if len(internal_audio_buffer) >= buffer_size:
chunk_to_send = internal_audio_buffer.popleft()
await websocket.send_json(chunk_to_send)
logging.info(f"[{time.strftime('%H:%M:%S')}] Sent echoed chunk #{chunk_to_send['chunkNumber']}")
except json.JSONDecodeError:
logging.error(f"[{time.strftime('%H:%M:%S')}] Error decoding JSON from message: {message.data}")
except Exception as e:
logging.error(f"[{time.strftime('%H:%M:%S')}] An error occurred in the main loop: {e}")
elif message.type == web.WSMsgType.ERROR:
logging.error(f"[{time.strftime('%H:%M:%S')}] WebSocket received an error: {websocket.exception()}")
except asyncio.CancelledError:
logging.info(f"[{time.strftime('%H:%M:%S')}] WebSocket connection closed unexpectedly.")
finally:
logging.info(f"[{time.strftime('%H:%M:%S')}] Client disconnected: {client_id}")
await websocket.close()
async def serve_index_html(request):
"""
HTTP handler to serve the index.html file.
"""
try:
with open('index.html', 'r', encoding='utf-8') as f:
content = f.read()
return web.Response(text=content, content_type='text/html')
except FileNotFoundError:
return web.Response(text="index.html not found", status=404)
async def main():
"""
Main function to run the aiohttp application.
"""
app = web.Application()
# Route for the HTML file
app.router.add_get('/', serve_index_html)
# Route for the WebSocket connection
app.router.add_get('/ws', websocket_handler)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, HOST, PORT)
logging.info(f"[{time.strftime('%H:%M:%S')}] Starting server on http://{HOST}:{PORT}")
logging.info(f"[{time.strftime('%H:%M:%S')}] WebSocket endpoint: ws://{HOST}:{PORT}/ws")
await site.start()
# Keep the server running until terminated
await asyncio.Event().wait()
if __name__ == '__main__':
if "win" in sys.platform:
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nServer shutting down gracefully...")
|