Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ Now when you move a knob or press a button on your MIDI device, the
corresponding property/method will be updated/called on the `CMMCorePlus`
object. :tada:

## Example

From this repo, run:

```bash
python example.py
```

and adjust `mini.yml` to assign buttons

## Debugging/Development

Use the environment variable `PYMMCORE_MIDI_DEBUG=1` to print out the MIDI
Expand Down
23 changes: 23 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from pymmcore_plus import CMMCorePlus
from pymmcore_widgets import ImagePreview, PropertyBrowser
from qtpy.QtWidgets import QApplication

from pymmcore_midi import DeviceMap, XTouchMini

app = QApplication([])

# from pymmcore-plus
core = CMMCorePlus.instance()
core.loadSystemConfiguration()

# this library
mini = XTouchMini()
dev_map = DeviceMap.from_file("mini.yml")
dev_map.connect_to_core(core, mini)

# just stuff from widgets to show that it's responding
img = ImagePreview(mmcore=core)
img.show()
browser = PropertyBrowser(mmcore=core)
browser.show()
app.exec()
17 changes: 17 additions & 0 deletions mini.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
device_name: X-TOUCH MINI
mappings:
- [button, 8, Camera, AllowMultiROI]
- [button, 9, Camera, Binning]
- [knob, 2, Camera, Gain]
- [knob, 9, Camera, CCDTemperature]
- message_type: control_change
control_id: 1
device_label: Camera
property_name: Exposure
- message_type: button
control_id: 22
core_method: snap
- message_type: knob
control_id: 17
core_method: setAutoFocusOffset

18 changes: 18 additions & 0 deletions src/pymmcore_midi/_device.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import os
import time
from threading import Thread
from typing import TYPE_CHECKING, ClassVar, Iterable, Iterator, Mapping, TypeVar

import mido
Expand Down Expand Up @@ -57,6 +59,18 @@ def release(self) -> None:
msg = mido.Message("note_off", channel=self._channel, note=self._note)
self._output.send(msg)

def blink(self, speed: float = 0.05, repeat: int = 1) -> None:
"""Blink the button on and off in another thread."""

def _blink(repeat: int = repeat) -> None:
for _ in range(repeat):
self.press()
time.sleep(speed)
self.release()
time.sleep(speed)

Thread(target=_blink, daemon=True).start()


class Knob:
"""A knob/slider on a midi device."""
Expand Down Expand Up @@ -150,6 +164,7 @@ def __init__(
self._buttons = Buttons(button_ids, self._output)
self._knobs = Knobs(knob_ids, self._output)
self._debug = debug
Thread(target=self.do_a_little_dance, daemon=True).start()

@property
def knob(self) -> Knobs:
Expand Down Expand Up @@ -196,3 +211,6 @@ def reset(self) -> None:
knob.set_value(0)
for button in self._buttons.values():
button.release()

def do_a_little_dance(self) -> None:
"""Do something nifty to indicate that the device is connected."""
10 changes: 10 additions & 0 deletions src/pymmcore_midi/_xtouch.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import time
from itertools import chain

from ._device import Button, MidiDevice


Expand Down Expand Up @@ -63,3 +66,10 @@ def play(self) -> Button:
@property
def record(self) -> Button:
return self._buttons[23]

def do_a_little_dance(self, speed: float = 0.04, repeat: int = 2) -> None:
"""Do something nifty to indicate that the device is connected."""
for _ in range(repeat):
for i in chain(range(8, 16), reversed(range(16, 24))):
self._buttons[i].blink()
time.sleep(speed)