1import os 2import importlib 3from .. import ports 4 5DEFAULT_BACKEND = 'mido.backends.rtmidi' 6 7 8class Backend(object): 9 """ 10 Wrapper for backend module. 11 12 A backend module implements classes for input and output ports for 13 a specific MIDI library. The Backend object wraps around the 14 object and provides convenient 'open_*()' and 'get_*_names()' 15 functions. 16 """ 17 def __init__(self, name=None, api=None, load=False, use_environ=True): 18 self.name = name or os.environ.get('MIDO_BACKEND', DEFAULT_BACKEND) 19 self.api = api 20 self.use_environ = use_environ 21 self._module = None 22 23 # Split out api (if present). 24 if api: 25 self.api = api 26 elif self.name and '/' in self.name: 27 self.name, self.api = self.name.split('/', 1) 28 else: 29 self.api = None 30 31 if load: 32 self.load() 33 34 @property 35 def module(self): 36 """A reference module implementing the backend. 37 38 This will always be a valid reference to a module. Accessing 39 this property will load the module. Use .loaded to check if 40 the module is loaded. 41 """ 42 self.load() 43 return self._module 44 45 @property 46 def loaded(self): 47 """Return True if the module is loaded.""" 48 return self._module is not None 49 50 def load(self): 51 """Load the module. 52 53 Does nothing if the module is already loaded. 54 55 This function will be called if you access the 'module' 56 property.""" 57 if not self.loaded: 58 self._module = importlib.import_module(self.name) 59 60 def _env(self, name): 61 if self.use_environ: 62 return os.environ.get(name) 63 else: 64 return None 65 66 def _add_api(self, kwargs): 67 if self.api and 'api' not in kwargs: 68 kwargs['api'] = self.api 69 return kwargs 70 71 def open_input(self, name=None, virtual=False, callback=None, **kwargs): 72 """Open an input port. 73 74 If the environment variable MIDO_DEFAULT_INPUT is set, 75 if will override the default port. 76 77 virtual=False 78 Passing True opens a new port that other applications can 79 connect to. Raises IOError if not supported by the backend. 80 81 callback=None 82 A callback function to be called when a new message arrives. 83 The function should take one argument (the message). 84 Raises IOError if not supported by the backend. 85 """ 86 kwargs.update(dict(virtual=virtual, callback=callback)) 87 88 if name is None: 89 name = self._env('MIDO_DEFAULT_INPUT') 90 91 return self.module.Input(name, **self._add_api(kwargs)) 92 93 def open_output(self, name=None, virtual=False, autoreset=False, **kwargs): 94 """Open an output port. 95 96 If the environment variable MIDO_DEFAULT_OUTPUT is set, 97 if will override the default port. 98 99 virtual=False 100 Passing True opens a new port that other applications can 101 connect to. Raises IOError if not supported by the backend. 102 103 autoreset=False 104 Automatically send all_notes_off and reset_all_controllers 105 on all channels. This is the same as calling `port.reset()`. 106 """ 107 kwargs.update(dict(virtual=virtual, autoreset=autoreset)) 108 109 if name is None: 110 name = self._env('MIDO_DEFAULT_OUTPUT') 111 112 return self.module.Output(name, **self._add_api(kwargs)) 113 114 def open_ioport(self, name=None, virtual=False, 115 callback=None, autoreset=False, **kwargs): 116 """Open a port for input and output. 117 118 If the environment variable MIDO_DEFAULT_IOPORT is set, 119 if will override the default port. 120 121 virtual=False 122 Passing True opens a new port that other applications can 123 connect to. Raises IOError if not supported by the backend. 124 125 callback=None 126 A callback function to be called when a new message arrives. 127 The function should take one argument (the message). 128 Raises IOError if not supported by the backend. 129 130 autoreset=False 131 Automatically send all_notes_off and reset_all_controllers 132 on all channels. This is the same as calling `port.reset()`. 133 """ 134 kwargs.update(dict(virtual=virtual, callback=callback, 135 autoreset=autoreset)) 136 137 if name is None: 138 name = self._env('MIDO_DEFAULT_IOPORT') or None 139 140 if hasattr(self.module, 'IOPort'): 141 # Backend has a native IOPort. Use it. 142 return self.module.IOPort(name, **self._add_api(kwargs)) 143 else: 144 # Backend has no native IOPort. Use the IOPort wrapper 145 # in midi.ports. 146 # 147 # We need an input and an output name. 148 149 # MIDO_DEFAULT_IOPORT overrides the other two variables. 150 if name: 151 input_name = output_name = name 152 else: 153 input_name = self._env('MIDO_DEFAULT_INPUT') 154 output_name = self._env('MIDO_DEFAULT_OUTPUT') 155 156 kwargs = self._add_api(kwargs) 157 158 return ports.IOPort(self.module.Input(input_name, **kwargs), 159 self.module.Output(output_name, **kwargs)) 160 161 def _get_devices(self, **kwargs): 162 if hasattr(self.module, 'get_devices'): 163 return self.module.get_devices(**self._add_api(kwargs)) 164 else: 165 return [] 166 167 def get_input_names(self, **kwargs): 168 """Return a list of all input port names.""" 169 devices = self._get_devices(**self._add_api(kwargs)) 170 names = [device['name'] for device in devices if device['is_input']] 171 return names 172 173 def get_output_names(self, **kwargs): 174 """Return a list of all output port names.""" 175 devices = self._get_devices(**self._add_api(kwargs)) 176 names = [device['name'] for device in devices if device['is_output']] 177 return names 178 179 def get_ioport_names(self, **kwargs): 180 """Return a list of all I/O port names.""" 181 devices = self._get_devices(**self._add_api(kwargs)) 182 inputs = [device['name'] for device in devices if device['is_input']] 183 outputs = set( 184 [device['name'] for device in devices if device['is_output']]) 185 return [name for name in inputs if name in outputs] 186 187 def __repr__(self): 188 if self.loaded: 189 status = 'loaded' 190 else: 191 status = 'not loaded' 192 193 if self.api: 194 name = '{}/{}'.format(self.name, self.api) 195 else: 196 name = self.name 197 198 return '<backend {} ({})>'.format(name, status) 199