diff --git a/README.md b/README.md index b520ca20..596dafdb 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Scale title, colors and labels. * Arseniiv A. - Co-developer / Language feedback * Inthar - Co-developer / Language feedback * Akselai - Quality assurance -* Godtone - Language feedback +* Godtone - Notation adviser / Language feedback * Joe Hildebrand - Grammar review * Marc Sabat - Notation adviser diff --git a/documentation/advanced-dsl.md b/documentation/advanced-dsl.md index eed92c84..01adedf2 100644 --- a/documentation/advanced-dsl.md +++ b/documentation/advanced-dsl.md @@ -270,94 +270,78 @@ or `1Hz * (-1) * 2^3 * 5^1 * 11^1` if you break it down linearly. Two dots after a prime number indicate that exponents of successive primes follow. -### True tone-splitters -Technically the term _semitone_ is a misnomer because the diatonic semitone `m2` doesn't split the tone `M2` in half with mathematical precission (and neither does the chromatic semitone `a1`). The true semiwholetone `M2 % 2` is notated using interordinals as `n1.5` (or `n1½`). +## The interordinal semioctave +The stepspan of the octave is odd so the semioctave `P8 / 2` doesn't have an obvious ordinal associated with it like how the semififth `P5 / 2` is obviously a third. -The difference `n1.5 - m2` is only `11.730c` so true tone-splitters are not very useful in their untempered form, but they do provide the notation `n4.5` for the semioctave `P8 / 2` which is present in all even equal divisions of the octave and stays in place during tempering. +The semioctave is between a fourth and a fifth therefore SonicWeave designates it as a 4½th. It is the period of [10L 4s](https://en.xen.wiki/w/10L_4s) (with the semififth as the generator) therefore the designated quality is *perfect*. +```c +"These two are the same" +P8 / 2 "Split octave" +P4½ "The perfect fourth-and-a-halfth" +``` + +Another valid spelling for this perfect fourth-and-a-halfth is `P4.5`. + +### Interordinal intervals +Adding or subtracting a period from an interval doesn't change its quality so we obtain the following central intervals. -The basic tone-splitters are as follows: | Name | Logarithmic | Linear | Size in cents | | ---------------------------- | ------------------ | ----------------- | ------------- | -| Neutral sesquith | `n1.5`, `M2 / 2` | `sqrt(9/8)` | `101.955` | -| Neutral second-and-a-halfth | `n2.5`, `M2 * 3/2` | `sqrt(729/512)` | `305.865` | -| Neutral third-and-a-halfth | `n3.5`, `M2 * 5/2` | `9/8 ^ 5/2` | `509.775` | -| Neutral fourth-and-a-halfth | `n4.5`, `P8 / 2` | `sqrt(2)` | `600.000` | -| Neutral fifth-and-a-halfth | `n5.5`, `M10 / 2` | `sqrt(81/32)` | `803.910` | -| Neutral sixth-and-a-halfth | `n6.5`, `a12 / 2` | `sqrt(6561/2048)` | `1007.820` | -| Neutral seventh-and-a-halfth | `n7.5`, `m14 / 2` | `sqrt(32/9)` | `1098.045` | - -### Absolute semioctave dodecanominal notation -When stacked against the semioctave the fifth spans a dodecatonal scale with ten large steps and two small steps inside the octave (10L 2s "soft-jaric" a.k.a. "jaramechromic"). - -The scale is nominated such that the Greek nominals form the Ionian mode starting from the semioctave. The nominals themselves follow their Latin counterparts: Alpha comes after A, beta comes after B, gamma after C, etc.. - -| Nominal | ASCII | Meaning | Cents from C4 | -| ------- | -------- | ----------- | ------------- | -| `C4` | | `C4 + P1` | `0.000` | -| `γ4` | `gamma4` | `C4 + n1½` | `101.955` | -| `D4` | | `C4 + M2` | `203.910` | -| `δ4` | `delta4` | `C4 + n2½` | `305.865` | -| `E4` | | `C4 + M3` | `407.820` | -| `F4` | | `C4 + P4` | `498.045` | -| `ζ4` | `zeta4` | `C4 + n4½` | `600.000` | -| `G4` | | `C4 + P5` | `701.955` | -| `η4` | `eta4` | `C4 + n5½` | `803.910` | -| `A4` | | `C4 + M6` | `905.865` | -| `α4` | `alpha4` | `C4 + n6½` | `1007.820` | -| `β4` | `beta4` | `C4 + n7½` | `1098.045` | -| `C5` | | `C4 + P8` | `1200.000` | - -Notice how the notation is half-way antisymmteric w.r.t. Latin and Greek nominals and how `B4` is missing. The final Greek nominal `ε4` (`epsilon4`) equal to `C4 + n3½` is also left out, but defined to complete the Ionian mode. Some temperaments stretch the scale to make room for both so e.g. 14-tone equal temperament can be fully notated with alternating Latin and Greek nominals. - -By their construction the 12 nominals in the table above correspond to each of the 12-tones in 12-TET. The 12 nominals are also well-suited for 22-tone equal temperament. - -The accidentals associated with this bihexatonic scale are `r` and `p`. - -| Accidental | Monzo | Size in cents | Mnemonics | -| ---------- | ----------- | ------------- | --------------------------- | -| `r` | `[-19/2 6>` | `+11.730` | **r**_aise_, _paja_**r**_a_ | -| `p` | `[19/2 -6>` | `-11.730` | (flipped `b`), **p**_ajara_ | - -Tone-splitter and decanominals are associated with the `t` flavor of commas e.g. `17/12` may be spelled `ζ4^17t` (assuming `C4 = 1/1`). - -#### Examples -Using these nominals C major chord is still spelled: -```c -"Major chord on C4" -C4 -E4 -G4 -``` -However the lack of B necessitates that the perfect fifth in E minor becomes `βr4`: +| Perfect sesquith | `P1.5`, `M2 / 2` | `sqrt(9/8)` | `101.955` | +| Neutral second-and-a-halfth | `n2.5`, `P4 / 2` | `sqrt(4/3)` | `249.022` | +| Neutral third-and-a-halfth | `n3.5`, `M6 / 2` | `sqrt(27/16)` | `452.933` | +| Perfect fourth-and-a-halfth | `P4.5`, `P8 / 2` | `sqrt(2)` | `600.000` | +| Neutral fifth-and-a-halfth | `n5.5`, `P4 * 3/2` | `(4/3) ^ (3/2)` | `747.067` | +| Neutral sixth-and-a-halfth | `n6.5`, `P12 / 2` | `sqrt(3)` | `950.978` | +| Perfect seventh-and-a-halfth | `P7.5`, `m14 / 2` | `sqrt(32/9)` | `1098.045` | + +Technically the term _semitone_ is a misnomer because the diatonic semitone `m2` doesn't split the tone `M2` in half with mathematical precission (and neither does the chromatic semitone `a1`). The the perfect sesquith `P1½` is the true semiwholetone `M2 / 2`. + +The central intervals can be semiaugmented to reach other intervals in the √2.√3 subgroup e.g. `m6 / 2` = `(M6 + d1) / 2` = `n3½ + ½d1` = `m3.5` ~ `sqrt(128/81)`. + +### Absolute semioctave notation +Absolute notation follows the ordinals. A scale starting at A has A 1st, B 2nd, C 3rd and so on alphabetically. What's the sesquith nominal in this sequence? + +SonicWeave designates Greek counterparts to each of the seven diatonic Latin nominals separated by a semioctave. The octave numbers must still tick at C by convention. Greek capital letters like Alpha are visually indistinguishable from their Latin counterparts so we use lowercase Greek nominals as follows: + +| Greek | Greek ASCII | Definition | Cents from C4 | +| ----- | ----------- | ----------- | ------------- | +| `η4` | `eta4` | `G4 - P4.5` | `101.955` | +| `α4` | `alp4` | `A4 - P4.5` | `305.865` | +| `β4` | `bet4` | `B4 - P4.5` | `509.775` | +| `γ4` | `gam4` | `C4 + P4.5` | `600.000` | +| `δ4` | `del4` | `D4 + P4.5` | `803.910` | +| `ε4` | `eps4` | `E4 + P4.5` | `1007.82` | +| `ζ4` | `zet4` | `F4 + P4.5` | `1098.045` | + +By their construction these nominals are all found in 12-tone equal temperament and are also well-suited for notating 22-TET which is connected to 12-TET along the diaschismic axis. + ```c -"Minor chord on E4" +"Srutal[12] a.k.a. Diaschismic[12] tuned to 22-TET" +defer 22@ + +// First period: Latin-Greek-Latin +C4 = mtof(60) +η4 +D4 +α4 E4 +β4 + +// Second period: Greek-Latin-Greek +γ4 G4 -betar4 -``` +δ4 +A4 +ε4 +B4 -The table below samples more translations between heptatonic and dodecatonic thinking. - -| Heptanominal | Dodecanominal | Fraction against C4 | -| ------------ | ------------- | ------------------- | -| Cb4 | βp3 | 2048/2187 | -| Gb4 | ζp4 | 1024/72 | -| Db4 | γp4 | 256/243 | -| Ab4 | ηp4 | 128/81 | -| Eb4 | δp4 | 32/27 | -| Bb4 | αp4 | 16/9 | -| B4 | βr4 | 243/128 | -| F#4 | ζr4 | 729/512 | -| C#4 | γr4 | 2187/2048 | - -Similarly when you build a minor chord on α, you would spell it α, γ, Fr to remain dodecanominal. -```c -"Minor chord on α4" -alpha4 -gamma5 -Fr5 +// Third period: (implicit repetition) +C5 ``` +Informally you may think of "α" as just the name of the black key between the white "D" and "E" keys on the 12-TET piano. Most equal temperaments with an even number of divisions of the octave can make good use of these new Greek notes. + #### Alternative semioctave notation You can also declare ups to transport notes between the two periods if you wish to avoid using more than 7 nominals while still retaining notational compatibility with most even edos. @@ -368,84 +352,50 @@ The labels indicate the equivalent Greek nominal. ^ = Aug4 - 1\2 C4 = 1/1 -vC#4 "gamma" +vC#4 "eta" D4 -vD#4 "delta" +vD#4 "alpha" E4 F4 -vF#4 "zeta" +vF#4 "gamma" G4 -vG#4 "eta" +vG#4 "delta" A4 -vA#4 "alpha" -vB4 "beta" +vA#4 "epsilon" +vB4 "zeta" C5 ``` -Note how it's vB instead of vB♯ like everything else in order to have beta to be a perfect fourth above zeta. This way we get two identical periods one semioctave apart. +Note how it's vB instead of vB♯ like everything else in order to have zeta to be a perfect fourth above gamma. This way we get two identical periods one semioctave apart. -Here the up inflection is mere 11.730 cents. You can also check out the [other alternative](https://github.com/xenharmonic-devs/sonic-weave/blob/main/examples/semioctave-alternative-2.sw) where zeta = ^F instead resulting in more compact notation. +Here the up inflection is mere 11.730 cents. You can also check out the [other alternative](https://github.com/xenharmonic-devs/sonic-weave/blob/main/examples/semioctave-alternative-2.sw) where γ = ^F instead resulting in more compact notation. -### The interordinal semifourth -When combined with a semiaugmented unison the true tone-splitters induce the notation `m2.5` for the semifourth `P4 / 2` basically for free. +### Splitting the fourth +The perfect intervals `2`, `3/2` and `4/3` are connected by `4/3 == 2 / (3/2)` so splitting any two of these implies the third. -Notable semiquartal intervals include: -| Name | Logarithmic | Linear | Size in cents | -| -------------------------- | ----------------- | -------------- | ------------- | -| Minor second-and-a-halfth | `m2½`, `P4 / 2` | `sqrt(4/3)` | `249.022` | -| Minor third-and-a-halfth | `m3½`, `M6 / 2` | `sqrt(27/16)` | `452.933` | -| Minor fifth-and-a-halfth | `m5½`, `P4 * 3/2` | `sqrt(64/27) ` | `747.067` | -| Minor sixth-and-a-halfth | `m6½`, `P12 / 2` | `sqrt(3)` | `950.978` | -| Major seventh-and-a-halfth | `M7½`, `M14 / 2` | `sqrt(243/64)` | `1154.888` | +In terms of interordinals `n2.5 == P4.5 - n3` so we already have relative notation for the split fourth. -### Absolute semifourth pentanominal notation -The split fourth spans a pentatonic (4L 1s "manual") scale: -```javascript -C4 = mtof(60) -φ4 -F4 -G4 -ψ4 -C5 -``` - -As with semioctave nominals `φ` can be spelled in ASCII as `phi` and `ψ` as `psi`. *Phi* was chosen due to similarity to *F* and *psi* comes from the full enneatonic (5L 4s "semiquartal") scale: -```javascript -C4 = mtof(60) -D4 -φ4 -χ4 -F4 -G4 -a4 -ψ4 -ω4 -C5 +Absolute semiquartal notation is obtained by mixing Greek nominals with half-sharps. +```c +"Semiquartal 5L 4s notation" +C♮4 = mtof(60) +D♮4 "Octave-reduced doubled 5th" +αd4 "Split 4th" +βd4 +F♮4 "Perfect 4th" +G♮4 "Perfect 5th" +δd4 +εd4 "Otave-complemented split 4th" +B♭4 "Doubled 4th" +C♮5 "Octave" ``` -| Nominal | ASCII | Meaning | -| ------- | -------- | ----------- | -| `φ4` | `phi4` | `C4 + m2.5` | -| `χ4` | `chi4` | `C4 + m3.5` | -| `ψ4` | `psi4` | `C4 + m6.5` | -| `ω4` | `omega4` | `C4 + M7.5` | - -The accidentals are associated with the 4L 1s scale: *em* (`&`) denotes the difference between a semifourth and a whole tone: `C&4` is `C4 + (P4%2 - M2)`. The accidental *at* (`@`) is the opposite. - -`χ` is equal to `F@` while `ω` is equal to `C@` of the next octave. - -Due to technical reasons the accidentals for the 5L 4s scale, *scarab* (`¤`) and *pound* (`£`), must also be defined despite their large size. - -| Accidental | Monzo | Size in cents | -| ----------- | ---------- | ------------- | -| `&` | `[4 -5/2>` | `+45.112` | -| `@` | `[-4 5/2>` | `-45.112` | -| `¤` | `[-7 9/2>` | `+158.798` | -| `£` | `[7 -9/2>` | `-158.798` | +#### Nicknames +Lumi, the lead developer, likes to call "C + P4 / 2" "φ" or "phi". Its octave complement is called "ψ" or "psi" and "beta half-flat" has the nickname "χ" or "chi". The last interordinal would be "ω" or "ome(ga)" but it tends to jump around depending on the mood and if the semiquartal scale was sullied by adding a flat sign ♭ in the mix or not. -Semiquartals are associated with the `q` comma flavor e.g. `7/6` can be spelled `φ4^7q` (assuming `C4 = 1/1`). +Absolute semiquartals might make a comeback as an opt-in, but for now they've been excluded from the main grammar. -### Quarter-augmented Pythagorean notation +## Quarter-augmented Pythagorean notation As previously mentioned the fifth spans 4 degrees so we can split it again without breaking the ordinal notation. It does require an intermediary quality between major and neutral called _semimajor_ and correspondingly between neutral and minor called _semiminor_. @@ -467,7 +417,7 @@ Vulgar fraction modifiers like `⅓` or `⅔` can be applied to augmented, major You may encounter them when splitting Pythagorean intervals like the *third-major second* `⅓M2` resulting from splitting the fourth into three parts `P4 / 3`. -You can even make interordinals like the *quarter-minor sesquith* `qm1.5` by splitting the fifth eight ways `P5 / 8`. +You can even make interordinals like the *eighth-diminished sesquith* `⅛d1½` by splitting the fifth eight ways `P5 / 8`. Absolute notation works too `C4 + M6 / 5` happens to be *D fifth-flat four* `D⅕♭4`. diff --git a/documentation/cli.md b/documentation/cli.md index 0136585a..677fe090 100644 --- a/documentation/cli.md +++ b/documentation/cli.md @@ -99,40 +99,38 @@ The program prints the result to standard output so if you're on Linux use stand ```bash $ npx sonic-weave examples/barbados9.sw > /tmp/desert-island-rain.scl $ cat /tmp/desert-island-rain.scl -!Created using SonicWeave 0.0.20 +!Created using SonicWeave 0.0.33 ! -Pentanominal enneatonic 5L 4s subset of 313edo used in Sevish's track Desert Island Rain +Enneatonic 5L 4s subset of 313edo used in Sevish's track Desert Island Rain 9 ! - 203.194888 φ@ - 249.201278 φ - 452.396166 F@ - 498.402556 F - 701.597444 G - 747.603834 G& - 950.798722 ψ - 996.805112 ψ& - 2 C -! A list of key colors, ascending from 1/1 -! white black white black white white black white black + 203.194888 Octave-reduced doubled 5th + 249.201278 Split 4th + 452.396166 + 498.402556 Perfect 4th + 701.597444 Perfect 5th + 747.603834 + 950.798722 Otave-complemented split 4th + 996.805112 Doubled 4th + 2 Octave ``` ### SonicWeave Interchange format The .swi format is suitable for data interchange between programs. It preserves the internal precision of the SonicWeave runtime. ```bash $ npx sonic-weave examples/pajara10.sw --format swi -// Created using SonicWeave 0.0.20 +// Created using SonicWeave 0.0.33 "Decanominal 2L 8s Pajara scale in 22edo" -[1/11> "γ" +[1/11> "η" [2/11> "D" -[3/11> "δ" +[3/11> "α" [4/11> "E" -[1/2> "ζ" +[1/2> "γ" [13/22> "G" -[15/22> "η" +[15/22> "δ" [17/22> "A" -[19/22> "α" +[19/22> "ε" [1> "C" ``` diff --git a/documentation/commas.md b/documentation/commas.md index 8580ed92..6b67582e 100644 --- a/documentation/commas.md +++ b/documentation/commas.md @@ -120,12 +120,12 @@ Reasons for existence assume `C4 = 1/1`. | `0l` | ? | * | `+0.797` | `15/14` ~ `sm2^0l` | | `1l` | ? | * | `+10.485` | `11/10` ~ `sM2_1l` | | `2l` | [apotomic](https://en.xen.wiki/w/2187/2048) | `sqrt(2187/2048)` | `+56.843` | `9/8` ~ `n2^2l` | -| `3l` | [island](https://en.xen.wiki/w/676/675) | `sqrt(676/675)` | `+1.281` | `15/13` ~ `φ4_3l` | -| `4l` | [varunismic](https://en.xen.wiki/w/Varunismic_temperaments) | `[-9/2 4 -2 1>` | `+4.018` | `25/14` ~ `α4^4l` | -| `5l` | [jubilismic](https://en.xen.wiki/w/50/49) | `sqrt(50/49)` | `+17.488` | `7/5` ~ `ζ4_5l` | +| `3l` | [island](https://en.xen.wiki/w/676/675) | `sqrt(676/675)` | `+1.281` | `15/13` ~ `αd4_3l` | +| `4l` | [varunismic](https://en.xen.wiki/w/Varunismic_temperaments) | `[-9/2 4 -2 1>` | `+4.018` | `25/14` ~ `ε4_4l` | +| `5l` | [jubilismic](https://en.xen.wiki/w/50/49) | `sqrt(50/49)` | `+17.488` | `7/5` ~ `γ4_5l` | | `6l` | [porcupine](https://en.xen.wiki/w/250/243) | `cbrt(250/243)` | `+16.389` | `6/5` ~ `⅓m3_6l` | | `7l` | [wizardharry](https://en.xen.wiki/w/4000/3993) | `cbrt(4000/3993)` | `+1.011` | `11/10` ~ `⅓M2_7l` | -| `8l` | ? | * | `+26.343` | `13/7` ~ `β4_8l` | +| `8l` | ? | * | `+26.343` | `13/7` ~ `ζ4_8l` | | `9l` | [amity](https://en.xen.wiki/w/Amity_comma) | `[9/5 -13/5 1>` | `1.231` | `10/9` ~ `⅗M2^9l` | The unnamed inflections are filler curiosities which should be revised by someone who is willing to do more research into irrational bridging. diff --git a/examples/barbados9.sw b/examples/barbados9.sw index 491110f7..2f33d83b 100644 --- a/examples/barbados9.sw +++ b/examples/barbados9.sw @@ -1,21 +1,19 @@ -"Pentanominal enneatonic 5L 4s subset of 313edo used in Sevish's track Desert Island Rain" +"Enneatonic 5L 4s subset of 313edo used in Sevish's track Desert Island Rain" // Sometimes labeled as Madagasgar[9] to emphasize the full 13-limit. // 313edo is not optimal for Barbados[9], but the scale is generated by ~15/13 nonetheless, so it's not a misnomer. // Temper to 313 equal tones defer 313@ -// Use labels without octaves prior to tempering -defer labelAbsoluteFJS // Symmetric mode on middle C -C4 = mtof(60) = 1/1 -φ@4 -φ4 -F@4 -F4 -G4 -G&4 -ψ4 -ψ&4 -C5 +C♮4 = mtof(60) +D♮4 "Octave-reduced doubled 5th" +αd4 "Split 4th" +βd4 +F♮4 "Perfect 4th" +G♮4 "Perfect 5th" +δd4 +εd4 "Otave-complemented split 4th" +B♭4 "Doubled 4th" +C♮5 "Octave" diff --git a/examples/notation/10edo-pentanominal.sw b/examples/notation/10edo-pentanominal.sw index d766ea8f..3d7eb8e4 100644 --- a/examples/notation/10edo-pentanominal.sw +++ b/examples/notation/10edo-pentanominal.sw @@ -1,14 +1,14 @@ "Recommended notation for 20 divisions of the octave" -C=4 = mtof(60) +C♮4 = mtof(60) ^C4 -φ=4 -^φ4 -F=4 +αd4 "φ♮4" +^αd4 "^φ4" +F♮4 ^F4 -G=4 +G♮4 ^G4 -ψ=4 -^ψ4 -C=5 +εd4 "ψ♮4" +^εd4 "^ψ4" +C♮5 10@ diff --git a/examples/notation/12edo-srutal.sw b/examples/notation/12edo-srutal.sw index 012d5a04..c27b2801 100644 --- a/examples/notation/12edo-srutal.sw +++ b/examples/notation/12edo-srutal.sw @@ -1,16 +1,16 @@ "Recommended notation for 12 divisions of the octave" -C=4 = mtof(60) -C=4 -γ=4 -D=4 -δ=4 -E=4 -F=4 -ζ=4 -G=4 -η=4 -A=4 -α=4 -β=4 +C4 = mtof(60) +η4 +D4 +α4 +E4 +F4 +γ4 +G4 +δ4 +A4 +ε4 +ζ4 // Not B4 to get equal number of Latin and Greek nominals +C5 12@ diff --git a/examples/notation/13edo-antidiatonic.sw b/examples/notation/13edo-antidiatonic.sw index e0f80901..29a3fbb7 100644 --- a/examples/notation/13edo-antidiatonic.sw +++ b/examples/notation/13edo-antidiatonic.sw @@ -3,14 +3,14 @@ C=4 = mtof(60) D=4 E=4 ^E4 -vvF4 // Or χ=4 if you're feeling spicy +vvF4 vF4 F=4 G=4 A=4 B=4 ^B4 -^^B4 // Or ω=4 for spice +^^B4 vC5 C=5 diff --git a/examples/notation/13edo-dodecanominal.sw b/examples/notation/13edo-dodecanominal.sw deleted file mode 100644 index b4d24d72..00000000 --- a/examples/notation/13edo-dodecanominal.sw +++ /dev/null @@ -1,17 +0,0 @@ -"Recommended notation for 13 divisions of the octave" -C=4 = mtof(60) -γ=4 -D=4 -δ=4 -E=4 -ε=4 -ζb4 -ζ#4 -G#4 -η#4 -A#4 -α#4 -B#4 -C=5 - -13@2.9 diff --git a/examples/notation/13edo-manual.sw b/examples/notation/13edo-manual.sw deleted file mode 100644 index 9e4ec6d0..00000000 --- a/examples/notation/13edo-manual.sw +++ /dev/null @@ -1,17 +0,0 @@ -"Recommended notation for 13 divisions of the octave" -C=4 = mtof(60) -^C4 -vφ4 -φ=4 -^φ4 -vF4 -F=4 -G=4 -^G4 -vψ4 -ψ=4 -^ψ4 -vC5 -C=5 - -13b@ diff --git a/examples/notation/13edo-tetracot.sw b/examples/notation/13edo-tetracot.sw index b108c995..e3da1596 100644 --- a/examples/notation/13edo-tetracot.sw +++ b/examples/notation/13edo-tetracot.sw @@ -4,7 +4,7 @@ sM2 sm3 n3 P4 -qA4 +qa4 qd5 P5 n6 diff --git a/examples/notation/14edo.sw b/examples/notation/14edo.sw index d19926f9..c7ead985 100644 --- a/examples/notation/14edo.sw +++ b/examples/notation/14edo.sw @@ -1,18 +1,18 @@ "Recommended notation for 14 divisions of the octave" -C=4 = mtof(60) -γ=4 -D=4 -δ=4 -E=4 -ε=4 -F=4 -ζ=4 -G=4 -η=4 -A=4 -α=4 -B=4 -β=4 -C=5 +C4 = mtof(60) +η4 +D4 +α4 +E4 +β4 +F4 +γ4 +G4 +δ4 +A4 +ε4 +B4 +ζ4 +C5 14@ diff --git a/examples/notation/15edo.sw b/examples/notation/15edo.sw deleted file mode 100644 index 5cd106fd..00000000 --- a/examples/notation/15edo.sw +++ /dev/null @@ -1,19 +0,0 @@ -"Recommended notation for 15 divisions of the octave" -C=4 = mtof(60) -^C4 -vφ4 -φ=4 -^φ4 -vF4 -F=4 -^F4 -vG4 -G=4 -^G4 -vψ4 -ψ=4 -^ψ4 -vC5 -C=5 - -15@ diff --git a/examples/notation/16edo.sw b/examples/notation/16edo.sw index b68e8bbe..667e14c9 100644 --- a/examples/notation/16edo.sw +++ b/examples/notation/16edo.sw @@ -1,20 +1,20 @@ "Recommended notation for 16 divisions of the octave" -C=4 = mtof(60) -γ=4 -D=4 -δ=4 -E=4 -ε=4 -^ε4 -F=4 -ζ=4 -G=4 -η=4 -A=4 -α=4 -B=4 +C♮4 = mtof(60) +η♮4 +D♮4 +α♮4 +E♮4 +β♮4 +^β4 +F♮4 +γ♮4 +G♮4 +δ♮4 +A♮4 +ε♮4 +B♮4 ^B4 -β=4 -C=5 +ζ♮4 +C♮5 16@ diff --git a/examples/notation/18edo-biantidiatonic.sw b/examples/notation/18edo-biantidiatonic.sw deleted file mode 100644 index b051c0e4..00000000 --- a/examples/notation/18edo-biantidiatonic.sw +++ /dev/null @@ -1,22 +0,0 @@ -"Recommended notation for 18 divisions of the octave" -C=4 = mtof(60) -γ=4 -D=4 -δ=4 -E=4 -ε=4 -^ε4 -vF4 -F=4 -ζ=4 -G=4 -η=4 -A=4 -α=4 -B=4 -^B4 -vβ4 -β=4 -C=5 - -18b@ diff --git a/examples/notation/18edo-decanominal.sw b/examples/notation/18edo-decanominal.sw deleted file mode 100644 index d501765e..00000000 --- a/examples/notation/18edo-decanominal.sw +++ /dev/null @@ -1,22 +0,0 @@ -"Recommended notation for 18 divisions of the octave" -C=4 = mtof(60) -^C4 -γ=4 -^γ4 -D=4 -^D4 -δ=4 -F=4 -^F4 -ζ=4 -^ζ4 -G=4 -^G4 -η=4 -^η4 -A=4 -β=4 -^β4 -C=5 - -18@ diff --git a/examples/notation/19edo-diatonic.sw b/examples/notation/19edo-diatonic.sw index 5476a23e..2f6f4288 100644 --- a/examples/notation/19edo-diatonic.sw +++ b/examples/notation/19edo-diatonic.sw @@ -6,7 +6,7 @@ D=4 D#4 Eb4 E=4 -E#4 // Or χ=4 if you want a unique nonimal for this one. +E#4 F=4 F#4 Gb4 @@ -17,7 +17,7 @@ A=4 A#4 Bb4 B=4 -B#4 // Or ω=4 if you want a unique nonimal for this one. +B#4 C=5 19@ diff --git a/examples/notation/19edo-semiquartal.sw b/examples/notation/19edo-semiquartal.sw index e1778d7e..5dfd0d4b 100644 --- a/examples/notation/19edo-semiquartal.sw +++ b/examples/notation/19edo-semiquartal.sw @@ -1,23 +1,23 @@ "Recommended notation for 19 divisions of the octave" -C=4 = mtof(60) -C&4 -D@4 -D=4 -φ=4 -φ&4 -χ@4 -χ=4 -F=4 -F&4 -G@4 -G=4 -G&4 -A@4 -A=4 -ψ=4 -ψ&4 -ω@4 -ω=4 -C=5 +C♮4 = mtof(60) +^C4 +vD4 +D♮4 +αd4 +^αd4 +vβd4 +βd4 +F♮4 +^F4 +vG4 +G♮4 +^G4 +vA4 +A♮4 +εd4 +^εd4 +B♮4 +^B4 +C♮5 19@ diff --git a/examples/notation/20edo.sw b/examples/notation/20edo.sw index 075dc860..388d7df4 100644 --- a/examples/notation/20edo.sw +++ b/examples/notation/20edo.sw @@ -1,24 +1,24 @@ "Recommended notation for 20 divisions of the octave" -C=4 = mtof(60) +C♮4 = mtof(60) ^C4 -γ=4 -^γ4 -D=4 +η♮4 +^η4 +D♮4 ^D4 -δ=4 -^δ4 -E=4 +α♮4 +^α4 +E♮4 ^E4 -ε=4 -^ε4 -G=4 +β♮4 +^β4 +G♮4 ^G4 -η=4 -^η4 -A=4 +δ♮4 +^δ4 +A♮4 ^A4 -α=4 -^α4 -C=5 +ε♮4 +^ε4 +C♮5 20@ diff --git a/examples/notation/22edo.sw b/examples/notation/22edo.sw index 33e5580d..9d8720e6 100644 --- a/examples/notation/22edo.sw +++ b/examples/notation/22edo.sw @@ -1,26 +1,26 @@ "Recommended notation for 22 divisions of the octave" -C=4 = mtof(60) -Cr4 -γ=4 -γr4 -D=4 -Dr4 -δ=4 -δr4 -E=4 -F=4 -Fr4 -ζ=4 -ζr4 -G=4 -Gr4 -η=4 -ηr4 -A=4 -Ar4 -α=4 -β=4 -βr4 -C=5 +C♮4 = mtof(60) +^C4 +η♮4 +^η4 +D♮4 +^D4 +α♮4 +^α4 +E♮4 +F♮4 +^F4 +γ♮4 +^γ4 +G♮4 +^G4 +δ♮4 +^δ4 +A♮4 +^A4 +ε♮4 +ζ♮4 +^ζ4 +C♮5 22@ diff --git a/examples/notation/23edo-antidiatonic.sw b/examples/notation/23edo-antidiatonic.sw index 6582f204..bc88996d 100644 --- a/examples/notation/23edo-antidiatonic.sw +++ b/examples/notation/23edo-antidiatonic.sw @@ -7,7 +7,7 @@ D=4 vE4 E=4 ^E4 -^^E4 // Or χ=4 if you want a unique nonimal for this one. +^^E4 vF4 F=4 ^F4 @@ -20,7 +20,7 @@ A=4 vB4 B=4 ^B4 -^^B4 // Or ω=4 if you want a unique nonimal for this one. +^^B4 vC5 C=5 diff --git a/examples/notation/23edo-semiquartal.sw b/examples/notation/23edo-semiquartal.sw deleted file mode 100644 index 275b4a2f..00000000 --- a/examples/notation/23edo-semiquartal.sw +++ /dev/null @@ -1,27 +0,0 @@ -"Recommended notation for 23 divisions of the octave" -C=4 = mtof(60) -^C4 -vD4 -D=4 -^D4 -φ=4 -^φ4 -vχ4 -χ=4 -^χ4 -F=4 -^F4 -vG4 -G=4 -^G4 -vA4 -A=4 -^A4 -ψ=4 -^ψ4 -vω4 -ω=4 -^ω4 -C=5 - -23@ diff --git a/examples/notation/24edo-semiquartal.sw b/examples/notation/24edo-semiquartal.sw deleted file mode 100644 index 71d254c1..00000000 --- a/examples/notation/24edo-semiquartal.sw +++ /dev/null @@ -1,28 +0,0 @@ -"Recommended notation for 24 divisions of the octave" -C=4 = mtof(60) -C&4 -C&&4 -D@4 -D=4 -φ=4 -φ&4 -φ&&4 -χ@4 -χ=4 -F=4 -F&4 -F&&4 -G@4 -G=4 -G&4 -G&&4 -A@4 -A=4 -ψ=4 -ψ&4 -ψ&&4 -ω@4 -ω=4 -C=5 - -24@ diff --git a/examples/notation/24edo-srutal.sw b/examples/notation/24edo-srutal.sw index 9852e8d3..71f3a2f3 100644 --- a/examples/notation/24edo-srutal.sw +++ b/examples/notation/24edo-srutal.sw @@ -1,28 +1,28 @@ "Recommended notation for 24 divisions of the octave" -C=4 = mtof(60) +C♮4 = mtof(60) ^C4 -γ=4 -^γ4 -D=4 +η♮4 +^η4 +D♮4 ^D4 -δ=4 -^δ4 -E=4 +α♮4 +^α4 +E♮4 ^E4 -F=4 +F♮4 ^F4 -ζ=4 -^ζ4 -G=4 +γ♮4 +^γ4 +G♮4 ^G4 -η=4 -^η4 -A=4 +δ♮4 +^δ4 +A♮4 ^A4 -α=4 -^α4 -β=4 -^β4 -C=5 +ε♮4 +^ε4 +ζ♮4 +^ζ4 +C♮5 24@ diff --git a/examples/notation/25edo-splittone.sw b/examples/notation/25edo-splittone.sw index 0ad9a662..af7db726 100644 --- a/examples/notation/25edo-splittone.sw +++ b/examples/notation/25edo-splittone.sw @@ -1,29 +1,29 @@ "Recommended notation for 25 divisions of the octave" -C=4 = mtof(60) +C♮4 = mtof(60) ^C4 -γ=4 -^γ4 -D=4 +η♮4 +^η4 +D♮4 ^D4 -δ=4 -^δ4 -E=4 +α♮4 +^α4 +E♮4 ^E4 -ε=4 -^ε4 +β♮4 +^β4 F#4 ^F#4 -ζ#4 -^ζ#4 +γ#4 +^γ#4 G#4 ^G#4 -η#4 -^η#4 +δ#4 +^δ#4 A#4 ^A#4 -α#4 -^α#4 +ε#4 +^ε#4 vC5 -C=5 +C♮5 25@2.9 diff --git a/examples/notation/2edo.sw b/examples/notation/2edo.sw index cf49c8ae..bd8bf3d9 100644 --- a/examples/notation/2edo.sw +++ b/examples/notation/2edo.sw @@ -1,4 +1,4 @@ "Recommended notation for 2 divisions of the octave" C4 = mtof(60) -ζ4 +γ4 C5 diff --git a/examples/notation/5edo.sw b/examples/notation/5edo.sw index 2a093732..9905c378 100644 --- a/examples/notation/5edo.sw +++ b/examples/notation/5edo.sw @@ -1,9 +1,9 @@ "Recommended notation for 5 divisions of the octave" C4 = mtof(60) -φ4 +αd4 "φ4" F4 G4 -ψ4 +εd4 "ψ4" C5 5@ diff --git a/examples/notation/7edo.sw b/examples/notation/7edo.sw index ab17d692..bd369f3e 100644 --- a/examples/notation/7edo.sw +++ b/examples/notation/7edo.sw @@ -1,11 +1,11 @@ "Recommended notation for 7 divisions of the octave" -C=4 = mtof(60) -D=4 -E=4 -F=4 -G=4 -A=4 -B=4 -C=5 +C4 = mtof(60) +D4 +E4 +F4 +G4 +A4 +B4 +C5 7@ diff --git a/examples/pajara10.sw b/examples/pajara10.sw index 6c28a289..cd8a4f98 100644 --- a/examples/pajara10.sw +++ b/examples/pajara10.sw @@ -5,17 +5,17 @@ defer 22@ // First period C4 = 1/1 -γ4 +η4 D4 -δ4 +α4 E4 // Second period -ζ4 +γ4 G4 -η4 +δ4 A4 -α4 +ε4 // Repeat at the octave C5 diff --git a/examples/semioctave-alternative-1.sw b/examples/semioctave-alternative-1.sw index 71d87a0d..79fef52d 100644 --- a/examples/semioctave-alternative-1.sw +++ b/examples/semioctave-alternative-1.sw @@ -2,15 +2,15 @@ ^ = Aug4 - 1\2 C4 = 1/1 -vC#4 "gamma" +vC#4 "eta" D4 -vD#4 "delta" +vD#4 "alpha" E4 F4 -vF#4 "zeta" +vF#4 "gamma" G4 -vG#4 "eta" +vG#4 "delta" A4 -vA#4 "alpha" -vB4 "beta" +vA#4 "epsilon" +vB4 "zeta" C5 diff --git a/examples/semioctave-alternative-2.sw b/examples/semioctave-alternative-2.sw index d9616dcf..7f5f0615 100644 --- a/examples/semioctave-alternative-2.sw +++ b/examples/semioctave-alternative-2.sw @@ -2,15 +2,15 @@ ^ = 1\2 - P4 C4 = 1/1 -^C4 "gamma" +^C4 "eta" D4 -^D4 "delta" +^D4 "alpha" E4 F4 -^F4 "zeta" +^F4 "gamma" G4 -^G4 "eta" +^G4 "delta" A4 -^A4 "alpha" -^Bb4 "beta" +^A4 "epsilon" +^Bb4 "zeta" C5 diff --git a/src/__tests__/pythagorean.spec.ts b/src/__tests__/pythagorean.spec.ts index 0631897f..a0d33925 100644 --- a/src/__tests__/pythagorean.spec.ts +++ b/src/__tests__/pythagorean.spec.ts @@ -6,7 +6,6 @@ import { AbsolutePitch, monzoToNode, absoluteToNode, - absoluteToSemiquartal, VulgarFraction, AugmentedQuality, SplitAccidental, @@ -67,35 +66,47 @@ describe('Pythagorean interval construction from parts', () => { ['sa', 6, -9.5, 6.5], ['sa', 3, -11.5, 7.5], ['sa', 7, -12.5, 8.5], - // Tonesplitters - ['sd', 3.5, 3.5, -2], - ['n', 7.5, 2.5, -1], - ['n', 4.5, 0.5, 0], - ['n', 1.5, -1.5, 1], - ['n', 5.5, -2.5, 2], - ['n', 2.5, -4.5, 3], - ['n', 6.5, -5.5, 4], - ['n', 3.5, -7.5, 5], - ['sa', 7.5, -8.5, 6], - // Semiquartal - ['d', 2.5, 12, -7.5], - ['d', 6.5, 11, -6.5], - ['d', 3.5, 9, -5.5], - ['m', 7.5, 8, -4.5], - ['m', 4.5, 6, -3.5], - ['m', 1.5, 4, -2.5], - ['m', 5.5, 3, -1.5], - ['m', 2.5, 1, -0.5], - ['m', 6.5, 0, 0.5], - ['m', 3.5, -2, 1.5], - ['M', 7.5, -3, 2.5], - ['M', 4.5, -5, 3.5], - ['M', 1.5, -7, 4.5], - ['M', 5.5, -8, 5.5], - ['M', 2.5, -10, 6.5], - ['M', 6.5, -11, 7.5], - ['M', 3.5, -13, 8.5], - ['a', 7.5, -14, 9.5], + // Mid + ['n', 4, -3.5, 2.5], + ['n', 5, 4.5, -2.5], + // Semioctave - minor + ['m', 5.5, 8.5, -5], + ['m', 2.5, 6.5, -4], + ['m', 6.5, 5.5, -3], + ['m', 3.5, 3.5, -2], + // Semioctave - perfect + ['P', 7.5, 2.5, -1], + ['P', 4.5, 0.5, 0], + ['P', 1.5, -1.5, 1], + // Semioctave - major + ['M', 5.5, -2.5, 2], + ['M', 2.5, -4.5, 3], + ['M', 6.5, -5.5, 4], + ['M', 3.5, -7.5, 5], + // Semioctave - mid + ['n', 1.5, 4, -2.5], + ['n', 7.5, -3, 2.5], + // Semioctave - augmented of perfect + ['a', 7.5, -8.5, 6], + // Chain of fifths on the semifourth + ['sd', 5.5, 14, -8.5], + ['sd', 2.5, 12, -7.5], + ['sd', 6.5, 11, -6.5], + ['sd', 3.5, 9, -5.5], + ['sd', 7.5, 8, -4.5], + ['sd', 4.5, 6, -3.5], + ['sd', 1.5, 4, -2.5], + ['n', 5.5, 3, -1.5], + ['n', 2.5, 1, -0.5], + ['n', 6.5, 0, 0.5], + ['n', 3.5, -2, 1.5], + ['sa', 7.5, -3, 2.5], + ['sa', 4.5, -5, 3.5], + ['sa', 1.5, -7, 4.5], + ['sa', 5.5, -8, 5.5], + ['sa', 2.5, -10, 6.5], + ['sa', 6.5, -11, 7.5], + ['sa', 3.5, -13, 8.5], // Quarter augmented ['qa', 1, -2.75, 1.75], ['Qa', 1, -8.25, 5.25], @@ -116,7 +127,7 @@ describe('Pythagorean interval construction from parts', () => { } const base = ((Math.abs(degree) - 1) % 7) + 1; const octaves = Math.floor((Math.abs(degree) - 1) / 7); - const imperfect = ![1, 4, 5].includes(base); + const imperfect = ![1, 4, 5, 1.5, 4.5, 7.5].includes(base); const node: Pythagorean = { type: 'Pythagorean', quality: {fraction, quality: quality as any}, @@ -237,10 +248,10 @@ describe('Monzo -> node converter', () => { type: 'Pythagorean', quality: { fraction: '', - quality: 'n', + quality: 'P', }, augmentations: [], - degree: {base: 4.5, negative: false, octaves: 0, imperfect: true}, + degree: {base: 4.5, negative: false, octaves: 0, imperfect: false}, }); }); @@ -269,21 +280,21 @@ describe('Absolute monzo -> node converter', () => { }); }); - it('converts zeta4', () => { + it('converts gam4', () => { const node = absoluteToNode(TimeMonzo.fromEqualTemperament('1/2')); expect(node).toEqual({ type: 'AbsolutePitch', - nominal: 'ζ', + nominal: 'γ', accidentals: [{fraction: '', accidental: '♮'}], octave: 4, }); }); - it('converts alphad4', () => { + it('converts etad4', () => { const node = absoluteToNode(TimeMonzo.fromEqualTemperament('1/2', 3)); expect(node).toEqual({ type: 'AbsolutePitch', - nominal: 'α', + nominal: 'ε', accidentals: [{fraction: '', accidental: 'd'}], octave: 4, }); @@ -326,47 +337,3 @@ describe('Absolute monzo -> node converter', () => { }); }); }); - -describe('Absolute monzo -> semiquartal node converter', () => { - it.each([ - ['9/8', 'D', '♮'], - ['32/27', 'χ', '£'], - ['81/64', 'φ', '¤'], - ['4/3', 'F', '♮'], - ['3/2', 'G', '♮'], - ['27/16', 'A', '♮'], - ['16/9', 'ω', '£'], - ['128/81', 'ψ', '£'], - ['243/128', 'ψ', '¤'], - ])('converts %s to %s%s4', (fraction, nominal, accidental) => { - const node = absoluteToSemiquartal(TimeMonzo.fromFraction(fraction)); - expect(node).toEqual({ - type: 'AbsolutePitch', - nominal, - accidentals: [{fraction: '', accidental}], - octave: 4, - }); - }); - - it('converts phi4', () => { - const node = absoluteToSemiquartal( - TimeMonzo.fromEqualTemperament('1/2', '4/3') - ); - expect(node).toEqual({ - type: 'AbsolutePitch', - nominal: 'φ', - accidentals: [{fraction: '', accidental: '♮'}], - octave: 4, - }); - }); - - it('converts C3', () => { - const node = absoluteToNode(TimeMonzo.fromFraction(0.5)); - expect(node).toEqual({ - type: 'AbsolutePitch', - nominal: 'C', - accidentals: [{fraction: '', accidental: '♮'}], - octave: 3, - }); - }); -}); diff --git a/src/extra-commas.ts b/src/extra-commas.ts index 2a5b197f..9692b9b5 100644 --- a/src/extra-commas.ts +++ b/src/extra-commas.ts @@ -27,17 +27,17 @@ const LUMIS_COMMAS = { '1': ['3/4', '1/4', '1', '0', '-1'], // For 32/27 ~ n3_2l '2': ['-11/2', '7/2'], - // For 15/13 ~ φ4_3l + // For 15/13 ~ αd4_3l '3': ['1', '-3/2', '-1', '0', '0', '1'], - // For 25/14 ~ α4^4l + // For 25/14 ~ ε4_4l '4': ['-9/2', '4', '-2', '1'], - // For 7/5 ~ ζ4_5l + // For 7/5 ~ γ4_5l '5': ['1/2', '0', '1', '-1'], // For 6/5 ~ ⅓m3_6l '6': ['1/3', '-5/3', '1'], // For 11/10 ~ ⅓M2_7l '7': ['5/3', '-1/3', '1', '0', '-1'], - // For 13/7 ~ β4_8l + // For 13/7 ~ ζ4_8l '8': ['5/2', '-1', '0', '1', '0', '-1'], // For 10/9` ~ ⅗M2^9 '9': ['9/5', '-13/5', '1'], diff --git a/src/fjs.ts b/src/fjs.ts index cd1915b3..3fed2da0 100644 --- a/src/fjs.ts +++ b/src/fjs.ts @@ -8,11 +8,7 @@ import { } from 'xen-dev-utils'; import {TimeMonzo, TimeReal} from './monzo'; import {AbsoluteFJS, FJS, FJSFlavor, FJSInflection} from './expression'; -import { - absoluteToNode, - absoluteToSemiquartal, - monzoToNode, -} from './pythagorean'; +import {absoluteToNode, monzoToNode} from './pythagorean'; import { HEJI_SWAPS, HEWM53_SWAPS, @@ -430,10 +426,7 @@ export function asAbsoluteFJS( } } const {pythagoreanMonzo, superscripts, subscripts} = uninflect(monzo, flavor); - const pitch = - flavor === 'q' - ? absoluteToSemiquartal(pythagoreanMonzo) - : absoluteToNode(pythagoreanMonzo); + const pitch = absoluteToNode(pythagoreanMonzo); if (!pitch) { return undefined; } diff --git a/src/grammars/sonic-weave.pegjs b/src/grammars/sonic-weave.pegjs index 4bfe78da..a74464c8 100644 --- a/src/grammars/sonic-weave.pegjs +++ b/src/grammars/sonic-weave.pegjs @@ -62,9 +62,9 @@ 'while', ]); - const PERFECT_DEGREES = new Set([1, 4, 5]); - const MID_DEGREES = new Set([4, 5]); - const IMPERFECT_DEGREES = new Set([2, 3, 6, 7]); + const PERFECT_DEGREES = new Set([1, 4, 5, 1.5, 4.5, 7.5]); + const MID_DEGREES = new Set([4, 5, 1.5, 7.5]); + const IMPERFECT_DEGREES = new Set([2, 3, 6, 7, 2.5, 3.5, 5.5, 6.5]); function UpdateExpression(operator, argument) { return { @@ -1292,8 +1292,8 @@ PerfectQuality } Degree - = sign: '-'? num: PositiveBasicInteger { - num--; + = sign: '-'? num: PositiveBasicInteger half: ('½' / '.5')? { + num = num - 1 + (half ? 0.5 : 0); return { negative: !!sign, base: (num % 7) + 1, @@ -1316,17 +1316,8 @@ ImperfectDegree }; } -HalfDegree - = degree: Degree ('½' / '.5') { - return { - ...degree, - base: degree.base + 0.5, - imperfect: true, - }; - } - SplitPythagorean - = quality: AugmentedQuality augmentations: AugmentedToken* degree: (HalfDegree / ImperfectDegree / PerfectDegree) { + = quality: AugmentedQuality augmentations: AugmentedToken* degree: (ImperfectDegree / PerfectDegree) { return { type: 'Pythagorean', quality, @@ -1334,7 +1325,7 @@ SplitPythagorean degree, }; } - / quality: ImperfectQuality degree: (HalfDegree / ImperfectDegree) { + / quality: ImperfectQuality degree: ImperfectDegree { return { type: 'Pythagorean', quality, @@ -1408,7 +1399,7 @@ FJS } AccidentalSign - = '𝄪' / '𝄫' / '𝄲' / '𝄳' / [x♯#‡t♮=d♭b&@rp¤£] + = '𝄪' / '𝄫' / '𝄲' / '𝄳' / [x♯#‡t♮=d♭b] Accidental 'accidental' = fraction: VulgarFraction accidental: AccidentalSign { @@ -1419,7 +1410,7 @@ Accidental 'accidental' } PitchNominal 'pitch nominal' - = 'alpha' / 'beta' / 'gamma' / 'delta' / 'epsilon' / 'zeta' / 'eta' / 'phi' / 'chi' / 'psi' / 'omega' / [\u03B1-ηφ-ωA-G] + = 'alp' / 'bet' / 'gam' / 'del' / 'eps' / 'zet' / 'eta' / [α-ηA-G] AbsolutePitch = nominal: PitchNominal accidentals: Accidental* octave: SignedBasicInteger { diff --git a/src/parser/__tests__/expression.spec.ts b/src/parser/__tests__/expression.spec.ts index 46434458..a73e3d25 100644 --- a/src/parser/__tests__/expression.spec.ts +++ b/src/parser/__tests__/expression.spec.ts @@ -100,13 +100,13 @@ describe('SonicWeave expression evaluator', () => { expect(fourthFifth.value.valueOf()).toBeCloseTo(1.5 ** 0.25); }); - it('supports tone-splitter interordinal', () => { - const splitTone = parseSingle('n1.5'); + it('supports tone-splitter interordinals', () => { + const splitTone = parseSingle('P1.5'); expect(splitTone.value.valueOf()).toBeCloseTo(Math.sqrt(9 / 8)); }); it('supports semiquartal interordinals', () => { - const semifourth = parseSingle('m2.5'); + const semifourth = parseSingle('n2.5'); expect(semifourth.value.valueOf()).toBeCloseTo(Math.sqrt(4 / 3)); }); @@ -213,7 +213,7 @@ describe('SonicWeave expression evaluator', () => { it('can format the half twelfth', () => { const {interval} = parseSingle('P12 % 2'); - expect(interval.toString()).toBe('m6.5'); + expect(interval.toString()).toBe('n6.5'); }); it("bails out when there's no Pythagorean to match", () => { @@ -516,8 +516,8 @@ describe('SonicWeave expression evaluator', () => { }); it('preserves absolute FJS formatting', () => { - const phiAt = evaluate('str(phi@4)'); - expect(phiAt).toBe('phi@4'); + const phiAt = evaluate('str(alpd4)'); + expect(phiAt).toBe('alpd4'); }); it('preserves lifts on monzos', () => { @@ -1056,7 +1056,7 @@ describe('SonicWeave expression evaluator', () => { }); it("has Lumi's bridging commas (island)", () => { - const semifourth = parseSingle('C4 = 1/1;φ4_3l'); + const semifourth = parseSingle('C4 = 1/1;alpd4_3l'); expect(semifourth.value.toFraction().toFraction()).toBe('15/13'); }); @@ -1420,23 +1420,23 @@ describe('SonicWeave expression evaluator', () => { }); it('has a semiquartal spelling for 7/6 (relative parsing)', () => { - const {interval} = parseSingle('m2.5^7q'); + const {interval} = parseSingle('n2.5^7q'); expect(interval.value.toFraction().toFraction()).toBe('7/6'); }); it('has a semiquartal spelling for 7/6 (absolute parsing)', () => { - const q = parseSingle('C4 = 1;phi4^7q'); + const q = parseSingle('C4 = 1;alpd4^7q'); expect(q.value.toFraction().toFraction()).toBe('7/6'); }); it('has a semiquartal spelling for 7/6 (relative)', () => { const q = evaluate('str(FJS(7/6, "q"))'); - expect(q).toBe('m2.5^7q'); + expect(q).toBe('n2.5^7q'); }); it('has a semiquartal spelling for 7/6 (absolute)', () => { const q = evaluate('str(absoluteFJS(7/6, "q"))'); - expect(q).toBe('φ♮4^7q'); + expect(q).toBe('αd4^7q'); }); it('has a simple semiquartal (canceling) spelling for 15/13', () => { @@ -1449,23 +1449,13 @@ describe('SonicWeave expression evaluator', () => { expect(q).toBe('m2^17q_5q'); }); - it('has true semiquartal accidentals (scarab)', () => { - const phiScarab = parseSingle('C4 = 1;φ¤4'); - expect(phiScarab.value.toFraction().toFraction()).toBe('81/64'); - }); - - it('has true semiquartal accidentals (pound)', () => { - const chiPound = parseSingle('C4 = 1;χ£4'); - expect(chiPound.value.toFraction().toFraction()).toBe('32/27'); - }); - it('has a tone-splitter spelling for 24/23', () => { - const lesserVicesimotertial = parseSingle('n1.5_23t'); + const lesserVicesimotertial = parseSingle('P1.5_23t'); expect(lesserVicesimotertial.value.toFraction().toFraction()).toBe('24/23'); }); it('has semioctave spelling for 17/12', () => { - const zeta = parseSingle('C4 = 1; ζ4^17t'); + const zeta = parseSingle('C4 = 1; γ4^17t'); expect(zeta.value.toFraction().toFraction()).toBe('17/12'); }); @@ -1612,11 +1602,11 @@ describe('SonicWeave expression evaluator', () => { it('has a string representation for the eighth fifth (relative)', () => { const {interval} = parseSingle('P5 % 8'); - expect(interval.toString()).toBe('¼m1.5'); + expect(interval.toString()).toBe('⅛d1.5'); }); it('parses the eighth fifth', () => { - const quarterMinorSesquith = parseSingle('qm1.5'); + const quarterMinorSesquith = parseSingle('⅛d1.5'); expect(quarterMinorSesquith.value.primeExponents[0].toFraction()).toBe( '-1/8' ); @@ -1627,23 +1617,23 @@ describe('SonicWeave expression evaluator', () => { it('has a string representation for the eighth fifth (absolute)', () => { const {interval} = parseSingle('absoluteFJS(P5 % 8)'); - expect(interval.toString()).toBe('γ⅛♭4'); + expect(interval.toString()).toBe('η⅛♭4'); }); it('parses the absolute eighth fifth (eighth flat)', () => { - const gamma = parseSingle('γ⅛♭4'); + const gamma = parseSingle('η⅛♭4'); expect(gamma.value.primeExponents[0].toFraction()).toBe('-1/8'); expect(gamma.value.primeExponents[1].toFraction()).toBe('1/8'); }); it('parses the absolute eighth fifth (quarter semiflat)', () => { - const gamma = parseSingle('gammaqd4'); + const gamma = parseSingle('etaqd4'); expect(gamma.value.primeExponents[0].toFraction()).toBe('-1/8'); expect(gamma.value.primeExponents[1].toFraction()).toBe('1/8'); }); it("is kind of weird how 4/8 - 11/8 is -7/8 and how it pairs up with 7/8 of the three's exponent", () => { - const {interval} = parseSingle('8 * relative(zeta⅛#4) - 7 * P5'); + const {interval} = parseSingle('8 * relative(gam⅛#4) - 7 * P5'); expect(interval.totalCents()).toBe(0); }); @@ -1787,12 +1777,6 @@ describe('SonicWeave expression evaluator', () => { expect(equave.toFraction()).toBe('27/16'); }); - it('can split the scarab for no particular reason', () => { - const whatever = parseSingle('C4 = 1;ψ⅒¤3'); - expect(whatever.value.primeExponents[0].toFraction()).toBe('-17/10'); - expect(whatever.value.primeExponents[1].toFraction()).toBe('19/20'); - }); - it('has "Aug" as an alternative spelling for "a"', () => { const {fraction} = parseSingle('Aug4'); expect(fraction).toBe('729/512'); diff --git a/src/parser/__tests__/source.spec.ts b/src/parser/__tests__/source.spec.ts index c5b9889b..16112ec8 100644 --- a/src/parser/__tests__/source.spec.ts +++ b/src/parser/__tests__/source.spec.ts @@ -202,7 +202,7 @@ describe('SonicWeave parser', () => { }); it('supports interordinal nominals', () => { - const scale = parseSource('C6 = 1000 Hz; ζ6;'); + const scale = parseSource('C6 = 1000 Hz; γ6;'); expect(scale).toHaveLength(1); expect(scale[0].value.valueOf()).toBeCloseTo(1414.213562373095); }); @@ -216,7 +216,7 @@ describe('SonicWeave parser', () => { }); it('can spell the major scale in Greek', () => { - const scale = parseSource('ζ0=1/1;η0;α0;β0;γ1;δ1;ε1;ζ1;'); + const scale = parseSource('γ0=1/1;δ0;ε0;ζ0;η1;α1;β1;γ1;'); expect(scale).toHaveLength(7); expect(scale.map(i => i.value.toFraction().toFraction()).join(';')).toBe( '9/8;81/64;4/3;3/2;27/16;243/128;2' @@ -230,19 +230,19 @@ describe('SonicWeave parser', () => { C0 = 1/1 = 261.6 Hz; // First cycle (Greek - Latin - Greek...) - gamma0; // Or γ0 if you want to get fancy. + eta0; // Or η0 if you want to get fancy. D0; - delta0; + alp0; E0; - epsilon0; // Or F0 depending on taste. - zeta0; // Period + bet0; // Or F0 depending on taste. + gam0; // Period // Second cycle (Latin - Greek - Latin ...) G0; - eta0; + del0; A0; - alpha0; - B0; // Or beta0 depending on taste. + eps0; + B0; // Or zet0 depending on taste. C1; // Equave = 2 * period // Temperament @@ -267,15 +267,15 @@ describe('SonicWeave parser', () => { }); it('has ups-and-downs', () => { - const scale = parseSource('C0=1/1;^C0;γ0;vD0;D0;22@;'); + const scale = parseSource('C0=1/1;^C0;η0;vD0;D0;22@;'); expect(scale).toHaveLength(4); expect(scale.map(i => i.toString()).join(';')).toBe( '1\\22;2\\22;3\\22;4\\22' ); }); - it('has soft-jaric accidentals', () => { - const scale = parseSource('C0=1/1;Cr0;γ0;Dp0;D0;22@;'); + it('can make soft-jaric accidentals', () => { + const scale = parseSource('^=a4-P8/2;C0=1/1;^C0;η0;vD0;D0;22@;'); expect(scale).toHaveLength(4); expect(scale.map(i => i.toString()).join(';')).toBe( '1\\22;2\\22;3\\22;4\\22' @@ -380,24 +380,32 @@ describe('SonicWeave parser', () => { it('has manual/semiquartal notation for 19edo', () => { const scale = parseSource(` + ^ = P4 / 2 - M2; + + const φ0 = P4 / 2; + const χ0 = φ0 + M2; + const ψ0 = P8 - P4 / 2; + const ω0 = ψ0 + M2; + const [vχ0, vω0] = v{[χ0, ω0]}; + C0 = 1/1; - C&0; - D@0; + ^C0; + vD0; D0; φ0; - φ&0; - χ@0; + ^φ0; + vχ0; χ0; F0; - F&0; - G@0; + ^F0; + vG0; G0; - G&0; - A@0; + ^G0; + vA0; A0; ψ0; - ψ&0; - ω@0; + ^ψ0; + vω0; ω0; C1; 19@; diff --git a/src/pythagorean.ts b/src/pythagorean.ts index 463d510e..e4234f16 100644 --- a/src/pythagorean.ts +++ b/src/pythagorean.ts @@ -88,7 +88,7 @@ export type Pythagorean = { }; /** - * Absolute pitch nominal: Traditional Pythagorean, semioctave or semiquartal. + * Absolute pitch nominal: Traditional Pythagorean or semioctave. */ export type Nominal = | 'A' @@ -98,28 +98,20 @@ export type Nominal = | 'E' | 'F' | 'G' - | 'alpha' + | 'alp' | 'α' - | 'beta' + | 'bet' | 'β' - | 'gamma' + | 'gam' | 'γ' - | 'delta' + | 'del' | 'δ' - | 'epsilon' + | 'eps' | 'ε' - | 'zeta' + | 'zet' | 'ζ' | 'eta' - | 'η' - | 'phi' - | 'φ' - | 'chi' - | 'χ' - | 'psi' - | 'ψ' - | 'omega' - | 'ω'; + | 'η'; /** * Musical accidental representing some powers of primes 2 and 3, possibly fractional. @@ -138,13 +130,7 @@ export type Accidental = | '=' | 'd' | '♭' - | 'b' - | '&' - | '@' - | 'r' - | 'p' - | '¤' - | '£'; + | 'b'; /** * Musical accidental representing some (possibly split) powers of primes 2 and 3, possibly fractional. @@ -173,12 +159,13 @@ const NEGATIVE_HALF = F(-1, 2); const SESQUI = F(3, 2); const NEGATIVE_SESQUI = F(-3, 2); const NEGATIVE_TWO = Object.freeze(TWO.neg()); +const NEGATIVE_THREE = Object.freeze(THREE.neg()); const NEGATIVE_SEVEN = Object.freeze(SEVEN.neg()); const FOUR = F(4, 1); -const NINE = F(9, 1); const NEGATIVE_ELEVEN = Object.freeze(ELEVEN.neg()); const FOURTEEN = F(14, 1); const SEMIFIVE = F(5, 2); +const NEGATIVE_SEMIFIVE = F(-5, 2); const SEMISEVEN = F(7, 2); const SEMININE = F(9, 2); const SEMIELEVEN = F(11, 2); @@ -195,19 +182,20 @@ const PYTH_VECTORS: PythInflection[] = [ ]; const MID_FOURTH: PythInflection = [F(-7, 2), SEMIFIVE]; -const MID_FIFTH: PythInflection = [SEMININE, F(-5, 2)]; - -// Exponents for "neutral" interordinal intervals related to Pythagoras by a semioctave. -// Splits the whole tone in half precisely in the middle. -// Implicitly define semiquartal intervals. -// Associated with eighth sharps. -const TONESPLITTER_VECTORS: PythInflection[] = [ +const MID_FIFTH: PythInflection = [SEMININE, NEGATIVE_SEMIFIVE]; +const MID_SESQUITH: PythInflection = [FOUR, NEGATIVE_SEMIFIVE]; +const MID_SESQUITH_COMPLEMENT: PythInflection = [NEGATIVE_THREE, SEMIFIVE]; + +// Exponents for interordinal intervals related to Pythagoras by a semioctave. +// Also splits the whole-tone in half precisely in the middle. +// Also splits the perfect fourth. +const SEMIOCTAVE_VECTORS: PythInflection[] = [ [NEGATIVE_SESQUI, ONE], - [F(-9, 2), THREE], - [F(-15, 2), FIVE], + [ONE, NEGATIVE_HALF], + [NEGATIVE_TWO, SESQUI], [HALF, ZERO], - [F(-5, 2), TWO], - [F(-11, 2), FOUR], + [THREE, NEGATIVE_SESQUI], + [ZERO, HALF], [SEMIFIVE, NEGATIVE_ONE], ]; @@ -220,40 +208,35 @@ const NOMINAL_VECTORS = new Map([ ['E', [F(-6, 1), FOUR]], ['B', [F(-7, 1), FIVE]], - // Tone-splitters - ['beta', [SEMIFIVE, NEGATIVE_ONE]], - ['β', [SEMIFIVE, NEGATIVE_ONE]], - - ['zeta', [HALF, ZERO]], - ['ζ', [HALF, ZERO]], - - ['gamma', [NEGATIVE_SESQUI, ONE]], - ['γ', [NEGATIVE_SESQUI, ONE]], + // Latin +- semioctave = Greek - ['eta', [F(-5, 2), TWO]], - ['η', [F(-5, 2), TWO]], + // F + 1\2 + ['zet', [SEMIFIVE, NEGATIVE_ONE]], + ['ζ', [SEMIFIVE, NEGATIVE_ONE]], - ['delta', [F(-9, 2), THREE]], - ['δ', [F(-9, 2), THREE]], + // C + 1\2 + ['gam', [HALF, ZERO]], + ['γ', [HALF, ZERO]], - ['alpha', [F(-11, 2), FOUR]], - ['α', [F(-11, 2), FOUR]], + // G - 1\2 + ['eta', [NEGATIVE_SESQUI, ONE]], + ['η', [NEGATIVE_SESQUI, ONE]], - ['epsilon', [F(-15, 2), FIVE]], - ['ε', [F(-15, 2), FIVE]], + // D + 1\2 + ['del', [F(-5, 2), TWO]], + ['δ', [F(-5, 2), TWO]], - // Manual / semiquartal - ['phi', [ONE, NEGATIVE_HALF]], - ['φ', [ONE, NEGATIVE_HALF]], + // A - 1\2 + ['alp', [F(-9, 2), THREE]], + ['α', [F(-9, 2), THREE]], - ['chi', [NEGATIVE_TWO, SESQUI]], - ['χ', [NEGATIVE_TWO, SESQUI]], + // E + 1\2 + ['eps', [F(-11, 2), FOUR]], + ['ε', [F(-11, 2), FOUR]], - ['psi', [ZERO, HALF]], - ['ψ', [ZERO, HALF]], - - ['omega', [F(-3, 1), SEMIFIVE]], - ['ω', [F(-3, 1), SEMIFIVE]], + // B - 1\2 + ['bet', [F(-15, 2), FIVE]], + ['β', [F(-15, 2), FIVE]], ]); const ACCIDENTAL_VECTORS = new Map([ @@ -277,18 +260,6 @@ const ACCIDENTAL_VECTORS = new Map([ ['𝄳', [SEMIELEVEN, F(-7, 2)]], ['d', [SEMIELEVEN, F(-7, 2)]], - - // Soft-jaric accidentals - ['r', [F(-19, 2), F(6, 1)]], - ['p', [F(19, 2), F(-6, 1)]], - - // Manual Diamond-MOS accidentals - ['&', [FOUR, F(-5, 2)]], - ['@', [F(-4, 1), SEMIFIVE]], - - // True semiquartal accidentals - ['¤', [F(-7, 1), SEMININE]], - ['£', [SEVEN, F(-9, 2)]], ]); const VULGAR_FRACTIONS = new Map([ @@ -331,11 +302,12 @@ const MINOR: PythInflection = [ ]; export function pythagoreanMonzo(node: Pythagorean): TimeMonzo { + const base = node.degree.base; let vector: PythInflection; - if (Number.isInteger(node.degree.base)) { - vector = [...PYTH_VECTORS[node.degree.base - 1]]; + if (Number.isInteger(base)) { + vector = [...PYTH_VECTORS[base - 1]]; } else { - vector = [...TONESPLITTER_VECTORS[node.degree.base - 1.5]]; + vector = [...SEMIOCTAVE_VECTORS[base - 1.5]]; } const quality = node.quality.quality; @@ -356,10 +328,14 @@ export function pythagoreanMonzo(node: Pythagorean): TimeMonzo { } } else { if (quality === 'n') { - if (node.degree.base === 4) { + if (base === 4) { vector = [...MID_FOURTH]; - } else { + } else if (base === 5) { vector = [...MID_FIFTH]; + } else if (base === 1.5) { + vector = [...MID_SESQUITH]; + } else if (base === 7.5) { + vector = [...MID_SESQUITH_COMPLEMENT]; } } } @@ -484,7 +460,7 @@ export function monzoToNode(monzo: TimeMonzo): Pythagorean | undefined { if (Number.isInteger(stepspan)) { offCenter = threes.sub(PYTH_VECTORS[base - 1][1]).div(SEVEN); } else if (mmod(stepspan, 1) === 0.5) { - offCenter = threes.sub(TONESPLITTER_VECTORS[base - 1.5][1]).div(SEVEN); + offCenter = threes.sub(SEMIOCTAVE_VECTORS[base - 1.5][1]).div(SEVEN); } else { return undefined; } @@ -493,7 +469,7 @@ export function monzoToNode(monzo: TimeMonzo): Pythagorean | undefined { if (offCenter.abs().compare(MAX_OFFSET) > 0) { return undefined; } - const imperfect = ![1, 4, 5].includes(base); + const imperfect = ![1, 4, 5, 1.5, 4.5, 7.5].includes(base); let quality: IntervalQuality | undefined; const augmentations: AugmentedQuality[] = []; if (imperfect) { @@ -535,7 +511,7 @@ export function monzoToNode(monzo: TimeMonzo): Pythagorean | undefined { const PURE_NOMINALS: Nominal[] = ['C', 'D', 'E', 'F', 'G', 'A', 'B']; -const TONESPLITTER_NOMINALS: Nominal[] = ['γ', 'δ', 'ε', 'ζ', 'η', 'α', 'β']; +const SEMIOCTAVE_NOMINALS: Nominal[] = ['η', 'α', 'β', 'γ', 'δ', 'ε', 'ζ']; const ACCIDENTAL_BY_OFFSET = new Map(); const BASE_OFFSETS: [Fraction, Accidental][] = []; @@ -575,7 +551,7 @@ export function absoluteToNode(monzo: TimeMonzo): AbsolutePitch | undefined { if (spanRemainder === 0) { nominal = PURE_NOMINALS[mmod(stepspan, 7)]; } else if (spanRemainder === 0.5) { - nominal = TONESPLITTER_NOMINALS[mmod(stepspan - 0.5, 7)]; + nominal = SEMIOCTAVE_NOMINALS[mmod(stepspan - 0.5, 7)]; } else { return undefined; } @@ -618,61 +594,3 @@ export function absoluteToNode(monzo: TimeMonzo): AbsolutePitch | undefined { octave, }; } - -const SEMIQUARTAL_NOMINALS: Nominal[] = [ - 'C', - 'D', - 'φ', - 'χ', - 'F', - 'G', - 'A', - 'ψ', - 'ω', -]; - -export function absoluteToSemiquartal( - monzo: TimeMonzo -): AbsolutePitch | undefined { - const twos = monzo.primeExponents[0]; - const threes = monzo.primeExponents[1]; - const stepspan = twos.mul(NINE).add(threes.mul(FOURTEEN)).valueOf(); - const octave = Math.floor(stepspan / 9) + 4; - - if (!Number.isInteger(stepspan)) { - return undefined; - } - const nominal = SEMIQUARTAL_NOMINALS[mmod(stepspan, 9)]; - - let offCenter = threes.sub(NOMINAL_VECTORS.get(nominal)![1]).div(SEMININE); - - // Enforce sanity limits. - if (offCenter.abs().compare(MAX_OFFSET) > 0) { - return undefined; - } - - const accidentals: SplitAccidental[] = []; - while (offCenter.s < 0) { - accidentals.push({fraction: '', accidental: '£'}); - offCenter = offCenter.add(ONE); - } - while (offCenter.s > 0) { - accidentals.push({fraction: '', accidental: '¤'}); - offCenter = offCenter.sub(ONE); - } - - if (offCenter.n) { - return undefined; - } - - if (!accidentals.length) { - accidentals.push({fraction: '', accidental: '♮'}); - } - - return { - type: 'AbsolutePitch', - nominal, - accidentals, - octave, - }; -}