X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fpart-combine-music-iterator.cc;h=3e0fccef6e5e559ac7d274ba85ff1ee8931c3938;hb=38655bc28e73a0b218ba1582bdb84bab0fdfec4b;hp=9c924c12842a8fafc366c93e330e416f06b24d75;hpb=6798fd9b3d52cbed28b24dbac067bff9af406230;p=lilypond.git diff --git a/lily/part-combine-music-iterator.cc b/lily/part-combine-music-iterator.cc index 9c924c1284..3e0fccef6e 100644 --- a/lily/part-combine-music-iterator.cc +++ b/lily/part-combine-music-iterator.cc @@ -3,7 +3,7 @@ source file of the GNU LilyPond music typesetter - (c) 2000 Jan Nieuwenhuizen + (c) 2000--2002 Jan Nieuwenhuizen */ #include "part-combine-music.hh" @@ -11,13 +11,11 @@ #include "translator-group.hh" #include "musical-request.hh" #include "music-sequence.hh" +#include "lily-guile.hh" #include "warn.hh" Part_combine_music_iterator::Part_combine_music_iterator () { - combined_b_ = true; - - now_ = 0; first_iter_p_ = 0; second_iter_p_ = 0; first_until_ = 0; @@ -30,38 +28,41 @@ Part_combine_music_iterator::~Part_combine_music_iterator () delete first_iter_p_; } +Part_combine_music_iterator::Part_combine_music_iterator (Part_combine_music_iterator const &src) + : Music_iterator (src) +{ + second_iter_p_ = src.second_iter_p_ ? src.second_iter_p_->clone () : 0; + first_iter_p_ = src.first_iter_p_ ? src.first_iter_p_->clone () : 0; + + first_until_ = src.first_until_; + second_until_ = src.second_until_; + state_ = src.state_; + suffix_ = src.suffix_; +} + Moment -Part_combine_music_iterator::next_moment () const +Part_combine_music_iterator::pending_moment () const { - Moment first_next = infinity_mom; + Moment p; + p.set_infinite (1); if (first_iter_p_->ok ()) - first_next = first_iter_p_->next_moment (); - Moment second_next = infinity_mom; + p = p pending_moment (); + if (second_iter_p_->ok ()) - second_next = second_iter_p_->next_moment (); - if (first_next == infinity_mom && second_next == infinity_mom) - return 0; - return first_next pending_moment (); + return p; } bool Part_combine_music_iterator::ok () const { - //hmm return first_iter_p_->ok () || second_iter_p_->ok (); } -void -Part_combine_music_iterator::do_print () const -{ - first_iter_p_->print (); - second_iter_p_->print (); -} - void Part_combine_music_iterator::construct_children () { - Part_combine_music const * m = dynamic_cast (music_l_); + Part_combine_music const * m = dynamic_cast (music_l ()); first_iter_p_ = get_iterator_p (m->first_l ()); second_iter_p_ = get_iterator_p (m->second_l ()); @@ -84,7 +85,7 @@ Part_combine_music_iterator::change_to (Music_iterator *it, String to_type, If \translator Staff = bass, then look for Staff = * */ - while (current && current->type_str_ != to_type) + while (current && current->type_str_ != to_type) { last = current; current = current->daddy_trans_l_; @@ -102,7 +103,7 @@ Part_combine_music_iterator::change_to (Music_iterator *it, String to_type, Translator_group * dest = it->report_to_l ()->find_create_translator_l (to_type, to_id); current->remove_translator_p (last); - dest->add_group_translator (last); + dest->add_used_group_translator (last); } else { @@ -118,374 +119,306 @@ Part_combine_music_iterator::change_to (Music_iterator *it, String to_type, error (_f ("none of these in my family: `%s'", to_id.ch_C ())); } -#if 0 -Pitch_interrogate_req* first_spanish_inquisition; // nobody expects it -Pitch_interrogate_req* second_spanish_inquisition; // won't strike twice - -Rhythm_interrogate_req* first_rhythmic_inquisition; -Rhythm_interrogate_req* second_rhythmic_inquisition; -void -Part_combine_music_iterator::do_process_and_next (Moment m) +// SCM*, moet / kan dat niet met set_x ofzo? +static void +get_music_info (Moment m, Music_iterator* iter, SCM *pitches, SCM *durations) { - Part_combine_music const * p = dynamic_cast (music_l_); - - now_ = next_moment (); - - /* - Hmm, shouldn't we check per iterator if next_moment < m? - */ - if (first_iter_p_->ok ()) - first_iter_p_->process_and_next (m); + if (iter->ok ()) + { + for (SCM i = iter->get_music (m); gh_pair_p (i); i = ly_cdr (i)) + { + Music *m = unsmob_music (ly_car (i)); + if (Melodic_req *r = dynamic_cast (m)) + *pitches = gh_cons (r->get_mus_property ("pitch"), *pitches); + if (Rhythmic_req *r = dynamic_cast (m)) + { + SCM d = r->get_mus_property ("duration"); + if (d == SCM_EOL) + r->origin ()->warning ("Rhythmic_req has no duration\n"); + else + // *durations = gh_cons (r->get_mus_property ("duration"), *durations); + *durations = gh_cons (d, *durations); + } + } + } +} - if (second_iter_p_->ok ()) - second_iter_p_->process_and_next (m); - - Music_iterator::do_process_and_next (m); - - /* - TODO: - - * Maybe we need a Skip_engraver? +int +Part_combine_music_iterator::get_state (Moment) +{ + int state = UNKNOWN; + Part_combine_music const *p = dynamic_cast (music_l ()); - (check): can this all be handled now? + String w = ly_scm2string (p->get_mus_property ("what")); - Maybe different modes exist? - - * Wind instruments (Flute I/II) - * Hymnals: - - - Rules for Hymnals/SATB (John Henckel ): - - 1. if S and A differ by less than a third, the stems should be up/down. - 2. else if S and A have different values, the stems should be up/down. - 3. else if A sings "la" or higher, both S and A stems should be down. - 4. else both stems should be up - - * This may get really tricky: combining voices/staffs: string instruments - - */ - - if (!first_spanish_inquisition) - first_spanish_inquisition = new Pitch_interrogate_req; - first_iter_p_->try_music (first_spanish_inquisition); - if (!second_spanish_inquisition) - second_spanish_inquisition = new Pitch_interrogate_req; - second_iter_p_->try_music (second_spanish_inquisition); - - Array* first_pitches = &first_spanish_inquisition->pitch_arr_; - Array* second_pitches = &second_spanish_inquisition->pitch_arr_; - - if (!first_rhythmic_inquisition) - first_rhythmic_inquisition = new Rhythm_interrogate_req; - first_iter_p_->try_music (first_rhythmic_inquisition); - - if (!second_rhythmic_inquisition) - second_rhythmic_inquisition = new Rhythm_interrogate_req; - second_iter_p_->try_music (second_rhythmic_inquisition); - - Array* first_durations = &first_rhythmic_inquisition->duration_arr_; - Array* second_durations = &second_rhythmic_inquisition->duration_arr_; - - SCM interval = SCM_BOOL_F; - if (first_pitches->size () && second_pitches->size ()) - { - first_pitches->sort (Musical_pitch::compare); - second_pitches->sort (Musical_pitch::compare); - interval = gh_int2scm (first_pitches->top ().steps () - - (*second_pitches)[0].steps ()); - } - if (first_durations->size ()) - { - first_durations->sort (Duration::compare); - Moment new_until = now_ + first_durations->top ().length_mom (); - if (new_until > first_until_) - first_until_ = new_until; - } - - if (second_durations->size ()) - { - second_durations->sort (Duration::compare); - Moment new_until = now_ + second_durations->top ().length_mom (); - if (new_until > second_until_) - second_until_ = new_until; - } - - Translator_group * fir = first_iter_p_->report_to_l (); - Translator_group * sir = second_iter_p_->report_to_l (); - - bool solo_b = (first_pitches->empty () != second_pitches->empty ()) - && !(first_until_ > now_ && second_until_ > now_); - - bool unirhythm_b = !solo_b && !compare (first_durations, second_durations); - bool unison_b = unirhythm_b && !first_pitches->empty () - &&!compare (first_pitches, second_pitches); - bool unisilence_b = unirhythm_b && first_pitches->empty (); + Translator_group *first_translator = first_iter_p_->report_to_l ()->find_create_translator_l (w, "one" + suffix_); - Translator_group * fd = fir->find_create_translator_l (p->what_str_, "one"); - Translator_group * sd = sir->find_create_translator_l (p->what_str_, "two"); + SCM s = first_translator->get_property ("changeMoment"); + if (!gh_pair_p (s)) + return state; - bool split_interval_b = false; - if (gh_number_p (interval)) - { - SCM s = fd->get_property (ly_symbol2scm ("splitInterval")); - int i = gh_scm2int (interval); - if (gh_pair_p (s) - && gh_number_p (gh_car (s)) - && gh_number_p (gh_cdr (s)) - && i >= gh_scm2int (gh_car (s)) - && i <= gh_scm2int (gh_cdr (s))) - split_interval_b = true; - } - - /* - Hmm, maybe we should set/check combined_b_ against - - first_iter_p_->report_to_l () == second_iter_p_->report_to_l () - - ? - */ - - String to_id = combined_b_ ? "one" : "two"; - if ((!unirhythm_b && combined_b_) - || (split_interval_b && combined_b_) - || (solo_b && combined_b_) - /*|| (unisilence_b && combined_b_) */ - || ((unirhythm_b || unison_b || unisilence_b) - && !combined_b_ && !split_interval_b && !solo_b)) - { - combined_b_ = !combined_b_; - to_id = combined_b_ ? "one" : "two"; - change_to (second_iter_p_, p->what_str_, to_id); - } - - if (!combined_b_) - sir = second_iter_p_->report_to_l (); - - SCM b = unirhythm_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("unirhythm", b); - sd->set_property ("unirhythm", b); - - b = split_interval_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("split-interval", b); - sd->set_property ("split-interval", b); - - b = unisilence_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("unisilence", b); - sd->set_property ("unisilence", b); - - b = unison_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("unison", b); - sd->set_property ("unison", b); + Moment change_mom = *unsmob_moment (ly_car (s)); + Moment diff_mom = *unsmob_moment (ly_cdr (s)); + + Moment now = pending_moment (); - b = solo_b ? SCM_BOOL_T : SCM_BOOL_F; - if (first_pitches->size ()) + if (!now.main_part_.mod_rat (change_mom.main_part_)) { - fd->set_property ("solo", b); - sd->set_property ("solo", SCM_BOOL_F); + SCM interval = SCM_BOOL_F; + if (first_until_ < now) + first_until_ = now; + if (second_until_ < now) + second_until_ = now; + + Moment first_mom = first_until_; + Moment second_mom = second_until_; + Moment diff_until = diff_mom + now; + + bool first = true; + Music_iterator *first_iter = first_iter_p_->clone (); + Music_iterator *second_iter = second_iter_p_->clone (); + + Moment last_pending (-1); + Moment pending = now; + while (now < diff_until + && (first_iter->ok () || second_iter->ok ()) + + // urg, this is a hack, haven't caught this case yet + && (pending != last_pending)) + { + if (!second_iter->ok ()) + pending = first_iter->pending_moment (); + else if (!first_iter->ok ()) + pending = second_iter->pending_moment (); + else + pending = first_iter->pending_moment () pending_moment (); + last_pending = pending; + + SCM first_pitches = SCM_EOL; + SCM first_durations = SCM_EOL; + get_music_info (pending, first_iter, + &first_pitches, &first_durations); + + SCM second_pitches = SCM_EOL; + SCM second_durations = SCM_EOL; + get_music_info (pending, second_iter, + &second_pitches, &second_durations); + + if (first_pitches != SCM_EOL && second_pitches != SCM_EOL) + { + scm_sort_list_x (first_pitches, + scm_primitive_eval (ly_symbol2scm ("Pitch::less_p"))); + scm_sort_list_x (second_pitches, + scm_primitive_eval (ly_symbol2scm ("Pitch::less_p"))); + + interval = gh_int2scm (unsmob_pitch (ly_car (first_pitches))->steps () + - unsmob_pitch (ly_car (scm_last_pair (second_pitches)))->steps ()); + } + + if (first_durations != SCM_EOL) + { + scm_sort_list_x (first_durations, + scm_primitive_eval (ly_symbol2scm ("Duration::less_p"))); + first_mom += unsmob_duration (ly_car (first_durations))->length_mom (); + } + + if (second_durations != SCM_EOL) + { + scm_sort_list_x (second_durations, + scm_primitive_eval (ly_symbol2scm ("Duration::less_p"))); + second_mom += unsmob_duration (ly_car (second_durations))->length_mom (); + } + + if (first_pitches != SCM_EOL && second_pitches == SCM_EOL + && ! (second_until_ > now)) + { + state |= UNRELATED; + state &= ~UNISILENCE; + if (! (state & ~ (UNRELATED | SOLO1 | UNISILENCE))) + state |= SOLO1; + } + else + state &= ~SOLO1; + + if (first_pitches == SCM_EOL && second_pitches != SCM_EOL + && ! (first_until_ > now)) + { + state |= UNRELATED; + state &= ~UNISILENCE; + if (! (state & ~ (UNRELATED | SOLO2 | UNISILENCE))) + state |= SOLO2; + } + else + state &= ~SOLO2; + + if (gh_equal_p (first_durations, second_durations)) + { + state &= ~UNISILENCE; + if (! (state & ~ (UNIRHYTHM | UNISON))) + state |= UNIRHYTHM; + } + else + state &= ~ (UNIRHYTHM | UNISILENCE); + + if (first_pitches != SCM_EOL + && gh_equal_p (first_pitches, second_pitches)) + { + state &= ~UNISILENCE; + if (! (state & ~ (UNIRHYTHM | UNISON))) + state |= UNISON; + } + else + state &= ~ (UNISON); + + if (first_pitches == SCM_EOL && second_pitches == SCM_EOL) + { + if (! (state & ~ (UNIRHYTHM | UNISILENCE))) + state |= UNISILENCE; + } + else if (!state) + state |= UNRELATED; + else + state &= ~ (UNISILENCE); + + if (gh_number_p (interval)) + { + SCM s = first_translator->get_property ("splitInterval"); + int i = gh_scm2int (interval); + if (gh_pair_p (s) + && gh_number_p (ly_car (s)) + && gh_number_p (ly_cdr (s)) + && i >= gh_scm2int (ly_car (s)) + && i <= gh_scm2int (ly_cdr (s))) + { + if (! (state & ~ (SPLIT_INTERVAL | UNIRHYTHM | UNISON))) + state |= SPLIT_INTERVAL; + } + else + state &= ~ (SPLIT_INTERVAL); + } + + if (first && first_pitches != SCM_EOL) + first_until_ = first_mom; + if (first && second_pitches != SCM_EOL) + second_until_ = second_mom; + first = false; + + if (first_iter->ok ()) + first_iter->skip (pending); + if (second_iter->ok ()) + second_iter->skip (pending); + now = pending; + } + delete first_iter; + delete second_iter; } + return state; +} - if (second_pitches->size ()) - { - fd->set_property ("solo", SCM_BOOL_F); - sd->set_property ("solo", b); - } +static Span_req* abort_req = NULL; - first_pitches->clear (); - second_pitches->clear (); - first_durations->clear (); - second_durations->clear (); -} -#else void -Part_combine_music_iterator::do_process_and_next (Moment m) +Part_combine_music_iterator::process (Moment m) { - Part_combine_music const * p = dynamic_cast (music_l_); - - /* - Huh? - */ - // now_ = next_moment (); /* TODO: + - Use three named contexts (be it Thread or Voice): one, two, solo. + Let user pre-set (pushproperty) stem direction, remove + dynamic-engraver, and such. - * Maybe we need a Skip_engraver? - - (check): can this all be handled now? - - Maybe different modes exist? - - * Wind instruments (Flute I/II) - * Hymnals: - - - Rules for Hymnals/SATB (John Henckel ): - - 1. if S and A differ by less than a third, the stems should be up/down. - 2. else if S and A have different values, the stems should be up/down. - 3. else if A sings "la" or higher, both S and A stems should be down. - 4. else both stems should be up - - * This may get really tricky: combining voices/staffs: string instruments + **** Tried this, but won't work: + Consider thread switching: threads "one", "two" and "both". + User can't pre-set the (most important) stem direction at + thread level! */ - -#if 1 - Music *first_music; - Music *second_music; - if (first_iter_p_->ok ()) - first_music = first_iter_p_->get_music (); - - if (second_iter_p_->ok ()) - second_music = second_iter_p_->get_music (); -#endif - - Array first_pitches; - Array first_durations; - if (Music_sequence* m = dynamic_cast (first_music)) - { - for (SCM s = m->music_list (); gh_pair_p (s); s = gh_cdr (s)) - { - Music *u = unsmob_music (gh_car (s)); - if (Melodic_req *r = dynamic_cast (u)) - first_pitches.push (r->pitch_); - if (Rhythmic_req *r = dynamic_cast (u)) - first_durations.push (r->duration_); - } - } - - Array second_pitches; - Array second_durations; - if (Music_sequence* m = dynamic_cast (second_music)) - { - for (SCM s = m->music_list (); gh_pair_p (s); s = gh_cdr (s)) - { - Music *u = unsmob_music (gh_car (s)); - if (Melodic_req *r = dynamic_cast (u)) - second_pitches.push (r->pitch_); - if (Rhythmic_req *r = dynamic_cast (u)) - second_durations.push (r->duration_); - } - } + + if (suffix_.empty_b ()) + suffix_ = first_iter_p_->report_to_l ()->daddy_trans_l_->id_str_.cut_str (3, INT_MAX); - SCM interval = SCM_BOOL_F; - if (first_pitches.size () && second_pitches.size ()) - { - first_pitches.sort (Musical_pitch::compare); - second_pitches.sort (Musical_pitch::compare); - interval = gh_int2scm (first_pitches.top ().steps () - - second_pitches[0].steps ()); - } - if (first_durations.size ()) - { - first_durations.sort (Duration::compare); - Moment new_until = now_ + first_durations.top ().length_mom (); - if (new_until > first_until_) - first_until_ = new_until; - } - - if (second_durations.size ()) - { - second_durations.sort (Duration::compare); - Moment new_until = now_ + second_durations.top ().length_mom (); - if (new_until > second_until_) - second_until_ = new_until; - } - - Translator_group * fir = first_iter_p_->report_to_l (); - Translator_group * sir = second_iter_p_->report_to_l (); + int state = get_state (m); + if (state) + state_ = state; + else + state = state_; + + Part_combine_music const *p = dynamic_cast (music_l ()); - bool solo_b = (first_pitches.empty () != second_pitches.empty ()) - && !(first_until_ > now_ && second_until_ > now_); - bool unirhythm_b = !solo_b && !compare (&first_durations, &second_durations); - bool unison_b = unirhythm_b && !first_pitches.empty () - &&!compare (&first_pitches, &second_pitches); - bool unisilence_b = unirhythm_b && first_pitches.empty (); + bool previously_combined_b = first_iter_p_->report_to_l ()->daddy_trans_l_ + == second_iter_p_->report_to_l ()->daddy_trans_l_; - Translator_group * fd = fir->find_create_translator_l (p->what_str_, "one"); - Translator_group * sd = sir->find_create_translator_l (p->what_str_, "two"); + bool combine_b = previously_combined_b; - bool split_interval_b = false; - if (gh_number_p (interval)) - { - SCM s = fd->get_property (ly_symbol2scm ("splitInterval")); - int i = gh_scm2int (interval); - if (gh_pair_p (s) - && gh_number_p (gh_car (s)) - && gh_number_p (gh_cdr (s)) - && i >= gh_scm2int (gh_car (s)) - && i <= gh_scm2int (gh_cdr (s))) - split_interval_b = true; - } + if (! (state & UNIRHYTHM) + || (state & SPLIT_INTERVAL) + || (state & (SOLO1 | SOLO2))) + combine_b = false; + else if (state & (UNIRHYTHM | UNISILENCE)) + combine_b = true; /* - URG, dememberme + When combining, abort all running spanners */ - combined_b_ = first_iter_p_->report_to_l ()->daddy_trans_l_ - == second_iter_p_->report_to_l ()->daddy_trans_l_; - String to_id = combined_b_ ? "one" : "two"; - if ((!unirhythm_b && combined_b_) - || (split_interval_b && combined_b_) - || (solo_b && combined_b_) - /*|| (unisilence_b && combined_b_) */ - || ((unirhythm_b || unison_b || unisilence_b) - && !combined_b_ && !split_interval_b && !solo_b)) + if (!abort_req) { - combined_b_ = !combined_b_; - to_id = combined_b_ ? "one" : "two"; - change_to (second_iter_p_, p->what_str_, to_id); + abort_req = new Span_req; + abort_req->set_mus_property ("span-type", ly_str02scm ("abort")); } - - if (!combined_b_) - sir = second_iter_p_->report_to_l (); - - SCM b = unirhythm_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("unirhythm", b); - sd->set_property ("unirhythm", b); - - b = split_interval_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("split-interval", b); - sd->set_property ("split-interval", b); - - b = unisilence_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("unisilence", b); - sd->set_property ("unisilence", b); - - b = unison_b ? SCM_BOOL_T : SCM_BOOL_F; - fd->set_property ("unison", b); - sd->set_property ("unison", b); - - b = solo_b ? SCM_BOOL_T : SCM_BOOL_F; - if (first_pitches.size ()) + + if (combine_b && combine_b != previously_combined_b) { - fd->set_property ("solo", b); - sd->set_property ("solo", SCM_BOOL_F); - } + if (second_iter_p_ && second_iter_p_->ok ()) + second_iter_p_->try_music (abort_req); + } + String w = ly_scm2string (p->get_mus_property ("what")); + if (combine_b != previously_combined_b) + change_to (second_iter_p_, w, (combine_b ? "one" : "two") + + suffix_); + + Translator_group *first_translator = first_iter_p_->report_to_l ()->find_create_translator_l (w, "one" + suffix_); + Translator_group *second_translator = second_iter_p_->report_to_l ()->find_create_translator_l (w, "two" + suffix_); + - if (second_pitches.size ()) - { - fd->set_property ("solo", SCM_BOOL_F); - sd->set_property ("solo", b); - } + /* Hmm */ + first_translator->set_property ("combineParts", SCM_BOOL_T); + second_translator ->set_property ("combineParts", SCM_BOOL_T); + + + /* hmm */ + SCM b = (state & UNIRHYTHM) ? SCM_BOOL_T : SCM_BOOL_F; + first_translator->set_property ("unirhythm", b); + second_translator->set_property ("unirhythm", b); + + b = (state & SPLIT_INTERVAL) ? SCM_BOOL_T : SCM_BOOL_F; + first_translator->set_property ("split-interval", b); + second_translator->set_property ("split-interval", b); + + b = (state & UNISILENCE) ? SCM_BOOL_T : SCM_BOOL_F; + first_translator->set_property ("unisilence", b); + second_translator->set_property ("unisilence", b); + + // difference in definition... + //b = ((state & UNISON) ? SCM_BOOL_T : SCM_BOOL_F; + b = ((state & UNISON) && (state & UNIRHYTHM)) ? SCM_BOOL_T : SCM_BOOL_F; + first_translator->set_property ("unison", b); + second_translator->set_property ("unison", b); + + SCM b1 = (state & SOLO1) ? SCM_BOOL_T : SCM_BOOL_F; + SCM b2 = (state & SOLO2) ? SCM_BOOL_T : SCM_BOOL_F; + first_translator->set_property ("solo", b1); + second_translator->set_property ("solo", b2); - /* - Hmm, shouldn't we check per iterator if next_moment < m? - */ if (first_iter_p_->ok ()) - first_iter_p_->process_and_next (m); + first_iter_p_->process (m); if (second_iter_p_->ok ()) - second_iter_p_->process_and_next (m); - - Music_iterator::do_process_and_next (m); - now_ = next_moment (); + second_iter_p_->process (m); } -#endif Music_iterator* Part_combine_music_iterator::try_music_in_children (Music *m) const @@ -497,3 +430,16 @@ Part_combine_music_iterator::try_music_in_children (Music *m) const return second_iter_p_->try_music (m); } + +SCM +Part_combine_music_iterator::get_music (Moment m)const +{ + SCM s = SCM_EOL; + if (first_iter_p_) + s = gh_append2 (s,first_iter_p_->get_music (m)); + if (second_iter_p_) + s = gh_append2 (second_iter_p_->get_music (m),s); + return s; +} + +IMPLEMENT_CTOR_CALLBACK (Part_combine_music_iterator);