Last active
May 1, 2025 20:22
-
-
Save pckv/5bd4d46f1f5ffa2dc02b74ccc826a08e to your computer and use it in GitHub Desktop.
Unrepeat - FL Studio Piano Roll Script
This file contains hidden or 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
""" | |
Title: Unrepeat | |
Author: pckv | |
Category: Edit | |
Version: 1.1 | |
Description: | |
Splits all notes with repeats into regular notes | |
Changelog: | |
v1.1 (2025-05-01) | |
Fixed division by zero error for PPQs lower than 96 | |
v1.0 (2025-05-01) | |
Initial version | |
""" | |
from flpianoroll import score, Note | |
import math | |
# By default, split notes retain their repeats value, which allows you to | |
# extend unrepeated notes to restore the previous repeat-value. They should | |
# still only play once after repeating anyway, since the new length is equal | |
# to the individual repeat length. | |
# | |
# Set this to True if you'd like the repeats to be set to 0 after splitting. | |
REMOVE_REPEATS = False | |
# Lengths of each individual note at 96 PPQ | |
repeat_note_lengths = { | |
1: 96, | |
2: 72, | |
3: 64, | |
4: 48, | |
5: 36, | |
6: 32, | |
7: 24, | |
8: 18, | |
9: 16, | |
10: 12, | |
11: 9, | |
12: 8, | |
13: 6, | |
14: 4, | |
} | |
def get_repeat_length(note: Note) -> int: | |
"""Get the length of a single repeat note for the given note.""" | |
new_length = repeat_note_lengths[note.repeats] | |
# Scale the length based on the current PPQ | |
return math.floor(new_length * (score.PPQ / 96)) | |
def split_note(note: Note): | |
"""Split a note into multiple notes based on its repeat length.""" | |
original_length = note.length | |
end_time = note.time + original_length | |
# Get the length of a single repeat note | |
repeat_length = get_repeat_length(note) | |
# Update the original note's length to the repeat length | |
note.length = repeat_length | |
# Ceil to include the remaining note that might be shorter than the repeat | |
# length | |
num_splits = math.ceil(original_length / repeat_length) | |
for i in range(num_splits): | |
# Create a new note for each split after the first one | |
split_note = note if i == 0 else note.clone() | |
# Place the split note | |
split_note.time = note.time + repeat_length * i | |
if REMOVE_REPEATS: | |
# Disable repeats for all new notes | |
split_note.repeats = 0 | |
# Cut the final split's length to end at the original note's end | |
# position | |
remaining_time = end_time - (split_note.time + split_note.length) | |
if remaining_time < 0: | |
split_note.length = repeat_length + remaining_time | |
# Add all new notes | |
if i > 0: | |
score.addNote(split_note) | |
def unrepeat(): | |
"""Split all selected notes with repeats""" | |
for i in range(score.noteCount): | |
note = score.getNote(i) | |
# Skip notes without repeats | |
if note.repeats == 0: | |
continue | |
split_note(note) | |
if __name__ == "__main__": | |
unrepeat() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment