Skip to content

Instantly share code, notes, and snippets.

@lumiobrie
Last active January 30, 2025 13:10
Show Gist options
  • Save lumiobrie/eef3064b115cdafe11742ad326420ebc to your computer and use it in GitHub Desktop.
Save lumiobrie/eef3064b115cdafe11742ad326420ebc to your computer and use it in GitHub Desktop.
Módulo multipropósito para configuraciones varias en Windows y análisis y tratamiento de archivos de audio
using namespace System.IO;
function Get-GistContent {
param (
[Parameter(Position = 0, Mandatory = $true)]
[string] $gistId
)
# Define the Gist API URL using the provided gistId
$apiUrl = "https://api.github.com/gists/$gistId"
# Make the request to the GitHub API
$response = Invoke-RestMethod -Uri $apiUrl -UseBasicParsing
# Get the first file from the gist (assumes there's only one file
$gistFiles = $response.files
$firstFile = $gistFiles.PSObject.Properties.Name | Select-Object -First 1
# Get the content of the first file
$content = $gistFiles.$firstFile.content
# Return the content
return $content
}
function Import-PowerToolkit {
if (-not (Get-Module -Name PowerToolkit)) {
$psModulePath = "$(($env:PSModulePath -split ([System.IO.Path]::PathSeparator))[0])\PowerToolkit\PowerToolkit.psm1"
New-Item -Path $psModulePath -ItemType File -Force
Get-GistContent "eef3064b115cdafe11742ad326420ebc" | Out-File -FilePath $psModulePath
}
Import-Module PowerToolkit -Force
}
#region Partitions Manager
function Split-VolumeFromC {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0)]
[string]
$DriveLetter,
[Parameter(Mandatory = $false, Position = 1)]
[string]
$DriveLabel = "Data",
[Parameter(Mandatory = $false, Position = 2)]
[double]
$RemainingPercentageOnDriveC = 0.7
)
if (Get-Partition -DiskNumber 0 | Where-Object DriveLetter -eq $DriveLetter) {
Write-Error "There was already a $DriveLetter partition"
return
}
$DriveFormatInfo = Get-Partition -DiskNumber 0 | Where-Object DriveLetter -eq "C" | Select-Object -First 1 | ForEach-Object {
$newVolumeSize = $_.Size - [math]::Round($_.Size * $RemainingPercentageOnDriveC)
[PSCustomObject]@{
CurrentPartitionNumber = $_.PartitionNumber;
NewVolumeLetter = $DriveLetter;
NewVolumeFileSystem = "ntfs";
NewVolumeLabel = "Data";
NewVolumeSize = $newVolumeSize
NewVolumeSizeMB = [math]::floor($newVolumeSize / 1MB)
} }
if ((Get-Volume -DriveLetter "C").SizeRemaining -le $DriveFormatInfo.NewVolumeSize) {
Write-Error "There is not enough size on C drive"
return
}
# Step 2: Create DiskPart Script
$diskpartScriptContent = @"
select disk 0
select partition $($DriveFormatInfo.CurrentPartitionNumber)
shrink desired=$($DriveFormatInfo.NewVolumeSizeMB)
create partition primary size=$($DriveFormatInfo.NewVolumeSizeMB)
format fs=$($DriveFormatInfo.NewVolumeFileSystem) quick label=$($DriveFormatInfo.NewVolumeLabel)
assign letter=$($DriveFormatInfo.NewVolumeLetter)
"@
# Step 3: Save DiskPart Script to a Temporary File
# Save with a `.txt` extension for compatibility
$tempScript = "$env:temp\diskpart_script.txt"
Set-Content -Path $tempScript -Value $diskpartScriptContent -Encoding ASCII
# Step 4: Run DiskPart with the Script
Start-Process -FilePath "diskpart.exe" -ArgumentList "/s $tempScript" -Wait -NoNewWindow
# Cleanup: Remove the temporary file
Remove-Item -Path $tempScript -Force
Get-Volume -DriveLetter $DriveLetter
}
function Merge-VolumeWithC {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0)]
[ValidateSet("D", "E", "F", "G", "H", "I", "J", "K")]
[char]
$DriveLetter
)
$Drive = Get-Partition -DiskNumber 0 | Where-Object DriveLetter -eq $DriveLetter;
if (-not $Drive) {
Write-Error "There is not a $DriveLetter Drive"
return
}
$DriveC = Get-Partition -DiskNumber 0 | Where-Object DriveLetter -eq "C";
# Step 2: Create DiskPart Script
$diskpartScriptContent = @"
select disk 0
select partition $($Drive.PartitionNumber)
delete partition override
select partition $($DriveC.PartitionNumber)
extend
exit
"@
$tempScript = "$env:temp\diskpart_script.txt"
Set-Content -Path $tempScript -Value $diskpartScriptContent -Encoding ASCII
# Step 4: Run DiskPart with the Script
Start-Process -FilePath "diskpart.exe" -ArgumentList "/s $tempScript" -Wait -NoNewWindow
# Cleanup: Remove the temporary file
Remove-Item -Path $tempScript -Force
Get-Volume -DriveLetter C
}
#endregion
#region Special Folders
function Set-SpecialFolders {
param (
[Parameter(Mandatory, Position = 0)]
[ValidateSet("C", "D")]
[string]$TargetVolume
)
if ($TargetVolume.ToUpper() -eq "C") {
$prefix = $env:USERPROFILE
}
else {
$prefix = "D:"
}
# Consolidate paths and icons into $shellFolders
# Dynamically retrieve GUIDs from the User Shell Folders registry key
$regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
$specialFolders = Get-ItemProperty -Path $regPath | Select-Object * -ExcludeProperty PS*
# Helper function to match GUIDs by associated folder names
function Get-GUIDForFolder {
param (
[Parameter(Mandatory)]
[string]$FolderName
)
foreach ($key in $specialFolders.PSObject.Properties) {
# Ensure we're only processing GUID keys (not friendly names like "Downloads")
if ($key.Name -match "^{[A-Fa-f0-9\-]+}$") {
$folderPath = $specialFolders.$($key.Name)
# Match folder name based on its path (e.g., ends with "\Downloads")
if ($folderPath -like "*\$FolderName") {
return $key.Name # Return the GUID
}
}
}
throw "Could not find GUID for $FolderName"
}
# Consolidate paths and icons into $shellFolders
$shellFolders = @{
"Personal" = @{
Path = Join-Path $prefix "Documents"; Icon = "%SystemRoot%\system32\imageres.dll,-112"; GUID = ""
}
"Desktop" = @{
Path = Join-Path $prefix "Desktop"; Icon = "%SystemRoot%\system32\imageres.dll,-183"; GUID = ""
}
"Downloads" = @{
Path = Join-Path $prefix "Downloads"; Icon = "%SystemRoot%\system32\imageres.dll,-184"; GUID = (Get-GUIDForFolder -FolderName "Downloads")
}
"My Pictures" = @{
Path = Join-Path $prefix "Pictures"; Icon = "%SystemRoot%\system32\imageres.dll,-113"; GUID = ""
}
"My Music" = @{
Path = Join-Path $prefix "Music"; Icon = "%SystemRoot%\system32\imageres.dll,-108"; GUID = ""
}
"My Video" = @{
Path = Join-Path $prefix "Videos"; Icon = "%SystemRoot%\system32\imageres.dll,-189"; GUID = ""
}
}
# Registry paths for User Shell Folders and Shell Folders
$regPathUserShellFolders = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
$regPathShellFolders = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
# Process each special folder
foreach ($key in $shellFolders.Keys) {
$newPath = $shellFolders[$key].Path
$iconResource = $shellFolders[$key].Icon
if ($key -eq "Downloads") {
$key = $shellFolders[$key].GUID
}
# Get the current path (oldPath) before modifying the registry
$oldPath = (Get-ItemProperty -Path $regPathUserShellFolders -Name $key).$key
if ($oldPath -eq $newPath) {
continue;
}
# Create the new folder path if it doesn't exist
if (-not (Test-Path $newPath)) {
Write-Host "Creating folder: $newPath"
New-Item -ItemType Directory -Path $newPath -Force | Out-Null
}
# Update registry for both User Shell Folders and Shell Folders
Set-ItemProperty -Path $regPathUserShellFolders -Name $key -Value $newPath
Set-ItemProperty -Path $regPathShellFolders -Name $key -Value $newPath
Write-Host "Configured '$key' to '$newPath'"
# Move files from the old location to the new location
if (Test-Path $oldPath) {
Write-Host "Moving files from '$oldPath' to '$newPath'..."
Move-Item -Path "$oldPath\*" -Destination $newPath -Force -ErrorAction SilentlyContinue
# Remove the old folder if it's empty
if ((Get-ChildItem -Path $oldPath -Recurse -ErrorAction SilentlyContinue | Measure-Object).Count -eq 0) {
Write-Host "Deleting empty folder: $oldPath"
Remove-Item -Path $oldPath -Recurse -Force
}
else {
Write-Host "Skipped deleting $oldPath because it contains remaining files or folders."
}
}
# Create or update the desktop.ini file for special icons
$desktopIniPath = Join-Path $newPath "desktop.ini"
$desktopIniContent = @"
[.ShellClassInfo]
IconResource=$iconResource
"@
Set-Content -Path $desktopIniPath -Value $desktopIniContent -Force
# Set desktop.ini as hidden and system
attrib +h +s $desktopIniPath
# Set folder as System and ReadOnly
attrib +s +r $newPath
Write-Host "Special icon configured for '$key' in '$newPath'"
}
# Refresh Explorer to apply changes
Stop-Process -Name explorer -Force
Start-Process explorer
}
function Get-SpecialFolders {
# Create the Shell.Application object
$shell = New-Object -ComObject Shell.Application
# List of common special folders with their Shell folder names
$specialFolders = @(
'shell:Desktop',
'shell:Personal',
'shell:Downloads',
'shell:My Music',
'shell:My Pictures',
'shell:My Video'
)
# Loop through and retrieve each special folder's path
foreach ($folder in $specialFolders) {
$path = $shell.Namespace($folder).Self.Path
Write-Output "${folder}: $path"
}
}
function Get-SpecialFolder {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0)]
[ValidateSet("Desktop", "Documents", "Downloads", "Music", "Pictures", "Videos")]
[String]
$FolderName
)
# Create the Shell.Application object
$shell = New-Object -ComObject Shell.Application
$folderName = $FolderName.ToUpper();
# List of common special folders with their Shell folder names
$specialFolders = @{
Desktop = 'shell:Desktop'
Documents = 'shell:Personal'
Downloads = 'shell:Downloads'
Music = 'shell:My Music'
Pictures = 'shell:My Pictures'
Videos = 'shell:My Video'
}
try {
$path = $shell.Namespace($specialFolders[$folderName]).Self.Path
if (-not $path) {
throw "Could not resolve the path for folder: $FolderName"
}
}
catch {
Write-Error $_.Exception.Message
return $null
}
Write-Output $path
}
#endregion
#region Windows Configuration
function New-InitialAdminSetup {
if (-not (Get-Partition -DiskNumber 0 | Where-Object DriveLetter -eq "D")) {
Split-VolumeFromC "D"
}
Set-SpecialFolders "D"
Remove-Telemetry
Get-Fonts
Set-HiddenItemsVisibility
}
function Get-Fonts {
# Step 1: Get the latest version tag from the GitHub API
$apiUrl = "https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest"
$response = Invoke-RestMethod -Uri $apiUrl -UseBasicParsing
# Extract the tag name (the version number, e.g., v3.2.1)
$latestVersion = $response.tag_name
Write-Host "The latest version of nerd-fonts is: $latestVersion"
# Step 2: Define the URL for the CascadiaCode.zip based on the latest version
$zipUrl = "https://github.com/ryanoasis/nerd-fonts/releases/download/$latestVersion/CascadiaCode.zip"
# Step 3: Define where to download the file (in this example, the Downloads folder)
$downloadPath = "$(Get-SpecialFolder Downloads)\CascadiaCode.zip"
# Step 4: Download the zip file
Invoke-WebRequest -Uri $zipUrl -OutFile $downloadPath
Write-Host "Downloaded CascadiaCode.zip to $downloadPath"
# Step 5: Unzip the file to a destination folder (e.g., "UnzippedCascadiaCode" in Downloads)
$destinationPath = "$(Get-SpecialFolder Downloads)\UnzippedCascadiaCode"
# Cargar el ensamblado necesario en PowerShell 5
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($downloadPath, $destinationPath)
Write-Host "Extracted CascadiaCode.zip to $destinationPath"
# Step 6: Define the system fonts directory (or user fonts directory)
$fontsDirectory = "$env:WINDIR\Fonts"
# Alternatively, to install fonts for the current user only (without admin privileges), use:
# $fontsDirectory = "$env:USERPROFILE\AppData\Local\Microsoft\Windows\Fonts"
# Step 7: Find all font files (.ttf or .otf) in the unzipped folder
$fontFiles = Get-ChildItem -Path $destinationPath -Include *.ttf, *.otf -Recurse
# Step 8: Copy the font files to the system fonts directory (requires admin privileges)
foreach ($fontFile in $fontFiles) {
$destinationFontPath = Join-Path $fontsDirectory $fontFile.Name
Copy-Item -Path $fontFile.FullName -Destination $destinationFontPath -Force
Write-Host "Installed $($fontFile.Name) to $fontsDirectory"
}
# Step 9: Register the fonts in the Windows registry
foreach ($fontFile in $fontFiles) {
$fontName = [System.IO.Path]::GetFileNameWithoutExtension($fontFile.Name)
$fontRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
New-ItemProperty -Path $fontRegistryPath -Name "$fontName (TrueType)" -Value $fontFile.Name -PropertyType String -Force
Write-Host "Registered $($fontFile.Name) in the registry."
}
Write-Host "All fonts have been installed successfully!"
}
function Update-ProfileDependencies {
Install-Module -Name Terminal-Icons -Repository PSGallery -Scope CurrentUser
winget upgrade --id JanDeDobbeleer.OhMyPosh -s winget --accept-source-agreements --accept-package-agreements
if ($LASTEXITCODE -ne 0) {
Write-Host "Oh My Posh is not installed. Installing it now..."
winget install JanDeDobbeleer.OhMyPosh -s winget --accept-source-agreements --accept-package-agreements
}
else {
Write-Host "Oh My Posh is already up to date."
}
}
<#
Para que el PROFILE funcione, ejecutar primero los siguientes dos comandos comentados:
Install-Module -Name Terminal-Icons -Repository PSGallery
winget install JanDeDobbeleer.OhMyPosh -s winget
#>
function Set-PSProfile {
$ohMyPoshConfigGistId = "1f69b28bfcc4f7716e49eb5bb34d7b2c"
$ohMyPoshConfigurationPath = "$env:POSH_THEMES_PATH\ohmyposhv3-v2.json"
Get-GistContent $ohMyPoshConfigGistId | Out-File -FilePath $ohMyPoshConfigurationPath
$powerShellProfileGistId = "25f5550ad186189e0e68916c6d7f44c3"
New-Item -Path $PROFILE -ItemType File -Force
(Get-GistContent $powerShellProfileGistId) `
-replace 'Import-Module z', '#Import-Module z' `
-replace 'c:\\Users\\scottha\\OneDrive\\poshv4.json', '$env:POSH_THEMES_PATH/ohmyposhv3-v2.json' | `
Out-File -FilePath $PROFILE
. $PROFILE
}
function Set-HiddenItemsVisibility {
#region Registry Helpers
function Set-RegistryValue {
param (
[string]$RegistryPath,
[string]$PropertyName,
[string]$PropertyType = "DWORD",
[int]$PropertyValue = 0
)
try {
# Verificar si la ruta existe, si no, crearla
if (-not (Test-Path $RegistryPath)) {
New-Item -Path $RegistryPath -Force
}
# Crear o actualizar el valor del registro
New-ItemProperty -Path $RegistryPath -Name $PropertyName -Value $PropertyValue -PropertyType $PropertyType -Force
# Confirmar que el valor se ha creado o actualizado correctamente
$result = Get-ItemProperty -Path $RegistryPath -Name $PropertyName
Write-Host "Clave creada/actualizada: $($result.$PropertyName) en $RegistryPath"
}
catch {
Write-Host "Error al crear/actualizar la clave o valor: $_" -ForegroundColor Red
}
}
# Función para crear una clave vacía en el registro
function Set-EmptyRegistryKey {
param (
[string]$RegistryPath,
[string]$SubKeyName
)
try {
# Verificar si la ruta de la clave padre existe, si no, crearla
if (-not (Test-Path $RegistryPath)) {
New-Item -Path $RegistryPath -Force
}
# Verificar si la subclave existe, si no, crearla
$subKeyPath = "$RegistryPath\$SubKeyName"
if (-not (Test-Path $subKeyPath)) {
New-Item -Path $subKeyPath -Force
}
# Vaciar el valor predeterminado de la subclave
Set-ItemProperty -Path $subKeyPath -Name "(default)" -Value ""
Write-Host "Subclave creada y valor predeterminado vaciado en: $subKeyPath"
}
catch {
Write-Host "Error al crear la subclave o vaciar el valor predeterminado: $_" -ForegroundColor Red
}
}
#endregion
# 1. Desactivar la telemetría
Set-RegistryValue -RegistryPath "HKLM:\Software\Policies\Microsoft\Windows\DataCollection" -PropertyName "AllowTelemetry" -PropertyValue 0
# 2. Desactivar sugerencias de la barra de búsqueda
Set-RegistryValue -RegistryPath "HKCU:\Software\Policies\Microsoft\Windows\Explorer" -PropertyName "DisableSearchBoxSuggestions" -PropertyValue 1
# 3. Crear clave CLSID y vaciar el valor predeterminado en InprocServer32
Set-EmptyRegistryKey -RegistryPath "HKCU:\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" -SubKeyName "InprocServer32"
# 4. Mostrar extensiones de archivos conocidos
Set-RegistryValue -RegistryPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -PropertyName "HideFileExt" -PropertyValue 0
# 5. Mostrar archivos y carpetas ocultas
Set-RegistryValue -RegistryPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -PropertyName "Hidden" -PropertyValue 1
# 6. Mostrar archivos protegidos del sistema
# Set-RegistryValue -RegistryPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -PropertyName "ShowSuperHidden" -PropertyValue 1
# Reiniciar el proceso explorer.exe para aplicar los cambios
Stop-Process -Name explorer -Force
Start-Process explorer
}
function Remove-Telemetry {
# Define the URL for the hosts file to be downloaded
$downloadUrl = "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/hosts/native.winoffice.txt"
# Define the path to the system hosts file
$hostsFilePath = "C:\Windows\System32\drivers\etc\hosts"
# Download the content from the URL
$hostsContent = Invoke-WebRequest -Uri $downloadUrl -UseBasicParsing
# Check if the content was successfully downloaded
if ($hostsContent.StatusCode -eq 200) {
Write-Host "Downloaded new hosts content successfully."
# Overwrite the system hosts file with the downloaded content
$hostsContent.Content | Set-Content -Path $hostsFilePath -Force -Encoding UTF8
Write-Host "Hosts file has been replaced successfully."
}
else {
Write-Host "Failed to download the content. Status code: $($hostsContent.StatusCode)"
}
}
#endregion
#region Audio tools
<#
.SYNOPSIS
Converts audio files of a specified format to WAV format.
.DESCRIPTION
The ConvertTo-Wav function searches for audio files in the specified format within a directory (non-recursively)
and converts each file to WAV format. You can specify the directory to search and an optional output directory.
.PARAMETER Format
Specifies the format of the input audio files (e.g., "mp3", "ogg", "opus").
.PARAMETER Path
Specifies the path to the directory containing the input files. Defaults to the current directory.
.PARAMETER OutputDirectory
Specifies the directory to save the converted WAV files. Defaults to a folder named "Output" in the current directory.
.EXAMPLE
ConvertTo-Wav -Format "mp3" -Path "C:\path\to\source" -OutputDirectory "C:\path\to\output"
Description:
Converts all MP3 files in the specified source directory to WAV format and saves them in the specified output directory.
.EXAMPLE
ConvertTo-Wav -Format "ogg"
Description:
Converts all OGG files in the current directory to WAV format, saving the converted files in a folder named "Output" in the current directory.
.NOTES
Requires FFmpeg to be installed and accessible in the system PATH.
#>
function ConvertTo-Wav {
[CmdletBinding()] # Enables support for -Verbose and advanced function features
param (
[Parameter(
Mandatory = $true,
Position = 0,
HelpMessage = "Specify the input audio format (e.g., 'opus', 'mp3', 'ogg')"
)]
[ValidateSet("opus", "mp3", "ogg")]
[string]$Format,
[Parameter(
Position = 1,
Mandatory = $false,
HelpMessage = "Specify the directory path with the input files."
)]
[string]$Path = (Get-Location).Path,
#Specify an optional output directory
[Parameter(
Position = 2,
Mandatory = $false,
HelpMessage = "Specify the directory to save converted WAV files."
)]
[string]$OutputDirectory = "./Output"
)
# Resolve absolute paths for input and output directories
$resolvedPath = Resolve-Path -Path $Path
# Validate that the input directory exists
if (!(Test-Path -Path $resolvedPath -PathType Container)) {
Write-Error "The directory $resolvedPath doesn't exist."
return
}
# Check if FFmpeg is installed and available
if (!(Get-Command ffmpeg -ErrorAction SilentlyContinue)) {
Write-Error "FFmpeg is not installed or is not in your system PATH. Please install it to use this function."
return
}
# Process each file of the specified format
$files = Get-ChildItem -Path $resolvedPath -Filter *.$Format -File -ErrorAction Stop
if ($files.Count -eq 0) {
Write-Output "No *.$Format files found in $resolvedPath."
return
}
# Create the output directory if it doesn't exist
$resolvedOutputDir = Resolve-Path -Path $OutputDirectory -ErrorAction SilentlyContinue
if (-not $resolvedOutputDir) {
$resolvedOutputDir = New-Item -Path $OutputDirectory -ItemType Directory -Force -Verbose
}
foreach ($file in $files) {
$inputFile = $file.FullName;
$outputFile = Join-Path -Path $resolvedOutputDir -ChildPath $([Path]::ChangeExtension($file.Name, ".wav"))
Write-Verbose "Converting: $inputFile to $outputFile"
ffmpeg -i $inputFile -ac 2 -ar 48000 -acodec pcm_s16le $outputFile
# Check if the output file was created successfully
if (Test-Path -Path $outputFile) {
Write-Host "Converted: $($file.Name) to $([Path]::GetFileName($outputFile))"
}
else {
Write-Warning "Conversion failed for: $inputFile"
}
}
}
function ConvertTo-WavWithNoiseGate {
[CmdletBinding()] # Enables support for -Verbose and advanced function features
param (
[Parameter(
Mandatory = $true,
Position = 0,
HelpMessage = "Specify the input audio format (e.g., 'opus', 'mp3', 'ogg')"
)]
[ValidateSet("opus", "mp3", "ogg")]
[string]$Format,
[Parameter(
Position = 1,
Mandatory = $false,
HelpMessage = "Specify the directory path with the input files."
)]
[string]$Path = (Get-Location).Path,
#Specify an optional output directory
[Parameter(
Position = 2,
Mandatory = $false,
HelpMessage = "Specify the directory to save converted WAV files."
)]
[string]$OutputDirectory = "./Output"
)
# Resolve absolute paths for input and output directories
$resolvedPath = Resolve-Path -Path $Path
# Validate that the input directory exists
if (!(Test-Path -Path $resolvedPath -PathType Container)) {
Write-Error "The directory $resolvedPath doesn't exist."
return
}
# Check if FFmpeg is installed and available
if (!(Get-Command ffmpeg -ErrorAction SilentlyContinue)) {
Write-Error "FFmpeg is not installed or is not in your system PATH. Please install it to use this function."
return
}
# Process each file of the specified format
$files = Get-ChildItem -Path $resolvedPath -Filter *.$Format -File -ErrorAction Stop
if ($files.Count -eq 0) {
Write-Output "No *.$Format files found in $resolvedPath."
return
}
# Create the output directory if it doesn't exist
$resolvedOutputDir = Resolve-Path -Path $OutputDirectory -ErrorAction SilentlyContinue
if (-not $resolvedOutputDir) {
$resolvedOutputDir = New-Item -Path $OutputDirectory -ItemType Directory -Force -Verbose
}
foreach ($file in $files) {
$inputFile = $file.FullName;
$outputFile = Join-Path -Path $resolvedOutputDir -ChildPath $([Path]::ChangeExtension($file.Name, ".wav"))
Write-Verbose "Converting: $inputFile to $outputFile"
ffmpeg -i $inputFile -ac 2 -ar 48000 -acodec pcm_s16le $outputFile
# Check if the output file was created successfully
if (Test-Path -Path $outputFile) {
$astatsData = & ffprobe -f lavfi -i "amovie='$($outputFile -replace "\\", "/" -replace ":", "\:")',astats=metadata=1:reset=0" -show_entries frame_tags="lavfi.astats.Overall.Peak_level,lavfi.astats.Overall.RMS_level" -of csv 2>&1
$noiseFloorMatch = [regex]::Matches($astatsData, "Noise floor dB:\s*(-?\d+\.\d+)")[2]
$noiseFloor = [math]::Round([float]$noiseFloorMatch.Groups[1].Value, 2)
Write-Host "Noise floor: $noiseFloor"
$threshold = "$($noiseFloor + 5) dB"
$ratio = "4"
$attack = "5" # Attack in milliseconds
$release = "250"
ffmpeg -i $outputFile -af "agate=threshold=${threshold}:ratio=${ratio}:attack=${attack}:release=${release}" "./NoiseGate/$([Path]::ChangeExtension($file.Name, ".wav"))"
Write-Host "Converted: $($file.Name) to $([Path]::GetFileName($outputFile))"
}
else {
Write-Warning "Conversion failed for: $inputFile"
}
}
}
function Get-AudioAnalysis() {
[CmdletBinding()]
param (
[Parameter(
Mandatory = $true,
ValueFromPipeline,
Position = 0,
HelpMessage = "Specify the input audio file"
)]
[string]$InputFile,
[Switch]$Parallel, # Add a -Parallel switch
[int]$ThrottleLimit = 5 # Optional: Control max parallel threads
)
process {
if ($Parallel) {
$InputFile | ForEach-Object -Parallel {
$LocalInputFile = $using:InputFile
if (!(Test-Path $LocalInputFile)) {
Write-Error "Error: Input file [$LocalInputFile] does not exist."
return
}
$FormattedFilePath = $LocalInputFile -replace "\\", "/" -replace ":", "\:"
#lavfi.astats.Overall.Min_level_dB,
# Run FFprobe with astats filter to get Peak and RMS levels
$astatsData = & ffprobe -f lavfi -i "amovie='$FormattedFilePath', astats=metadata=1:reset=0" -show_entries frame_tags="lavfi.astats.Overall.Peak_level, lavfi.astats.Overall.RMS_level" -of csv 2>&1
# Regex patterns for Peak level dB and RMS level dB in Overall section
$peakLevelMatch = [regex]::Matches($astatsData, "Peak level dB:\s*(-?\d+\.\d+)")[2]
$rmsLevelMatch = [regex]::Matches($astatsData, "RMS level dB:\s*(-?\d+\.\d+)")[2]
# $noiseFloorMatch = [regex]::Matches($astatsData, "Noise floor dB:\s*( - ?\d+\.\d+)")[2]
$noiseFloorMatch = [regex]::Matches($astatsData, "Noise floor dB:\s*(-(?:inf|\d+\.\d+))")[2]
# Extract the values
if ($peakLevelMatch.Success -and $rmsLevelMatch.Success) {
$peakLevel = [math]::Round([float]$peakLevelMatch.Groups[1].Value, 2)
$rmsLevel = [math]::Round([float]$rmsLevelMatch.Groups[1].Value, 2)
if ($noiseFloorMatch.Groups[1].Value -eq "-inf") {
$noiseFloor = [float]-999
}
else {
$noiseFloor = [math]::Round([float]$noiseFloorMatch.Groups[1].Value, 2)
}
}
else {
Write-Host "Failed to extract the Peak Level and/or RMS Level from the Overall section."
}
# Extract Peak Level and RMS Level
# Run FFmpeg with loudnorm filter to get Loudness Range (LRA)
$loudnormOutput = & ffmpeg -i $LocalInputFile -af "loudnorm=print_format=json" -f null - 2>&1
# Extract JSON portion from the output
$jsonData = ($loudnormOutput | Select-String -Pattern '^\s*{|\}$|^\s*".*"\s*:' -Raw) -join "`n" | ConvertFrom-Json
$lra = $jsonData.input_lra
$truePeakLevel = $jsonData.input_tp
$integratedLoudness = $jsonData.input_i
# Calculate Dynamic Range
$dynamicRange = [math]::Abs($peakLevel - $rmsLevel)
$result = [PSCustomObject]@{
FileName = [System.IO.Path]::GetFileName($LocalInputFile)
NoiseFloorDB = $noiseFloor
PeakLevelDB = $peakLevel
TruePeakLevelDBTP = $truePeakLevel
RMSLevelDB = $rmsLevel
DynamicRangeDB = $dynamicRange
LoudnesRangeLUFS = $lra
IntegratedLoudnessLUFS = $integratedLoudness
}
Write-Output $result
} -ThrottleLimit $ThrottleLimit
}
else {
# Check if the input file exists
if (!(Test-Path $InputFile)) {
Write-Error "Error: Input file [$InputFile] does not exist."
return
}
$FormattedFilePath = $InputFile -replace "\\", "/" -replace ":", "\:"
#lavfi.astats.Overall.Min_level_dB,
# Run FFprobe with astats filter to get Peak and RMS levels
$astatsData = & ffprobe -f lavfi -i "amovie='$FormattedFilePath', astats=metadata=1:reset=0" -show_entries frame_tags="lavfi.astats.Overall.Peak_level, lavfi.astats.Overall.RMS_level" -of csv 2>&1
# Regex patterns for Peak level dB and RMS level dB in Overall section
$peakLevelMatch = [regex]::Matches($astatsData, "Peak level dB:\s*(-?\d+\.\d+)")[2]
$rmsLevelMatch = [regex]::Matches($astatsData, "RMS level dB:\s*(-?\d+\.\d+)")[2]
# $noiseFloorMatch = [regex]::Matches($astatsData, "Noise floor dB:\s*( - ?\d+\.\d+)")[2]
$noiseFloorMatch = [regex]::Matches($astatsData, "Noise floor dB:\s*(-(?:inf|\d+\.\d+))")[2]
# Extract the values
if ($peakLevelMatch.Success -and $rmsLevelMatch.Success) {
$peakLevel = [math]::Round([float]$peakLevelMatch.Groups[1].Value, 2)
$rmsLevel = [math]::Round([float]$rmsLevelMatch.Groups[1].Value, 2)
if ($noiseFloorMatch.Groups[1].Value -eq "-inf") {
$noiseFloor = [float]-999
}
else {
$noiseFloor = [math]::Round([float]$noiseFloorMatch.Groups[1].Value, 2)
}
# Output the results
Write-Host "Extracted Peak Level dB: $peakLevel"
Write-Host "Extracted RMS Level dB: $rmsLevel"
Write-Host "Extracted Noise Floor dB: $noiseFloor"
}
else {
Write-Host "Failed to extract the Peak Level and/or RMS Level from the Overall section."
}
# Extract Peak Level and RMS Level
# Run FFmpeg with loudnorm filter to get Loudness Range (LRA)
$loudnormOutput = & ffmpeg -i $InputFile -af "loudnorm=print_format=json" -f null - 2>&1
# Extract JSON portion from the output
$jsonData = ($loudnormOutput | Select-String -Pattern '^\s*{|\}$|^\s*".*"\s*:' -Raw) -join "`n" | ConvertFrom-Json
$lra = $jsonData.input_lra
$truePeakLevel = $jsonData.input_tp
$integratedLoudness = $jsonData.input_i
# Calculate Dynamic Range
$dynamicRange = [math]::Abs($peakLevel - $rmsLevel)
# Output results
Write-Host "Analysis of: $InputFile"
Write-Host "----------------------------------"
Write-Host "Noise Floor: $noiseFloor dB"
Write-Host "Peak Level: $peakLevel dB"
Write-Host "True Peak Level: $truePeakLevel dBTP"
Write-Host "RMS Level: $rmsLevel dB"
Write-Host "Dynamic Range (DR): $dynamicRange dB"
Write-Host "Loudness Range (LRA): $lra LUFS"
Write-Host "Integrated Loudness: $integratedLoudness LUFS"
[PSCustomObject]@{
FileName = [Path]::GetFileName($InputFile)
NoiseFloorDB = $noiseFloor
PeakLevelDB = $peakLevel
TruePeakLevelDBTP = $truePeakLevel
RMSLevelDB = $rmsLevel
DynamicRangeDB = $dynamicRange
LoudnesRangeLUFS = $lra
IntegratedLoudnessLUFS = $integratedLoudness
}
}
}
}
function Export-ToExcel {
param (
[Parameter(Mandatory, ValueFromPipeline)]
[PSObject[]]$Data, # Array of PSCustomObjects
[Parameter()]
[string]$FilePath = "./ExportedData.csv" # Default CSV path
)
# Ensure the input data is not null or empty
if (-not $Data -or $Data.Count -eq 0) {
Write-Host "Error: No data to export." -ForegroundColor Red
return
}
try {
# Export data to CSV
$Data | Export-Csv -Path $FilePath -NoTypeInformation -Force
Write-Host "Data successfully exported to $FilePath." -ForegroundColor Green
# Open the CSV in Excel
Start-Process -FilePath "excel.exe" -ArgumentList $FilePath
Write-Host "CSV opened in Excel." -ForegroundColor Green
}
catch {
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
}
}
function ConvertTo-Wav48 {
[CmdletBinding()] # Enables support for -Verbose and advanced function features
param (
[Parameter(
Mandatory = $true,
Position = 0,
HelpMessage = "Specify the input audio format (e.g., 'wav', 'flac')"
)]
[ValidateSet("wav", "flac")]
[string]$Format,
[Parameter(
Position = 1,
Mandatory = $false,
HelpMessage = "Specify the directory path with the input files."
)]
[string]$Path = (Get-Location).Path,
#Specify an optional output directory
[Parameter(
Position = 2,
Mandatory = $false,
HelpMessage = "Specify the directory to save converted WAV files."
)]
[string]$OutputDirectory = "./Output"
)
# Resolve absolute paths for input and output directories
$resolvedPath = Resolve-Path -Path $Path
# Validate that the input directory exists
if (!(Test-Path -Path $resolvedPath -PathType Container)) {
Write-Error "The directory $resolvedPath doesn't exist."
return
}
# Check if FFmpeg is installed and available
if (!(Get-Command ffmpeg -ErrorAction SilentlyContinue)) {
Write-Error "FFmpeg is not installed or is not in your system PATH. Please install it to use this function."
return
}
# Process each file of the specified format
$files = Get-ChildItem -Path $resolvedPath -Filter *.$Format -File -ErrorAction Stop
if ($files.Count -eq 0) {
Write-Output "No *.$Format files found in $resolvedPath."
return
}
# Create the output directory if it doesn't exist
$resolvedOutputDir = Resolve-Path -Path $OutputDirectory -ErrorAction SilentlyContinue
if (-not $resolvedOutputDir) {
$resolvedOutputDir = New-Item -Path $OutputDirectory -ItemType Directory -Force -Verbose
}
foreach ($file in $files) {
$inputFile = $file.FullName;
$outputFile = Join-Path -Path $resolvedOutputDir -ChildPath $([Path]::ChangeExtension($file.Name, ".wav"))
Write-Verbose "Converting: $inputFile to $outputFile"
ffmpeg -i $inputFile -ar 48000 $outputFile
# Check if the output file was created successfully
if (Test-Path -Path $outputFile) {
Write-Host "Converted: $($file.Name) to $([Path]::GetFileName($outputFile))"
}
else {
Write-Warning "Conversion failed for: $inputFile"
}
}
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment