Skip to content

Instantly share code, notes, and snippets.

@matthewryanscott
Last active January 11, 2026 21:08
Show Gist options
  • Select an option

  • Save matthewryanscott/52c3d3cc61fb6ef1ea9889aa7c9ce3dc to your computer and use it in GitHub Desktop.

Select an option

Save matthewryanscott/52c3d3cc61fb6ef1ea9889aa7c9ce3dc to your computer and use it in GitHub Desktop.
SunVox pattern expander

Written using Claude Code and z.ai's GLM-4.7 model using this prompt:

Write a script called "pattern_expander.py" that takes two arguments: a sunvox project filename, and a factor (positive int).

The script will use radiant-voices (rv) to open the sunvox file, and for all patterns it will do this:

  • expand the length of the pattern by the factor
  • rewrite the pattern data so that the pattern data is expanded to fill the new pattern length (e.g. for factor of 2, 1 new blank line between each existing line... for factor of 3, 2 new blank lines, etc.)
  • multiply the "x" position of the pattern by the factor

For all pattern clones, it only needs to multiply the x position by the factor.

If the project's TPL is evenly divisible by the factor, then divide the TPL by the factor. For example if TPL is 6 and factor is 2, TPL becomes 3. If factor is 3 TPL becomes 2. If factor is 4 or 5 then TPL doesn't change and a warning is shown (but script still works otherwise) and if factor is 6 then TPL becomes 1.

The script will write the expanded version out to ".expanded-by-.sunvox"

You can use "color_dreams.sunvox" as a file to test against.

# /// script
# requires-python = ">=3.11"
# dependencies = [
# "radiant-voices>=2.0.0",
# ]
# ///
#!/usr/bin/env python3
"""Expand SunVox project patterns by a given factor.
For each pattern:
- Expand the length by the factor
- Insert blank lines between existing lines to fill the new length
- Multiply the x position by the factor
For pattern clones:
- Only multiply the x position by the factor
For TPL (ticks per line):
- Divide by factor if evenly divisible, otherwise show warning
"""
from argparse import ArgumentParser
from pathlib import Path
from rv.api import read_sunvox_file
from rv.note import Note
def expand_pattern_data(pattern, factor):
"""Expand pattern data by inserting blank lines between existing lines."""
original_data = pattern.data
new_lines = pattern.lines * factor
new_data = []
for original_line_idx in range(pattern.lines):
# Add the original line
new_data.append(original_data[original_line_idx])
# Add (factor - 1) blank lines after it
for _ in range(factor - 1):
blank_line = [Note(pattern=pattern) for _ in range(pattern.tracks)]
new_data.append(blank_line)
pattern._data = new_data
pattern.lines = new_lines
def expand_pattern(pattern, factor):
"""Expand a single pattern by the given factor."""
# Expand length and data
expand_pattern_data(pattern, factor)
# Multiply x position
pattern.x *= factor
def expand_pattern_clone(clone, factor):
"""Expand a pattern clone by the given factor (only x position)."""
clone.x *= factor
def expand_project(project, factor):
"""Expand all patterns in a project by the given factor."""
# Handle TPL
original_tpl = project.initial_tpl
if original_tpl % factor == 0:
project.initial_tpl = original_tpl // factor
print(f"TPL: {original_tpl} -> {project.initial_tpl}")
else:
print(f"Warning: TPL ({original_tpl}) not divisible by {factor}, leaving unchanged")
# Process all patterns
pattern_count = 0
clone_count = 0
for pattern in project.patterns:
if pattern is None:
continue
# Check if it's a clone by checking the type
from rv.pattern import PatternClone
if isinstance(pattern, PatternClone):
expand_pattern_clone(pattern, factor)
clone_count += 1
else:
expand_pattern(pattern, factor)
pattern_count += 1
print(f"Expanded {pattern_count} patterns and {clone_count} clones by factor of {factor}")
def main():
parser = ArgumentParser(description="Expand SunVox project patterns by a given factor")
parser.add_argument("sunvox_file", type=str, help="Path to the SunVox project file")
parser.add_argument("factor", type=int, help="Positive integer expansion factor")
args = parser.parse_args()
if args.factor <= 0:
parser.error("Factor must be a positive integer")
input_path = Path(args.sunvox_file)
if not input_path.exists():
parser.error(f"File not found: {input_path}")
print(f"Reading project from: {input_path}")
project = read_sunvox_file(input_path)
# Verify it's a Project (not a Synth)
from rv.project import Project
if not isinstance(project, Project):
parser.error(f"Input file is not a SunVox project (got {type(project).__name__})")
print(f"Original project has {len(project.patterns)} patterns")
expand_project(project, args.factor)
# Generate output filename
output_path = input_path.with_name(f"{input_path.stem}.expanded-by-{args.factor}{input_path.suffix}")
print(f"Writing expanded project to: {output_path}")
with open(output_path, "wb") as f:
project.write_to(f)
print("Done!")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment