Александр Иванов – Python для творческих. Звук на твоей стороне. (страница 5)
Прежде чем запускать скрипт, нам нужен аудиофайл. Если у вас уже есть запись голоса в формате WAV, скопируйте её в папку audio_book и переименуйте в my_voice.wav. Если нет — не беда. Откройте диктофон на телефоне, наговорите минуту любого текста — например, прочитайте вслух рецепт борща или опишите, что вы видите за окном. Перекиньте файл на компьютер, положите в папку audio_book и назовите my_voice.wav. Формат WAV обязателен: librosa лучше всего работает именно с WAV-файлами. Если ваш файл в другом формате, например MP3 или M4A, вы можете конвертировать его в WAV с помощью бесплатной программы Audacity или онлайн-конвертера. В следующих главах я покажу, как конвертировать форматы прямо в коде, а пока — пусть будет WAV.
Всё готово. В командной строке введите python first_script.py и нажмите Enter. Если всё сделано правильно, вы увидите на экране что-то вроде:
text
Файл загружен!
Частота измерений: 22050 раз в секунду
Длина записи: 58.34 секунд
Всего чисел в файле: 1286397
Поздравляю! Вы только что написали и запустили свою первую программу на Python. Она сделала полезное дело: загрузила аудиофайл и рассказала о нём главное. Теперь давайте разберём, что именно произошло.
Разбор кода: строка за строкой
import librosa — этой строкой мы говорим Python: «Достань с полки инструмент под названием librosa, он нам понадобится». В языке Python все дополнительные возможности хранятся в библиотеках. Чтобы воспользоваться библиотекой, её нужно сначала импортировать — то есть подключить. Это как включить кухонный комбайн в розетку перед использованием. Без этой строчки Python не будет знать, что мы вообще собираемся работать со звуком.
y, sr = librosa.load('my_voice.wav') — это главная строка скрипта. Она делает сразу несколько вещей. Во-первых, она ищет файл с именем my_voice.wav в той же папке, где лежит скрипт. Во-вторых, она считывает его содержимое и превращает в те самые списки чисел, о которых мы говорили. В-третьих, она возвращает нам два значения. Первое значение — это массив чисел y. Каждое число в этом массиве — одно измерение громкости. Второе значение — частота дискретизации sr, то есть сколько раз в секунду делались эти измерения. Мы сохраняем эти два значения в две переменные с короткими именами y и sr, чтобы потом было удобно к ним обращаться. Переменная — это просто коробочка с именем, в которую можно положить значение и потом достать его, когда понадобится.
print("Файл загружен!") — выводит сообщение на экран. Функция print — это способ Python поговорить с нами. Всё, что внутри скобок и кавычек, появится на экране. Это полезно для того, чтобы понимать, что скрипт работает и дошёл до определённой точки.
print(f"Частота измерений: {sr} раз в секунду") — здесь мы используем особый вид строк, который называется f-строка. Буква f перед открывающей кавычкой говорит Python: «Внутри этой строки могут быть фигурные скобки, и всё, что в них, нужно заменить на значения переменных». Python видит фигурные скобки {sr}, смотрит, что в переменной sr лежит число 22050, подставляет это число в строку, и на экране мы видим «Частота измерений: 22050 раз в секунду». Удобно и читаемо.
print(f"Длина записи: {len(y) / sr:.2f} секунд") — здесь мы считаем длительность записи. len(y) — это функция, которая возвращает длину массива y, то есть общее количество чисел в нём. Делим это количество на частоту дискретизации sr и получаем длительность в секундах. Двоеточие и .2f в конце говорят Python: «Покажи результат с двумя знаками после запятой, не нужно больше». Если файл длится почти минуту, мы увидим «58.34 секунд», а не «58.3412345...» с кучей лишних цифр.
print(f"Всего чисел в файле: {len(y)}") — просто показываем общее количество измерений в файле. Для минутной записи это число будет больше миллиона. Представляете, сколько данных? Миллион точек только для того, чтобы описать одну минуту речи. А теперь представьте, что компьютер обрабатывает этот миллион точек за доли секунды. Именно поэтому программирование — это мощный инструмент.
Продолжаем знакомство: измеряем громкость
Теперь, когда мы умеем загружать файл и узнавать его базовые характеристики, давайте научимся измерять громкость. Это пригодится нам во всех следующих главах. Дополните ваш скрипт новыми строками:
python
import numpy as np
max_volume = np.max(np.abs(y))
min_volume = np.min(y)
avg_volume = np.mean(np.abs(y))
print(f"Самая большая громкость: {max_volume:.4f}")
print(f"Самая маленькая громкость: {min_volume:.4f}")
print(f"Средняя громкость: {avg_volume:.4f}")
Сначала мы импортируем ещё одну библиотеку — numpy. Это библиотека для работы с массивами чисел. Она умеет делать вычисления над целыми массивами быстро и удобно. np — это короткое имя, которое мы даём библиотеке при импорте, чтобы не писать numpy каждый раз полностью. Сокращение np — общепринятое, все программисты его используют.
np.max(np.abs(y)) — здесь мы делаем три вещи за один раз. Сначала np.abs(y) берёт весь массив y и превращает все отрицательные числа в положительные, не трогая уже положительные. Минус ноль пять становится плюс ноль пять. Плюс ноль три остаётся плюс ноль три. Это нужно потому, что громкость нас интересует без знака: неважно, вперёд идёт мембрана динамика или назад, она в обоих случаях создаёт звуковое давление. Затем np.max находит самое большое число в получившемся массиве. Это и есть пиковая громкость — самый громкий момент записи.
np.min(y) находит самое маленькое число в оригинальном массиве, без взятия модуля. Обычно это отрицательное число, близкое к минус единице. Интересно посмотреть на него и сравнить с максимумом: если они примерно равны по модулю, значит, запись симметрична, что хорошо. Если максимум сильно отличается от модуля минимума, возможно, с записью что-то не так — например, микрофон был смещён.
np.mean(np.abs(y)) считает среднюю абсолютную громкость. Мы снова берём модуль каждого числа, чтобы отрицательные и положительные не гасили друг друга, и затем вычисляем среднее арифметическое. Это число говорит нам, насколько запись громкая в целом, а не только в пиках. Две записи могут иметь одинаковую пиковую громкость, но совершенно разную среднюю. Первая может быть плотной и насыщенной, вторая — тихой с редкими громкими выкриками. Средняя громкость лучше отражает то, как человек воспринимает громкость на слух.
Слушаем аудио прямо из кода
Смотреть на числа интересно, но звук хочется ещё и слышать. Давайте научимся проигрывать аудиофайл прямо из Python-скрипта. Для этого мы используем библиотеку sounddevice, которую установили ранее. Добавьте в конец скрипта:
python
import sounddevice as sd
print("Сейчас вы услышите запись...")
sd.play(y, sr)
sd.wait()
print("Воспроизведение закончено.")
sd.play(y, sr) говорит компьютеру: «Возьми массив чисел y и отправь его в динамики, воспроизводя с частотой sr измерений в секунду». Компьютер начинает проигрывать звук и в этот же момент продолжает выполнять следующие строчки кода. Поэтому следующая строка критически важна: sd.wait() говорит компьютеру: «Стой здесь и жди, пока воспроизведение не закончится». Если убрать эту строку, скрипт запустит проигрывание и тут же закроется, музыка оборвётся через долю секунды. Мы этого не хотим, поэтому ждём.
Теперь у вас есть полноценный инструмент для первичного анализа аудиофайлов. Вы можете загрузить любой WAV-файл, узнать его длительность, частоту дискретизации, пиковую и среднюю громкость, а затем прослушать его. Сохраните этот скрипт — мы будем использовать его на протяжении всей книги для быстрой проверки результатов.
Находим тишину в записи
Следующий важный навык — умение отличать речь от тишины. В любой записи, сделанной без подготовки, есть паузы: между предложениями, между словами, иногда просто потому что вы задумались или отвлеклись. Слушатель не обязан ждать, пока вы соберётесь с мыслями. Слушатель хочет получать информацию плотно и без проволочек. Поэтому нам нужно уметь находить тишину. Сначала — чтобы измерить её количество, а в следующих главах — чтобы её сократить.
Библиотека librosa содержит удобную функцию для поиска тишины. Она называется split и делает ровно то, что нам нужно: находит в аудио непрерывные участки, где есть значимый звук. Всё, что между этими участками — тишина. Давайте напишем скрипт для анализа тишины:
python
import librosa
import numpy as np
y, sr = librosa.load('my_voice.wav')
intervals = librosa.effects.split(y, top_db=30)
total_speech = 0
for start, end in intervals:
duration = (end - start) / sr
total_speech += duration
total_length = len(y) / sr
silence = total_length - total_speech
print(f"Общая длина записи: {total_length:.2f} секунд")
print(f"Длина речи: {total_speech:.2f} секунд")
print(f"Длина тишины: {silence:.2f} секунд")
print(f"Тишина занимает {silence / total_length * 100:.1f} процентов записи")
Давайте разберём, что здесь происходит. librosa.effects.split(y, top_db=30) — это функция, которая сканирует весь массив y и ищет участки, где громкость достаточна, чтобы считать это речью. Параметр top_db=30 задаёт порог чувствительности. Число 30 означает, что всё, что тише самого громкого места в записи на 30 децибел, считается тишиной. Это довольно чувствительный порог, он хорошо работает для записей в тихой комнате. Если у вас шумно, можно уменьшить число до 20 или даже 15 — тогда детектор станет менее чувствительным и не будет принимать слабый шум за речь.