Python для творческих. Звук на твоей стороне

- -
- 100%
- +
Если у вас есть файлы в других форматах, librosa может не справиться с их загрузкой без дополнительных инструментов. На этот случай есть библиотека pydub, которая умеет работать практически со всеми форматами. Установите её командой pip install pydub, и вы сможете конвертировать что угодно в WAV одной строкой кода. Но для простоты в этой книге мы будем исходить из того, что все исходные файлы уже в формате WAV.
Что мы узнали о звуке
Давайте подведём промежуточный итог. Мы начали с нуля — и теперь у нас есть работающий инструмент для анализа аудиофайлов. Мы знаем, что звук для компьютера — это просто длинный список чисел, измеряющих громкость много тысяч раз в секунду. Мы умеем загружать этот список в Python, измерять его длину, находить пиковую и среднюю громкость, отличать речь от тишины. Мы понимаем, почему формат WAV лучше для работы, а MP3 — для публикации. Мы написали несколько скриптов, каждый из которых делает полезную работу.
Это может показаться скромным началом, но не недооценивайте его. Вы только что освоили фундамент, на котором стоит вся обработка звука. Всё, что мы будем делать дальше — вырезать куски, склеивать, убирать шум, выравнивать громкость, — строится на этом фундаменте. Зная, как устроен звук внутри компьютера, вы сможете управлять им с точностью, недоступной ни одному графическому редактору. В следующих главах мы начнём это управление, и я обещаю: будет интересно.
Творческое задание
Прежде чем перейти к следующей главе, попробуйте применить полученные знания на практике. Проанализируйте несколько своих записей с помощью написанных скриптов. Найдите самую длинную и самую короткую запись. Найдите запись с самым большим процентом тишины. Найдите запись с самой высокой и самой низкой средней громкостью. Сравните цифры. Есть ли закономерности? Может быть, записи, сделанные утром, тише, чем вечерние? Или записи на определённую тему содержат больше пауз? Такое исследование собственного материала — первый шаг к тому, чтобы стать профессионалом. Вы начинаете видеть в звуке не просто шум, а структуру. И это видение останется с вами навсегда.
Также попробуйте записать один и тот же текст в разных условиях: в тихой комнате, при открытом окне, с работающим телевизором на фоне. Прогоните все три записи через скрипт анализа тишины и сравните результаты. Вы увидите, как шум влияет на способность детектора отличать речь от тишины. Это понимание пригодится нам в главе про шумоподавление.
И последнее: не бойтесь экспериментировать с кодом. Поменяйте параметр top_db с 30 на 20, на 40, на 10. Посмотрите, как меняется результат. Добавьте в вывод свои метрики — например, количество найденных речевых фрагментов. Чем больше вы играетесь с кодом, тем быстрее он становится для вас естественным языком, как родная речь. Код — это не магия, это навык. И как любой навык, он развивается практикой.
Чек-лист главы
Вы освоили первую главу, если: знаете, что звук внутри компьютера — это список чисел, измеряющих громкость; понимаете, что такое частота дискретизации и почему она равна 22050 для речи; умеете устанавливать Python и библиотеки через pip; можете загрузить WAV-файл в Python с помощью librosa.load; умеете измерять длительность записи, пиковую и среднюю громкость; можете проиграть аудио из кода через sounddevice; умеете находить участки речи и тишины с помощью librosa.effects.split; понимаете разницу между форматами WAV, MP3, FLAC и знаете, какой для чего использовать; написали и запустили свои первые Python-скрипты для анализа звука. В следующей главе мы перейдём от анализа к действию: научимся резать, склеивать и собирать аудиофайлы, как детали конструктора.
Глава 2. Учимся резать и склеивать звук
О чём эта глава
В предыдущей главе мы смотрели на звук и изучали его свойства. Мы были как учёные, которые исследуют образец под микроскопом. Теперь пришло время стать ремесленниками. Мы возьмём аудиофайлы в руки и начнём с ними работать: отрезать лишнее, соединять нужное, переставлять куски местами. Всё то, что обычно делается мышкой в программах вроде Audacity, мы будем делать кодом. Почему кодом, а не мышкой? Потому что код можно запустить повторно. Потому что код не устаёт и не ошибается от усталости. Потому что код может обработать сто файлов за время, которое вы тратите на ручную обработку одного. В этой главе мы напишем инструменты, которые превратят монтаж аудио из утомительной рутины в быстрое и приятное занятие. Мы научимся вырезать фрагменты из аудио, склеивать несколько файлов в один, добавлять паузы нужной длины, выравнивать громкость разных фрагментов и собирать полноценный эпизод подкаста из заготовок. К концу главы у вас будет скрипт, который делает всю черновую работу по монтажу за вас.
Как резать аудио в коде
Начнём с самого простого действия: отрезать кусок от начала или конца записи. Представьте, что вы записали подкаст. Вы начали запись, проверили микрофон, сказали «раз-раз, меня слышно?», потом выпили воды, прокашлялись и только через три минуты начали говорить по делу. Эти три минуты не должны попасть в финальную версию. Более того, в конце записи вы, скорее всего, скажете что-то вроде «Всё, выключаю запись. Ой, она ещё пишет? Ну всё, пока». Эти последние десять секунд тоже нужно убрать. В графическом редакторе вы бы выделили ненужный фрагмент мышкой и нажали Delete. В коде мы сделаем то же самое, только вместо мышки у нас будут индексы массива.
Вспомним, что наш аудиофайл — это массив чисел y. Каждое число имеет свой порядковый номер, или индекс. Первое число имеет индекс ноль, второе — индекс один, сотое — индекс девяносто девять. В Python мы можем взять любую часть массива, указав начальный и конечный индекс в квадратных скобках через двоеточие. Это называется срез. Если мы хотим убрать первые три минуты, нам нужно выбросить все числа с нулевого индекса до индекса, соответствующего трём минутам. А этот индекс мы вычисляем умножением: три минуты — это сто восемьдесят секунд, умножаем на частоту дискретизации 22050, получаем около четырёх миллионов. Значит, нам нужны все числа начиная с индекса четыре миллиона и до конца массива.
Вот как это выглядит в коде:
python
import librosa
import soundfile as sf
y, sr = librosa.load('long_record.wav')
# Сколько секунд отрезать от начала
cut_seconds = 180
cut_samples = cut_seconds * sr
# Берём всё, начиная с cut_samples и до конца
y_trimmed = y[cut_samples:]
sf.write('record_trimmed.wav', y_trimmed, sr)
print(f"Было: {len(y) / sr:.1f} сек")
print(f"Стало: {len(y_trimmed) / sr:.1f} сек")
Запись y[cut_samples:] — это и есть срез. Квадратные скобки говорят Python, что мы хотим взять часть массива. Число до двоеточия — начальный индекс. Двоеточие без числа после него означает «и до самого конца». Всё, что было до индекса cut_samples, игнорируется. Обратите внимание: мы не удаляем данные из исходного массива y. Мы создаём новый массив y_trimmed, который содержит только нужную нам часть. Исходный массив остаётся нетронутым, и мы всегда можем к нему вернуться.
Теперь обратная задача: обрезать конец. Это делается похоже, только мы указываем конечный индекс, а начальный оставляем пустым:
python
# Сколько секунд отрезать от конца
cut_end_seconds = 10
cut_end_samples = len(y) - (cut_end_seconds * sr)
# Берём всё от начала до cut_end_samples
y_trimmed = y[:cut_end_samples]
Здесь срез y[:cut_end_samples] означает «от начала до индекса cut_end_samples, не включая его». Мы вычисляем этот индекс как общую длину массива минус количество измерений в десяти секундах. Таким образом мы отрезаем ровно последние десять секунд.
Если нужно обрезать и начало, и конец одновременно, оба среза можно объединить в одну строчку:
python
y_trimmed = y[180 * sr : len(y) - 10 * sr]
Читается это так: «Возьми массив y от индекса, соответствующего ста восьмидесяти секундам, до индекса, который находится за десять секунд до конца». Всё просто и понятно.
Склеиваем несколько файлов
С разрезанием разобрались. Теперь обратная операция — склеивание. Предположим, у вас есть три файла: музыкальное вступление, записанный голос и музыкальная концовка. Их нужно объединить в один файл, который пойдёт на публикацию. В видеоредакторе это делается перетаскиванием файлов на дорожку. В коде это делает функция np.concatenate из библиотеки numpy. Она берёт несколько массивов и соединяет их в один длинный массив, как будто склеивает ленту из кусков.
Вот базовый пример:
python
import librosa
import numpy as np
import soundfile as sf
# Загружаем три файла
intro, sr1 = librosa.load('intro.wav')
voice, sr2 = librosa.load('voice.wav')
outro, sr3 = librosa.load('outro.wav')
# Склеиваем
final = np.concatenate([intro, voice, outro])
# Сохраняем
sf.write('final.wav', final, sr1)
Однако здесь есть подводный камень: все три файла должны иметь одинаковую частоту дискретизации. Если вступление записано с частотой 44100, голос — с 22050, а концовка — с 48000, склеенный файл будет звучать неправильно. На стыках скорость воспроизведения будет меняться, и голос может стать то быстрее, то медленнее. Поэтому перед склеиванием нужно убедиться, что все файлы имеют одинаковую частоту, и если нет — привести их к единому стандарту.
Вот улучшенная версия скрипта с проверкой частоты:
python
import librosa
import numpy as np
import soundfile as sf
# Загружаем и проверяем частоту
target_sr = 22050
intro, sr_i = librosa.load('intro.wav', sr=target_sr)
voice, sr_v = librosa.load('voice.wav', sr=target_sr)
outro, sr_o = librosa.load('outro.wav', sr=target_sr)
print(f"Интро: {len(intro) / target_sr:.1f} сек")
print(f"Голос: {len(voice) / target_sr:.1f} сек")
print(f"Аутро: {len(outro) / target_sr:.1f} сек")
# Склеиваем
final = np.concatenate([intro, voice, outro])
print(f"Итого: {len(final) / target_sr:.1f} сек")
sf.write('episode.wav', final, target_sr)
Параметр sr=target_sr в функции librosa.load говорит библиотеке: «Загрузи файл и пересчитай его так, как будто он был записан с частотой target_sr». Если исходный файл был записан с другой частотой, librosa автоматически подгонит его под нужную. Это очень удобно и экономит нам кучу ручной работы.
Добавляем паузы между фрагментами
Когда вы склеиваете вступление и голос вплотную, они могут звучать неестественно. Обычно между музыкальной заставкой и началом речи нужна небольшая пауза — полсекунды или секунда, чтобы слушатель переключился с музыки на голос. В коде пауза — это просто массив из нулей. Ноль в аудио означает абсолютную тишину. Чтобы создать секунду тишины, нам нужно создать массив из нулей длиной в частоту дискретизации. При частоте 22050 это будет 22050 нулей.
python
# Создаём паузу в одну секунду
silence_sec = 1.0
silence = np.zeros(int(target_sr * silence_sec))
# Склеиваем с паузами
final = np.concatenate([intro, silence, voice, silence, outro])
Функция np.zeros(n) создаёт массив из n нулей. Мы передаём ей количество сэмплов, которое равно длительности паузы в секундах, умноженной на частоту дискретизации. Этот массив нулей можно вставлять между любыми фрагментами, и на слух это будет восприниматься как естественная пауза. Длину паузы можно менять по своему вкусу. Для энергичного контента паузы делают короче — четверть секунды или даже меньше. Для спокойного, вдумчивого — длиннее, до полутора секунд.
Выравниваем громкость фрагментов
Ещё одна важная задача при склеивании — выравнивание громкости. Музыкальная заставка часто бывает записана громче, чем голос. Если их склеить без обработки, слушателю придётся убавлять громкость на музыке и прибавлять на голосе. Это раздражает. Профессиональный монтаж подразумевает, что все части звучат на одном уровне.
Выравнивать громкость мы будем через пиковую нормализацию — метод, который мы кратко упоминали в Главе 1, а подробно разберём в Главе 3. Сейчас используем упрощённый вариант:
python
def normalize_volume(y, target_peak=0.9):
"""Подгоняет пиковую громкость к заданному уровню."""
current_peak = np.max(np.abs(y))
if current_peak == 0:
return y
gain = target_peak / current_peak
return y * gain
# Нормализуем все части
intro = normalize_volume(intro, 0.9)
voice = normalize_volume(voice, 0.9)
outro = normalize_volume(outro, 0.9)
После такого выравнивания все три части будут иметь одинаковый пиковый уровень, и слушателю не придётся тянуться к регулятору громкости.
Собираем готовый эпизод подкаста
Теперь давайте соберём всё вместе в один большой скрипт, который принимает набор файлов и выдаёт готовый смонтированный эпизод. Этот скрипт можно будет использовать для каждого выпуска подкаста, меняя только имена файлов.
python
import librosa
import numpy as np
import soundfile as sf
def build_podcast(intro_file, voice_file, outro_file, output_file,
trim_start=180, trim_end=10,
silence_between=1.0,
target_sr=22050,
target_peak=0.9):
"""
Собирает эпизод подкаста из трёх файлов.
trim_start - сколько секунд отрезать от начала голосовой дорожки
trim_end - сколько секунд отрезать от конца голосовой дорожки
silence_between - длина паузы между частями в секундах
"""
print("=== Сборка эпизода ===")
# Загружаем
print("Загружаю файлы...")
intro, _ = librosa.load(intro_file, sr=target_sr)
voice, _ = librosa.load(voice_file, sr=target_sr)
outro, _ = librosa.load(outro_file, sr=target_sr)
# Обрезаем голос
start_sample = int(trim_start * target_sr)
end_sample = len(voice) - int(trim_end * target_sr)
voice = voice[start_sample:end_sample]
print(f"Голос после обрезки: {len(voice) / target_sr:.1f} сек")
# Нормализуем громкость
for arr in [intro, voice, outro]:
peak = np.max(np.abs(arr))
if peak > 0:
arr *= target_peak / peak
# Создаём паузу
silence = np.zeros(int(target_sr * silence_between))
# Склеиваем
final = np.concatenate([intro, silence, voice, silence, outro])
print(f"Финальная длина: {len(final) / target_sr:.1f} сек")
# Сохраняем
sf.write(output_file, final, target_sr)
print(f"Сохранено: {output_file}")
print("Готово!")
# Запускаем
build_podcast(
intro_file='intro.wav',
voice_file='raw_voice.wav',
outro_file='outro.wav',
output_file='episode_ready.wav',
trim_start=180,
trim_end=10,
silence_between=1.0
)
Запустите этот скрипт, указав свои файлы. Если у вас нет готовых джинглов для интро и аутро, не беда — можете пока использовать любые музыкальные фрагменты или даже тишину. Главное — понять процесс. Когда появятся настоящие джинглы, вы просто подставите их в скрипт, и всё будет работать.
Пакетная обработка: монтируем много файлов сразу
Одно из главных преимуществ кода перед ручным монтажом — возможность обрабатывать много файлов одной командой. Предположим, у вас в папке лежит десять записей, и каждую нужно обрезать с начала и с конца, а потом сохранить результат в другую папку. В Audacity это заняло бы час. В Python это делается одним скриптом и занимает минуту.
python
import os
import librosa
import soundfile as sf
input_folder = 'raw_recordings'
output_folder = 'trimmed_recordings'
Конец ознакомительного фрагмента.
Текст предоставлен ООО «Литрес».
Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.
Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.



