Skip to content

Instantly share code, notes, and snippets.

@natecraddock
Last active October 26, 2018 18:48
Show Gist options
  • Save natecraddock/edc9793630bd657c801c35d1bc8a05b7 to your computer and use it in GitHub Desktop.
Save natecraddock/edc9793630bd657c801c35d1bc8a05b7 to your computer and use it in GitHub Desktop.
# Audio Visualizer Script
# Load the script into blender
# Make sure Auto Run Python scripts is enabled in your
# User preferences for the drivers
# Enjoy!
import bpy
import bmesh
import mathutils
import time
import math
import os
start = time.time()
# Set these variables to change things
initial = mathutils.Vector((0.0, 0.0, 0.0))
radius = 1
h = 2
width = 4
height = 20
multiplier = 5
min = 20
max = 20000
# Place the filepath of the song here
# You it looks relative to the .blend file path, so save your file
audio_file = 'dub.ogg'
#mat = bpy.data.materials['color']
bpy.data.scenes['Scene'].frame_current = 1
def cache_audio():
fp = os.path.join(os.path.dirname(bpy.data.filepath), audio_file)
for sound in bpy.data.sounds:
if sound.filepath == fp and not sound.use_memory_cache:
sound.use_memory_cache = True
def add_driver_prop(entity, type, axis, data_path, expression):
# Adds a driver with the custom property as the variable to the object
# Type..
driver = entity.driver_add(type, axis)
driver.driver.type = 'SCRIPTED'
v = driver.driver.variables.new()
v.name = 'data'
v.type = 'SINGLE_PROP'
t = v.targets[0]
t.id = bpy.context.scene.objects['Audio Data']
t.data_path = data_path
driver.driver.expression = expression
def add_driver_prop_mat(entity, data_path, expression):
# Adds a driver with the custom property as the variable to the object
# Type..
driver = entity.data.materials[entity.data.materials[0].name].node_tree.nodes['ColorRamp'].inputs['Fac'].driver_add('default_value')
driver.driver.type = 'SCRIPTED'
v = driver.driver.variables.new()
v.name = 'data'
v.type = 'SINGLE_PROP'
t = v.targets[0]
t.id = entity
t.data_path = data_path
driver.driver.expression = expression
def bake_audio(object, l, h, c):
object['audio_data_' + str(c)] = 0.0
object.keyframe_insert(data_path='["audio_data_' + str(c)+ '"]')
back = bpy.context.area.type
bpy.context.area.type = 'GRAPH_EDITOR'
fp = os.path.join(os.path.dirname(bpy.data.filepath), audio_file)
bpy.ops.graph.sound_bake(filepath=fp, low=l, high=h, release=0.5)
bpy.context.area.type = back
def create_hex_geo(r, l, h):
# Creates a hexagon of radius r, at location l, with height h
# Returns the created hexagon
bm = bmesh.new()
verts = []
# Create the vertices
for v in range(0, 360, 60):
x = r * math.cos(math.radians(v)) + l.x
y = r * math.sin(math.radians(v)) + l.y
z = l.z
co = (x, y, z)
bm.verts.new(co)
# Create face
bm.faces.new(bm.verts)
# Extrude height
extrude = bmesh.ops.extrude_discrete_faces(bm, faces=bm.faces)
for v in extrude['faces'][0].verts:
v.co.z += h
me = bpy.data.meshes.new("Mesh")
bm.to_mesh(me)
bm.free()
# Add the mesh to the scene
scene = bpy.context.scene
obj = bpy.data.objects.new("audio_vis_hex", me)
scene.objects.link(obj)
scene.objects.active = obj
obj.select = True
return obj
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0,0,0))
empty = bpy.context.active_object
empty.name = "Audio Data"
m = math.pow((max - min), (1 / height))
cache_audio()
# Create the grid
x_loc = 0
y_loc = 0
z_loc = 0
c = 0
for y in range(0, height):
if y % 2 == 0:
x_loc = 0
else:
x_loc = (radius * 3) / 2
# Create the row object
empty["audio_data_" + str(c)] = 0.0
low = math.pow(m, c) + min
high = math.pow(m, c + 1) + min
empty.select = True
bake_audio(empty, low, high, c)
for f in empty.animation_data.action.fcurves:
f.select = False
for x in range(0, width):
x_loc += (radius * 3)
z_loc = initial.z
loc = mathutils.Vector((x_loc, y_loc, z_loc))
obj = create_hex_geo(radius, loc, h)
# For now, call this.. Later we can fix the creation script
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
# Attach the baked data to the z scale and location
add_driver_prop(obj, 'location', 2, '["audio_data_' + str(c) + '"]', "data * " + str(multiplier))
add_driver_prop(obj, 'scale', 2, '["audio_data_' + str(c) + '"]', "data * " + str(multiplier))
#material = bpy.data.materials['color'].copy()
#obj.data.materials.append(material)
#node = obj.data.materials[material.name].node_tree.nodes['ColorRamp'].inputs['Fac']
#add_driver_prop_mat(obj, '["audio_data_' + str(c) + ']', "data")
# Deselect everything
bpy.ops.object.select_all(action='DESELECT')
c += 1
y_loc += math.sqrt(3) / 2
# Add the music to the VSE
bpy.context.area.type = 'SEQUENCE_EDITOR'
bpy.context.scene.sequence_editor_clear()
bpy.ops.sequencer.sound_strip_add(filepath = os.path.join(os.path.dirname(bpy.data.filepath), audio_file), frame_start = 1)
bpy.context.scene.frame_end = bpy.context.sequences[0].frame_final_duration
#bpy.ops.time.view_all()
bpy.context.area.type = 'TEXT_EDITOR'
end = time.time()
print("Time elapsed:", str(end - start))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment