add support for midi devices.

midi example added.

minor version bump
This commit is contained in:
onyx-and-iris
2022-07-24 14:38:16 +01:00
parent 43d4496378
commit 9d446ea17d
11 changed files with 166 additions and 3 deletions

View File

@@ -9,8 +9,9 @@ from .cbindings import CBindings
from .error import CAPIError, VMError
from .inst import bits
from .kinds import KindId
from .misc import Midi
from .subject import Subject
from .util import comp, polling, script
from .util import comp, grouper, polling, script
class Remote(CBindings):
@@ -20,6 +21,7 @@ class Remote(CBindings):
def __init__(self, **kwargs):
self.cache = {}
self.midi = Midi()
self.subject = Subject()
self.strip_mode = 0
self.running = None
@@ -69,6 +71,8 @@ class Remote(CBindings):
self.cache["strip_level"] = self._strip_buf
self.cache["bus_level"] = self._bus_buf
self.subject.notify("ldirty")
if self.get_midi_message():
self.subject.notify("midi")
time.sleep(self.ratelimit)
@@ -231,6 +235,22 @@ class Remote(CBindings):
),
)
def get_midi_message(self):
n = ct.c_long(1024)
buf = ct.create_string_buffer(1024)
res = self.vm_get_midi_message(ct.byref(buf), n)
if res > 0:
vals = tuple(grouper(3, (int.from_bytes(buf[i]) for i in range(res))))
for msg in vals:
ch, pitch, vel = msg
if not self.midi._channel or self.midi._channel != ch:
self.midi._channel = ch
self.midi._most_recent = pitch
self.midi._set(pitch, vel)
return True
elif res == -1 or res == -2:
raise CAPIError(f"VBVMR_GetMidiMessage returned {res}")
@script
def sendtext(self, script: str):
"""Sets many parameters from a script"""

View File

@@ -99,6 +99,10 @@ class CBindings(metaclass=ABCMeta):
ct.POINTER(WCHAR * 256),
]
vm_get_midi_message = libc.VBVMR_GetMidiMessage
vm_get_midi_message.restype = LONG
vm_get_midi_message.argtypes = [ct.POINTER(CHAR * 1024), LONG]
def call(self, func):
res = func()
if res != 0:

View File

@@ -1,3 +1,5 @@
from typing import Optional
from .error import VMError
from .iremote import IRemote
from .kinds import kinds_all
@@ -227,3 +229,24 @@ class Delay(IRemote):
def set(self, val: int):
self.setter(f"delay[{self.index}]", val)
class Midi:
def __init__(self):
self._channel = None
self.cache = {}
self._most_recent = None
@property
def channel(self) -> int:
return self._channel
@property
def current(self) -> int:
return self._most_recent
def get(self, key: int) -> Optional[int]:
return self.cache.get(key)
def _set(self, key: int, velocity: int):
self.cache[key] = velocity

View File

@@ -12,7 +12,7 @@ class Subject:
return self._observers
def notify(self, modifier=None):
def notify(self, modifier):
"""run callbacks on update"""
[o.on_update(modifier) for o in self._observers]

View File

@@ -1,4 +1,5 @@
import functools
from itertools import zip_longest
from typing import Iterator
@@ -61,3 +62,11 @@ def comp(t0: tuple, t1: tuple) -> Iterator[bool]:
"""
for a, b in zip(t0, t1):
yield a == b
def grouper(n, iterable, fillvalue=None):
"""
Group elements of an iterable by sets of n length
"""
args = [iter(iterable)] * n
return zip_longest(fillvalue=fillvalue, *args)