Skip to content

Commit

Permalink
fix logger and fix instruments signature/method
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesa08 committed May 16, 2023
1 parent bf378fa commit b2eb671
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 64 deletions.
23 changes: 16 additions & 7 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
about: Create a report to help us improve MIDIAnimator.
title: ''
labels: bug
assignees: jamesa08
Expand All @@ -11,22 +11,31 @@ assignees: jamesa08



**To Reproduce**
**Steps to Reproduce**



**Expected behavior**
A clear and concise description of what you expected to happen.



**Screenshots**
If applicable, add screenshots to help explain your problem.


**Information (please complete the following information):**
- OS:
- Version of MIDIAnimator (see Add-on preferences, if you have the ZIP, upload it)
- Version of Blender (see Blender icon > About)


**Additional context**



**Logs**

To get the log:
- Produce the bug in Blender.
- Find the `scene.copy_log()` operator by searching for the parameter in Blender by pressing `Space` in the 3D Viewport
- Paste the log below:

```
log goes here...
```
10 changes: 6 additions & 4 deletions MIDIAnimator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

bl_info = {
"name": "MIDI Animator beta4.0",
"name": "MIDIAnimator beta4.0",
"description": "A cohesive, open-source solution to animating Blender objects using a MIDI file.",
"author": "James Alt (et al.)",
"version": (1, 0, 0),
Expand All @@ -39,13 +39,14 @@
from . src import *
from . src.instruments import Instruments, MIDIAnimatorObjectProperties, MIDIAnimatorCollectionProperties, MIDIAnimatorSceneProperties
from . utils import *
from . utils.loggerSetup import *
from . utils.logger import logger
from . ui import *
from . ui.operators import SCENE_OT_quick_add_props
from . ui.operators import SCENE_OT_quick_add_props, SCENE_OT_copy_log
from . ui.panels import VIEW3D_PT_edit_instrument_information, VIEW3D_PT_edit_object_information, VIEW3D_PT_add_notes_quick


classes = (SCENE_OT_quick_add_props, VIEW3D_PT_edit_instrument_information, VIEW3D_PT_edit_object_information, VIEW3D_PT_add_notes_quick, MIDIAnimatorObjectProperties, MIDIAnimatorCollectionProperties, MIDIAnimatorSceneProperties)

classes = (SCENE_OT_quick_add_props, SCENE_OT_copy_log, VIEW3D_PT_edit_instrument_information, VIEW3D_PT_edit_object_information, VIEW3D_PT_add_notes_quick, MIDIAnimatorObjectProperties, MIDIAnimatorCollectionProperties, MIDIAnimatorSceneProperties)

def register():
for bpyClass in classes:
Expand All @@ -61,6 +62,7 @@ def register():
item.value.cls.properties()

logger.info("MIDIAnimator registered successfully")
logger.info(f"MIDIAnimator version {'.'.join([str(i) for i in bl_info['version']])} {bl_info['name'].replace('MIDIAnimator ', '')}.")

def unregister():
for bpyClass in classes:
Expand Down
3 changes: 2 additions & 1 deletion MIDIAnimator/data_structures/midi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations
from .. utils import removeDuplicates, gmProgramToName, _closestTempo
from .. utils.logger import logger
from typing import List, Tuple, Dict
from dataclasses import dataclass
from .. libs import mido
Expand Down Expand Up @@ -206,7 +207,7 @@ def __str__(self) -> str:
return "".join(out)

def __add__(self, other) -> MIDITrack:
print(f"INFO: Attempting to merge tracks '{self.name}' & '{other.name}' ...")
logger.info(f"Attempting to merge tracks '{self.name}' & '{other.name}' ...")
try:
addedTrack = MIDITrack(f"{self.name} & {other.name}")

Expand Down
66 changes: 35 additions & 31 deletions MIDIAnimator/src/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import bpy

from .. data_structures.midi import MIDITrack
from .. utils.loggerSetup import *
from ..utils.logger import logger, buffer
from .. src.instruments import *
from . algorithms import *

Expand All @@ -14,48 +14,52 @@ class MIDIAnimatorNode:
def __init__(self):
self._instruments = []

def addInstrument(self, instrumentType: str, midiTrack: MIDITrack, objectCollection: bpy.types.Collection, properties: Dict[str]=None, custom=None, customVars: Dict=None):
def addInstrument(self, instrumentType: str=None, midiTrack: MIDITrack=None, objectCollection: bpy.types.Collection=None, custom=None, customVars: Dict=None):
"""adds an instrument to MIDIAnimator. This will create the class for you
:param str instrumentType: The instrument type. Choose from "evaluate", "Projectile" or "Custom".
:param MIDITrack midiTrack: The `MIDITrack` object to create the instrument from
:param bpy.types.Collection objectCollection: The collection (`bpy.types.Collection`) of Blender objects to be animated.
:param Dict[str] properties: dictionary of properties for classes, defaults to None
:param class(Instrument) custom: a custom Instrument class that inherits from Instrument, defaults to None
:param Dict customVars: a dictionary of custom vars you would like to send to your custom class, defaults to None
:raises RuntimeError: if instrumentType="custom" and the customClass is None.
"""

assert type(midiTrack).__name__ == "MIDITrack", "Please pass in a type MIDITrack object."
assert isinstance(objectCollection, bpy.types.Collection), "Please pass in a type collection for the objects to be animated."
assert instrumentType in {"evaluate", "projectile", "custom"}, "Instrument type invalid."

if instrumentType == "projectile":
# assert properties is not None, "Please pass a dictionary of properties in. Refer to the docs for help."
# assert "projectile_collection" in properties, "Please pass 'projectile_collection' into the properties dictionary."
# assert "reference_projectile" in properties, "Please pass 'reference_projectile' into the properties dictionary."

# assert isinstance(properties["projectile_collection"], bpy.types.Collection), "Please pass in a bpy.types.Collection for property 'projectile_collection'."
# assert isinstance(properties["reference_projectile"], bpy.types.Object), "Please pass in a bpy.types.Object for property 'reference_projectile'."

cls = ProjectileInstrument(midiTrack=midiTrack, objectCollection=objectCollection)

elif instrumentType == "evaluate":
cls = EvaluateInstrument(midiTrack, objectCollection)

elif instrumentType == "custom":
if custom is None: raise RuntimeError("Please pass a custom class object. Refer to the docs for help.")
if customVars is not None:
cls = custom(midiTrack, objectCollection, **customVars)
if instrumentType:
logger.warn("The `instrumentType` parameter has been depercated and is no longer required.")
objectCollection.midi.instrument_type = instrumentType

instrumentType = objectCollection.midi.instrument_type

try:
if instrumentType == "custom":
if custom is None: raise RuntimeError("Please pass a custom class object. Refer to the docs for help.")
if customVars is not None:
cls = custom(midiTrack, objectCollection, **customVars)
else:
cls = custom(midiTrack, objectCollection)
else:
cls = custom(midiTrack, objectCollection)
for item in Instruments:
value = item.value
if value.identifier == instrumentType:
instrumentCls = value.cls
break

cls = instrumentCls(midiTrack, objectCollection)
except Exception as e:
logger.exception(f"Error while creating instrument: '{e}'")
raise e

self._instruments.append(cls)

def animate(self, offset: int = 0) -> None:
"""animate all of the tracks
self._instruments.append(cls)

:param int offset: amount to add to the frame number for each keyframe (in case we have negative keyframes), defaults to 0
"""

for instrument in self._instruments:
instrument.animate()
def animate(self) -> None:
"""animate all of the tracks"""
try:
for instrument in self._instruments:
instrument.animate()
except Exception as e:
logger.exception(f"Error while animating instrument: {e}")
raise e
23 changes: 15 additions & 8 deletions MIDIAnimator/src/instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

from .. data_structures.midi import MIDITrack
from .. utils import convertNoteNumbers
from .. utils import rotateAroundCircle, animateAlongTwoPoints
from .. utils import animateAlongTwoPoints
from .. utils import mapRangeLinear as mLin, mapRangeLog as mLog, mapRangeExp as mExp, mapRangeArcSin as mASin, mapRangePara as mPara, mapRangeRoot as mRoot, mapRangeSin as mSin
from .. utils.loggerSetup import *
from .. utils.logger import logger
from .. data_structures import *
from .. utils.blender import *
from . algorithms import *
Expand Down Expand Up @@ -129,6 +129,12 @@ def __init__(self, midiTrack: MIDITrack, objectCollection: bpy.types.Collection)
self.projectileCollection = objectCollection.midi.projectile_collection
self.referenceProjectile = objectCollection.midi.reference_projectile

assert self.projectileCollection is not None, "Please pass in a projectile collection!"
assert self.referenceProjectile is not None, "Please pass in a reference projectile!"

assert isinstance(self.projectileCollection, bpy.types.Collection), "Please pass in a bpy.types.Collection for property 'projectile_collection'."
assert isinstance(self.referenceProjectile, bpy.types.Object), "Please pass in a bpy.types.Object for property 'reference_projectile'."

# clean objects
self.preAnimate()

Expand Down Expand Up @@ -289,7 +295,7 @@ def createNoteToObjectWpr(self) -> None:

for noteNumber in wpr.noteNumbers:
if noteNumber not in allUsedNotes:
print(f"WARNING: Object `{wpr.obj.name}` with MIDI note `{noteNumber}` does not exist in the MIDI track provided (MIDI track `{self.midiTrack.name}`)!")
logger.warning(f"Object `{wpr.obj.name}` with MIDI note `{noteNumber}` does not exist in the MIDI track provided (MIDI track `{self.midiTrack.name}`)!")

if noteNumber in self.noteToWpr:
self.noteToWpr[noteNumber].append(wpr)
Expand Down Expand Up @@ -544,14 +550,14 @@ def properties():
options=set()
)
MIDIAnimatorObjectProperties.x_mapper = bpy.props.StringProperty(
name="X axis mapper",
description="X axis mapper (time) of the animation. Use 'x' for time, 'note' for note number, and 'vel' for velocity.",
name="Time mapper",
description="Time mapper (X axis) of the animation. Use 'x' for time, 'note' for note number, and 'vel' for velocity.",
default="x",
options=set()
)
MIDIAnimatorObjectProperties.y_mapper = bpy.props.StringProperty(
name="Y axis mapper",
description="Y axis mapper (amplitude) of the animation. Use 'y' for amplitude, 'note' for note number, and 'vel' for velocity.",
name="Amplitude mapper",
description="Amplitude mapper (Y axis) of the animation. Use 'y' for amplitude, 'note' for note number, and 'vel' for velocity.",
default="y",
options=set()
)
Expand Down Expand Up @@ -579,7 +585,7 @@ def createNoteToObjectWpr(self) -> None:

for noteNumber in wpr.noteNumbers:
if noteNumber not in allUsedNotes:
print(f"WARNING: Object `{wpr.obj.name}` with MIDI note `{noteNumber}` does not exist in the MIDI track provided (MIDI track `{self.midiTrack.name}`)!")
logger.warning(f"Object `{wpr.obj.name}` with MIDI note `{noteNumber}` does not exist in the MIDI track provided (MIDI track `{self.midiTrack.name}`)!")

if noteNumber in self.noteToWpr:
self.noteToWpr[noteNumber].append(wpr)
Expand Down Expand Up @@ -767,6 +773,7 @@ class InstrumentItem:
class Instruments(Enum):
projectile = InstrumentItem(identifier="projectile", name="Projectile", description="A projectile that is fired from the instrument", cls=ProjectileInstrument)
evaluate = InstrumentItem(identifier="evaluate", name="Evaluate", description="Evaluate thing", cls=EvaluateInstrument)
custom = InstrumentItem(identifier="custom", name="Custom", description="Custom Instrument. Must pass the class via `MIDIAnimatorNode.addInstrument()`. See the docs for help.", cls=Instrument)


# Shared properties throughout all objects, instruments(collections) and scences.
Expand Down
28 changes: 27 additions & 1 deletion MIDIAnimator/ui/operators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from ..utils import nameToNote, noteToName, convertNoteNumbers
from ..utils.blender import FCurvesFromObject
from .. utils.logger import logger, buffer
from .. import bl_info
from operator import attrgetter
from ast import literal_eval
import platform
import sys
import bpy

def col_sort_key(obj):
Expand Down Expand Up @@ -30,14 +34,17 @@ def preExecute(self, context):
note_numbers = [col_sort_key(obj) for obj in col.all_objects]
else:
self.report({"ERROR"}, f"List contains errors. Please check the list and try again.")
logger.error(f"List contains errors. Please check the list and try again.")
return {'CANCELLED'}
except Exception as e:
self.report({"ERROR"}, f"List contains errors. Please check the list and try again. Error message: {e}.")
logger.error(f"List contains errors. Please check the list and try again. Error message: {e}.")
return {'CANCELLED'}

if note_numbers is not None:
if len(note_numbers) != len(col.all_objects):
self.report({"ERROR"}, f"Objects and note numbers unbalanced! \nObjects: {len(col.all_objects)}, Note Numbers: {len(note_numbers)}")
logger.error(f"Objects and note numbers unbalanced! \nObjects: {len(col.all_objects)}, Note Numbers: {len(note_numbers)}")
return {"CANCELLED"}

sortedObjs = sorted(col.all_objects, key=attrgetter('name')) if sceneMidi.quick_sort_by_name else sorted(col.all_objects, key=col_sort_key)
Expand Down Expand Up @@ -78,4 +85,23 @@ def draw(self, context):
table = self.mapNoteToObjStr(note_numbers, sortedObjs)

