Skip to content

Instantly share code, notes, and snippets.

@JacquesLucke
Last active October 23, 2017 17:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JacquesLucke/5ba7ca942fc7f6c7664e76f0fdec3847 to your computer and use it in GitHub Desktop.
Save JacquesLucke/5ba7ca942fc7f6c7664e76f0fdec3847 to your computer and use it in GitHub Desktop.
Idea for the structure of a simple Midi Input node.
import bpy
from bpy.props import *
from ... base_types import AnimationNode
from ... data_structures import DoubleList
class MidiNoteData(bpy.types.PropertyGroup):
noteName = StringProperty() # e.g. C4, B5, ...
# This value can be keyframed.
# It is possible but not easy to 'find' the fcurve of this property.
# Therefor only the value in the current frame can be accessed efficiently.
# In most use cases this should be enough, otherwise you'll have to find another alternative.
value = FloatProperty()
class MidiInputNode(bpy.types.Node, AnimationNode):
bl_idname = "an_MidiInputNode"
bl_label = "MIDI Input Node"
bl_width_default = 300
# I'd suggest to bake one channel per node for now.
# You can have multiple nodes of course.
channelName = StringProperty() # e.g. Piano, ...
notes = CollectionProperty(type = MidiNoteData)
def create(self):
self.newOutput("Text List", "Notes", "notes")
self.newOutput("Float List", "Values", "values")
def draw(self, layout):
layout.prop(self, "channelName")
self.invokeSelector(layout, "PATH", "bakeMidi", text = "Bake Midi")
def execute(self):
notes = [item.noteName for item in self.notes]
values = [item.value for item in self.notes]
return notes, DoubleList.fromValues(values)
def bakeMidi(self, path):
# Remove previously baked data
self.removeFCurvesOfThisNode()
self.notes.clear()
# This function creates an abstraction for the somewhat complicated stuff
# that is needed to insert the keyframes. It is needed because in Blender
# custom node trees don't work well with fcurves yet.
def createNote(name):
dataPath = "nodes[\"{}\"].notes[{}].value".format(self.name, len(self.notes))
item = self.notes.add()
item.noteName = name
def insertKeyframe(value, frame):
item.value = value
self.id_data.keyframe_insert(dataPath, frame = frame)
return insertKeyframe
# Add a first note with keyframes
addKeyframe = createNote("First Note")
addKeyframe(value = 2, frame = 0)
addKeyframe(value = 42, frame = 100)
# Add a second note with keyframes
addKeyframe = createNote("Second Note")
addKeyframe(value = 13, frame = 5)
addKeyframe(value = 20, frame = 20)
def removeFCurvesOfThisNode(self):
try: action = self.id_data.animation_data.action
except: return
if action is None:
return
fCurvesToRemove = []
pathPrefix = "nodes[\"{}\"]".format(self.name)
for fCurve in action.fcurves:
if fCurve.data_path.startswith(pathPrefix):
fCurvesToRemove.append(fCurve)
for fCurve in fCurvesToRemove:
action.fcurves.remove(fCurve)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment