|
|
import asyncio |
|
|
import websockets |
|
|
import json |
|
|
import numpy as np |
|
|
import traceback |
|
|
from music_generator import MusicGenerator |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clients = { |
|
|
"webapp": set() |
|
|
} |
|
|
audio_source = None |
|
|
|
|
|
|
|
|
|
|
|
def initialize_dependencies(): |
|
|
"""Loads all necessary files and initializes objects.""" |
|
|
global notes, generator |
|
|
print("Initializing dependencies...") |
|
|
try: |
|
|
|
|
|
import os |
|
|
dir_path = os.path.dirname(os.path.realpath(__file__)) |
|
|
|
|
|
with open(os.path.join(dir_path, 'consonance_matrix.json')) as f: |
|
|
consonance_matrix = np.array(json.load(f)) |
|
|
|
|
|
notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] |
|
|
generator = MusicGenerator(len(notes)) |
|
|
print("Dependencies initialized successfully.") |
|
|
return True |
|
|
except Exception as e: |
|
|
print("--- CRITICAL: FAILED TO INITIALIZE DEPENDENCIES ---") |
|
|
print(traceback.format_exc()) |
|
|
print("----------------------------------------------------") |
|
|
return False |
|
|
|
|
|
async def broadcast_to_webapps(message): |
|
|
"""Sends a message to all connected webapp clients.""" |
|
|
if clients["webapp"]: |
|
|
tasks = [client.send(message) for client in clients["webapp"]] |
|
|
await asyncio.gather(*tasks, return_exceptions=True) |
|
|
|
|
|
async def handle_audio_data(data): |
|
|
"""Mocks the audio analysis.""" |
|
|
import random |
|
|
detected_chord = random.choice(notes) |
|
|
predicted_chord = random.choice(notes) |
|
|
key = "C Major" |
|
|
|
|
|
analysis_result = { |
|
|
"type": "analysis_update", |
|
|
"current_chord": detected_chord, |
|
|
"predicted_chord": predicted_chord, |
|
|
"musical_key": key |
|
|
} |
|
|
await broadcast_to_webapps(json.dumps(analysis_result)) |
|
|
|
|
|
|
|
|
|
|
|
async def connection_handler(websocket, path): |
|
|
"""Handles incoming WebSocket connections with robust error logging.""" |
|
|
global audio_source |
|
|
print(f"New client connected: {websocket.remote_address}") |
|
|
|
|
|
try: |
|
|
initial_message = await websocket.recv() |
|
|
message_data = json.loads(initial_message) |
|
|
client_type = message_data.get("type") |
|
|
|
|
|
if client_type == "extension_hello": |
|
|
if audio_source is not None: |
|
|
await audio_source.close(code=1012, reason="New extension connected.") |
|
|
audio_source = websocket |
|
|
print("Audio capture extension connected.") |
|
|
await websocket.send(json.dumps({"status": "connected", "role": "audio_source"})) |
|
|
await broadcast_to_webapps(json.dumps({"type": "status_update", "message": "Audio source connected."})) |
|
|
|
|
|
elif client_type == "webapp_hello": |
|
|
clients["webapp"].add(websocket) |
|
|
print("Web app client connected.") |
|
|
await websocket.send(json.dumps({"status": "connected", "role": "viewer"})) |
|
|
status_msg = "Audio source connected." if audio_source else "Waiting for audio source..." |
|
|
await websocket.send(json.dumps({"type": "status_update", "message": status_msg})) |
|
|
|
|
|
else: |
|
|
print(f"Unknown client type: {client_type}. Disconnecting.") |
|
|
return |
|
|
|
|
|
async for message in websocket: |
|
|
if websocket == audio_source: |
|
|
await handle_audio_data(message) |
|
|
|
|
|
except websockets.exceptions.ConnectionClosed as e: |
|
|
print(f"Connection closed normally for {websocket.remote_address}. Code: {e.code}, Reason: {e.reason}") |
|
|
except Exception as e: |
|
|
|
|
|
print(f"--- UNEXPECTED SERVER ERROR in connection_handler ---") |
|
|
print(f"Error Type: {type(e).__name__}") |
|
|
print(f"Error Message: {e}") |
|
|
print("Traceback:") |
|
|
print(traceback.format_exc()) |
|
|
print("----------------------------------------------------") |
|
|
|
|
|
if not websocket.closed: |
|
|
await websocket.close(code=1011, reason="Internal Server Error") |
|
|
finally: |
|
|
if websocket in clients["webapp"]: |
|
|
clients["webapp"].remove(websocket) |
|
|
if websocket == audio_source: |
|
|
audio_source = None |
|
|
print("Audio capture extension disconnected.") |
|
|
await broadcast_to_webapps(json.dumps({"type": "status_update", "message": "Audio source disconnected."})) |
|
|
|
|
|
async def main(): |
|
|
"""Initializes dependencies and starts the WebSocket server.""" |
|
|
if not initialize_dependencies(): |
|
|
print("Server cannot start due to initialization failure.") |
|
|
return |
|
|
|
|
|
websocket_port = 7860 |
|
|
print(f"Starting WebSocket server on port {websocket_port}...") |
|
|
async with websockets.serve(connection_handler, "0.0.0.0", websocket_port): |
|
|
await asyncio.Future() |
|
|
|
|
|
if __name__ == "__main__": |
|
|
asyncio.run(main()) |