File: _midiin.py

package info (click to toggle)
python-expyriment 0.7.0%2Bgit34-g55a4e7e-3.2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,504 kB
  • ctags: 2,094
  • sloc: python: 12,766; makefile: 150
file content (183 lines) | stat: -rw-r--r-- 4,936 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
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""MIDI input.

This module contains a class implementing a MIDI input device.

"""

__author__ = 'Florian Krause <florian@expyriment.org>, \
Oliver Lindemann <oliver@expyriment.org>'
__version__ = '0.7.0'
__revision__ = '55a4e7e'
__date__ = 'Wed Mar 26 14:33:37 2014 +0100'


import _midiin_defaults as defaults
import expyriment
from expyriment.misc._timer import get_time
from expyriment.io._keyboard import Keyboard
from expyriment.io._input_output import Input

import time

try:
    from pygame import midi as _midi
    _midi.init()
except:
    _midi = None


class MidiIn(Input):
    """A class implementing a MIDI input.

    **EXPERIMENTAL!**

    Due to a bug in Pygame's midi module, closing a MidiIn (or the programme)
    will cause an error message. Until this is fixed in Pygame, MidiIn will
    stay in extras.

    """

    @staticmethod
    def get_devices():
        """Get a list of all MIDI input devices connected to the system."""

        if _midi is None:
            return
        indevices = []
        all_ids = _midi.get_count()
        for device_id in all_ids:
            info = _midi.get_device_info(device_id)
            if info[2] == 1:
                indevices.add([device_id, info[1]])
        return indevices

    def __init__(self, device, buffer_size=None):
        """Create a MIDI input.

        Parameters
        ----------
        device : int or str
            id or name of the MIDI device
        buffer_size : int, optional
            number of events to be buffered

        """

        import types
        if type(_midi) is not types.ModuleType:
            raise ImportError("""Sorry, MIDI input is not supported on this computer.""")

        if not expyriment._active_exp.is_initialized:
            raise RuntimeError(
                "Cannot create MidiIn before expyriment.initialize()!")
        _midi.init()
        Input.__init__(self)
        self._id = device
        if buffer_size is None:
            buffer_size = defaults.midiin_buffer_size
        self._buffer_size = buffer_size
        self.input = _midi.Input(device, buffer_size)

    @property
    def id(self):
        """Getter for id."""

        return self._id

    @property
    def buffer_size(self):
        """Getter for buffer_size."""

        return self._buffer_size

    def read(self, num_events=1):
        """Read MIDI events from device.

        Parameters
        ----------
        num_events : int, optional
            number of events to read (default=1)

        Returns
        -------
        out : timestpamed
            A timestpamed midi event will look like this:
            [status, data1, data2, data3], timestamp]

        """

        if self.input.poll():
            if self._logging:
                expyriment._active_exp._event_file_log(
                    "MIDI In ({0}),received".format(self.id), 2)
            return self.input.read(num_events)

    def clear(self):
        """Clear the input buffer.

        This can take more than 1 ms!

        """

        for _i in range(self._buffer_size):
            self.input.read(1)
            if self._logging:
                expyriment._active_exp._event_file_log(
                "MIDI In ({0}),cleared".format(self.id), 2)

    def wait(self, events, duration=None):
        """Wait for (a) certain event(s).

        Events to wait for are in the form of a list with 4 elements and do
        not include a timestamp: [status, data1, data2, data3]

        Parameters
        ----------
        events : int or list
            event(s) to wait for
        duration : int, optional
            maximal time to wait in ms

        Returns
        -------
        evt : int
            found event
        rt : int
            reaction timein ms

        """

        start = get_time()
        rt = None
        _event = None
        self.clear()
        if type(events) is list and \
           len(events) == 4 and \
           type(events[0]) is int and \
           type(events[1]) is int and \
           type(events[2]) is int and \
           type(events[3]) is int:
            events = [events]
        done = False
        while not done:
            expyriment._active_exp._execute_wait_callback()
            event = self.read(1)
            if event is not None and event[0][0] in events:
                rt = int((get_time() - start) * 1000)
                _event = event[0][0]
                done = True
                break
            if Keyboard.process_control_keys():
                done = True
                break
            if duration:
                if int((get_time() - start) * 1000) >= duration:
                    done = True
                    break

            time.sleep(0.0005)

        if self._logging:
            expyriment._active_exp._event_file_log(
                "MIDI In ({0}),received,{1},wait".format(self.id, _event), 2)
        return _event, rt