Skip to content

Instantly share code, notes, and snippets.

@amstocker
Last active February 25, 2025 22:03
Show Gist options
  • Select an option

  • Save amstocker/c06390a0b97fd53d5d5fccc8ac0f3c13 to your computer and use it in GitHub Desktop.

Select an option

Save amstocker/c06390a0b97fd53d5d5fccc8ac0f3c13 to your computer and use it in GitHub Desktop.
import numpy as np
import wave
from fractions import Fraction
version = 3
sample_rate = 44100
samples_per_cycle = 512
amplitude_threshold = 1e-5
divisions = 8
multiplier = 4
decode_harmonics = lambda i: (
(i & 0b0001) >> 0,
(i & 0b0010) >> 1,
(i & 0b0100) >> 2,
(i & 0b1000) >> 3
)
fundamentals = {}
for i in range(16):
n = i.bit_count() + 1
x = decode_harmonics(i)
fundamentals[x] = { Fraction(1, 1): 1.0 / n }
for j in range(4):
if x[j] != 0:
fundamentals[x][Fraction(2 + j, 1)] = 1.0 / n
def refract(fundamental, voice, feedback, balance):
new_voice = { fraction: (1.0 - feedback) * amp for fraction, amp in fundamental.items() }
for fraction, amp in voice.items():
if amp < amplitude_threshold:
continue
first_harmonic = fraction / 2
first_harmonic_amp = feedback * (1 - balance) * amp
if first_harmonic in new_voice:
new_voice[first_harmonic] += first_harmonic_amp
else:
new_voice[first_harmonic] = first_harmonic_amp
second_harmonic = 3 * fraction / 2
second_harmonic_amp = feedback * balance * amp
if second_harmonic in new_voice:
new_voice[second_harmonic] += second_harmonic_amp
else:
new_voice[second_harmonic] = second_harmonic_amp
delta = 0
for fraction, amp in new_voice.items():
if fraction in voice:
delta += abs(amp - voice[fraction])
else:
delta += amp
return new_voice, delta
def generate(fundamental, feedback, balance, threshold=1e-4):
voice = fundamental
delta = float('inf')
while delta > threshold:
voice, delta = refract(fundamental, voice, feedback, balance)
return voice
for i in range(16):
for j, X in enumerate(np.linspace(0, 0.99, divisions)):
for k, Y in enumerate(np.linspace(0, 1, divisions)):
print(i, X, Y)
t = decode_harmonics(i)
voice = generate(fundamentals[t], float(X), float(Y))
x = np.linspace(0, multiplier, samples_per_cycle * multiplier)
output = 0.0 * x
for fraction, amp in voice.items():
frequency = float(fraction)
if frequency < 256 and (fraction * 4).is_integer():
output += amp * np.sin(2 * np.pi * frequency * x)
output /= np.max(np.abs(output))
output = (output * (2 ** 15 - 1)).astype("<h")
filename = "singlecycles/v{}/{}-{}-{}-{}_{}-{}.wav".format(version, *t, j, k)
with wave.open(filename, mode="wb") as wav_file:
wav_file.setnchannels(1)
wav_file.setsampwidth(2)
wav_file.setframerate(sample_rate)
wav_file.writeframes(output)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment