Multi-stage distortion: preamp, power amp, asymmetric clipping — v0.40.4

Single tanh was too mild. Now chains preamp gain → power amp clip →
asymmetric rectifier sag for proper overdrive/fuzz character.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 07:08:10 -04:00
parent 9b3cbd9065
commit 40901d603d
5 changed files with 22 additions and 4 deletions
+6
View File
@@ -2,6 +2,12 @@
All notable changes to PyTheory are documented here.
## 0.40.4
- **Distortion overhaul** — multi-stage clipping (preamp → power amp →
asymmetric rectifier) replaces single-stage tanh. Crunch, distorted,
orange crunch, and metal guitar presets now sound properly driven.
## 0.40.3
- **Crotales synth** — tuned bronze discs with long ring and bright harmonics
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "pytheory"
version = "0.40.3"
version = "0.40.4"
description = "Music Theory for Humans"
readme = "README.md"
license = "MIT"
+1 -1
View File
@@ -1,6 +1,6 @@
"""PyTheory: Music Theory for Humans."""
__version__ = "0.40.3"
__version__ = "0.40.4"
from .tones import Tone, Interval
from .systems import System, SYSTEMS, TET
+13 -1
View File
@@ -4902,7 +4902,19 @@ def _apply_distortion(samples, drive=1.0, mix=1.0):
"""
if mix <= 0 or drive <= 0:
return samples
driven = numpy.tanh(samples * drive)
# Multi-stage gain + clipping like a real amp:
# Stage 1: preamp gain — push the signal hard
stage1 = numpy.tanh(samples * drive)
# Stage 2: power amp — clip again with more gain for sustain and grit
stage2 = numpy.tanh(stage1 * drive * 0.5)
# Stage 3: at high drive, add asymmetric clipping (tube rectifier sag)
if drive > 3.0:
# Positive peaks clip harder than negative — asymmetric harmonics
driven = numpy.where(stage2 > 0,
numpy.tanh(stage2 * 1.5),
numpy.tanh(stage2 * 1.2))
else:
driven = stage2
return samples * (1 - mix) + driven * mix
Generated
+1 -1
View File
@@ -690,7 +690,7 @@ wheels = [
[[package]]
name = "pytheory"
version = "0.40.3"
version = "0.40.4"
source = { editable = "." }
dependencies = [
{ name = "rich" },