Пример пакетного анализа аудиофайлов с использованием сценария PowerShell

В прошлой статье я рассмотрел задачу и привёл сценарий PowerShell для определения начальной и конечной тишины в аудиофайле. Сценарий работает для одного файла, но как быть, если требуется автоматизировать анализ нескольких файлов? Например, всех файлов, хранящихся в директории.

Самый простой и правильный вариант решения задачи - не изменять исходный сценарий, а использовать возможности PowerShell для перебора файлов. Сценарий можно запускать для каждого найденного аудиофайла по очереди, передавая его полное имя в параметр -InputFile. Таким образом, для пакетного анализа используется конвейерная обработка данных, встроенная в PowerShell.

Предположим, в директории D:\tmp-audio\ находятся следующие аудиофайлы:

audio-01.mp3
audio-02.mp3
audio-03.mp3
audio-04.mp3
audio-05.mp3
audio-06.m4a
audio-07.ogg
audio-08.wma
audio-09.wma
audio-10.m4a

Если код сценария сохранён в файле с именем Get-AudioContentBounds.ps1, выполнить его для всех файлов в директории можно следующим образом:

Get-ChildItem -Path 'D:\tmp-audio\' -File | ForEach-Object {
    .\Get-AudioContentBounds.ps1 -InputFile $_.FullName
}

Если в директории окажутся файлы, которые не могут быть проанализированы утилитами FFmpeg, сценарий завершится ошибкой. Для анализа файлов одного типа можно использоваться параметр -Filter командлета Get-ChildItem. Но этот пример слишком простой, поэтому предлагаю усложнить задачу и выполнить сценарий только для mp3 и m4a файлов:

$files = Get-ChildItem -Path 'D:\tmp-audio\' -File | Where-Object {
    $_.Extension -in '.mp3', '.m4a'
}
$files | ForEach-Object {
    .\Get-AudioContentBounds.ps1 -InputFile $_.FullName
}

В результате выполнения этого кода в консоль будут выведены данные анализа всех найденных файлов. Однако такой вывод не слишком информативен: в нём нет сопоставления результата анализа с именем файла.

Это легко исправить. Вместо непосредственного вывода результата работы сценария можно создать новый объект, добавив к данным полное имя исходного файла:

$files | ForEach-Object {
    $result = .\Get-AudioContentBounds.ps1 -InputFile $_.FullName
    [PSCustomObject]@{
        'File' = $_.FullName
        'Duration' = $result.Duration
        'Start Time' = $result.'Start Time'
        'Start Silence Duration' = $result.'Start Silence Duration'
        'End Time' = $result.'End Time'
        'End Silence Duration' = $result.'End Silence Duration'
    }
}

Теперь всегда понятно, к какому файлу относится конкретный результат.

Чтобы каждый раз не выполнять анализ файлов, а если файлов много, то это затратно по времени, можно сохранить результат работы сценария для последующей работы с ним. Например, в файл в формате CSV.

Для этого достаточно передать результат выполнения конвейера командлету Export-Csv:

$files | ForEach-Object {
    $result = .\Get-AudioContentBounds.ps1 -InputFile $_.FullName
    [PSCustomObject]@{
        'File' = $_.FullName
        'Duration' = $result.Duration
        'Start Time' = $result.'Start Time'
        'Start Silence Duration' = $result.'Start Silence Duration'
        'End Time' = $result.'End Time'
        'End Silence Duration' = $result.'End Silence Duration'
    }
} | Export-Csv -Path 'D:\tmp-audio\AudioContentBounds.csv' -NoTypeInformation -Encoding UTF8

В этих примерах хорошо раскрываются удобство и возможности PowerShell для конвейерной обработки данных. Поэтому для пакетного анализа необязательно усложнять логику исходного сценария - достаточно использовать его как отдельный этап конвейера, а обработку множества файлов и сохранение результатов поручить самому PowerShell.