--- /dev/null
+#!/usr/bin/env python3
+#
+# vidi-player - play video samples interactively from a midi file
+#
+# Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+import os
+import sys
+import vidi
+
+
+def usage():
+ print("usage: %s <midi_file> <videofont_directory>"
+ % os.path.basename(sys.argv[0]))
+
+
+def main():
+ if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
+ usage()
+ return 0
+
+ if len(sys.argv) < 3:
+ usage()
+ return 1
+
+ if not os.path.isdir(sys.argv[2]):
+ sys.stderr.write("The second argument must be the path of the videofont directory\n")
+ usage()
+ return 1
+
+ video_font_path = os.path.realpath(sys.argv[2])
+ sampler = vidi.FileSampler(video_font_path, sys.argv[1])
+ sampler.play()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
--- /dev/null
+#!/usr/bin/env python3
+#
+# vidi-sampler - play video samples interactively from a midi device
+#
+# Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+import os
+import sys
+import vidi
+
+
+def usage():
+ print("usage: %s <midi_input_device> <videofont_directory>"
+ % os.path.basename(sys.argv[0]))
+
+
+def main():
+ if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
+ usage()
+ return 0
+
+ if len(sys.argv) < 3:
+ usage()
+ return 1
+
+ if not os.path.isdir(sys.argv[2]):
+ sys.stderr.write("The second argument must be the path of the videofont directory\n")
+ usage()
+ return 1
+
+ video_font_path = os.path.realpath(sys.argv[2])
+ sampler = vidi.DeviceSampler(video_font_path, sys.argv[1])
+ sampler.play()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
--- /dev/null
+#!/usr/bin/env python3
+#
+# Sampler - play video samples interactively according to midi messages
+#
+# Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+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)