Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active August 9, 2024 05:24
Show Gist options
  • Save Jaykul/01a0045e1551dffe6437bf63382c9813 to your computer and use it in GitHub Desktop.
Save Jaykul/01a0045e1551dffe6437bf63382c9813 to your computer and use it in GitHub Desktop.
Convert Nokia RTTL to VT256 DEC PS (PlaySound) escape sequences
function Convert-RTTTL {
<#
.SYNOPSIS
Convert Nokia RTTL to VT256 DEC PS (PlaySound) escape sequences for Windows Terminal, for instance.
.EXAMPLE
Convert-RTTTL 'Mario World Theme:d=4,o=5,b=125:a,8f.,16c,16d,16f,16p,f,16d,16c,16p,16f,16p,16f,16p,8c6,8a.,g,16c,a,8f.,16c,16d,16f,16p,f,16d,16c,16p,16f,16p,16a#,16a,16g,2f,16P,8a.,8f.,8c,8a.,f,16g#,16f,16c,16p,8g#.,2g,8a.,8f.,8c,8a.,f,16g#,16f,8c,2c6' -InformationAction Continue
Plays the Mario World Theme song (and show the name in the console).
.EXAMPLE
Convert-RTTTL 'HauntHouse: d=4,o=6,b=108: 2a5, 2e, 2d#, 2b5, 2a5, 2c, 2d, 2a#5, 2e., e, 1f5, 1a5, 1d#, 2e., d, 2c., b5, 1a5, 1p, 2a5, 2e, 2d#, 2b5, 2a5, 2c, 2d, 2a#5, 2e., e, 1f5, 1a5, 1d#, 2e., d, 2c., b5, 1a5' | Set-Clipboard
Converts the HauntHouse song to escape sequences and puts them on your clipboard, instead of playing it.
.LINK
https://en.wikipedia.org/wiki/Ring_Tone_Text_Transfer_Language
Thanks to GraphicHealer for the inspiration!
#>
param(
[Parameter(Mandatory)][string]$String,
[ValidateRange(0, 7)][int]$Volume = 3
)
$Notes = @{
"C5" = 1; "C#5" = 2; "D5" = 3; "D#5" = 4; "E5" = 5; "F5" = 6; "F#5" = 7; "G5" = 8; "G#5" = 9; "A5" = 10; "A#5" = 11; "B5" = 12
"C6" = 13; "C#6" = 14; "D6" = 15; "D#6" = 16; "E6" = 17; "F6" = 18; "F#6" = 19; "G6" = 20; "G#6" = 21; "A6" = 22; "A#6" = 23; "B6" = 24
"C7" = 25
}
# Get Info and Settings for Song
$Title, $Defaults, $Song = $String -split ':'
Write-Information $Title
# D = Duration, B = BPM, O = Octave
$Default = $Defaults -split ',' | ConvertFrom-StringData
# Nokia RTTL specifies the tempo in beats per minute.
# VT520 DEC PS (PlaySound) specifies note duration in 1/32nds of a second. 60 * 32 = 1920
$Adjustment = 32 / (1920 / 120)
$StringBuilder = [System.Text.StringBuilder]::new()
foreach ($Note in ($Song -split ',')) {
if (!($Note.Trim() -match "^(?<duration>\d+)?(?<note>[abcdefgp]#?)(?:(?<dot>\.)?|(?<octave>\d)?)*$")) {
Write-Error "Not a valid note: $Note"
}
$Note = $Matches.Note
$Duration = (32 / ($Matches.Duration ?? $Default.D)) * $Adjustment
if ($Matches.Dot) {
$Duration *= 1.5
}
$Octave = $Matches.Octave ?? $Default.O
if (($Octave -notin 5, 6) -and -Not ($Octave -eq 7 -and $Note -eq "C")) {
Write-Error "Note out of range. Only C5..C7 are supported."
}
if ($Duration -ne $PreviousDuration) {
if ($StringBuilder.Length -gt 0) {
$null = $StringBuilder.Append(",~")
}
$null = $StringBuilder.Append("$([char]27)[$Volume;$Duration")
} elseif(!$PreviousDuration) {
$null = $StringBuilder.Append("$([char]27)[$Volume;$Duration")
}
$null = $StringBuilder.Append(";" + $Notes["$Note$Octave"])
$PreviousDuration = $Duration
}
if ($StringBuilder.Length -gt 0) {
$null = $StringBuilder.Append(",~")
$StringBuilder.ToString()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment