]> git.donarmstrong.com Git - lilypond.git/commitdiff
Merge branch 'master' of /home/jcharles/GIT/Lily/. into translation
authorJean-Charles Malahieude <lilyfan@orange.fr>
Sat, 30 Jul 2016 10:46:33 +0000 (12:46 +0200)
committerJean-Charles Malahieude <lilyfan@orange.fr>
Sat, 30 Jul 2016 10:46:33 +0000 (12:46 +0200)
50 files changed:
Documentation/contributor/release-work.itexi
Documentation/cyrillic.itexi
Documentation/macros.itexi
Documentation/notation/ancient.itely
Documentation/notation/changing-defaults.itely
Documentation/notation/contemporary.itely
Documentation/notation/editorial.itely
Documentation/notation/expressive.itely
Documentation/notation/fretted-strings.itely
Documentation/notation/input.itely
Documentation/notation/notation-appendices.itely
Documentation/notation/percussion.itely
Documentation/notation/pitches.itely
Documentation/notation/rhythms.itely
Documentation/notation/simultaneous.itely
Documentation/notation/spacing.itely
Documentation/notation/staff.itely
Documentation/notation/unfretted-strings.itely
Documentation/notation/vocal.itely
Documentation/web/news-front.itexi
Documentation/web/news.itexi
VERSION
input/regression/midi/crescendo-abutting.ly [deleted file]
input/regression/midi/crescendo-gap-compatible-target.ly [new file with mode: 0644]
input/regression/midi/crescendo-return-crescendo.ly [new file with mode: 0644]
input/regression/midi/crescendo-return-louder-target.ly [new file with mode: 0644]
input/regression/midi/crescendo-return-softer-target.ly [new file with mode: 0644]
input/regression/midi/crescendo-return-unspecified-target.ly [new file with mode: 0644]
input/regression/midi/crescendo-single-compatible-target.ly [new file with mode: 0644]
input/regression/midi/crescendo-single-unspecified-target.ly [new file with mode: 0644]
input/regression/midi/decrescendo-multiple-compatible-target.ly [new file with mode: 0644]
input/regression/midi/decrescendo-single-contrary-target.ly [new file with mode: 0644]
lily/audio-item.cc
lily/dynamic-performer.cc
lily/grob.cc
lily/include/audio-item.hh
lily/include/lily-proto.hh
lily/include/midi-cc-announcer.hh [new file with mode: 0644]
lily/include/midi-item.hh
lily/midi-cc-announcer.cc [new file with mode: 0644]
lily/midi-cc-performer.cc [new file with mode: 0644]
lily/midi-control-function-performer.cc [deleted file]
lily/midi-item.cc
lily/staff-performer.cc
ly/Welcome-to-LilyPond-MacOS.ly
ly/Welcome_to_LilyPond.ly
ly/performer-init.ly
po/lilypond.pot
scm/framework-ps.scm
tex/texinfo.tex

