Class Sound
- Namespace
- Sigil.Sound
- Assembly
- Sigil.Sound.dll
An immutable description of a sound. Composing a Sigil.Sound.Sound never renders anything and never mutates the original; every operator returns a new value, so a sound can be reused, shared and rendered many times. State only exists in the Sigil.Sound.IOperator produced by Sigil.Sound.Sound.Compile(Sigil.Sound.RenderContext).
public sealed class SoundInheritance
Inherited Members
Methods
Adsr(double, double, double, double, double?)
Shapes the amplitude with an attack-decay-sustain-release envelope. Times are in seconds.
Inside a note voice (Play) the release starts when the note ends. Anywhere else the sound
sustains until you give hold, or for as long as it plays.
public Sound Adsr(double attack = 0.01, double decay = 0.1, double sustain = 0.7, double release = 0.3, double? hold = null)Parameters
attackdouble-
Seconds to rise from silence to full level. Defaults to 0.01.
decaydouble-
Seconds to fall from full level to
sustain. Defaults to 0.1. sustaindouble-
The level held after the decay, from 0 to 1. Defaults to 0.7.
releasedouble-
Seconds to fall to silence once the release starts. Defaults to 0.3.
holddouble?-
Seconds from the start until the release begins. Omit it (the default) to follow the note, or to sustain.
Returns
Examples
Saw(220).Adsr(0.01, 0.2, 0.6, 1.5, hold: 3)
Adsr(Param, Param?, Param?, Param?, double?)
Applies an attack-decay-sustain-release envelope driven by constant or modulated parameters. Times are in seconds; sustain is a level from 0 to 1.
public Sound Adsr(Param attack, Param? decay = null, Param? sustain = null, Param? release = null, double? hold = null)Parameters
attackParam-
Seconds to ramp from 0 to peak. A number, or a sound/knob to modulate it.
decayParam?-
Seconds to fall from peak to the sustain level. A number, or a sound/knob to modulate it. Defaults to 0.1.
sustainParam?-
Level held until release begins, between 0 and 1. A number, or a sound/knob to modulate it. Defaults to 0.7.
releaseParam?-
Seconds to fade to silence once released. A number, or a sound/knob to modulate it. Defaults to 0.3.
holddouble?-
Seconds from the start until the release begins. Omit it (the default) to follow the note, or to sustain.
Returns
Examples
Saw(220).Adsr(0.01, 0.2, 0.6, 1.5, hold: 3)
Balance(Param)
Balances the stereo field by turning down the side you pan away from; the other side is left alone. A mono source balanced to one side ends up silent on the other, as with a balance knob, and a stereo image is kept.
public Sound Balance(Param position)Parameters
positionParam-
-1 is hard left, 0 the center (unchanged), 1 hard right. A number, or a sound or
Fn(t => ...)to move it.
Returns
Examples
// A wide pad nudged left: the right side is turned down, the stereo image is kept.
var pad = (Saw(110) + Saw(165)).LowPass(1500).Chorus(rate: 0.5, voices: 4) * 0.3;
pad.Reverb().Balance(-0.3)
BandPass(Param?, Param?)
A band-pass filter: keeps a band around center and removes the rest.
public Sound BandPass(Param? center = null, Param? q = null)Parameters
centerParam?-
Frequency in Hz at the middle of the band. A number, or a sound or
Fn(t => ...)to sweep it. Defaults to 1000. qParam?-
Narrowness of the band: higher is narrower. Defaults to 0.7071.
Returns
Examples
Noise().BandPass(Sine(0.3).Range(300, 3000), q: 8)
BitCrush(Param?, Param?)
A lo-fi crusher: reduces the bit depth, and optionally the sample rate, for a gritty, aliased sound.
public Sound BitCrush(Param? bits = null, Param? rate = null)Parameters
bitsParam?-
Bits of resolution: 16 is clean, 8 is classic lo-fi, 2 or 3 is brutal. Fractions work. Defaults to 8.
rateParam?-
The reduced sample rate in Hz. 8000 sounds like an old sampler; omit it to keep the full rate, which is the default.
Returns
Examples
Saw(110).BitCrush(bits: 5, rate: 6000)
Chorus(Param?, Param?, Param?, int)
A chorus: thickens the sound by mixing in slightly detuned, slowly drifting copies spread across the stereo field.
public Sound Chorus(Param? rate = null, Param? depth = null, Param? mix = null, int voices = 3)Parameters
rateParam?-
How fast the copies drift, in Hz. Defaults to 0.8.
depthParam?-
How far they drift, from 0 (none) to 1 (up to 12 ms). Defaults to 0.5.
mixParam?-
0 is only the original, 1 only the copies. Defaults to 0.5.
voicesint-
How many copies, from 1 to 8. More is wider and thicker. Defaults to 3.
Returns
Examples
(Saw(110) + Saw(165)).LowPass(1500).Chorus(rate: 0.5, voices: 4) * 0.3
Comb(Param?, Param?, Param?)
A comb filter tuned to a pitch: it resonates at frequency and its multiples.
Fed a short burst it rings like a plucked string (Karplus-Strong); fed a steady sound it adds a metallic ring.
public Sound Comb(Param? frequency = null, Param? feedback = null, Param? damping = null)Parameters
frequencyParam?-
The pitch it resonates at, in Hz (20 and up). A number, or a sound or
Fn(t => ...)to sweep it. Defaults to 440 (A4). feedbackParam?-
How long it rings, from 0 (not at all) to 0.999 (very long). Defaults to 0.95.
dampingParam?-
How quickly the highs fade while it rings, from 0 (bright) to 0.99 (dull). Defaults to 0.2.
Returns
Examples
Noise().Adsr(0, 0.01, 0, 0.001).Comb(220, feedback: 0.995) // a plucked string
Compile(RenderContext)
Builds a running instance of this sound for context.
May allocate; the returned operator's Sigil.Sound.IOperator.RenderBlock(System.Span{Sigil.Sound.StereoFrame}) must not.
public IOperator Compile(RenderContext context)Parameters
contextRenderContext
Returns
Remarks
A sound used in several places of one graph is built once and fans out to all of them,
so var bus = ...; bus + bus.Delay(0.2) renders bus a single time.
The result is System.IDisposable when anything inside it is; dispose it when you are done rendering.
Compress(Param?, Param?, Param?, Param?, Param?, Param?, Sound?, double)
A dynamic range compressor: turns down the parts above thresholdDb, so loud and quiet passages
sit closer together, then adds makeup gain to bring the overall level back up. Use it to glue a drum bus together
or to tame a lead, or with a sidechain to duck one sound under another.
public Sound Compress(Param? thresholdDb = null, Param? ratio = null, Param? attack = null, Param? release = null, Param? kneeDb = null, Param? makeupGainDb = null, Sound? sidechain = null, double lookahead = 0.003)Parameters
thresholdDbParam?-
The level, in dBFS, above which gain reduction starts. Full scale is 0 and quieter is more negative. Defaults to -18.
ratioParam?-
How hard it compresses above the threshold: 4 means 4 dB of input above the threshold gives 1 dB out. 1 does nothing, and very high values act as a limiter. Defaults to 4.
attackParam?-
Seconds to react once the level rises. Shorter catches more transients but can dull the attack. Defaults to 0.01.
releaseParam?-
Seconds for the gain to recover after the level falls. Shorter pumps more audibly. Defaults to 0.1.
kneeDbParam?-
Width of the soft transition around the threshold, in dB. 0 is a hard knee. Defaults to 6.
makeupGainDbParam?-
Gain added after compression, in dB, to restore the level. Defaults to 0.
sidechainSound?-
A sound whose level drives the compressor instead of this one: a kick keyed into a pad ducks the pad. By default (null) it uses this sound.
lookaheaddouble-
Seconds the detector runs ahead of the audio, so the gain is already falling when a transient arrives: 0 to 0.02. It adds that much latency. Defaults to 0.003 (3 ms); use 0 for none.
Returns
Examples
// A drum bus: a kick and a hat glued together, pushed down 4:1 above -16 dB, then made up.
var kick = Sine(Fn(t => 50 + 90 * Math.Exp(-(t % 0.5) * 30))) // the pitch drops every half second
* Fn(t => Math.Exp(-(t % 0.5) * 7)); // and the kick fades out
var hat = Noise().HighPass(6000) * Fn(t => Math.Exp(-(t % 0.25) * 40)) * 0.3;
(kick + hat).Compress(thresholdDb: -16, ratio: 4, attack: 0.005, release: 0.1, kneeDb: 6, makeupGainDb: 3)
// Ducking: while the kick sounds the pad is turned down, then it swells back (a pumping pad).
var kick = Sine(Fn(t => 50 + 90 * Math.Exp(-(t % 0.5) * 30))) // the pitch drops every half second
* Fn(t => Math.Exp(-(t % 0.5) * 7)); // and the kick fades out
var pad = (Saw(110) + Saw(165)).LowPass(900);
(pad.Compress(thresholdDb: -20, ratio: 8, attack: 0.002, release: 0.25, sidechain: kick) * 0.3 + kick * 0.6).Limit()
Remarks
The detector follows the louder of the left and right channels, with separate attack and release times. A soft knee eases the compression in around the threshold so there is no audible corner. Lookahead delays the audio, so the gain is already falling when a transient arrives, at the cost of that much latency.
Exceptions
- ArgumentOutOfRangeException
-
A parameter is outside its range, such as a ratio below 1 or a lookahead above 0.02 seconds.
Convolve(AudioBuffer, Param?, IrNormalize)
Convolves the sound with an impulse response: a recording of how a space, a speaker cabinet or any other linear system answers a single click. The result is the sound as if it were played through that system.
public Sound Convolve(AudioBuffer impulse, Param? mix = null, IrNormalize normalize = IrNormalize.Energy)Parameters
impulseAudioBuffer-
The impulse response, such as
AudioBuffer.FromWav(...)orSamples.Load("cab.wav"). It is copied, so changing the buffer afterwards has no effect. mixParam?-
0 is only the dry sound, 1 only the convolved sound. A number, or a sound or
Fn(t => ...)to move it. Defaults to 1, for a cabinet. Use about 0.3 for a reverb. normalizeIrNormalize-
How the response is scaled before use. Defaults to Sigil.Sound.IrNormalize.Energy.
Returns
Examples
// A guitar cabinet, as it would load from a file: a short response whose resonance colours the sound.
// This one is built in code, a 1.2 kHz ring that fades in 12 ms. Listen for the saw going from buzzy to boxy.
const int rate = 48_000;
var frames = new StereoFrame[rate * 12 / 1000];
for (int i = 0; i < frames.Length; i++)
frames[i] = StereoFrame.Mono((float)(Sin(2 * PI * 1200 * i / rate) * Exp(-i / 90.0)));
var cabinet = new AudioBuffer(frames, rate);
Saw(110).Distort(drive: 4).Convolve(cabinet) * 0.4
// A room from noise: a response that starts loud and dies away over 1.5 s is what a reverb tail is.
// Left and right get different noise, so the room is wide. Plucks go in, the room comes back with them.
const int rate = 48_000;
var random = new Random(7);
var frames = new StereoFrame[rate * 3 / 2];
for (int i = 0; i < frames.Length; i++)
{
double fade = Exp(-4.6 * i / frames.Length);
frames[i] = new StereoFrame((float)((random.NextDouble() * 2 - 1) * fade), (float)((random.NextDouble() * 2 - 1) * fade));
}
var room = new AudioBuffer(frames, rate);
var pluck = Saw(220).LowPass(2000).Adsr(0.002, 0.2, 0, 0.1) * Fn(t => t % 1 < 0.5 ? 1 : 0);
pluck.Convolve(room, mix: 0.4) * 0.5
Remarks
The convolver adds no latency, and its cost grows slowly with the length of the response (the first 128 taps run directly, the rest in partitions of an FFT), so a ten second cathedral is practical. A response longer than 10 seconds is cut there with a short fade-out. A response recorded at another sample rate is converted to the rate of the render when the sound is compiled.
A stereo response convolves the left channel with its left side and the right channel with its right side, so a stereo room keeps its image. A response whose two channels are identical (a mono file, for instance) is applied to both channels at half the cost.
The response is prepared when the sound is compiled, not while it plays, and the preparation is shared by every voice and every
recompile of the same sound. Memory: the prepared response takes about 32 bytes per tap for a stereo response (16 for mono),
and each running instance, such as each voice of a Play, adds the same again for its own signal state. A ten second stereo
response is about 16 MB shared plus 16 MB per instance, so use shorter responses where many voices run at once.
Pre-delay, a low or high cut and the width are not parameters here: compose them
(Sigil.Sound.Sound.Delay(System.Nullable{Sigil.Sound.Param},System.Nullable{Sigil.Sound.Param},System.Nullable{Sigil.Sound.Param},System.Double), Sigil.Sound.Sound.HighPass(System.Nullable{Sigil.Sound.Param},System.Nullable{Sigil.Sound.Param}), Sigil.Sound.Sound.Spread(System.Nullable{Sigil.Sound.Param})) before or after.
Exceptions
- ArgumentNullException
-
impulseis null. - ArgumentException
-
impulsehas no frames.
Delay(Param?, Param?, Param?, double)
An echo: repeats the sound after time seconds, and repeats the repeats.
public Sound Delay(Param? time = null, Param? feedback = null, Param? mix = null, double maxTime = 2)Parameters
timeParam?-
Seconds between repeats, up to
maxTime. A number, or a sound orFn(t => ...)to bend the pitch of the echoes. Defaults to 0.375, a dotted eighth at 120 bpm. feedbackParam?-
How much of each echo feeds the next, from 0 (one echo) to 0.99 (nearly endless). Defaults to 0.4.
mixParam?-
0 is only the original, 1 only the echoes. Defaults to 0.5.
maxTimedouble-
The longest delay time in seconds, above 0 and at most 60; it sizes the buffer. Defaults to 2.
Returns
Examples
Saw(220).Adsr(0.01, 0.1, 0, 0.1).Delay(0.375, feedback: 0.5)
Exceptions
- ArgumentOutOfRangeException
-
The maximum time is not above 0 or is over 60 seconds.
Distort(Param?, DistortionMode, Param?)
Distortion: pushes the sound through a curve that adds harmonics, from warm saturation to fuzz.
public Sound Distort(Param? drive = null, DistortionMode mode = DistortionMode.Soft, Param? level = null)Parameters
driveParam?-
How hard the sound is pushed into the curve. 1 is gentle; 10 is heavy. A number, or a sound or
Fn(t => ...). Defaults to 4. modeDistortionMode-
The curve: Sigil.Sound.DistortionMode.Soft (default), Sigil.Sound.DistortionMode.Hard, Sigil.Sound.DistortionMode.Fold or Sigil.Sound.DistortionMode.Fuzz.
levelParam?-
The output level, to balance against the clean sound. Defaults to 1.
Returns
Examples
Saw(110).Distort(drive: 8, DistortionMode.Fold, level: 0.5)
Equalize(double, double, double)
A simple three-band tone control: a low shelf at 200 Hz, a broad bell at 1 kHz and a high shelf at 5 kHz. Bands at 0 dB are skipped, so an all-zero call returns this sound unchanged.
public Sound Equalize(double lowDb = 0, double midDb = 0, double highDb = 0)Parameters
lowDbdouble-
Boost or cut of the low shelf, in decibels. Defaults to 0.
midDbdouble-
Boost or cut of the 1 kHz bell, in decibels. Defaults to 0.
highDbdouble-
Boost or cut of the high shelf, in decibels. Defaults to 0.
Returns
Examples
// A tone control: a little more low end and treble, a little less mid.
(Saw(110) + Saw(220) * 0.5 + Noise() * 0.02).Equalize(lowDb: 2, midDb: -1.5, highDb: 3) * 0.3
FdnReverb(double, double, double, double, Param?, Param?)
A large algorithmic reverb built from eight feedback delay lines that mix into one another. It gives a denser, smoother
tail than Sigil.Sound.Sound.Reverb(System.Double,System.Double,System.Double,System.Nullable{Sigil.Sound.Param}), and with sheen it can shimmer upward like a cathedral.
public Sound FdnReverb(double decay = 3, double size = 1, double damping = 0.35, double predelay = 0.01, Param? sheen = null, Param? mix = null)Parameters
decaydouble-
Seconds for the tail to fade by 60 dB, above 0 and at most 120. Defaults to 3.
sizedouble-
Scales the room: 0.5 is a small room, 1 a medium one, 4 a hall. Defaults to 1.
dampingdouble-
How fast the high frequencies die in the tail, from 0 (bright) to 0.99 (dark). Defaults to 0.35.
predelaydouble-
Seconds between the dry sound and the first reflection, from 0 to 0.5. A longer predelay separates the sound from its room. Defaults to 0.01.
sheenParam?-
How much of an octave-up copy of the tail is mixed in, from 0 (none) to 0.7 (very ethereal). The decay time stays the same. A number, or a sound to move it. Defaults to 0.
mixParam?-
0 is only the dry sound, 1 only the reverb. Defaults to 0.3.
Returns
Examples
// A shimmering hall around a pad.
var pad = (Saw(110) + Saw(165) + Saw(220)).LowPass(1200) * 0.2;
pad.FdnReverb(decay: 5.0, size: 1.5, damping: 0.35, predelay: 0.03, sheen: 0.4, mix: 0.4)
Exceptions
- ArgumentOutOfRangeException
-
A parameter is outside its range.
Feedback(Func<Sound, Sound>, double)
Feeds the sound's own output back through a chain you write, so echoes, shimmer and resonances that grow from what
came before can be built from the same pieces as everything else. The result is this sound plus loop
applied to that result, a moment late.
public Sound Feedback(Func<Sound, Sound> loop, double latencySeconds = 0.001)Parameters
loopFunc<Sound, Sound>-
Builds what is fed back from the delayed output.
latencySecondsdouble-
How late the loop sees the output, from 0.0001 to 0.05. Defaults to 0.001.
Returns
Examples
Saw(220).Adsr(0.005, 0.1, 0, 0.1).Feedback(echo => echo.Delay(0.35, mix: 1).LowPass(3000) * 0.6)
Remarks
The loop sees the output one fixed chunk (latencySeconds, 1 ms by default) late, so a loop with a delay of
0.35 s repeats every 0.35 s plus that chunk. The chunk is the same whatever block size the host renders in, so the sound does
not change between hosts. Loops shorter than the chunk cannot be built this way; use Sigil.Sound.Sound.Comb(System.Nullable{Sigil.Sound.Param},System.Nullable{Sigil.Sound.Param},System.Nullable{Sigil.Sound.Param}).
Give the loop a gain below 1 so the repeats die away. A loop that keeps growing is held in check instead of running away: values past full scale are eased toward a ceiling of 2 and anything that is not a number is dropped, so a mistake gives a loud sound, not silence or a crash.
loop is called each time the sound is compiled, with a stand-in for the delayed output. It should only build a
sound from it, and may use the stand-in more than once.
Fold(Param?, Param?, Param?)
A West Coast wavefolder: drives the sound past ±1 and folds it back, adding harmonics that sound bright, bell-like and metallic rather than clipped. Symmetry adds warm even harmonics on top of the odd ones.
public Sound Fold(Param? amount = null, Param? symmetry = null, Param? mix = null)Parameters
amountParam?-
How many times the sound folds: 1 is barely touched, 3 to 5 is typical, and higher gets very bright. A number, or a sound to sweep it. Defaults to 3.
symmetryParam?-
An offset applied before folding, from -1 to 1. Zero keeps only odd harmonics; a small value brings in even ones. Defaults to 0.
mixParam?-
0 is only the dry sound, 1 only the folded sound. Defaults to 1.
Returns
Examples
Sine(110).Fold(amount: Sine(0.2).Range(1.0, 7.0), symmetry: 0.25, mix: 1.0).LowPass(4000)
FollowEnvelope(Param?, Param?)
Tracks the loudness of this sound as a control signal from 0 to 1, so the sound can drive other parameters. Pair it with Sigil.Sound.Sound.Range(System.Double,System.Double) to map the level onto a filter cutoff, a gain or a pitch.
public Sound FollowEnvelope(Param? attack = null, Param? release = null)Parameters
attackParam?-
Seconds for the output to rise when the sound gets louder. Defaults to 0.01.
releaseParam?-
Seconds for the output to fall when the sound gets quieter. Defaults to 0.1.
Returns
Examples
// An auto-wah: the loudness of the plucks opens the filter. Each pluck is brighter at its start.
var guitar = Saw(110) * Fn(t => Math.Exp(-(t % 0.6) * 5));
var level = guitar.FollowEnvelope(attack: 0.005, release: 0.08);
guitar.LowPass(level.Range(350, 3200), q: 4.0) * 0.5
FromOperator(Func<RenderContext, IOperator>, string?)
Wraps a custom operator. The factory is called once per Sigil.Sound.Sound.Compile(Sigil.Sound.RenderContext) and must return a fresh instance each time.
public static Sound FromOperator(Func<RenderContext, IOperator> factory, string? description = null)Parameters
factoryFunc<RenderContext, IOperator>-
Creates the running operator for a render context. It runs off the audio thread, so allocate here.
descriptionstring?-
Text shown for the sound in Sigil.Sound.Sound.ToString. Defaults to "Custom".
Returns
Gain(Param)
Scales the amplitude.
public Sound Gain(Param gain)Parameters
gainParam-
The factor: a number, another sound (amplitude modulation) or
Fn(t => ...). 1 leaves the level unchanged.
Returns
Examples
Saw(110).Gain(0.5) // same as Saw(110) * 0.5
HighPass(Param?, Param?)
A resonant two-pole high-pass filter: removes what is below cutoff. Mirror image of Sigil.Sound.Sound.LowPass(System.Nullable{Sigil.Sound.Param},System.Nullable{Sigil.Sound.Param}).
public Sound HighPass(Param? cutoff = null, Param? q = null)Parameters
cutoffParam?-
Frequency in Hz below which the sound is attenuated. A number, or a sound or
Fn(t => ...)to sweep it. Defaults to 100, which clears rumble and mud and leaves the body of most sounds alone. qParam?-
Resonance at the cutoff. 0.7 is flat; higher values ring. Defaults to 0.7071.
Returns
Examples
Noise().HighPass(Sine(0.2).Range(500, 6000), q: 3) * 0.3
HighShelf(Param?, Param?, Param?)
A high shelf: boosts or cuts everything above frequency by the same amount, for air or for taming hiss.
public Sound HighShelf(Param? frequency = null, Param? gainDb = null, Param? q = null)Parameters
frequencyParam?-
The shelf's corner in Hz, roughly where the change is half of
gainDb. A number, or a sound orFn(t => ...). Defaults to 5000. gainDbParam?-
Boost (positive) or cut (negative) above the corner, in decibels. Defaults to 4, an audible lift.
qParam?-
How steep the transition is: 0.7 is gentle and musical, higher is sharper. Defaults to 0.7071.
Returns
Examples
// A bright pad with the top end softened.
(Saw(220) + Saw(330)).HighShelf(8000, gainDb: -4) * 0.2
Limit(double, double)
A limiter: keeps the sound from passing a ceiling by turning it down just enough, then lets the level recover. Put it last in a chain so a loud mix cannot clip.
public Sound Limit(double ceiling = 0.95, double release = 0.08)Parameters
ceilingdouble-
The highest level that may come out, above 0 and up to 1. Defaults to 0.95.
releasedouble-
Seconds for the level to recover after a peak. Defaults to 0.08.
Returns
Examples
(Saw(110) + Saw(165) + Saw(220)).Limit()
LowPass(Param?, Param?)
A resonant two-pole low-pass filter. cutoff is in Hz.
public Sound LowPass(Param? cutoff = null, Param? q = null)Parameters
cutoffParam?-
Frequency in Hz above which the sound is attenuated. A number, or a sound or
Fn(t => ...)to sweep it. Defaults to 1000. qParam?-
Resonance at the cutoff. 0.7 is flat; higher values ring. Defaults to 0.7071.
Returns
Examples
Saw(110).LowPass(Sine(0.25).Range(200, 2000), q: 4)
LowShelf(Param?, Param?, Param?)
A low shelf: boosts or cuts everything below frequency by the same amount, for bass weight or thinning.
public Sound LowShelf(Param? frequency = null, Param? gainDb = null, Param? q = null)Parameters
frequencyParam?-
The shelf's corner in Hz, roughly where the change is half of
gainDb. A number, or a sound orFn(t => ...). Defaults to 200. gainDbParam?-
Boost (positive) or cut (negative) below the corner, in decibels. Defaults to 4, an audible lift.
qParam?-
How steep the transition is: 0.7 is gentle and musical, higher is sharper. Defaults to 0.7071.
Returns
Examples
// A bass with a little more weight below 120 Hz.
Saw(55).LowPass(900).LowShelf(120, gainDb: 3) * 0.4
Notch(Param?, Param?)
A notch filter: removes a narrow band around center and keeps the rest.
public Sound Notch(Param? center = null, Param? q = null)Parameters
centerParam?-
Frequency in Hz at the middle of the notch. A number, or a sound or
Fn(t => ...)to sweep it. Defaults to 1000. qParam?-
Narrowness of the notch: higher is narrower. Defaults to 0.7071.
Returns
Examples
Saw(110).Notch(1000, q: 4)
Pan(Param)
Places the sound in the stereo field with an equal-power law, so it keeps the same loudness wherever it sits. A stereo sound is folded to mono first; use Sigil.Sound.Sound.Balance(Sigil.Sound.Param) to keep a stereo image.
public Sound Pan(Param position)Parameters
positionParam-
-1 is hard left, 0 the center (a mono source keeps its level), 1 hard right. A number, or a sound or
Fn(t => ...)to move it.
Returns
Examples
Saw(110).Pan(Sine(0.3)) // drifts between the speakers
Peak(Param?, Param?, Param?)
A peaking (bell) equalizer band: boosts or cuts a band around frequency and leaves the rest of
the spectrum alone. Chain several for a parametric EQ.
public Sound Peak(Param? frequency = null, Param? q = null, Param? gainDb = null)Parameters
frequencyParam?-
Centre of the band in Hz. A number, or a sound or
Fn(t => ...)to sweep it. Defaults to 1000. qParam?-
How narrow the band is: 0.7 is broad and musical, 3 to 10 is surgical. Defaults to 0.7071.
gainDbParam?-
Boost (positive) or cut (negative) at the centre, in decibels. 0 changes nothing. Defaults to 4, an audible lift.
Returns
Examples
// A lead with a presence lift: the 2.5 kHz band is pushed up 4 dB.
Saw(220).LowPass(6000).Peak(2500, q: 3, gainDb: 4) * 0.3
Phaser(Param?, Param?, Param?, int, double, double)
A phaser: sweeps notches up and down the spectrum with a chain of all-pass filters, for a swirling, jet-like movement.
public Sound Phaser(Param? rate = null, Param? feedback = null, Param? mix = null, int stages = 4, double minFrequency = 400, double maxFrequency = 1500)Parameters
rateParam?-
Sweeps per second. Defaults to 0.4.
feedbackParam?-
Sharpens the notches, from 0 to 0.95. Negative values change the character. Defaults to 0.3.
mixParam?-
0 is only the original, 1 only the filtered sound; around 0.5 gives the deepest notches. Defaults to 0.5.
stagesint-
All-pass stages, from 1 to 12; every two make one notch. Defaults to 4.
minFrequencydouble-
The bottom of the sweep in Hz. Defaults to 400.
maxFrequencydouble-
The top of the sweep in Hz. Defaults to 1500.
Returns
Examples
Saw(110).Phaser(rate: 0.2, feedback: 0.6, stages: 6)
PitchShift(Param?, Param?)
Shifts the pitch up or down without changing the speed, by moving every frequency in the sound's spectrum. Whatever shape the spectrum has moves with it, so formants follow the shift: a voice shifted up gets smaller, shifted down gets bigger, as when a tape is sped up or slowed. Shifted down, the top of the spectrum falls too, so a dive darkens as well as lowers.
public Sound PitchShift(Param? semitones = null, Param? mix = null)Parameters
semitonesParam?-
How far to move the pitch: 12 is an octave up, -12 an octave down, 0 leaves it alone. Limited to ±24. A number, or a sound,
ControlorFn(t => ...)to bend it; it is read once per frame, so a fast sweep steps every 10 ms. Defaults to 12, an octave up. mixParam?-
0 is only the original, 1 only the shifted sound. Use a middle value to add a harmony. Defaults to 1.
Returns
Examples
Saw(220).PitchShift(7, mix: 0.5) // the sound plus a fifth above it
// A two-second dive down an octave.
var pad = (Saw(220) + Saw(330)).LowPass(2000) * 0.2;
pad.PitchShift(Fn(t => -12 * Math.Min(t / 2, 1)))
Remarks
This is a phase-vocoder shifter that finds each spectral peak, moves it and the bins around it together, and keeps their phases in step. It is clean on steady and layered material (chords, pads, voices) and does best within about an octave either way. Sharp transients are softened a little by the analysis window.
The frame is about 40 ms (2048 samples at 44.1 and 48 kHz) and the sound comes out that much later than it went in; the
mix dry signal is delayed to match. For a short-latency shifter, such as inside a feedback loop,
shift in the time domain instead. A stereo sound is shifted channel by channel.
Range(double, double)
Maps a bipolar signal in [-1, 1] to [min, max]; handy for LFOs.
public Sound Range(double min = 0, double max = 1)Parameters
mindouble-
The value the signal's -1 maps to. Defaults to 0.
maxdouble-
The value the signal's +1 maps to. Defaults to 1, so with no arguments an LFO becomes a 0 to 1 control.
Returns
Examples
Sine(0.25).Range(200, 2000) // sweeps between 200 and 2000, four seconds per cycle
Render(double, int, int, double)
Renders seconds of audio offline, beginning startSeconds into the sound.
The skipped part is still run (and discarded) so oscillators, envelopes, delay lines and filters are in the state
they would have reached by then, and the result matches that same stretch of a longer render from zero.
public AudioBuffer Render(double seconds, int sampleRate = 48000, int blockSize = 256, double startSeconds = 0)Parameters
secondsdouble-
How much audio to keep, in seconds.
sampleRateint-
Sample rate in Hz.
blockSizeint-
Processing block size in frames. The skipped part is processed in the same block grid as a render from zero.
startSecondsdouble-
How far into the sound to begin, in seconds. Defaults to 0 (the start).
Returns
RenderToWav(string, double, int, WavFormat, double)
Renders seconds of audio and writes it as a WAV file.
Pass startSeconds to capture a stretch from well into the sound (see Sigil.Sound.Sound.Render(System.Double,System.Int32,System.Int32,System.Double)).
public void RenderToWav(string path, double seconds, int sampleRate = 48000, WavFormat format = WavFormat.Pcm16, double startSeconds = 0)Parameters
RenderToWav(Stream, double, int, WavFormat, double)
Renders seconds of audio and writes it as WAV to a stream (works without a file system).
Pass startSeconds to capture a stretch from well into the sound (see Sigil.Sound.Sound.Render(System.Double,System.Int32,System.Int32,System.Double)).
public void RenderToWav(Stream stream, double seconds, int sampleRate = 48000, WavFormat format = WavFormat.Pcm16, double startSeconds = 0)Parameters
Reverb(double, double, double, Param?)
A reverb: the sound as if played in a room. Built from parallel comb filters and all-pass filters (a Schroeder design), with a slightly different room for each ear.
public Sound Reverb(double size = 1, double decay = 2.5, double damping = 0.4, Param? mix = null)Parameters
sizedouble-
Scales the room: below 1 is smaller, above 1 larger, up to 100. Defaults to 1.
decaydouble-
Seconds for the tail to fade by 60 dB, above 0 and at most 120. Defaults to 2.5.
dampingdouble-
How fast high frequencies die in the tail, from 0 (bright) to 1 (dark). Defaults to 0.4.
mixParam?-
0 is only the dry sound, 1 only the reverb. Defaults to 0.3.
Returns
Examples
Sine(440).Adsr(0.005, 0.2, 0, 0.1).Reverb(decay: 4, mix: 0.4)
Exceptions
- ArgumentOutOfRangeException
-
The size is not above 0 or is over 100, the decay is not above 0 or is over 120 seconds, or the damping is outside 0 to 1.
Spread(Param?)
Stereo width: scales the difference between the left and right channels while keeping their total level. Narrowing collapses a stereo sound toward mono; widening spreads it out. Mono input stays mono.
public Sound Spread(Param? width = null)Parameters
widthParam?-
0 is mono, 1 leaves the sound unchanged, and values above 1 widen it, up to 4. A number, or a sound or
Fn(t => ...)to move it. Defaults to 1.5, a clearly wider image.
Returns
Examples
// Width 0 collapses a stereo sound to mono: how you keep the low end centered for club systems.
var bass = Saw(55).LowPass(300).Chorus(rate: 0.4, voices: 3) * 0.5;
bass.Spread(0.0)
// Width above 1 pushes the chorus voices further apart: listen on headphones.
var pad = (Saw(110) + Saw(165)).LowPass(1500) * 0.3;
pad.Chorus().Spread(1.8)
TapeDelay(Param?, Param?, Param?, Param?, Param?, double, bool)
A tape echo: repeats the sound, and each repeat is darkened and saturated as it feeds back, like a tape delay. Changing the delay time glides the pitch of the repeats, as a tape head does, instead of clicking.
public Sound TapeDelay(Param? time = null, Param? feedback = null, Param? damping = null, Param? drive = null, Param? mix = null, double maxTime = 2, bool pingPong = false)Parameters
timeParam?-
Seconds between repeats, up to
maxTime. A sound orFn(t => ...)makes the time wander for warble and dub-style sweeps. Defaults to 0.375, a dotted eighth at 120 bpm. feedbackParam?-
How much of each repeat feeds the next, from 0 (one echo) to 0.95 (a long, slowly dying trail). Defaults to 0.5.
dampingParam?-
The cutoff in Hz of the low-pass in the feedback loop. Lower values make each repeat darker. Defaults to 3000.
driveParam?-
How hard each repeat is pushed into the tape saturation: 1 or more. 1 is gentle; 3 or more is heavy. Defaults to 1.
mixParam?-
0 is only the original, 1 only the echoes. Defaults to 0.35.
maxTimedouble-
The longest delay time in seconds, above 0 and at most 60; it sizes the buffer. Defaults to 2.
pingPongbool-
When true, the repeats bounce between the left and right channels, so each echo lands on the opposite side. Defaults to false.
Returns
Examples
// Each repeat is darker and more saturated, and the wobbling delay time bends their pitch like a tape head.
var synth = Saw(220).LowPass(1500) * Fn(t => Math.Exp(-(t % 0.5) * 9));
synth.TapeDelay(time: Sine(0.2).Range(0.2, 0.4), feedback: 0.65, damping: 2800, drive: 1.4, mix: 0.35) * 0.4
// Echoes that bounce across the stereo field.
var pluck = Saw(330).LowPass(2500).Adsr(0.002, 0.15, 0, 0.1) * Fn(t => Math.Exp(-(t % 1) * 2));
pluck.TapeDelay(time: 0.375, feedback: 0.5, pingPong: true) * 0.5
Exceptions
- ArgumentOutOfRangeException
-
The maximum time is not above 0 or is over 60 seconds, or a constant drive is below 1.
Through(Func<IOperator, IOperator>, string?)
Escape hatch to the operator layer: feeds this sound into an operator you write.
public Sound Through(Func<IOperator, IOperator> wrap, string? description = null)Parameters
wrapFunc<IOperator, IOperator>-
Called once per compile with this sound's running operator; returns the operator that processes it. Create any state inside it so every compile starts fresh. Use Sigil.Sound.Operator.From(Sigil.Sound.BlockRenderer) to write one as a lambda.
descriptionstring?-
Text shown for the result in Sigil.Sound.Sound.ToString. Defaults to a description built from the sound it wraps.
Returns
Examples
Saw(110).Through(input => Operator.From(block =>
{
input.RenderBlock(block);
for (int i = 0; i != block.Length; i++)
block[i] = block[i] * 0.5f;
}))
ToString()
A description of how the sound is built, such as Saw(110).LowPass(800).
public override string ToString()Returns
Vowel(Param?, Param?)
A vowel filter: four formant resonators that make a sound speak. The position morphs through A, E, I, O and U. It is good for talkbox leads, choir-like sweeps and humanizing a synth.
public Sound Vowel(Param? vowel = null, Param? resonance = null)Parameters
vowelParam?-
The position from 0 (A) through 0.25 (E), 0.5 (I), 0.75 (O) to 1 (U), with the vowels blended in between. A number, or a sound or
Fn(t => ...)to talk. Values outside 0 to 1 are clamped. Defaults to 0 (A). resonanceParam?-
How sharp the formants are, from 0 (soft and broad) to 1 (sharp and ringing). Defaults to 0.5.
Returns
Examples
Saw(110).Vowel(vowel: Sine(0.5).Range(0.0, 1.0), resonance: 0.8).Distort(drive: 1.5)
Operators
operator +(Sound, Param)
Adds a constant or a modulating signal to this sound; sound + 12 shifts a control signal up by 12.
public static Sound operator +(Sound a, Param offset)Parameters
Returns
operator +(Param, Sound)
Adds a constant or a modulating signal to this sound.
public static Sound operator +(Param offset, Sound a)Parameters
Returns
operator +(Sound, Sound)
Mixes two sounds by summing them.
public static Sound operator +(Sound a, Sound b)Parameters
Returns
operator *(Sound, Param)
Scales a by a parameter (a constant, a function or another sound).
public static Sound operator *(Sound a, Param gain)Parameters
Returns
operator *(Param, Sound)
Scales a by a parameter (a constant, a function or another sound).
public static Sound operator *(Param gain, Sound a)Parameters
Returns
operator *(Sound, Sound)
Ring modulation: multiplies a by b channel by channel, left by left and right by right.
A mono modulator (an LFO) scales both sides alike; to scale by the mono mix of a stereo sound, use a * (Param)b.
public static Sound operator *(Sound a, Sound b)