File: recvrpn.py

package info (click to toggle)
python-rtmidi 1.5.8-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,248 kB
  • sloc: cpp: 4,228; python: 2,853; makefile: 287; sh: 109; ansic: 19
file content (84 lines) | stat: -rw-r--r-- 2,585 bytes parent folder | download | duplicates (2)
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# recrpn.py
#
"""Receive and decode RPN messages.

RPN (registered parameter number) messages are just regular Control Change
messages with a special semantic. To change an RPN value, the sender first
sends the parameter number with CC #100 (LSB) and #101 (MSB) and then the
parameter value with CC #38 (LSB) and #6 (MSB). Both the parameter number and
value are 14-bit values (MSB * 128 + LSB).

See also: http://www.somascape.org/midi/tech/spec.html#rpns

"""

import time
from collections import defaultdict

from rtmidi.midiconstants import (CONTROL_CHANGE, DATA_DECREMENT,
                                  DATA_ENTRY_LSB, DATA_ENTRY_MSB,
                                  DATA_INCREMENT, RPN_LSB, RPN_MSB)
from rtmidi.midiutil import open_midiinput


class RPNDecoder:
    def __init__(self, channel=1):
        self.channel = (channel - 1) & 0xF
        self.rpn = 0
        self.values = defaultdict(int)
        self.last_changed = None

    def __call__(self, event, data=None):
        msg, deltatime = event

        # event type = upper four bits of first byte
        if msg[0] == (CONTROL_CHANGE | self.channel):
            cc, value = msg[1], msg[2]

            if cc == RPN_LSB:
                self.rpn = (self.rpn >> 7) * 128 + value
            elif cc == RPN_MSB:
                self.rpn = value * 128 + (self.rpn & 0x7F)
            elif cc == DATA_INCREMENT:
                self.set_rpn(self.rpn, min(2 ** 14, self.values[self.rpn] + 1))
            elif cc == DATA_DECREMENT:
                self.set_rpn(self.rpn, max(0, self.values[self.rpn] - 1))
            elif cc == DATA_ENTRY_LSB:
                self.set_rpn(self.rpn,
                             (self.values[self.rpn] >> 7) * 128 + value)
            elif cc == DATA_ENTRY_MSB:
                self.set_rpn(self.rpn,
                             value * 128 + (self.values[self.rpn] & 0x7F))

    def set_rpn(self, rpn, value):
        self.values[rpn] = value
        self.last_changed = rpn


def main(args=None):
    decoder = RPNDecoder()
    m_in, port_name = open_midiinput(args[0] if args else None)
    m_in.set_callback(decoder)

    try:
        while True:
            rpn = decoder.last_changed

            if rpn:
                print("RPN %i: %i" % (rpn, decoder.values[rpn]))
                decoder.last_changed = None

            time.sleep(0.1)
    except KeyboardInterrupt:
        pass
    finally:
        m_in.close_port()
        del m_in


if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv[1:]) or 0)