ReaFruit MIDI Editor ManualReferenceDownload v0.9.6-beta

ReaFruit reference

For scripters and integrators: the ReaScript API, script parameter boxes, the MCP server, REAPER action ids, settings keys and the files ReaFruit reads and writes. The Manual covers the editor itself.

ReaScript API #

The extension exports a ReaFruit_* family to ReaScript. Lua and EEL2 call reaper.ReaFruit_CountNotes() and friends; REAPER's IDE completes them and Help › ReaScript documentation lists them. Scripts are ordinary ReaScripts: they run from the Actions list, keys, toolbars and gear › Scripts. Python reaches extension functions only through a generated ctypes module, which this version does not ship.

Conventions #

Editor and info #

FunctionReturnsNotes
ReaFruit_IsOpen()boolTrue while the editor window is open.
ReaFruit_GetTake(), ReaFruit_GetTrack()MediaItem_Take*, MediaTrack*The editor's take and its track. Use REAPER's MIDI_* on the take freely, then call Refresh.
ReaFruit_OpenTake(take)boolOpens the editor on a take; nil = the selected item's active take.
ReaFruit_Refresh()intRe-reads the take: 1 reloaded, 0 unchanged, -1 failed.
ReaFruit_GetVersion(), ReaFruit_GetLastError()stringThe extension's version; why the last call failed.
ReaFruit_GetInfo_Value(key)numberKeys: api_version open batch tempo cursor_qn cursor_s play_state play_qn ts_num ts_den snap_index grid_qn sheet_mode track_clips px_per_qn scroll_qn key_h def_vel def_chan def_color def_len scale_root scale_idx scale_lock tune_hz bend_range mpe notes selected clips clip_cur item_qn0 item_qn1 track_index.
ReaFruit_GetInfo_String(key)bool, stringKeys: version reaper_version tool snap_name scripts_dir lane_key lane_name artmap nn_map model_version take_name track_name.
ReaFruit_GetMousePosition()bool, qn, pitchThe grid cell under the mouse in the last frame; a script run from a key knows where the mouse is.

Notes #

FunctionReturnsNotes
ReaFruit_CountNotes()int
ReaFruit_GetNote(idx)ok, sel, mute, startqn, endqn, chan, pitch, velThe MIDI_GetNote shape, times in QN.
ReaFruit_SetNote(idx, sel, mute, startqn, endqn, chan, pitch, vel)boolnil leaves a field unchanged.
ReaFruit_InsertNote(sel, mute, startqn, endqn, chan, pitch, vel)intThe new note's index.
ReaFruit_DeleteNote(idx)boolLater indices shift down.
ReaFruit_GetNoteInfo_Value(idx, parm), ReaFruit_SetNoteInfo_Value(idx, parm, value)number / boolFields: B_SEL B_MUTE D_START D_END D_LEN I_PITCH I_VEL I_CHAN I_COLOR B_SLIDE B_VIB I_VSCALE I_TENSION D_VIBMS; read-only I_SWING I_CLIP D_HEAD D_TAIL I_BAR D_BEAT. Values are clamped.
ReaFruit_GetSetNoteInfo_String(idx, parm, str, set)bool, stringP_ARTICULATION (settable, by slot name), P_NAME, P_KEYNAME.
ReaFruit_CountSelectedNotes(), ReaFruit_EnumSelectedNotes(idx)intEnum returns the next selected note after idx; start at -1.
ReaFruit_SelectAllNotes(select)boolNo undo step.

Batches #

Every edit above writes the take at once as its own undo step. ReaFruit_BeginEdit() … ReaFruit_EndEdit(desc) makes them one write and one undo step named desc; EndEdit also returns whether the model reloaded. Inside a batch indices stay put, and actions that write the take are refused until it ends. Finish a batch in the same pass: one left open is written at the editor's next frame as "script edit". Per-note automation of moved notes follows; deleted notes' points are cleaned.

Clips, actions, settings #

