File: rtm.py

package info (click to toggle)
python-mido 1.3.3-0.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 920 kB
  • sloc: python: 4,006; makefile: 127; sh: 4
file content (109 lines) | stat: -rw-r--r-- 3,140 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# SPDX-FileCopyrightText: 2013 Ole Martin Bjorndalen <ombdalen@gmail.com>
#
# SPDX-License-Identifier: MIT

"""
Experimental backend for rtmidi-python:

http://github.com/superquadratic/rtmidi-python

- Doesn't work with Python 3.3:

  File "rtmidi_python.pyx", line 61, in rtmidi_python.MidiIn.__cinit__
       (rtmidi_  python.cpp:1214)
  TypeError: expected bytes, str found

- Virtual ports don't show up, so other programs can't connect to them.

- There is no way to select API.

Other than that, it works exactly like the included python-rtmidi
backend.
"""
import rtmidi_python as rtmidi

from mido.ports import BaseInput, BaseOutput


def get_devices():
    devices = []

    input_names = set(rtmidi.MidiIn().ports)
    output_names = set(rtmidi.MidiOut().ports)

    for name in sorted(input_names | output_names):
        devices.append({
            'name': name,
            'is_input': name in input_names,
            'is_output': name in output_names,
        })

    return devices


class PortCommon:
    def _open(self, virtual=False, callback=None):
        opening_input = hasattr(self, 'receive')

        if opening_input:
            self._rt = rtmidi.MidiIn()
            self._rt.ignore_types(False, False, False)
            if callback:
                def callback_wrapper(message, delta_time):
                    self._parser.feed(message)
                    for message in self._parser:
                        callback(message)

                self._rt.callback = self._callback = callback_wrapper
                self._has_callback = True
            else:
                self._has_callback = False
        else:
            self._rt = rtmidi.MidiOut()
            # Turn of ignore of sysex, time and active_sensing.

        ports = self._rt.ports

        if virtual:
            if self.name is None:
                raise OSError('virtual port must have a name')
            self._rt.open_virtual_port(self.name)
        else:
            if self.name is None:
                # Todo: this could fail if list is empty.
                # In RtMidi, the default port is the first port.
                try:
                    self.name = ports[0]
                except IndexError as ie:
                    raise OSError('no ports available') from ie

            try:
                port_id = ports.index(self.name)
            except ValueError as ve:
                raise OSError(f'unknown port {self.name!r}') from ve

            self._rt.open_port(port_id)

        self._device_type = 'rtmidi_python'

    def _close(self):
        self._rt.close_port()


class Input(PortCommon, BaseInput):
    # Todo: sysex messages do not arrive here.
    def _receive(self, block=True):
        if self._has_callback:
            raise OSError('a callback is currently set for this port')

        while True:
            (message, delta_time) = self._rt.get_message()
            if message is None:
                break
            else:
                self._parser.feed(message)


class Output(PortCommon, BaseOutput):
    def _send(self, message):
        self._rt.send_message(message.bytes())