Toshusai blog

知識の保管庫

Blenderで音に合わせてキーフレームを打つ(Blender API)

Blenderで音に合わせてキーフレームを打つ(Blender API

Pythonで音声ファイル(wav)を読み込んで、それに合わせてBlenderにあるオブジェクトの大きさを変えてアニメーションさせたらきっとかっこいいと思ったので。(あとから知ったのだが、Blenderは音声からキーフレームに変える機能がついているらしい)

Blender APIとは

Blenderの操作をスクリプトから操作することができるもの。Pythonで使うことができる。いつか入門の記事を書きたい。 https://docs.blender.org/api/current/

Pythonからキーフレームを打つ

1フレームに位置のキーフレームを打ちたかったら、下のように書く。

import bpy
obj = bpy.context.object
# set the keyframe at frame 1
obj.location = 3.0, 4.0, 10.0
obj.keyframe_insert(data_path="location", frame=1)

メソッドの説明

keyframe_insert(data_path, index=-1, frame=bpy.context.scene.frame_current, group="") Insert a keyframe on the property given, adding fcurves and animation data when necessary.
Parameters:
data_path (string) – path to the property to key, analogous to the fcurve’s data path.
index (int) – array index of the property to key. Defaults to -1 which will key all indices or a single channel if the property is not an array.
frame (float) – The frame on which the keyframe is inserted, defaulting to the current frame.
group (str) – The name of the group the F-Curve should be added to if it doesn’t exist yet.
options –
Optional flags:
INSERTKEY_NEEDED Only insert keyframes where they’re needed in the relevant F-Curves.
INSERTKEY_VISUAL Insert keyframes based on ‘visual transforms’.
INSERTKEY_XYZ_TO_RGB Color for newly added transformation F-Curves (Location, Rotation, Scale)
and also Color is based on the transform axis.
Returns:
Success of keyframe insertion.
Return type:
boolean

Blenderにオブジェクトを追加する

Blender起動時にあるキューブを使ってもいいが、今回は新しくキューブを作って、それにアニメーションを適用させる。
1行目で生成し、2行目でシーンでアクティブにする。

bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
obj = bpy.context.scene.objects.active

wavから波形データをアニメーションに移す

以前の記事でやった配列を使ってキーフレームを打っていくだけ。取れるデータは大きいので、スケールの倍率は配列の中の最大値で割って正規化する。今回使った音声データは3分ほどあるので、最初の250フレームだけキーを打った。

import wave
import numpy as np
import logging
file = "絶対パス"
wf = wave.open(file, "r")
buf = wf.readframes(wf.getnframes())
data = np.frombuffer(buf, dtype = "int16")
frame = wf.getnframes()
rate = wf.getframerate()
vframe = int(rate / 24)
vdata = []

for i in range(int(frame / vframe)):
    vdata.append(data[i * vframe * 2])
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
obj = bpy.context.scene.objects.active
vdata_max = max(vdata)
for i in range(250):
    obj.keyframe_insert( data_path='scale', frame=i)
    tmp = vdata[i] / vdata_max
    obj.scale = (tmp, tmp, tmp)

実行するとキーフレームが自動的に打たれる。

参考