FunctionReturnsNotes
ReaFruit_CountClips(), ReaFruit_GetClip(idx), ReaFruit_GetClipTake(idx)int; ok, startqn, endqn, current, name; takeThe track-level clips.
ReaFruit_RunAction(name)boolRuns an editor action by its ini name (the list plus copy, cut, paste, help).
ReaFruit_EnumActions(idx)ok, name, label, mutatingLists the actions RunAction accepts.
ReaFruit_GetSetting(key), ReaFruit_SetSetting(key, text), ReaFruit_EnumSettings(idx)bool, string; bool; ok, key, type, descThe settings keys, as text.

The JSON gateway #

ReaFruit_CallTool(name, argsJson) runs any of the 30 MCP tools and returns the result's length (and whether it is an error); ReaFruit_GetResult(offset) hands the JSON text back in chunks. Unlike the MCP client, a script may place and move notes, and these calls never count against the license trial.

The helper library #

At load, the extension writes Scripts/ReaFruit/reafruit.lua into the resource folder: a JSON codec and wrappers. rf.notes(), rf.selected(), rf.targets() (the selection, or every note), rf.set(idx, {vel=, slide=, …}), rf.insert{qs=, len=, pitch=}, rf.delete, rf.select, rf.edit(fn, desc) (one undo step with error safety), rf.run, rf.setting, rf.tool(name, args) with Lua tables, rf.status(), rf.mouse(), rf.snap(qn), rf.boxes(title, defs, apply, done), and the automation wrappers rf.set_note_auto, rf.note_auto, rf.set_auto_points, rf.auto_points, rf.delete_auto_points, rf.lane_key(). A shipped file's first line is a marker; files with the marker are updated when a new version ships, files without it are yours.

package.path = reaper.GetResourcePath() .. '/Scripts/ReaFruit/?.lua;' .. package.path
local rf = require('reafruit')
rf.require_open()
rf.edit(function()
  for _, n in ipairs(rf.selected()) do rf.set(n.idx, { vel = math.min(127, n.vel + 10) }) end
end, 'Louder')

Script parameter boxes #

A script can put up its own parameter boxes, at most eight, with the keep and discard pair, and preview its edits live as the user turns them, like Humanize.

Box definitions #

ReaFruit_BoxesBegin(title, boxesJson) opens them from a JSON array. Each box has a name, a type and an optional picture: bar (a range's default), knob, text (a choice's default), switch (a toggle's), die (a seed's).

TypeFieldsValue
rangemin max default decimals unitA number.
choiceoptions (strings), wrapThe option's index from 0.
toggledefault0 or 1.
seeddefault (optional)A die: a click, its key or the wheel rolls a new number for math.randomseed.

Kept values come back the next time boxes with the same title open; "remember": false opts a box out, and a seed rolls anew each time unless it has a default.

The poll loop #

The script polls ReaFruit_BoxesPoll() from a defer loop.

ResultMeaning
changedThe notes and automation are back as the boxes found them (the first poll too): read ReaFruit_BoxesGetValue(box) and apply the edits again as a preview.
openNothing new.
keptEnter or the tick wrote everything as one undo step named after the title.
cancelledEscape or the X put everything back.
noneNo boxes are open.

Automation previews too: what CallTool writes with the automation tools lands in REAPER at once and is restored on every changed and on a cancel. ReaFruit_BoxesSetValue(box, value) moves a box; ReaFruit_BoxesEnd(keep) closes them from the script. A script that stops polling for 1.5 s has its boxes cancelled; closing the editor or switching clips drops them. The helper wraps the whole loop as rf.boxes(title, defs, apply, done).

Native MIDI editor scripts #

gear › Scripts › Run as native MIDI editor script…, or a key slot assigned "as native", runs a script written for REAPER's MIDI editor against ReaFruit's take. reafruit_compat.lua redefines MIDIEditor_GetActive, MIDIEditor_GetTake, MIDIEditor_EnumTakes and MIDIEditor_GetMode around the editor's take, maps MIDIEditor_OnCommand to the editor's actions where one exists (select all, unselect, delete, copy, paste, cut, undo, redo, semitone and octave moves, grid nudges) and refreshes the editor afterwards.

