]> git.donarmstrong.com Git - lilypond.git/blob - lily/midi-walker.cc
Plug MIDI memory leak, and correct end-of-track issues.
[lilypond.git] / lily / midi-walker.cc
1 /*
2   midi-walker.cc -- implement Midi_walker
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 #include "midi-walker.hh"
11
12 #include "audio-column.hh"
13 #include "audio-staff.hh"
14 #include "midi-item.hh"
15 #include "midi-stream.hh"
16 #include "warn.hh"
17
18 Midi_note_event::Midi_note_event ()
19 {
20   ignore_ = false;
21 }
22
23 int
24 compare (Midi_note_event const &left, Midi_note_event const &right)
25 {
26   Moment m = (left.key - right.key);
27
28   if (m < 0)
29     return -1;
30   else if (m > 0)
31     return 1;
32   else
33     return 0;
34 }
35
36 bool
37 audio_item_less (Audio_item * const a,
38                  Audio_item * const b)
39 {
40   return a->get_column ()->when_ <  b->get_column ()->when_;
41 }
42
43 Midi_walker::Midi_walker (Audio_staff *audio_staff, Midi_track *track,
44                           int channel)
45 {
46   channel_ = channel;
47   track_ = track;
48   index_ = 0;
49   items_ = audio_staff->audio_items_;
50   vector_sort (items_, audio_item_less);
51   last_tick_ = 0;
52 }
53
54 Midi_walker::~Midi_walker ()
55 {
56   junk_pointers (midi_events_);
57 }
58
59 void
60 Midi_walker::finalize ()
61 {
62   do_stop_notes (INT_MAX);
63 }
64
65 /**
66    Find out if start_note event is needed, and do it if needed.
67 */
68 void
69 Midi_walker::do_start_note (Midi_note *note)
70 {
71   Audio_item *ptr = items_[index_];
72   int stop_ticks = int (moment_to_real (note->audio_->length_mom_) * Real (384 * 4))
73     + ptr->audio_column_->ticks ();
74
75   bool play_start = true;
76   for (vsize i = 0; i < stop_note_queue.size (); i++)
77     {
78       /* if this pith already in queue */
79       if (stop_note_queue[i].val->get_semitone_pitch ()
80           == note->get_semitone_pitch ())
81         {
82           if (stop_note_queue[i].key < stop_ticks)
83             {
84               /* let stopnote in queue be ignored,
85                  new stop note wins */
86               stop_note_queue[i].ignore_ = true;
87
88               /* don't replay start note, */
89               play_start = false;
90               break;
91             }
92           else
93             {
94               /* skip this stopnote,
95                  don't play the start note */
96               note = 0;
97               break;
98             }
99         }
100     }
101
102   if (note)
103     {
104       Midi_note_event e;
105       e.val = new Midi_note_off (note);
106       e.key = int (stop_ticks);
107       stop_note_queue.insert (e);
108
109       if (play_start)
110         output_event (ptr->audio_column_->ticks (), note);
111     }
112 }
113
114 /**
115    Output note events for all notes which end before #max_mom#
116 */
117 void
118 Midi_walker::do_stop_notes (int max_ticks)
119 {
120   while (stop_note_queue.size () && stop_note_queue.front ().key <= max_ticks)
121     {
122       Midi_note_event e = stop_note_queue.get ();
123       if (e.ignore_)
124         {
125           continue;
126         }
127
128       int stop_ticks = e.key;
129       Midi_note *note = e.val;
130
131       output_event (stop_ticks, note);
132     }
133 }
134
135 void
136 Midi_walker::output_event (int now_ticks, Midi_item *l)
137 {
138   int delta_ticks = now_ticks - last_tick_;
139   last_tick_ = now_ticks;
140
141   /*
142     this is not correct, but at least it doesn't crash when you
143     start with graces
144   */
145   if (delta_ticks < 0)
146     {
147       programming_error ("Going back in MIDI time.");
148       delta_ticks = 0;
149     }
150
151   track_->add (delta_ticks, l);
152 }
153
154 void
155 Midi_walker::process ()
156 {
157   Audio_item *audio = items_[index_];
158   do_stop_notes (audio->audio_column_->ticks ());
159
160   if (Midi_item *midi = get_midi (audio))
161     {
162       if (Midi_channel_item *mci = dynamic_cast<Midi_channel_item*> (midi))
163         mci->channel_ = channel_;
164       
165       if (Midi_note *note = dynamic_cast<Midi_note *> (midi))
166         {
167           if (note->audio_->length_mom_.to_bool ())
168             do_start_note (note);
169         }
170       else
171         output_event (audio->audio_column_->ticks (), midi);
172     }
173 }
174
175 Midi_item*
176 Midi_walker::get_midi (Audio_item *i)
177 {
178   Midi_item *mi = Midi_item::get_midi (i);
179   midi_events_.push_back (mi);
180   return mi;
181 }
182
183 bool
184 Midi_walker::ok () const
185 {
186   return index_ < items_.size ();
187 }
188
189 void
190 Midi_walker::operator ++ (int)
191 {
192   assert (ok ());
193   index_++;
194 }