1 from __future__ import absolute_import
2 """Chord sequence realizer for the output of a chord labeler.
3
4 """
5 """
6 ============================== License ========================================
7 Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding
8
9 This file is part of The Jazz Parser.
10
11 The Jazz Parser is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 The Jazz Parser is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with The Jazz Parser. If not, see <http://www.gnu.org/licenses/>.
23
24 ============================ End license ======================================
25
26 """
27 __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"
28
29 from midi import EventStream, NoteOnEvent, read_midifile, NoteOffEvent, \
30 ProgramChangeEvent, LyricsEvent
31
33 """
34 Factory to take the output from the labeler and realize the chord sequence
35 as a midi file.
36
37 Very basic - not going to sound great, but it's easier than playing it
38 myself.
39
40 """
41 - def __init__(self, labels, chord_vocab, resolution=120, chord_length=2, \
42 text_events=False):
43 """
44 @type labels: list of L{jazzparser.misc.chordlabel.data.ChordLabel}s
45 @param labels: chord labels output by the model
46 @type chord_vocab: dict
47 @param chord_vocab: mapping from the chord labels that may appear in
48 C{labels} to the notes of the chord
49 @type resolution: int
50 @param resolution: midi resolution to give the result (midi ticks per beat)
51 @type chord_length: int
52 @param chord_length: length of each chord as a number of beats
53 @type text_events: bool
54 @param text_events: include chord labels in the midi as text events
55
56 """
57 self.labels = labels
58 self.chord_vocab = chord_vocab
59 self.resolution = resolution
60 self.chord_length = chord_length
61 self.text_events = text_events
62
63 - def generate(self, overlay=None, offset=0):
64 """
65 Generates a midi stream.
66
67 """
68 octaves = 1
69
70 if overlay is not None:
71 stream = overlay
72
73 instrument = 23
74
75 channel = max(ev.channel for ev in stream.trackpool) + 1
76 volume = 50
77 else:
78 stream = EventStream()
79 stream.resolution = self.resolution
80
81 instrument = 0
82 channel = 0
83 volume = 127
84
85 stream.add_track()
86 pc = ProgramChangeEvent()
87 pc.value = instrument
88 pc.tick = 0
89 pc.channel = channel
90 stream.add_event(pc)
91
92
93 chord_length = int(self.resolution * self.chord_length)
94
95 times = [i*chord_length + offset for i in range(len(self.labels))]
96
97 pending_note_offs = []
98 for label,time in zip(self.labels, times):
99 chord_root = label.root
100
101 triad_notes = [(chord_root + note) % (octaves*12) + 72 for \
102 note in self.chord_vocab[label.label]]
103
104 triad_notes.append(chord_root + 48)
105
106
107 for noff in pending_note_offs:
108 noff.tick = time-1
109 stream.add_event(noff)
110 pending_note_offs = []
111
112 if self.text_events:
113
114 tevent = LyricsEvent()
115 tevent.data = "%s\n" % label
116 tevent.tick = time
117 stream.add_event(tevent)
118
119
120 for note in triad_notes:
121 non = NoteOnEvent()
122 non.tick = time
123 non.pitch = note
124 non.channel = channel
125 non.velocity = volume
126 stream.add_event(non)
127
128
129 noff = NoteOffEvent()
130 noff.pitch = note
131 noff.channel = channel
132 noff.velocity = volume
133 pending_note_offs.append(noff)
134
135
136 for noff in pending_note_offs:
137 noff.tick = time+chord_length
138 stream.add_event(noff)
139 return stream
140