>>
@end lilypond
+@cindex no chord symbol
+@cindex N.C. symbol
+@cindex indicating No Chord in ChordNames
+
+Rests passed to a @code{ChordNames} context will cause the
+@code{noChordSymbol} markup to be displayed.
+
+@lilypond[verbatim, quote, relative=1]
+<<
+ \new ChordNames \chordmode {
+ c1
+ r1
+ g1
+ c1
+ }
+ \new Score \chordmode {
+ c1
+ r1
+ g1
+ c1
+ }
+>>
+@end lilypond
+
@funindex{\chords}
@code{\chords @{ ... @}} is a shortcut notation for
--- /dev/null
+\version "2.13.1"
+
+\header {
+ texidoc = "Rests in music passed to ChordNames context display noChordSymbol.
+noChordSymbol is treated like a ChordName with respect to chordChanges.
+"
+}
+
+myChords = \chordmode {
+ c1 r1 r1 \break
+ r1 g1 c1 \break
+}
+
+\score {
+ <<
+ \new ChordNames {
+ \myChords
+ \set chordChanges = ##t
+ \myChords
+ }
+ \new FretBoards {
+ \myChords
+ \myChords
+ }
+ \new Staff {
+ \myChords
+ \myChords
+ }
+ >>
+}
an empty extent, delete it from the list instead. If the extent is
non-empty but there is no skyline available (or pure is true), just
create a flat skyline from the bounding box */
+// TODO(jneem): the pure and non-pure parts seem to share very little
+// code. Split them into 2 functions, perhaps?
static void
get_skylines (Grob *me,
vector<Grob*> *const elements,
{
Box b;
b[a] = extent;
- b[other_axis (a)] = Interval (-infinity_f, infinity_f);
+ b[other_axis (a)] = Interval (0, infinity_f);
skylines.insert (b, 0, other_axis (a));
}
+
+ // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
+ // It's quite common for a treble clef to be the highest element of one system
+ // and for a low note (or lyrics) to be the lowest note on another. The two will
+ // never collide, but the pure-height stuff only works with bounding boxes, so it
+ // doesn't know that. The result is a significant over-estimation of the pure-height,
+ // especially on systems with many staves. To correct for this, we build a skyline
+ // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
+ // while the bit we're about to do only contains the breakable grobs at the beginning
+ // of the system. This way, the tall treble clefs are only compared with the treble
+ // clefs of the other staff and they will be ignored if the staff above is, for example,
+ // lyrics.
+ if (Axis_group_interface::has_interface (g))
+ {
+ Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
+ if (!begin_of_line_extent.is_empty ())
+ {
+ Box b;
+ b[a] = begin_of_line_extent;
+ b[other_axis (a)] = Interval (-infinity_f, -1);
+ skylines.insert (b, 0, other_axis (a));
+ }
+ }
}
if (skylines.is_empty ())
return r;
}
+Interval
+Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
+{
+ SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
-/*
- FIXME: pure extent handling has a lot of ad-hoc caching.
- This should be done with grob property callbacks.
+ if (!scm_is_pair (adjacent_pure_heights)
+ || !scm_is_vector (scm_cdr (adjacent_pure_heights)))
+ return Interval (0, 0);
- --hwn
-*/
+ return combine_pure_heights (me, scm_cdr (adjacent_pure_heights), start, end);
+}
Interval
-Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
+Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
+{
+ SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
+
+ if (!scm_is_pair (adjacent_pure_heights)
+ || !scm_is_vector (scm_car (adjacent_pure_heights)))
+ return Interval (0, 0);
+
+ return combine_pure_heights (me, scm_car (adjacent_pure_heights), start, start+1);
+}
+
+Interval
+Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
{
Paper_score *ps = get_root_system (me)->paper_score ();
vector<vsize> breaks = ps->get_break_indices ();
vector<Grob*> cols = ps->get_columns ();
- SCM extents = me->get_property ("adjacent-pure-heights");
-
- if (!scm_is_vector (extents))
- return Interval (0, 0);
-
Interval ext;
for (vsize i = 0; i + 1 < breaks.size (); i++)
{
break;
if (r >= start)
- ext.unite (ly_scm2interval (scm_c_vector_ref (extents, i)));
+ ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
}
return ext;
}
+// adjacent-pure-heights is a pair of vectors, each of which has one element
+// for every measure in the score. The first vector stores, for each measure,
+// the combined height of the elements that are present only when the bar
+// is at the beginning of a line. The second vector stores, for each measure,
+// the combined height of the elements that are present only when the bar
+// is not at the beginning of a line.
+
MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
SCM
Axis_group_interface::adjacent_pure_heights (SCM smob)
vector<vsize> breaks = ps->get_break_indices ();
vector<Grob*> cols = ps->get_columns ();
- SCM ret = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+ SCM begin_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+ SCM mid_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+
vsize it_index = 0;
for (vsize i = 0; i + 1 < breaks.size (); i++)
{
int start = Paper_column::get_rank (cols[breaks[i]]);
int end = Paper_column::get_rank (cols[breaks[i+1]]);
- Interval iv;
+ Interval begin_line_iv;
+ Interval mid_line_iv;
for (vsize j = it_index; j < items.size (); j++)
{
&& !to_boolean (it->get_property ("cross-staff")))
{
Interval dims = items[j]->pure_height (common, start, end);
+ Interval &target_iv = it->pure_is_visible (start-1, end) ? mid_line_iv : begin_line_iv;
+
if (!dims.is_empty ())
- iv.unite (dims);
+ target_iv.unite (dims);
}
if (rank < end)
&& !to_boolean (spanners[j]->get_property ("cross-staff")))
{
Interval dims = spanners[j]->pure_height (common, start, end);
+
if (!dims.is_empty ())
- iv.unite (dims);
+ mid_line_iv.unite (dims);
}
}
- scm_vector_set_x (ret, scm_from_int (i), ly_interval2scm (iv));
+ scm_vector_set_x (begin_line_heights, scm_from_int (i), ly_interval2scm (begin_line_iv));
+ scm_vector_set_x (mid_line_heights, scm_from_int (i), ly_interval2scm (mid_line_iv));
}
- return ret;
+ return scm_cons (begin_line_heights, mid_line_heights);
}
Interval
#include "pitch.hh"
#include "protected-scm.hh"
#include "stream-event.hh"
+#include "text-interface.hh"
#include "warn.hh"
#include "translator.icc"
virtual void finalize ();
virtual void derived_mark () const;
DECLARE_TRANSLATOR_LISTENER (note);
+ DECLARE_TRANSLATOR_LISTENER (rest);
private:
Item *chord_name_;
vector<Stream_event*> notes_;
-
+
SCM last_chord_;
+ Stream_event *rest_event_;
};
void
{
chord_name_ = 0;
last_chord_ = SCM_EOL;
+ rest_event_ = 0;
}
void
Chord_name_engraver::process_music ()
-{
- if (!notes_.size ())
- return;
-
+{
+ SCM markup;
SCM bass = SCM_EOL;
SCM inversion = SCM_EOL;
SCM pitches = SCM_EOL;
- Stream_event *inversion_event = 0;
- for (vsize i = 0; i < notes_.size (); i++)
+ if (rest_event_)
{
- Stream_event *n = notes_[i];
- SCM p = n->get_property ("pitch");
- if (!unsmob_pitch (p))
- continue;
-
- if (n->get_property ("inversion") == SCM_BOOL_T)
- {
- inversion_event = n;
- inversion = p;
- }
- else if (n->get_property ("bass") == SCM_BOOL_T)
- bass = p;
- else
- pitches = scm_cons (p, pitches);
+ SCM no_chord_markup = get_property ("noChordSymbol");
+ if (!Text_interface::is_markup (no_chord_markup))
+ return;
+ markup = no_chord_markup;
}
-
- if (inversion_event)
- {
- SCM oct = inversion_event->get_property ("octavation");
- if (scm_is_number (oct))
- {
- Pitch *p = unsmob_pitch (inversion_event->get_property ("pitch"));
- int octavation = scm_to_int (oct);
- Pitch orig = p->transposed (Pitch (-octavation, 0, 0));
-
- pitches = scm_cons (orig.smobbed_copy (), pitches);
- }
- else
- programming_error ("inversion does not have original pitch");
+ else
+ {
+ if (!notes_.size ())
+ return;
+
+ Stream_event *inversion_event = 0;
+ for (vsize i = 0; i < notes_.size (); i++)
+ {
+ Stream_event *n = notes_[i];
+ SCM p = n->get_property ("pitch");
+ if (!unsmob_pitch (p))
+ continue;
+
+ if (n->get_property ("inversion") == SCM_BOOL_T)
+ {
+ inversion_event = n;
+ inversion = p;
+ }
+ else if (n->get_property ("bass") == SCM_BOOL_T)
+ bass = p;
+ else
+ pitches = scm_cons (p, pitches);
+ }
+
+ if (inversion_event)
+ {
+ SCM oct = inversion_event->get_property ("octavation");
+ if (scm_is_number (oct))
+ {
+ Pitch *p = unsmob_pitch (inversion_event->get_property ("pitch"));
+ int octavation = scm_to_int (oct);
+ Pitch orig = p->transposed (Pitch (-octavation, 0, 0));
+
+ pitches = scm_cons (orig.smobbed_copy (), pitches);
+ }
+ else
+ programming_error ("inversion does not have original pitch");
+ }
+
+ pitches = scm_sort_list (pitches, Pitch::less_p_proc);
+
+ SCM name_proc = get_property ("chordNameFunction");
+ markup = scm_call_4 (name_proc, pitches, bass, inversion,
+ context ()->self_scm ());
}
-
- pitches = scm_sort_list (pitches, Pitch::less_p_proc);
-
- SCM name_proc = get_property ("chordNameFunction");
- SCM markup = scm_call_4 (name_proc, pitches, bass, inversion,
- context ()->self_scm ());
-
/*
Ugh.
*/
SCM chord_as_scm = scm_cons (pitches, scm_cons (bass, inversion));
- chord_name_ = make_item ("ChordName", notes_[0]->self_scm ());
+ chord_name_ = make_item ("ChordName",
+ rest_event_ ? rest_event_->self_scm () : notes_[0]->self_scm ());
chord_name_->set_property ("text", markup);
- SCM s = get_property ("chordChanges");
- if (to_boolean (s) && scm_is_pair (last_chord_)
+ SCM chord_changes = get_property("chordChanges");
+ if (to_boolean (chord_changes) && scm_is_pair (last_chord_)
&& ly_is_equal (chord_as_scm, last_chord_))
chord_name_->set_property ("begin-of-line-visible", SCM_BOOL_T);
notes_.push_back (ev);
}
+IMPLEMENT_TRANSLATOR_LISTENER (Chord_name_engraver, rest);
+void
+Chord_name_engraver::listen_rest (Stream_event *ev)
+{
+ ASSIGN_EVENT_ONCE(rest_event_, ev);
+}
+
void
Chord_name_engraver::stop_translation_timestep ()
{
chord_name_ = 0;
notes_.clear ();
+ rest_event_ = 0;
}
/*
*/
ADD_TRANSLATOR (Chord_name_engraver,
/* doc */
- "Catch note events and generate the appropriate chordname.",
+ "Catch note and rest events and generate the appropriate chordname.",
/* create */
"ChordName ",
"chordNoteNamer "
"chordRootNamer "
"chordNameExceptions "
- "majorSevenSymbol ",
+ "majorSevenSymbol "
+ "noChordSymbol ",
/* write */
""
static Interval relative_group_extent (vector<Grob*> const &list,
Grob *common, Axis);
static Interval relative_pure_height (Grob *me, int start, int end);
+ static Interval combine_pure_heights (Grob *me, SCM, int, int);
static Interval cached_pure_height (Grob *me, int, int);
+ static Interval begin_of_line_pure_height (Grob *me, int);
static Grob *calc_pure_elts_and_common (Grob*);
static Skyline_pair skyline_spacing (Grob *me, vector<Grob*> elements);
void
Page_spacing::calc_force ()
{
- /* If the first system is a title, we add back in the page-top-space. */
- Real height = first_line_.title_ ? page_height_ + page_top_space_ : page_height_;
+ /* If the first system contains a title, we add back in the page-top-space. */
+ bool starts_with_title = first_line_.compressed_nontitle_lines_count_ < first_line_.compressed_lines_count_;
+ Real height = starts_with_title ? page_height_ + page_top_space_ : page_height_;
if (rod_height_ + last_line_.bottom_padding_ >= height)
force_ = infinity_f;
chordPrefixSpacer = #0
chordNameExceptionsFull = #fullJazzExceptions
chordNameExceptionsPartial = #partialJazzExceptions
-
+ noChordSymbol = #(make-simple-markup "N.C.")
bassStaffProperties = #'((assign clefGlyph "clefs.F")
(assign clefPosition 2)
repeated section for a page turn to be allowed within that section.")
+ (noChordSymbol ,markup? "Markup to be displayed for rests in a
+ChordNames context.")
(noteToFretFunction ,procedure? "How to produce a fret diagram.
Parameters: A list of note events and a list of tabstring events.")
(accidental-grob ,ly:grob? "The accidental for this note.")
(accidental-grobs ,list? "An alist with @code{(@var{notename} .
@var{groblist})} entries.")
- (adjacent-pure-heights ,vector? "Used by a @code{VerticalAxisGroup} to
+ (adjacent-pure-heights ,pair? "A pair of vectors. Used by a @code{VerticalAxisGroup} to
cache the @code{Y-extent}s of different column ranges.")
(adjacent-hairpins ,ly:grob-array? "A list of directly neighboring
hairpins.")
(else
(cons string-coordinate (- fret-coordinate)))))
+ (define (stencil-coordinate-offset fret-offset string-offset)
+ "Return a pair @code{(x-offset . y-offstet)}
+ for translation in stencil coordinate system."
+ (cond
+ ((eq? orientation 'landscape)
+ (cons fret-offset (- string-offset)))
+ ((eq? orientation 'opposing-landscape)
+ (cons (- fret-offset) string-offset))
+ (else
+ (cons string-offset (- fret-offset)))))
+
+
+
(define (make-bezier-sandwich-list start stop base height
half-thickness)
"Make the argument list for a bezier sandwich from
(if (null? restlist)
positioned-glyph
(ly:stencil-add
+ positioned-glyph
(draw-xo restlist)))))
(define (draw-capo fret)
(xo-stencil (draw-xo xo-list))
(xo-fret-offset
(stencil-fretboard-offset
- xo-stencil 'fret orientation)))
+ xo-stencil 'fret orientation))
+ (xo-stencil-offset
+ (stencil-coordinate-offset
+ (- diagram-fret-top
+ xo-fret-offset
+ (* size xo-padding))
+ 0)))
(set! fret-diagram-stencil
(ly:stencil-add
fret-diagram-stencil
(ly:stencil-translate
xo-stencil
- (stencil-coordinates
- (- diagram-fret-top
- xo-fret-offset
- (* size xo-padding))
- 0)))))) ; no string offset
-
+ xo-stencil-offset)))))
(if (> capo-fret 0)
(set! fret-diagram-stencil
(ly:stencil-add
# page-number CDATA #IMPLIED
# >
elts = []
- if (hasattr (el, "new-system")):
+ if (hasattr (el, "new-system") and conversion_settings.convert_page_layout):
val = getattr (el, "new-system")
if (val == "yes"):
elts.append (musicexp.Break ("break"))
- if (hasattr (el, "new-page")):
+ if (hasattr (el, "new-page") and conversion_settings.convert_page_layout):
val = getattr (el, "new-page")
if (val == "yes"):
elts.append (musicexp.Break ("pageBreak"))
dest = "convert_rest_positions",
help = _ ("do not convert exact vertical positions of rests"))
+ p.add_option ('--npl', '--no-page-layout',
+ action = "store_false",
+ default = True,
+ dest = "convert_page_layout",
+ help = _ ("do not convert the exact page layout and breaks"))
+
p.add_option ('--no-beaming',
action = "store_false",
default = True,
print_ly_additional_definitions (printer, filename)
if score_information:
score_information.print_ly (printer)
- if paper_information:
+ if paper_information and conversion_settings.convert_page_layout:
paper_information.print_ly (printer)
if layout_information:
layout_information.print_ly (printer)
needed_additional_definitions.append (options.language)
additional_definitions[options.language] = "\\include \"%s.ly\"\n" % options.language
conversion_settings.ignore_beaming = not options.convert_beaming
+ conversion_settings.convert_page_layout = options.convert_page_layout
# Allow the user to leave out the .xml or xml on the filename
basefilename = args[0].decode('utf-8')