Note Transforms
Shift, align, flip, scale, clone, split, merge, and convert notes.
Transforms are methods on Note objects. Use them when you want to adjust existing notes instead of rebuilding them by hand. This guide shows the common patterns; use the Note functions reference for core transform method signatures. For the note constructors these methods apply to, see Notes.
from margrete_rpc import Margrete
m = Margrete()
with m.open_edit() as tx:
for note in tx.chart.notes:
note.shift(x=2)The edit is sent to Margrete when the with block exits normally. If the block raises an
exception, no changes are applied.
Mutating or cloning
Most transform methods come in two forms:
| Mutating | Cloning | Meaning |
|---|---|---|
shift(...) | shifted(...) | Change this note, or return a changed copy. |
scale(...) | scaled(...) | Change this note, or return a changed copy. |
align(...) | aligned(...) | Change this note, or return a changed copy. |
flip(...) | flipped(...) | Change this note, or return a changed copy. |
clamp(...) | clamped_w(...) | Change this note, or return a changed copy. |
| n/a | converted(...) | Return a compatible note type with copied geometry. |
| n/a | clone() | Return a deep copy. |
Use the mutated form when editing tx.chart.notes directly:
with m.open_edit() as tx:
for note in tx.chart.notes:
note.shift(x=2)Use the cloned form when you want to keep the original note:
with m.open_edit() as tx:
copies = [note.shifted(t=1920, x=4) for note in tx.chart.notes]
tx.chart.notes.extend(copies)Shift notes
shift() adds deltas to note fields.
note.shift(t=1920) # move later by one beat
note.shift(x=2) # move right by 2 lanes
note.shift(w=1) # make 1 lane wider
note.shift(h=20) # raise air height by 20For t, you can also pass a Position tuple. This is resolved the same way as constructor positions.
note.shift(t=(1, 0)) # shift by one bar in the active time-signature contextEach delta can also be a callable that receives the current value:
note.shift(x=lambda x: 15 - x)
note.shift(w=lambda w: max(1, w - 1))Align notes to a grid
align() snaps timing to a grid interval.
with m.open_edit() as tx:
for note in tx.chart.notes:
note.align((1, 384))mode controls how snapping works:
| Mode | Behavior |
|---|---|
"round" | Snap to the nearest grid line. This is the default. |
"floor" | Snap backward to the previous grid line. |
"ceil" | Snap forward to the next grid line. |
note.align((1, 384), mode="floor")Flip notes
flip() mirrors notes horizontally across the field. The default field width is 16 lanes.
note.flip()For a note at x=2, w=4, flipping in a 16-lane field moves it to x=10, w=4.
Use field= for a different lane width:
note.flip(field=8)Directional notes are mirrored too. For example, "left" flicks become "right" flicks.
Clamp lane
clamp() keeps note geometry inside a lane range, defaulting to [0, 16).
note.clamp() # default left=0, right=16The right edge is exclusive, so right=16 means the note must end before lane 16. Also, width is never clamped below 1.
This is useful after shifting or flipping generated notes:
for note in tx.chart.notes:
note.shift(x=-2).clamp(left=0, right=16)Scale timing
scale() changes timing around a pivot tick which defaults to 0.
note.scale(2.0, pivot=0)With factor=2.0, every point moves twice as far from the pivot. With factor=0.5, every point moves halfway toward the pivot.
for note in tx.chart.notes:
note.scale(0.5)pivot can be a tick or a Position tuple:
note.scale(1.5, pivot=(4, 0))Clone notes
clone() makes a deep copy. Use it when duplicating notes.
with m.open_edit() as tx:
copies = []
for note in tx.chart.notes:
copies.append(note.clone().shift(t=1920))
tx.chart.notes.extend(copies)Split slides
split() divides a slide-like note into two notes. It supports Slide, AirSlide, and AirCrush.
from margrete_rpc.chart.notes import Slide, split
slide = (
Slide(t=(0, 0), x=0, w=4)
.with_ctrl(t=(0, 2), x=6, w=4) # joints[0]
.with_step(t=(1, 0), x=8, w=4) # joints[1]
)
left, right = split(slide, (0, 2))
left, right = split(slide, slide.joints[0])The split point can be a Position tuple, a raw tick, or one of the note's joints. The split must be inside the note, not at the final joint.
Merge slides
merge() combines consecutive slide-like notes of the same type. It supports Slide, AirSlide, and AirCrush.
from margrete_rpc.chart.notes import Slide, merge
first = Slide(t=(0, 0), x=0, w=4).with_step(t=(1, 0), x=4, w=4)
second = Slide(t=(1, 0), x=4, w=4).with_step(t=(2, 0), x=8, w=4)
combined = merge([first, second])The notes must not overlap, and all notes passed to merge() must be the same type.
Convert notes
Use converted() when you want to rebuild a note as another compatible note type without manually copying its timing and geometry. It is documented on the concrete note classes that support it, such as Tap, Hold, and AirSlide.
converted() always returns a new note. It does not change the original note. Pass keyword overrides for fields the target type needs, such as dir, h, gap, or color.
| Source note | Convertible targets |
|---|---|
Tap | Extap, Flick, Damage |
Extap | Tap, Flick, Damage |
Flick | Tap, Extap, Damage |
Damage | Tap, Extap, Flick |
Hold | Slide, AirSlide, AirCrush, AirHold |
Slide | AirSlide, AirCrush |
AirSlide | Slide, AirCrush |
AirCrush | Slide, AirSlide |
Ground note conversions
Ground notes can convert between Tap, Flick, Extap, and Damage.
from margrete_rpc.chart.notes import Damage, Extap, Flick, Tap
tap = Tap(t=(0, 0), x=4, w=4)
flick = tap.converted(Flick, dir="right")
extap = flick.converted(Extap, dir="up")
damage = extap.converted(Damage)
tap_again = damage.converted(Tap)Attached air notes are copied when converting between ground note types.
from margrete_rpc.chart.notes import Air
tap = Tap(t=(0, 0), x=4, w=4).with_air(Air("up", t=(0, 0), x=4, w=4))
flick = tap.converted(Flick, dir="left")Long note conversions
Long notes copy their begin point and joints into the target type.
from margrete_rpc.chart.notes import AirCrush, AirSlide, Hold, Slide
hold = Hold(t=(0, 0), x=4, w=4).with_step(t=(1, 0), x=8, w=4)
slide = hold.converted(Slide)
air_slide = hold.converted(AirSlide, h=80)
crush = slide.converted(AirCrush, h=80, gap=(1, 8), color="green")Air-long conversions
AirSlide and AirCrush can convert back to compatible long-note types.
from margrete_rpc.chart.notes import AirCrush, AirSlide, Slide
air_slide = (
AirSlide(t=(0, 0), x=4, w=4, h=80)
.with_ctrl(t=(0, 2), x=6, w=4, h=100)
.with_step(t=(1, 0), x=8, w=4, h=80)
)
ground_slide = air_slide.converted(Slide)
crush = air_slide.converted(AirCrush, gap=(1, 8), color="blue")
back_to_air_slide = crush.converted(AirSlide)Hold can also convert to AirHold:
from margrete_rpc.chart.notes import AirHold, Hold
hold = Hold(t=(0, 0), x=4, w=4).with_step(t=(1, 0), x=4, w=4)
air_hold = hold.converted(AirHold, h=80)