CG制作もChatGPTでワンパン~BlenderとChatGPTを活用して簡単にオーディオビジュアル(オーディオスペクトラム)を作成しよう!~

チュートリアル

はじめに

BlenderChatGPT の組み合わせて、あっと驚く簡単さでオーディオビジュアルを作る方法を学びましょう!初心者の方でも分かりやすく解説していきますので、ぜひ最後までご覧ください。

解説動画

@maki.sunwood.ai.labs

Blender と ChatGPT の組み合わせて、あっと驚く簡単さでオーディオビジュアル(オーディオスペクトラム)を作る方法を学びましょう!初心者の方でも分かりやすく解説していきますので、ぜひ最後までご覧ください。 #ChatGPT #オーディオスペクトラム #オーディオビジュアル #blender #blender3d #3dcg #cg

♬ オリジナル楽曲 Maki@sunwood.ai.labs

Blenderランチャーを用意しよう

file

まず最初に、Blenderを使い始めるための「Blenderランチャー」を準備する必要があります。これはBlenderのさまざまなバージョンを一括で管理できる便利なツールです。

  • なぜBlenderランチャーが必要なのか?
    Blenderは、作成者やプロジェクトによって使用するバージョンが異なることがよくあります。加えて、バージョンによっては機能に違いが存在するため、Blenderランチャーを利用することで、これらのバージョンを簡単に管理できます。
  • Blenderランチャーの入手方法
    Blenderランチャーの導入方法やダウンロードリンクについては、別のブログ記事で詳しく解説しています。リンクから詳細を確認して、ダウンロードしてください。
Releases · DotBow/Blender-Launcher
Standalone client for managing official builds of Blender 3D - DotBow/Blender-Launcher

Blenderの起動と初期設定

Blenderランチャーを起動したら、今回のチュートリアルでは「3.3.7」のバージョンを使用します。

  1. Blenderを起動すると、デフォルトでキューブが表示されていますが、これを削除します。
  2. 画面上にはカメラのみが残っている状態になります。これがBlenderの基本的な起動画面です。

コードを使用してオーディオビジュアルを作成

このステップでは、ChatGPTを利用したコードをBlenderに適用し、オーディオビジュアルを作成します。

  1. 用意したコードをBlender内にコピーします。
  2. Blenderの上部にある「スクリプティング」タブを選択します。
  3. コードをペーストし、再生マークをクリックすることで、オーディオビジュアルが生成されます。

コード


import bpy
import numpy as np
import librosa
from itertools import product
import subprocess

def convert_mp4_to_mp3(mp4_filepath, mp3_filepath):
    """Convert MP4 to MP3 using ffmpeg."""
    command = [
        'ffmpeg',
        '-i', mp4_filepath,
        '-q:a', '0',
        '-map', 'a',
        mp3_filepath
    ]
    subprocess.run(command)

def random_pastel_color():
    """Generate a random pastel color."""
    return (np.random.uniform(0.6, 1.0), np.random.uniform(0.6, 1.0), np.random.uniform(0.6, 1.0))

def gradient_to_blue(base_color, index, total_bars):
    """Calculate a gradient from the base color to blue."""
    blue_intensity = index / total_bars
    return (base_color[0] * (1 - blue_intensity), base_color[1] * (1 - blue_intensity), 1)

def delete_all_except_cameras():
    """Delete all objects in the scene except for cameras."""
    for obj in bpy.data.objects:
        if obj.type != 'CAMERA':
            obj.select_set(True)
        else:
            obj.select_set(False)
    bpy.ops.object.delete()

def create_spectrum_bars(num_bars, bar_width, spacing):
    """Create bars for the audio spectrum visualization."""
    base_color = random_pastel_color()
    for i in range(num_bars):
        x = i * spacing
        bpy.ops.mesh.primitive_cube_add(size=bar_width, location=(x, 0, 0.5 * bar_width))
        create_glowing_material(i, num_bars, base_color)

def create_glowing_material(index, total_bars, base_color):
    """Create a glowing material for a bar."""
    mat = bpy.data.materials.new(name=f"Material_{index}")
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    emission = nodes.new(type='ShaderNodeEmission')
    gradient_color = gradient_to_blue(base_color, index, total_bars)
    emission.inputs["Color"].default_value = (gradient_color[0], gradient_color[1], gradient_color[2], 1)
    emission.inputs["Strength"].default_value = 5.0
    material_output = nodes.get("Material Output")
    mat.node_tree.links.new(emission.outputs["Emission"], material_output.inputs["Surface"])
    bpy.context.active_object.data.materials.append(mat)

def scale_bar(bar_data):
    """Calculate the scaling factor for a bar based on audio data."""
    scale_value = (bar_data + 80) / 80
    amplification_factor = 10
    return 1 + scale_value * amplification_factor

def animate_spectrum_bars(y, sr, S_dB, frame_rate, num_bars, bar_width):
    """
    オーディオスペクトルデータに基づいてバーをアニメーション化します。

    パラメータ:
    - y: オーディオの波形データ。
    - sr: サンプリングレート。
    - S_dB: デシベルスケールのメルスペクトログラム。
    - frame_rate: アニメーションのフレームレート。
    - num_bars: ビジュアライゼーションのバーの数。
    - bar_width: 各バーの幅。
    """

    duration = len(y) / sr  # 音楽の長さ(秒)
    print(f"duration:{duration}")
    total_frames = int(duration * frame_rate)  # 総フレーム数

    # カメラを除外したオブジェクトのリストを作成
    non_camera_objects = [obj for obj in bpy.data.objects if obj.type != 'CAMERA']

    for frame in range(total_frames):    
        for j, obj in enumerate(non_camera_objects):

            # 現在のバーとフレームのオーディオデータを取得します。
            bar_data = S_dB[int((j/len(bpy.data.objects)*S_dB.shape[0])), int(S_dB.shape[1]*(frame/total_frames))]

            # オーディオデータに基づいてバーのスケーリング係数を計算します。
            scale_factor = scale_bar(bar_data)

            # バーのスケールを設定します。これにより、バーの高さが決まります。
            obj.scale[2] = scale_factor

            # バーの位置を設定します。これにより、バーのベースが常に地面に残るようになります。
            obj.location[2] = scale_factor * bar_width * 0.5

            # 現在のフレームでバーのスケールのキーフレームを挿入します。
            obj.keyframe_insert(data_path="scale", index=2, frame=frame)

            # 現在のフレームでバーの位置のキーフレームを挿入します。
            obj.keyframe_insert(data_path="location", index=2, frame=frame)

def set_world_background_to_black():
    """Set the world background color to black."""
    bpy.context.scene.world.node_tree.nodes["Background"].inputs[0].default_value = (0, 0, 0, 1)

def set_rendering_end_frame(total_frames):
    """
    レンダリングの最終フレームをアニメーションの最終フレームに設定します。

    パラメータ:
    - total_frames: アニメーションの総フレーム数。
    """

    bpy.context.scene.frame_end = total_frames

def main():
    # Load the audio file and compute the mel spectrogram

    y, sr = librosa.load(r"E:\Prj\AudioVisual\chirp (24).mp3", sr=None)
    S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=64, fmax=8000)
    S_dB = librosa.power_to_db(S, ref=np.max)
    frame_rate = 30

    # Prepare the Blender scene
    delete_all_except_cameras()

    # Create the spectrum bars
    create_spectrum_bars(15, 0.8, 1.2)

    # Animate the bars based on the audio data
    animate_spectrum_bars(y, sr, S_dB, 30, 15, 0.8)

    # Set the world background color
    set_world_background_to_black()

    # animate_spectrum_bars関数の後に以下のコードを追加してレンダリング設定を変更します
    duration = len(y) / sr  # 音楽の長さ(秒)
    total_frames = int(duration * frame_rate)  # 総フレーム数
    print(f"total_frames:{total_frames}")
    set_rendering_end_frame(total_frames)

if __name__ == "__main__":
    main()

アニメーションの確認

  1. アニメーションタブを開きます。
  2. 下部に表示されているアニメーションバーから、スペースボタンを押してアニメーションを開始します。

このアニメーションは、実際の音楽の発音に合わせて動きます。また、色などのデザインも自由に変更できるので、好みに合わせてカスタマイズしてみましょう!---

以上が、BlenderとChatGPTを活用して簡単にオーディオビジュアルを作成する方法です。次回はさらに詳しいテクニックを学んでいきますので、お楽しみに!

コードの解説

概要

このコードは、BlenderのPython APIを使用してオーディオビジュアライザを作成するものです。オーディオデータからスペクトル情報を取得し、それに基づいてバーのアニメーションを生成します。

必要なモジュールのインポート


import bpy
import numpy as np
import librosa
from itertools import product
import subprocess
  • bpy: BlenderのPython APIにアクセスするためのモジュール。
  • numpy: 数値計算ライブラリ。
  • librosa: オーディオ解析ライブラリ。
  • subprocess: 外部プロセスを呼び出すためのモジュール。---

MP4からMP3への変換関数


def convert_mp4_to_mp3(mp4_filepath, mp3_filepath):
    ...

この関数は、ffmpegを使ってMP4ファイルをMP3に変換するためのものです。しかし、このスクリプト内でこの関数は使用されていません。---

色関連のヘルパー関数


def random_pastel_color():
    ...
def gradient_to_blue(base_color, index, total_bars):
    ...

これらの関数は、ビジュアライザのバーの色を計算するためのものです。---

