Now the best way to do this reliably is to use Reaper's save as. But I thought to write a simple 'consolidate' script, and in the process a script to list samples used in a project. As written, the consolidate script only works with cygwin, but is straightforward to adapt to mac or Linux (just remove the cygpath step).
Samples Used
This lists all samples used in at least one .rpp
#!/usr/bin/env python
from subprocess import run, PIPE, DEVNULL
import os
import shutil
import re
import sys
samples = set()
def main():
args = sys.argv[1:]
if len(args) < 1:
print(f"{sys.argv[0]} <rpp file> [<rpp file> ...]")
exit(1)
for fn in args[1:]:
if not os.path.exists(fn):
print(f"{fn} does not exist")
continue
show(fn)
for s in sorted(samples):
print(s)
def show(fn):
with open(fn) as f:
rpp = f.read().splitlines()
while len(rpp) > 0:
line = rpp.pop(0)
if re.match(r"\s*<SOURCE WAVE",line):
line = rpp.pop(0)
m = re.match(r"(\s*FILE )(['`\"])(.*)\2",line)
if m:
wfn = m.group(3)
samples.add(wfn)
if __name__ == "__main__":
main()
This one does multiple files and a little formatting
#!/usr/bin/env python
from subprocess import run, PIPE, DEVNULL
import os
import shutil
import re
import sys
def main():
args = sys.argv[1:]
if len(args) < 1:
print(f"{sys.argv[0]} <rpp file> [<rpp file> ...]")
exit(1)
for fn in args[1:]:
if not os.path.exists(fn):
print(f"{fn} does not exist")
continue
show(fn)
def show(fn):
samples = set()
with open(fn) as f:
rpp = f.read().splitlines()
while len(rpp) > 0:
line = rpp.pop(0)
if re.match(r"\s*<SOURCE WAVE",line):
line = rpp.pop(0)
m = re.match(r"(\s*FILE )(['`\"])(.*)\2",line)
if m:
wfn = m.group(3)
samples.add(wfn)
if len(samples) > 0:
print(f"{fn}:")
for s in sorted(samples):
print(f" {s}")
if __name__ == "__main__":
main()```
Consolidate
Note that as written, this only works with cygwin. For mac or linux, just replace the cygpath(x)
with a function that returns its argument unchanged. For non-cygwin you need to see if the
path is an absolute or relative path by a different method than seeing if it starts with "/".
I assume that Reaper will only use a relative pathname when the pathname points to within the
project folder (i.e. it won't use '../' anywhere in the path).
#!/usr/bin/env python
from subprocess import run, PIPE, DEVNULL
import os
import shutil
import re
import sys
import platform
args = sys.argv[1:]
if len(args) != 2:
print(f"{sys.argv[0]} <rpp file> <out rpp file>")
exit(1)
ps = platform.system()
if "CYGWIN" in ps:
def cygpath(x):
m = run(["cygpath",x],stdout=PIPE)
return m.stdout.decode().rstrip("\r\n")
elif "Windows" in ps:
print(f"You need to use cygwin on Windows")
exit(2)
else:
def cygpath(x):
return x
with open(args[0]) as f:
rpp = f.read().splitlines()
outlines = []
while len(rpp) > 0:
line = rpp.pop(0)
if re.match(r"\s*<SOURCE WAVE",line):
outlines.append(line)
line = rpp.pop(0)
m = re.match(r"(\s*FILE )(['`\"])(.*)\2",line)
if not m:
outlines.append(line)
else:
wfn = m.group(3)
fn = cygpath(wfn)
if not fn.startswith("/"):
outlines.append(line)
continue
bfn = fn.split("/")[-1]
xs = bfn.split(".")
stem = ".".join(xs[:-1])
ext = xs[-1]
ofn = "Audio/"+bfn
if os.path.exists(ofn):
# find unused filename
i = 0
while os.path.exists(ofn):
ofn = f"Audio/{stem}_{i:04d}.{ext}"
if not os.path.isdir("Audio"):
os.makedirs("Audio",exist_ok=True)
shutil.copy(fn,ofn)
f = m.group(1)
q = m.group(2)
l = f"{f}{q}{ofn}{q}"
outlines.append(l)
print(ofn)
else:
outlines.append(line)
outrpp = "\r\n".join(outlines)
with open(args[1],"wt") as f:
f.write(outrpp)