diff --git a/platypush/backend/midi.py b/platypush/backend/midi.py index e4f08630..61f182e4 100644 --- a/platypush/backend/midi.py +++ b/platypush/backend/midi.py @@ -24,13 +24,19 @@ class MidiBackend(Backend): def __init__(self, device_name=None, port_number=None, midi_throttle_time=None, *args, **kwargs): """ - :param device_name: Name of the MIDI device. *N.B.* either `device_name` or `port_number` must be set + :param device_name: Name of the MIDI device. *N.B.* either + `device_name` or `port_number` must be set. + Use :method:`platypush.plugins.midi.query_ports` to get the + available ports indices and names :type device_name: str :param port_number: MIDI port number :type port_number: int - :param midi_throttle_time: If set, the MIDI events will be throttled - max one per selected time frame (in seconds). Set this parameter if you want to synchronize MIDI events with plugins that normally operate with a lower throughput. + :param midi_throttle_time: If set, the MIDI events will be throttled - + max one per selected time frame (in seconds). Set this parameter if + you want to synchronize MIDI events with plugins that normally + operate with a lower throughput. :type midi_throttle_time: int """ diff --git a/platypush/plugins/midi.py b/platypush/plugins/midi.py index de0e35a1..ab9bba8a 100644 --- a/platypush/plugins/midi.py +++ b/platypush/plugins/midi.py @@ -120,5 +120,20 @@ class MidiPlugin(Plugin): self.release_note(note) + @action + def query_ports(self): + """ + :returns: dict: A list of the available MIDI ports with index and name + """ + + in_ports = rtmidi.MidiIn().get_ports() + out_ports = rtmidi.MidiOut().get_ports() + + return { + 'in': { i: port for i, port in enumerate(in_ports) }, + 'out': { i: port for i, port in enumerate(out_ports) }, + } + + # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/sound/core.py b/platypush/plugins/sound/core.py index fba2302d..bbca23c0 100644 --- a/platypush/plugins/sound/core.py +++ b/platypush/plugins/sound/core.py @@ -214,6 +214,15 @@ class Mix(object): self._sounds.append(Sound.build(sound)) + def remove(self, sound_index): + if sound_index >= len(self._sounds): + self.logger.error('No such sound index: {} in mix {}'.format( + sound_index, list(self))) + return + + self._sounds.pop(sound_index) + + def get_wave(self, t_start=0., t_end=0., normalize_range=(-1.0, 1.0), on_clip='scale', samplerate=Sound._DEFAULT_SAMPLERATE): """ diff --git a/platypush/plugins/sound/plugin.py b/platypush/plugins/sound/plugin.py index 35fb9e4e..2d3d0bd9 100644 --- a/platypush/plugins/sound/plugin.py +++ b/platypush/plugins/sound/plugin.py @@ -325,7 +325,7 @@ class SoundPlugin(Plugin): if duration is not None else t+blocktime data = mix.get_wave(t_start=t, t_end=next_t, - samplerate=samplerate) + samplerate=samplerate) t = next_t if duration is not None and t >= duration: @@ -706,6 +706,47 @@ class SoundPlugin(Plugin): self.logger.info('Recording paused state toggled') self.recording_paused_changed.set() + @action + def release(self, stream, index=None, midi_note=None, frequency=None): + """ + Remove a sound from an active stream, either by sound index (use + :method:`platypush.sound.plugin.SoundPlugin.query_streams` to get + the sounds playing on the active streams), midi_note, frequency + or absolute file path. + + :param stream: Stream index + :type stream: int + + :param index: Sound index + :type index: int + + :param midi_note: MIDI note + :type midi_note: int + + :param frequency: Sound frequency + :type frequency: float + """ + + if index is None and midi_note is None and frequency is None: + raise RuntimeError('Please specify either a sound index, ' + + 'midi_note or frequency to release') + + mix = self.stream_mixes.get(stream) + if not mix: + raise RuntimeError('No such stream: {}'.format(stream)) + + for i, sound in enumerate(mix): + if (index is not None and i == index) or \ + (midi_note is not None + and sound.get('midi_note') == midi_note) or \ + (frequency is not None + and sound.get('frequency') == frequency): + if len(list(mix)) == 1: + # Last sound in the mix + self.stop_playback([stream]) + else: + mix.remove(i) + def _get_playback_state(self, stream_index): with self.playback_state_lock: return self.playback_state[stream_index]