forked from platypush/platypush
Fabio Manganiello
c3337ccc6c
Added an `add_dependencies` plugin to the Sphinx build process that parses the manifest files of the scanned backends and plugins and automatically generates the documentation for the required dependencies and triggered events. This means that those dependencies are no longer required to be listed in the docstring of the class itself. Also in this commit: - Black/LINT for some integrations that hadn't been touched in a long time. - Deleted some leftovers from previous refactors (deprecated `backend.mqtt`, `backend.zwave.mqtt`, `backend.http.request.rss`). - Deleted deprecated `inotify` backend - replaced by `file.monitor` (see #289).
129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
import time
|
|
|
|
from platypush.plugins import Plugin, action
|
|
|
|
|
|
class MidiPlugin(Plugin):
|
|
"""
|
|
Virtual MIDI controller plugin. It allows you to send custom MIDI messages
|
|
to any connected devices.
|
|
"""
|
|
|
|
_played_notes = set()
|
|
|
|
def __init__(self, device_name='Platypush virtual MIDI output', **kwargs):
|
|
"""
|
|
:param device_name: MIDI virtual device name (default: *Platypush virtual MIDI output*)
|
|
:type device_name: str
|
|
"""
|
|
import rtmidi
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
self.device_name = device_name
|
|
self.midiout = rtmidi.MidiOut()
|
|
available_ports = self.midiout.get_ports()
|
|
|
|
if available_ports:
|
|
self.midiout.open_port(0)
|
|
self.logger.info('Initialized MIDI plugin on port 0')
|
|
else:
|
|
self.midiout.open_virtual_port(self.device_name)
|
|
self.logger.info(
|
|
'Initialized MIDI plugin on virtual device {}'.format(self.device_name)
|
|
)
|
|
|
|
@action
|
|
def send_message(self, values):
|
|
"""
|
|
:param values: Values is expected to be a list containing the MIDI command code and the command parameters -
|
|
see reference at https://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html
|
|
:type values: list[int]
|
|
|
|
Available MIDI commands:
|
|
* ``0x80`` Note Off
|
|
* ``0x90`` Note On
|
|
* ``0xA0`` Aftertouch
|
|
* ``0xB0`` Continuous controller
|
|
* ``0xC0`` Patch change
|
|
* ``0xD0`` Channel Pressure
|
|
* ``0xE0`` Pitch bend
|
|
* ``0xF0`` Start of system exclusive message
|
|
* ``0xF1`` MIDI Time Code Quarter Frame (Sys Common)
|
|
* ``0xF2`` Song Position Pointer (Sys Common)
|
|
* ``0xF3`` Song Select
|
|
* ``0xF6`` Tune Request (Sys Common)
|
|
* ``0xF7`` End of system exclusive message
|
|
* ``0xF8`` Timing Clock (Sys Realtime)
|
|
* ``0xFA`` Start (Sys Realtime)
|
|
* ``0xFB`` Continue (Sys Realtime)
|
|
* ``0xFC`` Stop (Sys Realtime)
|
|
* ``0xFE`` Active Sensing (Sys Realtime)
|
|
* ``0xFF`` System Reset (Sys Realtime)
|
|
"""
|
|
|
|
self.midiout.send_message(values)
|
|
|
|
@action
|
|
def play_note(self, note, velocity, duration=0):
|
|
"""
|
|
Play a note with selected velocity and duration.
|
|
|
|
:param note: MIDI note in range 0-127 with #60 = C4
|
|
:type note: int
|
|
|
|
:param velocity: MIDI note velocity in range 0-127
|
|
:type velocity: int
|
|
|
|
:param duration: Note duration in seconds. Pass 0 if you don't want the note to get off
|
|
:type duration: float
|
|
"""
|
|
|
|
self.send_message([0x90, note, velocity]) # Note on
|
|
self._played_notes.add(note)
|
|
|
|
if duration:
|
|
time.sleep(duration)
|
|
self.send_message([0x80, note, 0]) # Note off
|
|
self._played_notes.remove(note)
|
|
|
|
@action
|
|
def release_note(self, note):
|
|
"""
|
|
Release a played note.
|
|
|
|
:param note: MIDI note in range 0-127 with #60 = C4
|
|
:type note: int
|
|
"""
|
|
|
|
self.send_message([0x80, note, 0]) # Note off
|
|
self._played_notes.remove(note)
|
|
|
|
@action
|
|
def release_all_notes(self):
|
|
"""
|
|
Release all the notes being played.
|
|
"""
|
|
|
|
played_notes = self._played_notes.copy()
|
|
for note in played_notes:
|
|
self.release_note(note)
|
|
|
|
@action
|
|
def query_ports(self):
|
|
"""
|
|
:returns: dict: A list of the available MIDI ports with index and name
|
|
"""
|
|
|
|
import rtmidi
|
|
|
|
in_ports = rtmidi.MidiIn().get_ports()
|
|
out_ports = rtmidi.MidiOut().get_ports()
|
|
|
|
return {
|
|
'in': dict(enumerate(in_ports)),
|
|
'out': dict(enumerate(out_ports)),
|
|
}
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|