]> git.donarmstrong.com Git - lilypond.git/blobdiff - ly/articulate.ly
Web-ja: update introduction
[lilypond.git] / ly / articulate.ly
index 5afb4adfe7d132e71b35b4bced2c03244ab1b709..2b418cee7b265e13083e5aeec1eae4595ab61425 100644 (file)
 % replacing notes  with sequential music of suitably time-scaled note plus
 % skip.
 %
-% It also tries to unfold trills turns etc., and take rallentendo
-% and accelerando into account.
+% Trills, turns, mordents and pralls are expanded with rallentendo
+% and accelerando taken into account.
 %
 % As my scheme knowledge is poor (I was teaching myself as I went), there
 % is much scope for improvement.
 
+% See: http://nicta.com.au/people/chubbp/articulate for additional
+% information about how the articulate function works.
+
+%%% Supported items:
+% Articulations on a single note (staccato, staccatissimo, portato, tenuto).
+% Slurs and phrasing slurs.
+% Ornaments (i.e. mordents, trills, turns).
+% Rallentando, accelerando, ritard and 'a tempo'.
+%
+% Please refer to 'MIDI output' (Section 3.5) in the Notation Reference
+% Manual for a more detailed list of supported items.
+
+%%% Technical Details:
+% * Any note not under a slur or phrasing slur, and not marked with an
+%   explicit articulation, is shortened by ac:normalFactor (default 7/8).
+%   (Shortening a note means replacing the note with a note of a smaller
+%   duration, and a rest to make up for the difference between the durations
+%   of the original and the shortened note.)
+% * Notes marked with articulations are shortened by factors specific to the
+%   articulation as follows:
+%     staccato       not under a slur: ac:staccatoFactor (default 1/2)
+%                    under a slur: ac:portatoFactor (default 3/4)
+%     staccatissimo  ac:staccatissimoFactor (default 1/4)
+%     portato        ac:portatoFactor (default 3/4)
+%     tenuto         ac:tenutoFactor (default 1/1 - by default, notes marked
+%                                     tenuto are not shortened)
+% * Appoggiaturas are made to take half the value of the note following,
+%   without taking dots into account (so in \appoggiatura c8 d2. the c
+%   will take the time of a crotchet).
+% * Trills and turns are expanded. The algorithm tries to choose notes
+%   within the time of the current tempo that lead to each twiddle being
+%   around 1/8 seconds; this can be adjusted with the ac:maxTwiddleTime
+%   variable.
+% * Rall, poco rall and a tempo are observed. It'd be fairly trivial to
+%   make accel. and stringendo and so on work too.
+
 %
 %%%USAGE
 % In the \score section do:
-%  \unfoldRepeats \articulate <<
-%      all the rest of the score
+% \articulate <<
+%       all the rest of the score
 % >>
 % or use the lilywrap script.
 %
-% TO DO (prioritised, the ones that'll make the most difference first)
+% TO DO:
 %
 % * Dynamics.
 %   * Fix quantisation for dynamics on single note (replace note
 % * Cope with more ornaments/articulations.
 %    inverted-turns, etc.
 %   -- accent needs better control of dynamics.
-%   -- Articulations: mezzo-staccato, portato.
 %   -- Handling of generic ornaments (in lily, `\stopped'; in
-%              most early music:  ornament this note (trill, turn
-%              or mordent as the player wishes))
+%               most early music:  ornament this note (trill, turn
+%               or mordent as the player wishes))
+
 % * Automatic gruppetto at end of trill; better handling of
 %      initial/final grace notes on trill
+
 % * Automatic ornaments.
 %   * Spot cadences and ornament
 %   * Look for quaver-dotted note for trills, for example.
 % * accidentals for trills and turns
 
 % CHANGELOG
+%  * Heikki Tauriainen: handle also the \portato articulation (both as an
+%    explicit articulation, and as the articulation to use for slurred
+%    notes marked \staccato).
+%  * David Kastrup: remove redefinitions of \afterGrace and \appoggiatura
+%    and let their actions be performed when \articulate is called by
+%    recognizing and replacing LilyPond's default code for these constructs.
+%    Cf issue 4517 in LilyPond's tracker.
+%  * David Kastrup: basic 2.15.28 compatibility by using event-chord-wrap!
+%    This should really be done by rewriting the code more thoroughly.
 %  * From Iain Nicol: appoggiatura timings were out; add staccatissimo; fix
 %    trillSpanner endpoints.
 %  * Also handle Breathing events (by throwing them away).  This isn't ideal;
 %    how to do lookahead in scheme.
 %  * Also ignore explicit line breaks.
 %  * Add Mordents (reported by Patrick Karl)
-%
+%  * Thomas Morley: extend unfold-repeats to reflect the possibility to
+%    customize its effect to user-settable repeat-types. Here the most general
+%    setting is hard-coded, resulting in unchanged behaviour.
 
-\version "2.13.58"
+\version "2.19.22"
 
+#(use-modules (srfi srfi-1))
+#(use-modules (srfi srfi-11))
 #(use-modules (ice-9 debug))
 #(use-modules (scm display-lily))
 
 % How much to compress notes marked staccatissimo.
 #(define ac:staccatissimoFactor '(1 . 4))
 
+% Shortening factor for notes marked portato (or slurred notes marked
+% staccato).
+#(define ac:portatoFactor '(3 . 4))
+
 % And tenuto (if we ever implement time stealing, this should be >1.0)
 #(define ac:tenutoFactor '(1 . 1))
 
 
 % How much to slow down for a rall. or a poco rall.
 % (or speed up for accel or poco accel)
-#(define ac:rallFactor (ly:make-moment 60 100)) % 40% slowdown
-#(define ac:pocoRallFactor (ly:make-moment 90 100)) % 10% slowdown
+#(define ac:rallFactor (ly:make-moment 60/100)) % 40% slowdown
+#(define ac:pocoRallFactor (ly:make-moment 90/100)) % 10% slowdown
 
 % The absolute time for a twiddle in a trill, in minutes.
 % Start with 1/4 seconds == 1/240 minutes
-#(define ac:maxTwiddleTime (ly:make-moment 1 240))
+#(define ac:maxTwiddleTime (ly:make-moment 1/240))
+
+% How long ordinary grace notes should be relative to their notated
+% duration.  9/40 is LilyPond's built-in behaviour for MIDI output
+% (though the notation reference says 1/4).
+#(define ac:defaultGraceFactor 9/40)
+
+% What proportion of an ordinary grace note's time should be stolen
+% from preceding notes (as opposed to stealing from the principal note).
+% Composers' intentions for this vary.  Taking all from the preceding
+% notes is LilyPond's built-in behaviour for MIDI output.
+#(define ac:defaultGraceBackwardness 1)
 
 
 % Internal variables, don't touch.
                 (cons 6 0))))
 
 
-#(define ac:currentTempo (ly:make-moment 15 1)) % 4 = 60, measured wholes per minute
+#(define ac:currentTempo (ly:make-moment 15/1)) % 4 = 60, measured wholes per minute
 #(define ac:lastTempo ac:currentTempo) % for 'a tempo' or 'tempo I'
 
 % The duration of the current note.  Start at a crotchet
 % for no good reason.
-#(define ac:currentDuration (ly:make-duration 2 0 1 1))
+#(define ac:currentDuration (ly:make-duration 2 0 1/1))
+
+% Amount of musical time (in whole notes) that we need to steal from the
+% next events seen.
+#(define ac:stealForward 0)
+
+% List of events in the output so far, in reverse order, from which we can
+% steal time.
+#(define ac:eventsBackward '())
+
+% Log events for the backward chain.
+#(define (ac:logEventsBackward music)
+  (music-map
+   (lambda (m)
+    (case (ly:music-property m 'name)
+     ((EventChord)
+      (set! ac:eventsBackward (cons m ac:eventsBackward))
+      m)
+     ((BarCheck SkipMusic)
+      (let ((wm (make-sequential-music (list m))))
+       (set! ac:eventsBackward (cons wm ac:eventsBackward))
+       wm))
+     (else
+      m)))
+   music))
+
+% Steal time from the backward chain.  Adds to ac:stealForward (with a
+% warning) if it couldn't backward-steal all that was desired.
+#(define (ac:stealTimeBackward tosteal)
+  (if (<= tosteal 0)
+   #t
+   (if (null? ac:eventsBackward)
+    (begin
+     (ly:warning (_ "articulation failed to steal ~a note backward at beginning of music; stealing forward instead") tosteal)
+     (set! ac:stealForward (+ ac:stealForward tosteal)))
+    (let*
+     ((lastev (car ac:eventsBackward))
+      (levlen (ly:moment-main (ly:music-length lastev))))
+     (if (< tosteal levlen)
+      (begin
+       (ly:music-compress lastev (ly:make-moment (/ (- levlen tosteal) levlen)))
+       #t)
+      (begin
+       (if (any (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+                (ly:music-property lastev 'elements))
+        (ly:warning (_ "stealing the entirety of a note's time")))
+       (set! (ly:music-property lastev 'elements) '())
+       (set! ac:eventsBackward (cdr ac:eventsBackward))
+       (ac:stealTimeBackward (- tosteal levlen))))))))
 
 % Debugging: display a moment plus some text.
 % Returns its moment argument so can be used in-line.
 % Raise note one step in the current diatonic scale.
 #(define (ac:up note)
   (let* ((pitch (ly:music-property note 'pitch))
-        (notename (ly:pitch-notename pitch))
-        (new-notename (if (eq? notename 6) 0 (+ 1 notename)))
-        (alterations (ly:music-property ac:current-key 'pitch-alist))
-        (new-alteration (cdr (assq new-notename alterations)))
-        (new-octave (if (eq? new-notename 0) (+ 1 (ly:pitch-octave pitch))
-                     (ly:pitch-octave pitch)))
+         (notename (ly:pitch-notename pitch))
+         (new-notename (if (eqv? notename 6) 0 (+ 1 notename)))
+         (alterations (ly:music-property ac:current-key 'pitch-alist))
+         (new-alteration (cdr (assq new-notename alterations)))
+         (new-octave (if (eqv? new-notename 0) (+ 1 (ly:pitch-octave pitch))
+                      (ly:pitch-octave pitch)))
        )
    (set! (ly:music-property note 'pitch)(ly:make-pitch new-octave new-notename new-alteration))))
 
 % Lower note one step in the current diatonic scale.
 #(define (ac:down note)
   (begin  (let* ((pitch (ly:music-property note 'pitch))
-        (notename (ly:pitch-notename pitch))
-        (new-notename (if (eq? notename 0) 6 (- notename 1)))
-        (alterations (ly:music-property ac:current-key 'pitch-alist))
-        (new-alteration (cdr (assq new-notename alterations)))
-        (new-octave (if (eq? new-notename 6) (- (ly:pitch-octave pitch) 1)
-                     (ly:pitch-octave pitch)))
+         (notename (ly:pitch-notename pitch))
+         (new-notename (if (eqv? notename 0) 6 (- notename 1)))
+         (alterations (ly:music-property ac:current-key 'pitch-alist))
+         (new-alteration (cdr (assq new-notename alterations)))
+         (new-octave (if (eqv? new-notename 6) (- (ly:pitch-octave pitch) 1)
+                      (ly:pitch-octave pitch)))
        )
    (set! (ly:music-property note 'pitch)(ly:make-pitch new-octave new-notename new-alteration))))
 )
 % Used in afterGrace to mark all notes as tenuto, so they're not shortened
 #(define (ac:add-articulation type music)
     (music-map (lambda (m)
-               (if (eq? 'EventChord (ly:music-property m 'name))
-                (set! (ly:music-property m 'elements)
-                 (append (ly:music-property m 'elements)
-                  (list (make-music 'ArticulationEvent 'articulation-type type)))))
-               m)
+                (if (eq? 'EventChord (ly:music-property m 'name))
+                 (set! (ly:music-property m 'elements)
+                  (append (ly:music-property m 'elements)
+                   (list (make-music 'ArticulationEvent 'articulation-type type)))))
+                m)
      music))
 
 % Convert a long note to an equivalent set of short notes, tied together.
 
 #(define (ac:to128_disabled music)
   (if (or (eq? 'SkipEvent (ly:music-property music 'name))
-       (eq? 'NoteEvent (ly:music-property music 'name)))
+        (eq? 'NoteEvent (ly:music-property music 'name)))
    (let* ((dur (ly:music-property music 'duration))
-         (log2 (ly:duration-log dur))
-        (shiftcount (- 6 log2))
-        (lastm (ly:music-deep-copy (shift-duration-log music shiftcount 0))))
+          (log2 (ly:duration-log dur))
+         (shiftcount (- 6 log2))
+         (lastm (ly:music-deep-copy (shift-duration-log music shiftcount 0))))
    (set! (ly:music-property music 'elements)
     (cons (make-music 'TieEvent) (ly:music-property music 'elements)))
    (make-sequential-music (list
-                          (make-repeat "unfold" (1- (expt 2 shiftcount))
-                           (make-sequential-music (list music)) '())
-                          lastm)))
+                           (make-repeat "unfold" (1- (expt 2 shiftcount))
+                            (make-sequential-music (list music)) '())
+                           lastm)))
  music))
 
 
 % If the music has a precomputed twiddletime (e.g., from \afterGrace) use that.
 #(define (ac:twiddletime music)
   (let* ((tr (filter (lambda (x)
-                    (and (eq? 'ArticulationEvent (ly:music-property x 'name))
-                     (string= "trill" (ly:music-property x 'articulation-type))))
-             (ly:music-property music 'elements)))
-        (pre-t (if (pair? tr) (ly:music-property (car tr) 'twiddle)
-                '()))
-        (t (ac:targetTwiddleTime)))
+                     (and (eq? 'ArticulationEvent (ly:music-property x 'name))
+                      (string= "trill" (ly:music-property x 'articulation-type))))
+              (ly:music-property music 'elements)))
+         (pre-t (if (pair? tr) (ly:music-property (car tr) 'twiddle)
+                 '()))
+         (hemisemimom (ly:make-moment 1/64))
+         (t (ac:targetTwiddleTime)))
    (if (ly:moment? pre-t)
     pre-t
-    (let loop ((len (ly:music-length music)))
-     (if (ly:moment<? t len)
-      (loop (ly:moment-mul len (ly:make-moment 1 2)))
-      len)))))
+    hemisemimom)))
 
 
 
 #(define (ac:trill music)
   " Replace music with time-compressed repeats of the music,
     maybe accelerating if the length is longer than a crotchet "
-  (let* ((hemisemidur (ly:make-duration 5 0 1 1))
-        (orig-len  (ly:music-length music))
-        (t (ac:twiddletime music))
-        (uppernote '())
-        (note_moment (ly:moment-mul t (ly:make-moment 1 2)))
-        (c1 (ly:moment-div orig-len note_moment))
-        (c2 (inexact->exact
-             (round (/ (ly:moment-main-numerator c1)
-                     (* 2 (ly:moment-main-denominator c1))))))
-        (count (if (< c2 2) 2 c2)))
+  (let* ((hemisemidur (ly:make-duration 5 0 1/1))
+         (orig-len  (ly:music-length music))
+         (t (ac:twiddletime music))
+         (uppernote '())
+         (note_moment (ly:moment-mul t (ly:make-moment 1/2)))
+         (c1 (ly:moment-div orig-len t))
+         (c2 (inexact->exact
+              (round (/ (ly:moment-main-numerator c1)
+                      (* 2 (ly:moment-main-denominator c1))))))
+         (count (if (< c2 2) 2 c2)))
 
    (set! (ly:music-property music 'elements)
     (filter (lambda (y) (eq? 'NoteEvent (ly:music-property y 'name)))
    (map (lambda (y) (ac:setduration y hemisemidur))
     (ly:music-property music 'elements))
    (set! uppernote (ly:music-deep-copy music))
-   (map (lambda (y) (ac:up y))
+   (map ac:up
     (filter
      (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
      (ly:music-property uppernote 'elements)))
 
    (let* ((trillMusicElements
-         (let loop ((so_far (list uppernote music))
-                    (c count))
-          (if (> c 1)
-           (loop (append (list (ly:music-deep-copy uppernote) (ly:music-deep-copy music)) so_far) (1- c))
-           so_far)))
-         (trillMusic (make-sequential-music trillMusicElements))
-         (newlen (ly:music-length trillMusic))
-         (factor (ly:moment-div  orig-len newlen)))
+          (let loop ((so_far (list uppernote music))
+                     (c count))
+           (if (> c 1)
+            (loop (append (list (ly:music-deep-copy uppernote) (ly:music-deep-copy music)) so_far) (1- c))
+            so_far)))
+          (trillMusic (make-sequential-music trillMusicElements))
+          (newlen (ly:music-length trillMusic))
+          (factor (ly:moment-div  orig-len newlen)))
     (ly:music-compress trillMusic factor)
 ; accelerating the music seems to put lily into an infinite loop in
 ; its layout and midi engines.
 ;    (let* ((realfactor (exp (* (/ 1.0 count) (log 0.75))))
-;         (factor (ly:make-moment (inexact->exact (round (* 1024 realfactor)))
-;                  1024)))
+;          (factor (ly:make-moment (inexact->exact (round (* 1024 realfactor)))
+;                   1024)))
 ;     (ac:accel trillMusic factor))
  )))
 
 
+% Copy music and strip articulations, ties, etc., for generating
+% mordents etc.
+#(define (ac:note-copy music)
+  "return a copy of music that is only notes, no articulations, ties, slurs etc"
+  (let ((new-music (ly:music-deep-copy music)))
+   (set! (ly:music-property new-music 'articulations) '())
+   (set! (ly:music-property new-music 'elements)
+    (filter (lambda (y) (eq? 'NoteEvent (ly:music-property y 'name)))
+     (ly:music-property new-music 'elements)))
+   new-music))
+
+%
+% Generate a tempoChangeEvent and its associated property setting.
+%
+#(define (ac:tempoChange tempo)
+  (make-sequential-music
+   (list (make-music 'TempoChangeEvent
+          'metronome-count
+          tempo
+          'tempo-unit
+          (ly:make-duration 0 0 1/1))
+    (context-spec-music
+    (make-property-set 'tempoWholesPerMinute  tempo) 'Score))))
 
+%
+% Totally unfold repeats, so that the non-obvious sequencing doesn't
+% confuse us.  This is necessary for time stealing to work, because
+% that relies on the sequence in which we see events matching their
+% audible sequence.  Also unfold multi-measure rests to equivalent
+% skips, with preceding and following bar checks, so that time stealing
+% can change the length of the pause without falling foul of the
+% implicit bar checks.
+%
+#(define (ac:unfoldMusic music)
+  (music-map
+   (lambda (m)
+    (case (ly:music-property m 'name)
+     ((UnfoldedRepeatedMusic)
+      (let
+       ((body (ly:music-property m 'element))
+        (altl (ly:music-property m 'elements))
+        (rc (ly:music-property m 'repeat-count)))
+       (if (null? altl)
+        (make-sequential-music
+         (list-tabulate rc (lambda (i) (ly:music-deep-copy body))))
+        (let ((ealtl (if (> (length altl) rc) (take altl rc) altl)))
+         (make-sequential-music
+          (apply append!
+           (append!
+            (list-tabulate
+             (- rc (length ealtl))
+             (lambda (i) (list (ly:music-deep-copy body) (ly:music-deep-copy (car ealtl)))))
+            (map (lambda (alt) (list (ly:music-deep-copy body) alt)) ealtl))))))))
+     ((EventChord)
+      (let-values
+       (((trem evl)
+         (partition (lambda (v) (eq? (ly:music-property v 'name) 'TremoloEvent))
+          (ly:music-property m 'elements))))
+       (if (null? trem)
+        m
+        (let*
+         ((tremtype (ly:music-property (car trem) 'tremolo-type))
+          (tremtype-log (1- (integer-length tremtype)))
+          (durev (find (lambda (v) (not (null? (ly:music-property v 'duration)))) evl))
+          (totaldur (if durev (ly:music-property durev 'duration) (ly:make-duration tremtype-log 0 1)))
+          (tgt-nrep (/ (duration-visual-length totaldur) (duration-log-factor tremtype-log)))
+          (eff-nrep (max (truncate tgt-nrep) 1))
+          (tremdur (ly:make-duration tremtype-log 0
+                    (* (/ tgt-nrep eff-nrep) (ly:duration-scale totaldur)))))
+         (or (and (= eff-nrep tgt-nrep) (= (ash 1 tremtype-log) tremtype))
+          (ly:warning (_ "non-integer tremolo ~a:~a")
+           (duration->lily-string (duration-visual totaldur) #:force-duration #t #:time-scale 1)
+           tremtype))
+         (for-each
+          (lambda (v)
+           (or (null? (ly:music-property v 'duration))
+            (set! (ly:music-property v 'duration) tremdur)))
+          evl)
+         (set! (ly:music-property m 'elements) evl)
+         (make-sequential-music
+          (list-tabulate eff-nrep (lambda (i) (ly:music-deep-copy m))))))))
+     ((MultiMeasureRestMusic)
+      (make-sequential-music
+       (list
+        (make-music 'BarCheck)
+        (make-music 'SkipMusic 'duration (ly:music-property m 'duration))
+        (make-music 'BarCheck))))
+     (else
+      m)))
+   (unfold-repeats '() music)))
 
 % If there's an articulation, use it.
-% If in a slur, use (1 . 1) instead.
+% If in a slur, use (1 . 1) instead (unless the note is marked staccato,
+% in which case use ac:portatoFactor).
 % Treat phrasing slurs as slurs, but allow explicit articulation.
-% (Maybe should treat staccato under a phrasing slur as mezzo-staccato?)
 %
 % Expect an EventChord.
 %
 % trills, turns, ornaments etc.  are also treated as Articulations.
 % Split into two functions:
 %  ac:getactions traverses the elements in the EventChord
-%              and calculates the parameters.
+%               and calculates the parameters.
 %  ac:articulate-chord applies the actions to each NoteEvent in
-%              the EventChord.
+%               the EventChord.
 #(define (ac:getactions music)
-  (let  loop ((factor ac:normalFactor)
-             (newelements '())
-             (es (ly:music-property music 'elements))
-             (actions '()))
-   (if (null? es)
-    (begin
-     (set! (ly:music-property music 'elements) (reverse newelements))
-     (cond
-      (ac:inTrill (cons 'trill actions))
-      ((and (eq? factor ac:normalFactor) (or ac:inSlur ac:inPhrasingSlur))
-       (append actions (list 'articulation  '(1 . 1)) ))
-      (else (append actions (list 'articulation  factor)))))
-    ; else part
-    (let ((e (car es))
-         (tail (cdr es)))
-     (case (ly:music-property e 'name)
-
-      ((BeamEvent) ; throw away beam events, or they'll be duplicated by turn or trill
-       (loop factor newelements tail actions))
-      ((LineBreakEvent) ; pass through linebreak events.
-       (loop (cons 1 1) (cons e newelements) tail actions))
-      ((FingeringEvent) ; and fingering events too.
-       (loop factor newelements tail actions))
-
-      ((BreathingEvent) ; throw away BreathingEvent ---
-       ; should really shorten previous note a little.
-       (loop (cons 1 1) (cons e newelements) tail actions))
-
-      ((TieEvent)
-       (loop (cons 1 1) (cons e newelements) tail actions))
-
-      ((SkipEvent)
-       (loop (cons 1 1) (cons e newelements) tail actions))
-
-      ((RestEvent)
-       (loop (cons 1 1) (cons e newelements) tail actions))
-
-      ((ArticulationEvent)
-       (let ((articname (ly:music-property e 'articulation-type)))
-       ; TODO: add more here
-       (cond
-        ((string= articname "staccato")
-         (loop ac:staccatoFactor newelements tail actions))
-        ((string= articname "staccatissimo")
-         (loop ac:staccatissimoFactor newelements tail actions))
-        ((string= articname "tenuto")
-         (loop ac:tenutoFactor newelements tail actions))
-        ((string= articname "mordent")
-         (loop (cons 1 1) newelements tail (cons 'mordent actions)))
-        ((string= articname "prall")
-         (loop (cons 1 1) newelements tail (cons 'trill actions)))
-        ((string= articname "trill")
-         (loop (cons 1 1) newelements tail (cons 'trill actions)))
-        ((string= articname "turn")
-         (loop (cons 1 1) newelements tail (cons 'turn actions)))
-        (else (loop factor (cons e newelements) tail actions)))))
-
-      ((TextScriptEvent)
-       (let ((t (ly:music-property e 'text)))
-       (if (not (string? t))
-        (loop factor (cons e newelements) tail actions)
-        (begin
-         (cond
-          ((or
-            (string= t "rall")
-            (string= t "Rall")
-            (string= t "rit.")
-            (string= t "rall."))
-           (loop factor (cons e newelements) tail (cons 'rall actions)))
-          ((or
-            (string= t "poco rall.")
-            (string= t "poco rit."))
-           (loop factor (cons e newelements) tail (cons 'pocoRall actions)))
-          ((or (string= t "a tempo")
-            (string= t "tempo I"))
-         (loop factor (cons e newelements) tail (cons 'aTempo actions)))
-          (else (loop factor (cons e newelements) tail actions)))))))
-
-      ((SlurEvent)
-       (let ((direction (ly:music-property e 'span-direction)))
-       (set! ac:inSlur (eq? direction -1))
-       (loop factor newelements tail actions)))
-
-      ((TrillSpanEvent)
-       (let ((direction (ly:music-property e 'span-direction)))
-       (set! ac:inTrill (eq? direction -1))
-       (if ac:inTrill
-        (loop factor newelements tail (cons 'trill actions))
-        (loop factor (cons e newelements) tail actions))))
-
-      ((PhrasingSlurEvent)
-       (let ((direction (ly:music-property e 'span-direction)))
-       (set! ac:inPhrasingSlur (eq? direction -1))
-       (loop factor newelements tail actions)))
-
-      (else (loop factor (cons e newelements) tail actions)))))))
+  (let ((at-end-of-slur #f))
+   (let  loop ((factor ac:normalFactor)
+               (newelements '())
+               (es (ly:music-property music 'elements))
+               (actions '()))
+    (if (null? es)
+     (begin
+      (set! (ly:music-property music 'elements) (reverse newelements))
+      (if
+       (not (any (lambda (m) (music-is-of-type? m 'rhythmic-event))
+                 newelements))
+       actions
+       (append
+        (let ((st ac:stealForward))
+         (if (= st 0)
+          '()
+          (begin
+           (set! ac:stealForward 0)
+           (list 'steal st))))
+        actions
+        (cond
+         (ac:inTrill '(trill))
+         ((and (eq? factor ac:normalFactor) (or ac:inSlur ac:inPhrasingSlur))
+          (list 'articulation  '(1 . 1)))
+         ((and (eq? factor ac:staccatoFactor) (or ac:inSlur at-end-of-slur))
+          (list 'articulation ac:portatoFactor))
+         (else (list 'articulation  factor))))))
+     ; else part
+     (let ((e (car es))
+           (tail (cdr es)))
+      (case (ly:music-property e 'name)
+
+       ((BeamEvent) ; throw away beam events, or they'll be duplicated by turn or trill
+        (loop factor newelements tail actions))
+
+       ((LineBreakEvent FingeringEvent MarkEvent BreathingEvent TieEvent SkipEvent RestEvent) ; pass through some events.
+        (loop (cons 1 1) (cons e newelements) tail actions))
+
+       ((ArticulationEvent)
+        (let ((articname (ly:music-property e 'articulation-type)))
+         ; TODO: add more here
+         (cond
+          ((string= articname "staccato")
+           (loop ac:staccatoFactor newelements tail actions))
+          ((string= articname "staccatissimo")
+           (loop ac:staccatissimoFactor newelements tail actions))
+          ((string= articname "portato")
+           (loop ac:portatoFactor newelements tail actions))
+          ((string= articname "tenuto")
+           (loop ac:tenutoFactor newelements tail actions))
+          ((string= articname "mordent")
+           (loop (cons 1 1) newelements tail (cons 'mordent actions)))
+          ((string= articname "prall")
+           (loop (cons 1 1) newelements tail (cons 'prall actions)))
+          ((string= articname "trill")
+           (loop (cons 1 1) newelements tail (cons 'trill actions)))
+          ((string= articname "turn")
+           (loop (cons 1 1) newelements tail (cons 'turn actions)))
+          (else (loop factor (cons e newelements) tail actions)))))
+
+       ((TextScriptEvent)
+        (let ((t (ly:music-property e 'text)))
+         (if (not (string? t))
+          (loop factor (cons e newelements) tail actions)
+          (begin
+           (cond
+            ((or
+              (string= t "rall")
+              (string= t "Rall")
+              (string= t "rit.")
+              (string= t "rall."))
+             (loop factor (cons e newelements) tail (cons 'rall actions)))
+            ((or
+              (string= t "accelerando")
+              (string= t "accel")
+              (string= t "accel."))
+             (loop factor (cons e newelements) tail (cons 'accel actions)))
+            ((or
+              (string= t "poco accel."))
+             (loop factor (cons e newelements) tail (cons 'pocoAccel actions)))
+            ((or
+              (string= t "poco rall.")
+              (string= t "poco rit."))
+             (loop factor (cons e newelements) tail (cons 'pocoRall actions)))
+            ((or (string= t "a tempo")
+              (string= t "tempo I"))
+           (loop factor (cons e newelements) tail (cons 'aTempo actions)))
+            (else (loop factor (cons e newelements) tail actions)))))))
+
+       ((SlurEvent)
+        (let ((direction (ly:music-property e 'span-direction)))
+         (set! ac:inSlur (eqv? direction -1))
+         (set! at-end-of-slur (eqv? direction 1))
+         (loop factor newelements tail actions)))
+
+       ((TrillSpanEvent)
+        (let ((direction (ly:music-property e 'span-direction)))
+         (set! ac:inTrill (eqv? direction -1))
+         (if ac:inTrill
+          (loop factor newelements tail (cons 'trill actions))
+          (loop factor (cons e newelements) tail actions))))
+
+       ((PhrasingSlurEvent)
+        (let ((direction (ly:music-property e 'span-direction)))
+         (set! ac:inPhrasingSlur (eqv? direction -1))
+         (loop factor newelements tail actions)))
+
+       (else (loop factor (cons e newelements) tail actions))))))))
 
 
 
 #(define (ac:articulate-chord music)
-  (begin
-   (cond
-
-    ((eq? 'EventChord (ly:music-property music 'name))
+  (cond
+   ((eq? 'EventChord (ly:music-property music 'name))
+    (ac:logEventsBackward
      (let loop ((actions (ac:getactions music)))
       (if (null? actions)
-       (if (ly:moment> (ly:music-length music) (make-moment 1 4))
-        (ac:to128  music)
-        music)
+        (if (ly:moment<? (ly:make-moment 1/4) (ly:music-length music))
+         (ac:to128  music)
+         music)
 
       (case (car actions)
 
        ((articulation)
-       (map
-        (lambda (x) (ac:articulate-one-note x (cadr actions)))
-        (ly:music-property music 'elements))
-       (let*
-        ((num (caadr actions))
-         (denom (cdadr actions))
-         (mult (ly:duration-factor ac:currentDuration))
-         (newnum (* (- denom num) (car mult)))
-         (newdenom (* (cdr mult) denom))
-         (len (ly:duration-log ac:currentDuration))
-         (dots (ly:duration-dot-count ac:currentDuration)))
-
-        (if (not (eq? num denom))
-         (make-sequential-music
-          (list (ac:to128 music)
-          (make-music 'EventChord 'elements
-           (list
-            (make-music 'RestEvent 'duration (ly:make-duration len dots newnum newdenom))))))
-         music)))
+        (map
+         (lambda (x) (ac:articulate-one-note x (cadr actions)))
+         (ly:music-property music 'elements))
+        (let*
+         ((num (caadr actions))
+          (denom (cdadr actions))
+          (mult (ly:duration-factor ac:currentDuration))
+          (newnum (* (- denom num) (car mult)))
+          (newdenom (* (cdr mult) denom))
+          (len (ly:duration-log ac:currentDuration))
+          (dots (ly:duration-dot-count ac:currentDuration)))
+
+         (if (not (eqv? num denom))
+          (make-sequential-music
+           (list (ac:to128 music)
+           (make-music 'EventChord 'elements
+            (list
+             (make-music 'RestEvent 'duration (ly:make-duration len dots newnum newdenom))))))
+          music)))
+
+       ((accel)
+        (set! ac:lastTempo ac:currentTempo)
+        (set! ac:currentTempo (ly:moment-div ac:currentTempo ac:rallFactor))
+        (let ((pset (ac:tempoChange ac:currentTempo)))
+         (if (null? (cdr actions))
+          (make-sequential-music (list pset music))
+          (make-sequential-music
+           (list pset (loop (cdr actions)))))))
+
+       ((pocoAccel)
+        (set! ac:lastTempo ac:currentTempo)
+        (set! ac:currentTempo (ly:moment-div ac:currentTempo ac:pocoRallFactor))
+        (let ((pset (ac:tempoChange ac:currentTempo)))
+         (if (null? (cdr actions))
+          (make-sequential-music (list pset music))
+          (make-sequential-music
+           (list pset (loop (cdr actions)))))))
 
        ((rall)
-       (set! ac:currentTempo (ly:moment-mul ac:currentTempo ac:rallFactor))
-       (let ((pset (make-music 'PropertySet
-          'value
-          ac:currentTempo
-          'symbol
-          'tempoWholesPerMinute)))
-        (if (null? (cdr actions))
-         (make-sequential-music (list pset music))
-         (make-sequential-music
-          (list pset (loop (cdr actions)))))))
+        (set! ac:lastTempo ac:currentTempo)
+        (set! ac:currentTempo (ly:moment-mul ac:currentTempo ac:rallFactor))
+        (let ((pset (ac:tempoChange ac:currentTempo)))
+         (if (null? (cdr actions))
+          (make-sequential-music (list pset music))
+          (make-sequential-music
+           (list pset (loop (cdr actions)))))))
 
        ((pocoRall)
-       (set! ac:currentTempo (ly:moment-mul ac:currentTempo ac:pocoRallFactor))
-       (let ((pset (make-music 'PropertySet
-          'value
-          ac:currentTempo
-          'symbol
-          'tempoWholesPerMinute)))
-        (if (null? (cdr actions))
-         (make-sequential-music (list pset music))
-         (make-sequential-music
-          (list pset (loop (cdr actions)))))))
+        (set! ac:lastTempo ac:currentTempo)
+        (set! ac:currentTempo (ly:moment-mul ac:currentTempo ac:pocoRallFactor))
+        (let ((pset (ac:tempoChange ac:currentTempo)))
+         (if (null? (cdr actions))
+          (make-sequential-music (list pset music))
+          (make-sequential-music
+           (list pset (loop (cdr actions)))))))
 
        ((aTempo)
-       (set! ac:currentTempo ac:lastTempo)
-       (let ((pset (make-music 'PropertySet
-          'value
-          ac:currentTempo
-          'symbol
-          'tempoWholesPerMinute)))
-        (if (null? (cdr actions))
-         (make-sequential-music (list pset music))
-         (make-sequential-music
-          (list pset (loop (cdr actions)))))))
+        (set! ac:currentTempo ac:lastTempo)
+
+        (let ((pset (ac:tempoChange ac:currentTempo)))
+         (if (null? (cdr actions))
+          (make-sequential-music (list pset music))
+          (make-sequential-music
+           (list pset (loop (cdr actions)))))))
 
        ((trill)
-        (ac:trill music))
+         (ac:trill music))
+
+       ((prall)
+        ; A pralltriller symbol can either mean an inverted mordent
+        ; or a half-shake -- a short, two twiddle trill.
+        ; We implement as a half-shake.
+        (let*
+         ((origlength (ly:music-length music))
+          (gracedur (ly:make-duration 5 0 1/1))
+          (gracenote (ac:note-copy music))
+          (abovenote (ac:note-copy music))
+          (abovenoteTwo (ac:note-copy music))
+          (mainnote (ly:music-deep-copy music)))
+
+         (map (lambda (y) (ac:setduration y gracedur))
+          (ly:music-property gracenote 'elements))
+         (map (lambda (y) (ac:setduration y gracedur))
+          (ly:music-property abovenote 'elements))
+         (map (lambda (y) (ac:setduration y gracedur))
+          (ly:music-property abovenoteTwo 'elements))
+         (map ac:up
+          (filter
+           (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+           (ly:music-property abovenote 'elements)))
+         (map ac:up
+          (filter
+           (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+           (ly:music-property abovenoteTwo 'elements)))
+         (let* ((prallMusic (make-sequential-music
+                              (list abovenote gracenote abovenoteTwo mainnote)))
+                 (newlen (ly:music-length prallMusic))
+                 (factor (ly:moment-div origlength newlen)))
+           (ly:music-compress prallMusic factor))))
 
        ((mordent)
-       (let*
-        ((dur (ly:music-property
-               (car (ly:music-property music 'elements)) 'duration))
-         (factor (ly:duration-factor dur))
-         (gracenote (ly:music-deep-copy music))
-         (mainnote (ly:music-deep-copy music))
-         (belownote (ly:music-deep-copy music))
-         (mordent (make-sequential-music (list gracenote belownote)))
-)
-        (begin
-         (music-map (lambda (n)
-          (if (eq? 'NoteEvent (ly:music-property n 'name))
-           (set! (ly:music-property n 'duration)(ly:make-duration 3 0 1 1)))
-                     n)
-          mordent)
-         (map (lambda (y) (ac:down y))
-          (filter
-           (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
-           (ly:music-property belownote 'elements)))
-         (make-sequential-music (list (make-grace-music mordent) mainnote)))))
+        (let*
+         ((origlength (ly:music-length music))
+          (gracedur (ly:make-duration 5 0 1/1))
+          (gracenote (ac:note-copy music))
+          (belownote (ac:note-copy music)))
+         (map (lambda (y) (ac:setduration y gracedur))
+          (ly:music-property gracenote 'elements))
+         (map (lambda (y) (ac:setduration y gracedur))
+               (ly:music-property belownote 'elements))
+         (map ac:down
+          (filter
+           (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+           (ly:music-property belownote 'elements)))
+
+         (let* ((mordentMusic (make-sequential-music (list gracenote belownote music)))
+                (newlen (ly:music-length mordentMusic))
+                (factor (ly:moment-div origlength newlen)))
+          (ly:music-compress mordentMusic factor))))
+
        ((turn)
-       (let*
-        ((dur (ly:music-property
-               (car (ly:music-property music 'elements)) 'duration))
-         (factor (ly:duration-factor dur))
-         (newdur (ly:make-duration (+ (ly:duration-log dur) 2)
-                  (ly:duration-dot-count dur) (car factor)(cdr factor))))
-        (begin
-         (map (lambda (y) (ac:setduration y newdur))
-          (ly:music-property music 'elements))
-         (let* ((above (ly:music-deep-copy music))
-                (below (ly:music-deep-copy music))
-                (newmusic (make-sequential-music (list above music below music))))
-          (begin
-           (map (lambda (y) (ac:down y))
-            (filter
-             (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
-             (ly:music-property below 'elements)))
-           (map (lambda (y) (ac:up y))
-            (filter
-             (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
-             (ly:music-property above 'elements)))
-           newmusic)))))
-     ))))
-
-    ((eq? 'KeyChangeEvent (ly:music-property music 'name))
-     (set! ac:current-key music)
-     music
-   )
-
-    ((eq? 'PropertySet (ly:music-property music 'name))
-     (ac:adjust-props (ly:music-property music 'symbol) music)
-     music)
-
-    (else  music))
- ))
-
-
-
-% At last ... here's the music function that aplies all the above to a
+        (let*
+         ((dur (ly:music-property
+                (car (ly:music-property music 'elements)) 'duration))
+          (factor (ly:duration-factor dur))
+          (newdur (ly:make-duration (+ (ly:duration-log dur) 2)
+                   (ly:duration-dot-count dur) (car factor)(cdr factor))))
+         (begin
+          (map (lambda (y) (ac:setduration y newdur))
+           (ly:music-property music 'elements))
+          (let* ((above (ly:music-deep-copy music))
+                 (below (ly:music-deep-copy music))
+                 (newmusic (make-sequential-music (list above music below music))))
+           (begin
+            (map ac:down
+             (filter
+              (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+              (ly:music-property below 'elements)))
+            (map ac:up
+             (filter
+              (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+              (ly:music-property above 'elements)))
+            newmusic)))))
+       ((steal)
+        (let
+         ((totallen (ly:moment-main (ly:music-length music)))
+          (steallen (cadr actions)))
+         (if (>= steallen totallen)
+          (begin
+           (if (any (lambda (z) (eq? 'NoteEvent (ly:music-property z 'name)))
+                    (ly:music-property music 'elements))
+            (ly:warning (_ "stealing the entirety of a note's time")))
+           (set! ac:stealForward (- steallen totallen))
+           (make-sequential-music '()))
+          (begin
+           (ly:music-compress music (ly:make-moment (/ (- totallen steallen) totallen)))
+           (loop (cddr actions))))))
+     )))))
+
+   ((eq? 'GraceMusic (ly:music-property music 'name))
+    (let
+     ((first-ev
+       (call-with-current-continuation
+        (lambda (yield-fev)
+         (music-map
+          (lambda (m)
+           (if (eq? 'EventChord (ly:music-property m 'name))
+            (yield-fev m)
+            m))
+          music)
+         #f))))
+     (if first-ev
+      (let ((fev-pos (find-tail (lambda (m) (eq? m first-ev)) ac:eventsBackward)))
+       (if fev-pos
+        (set! ac:eventsBackward (cdr fev-pos))
+        (ly:warning (_ "articulation of grace notes has gone awry"))))))
+    (let*
+     ((gmus (ly:music-compress (ly:music-property music 'element)
+                               (ly:make-moment ac:defaultGraceFactor)))
+      (glen (ly:moment-main (ly:music-length gmus))))
+     (ac:stealTimeBackward (* glen ac:defaultGraceBackwardness))
+     (set! ac:stealForward (+ ac:stealForward (* glen (- 1 ac:defaultGraceBackwardness))))
+     gmus))
+
+   ((memq (ly:music-property music 'name) '(BarCheck SkipMusic))
+    (let ((totallen (ly:moment-main (ly:music-length music)))
+          (steallen ac:stealForward))
+     (cond
+      ((= steallen 0)
+       (ac:logEventsBackward music))
+      ((< steallen totallen)
+       (set! ac:stealForward 0)
+       (ac:logEventsBackward
+        (ly:music-compress music (ly:make-moment (/ (- totallen steallen) totallen)))))
+      (else
+       (set! ac:stealForward (- steallen totallen))
+       (make-sequential-music '())))))
+
+   ((eq? 'KeyChangeEvent (ly:music-property music 'name))
+    (set! ac:current-key music)
+    music)
+
+   ((eq? 'PropertySet (ly:music-property music 'name))
+    (ac:adjust-props (ly:music-property music 'symbol) music)
+    music)
+
+   (else music)))
+
+
+
+% At last ... here's the music function that applies all the above to a
 % score.
-articulate = #(define-music-function (parser location music)
-              (ly:music?)
-              "Adjust times of note to add tenuto, staccato and
+articulate = #(define-music-function (music)
+               (ly:music?)
+               "Adjust times of note to add tenuto, staccato and
                 normal articulations.
-               "
-              (music-map ac:articulate-chord music)
-              )
-
+                "
+               (dynamic-wind
+                (lambda ()
+                 (set! ac:stealForward 0)
+                 (set! ac:eventsBackward '()))
+                (lambda ()
+                 (music-map
+                  ac:articulate-chord
+                  (ac:startup-replacements music)))
+                (lambda ()
+                 (or (= ac:stealForward 0)
+                  (begin
+                   (ly:warning (_ "articulation failed to steal ~a note at end of music") ac:stealForward)
+                   (set! ac:stealForward 0)))
+                 (set! ac:eventsBackward '()))))
+
+#(define (ac:startup-replacements music)
+   (fold (lambda (f m) (f m))
+        music
+        (list
+         event-chord-wrap!
+         ac:replace-aftergrace
+         ac:replace-appoggiatura
+         ac:unfoldMusic)))
+
+#(define (ac:replace-aftergrace music)
+   (map-some-music
+    (lambda (expr)
+      (with-music-match
+       (expr (music 'SimultaneousMusic
+                   elements (?before-grace
+                             (music 'SequentialMusic
+                                    elements ((music 'SkipMusic)
+                                              (music 'GraceMusic
+                                                     element ?grace))))))
+       (ac:aftergrace ?before-grace ?grace)))
+    music))
+
+#(define (ac:replace-appoggiatura music)
+   ;; appoggiature are ugly to deal with since they require a main
+   ;; note following them.  We only try dealing with this followership
+   ;; in sequential music
+   (map-some-music
+    (lambda (m)
+      (if (eq? 'SequentialMusic (ly:music-property m 'name))
+         (pair-for-each
+          (lambda (elts)
+            (let ((expr (car elts))
+                  (main (and (pair? (cdr elts)) (cadr elts))))
+              (and main
+                   ;;stolen from define-music-display-methods
+                   (with-music-match
+                    (expr (music
+                           'GraceMusic
+                           element (music
+                                    'SequentialMusic
+                                    elements (?start
+                                              ?music
+                                              ?stop))))
+                    ;; we check whether ?start and ?stop look like
+                    ;; startAppoggiaturaMusic stopAppoggiaturaMusic
+                    (and (with-music-match (?start (music
+                                                    'SequentialMusic
+                                                    elements ((music
+                                                               'EventChord
+                                                               elements
+                                                               ((music
+                                                                 'SlurEvent
+                                                                 span-direction START))))))
+                                           #t)
+                         (with-music-match (?stop (music
+                                                   'SequentialMusic
+                                                   elements ((music
+                                                              'EventChord
+                                                              elements
+                                                              ((music
+                                                                'SlurEvent
+                                                                span-direction STOP))))))
+                                           #t)
+                         (let* ((app (ac:appoggiatura ?music main))
+                                (apps (ly:music-property app 'elements)))
+                           (set-car! elts (car apps))
+                           (set-car! (cdr elts) (cadr apps))
+                           #f))))))
+          (ly:music-property m 'elements)))
+      #f)
+    music))
 
 % Override \afterGrace to be in terms of audio, not spacing.
 % Special handling for a gruppetto after a trill.
-afterGrace =
-#(define-music-function
-  (parser location main grace)
-  (ly:music? ly:music?)p
-
+#(define (ac:aftergrace main grace)
   (let*
    ((main-length (ly:music-length main))
     (grace-orig-length (ly:music-length grace))
@@ -615,11 +996,11 @@ afterGrace =
     (factor (ly:moment-div new-main-length main-length))
   )
    (map (lambda (y) (set! (ly:music-property y 'twiddle) gracelen))
-        (filter (lambda (z)
-                 (and
-                  (eq? 'ArticulationEvent (ly:music-property z 'name))
-                  (string= "trill" (ly:music-property z 'articulation-type))))
-         (ly:music-property main 'elements)))
+         (filter (lambda (z)
+                  (and
+                   (eq? 'ArticulationEvent (ly:music-property z 'name))
+                   (string= "trill" (ly:music-property z 'articulation-type))))
+          (ly:music-property main 'elements)))
    (ac:add-articulation "tenuto" grace)
    (make-sequential-music  (list (ly:music-compress main factor) (ly:music-compress grace grace-factor)))))
 
@@ -627,18 +1008,16 @@ afterGrace =
 % or 1/3 if the note is dotted (i.e., half the undotted equivalent time)
 % Somewhere around the end of the 19th, start of 20th century the rules
 % changed, but my main interest is early music.
-appoggiatura =
-#(define-music-function (parser location grace main)
-  (ly:music? ly:music?)
+#(define (ac:appoggiatura grace main)
   (let* ((maindur (ly:music-length main))
-        (grace-orig-len (ly:music-length grace))
-        (main-orig-len (ly:music-length main))
-        (numerator (ly:moment-main-numerator maindur))
-        (factor (if (eq? (remainder numerator 3) 0)
-                 (ly:make-moment 1 3) (ly:make-moment 1 2))))
+         (grace-orig-len (ly:music-length grace))
+         (main-orig-len (ly:music-length main))
+         (numerator (ly:moment-main-numerator maindur))
+         (factor (if (eqv? (remainder numerator 3) 0)
+                  (ly:make-moment 1/3) (ly:make-moment 1/2))))
    (ly:music-compress grace
     (ly:moment-mul factor (ly:moment-div main-orig-len grace-orig-len)))
-   (ly:music-compress main (ly:moment-sub (ly:make-moment 1 1) factor))
+   (ly:music-compress main (ly:moment-sub (ly:make-moment 1/1) factor))
 
     (set! (ly:music-property grace 'elements)
      (append (ly:music-property grace 'elements)
@@ -647,4 +1026,3 @@ appoggiatura =
      (append (ly:music-property main 'elements)
       (list (make-music 'SlurEvent 'span-direction 1))))
      (make-sequential-music (list grace main))))
-