1
$\begingroup$

I'm making an audio visualizer which I want to render from the command line. Unfortunately the bpy.ops.graph.sound_bake() function seems only to work from the GUI.

I've found various tutorials, but they all use the gui to create the animation. They always require this fix

bpy.context.area.type = 'GRAPH_EDITOR'

to set the correct context for bpy.ops.graph.sound_bake() to work. This works for scripts run from the gui, but when a script is started from he command line bpy.context.area is undefined.

For materials I already found that instead of bpy.ops.material.new() I should use mat = bpy.data.materials.new("") to avoid problems with the context. I have not been able how to work out how to do something similar with sounds.

It seems in scripting, using bpy.ops.* can easily give problems with the context, as these function might check the mouse position for some reason, etc. and there would/should be alternative functions. I have no clue on how to find those alternatives. So far I found them by chance, but I have not been able to find an alternative for bpy.ops.graph.sound_bake()

How can I bake an mp3 to an fcurve and apply that curve to an object, in a script?

$\endgroup$

3 Answers 3

2
$\begingroup$

For Sound Drivers Addon I do a lot of sound baking, so I thought I'd check out how it would perform running in background mode (this has led to another q re running modal timer operators in bg mode). Anyhow using context override the following bakes in bg mode using command

blender -b ./Desktop/baketest.blend --python-text bake.py

using the following as a text block named bake.py.

import bpy
#from sound_drivers.utils import get_context_area
def get_context_area(context, context_dict, area_type='GRAPH_EDITOR',
                     context_screen=False):
    '''
    context : the current context
    context_dict : a context dictionary. Will update area, screen, scene, 
                   area, region
    area_type: the type of area to search for
    context_screen: Boolean. If true only search in the context screen.
    '''
    if not context_screen:  # default
        screens = bpy.data.screens
    else:
        screens = [context.screen]
    for screen in screens:
        for area_index, area in screen.areas.items():
            if area.type == area_type:
                for region in area.regions:
                    if region.type == 'WINDOW':
                        context_dict["area"] = area
                        context_dict["screen"] = screen
                        context_dict["scene"] = context.scene
                        context_dict["window"] = context.window
                        context_dict["region"] = region
                        return area
    return None

c = bpy.context.copy()
get_context_area(bpy.context, c)
obj = bpy.data.objects["Cube"]
obj["y"] = 0.0
obj.keyframe_insert('["y"]', frame=1)

speaker = bpy.data.speakers["Speaker"]
bpy.ops.graph.sound_bake(c, filepath=speaker.sound.filepath)
bpy.ops.wm.save_mainfile()

Note for convenience sake I grabbed the filepath from a speaker's sound. This successfully baked the sound file onto the "y" custom property of the Cube in bg mode.

Use something like How to pass command line arguments to a Blender Python script? to extend functionality,.

NB: Example of creating a simple sound visualiser using sound_drivers.

$\endgroup$
2
  • $\begingroup$ You sir are my hero for the day. May good things come your way. $\endgroup$
    – doakey3
    Jun 17, 2018 at 20:15
  • $\begingroup$ There is trouble with selecting only channel ["y"] in the graph editor. So at the start it somehow have to deselect all channels, groups, objects > then select only obj["y"] and then bpy.ops.graph.sound_bake. But with the blender background deselecting is somehow tricky, I don't know how to do that properly trough bpy.data.. because data.actions[].groups[].select ... is not setable trough text editor. $\endgroup$
    – MRL
    Oct 12, 2021 at 15:14
1
$\begingroup$

The solution provided by @batFINGER didn't work for me, possibly due to changes in the context override technique. However, I found a similar approach that worked.

First we need the Context

def get_context(type):
    for screen in bpy.data.screens:
        for area in screen.areas:
            if area.type == type:
                context_override = {"area": area, "screen": screen}
                return context_override

after that we set the Context in temp_override also its important to set the active object

def sound_to_sample(context, mp3):
    override = get_context("GRAPH_EDITOR")

    with context.temp_override(**override):
        obj = bpy.data.objects.get("Cube")
        context.view_layer.objects.active = obj
        obj.select_set(True)
        bpy.ops.graph.sound_to_samples(filepath=mp3)
$\endgroup$
0
$\begingroup$

batFINGER's answer is great, but I was getting an incorrect context error until I selected the object I was trying to bake the sound curve onto.

...
obj = bpy.data.objects["Cube"]
obj["y"] = 0.0
obj.keyframe_insert('["y"]', frame=1)

speaker = bpy.data.speakers["Speaker"]
obj.select_set(True)  # This was important
bpy.ops.graph.sound_bake(c, filepath=speaker.sound.filepath)
$\endgroup$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .