3 # Sampler - play video samples interactively according to midi messages
5 # Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 gi.require_version('Gst', '1.0')
25 from gi.repository import Gst
28 from gi.repository import GObject
29 GObject.threads_init()
33 class Sampler(vidi.Player):
34 def __init__(self, videofont_path):
35 playbin = Gst.ElementFactory.make("playbin", "player")
36 vidi.Player.__init__(self, playbin)
38 self.videofont_path = videofont_path
41 self.uri = Gst.filename_to_uri(self.get_sample_path(self.last_note))
42 self.pipeline.set_property("uri", self.uri)
43 self.pipeline.connect("about-to-finish", self.on_about_to_finish)
45 def on_about_to_finish(self, element):
46 element.set_property("uri", self.uri)
48 def get_sample_path(self, note):
52 sample_name = vidi.MidiNote(note).name
54 return "%s/sample_%s.webm" % (self.videofont_path, sample_name)
56 def midi_message_cb(self, msg):
60 # The logic is as follows:
61 # - Always play a new note on;
62 # - only play silence if the note off is the same one that was
64 # - otherwise do nothing.
65 if vidi.is_note_on(msg):
67 elif vidi.is_note_off(msg) \
68 and new_note == self.last_note:
73 if note != self.last_note:
75 sample_path = self.get_sample_path(note)
76 if os.path.exists(sample_path):
77 self.switch(sample_path)
79 print("Warning: videofont is missing sample '%s'" % sample_path)
81 def switch(self, sample_path):
82 print("Next: %s" % sample_path)
83 self.uri = Gst.filename_to_uri(sample_path)
84 seek_event = Gst.Event.new_seek(1.0,
89 self.pipeline.send_event(seek_event)
92 class DeviceSampler(Sampler):
93 def __init__(self, videofont_path, midi_source_name):
94 Sampler.__init__(self, videofont_path)
97 midi_source = mido.open_input(midi_source_name)
98 midi_source.callback = self.midi_message_cb
101 class FileSampler(Sampler):
102 def __init__(self, videofont_path, midi_file_name):
103 Sampler.__init__(self, videofont_path)
105 self.midi_file = mido.MidiFile(midi_file_name)
106 overlapping_notes = vidi.check_overlapping_notes(self.midi_file)
107 if overlapping_notes:
108 print("Sorry, supporting only midi file with no overlapping notes on channel 0")
112 def next_midi_msg(midi_file_generator):
114 msg = midi_file_generator.__next__()
115 if vidi.is_note(msg) and msg.channel == 0:
116 self.midi_message_cb(msg)
118 except StopIteration:
123 GObject.timeout_add(1, next_midi_msg, self.midi_file.play())