Last active
October 23, 2017 17:54
-
-
Save JacquesLucke/5ba7ca942fc7f6c7664e76f0fdec3847 to your computer and use it in GitHub Desktop.
Idea for the structure of a simple Midi Input node.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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