ConvertTo-Waveform.ps1

function ConvertTo-WaveForm
{
    <#
    .Synopsis
        Converts audio into a waveform
    .Description
        Converts audio into a waveform visualization.
    .Example
        ConvertTo-WaveForm -InputPath $home\Music\ASong.mp3 -OutputPath $home\Videos\ASong.mp4
    .Example
        ConvertTo-WaveForm -InputPath $home\Music\ASong.mp3 -OutputPath $home\Videos\ASongScales.mp4 -Visualizer TonalScale
    .Example
        ConvertTo-WaveForm -InputPath $home\Music\ASong.mp3 -OutputPath $home\Videos\ASongScales.mp4 -Visualizer Volume
    .Link
        Get-Media
    .Link
        Convert-Media
    #>

    [OutputType([IO.FileInfo], [Management.Automation.Job])]
    param(
    # The input path
    [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName)]
    [Alias('Fullname')]
    [string]
    $InputPath,

    # The output path
    [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName)]
    [string]
    $OutputPath,

    # The path to FFMpeg.exe. Download it from http://ffmpeg.org/
    [Parameter(ValueFromPipelineByPropertyName)]
    [string]
    $FFMpegPath,

    # The width of the generated file: by default, 640
    [Uint32]
    $Width = 640,

    # The height of the generated file: by default, 120
    [Uint32]
    $Height = 120,

    # The timespan to start splitting the video
    [Parameter(Position=4, ValueFromPipelineByPropertyName)]
    [Timespan]
    $Start,

    # The time span to end splitting the video
    [Parameter(Position=5, ValueFromPipelineByPropertyName)]
    [Timespan]
    $End,

    # The name of the visualizer
    [ValidateSet('LineWaveform', 'Spectrum', 'TonalScale', 'PhaseMeter', 'VectorScope', 'Volume')]
    [string]
    $Visualizer = 'LineWaveform',

    # Any options for the visualizer
    [Collections.IDictionary]
    [Alias('Options')]
    $Option = @{},

    # The pixel format. By default, yuv420p.
    [string]
    $PixelFormat = "yuv420p",

    # If set, will run this in a background job
    [Switch]
    $AsJob
    )

    process {
        if ($AsJob) {
            return (& $StartRoughDraftJob)
        }
        #region Find FFMpeg
        $ffMpeg = Get-FFMpeg -ffmpegPath $FFMpegPath
        if (-not $ffmpeg) { return }
        #endregion Find FFMpeg

        $ri = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($InputPath)
        $uro = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)

        $startEndParams = @()

        $duration =
            if ($Start -and $end) {
                if ($end -gt $Start) {
                    $end - $Start
                } else {
                    $Start - $end
                }
                $startEndParams += "-ss","$start","-t", "$($duration.TotalSeconds)"
            } else {
                $mi = Get-Media -InputPath $ri
                $mi.Duration
            }

        $Resolution = "${Width}x${Height}"

        $filterParams = @()

        $optionStr = if ($Option) {
            (@(foreach ($_ in $option.GetEnumerator()) {
                "$($_.Key)=$($_.Value)"
            }) -join ':') + ':'
        } else {
            ''
        }

        if ($optionStr -eq ':') {
            $optionStr = ''
        }

        if ($Visualizer -eq 'LineWaveform') {
             $FilterParams +=
                "-filter_complex",
                "[0:a]showwaves=${OptionStr}s=${Resolution}:mode=line,format=$pixelFormat[v]",
                "-map",
                "[v]",
                "-map",
                '0:a',
                '-c:v',
                'libx264',
                '-c:a',
                'copy'

        } elseif ($Visualizer -eq 'Spectrum') {
            $FilterParams +=
                "-filter_complex",
                "[0:a]showspectrum=${OptionStr}s=${Resolution},format=$PixelFormat[v]",
                "-map",
                "[v]",
                "-map",
                '0:a',
                '-c:v',
                'libx264',
                '-c:a',
                'copy'

        } elseif ($Visualizer -eq 'Volume') {
            $FilterParams +=
                "-filter_complex",
                "[0:a]showvolume=${OptionStr},format=$PixelFormat[v]",
                "-map",
                "[v]",
                "-map",
                '0:a',
                '-c:v',
                'libx264',
                '-c:a',
                'copy'

        } elseif ($Visualizer -eq 'PhaseMeter') {
            $FilterParams +=
                "-filter_complex",
                "[0:a]aphasemeter=${optionStr}s=${Resolution}:mpc=cyan,format=$PixelFormat[v]",
                "-map",
                "[v]",
                "-map",
                '0:a',
                '-c:v',
                'libx264',
                '-c:a',
                'copy'

        } elseif ($Visualizer -eq 'VectorScope') {
            $FilterParams +=
                "-filter_complex",
                "[0:a]avectorscope=${optionStr}s=${Resolution},format=yuv420p[v]",
                "-map",
                "[v]",
                "-map",
                '0:a',
                '-c:v',
                'libx264',
                '-c:a',
                'copy'

        } elseif ($Visualizer -eq 'TonalScale') {
            $FilterParams +=
                "-filter_complex",
                "[0:a]showcqt=${optionStr}s=${Resolution},format=yuv420p[v]",
                "-map",
                "[v]",
                "-map",
                '0:a',
                '-c:v',
                'libx264',
                '-c:a',
                'copy'

        }
        $progId = Get-Random

        & $ffmpeg -i $ri @FilterParams @startEndParams $uro -y 2>&1 |
             ForEach-Object {
                if ($_ -like "*time=*" -and $_ -like "*bitrate=*") {
                    $lineChunks = $_.Tostring() -split "[ =]" -ne '' | Where-Object { $_.Trim() }
                    $lineData = New-Object PSObject
                    for ($i =0; $i -lt $lineChunks.Count; $i+=2) {
                        $lineData |Add-Member NoteProperty $lineChunks[$i].TrimEnd("=") $lineChunks[$i + 1] -force
                    }

                    $time = $lineData.Time -as [Timespan]
                    $perc = $time.TotalMilliseconds * 100 / $duration.TotalMilliseconds

                    if ($perc -gt 100) {
                        $perc = 100
                    }

                    Write-Progress "Encoding" "$lineData".TrimStart("@{").TrimEnd("}") -PercentComplete $perc -id $ProgId
                } else {
                    Write-Verbose "$_"
                }

            } -End {
                Write-Progress "Encoding" "Complete"  -id $ProgId -Completed
                Get-Item -LiteralPath $uro
            }

    }
}