mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 06:46:14 +00:00
Add analog oscillator drift for synth warmth
Per-note random pitch wobble (gaussian, ±cents scaled by analog param) simulates analog oscillator instability. Applied to synth_lead (0.3), synth_pad (0.4), synth_bass (0.2), acid_bass (0.3), electric_piano (0.2), organ (0.15). Subtle enough to add life without sounding out of tune. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+9
-2
@@ -2660,7 +2660,7 @@ def _render_notes_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
filter_sustain=0.0, filter_amount=0.0,
|
||||
vel_to_filter=0.0, filter_q=0.707,
|
||||
synth_kwargs=None, temperament="equal",
|
||||
reference_pitch=440.0):
|
||||
reference_pitch=440.0, analog=0.0):
|
||||
"""Render a list of Notes into an existing buffer at the correct positions."""
|
||||
import random as _rnd
|
||||
|
||||
@@ -2692,6 +2692,12 @@ def _render_notes_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
pitches = [t.pitch(temperament=temperament, reference_pitch=reference_pitch) for t in note.tone.tones]
|
||||
else:
|
||||
pitches = [note.tone.pitch(temperament=temperament, reference_pitch=reference_pitch)]
|
||||
# Analog drift: slight random pitch offset per note,
|
||||
# simulating analog oscillator instability. Each note
|
||||
# gets a unique drift amount (±cents scaled by analog).
|
||||
if analog > 0:
|
||||
pitches = [hz * (2 ** (_rnd.gauss(0, analog * 5) / 1200))
|
||||
for hz in pitches]
|
||||
# Render oscillators (pass synth_kwargs for FM etc.)
|
||||
waves = [synth_fn(hz, n_samples=n_samples, **_skw)
|
||||
for hz in pitches]
|
||||
@@ -2956,7 +2962,8 @@ def render_score(score):
|
||||
filter_q=part.lowpass_q,
|
||||
synth_kwargs=synth_kwargs,
|
||||
temperament=_temperament,
|
||||
reference_pitch=_ref_pitch)
|
||||
reference_pitch=_ref_pitch,
|
||||
analog=part.analog)
|
||||
|
||||
# Apply effects — segmented if automation exists
|
||||
auto_points = part._get_automation_points()
|
||||
|
||||
@@ -26,12 +26,14 @@ INSTRUMENTS = {
|
||||
"detune": 6, "chorus": 0.2, "chorus_rate": 1.0,
|
||||
"lowpass": 4000, "saturation": 0.15,
|
||||
"tremolo_depth": 0.15, "tremolo_rate": 4.5,
|
||||
"analog": 0.2,
|
||||
},
|
||||
"organ": {
|
||||
"synth": "organ_synth", "envelope": "organ",
|
||||
"chorus": 0.2, "chorus_rate": 5.5,
|
||||
"lowpass": 5000,
|
||||
"phaser": 0.15, "phaser_rate": 0.4,
|
||||
"analog": 0.15,
|
||||
},
|
||||
"harpsichord": {
|
||||
"synth": "pluck_synth", "envelope": "none",
|
||||
@@ -227,6 +229,7 @@ INSTRUMENTS = {
|
||||
"delay": 0.2, "delay_time": 0.25, "delay_feedback": 0.3,
|
||||
"filter_attack": 0.01, "filter_decay": 0.3,
|
||||
"filter_sustain": 0.2, "filter_amount": 3000,
|
||||
"analog": 0.3,
|
||||
},
|
||||
"synth_pad": {
|
||||
"synth": "supersaw", "envelope": "pad",
|
||||
@@ -234,6 +237,7 @@ INSTRUMENTS = {
|
||||
"chorus": 0.2,
|
||||
"phaser": 0.3, "phaser_rate": 0.3,
|
||||
"sub_osc": 0.2,
|
||||
"analog": 0.4,
|
||||
},
|
||||
"synth_bass": {
|
||||
"synth": "saw", "envelope": "pluck",
|
||||
@@ -241,6 +245,7 @@ INSTRUMENTS = {
|
||||
"filter_attack": 0.005, "filter_decay": 0.2,
|
||||
"filter_sustain": 0.0, "filter_amount": 2000,
|
||||
"sub_osc": 0.4,
|
||||
"analog": 0.2,
|
||||
},
|
||||
"acid_bass": {
|
||||
"synth": "saw", "envelope": "pad",
|
||||
@@ -250,6 +255,7 @@ INSTRUMENTS = {
|
||||
"filter_attack": 0.005, "filter_decay": 0.15,
|
||||
"filter_sustain": 0.0, "filter_amount": 4000,
|
||||
"vel_to_filter": 3000,
|
||||
"analog": 0.3,
|
||||
},
|
||||
"808_bass": {
|
||||
"synth": "sine", "envelope": "pluck",
|
||||
@@ -2006,6 +2012,7 @@ class Part:
|
||||
phaser_rate: float = 0.5,
|
||||
cabinet: float = 0.0,
|
||||
cabinet_brightness: float = 0.5,
|
||||
analog: float = 0.0,
|
||||
fm_ratio: float = 2.0,
|
||||
fm_index: float = 3.0):
|
||||
self.name = name
|
||||
@@ -2051,6 +2058,7 @@ class Part:
|
||||
self.phaser_rate = phaser_rate
|
||||
self.cabinet = cabinet
|
||||
self.cabinet_brightness = cabinet_brightness
|
||||
self.analog = analog
|
||||
self.fm_ratio = fm_ratio
|
||||
self.fm_index = fm_index
|
||||
self._system = "western" # default, overridden by Score.part()
|
||||
@@ -2648,6 +2656,7 @@ class Score:
|
||||
phaser_rate: float = None,
|
||||
cabinet: float = None,
|
||||
cabinet_brightness: float = None,
|
||||
analog: float = None,
|
||||
fm_ratio: float = None,
|
||||
fm_index: float = None,
|
||||
fretboard=None) -> Part:
|
||||
@@ -2760,6 +2769,7 @@ class Score:
|
||||
"tremolo_depth": tremolo_depth, "tremolo_rate": tremolo_rate,
|
||||
"phaser": phaser, "phaser_rate": phaser_rate,
|
||||
"cabinet": cabinet, "cabinet_brightness": cabinet_brightness,
|
||||
"analog": analog,
|
||||
"fm_ratio": fm_ratio, "fm_index": fm_index,
|
||||
}
|
||||
for k, v in _locals.items():
|
||||
|
||||
Reference in New Issue
Block a user