for line in table.split("\n"):
row.label(text=line)
row.label(text=line)

class SCENE_OT_copy_log(bpy.types.Operator):
bl_idname = "scene.copy_log"
bl_label = "Copy Output"
bl_description = "This will grab the output from the console and copy it to the clipboard."

def execute(self, context):
output = buffer.getvalue() + f"""\n#-#-#-#-#-# SYSTEM SPECS #-#-#-#-#-#
MIDIAnimator version {'.'.join([str(i) for i in bl_info['version']])} {bl_info['name'].replace('MIDIAnimator ', '')}.
Blender version {bpy.app.version_string}.
Python version {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}.
OS: {platform.platform()}.
System: {platform.system()}.
Version: {platform.version()}.
"""
context.window_manager.clipboard = output
logger.info("Copied output to clipboard.")
self.report({'INFO'}, "Copied MIDIAnimator output to clipboard.")
return {'FINISHED'}
36 changes: 36 additions & 0 deletions MIDIAnimator/utils/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import logging
from io import StringIO

# Create and configure the logger
logger = logging.getLogger('MIDIAnimator')
logger.setLevel(logging.INFO)
buffer = StringIO()

# clear the handlers from the logger before adding new ones
logger.handlers.clear()

# Create a custom log formatter
class ColoredFormatter(logging.Formatter):
COLORS = {
'DEBUG': '\033[36m', # Cyan
'INFO': '\033[32m', # Green
'WARNING': '\033[33m', # Yellow
'ERROR': '\033[31m', # Red
'CRITICAL': '\033[1;31m' # Bold Red
}
RESET = '\033[0m'

def format(self, record):
levelname = record.levelname
if levelname in self.COLORS:
record.levelname = f'{self.COLORS[levelname]}{levelname}{self.RESET}'
return super().format(record)

# add two stream handlers, one to the buffer and one to the console
bufferStream = logging.StreamHandler(buffer)
bufferStream.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
logger.addHandler(bufferStream)

consoleStream = logging.StreamHandler()
consoleStream.setFormatter(ColoredFormatter('%(name)s - %(levelname)s - %(message)s'))
logger.addHandler(consoleStream)
12 changes: 0 additions & 12 deletions MIDIAnimator/utils/loggerSetup.py

This file was deleted.

0 comments on commit b2eb671

Please sign in to comment.