]> git.donarmstrong.com Git - lilypond.git/blob - midi2ly/mudela-staff.cc
patch::: 1.3.76.jcn1
[lilypond.git] / midi2ly / mudela-staff.cc
1 //
2 // mudela-staff.cc -- implement Mudela_staff
3 //
4 // copyright 1997 Jan Nieuwenhuizen <janneke@gnu.org>
5
6 #include <assert.h>
7 #include <ctype.h>
8 #include "rational.hh"
9 #include "duration-convert.hh"
10 #include "string-convert.hh"
11 #include "midi2ly-proto.hh"
12 #include "midi2ly-global.hh"
13 #include "mudela-column.hh"
14 #include "mudela-item.hh"
15 #include "mudela-staff.hh"
16 #include "mudela-stream.hh"
17 #include "mudela-voice.hh"
18 #include "mudela-score.hh"
19
20 #include "killing-cons.tcc"
21
22 extern Mudela_score* mudela_score_l_g;
23
24 Mudela_staff::Mudela_staff (int number_i, String copyright_str, String track_name_str, String instrument_str)
25 {
26   number_i_ = number_i;
27   copyright_str_ = copyright_str;
28   instrument_str_ = instrument_str;
29   name_str_ = track_name_str;
30   mudela_key_l_ = 0;
31   mudela_time_signature_l_ = 0;
32   mudela_tempo_l_ = 0;
33 }
34
35 void
36 Mudela_staff::add_item (Mudela_item* mudela_item_p)
37 {
38   mudela_item_p_list_.append (new Killing_cons <Mudela_item> (mudela_item_p, 0));
39   if (mudela_item_p->mudela_column_l_)
40     mudela_item_p->mudela_column_l_->add_item (mudela_item_p);
41 }
42 /**
43    Walk ITEMS and find voices.  Remove categorised items.
44
45    TODO: 
46    
47      * collect all channels into separate voices. Use chords for sim
48        notes on same channel.
49      * assume voices/assume chords modes.
50    
51  */
52 void
53 Mudela_staff::eat_voice (Cons_list<Mudela_item>& items)
54 {
55   Mudela_voice* voice_p = new Mudela_voice (this);
56   mudela_voice_p_list_.append (new Killing_cons<Mudela_voice> (voice_p, 0));
57
58   //    Rational mom = items.top ()->at_mom ();
59   Rational mom = 0;
60
61   for (Cons<Mudela_item>** pp = &items.head_; *pp;)
62     {
63       Cons<Mudela_item>* i = *pp;
64       if (i->car_->at_mom () > mom)
65         {
66           if (no_rests_b_g && voice_p->last_note_l_)
67             {
68               voice_p->last_note_l_->end_column_l_ = i->car_->mudela_column_l_;
69             }
70           else
71             {
72               /* uh, what about quantisation?  This should probably
73                  use  mom2standardised_dur ()
74                  arg, urg: skip should get duration from start/end columns!
75                 */
76
77               Rational r = i->car_->at_mom () - mom;
78               // ugh, need score
79               Mudela_column* start = mudela_score_l_g->find_column_l (mom);
80               voice_p->add_item (new Mudela_skip (start, r));
81             }
82
83           mom = i->car_->at_mom ();
84           continue;             // unnecessary
85         }
86       
87       Link_array<Mudela_item> now_items;
88       for (Cons<Mudela_item> *cp = i; cp && cp->car_->at_mom () == mom; cp = cp->next_)
89         now_items.push (i->car_);
90
91 #if 0
92       /*
93         Why don't we use <note>, if voice has:
94
95           <note> <key-change>
96
97         we'd get last_item == key_change -> last_note == 0;
98         */
99       Mudela_note * last_note = dynamic_cast<Mudela_note*> (voice_p->last_item_l_);
100 #else
101       /*
102         Not sure, is this better?
103        */
104       Mudela_note * last_note = voice_p->last_note_l_;
105 #endif
106
107       Link_array<Mudela_item> candidates; 
108
109       for (int i=0; last_note && i < now_items.size (); i++)
110         {
111           Mudela_note * now_note = dynamic_cast<Mudela_note*> (now_items[i]);
112           if (now_note && last_note->channel_i_ != now_note->channel_i_)
113             candidates.push (now_note);
114         }
115
116       if (candidates.size())
117         {
118           now_items = candidates;
119         }
120
121       Mudela_item * which = 0;
122       if (now_items.size () > 1)
123         {
124           int mindiff = 100000; // ugh
125           for (int i=0; last_note && i < now_items.size (); i++)
126             {
127               Mudela_note *nt = dynamic_cast<Mudela_note*> (now_items[i]);
128               if (!nt)
129                 continue;
130               int diff = abs (last_note->pitch_i_ - nt->pitch_i_ );
131               if(diff < mindiff)
132                 {
133                   mindiff =  diff;
134                   which = now_items [i];
135                 }
136             }
137
138           if (which && mindiff > 18)            // more than 1.5 octaves apart.  Don't put in same voice.
139             {
140               which =0;
141             }
142         }
143       else if (now_items.size () == 1)
144         which = now_items[0];
145       
146       if (which)
147         {
148           while ((*pp)->car_ != which)
149             pp = &(*pp)->next_;
150       
151           mom += (*pp)->car_->duration_mom ();
152           Cons<Mudela_item>* c = items.remove_cons (pp);
153           voice_p->add_item (c->car_);
154           delete c;
155         }
156       else 
157         {
158           pp = &(*pp)->next_;
159           continue;
160         }
161     }
162 }
163
164 String
165 Mudela_staff::id_str ()
166 {
167   String id (name_str ());
168   char *cp = id.ch_l ();
169   char *end = cp + id.length_i ();
170   for (;cp < end; cp++)
171     {
172       if (!isalpha (*cp))
173         {
174           *cp = 'X';
175         }
176     }
177   return id;
178 }
179
180 String
181 Mudela_staff::name_str ()
182 {
183   if (name_str_.length_i ())
184     return name_str_;
185   return String ("track") + to_str (char ('A' - 1 + number_i_));
186 }
187
188
189
190 void
191 Mudela_staff::output (Mudela_stream& mudela_stream_r)
192 {
193   int c =0;
194   
195   String trackbody = "";
196   for (Cons<Mudela_voice>* i = mudela_voice_p_list_.head_; i; i = i->next_)
197     {
198       String voicename = id_str () + "voice" + to_str (char (c + 'A'));
199       
200       mudela_stream_r << voicename << " = \\notes ";
201
202       trackbody += "\\"  + voicename + "\n";
203
204       mudela_stream_r << '\n';
205       i->car_->output (mudela_stream_r);
206       c++;      
207     }
208
209   mudela_stream_r << _ ("% MIDI copyright:") << copyright_str_ << '\n';
210   mudela_stream_r << _ ("% MIDI instrument:") << instrument_str_ << '\n';
211   mudela_stream_r << id_str () << " = ";
212   mudela_stream_r << "<\n " << trackbody << " >\n";
213
214   mudela_stream_r << " % " << name_str () << '\n';
215 }
216
217 void
218 Mudela_staff::output_mudela_begin_bar (Mudela_stream& mudela_stream_r, Rational now_mom, int bar_i)
219 {
220   Rational bar_mom = mudela_time_signature_l_->bar_mom ();
221   Rational into_bar_mom = now_mom - Rational (bar_i - 1) * bar_mom;
222   if (bar_i > 1)
223     {
224       if (!into_bar_mom)
225         mudela_stream_r << "|\n";
226     }
227   mudela_stream_r << "% " << String_convert::i2dec_str (bar_i, 0, ' ');
228   if (into_bar_mom)
229     mudela_stream_r << ":" << Duration_convert::dur2_str (Duration_convert::mom2_dur (into_bar_mom));
230   mudela_stream_r << '\n';
231 }
232
233
234 #if 0 // not used for now
235 void
236 Mudela_staff::output_mudela_rest (Mudela_stream& mudela_stream_r, Rational begin_mom, Rational end_mom)
237 {
238   Rational bar_mom = mudela_time_signature_l_->bar_mom ();
239   Rational now_mom = begin_mom;
240
241   int begin_bar_i = (int) (now_mom / bar_mom) + 1;
242   int end_bar_i = (int) (end_mom / bar_mom) + 1;
243
244   if (end_bar_i == begin_bar_i)
245     {
246       output_mudela_rest_remain (mudela_stream_r, end_mom - begin_mom);
247       return;
248     }
249
250   // multiple bars involved
251   int bar_i = (int) (now_mom / bar_mom) + 1;
252
253   //fill current bar
254   Rational begin_bar_mom = Rational (begin_bar_i - 1) * bar_mom;
255   if (now_mom > begin_bar_mom)
256     {
257       int next_bar_i = (int) (now_mom / bar_mom) + 2;
258       Rational next_bar_mom = Rational (next_bar_i - 1) * bar_mom;
259       assert (next_bar_mom <= end_mom);
260
261       Rational remain_mom = next_bar_mom - now_mom;
262       if (remain_mom > Rational (0))
263         {
264           output_mudela_rest_remain (mudela_stream_r, remain_mom);
265           now_mom += remain_mom;
266         }
267
268       bar_i = check_end_bar_i (now_mom, bar_i);
269     }
270
271   // fill whole bars
272   int count_i = end_bar_i - bar_i;
273   for (int i = 0; i < count_i; i++)
274     {
275       int begin_bar_i = check_begin_bar_i (now_mom, bar_i);
276       if (begin_bar_i)
277         output_mudela_begin_bar (mudela_stream_r, now_mom, begin_bar_i);
278       mudela_stream_r << "r1 ";
279       //        *mudela_stream_r.os_p_ << flush;
280       if (begin_bar_i)
281         LOGOUT (NORMAL_ver) << begin_bar_i << flush;
282       bar_i = check_end_bar_i (now_mom, bar_i);
283       now_mom += bar_mom;
284     }
285
286   // use "int i" here, and gcc 2.7.2 hits internal compiler error
287   int ii = check_begin_bar_i (now_mom, bar_i);
288   if (ii)
289     output_mudela_begin_bar (mudela_stream_r, now_mom, ii);
290
291   //    bar_i = check_end_bar_i (now_mom, bar_i);
292
293   Rational remain_mom = end_mom - Rational (end_bar_i - 1) * bar_mom;
294   if (remain_mom > Rational (0))
295     {
296       output_mudela_rest_remain (mudela_stream_r, remain_mom);
297       now_mom += remain_mom;
298     }
299   assert (now_mom == end_mom);
300 }
301
302 void
303 Mudela_staff::output_mudela_rest_remain (Mudela_stream& mudela_stream_r, Rational mom)
304 {
305   if (Duration_convert::no_quantify_b_s)
306     {
307       Duration dur = Duration_convert::mom2_dur (mom);
308       mudela_stream_r << "r" << dur.str () << " ";
309       //        assert (mom == dur.mom ());
310       assert (mom == dur.length ());
311       return;
312     }
313
314   Duration dur = Duration_convert::mom2standardised_dur (mom);
315   if (dur.type_i_>-10)
316     mudela_stream_r << "r" << dur.str () << " ";
317 }
318 #endif
319
320
321 void
322 Mudela_staff::process ()
323 {
324   /*
325      group items into voices
326      */
327
328   assert (mudela_score_l_g);
329   mudela_key_l_ = mudela_score_l_g->mudela_key_l_;
330   mudela_time_signature_l_ = mudela_score_l_g->mudela_time_signature_l_;
331   mudela_tempo_l_ = mudela_score_l_g->mudela_tempo_l_;
332
333   Cons_list<Mudela_item> items;
334   for (Cons<Mudela_item>* i = mudela_item_p_list_.head_; i; i = i->next_)
335     items.append (new Cons<Mudela_item> (i->car_, 0));
336
337   while (items.size_i ())
338     eat_voice (items);
339 }