Skip to content
Raymond Radet edited this page Nov 25, 2022 · 11 revisions

REAPER RPP Project Parser and Generator

Introduction

This script (Development/RPP-Parser/ReaTeam_RPP-Parser.lua) is about the most advanced third-party REAPER .rpp project file parser.

It is a Lua port of the parser used by mrlimbic on its acclaimed Vordio v6 software. It could be ported to other languages as well.

It is shared and updated right on Reateam/ReaScripts reapack compatible and widely distributed repository to make it easily accessible by scripters.

It doesn't rely on REAPER API so it can be used outside REAPER (using Lua Binaries and call from shell/terminal for eg).

Usage

Concepts

With this script, you can parse .rpp files or rpp text chunks, manipulate them extensively (any nodes, any value etc), stringify them and write them back to .rpp files. You can even create them from scratch.

In practice, this means you can build things like these examples:

  • a script to list all media sources used as active take in all .rpp files in a directory
  • a script to merge projects tracks from different .rpp into one
  • a script to create a project with tracks and items already in place, based on a CSV
  • an advanced diff checker for .rpp for advanced collaboration
  • a .rpp to converter toward other format
  • etc...

Because it works with rpp format definition (data as written in the .rpp file definition), a good knowledge on this and State Chunk formatting are required. Meo-Ada Mespotine various State Chunk Documentations may be the more advanced doc available. Other State Chunk definitions can be found on ReaTeam/Doc repository.

The parser is very generic, object based, and so, flexible. The parser filer only have function for raw data manipulation (adding, delete, remove, updating, find etc), as shown on Definitions below. It is up to the user to write custom functions to actually do actual reaper stuff with it, and do the data sanitization. Extra files could be added to the Parser folder to pack some user made functions by category (items, tracks etc).

If you do project generation from scratch, here is a key feature that will help you: you can write only the data you need, as REAPER doesn't need .rpp to be completed to be readable. Any missing data will be filled with default ones, based on user default project template.

Definitions

  • RChunk: Main RPP State Chunks. It starts with < and ends with >. It is a type of RNode.

  • RNode: RNode are all kind of line or block structure in the RPP. A node is made of a line (arbitrary data), or RToken tokens (value sepatated by spaces). If both tokens and line are present on a RNode at stringification tokens will have priority. RNode can have parent and children nodes, accesisble with RNode.parent and RNode.children.

  • RToken: Value of a certain RNode. When you parse a rpp to string, values are just stored in node.line, and are only Tokenized to one or ``several tokens if needed (this allows to save CPU). First token is usually RNode main tag, like TRACK, ITEM... for RChunks, or NAME, POSITION.. for an item chunk RNodes childs. Some helpers functions are associated with RToken. Tokens can be accessed with node.tokens.

List of all functions and method right in the Reateam_RPP-Parser.lua source code for now.

This give something like this:

<RCHUNK -- This is a RChunk type of RNode. "RCHUNK" string itself is a RToken, the first and only of this RNode.
  RToken1 RToken2 RToken3 -- All these tokens are linked to an RNode
> -- End of RChunk

Demo

See Reateam_RPP-Parser-test.lua file for more demo.

More complete examples in the Reateam_RPP-Parser_test.lua file.

Working from .rpp file

dofile(reaper.GetResourcePath() .. "\\Scripts\\Reateam Scripts\\Development\\RPP-Parser\\Reateam_RPP-Parser.lua") -- Import the parser

-- INPUT
path = "TEST.rpp" -- Path to your initial .rpp
root = ReadRPP(path) -- Parse the RPP

-- Do what you want
tracks = root:findAllChunksByName("TRACK") -- Find chunks and manipulate them

-- OUTPUT
outputpath = "output.RPP"
output_retval, output_message = WriteRPP(outputpath, root) -- Write to .rpp file

Working from rpp chunk text

dofile(reaper.GetResourcePath() .. "\\Scripts\\Reateam Scripts\\Development\\RPP-Parser\\Reateam_RPP-Parser.lua") -- Import the parser

root = StringifyRPPChunk(str) -- Parse the RPP text chunk

-- Do what you want

-- OUTPUT
outputpath = "output.RPP"
output_retval, output_message = WriteRPP(outputpath, root) -- Write to .rpp file

Create a RPP from scratch

dofile(reaper.GetResourcePath() .. "\\Scripts\\Reateam Scripts\\Development\\RPP-Parser\\Reateam_RPP-Parser.lua") -- Import the parser

root = CreateRPP() -- Create the root

local index = 0
tracks={}
for j = 1, 5 do
  local track = AddRChunk(root, {"TRACK"}) -- Add track
  table.insert(tracks, track)
  local name = AddRNode(track, {"NAME", j}) -- Add track name
  for i = 1, 1 do
    index = index + 1
    local item = AddRChunk(track, {"ITEM"}) -- Add item
    local position = AddRNode(item, {"POSITION", i-1}) -- Add item position
    local length = AddRNode(item, {"LENGTH", "1"}) -- Add length
    local notes = AddRChunk(item, {"NOTES"}) -- Add notes
    notes:setTextNotes("This is a multiline text.\nOr is it?")
    local notes_text = notes:getTextNotes()
  end
end

output_path = script_folder .. "OUTPUT.rpp"
output_retval, output_message = WriteRPP(output_path, root)

References

Here is list of main functions provided by the parser. Other functions can be found in the parser file code directly.

Main functions

  • ReadRPPChunk(input)
  • CreateRPP(version, system, time)
  • CreateRTokens(tab)
  • CreateRChunk(tab) -- Table of string
  • CreateRNode(var) -- Table or String
  • AddRChunk(parent, tab)
  • AddRNode(parent, tab)
  • AddRToken(node, tab)
  • TableRPPNode(node, indent, tab)
  • StringifyRPPNode(node)
  • WriteRPP(filename, root)

RNode methods

  • RNode:new(o)
  • RNode:getTokens()
  • RNode:getToken(index)
  • RNode:getName()
  • RNode:getParam(index)
  • RNode:getTokensAsLine()
  • RNode:remove()

RChunk methods (they also have RNode methods)

  • RChunk:findFirstNodeByName(name, start_index, end_index)
  • RChunk:findFirstChunkByName(name, start_index, end_index)
  • RChunk:findAllNodesByFilter(filter, start_index, end_index)
  • RChunk:findAllChunksByFilter(filter, out, start_index, end_index)
  • RChunk:findAllNodesByName(name, start_index, end_index)
  • RChunk:findAllChunksByName(name, start_index, end_index)
  • RChunk:indexOf(node)
  • RChunk:getTextNotes()
  • RChunk:setTextNotes(str)
  • RChunk:addNode(node)
  • RChunk:removeNode(node)
  • RChunk:StripGUID()
  • RChunk:copy(parent) - Don't forget to delete any GUID
  • ReadRPP(filename)

RToken methods

  • RToken:new(o)
  • RToken:getString()
  • RToken:getNumber()
  • RToken:getBoolean()
  • RToken:setString(token)
  • RToken:setNumber(token)
  • RToken:setBoolean(b)
  • RToken:toSafeString(s)
  • Tokenize(line)

Contributors

  • mrlimbic - Original Java design, Lua conversion supervision
  • X-Raym - Lua conversion, optimization and documentation
  • Acendan - Lua conversion assistance

Links