Table of Contents

Class NoteInput

Namespace
Sigil.Sound
Assembly
Sigil.Sound.Notes.dll

Notes played live, from a keyboard, a game or a UI, as opposed to a Sigil.Sound.Pattern written in advance. Create one, build a sound from it with Sigil.Sound.NoteInput.Play(System.Func{Sigil.Sound.Voice,Sigil.Sound.Sound},System.Int32), and call Sigil.Sound.NoteInput.NoteOn(System.Int32,System.Single,System.Double) and Sigil.Sound.NoteInput.NoteOff(System.Int32) from any thread while that sound renders.

public sealed class NoteInput

Inheritance

Inherited Members

Examples

using Sigil.Sound;
using static Sigil.Sound.Sounds;

var keys = new NoteInput();
var synth = keys.Play(v => Saw(v.Frequency).LowPass(2000).Adsr(0.01, 0.2, 0.6, 0.4), polyphony: 6);
keys.NoteOn("c4");   // middle C sounds until...
keys.NoteOff("c4");  // ...it is released and its envelope rings out

Remarks

The input outlives the sounds built from it. When a sound is replaced (a Sigil.Sound.Slot swap, a new Sigil.Sound.SoundContext.Play(Sigil.Sound.Sound,System.Nullable{System.Double})) the replacement starts with the keys that are held right now already sounding, so editing a patch while holding a chord does not drop the chord.

Calls take a short lock, so they are for control threads (MIDI, UI, game logic), not for the audio callback. The audio side never locks and never allocates: events pass through a fixed queue and are applied at the start of the next block, so a note begins within one block (a few milliseconds) of the call.

Properties

Clock

The clock these notes are played against, or null. Note streams measure time with it: the delay of an echo, the boundary a chord changes on. A host sets it to the clock it advances for its sounds. Without one, streams count from when they start, at 120 bpm, like a pattern played without a host clock.

public HostClock? Clock { get; set; }

Property Value

HostClock?

Stream

The notes pressed into this input, as a Sigil.Sound.NoteStream to shape and send on: transpose it, harmonize it, filter it, follow its chord with a pattern, and play it or send it out. keys.Play(voice) plays the notes as they are; this is the way to do something to them first.

public NoteStream Stream { get; }

Property Value

NoteStream

Examples

// The notes of an input as a stream to shape: here every key also sounds the note an octave below it.
var keys = new NoteInput();
var sound = keys.Stream.Harmonize(0, -12)
    .Play(v => Square(v.Frequency).LowPass(1500).Adsr(0.01, 0.2, 0.6, 0.3));
keys.NoteOn("e3 b3");
sound * 0.25

Methods

AllNotesOff()

Releases every held note, including the ones started with Sigil.Sound.NoteInput.Start(System.Int32,System.Single).

public void AllNotesOff()

IsHeld(string)

True while every note written in Sigil.Sound.Mini notation is held, as Sigil.Sound.NoteInput.IsHeld(System.Int32) reports it. False for text with no notes.

public bool IsHeld(string notes)

Parameters

notes string

Mini-notation for notes, such as "c4 e4 g4".

Returns

bool

Exceptions

MiniNotationException

The text is not valid.

IsHeld(int)

True while pitch is held: between its Sigil.Sound.NoteInput.NoteOn(System.Int32,System.Single,System.Double) and Sigil.Sound.NoteInput.NoteOff(System.Int32), or while a note started on it with Sigil.Sound.NoteInput.Start(System.Int32,System.Single) has not been released.

public bool IsHeld(int pitch)

Parameters

pitch int

The MIDI pitch, 0 to 127.

Returns

bool

NoteOff(string)

Ends the notes written in Sigil.Sound.Mini notation, the counterpart of Sigil.Sound.NoteInput.NoteOn(System.String). Velocities in the text are ignored. A note that is not held is skipped.

public void NoteOff(string notes)

Parameters

notes string

Mini-notation for notes, such as "a2 g3 e4".

Exceptions

MiniNotationException

The text is not valid. No note is ended.

NoteOff(int)

Ends every held note of pitch; its voice releases and rings out. Does nothing if none is held.

public void NoteOff(int pitch)

Parameters

pitch int

The MIDI pitch, 0 to 127.

NoteOn(int, float, double)

Starts a note. A velocity of 0 ends it instead, as in MIDI.

public void NoteOn(int pitch, float velocity = 0.8, double cents = 0)

Parameters

pitch int

The MIDI pitch, 0 to 127; 60 is middle C.

velocity float

How hard the note is played, from 0 to 1. Defaults to 0.8.

cents double

How far the note is from pitch, in hundredths of a semitone, from -100 to 100. This is how a source with another tuning enters (a Tonnetz in 19 steps to the octave names the nearest MIDI pitch and the cents off it); the cents travel with the note through every Sigil.Sound.NoteStream operator and sound in Sigil.Sound.NoteInput.Play(System.Func{Sigil.Sound.Voice,Sigil.Sound.Sound},System.Int32).

Examples

// A note 50 cents above A4, a quarter tone sharp, beating against the A4 beside it.
var keys = new NoteInput();
var synth = keys.Play(v => Saw(v.Frequency).LowPass(1800).Adsr(0.01, 0.2, 0.8, 0.3), polyphony: 4);
keys.NoteOn(69);
keys.NoteOn(69, 0.8f, 50.0);
synth * 0.25

Exceptions

ArgumentOutOfRangeException

The pitch, velocity or cents are out of range.

NoteOn(string)

Starts the notes written in Sigil.Sound.Mini notation, all at once: "a2 g3 e4" is the same as three calls to Sigil.Sound.NoteInput.NoteOn(System.Int32,System.Single,System.Double). This is the way to name notes in a score's own terms (c4, eb3) instead of MIDI numbers, and a chord can be written as "[c3,e3,g3]" or "c3,e3,g3".

public void NoteOn(string notes)

Parameters

notes string

Mini-notation for notes, as in Sigil.Sound.Mini.Notes(System.String): c4:0.6 sets the velocity, a name without an octave is in octave 3, and ~ is a rest. Time has no meaning for a live input, so the notes of the first cycle all start now; "c3 e3 g3" is a chord, not an arpeggio. A velocity of 0 (c4:0) ends the note, as in MIDI.

Examples

var keys = new NoteInput();
var synth = keys.Play(v => Saw(v.Frequency).LowPass(1500).Adsr(0.01, 0.2, 0.7, 0.4), polyphony: 6);
keys.NoteOn("a2 g3 e4:0.6");
synth * 0.25

Exceptions

MiniNotationException

The text is not valid. No note is started.

Play(Func<Voice, Sound>, int)

Turns this input into sound: each note on starts a voice built from voice, which sounds until the matching note off and then releases. Voices are mixed, up to polyphony at once; past that, a voice that is already releasing is cut first, then the oldest held one.

public Sound Play(Func<Voice, Sound> voice, int polyphony = 8)

Parameters

voice Func<Voice, Sound>

Describes the sound of one note from its Sigil.Sound.Voice signals. Called while the sound is built, not per note.

polyphony int

The most notes sounding at once, from 1 to 64. Defaults to 8.

Returns

Sound

Examples

using Sigil.Sound;
using static Sigil.Sound.Sounds;

var keys = new NoteInput();
var synth = keys.Play(v => Square(v.Frequency).Adsr(0.005, 0.1, 0.7, 0.3), polyphony: 4);

Remarks

Each voice is scaled by the note's velocity. An Adsr inside it holds its sustain level for as long as the key is down and releases at the note off; v.Gate is 1 until then and 0 after. The voices are built up front and reused, so playing allocates nothing. The same input can feed several sounds at once.

Start(int, float)

Starts a note you can keep shaping while it sounds, and returns it. Unlike Sigil.Sound.NoteInput.NoteOn(System.Int32,System.Single,System.Double), which names a note by its pitch, the note is its own object: two started on the same pitch are two notes, and ending or bending one leaves the other alone. This is the entry for a player with per-note expression, such as an MPE controller.

public PlayingNote Start(int pitch, float velocity = 0.8)

Parameters

pitch int

The MIDI pitch, 0 to 127; 60 is middle C.

velocity float

How hard the note is played, above 0 up to 1. Defaults to 0.8.

Returns

PlayingNote

The note. Set its Sigil.Sound.PlayingNote.Bend, Sigil.Sound.PlayingNote.Pressure and Sigil.Sound.PlayingNote.Slide; end it with Sigil.Sound.PlayingNote.Release.

Examples

// A note bent up a whole tone while it is held, as a finger would on an MPE keyboard.
var keys = new NoteInput();
var synth = keys.Play(v => Sine(v.Frequency).Adsr(0.01, 0.1, 0.9, 0.3));
var note = keys.Start(60);
note.Bend = 2;
synth * 0.4

Remarks

Sigil.Sound.NoteInput.NoteOff(System.Int32) does not end a note started here; call Sigil.Sound.PlayingNote.Release (or Sigil.Sound.NoteInput.AllNotesOff). A sound built later still starts with the notes held now, with their current bend, pressure and slide.

Exceptions

ArgumentOutOfRangeException

The pitch is not 0 to 127, or the velocity is not above 0 and at most 1.

Start(string)

Starts the notes written in Sigil.Sound.Mini notation and returns them, one Sigil.Sound.PlayingNote each, in the order written: keys.Start("c4 e4"). See Sigil.Sound.NoteInput.Start(System.Int32,System.Single) for what a started note can do.

public IReadOnlyList<PlayingNote> Start(string notes)

Parameters

notes string

Mini-notation for notes, read as in Sigil.Sound.NoteInput.NoteOn(System.String). A velocity of 0 is not allowed here.

Returns

IReadOnlyList<PlayingNote>

Exceptions

MiniNotationException

The text is not valid. No note is started.

ArgumentOutOfRangeException

A velocity is 0.