From c0dd51f44faf0962b3d2a37f61fa539893e7c4ba Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:51:20 -0400 Subject: [PATCH 1/9] fix small out of range error, stubs for methods --- MIDIAnimator/src/algorithms.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/MIDIAnimator/src/algorithms.py b/MIDIAnimator/src/algorithms.py index ab3c0ff..b07430e 100644 --- a/MIDIAnimator/src/algorithms.py +++ b/MIDIAnimator/src/algorithms.py @@ -229,7 +229,7 @@ def interval(keyList, frame) -> Tuple[Keyframe]: # out of range to the right of the list return (keyList[-1], keyList[-1]) - for i in range(len(keyList)): + for i in range(len(keyList) - 1): if keyList[i].frame <= frame <= keyList[i+1].frame: return (keyList[i], keyList[i+1]) @@ -262,3 +262,24 @@ def interval(keyList, frame) -> Tuple[Keyframe]: keysOverlapping.sort(key=lambda keyframe: keyframe.frame) insertedKeys.extend(keysOverlapping) + + +def minKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: + return + + +def maxKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: + return + +def previousKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: + return + +def nextKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: + return + +def restValueCrossingKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: + return + +def keyPruningKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: + return + From 468d4d627c1958d529bde0df17650a6961ec5aeb Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:56:18 -0400 Subject: [PATCH 2/9] add props to inst --- MIDIAnimator/src/instruments.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MIDIAnimator/src/instruments.py b/MIDIAnimator/src/instruments.py index c43031a..abf770e 100644 --- a/MIDIAnimator/src/instruments.py +++ b/MIDIAnimator/src/instruments.py @@ -540,7 +540,12 @@ def properties(): ) MIDIAnimatorObjectProperties.anim_overlap = bpy.props.EnumProperty( items=[ - ("add", "Add", "Curves will add motion. More options will be added in the future") + ("add", "Add", "Curves will add motion."), + ("min", "Min", ""), + ("max", "Min", ""), + ("prev", "Previous", ""), + ("next", "Next", ""), + ("prune", "Keyframe Pruning", ""), ], name="Animation Overlap", default="add", From ce33237a3a46fe735ed72ea2e50de523ab0ffab7 Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:57:55 -0400 Subject: [PATCH 3/9] add overlap types for eval instrument --- MIDIAnimator/src/algorithms.py | 5 ++--- MIDIAnimator/src/instruments.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/MIDIAnimator/src/algorithms.py b/MIDIAnimator/src/algorithms.py index b07430e..61427e2 100644 --- a/MIDIAnimator/src/algorithms.py +++ b/MIDIAnimator/src/algorithms.py @@ -267,11 +267,10 @@ def interval(keyList, frame) -> Tuple[Keyframe]: def minKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: return - def maxKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: return -def previousKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: +def prevKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: return def nextKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: @@ -280,6 +279,6 @@ def nextKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> Non def restValueCrossingKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: return -def keyPruningKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: +def pruneKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: return diff --git a/MIDIAnimator/src/instruments.py b/MIDIAnimator/src/instruments.py index abf770e..9cb9ad4 100644 --- a/MIDIAnimator/src/instruments.py +++ b/MIDIAnimator/src/instruments.py @@ -741,6 +741,16 @@ def processNextKeys(curve, note, wpr, nextKeys): # take keyframes that are next and "add" them to the already insrted keyframes if obj.midi.anim_overlap == "add": addKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) + elif obj.midi.anim_overlap == "min": + minKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) + elif obj.midi.anim_overlap == "max": + maxKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) + elif obj.midi.anim_overlap == "prev": + prevKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) + elif obj.midi.anim_overlap == "next": + nextKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) + elif obj.midi.anim_overlap == "prune": + pruneKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) elif obj.midi.anim_type == "osc": # for now, we're going to use a keyframed object to determine which channels to keyframe to From 20bbaff3f65f7fc610c1682d752c10961c2a2b73 Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 20:02:02 -0400 Subject: [PATCH 4/9] whoops forgot one --- MIDIAnimator/src/instruments.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MIDIAnimator/src/instruments.py b/MIDIAnimator/src/instruments.py index 9cb9ad4..608649a 100644 --- a/MIDIAnimator/src/instruments.py +++ b/MIDIAnimator/src/instruments.py @@ -545,6 +545,7 @@ def properties(): ("max", "Min", ""), ("prev", "Previous", ""), ("next", "Next", ""), + ("rvc", "Rest Value Crossing", ""), ("prune", "Keyframe Pruning", ""), ], name="Animation Overlap", @@ -749,6 +750,8 @@ def processNextKeys(curve, note, wpr, nextKeys): prevKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) elif obj.midi.anim_overlap == "next": nextKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) + elif obj.midi.anim_overlap == "rvc": + restValueCrossingKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) elif obj.midi.anim_overlap == "prune": pruneKeyframes(insertedKeys=keyframes, nextKeys=nextKeys) From 7f5153f317bca3466a88f6c3671fa5ef41954955 Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 20:02:20 -0400 Subject: [PATCH 5/9] move methods out from addKeyframes --- MIDIAnimator/src/algorithms.py | 163 +++++++++++++++++---------------- 1 file changed, 82 insertions(+), 81 deletions(-) diff --git a/MIDIAnimator/src/algorithms.py b/MIDIAnimator/src/algorithms.py index 61427e2..09a12fa 100644 --- a/MIDIAnimator/src/algorithms.py +++ b/MIDIAnimator/src/algorithms.py @@ -140,6 +140,88 @@ def localExtrema(x): return out +# for handling adding keyframes together +def findOverlap(keyList1: List[Keyframe], keyList2: List[Keyframe]) -> List[Keyframe]: + """finds the overlap between two sets of keylists + for this to work, the second keylist must be bigger than the first keylist + + :param List[Keyframe] keyList1: first keylist + :param List[Keyframe] keyList2: second keylist + :raises ValueError: if the first keylist's first frame is bigger than the second keylist's first frame + :return List[Keyframe]: the list of keyframes that are overlapping + """ + if len(keyList1) == 0 or len(keyList2) == 0: + return [] + + if keyList1[0].frame > keyList2[0].frame: + # this means a note is somehow going back in time? is this even possible? + # notes should always be sequential, and not in reverse time + raise ValueError("first keyframe in keyList1 is bigger than first keyframe in keyList2! Please open a issue on GitHub along with the MIDI file.") + + overlappingKeyList = [] + overlapping = False + for key1 in reversed(keyList1): + if key1.frame > keyList2[0].frame: + overlapping = True + overlappingKeyList.append(key1) + else: + # not overlapping + if overlapping: + overlappingKeyList.append(key1) + break + + return list(reversed(overlappingKeyList)) + +# for handling adding keyframes together +def getValue(key1: Keyframe, key2: Keyframe, frame: float) -> float: + """this interpolates 2 keyframes to get a intermediate value + + :param Keyframe key1: first keyframe + :param Keyframe key2: second keyframe + :param float frame: the frame to evaluate + :return float: the evaluated value at the given frame + """ + x1, y1 = key1.frame, key1.value + x2, y2 = key2.frame, key2.value + try: + m = (y2 - y1) / (x2 - x1) + except ZeroDivisionError: + # i dont know if this will work every time + m = 0 + + c = y1 - m * x1 + return (m * frame) + c + +# for handling adding keyframes together +def interval(keyList, frame) -> Tuple[Keyframe]: + """returns the interval keyframes back given a frame number + e.g., if you had a keyList of + [ + Keyframe(frame=0.0, value=0.0), + Keyframe(frame=10.0, value=1.0), + Keyframe(frame=20.0, value=0.0) + ] + and you wanted to know the interval at frame 12.0 + the function would return + (Keyframe(frame=10.0, value=1.0), Keyframe(frame=20.0, value=0.0)) + + :param List[Keyframes] keyList: the list of keyframes to check + :param float frame: the frame to check the interval between + :return Tuple[Keyframe]: the keyframes that are within that interval + """ + if len(keyList) == 0: + return (None, None) + if keyList[0].frame > frame: + # out of range to the left of the list + return (keyList[0], keyList[0]) + elif keyList[-1].frame < frame: + # out of range to the right of the list + return (keyList[-1], keyList[-1]) + + for i in range(len(keyList) - 1): + if keyList[i].frame <= frame <= keyList[i+1].frame: + return (keyList[i], keyList[i+1]) + def addKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: """adds the two lists of keyframes together. check out the desmos graph to learn a bit more on how this works @@ -151,87 +233,6 @@ def addKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None :raises ValueError: if the `insertedKeys'` first frame is bigger than `nextKeys'` first frame :return None: this function mutates the insertedKeys list """ - # for handling adding keyframes together - def findOverlap(keyList1: List[Keyframe], keyList2: List[Keyframe]) -> List[Keyframe]: - """finds the overlap between two sets of keylists - for this to work, the second keylist must be bigger than the first keylist - - :param List[Keyframe] keyList1: first keylist - :param List[Keyframe] keyList2: second keylist - :raises ValueError: if the first keylist's first frame is bigger than the second keylist's first frame - :return List[Keyframe]: the list of keyframes that are overlapping - """ - if len(keyList1) == 0 or len(keyList2) == 0: - return [] - - if keyList1[0].frame > keyList2[0].frame: - # this means a note is somehow going back in time? is this even possible? - # notes should always be sequential, and not in reverse time - raise ValueError("first keyframe in keyList1 is bigger than first keyframe in keyList2! Please open a issue on GitHub along with the MIDI file.") - - overlappingKeyList = [] - overlapping = False - for key1 in reversed(keyList1): - if key1.frame > keyList2[0].frame: - overlapping = True - overlappingKeyList.append(key1) - else: - # not overlapping - if overlapping: - overlappingKeyList.append(key1) - break - - return list(reversed(overlappingKeyList)) - - # for handling adding keyframes together - def getValue(key1: Keyframe, key2: Keyframe, frame: float) -> float: - """this interpolates 2 keyframes to get a intermediate value - - :param Keyframe key1: first keyframe - :param Keyframe key2: second keyframe - :param float frame: the frame to evaluate - :return float: the evaluated value at the given frame - """ - x1, y1 = key1.frame, key1.value - x2, y2 = key2.frame, key2.value - try: - m = (y2 - y1) / (x2 - x1) - except ZeroDivisionError: - # i dont know if this will work every time - m = 0 - - c = y1 - m * x1 - return (m * frame) + c - - # for handling adding keyframes together - def interval(keyList, frame) -> Tuple[Keyframe]: - """returns the interval keyframes back given a frame number - e.g., if you had a keyList of - [ - Keyframe(frame=0.0, value=0.0), - Keyframe(frame=10.0, value=1.0), - Keyframe(frame=20.0, value=0.0) - ] - and you wanted to know the interval at frame 12.0 - the function would return - (Keyframe(frame=10.0, value=1.0), Keyframe(frame=20.0, value=0.0)) - - :param List[Keyframes] keyList: the list of keyframes to check - :param float frame: the frame to check the interval between - :return Tuple[Keyframe]: the keyframes that are within that interval - """ - if len(keyList) == 0: - return (None, None) - if keyList[0].frame > frame: - # out of range to the left of the list - return (keyList[0], keyList[0]) - elif keyList[-1].frame < frame: - # out of range to the right of the list - return (keyList[-1], keyList[-1]) - - for i in range(len(keyList) - 1): - if keyList[i].frame <= frame <= keyList[i+1].frame: - return (keyList[i], keyList[i+1]) keysOverlapping = findOverlap(insertedKeys, nextKeys) From cbdc1353d0bfcbbf2d09169aadc65a02b06e3a31 Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 20:14:18 -0400 Subject: [PATCH 6/9] remove prints --- MIDIAnimator/data_structures/midi.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/MIDIAnimator/data_structures/midi.py b/MIDIAnimator/data_structures/midi.py index 8311cc6..504d2e0 100644 --- a/MIDIAnimator/data_structures/midi.py +++ b/MIDIAnimator/data_structures/midi.py @@ -291,7 +291,6 @@ def _parseMIDI(self, file: str) -> List[MIDITrack]: tempo = msg.tempo tempoMap.append((time, msg.tempo)) - print(tempoMap) for track in midiFile.tracks: time = 0 tempo = 500000 @@ -308,7 +307,6 @@ def _parseMIDI(self, file: str) -> List[MIDITrack]: for msg in mido.merge_tracks([track]): time += mido.tick2second(msg.time, midiFile.ticks_per_beat, tempo) - print(time) curType = msg.type # channel messages From 791a16ab44a3673baec27392c4782d3e5518f437 Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:05:43 -0400 Subject: [PATCH 7/9] fix error --- MIDIAnimator/src/instruments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIDIAnimator/src/instruments.py b/MIDIAnimator/src/instruments.py index 608649a..cd8c7ec 100644 --- a/MIDIAnimator/src/instruments.py +++ b/MIDIAnimator/src/instruments.py @@ -542,7 +542,7 @@ def properties(): items=[ ("add", "Add", "Curves will add motion."), ("min", "Min", ""), - ("max", "Min", ""), + ("max", "Max", ""), ("prev", "Previous", ""), ("next", "Next", ""), ("rvc", "Rest Value Crossing", ""), From 4efd649b973dde4f731bdbc2f514ec3ac1dff723 Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:06:16 -0400 Subject: [PATCH 8/9] add implementations some of this is experimental, as it doesn't always work correctly --- MIDIAnimator/src/algorithms.py | 184 +++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 6 deletions(-) diff --git a/MIDIAnimator/src/algorithms.py b/MIDIAnimator/src/algorithms.py index 09a12fa..bae1adf 100644 --- a/MIDIAnimator/src/algorithms.py +++ b/MIDIAnimator/src/algorithms.py @@ -266,20 +266,192 @@ def addKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None def minKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: - return + keysOverlapping = findOverlap(insertedKeys, nextKeys) + + insertedKeysInterValues = [] + nextKeysInterValues = [] + + # interpolate the keyframes for each set of keyframes + for key in nextKeys: + inv1, inv2 = interval(keysOverlapping, key.frame) + if inv1 is None and inv2 is None: continue + nextKeysInterValues.append(Keyframe(key.frame, getValue(inv1, inv2, key.frame))) + + for key in keysOverlapping: + inv1, inv2 = interval(nextKeys, key.frame) + if inv1 is None and inv2 is None: continue + insertedKeysInterValues.append(Keyframe(key.frame, getValue(inv1, inv2, key.frame))) + + # most important part: do the thing + for key, interp in zip(keysOverlapping, insertedKeysInterValues): + # check if the value is less than the interpolated value + if key.value < interp.value: + key.value = interp.value + + for key, interp in zip(nextKeys, nextKeysInterValues): + # check if the value is less than the interpolated value + if key.value < interp.value: + key.value = interp.value + + # extend the lists + # TODO (need a better method to ensure the keyframes before get cut off and then start ) + keysOverlapping.extend(nextKeys) + keysOverlapping.sort(key=lambda keyframe: keyframe.frame) + + insertedKeys.extend(keysOverlapping) + def maxKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: - return + keysOverlapping = findOverlap(insertedKeys, nextKeys) + + insertedKeysInterValues = [] + nextKeysInterValues = [] + + # interpolate the keyframes for each set of keyframes + for key in nextKeys: + inv1, inv2 = interval(keysOverlapping, key.frame) + if inv1 is None and inv2 is None: continue + nextKeysInterValues.append(Keyframe(key.frame, getValue(inv1, inv2, key.frame))) + + for key in keysOverlapping: + inv1, inv2 = interval(nextKeys, key.frame) + if inv1 is None and inv2 is None: continue + insertedKeysInterValues.append(Keyframe(key.frame, getValue(inv1, inv2, key.frame))) + + # most important part: do the thing + for key, interp in zip(keysOverlapping, insertedKeysInterValues): + # check if the value is more than the interpolated value + if key.value > interp.value: + key.value = interp.value + + for key, interp in zip(nextKeys, nextKeysInterValues): + # check if the value is more than the interpolated value + if key.value > interp.value: + key.value = interp.value + + # extend the lists + # TODO (need a better method to ensure the keyframes before get cut off and then start ) + keysOverlapping.extend(nextKeys) + keysOverlapping.sort(key=lambda keyframe: keyframe.frame) + + insertedKeys.extend(keysOverlapping) def prevKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: - return + keysOverlapping = findOverlap(insertedKeys, nextKeys) + + # maintain the value from the first set during overlap + for key in keysOverlapping: + for nextKey in nextKeys: + if key.frame == nextKey.frame: + nextKey.value = key.value + + # append non-overlapping keyframes from nextKeys to insertedKeys + nonOverlappingNextKeys = [key for key in nextKeys if key.frame not in [k.frame for k in keysOverlapping]] + insertedKeys.extend(nonOverlappingNextKeys) + insertedKeys.sort(key=lambda keyframe: keyframe.frame) + def nextKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: - return + # Find overlapping keyframes between insertedKeys and nextKeys + keysOverlapping = findOverlap(insertedKeys, nextKeys) + + # Replace values from insertedKeys with nextKeys during overlap + for key in keysOverlapping: + for nextKey in nextKeys: + if key.frame == nextKey.frame: + key.value = nextKey.value + + # Identify keyframes in nextKeys that are not in the overlap + nonOverlappingNextKeys = [key for key in nextKeys if key.frame not in [k.frame for k in keysOverlapping]] + + # Combine non-overlapping nextKeys with all the original insertedKeys + finalKeyframes = [] + i, j = 0, 0 + while i < len(insertedKeys) and j < len(nonOverlappingNextKeys): + if insertedKeys[i].frame < nonOverlappingNextKeys[j].frame: + finalKeyframes.append(insertedKeys[i]) + i += 1 + else: + finalKeyframes.append(nonOverlappingNextKeys[j]) + j += 1 + + # Add remaining keyframes + finalKeyframes.extend(insertedKeys[i:]) + finalKeyframes.extend(nonOverlappingNextKeys[j:]) + + # Clear the insertedKeys list and replace it with finalKeyframes + insertedKeys.clear() + insertedKeys.extend(finalKeyframes) + def restValueCrossingKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: - return + restValue = 0 + keysOverlapping = findOverlap(insertedKeys, nextKeys) + + for key in keysOverlapping: + for nextKey in nextKeys: + if key.frame == nextKey.frame: + # Interpolate the values to cross the rest value (0) smoothly + insertedValueToRest = (key.value + restValue) / 2 + nextValueFromRest = (nextKey.value + restValue) / 2 + + # Adjust the current keyframe value to fade out to rest value + key.value = insertedValueToRest + + # Adjust the next keyframe value to fade in from rest value + nextKey.value = nextValueFromRest + + # Identify non-overlapping keyframes in nextKeys + nonOverlappingNextKeys = [key for key in nextKeys if key.frame not in [k.frame for k in keysOverlapping]] + + # Combine non-overlapping nextKeys with insertedKeys + finalKeyframes = [] + i, j = 0, 0 + while i < len(insertedKeys) and j < len(nonOverlappingNextKeys): + if insertedKeys[i].frame < nonOverlappingNextKeys[j].frame: + finalKeyframes.append(insertedKeys[i]) + i += 1 + else: + finalKeyframes.append(nonOverlappingNextKeys[j]) + j += 1 + + # Add any remaining keyframes + finalKeyframes.extend(insertedKeys[i:]) + finalKeyframes.extend(nonOverlappingNextKeys[j:]) + + # Update insertedKeys with final combined keyframes + insertedKeys.clear() + insertedKeys.extend(finalKeyframes) + def pruneKeyframes(insertedKeys: List[Keyframe], nextKeys: List[Keyframe]) -> None: - return + keysOverlapping = findOverlap(insertedKeys, nextKeys) + + # Prune strategy: remove the last couple of keyframes from insertedKeys + if keysOverlapping: + last_overlap_frame = max(key.frame for key in keysOverlapping) + + # Identify keyframes in insertedKeys that overlap and are near the end + prunedInsertedKeys = [key for key in insertedKeys if key.frame <= last_overlap_frame] + + # If there's overlap, we'll remove the last keyframe or two + if len(prunedInsertedKeys) > 1: + prunedInsertedKeys.pop() # Remove the last overlapping keyframe + if len(prunedInsertedKeys) > 1: + prunedInsertedKeys.pop() # Optionally remove one more for smoother transition + + # Remaining keyframes + remainingInsertedKeys = [key for key in insertedKeys if key.frame > last_overlap_frame] + + # Final keyframes after pruning + finalKeyframes = prunedInsertedKeys + remainingInsertedKeys + nextKeys + else: + # If no overlap, just concatenate the keys + finalKeyframes = insertedKeys + nextKeys + + # Sort and update the insertedKeys with pruned results + finalKeyframes.sort(key=lambda keyframe: keyframe.frame) + insertedKeys.clear() + insertedKeys.extend(finalKeyframes) + From 83e22c253f2c83694941749241110fd36c27c3ca Mon Sep 17 00:00:00 2001 From: jamesa08 <79052050+jamesa08@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:07:30 -0400 Subject: [PATCH 9/9] bump to beta 4.1 --- MIDIAnimator/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MIDIAnimator/__init__.py b/MIDIAnimator/__init__.py index f1b97f2..56761fe 100644 --- a/MIDIAnimator/__init__.py +++ b/MIDIAnimator/__init__.py @@ -14,10 +14,10 @@ from __future__ import annotations bl_info = { - "name": "MIDIAnimator beta4.0", + "name": "MIDIAnimator beta4.1", "description": "A cohesive, open-source solution to animating Blender objects using a MIDI file.", "author": "James Alt (et al.)", - "version": (1, 0, 0), + "version": (0, 4, 1), "blender": (3, 0, 0), "location": "Scripting Space", "doc_url": "https://midianimatordocs.readthedocs.io/en/latest/",