Hi,

Here's a patch that massively refactors tuplets and percent repeats.

Highlights:
- tuplets are now signalled by start/stop events.
- each percent/slash is signalled by an individual event, whose
repeat-count property tells whether it should be numbered (this moves
some decisions from engraver to iterator)

The diffs of some files are huge, so I'm sending the new files instead.

I think it's a bit out of place to merge percent_repeat and
double_percent_repeat into one engraver. Some next steps that would be
nice are:
- move handling of double percent repeats to slash_engraver (or
possibly to a new double_percent_engraver, but imho they are similar
enough to share engraver)
- handle percent_repeat with start/stop event
- create different event types for percent event, slash event and
double percent event.

These are however not very related to music streams.

Erik
/*
  new-chord-tremolo-engraver.cc -- implement Chord_tremolo_engraver

  source file of the GNU LilyPond music typesetter

  (c) 2000--2006 Han-Wen Nienhuys <[EMAIL PROTECTED]>, Erik Sandberg <[EMAIL 
PROTECTED]>
*/


#include "score-engraver.hh"

#include "bar-line.hh"
#include "global-context.hh"
#include "international.hh"
#include "item.hh"
#include "misc.hh"
#include "percent-repeat-iterator.hh"
#include "repeated-music.hh"
#include "score-context.hh"
#include "side-position-interface.hh"
#include "spanner.hh"
#include "warn.hh"

#include "translator.icc"

/*
* TODO: Create separate Double_percent_repeat_engraver? 
* Or, at least move double percent handling to Slash_repeat_engraver
*/

class Percent_repeat_engraver : public Engraver
{
  void typeset_perc ();
public:
  TRANSLATOR_DECLARATIONS (Percent_repeat_engraver);
  
protected:
  Music *percent_event_;

  /// moment (global time) where percent started.
  Moment stop_mom_;
  Moment start_mom_;

  enum Repeat_sign_type
    {
      UNKNOWN,
      MEASURE,
      DOUBLE_MEASURE,
    };
  Repeat_sign_type repeat_sign_type_;

  Spanner *percent_;
  Spanner *percent_counter_;

protected:
  virtual void finalize ();
  virtual bool try_music (Music *);

  void stop_translation_timestep ();
  void start_translation_timestep ();
  void process_music ();
};

Percent_repeat_engraver::Percent_repeat_engraver ()
{
  percent_ = 0;
  percent_counter_ = 0;

  percent_event_ = 0;
}

bool
Percent_repeat_engraver::try_music (Music *m)
{
  if (m->is_mus_type ("percent-event")
      && !percent_event_)
    {
      Moment body_length = m->get_length ();
      Moment meas_len (robust_scm2moment (get_property ("measureLength"),
                                          Moment (1)));

      if (meas_len == body_length)
      {
        repeat_sign_type_ = MEASURE;
        start_mom_ = now_mom ();
        stop_mom_ = now_mom () + body_length;
        get_global_context ()->add_moment_to_process (stop_mom_);
      }
      else if (Moment (2) * meas_len == body_length)
      {
        repeat_sign_type_ = DOUBLE_MEASURE;
        start_mom_ = now_mom () + meas_len;
        stop_mom_ = now_mom () + body_length; /* never used */
        get_global_context ()->add_moment_to_process (start_mom_);
      }
      else
        return false;

      percent_event_ = m;

      return true;
    }

  return false;
}

