TODO: add an entry about a better format for the VideoFont
[vidi-player.git] / vidi-timeline.py
1 #!/usr/bin/env python3
2 #
3 # vidi-timeline - generate GStreamer Editing Services timelines from midi
4 #
5 # Copyright (C) 2016  Antonio Ospite <ao2@ao2.it>
6 #
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.
11 #
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.
16 #
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/>.
19
20 import os
21 import sys
22 import mido
23 import vidi
24
25 # TODO: turn that into a command line option
26 ADD_REST_BACKGROUND = True
27
28
29 def timeline_from_midi(midi_file, video_font_path):
30     timeline = vidi.Timeline()
31
32     elapsed_time = 0
33     start_time = 0
34     for msg in midi_file:
35         elapsed_time += msg.time
36         if vidi.is_note_on(msg) and msg.channel == 0:
37             start_time = elapsed_time
38         elif vidi.is_note_off(msg) and msg.channel == 0:
39             note = vidi.MidiNote(msg.note)
40             duration = elapsed_time - start_time
41             print("Note name: %3s start_time: %f duration: %f" %
42                   (note.name, start_time, duration))
43
44             video_sample_path = "%s/sample_%s.webm" % (video_font_path, note.name)
45
46             timeline.add_clip(video_sample_path, start_time, duration)
47
48     if ADD_REST_BACKGROUND:
49         rest_sample_path = "%s/sample_rest.png" % video_font_path
50         timeline.add_layer_clip(rest_sample_path, 0, elapsed_time)
51
52     return timeline
53
54
55 def usage():
56     print("usage: %s <midi_file> <videofont_directory> [<destination_file>]"
57           % os.path.basename(sys.argv[0]))
58
59
60 def main():
61     if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
62         usage()
63         return 0
64
65     if len(sys.argv) < 3:
66         usage()
67         return 1
68
69     if not os.path.isdir(sys.argv[2]):
70         sys.stderr.write("The second argument must be the path of the videofont directory\n")
71         usage()
72         return 1
73
74     if len(sys.argv) > 3 and os.path.exists(sys.argv[3]):
75         sys.stderr.write("File '%s' exists, exiting!\n" % sys.argv[3])
76         return 1
77
78     midi_file = mido.MidiFile(sys.argv[1])
79
80     overlapping_notes = vidi.check_overlapping_notes(midi_file)
81     if overlapping_notes:
82         sys.stderr.write("Sorry, supporting only midi file with no overlapping notes on channel 0\n")
83         return 1
84
85     video_font_path = os.path.realpath(sys.argv[2])
86
87     timeline = timeline_from_midi(midi_file, video_font_path)
88
89     if len(sys.argv) > 3:
90         timeline.save(sys.argv[3])
91     else:
92         try:
93             timeline.play()
94         except KeyboardInterrupt:
95             timeline.stop()
96             return 1
97
98
99 if __name__ == "__main__":
100     sys.exit(main())