Демонстрации Yamaha W7 и автоматическое деление аудиофайла на части

Обнаружил сайт demodb.org, где можно послушать демки со старых синтезаторов, меня интересовала Yamaha W7.

Сайт этот скачать аудиофайлы не даёт: при попытке открыть консоль разработчика в браузере закрывает свою вкладку или переадресует на другой сайт, а если всё-таки через плагин браузера выцепляешь прямой путь к аудиофайлу, то скачивается заглушка, где говорится, что по прямым ссылкам качать нельзя.

Оставался единственный вариант — запись на локальном компьютере. Я взял аудиоредактор Wavosaur, отрегулировал входной сигнал, выключил микрофон веб-камеры, чтобы он не фонил, записал все нужные мне треки, нормализовал всё до 0 дБ. Получилось следующая картина:

Теперь нужно было порезать получившуюся запись на части. Конечно, мне сразу же захотелось это автоматизировать. Очевидно, разделителями являются периоды тишины, и после недолгого поиска я обнаружил, что в великом и могучем ffmpeg обнаружением тишины в аудиосигнале занимается фильтр silencedetect.

Экспериментальным способом выяснилось, что подходящим порогом срабатывания является -50 дБ, и silencedetect выдаёт такое:

[silencedetect @ 0000025b206a7740] silence_start: 0
[silencedetect @ 0000025b206a7740] silence_end: 4.11882 | silence_duration: 4.11882
[silencedetect @ 0000025b206a7740] silence_start: 213.267
[silencedetect @ 0000025b206a7740] silence_end: 218.22 | silence_duration: 4.95304
[silencedetect @ 0000025b206a7740] silence_start: 342.852
[silencedetect @ 0000025b206a7740] silence_end: 354.239 | silence_duration: 11.387
[silencedetect @ 0000025b206a7740] silence_start: 530.661
[silencedetect @ 0000025b206a7740] silence_end: 540.122 | silence_duration: 9.4607
[silencedetect @ 0000025b206a7740] silence_start: 670.007
[silencedetect @ 0000025b206a7740] silence_end: 680.196 | silence_duration: 10.1893
[silencedetect @ 0000025b206a7740] silence_start: 853.138
[silencedetect @ 0000025b206a7740] silence_end: 867.126 | silence_duration: 13.9884

Полезный сигнал начинается с silence_end, а заканчивается на silence_start, поэтому нужно выбросить первый silence_start и последний silence_end, итого 5 треков. Перед началом трека делается отступ в 0,25 сек, а в конце добавляется 1 сек, чтобы треки в списке не игрались attacca и между ними была какая-то пауза. В ffmpeg указывается не конечное время, а длительность нужного куска, поэтому нужно для этого из конечного времени вычесть начальное.

Иногда, например, у альбомов на Youtube, звук начинается сразу и первая метка silence_end оказывается уже на втором треке, поэтому нужно предусмотреть такие ситуации. Здесь я предположил, что если метка позже 30-й секунды, то вставлять в начало списка silence_end ноль и не удалять первый silence_start, т. к. нужно знать, где первая дорожка кончается. Соответственно, команда ffmpeg будет без указания стартовой позиции.

# Исходный файл
$file = 'D:\Музыка\Yamaha W7 demos.wav'
# Отступ до и после полезного сигнала (чтобы треки не начинались сразу один за другим)
$preSec = 0.25
$postSec = 1
# Формат файлов на выходе
$outputExt = ".mp3"

$file = get-item -literalpath $file
$log = (& ffmpeg -i $file.FullName -af silencedetect=n=-50dB:d=1 -f null - 2>&1) -match '^\[silencedetect'

$starts,$ends = $log.where({$_ -match 'silence_end'}, 'Split')
[regex]$replOut =  '.*?: (\d+\.\d+).*'
$starts = $starts -replace $replOut,'$1' |select -SkipLast 1
$ends = $ends -replace $replOut,'$1'
# Если в начале тишины нет (первое начало позднее 30-й секунды)
if ([double]$starts[0] -gt 30) {
    $starts = ,"0" + $starts
}
else {
    $ends = $ends |select -Skip 1
}

$c = 0
$starts |% {
    if ($_ -eq 0) {
        & ffmpeg -y -hide_banner `
        -t ([double]$ends[$c] + $postSec) `
        -i $file.FullName `
        ($file.DirectoryName + "\" + ($c+1).tostring("00") + " $($file.BaseName)" + $outputExt)
    }
    else {
        & ffmpeg -y -hide_banner `
        -ss ([double]$starts[$c] - $preSec) `
        -t ([double]$ends[$c] - [double]$starts[$c] + $postSec) `
        -i $file.FullName `
        ($file.DirectoryName + "\" + ($c+1).tostring("00") + " $($file.BaseName)" + $outputExt)
    }
    $c++
}

Получается просто отлично — больше ничего делать не потребовалось. Чуть позже я нашёл запись этих же демонстраций на Youtube, и скрипт так же хорошо работает и на ней.

Надо сказать, что тех демонстраций, которые я выкладывал, когда рассказывал о своей неудачной карьере аранжировщика, я нигде не нашёл; видимо, они были на какой-то дополнительно приобретаемой дискете и их никто не записал. Зато нашёл другие, которые я тоже вспомнил, и они великолепны, а Isn’t it hip и Halftime просто, я бы сказал, исключительны. Структура композиций, динамика, гармонизация, подголоски, выбор тембров и их обработка — всё на высшем уровне.

Yamaha - Isn’t It Hip (Yamaha W5/W7 demo, 1994)