diff options
author | Jean-Charles Malahieude <lilyfan@orange.fr> | 2016-07-30 12:46:33 +0200 |
---|---|---|
committer | Jean-Charles Malahieude <lilyfan@orange.fr> | 2016-07-30 12:46:33 +0200 |
commit | fb2041257a9f00d4dbfab1766beefc0ef1425ea4 (patch) | |
tree | bdb9d2c718c0429a7549ded50cf8614155ba9d99 | |
parent | 57fec051567e7ba777bfce12cbe9d27e2c9756ec (diff) | |
parent | 97f856e8c577fdf301ac2a15b336eae8729e53f7 (diff) |
Merge branch 'master' of /home/jcharles/GIT/Lily/. into translation
50 files changed, 1443 insertions, 914 deletions
diff --git a/Documentation/contributor/release-work.itexi b/Documentation/contributor/release-work.itexi index a2e3cb15d5..517b5e3c14 100644 --- a/Documentation/contributor/release-work.itexi +++ b/Documentation/contributor/release-work.itexi @@ -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 diff --git a/Documentation/cyrillic.itexi b/Documentation/cyrillic.itexi index 4e7569cf40..e38eb99c69 100644 --- a/Documentation/cyrillic.itexi +++ b/Documentation/cyrillic.itexi @@ -387,7 +387,7 @@ \DeclareUnicodeCharacter{2116}{\textnumero} -% Add all Cyrillic character names to \commondummies. +% Add all Cyrillic character names to \definedummies. \gdef\cyrdummies{% \definedummyword\textnumero @@ -570,8 +570,8 @@ \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. diff --git a/Documentation/macros.itexi b/Documentation/macros.itexi index 6e567a56d5..cc09619dcc 100644 --- a/Documentation/macros.itexi +++ b/Documentation/macros.itexi @@ -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 diff --git a/Documentation/notation/ancient.itely b/Documentation/notation/ancient.itely index 714584210a..7e44224426 100644 --- a/Documentation/notation/ancient.itely +++ b/Documentation/notation/ancient.itely @@ -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 diff --git a/Documentation/notation/changing-defaults.itely b/Documentation/notation/changing-defaults.itely index f5546a790a..d213342754 100644 --- a/Documentation/notation/changing-defaults.itely +++ b/Documentation/notation/changing-defaults.itely @@ -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 diff --git a/Documentation/notation/contemporary.itely b/Documentation/notation/contemporary.itely index 665a847d7f..8a0466903b 100644 --- a/Documentation/notation/contemporary.itely +++ b/Documentation/notation/contemporary.itely @@ -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 diff --git a/Documentation/notation/editorial.itely b/Documentation/notation/editorial.itely index e329810ae5..73e5ffbece 100644 --- a/Documentation/notation/editorial.itely +++ b/Documentation/notation/editorial.itely @@ -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] diff --git a/Documentation/notation/expressive.itely b/Documentation/notation/expressive.itely index defe8ad946..60f0cc66c0 100644 --- a/Documentation/notation/expressive.itely +++ b/Documentation/notation/expressive.itely @@ -1224,7 +1224,7 @@ information, see @ref{Ties}. @code{\arpeggioArrowDown}, @code{\arpeggioNormal}, @code{\arpeggioBracket}, -@code{\arpeggioParenthesis} +@code{\arpeggioParenthesis}, @code{\arpeggioParenthesisDashed}. @endpredefined diff --git a/Documentation/notation/fretted-strings.itely b/Documentation/notation/fretted-strings.itely index a69f678735..bd0dfca32d 100644 --- a/Documentation/notation/fretted-strings.itely +++ b/Documentation/notation/fretted-strings.itely @@ -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. diff --git a/Documentation/notation/input.itely b/Documentation/notation/input.itely index a23943c16c..0a873317d4 100644 --- a/Documentation/notation/input.itely +++ b/Documentation/notation/input.itely @@ -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}. diff --git a/Documentation/notation/notation-appendices.itely b/Documentation/notation/notation-appendices.itely index 3d8bf79f43..388c8bc388 100644 --- a/Documentation/notation/notation-appendices.itely +++ b/Documentation/notation/notation-appendices.itely @@ -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 diff --git a/Documentation/notation/percussion.itely b/Documentation/notation/percussion.itely index be6bc04c35..b17dd888ee 100644 --- a/Documentation/notation/percussion.itely +++ b/Documentation/notation/percussion.itely @@ -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. diff --git a/Documentation/notation/pitches.itely b/Documentation/notation/pitches.itely index 3aa7738e2b..0699239db5 100644 --- a/Documentation/notation/pitches.itely +++ b/Documentation/notation/pitches.itely @@ -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. diff --git a/Documentation/notation/rhythms.itely b/Documentation/notation/rhythms.itely index 704d96e007..d3263d4f6a 100644 --- a/Documentation/notation/rhythms.itely +++ b/Documentation/notation/rhythms.itely @@ -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 diff --git a/Documentation/notation/simultaneous.itely b/Documentation/notation/simultaneous.itely index a29dd9c4ae..fc31736c09 100644 --- a/Documentation/notation/simultaneous.itely +++ b/Documentation/notation/simultaneous.itely @@ -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 diff --git a/Documentation/notation/spacing.itely b/Documentation/notation/spacing.itely index bd6d4d4b78..23ed5c0f30 100644 --- a/Documentation/notation/spacing.itely +++ b/Documentation/notation/spacing.itely @@ -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}. diff --git a/Documentation/notation/staff.itely b/Documentation/notation/staff.itely index ce859e2bb5..a114434664 100644 --- a/Documentation/notation/staff.itely +++ b/Documentation/notation/staff.itely @@ -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, diff --git a/Documentation/notation/unfretted-strings.itely b/Documentation/notation/unfretted-strings.itely index becdc6f3e1..f3eae55765 100644 --- a/Documentation/notation/unfretted-strings.itely +++ b/Documentation/notation/unfretted-strings.itely @@ -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}. diff --git a/Documentation/notation/vocal.itely b/Documentation/notation/vocal.itely index b06de6f40b..118a58e482 100644 --- a/Documentation/notation/vocal.itely +++ b/Documentation/notation/vocal.itely @@ -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: diff --git a/Documentation/web/news-front.itexi b/Documentation/web/news-front.itexi index 349678b75d..49cdaab35f 100644 --- a/Documentation/web/news-front.itexi +++ b/Documentation/web/news-front.itexi @@ -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 diff --git a/Documentation/web/news.itexi b/Documentation/web/news.itexi index c92da5f534..da830d3e15 100644 --- a/Documentation/web/news.itexi +++ b/Documentation/web/news.itexi @@ -27,6 +27,18 @@ NOTE: @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} We are happy to announce the release of LilyPond @@ -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 index da0ff4ffa0..0000000000 --- a/input/regression/midi/crescendo-abutting.ly +++ /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 index 0000000000..38c19a3d92 --- /dev/null +++ b/input/regression/midi/crescendo-gap-compatible-target.ly @@ -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 index 0000000000..4ab8dac24f --- /dev/null +++ b/input/regression/midi/crescendo-return-crescendo.ly @@ -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 index 0000000000..f3fcb36233 --- /dev/null +++ b/input/regression/midi/crescendo-return-louder-target.ly @@ -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 index 0000000000..788724be3f --- /dev/null +++ b/input/regression/midi/crescendo-return-softer-target.ly @@ -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 index 0000000000..065f674ae1 --- /dev/null +++ b/input/regression/midi/crescendo-return-unspecified-target.ly @@ -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 index 0000000000..fcc0f5a6ef --- /dev/null +++ b/input/regression/midi/crescendo-single-compatible-target.ly @@ -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 index 0000000000..d995609be9 --- /dev/null +++ b/input/regression/midi/crescendo-single-unspecified-target.ly @@ -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 index 0000000000..d066381102 --- /dev/null +++ b/input/regression/midi/decrescendo-multiple-compatible-target.ly @@ -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 index 0000000000..7c13541747 --- /dev/null +++ b/input/regression/midi/decrescendo-single-contrary-target.ly @@ -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 {} +} diff --git a/lily/audio-item.cc b/lily/audio-item.cc index 97a15d552a..a8a78199c7 100644 --- a/lily/audio-item.cc +++ b/lily/audio-item.cc @@ -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 } -}; diff --git a/lily/dynamic-performer.cc b/lily/dynamic-performer.cc index 5ce67f0463..857b3590dc 100644 --- a/lily/dynamic-performer.cc +++ b/lily/dynamic-performer.cc @@ -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 diff --git a/lily/grob.cc b/lily/grob.cc index 7ce89d5015..eafa66288e 100644 --- a/lily/grob.cc +++ b/lily/grob.cc @@ -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; } diff --git a/lily/include/audio-item.hh b/lily/include/audio-item.hh index 8c41d18a52..f3a97a2949 100644 --- a/lily/include/audio-item.hh +++ b/lily/include/audio-item.hh @@ -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); diff --git a/lily/include/lily-proto.hh b/lily/include/lily-proto.hh index ee3946e0fd..6de69e16b9 100644 --- a/lily/include/lily-proto.hh +++ b/lily/include/lily-proto.hh @@ -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 index 0000000000..38cb93f15a --- /dev/null +++ b/lily/include/midi-cc-announcer.hh @@ -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 diff --git a/lily/include/midi-item.hh b/lily/include/midi-item.hh index b593ce1527..57bb2d4da2 100644 --- a/lily/include/midi-item.hh +++ b/lily/include/midi-item.hh @@ -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 index 0000000000..7fb4ed6c27 --- /dev/null +++ b/lily/midi-cc-announcer.cc @@ -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 index 0000000000..2aff5d0039 --- /dev/null +++ b/lily/midi-cc-performer.cc @@ -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 index a112e6bcef..0000000000 --- a/lily/midi-control-function-performer.cc +++ /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 */ - "" - ); diff --git a/lily/midi-item.cc b/lily/midi-item.cc index 33dd9f11bd..ba29ba0ba5 100644 --- a/lily/midi-item.cc +++ b/lily/midi-item.cc @@ -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; } diff --git a/lily/staff-performer.cc b/lily/staff-performer.cc index 3a05cbd0e2..d413a46835 100644 --- a/lily/staff-performer.cc +++ b/lily/staff-performer.cc @@ -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)); +} diff --git a/ly/Welcome-to-LilyPond-MacOS.ly b/ly/Welcome-to-LilyPond-MacOS.ly index fe8e3b02a7..657b9b1f50 100644 --- a/ly/Welcome-to-LilyPond-MacOS.ly +++ b/ly/Welcome-to-LilyPond-MacOS.ly @@ -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" diff --git a/ly/Welcome_to_LilyPond.ly b/ly/Welcome_to_LilyPond.ly index cb4128f17f..9f950733bd 100644 --- a/ly/Welcome_to_LilyPond.ly +++ b/ly/Welcome_to_LilyPond.ly @@ -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" diff --git a/ly/performer-init.ly b/ly/performer-init.ly index 00c4bdf3da..644a0bd2d6 100644 --- a/ly/performer-init.ly +++ b/ly/performer-init.ly @@ -31,7 +31,7 @@ \consists "Staff_performer" \consists "Key_performer" - \consists "Midi_control_function_performer" + \consists "Midi_control_change_performer" } \context { diff --git a/po/lilypond.pot b/po/lilypond.pot index ae5cfd4108..7bd1d9a0eb 100644 --- a/po/lilypond.pot +++ b/po/lilypond.pot @@ -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 "" diff --git a/scm/framework-ps.scm b/scm/framework-ps.scm index 6e03a3b967..a404119678 100644 --- a/scm/framework-ps.scm +++ b/scm/framework-ps.scm @@ -328,7 +328,7 @@ ((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) diff --git a/tex/texinfo.tex b/tex/texinfo.tex index d7e6b1f6b8..79bc925655 100644 --- a/tex/texinfo.tex +++ b/tex/texinfo.tex @@ -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}}} + % 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 } + + \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 |