Skip to content

Instantly share code, notes, and snippets.

@unbracketed
Last active July 11, 2019 04:26
Show Gist options
  • Save unbracketed/cea880dd291b673310411cde2d970a8b to your computer and use it in GitHub Desktop.
Save unbracketed/cea880dd291b673310411cde2d970a8b to your computer and use it in GitHub Desktop.
Utility for managing BOSS RC-300 memory slots and settings
import csv
import os
import shutil
import logging
log = logging.getLogger(__name__)
UPLOAD_QUEUE = "~/.rc300-queue.csv"
RC300_BASE = "/Volumes/BOSS_RC-300/ROLAND/"
DATA_DIR = "{}/DATA".format(RC300_BASE)
WAVE_DIR = "{}/WAVE".format(RC300_BASE)
MEMORY_HEADER_SIZE = 32
MEMORY_SLOT_SIZE = 748
MEMORY_SLOT_NAME_SIZE = 64
MEMORY_TRACK_NAME_SIZE = 40
class MemorySlot:
def __init__(self, name, track_1, track_2, track_3):
self.name = name
self.track_1 = track_1
self.track_2 = track_2
self.track_3 = track_3
class BossMemoryFile:
def __init__(self, path):
self.data = open(path, "rb").read()
def get_memory_slot(self, num):
"Gets data for a memory slot"
start = MEMORY_HEADER_SIZE + (MEMORY_SLOT_SIZE * (num - 1))
end = start + MEMORY_SLOT_SIZE
slot = self.data[start:end]
name_chrs = []
for i in range(0, 64, 4):
name_chrs.append(chr(slot[i]))
name = "".join(name_chrs)
track_1, track_2, track_3 = self.get_slot_tracks(num)
return MemorySlot(name.strip(), track_1, track_2, track_3)
def get_slot_tracks(self, num):
start = (
MEMORY_HEADER_SIZE
+ (99 * MEMORY_SLOT_SIZE)
+ ((num - 1) * MEMORY_TRACK_NAME_SIZE) * 3
)
end = start + MEMORY_TRACK_NAME_SIZE
track_1 = self.data[start:end]
start += MEMORY_TRACK_NAME_SIZE
end += MEMORY_TRACK_NAME_SIZE
track_2 = self.data[start:end]
start += MEMORY_TRACK_NAME_SIZE
end += MEMORY_TRACK_NAME_SIZE
track_3 = self.data[start:end]
return track_1, track_2, track_3
def rename_memory_slot(self, num, name):
"""Updates name for a memory slot"""
# TODO max length
buf = bytearray(MEMORY_TRACK_NAME_SIZE)
slot = self.get_memory_slot(num)
def as_csv(self):
with open("BOSS_RC-300_Memory.csv", "w") as outfile:
writer = csv.writer(outfile)
for i in range(1, 100):
slot = self.get_memory_slot(i)
writer.writerow((slot["name"],))
def get_memory_file():
""""Copy MEMORY.RC3 to local system"""
shutil.copyfile(os.path.join(DATA_DIR, "MEMORY.RC3"), ".")
def get_system_file():
"""Copy SYSTEM.RC3 to local system"""
shutil.copyfile(os.path.join(DATA_DIR, "SYSTEM.RC3"), ".")
def push_memory_file():
"""Copy local MEMORY.RC3 to RC-300"""
shutil.copyfile("MEMORY.RC3", DATA_DIR)
def push_system_file():
""""Copy local SYSTEM.RC3 to RC-300"""
shutil.copyfile("SYSTEM.RC3", DATA_DIR)
def push_track(slot, track_num, path):
# Todo verify source 16bit stereo 44kHz
dst = "{}/{}/{}_{}".format(WAVE_DIR, slot, slot, track_num)
shutil.copyfile(path, dst)
def do_push_from_csv(slots, dry_run=True):
with open(UPLOAD_QUEUE, "r") as queue:
reader = csv.reader(queue)
for (slot_num, name, track_1, track_2, track_3) in reader:
slot = slots.get_memory_slot(slot_num)
if slot.name != name:
log.warning("Overwriting name")
if not dry_run:
slots.rename_memory_slot(slot_num, name)
# TODO DOS shorten style
# TODO clear existing track
if track_1 and track_1 != slot.track_1:
log.warning("Overwriting track 1")
if not dry_run:
push_track(slot_num, 1, track_1)
if track_2 and track_2 != slot.track_2:
log.warning("Overwriting track 2")
if not dry_run:
push_track(slot_num, 2, track_2)
if track_3 and track_3 != slot.track_3:
log.warning("Overwriting track 3")
if not dry_run:
push_track(slot_num, 3, track_3)
"""
First 32 bytes are the header / ignore
---
Memory Slots:
748 bytes per slot
1 - 64 Name
---
File / track list:
40 bytes per name, some have additional chars...
"""
if __name__ == "__main__":
slots = BossMemoryFile("MEMORY.RC3")
slots.as_csv()
# TODO full backup
do_push_from_csv(slots)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment