Как упомянуто в предыдущем разделе, API Блендера не обеспечивает большого количества инструментов для работы со звуковыми файлами, в основном модуль Sound обеспечивает нас способом загрузки и воспроизведения звуковых файлов, но на этом и всё. Нет способа получить доступ к индивидуальным точкам волны, закодированным в файле.
К счастью, в стандартный дистрибутив Питона включен модуль wave, который обеспечивает нас средствами для чтения файлов в обыкновенном формате .wav . Хотя он поддерживает только несжатый формат, этого будет достаточно, так как этот формат является очень распространённым, и большинство инструментов работы со звуком, как например, Audacity, могут преобразовывать в этот формат. С этим модулем мы можем открыть .wav-файл, определить частоту сэмплов и длительность звукового клипа, и получить индивидуальные сэмплы. Как мы увидим в объяснении следующего кода, мы все еще должны преобразовывать эти сэмплы в величины, которые мы можем использовать как значения ключей для наших ключей формы, но тяжелую работу уже сделали для нас.
Схема кода: Sound.py
Вооружившись знаниями о том, как создавать кривые IPO и получать доступ к .wav-файлам, мы можем наметить следующую схему программы:
1. Определить, имеет ли активный объект пригодные заданные формы, и предложить выбрать их них.
2. Позволить пользователю выбрать .wav-файл.
3. Определить количество звуковых сэмплов в секунду в файле (частота дискретизации).
4. Вычислить количество необходимых кадров анимации, основываясь на длительности звукового файла и показателе количества видеокадров в секунду.
5. Затем, для каждого кадра анимации:
◦ Усреднить звуковые сэмплы, проходящие в этом кадре
◦ Установить величину смешивания выбранной кривой IPO этому среднему (нормализованному) числу
Полный код доступен как Sound.py в файле sound000.blend и объясняется следующим образом:
import Blender
from Blender import Scene,Window,Draw
from Blender.Scene import Render
import struct
import wave
Мы начинаем, импортируя необходимые модули, включая модуль Питона wave, чтобы иметь доступ к нашему .wav-файлу и модуль struct , который предоставляет функции для манипулирования двоичными данными, которые мы получим из .wav-файла.
Затем, мы определяем вспомогательную функцию, показывающую всплывающее меню в середине нашего экрана. Она ведёт себя просто подобно стандартной функции PupMenu() из модуля Draw , но устанавливает курсор в позицию середины экрана с помощью функций GetScreenSize() и SetMouseCoords() из модуля Блендера Window :
def popup(msg):
(w,h)=Window.GetScreenSize()
Window.SetMouseCoords(w/2,h/2)
return Draw.PupMenu(msg)
Основная часть работы будет осуществляться функцией sound2active() . Она принимает два аргумента - имя .wav-файла, для использования и имя ключа формы для анимации, основанной на информации из .wav-файла. Сначала, мы пытаемся создавать объект WaveReader , вызывая функцию open() модуля wave (выделено). Если это не удаётся, мы показываем ошибку во всплывающем окне и выходим:
def sound2active(filename,shapekey='Pop out'):
try:
wr = wave.open(filename,'rb')
except wave.Error,e:
return popup(str(e)+'%t|Ok')
Затем мы делаем некоторые разумные проверки: мы сначала проверяем, является ли .wav-файл МОНО файлом. Если Вы хотите использовать стерео файл, преобразуйте его сначала в моно, например с помощью свободного пакета Audacity ( http://audacity.sourceforge.net/). Затем мы проверяем, имеем ли мы дело с несжатым .wav-файлом, поскольку модуль wave не может работать с другими типами. (большинство .wav-файлов являются несжатыми, но если нужно, Audacity также может их преобразовать), и мы проверяем, что сэмплы 16-битовые. Если любая из этих проверок терпит неудачу, мы выводим соответствующее сообщение об ошибке:
c = wr.getnchannels()
if c!=1 : return popup('Only mono files are '+
'supported%t|Ok')
t = wr.getcomptype()
w = wr.getsampwidth()
if t!='NONE' or w!=2 :
return popup('Only 16-bit, uncompresses files '+
'are supported%t|Ok')
Теперь, когда мы можем работать с файлом, мы получаем его частоту дискретизации(frame rate, количество аудио сэмплов в секунду) и общее число байт (как ни странно, используя неуклюже названную функцию getnframes() из модуля wave ). Затем, мы считываем все эти байты и сохраняем их в переменной b .
fr= wr.getframerate()
n = wr.getnframes()
b = wr.readframes(n)
Наша следующая задача в том, чтобы получить контекст рендера у текущей сцены, чтобы извлечь количество видеокадров в секунду. Время в секундах нашей проигрываемой анимации будет определено длиной нашего аудио сэмпла, которое мы можем вычислить, разделив общее число аудио кадров в .wav-файле на количество аудио кадров в секунду (выделено в следующей части кода). Затем мы определяем константу sampleratio - количество аудио кадров в течение видео кадра:
Читать дальше