Шрифт:
Интервал:
Закладка:
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 - количество аудио кадров в течение видео кадра:
scn = Scene.GetCurrent()
context = scn.getRenderingContext()
seconds = float(n)/fr
sampleratio = fr/float(context.framesPerSec())
Как упомянуто раньше, модуль wave дает нам доступ ко множеству свойств .wav-файла и к сырым (raw) аудио сэмплам, но не предоставляет никаких функций для преобразования этих сырых сэмплов в удобные для использования целые величины. Следовательно, нам нужно сделать это самостоятельно. К счастью, это не так уж трудно, как это может показаться. Поскольку мы знаем, что 16-битовые аудио сэмплы представлены как 2-х байтовое целое в формате "меньший-в-конце" ("little-endian"), мы можем использовать функцию unpack() из модуля Питона struct, чтобы эффективно преобразовывать список байтов в список целых, передавая подходящую спецификацию формата. (Вы можете прочитать больше о .wav-файлах здесь https://ccrma.stanford.edu/courses/422/ проекты/WaveFormat/, на русском здесь: http://www.fpga-cpld.ru/wave.html, работа с модулем struct описана здесь: http://world-python.org/article/tutorialmodules/32-modul-struct.html — прим. пер.)
samples = struct.unpack('<%dh'%n,b)
Теперь мы можем начать анимацию ключей формы. Мы получаем стартовый кадр из контекста рендера и вычисляем конечный кадр, умножая время в секундах в .wav-файле на частоту видеокадров. Заметьте, что он может оказаться дальше или ближе, чем конечный кадр, который мы можем получить из контекста рендера. Последний определяет конечный кадр, который будет отрендерен, когда пользователь нажмёт на кнопку Anim, но мы будем анимировать движение нашего активного объекта независимо от этой величины.
Затем для каждого кадра мы вычисляем от стартового кадра до последнего кадра (исключительно) среднее значение аудио сэмплов, которые попадают на каждый видеокадр суммированием этих аудио сэмплов (находятся в списке samples) и деля на количество этих аудио сэмплов за видеокадр (выделено в следующем куске кода).
Мы задаём выбранный ключ формы в величину в дипазоне [0:1], так что мы должны нормализовать рассчитанные средние числа, определяя минимальную и максимальную величины, и вычислить масштаб:
staframe = context.startFrame()
endframe = int(staframe +
seconds*context.framesPerSec())
popout=[]
for i in range(staframe,endframe):
popout.append(sum(samples[int(
(i-1)*sampleratio):int(i*sampleratio)])/sampleratio)
minvalue = min(popout)
maxvalue = max(popout)
scale = 1.0/(maxvalue-minvalue)
Наконец, мы получаем активный объект в текущей сцене и получаем его IPO Формы (выделено). Мы заканчиваем, устанавливая величину ключа формы для каждого кадра в рассматриваемом нами диапазоне в масштабированное среднее аудио сэмплов:
ob=Blender.Scene.GetCurrent().objects.active
ipo = ob.getData().getKey().getIpo()
for i,frame in enumerate(range(staframe,endframe)):
ipo[shapekey][frame]=(popout[i]-minvalue)*scale
Остальной скрипт теперь довольно прост. Он выбирает активный объект, затем пытается извлечь список имен ключей формы из него (выделено в следующей части). Это действие может потерпеть неудачу (следовательно, применяется try … except), если, например, активный объект - не меш или он не имеет связанных ключей формы, в этом случае мы предупреждаем пользователя с помощью всплывающего сообщения:
if __name__ == "__main__":
ob=Blender.Scene.GetCurrent().objects.active
try:
shapekeys = ob.getData().getKey(
).getIpo().curveConsts
key = popup(('Select a shape key%t|'+
'|').join(shapekeys))
if key>0:
Window.FileSelector (lambda f:sound2active(f,
shapekeys[key-1]),
"Select a .wav file",
Blender.Get('soundsdir'))
except:
popup('Not a mesh or no shapekeys defined%t|Ok')
Если мы смогли извлечь список ключей формы, мы предоставляем пользователю всплывающее меню для выбора из этого списка. Если пользователь выбирает один из пунктов, переменная key будет положительной и мы предоставляем пользователю диалог выбора файлов (выделено). В этот диалог передаётся lambda-функция, которая будет вызвана, если пользователь выберет файл, с передачей имени этого выбранного файла в качестве аргумента. В нашем случае мы создаём эту lambda-функцию, чтобы вызвать функцию sound2active(), определённую ранее с этим именем файла и выбранным ключом формы.
Начальный каталог, который будет представлен пользователю в выборе файлов, определяется последним аргументом в функции FileSelector(). Мы задали его параметром Блендера soundsdir. Это обычно // (то есть, относительный путь, указывающий на тот же каталог, где находится .blend-файл, с которым пользователь работает), но может быть установлен в окне Пользовательских настроек (секция File Paths) на нечто другое.
Анимация меша .wav-файлом: последовательность действийТеперь, когда у нас есть наш скрипт Sounds.py, мы можем применить его следующим образом:
1. Выбрать Меш-объект
2. Добавить ключ формы "Basis" к нему (окно Кнопок, контекст редактирования, панель Shapes). Он будет соответствовать наименее искаженной форме меша.
3. Добавить второй ключ формы и дать ему значимое имя.
4. Отредактировать этот меш, чтобы он представлял наиболее искаженную форму.
5. В режиме объектов, запустить Sound.py из текстового редактора, нажимая Alt + P.
- 33 лучшие программы для ноутбука. Популярный самоучитель - Владимир Пташинский - Программы
- Защита вашего компьютера - Сергей Яремчук - Программы
- Delphi. Трюки и эффекты - Валерий Борисок - Программы
- Google Таблицы. Это просто. Функции и приемы - Евгений Намоконов - Программы
- 200 лучших программ для Linux - Сергей Яремчук - Программы