void
Percent_repeat_engraver::process_music ()
{
  if (percent_event_ && now_mom ().main_part_ == start_mom_.main_part_)
    {
      if (repeat_sign_type_ == MEASURE)
        {
          if (percent_)
            typeset_perc ();
          percent_ = make_spanner ("PercentRepeat", percent_event_->self_scm 
());

          Grob *col = unsmob_grob (get_property ("currentCommandColumn"));
          percent_->set_bound (LEFT, col);

          SCM count = percent_event_->get_property ("repeat-count");
          if (count != SCM_EOL && to_boolean (get_property 
("countPercentRepeats")))
            {
              percent_counter_
                = make_spanner ("PercentRepeatCounter", 
percent_event_->self_scm ());

              SCM text = scm_number_to_string (count, scm_from_int (10));
              percent_counter_->set_property ("text", text);
              percent_counter_->set_bound (LEFT, col);
              Side_position_interface::add_support (percent_counter_,
                                                    percent_);
              percent_counter_->set_parent (percent_, Y_AXIS);
            }
          else
            percent_counter_ = 0;
        }
      else if (repeat_sign_type_ == DOUBLE_MEASURE)
        {
          Item *double_percent = make_item ("DoublePercentRepeat", 
percent_event_->self_scm ());

          SCM count = percent_event_->get_property ("repeat-count");
          if (count != SCM_EOL
              && to_boolean (get_property ("countPercentRepeats")))
            {
              Item *double_percent_counter = make_item 
("DoublePercentRepeatCounter",
                                                        
percent_event_->self_scm());

              SCM text = scm_number_to_string (count,
                                               scm_from_int (10));
              double_percent_counter->set_property ("text", text);

              Side_position_interface::add_support (double_percent_counter,
                                                    double_percent);
              double_percent_counter->set_parent (double_percent, Y_AXIS);
              double_percent_counter->set_parent (double_percent, X_AXIS);
            }
          
          /* forbid breaks on a % line. Should forbid all breaks, really. */
          context ()->get_score_context ()->set_property ("forbidBreak", 
SCM_BOOL_T);

          /* No more processing needed. */
          repeat_sign_type_ = UNKNOWN;
        }
    }
}

void
Percent_repeat_engraver::finalize ()
{
  if (percent_)
    {
      percent_event_->origin ()->warning (_ ("unterminated percent repeat"));
      percent_->suicide ();
      percent_counter_->suicide();
    }
}

void
Percent_repeat_engraver::typeset_perc ()
{
  if (percent_)
    {
      Grob *col = unsmob_grob (get_property ("currentCommandColumn"));

      percent_->set_bound (RIGHT, col);
      percent_ = 0;

      if (percent_counter_)
        percent_counter_->set_bound (RIGHT, col);
      percent_counter_ = 0;
    }
}

void
Percent_repeat_engraver::start_translation_timestep ()
{
  if (stop_mom_.main_part_ == now_mom ().main_part_)
    {
      if (percent_)
        typeset_perc ();
      percent_event_ = 0;
      repeat_sign_type_ = UNKNOWN;
    }
}

void
Percent_repeat_engraver::stop_translation_timestep ()
{
}

ADD_TRANSLATOR (Percent_repeat_engraver,
                /* doc */
                "Make whole bar and double bar repeats.",
                
                /* create */
                "PercentRepeat "
                "DoublePercentRepeat "
                "PercentRepeatCounter "
                "DoublePercentRepeatCounter",
                
                /* accept */
                "percent-event",

                /* read */
                "measureLength "
                "currentCommandColumn "
                "countPercentRepeats",

                /* write */
                "forbidBreak "
                );
/*
  percent-repeat-iterator.cc -- implement Percent_repeat_iterator

  source file of the GNU LilyPond music typesetter

  (c) 2001--2006  Han-Wen Nienhuys <[EMAIL PROTECTED]>, Erik Sandberg <[EMAIL 
PROTECTED]>
*/

#include "percent-repeat-iterator.hh"

#include "input.hh"
#include "international.hh"
#include "music.hh"
#include "repeated-music.hh"

IMPLEMENT_CTOR_CALLBACK (Percent_repeat_iterator);

Percent_repeat_iterator::Percent_repeat_iterator ()
{
  child_list_ = SCM_EOL;
}

void
Percent_repeat_iterator::construct_children ()
{
  /* TODO: Distinction between percent and slash */
  Music *mus = get_music ();
  Music *child = Repeated_music::body (mus);
  SCM length = child->get_length ().smobbed_copy ();
  child_list_ = SCM_EOL;

  int repeats = scm_to_int (mus->get_property ("repeat-count"));
  for (int i = repeats; i > 1; i--)
  {
    Music *percent = make_music_by_name (ly_symbol2scm ("PercentEvent"));
    percent->set_spot (*mus->origin ());
    percent->set_property ("length", length);
    if (repeats > 1)
      percent->set_property ("repeat-count", scm_int2num (i - 1));
    Music *percent_chord = make_music_by_name (ly_symbol2scm ("EventChord"));
    percent_chord->set_spot (*mus->origin ());
    percent_chord->set_property ("elements", scm_list_1 (percent->self_scm ()));
    child_list_ = scm_cons (percent_chord->self_scm (), child_list_);
  }
  child_list_ = scm_cons (child->self_scm (), child_list_);
  
  Sequential_iterator::construct_children ();
}

SCM
Percent_repeat_iterator::get_music_list () const
{
  return child_list_;
}

void
Percent_repeat_iterator::derived_mark () const
{
  scm_gc_mark (child_list_);
  Sequential_iterator::derived_mark ();
}
/*
  slash-repeat-engraver.cc -- implement Slash_repeat_engraver

  source file of the GNU LilyPond music typesetter

  (c) 2000--2006 Han-Wen Nienhuys <[EMAIL PROTECTED]>, Erik Sandberg <[EMAIL 
PROTECTED]>
*/

#include "repeated-music.hh"
#include "global-context.hh"
#include "warn.hh"
#include "misc.hh"
#include "spanner.hh"
#include "item.hh"
#include "percent-repeat-iterator.hh"
#include "bar-line.hh"
#include "score-engraver.hh"

/**
   This acknowledges repeated music with "percent" style.  It typesets
   a slash sign.
*/
class Slash_repeat_engraver : public Engraver
{
public:
  TRANSLATOR_DECLARATIONS (Slash_repeat_engraver);
protected:
  Music *slash_;
protected:
  virtual bool try_music (Music *);
  void process_music ();
};

Slash_repeat_engraver::Slash_repeat_engraver ()
{
  slash_ = 0;
}

bool
Slash_repeat_engraver::try_music (Music *m)
{
  /*todo: separate events for percent and slash */
  if (m->is_mus_type ("percent-event"))
    {
      Moment meas_length
        = robust_scm2moment (get_property ("measureLength"), Moment (0));

      if (m->get_length () < meas_length)
        slash_ = m;
      else
        return false;

      return true;
    }

  return false;
}

void
Slash_repeat_engraver::process_music ()
{
  if (slash_)
    {
      make_item ("RepeatSlash", slash_->self_scm ());
      slash_ = 0;
    }
}

#include "translator.icc"

ADD_TRANSLATOR (Slash_repeat_engraver,
                /* doc */ "Make beat repeats.",
                /* create */ "RepeatSlash",
                /* accept */ "percent-event",
                /* read */ "measureLength",
                /* write */ "");
/*
  tuplet-engraver.cc -- implement Tuplet_engraver

  source file of the GNU LilyPond music typesetter

  (c) 1998--2006 Han-Wen Nienhuys <[EMAIL PROTECTED]>
*/

#include "tuplet-bracket.hh"
#include "note-column.hh"
#include "beam.hh"
#include "engraver.hh"
#include "spanner.hh"

#include "translator.icc"

struct Tuplet_description
{
  Music *music_;
  Spanner *bracket_;
  Spanner *number_;
  Tuplet_description ()
  {
    music_ = 0;
    bracket_ = 0;
    number_ = 0;
  }
};

class Tuplet_engraver : public Engraver
{
public:
  TRANSLATOR_DECLARATIONS (Tuplet_engraver);

protected:
  vector<Tuplet_description> tuplets_;
  vector<Tuplet_description> stopped_tuplets_;
  vector<Spanner*> last_tuplets_;
  DECLARE_ACKNOWLEDGER (note_column);
  virtual bool try_music (Music *r);
  virtual void finalize ();
  void start_translation_timestep ();
  void process_music ();
};

bool
Tuplet_engraver::try_music (Music *music)
{
  if (music->is_mus_type ("tuplet-spanner-event"))
    {
      Direction dir = to_dir (music->get_property ("span-direction"));
      if (dir == START)
        {
          Tuplet_description d;
          d.music_ = music;
          tuplets_.push_back (d);
        }
      if (dir == STOP)
        {
          stopped_tuplets_.push_back (tuplets_.back ());
          tuplets_.pop_back ();
        }
      return true;
    }
  return false;
}

