]> git.donarmstrong.com Git - lilypond.git/commitdiff
* input/test/ambitus.ly:
authorJan Nieuwenhuizen <janneke@gnu.org>
Wed, 26 Jun 2002 11:15:21 +0000 (11:15 +0000)
committerJan Nieuwenhuizen <janneke@gnu.org>
Wed, 26 Jun 2002 11:15:21 +0000 (11:15 +0000)
* lily/ambitus-engraver.cc:
* lily/ambitus.cc:
* lily/include/ambitus.hh: New file.

ChangeLog
VERSION
input/test/ambitus.ly [new file with mode: 0644]
lily/ambitus-engraver.cc [new file with mode: 0644]
lily/ambitus.cc [new file with mode: 0644]
lily/include/ambitus.hh [new file with mode: 0644]

index a4bc93e30075b75d3543bd5a7cee0df4f85c68d5..d5b41c7afe00e247034b213fdf44368866673f6d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2002-06-26  Jan Nieuwenhuizen  <janneke@gnu.org>
 
+       * input/test/ambitus.ly:
+       * lily/ambitus-engraver.cc:
+       * lily/ambitus.cc:
+       * lily/include/ambitus.hh: New file.
+
        * GNUmakefile.in (local-clean): Also remove builddir-setup's symlinks.
 
 2002-06-25  Juergen Reuter  <reuter@ipd.uka.de>
@@ -7,7 +12,7 @@
        * input/test/ambitus.ly, lily/ambitus-engraver.cc,
        lily/ambitus.cc, lily/include/ambitus.hh, ly/engraver-init.ly,
        scm/basic-properties.scm, scm/grob-description.scm,
-       scm/grob-property-description.scm: added support for ambitus
+       scm/grob-property-description.scm: Add support for ambitus.
 
 2002-06-24  Han-Wen Nienhuys  <hanwen@cs.uu.nl>
 
diff --git a/VERSION b/VERSION
index 4ab904b6702b7635aff40085ba9e9de95193e52f..cec8622582de6626ca427c0cd2b299006d4f9e14 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -2,7 +2,7 @@ PACKAGE_NAME=LilyPond
 MAJOR_VERSION=1
 MINOR_VERSION=5
 PATCH_LEVEL=63
-MY_PATCH_LEVEL=
+MY_PATCH_LEVEL=jcn1
 
 # Use the above to send patches: MY_PATCH_LEVEL is always empty for a
 # released version.
diff --git a/input/test/ambitus.ly b/input/test/ambitus.ly
new file mode 100644 (file)
index 0000000..54f8165
--- /dev/null
@@ -0,0 +1,45 @@
+\version "1.5.49"
+
+upper = \notes \relative c {
+       \clef "treble"
+       \key c \minor
+       as'' c e bes f cis d e f g f e d f d e
+       f d e e d f d e e d f d e e d f d e
+       f d e e d f d e e d f d e e d f d e
+}
+
+lower = \notes \relative c {
+       \clef "treble"
+       \key e \major
+       e'2 b4 g a c es fis a cis b a g f e d
+       f e d e f g f e d e f g f e d e f g
+       f e d e f g f e d e f g f e d e f g
+}
+
+\score { \context ChoirStaff {
+       <
+               \context Staff = one { \upper }
+               \context Staff = three { \lower }
+       > }
+       \paper {
+              \translator {
+                       \ScoreContext
+                       breakAlignOrder = #'(
+                               instrument-name
+                               left-edge
+                               ambitus
+                               span-bar
+                               breathing-sign
+                               clef
+                               key-signature
+                               staff-bar
+                               time-signature
+                               custos
+                       )
+               }
+               \translator {
+                       \VoiceContext
+                       \consists Ambitus_engraver
+               }
+       }
+}
diff --git a/lily/ambitus-engraver.cc b/lily/ambitus-engraver.cc
new file mode 100644 (file)
index 0000000..fb0af02
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+  ambitus-engraver.cc -- implement Ambitus_engraver
+
+  source file of the GNU LilyPond music typesetter
+
+  (C) 2002 Juergen Reuter <reuter@ipd.uka.de>
+*/
+
+#include "engraver.hh"
+#include "item.hh"
+#include "note-head.hh"
+#include "staff-symbol-referencer.hh"
+#include "musical-request.hh"
+#include "pitch.hh"
+
+/*
+ * This class implements an engraver for ambitus grobs.
+ *
+ * TODO: There are quite some conceptional issues left open:
+ *
+ * - Many publishers put ambitus _before_ the first occurrence of a
+ * clef.  Hence, formally the pitches are undefined in this case.  Of
+ * course, one could always silently assume that ambitus pitches refer
+ * to the first occurrence of a clef.  Or should we, by default, put
+ * the ambitus always after the first clef, if any?
+ *
+ * - Enharmonically equal pitches: Assume piece contains once a "gis",
+ * another time an "aes" as highest pitch.  Which one should be
+ * selected for the ambitus grob?  The "aes", because it is
+ * musically/notationally "higher" than "gis"?  Or "gis", because (if
+ * using pure temperament) it has a slightly higher frequency?  Or
+ * that pitch that come closer to the key signature?  But there may be
+ * key signature changes in the piece...
+ *
+ * - Multiple voices in single staff: Assume a vocal piece of music,
+ * where the soprano voice and the alto voice are put into the same
+ * staff (this is generally a bad idea, but unfortunately common
+ * practice).  Then, there probably should be two ambitus grobs, one
+ * for each voice.  But how can you see which ambitus grob refers to
+ * which voice?  Most probably you can guess it from the fact that the
+ * ambitus of the alto voice typically lies in a lower range than that
+ * of the soprano voice, but this is just a heuristic rather than a
+ * generally valid rule.  In the case of only two voices, using stems
+ * in the ambitus grob might help, but probably looks quite ugly.
+ *
+ * - If a piece consists of several loosely coupled sections, should
+ * there be multiple ambitus grobs allowed, one for each section?
+ * Then there probably should be some "\ambitus" request added to
+ * mudela, stating where an ambitus grob should be placed.  This
+ * ambitus grob should then represent the ambitus in the range of time
+ * between this "\ambitus" request and the next one (or the end of the
+ * piece, if there is no more such request).  To be compliant with the
+ * current implementation, we might implicitly assume an "\ambitus"
+ * request at the beginning of the piece, but then the question where
+ * to put this first ambitus grob (before/after the clef?) becomes
+ * even more urgent.
+ *
+ * - Incipits of transcribed music may need special treatment for
+ * ambitus, since, for readability, the ambitus most probably should
+ * not refer to the ancient clefs of the incipit, but rather to the
+ * clefs used in the transcribed parts.
+ */
+class Ambitus_engraver : public Engraver
+{
+public:
+TRANSLATOR_DECLARATIONS(Ambitus_engraver);
+  virtual void start_translation_timestep ();
+  virtual void acknowledge_grob (Grob_info);
+  virtual void create_grobs ();
+  virtual void stop_translation_timestep ();
+
+private:
+  void create_ambitus ();
+  Item *ambitus_p_;
+  int isActive;
+  Pitch pitch_min, pitch_max;
+};
+
+Ambitus_engraver::Ambitus_engraver ()
+{
+  ambitus_p_ = 0; isActive = 0;
+
+  // (pitch_min > pitch_max) means that pitches are not yet
+  // initialized
+  pitch_min = Pitch (0, 0, +1);
+  pitch_max = Pitch (0, 0, -1);
+}
+
+void
+Ambitus_engraver::stop_translation_timestep ()
+{
+  if (!ambitus_p_) {
+    create_ambitus ();
+  }
+  if (ambitus_p_ && isActive)
+    {
+      SCM key_signature = get_property ("keySignature");
+      ambitus_p_->set_grob_property ("keySignature", key_signature);
+      typeset_grob (ambitus_p_);
+      //ambitus_p_ = 0;
+      isActive = 0;
+    }
+}
+
+void
+Ambitus_engraver::start_translation_timestep ()
+{
+  if (!ambitus_p_) {
+    create_ambitus ();
+  }
+}
+
+void
+Ambitus_engraver::create_grobs ()
+{
+  if (!ambitus_p_) {
+    create_ambitus ();
+  }
+}
+
+void
+Ambitus_engraver::acknowledge_grob (Grob_info info)
+{
+  if (!ambitus_p_) {
+    create_ambitus ();
+  }
+  if (!ambitus_p_)
+    return;
+  Item *item = dynamic_cast <Item *>(info.grob_l_);
+  if (item)
+    {
+      if (Note_head::has_interface (info.grob_l_))
+       {
+         Note_req *nr = dynamic_cast<Note_req*> (info.music_cause ());
+         if (nr)
+           {
+             Pitch pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
+             if (Pitch::compare (pitch_min, pitch_max) > 0) // already init'd?
+               {
+                 // not yet init'd; use current pitch to init min/max
+                 pitch_min = pitch;
+                 pitch_max = pitch;
+                 ambitus_p_->set_grob_property ("pitch-min",
+                                                pitch_min.smobbed_copy ());
+                 ambitus_p_->set_grob_property ("pitch-max",
+                                                pitch_max.smobbed_copy ());
+               }
+             else if (Pitch::compare (pitch, pitch_max) > 0) // new max?
+               {
+                 pitch_max = pitch;
+                 ambitus_p_->set_grob_property ("pitch-max",
+                                                pitch_max.smobbed_copy ());
+               }
+             else if (Pitch::compare (pitch, pitch_min) < 0) // new min?
+               {
+                 pitch_min = pitch;
+                 ambitus_p_->set_grob_property ("pitch-min",
+                                                pitch_min.smobbed_copy ());
+               }
+           }
+       }
+    }
+}
+
+void
+Ambitus_engraver::create_ambitus ()
+{
+  SCM basicProperties = get_property ("Ambitus");
+  SCM c0 = get_property ("centralCPosition");
+  ambitus_p_ = new Item (basicProperties); isActive = 1;
+  ambitus_p_->set_grob_property ("centralCPosition", c0);
+  announce_grob (ambitus_p_, SCM_EOL);
+}
+
+ENTER_DESCRIPTION(Ambitus_engraver,
+/* descr */       "",
+/* creats*/       "Ambitus",
+/* acks  */       "note-head-interface",
+/* reads */       "",
+/* write */       "");
diff --git a/lily/ambitus.cc b/lily/ambitus.cc
new file mode 100644 (file)
index 0000000..ccc7ba3
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+  ambitus.cc -- implement Ambitus
+
+  source file of the GNU LilyPond music typesetter
+
+  (C) 2002 Juergen Reuter <reuter@ipd.uka.de>
+*/
+
+#include "staff-symbol-referencer.hh"
+#include "pitch.hh"
+#include "ambitus.hh"
+#include "molecule.hh"
+#include "note-head.hh"
+#include "item.hh"
+#include "font-interface.hh"
+#include "paper-def.hh"
+#include "lookup.hh"
+
+/*
+ * TODO: note-head collision handling
+ *
+ * TODO: accidentals collision handling
+ *
+ * TODO: alternative representation: adding the ambitus as text script
+ * to the instrument name (e.g. "Soprano (c^1 - f^2)").
+ *
+ * FIXME: Accidentals are too close at the note heads (it seems that
+ * the extent of the ledger lines is ignored).
+ *
+ * TODO: If (depending on breakAlignOrder) ambitus is put behind
+ * key-signature, then do not repeat accidentals that already appear
+ * in the key signature.
+ *
+ * FIXME: A staff containing more than a single context will result in
+ * multiple ambitus grobs per staff.  This is basically ok, but there is
+ * currently no proper collision handling for this case.
+ *
+ * TODO: make ignore_octave and force_accidental of function
+ * number_accidentals accessible via grob properties.
+ */
+
+/**
+ * Given a pitch and a key_signature, decide what accidentals to show.
+ *
+ * Possible return values:
+ *
+ * 0: do not show any accidental
+ * 1: show pitch->alteration_i_ only
+ * 2: show pitch->alteration_i_, preceded by a natural sign
+ */
+static int
+number_accidentals (SCM key_signature, Pitch *pitch,
+                   bool ignore_octave_b, bool force_accidental)
+{
+  int notename = pitch->notename_i_;
+  int octave = pitch->octave_i_;
+  int alteration = pitch->alteration_i_;
+
+  if (force_accidental) // ignore key signature
+    return 1;
+
+#if DEBUG_AMBITUS
+  scm_display (key_signature, scm_current_output_port ());
+#endif
+
+  SCM prev;
+  if (ignore_octave_b)
+    prev = ly_assoc_cdr (gh_int2scm (notename), key_signature);
+  else
+    prev = gh_assoc (gh_cons (gh_int2scm (octave), gh_int2scm (notename)),
+                    key_signature);
+
+  /* should really be true unless prev == SCM_BOOL_F */
+  if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
+    {
+      prev = gh_cons (ly_car (prev), ly_cadr (prev));
+    }
+
+  /* If an accidental was not found */
+  if (prev == SCM_BOOL_F)
+    prev = gh_assoc (gh_int2scm (notename), key_signature);
+
+  SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
+  int sig_alteration = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
+
+  if (alteration == sig_alteration) // no accidental at all needed
+    return 0;
+
+  if ((alteration == 0) && (sig_alteration != 0)) // need ordinary natural
+    return 2;
+
+  if (sig_alteration == 0) // use pitch's alteration
+    return 1;
+
+  return 2;
+}
+
+void
+add_accidentals (Item *me, Molecule *head, int num_acc,
+                Pitch *pitch, String accidentals_style, Real yoffs)
+{
+  if (!num_acc)
+    return;
+  if (pitch->alteration_i_)
+    {
+      Molecule accidental (Font_interface::get_default_font (me)->
+                          find_by_name (String ("accidentals-") +
+                                        accidentals_style +
+                                        to_str (pitch->alteration_i_)));
+      accidental.translate_axis (yoffs, Y_AXIS);
+      head->add_at_edge (X_AXIS,  LEFT, accidental, 0.1);
+    }
+  if (num_acc == 2)
+    {
+      Molecule natural (Font_interface::get_default_font (me)->
+                       find_by_name (String ("accidentals-") +
+                                     accidentals_style +
+                                     to_str ("0")));
+      natural.translate_axis (yoffs, Y_AXIS);
+      head->add_at_edge (X_AXIS,  LEFT, natural, 0.1);
+    }
+}
+
+MAKE_SCHEME_CALLBACK (Ambitus,brew_molecule,1);
+SCM
+Ambitus::brew_molecule (SCM smob)
+{
+  Item *me = (Item *)unsmob_grob (smob);
+  Molecule molecule = Molecule ();
+
+  SCM scm_note_head_style = me->get_grob_property ("note-head-style");
+  String note_head_style;
+  if (gh_symbol_p (scm_note_head_style))
+    {
+      String note_head_style =
+       ly_scm2string (scm_symbol_to_string (scm_note_head_style));
+    }
+  else
+    {
+      note_head_style = String ("noteheads-2");
+    }
+  if (Font_interface::get_default_font (me)->find_by_name (note_head_style).empty_b ())
+    {
+      String message = "Ambitus: no such note head: `" + note_head_style + "'";
+      me->warning (_ (message.ch_C ()));
+      return SCM_EOL;
+    }
+
+  Pitch *pitch_min = unsmob_pitch (me->get_grob_property ("pitch-min"));
+  int p_min = pitch_min->steps ();
+  Pitch *pitch_max = unsmob_pitch (me->get_grob_property ("pitch-max"));
+  int p_max = pitch_max->steps ();
+  if (p_min > p_max)
+    {
+      String message = "Ambitus: no range to output";
+      me->warning (_ (message.ch_C ()));
+      return SCM_EOL;
+    }
+
+  SCM c0 = me->get_grob_property ("centralCPosition");
+  if (gh_number_p (c0))
+    {
+      p_min += gh_scm2int (c0);
+      p_max += gh_scm2int (c0);
+    }
+
+  // create heads
+  Molecule head_min =
+    Font_interface::get_default_font (me)->find_by_name (note_head_style);
+  head_min.translate_axis (0.5*p_min, Y_AXIS);
+  Molecule head_max =
+    Font_interface::get_default_font (me)->find_by_name (note_head_style);
+  head_max.translate_axis (0.5*p_max, Y_AXIS);
+
+  // join heads
+  if (to_boolean (me->get_grob_property ("join-heads")) &&
+      ((p_max - p_min) >= 3))
+    {
+      Real linethickness = me->paper_l ()->get_var ("linethickness");
+      Real blotdiameter = me->paper_l ()->get_var ("blotdiameter");
+      Interval x_extent = 0.5 * Interval (-linethickness, +linethickness);
+      Interval y_extent = 0.5 * Interval (p_min + 1.35, p_max - 1.35);
+      Box line_box (x_extent, y_extent);
+      Molecule line = Lookup::roundfilledbox (line_box, blotdiameter);
+      line.translate_axis (0.5 * head_min.extent (X_AXIS).length (), X_AXIS);
+      molecule.add_molecule (line);
+    }
+
+  // add ledger lines
+  Interval hd = head_min.extent (X_AXIS);
+  Real left_ledger_protusion = hd.length () / 4;
+  Real right_ledger_protusion = left_ledger_protusion;
+  Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
+                                hd[RIGHT] + right_ledger_protusion);
+  Molecule ledger_lines;
+  int interspaces = Staff_symbol_referencer::line_count (me) - 1;
+  ledger_lines =
+    Note_head::brew_ledger_lines (me, p_min, interspaces, l_extents, true);
+  ledger_lines.translate_axis (0.5 * p_min, Y_AXIS);
+  molecule.add_molecule (ledger_lines);
+  ledger_lines =
+    Note_head::brew_ledger_lines (me, p_max, interspaces, l_extents, true);
+  ledger_lines.translate_axis (0.5 * p_max, Y_AXIS);
+  molecule.add_molecule (ledger_lines);
+
+  // add accidentals
+  SCM key_signature = me->get_grob_property ("keySignature");
+  SCM scm_accidentals_style = me->get_grob_property ("accidentals-style");
+  String accidentals_style;
+  if (gh_symbol_p (scm_accidentals_style))
+    {
+      accidentals_style =
+       ly_scm2string (scm_symbol_to_string (scm_accidentals_style));
+    }
+  else
+    {
+      accidentals_style = String ("");
+    }
+  int num_acc;
+  num_acc = number_accidentals (key_signature, pitch_min, true, false);
+  add_accidentals (me, &head_min, num_acc, pitch_min,
+                  accidentals_style, 0.5 * p_min);
+  num_acc = number_accidentals (key_signature, pitch_max, true, false);
+  add_accidentals (me, &head_max, num_acc, pitch_max,
+                  accidentals_style, 0.5 * p_max);
+
+  // add heads
+  molecule.add_molecule (head_min);
+  molecule.add_molecule (head_max);
+
+  return molecule.smobbed_copy ();
+}
+
+ADD_INTERFACE (Ambitus, "ambitus-interface",
+  "An ambitus represents the pitch range of a voice.",
+  "note-head-style join-heads");
diff --git a/lily/include/ambitus.hh b/lily/include/ambitus.hh
new file mode 100644 (file)
index 0000000..c6b2138
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+  ambitus.hh
+
+  source file of the GNU LilyPond music typesetter
+
+ (C) 2000 Juergen Reuter <reuter@ipd.uka.de>
+*/
+
+#ifndef AMBITUS_HH
+#define AMBITUS_HH
+
+#include "lily-guile.hh"
+
+struct Ambitus
+{
+  DECLARE_SCHEME_CALLBACK (brew_molecule, (SCM smob));
+  static bool has_interface (Grob*);
+};
+
+#endif // AMBITUS_HH
+