from scamp import *
import random
import math
# ---------------------------------------------
# SCAMP session setup
# ---------------------------------------------
s = Session(100) # 100 bpm ≈ 8 minutes of generative material
# Define the instruments
piano = s.new_part("piano")
guitar = s.new_part("guitar")
cello = s.new_part("cello")
violin = s.new_part("violin")
flute = s.new_part("flute")
trumpet = s.new_part("trumpet")
timpani = s.new_part("timpani")
# ---------------------------------------------
# Base scales (in semitones from C4)
# ---------------------------------------------
major_scale = [0, 2, 4, 5, 7, 9, 11, 12]
minor_scale = [0, 2, 3, 5, 7, 8, 10, 12]
# ---------------------------------------------
# Prime factorization utility
# ---------------------------------------------
def prime_factors(n):
"""Return the list of prime factors of n."""
factors = []
d = 2
while n > 1:
while n % d == 0:
factors.append(d)
n //= d
d += 1
if d * d > n and n > 1:
factors.append(n)
break
return factors
# ---------------------------------------------
# Algebraic "factorization" patterns
# Inspired by Baldor's algebraic forms:
# (a + b)^2 = a^2 + 2ab + b^2, etc.
# We'll simulate this by creating harmonic & rhythmic relations.
# ---------------------------------------------
def algebraic_pattern(a, b, variation=1):
"""Generate a list of derived numbers based on algebraic factorization ideas."""
if variation == 1:
# Quadratic expansion style
return [a*a, 2*a*b, b*b]
elif variation == 2:
# Difference of squares
return [a+b, a-b]
elif variation == 3:
# Cubic relation
return [a**3, 3*a*a*b, 3*a*b*b, b**3]
else:
# Randomized polynomial
return [a*b + a + b, a*a - b, b*b - a]
# ---------------------------------------------
# Convert numbers into musical notes
# ---------------------------------------------
def number_to_note(base_pitch, scale, number):
idx = number % len(scale)
return base_pitch + scale[idx]
# ---------------------------------------------
# Phrase generator for one instrument
# ---------------------------------------------
def generate_phrase(part, scale, base_pitch, numbers, dur_base=0.5, dynamic_shift=0.0):
"""Generate a musical phrase based on number factorization."""
for n in numbers:
factors = prime_factors(abs(n)) if n != 0 else [1]
for f in factors:
note = number_to_note(base_pitch, scale, f)
dur = dur_base * (1 + (f % 3) * 0.5)
vol = 0.4 + (f % 5) * 0.1 + dynamic_shift
part.play_note(note, vol, dur)
# ---------------------------------------------
# Main composition
# ---------------------------------------------
def composition():
total_sections = 8 # 8 sections ≈ 8 minutes
section_duration = 60 # seconds per section
instruments = [piano, guitar, cello, violin, flute, trumpet, timpani]
base_pitches = [60, 55, 48, 67, 72, 58, 42]
base_durations = [0.6, 0.7, 0.8, 0.5, 0.4, 0.7, 1.0]
dynamic_offsets = [0.0, 0.1, 0.15, 0.05, 0.2, 0.25, 0.3]
for section in range(total_sections):
# Switch between major and minor modes for contrast
scale = major_scale if section % 2 == 0 else minor_scale
# Generate algebraic source numbers
a, b = random.randint(3, 12), random.randint(4, 15)
variation = (section % 4) + 1
base_numbers = algebraic_pattern(a, b, variation)
# Create additional rhythmic diversity
numbers = []
for n in base_numbers:
numbers += [n + random.randint(-5, 5) for _ in range(6)]
# Each instrument interprets numbers slightly differently
for i, part in enumerate(instruments):
s.fork(
generate_phrase,
args=(part, scale, base_pitches[i], numbers[::-1] if i % 2 else numbers, base_durations[i], dynamic_offsets[i])
)
# Keep section gap under 4 seconds
s.wait(section_duration - 3 + random.uniform(-1, 1))
# ---------------------------------------------
# Run the composition
# ---------------------------------------------
composition()
My new SCAMP_Python work:
Casio2.