diff options
-rw-r--r-- | Documentation/changes.tely | 19 | ||||
-rw-r--r-- | Documentation/contributor/programming-work.itexi | 96 | ||||
-rw-r--r-- | Documentation/notation/expressive.itely | 3 | ||||
-rw-r--r-- | Documentation/notation/pitches.itely | 11 | ||||
-rw-r--r-- | input/regression/retrograde.ly | 30 | ||||
-rw-r--r-- | lily/include/slur-engraver.hh | 4 | ||||
-rw-r--r-- | lily/parser.yy | 74 | ||||
-rw-r--r-- | lily/slur-engraver.cc | 22 | ||||
-rw-r--r-- | ly/grace-init.ly | 4 | ||||
-rw-r--r-- | ly/music-functions-init.ly | 7 | ||||
-rw-r--r-- | ly/spanners-init.ly | 11 | ||||
-rw-r--r-- | scm/define-grob-properties.scm | 2 | ||||
-rw-r--r-- | scm/define-grobs.scm | 2 | ||||
-rw-r--r-- | scm/define-music-properties.scm | 2 | ||||
-rw-r--r-- | scm/define-music-types.scm | 2 | ||||
-rw-r--r-- | scm/modal-transforms.scm | 122 |
16 files changed, 292 insertions, 119 deletions
diff --git a/Documentation/changes.tely b/Documentation/changes.tely index fb1a77f633..c9315d59da 100644 --- a/Documentation/changes.tely +++ b/Documentation/changes.tely @@ -62,6 +62,25 @@ which scares away people. @end ignore @item +Slurs and phrasing slurs may now be started from individual notes +in a chord. Several simultanous slurs per @code{Voice} need to be +distinguished by @code{spanner-id} setting. + +@item +The music and grob property @code{spanner-id} for distinguishing +simultaneous slurs and phrasing slurs has been changed from a +string to a @q{key}, a non-negative integer or symbol. + +@item +There is a new command @code{\=} for specifying the +@code{spanner-id} for simultaneous slurs and phrasing slurs. +@lilypond[verbatim,quote] +\fixed c' { + <c~ f\=1( g\=2( >2 <c e\=1) a\=2) > +} +@end lilypond + +@item Blocks introduced with @code{\header} can be stored in variables and used as arguments to music and scheme functions and as the body of @code{#@{@dots{}#@}} constructs. They are represented as diff --git a/Documentation/contributor/programming-work.itexi b/Documentation/contributor/programming-work.itexi index 4b0b43dad4..8dc8d2755b 100644 --- a/Documentation/contributor/programming-work.itexi +++ b/Documentation/contributor/programming-work.itexi @@ -367,40 +367,35 @@ If you like using font-lock, you can also add this to your @end example -@subheading Indenting with vim - -Although emacs indentation is the GNU standard, acceptable -indentation can usually be accomplished with vim. Some hints for -vim are as follows: - -A workable .vimrc: - -@example -set cindent -set smartindent -set autoindent -set expandtab -set softtabstop=2 -set shiftwidth=2 -filetype plugin indent on -set incsearch -set ignorecase smartcase -set hlsearch -set confirm -set statusline=%F%m%r%h%w\ %@{&ff@}\ %Y\ [ASCII=\%03.3b]\ [HEX=\%02.2B]\ %04l,%04v\ %p%%\ [LEN=%L] -set laststatus=2 -set number -" Remove trailing whitespace on write +@subsubheading Indenting with vim + +Although emacs indentation is the GNU standard, correct +indentation for C++ files can be achieved by using the settings +recommended in the +@url{https://gcc.gnu.org/wiki/FormattingCodeForGCC, GNU GCC Wiki}. +Save the following in @file{~/.vim/after/ftplugin/cpp.vim}: + +@example +setlocal cindent +setlocal cinoptions=>4,n-2,@{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1 +setlocal shiftwidth=2 +setlocal softtabstop=2 +setlocal textwidth=79 +setlocal fo-=ro fo+=cql +" use spaces instead of tabs +setlocal expandtab +" remove trailing whitespace on write autocmd BufWritePre * :%s/\s\+$//e @end example -With this @file{.vimrc}, files can be reindented automatically by +With these settings, files can be reindented automatically by highlighting the lines to be indented in visual mode (use V to -enter visual mode) and pressing @code{=}. +enter visual mode) and pressing @code{=}, or a single line +correctly indented in normal mode by pressing @code{==}. -A @file{scheme.vim} file will help improve the indentation. This -one was suggested by Patrick McCarty. It should be saved in -@file{~/.vim/after/syntax/scheme.vim}. +A @file{scheme.vim} file will help improve the indentation of +Scheme code. This one was suggested by Patrick McCarty. It +should be saved in @file{~/.vim/after/syntax/scheme.vim}. @example " Additional Guile-specific 'forms' @@ -417,24 +412,45 @@ syn keyword schemeSyntax define-safe-public define-music-function syn keyword schemeSyntax def-grace-function " All of the above should influence indenting too -set lw+=define-public,define*-public -set lw+=define*,lambda*,let-keywords* -set lw+=defmacro,defmacro*,define-macro -set lw+=defmacro-public,defmacro*-public -set lw+=use-modules,define-module -set lw+=define-method,define-class -set lw+=define-markup-command,define-markup-list-command -set lw+=define-safe-public,define-music-function -set lw+=def-grace-function +setlocal lw+=define-public,define*-public +setlocal lw+=define*,lambda*,let-keywords* +setlocal lw+=defmacro,defmacro*,define-macro +setlocal lw+=defmacro-public,defmacro*-public +setlocal lw+=use-modules,define-module +setlocal lw+=define-method,define-class +setlocal lw+=define-markup-command,define-markup-list-command +setlocal lw+=define-safe-public,define-music-function +setlocal lw+=def-grace-function " These forms should not influence indenting -set lw-=if -set lw-=set! +setlocal lw-=if +setlocal lw-=set! " Try to highlight all ly: procedures syn match schemeFunc "ly:[^) ]\+" @end example +For documentation work on texinfo files, identify the file +extensions used as texinfo files in your @file{.vim/filetype.vim}: + +@example +if exists("did_load_filetypes") + finish +endif +augroup filetypedetect + au! BufRead,BufNewFile *.itely setfiletype texinfo + au! BufRead,BufNewFile *.itexi setfiletype texinfo + au! BufRead,BufNewFile *.tely setfiletype texinfo +augroup END +@end example + +and add these settings in @file{.vim/after/ftplugin/texinfo.vim}: + +@example +setlocal expandtab +setlocal shiftwidth=2 +setlocal textwidth=66 +@end example @node Naming conventions @subsection Naming Conventions diff --git a/Documentation/notation/expressive.itely b/Documentation/notation/expressive.itely index d3f0235246..3c249389c2 100644 --- a/Documentation/notation/expressive.itely +++ b/Documentation/notation/expressive.itely @@ -685,7 +685,8 @@ occurences of outer slurs actually indicate phrasing, and phrasing slurs may overlap a regular slur, see @ref{Phrasing slurs}. When multiple regular slurs are needed in a single @code{Voice}, matching slur starts and ends need to be labelled by preceding -them with @code{\=} followed by an identifying number or string. +them with @code{\=} followed by an identifying key (a symbol or +non-negative integer). @lilypond[verbatim,quote] \fixed c' { diff --git a/Documentation/notation/pitches.itely b/Documentation/notation/pitches.itely index 0699239db5..bfed32447e 100644 --- a/Documentation/notation/pitches.itely +++ b/Documentation/notation/pitches.itely @@ -903,9 +903,14 @@ music = \relative { c'8. ees16( fis8. a16 b8.) gis16 f8. d16 } @end lilypond @knownissues -Manual ties inside @code{\retrograde} will be broken and -generate warnings. Some ties can be generated automatically -by enabling @ref{Automatic note splitting}. +@code{\retrograde} is a rather simple tool. Since many events are +@q{mirrored} rather than exchanged, tweaks and directional +modifiers for opening spanners need to be added at the matching +closing spanners: @code{^(} needs to be ended by @code{^)}, every +@code{\<} or @code{\cresc} needs to be ended by @code{\!} or +@code{\endcr}, every @code{\>} or @code{\decr} needs to be ended +by @code{\enddecr}. Property-changing commands/overrides with a +lasting effect will likely cause surprises. @seealso Notation Reference: diff --git a/input/regression/retrograde.ly b/input/regression/retrograde.ly new file mode 100644 index 0000000000..dd7f7eba95 --- /dev/null +++ b/input/regression/retrograde.ly @@ -0,0 +1,30 @@ +\version "2.18.0" + +\header { + texidoc = "@code{\\retrograde} can deal with crescendo and + decrescendo as long as they are properly paired with + @code{\\endcr}/@code{\\!} and @code{\\enddecr}. Direction modifiers + on slurs like @code{^(} need to be repeated as @code{^)} at the end. + Ties and glissandi work mostly (in-chord ties are turned into + ordinary per-chord/note ties, however)." +} + +\layout { ragged-right = ##t } + +motif = +\relative { + \override TextSpanner.bound-details.left.text = "motif" + <c' e>2~\startTextSpan c16\< d^( e f~ f4:32^)\!\> | + << + \context Voice = "voice" { + <g~ b>4 g8\glissando f\stopTextSpan\enddecr } + \\ + { c2 } + >> +} + +\new Voice = "voice" { + \motif + \override TextSpanner.bound-details.left.text = "retrograde motif" + \retrograde \motif \bar "|." +} diff --git a/lily/include/slur-engraver.hh b/lily/include/slur-engraver.hh index 572e7ea023..1885625ed9 100644 --- a/lily/include/slur-engraver.hh +++ b/lily/include/slur-engraver.hh @@ -60,8 +60,8 @@ protected: void stop_translation_timestep (); void process_music (); - bool can_create_slur (const string&, vsize, vsize *, Stream_event *); - void create_slur (const string &spanner_id, Event_info evi, Grob *g_cause, Direction dir, bool left_broken); + bool can_create_slur (SCM, vsize, vsize *, Stream_event *); + void create_slur (SCM spanner_id, Event_info evi, Grob *g_cause, Direction dir, bool left_broken); bool try_to_end (Event_info evi); virtual void set_melisma (bool); diff --git a/lily/parser.yy b/lily/parser.yy index 159786b93c..83e95491c2 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -229,7 +229,7 @@ static Music *make_music_with_input (SCM name, Input where); SCM check_scheme_arg (Lily_parser *parser, Input loc, SCM arg, SCM args, SCM pred, SCM disp = SCM_UNDEFINED); SCM make_music_from_simple (Lily_parser *parser, Input loc, SCM pitch); -SCM loc_on_music (Lily_parser *parser, Input loc, SCM arg); +SCM loc_on_copy (Lily_parser *parser, Input loc, SCM arg); SCM make_chord_elements (Input loc, SCM pitch, SCM dur, SCM modification_list); SCM make_chord_step (SCM step, Rational alter); SCM make_simple_markup (SCM a); @@ -500,8 +500,8 @@ lookup: LOOKUP_IDENTIFIER | LOOKUP_IDENTIFIER '.' symbol_list_rev { - $$ = loc_on_music (parser, @$, - nested_property ($1, scm_reverse_x ($3, SCM_EOL))); + $$ = loc_on_copy (parser, @$, + nested_property ($1, scm_reverse_x ($3, SCM_EOL))); } ; @@ -1992,7 +1992,7 @@ function_arglist_backup: $$ = scm_cons ($$, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (SCM_ARG, $4, @4); } } @@ -2003,7 +2003,7 @@ function_arglist_backup: { $$ = scm_cons ($4, $3); } else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (EVENT_IDENTIFIER, $4, @4); } } @@ -2019,7 +2019,7 @@ function_arglist_backup: } else if (scm_is_true (scm_call_1 ($2, $4))) $$ = scm_cons ($4, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (PITCH_IDENTIFIER, $4, @4); } } @@ -2035,7 +2035,7 @@ function_arglist_backup: } else if (scm_is_true (scm_call_1 ($2, $4))) $$ = scm_cons ($4, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (TONICNAME_PITCH, $4, @4); } } @@ -2044,7 +2044,7 @@ function_arglist_backup: if (scm_is_true (scm_call_1 ($2, $4))) $$ = scm_cons ($4, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (SCM_IDENTIFIER, $4, @4); } } @@ -2066,11 +2066,11 @@ function_arglist_backup: ($2, make_music_from_simple (parser, @4, d)))) MYREPARSE (@4, $2, DURATION_ARG, d); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (UNSIGNED, $4, @4); } } else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (UNSIGNED, $4, @4); } } @@ -2082,7 +2082,7 @@ function_arglist_backup: $$ = $3; MYREPARSE (@4, $2, REAL, $4); } else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (REAL, $4, @4); } } @@ -2092,7 +2092,7 @@ function_arglist_backup: { $$ = scm_cons ($4, $3); } else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (NUMBER_IDENTIFIER, $4, @4); } } @@ -2109,7 +2109,7 @@ function_arglist_backup: if (scm_is_true (scm_call_1 ($2, $$))) $$ = scm_cons ($$, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (UNSIGNED, $5, @5); parser->lexer_->push_extra_token (@4, '-'); } @@ -2122,7 +2122,7 @@ function_arglist_backup: MYREPARSE (@5, $2, REAL, n); $$ = $3; } else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (REAL, n, @5); } } @@ -2132,7 +2132,7 @@ function_arglist_backup: if (scm_is_true (scm_call_1 ($2, n))) { $$ = scm_cons (n, $3); } else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (NUMBER_IDENTIFIER, n, @5); } } @@ -2146,7 +2146,7 @@ function_arglist_backup: ($2, make_music_from_simple (parser, @4, $4)))) MYREPARSE (@4, $2, DURATION_ARG, $4); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (DURATION_IDENTIFIER, $4, @4); } } @@ -2161,7 +2161,7 @@ function_arglist_backup: else $$ = scm_cons (res, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (SCM_IDENTIFIER, $4, @4); } } @@ -2176,7 +2176,7 @@ function_arglist_backup: else $$ = scm_cons (res, $3); else { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); MYBACKUP (STRING, $4, @4); } } @@ -2215,7 +2215,7 @@ function_arglist: function_arglist_nonbackup | EXPECT_OPTIONAL EXPECT_SCM function_arglist_skip_nonbackup DEFAULT { - $$ = scm_cons (loc_on_music (parser, @4, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @4, $1), $3); } ; @@ -2223,7 +2223,7 @@ function_arglist_skip_nonbackup: function_arglist_nonbackup | EXPECT_OPTIONAL EXPECT_SCM function_arglist_skip_nonbackup { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); } ; @@ -2483,7 +2483,7 @@ function_arglist_optional: function_arglist_backup | EXPECT_OPTIONAL EXPECT_SCM function_arglist_skip_backup DEFAULT { - $$ = scm_cons (loc_on_music (parser, @4, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @4, $1), $3); } | function_arglist_skip_backup BACKUP ; @@ -2492,7 +2492,7 @@ function_arglist_skip_backup: function_arglist_backup | EXPECT_OPTIONAL EXPECT_SCM function_arglist_skip_backup { - $$ = scm_cons (loc_on_music (parser, @3, $1), $3); + $$ = scm_cons (loc_on_copy (parser, @3, $1), $3); } ; @@ -4146,7 +4146,7 @@ SCM check_scheme_arg (Lily_parser *parser, Input loc, return args; } -SCM loc_on_music (Lily_parser *parser, Input loc, SCM arg) +SCM loc_on_copy (Lily_parser *parser, Input loc, SCM arg) { if (Music *m = unsmob<Music> (arg)) { @@ -4154,6 +4154,34 @@ SCM loc_on_music (Lily_parser *parser, Input loc, SCM arg) m->set_spot (parser->lexer_->override_input (loc)); return m->unprotect (); } + if (Book *b = unsmob<Book> (arg)) + { + b = b->clone (); + b->origin ()->set_spot (parser->lexer_->override_input (loc)); + return b->unprotect (); + } + if (Context_def *cd = unsmob<Context_def> (arg)) + { + cd = cd->clone (); + cd->origin ()->set_spot (parser->lexer_->override_input (loc)); + return cd->unprotect (); + } + if (Output_def *od = unsmob<Output_def> (arg)) + { + od = od->clone (); + od->input_origin_ = parser->lexer_->override_input (loc); + return od->unprotect (); + } + if (Score *s = unsmob<Score> (arg)) + { + s = s->clone (); + s->origin ()->set_spot (parser->lexer_->override_input (loc)); + return s->unprotect (); + } + if (Context_mod *cm = unsmob<Context_mod> (arg)) + { + return cm->smobbed_copy (); + } return arg; } diff --git a/lily/slur-engraver.cc b/lily/slur-engraver.cc index 1a06ca6422..80ceb5f431 100644 --- a/lily/slur-engraver.cc +++ b/lily/slur-engraver.cc @@ -189,14 +189,14 @@ Slur_engraver::finalize () } void -Slur_engraver::create_slur (const string &spanner_id, Event_info evi, Grob *g_cause, Direction dir, bool left_broken) +Slur_engraver::create_slur (SCM spanner_id, Event_info evi, Grob *g_cause, Direction dir, bool left_broken) { Grob *ccc = left_broken ? unsmob<Grob> (get_property ("currentCommandColumn")) : 0; // efficiency SCM cause = evi.slur_ ? evi.slur_->self_scm () : g_cause->self_scm (); Spanner *slur = make_spanner (grob_symbol (), cause); - slur->set_property ("spanner-id", ly_string2scm (spanner_id)); + slur->set_property ("spanner-id", spanner_id); if (dir) set_grob_direction (slur, dir); if (left_broken) @@ -209,7 +209,7 @@ Slur_engraver::create_slur (const string &spanner_id, Event_info evi, Grob *g_ca { set_grob_direction (slur, DOWN); slur = make_spanner (grob_symbol (), cause); - slur->set_property ("spanner-id", ly_string2scm (spanner_id)); + slur->set_property ("spanner-id", spanner_id); set_grob_direction (slur, UP); if (left_broken) slur->set_bound (LEFT, ccc); @@ -221,7 +221,7 @@ Slur_engraver::create_slur (const string &spanner_id, Event_info evi, Grob *g_ca } bool -Slur_engraver::can_create_slur (const string &id, vsize old_slurs, vsize *event_idx, Stream_event *ev) +Slur_engraver::can_create_slur (SCM id, vsize old_slurs, vsize *event_idx, Stream_event *ev) { for (vsize j = slurs_.size (); j--;) { @@ -229,7 +229,7 @@ Slur_engraver::can_create_slur (const string &id, vsize old_slurs, vsize *event_ Direction updown = to_dir (ev->get_property ("direction")); // Check if we already have a slur with the same spanner-id. - if (id == robust_scm2string (slur->get_property ("spanner-id"), "")) + if (ly_is_equal (id, slur->get_property ("spanner-id"))) { if (j < old_slurs) { @@ -280,13 +280,13 @@ Slur_engraver::can_create_slur (const string &id, vsize old_slurs, vsize *event_ bool Slur_engraver::try_to_end (Event_info evi) { - string id = robust_scm2string (evi.slur_->get_property ("spanner-id"), ""); + SCM id = evi.slur_->get_property ("spanner-id"); // Find the slurs that are ended with this event (by checking the spanner-id) bool ended = false; for (vsize j = slurs_.size (); j--;) { - if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), "")) + if (ly_is_equal (id, slurs_[j]->get_property ("spanner-id"))) { ended = true; end_slurs_.push_back (slurs_[j]); @@ -305,16 +305,14 @@ Slur_engraver::process_music () { for (vsize i = 0; i < stop_events_.size (); i++) { - string id = robust_scm2string - (stop_events_[i].slur_->get_property ("spanner-id"), ""); + SCM id = stop_events_[i].slur_->get_property ("spanner-id"); bool ended = try_to_end (stop_events_[i]); if (ended) { // Ignore redundant stop events for this id for (vsize j = stop_events_.size (); --j > i;) { - if (id == robust_scm2string - (stop_events_[j].slur_->get_property ("spanner-id"), "")) + if (ly_is_equal (id, stop_events_[j].slur_->get_property ("spanner-id"))) stop_events_.erase (stop_events_.begin () + j); } } @@ -326,7 +324,7 @@ Slur_engraver::process_music () for (vsize i = start_events_.size (); i--;) { Stream_event *ev = start_events_[i].slur_; - string id = robust_scm2string (ev->get_property ("spanner-id"), ""); + SCM id = ev->get_property ("spanner-id"); Direction updown = to_dir (ev->get_property ("direction")); if (can_create_slur (id, old_slurs, &i, ev)) diff --git a/ly/grace-init.ly b/ly/grace-init.ly index 6fe32f49fb..a46111e4d9 100644 --- a/ly/grace-init.ly +++ b/ly/grace-init.ly @@ -1,7 +1,7 @@ \version "2.17.6" -startGraceSlur = #(make-music 'SlurEvent 'span-direction START 'spanner-id "grace") -stopGraceSlur = #(make-music 'SlurEvent 'span-direction STOP 'spanner-id "grace") +startGraceSlur = #(make-music 'SlurEvent 'span-direction START 'spanner-id 'grace) +stopGraceSlur = #(make-music 'SlurEvent 'span-direction STOP 'spanner-id 'grace) startGraceMusic = { diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index ee9fdb8c20..76c2ec64b8 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -1551,7 +1551,12 @@ retrograde = #(define-music-function (music) (ly:music?) (_i "Return @var{music} in reverse order.") - (retrograde-music music)) + (retrograde-music + (expand-repeat-notes! + (expand-repeat-chords! + (cons 'rhythmic-event + (ly:parser-lookup '$chord-repeat-events)) + music)))) revertTimeSignatureSettings = #(define-music-function diff --git a/ly/spanners-init.ly b/ly/spanners-init.ly index 756a55579e..8d8a69444f 100644 --- a/ly/spanners-init.ly +++ b/ly/spanners-init.ly @@ -1,18 +1,15 @@ \version "2.19.29" "\\=" = -#(define-event-function (id event) (number-or-string? ly:event?) +#(define-event-function (id event) (key? ly:event?) (_i "This sets the @code{spanner-id} property of the following -@var{event} to the given @var{id} (numbers will be converted to a -string). This can be used to tell LilyPond how to connect overlapping +@var{event} to the given @var{id} (non-negative integer or symbol). +This can be used to tell LilyPond how to connect overlapping or parallel slurs or phrasing slurs within a single @code{Voice}. @lilypond[quote,verbatim] \\fixed c' { c\\=1( d\\=2( e\\=1) f\\=2) } @end lilypond\n") - (set! (ly:music-property event 'spanner-id) - (if (number? id) - (number->string id) - id)) + (set! (ly:music-property event 'spanner-id) id) event) startGroup = #(make-span-event 'NoteGroupingEvent START) diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index cb9103a6fc..7ca44b6f3d 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -992,7 +992,7 @@ override: \\override MultiMeasureRest #'spacing-pair = #'(staff-bar . staff-bar) @end example") - (spanner-id ,string? "An identifier to distinguish concurrent spanners.") + (spanner-id ,key? "An identifier to distinguish concurrent spanners.") (springs-and-rods ,boolean? "Dummy variable for triggering spacing routines.") (stacking-dir ,ly:dir? "Stack objects in which direction?") diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index f525edd69a..6ca25b586e 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -1787,7 +1787,6 @@ (height-limit . 2.0) (minimum-length . 1.5) (ratio . 0.333) - (spanner-id . "") (springs-and-rods . ,ly:spanner::set-spacing-rods) (stencil . ,ly:slur::print) (thickness . 1.1) @@ -1957,7 +1956,6 @@ (line-thickness . 0.8) (minimum-length . 1.5) (ratio . 0.25) - (spanner-id . "") (springs-and-rods . ,ly:spanner::set-spacing-rods) (stencil . ,ly:slur::print) (thickness . 1.2) diff --git a/scm/define-music-properties.scm b/scm/define-music-properties.scm index 4df3f93340..f8c567d1fa 100644 --- a/scm/define-music-properties.scm +++ b/scm/define-music-properties.scm @@ -183,7 +183,7 @@ If zero, signals a beat containing varying durations.") Options are @code{'text} and @code{'hairpin}.") (span-text ,markup? "The displayed text for dynamic text spanners (e.g., cresc.)") - (spanner-id ,string? "Identifier to distinguish concurrent spanners.") + (spanner-id ,key? "Identifier to distinguish concurrent spanners.") (start-callback ,procedure? "Function to compute the negative length of starting grace notes. This property can only be defined as initializer in @file{scm/@/define-music-types.scm}.") diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm index e0190eda55..348bcd0e87 100644 --- a/scm/define-music-types.scm +++ b/scm/define-music-types.scm @@ -438,7 +438,6 @@ goes down).") . ((description . "Start or end phrasing slur. Syntax: @var{note}@code{\\(} and @var{note}@code{\\)}") - (spanner-id . "") (types . (post-event span-event event phrasing-slur-event)) )) @@ -570,7 +569,6 @@ Syntax: @code{\\skip} @var{duration}") . ((description . "Start or end slur. Syntax: @var{note}@code{(} and @var{note}@code{)}") - (spanner-id . "") (types . (post-event span-event event slur-event)) )) diff --git a/scm/modal-transforms.scm b/scm/modal-transforms.scm index 98e9ac3021..f9e26ed360 100644 --- a/scm/modal-transforms.scm +++ b/scm/modal-transforms.scm @@ -185,32 +185,110 @@ Typically used to construct a scale for input to (define-public (retrograde-music music) "Returns @var{music} in retrograde (reversed) order." - ;; Copied from LSR #105 and renamed. ;; Included here to allow this module to provide a complete set of ;; common formal operations on motives, i.e transposition, ;; inversion and retrograding. - (let* ((elements (ly:music-property music 'elements)) - (arts (ly:music-property music 'articulations)) - (reversed (reverse elements)) - (element (ly:music-property music 'element)) - (span-dir (ly:music-property music 'span-direction))) - - (ly:music-set-property! music 'elements reversed) - - (for-each retrograde-music arts) - - (if (ly:music? element) - (ly:music-set-property! - music 'element - (retrograde-music element))) - - (if (ly:dir? span-dir) - (ly:music-set-property! music 'span-direction (- span-dir))) - - (for-each retrograde-music reversed) - - music)) + (define (reverse-span! m) + ;; invert direction of two-sided spanners + (let ((spd (ly:music-property m 'span-direction))) + (if (ly:dir? spd) + (begin + (set! (ly:music-property m 'span-direction) (- spd)) + (case (ly:music-property m 'name) + ((CrescendoEvent) + (make-music 'DecrescendoEvent m)) + ((DecrescendoEvent) + (make-music 'CrescendoEvent m)) + (else m))) + m))) + + ;; carryover is a possible list of tie events, the loop returns any + ;; such trailing list from the given expression + (define (loop m carryover) + (define (filter-ties! m carryover field) + (let ((vals (ly:music-property m field))) + (if (pair? vals) + (call-with-values + (lambda () + (partition! (music-type-predicate + '(tie-event glissando-event)) vals)) + (lambda (ties no-ties) + (set! (ly:music-property m field) + (append! (map! reverse-span! no-ties) carryover)) + ties)) + (begin + (if (pair? carryover) + (set! (ly:music-property m field) carryover)) + '())))) + + ;; The reversal will let some prefatory material stay in front of + ;; the following element. Most prominently single + ;; overrides/reverts/sets/unsets and applyContext. This does not + ;; change the position of a clef (which will generally be useless + ;; after retrograding) but it does not jumble the clef change + ;; command internals. Also, stuff like \once\override stays at + ;; the affected element. + + (define (prefatory? m) + (or ((music-type-predicate + '(apply-context apply-output-event layout-instruction-event)) m) + (and + (music-is-of-type? m 'music-wrapper-music) + (prefatory? (ly:music-property m 'element))))) + + (define (musiclistreverse lst) + (let loop ((lst lst) (res '()) (zeros '())) + (cond ((null? lst) (reverse! zeros res)) + ((prefatory? (car lst)) + (loop (cdr lst) res (cons (car lst) zeros))) + (else + (loop (cdr lst) (reverse! zeros (cons (car lst) res)) '()))))) + + (cond ((music-is-of-type? m 'event-chord) + (let* ((chord-ties + (append! + (filter-ties! m carryover 'elements) + ;; articulations on an event-chord do not occur + ;; "naturally" but are supported when user-generated + ;; elsewhere, so we treat them properly + (filter-ties! m '() 'articulations))) + ;; in-chord ties are converted to per-chord ties. + ;; This is less than optimal but pretty much the + ;; best we can hope to achieve with this approach. + (element-ties + (append-map! + (lambda (m) (filter-ties! m '() 'articulations)) + (ly:music-property m 'elements)))) + (append! chord-ties element-ties))) + + ((music-is-of-type? m 'rhythmic-event) + (filter-ties! m carryover 'articulations)) + + ;; The following is hardly correct but tieing inside of + ;; <<...>> is really beyond our pay grade. + ((music-is-of-type? m 'simultaneous-music) + (append-map! (lambda (m) (loop m (ly:music-deep-copy carryover))) + (ly:music-property m 'elements))) + (else + (let ((elt (ly:music-property m 'element)) + (elts (ly:music-property m 'elements))) + (let ((res + (fold loop + (if (ly:music? elt) (loop elt carryover) carryover) + elts))) + (if (ly:music? elt) + (set! (ly:music-property m 'element) + (reverse-span! elt))) + (if (pair? elts) + (set! (ly:music-property m 'elements) + (map! reverse-span! (musiclistreverse elts)))) + (append! res (filter-ties! m '() 'articulations))))))) + (let ((dangling (loop music '()))) + (for-each + (lambda (t) (ly:music-warning t (_ "Dangling tie in \\retrograde"))) + dangling)) + music) (define-public (pitch-invert around to music) "If @var{music} is a single pitch, inverts it about @var{around} |