Margrete RPC
Basic

Edit Transactions

Read and modify chart notes and events.

An edit transaction is the normal way to script chart changes with Margrete RPC. Open a transaction, modify the chart, then exit the with block.

from margrete_rpc import Margrete
from margrete_rpc.chart.notes import Tap

m = Margrete()

with m.open_edit() as tx:
    tx.chart.notes.append(Tap(t=(0, 0), x=4, w=4))

The edit is sent to Margrete when the with block exits normally. If the block raises an exception, no changes are applied.

For the exact method signature, see Margrete.open_edit() and the EditTransaction reference.

Mental model

Think of open_edit() as a four-step workflow:

  1. Snapshot - Margrete sends the current raw notes, events, and cursor tick to Python.
  2. Parse - The Python API converts RawNote objects to Note objects such as Tap and Slide.
    If raw_notes=True, this step keeps every note as RawNote instead.
  3. Mutate - Your script edits normal Python objects: lists, notes, and event objects.
  4. Apply - The Python client compares the final chart to the snapshot and sends one undoable edit.

The EditTransaction object has three things you will use most often:

ObjectWhat it is for
tx.chart.notesA mutable list of chart notes. See the Chart reference.
tx.chart.eventsMutable event lists for BPM, time signatures, scroll speed, and note speed.
tx.current_tickThe editor cursor tick captured when the transaction opened.

tx.chart.events groups chart events by kind. See Events for examples that add, mutate, and filter each event list:

ListContains
tx.chart.events.bpmBpmEvent(t, bpm) tempo changes.
tx.chart.events.beatBeatEvent(bar, beats_per_bar, beat_unit) time-signature changes.
tx.chart.events.tilTimelineSpeedEvent(til, t, speed) scroll-speed changes.
tx.chart.events.note_speedNoteSpeedEvent(t, speed) note-speed changes.

Practical examples

Reading the chart

from margrete_rpc import Margrete
from margrete_rpc.chart.notes import Tap

m = Margrete()

with m.open_edit() as tx:
    for note in tx.chart.notes:
        if isinstance(note, Tap):
            print(f"Tap at tick {note.t}, lane {note.x}")

    for event in tx.chart.events.bpm:
        print(f"BPM {event.bpm} at tick {event.t}")

    for event in tx.chart.events.beat:
        print(f"Bar {event.bar}: {event.beats_per_bar}/{event.beat_unit}")

Add a note at the current cursor

Use tx.current_tick when you want the edit to happen where the red playhead is in Margrete.

from margrete_rpc import Margrete
from margrete_rpc.chart.notes import Tap

m = Margrete()

with m.open_edit() as tx:
    tx.chart.notes.append(Tap(t=tx.current_tick, x=4, w=4))

Move every note right by two lanes

Notes are regular objects. Transform methods mutate them in place. See Note Transforms for examples and the Note reference for exact signatures.

with m.open_edit() as tx:
    for note in tx.chart.notes:
        note.shift(x=2)

Transforms also update long-note joints and attached air notes, so this works for more than just taps.

Delete notes in a lane range

Assign a filtered list back to tx.chart.notes.

with m.open_edit() as tx:
    # Keep only notes that start at lane 4 or later.
    tx.chart.notes = [note for note in tx.chart.notes if note.x >= 4]

There is a known issue with undoing transactions that deleted notes. See Limitations for details.

Replace all notes with new notes

Use replace_all=True when the script is intentionally replacing every note in the chart.

from margrete_rpc.chart.notes import Tap

with m.open_edit(replace_all=True) as tx:
    tx.chart.notes = [
        Tap(t=(0, 0), x=0, w=4),
        Tap(t=(0, 1), x=4, w=4),
        Tap(t=(0, 2), x=8, w=4),
        Tap(t=(0, 3), x=12, w=4),
    ]

Edit BPM and speed events

Events live under tx.chart.events. You can inspect, append, filter, or mutate those lists the same way you edit notes.

from margrete_rpc.chart.events import BpmEvent, TimelineSpeedEvent

with m.open_edit() as tx:
    tx.chart.events.bpm.append(BpmEvent(t=0, bpm=180.0))

    # Timeline speed event: timeline id, tick, speed multiplier.
    tx.chart.events.til.append(TimelineSpeedEvent(til=0, t=(4 * 1920), speed=1.5))

    # Remove note-speed events after bar 8 in a 4/4 chart.
    tx.chart.events.note_speed = [
        event for event in tx.chart.events.note_speed if event.t < 8 * 4 * 1920
    ]

Inspect unsupported notes safely

By default, the Python API parses recognized RawNote objects into Note objects such as Tap or Slide. If it sees an unsupported note, it keeps that note as RawNote instead of dropping it.

from margrete_rpc.chart.notes import RawNote

with m.open_edit() as tx:
    for note in tx.chart.notes:
        if isinstance(note, RawNote):
            print(note.type, note.t, note.x, note.w)

Pass raw_notes=True when you want every note to stay in the low-level raw form.

with m.open_edit(raw_notes=True) as tx:
    for note in tx.chart.notes:
        print(note.type, note.t, note.x, note.w)

Cursor position

The cursor is the red playhead line in the editor. See the Margrete docs if you are unfamiliar with it.

Current cursor playhead in Margrete

Use m.current_tick() when you need the live cursor position outside a transaction:

tick = m.current_tick()

Inside a transaction, use tx.current_tick. It is captured when the transaction opens and does not change while the block runs:

with m.open_edit() as tx:
    tick = tx.current_tick

Undo and redo

You can trigger undo and redo from Python:

m.undo()  # returns True if something was undone
m.redo()  # returns True if something was redone

open_edit options

ParameterDefaultUse it when
snapshotTrueYou want to read the current chart and send only the differences on exit. This is the default for normal edits.
replace_allFalseYou want the final tx.chart.notes list to replace all notes in the chart. Useful for generated rewrites.
raw_notesFalseYou want every note as a RawNote instead of Note objects.
event_scan_lookahead_ticksNoneYou need to scan for scroll-speed events farther past the last note. None uses the server default.
event_scan_til_idsNoneYou need to control which timelines are scanned for scroll-speed events. None scans timelines that carry notes; [] scans all default timelines 0 through 15.

Common forms:

# Normal edit: snapshot on enter, diff on exit.
with m.open_edit() as tx:
    ...

# Full note rewrite.
with m.open_edit(replace_all=True) as tx:
    tx.chart.notes = build_notes()

# Raw low-level editing.
with m.open_edit(raw_notes=True) as tx:
    ...

# Insert generated notes without reading existing notes first.
with m.open_edit(snapshot=False) as tx:
    tx.chart.notes = build_notes()

# Read scroll-speed events farther beyond the last note.
with m.open_edit(event_scan_lookahead_ticks=1920 * 16) as tx:
    ...

# Scan specific timelines for scroll-speed events.
with m.open_edit(event_scan_til_ids=[0, 1, 2]) as tx:
    ...

On this page