index a2e3cb15d5e7e171c7f77c99cca4263e9444d2be..517b5e3c1462adc4cbae18d94daff245bc73b495 100644 (file)
@@ -615,8 +615,8 @@ replace @code{2.15.33-1} with the latest build):
 @uref{http://lilypond.org/downloads/binaries/test-output/lilypond-2.15.33-1.test-output.tar.bz2}
 @end smallexample
 
-Copy the tarball into @file{gub/regtests/}, and tell the build
-system that you have done this:
+Copy the tarball into @file{regtests/}, and tell the build system that
+you have done this:
 
 @example
 touch regtests/ignore
index 4e7569cf403daac105875c14510f4c54100ef6e2..e38eb99c69cd6737d87184d5f1a2c48728d7bf7c 100644 (file)
 \DeclareUnicodeCharacter{2116}{\textnumero}
 
 
-% Add all Cyrillic character names to \commondummies.
+% Add all Cyrillic character names to \definedummies.
 
 \gdef\cyrdummies{%
   \definedummyword\textnumero
   \definedummyword\cyrudoubleacute
 }
 
-\toks0 = \expandafter{\commondummies\cyrdummies}
-\xdef\commondummies{\the\toks0}
+\toks0 = \expandafter{\definedummies\cyrdummies}
+\xdef\definedummies{\the\toks0}
 
 
 % Add all Cyrillic character names to \indexnofonts.
index 6e567a56d555a52f71e94cfac997ba50c2dd07fb..cc09619dccbfdc4f3fe6a3838c5fb3f639e8df98 100644 (file)
@@ -186,16 +186,20 @@ For LilyPond version @version{}
 @c it doesn't matter, but a future implementation will probably
 @c add some code which needs this restriction.
 
+@c @raggedright can be enabled as soon as texinfo 5.x or larger
+@c is in use for Gub and LilyDev 3.
+
 @macro predefined
 @noindent
 @subsubheading Predefined commands
+@c @raggedright
 @end macro
 
 @c Due to a bug in texi2html (texi2html.pl CVS versions <= 1.245)
 @c the next macro must not be empty.
 
 @macro endpredefined
-@c
+@c @end raggedright
 @end macro
 
 
index 714584210af7d2dc7026a1b7561b71287cc19820..7e442244269dca60ae48055a8e13521671661024 100644 (file)
@@ -682,7 +682,7 @@ mensural notation or coloratio sections in white mensural notation.
 Because note head style does not influence flag count, in
 this style a semiminima should be notated as @code{a8*2}, not
 @code{a4}, otherwise it will look like a minima.
-The multiplier can be different if coloratio is used e.g. to notate
+The multiplier can be different if coloratio is used, e.g., to notate
 triplets.
 
 Use @code{semipetrucci} style to draw half-colored
@@ -1471,7 +1471,7 @@ with any of the following commands:
 @funindex \linea
 @code{\linea}.
 
-@item Ligatures, properly speaking (i.e. notes joined together), are
+@item Ligatures, properly speaking (i.e., notes joined together), are
 produced by placing one of the joining commands @code{\pes} or
 @code{\flexa}, for upwards and downwards movement, respectively,
 @emph{between} the notes to be joined.
@@ -1525,7 +1525,7 @@ Liquescent neumes
 
 Another main category of notes in Gregorian chant is the so-called
 liquescent neumes.  They are used under certain circumstances at
-the end of a syllable which ends in a @q{liquescent} letter, i.e.
+the end of a syllable which ends in a @q{liquescent} letter, i.e.,
 the sounding consonants that can hold a tone (the nasals, l, r, v,
 j, and their diphthong equivalents).  Thus, the liquescent neumes
 are never used alone (although some of them can be produced), and
@@ -1554,7 +1554,7 @@ with the corresponding modifier, @code{\quilisma},
 Virtually, within the ligature delimiters @code{\[} and @code{\]},
 any number of heads may be accumulated to form a single ligature,
 and head prefixes like @code{\pes}, @code{\flexa}, @code{\virga},
-@code{\inclinatum}, etc. may be mixed in as desired.  The use of
+@code{\inclinatum}, etc., may be mixed in as desired.  The use of
 the set of rules that underlies the construction of the ligatures
 in the above table is accordingly extrapolated.  This way,
 infinitely many different ligatures can be created.
@@ -2637,7 +2637,7 @@ typical scenarios are outlined, with suggestions of solutions.
 These involve:
 
 @itemize
-@item how to make incipits (i.e. prefatory material to indicate
+@item how to make incipits (i.e., prefatory material to indicate
 what the original has looked like) to modern transcriptions of
 mensural music;
 @item how to achieve the @emph{Mensurstriche} layout frequently
@@ -2697,7 +2697,7 @@ produced.  If no instrument name is required then use
 @emph{Mensurstriche} (@q{mensuration lines}) is the accepted term
 for bar lines that are drawn between the staves of a system but
 not through the staves themselves.  It is a common way to preserve
-the rhythmic appearance of the original, i.e. not having to break
+the rhythmic appearance of the original, i.e., not having to break
 syncopated notes at bar lines, while still providing the
 orientation aids that bar lines give.
 
@@ -2835,7 +2835,7 @@ verba = \lyricmode {
 @end lilypond
 
 Another common situation is transcription of neumatic or
-melismatic chants, i.e. chants with a varying number of notes
+melismatic chants, i.e., chants with a varying number of notes
 to each syllable.  In this case, one would want to set the
 syllable groups clearly apart, usually also the subdivisions of a
 longer melisma.  One way to achieve this is to use a fixed
index f5546a790ab54b3027985075ce0cdcd08d92b536..d213342754b4bb9feea7b6c9ba5cb3156d515723 100644 (file)
@@ -522,8 +522,8 @@ Notation Reference:
 
 Contexts are usually terminated at the first musical moment in
 which they have nothing to do.  So @code{Voice} contexts die as
-soon as they contain no events; @code{Staff} contexts die as soon
-as all the @code{Voice} contexts within them contain no events; etc.
+soon as they contain no events, @code{Staff} contexts die as soon
+as all the @code{Voice} contexts within them contain no events, etc.
 This can cause difficulties if earlier contexts which have died
 have to be referenced, for example, when changing staves with
 @code{\change} commands, associating lyrics with a voice with
@@ -1952,7 +1952,7 @@ the @code{#}@tie{}character.
 
 Contexts properties are usually named in
 @code{studlyCaps}.  They mostly control the translation from
-music to notation, e.g. @code{localAlterations} (for determining
+music to notation, e.g., @code{localAlterations} (for determining
 whether to print accidentals), or @code{measurePosition} (for
 determining when to print a bar line).  Context properties can
 change value over time while interpreting a piece of music;
@@ -2436,7 +2436,7 @@ a context will still show the values of their respective parent's
 context.
 
 The lifetime and value of a context property is dynamic and only
-available when music is being interpreted (i.e. @q{iterated}).  At the
+available when music is being interpreted (i.e., @q{iterated}).  At the
 time of the context's creation, properties are initialized from its
 corresponding definitions (along with any other modifications) of that
 context.  Any subsequent changes are achieved with any
@@ -2467,7 +2467,7 @@ own grob definition.
 
 Grob definitions are accessed with a different set of commands and are
 manipulated using @code{\override} and @code{\revert} and have a name
-starting with a capital letter (e.g. @samp{NoteHead}); whereas normal
+starting with a capital letter (e.g., @samp{NoteHead}); whereas normal
 context properties are manipulated using @code{\set} and @code{\unset}
 and are named starting with a lowercase letter.
 
@@ -2715,7 +2715,7 @@ be desirable to force a particular direction or placement.
 @node Articulation direction indicators
 @unnumberedsubsubsec Articulation direction indicators
 
-By default some directions are always up or always down (e.g.
+By default some directions are always up or always down (e.g.,
 dynamics or fermata), while other things can alternate between
 up or down based on the stem direction (like slurs or accents).
 
@@ -2732,9 +2732,9 @@ but a direction indicator is @strong{always} required before
 @item @code{\tweak} commands
 @item @code{\markup} commands
 @item @code{\tag} commands
-@item string markups, e.g. -"string"
-@item fingering instructions, e.g. @w{@code{-1}}
-@item articulation shortcuts, e.g. @w{@code{-.}}, @w{@code{->}}, @w{@code{--}}
+@item string markups, e.g., -"string"
+@item fingering instructions, e.g., @w{@code{-1}}
+@item articulation shortcuts, e.g., @w{@code{-.}}, @w{@code{->}}, @w{@code{--}}
 @end itemize
 
 Direction indicators affect only the next note:
@@ -3505,7 +3505,7 @@ to print them and @code{all-invisible} to suppress them.
 
 The @code{break-visibility} property controls the visibility of
 key signatures and changes of clef only at the start of lines,
-i.e. after a break.  It has no effect on the visibility of the
+i.e., after a break.  It has no effect on the visibility of the
 key signature or clef following an explicit key change or an
 explicit clef change within or at the end of a line.  In the
 following example the key signature following the explicit change
@@ -4146,7 +4146,7 @@ The VerticalAlignment and VerticalAxisGroup grobs work together.
 VerticalAxisGroup groups together different grobs like Staff, Lyrics,
 etc.  VerticalAlignment then vertically aligns the different grobs
 grouped together by VerticalAxisGroup.  There is usually only one
-VerticalAlignment per score but every Staff, Lyrics, etc. has its own
+VerticalAlignment per score but every Staff, Lyrics, etc., has its own
 VerticalAxisGroup.
 
 
@@ -4567,7 +4567,7 @@ Extending LilyPond:
 
 Unpure-pure containers are useful for overriding @emph{Y-axis} spacing
 calculations - specifically @code{Y-offset} and @code{Y-extent} - with a
-Scheme function instead of a literal (i.e. a number or pair).
+Scheme function instead of a literal (i.e., a number or pair).
 
 For certain grobs, the @code{Y-extent} is based on the @code{stencil}
 property, overriding the stencil property of one of these will
index 665a847d7f03d62ffaf0e65ef94f11d09a630c18..8a0466903b156b5889514817aed204007b478786 100644 (file)
@@ -142,7 +142,7 @@ addressed in @ref{Grouping staves}.
 @unnumberedsubsubsec Extended polymetric notation
 
 @ignore
-    Extended examples e.g. different instruments
+    Extended examples e.g., different instruments
     or ensembles with independent tempi
 @end ignore
 
index e329810ae56844dd5ab9634742d257a588d45548..73e5ffbecea4376c0928fc5e407b2bd581d87bba 100644 (file)
@@ -350,7 +350,7 @@ Markup texts or strings may be used for finger changes.
 
 @funindex \thumb
 
-A thumb-script can be added (e.g. cello music) to indicate
+A thumb-script can be added (e.g., cello music) to indicate
 that a note should be played with the thumb.
 
 @lilypond[verbatim,quote]
index defe8ad946803368a59ed83492f90fe794009a1b..60f0cc66c0413989d6ca9a6b5f119ed4612dd531 100644 (file)
@@ -1224,7 +1224,7 @@ information, see @ref{Ties}.
 @code{\arpeggioArrowDown},
 @code{\arpeggioNormal},
 @code{\arpeggioBracket},
-@code{\arpeggioParenthesis}
+@code{\arpeggioParenthesis},
 @code{\arpeggioParenthesisDashed}.
 @endpredefined
 
index a69f6787355fa32ab9240280284248bd8de587e3..bd0dfca32d279ed1c8675bb2ec55576243d277c8 100644 (file)
@@ -194,7 +194,7 @@ calligraphic tablature clef is added automatically.
 @end lilypond
 
 Default tablatures do not contain any symbols for tone duration nor any
-other musical symbols such as e.g. expressive marks.
+other musical symbols such as expressive marks, for example.
 
 @lilypond[quote,ragged-right,verbatim]
 symbols = {
@@ -668,7 +668,7 @@ one for each string, ordered by string number from 1 to N,
 where string 1 is at the top of the tablature staff and
 string N is at the bottom.  This ordinarily results in ordering
 from highest pitch to lowest pitch, but some instruments
-(e.g. ukulele) do not have strings ordered by pitch.
+(e.g., ukulele) do not have strings ordered by pitch.
 
 A string pitch in a string tuning list is a LilyPond pitch
 object.  Pitch objects are created with the Scheme function
@@ -2019,7 +2019,7 @@ LilyPond supports tablature for lute.
 
 To get additional bass strings use @code{additionalBassStrings}, where the
 pitches of those strings are set. They will be printed below lowest line as:
-a, /a, //a, ///a, 4, 5 etc.
+a, /a, //a, ///a, 4, 5, etc.
 
 @code{fret-letter-tablature-format} for @code{tablatureFormat} should be used,
 probably @code{fretLabels} for further customizing.
index a23943c16c8e02279549c8baa8cb20854aef37bb..0a873317d4999b49e855bfc2fe49a65a4159bc32 100644 (file)
@@ -365,9 +365,9 @@ A direct scheme expression, such as
 @code{#(ly:set-option 'point-and-click #f)}.
 
 @item
-A @code{\header} block.  This sets the global (i.e. the top of
+A @code{\header} block.  This sets the global (i.e., the top of
 file) header block.  This is the block containing the default
-settings of titling fields like composer, title, etc. for all
+settings of titling fields like composer, title, etc., for all
 books within the file (see @ref{Titles explained}).
 
 @item
@@ -496,11 +496,11 @@ circumstances to avoid errors:
 
 @item Around every opening and closing curly bracket.
 
-@item After every command or variable, i.e. every item that
+@item After every command or variable, i.e., every item that
 begins with a @code{\} sign.
 
 @item After every item that is to be interpreted as a Scheme
-expression, i.e. every item that begins with a @code{#}@tie{}sign.
+expression, i.e., every item that begins with a @code{#}@tie{}sign.
 
 @item To separate all elements of a Scheme expression.
 
@@ -531,7 +531,7 @@ some pieces include a lot more information.
 @menu
 * Creating titles headers and footers::
 * Custom titles headers and footers::
-* Creating PDF metadata::
+* Creating output file metadata::
 * Creating footnotes::
 * Reference to page numbers::
 * Table of contents::
@@ -1128,7 +1128,7 @@ variable = \markup @{
 
 The @var{procedure} is called each time the @code{\markup} command
 in which it appears is evaluated.  The @var{procedure} should test
-for a particular condition and interpret (i.e. print) the
+for a particular condition and interpret (i.e., print) the
 @var{markup} argument if and only if the condition is true.
 
 A number of ready-made procedures for testing various conditions are
@@ -1200,21 +1200,30 @@ Notation Reference:
 Installed Files:
 @file{../ly/titling-init.ly}.
 
-@node Creating PDF metadata
-@subsection Creating PDF metadata
+@node Creating output file metadata
+@subsection Creating output file metadata
 
 @cindex PDF metadata
+@cindex MIDI metadata
 
 In addition to being shown in the printed output, @code{\header} variables
-are also used to set PDF metadata (the information displayed by PDF readers
-as the @code{properties} of the PDF file).  For example, setting the
-@code{title} property of the @code{header} block @q{Symphony I} will also give
-this title to the PDF document.
-
-@example
-  \header@{
-    title = "Symphony I"
-  @}
+are also used to set metadata for output files.  For example, with PDF
+files, this metadata could be displayed by PDF readers as the
+@code{properties} of the PDF file.  For each type of output file, only the
+@code{\header} definitions of blocks that define separate files of that
+type, and blocks higher in the block hierarchy, will be consulted.
+Therefore, for PDF files, only the @code{\book} level and the top level
+@code{\header} definitions affect the document-wide PDF metadata, whereas
+for MIDI files, all headers above or at the @code{\score} level are used.
+
+For example, setting the @code{title} property of the @code{header} block
+to @q{Symphony I} will also give this title to the PDF document, and use
+it as the sequence name of the MIDI file.
+
+@example
+\header@{
+  title = "Symphony I"
+@}
 @end example
 
 If you want to set the title of the printed output to one value, but have the
@@ -1222,10 +1231,10 @@ title property of the PDF to have a different value, you can use
 @code{pdftitle}, as below.
 
 @example
-  \header@{
-    title = "Symphony I"
-    pdftitle = "Symphony I by Beethoven"
-  @}
+\header@{
+  title = "Symphony I"
+  pdftitle = "Symphony I by Beethoven"
+@}
 @end example
 
 The variables @code{title}, @code{subject}, @code{keywords},
@@ -1239,6 +1248,10 @@ both set to the current date and time.  @code{ModDate} can be overridden by
 setting the header variable @code{moddate} (or @code{pdfmoddate}) to a
 valid PDF date string.
 
+The @code{title} variable sets also the sequence name for MIDI.  The
+@code{midititle} variable can be used to set the sequence name
+independently of the value used for typeset output.
+
 @node Creating footnotes
 @subsection Creating footnotes
 
@@ -1325,7 +1338,7 @@ left/bottom edge and zero implies the mark is centered on the edge.
 
 @item Context
 is the context in which the grob being footnoted is created.  It
-may be omitted if the grob is in a bottom context, e.g. a
+may be omitted if the grob is in a bottom context, e.g., a
 @code{Voice} context.
 
 @item GrobName
@@ -2206,7 +2219,7 @@ followed by a music expression.  If @emph{and only if} the symbols
 are valid LilyPond identifiers (alphabetic characters only, no
 numbers, underscores, or dashes) which cannot be confused with notes,
 the @code{#'} may be omitted and, as a shorthand, a list of symbols
-can use the dot separator: i.e. @code{\tag #'(violinI violinII)} can
+can use the dot separator: i.e., @code{\tag #'(violinI violinII)} can
 be written @code{\tag violinI.violinII}.  The same applies to
 @code{\keepWithTag} and @code{\removeWithTag}.
 
@@ -2251,7 +2264,7 @@ music = \relative {
 }
 @end lilypond
 
-Tagged filtering can be applied to articulations, texts, etc. by
+Tagged filtering can be applied to articulations, texts, etc., by
 prepending
 
 @example
@@ -2728,7 +2741,7 @@ in your source file.  This will render only the last 5 measures
 (assuming 4/4 time signature) of every @code{\score} in the input
 file.  For longer pieces, rendering only a small part is often an order
 of magnitude quicker than rendering it completely.  When working on the
-beginning of a score you have already typeset (e.g. to add a new part),
+beginning of a score you have already typeset (e.g., to add a new part),
 the @code{showFirstLength} property may be useful as well.
 
 Skipping parts of a score can be controlled in a more fine-grained
@@ -2882,7 +2895,7 @@ following (without taking dots into account).  For example;
 @noindent
 The c will take the value of a crotchet.
 
-@item Ornaments (i.e. mordents, trills and turns et al.)
+@item Ornaments (i.e., mordents, trills and turns et al.)
 @item Rallentando, accelerando, ritardando and a tempo
 @item Slurs, including phrasing slurs
 @item Tenuto
@@ -2907,8 +2920,8 @@ and portato
 @item Glissandi
 @item Falls and doits
 @item Microtonal chords
-@item Rhythms entered as annotations, e.g. swing
-@item Tempo changes without @code{\tempo} (e.g. entered as annotations)
+@item Rhythms entered as annotations, e.g., swing
+@item Tempo changes without @code{\tempo} (e.g., entered as annotations)
 @item Tremolos that @emph{are} entered with a @q{@code{:}[@var{number}]}
 value
 @end itemize
@@ -2930,8 +2943,8 @@ To create a MIDI output file from a LilyPond input file, insert a
 @}
 @end example
 
-@warning{ A @code{@bs{}score} block that, as well as the music, contains
-only a @code{@bs{}midi} block (i.e. @emph{without} the @code{@bs{}layout}
+@warning{A @code{@bs{}score} block that, as well as the music, contains
+only a @code{@bs{}midi} block (i.e., @emph{without} the @code{@bs{}layout}
 block), will only produce MIDI output files.  No notation will be
 printed.}
 
@@ -2952,7 +2965,8 @@ either the @code{\book}, @code{\bookpart} or @code{\score} blocks.  See
 
 @seealso
 Notation Reference:
-@ref{File structure}.
+@ref{File structure},
+@ref{Creating output file metadata}.
 
 Installed Files:
 @file{scm/midi.scm}.
index 3d8bf79f43efacfbd0afb4d72cd8345ee2debaed..388c8bc388bb9cc7cf94d081bd1a46dce8c90e9e 100644 (file)
@@ -859,12 +859,12 @@ darkcyan    darkmagenta    darkyellow
 X color names come several variants:
 
 Any name that is spelled as a single word with capitalization
-(e.g. @q{LightSlateBlue}) can also be spelled as space separated
-words without capitalization (e.g. @q{light slate blue}).
+(e.g., @q{LightSlateBlue}) can also be spelled as space separated
+words without capitalization (e.g., @q{light slate blue}).
 
-The word @q{grey} can always be spelled @q{gray} (e.g. @q{DarkSlateGray}).
+The word @q{grey} can always be spelled @q{gray} (e.g., @q{DarkSlateGray}).
 
-Some names can take a numerical suffix (e.g. @q{LightSalmon4}).
+Some names can take a numerical suffix (e.g., @q{LightSalmon4}).
 
 
 @subsubheading Color Names without a numerical suffix:
@@ -2366,7 +2366,7 @@ An association list or @strong{alist} for short is a Scheme pair
 which associates a value with a key: @w{@code{(key . value)}}.  For
 example, in @file{scm/lily.scm}, the alist
 @w{@qq{type-p-name-alist}} associates certain type predicates
-(e.g.@tie{}@code{ly:music?}) with names (e.g.@tie{}@qq{music}) so
+(e.g., @code{ly:music?}) with names (e.g., @qq{music}) so
 that type-check failures can be reported with a console message that
 includes the name of the expected type predicate.
 
@@ -2392,7 +2392,7 @@ performed.
 In Scheme, a @strong{closure} is created when a function, usually
 a lambda expression, is passed as a variable.  The closure contains
 the function's code plus references to the lexical bindings of the
-function's free variables (i.e. those variables used in the
+function's free variables (i.e., those variables used in the
 expression but defined outside it).  When this function is applied
 to different arguments later, the free variable bindings that were
 captured in the closure are used to obtain the values of the free
index be6bc04c35662e688bdd0afbdc95e337fa274d1e..b17dd888ee6d79047090f0c4a2389b3406279ade 100644 (file)
@@ -168,7 +168,7 @@ Snippets:
 @node Pitched percussion
 @unnumberedsubsubsec Pitched percussion
 
-Certain pitched percussion instruments (e.g. xylophone,
+Certain pitched percussion instruments (e.g., xylophone,
 vibraphone, and timpani) are written using normal staves.
 This is covered in other sections of the manual.
 
index 3aa7738e2bbfcb71812369cc2edccb40207dbbb0..0699239db55b6e8bdc52a3add4b21dd39d3d926a 100644 (file)
@@ -456,7 +456,7 @@ Internals Reference:
 
 @knownissues
 There are no generally accepted standards for denoting
-quarter-tone accidentals, so LilyPond's symbol does not conform to
+quarter-tone accidentals, so LilyPond's symbols do not conform to
 any standard.
 
 
@@ -518,7 +518,7 @@ In addition to note names, accidental suffixes may
 also vary depending on the language:
 
 @quotation
-@multitable {@code{nederlands}} {-@code{s}/-@code{-sharp}} {-@code{ess}/-@code{es}} {-@code{ss}/-@code{x}/-@code{-sharpsharp}} {-@code{essess}/-@code{eses}}
+@multitable {@code{nederlands}} {-@code{s}/-@code{-sharp}} {-@code{f}/-@code{-flat}} {-@code{ss}/-@code{x}/-@code{-sharpsharp}} {-@code{ff}/-@code{-flatflat}}
 @headitem Language
   @tab sharp @tab flat @tab double sharp @tab double flat
 @item @code{nederlands}
@@ -841,7 +841,7 @@ relative mode within transposed music, an additional @code{\relative}
 must be placed inside @code{\transpose}.
 
 Triple accidentals will not be printed if using @code{\transpose}. An
-@q{enharmonically equivalent} pitch will be used instead (e.g. d-flat
+@q{enharmonically equivalent} pitch will be used instead (e.g., d-flat
 rather than e-triple-flat).
 
 
@@ -1214,6 +1214,13 @@ will be ignored.  The command
   c'1
 @end lilypond
 
+@noindent
+To be more precise, it is not the @code{\clef} command itself that
+prints a clef.  Instead, it sets or changes a property of the
+@code{Clef_engraver}, which then decides by its own whether to
+display a clef or not in the current staff.  The @code{forceClef}
+property overrides this decision locally to re-print a clef once.
+
 When there is a manual clef change, the glyph of the changed clef
 will be smaller than normal.  This behaviour can be overridden.
 
index 704d96e007f54d8558aa9651ed4e99f51e164fd4..d3263d4f6a43f93d6e6c50e1f78868aa80bb13e0 100644 (file)
@@ -186,7 +186,7 @@ Internals Reference:
 @c Deliberately duplicated in Durations and Rests.  -gp
 There is no fundamental limit to rest durations (both in terms of
 longest and shortest), but the number of glyphs is limited:
-rests from 128th to maxima (8 x whole) may be printed.
+rests from 128th to maxima (8× whole) may be printed.
 
 
 @node Tuplets
@@ -713,7 +713,7 @@ Internals Reference:
 @c Deliberately duplicated in Durations and Rests.  -gp
 There is no fundamental limit to rest durations (both in terms of
 longest and shortest), but the number of glyphs is limited: there
-are rests from 128th to maxima (8 x whole).
+are rests from 128th to maxima (8× whole).
 
 
 @node Invisible rests
@@ -981,7 +981,7 @@ Internals Reference:
 @cindex multi-measure rests and fingerings
 
 @knownissues
-Fingerings over multi-measure rests (e.g. @code{R1*10-4}) may result
+Fingerings over multi-measure rests (e.g., @code{R1*10-4}) may result
 in the fingering numeral colliding with the bar counter
 numeral.
 
@@ -1489,7 +1489,7 @@ Internal Reference:
 @funindex \cadenzaOff
 
 In metered music bar lines are inserted and bar numbers are calculated
-automatically. In unmetered music (i.e. cadenzas), this is not
+automatically. In unmetered music (i.e., cadenzas), this is not
 desirable and can be @q{switched off} using the command
 @code{\cadenzaOn}, then @q{switched back on} at the appropriate place
 using @code{\cadenzaOff}.
@@ -1864,7 +1864,7 @@ For consistency with previous behavior, notes and rests with
 duration longer than a measure, such as @code{c1*2}, are split into
 notes without any scale factor, @code{@{ c1 c1 @}}.  The property
 @code{completionFactor} controls this behavior, and setting it to
-@code{#f} cause split notes and rest to have the scale factor
+@code{#f} cause split notes and rests to have the scale factor
 of the input durations.
 
 
@@ -2666,7 +2666,7 @@ all notes end before the end of a measure.
 
 @warning{An incorrect duration can cause line breaks to be
 inhibited, leading to a line of highly compressed music or
-music which flows off the page.}
+music that flows off the page.}
 
 @cindex line breaks
 @cindex bar lines, invisible
@@ -2941,7 +2941,7 @@ for defining span bars correctly aligned to the main bar lines:
 @end lilypond
 
 If additional elements are needed, LilyPond provides a simple
-way to define them.  For more informations on modifying or adding
+way to define them.  For more information on modifying or adding
 bar lines, see file @file{scm/bar-line.scm}.
 
 In scores with many staves, a @code{\bar} command in one staff is
@@ -3717,10 +3717,10 @@ The next bar line then falls at 9/8 rather than 5/4.
 @end lilypond
 
 @noindent
-As the example illustrates, @code{ly:make-moment n m} constructs a
+As the example illustrates, @code{ly:make-moment n/m} constructs a
 duration of n/m of a whole note.  For example,
-@code{ly:make-moment 1 8} is an eighth note duration and
-@code{ly:make-moment 7 16} is the duration of seven sixteenths
+@code{ly:make-moment 1/8} is an eighth note duration and
+@code{ly:make-moment 7/16} is the duration of seven sixteenths
 notes.
 
 @seealso
index a29dd9c4ae25a89d80dfceb1fec95ec6c0c1c5be..fc31736c09cb38e15662f02dbb2765830b97dde2 100644 (file)
@@ -180,7 +180,7 @@ chord.  The chord repetition symbol is @code{q}:
 @end lilypond
 
 As with regular chords, the chord repetition symbol can be used with
-durations, articulations, markups, slurs, beams, etc. as only the
+durations, articulations, markups, slurs, beams, etc., as only the
 pitches of the previous chord are duplicated.
 
 @lilypond[verbatim,quote]
@@ -559,7 +559,7 @@ upstems, and the even-numbered voices are given downstems:
 >>
 @end lilypond
 
-@warning{Lyrics, spanners (such as slurs, ties, hairpins etc.) cannot be
+@warning{Lyrics, spanners (such as slurs, ties, hairpins, etc.) cannot be
 created @q{across} voices.}
 
 @subsubsubheading Identical rhythms
@@ -1118,12 +1118,12 @@ started when combining notes that have just started in the other
 @code{Voice}.  This can lead to a number of unexpected issues including
 @qq{Solo} or @qq{Unison} marks being printed incorrectly.
 
-@code{\partcombine} keeps all spanners (slurs, ties, hairpins etc.) in
+@code{\partcombine} keeps all spanners (slurs, ties, hairpins, etc.) in
 the same @code{Voice} so that if any such spanners start or end in a
 different @code{Voice}, they may not be printed properly or at all.
 
 If the @code{\partcombine} function cannot combine both music
-expressions (i.e. when both voices have different durations), it will
+expressions (i.e., when both voices have different durations), it will
 give the voices, internally, its own custom names: @code{one} and
 @code{two} respectively.  This means if there is any @qq{switch} to a
 differently named @code{Voice} context, the events in that differently
index bd6d4d4b78645d164f13a49c37180eb1210a12c0..23ed5c0f308f7d26913e2c2c1ae407900124715a 100644 (file)
@@ -570,20 +570,20 @@ the distance between two (title or top-level) markups.
 @funindex last-bottom-spacing
 
 the distance from the last system or top-level markup on a page to
-the bottom of the printable area (i.e. the top of the bottom
+the bottom of the printable area (i.e., the top of the bottom
 margin).
 
 @item top-system-spacing
 @funindex top-system-spacing
 
-the distance from the top of the printable area (i.e. the bottom
+the distance from the top of the printable area (i.e., the bottom
 of the top margin) to the first system on a page, when there is no
 (title or top-level) markup between the two.
 
 @item top-markup-spacing
 @funindex top-markup-spacing
 
-the distance from the top of the printable area (i.e. the bottom
+the distance from the top of the printable area (i.e., the bottom
 of the top margin) to the first (title or top-level) markup on a
 page, when there is no system between the two.
 @end table
@@ -927,7 +927,7 @@ scores (if there are two or more scores), or by ending a score on an
 even-numbered page.  The values of the following three variables may
 be increased to make these actions less likely.
 
-The values are penalties, i.e. the higher the value the less likely
+The values are penalties, i.e., the higher the value the less likely
 will be the associated action relative to other choices.
 
 @table @code
@@ -1431,7 +1431,7 @@ issue:
 @end lilypond
 
 A @code{\break} command that occurrs at a bar line will also ignored if
-the previous measure ends in the middle of a note (e.g. when a tuplet
+the previous measure ends in the middle of a note (e.g., when a tuplet
 begins in one measure and ends in another).  In this case remove the
 @code{Forbid_line_break_engraver} from the @code{Voice} context and,
 use a simultaneous music construction inserting the @code{\break} at the
@@ -1943,7 +1943,7 @@ non-staff line if @code{staff-affinity} is @code{UP}.
 
 Each distance is measured between the @emph{reference points} of
 the two items.  The reference point for a staff is the vertical
-center of its @code{StaffSymbol} (i.e. the middle line if
+center of its @code{StaffSymbol} (i.e., the middle line if
 @code{line-count} is odd; the middle space if @code{line-count} is
 even).  The reference points for individual non-staff lines are
 given in the following table:
@@ -2107,7 +2107,7 @@ non-staff line.  Choices are @code{UP}, @code{DOWN}, and
 placed equidistant between the two nearest staves on either side,
 unless collisions or other spacing constraints prevent this.
 Adjacent non-staff lines should have non-increasing
-@code{staff-affinity} from top to bottom, e.g. a non-staff line
+@code{staff-affinity} from top to bottom, e.g., a non-staff line
 set to @code{UP} should not immediately follow one that is set to
 @code{DOWN}.  Non-staff lines at the top of a system should use
 @code{DOWN}; those at the bottom should use @code{UP}.  Setting
@@ -2333,7 +2333,7 @@ Internals Reference:
 
 @emph{Non-staff lines} (such as @code{Lyrics}, @code{ChordNames},
 etc.) are contexts whose layout objects are engraved like staves
-(i.e. in horizontal lines within systems).  Specifically,
+(i.e., in horizontal lines within systems).  Specifically,
 non-staff lines are non-staff contexts that contain the
 @rinternals{Axis_group_engraver}.
 
index ce859e2bb5dd08757915e391893f346be8bd5c61..a1144346644864b8298f30a4fe6e9df2423c9078 100644 (file)
@@ -1326,7 +1326,7 @@ It is possible to adjust which aspects of the music are quoted with
 property.  Its default value is @code{'(note-event rest-event
 tie-event beam-event tuplet-span-event)}, which means that only
 notes, rests, ties, beams and tuplets are quoted, but not
-articulations, dynamic marks, markup etc.
+articulations, dynamic marks, markup, etc.
 
 @warning{When a @code{Voice} starts with @code{\cueDuring}, as in the
 following example, the @code{Voice} context must be explicitly declared,
index becdc6f3e137a1469fa78ed6a4d3965ae8b5a09e..f3eae557659034137a17fca8574dcec93dda7bd3 100644 (file)
@@ -198,10 +198,6 @@ dots are required.
 }
 @end lilypond
 
-@warning{@code{@bs{}harmonic} @strong{must} be placed inside a
-chord construct even if there is only a single note.  Normally
-@code{@bs{}harmonicsOn} would be used in this situation.}
-
 @seealso
 Music Glossary:
 @rglos{harmonics}.
index b06de6f40b42b17d60b67326229f5e8f093a101d..118a58e4822b7ca9959377fbfc1550f4694e438f 100644 (file)
@@ -63,7 +63,7 @@ introduction to this notation is to be found in
 @item
 Vocal music is likely to require the use of @code{markup} mode,
 either for lyrics or for other text elements (characters' names,
-etc.)  This syntax is described in @ref{Text markup introduction}.
+etc.).  This syntax is described in @ref{Text markup introduction}.
 
 @item
 @notation{Ambitus} may be added at the beginning of vocal staves,
@@ -791,11 +791,11 @@ should be included in the melisma:
 
 
 @predefined
-
 @code{\autoBeamOff},
 @code{\autoBeamOn},
 @code{\melisma},
 @code{\melismaEnd}.
+@endpredefined
 
 @seealso
 Musical Glossary:
@@ -1902,7 +1902,7 @@ are to be ignored.
 @cindex grace notes and lyrics
 @cindex lyrics on grace notes
 
-By default, grace notes (e.g. via @code{\grace}) do not get assigned
+By default, grace notes (e.g., via @code{\grace}) do not get assigned
 syllables when using @code{\lyricsto}, but this behavior can be
 changed:
 
@@ -2300,6 +2300,7 @@ When a vocal part temporarily splits, you should use
 @code{\oneVoice},
 @code{\voiceOne},
 @code{\voiceTwo}.
+@endpredefined
 
 @seealso
 Learning Manual:
@@ -2392,7 +2393,10 @@ above their respective staves, as shown here:
 @end lilypond
 
 @predefined
-@code{\dynamicUp}, @code{\dynamicDown}, @code{\dynamicNeutral}.
+@code{\dynamicUp},
+@code{\dynamicDown},
+@code{\dynamicNeutral}.
+@endpredefined
 
 @seealso
 Notation Reference:
index 349678b75df7f620510f7fbe57c027638983c005..49cdaab35f85252a3458571872f4c76ab2f2dee5 100644 (file)
@@ -9,10 +9,10 @@
 @c used for news about the upcoming release; see CG 10.2
 
 @newsItem
-@subheading LilyPond 2.19.45 released  @emph{July 09, 2016}
+@subheading LilyPond 2.19.46 released  @emph{July 26, 2016}
 
 We are happy to announce the release of LilyPond
-2.19.45.  This release includes a number of enhancements, and contains some
+2.19.46.  This release includes a number of enhancements, and contains some
 work in progress.  You will have access to the very latest features, but
 some may be incomplete, and you may encounter bugs and crashes.  If you
 require a stable version of Lilypond, we recommend using the 2.18
index c92da5f53461eca38257a7f9927fe5853f701d3d..da830d3e151903c39e4cf10ffd0e2161cee11f40 100644 (file)
@@ -26,6 +26,18 @@ NOTE:
   * don't duplicate entries from news-front.itexi
 @end ignore
 
+@newsItem
+@subheading LilyPond 2.19.45 released  @emph{July 09, 2016}
+
+We are happy to announce the release of LilyPond
+2.19.45.  This release includes a number of enhancements, and contains some
+work in progress.  You will have access to the very latest features, but
+some may be incomplete, and you may encounter bugs and crashes.  If you
+require a stable version of Lilypond, we recommend using the 2.18
+version.
+
+@newsEnd
+
 @newsItem
 @subheading LilyPond 2.19.44 released  @emph{June 21, 2016}
 
diff --git a/VERSION b/VERSION
index 0a8b4140a99cd0e65ea510002191cc6811e634fd..1b58b2ee855a45a009698abb451e899ab7f43df1 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1,7 +1,7 @@
 PACKAGE_NAME=LilyPond
 MAJOR_VERSION=2
 MINOR_VERSION=19
-PATCH_LEVEL=46
+PATCH_LEVEL=47
 MY_PATCH_LEVEL=
 VERSION_STABLE=2.18.2
-VERSION_DEVEL=2.19.45
+VERSION_DEVEL=2.19.46
diff --git a/input/regression/midi/crescendo-abutting.ly b/input/regression/midi/crescendo-abutting.ly
deleted file mode 100644 (file)
index da0ff4f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-\version "2.19.44"
-
-\header {
-  texidoc="One (de)crescendo ends as the next begins."
-}
-
-\score {
-   { c\< c\> c\! }
-   \midi {}
-}
diff --git a/input/regression/midi/crescendo-gap-compatible-target.ly b/input/regression/midi/crescendo-gap-compatible-target.ly
new file mode 100644 (file)
index 0000000..38c19a3
--- /dev/null
@@ -0,0 +1,18 @@
+\version "2.19.45"
+
+\header {
+  texidoc="When there is a gap between the end of a crescendo and a
+  subsequent explicit dynamic, the dynamic performer uses the explicit
+  dynamic as the target of the crescendo."
+
+  %% Note: Choosing this behavior simplified the implementation.  In
+  %% the developer's opinion, it is difficult to argue that choosing a
+  %% target dynamic that under- or overshoots the explicit dynamic is
+  %% more correct.
+
+}
+
+\score {
+   { c\mf\< c\! c\f }
+   \midi {}
+}
diff --git a/input/regression/midi/crescendo-return-crescendo.ly b/input/regression/midi/crescendo-return-crescendo.ly
new file mode 100644 (file)
index 0000000..4ab8dac
--- /dev/null
@@ -0,0 +1,12 @@
+\version "2.19.44"
+
+\header {
+  texidoc="The dynamic performer chooses a reasonable peak dynamic and
+  returns to the original dynamic.  The latter, extreme crescendo does
+  not affect the former."
+}
+
+\score {
+   { c\mf\< c\> c\< c\sf }
+   \midi {}
+}
diff --git a/input/regression/midi/crescendo-return-louder-target.ly b/input/regression/midi/crescendo-return-louder-target.ly
new file mode 100644 (file)
index 0000000..f3fcb36
--- /dev/null
@@ -0,0 +1,11 @@
+\version "2.19.45"
+
+\header {
+  texidoc="The dynamic performer chooses a reasonable peak dynamic and
+  returns to the specified dynamic."
+}
+
+\score {
+   { c\mf\< c\> c\f }
+   \midi {}
+}
diff --git a/input/regression/midi/crescendo-return-softer-target.ly b/input/regression/midi/crescendo-return-softer-target.ly
new file mode 100644 (file)
index 0000000..788724b
--- /dev/null
@@ -0,0 +1,11 @@
+\version "2.19.45"
+
+\header {
+  texidoc="The dynamic performer chooses a reasonable peak dynamic and
+  returns to the specified dynamic."
+}
+
+\score {
+   { c\mf\< c\> c\p }
+   \midi {}
+}
diff --git a/input/regression/midi/crescendo-return-unspecified-target.ly b/input/regression/midi/crescendo-return-unspecified-target.ly
new file mode 100644 (file)
index 0000000..065f674
--- /dev/null
@@ -0,0 +1,11 @@
+\version "2.19.44"
+
+\header {
+  texidoc="The dynamic performer chooses a reasonable peak dynamic and
+  returns to the original dynamic."
+}
+
+\score {
+   { c\mf\< c\> c\! }
+   \midi {}
+}
diff --git a/input/regression/midi/crescendo-single-compatible-target.ly b/input/regression/midi/crescendo-single-compatible-target.ly
new file mode 100644 (file)
index 0000000..fcc0f5a
--- /dev/null
@@ -0,0 +1,11 @@
+\version "2.19.44"
+
+\header {
+  texidoc="The velocity of notes during a crescendo is linearly
+  interpolated between the starting and target dynamics."
+}
+
+\score {
+   { c\mf\< c c\f }
+   \midi {}
+}
diff --git a/input/regression/midi/crescendo-single-unspecified-target.ly b/input/regression/midi/crescendo-single-unspecified-target.ly
new file mode 100644 (file)
index 0000000..d995609
--- /dev/null
@@ -0,0 +1,11 @@
+\version "2.19.44"
+
+\header {
+  texidoc="If no explicit dynamic follows a crescendo, the dynamic
+  performer chooses a reasonable target dynamic."
+}
+
+\score {
+   { c\mf\< c\! }
+   \midi {}
+}
diff --git a/input/regression/midi/decrescendo-multiple-compatible-target.ly b/input/regression/midi/decrescendo-multiple-compatible-target.ly
new file mode 100644 (file)
index 0000000..d066381
--- /dev/null
@@ -0,0 +1,12 @@
+\version "2.19.45"
+
+\header {
+  texidoc="The dynamic performer apportions changes to consecutive
+  decrescendi in proportion to their duration.  In this case, 1/3 of
+  the change occurs over the first decrescendo."
+}
+
+\score {
+   { c\f\> c\! c2\> | c1\p }
+   \midi {}
+}
diff --git a/input/regression/midi/decrescendo-single-contrary-target.ly b/input/regression/midi/decrescendo-single-contrary-target.ly
new file mode 100644 (file)
index 0000000..7c13541
--- /dev/null
@@ -0,0 +1,15 @@
+\version "2.19.45"
+
+\header {
+  texidoc="When a decrescendo is followed by an explicit dynamic that
+  is louder than the starting dynamic, the dynamic performer chooses a
+  reasonable target dynamic.  The velocity of notes during the
+  decrescendo is linearly interpolated between the starting and target
+  dynamics, with the explicit dynamic taking effect at the last
+  moment."
+}
+
+\score {
+   { c\mf\> c c\f }
+   \midi {}
+}
index 97a15d552aafaedc86053945c291d1d796d2eb7d..a8a78199c73096c15375110f5c50020b28912d13 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "midi-item.hh"
 #include "audio-column.hh"
+#include "international.hh"
 
 Audio_instrument::Audio_instrument (string instrument_string)
 {
@@ -101,24 +102,15 @@ Audio_key::Audio_key (int acc, bool major)
   major_ = major;
 }
 
-Audio_dynamic::Audio_dynamic ()
-  : volume_ (-1),
-    silent_ (false)
-{
-}
-
-Audio_span_dynamic::Audio_span_dynamic (Real min_volume, Real max_volume)
-{
-  grow_dir_ = CENTER;
-  min_volume_ = min_volume;
-  max_volume_ = max_volume;
-}
+const Real Audio_span_dynamic::MINIMUM_VOLUME = 0.0;
+const Real Audio_span_dynamic::MAXIMUM_VOLUME = 1.0;
+const Real Audio_span_dynamic::DEFAULT_VOLUME = 90.0 / 127.0;
 
-void
-Audio_span_dynamic::add_absolute (Audio_dynamic *d)
+Audio_span_dynamic::Audio_span_dynamic (Moment mom, Real volume)
+  : start_moment_ (mom),
+    duration_ (0)
 {
-  assert (d);
-  dynamics_.push_back (d);
+  set_volume (volume, volume);
 }
 
 Moment
@@ -140,53 +132,60 @@ moment_to_ticks (Moment m)
   return int (moment_to_real (m) * 384 * 4);
 }
 
-void
-Audio_span_dynamic::render ()
+void Audio_span_dynamic::set_end_moment (Moment mom)
 {
-  if (dynamics_.size () <= 1)
-    return;
-
-  assert (dynamics_[0]->volume_ >= 0);
-
-  while (dynamics_.back ()->volume_ > 0
-         && dynamics_.size () > 1
-         && sign (dynamics_.back ()->volume_ - dynamics_[0]->volume_) != grow_dir_)
+  if (mom < start_moment_)
     {
-      dynamics_.erase (dynamics_.end () - 1);
+      programming_error (_f ("end moment (%s) < start moment (%s)",
+                             mom.to_string ().c_str (),
+                             start_moment_.to_string ().c_str ()));
+      mom = start_moment_;
     }
 
-  if (dynamics_.size () <= 1)
+  duration_ = moment_to_real (mom - start_moment_);
+}
+
+void
+Audio_span_dynamic::set_volume (Real start, Real target)
+{
+  if (!(start >= 0))
     {
-      programming_error ("Impossible or ambiguous (de)crescendo in MIDI.");
-      return;
+      programming_error (_f ("invalid start volume: %f", start));
+      start = DEFAULT_VOLUME;
     }
 
-  Real start_v = dynamics_[0]->volume_;
-  if (dynamics_.back ()->volume_ < 0)
+  if (!(target >= 0))
     {
-      // The dynamic spanner does not end with an explicit dynamic script
-      // event.  Adjust the end volume by at most 1/4 of the available
-      // volume range in this case.
-      dynamics_.back ()->volume_ = max (min (start_v + grow_dir_ * (max_volume_ - min_volume_) * 0.25, max_volume_), min_volume_);
+      programming_error (_f ("invalid target volume: %f", target));
+      target = start;
     }
 
-  Real delta_v = dynamics_.back ()->volume_ - dynamics_[0]->volume_;
-
-  Moment start = dynamics_[0]->get_column ()->when ();
+  start_volume_ = start;
+  gain_ = target - start;
+}
 
-  Real total_t = moment_to_real (dynamics_.back ()->get_column ()->when () - start);
+Real Audio_span_dynamic::get_volume (Moment mom) const
+{
+  const Real when = moment_to_real (mom - start_moment_);
 
-  for (vsize i = 1; i < dynamics_.size (); i++)
+  if (when <= 0)
     {
-      Moment dt_moment = dynamics_[i]->get_column ()->when ()
-                         - start;
-
-      Real dt = moment_to_real (dt_moment);
-
-      Real v = start_v + delta_v * (dt / total_t);
+      if (when < 0)
+        programming_error (_f ("asked to compute volume at %f for dynamic span of duration %f starting at %s",
+                               when, duration_,
+                               start_moment_.to_string ().c_str ()));
+      return start_volume_;
+    }
 
-      dynamics_[i]->volume_ = v;
+  if (when >= duration_)
+    {
+      programming_error (_f ("asked to compute volume at +%f for dynamic span of duration %f starting at %s",
+                             when, duration_,
+                             start_moment_.to_string ().c_str ()));
+      return start_volume_ + gain_;
     }
+
+  return start_volume_ + gain_ * (when / duration_);
 }
 
 Audio_tempo::Audio_tempo (int per_minute_4)
@@ -206,20 +205,8 @@ Audio_text::Audio_text (Audio_text::Type type, const string &text_string)
   type_ = type;
 }
 
-Audio_control_function_value_change
-::Audio_control_function_value_change (Control control, Real value)
-  : control_ (control), value_ (value)
+Audio_control_change::Audio_control_change (int control, int value)
+  : control_ (control),
+    value_ (value)
 {
 }
-
-const Audio_control_function_value_change::Context_property
-Audio_control_function_value_change::context_properties_[] = {
-  // property name, enum constant, lower bound for range, upper bound for range
-  { "midiBalance",     BALANCE,      -1.0, 1.0 },
-  { "midiPanPosition", PAN_POSITION, -1.0, 1.0 },
-  { "midiExpression",  EXPRESSION,    0.0, 1.0 },
-  { "midiReverbLevel", REVERB_LEVEL,  0.0, 1.0 },
-  { "midiChorusLevel", CHORUS_LEVEL,  0.0, 1.0 },
-  // extra element to signify the end of the mapping, must be kept last
-  { 0,                 NUM_CONTROLS,  0.0, 0.0 }
-};
index 5ce67f0463d67176de80df71d0016d997ea704cb..857b3590dc1a143e29ec813c71bb1fa523129c2f 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "performer.hh"
 #include "audio-item.hh"
+#include "std-vector.hh"
 #include "stream-event.hh"
 #include "international.hh"
 
@@ -29,6 +30,7 @@ class Dynamic_performer : public Performer
 public:
   TRANSLATOR_DECLARATIONS (Dynamic_performer);
 protected:
+  virtual void finalize ();
   void stop_translation_timestep ();
   void process_music ();
   Real equalize_volume (Real);
@@ -36,25 +38,275 @@ protected:
   void listen_decrescendo (Stream_event *);
   void listen_crescendo (Stream_event *);
   void listen_absolute_dynamic (Stream_event *);
+
+private:
+  void close_and_enqueue_span ();
+  Real compute_departure_volume (Direction depart_dir,
+                                 Real start_vol,
+                                 Real end_vol,
+                                 Real min_vol,
+                                 Real max_vol);
+  bool drive_state_machine (Direction next_grow_dir);
+  // next_vol < 0 means select a target dynamic based on growth direction.
+  // return actual next volume (computed if not provided)
+  Real finish_queued_spans (Real next_vol = -1.0);
+  Real look_up_absolute_volume (SCM dynamicString,
+                                Real defaultValue);
+
+private:
+  // This performer queues a number of dynamic spans waiting for the following
+  // pattern before computing their volume levels.
+  //
+  //  1. the first (de)crescendo, followed by ...
+  //  2. zero or more spans that either change in the same direction as the
+  //     first or do not change, followed by ...
+  //  3. zero or more spans that either change in the opposite direction as the
+  //     first or do not change
+  //
+  // The search may be cut short by an absolute dynamic or the end of the
+  // context.
+  enum State
+  {
+    STATE_INITIAL = 0, // waiting for a (de)crescendo
+    STATE_DEPART, // enqueued the first span, gathering same-direction spans
+    STATE_RETURN // gathering opposite-direction spans
+  };
+
+  struct UnfinishedSpan
+  {
+    Audio_span_dynamic *dynamic_;
+    Direction grow_dir_;
+
+    UnfinishedSpan () : dynamic_ (0), grow_dir_ (CENTER) {}
+  };
+
+  struct DynamicQueue
+  {
+    vector<UnfinishedSpan> spans_;
+    // total duration of (de)crescendi (i.e. excluding fixed-volume spans)
+    Real change_duration_;
+    Real min_target_vol_;
+    Real max_target_vol_;
+
+    DynamicQueue () : change_duration_ (0) {}
+
+    void clear ()
+    {
+      spans_.clear ();
+      change_duration_ = 0;
+    }
+
+    void push_back (const UnfinishedSpan &span,
+                    Real min_target_vol,
+                    Real max_target_vol)
+    {
+      if (span.grow_dir_ != CENTER)
+        change_duration_ += span.dynamic_->get_duration ();
+      min_target_vol_ = min_target_vol;
+      max_target_vol_ = max_target_vol;
+      spans_.push_back (span);
+    }
+
+    void set_volume (Real start_vol, Real target_vol);
+  };
+
 private:
   Stream_event *script_event_;
   Drul_array<Stream_event *> span_events_;
-  Drul_array<Direction> grow_dir_;
-  Real last_volume_;
-  Audio_dynamic *absolute_;
-  Audio_span_dynamic *span_dynamic_;
-  Audio_span_dynamic *finished_span_dynamic_;
+  Direction next_grow_dir_;
+  Direction depart_dir_;
+  UnfinishedSpan open_span_;
+  DynamicQueue depart_queue_;
+  DynamicQueue return_queue_;
+  State state_;
 };
 
 Dynamic_performer::Dynamic_performer ()
+  : script_event_ (0),
+    next_grow_dir_ (CENTER),
+    depart_dir_ (CENTER),
+    state_ (STATE_INITIAL)
 {
-  last_volume_ = -1;
-  script_event_ = 0;
-  absolute_ = 0;
   span_events_[LEFT]
-    = span_events_[RIGHT] = 0;
-  span_dynamic_ = 0;
-  finished_span_dynamic_ = 0;
+  = span_events_[RIGHT] = 0;
+}
+
+bool
+Dynamic_performer::drive_state_machine (Direction next_grow_dir)
+{
+  switch (state_)
+    {
+    case STATE_INITIAL:
+      if (next_grow_dir != CENTER)
+        {
+          state_ = STATE_DEPART;
+          depart_dir_ = next_grow_dir;
+        }
+      break;
+
+    case STATE_DEPART:
+      if (next_grow_dir == -depart_dir_)
+        state_ = STATE_RETURN;
+      break;
+
+    case STATE_RETURN:
+      if (next_grow_dir == depart_dir_)
+        {
+          state_ = STATE_DEPART;
+          return true;
+        }
+      break;
+    }
+
+  return false;
+}
+
+void
+Dynamic_performer::close_and_enqueue_span ()
+{
+  if (!open_span_.dynamic_)
+    programming_error ("no open dynamic span");
+  else
+    {
+      DynamicQueue &dq
+      = (state_ == STATE_RETURN) ? return_queue_ : depart_queue_;
+
+      // Changing equalizer settings in the course of the performance does not
+      // seem very likely.  This is a fig leaf: Equalize these limit volumes
+      // now as the required context properties are current.  Note that only
+      // the limits at the end of the last span in the queue are kept.
+
+      // Resist diminishing to silence.  (Idea: Look up "ppppp"
+      // with dynamicAbsoluteVolumeFunction, however that would yield 0.25.)
+      const Real min_target = equalize_volume (0.1);
+      const Real max_target
+      = equalize_volume (Audio_span_dynamic::MAXIMUM_VOLUME);
+
+      open_span_.dynamic_->set_end_moment (now_mom ());
+      dq.push_back (open_span_, min_target, max_target);
+    }
+
+  open_span_ = UnfinishedSpan ();
+}
+
+// Set the starting and target volume for each span in the queue.  The gain
+// (loss) of any (de)crescendo is proportional to its share of the total time
+// spent changing.
+void
+Dynamic_performer::DynamicQueue::set_volume (Real start_vol,
+                                             Real target_vol)
+{
+  const Real gain = target_vol - start_vol;
+  Real dur = 0; // duration of (de)crescendi processed so far
+  Real vol = start_vol;
+  for (vector<UnfinishedSpan>::iterator it = spans_.begin ();
+       it != spans_.end (); ++it)
+    {
+      const Real prev_vol = vol;
+      if (it->grow_dir_ != CENTER)
+        {
+          // grant this (de)crescendo its portion of the gain
+          dur += it->dynamic_->get_duration ();
+          vol = start_vol + gain * (dur / change_duration_);
+        }
+      it->dynamic_->set_volume (prev_vol, vol);
+    }
+}
+
+// Return a volume which is reasonably distant from the given start and end
+// volumes in the given direction, for use as a peak volume in a passage with a
+// crescendo followed by a decrescendo (or vice versa).  If the given volumes
+// are equal, the returned volume is a also reasonable target volume for a
+// single (de)crescendo.
+//
+// The given minimum and maximum volumes are the allowable dynamic range.
+Real
+Dynamic_performer::compute_departure_volume (Direction depart_dir,
+                                             Real start_vol,
+                                             Real end_vol,
+                                             Real min_vol,
+                                             Real max_vol)
+{
+  if (depart_dir == CENTER)
+    return start_vol;
+
+  // Try to find a volume that is a minimum distance from the starting and
+  // ending volumes.  If the endpoint volumes differ, the nearer one is padded
+  // less than the farther one.
+  //
+  // Example: mf < ... > p.  The legacy behavior was to use a 25% of the
+  // dynamic range for a (de)crescendo to an unspecified target, and this tries
+  // to preserve that, but is not possible to use a 25% change for both the
+  // crescendo and the decrescendo and meet the constraints of this example.
+  // The decrescendo is a greater change than the crescendo.  Believing that
+  // 25% is already more than enough for either, pad using 25% for the greater
+  // change and 7% for the lesser change.
+  //
+  // Idea: Use a context property or callback, e.g. the difference between two
+  // dynamics in dynamicAbsoluteVolumeFunction.  0.25 is the default difference
+  // between "p" and "ff". (Isn't that rather wide for this purpose?)  0.07 is
+  // the default difference between "mp" and "mf".
+  const Real far_padding = 0.25;
+  const Real near_padding = 0.07;
+
+  // If for some reason one of the endpoints is already below the supposed
+  // minimum or maximum, just accept it.
+  min_vol = min (min (min_vol, start_vol), end_vol);
+  max_vol = max (max (max_vol, start_vol), end_vol);
+
+  const Real vol_range = max_vol - min_vol;
+
+  const Real near_vol = minmax (depart_dir, start_vol, end_vol)
+                    + depart_dir * near_padding * vol_range;
+  const Real far_vol = minmax (-depart_dir, start_vol, end_vol)
+                   + depart_dir * far_padding * vol_range;
+  const Real depart_vol = minmax (depart_dir, near_vol, far_vol);
+  return max (min (depart_vol, max_vol), min_vol);
+}
+
+Real
+Dynamic_performer::finish_queued_spans (Real next_vol)
+{
+  if (depart_queue_.spans_.empty ())
+    {
+      programming_error ("no dynamic span to finish");
+      return next_vol;
+    }
+
+  const Real start_vol = depart_queue_.spans_.front ().dynamic_->get_start_volume ();
+
+  if (return_queue_.spans_.empty ())
+    {
+      Real depart_vol = next_vol;
+
+      // If the next dynamic is not specified or is inconsistent with the
+      // direction of growth, choose a reasonable target.
+      if ((next_vol < 0) || (depart_dir_ != sign (next_vol - start_vol)))
+        {
+          depart_vol = compute_departure_volume (depart_dir_,
+                                                 start_vol, start_vol,
+                                                 depart_queue_.min_target_vol_,
+                                                 depart_queue_.max_target_vol_);
+        }
+
+      depart_queue_.set_volume (start_vol, depart_vol);
+      depart_queue_.clear ();
+      return (next_vol >= 0) ? next_vol : depart_vol;
+    }
+  else
+    {
+      // If the next dynamic is not specified, return to the starting volume.
+      const Real return_vol = (next_vol >= 0) ? next_vol : start_vol;
+      Real depart_vol = compute_departure_volume (depart_dir_,
+                                                  start_vol, return_vol,
+                                                  depart_queue_.min_target_vol_,
+                                                  depart_queue_.max_target_vol_);
+      depart_queue_.set_volume (start_vol, depart_vol);
+      depart_queue_.clear ();
+      return_queue_.set_volume (depart_vol, return_vol);
+      return_queue_.clear ();
+      return return_vol;
+    }
 }
 
 Real
@@ -67,7 +319,8 @@ Dynamic_performer::equalize_volume (Real volume)
   SCM max = get_property ("midiMaximumVolume");
   if (scm_is_number (min) || scm_is_number (max))
     {
-      Interval iv (0, 1);
+      Interval iv (Audio_span_dynamic::MINIMUM_VOLUME,
+                   Audio_span_dynamic::MAXIMUM_VOLUME);
       if (scm_is_number (min))
         iv[MIN] = scm_to_double (min);
       if (scm_is_number (max))
@@ -97,125 +350,111 @@ Dynamic_performer::equalize_volume (Real volume)
           volume = iv[MIN] + iv.length () * volume;
         }
     }
-  return volume;
+  return std::max (std::min (volume, Audio_span_dynamic::MAXIMUM_VOLUME),
+                   Audio_span_dynamic::MINIMUM_VOLUME);
+}
+
+void
+Dynamic_performer::finalize ()
+{
+  if (open_span_.dynamic_)
+    close_and_enqueue_span ();
+  finish_queued_spans ();
+}
+
+Real
+Dynamic_performer::look_up_absolute_volume (SCM dynamicString,
+                                            Real defaultValue)
+{
+  SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
+
+  SCM svolume = SCM_EOL;
+  if (ly_is_procedure (proc))
+    svolume = scm_call_1 (proc, dynamicString);
+
+  return robust_scm2double (svolume, defaultValue);
 }
 
 void
 Dynamic_performer::process_music ()
 {
-  if (span_events_[START] || span_events_[STOP] || script_event_)
+  Real volume = -1;
+
+  if (script_event_) // explicit dynamic
     {
-      // End the previous spanner when a new one begins or at an explicit stop
-      // or absolute dynamic.
-      finished_span_dynamic_ = span_dynamic_;
-      span_dynamic_ = 0;
+      volume = look_up_absolute_volume (script_event_->get_property ("text"),
+                                        Audio_span_dynamic::DEFAULT_VOLUME);
+      volume = equalize_volume (volume);
     }
-
-  if (span_events_[START])
+  else if (!open_span_.dynamic_) // first time only
     {
-      // Start of a dynamic spanner.  Create a new Audio_span_dynamic for
-      // collecting changes in dynamics within this spanner.
-      span_dynamic_ = new Audio_span_dynamic (equalize_volume (0.1), equalize_volume (1.0));
-      announce_element (Audio_element_info (span_dynamic_, span_events_[START]));
-
-      span_dynamic_->grow_dir_ = grow_dir_[START];
+      // Idea: look_up_absolute_volume (ly_symbol2scm ("mf")).
+      // It is likely to change regtests.
+      volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
     }
 
-  if (script_event_
-      || span_dynamic_
-      || finished_span_dynamic_)
+  // end the current span at relevant points
+  if (open_span_.dynamic_
+      && (span_events_[START] || span_events_[STOP] || script_event_))
     {
-      // New change in dynamics.
-      absolute_ = new Audio_dynamic ();
-
+      close_and_enqueue_span ();
       if (script_event_)
         {
-          // Explicit dynamic script event: determine the volume.
-          SCM proc = get_property ("dynamicAbsoluteVolumeFunction");
-
-          SCM svolume = SCM_EOL;
-          if (ly_is_procedure (proc))
-            {
-              // urg
-              svolume = scm_call_1 (proc, script_event_->get_property ("text"));
-            }
-
-          Real volume = robust_scm2double (svolume, 0.5);
-
-          last_volume_
-            = absolute_->volume_ = equalize_volume (volume);
+          state_ = STATE_INITIAL;
+          volume = finish_queued_spans (volume);
         }
-
-      Audio_element_info info (absolute_, script_event_);
-      announce_element (info);
     }
 
-  if (last_volume_ < 0)
+  // start a new span so that some dynamic is always in effect
+  if (!open_span_.dynamic_)
     {
-      absolute_ = new Audio_dynamic ();
-
-      last_volume_
-       = absolute_->volume_ = equalize_volume (0.71); // Backward compatible
+      if (drive_state_machine (next_grow_dir_))
+        volume = finish_queued_spans (volume);
 
-      Audio_element_info info (absolute_, script_event_);
-      announce_element (info);
-    }
+      // if not known by now, use a default volume for robustness
+      if (volume < 0)
+        volume = equalize_volume (Audio_span_dynamic::DEFAULT_VOLUME);
 
-  if (span_dynamic_)
-    span_dynamic_->add_absolute (absolute_);
+      Stream_event *cause
+      = span_events_[START] ? span_events_[START]
+        : script_event_ ? script_event_
+        : span_events_[STOP];
 
-  if (finished_span_dynamic_)
-    finished_span_dynamic_->add_absolute (absolute_);
+      open_span_.dynamic_ = new Audio_span_dynamic (now_mom (), volume);
+      open_span_.grow_dir_ = next_grow_dir_;
+      announce_element (Audio_element_info (open_span_.dynamic_, cause));
+    }
 }
 
 void
 Dynamic_performer::stop_translation_timestep ()
 {
-  if (finished_span_dynamic_)
-    {
-      finished_span_dynamic_->render ();
-      finished_span_dynamic_ = 0;
-    }
-
-  if (absolute_)
-    {
-      if (absolute_->volume_ < 0)
-        {
-          absolute_->volume_ = last_volume_;
-        }
-      else
-        {
-          last_volume_ = absolute_->volume_;
-        }
-    }
-
-  absolute_ = 0;
   script_event_ = 0;
   span_events_[LEFT]
-    = span_events_[RIGHT] = 0;
+  = span_events_[RIGHT] = 0;
+  next_grow_dir_ = CENTER;
 }
 
 void
 Dynamic_performer::listen_decrescendo (Stream_event *r)
 {
   Direction d = to_dir (r->get_property ("span-direction"));
-  span_events_[d] = r;
-  grow_dir_[d] = SMALLER;
+  if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
+    next_grow_dir_ = SMALLER;
 }
 
 void
 Dynamic_performer::listen_crescendo (Stream_event *r)
 {
   Direction d = to_dir (r->get_property ("span-direction"));
-  span_events_[d] = r;
-  grow_dir_[d] = BIGGER;
+  if (ASSIGN_EVENT_ONCE (span_events_[d], r) && (d == START))
+    next_grow_dir_ = BIGGER;
 }
 
 void
 Dynamic_performer::listen_absolute_dynamic (Stream_event *r)
 {
-  if (!script_event_)
-    script_event_ = r;
+  ASSIGN_EVENT_ONCE (script_event_, r);
 }
 
 void
index 7ce89d50158ff737e2e702609c1f7750422b84d6..eafa66288efdab93c9ab72bf49a3831c749c24b2 100644 (file)
@@ -333,7 +333,7 @@ Real
 Grob::relative_coordinate (Grob const *refp, Axis a) const
 {
   /* eaa - hmmm, should we do a programming_error() here? */
-  if ((this == NULL) || (refp == this))
+  if (refp == this)
     return 0.0;
 
   /* We catch PARENT_L_ == nil case with this, but we crash if we did
@@ -342,7 +342,8 @@ Grob::relative_coordinate (Grob const *refp, Axis a) const
   if (refp == dim_cache_[a].parent_)
     return off;
 
-  off += dim_cache_[a].parent_->relative_coordinate (refp, a);
+  if (dim_cache_[a].parent_ != NULL)
+    off += dim_cache_[a].parent_->relative_coordinate (refp, a);
 
   return off;
 }
index 8c41d18a526c19198687a73d6b63f8374615b1e1..f3a97a2949d9805f1edc480474f78262fe9c5f3e 100644 (file)
@@ -40,26 +40,32 @@ private:
   Audio_item &operator = (Audio_item const &);
 };
 
-class Audio_dynamic : public Audio_item
+// Audio_span_dynamic is open at the end of the interval, so the volume
+// grows/diminshes toward a target, but whether it reaches it depends on the
+// next Audio_span_dynamic in the performance.  For example, a crescendo
+// notated as mf < p is represented as [mf < x) [p ...) i.e. growth to some
+// volume louder than mf followed by an abrupt change to p.
+class Audio_span_dynamic : public Audio_element
 {
 public:
-  Audio_dynamic ();
+  static const Real MINIMUM_VOLUME;
+  static const Real MAXIMUM_VOLUME;
+  static const Real DEFAULT_VOLUME;
 
-  Real volume_;
-  bool silent_;
-};
+private:
+  Moment start_moment_;
+  Real start_volume_;
+  Real duration_; // = target moment - start moment
+  Real gain_; // = target volume - start volume
 
-class Audio_span_dynamic : public Audio_element
-{
 public:
-  Direction grow_dir_;
-  vector<Audio_dynamic *> dynamics_;
-  Real min_volume_;
-  Real max_volume_;
-
-  virtual void render ();
-  void add_absolute (Audio_dynamic *);
-  Audio_span_dynamic (Real min_volume, Real max_volume);
+  Moment get_start_moment () const { return start_moment_; }
+  Real get_start_volume () const { return start_volume_; }
+  Real get_duration () const { return duration_; }
+  void set_end_moment (Moment);
+  void set_volume (Real start, Real target);
+  Real get_volume (Moment) const;
+  Audio_span_dynamic (Moment mom, Real volume);
 };
 
 class Audio_key : public Audio_item
@@ -92,7 +98,7 @@ public:
   Pitch pitch_;
   Moment length_mom_;
   Pitch transposing_;
-  Audio_dynamic *dynamic_;
+  Audio_span_dynamic *dynamic_;
   int extra_velocity_;
 
   Audio_note *tied_;
@@ -138,36 +144,13 @@ public:
   int one_beat_;
 };
 
-class Audio_control_function_value_change : public Audio_item
+class Audio_control_change : public Audio_item
 {
 public:
-  // Supported control functions.
-  enum Control
-  {
-    BALANCE = 0, PAN_POSITION, EXPRESSION, REVERB_LEVEL, CHORUS_LEVEL,
-    // pseudo value for representing the size of the enum; must be kept last
-    NUM_CONTROLS
-  };
-
-  Audio_control_function_value_change (Control control, Real value);
-
-  // Information about a context property corresponding to a control function
-  // (name, the corresponding enumeration value, and the allowed range for the
-  // value of the context property).
-  struct Context_property
-  {
-    const char *name_;
-    Control control_;
-    Real range_min_;
-    Real range_max_;
-  };
-
-  // Mapping from supported control functions to the corresponding context
-  // properties.
-  static const Context_property context_properties_[];
+  Audio_control_change (int control, int value);
 
-  Control control_;
-  Real value_;
+  int control_;
+  int value_;
 };
 
 int moment_to_ticks (Moment);
index ee3946e0fd02921167b4a3f57d142bee0a64d017..6de69e16b92157352f6bd7955196ece89cd2cc93 100644 (file)
@@ -24,7 +24,7 @@
 
 class All_font_metrics;
 class Audio_column;
-class Audio_control_function_value_change;
+class Audio_control_change;
 class Audio_dynamic;
 class Audio_element;
 class Audio_instrument;
@@ -90,7 +90,8 @@ class Lyric_engraver;
 class Lyric_performer;
 class Mensural_ligature_engraver;
 class Midi_chunk;
-class Midi_control_function_value_change;
+class Midi_control_change;
+class Midi_control_change_announcer;
 class Midi_duration;
 class Midi_dynamic;
 class Midi_event;
diff --git a/lily/include/midi-cc-announcer.hh b/lily/include/midi-cc-announcer.hh
new file mode 100644 (file)
index 0000000..38cb93f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2016 by Heikki Tauriainen <g034737@welho.com>.
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MIDI_CC_ANNOUNCER_HH
+#define MIDI_CC_ANNOUNCER_HH
+
+#include "input.hh"
+#include "performer.hh"
+#include "audio-item.hh"
+
+/* Base class for announcing MIDI control changes. */
+class Midi_control_change_announcer
+{
+public:
+  /* Constructor.  The optional parameter can be used to specify an Input
+     to use for relativizing warning messages about out-of-range values. */
+  Midi_control_change_announcer (Input *origin = 0);
+  virtual ~Midi_control_change_announcer ();
+
+  void announce_from_context_properties ();
+
+  /* Announces MIDI CC changes by creating new Audio_control_change events
+     from them, and calling 'do_announce' on each event.  Control change
+     events will be created from every supported MIDI context property for
+     which the 'get_property_value' function returns a value that is
+     compatible with the expected type of the context property's value. */
+  void announce_control_changes ();
+
+private:
+  virtual SCM get_property_value (const char *property_name) = 0;
+  virtual void do_announce (Audio_control_change *item) = 0;
+  void warn (const string &message);
+
+  Input *origin_;
+
+  struct Control_spec
+  {
+    const char *const context_property_name_;
+    const Real range_min_;
+    const Real range_max_;
+    const int msb_control_number_;
+    const int lsb_control_number_;
+  };
+
+  static const Control_spec controls_[];
+};
+
+#endif // MIDI_CC_ANNOUNCER_HH
index b593ce152739e7bf82eb904818213905cc8b7a67..57bb2d4da22eb4d7840b0b85b0ac87d9a07a61d8 100644 (file)
@@ -52,27 +52,27 @@ public:
   Midi_channel_item (Audio_item *ai);
 };
 
-/**
-   Midi control function value changes.
-*/
-class Midi_control_function_value_change : public Midi_channel_item
+class Midi_duration : public Midi_item
 {
 public:
-  DECLARE_CLASSNAME (Midi_control_function_value_change);
-  Midi_control_function_value_change (Audio_control_function_value_change *ai);
-  virtual ~Midi_control_function_value_change ();
+  Midi_duration (Real seconds_f);
+
   virtual string to_string () const;
-  Audio_control_function_value_change::Control control_;
-  Real value_;
+  Real seconds_;
 };
 
-class Midi_duration : public Midi_item
+/**
+   MIDI control change
+*/
+class Midi_control_change : public Midi_channel_item
 {
 public:
-  Midi_duration (Real seconds_f);
-
+  DECLARE_CLASSNAME (Midi_control_change);
+  Midi_control_change (Audio_control_change *ai);
+  virtual ~Midi_control_change ();
   virtual string to_string () const;
-  Real seconds_;
+
+  Audio_control_change *audio_;
 };
 
 /**
@@ -157,17 +157,6 @@ public:
   Audio_text *audio_;
 };
 
-class Midi_dynamic : public Midi_channel_item
-{
-public:
-  Midi_dynamic (Audio_dynamic *);
-  DECLARE_CLASSNAME (Midi_dynamic);
-
-  virtual string to_string () const;
-
-  Audio_dynamic *audio_;
-};
-
 class Midi_piano_pedal : public Midi_channel_item
 {
 public:
diff --git a/lily/midi-cc-announcer.cc b/lily/midi-cc-announcer.cc
new file mode 100644 (file)
index 0000000..7fb4ed6
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2016 by Heikki Tauriainen <g034737@welho.com>.
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "audio-item.hh"
+#include "input.hh"
+#include "international.hh"
+#include "libc-extension.hh"
+#include "midi-cc-announcer.hh"
+
+/*
+  Context properties for setting MIDI controls.  Each MIDI control
+  specification has the following components:
+    1. The name of the LilyPond context property used to change the value of
+       the MIDI control.
+    2. The lower bound for the numeric range of the LilyPond context property.
+    3. The upper bound for the numeric range of the LilyPond context property.
+    4. The MIDI control number for setting the most significant 7 bits of the
+       control value.
+    5. The MIDI control number for setting the least significant 7 bits of the
+       control value, if the control supports 14-bit ("fine") resolution.  If
+       the control supports only 7-bit ("coarse") resolution, the LSB control
+       number should be negative.
+*/
+const Midi_control_change_announcer::Control_spec
+Midi_control_change_announcer::controls_[]
+=
+{
+  { "midiBalance", -1.0, 1.0, 8, 40 },
+  { "midiPanPosition", -1.0, 1.0, 10, 42 },
+  { "midiExpression", 0.0, 1.0, 11, 43 },
+  { "midiReverbLevel", 0.0, 1.0, 91, -1 },
+  { "midiChorusLevel", 0.0, 1.0, 93, -1 },
+  // This element should be kept last in the array.
+  { 0, 0.0, 0.0, 0, 0 }
+};
+
+Midi_control_change_announcer::Midi_control_change_announcer (Input *origin)
+  : origin_ (origin)
+{
+}
+
+Midi_control_change_announcer::~Midi_control_change_announcer ()
+{
+}
+
+void Midi_control_change_announcer::announce_control_changes ()
+{
+  for (const Control_spec *spec = controls_; spec->context_property_name_;
+       ++spec)
+    {
+      SCM value = get_property_value (spec->context_property_name_);
+      if (!scm_is_number (value))
+        continue;
+      Real val = scm_to_double (value);
+      if (val >= spec->range_min_ && val <= spec->range_max_)
+        {
+          // Normalize the value to the 0.0 to 1.0 range.
+          val = ((val - spec->range_min_)
+                 / (spec->range_max_ - spec->range_min_));
+          // Transform the normalized context property value into a 14-bit or
+          // a 7-bit (non-negative) integer depending on the MIDI control's
+          // resolution.  For directional value changes, #CENTER will
+          // correspond to 0.5 exactly, and my_round rounds upwards when in
+          // case of doubt.  That means that center position will round to
+          // 0x40 or 0x2000 by a hair's breadth.
+          const Real full_fine_scale = 0x3FFF;
+          const Real full_coarse_scale = 0x7F;
+          const bool fine_resolution = (spec->lsb_control_number_ >= 0);
+          const int v = (int) (my_round (val * (fine_resolution
+                                                ? full_fine_scale
+                                                : full_coarse_scale)));
+          // Announce a control change for the most significant 7 bits of the
+          // control value (and, if the control supports fine resolution, for
+          // the least significant 7 bits as well).
+          do_announce (new Audio_control_change (spec->msb_control_number_,
+                                                 fine_resolution
+                                                 ? (v >> 7) : v));
+          if (fine_resolution)
+            do_announce (new Audio_control_change (spec->lsb_control_number_,
+                                                   v & 0x7F));
+        }
+      else
+        warn (_f ("ignoring out-of-range value change for MIDI property `%s'",
+                  spec->context_property_name_));
+    }
+}
+
+void Midi_control_change_announcer::warn (const string &message)
+{
+  if (origin_)
+    origin_->warning (message);
+  else
+    warning (message);
+}
diff --git a/lily/midi-cc-performer.cc b/lily/midi-cc-performer.cc
new file mode 100644 (file)
index 0000000..2aff5d0
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2013--2016 by Heikki Tauriainen <g034737@welho.com>.
+  Adapted from performer implementations
+  Copyright (C) 1996--2015 Jan Nieuwenhuizen <janneke@gnu.org>,
+  Han-Wen Nienhyus <hanwen@xs4all.nl> and others.
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "performer.hh"
+
+#include "audio-item.hh"
+#include "context.hh"
+#include "dispatcher.hh"
+#include "international.hh"
+#include "listener.hh"
+#include "midi-cc-announcer.hh"
+#include "stream-event.hh"
+
+#include "translator.icc"
+
+/**
+   MIDI control change performer.  Announces "set property" events on MIDI
+   context properties.
+*/
+class Midi_control_change_performer : public Performer
+{
+public:
+  TRANSLATOR_DECLARATIONS (Midi_control_change_performer);
+  void announce_control_change (SCM);
+  ~Midi_control_change_performer ();
+
+  void connect_to_context (Context *c);
+  void disconnect_from_context (Context *c);
+
+private:
+  class Control_change_announcer : public Midi_control_change_announcer
+  {
+  public:
+    Control_change_announcer (Midi_control_change_performer *p,
+                              Stream_event *ev, const string &s);
+
+    SCM get_property_value (const char *property_name);
+    void do_announce (Audio_control_change *item);
+
+  private:
+    Midi_control_change_performer *performer_;
+    Stream_event *event_;
+    string symbol_;
+  };
+};
+
+Midi_control_change_performer::Midi_control_change_performer ()
+{
+}
+
+Midi_control_change_performer::~Midi_control_change_performer ()
+{
+}
+
+void
+Midi_control_change_performer::connect_to_context (Context *c)
+{
+  c->events_below ()->
+  add_listener (GET_LISTENER (Midi_control_change_performer,
+                              announce_control_change),
+                ly_symbol2scm ("SetProperty"));
+}
+
+void
+Midi_control_change_performer::disconnect_from_context (Context *c)
+{
+  c->events_below ()->
+  remove_listener (GET_LISTENER (Midi_control_change_performer,
+                                 announce_control_change),
+                   ly_symbol2scm ("SetProperty"));
+}
+
+void
+Midi_control_change_performer::announce_control_change (SCM sev)
+{
+  Stream_event *ev = unsmob<Stream_event> (sev);
+  SCM sym = ev->get_property ("symbol");
+  if (!scm_is_symbol (sym))
+    return;
+
+  Control_change_announcer a (this, ev, ly_symbol2string (sym));
+  a.announce_control_changes ();
+}
+
+Midi_control_change_performer::Control_change_announcer::Control_change_announcer
+(Midi_control_change_performer *p, Stream_event *ev, const string &s)
+  : Midi_control_change_announcer (ev->origin ()),
+    performer_ (p),
+    event_ (ev),
+    symbol_ (s)
+{
+}
+
+SCM
+Midi_control_change_performer::Control_change_announcer::get_property_value
+(const char *property_name)
+{
+  return symbol_ == property_name ? event_->get_property ("value") : SCM_EOL;
+}
+
+void Midi_control_change_performer::Control_change_announcer::do_announce
+(Audio_control_change *item)
+{
+  performer_->announce_element (Audio_element_info (item, 0));
+}
+
+void
+Midi_control_change_performer::boot ()
+{
+
+}
+
+ADD_TRANSLATOR (Midi_control_change_performer,
+                /* doc */
+                "This performer listens to SetProperty events on context "
+                "properties for generating MIDI control changes and "
+                "prepares them for MIDI output.",
+
+                /* create */
+                "",
+
+                /* read */
+                "midiBalance "
+                "midiPanPosition "
+                "midiExpression "
+                "midiReverbLevel "
+                "midiChorusLevel ",
+
+                /* write */
+                ""
+               );
diff --git a/lily/midi-control-function-performer.cc b/lily/midi-control-function-performer.cc
deleted file mode 100644 (file)
index a112e6b..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
-  This file is part of LilyPond, the GNU music typesetter.
-
-  Copyright (C) 2013--2015 by Heikki Tauriainen <g034737@welho.com>.
-  Adapted from performer implementations
-  Copyright (C) 1996--2015 Jan Nieuwenhuizen <janneke@gnu.org>,
-  Han-Wen Nienhyus <hanwen@xs4all.nl> and others.
-
-  LilyPond is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 3 of the License, or
-  (at your option) any later version.
-
-  LilyPond is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "performer.hh"
-
-#include "audio-item.hh"
-#include "context.hh"
-#include "dispatcher.hh"
-#include "international.hh"
-#include "listener.hh"
-#include "stream-event.hh"
-
-#include "translator.icc"
-
-/**
-   MIDI control function performer.  Announces "set property" events on MIDI
-   context properties.
-*/
-class Midi_control_function_performer : public Performer
-{
-public:
-  TRANSLATOR_DECLARATIONS (Midi_control_function_performer);
-  void announce_function_value_change (SCM);
-  ~Midi_control_function_performer ();
-
-  void connect_to_context (Context *c);
-  void disconnect_from_context (Context *c);
-};
-
-Midi_control_function_performer::Midi_control_function_performer ()
-{
-}
-
-Midi_control_function_performer::~Midi_control_function_performer ()
-{
-}
-
-void
-Midi_control_function_performer::connect_to_context (Context *c)
-{
-  c->events_below ()->
-    add_listener (GET_LISTENER (Midi_control_function_performer, announce_function_value_change),
-                  ly_symbol2scm ("SetProperty"));
-}
-
-void
-Midi_control_function_performer::disconnect_from_context (Context *c)
-{
-  c->events_below ()->
-    remove_listener (GET_LISTENER (Midi_control_function_performer, announce_function_value_change),
-                     ly_symbol2scm ("SetProperty"));
-}
-
-void
-Midi_control_function_performer::announce_function_value_change (SCM sev)
-{
-  Stream_event *ev = unsmob<Stream_event> (sev);
-  SCM sym = ev->get_property ("symbol");
-  if (!scm_is_symbol (sym))
-    return;
-
-  // Search for a matching context property; if found, check that the value
-  // of the property is within the allowed range, and announce a possible
-  // change in the value of the corresponding control function.
-  string symbol = ly_symbol2string (sym);
-  for (const Audio_control_function_value_change::Context_property *p
-         = Audio_control_function_value_change::context_properties_;
-       p->name_; ++p)
-    {
-      if (symbol == p->name_)
-        {
-          SCM value = ev->get_property ("value");
-          if (scm_is_number (value))
-            {
-              Real val = scm_to_double (value);
-              if (val >= p->range_min_ && val <= p->range_max_)
-                {
-                  // Normalize the value to the 0.0 to 1.0 range.
-                  val = ((val - p->range_min_)
-                         / (p->range_max_ - p->range_min_));
-                  Audio_control_function_value_change *item
-                    = new Audio_control_function_value_change (p->control_,
-                                                               val);
-                  announce_element (Audio_element_info (item, 0));
-                }
-              else
-                ev->origin ()->
-                  warning (_f ("ignoring out-of-range value change for MIDI "
-                               "property `%s'",
-                               p->name_));
-            }
-          break;
-        }
-    }
-}
-
-void
-Midi_control_function_performer::boot ()
-{
-
-}
-
-ADD_TRANSLATOR (Midi_control_function_performer,
-                /* doc */
-                "",
-
-                /* create */
-                "",
-
-                /* read */
-                "midiBalance "
-                "midiPanPosition "
-                "midiExpression "
-                "midiReverbLevel "
-                "midiChorusLevel ",
-
-                /* write */
-                ""
-               );
index 33dd9f11bde71fedb14e67b0174fc4b0e277fc1a..ba29ba0ba59264a2d51682489a34f3e1300eb7db 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "midi-item.hh"
 
+#include "audio-column.hh"
 #include "duration.hh"
 #include "international.hh"
 #include "libc-extension.hh"
@@ -42,8 +43,6 @@ Midi_item::get_midi (Audio_item *a)
     return i->str_.length () ? new Midi_instrument (i) : 0;
   else if (Audio_note *i = dynamic_cast<Audio_note *> (a))
     return new Midi_note (i);
-  else if (Audio_dynamic *i = dynamic_cast<Audio_dynamic *> (a))
-    return new Midi_dynamic (i);
   else if (Audio_piano_pedal *i = dynamic_cast<Audio_piano_pedal *> (a))
     return new Midi_piano_pedal (i);
   else if (Audio_tempo *i = dynamic_cast<Audio_tempo *> (a))
@@ -52,9 +51,8 @@ Midi_item::get_midi (Audio_item *a)
     return new Midi_time_signature (i);
   else if (Audio_text *i = dynamic_cast<Audio_text *> (a))
     return new Midi_text (i);
-  else if (Audio_control_function_value_change *i
-           = dynamic_cast<Audio_control_function_value_change *> (a))
-    return new Midi_control_function_value_change (i);
+  else if (Audio_control_change *i = dynamic_cast<Audio_control_change *> (a))
+    return new Midi_control_change (i);
   else
     assert (0);
 
@@ -106,9 +104,9 @@ Midi_channel_item::Midi_channel_item (Audio_item *ai)
 {
 }
 
-Midi_control_function_value_change
-::Midi_control_function_value_change (Audio_control_function_value_change *ai)
-  : Midi_channel_item (ai), control_ (ai->control_), value_ (ai->value_)
+Midi_control_change::Midi_control_change (Audio_control_change *ai)
+  : Midi_channel_item (ai),
+    audio_ (ai)
 {
 }
 
@@ -120,7 +118,7 @@ Midi_channel_item::~Midi_channel_item ()
 {
 }
 
-Midi_control_function_value_change::~Midi_control_function_value_change ()
+Midi_control_change::~Midi_control_change ()
 {
 }
 
@@ -193,8 +191,8 @@ Midi_time_signature::to_string () const
 Midi_note::Midi_note (Audio_note *a)
   : Midi_channel_item (a),
     audio_ (a),
-    dynamic_byte_ (min (max (Byte ((a->dynamic_ && a->dynamic_->volume_ >= 0
-                                    ? a->dynamic_->volume_ * 0x7f : 0x5a)
+    dynamic_byte_ (min (max (Byte ((a->dynamic_
+                                    ? a->dynamic_->get_volume (a->audio_column_->when ()) * 0x7f : 0x5a)
                                    + a->extra_velocity_),
                              Byte (0)), Byte (0x7f)))
 {
@@ -275,40 +273,6 @@ Midi_note_off::to_string () const
   return str;
 }
 
-Midi_dynamic::Midi_dynamic (Audio_dynamic *a)
-  : Midi_channel_item (a),
-    audio_ (a)
-{
-}
-
-string
-Midi_dynamic::to_string () const
-{
-  Byte status_byte = (char) (0xB0 + channel_);
-  string str = ::to_string ((char)status_byte);
-
-  /*
-    Main volume controller (per channel):
-    07 MSB
-    27 LSB
-  */
-  static Real const full_scale = 127;
-
-  int volume = (int) (audio_->volume_ * full_scale);
-  if (volume <= 0)
-    volume = 1;
-  if (volume > full_scale)
-    volume = (int)full_scale;
-
-  int const volume_default = 100;
-  if (audio_->volume_ < 0 || audio_->silent_)
-    volume = volume_default;
-
-  str += ::to_string ((char)0x07);
-  str += ::to_string ((char)volume);
-  return str;
-}
-
 Midi_piano_pedal::Midi_piano_pedal (Audio_piano_pedal *a)
   : Midi_channel_item (a),
     audio_ (a)
@@ -363,63 +327,12 @@ Midi_text::to_string () const
 }
 
 string
-Midi_control_function_value_change::to_string () const
+Midi_control_change::to_string () const
 {
-  // MIDI control function information.  A MIDI control function may have one
-  // or two assigned control numbers depending on whether it supports coarse
-  // (7-bit) or fine (14-bit) resolution.  If the control function supports
-  // fine resolution, the first (respectively, second) member of the structure
-  // represents the control number for setting the most (least) significant 7
-  // bits of the control function's value.
-  struct Control_function
-  {
-    int msb_control_number_;
-    int lsb_control_number_;
-  };
-
-  // Mapping from supported control functions (enumeration values defined in
-  // Audio_controller_value_change::Control) to the corresponding MIDI control
-  // numbers.
-  static const Control_function control_functions[] =
-    {
-      // When adding support for new control functions, please note the
-      // following:
-      // - The order of the control number definitions should be kept
-      //   consistent with the order of the enumeration values defined in
-      //   Audio_control_function_value_change::Control.
-      // - If the control function has only coarse resolution, the function's
-      //   control number should be stored in the MSB member of the array
-      //   element, and the LSB member should be set to a negative value.
-
-      {  8, 40 }, // balance
-      { 10, 42 }, // pan position
-      { 11, 43 }, // expression
-      { 91, -1 }, // reverb level (only coarse resolution available)
-      { 93, -1 }  // chorus level (only coarse resolution available)
-    };
-
-  string str;
-  const Control_function *control_function = &control_functions[control_];
-  static const Real full_fine_scale = 0x3FFF;
-  static const Real full_coarse_scale = 0x7F;
-  bool fine_resolution = (control_function->lsb_control_number_ >= 0);
-  // value_ is in range [0.0 .. 1.0].  For directional value ranges,
-  // #CENTER will correspond to 0.5 exactly, and my_round rounds
-  // upwards when in case of doubt.  That means that center position
-  // will round to 0x40 or 0x2000 by a hair's breadth.
-  int value = (int) my_round (value_ * (fine_resolution ?
-                                        full_fine_scale : full_coarse_scale));
   Byte status_byte = (char) (0xB0 + channel_);
-  str += ::to_string ((char)status_byte);
-  str += ::to_string ((char)(control_function->msb_control_number_));
-  str += ::to_string ((char)(fine_resolution ? (value >> 7) : value));
-  if (fine_resolution)
-    {
-      str += ::to_string ((char)0x00);
-      str += ::to_string ((char)status_byte);
-      str += ::to_string ((char)(control_function->lsb_control_number_));
-      str += ::to_string ((char)(value & 0x7F));
-    }
+  string str = ::to_string ((char)status_byte);
+  str += ::to_string ((char) (audio_->control_));
+  str += ::to_string ((char) (audio_->value_));
   return str;
 }
 
index 3a05cbd0e25213673aa5577c55c7eb7d3fe5feaf..d413a46835b8694fc1c9af157de340fb35059a88 100644 (file)
@@ -25,6 +25,7 @@
 #include "audio-staff.hh"
 #include "context.hh"
 #include "international.hh"
+#include "midi-cc-announcer.hh"
 #include "performer-group.hh"
 #include "warn.hh"
 #include "lily-imports.hh"
@@ -54,7 +55,23 @@ private:
   int get_channel (const string &instrument);
   Audio_staff *get_audio_staff (const string &voice);
   Audio_staff *new_audio_staff (const string &voice);
-  Audio_dynamic *get_dynamic (const string &voice);
+  Audio_span_dynamic *get_dynamic (const string &voice);
+
+  class Midi_control_initializer : public Midi_control_change_announcer
+  {
+  public:
+    Midi_control_initializer (Staff_performer *performer,
+                              Audio_staff *audio_staff,
+                              int channel);
+
+    SCM get_property_value (const char *property_name);
+    void do_announce (Audio_control_change *item);
+
+  private:
+    Staff_performer *performer_;
+    Audio_staff *audio_staff_;
+    int channel_;
+  };
 
   string instrument_string_;
   int channel_;
@@ -65,7 +82,7 @@ private:
   map<string, deque<Audio_note *> > note_map_;
   map<string, Audio_staff *> staff_map_;
   map<string, int> channel_map_;
-  map<string, Audio_dynamic *> dynamic_map_;
+  map<string, Audio_span_dynamic *> dynamic_map_;
   // Would prefer to have the following two items be
   // members of the containing class Performance,
   // so they can be reset for each new midi file output.
@@ -135,32 +152,9 @@ Staff_performer::new_audio_staff (const string &voice)
   staff_map_[voice] = audio_staff;
   if (!instrument_string_.empty ())
     set_instrument (channel_, voice);
-  // Set initial values (if any) for control functions.
-  for (const Audio_control_function_value_change::Context_property *p
-         = Audio_control_function_value_change::context_properties_;
-       p->name_; ++p)
-    {
-      SCM value = get_property (p->name_);
-      if (scm_is_number (value))
-        {
-          Real val = scm_to_double (value);
-          if (val >= p->range_min_ && val <= p->range_max_)
-            {
-              // Normalize the value to the 0.0 to 1.0 range.
-              val = ((val - p->range_min_)
-                     / (p->range_max_ - p->range_min_));
-              Audio_control_function_value_change *item
-                = new Audio_control_function_value_change (p->control_, val);
-              item->channel_ = channel_;
-              audio_staff->add_audio_item (item);
-              announce_element (Audio_element_info (item, 0));
-            }
-          else
-            warning (_f ("ignoring out-of-range value change for MIDI "
-                         "property `%s'",
-                         p->name_));
-        }
-    }
+  // Set initial values (if any) for MIDI controls.
+  Midi_control_initializer i (this, audio_staff, channel_);
+  i.announce_control_changes ();
   return audio_staff;
 }
 
@@ -184,10 +178,10 @@ Staff_performer::get_audio_staff (const string &voice)
   return new_audio_staff (voice);
 }
 
-Audio_dynamic *
+Audio_span_dynamic *
 Staff_performer::get_dynamic (const string &voice)
 {
-  map<string, Audio_dynamic *>::const_iterator i = dynamic_map_.find (voice);
+  map<string, Audio_span_dynamic *>::const_iterator i = dynamic_map_.find (voice);
   if (i != dynamic_map_.end ())
     return i->second;
   return 0;
@@ -227,13 +221,12 @@ Staff_performer::stop_translation_timestep ()
   instrument_name_ = 0;
   instrument_ = 0;
   // For each voice with a note played in the current translation time step,
-  // check if the voice has an Audio_dynamic registered: if yes, apply this
-  // dynamic to every note played in the voice in the current translation time
-  // step.
+  // check if the voice has a dynamic registered: if yes, apply the dynamic
+  // to every note played in the voice in the current translation time step.
   for (map<string, deque<Audio_note *> >::iterator vi = note_map_.begin ();
        vi != note_map_.end (); ++vi)
     {
-      Audio_dynamic *d = get_dynamic (vi->first);
+      Audio_span_dynamic *d = get_dynamic (vi->first);
       if (d)
         {
           for (deque<Audio_note *>::iterator ni = vi->second.begin ();
@@ -316,47 +309,61 @@ Staff_performer::get_channel (const string &instrument)
 void
 Staff_performer::acknowledge_audio_element (Audio_element_info inf)
 {
-  if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
+  /* map each context (voice) to its own track */
+  Context *c = inf.origin_contexts (this)[0];
+  string voice;
+  if (c->is_alias (ly_symbol2scm ("Voice")))
+    voice = c->id_string ();
+  SCM channel_mapping = get_property ("midiChannelMapping");
+  string str = new_instrument_string ();
+  if (!scm_is_eq (channel_mapping, ly_symbol2scm ("instrument")))
+    channel_ = get_channel (voice);
+  else if (channel_ < 0 && str.empty ())
+    channel_ = get_channel (str);
+  if (str.length ())
     {
-      /* map each context (voice) to its own track */
-      Context *c = inf.origin_contexts (this)[0];
-      string voice;
-      if (c->is_alias (ly_symbol2scm ("Voice")))
-        voice = c->id_string ();
-      SCM channel_mapping = get_property ("midiChannelMapping");
-      string str = new_instrument_string ();
-      if (!scm_is_eq (channel_mapping, ly_symbol2scm ("instrument")))
-        channel_ = get_channel (voice);
-      else if (channel_ < 0 && str.empty ())
+      if (!scm_is_eq (channel_mapping, ly_symbol2scm ("voice")))
         channel_ = get_channel (str);
-      if (str.length ())
-        {
-          if (!scm_is_eq (channel_mapping, ly_symbol2scm ("voice")))
-            channel_ = get_channel (str);
-          set_instrument (channel_, voice);
-          set_instrument_name (voice);
-        }
+      set_instrument (channel_, voice);
+      set_instrument_name (voice);
+    }
+  Audio_staff *audio_staff = get_audio_staff (voice);
+  if (Audio_item *ai = dynamic_cast<Audio_item *> (inf.elem_))
+    {
       ai->channel_ = channel_;
-      Audio_staff *audio_staff = get_audio_staff (voice);
-      bool encode_dynamics_as_velocity_ = true;
-      if (encode_dynamics_as_velocity_)
+      if (Audio_note *n = dynamic_cast<Audio_note *> (inf.elem_))
         {
-          if (Audio_note *n = dynamic_cast<Audio_note *> (inf.elem_))
-            {
-              // Keep track of the notes played in the current voice in this
-              // translation time step (for adjusting their dynamics later in
-              // stop_translation_timestep).
-              note_map_[voice].push_back (n);
-            }
-          else if (Audio_dynamic *d = dynamic_cast<Audio_dynamic *> (inf.elem_))
-            {
-              dynamic_map_[voice] = d;
-              // Output volume as velocity: skip Midi_dynamic output for the
-              // current element.
-              return;
-            }
+          // Keep track of the notes played in the current voice in this
+          // translation time step (for adjusting their dynamics later in
+          // stop_translation_timestep).
+          note_map_[voice].push_back (n);
         }
       audio_staff->add_audio_item (ai);
     }
+  else if (Audio_span_dynamic *d = dynamic_cast<Audio_span_dynamic *> (inf.elem_))
+    {
+      dynamic_map_[voice] = d;
+    }
+}
+
+Staff_performer::Midi_control_initializer::Midi_control_initializer
+(Staff_performer *performer, Audio_staff *audio_staff, int channel)
+  : performer_ (performer),
+    audio_staff_ (audio_staff),
+    channel_ (channel)
+{
+}
+
+SCM Staff_performer::Midi_control_initializer::get_property_value
+(const char *property_name)
+{
+  return performer_->get_property (property_name);
 }
 
+void Staff_performer::Midi_control_initializer::do_announce
+(Audio_control_change *item)
+{
+  item->channel_ = channel_;
+  audio_staff_->add_audio_item (item);
+  performer_->announce_element (Audio_element_info (item, 0));
+}
index fe8e3b02a7f1dcea22cfc178686f7b329a4e95b1..657b9b1f507fc4ccdca638fba5f1ab290311f160 100644 (file)
@@ -23,7 +23,7 @@ That's it.  For more information, visit http://lilypond.org .
 
 %}
 
-\version "2.19.45"  % necessary for upgrading to future LilyPond versions.
+\version "2.19.46"  % necessary for upgrading to future LilyPond versions.
 
 \header{
   title = "A scale in LilyPond"
index cb4128f17fc151222676a225450254fb4e18026b..9f950733bd01327316e36dca3b72ccf997f15f18 100644 (file)
@@ -32,7 +32,7 @@ Good luck with LilyPond!  Happy engraving.
 
 %}
 
-\version "2.19.45"  % necessary for upgrading to future LilyPond versions.
+\version "2.19.46"  % necessary for upgrading to future LilyPond versions.
 
 \header{
   title = "A scale in LilyPond"
index 00c4bdf3dabcd3b64b4b07ac369acc627cbf78b5..644a0bd2d6cc0c83c1dce217b3f6d3c73b9f8a14 100644 (file)
@@ -31,7 +31,7 @@
 
   \consists "Staff_performer"
   \consists "Key_performer"
-  \consists "Midi_control_function_performer"
+  \consists "Midi_control_change_performer"
 }
 
 \context {
index ae5cfd4108d8996e67d91acc19dad0e04d7417c0..7bd1d9a0ebe654202c77f2d5af5384cce9e3f67b 100644 (file)
@@ -6,10 +6,10 @@
 #, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: lilypond 2.19.45\n"
+"Project-Id-Version: lilypond 2.19.46\n"
 "Report-Msgid-Bugs-To: http://post.gmane.org/post.php?group=gmane.comp.gnu."
 "lilypond.bugs\n"
-"POT-Creation-Date: 2016-07-08 13:56+0100\n"
+"POT-Creation-Date: 2016-07-24 11:09+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1770,6 +1770,33 @@ msgstr ""
 msgid "no heads for arpeggio found?"
 msgstr ""
 
+#: audio-item.cc:139
+#, c-format
+msgid "end moment (%s) < start moment (%s)"
+msgstr ""
+
+#: audio-item.cc:153
+#, c-format
+msgid "invalid start volume: %f"
+msgstr ""
+
+#: audio-item.cc:159
+#, c-format
+msgid "invalid target volume: %f"
+msgstr ""
+
+#: audio-item.cc:174
+#, c-format
+msgid ""
+"asked to compute volume at %f for dynamic span of duration %f starting at %s"
+msgstr ""
+
+#: audio-item.cc:182
+#, c-format
+msgid ""
+"asked to compute volume at +%f for dynamic span of duration %f starting at %s"
+msgstr ""
+
 #: axis-group-engraver.cc:154
 msgid "Axis_group_engraver: vertical group already has a parent"
 msgstr ""
@@ -2080,7 +2107,7 @@ msgstr ""
 msgid "%d: %s"
 msgstr ""
 
-#: grob.cc:486
+#: grob.cc:487
 #, c-format
 msgid "ignored infinite %s-offset"
 msgstr ""
@@ -2522,12 +2549,12 @@ msgstr ""
 msgid "ignoring out-of-range value change for MIDI property `%s'"
 msgstr ""
 
-#: midi-item.cc:93
+#: midi-item.cc:92
 #, c-format
 msgid "no such MIDI instrument: `%s'"
 msgstr ""
 
-#: midi-item.cc:179
+#: midi-item.cc:178
 msgid "Time signature with more than 255 beats.  Truncating"
 msgstr ""
 
@@ -3075,11 +3102,11 @@ msgstr ""
 msgid "expected to read %d characters, got %d"
 msgstr ""
 
-#: staff-performer.cc:307
+#: staff-performer.cc:306
 msgid "MIDI channel wrapped around"
 msgstr ""
 
-#: staff-performer.cc:308
+#: staff-performer.cc:307
 msgid "remapping modulo 16"
 msgstr ""
 
@@ -3228,119 +3255,119 @@ msgstr ""
 msgid "giving up"
 msgstr ""
 
-#: parser.yy:483 parser.yy:650 parser.yy:989 parser.yy:1070 parser.yy:1306
+#: parser.yy:482 parser.yy:649 parser.yy:988 parser.yy:1069 parser.yy:1305
 msgid "bad expression type"
 msgstr ""
 
-#: parser.yy:902 parser.yy:1514 parser.yy:1583
+#: parser.yy:901 parser.yy:1508 parser.yy:1570
 msgid "not a context mod"
 msgstr ""
 
-#: parser.yy:983 parser.yy:1062 parser.yy:1214
+#: parser.yy:982 parser.yy:1061 parser.yy:1213
 msgid "need \\paper for paper block"
 msgstr ""
 
-#: parser.yy:1096
+#: parser.yy:1095
 msgid "Missing music in \\score"
 msgstr ""
 
-#: parser.yy:1133
+#: parser.yy:1132
 msgid "\\paper cannot be used in \\score, use \\layout instead"
 msgstr ""
 
-#: parser.yy:1184
+#: parser.yy:1183
 msgid "Spurious expression in \\score"
 msgstr ""
 
-#: parser.yy:1388
+#: parser.yy:1387
 msgid "music expected"
 msgstr ""
 
-#: parser.yy:1398 parser.yy:1432
+#: parser.yy:1397 parser.yy:1431
 msgid "unexpected post-event"
 msgstr ""
 
-#: parser.yy:1440
+#: parser.yy:1439
 msgid "Ignoring non-music expression"
 msgstr ""
 
-#: parser.yy:1763 parser.yy:1782
+#: parser.yy:1749 parser.yy:1768
 msgid "not a key"
 msgstr ""
 
-#: parser.yy:2642 parser.yy:2760 parser.yy:2773 parser.yy:2782
+#: parser.yy:2628 parser.yy:2746 parser.yy:2759 parser.yy:2768
 msgid "bad grob property path"
 msgstr ""
 
-#: parser.yy:2740
+#: parser.yy:2726
 msgid "only \\consists and \\remove take non-string argument."
 msgstr ""
 
-#: parser.yy:2801
+#: parser.yy:2787
 msgid "bad context property path"
 msgstr ""
 
-#: parser.yy:2886
+#: parser.yy:2872
 msgid "markup expected"
 msgstr ""
 
-#: parser.yy:2898
+#: parser.yy:2884
 msgid "simple string expected"
 msgstr ""
 
-#: parser.yy:2915
+#: parser.yy:2901
 msgid "symbol expected"
 msgstr ""
 
-#: parser.yy:3059
+#: parser.yy:3041
 msgid "not a rhythmic event"
 msgstr ""
 
-#: parser.yy:3109
+#: parser.yy:3091
 msgid "post-event expected"
 msgstr ""
 
-#: parser.yy:3118 parser.yy:3123
+#: parser.yy:3100 parser.yy:3105
 msgid "have to be in Lyric mode for lyrics"
 msgstr ""
 
-#: parser.yy:3199
+#: parser.yy:3181
 msgid "expecting string or post-event as script definition"
 msgstr ""
 
-#: parser.yy:3303
+#: parser.yy:3285
 msgid "not an articulation"
 msgstr ""
 
-#: parser.yy:3369 parser.yy:3421
+#: parser.yy:3351 parser.yy:3403
 msgid "not a duration"
 msgstr ""
 
-#: parser.yy:3442
+#: parser.yy:3424
 msgid "bass number expected"
 msgstr ""
 
-#: parser.yy:3534
+#: parser.yy:3516
 msgid "have to be in Note mode for notes"
 msgstr ""
 
-#: parser.yy:3573
+#: parser.yy:3555
 msgid "have to be in Chord mode for chords"
 msgstr ""
 
-#: parser.yy:3616
+#: parser.yy:3598
 msgid "markup outside of text script or \\lyricmode"
 msgstr ""
 
-#: parser.yy:3621
+#: parser.yy:3603
 msgid "unrecognized string, not in text script or \\lyricmode"
 msgstr ""
 
-#: parser.yy:3773 parser.yy:3782
+#: parser.yy:3755 parser.yy:3764
 msgid "not an unsigned integer"
 msgstr ""
 
-#: parser.yy:3869
+#: parser.yy:3851
 msgid "not a markup"
 msgstr ""
 
@@ -3641,12 +3668,12 @@ msgstr ""
 msgid "Writing ~a..."
 msgstr ""
 
-#: framework-ps.scm:259
+#: framework-ps.scm:260
 #, scheme-format
 msgid "CFF font `~a' already embedded, skipping."
 msgstr ""
 
-#: framework-ps.scm:262
+#: framework-ps.scm:263
 #, scheme-format
 msgid ""
 "Different CFF fonts which have the same name `~a' has been detected. The "
@@ -3655,51 +3682,55 @@ msgstr ""
 
 #: framework-ps.scm:267
 #, scheme-format
-msgid "Embedding CFF font `~a'"
+msgid "Embedding CFF font `~a'."
+msgstr ""
+
+#: framework-ps.scm:272
+msgid "Initializing embedded CFF font list."
 msgstr ""
 
-#: framework-ps.scm:317
+#: framework-ps.scm:325
 #, scheme-format
 msgid ""
 "Font ~a cannot be loaded via Ghostscript because its font-index (~a) is not "
 "zero."
 msgstr ""
 
-#: framework-ps.scm:323
+#: framework-ps.scm:331
 #, scheme-format
 msgid ""
-"Font ~a cannot be loaded via Ghostscript because it is an OpenType/CFF (OTC) "
-"font."
+"Font ~a cannot be loaded via Ghostscript because it is an OpenType/CFF "
+"Collection (OTC) font."
 msgstr ""
 
-#: framework-ps.scm:329
+#: framework-ps.scm:337
 #, scheme-format
 msgid ""
 "Font ~a cannot be used via Ghostscript because it is a TrueType font that "
 "does not have glyph names."
 msgstr ""
 
-#: framework-ps.scm:343
+#: framework-ps.scm:351
 #, scheme-format
 msgid "cannot embed ~S=~S"
 msgstr ""
 
-#: framework-ps.scm:386
+#: framework-ps.scm:394
 #, scheme-format
 msgid "cannot extract file matching ~a from ~a"
 msgstr ""
 
-#: framework-ps.scm:403
+#: framework-ps.scm:411
 #, scheme-format
 msgid "do not know how to embed ~S=~S"
 msgstr ""
 
-#: framework-ps.scm:428
+#: framework-ps.scm:436
 #, scheme-format
 msgid "do not know how to embed font ~s ~s ~s"
 msgstr ""
 
-#: framework-ps.scm:810
+#: framework-ps.scm:820
 msgid ""
 "\n"
 "The PostScript backend does not support the\n"
@@ -4008,12 +4039,12 @@ msgstr ""
 msgid "quoted music `~a' is empty"
 msgstr ""
 
-#: ps-to-png.scm:72 ps-to-png.scm:75
+#: ps-to-png.scm:74 ps-to-png.scm:77
 #, scheme-format
 msgid "Copying `~a' to `~a'..."
 msgstr ""
 
-#: ps-to-png.scm:77 ps-to-png.scm:79
+#: ps-to-png.scm:79 ps-to-png.scm:81
 #, scheme-format
 msgid "Deleting `~a'..."
 msgstr ""
index 6e03a3b967d186cb79c2012e5317ac03a7755a2a..a4041196789c989d7f7a5616cdd1c8ecd10b876c 100644 (file)
        ((and (string? bare-file-name)
              (eq? (ly:get-font-format bare-file-name font-index) 'CFF)
              (is-collection-font? bare-file-name))
-        (ly:warning (_ "Font ~a cannot be loaded via Ghostscript because it is an OpenType/CFF (OTC) font.")
+        (ly:warning (_ "Font ~a cannot be loaded via Ghostscript because it is an OpenType/CFF Collection (OTC) font.")
                     name)
         (load-font font-name-filename))
        ((and (string? bare-file-name)
index d7e6b1f6b802a1b82e7eaaad11697ebee30b7eb8..79bc925655d9deefe09158674f6a9affac10cc0d 100644 (file)
@@ -3,7 +3,7 @@
 % Load plain if necessary, i.e., if running under initex.
 \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
 %
-\def\texinfoversion{2016-05-26.20}
+\def\texinfoversion{2016-07-20.14}
 %
 % Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
 % 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
@@ -1192,6 +1192,7 @@ where each line of input produces a line of output.}
   \ifx\pdfescapestring\thisisundefined
     % No primitive available; should we give a warning or log?
     % Many times it won't matter.
+    \xdef#1{#1}%
   \else
     % The expandable \pdfescapestring primitive escapes parentheses,
     % backslashes, and other special chars.
@@ -1311,8 +1312,10 @@ output) for that.)}
     % We have to set dummies so commands such as @code, and characters
     % such as \, aren't expanded when present in a section title.
     \indexnofonts
-    \turnoffactive
     \makevalueexpandable
+    \turnoffactive
+    % Use ASCII approximations in destination names.
+    \passthroughcharsfalse
     \def\pdfdestname{#1}%
     \txiescapepdf\pdfdestname
     \safewhatsit{\pdfdest name{\pdfdestname} xyz}%
@@ -1357,8 +1360,21 @@ output) for that.)}
       \fi
       %
       % Also escape PDF chars in the display string.
-      \edef\pdfoutlinetext{#1}%
-      \txiescapepdf\pdfoutlinetext
+      \bgroup
+        \ifx \declaredencoding \latone
+          % The PDF format can use an extended form of Latin-1 in bookmark
+          % strings.  See Appendix D of the PDF Reference, Sixth Edition, for
+          % the "PDFDocEncoding".
+          \passthroughcharstrue
+        \fi
+        \ifx \declaredencoding \utfeight
+          % TODO: the PDF format can use UTF-16 in bookmark strings, but the
+          % code for this isn't done yet.
+        \fi
+        \globaldefs=1
+        \edef\pdfoutlinetext{#1}%
+        \txiescapepdf\pdfoutlinetext
+      \egroup
       %
       \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
     }
@@ -1525,11 +1541,21 @@ output) for that.)}
   %
   % XeTeX version check
   %
-  \ifnum\strcmp{\the\XeTeXversion\XeTeXrevision}{0.99995}>-1
+  \ifnum\strcmp{\the\XeTeXversion\XeTeXrevision}{0.99996}>-1
+    % TeX Live 2016 contains XeTeX 0.99996 and xdvipdfmx 20160307.
+    % It can be used `dvipdfmx:config' special (from TeX Live SVN r40941).
+    % For avoiding PDF destination name replacement, we use the special
+    % instead of xdvipdfmx commandline option `-C 0x0010'.
+    \special{dvipdfmx:config C 0x0010}
     % XeTeX 0.99995+ contains xdvipdfmx 20160307+.
     % It can handle Unicode destination name for PDF.
     \txiuseunicodedestnametrue
   \else
+    % XeTeX < 0.99996 (TeX Live < 2016) cannot be used
+    % `dvipdfmx:config' special.
+    % So for avoiding PDF destination name replacement,
+    % xdvipdfmx commandline option `-C 0x0010' is necessary.
+    %
     % XeTeX < 0.99995 can not handle Unicode destination name for PDF
     % because xdvipdfmx 20150315 has UTF-16 convert issue.
     % It fixed by xdvipdfmx 20160106 (TeX Live SVN r39753).
@@ -1540,7 +1566,7 @@ output) for that.)}
   %
   % Emulate the primitive of pdfTeX
   \def\pdfdest name#1 xyz{%
-    \special{pdf:dest (name#1) [@thispage /XYZ @xpos @ypos]}%
+    \special{pdf:dest (name#1) [@thispage /XYZ @xpos @ypos null]}%
   }
   \def\pdfmkdest#1{{%
     % We have to set dummies so commands such as @code, and characters
@@ -3870,7 +3896,7 @@ end
 
 
 \message{tables,}
-% Tables -- @table, @ftable, @vtable, @item(x).
+% Tables -- @table, @ftable, @ktable, @vtable, @item(x).
 
 % default indentation of table text
 \newdimen\tableindent \tableindent=.8in
@@ -3882,7 +3908,7 @@ end
 % used internally for \itemindent minus \itemmargin
 \newdimen\itemmax
 
-% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
+% Note @table, @ftable, @ktable and @vtable define @item, @itemx, etc., with
 % these defs.
 % They also define \itemindex
 % to index the item name in whatever manner is desired (perhaps none).
@@ -3950,7 +3976,7 @@ end
 \def\item{\errmessage{@item while not in a list environment}}
 \def\itemx{\errmessage{@itemx while not in a list environment}}
 
-% @table, @ftable, @vtable.
+% @table, @ftable, @ktable, @vtable.
 \envdef\table{%
   \let\itemindex\gobble
   \tablecheck{table}%
@@ -3959,6 +3985,10 @@ end
   \def\itemindex ##1{\doind {fn}{\code{##1}}}%
   \tablecheck{ftable}%
 }
+\envdef\ktable{%
+  \def\itemindex ##1{\doind {ky}{\code{##1}}}%
+  \tablecheck{ktable}%
+}
 \envdef\vtable{%
   \def\itemindex ##1{\doind {vr}{\code{##1}}}%
   \tablecheck{vtable}%
@@ -4002,6 +4032,7 @@ end
 }
 \def\Etable{\endgraf\afterenvbreak}
 \let\Eftable\Etable
+\let\Ektable\Etable
 \let\Evtable\Etable
 \let\Eitemize\Etable
 \let\Eenumerate\Etable
@@ -4609,11 +4640,23 @@ end
 % Like \expandablevalue, but completely expandable (the \message in the
 % definition above operates at the execution level of TeX).  Used when
 % writing to auxiliary files, due to the expansion that \write does.
+% If flag is undefined, pass through an unexpanded @value command: maybe it 
+% will be set by the time it is read back in.
 %
 % NB flag names containing - or _ may not work here.
 \def\dummyvalue#1{%
   \expandafter\ifx\csname SET#1\endcsname\relax
-    [No value for ``#1'']%
+    \noexpand\value{#1}%
+  \else
+    \csname SET#1\endcsname
+  \fi
+}
+
+% Used for @value's in index entries to form the sort key: expand the @value
+% if possible, otherwise sort late.
+\def\indexnofontsvalue#1{%
+  \expandafter\ifx\csname SET#1\endcsname\relax
+    ZZZZZZZ
   \else
     \csname SET#1\endcsname
   \fi
@@ -4760,7 +4803,7 @@ end
 
 % Define \doindex, the driver for all index macros.
 % Argument #1 is generated by the calling \fooindex macro,
-% and it the two-letter name of the index.
+% and it is the two-letter name of the index.
 
 \def\doindex#1{\edef\indexname{#1}\parsearg\doindexxxx}
 \def\doindexxxx #1{\doind{\indexname}{#1}}
@@ -4769,6 +4812,7 @@ end
 \def\docodeindex#1{\edef\indexname{#1}\parsearg\docodeindexxxx}
 \def\docodeindexxxx #1{\doind{\indexname}{\code{#1}}}
 
+\f
 % Used when writing an index entry out to an index file to prevent
 % expansion of Texinfo commands that can appear in an index entry.
 %
@@ -4787,9 +4831,11 @@ end
   \def\}{{\tt\char125}}%
   %
   % Do the redefinitions.
-  \commondummies
+  \definedummies
 }
 
+% Used for the aux and toc files, where @ is the escape character.
+%
 % For the aux and toc files, @ is the escape character.  So we want to
 % redefine everything using @ as the escape character (instead of
 % \realbackslash, still used for index files).  When everything uses @,
@@ -4802,30 +4848,35 @@ end
   \let\} = \rbraceatcmd
   %
   % Do the redefinitions.
-  \commondummies
+  \definedummies
   \otherbackslash
 }
 
-% Called from \indexdummies and \atdummies.
+% \definedummyword defines \#1 as \string\#1\space, thus effectively
+% preventing its expansion.  This is used only for control words,
+% not control letters, because the \space would be incorrect for
+% control characters, but is needed to separate the control word
+% from whatever follows.
 %
-\def\commondummies{%
-  % \definedummyword defines \#1 as \string\#1\space, thus effectively
-  % preventing its expansion.  This is used only for control words,
-  % not control letters, because the \space would be incorrect for
-  % control characters, but is needed to separate the control word
-  % from whatever follows.
-  %
-  % For control letters, we have \definedummyletter, which omits the
-  % space.
-  %
-  % These can be used both for control words that take an argument and
-  % those that do not.  If it is followed by {arg} in the input, then
-  % that will dutifully get written to the index (or wherever).
-  %
-  \def\definedummyword  ##1{\def##1{\string##1\space}}%
-  \def\definedummyletter##1{\def##1{\string##1}}%
-  \let\definedummyaccent\definedummyletter
+% These can be used both for control words that take an argument and
+% those that do not.  If it is followed by {arg} in the input, then
+% that will dutifully get written to the index (or wherever).
+%
+% For control letters, we have \definedummyletter, which omits the
+% space.
+%
+\def\definedummyword  #1{\def#1{\string#1\space}}%
+\def\definedummyletter#1{\def#1{\string#1}}%
+\let\definedummyaccent\definedummyletter
+
+% Called from \indexdummies and \atdummies, to effectively prevent
+% the expansion of commands.
+%
+\def\definedummies{%
   %
+  \let\commondummyword\definedummyword
+  \let\commondummyletter\definedummyletter
+  \let\commondummyaccent\definedummyaccent
   \commondummiesnofonts
   %
   \definedummyletter\_%
@@ -4910,77 +4961,77 @@ end
   \normalturnoffactive
 }
 
-% \commondummiesnofonts: common to \commondummies and \indexnofonts.
-% Define \definedumyletter, \definedummyaccent and \definedummyword before
-% using.
+% \commondummiesnofonts: common to \definedummies and \indexnofonts.
+% Define \commondummyletter, \commondummyaccent and \commondummyword before
+% using.  Used for accents, font commands, and various control letters.
 %
 \def\commondummiesnofonts{%
   % Control letters and accents.
-  \definedummyletter\!%
-  \definedummyaccent\"%
-  \definedummyaccent\'%
-  \definedummyletter\*%
-  \definedummyaccent\,%
-  \definedummyletter\.%
-  \definedummyletter\/%
-  \definedummyletter\:%
-  \definedummyaccent\=%
-  \definedummyletter\?%
-  \definedummyaccent\^%
-  \definedummyaccent\`%
-  \definedummyaccent\~%
-  \definedummyword\u
-  \definedummyword\v
-  \definedummyword\H
-  \definedummyword\dotaccent
-  \definedummyword\ogonek
-  \definedummyword\ringaccent
-  \definedummyword\tieaccent
-  \definedummyword\ubaraccent
-  \definedummyword\udotaccent
-  \definedummyword\dotless
+  \commondummyletter\!%
+  \commondummyaccent\"%
+  \commondummyaccent\'%
+  \commondummyletter\*%
+  \commondummyaccent\,%
+  \commondummyletter\.%
+  \commondummyletter\/%
+  \commondummyletter\:%
+  \commondummyaccent\=%
+  \commondummyletter\?%
+  \commondummyaccent\^%
+  \commondummyaccent\`%
+  \commondummyaccent\~%
+  \commondummyword\u
+  \commondummyword\v
+  \commondummyword\H
+  \commondummyword\dotaccent
+  \commondummyword\ogonek
+  \commondummyword\ringaccent
+  \commondummyword\tieaccent
+  \commondummyword\ubaraccent
+  \commondummyword\udotaccent
+  \commondummyword\dotless
   %
   % Texinfo font commands.
-  \definedummyword\b
-  \definedummyword\i
-  \definedummyword\r
-  \definedummyword\sansserif
-  \definedummyword\sc
-  \definedummyword\slanted
-  \definedummyword\t
+  \commondummyword\b
+  \commondummyword\i
+  \commondummyword\r
+  \commondummyword\sansserif
+  \commondummyword\sc
+  \commondummyword\slanted
+  \commondummyword\t
   %
   % Commands that take arguments.
-  \definedummyword\abbr
-  \definedummyword\acronym
-  \definedummyword\anchor
-  \definedummyword\cite
-  \definedummyword\code
-  \definedummyword\command
-  \definedummyword\dfn
-  \definedummyword\dmn
-  \definedummyword\email
-  \definedummyword\emph
-  \definedummyword\env
-  \definedummyword\file
-  \definedummyword\image
-  \definedummyword\indicateurl
-  \definedummyword\inforef
-  \definedummyword\kbd
-  \definedummyword\key
-  \definedummyword\math
-  \definedummyword\option
-  \definedummyword\pxref
-  \definedummyword\ref
-  \definedummyword\samp
-  \definedummyword\strong
-  \definedummyword\tie
-  \definedummyword\U
-  \definedummyword\uref
-  \definedummyword\url
-  \definedummyword\var
-  \definedummyword\verb
-  \definedummyword\w
-  \definedummyword\xref
+  \commondummyword\abbr
+  \commondummyword\acronym
+  \commondummyword\anchor
+  \commondummyword\cite
+  \commondummyword\code
+  \commondummyword\command
+  \commondummyword\dfn
+  \commondummyword\dmn
+  \commondummyword\email
+  \commondummyword\emph
+  \commondummyword\env
+  \commondummyword\file
+  \commondummyword\image
+  \commondummyword\indicateurl
+  \commondummyword\inforef
+  \commondummyword\kbd
+  \commondummyword\key
+  \commondummyword\math
+  \commondummyword\option
+  \commondummyword\pxref
+  \commondummyword\ref
+  \commondummyword\samp
+  \commondummyword\strong
+  \commondummyword\tie
+  \commondummyword\U
+  \commondummyword\uref
+  \commondummyword\url
+  \commondummyword\var
+  \commondummyword\verb
+  \commondummyword\w
+  \commondummyword\xref
 }
 
 % For testing: output @{ and @} in index sort strings as \{ and \}.
@@ -5036,11 +5087,11 @@ end
 %
 \def\indexnofonts{%
   % Accent commands should become @asis.
-  \def\definedummyaccent##1{\let##1\asis}%
+  \def\commondummyaccent##1{\let##1\asis}%
   % We can just ignore other control letters.
-  \def\definedummyletter##1{\let##1\empty}%
+  \def\commondummyletter##1{\let##1\empty}%
   % All control words become @asis by default; overrides below.
-  \let\definedummyword\definedummyaccent
+  \let\commondummyword\commondummyaccent
   \commondummiesnofonts
   %
   % Don't no-op \tt, since it isn't a user-level command
@@ -5125,8 +5176,11 @@ end
   % goes to end-of-line is not handled.
   %
   \macrolist
+  \let\value\indexnofontsvalue
 }
 
+\f
+
 
 \let\SETmarginindex=\relax % put index entries in margin (undocumented)?
 
@@ -5925,18 +5979,32 @@ end
         \global\advance\dimen@ by 1pt
       \repeat
     }%
-    \multiply\dimen@ii by 4
-    \divide\dimen@ii by 5
-    \ifdim\ht3<\dimen@ii
-      % Column heights are too different, so don't make their bottoms
-      % flush with each other.  The glue at the end of the second column
-      % allows a second column to stretch, reducing the difference in
-      % height between the two.
-      \setbox0=\vbox to\dimen@{\unvbox1\vfill}%
-      \setbox2=\vbox to\dimen@{\unvbox3\vskip 0pt plus 0.3\ht0}%
+    \ifdim2\ht1>\vsize
+      % The left column has come out longer than the page itself.  (Note
+      % that we have doubled \vsize for the double columns, so
+      % the actual height of the page is 0.5\vsize).  Just split the last
+      % of the double column material roughly in half.
+      \setbox2=\box0
+      \setbox0 = \vsplit2 to \dimen@ii
+      \setbox0=\vbox to\dimen@ii{\unvbox0}%
+      \setbox2=\vbox to\dimen@ii{\unvbox2}%
     \else
-      \setbox0=\vbox to\dimen@{\unvbox1}%
-      \setbox2=\vbox to\dimen@{\unvbox3}%
+      \multiply\dimen@ii by 5
+      \divide\dimen@ii by 4
+      \global\setbox3 = \copy0
+      \global\setbox1 = \vsplit3 to \dimen@ii
+      \global\setbox\balancedcolumns=\vbox{\pagesofar}%
+      \ifdim\ht3<\dimen@ii
+        % Column heights are too different, so don't make their bottoms
+        % flush with each other.  The glue at the end of the second column
+        % allows a second column to stretch, reducing the difference in
+        % height between the two.
+        \setbox0=\vbox to\dimen@{\unvbox1\vfill}%
+        \setbox2=\vbox to\dimen@{\unvbox3\vskip 0pt plus 0.3\ht0}%
+      \else
+        \setbox0=\vbox to\dimen@{\unvbox1}%
+        \setbox2=\vbox to\dimen@{\unvbox3}%
+      \fi
     \fi
   \fi
   %
@@ -7985,7 +8053,7 @@ end
 \newif\ifrecursive  % Is it recursive?
 
 % List of all defined macros in the form
-%    \definedummyword\macro1\definedummyword\macro2...
+%    \commondummyword\macro1\commondummyword\macro2...
 % Currently is also contains all @aliases; the list can be split
 % if there is a need.
 \def\macrolist{}
@@ -7993,7 +8061,7 @@ end
 % Add the macro to \macrolist
 \def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
 \def\addtomacrolistxxx#1{%
-     \toks0 = \expandafter{\macrolist\definedummyword#1}%
+     \toks0 = \expandafter{\macrolist\commondummyword#1}%
      \xdef\macrolist{\the\toks0}%
 }
 
@@ -8134,7 +8202,7 @@ end
     % Remove the macro name from \macrolist:
     \begingroup
       \expandafter\let\csname#1\endcsname \relax
-      \let\definedummyword\unmacrodo
+      \let\commondummyword\unmacrodo
       \xdef\macrolist{\macrolist}%
     \endgroup
   \else
@@ -8149,7 +8217,7 @@ end
   \ifx #1\relax
     % remove this
   \else
-    \noexpand\definedummyword \noexpand#1%
+    \noexpand\commondummyword \noexpand#1%
   \fi
 }
 
@@ -8424,8 +8492,7 @@ end
 % its parameters, looking like "\xeatspaces{\hash 1}".
 %    \paramno is the number of parameters
 %    \paramlist is a TeX parameter text, e.g. "#1,#2,#3,"
-% There are eight cases: recursive and nonrecursive macros of zero, one,
-% up to nine, and many arguments.
+% There are four cases: macros of zero, one, up to nine, and many arguments.
 % \xdef is used so that macro definitions will survive the file
 % they're defined in: @include reads the file inside a group.
 %
@@ -8440,91 +8507,48 @@ end
   \else
     \let\xeatspaces\relax % suppress expansion
   \fi
-  \ifrecursive   %%%%%%%%%%%%%% Recursive %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-    \ifcase\paramno
-    % 0
-      \expandafter\xdef\csname\the\macname\endcsname{%
-        \noexpand\scanmacro{\macrobody}}%
-    \or % 1
+  \ifcase\paramno
+  % 0
+    \expandafter\xdef\csname\the\macname\endcsname{%
+      \noexpand\scanmacro{\macrobody}}%
+  \or % 1
+    \expandafter\xdef\csname\the\macname\endcsname{%
+       \bgroup
+       \noexpand\braceorline
+       \expandafter\noexpand\csname\the\macname @@@\endcsname}%
+    \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
+      \egroup
+      \noexpand\scanmacro{\macrobody}%
+      }%
+  \else % at most 9
+    \ifnum\paramno<10\relax
+      % @MACNAME sets the context for reading the macro argument
+      % @MACNAME@@ gets the argument, processes backslashes and appends a 
+      % comma.
+      % @MACNAME@@@ removes braces surrounding the argument list.
+      % @MACNAME@@@@ scans the macro body with arguments substituted.
       \expandafter\xdef\csname\the\macname\endcsname{%
-         \bgroup
-         \noexpand\braceorline
-         \expandafter\noexpand\csname\the\macname @@@\endcsname}%
+        \bgroup
+        \noexpand\expandafter  % This \expandafter skip any spaces after the
+        \noexpand\macroargctxt % macro before we change the catcode of space.
+        \noexpand\expandafter
+        \expandafter\noexpand\csname\the\macname @@\endcsname}%
+      \expandafter\xdef\csname\the\macname @@\endcsname##1{%
+          \noexpand\passargtomacro
+          \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}%
       \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
-        \egroup
-        \noexpand\scanmacro{\macrobody}%
-        }%
-    \else
-      \ifnum\paramno<10\relax % at most 9
-        % See non-recursive section below for comments
-        \expandafter\xdef\csname\the\macname\endcsname{%
-          \bgroup
-          \noexpand\expandafter
-          \noexpand\macroargctxt
-          \noexpand\expandafter
-          \expandafter\noexpand\csname\the\macname @@\endcsname}%
-        \expandafter\xdef\csname\the\macname @@\endcsname##1{%
-            \noexpand\passargtomacro
-            \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}%
-        \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
-            \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}%
-        \expandafter\expandafter
-        \expandafter\xdef
-        \expandafter\expandafter
-          \csname\the\macname @@@@\endcsname\paramlist{%
-            \egroup\noexpand\scanmacro{\macrobody}}%
-      \else % 10 or more
-        \expandafter\xdef\csname\the\macname\endcsname{%
-          \noexpand\getargvals@{\the\macname}{\argl}%
-        }%    
-        \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody
-        \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble
-      \fi
-    \fi
-  \else  %%%%%%%%%%%%%%%%%%%%%% Non-recursive %%%%%%%%%%%%%%%%%%%%%%%%%%
-    \ifcase\paramno
-    % 0
+          \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}%
+      \expandafter\expandafter
+      \expandafter\xdef
+      \expandafter\expandafter
+        \csname\the\macname @@@@\endcsname\paramlist{%
+          \egroup\noexpand\scanmacro{\macrobody}}%
+    \else % 10 or more:
       \expandafter\xdef\csname\the\macname\endcsname{%
-        \noexpand\scanmacro{\macrobody}}%
-    \or % 1
-      \expandafter\xdef\csname\the\macname\endcsname{%
-         \bgroup
-         \noexpand\braceorline
-         \expandafter\noexpand\csname\the\macname @@@\endcsname}%
-      \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
-        \egroup
-        \noexpand\scanmacro{\macrobody}%
-        }%
-    \else % at most 9
-      \ifnum\paramno<10\relax
-        % @MACNAME sets the context for reading the macro argument
-        % @MACNAME@@ gets the argument, processes backslashes and appends a 
-        % comma.
-        % @MACNAME@@@ removes braces surrounding the argument list.
-        % @MACNAME@@@@ scans the macro body with arguments substituted.
-        \expandafter\xdef\csname\the\macname\endcsname{%
-          \bgroup
-          \noexpand\expandafter  % This \expandafter skip any spaces after the
-          \noexpand\macroargctxt % macro before we change the catcode of space.
-          \noexpand\expandafter
-          \expandafter\noexpand\csname\the\macname @@\endcsname}%
-        \expandafter\xdef\csname\the\macname @@\endcsname##1{%
-            \noexpand\passargtomacro
-            \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}%
-        \expandafter\xdef\csname\the\macname @@@\endcsname##1{%
-            \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}%
-        \expandafter\expandafter
-        \expandafter\xdef
-        \expandafter\expandafter
-          \csname\the\macname @@@@\endcsname\paramlist{%
-            \egroup\noexpand\scanmacro{\macrobody}}%
-      \else % 10 or more:
-        \expandafter\xdef\csname\the\macname\endcsname{%
-          \noexpand\getargvals@{\the\macname}{\argl}%
-        }%
-        \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody
-        \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse
-      \fi
+        \noexpand\getargvals@{\the\macname}{\argl}%
+      }%
+      \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody
+      \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble
     \fi
   \fi}
 
@@ -8871,6 +8895,9 @@ end
          % In this case, the replaced destination names of
          % remote PDF cannot be known. In order to avoid replacement,
          % you can use commandline option `-C 0x0010' for xdvipdfmx.
+         % If you use XeTeX 0.99996+ (TeX Live 2016+),
+         % the commandline option is not neccesary
+         % because we can use `dvipdfmx:config' special.
          \special{pdf:bann << /Border [0 0 0] /Type /Annot /Subtype /Link /A
            << /S /GoToR /F (\the\filename.pdf) /D (name\pdfxrefdest) >> >>}%
        \else
@@ -10227,7 +10254,7 @@ directory should work if nowhere else does.}
   \countUTFx = "80
   \countUTFy = "C2
   \def\UTFviiiTmp{%
-    \gdef~{
+    \gdef~{%
         \ifpassthroughchars $\fi}}%
   \UTFviiiLoop
 
@@ -10278,6 +10305,15 @@ directory should work if nowhere else does.}
   \fi
 }
 
+% These macros are used here to construct the name of a control
+% sequence to be defined.
+\def\UTFviiiTwoOctetsName#1#2{%
+  \csname u8:#1\string #2\endcsname}%
+\def\UTFviiiThreeOctetsName#1#2#3{%
+  \csname u8:#1\string #2\string #3\endcsname}%
+\def\UTFviiiFourOctetsName#1#2#3#4{%
+  \csname u8:#1\string #2\string #3\string #4\endcsname}%
+
 % For UTF-8 byte sequence (TeX, e-TeX and pdfTeX)
 % Definition macro to replace the Unicode character
 % Definition macro that is used by @U command
@@ -10294,17 +10330,18 @@ directory should work if nowhere else does.}
     \countUTFz = "#1\relax
     \begingroup
       \parseXMLCharref
+    
+      % Give \u8:... its definition.  The sequence of seven \expandafter's
+      % expands after the \gdef three times, e.g.
       %
-      % Access definitions of characters given UTF-8 sequences
-      \def\UTFviiiTwoOctets##1##2{%
-        \csname u8:##1\string ##2\endcsname}%
-      \def\UTFviiiThreeOctets##1##2##3{%
-        \csname u8:##1\string ##2\string ##3\endcsname}%
-      \def\UTFviiiFourOctets##1##2##3##4{%
-        \csname u8:##1\string ##2\string ##3\string ##4\endcsname}%
-      \expandafter\expandafter\expandafter\expandafter
-       \expandafter\expandafter\expandafter
-       \gdef\UTFviiiTmp{#2}%
+      % 1.  \UTFviiTwoOctetsName B1 B2
+      % 2.  \csname u8:B1 \string B2 \endcsname
+      % 3.  \u8: B1 B2  (a single control sequence token)
+      %
+      \expandafter\expandafter
+      \expandafter\expandafter
+      \expandafter\expandafter
+      \expandafter\gdef       \UTFviiiTmp{#2}%
       % 
       \expandafter\ifx\csname uni:#1\endcsname \relax \else
        \message{Internal error, already defined: #1}%
@@ -10314,37 +10351,53 @@ directory should work if nowhere else does.}
       \expandafter\globallet\csname uni:#1\endcsname \UTFviiiTmp
     \endgroup}
   %
-  % Given the value in \countUTFz as a Unicode code point, set \UTFviiiTmp.
+  % Given the value in \countUTFz as a Unicode code point, set \UTFviiiTmp
+  % to the corresponding UTF-8 sequence.
   \gdef\parseXMLCharref{%
     \ifnum\countUTFz < "A0\relax
       \errhelp = \EMsimple
       \errmessage{Cannot define Unicode char value < 00A0}%
     \else\ifnum\countUTFz < "800\relax
       \parseUTFviiiA,%
-      \parseUTFviiiB C\UTFviiiTwoOctets.,%
+      \parseUTFviiiB C\UTFviiiTwoOctetsName.,%
     \else\ifnum\countUTFz < "10000\relax
       \parseUTFviiiA;%
       \parseUTFviiiA,%
-      \parseUTFviiiB E\UTFviiiThreeOctets.{,;}%
+      \parseUTFviiiB E\UTFviiiThreeOctetsName.{,;}%
     \else
       \parseUTFviiiA;%
       \parseUTFviiiA,%
       \parseUTFviiiA!%
-      \parseUTFviiiB F\UTFviiiFourOctets.{!,;}%
+      \parseUTFviiiB F\UTFviiiFourOctetsName.{!,;}%
     \fi\fi\fi
   }
 
+  % Extract a byte from the end of the UTF-8 representation of \countUTFx.
+  % It must be a non-initial byte in the sequence.
+  % Change \uccode of #1 for it to be used in \parseUTFviiiB as one
+  % of the bytes.
   \gdef\parseUTFviiiA#1{%
     \countUTFx = \countUTFz
     \divide\countUTFz by 64
-    \countUTFy = \countUTFz
+    \countUTFy = \countUTFz  % Save to be the future value of \countUTFz.
     \multiply\countUTFz by 64
+    
+    % \countUTFz is now \countUTFx with the last 5 bits cleared.  Subtract
+    % in order to get the last five bits.
     \advance\countUTFx by -\countUTFz
+
+    % Convert this to the byte in the UTF-8 sequence.
     \advance\countUTFx by 128
     \uccode `#1\countUTFx
     \countUTFz = \countUTFy}
 
-  % Used to set \UTFviiiTmp to a UTF-8 byte sequence
+  % Used to put a UTF-8 byte sequence into \UTFviiiTmp
+  % #1 is the increment for \countUTFz to yield a the first byte of the UTF-8
+  %    sequence.
+  % #2 is one of the \UTFviii*OctetsName macros.
+  % #3 is always a full stop (.)
+  % #4 is a template for the other bytes in the sequence.  The values for these
+  %    bytes is substituted in here with \uppercase using the \uccode's.
   \gdef\parseUTFviiiB#1#2#3#4{%
     \advance\countUTFz by "#10\relax
     \uccode `#3\countUTFz