Skip to content

macOS port: rewrite as pure-Python menu bar app#1

Merged
AnyByte merged 22 commits into
mainfrom
macos-port
Apr 9, 2026
Merged

macOS port: rewrite as pure-Python menu bar app#1
AnyByte merged 22 commits into
mainfrom
macos-port

Conversation

@AnyByte
Copy link
Copy Markdown
Owner

@AnyByte AnyByte commented Apr 9, 2026

Summary

Replaces the Linux/ALSA implementation with a pure-Python macOS menu bar
app that forwards Ableton Link tempo to a hardware MIDI clock output.
The original C/ALSA sources are archived under legacy/ and not deleted.

What's new

  • linkbridge/ Python package — single-process menu bar app with
    three threads sharing a lock-guarded ClockState:
    • link_monitor.pyaalink callback wrapper, runs an asyncio loop
      on a daemon thread, pushes tempo and transport into shared state
    • clock_engine.py — 24 ppqn MIDI clock generator with
      drift-compensated absolute-time scheduling, transport sync, graceful
      device-disappearance handling
    • app.pyrumps menu bar UI with device picker, Start/Stop toggle,
      refresh, and quit
    • settings.py — JSON store at ~/Library/Application Support/LinkBridge/
      with corrupt-file recovery
    • midi_output.py — thin mido/python-rtmidi wrappers for CoreMIDI
  • 22 unit tests (pytest) covering Settings, MidiOutput, and
    ClockEngine using injected fake clocks / fake MIDI sinks; no hardware
    or Link peer required.
  • py2app packagingsetup.py + scripts/build_app.sh produce
    dist/LinkBridge.app, an unsigned menu-bar-only bundle (LSUIElement,
    arm64 native, ~50 MB).
  • Custom app icon at assets/LinkBridge.icns plus the source PNG and
    a regeneration script (scripts/build_icon.py) using Pillow + iconutil.
  • README rewritten with install/build steps, menu reference,
    hardware notes for Novation Circuit Tracks, and a clear explanation of
    Rekordbox's one-way Link constraint and the manual workflow it
    requires.

Verified end-to-end

  • aalink discovers Rekordbox 7.2.10 as a Link peer; tempo callbacks
    fire correctly
  • Clock thread streams 24 ppqn MIDI clock at the requested BPM with
    ~1% drift over 2 s windows (matches the original Linux Python loop)
  • Novation Circuit Tracks (with Clock Rx enabled in Setup view)
    enters SYN mode and follows the streamed tempo in real time
  • Bundled .app launches from Finder, registers for local network
    permission, opens last-used MIDI device, persists settings across
    launches

Test plan

  • `python3.11 -m venv venv && source venv/bin/activate && pip install -r requirements.txt`
  • `pytest` — 22 tests pass
  • `python -m linkbridge` — menu bar `♪ ` icon appears
  • `./scripts/build_app.sh` — produces `dist/LinkBridge.app`
  • `open dist/LinkBridge.app` — bundle launches, asks for local network permission
  • Pick `Circuit Tracks MIDI` from the Output Device menu, verify clock streams (Circuit Tracks shows `SYN`)
  • Quit cleanly via menu

Notes

  • Linux ALSA implementation moved to `legacy/`, not maintained
  • Bundle is unsigned; first launch may need right-click → Open to bypass Gatekeeper
  • Rekordbox users: see README for the manual Link tempo workflow

Максим Лясковский added 22 commits April 9, 2026 14:40
- Create linkbridge/ package with version stub
- Create tests/ and pytest.ini
- Move Linux C + Python sources into legacy/
- Replace requirements.txt with rumps/mido/python-rtmidi/aalink
- Add requirements-dev.txt for pytest
- Clock stream runs unconditionally once a port is set
- Toggleable MIDI START/STOP on Link transport transitions
- Gracefully drops device on send failure instead of crashing
- Drift-compensated absolute-time scheduling (port from Linux clock.py)
- Synchronous _tick_once() allows full TDD without real threads
@AnyByte AnyByte merged commit 67b49e8 into main Apr 9, 2026
1 check passed
@AnyByte AnyByte deleted the macos-port branch April 9, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant