X-Git-Url: https://git.ao2.it/vidi-player.git/blobdiff_plain/7debfa86ea34c87aead56d92bf08b9a0b51feeef..4cd0a64f760b6138a62141c087da38a1b787ac5b:/vidi/Sampler.py diff --git a/vidi/Sampler.py b/vidi/Sampler.py new file mode 100755 index 0000000..645e39e --- /dev/null +++ b/vidi/Sampler.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Sampler - play video samples interactively according to midi messages +# +# Copyright (C) 2016 Antonio Ospite +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import mido + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst +Gst.init(None) + +from gi.repository import GObject +GObject.threads_init() + +import vidi + +class Sampler(vidi.Player): + def __init__(self, videofont_path): + playbin = Gst.ElementFactory.make("playbin", "player") + vidi.Player.__init__(self, playbin) + + self.videofont_path = videofont_path + self.last_note = None + + self.uri = Gst.filename_to_uri(self.get_sample_path(self.last_note)) + self.pipeline.set_property("uri", self.uri) + self.pipeline.connect("about-to-finish", self.on_about_to_finish) + + def on_about_to_finish(self, element): + element.set_property("uri", self.uri) + + def get_sample_path(self, note): + if note is None: + sample_name = "rest" + else: + sample_name = vidi.MidiNote(note).name + + return "%s/sample_%s.webm" % (self.videofont_path, sample_name) + + def midi_message_cb(self, msg): + if vidi.is_note(msg): + new_note = msg.note + + # The logic is as follows: + # - Always play a new note on; + # - only play silence if the note off is the same one that was + # played last; + # - otherwise do nothing. + if vidi.is_note_on(msg): + note = new_note + elif vidi.is_note_off(msg) \ + and new_note == self.last_note: + note = None + else: + note = self.last_note + + if note != self.last_note: + self.last_note = note + sample_path = self.get_sample_path(note) + if os.path.exists(sample_path): + self.switch(sample_path) + else: + print("Warning: videofont is missing sample '%s'" % sample_path) + + def switch(self, sample_path): + print("Next: %s" % sample_path) + self.uri = Gst.filename_to_uri(sample_path) + seek_event = Gst.Event.new_seek(1.0, + Gst.Format.TIME, + Gst.SeekFlags.FLUSH, + Gst.SeekType.END, -1, + Gst.SeekType.NONE, 0) + self.pipeline.send_event(seek_event) + + +class DeviceSampler(Sampler): + def __init__(self, videofont_path, midi_source_name): + Sampler.__init__(self, videofont_path) + + print(mido) + midi_source = mido.open_input(midi_source_name) + midi_source.callback = self.midi_message_cb + + +class FileSampler(Sampler): + def __init__(self, videofont_path, midi_file_name): + Sampler.__init__(self, videofont_path) + + self.midi_file = mido.MidiFile(midi_file_name) + overlapping_notes = vidi.check_overlapping_notes(self.midi_file) + if overlapping_notes: + print("Sorry, supporting only midi file with no overlapping notes on channel 0") + return None + + def play(self): + def next_midi_msg(midi_file_generator): + try: + msg = midi_file_generator.__next__() + if vidi.is_note(msg) and msg.channel == 0: + self.midi_message_cb(msg) + return True + except StopIteration: + self.stop() + return False + + + GObject.timeout_add(1, next_midi_msg, self.midi_file.play()) + Sampler.play(self)