Dynamically Generate SCAMP Code (Boilerplate)

I had an idea today of something that might be inspiring for others and was able to throw it together in SCAMP.

Given a YAML config file:

---
name: "Scamp the Scamp"
composer: "Scampy McScampFace"
instruments: "Piano, Bass, Goblin, Woodblock"
tempo: 120
data: #you could always do normalization within SCAMP
  y: "1, 1, 1, 1"
  x: "40, 54, 34, 56"
  z: "1, 2, 1, 2"

…could I dynamically generate SCAMP code?

Here is some boilerplate I came up with:

#!/usr/bin/env python3

from scamp import *
#from scamp_extensions import * # not used in this boilerplate 
import sys
import yaml

DEBUG = False 

# Load YAML Config
with open("config.yaml", "r") as config_file:
    config = yaml.safe_load(config_file)

# Parse YAML Config
title = config['name']
composer = config['composer']
instruments = config['instruments'].split(',')
tempo = config['tempo']
notes = config['data']['x'].split(',')
durs = config['data']['y'].split(',')
vols = config['data']['z'].split(',')

# Check if all lists are the same length
if len(notes) != len(durs) or len(notes) != len(vols):
    print("ERROR: x, y, and z must be the same length.")
    sys.exit(1)

if DEBUG:
    print(config)

s = Session(tempo=tempo)
s.fast_forward_to_beat(99999)

# Dynamically assign instruments
inst_dict = {}
for i in range(len(instruments)):
    key = str("inst" + str(i))
    inst_dict[key] = instruments[i]
for key, value in inst_dict.items():
    if DEBUG:
        print(f'{key} = s.new_part("{value}")')
    exec(f'{key} = s.new_part("{value}")')

if DEBUG:
    print(inst_dict)

def compose(inst, notes, durs, vols):
    """This is where the magic could happen"""
    for i in range(len(notes)):
        if DEBUG:
            print(f'COMPOSE: {inst}.play_note({notes[i]}, {durs[i]}, {vols[i]})')
        exec(f'{inst}.play_note({notes[i]}, {durs[i]}, {vols[i]})')

# Score
s.start_transcribing()
for i in inst_dict.keys():
    s.fork(compose, args=[i, notes, durs, vols])

s.wait_for_children_to_finish()
performance = s.stop_transcribing()
performance.to_score(title=title, composer=composer).show()

Note: This could be sped up by using compile() but I am not upset with the current speed (insert Donald Knuth quote here)

To add/remove instruments or change the data, just edit the YAML config.

Cheers!
Stewart

1 Like

Thank you, Stewart, for this wonderful silliness.

1 Like

Thanks - I found your boilerplate and found it very useful for getting me to the next step of my little project. As a Python (and scamp) newbie, it was really helpful to have your code to help me understand how to make something like this work.
Thanks!
-Rebecca

2 Likes