Pedal control - davidpanderson/Numula GitHub Wiki

Numula lets you control MIDI pedals (sustain, sostenuto, soft), including fractional pedaling. It also provides "virtual sustain pedals" which affect only a specified subset of notes.

All pedal operations on a Score must precede timing adjustments.

MIDI pedals

Software piano synthesizers typically provide access (via MIDI) to three "pedals":

  • The sustain pedal lifts the dampers so that notes continue to sound after the key is released, and strings vibrate sympathetically.

  • The sostenuto pedal lifts the dampers of notes whose keys are down at the time the pedal is depressed.

  • The soft pedal lowers the volume and/or softens the timbre of all notes.

Some synthesizers (including Pianoteq) support partially lowering a pedal; for example, a half-lowered sustain pedal only partly damps the strings.

The MIDI pedal types are represented by:

PEDAL_SUSTAIN = 64
PEDAL_SOSTENUTO = 66
PEDAL_SOFT = 67

The class PedalSeg represents an application of a pedal.

from numula.nscore import *

p = PedalSeg(
    dur: float,
    level0 = 1, level1 = 1,
    closed_start = True, closed_end = True
)

Parameters:

  • time: when the pedal is depressed (score time).
  • dur: how long the pedal remains depressed (score time).
  • level0, level1: the levels of pedal depression (0 to 1) at the start and end of the interval. The level varies linearly between these.
  • closed_start: the pedal is applied before notes at the start time.
  • closed_end: the pedal is lifted after notes at the end time.
  • pedal_type: the pedal type.

A "pedal PFT" is a list of these segments. To apply a PFT for MIDI pedal to a Score starting at a given time:

Score.pedal_pft(pft: PFT, type: int = PEDAL_SUSTAIN, t0: float=0)

These PFTs can be created using a shorthand notation.

You can also add a single PedalSeg to a Score at a given time:

Score.insert_pedal(pedal: PedalSeg, type: int = PEDAL_SUSTAIN, t0: float)

If P0 and P1 are adjacent PedalSeg objects and there are notes that start at P1.time, the semantics are:

P0.closed_end P1.closed_start result
false false release pedal, play notes, set pedal to p1.level0
false true release pedal, set pedal to p1.level0, play notes
true false play notes, set pedal to p1.level0
true true set pedal to p1.level0, play notes

If P0.closed_end is set, the pedal is momentarily released, and all notes being sustained by the pedal stop sounding.

Virtual sustain pedal

Numula provides a "virtual sustain pedal" feature, which is like the MIDI sustain pedal except that affects only a subset of notes. For example, a virtual pedal might apply to the LH but not the RH.

To use a virtual sustain pedal:

Score.sustain(t0: float, t1: float, selector: Selector=None)

For each selected note starting at t0 or later: if the end time is before t1, change the duration so that the end time is t1.

You can control virtual sustain pedals using a PFT consisting of PedalSeg objects.

When such a PFT is used to control the virtual sustain pedal, pedal_type is ignored and level is 0 or 1. To apply such a PFT to a Score:

Score.vsustain_pft(pft: PFT, t0: float=0, selector:Selector=None)

t0 is the score start time, and selector specifies which notes are affected by the virtual pedal.