Update docs and screenshots
CI / backend (pull_request) Canceled after 0s
CI / shell (pull_request) Canceled after 0s
CI / frontend (pull_request) Canceled after 0s
CI / arm64-smoke (pull_request) Canceled after 0s
CI / backend (push) Canceled after 0s
CI / shell (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / arm64-smoke (push) Canceled after 0s
CI / backend (pull_request) Canceled after 0s
CI / shell (pull_request) Canceled after 0s
CI / frontend (pull_request) Canceled after 0s
CI / arm64-smoke (pull_request) Canceled after 0s
CI / backend (push) Canceled after 0s
CI / shell (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / arm64-smoke (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
from contextlib import suppress
|
||||
|
||||
from .client import TuxDmxMidiClient, reconnect_delay
|
||||
from .config import BridgeConfig, load_config
|
||||
from .logging_setup import configure_logging
|
||||
from .midi import MidiEventFilter, list_input_devices, match_input_device, normalize_message
|
||||
|
||||
logger = logging.getLogger("tuxdmx_midi_bridge")
|
||||
|
||||
|
||||
async def run_bridge(config: BridgeConfig, debug: bool = False) -> None:
|
||||
if debug:
|
||||
config.logging.level = "DEBUG"
|
||||
config.logging.show_midi_events = True
|
||||
configure_logging(config.logging.level)
|
||||
client = TuxDmxMidiClient(config.server)
|
||||
event_filter = MidiEventFilter(
|
||||
channel_filter=config.midi.channel_filter,
|
||||
control_change_interval_ms=config.performance.control_change_interval_ms,
|
||||
suppress_duplicate_values=config.performance.suppress_duplicate_values,
|
||||
)
|
||||
attempt = 0
|
||||
|
||||
while True:
|
||||
try:
|
||||
device_name = match_input_device(config.midi.device_name)
|
||||
if device_name is None:
|
||||
raise RuntimeError(f"MIDI-input matcher ikke '{config.midi.device_name}'.")
|
||||
logger.info("Valgt MIDI-input: %s", device_name)
|
||||
import mido
|
||||
|
||||
with mido.open_input(device_name) as port:
|
||||
await client.send_heartbeat(
|
||||
{
|
||||
"type": "heartbeat",
|
||||
"protocol_version": 1,
|
||||
"bridge_id": config.bridge_id,
|
||||
"device": device_name,
|
||||
}
|
||||
)
|
||||
logger.info("Forbundet til TuxDMX som bridge '%s'", config.bridge_id)
|
||||
attempt = 0
|
||||
heartbeat_task = asyncio.create_task(_heartbeat_loop(client, config.bridge_id, device_name))
|
||||
try:
|
||||
while True:
|
||||
for message in port.iter_pending():
|
||||
if config.midi.ignore_active_sensing and getattr(message, "type", "") == "active_sensing":
|
||||
continue
|
||||
payload = normalize_message(message, config.bridge_id, device_name)
|
||||
if payload is None or not event_filter.should_forward(payload):
|
||||
continue
|
||||
if config.logging.show_midi_events:
|
||||
logger.info(
|
||||
"MIDI %s ch=%s no=%s val=%s",
|
||||
payload["message"]["type"],
|
||||
payload["message"]["channel"],
|
||||
payload["message"]["number"],
|
||||
payload["message"]["value"],
|
||||
)
|
||||
await client.send_event(payload)
|
||||
await asyncio.sleep(0.01)
|
||||
finally:
|
||||
heartbeat_task.cancel()
|
||||
with suppress(asyncio.CancelledError):
|
||||
await heartbeat_task
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Afslutter MIDI-bridge kontrolleret.")
|
||||
return
|
||||
except Exception as exc:
|
||||
attempt += 1
|
||||
delay = reconnect_delay(attempt, config.server.reconnect_seconds)
|
||||
logger.warning("Bridge-fejl: %s. Nyt forsøg om %.1f sek.", exc, delay)
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
|
||||
async def _heartbeat_loop(client: TuxDmxMidiClient, bridge_id: str, device_name: str) -> None:
|
||||
while True:
|
||||
await asyncio.sleep(5)
|
||||
await client.send_heartbeat(
|
||||
{
|
||||
"type": "heartbeat",
|
||||
"protocol_version": 1,
|
||||
"bridge_id": bridge_id,
|
||||
"device": device_name,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="TuxDMX MIDI bridge")
|
||||
parser.add_argument("--config", default="config.yaml", help="Sti til YAML-konfiguration")
|
||||
parser.add_argument("--list-devices", action="store_true", help="List tilgængelige MIDI-inputs")
|
||||
parser.add_argument("--test-connection", action="store_true", help="Test forbindelse til TuxDMX")
|
||||
parser.add_argument("--debug", action="store_true", help="Aktivér debuglog")
|
||||
return parser
|
||||
|
||||
|
||||
async def _async_main() -> int:
|
||||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list_devices:
|
||||
for name in list_input_devices():
|
||||
print(name)
|
||||
return 0
|
||||
|
||||
config = load_config(args.config)
|
||||
configure_logging("DEBUG" if args.debug else config.logging.level)
|
||||
|
||||
if args.test_connection:
|
||||
client = TuxDmxMidiClient(config.server)
|
||||
device_name = match_input_device(config.midi.device_name) or config.midi.device_name
|
||||
result = await client.test_connection(config.bridge_id, device_name)
|
||||
print(result)
|
||||
return 0
|
||||
|
||||
await run_bridge(config, debug=args.debug)
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
return asyncio.run(_async_main())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user