void
Tuplet_engraver::process_music ()
{
  for (vsize i = 0; i < stopped_tuplets_.size (); i++)
    {
      bool full_length = to_boolean (get_property ("tupletFullLength"));
      if (stopped_tuplets_[i].bracket_)
        {
          if (full_length)
            {
              Item *col = unsmob_item (get_property ("currentMusicalColumn"));
              
              stopped_tuplets_[i].bracket_->set_bound (RIGHT, col);
              stopped_tuplets_[i].number_->set_bound (RIGHT, col);
            }
          else if (!stopped_tuplets_[i].bracket_->get_bound (RIGHT))
            {
              stopped_tuplets_[i].bracket_->set_bound (RIGHT,
                                                       
stopped_tuplets_[i].bracket_->get_bound (LEFT));
              stopped_tuplets_[i].number_->set_bound (RIGHT,
                                                      
stopped_tuplets_[i].bracket_->get_bound (LEFT));
            }
          // todo: scrap last_tuplets_, use stopped_tuplets_ only.
          // clear stopped_tuplets_ at start_translation_timestep
          last_tuplets_.push_back (tuplets_[i].bracket_);
          last_tuplets_.push_back (tuplets_[i].number_);
        }
    }
  stopped_tuplets_.clear ();

  if (!tuplets_.size ())
    return;

  for (vsize i = 0; i < tuplets_.size (); i++)
    {
      if (tuplets_[i].bracket_)
        continue;

      tuplets_[i].bracket_ = make_spanner ("TupletBracket",
                                           tuplets_[i].music_->self_scm ());
      tuplets_[i].number_ = make_spanner ("TupletNumber",
                                          tuplets_[i].music_->self_scm ());
      tuplets_[i].number_->set_object ("bracket", 
tuplets_[i].bracket_->self_scm ());
      tuplets_[i].bracket_->set_object ("tuplet-number", 
tuplets_[i].number_->self_scm ());
      
      if (i > 0 && tuplets_[i - 1].bracket_)
        Tuplet_bracket::add_tuplet_bracket (tuplets_[i].bracket_, tuplets_[i - 
1].bracket_);

      if (i < tuplets_.size () - 1 && tuplets_[i + 1].bracket_)
        Tuplet_bracket::add_tuplet_bracket (tuplets_[i + 1].bracket_, 
tuplets_[i].bracket_);

      SCM proc = get_property ("tupletNumberFormatFunction");
      if (ly_is_procedure (proc))
        {
          SCM t = scm_apply_0 (proc, scm_list_1 (tuplets_[i].music_->self_scm 
()));
          tuplets_[i].number_->set_property ("text", t);
        }
    }
}

void
Tuplet_engraver::acknowledge_note_column (Grob_info inf)
{
  for (vsize j = 0; j < tuplets_.size (); j++)
    if (tuplets_[j].bracket_)
      {
        Item *i = dynamic_cast<Item *> (inf.grob ());
        Tuplet_bracket::add_column (tuplets_[j].bracket_, i);
        add_bound_item (tuplets_[j].number_, i);
      }
}

void
Tuplet_engraver::start_translation_timestep ()
{
  last_tuplets_.clear ();
}

void
Tuplet_engraver::finalize ()
{
  if (to_boolean (get_property ("tupletFullLength")))
    {
      for (vsize i = 0; i < last_tuplets_.size (); i++)
        {
          Item *col = unsmob_item (get_property ("currentCommandColumn"));
          last_tuplets_[i]->set_bound (RIGHT, col);
        }
    }
}

Tuplet_engraver::Tuplet_engraver ()
{
}

ADD_ACKNOWLEDGER (Tuplet_engraver, note_column);
ADD_TRANSLATOR (Tuplet_engraver,
                /* doc */ "Catch TupletSpannerEvent and generate appropriate 
bracket  ",
                /* create */ "TupletBracket TupletNumber",
                /* accept */ "tuplet-spanner-event",
                /* read */ "tupletNumberFormatFunction tupletSpannerDuration 
tupletFullLength",
                /* write */ "");

Attachment: repeats.diff
Description: Binary data

_______________________________________________
lilypond-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to