MCP server #

An embedded Model Context Protocol server lets an LLM client read and edit the notes and settings of the running editor. It is off by default and listens only on localhost. It is the editor's one paid feature: the first 100 tool calls are free, status calls never count, and a license removes the limit. Enable it under gear › Advanced › MCP server (LLM access on localhost); the port submenu offers 7323 (the default), 7324, 7325, 8765 and 9765.

Client setup #

# Claude Code
claude mcp add --transport http reafruit http://127.0.0.1:7323/mcp

# stdio-only clients (Claude Desktop)
npx mcp-remote http://127.0.0.1:7323/mcp --allow-http

Tools #

Positions are project QN, pitches MIDI note numbers. Every note row carries its name, its clip and the bar and beat the ruler shows. status, list_tracks and the settings answer while the editor is closed; the note tools need it open on a MIDI take. Required parameters are in bold.

ToolDoesParameters
statusEditor and project state: defaults for new notes, MPE and bend range, the track's articulations and note names, the lane target, version and license, editable / blocked.
list_tracksTracks, their items and FX.
open_editorOpens the editor on a track's MIDI item.track_index, item_index
get_notesNotes, filtered and paged.selected_only, pitch_min, pitch_max, from_qn, to_qn, chan, color, offset, limit
add_notesRefused over MCP (notes are the user's); available to scripts through CallTool.notes[] {qs, qe or len, pitch, vel, chan, color or articulation, mute, slide, vib, vscale, ten, vib_ms}, select
edit_notesEdits note parameters by id; refuses qs, qe, len and pitch over MCP.version, edits[] {id, …}
delete_notesDeletes by id or the selection.version, ids[], selected
select_notesChanges the selection.ids[], filter{}, mode (set, add, remove), all, none
list_actions, run_actionThe actions below; the note-moving ones are refused over MCP.name
get_settings, set_settingThe settings with ranges, choices and descriptions; change one.key, value
list_articulation_maps, get_articulation_map, set_articulation_map, create_articulation_mapThe .txt maps and ReaBank banks; read one; put one on the track; write a new map from names with keyswitches or CC values and load it.name; create: name, articulations[], overwrite, activate
get_note_names, set_note_namesA track's note and CC names; write them as one undo step.track_index; names[], replace
list_note_name_maps, get_note_name_map, load_note_name_map, create_note_name_mapThe files under MIDINoteNames; read, load, or write a new one in REAPER's format.name; create: name, names[], overwrite, load
list_automation_paramsA track's automatable controls, FX parameters and MIDI CC lanes.track_index, fx, automated_only, cc, offset, limit
get_automationAn envelope's or CC lane's points, and the curve sampled on a QN grid.track_index, param, from_qn, to_qn, step_qn, at_qn[]
set_automation_points, edit_automation_points, delete_automation_pointsWrite, change or remove points on any track.track_index, param, points[] / edits[] / a range, indices or all
copy_automationReproduces one control's curve on another, by lane fraction.from, to, from_qn, to_qn, offset_qn, replace, visible
get_note_automation, set_note_automationThe editor's per-note lane values.param, ids[]; values[], version, show_lane

Controls are keyed by the lane target: track envelopes by name, FX parameters by plugin and parameter, MIDI controllers as cc:<n> or cc:bend (@<chan> for one channel, /<item> for another item), or by the names mod, expression, breath, sustain and bend. Lanes the editor writes itself come back locked.

run_action names #

Names marked * change notes and are refused over MCP; scripts may run them.

play_stop go_to_start select_all deselect clear_selection select_color toggle_ghosts follow bass_line pan_left pan_right pan_up pan_down zoom_in zoom_out tool_draw tool_paint tool_delete tool_mute tool_slice tool_select

duplicate* glue* legato* legato_slide* chop* undo* redo* redo_alt* octave_up* octave_down* transpose_up* transpose_down* nudge_left* nudge_right* recolor* quantize_quick* delete_notes* delete_notes_alt* slide_mode* vibrato_mode*

set_setting keys #

KeyTypeMeaning
toolenumdraw, paint, delete, mute, slice, select
snap_idxintThe snap grid, by index into the snap names
follow, follow_pinbool, intScroll with playback; where the playhead sits, percent of the view (-1 = page)
smooth_scroll, note_animboolEased scrolling; note animations
ghosts, ghost_colorsboolGhost notes; in their track's colour
bendlines, bass_line, tempo_flagsboolSlide curves; the bass threshold line; the tempo flags
lasso_grid, lasso_velboolThe magic lasso on the grid and on the lane
preview_chord, art_labelsboolChord preview while dragging; articulation labels
mpe_default, legslide_leadboolNew takes start in MPE; legato slides lead into the next chord
vel_closed, lane_pts, vel_hbool, bool, numberThe lane shut; edited as points; its height in px (36 to 500)
pb_scaleintParameter box size: 100, 125 or 150
track_clipsboolTrack-level clips (the model reloads)
scale_root, scale_idx, scale_lock, scale_flatsint, int, bool, boolRoot pitch class (0 = C); scale index (0 = off); lock; flats
cur_color, bend_rangeintThe colour slot new notes get (0 to 15); default bend range (1 to 48)
last_vel, last_lenint, numberThe velocity and length new notes get
px_per_qn, key_hnumberHorizontal zoom (6 to 900 px per QN); row height (6 to 30 px)
autofollowboolThe background helper
mcp_on, mcp_portbool, intRead-only: the server itself

Rules and refusals #

Transport #

A loopback listener thread reads each request and hands it to REAPER's main thread, which runs it against the editor's model between frames, never inside one. A request the main thread does not reach within 5 s (REAPER busy, a modal dialog up) gets 504. POST one JSON-RPC message to /mcp and read application/json back; no SSE stream (GET answers 405), stateless, protocol version 2025-06-18.

REAPER actions #

Main section #

Named command ids, for reaper.NamedCommandLookup("_REAFRUIT_MIDI_EDITOR") and the like. Each is listed as ReaFruit MIDI Editor: ….

Command idAction
_REAFRUIT_MIDI_EDITOROpen/toggle editor (reports its toggle state)
_REAFRUIT_MIDI_EDITOR_OPENOpen editor or media item properties (mouse modifier)
_REAFRUIT_MIDI_EDITOR_RECENTERMove window to the mouse cursor
_REAFRUIT_TB_SHEET, _REAFRUIT_TB_SNAP, _REAFRUIT_TB_SCALEToggle Sheet Music Mode; Snap menu; Toggle Scale Lock
_REAFRUIT_TB_TOOL_DRAW, _PAINT, _DELETE, _MUTE, _SLICE, _SELECTThe six tools
_REAFRUIT_TB_CHOP, _REAFRUIT_TB_SLIDE, _REAFRUIT_TB_VIBRATO, _REAFRUIT_TB_CHORD_STAMPChop; Toggle slide mode; Toggle vibrato mode; Chord stamp menu
_REAFRUIT_TB_HUMANIZE, _REAFRUIT_TB_STRUM, _REAFRUIT_TB_QUANTIZE, _REAFRUIT_TB_SWINGThe box sessions
_REAFRUIT_TB_TARGET, _REAFRUIT_TB_WAVEFORMToggle toolbar target name; Toggle waveform background

Scripts run from gear › Scripts are registered in the main section the first time they run.

The editor section #

The Actions window's section ReaFruit MIDI Editor (section id 0x52464D45) holds one command per editor action. The command id is 42000 plus the action's index below; the name is what reafruit_keys.ini and ReaFruit_RunAction use. Scope: A = anywhere, R = piano roll only, S = Sheet Music Mode, I = sheet note input only.

IdNameLabelDefaultScope
42000play_stopPlay/stop transportSpaceA
42001 to 42002select_all, deselectSelect all / Deselect all notesCtrl+A, Ctrl+DA
42003 to 42006copy, cut, paste, duplicateCopy / Cut / Paste at playhead / Duplicate notesCtrl+C, Ctrl+X, Ctrl+V, Ctrl+BA
42007 to 42009glue, legato, chopGlue notes / merge clips; Legato; Chop notes to gridCtrl+G, Ctrl+L, Ctrl+UA
42010 to 42012undo, redo, redo_altUndo; Redo; Redo (alternate)Ctrl+Z, Ctrl+Y, Ctrl+Shift+ZA
42013 to 42018octave_up, octave_down, transpose_up, transpose_down, nudge_left, nudge_rightOctave; transpose; nudgeCtrl+Up/Down, Shift+Up/Down, Shift+Left/RightA
42019 to 42020toggle_ghosts, recolorToggle ghost notes; Recolor with picked colorAlt+V, Alt+CA
42021 to 42026tool_draw, tool_paint, tool_delete, tool_mute, tool_slice, tool_selectTool: …P, B, D, T, C, ER
42027 to 42029slide_mode, vibrato_mode, followSlide notes; Vibrato notes; Scroll with playbackS, V, FR, R, A
42030 to 42033delete_notes, delete_notes_alt, clear_selection, go_to_startDelete selected notes / points; Clear selection; Go to project startDelete, Backspace, Escape, HomeA
42034 to 42039pan_left, pan_right, pan_up, pan_down, zoom_in, zoom_outPan the view; zoom the timelineLeft, Right, Up, Down, PageUp, PageDownA (pan up / down: R)
42040 to 42044humanize, strum, select_color, quantize_quick, quantizeHumanize; Strum; Select notes of the picked color; Quick quantize; QuantizeAlt+H, Alt+S, Shift+C, Ctrl+Q, Alt+QA
42045 to 42046sheet_mode, sheet_inputSheet music mode; Sheet: note input modeTab, NA, S
42047 to 42056sheet_dur_whole … sheet_dur_32nd, sheet_dot, sheet_tie, sheet_tie_alt, sheet_restDurations; dotted; tie; rest7 6 5 4 3 2, ., Shift+=, keypad +, 0S (ties: I)
42057 to 42070sheet_pitch_a … g, sheet_chord_a … gEnter a pitch; add it to the chordA to G, Shift+A to GI
42071 to 42080sheet_pitch_up/down, sheet_diat_up/down, sheet_octave_up/down, sheet_prev/next, sheet_bar_prev/nextPitch by semitone, scale step, octave; previous / next note, measureUp/Down, Alt+Shift+Up/Down, Ctrl+Up/Down, Left/Right, Ctrl+Left/RightS
42081 to 42089sheet_halve, sheet_double, sheet_repeat, sheet_respell, sheet_flat, sheet_natural, sheet_sharp, sheet_voice1, sheet_voice2Halve / double duration; repeat; respell; accidentals; voicesQ, W, R, J, -, \, =, Ctrl+Alt+1, Ctrl+Alt+2S
42090 to 42094sheet_staccato, sheet_accent, sheet_tenuto, sheet_marcato, sheet_input_exitArticulations; leave note inputShift+S, Shift+V, Shift+N, Shift+O, EscapeS, exit: I
42095 to 42097sheet_dur_64th, sheet_stem_flip, sheet_slur64th note; flip stems; slur1, X, SS
42098 to 42105sheet_tuplet2 … 7, sheet_insert, sheet_remove_timeDuplet to septuplet; insert mode; remove timeCtrl+2 to Ctrl+7, Ctrl+Shift+I, Ctrl+DeleteS
42106 to 42110legato_slide, lasso_show, pattern_mode, lane_last_touched, bass_lineLegato with slides; lasso path (hold); pattern mode (hold); last touched FX parameter; bass threshold lineCtrl+Alt+L, L, `, Alt+A, Alt+BA, R, R, R, R
42111 to 42115, 42117 to 42120param_box_1 … 9Parameter box N (hold + wheel, or press)1 to 9R
42116, 42121, 42122arpeggiator, arp_glue, chord_stampArpeggiator boxes; glue the pattern; Chord stamp boxesAlt+R, Ctrl+Enter, Alt+TR
42123 to 42130script_1 … 8Run script slot NunboundA
42131 to 42133swing, warp, tempo_changeSwing boxes; Time warp; Tempo change at the edit cursorAlt+Shift+S, Alt+W, unboundR, R, A
42134helpOpen the manual for the item under the mouseF1A

Settings and files #

reaper-extstate.ini #

Settings persist in REAPER's reaper-extstate.ini under the section [FL_PianoRoll]. The keys below are the user-facing ones; lic_*, upd_*, note_done, defaults_v and script_boxes:* are the extension's own bookkeeping.

KeysMeaning
follow follow_pin smooth_scroll pb_scale ghosts ghost_colors track_clips wave wave_pitch bendlines bass_line tempo_flags note_anim sheet_ghost_grand sheet_min64 sheet_extend hirate hirate_fps no_dockThe View menu
lasso_grid lasso_vel preview_chordThe Edit menu
mxl_tempo mxl_rename mxl_piano mxl_noslur mxl_nodyn mxl_dedup a2m_quantize a2m_*The File menu and the audio-import boxes
legslide_lead art_labels artmap_last groove_lastThe Tools menu
toolbar_hide toolbar_srcThe Toolbar menu
autofollow hirate_split mac_click_guard mcp_on mcp_port upd_enabledThe Advanced menu
tool snap_idx cur_color scale_root scale_idx scale_lock scale_flats scale_cmask scale_cname detect_favor_builtin bend_range mpe_defaultThe toolbar's state
last_vel last_vibvel last_vibscale last_vibms last_lenThe defaults new notes get
vel_h vel_closed lane_pts px_per_qn key_h sheet_mode sheet_durThe lane, the zoom, the sheet
stamp_type stamp_add stamp_inv stamp_inv_auto stamp_bassThe chord stamp boxes
mouse_mapThe mouse modifier table (non-default cells only)
script_slot_1 … script_slot_8The key slots (a native: prefix marks a native MIDI editor script)
docs_urlWhere the help key finds the manual: a web address or a local folder
ix_bridge_dirThe Instrument X bridge folder

Track and clip data #

KeyOnHolds
P_EXT:flpr_lanetrackThe lane's target: vel, swing, fx:<guid>:<param>, env:<chunk>, cc:<n>, tempo
P_EXT:flpr_lanestrackThe extra lanes: <target>;<points>;<height>|…
P_EXT:flpr_mpetrackMPE on for this track
P_EXT:flpr_ghosttakeGhostable (this clip)
P_EXT:flpr_endstakeThe drawn ends of slide and vibrato notes nested in their host

The scale is also saved per project. Note colours, slides, vibrato and per-note automation are stored as tags in the take, so a project opened without the extension keeps playing.

Data folders #

Folder (in the REAPER resource path)Holds
Data\ReaFruit Articulations\*.txt, Data\**\*.reabankArticulation maps and ReaBank banks
Data\Grooves\*.rgtSwing grooves
Data\ReaFruit ShapesSaved lane shapes
MIDINoteNamesNote-name maps in REAPER's format
Scripts\ReaFruitThe helper, the compat shim, the examples, your scripts
Data\reafruit_keys.iniFallback key bindings

reafruit_keys.ini #

One line per action, action = [ctrl+][shift+][alt+]key, for example duplicate = ctrl+shift+d. ctrl means Cmd on macOS; cmd and option are accepted as aliases for ctrl and alt; none unbinds. Key names: letters, digits, f1 to f24, up down left right space enter tab escape backspace delete insert home end pageup pagedown comma period slash semicolon apostrophe minus equals leftbracket rightbracket backslash grave and the keypad. Missing lines keep their defaults; a shortcut set in REAPER's Actions window always wins. The file is created with the defaults when missing and re-read when the editor opens.

ReaFruit MIDI Editor v0.9.6-beta · Manual