X-Git-Url: https://git.ao2.it/vidi-player.git/blobdiff_plain/6220aaf5bd9b66422ccacf165f54d5b20c79cb22..2780f9db87ca6f82d325aabe88fa0e8ba14f3817:/vidi-timeline.py diff --git a/vidi-timeline.py b/vidi-timeline.py new file mode 100755 index 0000000..1068545 --- /dev/null +++ b/vidi-timeline.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# +# vidi-timeline - generate GStreamer Editing Services timelines from midi +# +# 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 sys +import mido +import vidi + +# TODO: turn that into a command line option +ADD_REST_BACKGROUND = True + + +def is_note(msg): + return msg.type == 'note_on' or msg.type == 'note_off' + + +def is_note_on(msg): + return msg.type == 'note_on' and msg.velocity > 0 + + +def is_note_off(msg): + return ((msg.type == 'note_on' and msg.velocity == 0) or + (msg.type == 'note_off')) + + +def check_overlapping_notes(midi_file): + previous_note_on = False + for msg in midi_file: + if is_note_on(msg) and msg.channel == 0: + if previous_note_on: + return True + + previous_note_on = True + elif is_note_off(msg) and msg.channel == 0: + previous_note_on = False + + return False + + +def timeline_from_midi(midi_file, video_font_path): + timeline = vidi.Timeline() + + elapsed_time = 0 + start_time = 0 + for msg in midi_file: + elapsed_time += msg.time + if is_note_on(msg) and msg.channel == 0: + start_time = elapsed_time + elif is_note_off(msg) and msg.channel == 0: + note = vidi.MidiNote(msg.note) + duration = elapsed_time - start_time + print("Note name: %3s start_time: %f duration: %f" % + (note.name, start_time, duration)) + + video_sample_path = "%s/sample_%s.webm" % (video_font_path, note.name) + + timeline.add_clip(video_sample_path, start_time, duration) + + if ADD_REST_BACKGROUND: + rest_sample_path = "%s/sample_rest.png" % video_font_path + timeline.add_layer_clip(rest_sample_path, 0, elapsed_time) + + return timeline + + +def usage(): + print("usage: %s []" + % 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 + + if len(sys.argv) > 3 and os.path.exists(sys.argv[3]): + sys.stderr.write("File '%s' exists, exiting!\n" % sys.argv[3]) + return 1 + + midi_file = mido.MidiFile(sys.argv[1]) + + overlapping_notes = check_overlapping_notes(midi_file) + if overlapping_notes: + sys.stderr.write("Sorry, supporting only midi file with no overlapping notes on channel 0\n") + return 1 + + video_font_path = os.path.realpath(sys.argv[2]) + + timeline = timeline_from_midi(midi_file, video_font_path) + + if len(sys.argv) > 3: + timeline.save(sys.argv[3]) + else: + try: + timeline.play() + except KeyboardInterrupt: + timeline.stop() + return 1 + + +if __name__ == "__main__": + sys.exit(main())