シーンのクリーンアップ関数


def delete_all_except_cameras():
    ...

カメラを除くすべてのオブジェクトをシーンから削除する関数です。---

スペクトルバーの作成関数


def create_spectrum_bars(num_bars, bar_width, spacing):
    ...
def create_glowing_material(index, total_bars, base_color):
    ...

これらの関数は、オーディオビジュアライザのバーを作成し、それらのバーに輝くマテリアルを適用するためのものです。---

バーのアニメーション関数


def scale_bar(bar_data):
    ...
def animate_spectrum_bars(y, sr, S_dB, frame_rate, num_bars, bar_width):
    ...

これらの関数は、オーディオデータに基づいてバーをアニメーション化するためのものです。---

その他のヘルパー関数


def set_world_background_to_black():
    ...
def set_rendering_end_frame(total_frames):
    ...

これらの関数は、Blenderのシーンやレンダリングの設定を行うためのものです。---

メイン関数


def main():
    ...

この関数は、上記で定義したすべての関数を組み合わせて、オーディオビジュアライザを作成するためのものです。---

スクリプトの実行


if __name__ == "__main__":
    main()

この部分は、スクリプトが直接実行された場合(Blender内でこのスクリプトを実行した場合など)、main()関数を呼び出すためのものです。---

これが、このスクリプトのStep-by-Step解説です。このスクリプトをBlender内で実行すると、オーディオファイルからビジュアルスペクトルアニメーションが生成されます。

関数内の解説

convert_mp4_to_mp3


def convert_mp4_to_mp3(mp4_filepath, mp3_filepath):
    """Convert MP4 to MP3 using ffmpeg."""
    command = [
        'ffmpeg',
        '-i', mp4_filepath,
        '-q:a', '0',
        '-map', 'a',
        mp3_filepath
    ]
    subprocess.run(command)

この関数は、ffmpegを使用してMP4ファイルからMP3ファイルへの変換を行います。

  • mp4_filepath: 変換元のMP4ファイルのパス
  • mp3_filepath: 変換後のMP3ファイルの保存先のパス

subprocess.run()で、外部のffmpegコマンドを実行しています。

random_pastel_color


def random_pastel_color():
    """Generate a random pastel color."""
    return (np.random.uniform(0.6, 1.0), np.random.uniform(0.6, 1.0), np.random.uniform(0.6, 1.0))

この関数は、RGBの各値を0.6から1.0の間でランダムに選ぶことで、パステルカラーを生成します。

gradient_to_blue


def gradient_to_blue(base_color, index, total_bars):
    """Calculate a gradient from the base color to blue."""
    blue_intensity = index / total_bars
    return (base_color[0] * (1 - blue_intensity), base_color[1] * (1 - blue_intensity), 1)

基本の色から青へのグラデーションを計算する関数です。バーの数に応じて、それぞれのバーの色を計算します。

delete_all_except_cameras


def delete_all_except_cameras():
    """Delete all objects in the scene except for cameras."""
    for obj in bpy.data.objects:
        if obj.type != 'CAMERA':
            obj.select_set(True)
        else:
            obj.select_set(False)
    bpy.ops.object.delete()

Blenderのシーン内にあるカメラを除くすべてのオブジェクトを選択して削除する関数です。

create_spectrum_bars


def create_spectrum_bars(num_bars, bar_width, spacing):
    """Create bars for the audio spectrum visualization."""
    base_color = random_pastel_color()
    for i in range(num_bars):
        x = i * spacing
        bpy.ops.mesh.primitive_cube_add(size=bar_width, location=(x, 0, 0.5 * bar_width))
        create_glowing_material(i, num_bars, base_color)

オーディオスペクトルのバーを作成する関数です。指定された数、幅、間隔でバーを配置し、各バーに光るマテリアルを適用します。

create_glowing_material


def create_glowing_material(index, total_bars, base_color):
    """Create a glowing material for a bar."""
    mat = bpy.data.materials.new(name=f"Material_{index}")
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    emission = nodes.new(type='ShaderNodeEmission')
    gradient_color = gradient_to_blue(base_color, index, total_bars)
    emission.inputs["Color"].default_value = (gradient_color[0], gradient_color[1], gradient_color[2], 1)
    emission.inputs["Strength"].default_value = 5.0
    material_output = nodes.get("Material Output")
    mat.node_tree.links.new(emission.outputs["Emission"], material_output.inputs["Surface"])
    bpy.context.active_object.data.materials.append(mat)

バーに光る効果を追加するマテリアルを作成する関数です。

scale_bar


def scale_bar(bar_data):
    """Calculate the scaling factor for a bar based on audio data."""
    scale_value = (bar_data + 80) / 80
    amplification_factor = 10
    return 1 + scale_value * amplification_factor

音声データに基づいて、バーのスケーリング係数を計算します。

animate_spectrum_bars


def animate_spectrum_bars(y, sr, S_dB, frame_rate, num_bars, bar_width):
    """
    オーディオスペクトルデータに基づいてバーをアニメーション化します。

    パラメータ:
    - y: オーディオの波形データ。
    - sr: サンプリングレート。
    - S_dB: デシベルスケールのメルスペクトログラム。
    - frame_rate: アニメーションのフレームレート。
    - num_bars: ビジュアライゼーションのバーの数。
    - bar_width: 各バーの幅。
    """

    duration = len(y) / sr  # 音楽の長さ(秒)
    print(f"duration:{duration}")
    total_frames = int(duration * frame_rate)  # 総フレーム数

    # カメラを除外したオブジェクトのリストを作成
    non_camera_objects = [obj for obj in bpy.data.objects if obj.type != 'CAMERA']

    for frame in range(total_frames):    
        for j, obj in enumerate(non_camera_objects):

            # 現在のバーとフレームのオーディオデータを取得します。
            bar_data = S_dB[int((j/len(bpy.data.objects)*S_dB.shape[0])), int(S_dB.shape[1]*(frame/total_frames))]

            # オーディオデータに基づいてバーのスケーリング係数を計算します。
            scale_factor = scale_bar(bar_data)

            # バーのスケールを設定します。これにより、バーの高さが決まります。
            obj.scale[2] = scale_factor

            # バーの位置を設定します。これにより、バーのベースが常に地面に残るようになります。
            obj.location[2] = scale_factor * bar_width * 0.5

            # 現在のフレームでバーのスケールのキーフレームを挿入します。
            obj.keyframe_insert(data_path="scale", index=2, frame=frame)

            # 現在のフレームでバーの位置のキーフレームを挿入します。
            obj.keyframe_insert(data_path="location", index=2, frame=frame)

def animate_spectrum_bars(y, sr, S_dB, frame_rate, num_bars, bar_width):

この関数animate_spectrum_barsは、音楽のスペクトルデータに基づいて、3Dのバーをアニメーション化するためのものです。


duration = len(y) / sr  # 音楽の長さ(秒)
print(f"duration:{duration}")

上の部分は、波形データyの長さをサンプリングレートsrで割ることで、音楽の総再生時間(秒)を計算しています。


total_frames = int(duration * frame_rate)  # 総フレーム数

ここでは、計算した音楽の総再生時間とフレームレートframe_rateを乗算して、アニメーションでの総フレーム数を計算しています。


non_camera_objects = [obj for obj in bpy.data.objects if obj.type != 'CAMERA']

この行では、Blenderのシーン内の全オブジェクトから、カメラではないものをフィルタリングしてリスト化しています。これは、アニメーションを作成するためのバーのリストとして使用されます。


for frame in range(total_frames):

このループは、アニメーションの各フレームに対して処理を行うためのものです。


for j, obj in enumerate(non_camera_objects):

この内部のループは、各フレームでの全てのバーオブジェクトに対して処理を行うためのものです。


bar_data = S_dB[int((j/len(bpy.data.objects)*S_dB.shape[0])), int(S_dB.shape[1]*(frame/total_frames))]

ここでは、現在のバーオブジェクトとフレームに関連するスペクトルデータを取得しています。


scale_factor = scale_bar(bar_data)

取得したスペクトルデータを基に、バーの高さを計算するためのスケーリング係数を求めています。


obj.scale[2] = scale_factor

この行で、計算したスケーリング係数を用いて、バーオブジェクトの高さ(z軸のスケール)を設定しています。


obj.location[2] = scale_factor * bar_width * 0.5

バーオブジェクトの位置(z軸の位置)を、バーの高さが変わったときに地面に接触するように調整しています。


obj.keyframe_insert(data_path="scale", index=2, frame=frame)

現在のフレームでのバーオブジェクトのスケールをキーフレームとして保存しています。これにより、アニメーションが正しく再生されるようになります。


obj.keyframe_insert(data_path="location", index=2, frame=frame)

同様に、現在のフレームでのバーオブジェクトの位置もキーフレームとして保存しています。

オーディオデータを元にしてバーをアニメーション化する関数です。

set_world_background_to_black


def set_world_background_to_black():
    """Set the world background color to black."""
    bpy.context.scene.world.node_tree.nodes["Background"].inputs[0].default_value = (0, 0, 0, 1)

ワールドの背景色を黒に設定します。

set_rendering_end_frame


def set_rendering_end_frame(total_frames):
    """Set the end frame for rendering based on the total number of frames."""
    bpy.context.scene.frame_end = total_frames

アニメーションの総フレーム数に基づいてレンダリングの終了フレームを設定します。

コメント

タイトルとURLをコピーしました