MuseParse/classes/ObjectHierarchy/TreeClasses/NoteNode.py
import copy
from MuseParse.classes.ObjectHierarchy.TreeClasses.BaseTree import Node, FindPosition
from MuseParse.classes.ObjectHierarchy.TreeClasses import OtherNodes
from MuseParse.classes.ObjectHierarchy.ItemClasses.Directions import OctaveShift
from MuseParse.classes.ObjectHierarchy.ItemClasses.Note import Arpeggiate, NonArpeggiate, \
GraceNote
from MuseParse.classes.ObjectHierarchy.ItemClasses import Note
from MuseParse.classes.ObjectHierarchy.TreeClasses.OtherNodes import ExpressionNode
class NoteNode(Node):
"""Node which encapsulates the Note class.
Optional inputs are minimal on this one as info about the note itself is stored in the Note class.
In order to maintain lilypond's output flow, Notes have a specific child order:
- left: Expression (dynamic or other expressive thing that has to be attached to a note)
- middle: Any other notes, if this note is part of a chord
- right: direction (anything that's not a note or expression)"""
def __init__(self, **kwargs):
if "duration" in kwargs:
self.duration = kwargs["duration"]
Node.__init__(
self,
rules=[
OtherNodes.DirectionNode,
OtherNodes.ExpressionNode,
NoteNode],
limit=3)
if self.item is None:
self.item = Note.Note()
def Find(self, node_type, item_type):
'''
method for finding specific types of notation from nodes.
will currently return the first one it encounters because this method's only really intended
for some types of notation for which the exact value doesn't really
matter.
:param node_type: the type of node to look under
:param item_type: the type of item (notation) being searched for
:return: first item_type object encountered
'''
if node_type == OtherNodes.DirectionNode:
child = self.GetChild(len(self.children) - 1)
while child is not None and not isinstance(
child.GetItem(),
item_type):
if child.GetItem().__class__.__name__ == item_type.__name__:
return True
child = child.GetChild(0)
if node_type == OtherNodes.ExpressionNode:
child = self.GetChild(len(self.children) - 2)
while child is not None and not isinstance(
child.GetItem(),
item_type):
if child.GetItem().__class__.__name__ == item_type.__name__:
return True
child = child.GetChild(0)
def SetGrace(self):
if self.item.Search(GraceNote) is None:
self.item.addNotation(GraceNote())
def SetLast(self):
result = self.item.Search(GraceNote)
if result is not None:
result.last = True
def UpdateArpeggiates(self, type="start"):
'''
method which searches for all arpeggiates and updates the top one of each chord to be a start,
and the bottom one to be a stop ready for lilypond output
:param type:
:return:
'''
result = self.item.Search(Arpeggiate)
if result is not None:
if type == "start":
result.type = type
child = self.GetChild(0)
if child is not None:
if child.item.Search(Arpeggiate) is None:
new_obj = copy.deepcopy(result)
new_obj.type = "none"
child.GetItem().addNotation(new_obj)
if child is not None and hasattr(child, "UpdateArpeggiates"):
child.UpdateArpeggiates(type="stop")
else:
result.type = type
else:
result = self.item.Search(NonArpeggiate)
if result is not None:
if type == "start":
result.type = type
child = self.GetChild(0)
if child is not None:
search = child.item.Search(NonArpeggiate)
if search is None:
cpy = copy.deepcopy(result)
cpy.type = "none"
child.item.addNotation(cpy)
if hasattr(child, "UpdateArpeggiates"):
child.UpdateArpeggiates(type="bottom")
else:
result.type = type
def SetItem(self, new_item):
Node.SetItem(self, new_item)
if hasattr(new_item, "duration"):
self.duration = new_item.duration
else:
self.duration = 0
def CheckForGraceNotes(self):
result = self.item.Search(GraceNote)
if result is not None:
first_child = self.GetChild(0)
if isinstance(self.GetChild(0), NoteNode):
first_child.SetGrace()
first_child.CheckForGraceNotes()
else:
self.SetLast()
def AttachDirection(self, item):
if item.GetItem().__class__.__name__ == OctaveShift.__name__:
self.shift = True
if len(self.children) == 0:
self.AttachExpression(OtherNodes.ExpressionNode())
self.AddChild(item)
elif 3 > len(self.children) > 0:
if not isinstance(self.GetChild(0), NoteNode):
if not isinstance(self.GetChild(0), OtherNodes.ExpressionNode):
self.AttachExpression(OtherNodes.ExpressionNode())
dir = self.GetChild(1)
if isinstance(dir, OtherNodes.DirectionNode):
parent = FindPosition(dir, item)
if parent is not None:
parent.AddChild(item)
elif dir is None:
self.AddChild(item)
elif not isinstance(self.GetChild(1), ExpressionNode):
self.AttachExpression(ExpressionNode())
self.AddChild(item)
else:
dir_node = self.GetChild(len(self.children) - 1)
if dir_node is not None:
if dir_node.GetItem() is None:
dir_node.SetItem(item.GetItem())
else:
parent = FindPosition(dir_node, item)
if parent is not None:
parent.AddChild(item)
else:
self.AddChild(item)
def AttachExpression(self, new_node):
if len(self.children) > 0:
if isinstance(self.GetChild(0), OtherNodes.DirectionNode):
self.PositionChild(0, new_node)
if isinstance(self.GetChild(0), NoteNode):
second = self.GetChild(1)
if second is None:
self.AddChild(new_node)
elif isinstance(second, OtherNodes.ExpressionNode):
node = FindPosition(second, new_node)
node.AddChild(new_node)
else:
self.PositionChild(1, new_node)
elif isinstance(self.GetChild(0), OtherNodes.ExpressionNode):
parent = self.GetChild(0)
node = FindPosition(parent, new_node)
node.AddChild(new_node)
else:
self.AddChild(new_node)
def AttachNote(self, new_note):
if len(self.children) > 0:
firstchild = self.GetChild(0)
if isinstance(firstchild, NoteNode):
firstchild.AttachNote(new_note)
else:
if not isinstance(
new_note.GetItem(),
int) and not isinstance(
new_note.GetItem(),
str):
post, pre, wrap = new_note.GetItem().GetAllNotation()
[self.GetItem().addNotation(n)
for n in post if self.GetItem().Search(type(n)) is None]
[self.GetItem().addNotation(p)
for p in pre if self.GetItem().Search(type(p)) is None]
[self.GetItem().addNotation(w)
for w in wrap if self.GetItem().Search(type(w)) is None]
new_note.GetItem().FlushNotation()
self.PositionChild(0, new_note)
else:
if not isinstance(
new_note.GetItem(),
int) and not isinstance(
new_note.GetItem(),
str):
post, pre, wrap = new_note.GetItem().GetAllNotation()
[self.GetItem().addNotation(n)
for n in post if self.GetItem().Search(type(n)) is None]
[self.GetItem().addNotation(p)
for p in pre if self.GetItem().Search(type(p)) is None]
[self.GetItem().addNotation(w)
for w in wrap if self.GetItem().Search(type(w)) is None]
new_note.GetItem().FlushNotation()
self.AddChild(new_note)
def toLily(self):
'''
Method which converts the object instance, its attributes and children to a string of lilypond code
:return: str of lilypond code
'''
lilystring = ""
if self.item is not None:
if not isinstance(self.GetChild(0), NoteNode):
if hasattr(self.item, "chord") and self.item.chord:
self.item.chord = "stop"
if isinstance(self.GetChild(0), NoteNode):
if not hasattr(self.item, "chord") or not self.item.chord:
self.item.chord = "start"
lilystring += self.item.toLily()
children = self.GetChildrenIndexes()
written = False
for child in children:
if self.GetChild(child) is not None:
if isinstance(self.GetChild(child), NoteNode):
lilystring += " "
return_val = self.GetChild(child).toLily()
if isinstance(return_val, str):
lilystring += return_val
else:
lilystring = return_val[0] + lilystring + return_val[1]
if isinstance(child, OtherNodes.ExpressionNode):
written = True
lilystring += self.item.GetClosingNotationLilies()
if len(children) == 0 or not written:
lilystring += self.item.GetClosingNotationLilies()
return lilystring
def PositionChild(self, key, node):
children = self.GetChildrenIndexes()
if key in children:
start = key
popped = self.children[start:]
self.children = self.children[:start]
self.AddChild(node)
[self.AddChild(pop) for pop in popped]
class Placeholder(NoteNode):
def __init__(self, **kwargs):
NoteNode.__init__(self, **kwargs)
self.item = None
def toLily(self):
'''
Method which converts the object instance, its attributes and children to a string of lilypond code
:return: str of lilypond code
'''
lilystring = ""
if self.duration > 0:
lilystring += "r" + str(self.duration)
return lilystring