Types/Wave/Chirp.ps1

<#
.SYNOPSIS
    Chirp Wave
.DESCRIPTION
    Chirp Wave generator.
    
    Generates a Wave that "chirps" between two frequencies.
    
    This gets softer as it approaches the half, then louder as it approaches the end.
#>

[OutputType([double[]])]
param(
# The frequency
[Alias('Hz')]
[float]$Frequency = 0,

[Alias('Hz2')]
[float]$ToFrequency = 0,

# The amount of time to generate.
[Timespan]$Duration = $(
    if ($this.BPM -is [TimeSpan]) {$this.BPM} 
    else { [TimeSpan]::FromSeconds(60/128) }
),

# The volume
[float]$Volume = 0.5,

# The sample rate.
# Will default to the `.SampleRate` of `$this` wave.
# If there is no `$this` wave, will default to 44100
[uint32]$SampleRate = $(
    if ($this.SampleRate) { $this.SampleRate } else { 44100 }
),


# The bits per sample.
# Will default to the `.BitsPerSample` of `$this` wave.
# If there is no `$this` wave, will default to 4.
[uint16]$BitsPerSample = $(
    if ($this.BitsPerSample) { $this.BitsPerSample } else { 4 }
),

# The channel count.
# Will default to the `.ChannelCount` of `$this` wave.
# If there is no `$this` wave, will default to 1 (mono).
[uint16]$channelCount = $(
    if ($this.ChannelCount) { $this.ChannelCount } else { 1 }
)
)


if ($Frequency -eq 0) {
    $Frequency = 440
}

if ($ToFrequency -eq 0) {
    $ToFrequency = $Frequency / 2
}

$BytesPerSecond = $SampleRate * $channelCount * $BitsPerSample/8

# Cache our references, for the minor speed boost it may give us.
$math = [Math]

# Calculate the number of samples
$numberOfSamples = $math::Round($Duration.TotalSeconds * $BytesPerSecond) 

# Our step size is the bits per sample / 8
$step = $BitsPerSample/8


# The divisor will remain the same, so compute it now.
$divisor = ($sampleRate * $channelCount * $step)

$half = $numberOfSamples / 2

# Generate one sample at a time.
return ,[double[]]@(
    for ($i = 0; $i -lt $numberOfSamples; $i+=$step) {
        
        $t = $i/$numberOfSamples
        # Calculate the envelope
        $envelope = 1.0 - $t

        $currentFrequency = if ($i -lt $half) {
            $Frequency
        } else {
            $ToFrequency
        }
            
        $envelope = if ($i -lt $half) {
            1.0 - ($i / $half)
        } else {
            1.0 - ($i - $half) / ($numberOfSamples - $half)
        }

        # We can imagine each cycle as a series of circles
        # how many circles? Whatever our frequency may be.
        $cycle = 2 * $math::PI * $currentFrequency
        
        # Calculate the angle at this point in time (in radians)
        $angle = ($cycle * $i) / $divisor    

        # The sample at this moment is the sine of that angle
        $sample = $math::Sinh($math::Sin($angle))

        # We will scale this by the volume, and then by the envelope.
        $sample * $Volume * $envelope
    }
)