Carrying over controls

 

Performance controls such as _volume(), _pan(), _mod() (modulation), _press() (pressure) and _pitchbend() modify the sound of the notes and sound-objects that follow them. In MIDI they produce controller messages, and in Csound they set arguments of the score: see Csound check. Additional Csound parameters, declared in a Csound resource file, are set in the same way with _value(): see Csound objects.

A control applies to what follows it in its own expression. Until recently (version 3.6.0), an expression without controls of its own reverted to the default values: volume 90, panoramic 64 (centre), no modulation, no pressure, no pitchbend. This was impractical for scores made of successive polymetric expressions, such as scores imported from MusicXML, in which each measure is a polymetric expression whose fields are the parts of the score. Setting the stereo position of a string quartet required a _pan() control for each part in every measure.

Now, a fixed value of a control is carried over to the following expressions that do not set it. The Bol Processor decides which expressions receive it according to the part, the instrument or the MIDI channel to which it is attached. This page explains the rules.

The examples are found in the data project "‑da.checkCarryOver" (folder "ctests"), in which each item states the values it should produce. They are shown here with the Csound scores they produce, using the Csound resource "‑cs.Beethoven-fugue-b-flat-major" designed for a string quartet. In these scores:

  • arguments 5 and 6 (red) are the start and end volume (default 90);
  • arguments 15 and 16 (blue) are the start and end panoramic (default 64);
  • part 1 is played by instrument 1 (violin), part 2 by instrument 4 (second violin); notes without a part are played by instrument 1.

MIDI output follows the same rules, since the values of the controls are the same. In MIDI, a controller message is sent on the channel of a note whenever its value differs from the previous one on this channel.

Values carried over

In a sequence, a value set by a control applies to the following notes, even if they are sent to another MIDI channel. Here, all three notes are played at volume 20:

_volume(20) C4 _chan(2) D4 _chan(3) E4

The same is now true when the notes are in separate expressions:

{_volume(20) C4} {_chan(2) D4} {_chan(3) E4}

i1 0.000 1.000 8.00 20.000 20.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 1.000 1.000 8.02 20.000 20.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i1 2.000 1.000 8.04 20.000 20.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4

Before this change, D4 and E4 were played at the default volume 90.

To which part, instrument or channel does a value belong?

A value is attached to:

  1. the instrument if _ins() has been specified;
  2. otherwise, the part if _part() has been specified;
  3. otherwise, the MIDI channel if _chan() has been specified;
  4. otherwise, the current instrument: the one guessed by _value() for an additional parameter (see below), or instrument 1.

An item starts with the implicit values _ins(1) _part(1) _chan(1). A note that does not receive the value from its own expression takes the value attached to its instrument, its part or its channel, including implicit ones. In the previous example, the volume 20 was attached to the implicit instrument 1, which is also that of D4 and E4.

Values attached to parts remain separate, even if the parts are sent to the same MIDI channel:

{_part(1) _volume(30) C4, _part(2) _volume(110) E4} {_part(1) D4, _part(2) F4}

i1 0.000 1.000 8.00 30.000 30.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i4 0.000 1.000 8.04 110.000 110.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4
i1 1.000 1.000 8.02 30.000 30.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i4 1.000 1.000 8.05 110.000 110.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; F4

This is what makes it possible to set the stereo position of each part once and for all, in the first measure of a score. In the third measure, a new value changes the position of part 2:

{_part(1) _pan(23) C4 D4, _part(2) _pan(48) E4 F4} {_part(1) G4 A4, _part(2) B4 C5} {_part(1) D5 E5, _part(2) _pan(100) F5 G5}

i1 0.000 1.000 8.00 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; C4
i4 0.000 1.000 8.04 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 48.000 48.000 0.000 ; E4
i1 1.000 1.000 8.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; D4
i4 1.000 1.000 8.05 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 48.000 48.000 0.000 ; F4
i1 2.000 1.000 8.07 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; G4
i4 2.000 1.000 8.11 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 48.000 48.000 0.000 ; B4
i1 3.000 1.000 8.09 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; A4
i4 3.000 1.000 9.00 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 48.000 48.000 0.000 ; C5
i1 4.000 1.000 9.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; D5
i4 4.000 1.000 9.05 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 100.000 100.000 0.000 ; F5
i1 5.000 1.000 9.04 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; E5
i4 5.000 1.000 9.07 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 100.000 100.000 0.000 ; G5

A value set after _ins() belongs to the instrument, so it is not affected by _part() or _chan() instructions that follow:

{_ins(3) _volume(50) C4} {_ins(3) _part(2) D4 _chan(5) E4}

i3 0.000 1.000 8.00 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i3 1.000 1.000 8.02 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i3 2.000 1.000 8.04 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4

Conversely, it does not apply to another instrument, even in the same part. Here D4 gets the default volume:

{_ins(3) _part(2) _volume(50) C4} {_ins(4) _part(2) D4}

i3 0.000 1.000 8.00 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i4 1.000 1.000 8.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4

An explicit _chan(1) is not the same as the implicit channel 1. Here, the volume 50 belongs to channel 1, so D4 on channel 2 does not receive it, whereas it would without _chan(1):

{_chan(1) _volume(50) C4} {_chan(2) D4}

i1 0.000 1.000 8.00 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 1.000 1.000 8.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4

The most recent value

When several values are attached to the instrument, the part and the channel of a note, the most recent one wins. If they were set at the same time, as in the fields of a polymetric expression, the order of priority is: instrument specified by _ins(), part, channel, and finally the implicit instrument.

In the following example, the volumes 80 and 30 are both set at date 0. The volume 80 belongs to the implicit instrument 1, the volume 30 belongs to part 2. Therefore D4, in part 2, gets 30, while F4, which has no part, gets 80:

{_volume(80) C4, _part(2) _volume(30) E4} {_part(2) D4} {F4}

i1 0.000 1.000 8.00 80.000 80.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i4 0.000 1.000 8.04 30.000 30.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4
i4 1.000 1.000 8.02 30.000 30.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i1 2.000 1.000 8.05 80.000 80.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; F4

Variations are not carried over

Only fixed values are carried over. A value belonging to a continuous variation (_volumecont, _pitchcont, _cont(), etc.) or a stepwise variation (_volumestep, etc.) describes a movement within its own expression. It erases the remembered value, and the following expressions get the default value:

{_volume(80) C4} {_volumecont _volume(20) D4 E4 _volume(100)} {F4}

i1 0.000 1.000 8.00 80.000 80.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 1.000 1.000 8.02 20.000 60.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i1 2.000 1.000 8.04 60.000 100.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4
i1 3.000 1.000 8.05 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; F4

This is important for pitchbend: the end of a glissando such as _pitchcont _pitchbend(0) C4 D4 _pitchbend(100) should not detune the notes that follow.

The value erased is the one attached to the part, instrument or channel of the variation. A value attached elsewhere may then apply again. Below, the variation of part 2 erases the value of part 2, so E4 receives the volume 80 attached to the implicit instrument 1:

{_volume(80) C4} {_part(2) _volumecont _volume(20) D4 _volume(100)} {_part(2) E4}

i1 0.000 1.000 8.00 80.000 80.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i4 1.000 1.000 8.02 20.000 100.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i4 2.000 1.000 8.04 80.000 80.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4

Only for later expressions

A value only applies to expressions starting after the date at which it is set. Fields of the same polymetric expression starting at the same time do not share their values, otherwise the result would depend on the order in which the fields are written. Here, D4 gets the default volume and E4 gets 20:

{_volume(20) C4, D4} {E4}

i1 0.000 1.000 8.00 20.000 20.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 0.000 1.000 8.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i1 1.000 1.000 8.04 20.000 20.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4

As a consequence, a value written at the very end of an expression does not reach the next one, since its date is the start date of the next expression. Here, D4 is played at volume 90:

{C4 _volume(50)} {D4}

i1 0.000 1.000 8.00 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 1.000 1.000 8.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4

Values are carried over to expressions that do not set them. In the following example, E4 does not belong to an expression of its own: it follows C4 in the main sequence, which has already received the default value. So E4 is played at volume 90:

C4 {_volume(50) D4} E4

i1 0.000 1.000 8.00 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 1.000 1.000 8.02 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i1 2.000 1.000 8.04 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4

Writing E4 in an expression of its own, it receives the volume 50:

{C4} {_volume(50) D4} {E4}

i1 0.000 1.000 8.00 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; C4
i1 1.000 1.000 8.02 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; D4
i1 2.000 1.000 8.04 50.000 50.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 64.000 64.000 0.000 ; E4

Chunks

Values are also carried over from one line of a data item to the next, since each line is played as a chunk. This is the case when a long item is played with the "PLAY safe" button, which splits it into chunks to save memory. In the following item, D4 and F4 keep the stereo positions of their parts:

{_part(1) _pan(23) C4, _part(2) _pan(48) E4}
{_part(1) D4, _part(2) F4}

i1 0.000 1.000 8.00 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; C4
i4 0.000 1.000 8.04 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 48.000 48.000 0.000 ; E4
i1 1.000 1.000 8.02 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 23.000 23.000 0.000 ; D4
i4 1.000 1.000 8.05 90.000 90.000 0.000 0.000 0.000 0.000 64.000 0.000 0.000 0.000 48.000 48.000 0.000 ; F4

Items produced by a grammar do not share their values: each item starts with the default values.

Additional parameters

The same rules apply to additional Csound parameters set by _value(). Examples are found in the section "Carrying over additional parameters" of "‑da.tryCsoundObjects", using the Csound resource "‑cs.tryCsoundObjects" in which instrument 6 has a parameter "blah" on arguments 15 and 16 (default 231.5).

A _value() instruction guesses the instrument to which its parameter belongs, here _ins(6). This guessed instrument does not count as specified, so the values of the parts below remain separate:

{_part(1) _value(blah,10) C4, _part(2) _value(blah,20) E4} {_part(1) _ins(6) D4, _part(2) _ins(6) F4}

i6 0.000 1.000 8.00 0.000 0.000 90.000 90.000 0.000 0.000 0.000 1.000 1.000 0.000 10.000 10.000 0.000 ; C4
i6 0.000 1.000 8.04 0.000 0.000 90.000 90.000 0.000 0.000 0.000 1.000 1.000 0.000 20.000 20.000 0.000 ; E4
i6 1.000 1.000 8.02 0.000 0.000 90.000 90.000 0.000 0.000 0.000 1.000 1.000 0.000 10.000 10.000 0.000 ; D4
i6 1.000 1.000 8.05 0.000 0.000 90.000 90.000 0.000 0.000 0.000 1.000 1.000 0.000 20.000 20.000 0.000 ; F4

A carried value combines with the values found in the Csound scores of sound-objects, according to the combination mode of the parameter (additive or multiplicative), just like a value set explicitly.

These rules were designed with the score of Beethoven's Grosse Fugue (opus 133) for string quartet, imported from MusicXML: the stereo positions of the four parts are set by _pan() controls in the first measure only. In unusual situations, the result might not be the one you expect. Please let us know!

Beethoven's Fugue in B flat major interpreted by the Bol Processor + Csound, with Sauveur temperament
👉 Listen in stereo!

Leave a Reply

Your email address will not be published. Required fields are marked *

« Translate