import yaml # type: ignore import sys import jack # type: ignore import socket import numpy as np # type: ignore import time import threading def load_config(): with open("config.yaml", "r") as f: return yaml.safe_load(f) def run_zone(zone_name: str, zone_config: dict): jack_input = zone_config["jack_input"] ip = zone_config["ip"] port = zone_config["port"] sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client = jack.Client(jack_input) client.inports.register("in_l") client.inports.register("in_r") def send_pcm_frames(frames: np.ndarray): int16 = (frames * 32767.0).astype(np.int16) interleaved = np.column_stack(int16).ravel() sock.sendto(interleaved.tobytes(), (ip, port)) @client.set_process_callback def process(frames): l = client.inports[0].get_array() r = client.inports[1].get_array() data = np.vstack((l, r)) send_pcm_frames(data) with client: print(f"🎧 Streaming {zone_name} to {ip}:{port}") try: while True: time.sleep(1) except KeyboardInterrupt: print(f"🛑 {zone_name} stopped") if __name__ == "__main__": config = load_config() zones = config.get("zones", {}) if len(sys.argv) > 1: zone_name = sys.argv[1] zone = zones.get(zone_name) if not zone: print(f"[!] Zone '{zone_name}' not found in config") sys.exit(1) if not zone.get("active", True): print(f"[!] Zone '{zone_name}' is inactive in config") sys.exit(1) run_zone(zone_name, zone) else: threads = [] for zone_name, zone_config in zones.items(): if zone_config.get("active", True): t = threading.Thread(target=run_zone, args=(zone_name, zone_config), daemon=True) t.start() threads.append(t) for t in threads: t.join()