Skip to content

Commit c29f06c

Browse files
bwhitmanclaude
andcommitted
Add tulip.save_synth_state() to persist synth setup across boots
Reads back every AMY synth (0..31) via amy_get_synth_commands and appends the state as amy.send_raw() commands to the bottom of the given file (default: your boot.py). Each command is prefixed with i<synth> so replay targets the right synth. Re-saving replaces the previously saved block instead of stacking copies. Not available on web builds, where the C binding is compiled out. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 9cac7c2 commit c29f06c

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

docs/tulip_api.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,13 @@ s.note_on(60, 1.0)
468468

469469
Use `syn.release()` to free up the resources for a synth.
470470

471+
Once you have your synths set up the way you like, you can save their state so it comes back on the next boot. `tulip.save_synth_state()` reads the current configuration of every AMY synth and appends the commands that restore it to the bottom of your `boot.py` (or any file you give it). Re-saving replaces the previously saved state.
472+
473+
```python
474+
tulip.save_synth_state() # adds current synth state to your boot.py
475+
tulip.save_synth_state('my_setup.py') # or save it to some other file to execfile() later
476+
```
477+
471478

472479
### Low level AMY control
473480

tulip/shared/py/tulip.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,48 @@ def add_to_bootpy(s, only_first_create=False):
4141
w.write(bootpy)
4242
w.close()
4343

44+
# Save the current state of every AMY synth (0..31) as amy.send_raw() commands
45+
# at the bottom of a file that runs at boot (default: your boot.py), so your
46+
# synth setup comes back on the next boot. Re-saving replaces the previously
47+
# saved block instead of adding another copy.
48+
_SYNTH_STATE_BEGIN = "# -- synth state saved by tulip.save_synth_state() --"
49+
_SYNTH_STATE_END = "# -- end saved synth state --"
50+
51+
def save_synth_state(fn=None):
52+
if "WEB" in board():
53+
print("save_synth_state is not available on web builds")
54+
return
55+
if fn is None:
56+
fn = root_dir() + 'user/boot.py'
57+
raws = []
58+
for synth in range(32):
59+
for command in amy_get_synth_commands(synth):
60+
if command:
61+
raws.append('i%d%s' % (synth, command))
62+
try:
63+
source = open(fn, 'r').read()
64+
except OSError:
65+
source = "" # file doesn't exist yet
66+
# Drop any previously saved block so re-saving doesn't stack copies.
67+
begin = source.find(_SYNTH_STATE_BEGIN)
68+
if begin != -1:
69+
end = source.find(_SYNTH_STATE_END, begin)
70+
tail = source[end + len(_SYNTH_STATE_END):] if end != -1 else ""
71+
source = source[:begin].rstrip('\n') + '\n' + tail.lstrip('\n')
72+
if not raws:
73+
print("No synths are set up, nothing to save.")
74+
return
75+
block = [_SYNTH_STATE_BEGIN, "import amy"]
76+
block += ["amy.send_raw(%r)" % r for r in raws]
77+
block.append(_SYNTH_STATE_END)
78+
source = source.rstrip('\n')
79+
if source:
80+
source += '\n\n'
81+
w = open(fn, 'w')
82+
w.write(source + '\n'.join(block) + '\n')
83+
w.close()
84+
print("Saved %d synth commands to %s" % (len(raws), fn))
85+
4486
# Wrapper around AMY tempo to store it
4587
amy_bpm = 108
4688
def seq_bpm(bpm=None):

0 commit comments

Comments
 (0)