Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding “quasi-harmonic” inharmonic timbres based on L-systems #845

Open
1 of 2 tasks
arseniiv opened this issue Jan 23, 2025 · 4 comments
Open
1 of 2 tasks

Adding “quasi-harmonic” inharmonic timbres based on L-systems #845

arseniiv opened this issue Jan 23, 2025 · 4 comments

Comments

@arseniiv
Copy link
Contributor

arseniiv commented Jan 23, 2025

Status:

  • Add pre-baked spectra.
  • Add spectrum generator someday later to generate them in the app itself.

Proposal

There’s a method of making timbres that have constant partial density (in the limit) like harmonic or harmonic subgroup timbres do, but allowing irrational numbers being a suitable equave when partials of the note one equave up get to be exactly a subset of partials of the lower note.

This allows for a reasonably full timbral quality, at the same time without overcrowding higher partials into semblance of white noise, like in cymbals or gongs. Scaling property allows to have a weak form of JI: any interval between two partials, especially lower partials, gets found in the higher partials lots of times, and not only this, but transposing by this interval will actually match many pairs of partials, even if to a lesser degree than when using the equave.

An example “quasi-JI” scale to use with golden timbres 2…5: here. This scale was formed from intervals between partials of timbre 4 (IIRC) but in reality they’re kind of universal there. In note comments, “#n” means partial number n, where #‍1 = 1, #‍2 = ϕ. Notably, the scale allowed itself to be symmetric and also have rational 3/2. If adding further prominent partial ratios, one encounters 4/3 as well, and also 2/1 is easy to be found a lot between notes of this scale. That’s no surprise because ϕ is the algebraic irrational with the simplest defining equation using integer coefficients, so happy accidents happen probably most often in the case of ℤ[ϕ] to which the partials of golden timbres belong.

Framework and examples

The framework is simple, an L-system. We represent differences between partials, starting from zero, by letters and grow the string of letters until it’s long enough, applying the same replacements in parallel to each letter once for each iteration. Each system here is such that each iteration actually scales the whole set of partials by some fixed factor S and inserts new partials in between, including maintaining that the fundamental 1 is always present. Below, E always means a difference of 1, so the string always starts with E.

Examples (rulesets are in bold):

  1. For a familiar one, a harmonic timbre has the simplest system like E → EE, or E → Eᵏ for any positive integer k. An odd-harmonic timbre results from E → ED, D → DDD where D = 2: the rules scale by 3, so E and D subdivide accordingly [↓note 1].

  2. Now, a dense golden timbre has the simplest system: start from E, replacements are E → EF, F → E, and E again means 1 and F = ϕ − 1 = (√5 − 1)/2 ≈ 0.618. Scaling is by ϕ.

  3. We can actually have many timbres that look like this by choosing E → FE some times and E → EF other times, even on letter-by-letter basis (vs. each whole string iteration), but we need to retain the first E in the string so we can actually make a system like this for one of the other deterministic alternatives: E → EF, Ɛ → FƐ, F → Ɛ — the first E gets remembered as untouchable, all others get to be Ɛ (= 1 too) instead and replace in mirrored way.

  4. Having differences of 0.6 is a bit too much for my taste, so there’s also a rarefied version: just blow everything up by ϕ, don’t subdivide this time but again add 1 because we need it. Equivalently this can be achieved by: E → EF, F → Φ, Φ → FΦ where Φ = ϕ.

  5. And again, we can have a mirrored replacement variant for variety: E → EF, F → Φ, Φ → ΦF.

  6. A silver timbre scales by 1 + √2, it goes E → ER, R → EER where R = √2 (again, EER can be permuted without changing the overal flavor of the timbre).

So when we iteratively generate about 128 letters in the string, then they get replaced with numbers, and the sequence of their partial sums give timbre’s partials.

Notes

  1. As well as harmonic, an odd-harmonic timbre can result from other systems too, just using odd scale factors k (instead of arbitrary k), like 5: E → EDD, D → DDDDD. LIkewise, a golden timbre can be made using scale factors of ϕᵏ; and a silver one as well; and all other interesting timbres of this kind—but again that’s just unnecessary more complicated for no one’s sake.

Reference implementation

...in Python 12 (v10 probably suffices too) is here, with a couple of simple tests and heaps of sanity checks in the algorithm itself.

Naming

I have no idea how to better name the entire class of these L-system timbres:

  • Quasi-harmonic timbres in all honesty is probably either already in use, or will be contended because one can think up many generalizations of timbres suitably close to harmonic.
  • MOS-timbres doesn’t fly because these are actually both more diverse and don’t encompass all possible timbres where partial differences form a MOS pattern (either periodic or truly quasiperiodic).
  • L-timbres is too esoteric though true to their formation. Even if we allow stochastic rules, this still applies. (But for SW this is irrelevant because we need deterministic timbres that are reproducible between builds; if necessary, we can add some deterministic variation into a timbre by complicating the ruleset and adding more letter variants for the same numeric difference.)
    Help I’m bad at naming!

And now I’m off to making a couple of pre-baked timbres and adding them in the inharmonic section.

@arseniiv
Copy link
Contributor Author

arseniiv commented Jan 23, 2025

Dumping the partials with extra info here for convenience:

EFƐƐFƐFƐƐFƐƐFƐFƐƐFƐFƐƐFƐƐFƐ (27)
golden-dense1 [
  1.00000, 1.61803, 2.61803, 3.61803, 4.23607, 5.23607, 5.85410, 6.85410, 7.85410,
  8.47214, 9.47214, 10.47214, 11.09017, 12.09017, 12.70820, 13.70820, 14.70820, 15.32624,
  16.32624, 16.94427, 17.94427, 18.94427, 19.56231, 20.56231, 21.56231, 22.18034, 23.18034
]
EFƐFƐƐFƐFƐƐFƐƐFƐFƐƐFƐFƐƐFƐƐ (27)
golden-dense2 [
  1.00000, 1.61803, 2.61803, 3.23607, 4.23607, 5.23607, 5.85410, 6.85410, 7.47214,
  8.47214, 9.47214, 10.09017, 11.09017, 12.09017, 12.70820, 13.70820, 14.32624, 15.32624,
  16.32624, 16.94427, 17.94427, 18.56231, 19.56231, 20.56231, 21.18034, 22.18034, 23.18034
]
EFƐΦƐΦΦƐΦƐΦΦƐΦΦƐΦƐ (18)
golden-sparse1 [
  1.00000, 1.61803, 2.61803, 4.23607, 5.23607, 6.85410, 8.47214, 9.47214, 11.09017,
  12.09017, 13.70820, 15.32624, 16.32624, 17.94427, 19.56231, 20.56231, 22.18034, 23.18034
]
EFƐΦΦƐΦƐΦΦƐΦΦƐΦƐΦ (17)
golden-sparse2 [
  1.00000, 1.61803, 2.61803, 4.23607, 5.85410, 6.85410, 8.47214, 9.47214, 11.09017,
  12.70820, 13.70820, 15.32624, 16.94427, 17.94427, 19.56231, 20.56231, 22.18034
]
EREREEREREEREREREERE (20)
silver [
  1.00000, 2.41421, 3.41421, 4.82843, 5.82843, 6.82843, 8.24264, 9.24264, 10.65685,
  11.65685, 12.65685, 14.07107, 15.07107, 16.48528, 17.48528, 18.89949, 19.89949, 20.89949,
  22.31371, 23.31371
]
EPpEpEPEpEPpEPEpEPpEpE (22)
plastic [
  1.00000, 1.75488, 3.07960, 4.07960, 5.40431, 6.40431, 7.15919, 8.15919, 9.48391,
  10.48391, 11.23879, 12.56350, 13.56350, 14.31838, 15.31838, 16.64310, 17.64310, 18.39798,
  19.72270, 20.72270, 22.04741, 23.04741
]
EGEgEGEGEEGEgEGEgEGEG (21)
supergolden [
  1.00000, 2.14790, 3.14790, 4.61347, 5.61347, 6.76137, 7.76137, 8.90927, 9.90927,
  10.90927, 12.05717, 13.05717, 14.52274, 15.52274, 16.67064, 17.67064, 19.13621, 20.13621,
  21.28411, 22.28411, 23.43201
]

Edit: changed rules for silver, plastic and supergolden for their partials here to be more uniform.

@frostburn frostburn linked a pull request Jan 24, 2025 that will close this issue
@arseniiv
Copy link
Contributor Author

I suggest not closing it yet, maybe somebody will implement the partial generator itself to make with less data... But use your judgement yeahg.

@frostburn
Copy link
Member

Hmm... Maybe I'll just cross out the implemented stuff here then?

@arseniiv
Copy link
Contributor Author

arseniiv commented Jan 24, 2025

Yep, maybe add a couple checkboxes at the very top or where convenient.

EDIT: Added myself, add more if anything’s missing!

@frostburn frostburn removed a link to a pull request Jan 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants