platypush/platypush/plugins/midi.py
2018-06-06 20:09:23 +02:00

94 lines
3 KiB
Python

import rtmidi
import time
from platypush.message.response import Response
from platypush.plugins import Plugin
class MidiPlugin(Plugin):
"""
Virtual MIDI controller plugin.
It requires python-rtmidi - https://pypi.org/project/python-rtmidi/
"""
_played_notes = set()
def __init__(self, device_name='Platypush virtual MIDI output',
*args, **kwargs):
super().__init__(*args, **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.open_virtual_port(self.device_name)
self.logger.info('Initialized MIDI plugin on virtual device {}'.
format(self.device_name))
def send_message(self, values, *args, **kwargs):
"""
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
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, *args, **kwargs)
return Response(output={'status':'ok'})
def play_note(self, note, velocity, duration=0):
"""
Params:
- note: MIDI note in range 0-127 with #60 = C4
- velocity: MIDI note velocity in range 0-127
- duration: Note duration in (float) seconds. Pass 0 if you don't
want the note to get off
"""
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)
def release_note(self, note):
self.send_message([0x80, note, 0]) # Note off
self._played_notes.remove(note)
def release_all_notes(self):
played_notes = self._played_notes.copy()
for note in played_notes:
self.release_note(note)
# vim:sw=4:ts=4:et: