Void & Alcota
Привет, запустила небольшой скрипт, который сопоставляет разложение на простые множители с микротональными интервалами — вроде генератора гамм, основанного на логике. У тебя есть код, который превращает твои собственные паттерны в звук?
Конечно, есть простой способ превратить числовую последовательность в звуковую волну – сопоставить каждое число с частотой и сложить синусоиды. Вот минимальный пример, который берёт список простых чисел, преобразует каждое в частоту (используя простое масштабирование) и воспроизводит короткий тон для каждого. Потребуются numpy и sounddevice; если их нет, сначала установи их.
import numpy as np, sounddevice as sd
# Пример последовательности – первые несколько простых чисел
primes = [2,3,5,7,11,13]
# Преобразуем каждое простое число в частоту (Гц).
# Здесь мы используем базовую частоту 220 Гц и удваиваем её для каждого простого числа.
frequencies = [220 * p for p in primes]
duration = 0.3 # секунды на ноту
sample_rate = 44100 # выборки в секунду
def note(freq, dur):
t = np.linspace(0, dur, int(sample_rate*dur), endpoint=False)
return 0.3 * np.sin(2*np.pi*freq*t)
# Собираем полную волну, объединяя ноты
wave = np.concatenate([note(f, duration) for f in frequencies])
# Воспроизводим её
sd.play(wave, sample_rate)
sd.wait()
Здорово, но этот твой вариант с прямыми 220х простыми числами упустит все микротональные нюансы, которые мне нравятся. Может, попробуешь изменить масштабирование, чтобы попасть в сетку 17-тоновой равномерной темперации, а не использовать чистые октавы? И добавь небольшой ADSR-конверт, чтобы эти синусоидальные всплески не звучали как лазерные разрезы. Продолжай экспериментировать, пусть простые числа будут основой, а микротоны – сутью.
Понял, переделаю масштабирование, чтобы каждый тон попадал на ступень в 17-ступенчатой равномерной системе (E17), и добавлю небольшой ADSR-конверт, чтобы сгладить резкость. Вот обновленный фрагмент:
import numpy as np, sounddevice as sd
# Базовая частота для первой ступени E17 (до в 17-ступенчатой)
base = 220.0
steps = 17
# Генерируем таблицу соответствия: 1 ступень = 2^(1/ступени)
ratio = 2**(1/steps)
# Первые несколько простых чисел, как основа
primes = [2,3,5,7,11,13]
# Отображаем каждое простое число на микротональную ступень, возводя в степень
frequencies = [base * (ratio ** (p-1)) for p in primes]
duration = 0.25 # секунды на ноту
sr = 44100
def adsr(freq, dur, attack=0.01, decay=0.05, sustain=0.8, release=0.05):
t = np.linspace(0, dur, int(sr*dur), endpoint=False)
env = np.ones_like(t)
# attack
a = int(attack*sr)
env[:a] = np.linspace(0, 1, a)
# decay
d = int(decay*sr)
env[a:a+d] = np.linspace(1, sustain, d)
# sustain
s = int((dur-attack-decay-release)*sr)
env[a+d:a+d+s] = sustain
# release
r = int(release*sr)
env[-r:] = np.linspace(sustain, 0, r)
return 0.3 * np.sin(2*np.pi*freq*t) * env
wave = np.concatenate([adsr(f, duration) for f in frequencies])
sd.play(wave, sr)
sd.wait()
Переход с линейной октавы к настоящей E17 – впечатляет, кривая ADSR выглядит аккуратно, хотя в сегменте са sustain иногда может не хватить пространства, если нота очень короткая. Попробуй немного ограничить release, чтобы он не выходил за пределы буфера. И ещё, подумай добавить немного де-тюнинга на каждый основной шаг – у простых чисел своя внутренняя напряжённость, которую идеально микротональное деление не передаёт полностью. Следи за первыми нотами, они будут самыми низкими, и могут заклинить, если слишком сильно давить на басовую частоту в 220 Гц. И помни: иногда незаконченный мотив – это всего лишь подсказка для следующей импровизации.