from pythonosc.dispatcher import Dispatcher from pythonosc.osc_server import BlockingOSCUDPServer from datetime import datetime import sys class VrcOscGateway: def __init__(self, host="127.0.0.1", port=9001): self.host = host self.port = port self.dispatcher = Dispatcher() self.last_values = {} def log(self, level, message): now = datetime.now().strftime("%H:%M:%S") print(f"[{level}] {now} {message}", flush=True) def to_bool(self, value): if isinstance(value, bool): return value if isinstance(value, int) or isinstance(value, float): return value != 0 if isinstance(value, str): lowered = value.strip().lower() if lowered in ("true", "1", "on"): return True if lowered in ("false", "0", "off"): return False raise ValueError(f"unsupported OSC value: {value}") def is_rising_edge(self, address, value): current = self.to_bool(value) previous = self.last_values.get(address, False) self.last_values[address] = current return current and not previous def is_changed(self, address, value): current = self.to_bool(value) previous = self.last_values.get(address, None) if previous == current: return False self.last_values[address] = current return True def map(self, parameter_name, handler): address = f"/avatar/parameters/{parameter_name}" self.dispatcher.map(address, handler) self.log("INFO", f"mapped {address}") def set_default_debug(self, enabled=False): if not enabled: return def on_any(address, *args): if address.startswith("/avatar/parameters/"): self.log("INFO", f"other address={address} args={args}") self.dispatcher.set_default_handler(on_any) def serve_forever(self): self.log("CHECK", "STEP=1 状態確認") self.log("INFO", f"listen={self.host}:{self.port}") try: server = BlockingOSCUDPServer((self.host, self.port), self.dispatcher) server.serve_forever() except KeyboardInterrupt: self.log("INFO", "Ctrl+C detected") except Exception as e: self.log("FATAL", f"OSC server error STEP=3 detail={e}") input("Press Enter to exit...") sys.exit(1) self.log("CHECK", "STEP=4 結果確認") self.log("INFO", "終了しました")