Skip to content

Instantly share code, notes, and snippets.

@StartAutomating
Created September 11, 2026 03:09
Show Gist options
  • Select an option

  • Save StartAutomating/acc1c29821474b85d488ffd639083bec to your computer and use it in GitHub Desktop.

Select an option

Save StartAutomating/acc1c29821474b85d488ffd639083bec to your computer and use it in GitHub Desktop.
Gist make me a .wav
<#
.SYNOPSIS
`.wav` stream
.DESCRIPTION
Creates a `.wav` stream.
.LINK
https://en.wikipedia.org/wiki/WAV#WAV_file_header
#>
param(
# The number of channels
[ushort]
$ChannelCount = 2,
# The bits per sample
[ushort]
$BitsPerSample = 8,
# The sample rate, in hertz
[int]
$Rate = 44100,
# The Frequency. If no byte array is provided, this will generate a tone at this frequency.
[float]
$Frequency = 440,
# The time of the audio. If no byte array is provided, this will generate a tone of this duration.
[TimeSpan]
$Time = [timespan]::FromSeconds(60/128),
# The volume. If no byte array is provided, this will generate a tone of this volume.
[float]
$Volume = 0.5,
# The sample data.
[byte[]]
$PCM
)
if (-not $PCM.Length) {
filter tone(
[Timespan]$Time = '00:00:00.05',
[float]$Hz = 440,
[float]$Vol = $Volume,
[short]$BitsPerSample = $BitsPerSample,
[int]$ChannelCount = $ChannelCount,
[int]$Rate = 44100 # Default sample rate for audio
) {
# Calculate the number of samples
$math = [Math]
$n = $math::Round($Time.TotalSeconds * $Rate)
$half = $math::Round($math::Pow(2, $BitsPerSample)/2)
$max = $half * .9
for ($i = 0; $i -lt $n; $i++) {
# Calculate the envelope
$env = 1.0 - ($i / $n)
# Calculate the sample value
$angle = 2 * $math::PI * $Hz * $i / $Rate
$s = $math::Sign($math::Sin($angle)) * $Volume * $env
# Convert to byte and store in the array
foreach ($channel in 1..$ChannelCount) {
if ($BitsPerSample -eq 8) {
[byte]($math::Round($half + $s * $max))
} elseif ($BitsPerSample -eq 16) {
[BitConverter]::GetBytes(
[ushort]($math::Round($half + $s * $max))
)
} elseif ($BitsPerSample -eq 32) {
[BitConverter]::GetBytes(
[uint32]($math::Round($half + $s * $max))
)
}
}
}
}
$PCM = tone -ChannelCount $ChannelCount -Vol $Volume -Time $Time -Hz $Frequency -Rate $Rate
}
# Ported from an AI generated example, please report any issues.
$memoryStream = [IO.MemoryStream]::new()
$ascii = [Text.Encoding]::ASCII
$binaryWriter = [IO.BinaryWriter]::new($memoryStream)
# `.wav` Files must start with RIFF
$binaryWriter.Write($ascii.GetBytes("RIFF"))
# Then they are the total header size minus 8 bytes (44 - 8)
$binaryWriter.Write(36 + $PCM.Length)
$binaryWriter.Write($ascii.GetBytes("WAVEfmt "))
# Chunk size minus 8 bytes, which is 16 bytes here
$binaryWriter.Write(16)
# Audio format (1: PCM integer, 3: IEEE 754 float)
$binaryWriter.Write([short]1)
# Number of channels (only mono for now)
$binaryWriter.Write($ChannelCount)
# Sample rate (in hertz)
$binaryWriter.Write($Rate)
# Calculate number of bytes per block
[ushort]$BytesPerBlock = $ChannelCount * $BitsPerSample/8
# Number of bytes to read per second (Frequency * BytePerBloc).
$binaryWriter.Write($Rate * $BytesPerBlock)
# Number of bytes per block (NbrChannels * BitsPerSample / 8)
$binaryWriter.Write($BytesPerBlock)
# Number of bits per sample
$binaryWriter.Write($BitsPerSample)
# The data block
$binaryWriter.Write($ascii.GetBytes("data"))
# The length of the byte array
$binaryWriter.Write($PCM.Length)
# and the data chunk.
$binaryWriter.Write($PCM)
# Seek the stream back to 0.
$memoryStream.Position = 0
# And decorate the memory stream as a `wav`
$memoryStream.pstypenames.add('wav')
$memoryStream |
Add-Member NoteProperty BitsPerSample $BitsPerSample -Force -PassThru |
Add-Member NoteProperty Rate $Rate -Force -PassThru |
Add-Member NoteProperty ChannelCount $ChannelCount -Force -PassThru |
Add-Member NoteProperty BytesPerBlock $BytesPerBlock -Force -PassThru |
Add-Member ScriptMethod Save -Value {
param([string]$Path)
if (-not $path) {
$path = [IO.Path]::GetTempPath(), (
'' + [DateTimeOffset]::Now.Ticks + '.wav'
) -join '/'
}
$unresolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
$file = if (-not [IO.File]::Exists($unresolvedPath)) {
New-Item -ItemType File -Path $unresolvedPath -Force
} else {
[IO.FileInfo]$unresolvedPath
}
[IO.File]::WriteAllBytes($File.FullName, $this.ToArray())
return [IO.FileInfo]$file.FullName
} -Force -PassThru |
Add-Member ScriptMethod Play -Value {
if ($IsMacOS -or $IsLinux) {
speaker-test --wavfile $this.Save().FullName
} else {
if (-not ('Media.SoundPlayer' -as [type])) {
Add-Type -AssemblyName System.Windows.Extensions
}
$soundPlayer = [Media.SoundPlayer]::new($this)
$soundPlayer.Play()
$this.Position = 0
}
} -Force -PassThru
# Then return
return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment