]> git.donarmstrong.com Git - lilypond.git/blob - mi2mu/midi-track.cc
f575ccfe68ca8f3445a6392e6d9675ee07a4d5ee
[lilypond.git] / mi2mu / midi-track.cc
1 //
2 // midi-track.cc -- implement Midi_track
3 //
4 // copyright 1997 Jan Nieuwenhuizen <jan@digicash.com>
5
6 #include "mi2mu.hh"
7
8 Midi_track::Midi_track( int number_i, String copyright_str, String track_name_str, String instrument_str )
9 {
10         number_i_ = number_i;
11         copyright_str_ = copyright_str;
12         instrument_str_ = instrument_str;
13         name_str_ = track_name_str;
14         midi_time_p_ = new Midi_time( 4, 2, 24, 8 );
15         midi_tempo_p_ = new Midi_tempo( 1000000 );
16 #ifdef TCOL_LIST
17         tcol_p_list_.bottom().add( new Track_column( Moment( 0 ) ) );
18 #else
19         tcol_p_array_.push( new Track_column( Moment( 0 ) ) );
20 #endif
21 }
22
23 Midi_track::~Midi_track()
24 {
25         delete midi_time_p_;
26         delete midi_tempo_p_;
27 }
28
29 #ifdef MVOICE_LIST
30 void
31 Midi_track::add_begin_at( Link_list<Midi_voice*>& open_voices_r, Moment mom )
32 {
33         for ( PCursor<Midi_voice*> i( midi_voice_p_list_.top() ); i.ok(); i++ )
34                 if ( i->begin_mom() == mom ) {
35                         tor( DEBUG_ver ) << "open_voices (" << open_voices_r.size() << "): +1\n";
36                         open_voices_r.bottom().add( *i );
37                 }
38 }
39 #else
40 void
41 Midi_track::add_begin_at( Array<Midi_voice*>& open_voices_r, Moment mom )
42 {
43         for ( int i = 0; i < midi_voice_p_array_.size(); i++ )
44                 if ( midi_voice_p_array_[ i ]->begin_mom() == mom ) {
45                         tor( DEBUG_ver ) << "open_voices (" << open_voices_r.size() << "): +1\n";
46                         open_voices_r.push( midi_voice_p_array_[ i ] );
47                 }
48 }
49 #endif
50
51 void
52 Midi_track::add_event( Moment mom, Midi_event* midi_event_p )
53 {
54         if ( ! midi_event_p )
55                 return;
56         tcol_l( mom - midi_event_p->mom() )->add_event( midi_event_p );
57 }
58
59 int
60 Midi_track::check_begin_bar_i( Moment now_mom, int open_bar_i )
61 {
62         Moment bar_mom = midi_time_p_->bar_mom();
63         int bar_i = (int)( now_mom / bar_mom ) + 1;
64         if ( bar_i > open_bar_i ) { 
65                 tor( NORMAL_ver ) << '[' << flush; 
66                 return bar_i;
67         }
68         return 0;
69 }
70
71 int
72 Midi_track::check_end_bar_i( Moment now_mom, int open_bar_i )
73 {
74         Moment bar_mom = midi_time_p_->bar_mom();
75         int bar_i = (int)( now_mom / bar_mom ) + 1;
76         if ( bar_i > open_bar_i ) {
77                 tor( NORMAL_ver ) << ']' << flush; 
78                 return bar_i;
79         }
80         return open_bar_i;
81 }
82
83 Moment
84 Midi_track::end_mom()
85 {
86         // heu..
87         Moment mom = 0.0;
88 #ifdef MVOICE_LIST
89         for ( PCursor<Midi_voice*> i( midi_voice_p_list_.top() ); i.ok(); i++ ) 
90                 mom = i->end_mom() >? mom;
91 #else
92         for ( int i = 0; i < midi_voice_p_array_.size(); i++ ) 
93                 mom = midi_voice_p_array_[ i ]->end_mom() >? mom;
94 #endif
95         return mom;
96 }
97
98 Midi_voice*
99 Midi_track::get_free_midi_voice_l( Moment mom )
100 {
101 #ifdef MVOICE_LIST
102         Real f = mom;
103         for ( PCursor<Midi_voice*> i( midi_voice_p_list_.top() ); i.ok(); i++ ) {
104                 Real e = i->end_mom();
105                 if ( i->end_mom() == mom )
106                         return *i;
107         }
108
109         Midi_voice* midi_voice_p = new Midi_voice( mom );
110         Midi_voice* midi_voice_l = midi_voice_p;
111         midi_voice_p_list_.bottom().add( midi_voice_p );
112         return midi_voice_l; 
113 #else
114         Real f = mom;
115         for ( int i = 0; i < midi_voice_p_array_.size(); i++ ) {
116                 Real e = i->end_mom();
117                 if ( midi_voice_p_array_[ i ]->end_mom() == mom )
118                         return midi_voice_p_array_[ i ];
119         }
120
121         Midi_voice* midi_voice_p = new Midi_voice( mom );
122         Midi_voice* midi_voice_l = midi_voice_p;
123         midi_voice_p_array_.push( midi_voice_p );
124         return midi_voice_l; 
125 #endif
126 }
127
128 String
129 Midi_track::name_str()
130 {
131         if ( name_str_.length_i() )
132                 return name_str_;
133         return String( "track" ) + String( number_i_ );
134 }
135
136 Moment
137 Midi_track::next_begin_mom( Moment now_mom )
138 {
139 //      Moment begin_mom = Midi_track::end_mom() + 1;
140         Moment begin_mom = Midi_track::end_mom();
141 #ifdef MVOICE_LIST
142         for ( PCursor<Midi_voice*> i( midi_voice_p_list_.top() ); i.ok(); i++ )
143                 if ( i->begin_mom() > now_mom )
144                         begin_mom = begin_mom <? i->begin_mom();
145 #else
146         for ( int i = 0; i < midi_voice_p_array_.size(); i++ )
147                 if ( midi_voice_p_array_[ i ]->begin_mom() > now_mom )
148                         begin_mom = begin_mom <? midi_voice_p_array_[ i ]->begin_mom();
149 #endif
150         return begin_mom;
151 }
152
153 Moment
154 Midi_track::next_end_mom( Moment now_mom )
155 {
156         Moment end_mom = Midi_track::end_mom();
157 #ifdef MVOICE_LIST
158         for ( PCursor<Midi_voice*> i( midi_voice_p_list_.top() ); i.ok(); i++ ) 
159                 if ( i->end_mom() > now_mom )
160                         end_mom = end_mom <? i->end_mom();
161 #else
162         for ( int i = 0; i < midi_voice_p_array_.size(); i++ ) 
163                 if ( midi_voice_p_array_[ i ]->end_mom() > now_mom )
164                         end_mom = end_mom <? midi_voice_p_array_[ i ]->end_mom();
165 #endif
166         return end_mom;
167 }
168
169 void
170 Midi_track::process()
171 {
172         /* 
173            columns to voices
174         */
175 //      int bar_i = 1;
176         int bar_i = 0;
177 #ifdef TCOL_LIST
178         for ( PCursor<Track_column*> i( tcol_p_list_.top() ); i.ok(); i++ ) {
179                 int begin_bar_i = check_begin_bar_i( i->mom(), bar_i );
180                 if ( begin_bar_i )
181                         tor( NORMAL_ver ) << begin_bar_i << flush; 
182                 while ( i->midi_event_p_list_.size() ) 
183                         get_free_midi_voice_l( i->mom() )->add_event( i->midi_event_p_list_.top().remove_p() );
184                 bar_i = check_end_bar_i( i->mom(), bar_i );
185         }
186 #else
187         for ( int i = 0; i < tcol_p_array_.size(); i++ ) {
188                 Track_column* tcol_l = tcol_p_array_[ i ];
189                 int begin_bar_i = check_begin_bar_i( tcol_l->mom(), bar_i );
190                 if ( begin_bar_i )
191                         tor( NORMAL_ver ) << begin_bar_i << flush; 
192 #ifdef MEVENT_LIST
193                 while ( tcol_l->midi_event_p_list_.size() ) 
194                         get_free_midi_voice_l( tcol_l->mom() )->add_event( tcol_l->midi_event_p_list_.top().remove_p() );
195 #else
196                 // what's efficient here?
197 #if 0           // heu, what's different here?
198                 while ( tcol_l->midi_event_p_array_.size() ) {
199                         get_free_midi_voice_l( tcol_l->mom() )->add_event( tcol_l->midi_event_p_array_[ 0 ] );
200                         tcol_l->midi_event_p_array_.del( 0 );
201                 }
202 #else
203                 for ( int j = 0; j < tcol_l->midi_event_p_array_.size(); j++ ) {
204                         get_free_midi_voice_l( tcol_l->mom() )->add_event( tcol_l->midi_event_p_array_[ j ] );
205                         tcol_l->midi_event_p_array_[ j ] = 0;
206                 }
207                 tcol_l->midi_event_p_array_.clear();
208 #endif 
209 #endif
210                 bar_i = check_end_bar_i( tcol_l->mom(), bar_i );
211         }
212 #endif
213
214         tor( DEBUG_ver ) << "ends: " << endl;
215         int n = 0;
216 #ifdef MVOICE_LIST
217         for ( PCursor<Midi_voice*> i( midi_voice_p_list_.top() ); i.ok(); i++ ) 
218                 tor( VERBOSE_ver ) << "voice " << n++ << ": " << i->end_mom() << endl;
219 #else
220         for ( int i = 0; i < midi_voice_p_array_.size(); i++ ) 
221                 tor( VERBOSE_ver ) << "voice " << n++ << ": " << midi_voice_p_array_[ i ]->end_mom() << endl;
222 #endif
223         tor( DEBUG_ver ) << ":sdne" << endl;
224 }
225
226
227 void
228 Midi_track::output_mudela( Lily_stream& lily_stream_r )
229 {
230         lily_stream_r << name_str() << " = \\melodic{";
231         lily_stream_r.indent();
232         lily_stream_r << "% midi copyright:" << copyright_str_;
233         lily_stream_r.newline();
234         lily_stream_r << "% instrument:" << instrument_str_;
235         lily_stream_r.newline();
236
237 //      int bar_i = 1;
238         int bar_i = 0;
239
240 #ifdef MVOICE_LIST
241         Link_list<Midi_voice*> open_voices;
242 #else
243         Array<Midi_voice*> open_voices;
244 #endif
245         Moment now_mom = 0.0;
246         Real now_f = now_mom;
247         Real begin_f = 0;
248         Real end_f = end_mom();
249         Real then_f;
250
251         /* 
252            now we step through time while writing all voices
253
254            we can only output time slices that have a constant
255            number of open voices; each begin or end of a voice
256            starts or ends a chord or multivoice
257
258            [todo]
259            voice defragmentation/concatenation could make this
260            lost blonder
261         */
262
263         bool start_of_track_bo = true;
264
265         /// ugh, avoid status track 0 full of rests...
266         while ( number_i_ && ( now_mom < end_mom() ) ) {
267                 int begin_bar_i = check_begin_bar_i( now_mom, bar_i );
268                 if ( begin_bar_i )
269                         output_mudela_begin_bar( lily_stream_r, now_mom, begin_bar_i );
270                 add_begin_at( open_voices, now_mom );
271
272                 Moment begin_mom = next_begin_mom( now_mom ); 
273
274                 if ( begin_bar_i )
275                         tor( NORMAL_ver ) << begin_bar_i << flush; 
276
277                 Moment end_mom = next_end_mom( now_mom ); 
278                 Moment then_mom = 0.0;
279                 if ( ( begin_mom > now_mom ) && ( begin_mom < end_mom ) )
280                         then_mom = begin_mom;
281                 else 
282                         then_mom = end_mom;
283
284                 tor( DEBUG_ver ) << "begin: " << begin_mom << " end: " << end_mom << endl;
285                 tor( DEBUG_ver ) << "slice: " << now_mom << ", " << then_mom << endl;
286
287                 now_f = now_mom;
288                 begin_f = begin_mom;
289                 end_f = end_mom;
290                 then_f = then_mom;
291                 
292 // ugh, rests
293 // checking for no open voice does not work for initial rests.
294 // for some reason the voice is open, but does not procuce notes?
295                 if ( open_voices.size() > 1 )
296                         lily_stream_r << "< ";
297                 if ( start_of_track_bo ) {
298                         start_of_track_bo = false;
299                         String str;
300 #ifdef MVOICE_LIST
301                         for ( PCursor<Midi_voice*> i( open_voices.top() ); i.ok(); i++ )
302                                 lily_stream_r << i->mudela_str( now_mom, then_mom, open_voices.size() - 1 );
303 #else
304                         for ( int i = 0; i < open_voices.size(); i++ )
305                                 lily_stream_r << open_voices[ i ]->mudela_str( now_mom, then_mom, open_voices.size() - 1 );
306 #endif
307                         if ( str.length_i() )
308                                 lily_stream_r << str;
309                         else
310                                 output_mudela_rest( lily_stream_r, now_mom, then_mom );
311                 }
312                 else {
313 #ifdef MVOICE_LIST
314                         for ( PCursor<Midi_voice*> i( open_voices.top() ); i.ok(); i++ )
315                                 lily_stream_r << i->mudela_str( now_mom, then_mom, open_voices.size() - 1 );
316 #else
317                         for ( int i = 0; i < open_voices.size(); i++ )
318                                 lily_stream_r << open_voices[ i ]->mudela_str( now_mom, then_mom, open_voices.size() - 1 );
319 #endif
320                         if ( !open_voices.size() )
321                                 output_mudela_rest( lily_stream_r, now_mom, then_mom );
322                 }
323 //              *lily_stream_r.os_p_ << flush;
324                         
325                 if ( open_voices.size() > 1 )
326                         lily_stream_r << "> ";
327                 remove_end_at( open_voices, then_mom );
328
329                 bar_i = check_end_bar_i( now_mom, bar_i );
330
331                 now_mom = then_mom;
332         }
333 //      bar_i++;
334 //      tor( NORMAL_ver ) << '[' << bar_i << ']' << flush; 
335
336         lily_stream_r.tnedni();
337         lily_stream_r << "} % " << name_str();
338         lily_stream_r.newline();
339 }
340
341
342 void
343 Midi_track::output_mudela_begin_bar( Lily_stream& lily_stream_r, Moment now_mom, int bar_i )
344 {
345         Moment bar_mom = midi_time_p_->bar_mom();
346         Moment into_bar_mom = now_mom - Moment( bar_i - 1 ) * bar_mom;
347         if ( bar_i > 1 ) {
348                 if ( !into_bar_mom )
349                         lily_stream_r << "|";
350                 lily_stream_r.newline();
351         }
352         lily_stream_r << "% " << String_convert::i2dec_str( bar_i, 0, ' ' );
353         if ( into_bar_mom )
354                 lily_stream_r << ":" << Duration_convert::dur2_str( Duration_convert::mom2_dur( into_bar_mom ) );
355         lily_stream_r.newline();
356 }
357
358
359 void 
360 Midi_track::output_mudela_rest( Lily_stream& lily_stream_r, Moment begin_mom, Moment end_mom )
361 {
362         Moment bar_mom = midi_time_p_->bar_mom();
363         Moment now_mom = begin_mom;
364
365         int begin_bar_i = (int)( now_mom / bar_mom ) + 1; 
366         int end_bar_i = (int)( end_mom / bar_mom ) + 1;
367
368         if ( end_bar_i == begin_bar_i ) {
369                 output_mudela_rest_remain( lily_stream_r, end_mom - begin_mom );
370                 return;
371         }
372
373         // multiple bars involved
374         int bar_i = (int)( now_mom / bar_mom ) + 1;
375
376         //fill current bar
377         Moment begin_bar_mom = Moment( begin_bar_i - 1 ) * bar_mom;
378         if ( now_mom > begin_bar_mom ) {
379                 int next_bar_i = (int)( now_mom / bar_mom ) + 2; 
380                 Moment next_bar_mom = Moment( next_bar_i - 1 ) * bar_mom;
381                 assert( next_bar_mom <= end_mom );
382
383                 Moment remain_mom = next_bar_mom - now_mom;
384                 if ( remain_mom > Moment( 0 ) ) {
385                         output_mudela_rest_remain( lily_stream_r, remain_mom );
386                         now_mom += remain_mom;
387                 }
388
389                 bar_i = check_end_bar_i( now_mom, bar_i );
390         }
391
392         // fill whole bars
393         int count_i = end_bar_i - bar_i;
394         for ( int i = 0; i < count_i; i++ ) {
395                 int begin_bar_i = check_begin_bar_i( now_mom, bar_i );
396                 if ( begin_bar_i )
397                         output_mudela_begin_bar( lily_stream_r, now_mom, begin_bar_i );
398                 lily_stream_r << "r1 ";
399 //              *lily_stream_r.os_p_ << flush;
400                 if ( begin_bar_i )
401                         tor( NORMAL_ver ) << begin_bar_i << flush; 
402                 bar_i = check_end_bar_i( now_mom, bar_i );
403                 now_mom += bar_mom;
404         }
405
406         // use "int i" here, and gcc 2.7.2 hits internal compiler error
407         int ii = check_begin_bar_i( now_mom, bar_i );
408         if ( ii )
409                 output_mudela_begin_bar( lily_stream_r, now_mom, ii );
410
411 //      bar_i = check_end_bar_i( now_mom, bar_i );
412
413         Moment remain_mom = end_mom - Moment( end_bar_i - 1 ) * bar_mom;
414         if ( remain_mom > Moment( 0 ) ) {
415                 output_mudela_rest_remain( lily_stream_r, remain_mom );
416                 now_mom += remain_mom;
417         }
418         assert( now_mom == end_mom );
419 }
420
421 void
422 Midi_track::output_mudela_rest_remain( Lily_stream& lily_stream_r, Moment mom )
423 {
424         if ( Duration_convert::no_quantify_b_s ) {
425                 Duration dur = Duration_convert::mom2_dur( mom );
426                 lily_stream_r << "r" << dur.str() << " ";
427 //              assert( mom == dur.mom() );
428                 assert( mom == dur.length() );
429                 return;
430         }
431                 
432         Duration dur = Duration_convert::mom2standardised_dur( mom );
433         if ( dur.type_i_ )
434                 lily_stream_r << "r" << dur.str() << " ";
435 }
436
437
438 #ifdef MVOICE_LIST
439 void
440 Midi_track::remove_end_at( Link_list<Midi_voice*>& open_voices_r, Moment mom )
441 {
442         for ( PCursor<Midi_voice*> i( open_voices_r.top() ); i.ok(); i++ )
443                 if ( i->end_mom() <= mom ) {
444                         tor( DEBUG_ver ) << "open_voices (" << open_voices_r.size() << "): -1\n";
445                         i.remove_p();
446                         if ( !i.ok() )
447                                 break;
448                 }
449 }
450 #else
451 void
452 Midi_track::remove_end_at( Array<Midi_voice*>& open_voices_r, Moment mom )
453 {
454         for ( int i = 0; i < open_voices_r.size(); i++ )
455                 if ( midi_voice_p_array_[ i ]->end_mom() <= mom ) {
456                         tor( DEBUG_ver ) << "open_voices (" << open_voices_r.size() << "): -1\n";
457                         open_voices_r[ i ] = 0;
458 //                      open_voices_r.del( i-- );
459                         open_voices_r.del( i );
460                 }
461 }
462 #endif
463
464 void
465 Midi_track::set_tempo( int useconds_per_4_i )
466 {
467         delete midi_tempo_p_;
468         midi_tempo_p_ = new Midi_tempo( useconds_per_4_i );
469 }
470
471 void
472 Midi_track::set_time( int num_i, int den_i, int clocks_i, int count_32_i )
473 {
474         delete midi_time_p_;
475         midi_time_p_ = new Midi_time( num_i, den_i, clocks_i, count_32_i );
476 }
477
478 Track_column*
479 Midi_track::tcol_l( Moment mom )
480 {
481 #ifdef TCOL_LIST
482         for ( PCursor<Track_column*> i( tcol_p_list_.top() ); i.ok(); i++ ) {
483                 if ( i->mom() > mom ) { //not used, let's use array!
484 //                      assert( 0 );
485 // 97-07-21; it's used now! cannot use array
486                         Track_column* tcol_p = new Track_column( mom );
487                         i.insert( tcol_p );
488                         return tcol_p;
489                 }
490                 if ( i->mom() == mom )
491                         return *i;
492         }
493
494         Track_column* tcol_p = new Track_column( mom );
495         tcol_p_list_.bottom().add( tcol_p );
496         return tcol_p;
497 #elif 0
498         for ( int i = 0; i < tcol_p_array_.size(); i++ )
499                 if ( tcol_p_array_[ i ]->mom() == mom )
500                         return tcol_p_array_[ i ];
501
502         Track_column* tcol_p = new Track_column( mom );
503         tcol_p_array_.push( tcol_p );
504         return tcol_p;
505 #else
506         /*
507          as "insert" is never called, the right column will
508          always be found, unless mom > tcol_p_array[ i ]->mom().
509          */
510         int upper_i = max( 0, tcol_p_array_.size() - 1 );
511         int lower_i = 0;
512         int i = upper_i;
513         while ( 1 ) {
514                 Moment i_mom = tcol_p_array_[ i ]->mom();
515                 if ( i_mom == mom )
516                         return tcol_p_array_[ i ];
517                 if ( mom < i_mom )
518                         upper_i = i;
519                 else
520                         lower_i = i;
521                 if ( ( upper_i == lower_i ) || ( i == tcol_p_array_.size() - 1 ) ) {
522 // huh?                 assert ( upper_i == tcol_p_array_.size() );
523                         Track_column* tcol_p = new Track_column( mom );
524                         tcol_p_array_.push( tcol_p );
525                         return tcol_p;
526                 }
527                 i = ( upper_i + lower_i + 1 ) / 2;
528         }
529         assert( 0 );
530         return 0;
531 #endif
532 }
533