mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 23:00:20 +00:00
Add reference_pitch to Score and playback pipeline
Score(reference_pitch=415.0, temperament="meantone") renders an entire piece at Baroque pitch with historical tuning. Flows through to all .pitch() calls in both normal and legato renderers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
-8
@@ -1971,7 +1971,8 @@ def _render_notes_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
filter_attack=0.01, filter_decay=0.3,
|
||||
filter_sustain=0.0, filter_amount=0.0,
|
||||
vel_to_filter=0.0, filter_q=0.707,
|
||||
synth_kwargs=None, temperament="equal"):
|
||||
synth_kwargs=None, temperament="equal",
|
||||
reference_pitch=440.0):
|
||||
"""Render a list of Notes into an existing buffer at the correct positions."""
|
||||
import random as _rnd
|
||||
|
||||
@@ -2000,9 +2001,9 @@ def _render_notes_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
if n_samples > 0 and start >= 0:
|
||||
# Get pitches
|
||||
if hasattr(note.tone, 'tones'):
|
||||
pitches = [t.pitch(temperament=temperament) for t in note.tone.tones]
|
||||
pitches = [t.pitch(temperament=temperament, reference_pitch=reference_pitch) for t in note.tone.tones]
|
||||
else:
|
||||
pitches = [note.tone.pitch(temperament=temperament)]
|
||||
pitches = [note.tone.pitch(temperament=temperament, reference_pitch=reference_pitch)]
|
||||
# Render oscillators (pass synth_kwargs for FM etc.)
|
||||
waves = [synth_fn(hz, n_samples=n_samples, **_skw)
|
||||
for hz in pitches]
|
||||
@@ -2088,7 +2089,7 @@ def _render_notes_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
def _render_legato_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
synth_fn, envelope_tuple, volume, bpm,
|
||||
glide_time=0.0, swing=0.0, tempo_map=None,
|
||||
temperament="equal"):
|
||||
temperament="equal", reference_pitch=440.0):
|
||||
"""Render notes as one continuous waveform with pitch glide.
|
||||
|
||||
Instead of rendering each note separately with its own envelope,
|
||||
@@ -2118,9 +2119,9 @@ def _render_legato_to_buf(notes, buf, samples_per_beat, total_samples,
|
||||
vel = getattr(note, 'velocity', 100)
|
||||
if note.tone is not None:
|
||||
if hasattr(note.tone, 'tones'):
|
||||
hz = note.tone.tones[0].pitch(temperament=temperament)
|
||||
hz = note.tone.tones[0].pitch(temperament=temperament, reference_pitch=reference_pitch)
|
||||
else:
|
||||
hz = note.tone.pitch(temperament=temperament)
|
||||
hz = note.tone.pitch(temperament=temperament, reference_pitch=reference_pitch)
|
||||
events.append((start, end, hz, vel))
|
||||
else:
|
||||
events.append((start, end, 0, vel)) # rest
|
||||
@@ -2239,13 +2240,14 @@ def render_score(score):
|
||||
synth_kwargs["mod_ratio"] = part.fm_ratio
|
||||
synth_kwargs["mod_index"] = part.fm_index
|
||||
_temperament = getattr(score, 'temperament', 'equal')
|
||||
_ref_pitch = getattr(score, 'reference_pitch', 440.0)
|
||||
if part.legato:
|
||||
_render_legato_to_buf(
|
||||
part.notes, part_buf, samples_per_beat, total_samples,
|
||||
synth_fn, env_tuple, part.volume, score.bpm,
|
||||
glide_time=part.glide, swing=effective_swing,
|
||||
tempo_map=tempo_map if has_tempo_changes else None,
|
||||
temperament=_temperament)
|
||||
temperament=_temperament, reference_pitch=_ref_pitch)
|
||||
else:
|
||||
_render_notes_to_buf(
|
||||
part.notes, part_buf, samples_per_beat, total_samples,
|
||||
@@ -2265,7 +2267,8 @@ def render_score(score):
|
||||
vel_to_filter=part.vel_to_filter,
|
||||
filter_q=part.lowpass_q,
|
||||
synth_kwargs=synth_kwargs,
|
||||
temperament=_temperament)
|
||||
temperament=_temperament,
|
||||
reference_pitch=_ref_pitch)
|
||||
|
||||
# Apply effects — segmented if automation exists
|
||||
auto_points = part._get_automation_points()
|
||||
|
||||
+2
-1
@@ -2074,7 +2074,7 @@ class Score:
|
||||
|
||||
def __init__(self, time_signature="4/4", bpm=120, swing: float = 0.0,
|
||||
drum_humanize: float = 0.15, system: str = "western",
|
||||
temperament: str = "equal"):
|
||||
temperament: str = "equal", reference_pitch: float = 440.0):
|
||||
if isinstance(time_signature, str):
|
||||
self.time_signature = TimeSignature.from_string(time_signature)
|
||||
else:
|
||||
@@ -2083,6 +2083,7 @@ class Score:
|
||||
self.swing = swing
|
||||
self.system = system
|
||||
self.temperament = temperament
|
||||
self.reference_pitch = reference_pitch
|
||||
self._drum_humanize = drum_humanize
|
||||
self.notes: list[Note] = []
|
||||
self.parts: dict[str, Part] = {}
|
||||
|
||||
Reference in New Issue
Block a user