Types/Wave/Envelope.ps1

<#
.SYNOPSIS
    Envelope Generator
.DESCRIPTION
    An envelope is how a sound changes over time.

    An envelope multiplies the sound over time,
    which changes a sine wave plays.

    Accepts a function
.NOTES
    Most notes are "attenuated",
    which is a fancy way of saying they are softened over time.

    The default of this function, and of most envelopes, is a "hush".

    This gradually decreases the volume over time.
.LINK
    https://en.wikipedia.org/wiki/Envelope_(music)
.EXAMPLE
    # Play A4 with it's natural envelope
    wave note a4 play
.EXAMPLE
    # Play A4 inside of a "hush" envelope
    wave note a4 envelope play
.EXAMPLE
    # Play A4 switching phase every 40 samples
    wave note a4 envelope @(
        @(1) * 20
        @(-1) * 20
    ) play
.EXAMPLE
    # Play A4 switching phase every 200 samples
    wave note a4 envelope @(
        @(1) * 100
        @(-1) * 100
    ) play
.EXAMPLE
    # Play A4 switching phase every 100 and 300 amples
    wave note a4 envelope @(
        @(1) * 100
        @(0) * 100
        @(-1) * 100
    ) play
.EXAMPLE
    # An attack is an increase in magnitude over a short time
    wave note a4 envelope @(
        foreach ($n in 1..50) {
            $n/50
        }
    ) play
.EXAMPLE
    # A decay is a decrease in magnitude over a short time
    wave note a4 envelope @(
        foreach ($n in 1..50) {
            1 - $n/50
        }
    ) play
.EXAMPLE
    # Attack and decay
    wave note a4 envelope @(
        foreach ($n in 1..50) {
            $n/50
        }
        
        foreach ($n in 1..50) {
            1 - $n/50
        }
    ) play
.EXAMPLE
    # An attack, decay, sustain, release (ADSR)
    wave note a envelope @(
        # attack
        foreach ($n in 1..50) {
            $n/50
        }
        # decay
        foreach ($n in 25..1) {
            25 - ($n/25)/2
        }
        # sustain
        foreach ($n in 1..50) {
            0.5
        }
        
        # Release
        foreach ($n in 25..1) {
            0.5 - (1 - $n/25)
        }
    ) play
.EXAMPLE
    # An attack is an increase in magnitude over a short time
    wave note a4 envelope @(
        foreach ($step in 50, 100, 200) {
            foreach ($n in 1..$step) {
                $n/50
            }
            @(0) * $step
        }
        
    ) play
.EXAMPLE
    wave note a4 envelope @(
        foreach ($n in 1..100) {
            $n/100
        }
    ) play
.EXAMPLE
    wave note a4 envelope @(
        foreach ($n in 100..1) {
            $n/100
        }
        foreach ($n in 1..100) {
            $n/100
        }
    ) play
.EXAMPLE
    wave note a4 envelope {
        param([double[]]$samples)
        $half = $samples.Length / 2
        $sampleNumber = 0;
        foreach ($sample in $samples) {
            if ($sampleNumber -lt $half) {
                $sample * (1 - $sampleNumber/$half)
            } else {
                $sample * (2 - $sampleNumber/$half)
            }
            $sampleNumber++
        }
    } play
#>

param($function,
[double[]]$Samples = $($This.Samples)
)

$waveFormat = $this.WaveFormat
if (-not $waveFormat) { $waveFormat = @{} }

$sampleNumber = 0
if (
    (-not $function) -or 
    ($function -in 'hush', 'normal')
) {
    # A Normal hush envelope
    $hush = 1/$samples.Count    
    return wave @waveFormat -Samples $(
        foreach ($sample in $samples) {
            $sample * (1 - ($sampleNumber * $hush))
            $sampleNumber++
        }
    )    
} 
elseif (
    $function -as [double[]]
) {
    # A custom envelope of ratios
    $customEnvelope = $function -as [double[]]
    return wave @waveFormat -Samples $(        
        foreach ($sample in $samples) {
            $sample * ($customEnvelope[$sampleNumber % $customEnvelope.Length])
            $sampleNumber++
        }
    )
}
elseif ($function -is [ScriptBlock]) {
    return wave @waveFormat -Samples $(
        & $function $Samples
    )
}