lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Generating music expressions from within \applyContext?


From: Heikki Tauriainen
Subject: Re: Generating music expressions from within \applyContext?
Date: Wed, 27 Jul 2016 09:47:53 +0300

Hi,

> > "H. S. Teoh" <address@hidden> writes:
> [...]
> > 
> > > 
> > > The background of this is that I'm writing a Scheme function that
> > > generates single-note crescendos by emitting a series of \set
> > > Staff.midiExpression = ... events.
>

Your idea of writing a function to program changes in MIDI expression
level (to allow controlling single-note dynamics) from within LilyPond
input is exactly the same I've had ever since I added the support for
Staff.midiExpression...

Probably like in your case, this task of writing a music function to do
the job felt simple enough until there came the "inevitable" need to
access the value of a context property from within the function (to
avoid explicitly specifying the volume levels and the equalization
function every time the music function is used).  (I've since come to
understand that the concept of "reading the value of a context property
from within a music function to make decisions affecting the control
flow in the function" just makes no sense considering the way that
music functions and context properties work...)

I also gave up on my attempts to try and understand how to make use of
\applyContext back then, after I found the crude workaround of making
use of additional global variables to access the dynamic equalizer
function, and the maximum and minimum expression levels from within the
music function.  (I think the inspiration for this came from the
implementation of the \articulate function, which uses a number of such
variables for controlling the behavior of the function - by changing
the values of the variables between \articulate invocations, the
function can be made to work differently between invocations without
the need to specify extra function arguments.)

At the risk making a fool out of myself by letting the Scheme experts
tear this example code to shreds, I'll attach the workaround code
(which I've tried to clean up a bit from my local stuff without much
testing, it could contain bugs) here.

(I've been using code like this myself for quite some time to program
dynamics effects using the expression controller, and in my own use I
can live with the clumsy syntax as the code still seems to do the job
it's supposed to...  As a small demo that uses the expression control
for MIDI dynamics, I'll also include a rendition of the fifth movement
of Felix Mendelssohn-Bartholdy's "Lauda Sion" (Op. 73) as it was output
by an older version LilyPond with the support for Staff.midiExpression
backported.)


Including the attached "adjust-expression.ly" into a LilyPond source
file will define the following two music functions:

* the \setExpression function which can be used to change the 
  expression level to a dynamic value, for example, \setExpression 
  "mf", or \setExpression "0.7", and

* the \adjustExpression function which can be used to generate a 
  linear sequence of changes in the expression level between two
  dynamic levels (using the same variables for equalization), for
  example, \adjustExpression "pp" { s2 } { c1~ c1~ c1 } { s4 } "ff" .

  (What this will do is (1) set the expression level initially to
  "pp" at the start of the music expression { c1~ c1~ c1 }, and (2)
  increase the expression level from "pp" to "ff" such that the
  adjustment starts at a "distance" of { s2 } after the beginning of
  { c1~ c1~ c1 }, and ends at a distance of { s4 } before the end of
  { c1~ c1~ c1 }.  The initial and final "padding" music expressions 
  can be empty to make the crescendo span the whole of the middle music
  expression.  As I've ended up usually writing the expression changes 
  for each staff (instrument) into a separate music variable included 
  as a parallel music expression in the Staff myself, this interface 
  could probably be simplified by removing the padding arguments 
  altogether, however I've not done this here.)

  There are a few additional optional arguments which can be used to
  alter the behavior of the function; see the documentation of the
  function in the attached file.

Both of these functions use the values of the variables
adjustExpression:minLevel, adjustExpression:maxLevel (fractions between
0.0 and 1.0), and the adjustExpression:equalizer function (which
defaults to default-dynamic-absolute-volume) for equalizing expression
levels (similar to how the C++ Dynamic_performer translates dynamics
into note velocities equalized w.r.t. Staff.midiMinimumVolume
and Staff.midiMaximumVolume, or the values returned by
Score.instrumentEqualizer).

The values of the adjustExpression:... variables can be changed before
using either of the two above functions to change the equalization
settings.  If different instruments are kept in separate staves, the
redefinition of the variables usually needs to be done just before each
Staff definition in a \score block:

\score {
  ...
  #(set! adjustExpression:minLevel 0.2)
  #(set! adjustExpression:maxLevel 0.8)
  \new Staff <<
     % ... use \setExpression and \adjustExpression here ...
  >>
  ...
  \midi { }
}

(It should be kept in mind, however, that the function invocations
occur as the input is parsed.  Assuming that a music variable
containing invocations of the above music functions could be "expanded"
multiple times in the input with different values for the variables
used for the function invocations inside the expression will only lead
to confusion...)

When controlling MIDI dynamics using the expression controller,
something possibly also needs to be done to change (or disable) the
normal interpretation of dynamic marks (\pp, \f, \< etc.) for MIDI
output so that the Dynamic_performer will not make any note velocity
changes unless explicitly requested.  Removing the Dynamic_performer
from the Voice context probably works, but on the other hand makes it
impossible to control the MIDI velocities of notes from within the
input.  When using tags and multiple \score blocks for creating typeset
and MIDI output separately, one possibility to retain control of also
the note velocities for MIDI is to copy the definitions of the dynamic
marks to definitions for "setting note velocity explicitly for MIDI",
and then redefine the marks to be interpreted only when generating
printed output, for example, as

    % \midi_pp will set the velocity level only for MIDI output
    midi_pp = -\tag #'midi -\pp

    % redefine the dynamic mark to apply only when generating typeset
    % output
    pp = -\tag #'layout -\pp

    % the (de)crescendo marks need to be redefined using Scheme syntax
    #(define \< #{ -\tag #'layout -\< #})

-- 
Heikki Tauriainen

Attachment: adjust-expression.ly
Description: Text Data

Attachment: Mendelssohn-Bartholdy - Lauda Sion - 5 - Coro.midi
Description: MIDI audio


reply via email to

[Prev in Thread] Current Thread [Next in Thread]