From 0aeaea08d5882252c1787a574c0246640efe42c9 Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Mon, 8 Jul 2024 18:24:14 +0300 Subject: [PATCH] Migrate from temperaments package to sonic-weave/Temperament Fix circle step pattern display with multiple periods. Remove support for non-equave constraints. --- package-lock.json | 142 +------ package.json | 3 +- src/__tests__/tempering.spec.ts | 72 ---- src/components/PeriodCircle.vue | 3 +- src/components/modals/generation/RankTwo.vue | 233 +++++------ .../modals/generation/SpanLattice.vue | 170 ++++---- .../modals/modification/TemperScale.vue | 292 ++++++-------- src/constants.ts | 7 - src/stores/tempering.ts | 377 ++++++------------ src/tempering.ts | 122 ------ 10 files changed, 446 insertions(+), 975 deletions(-) delete mode 100644 src/__tests__/tempering.spec.ts delete mode 100644 src/tempering.ts diff --git a/package-lock.json b/package-lock.json index 9490034b..6b96716e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,8 @@ "moment-of-symmetry": "^0.8.2", "pinia": "^2.1.7", "qs": "^6.12.0", - "sonic-weave": "0.9.0", + "sonic-weave": "^0.10.1", "sw-synth": "^0.1.0", - "temperaments": "^0.5.3", "values.js": "^2.1.1", "vue": "^3.3.4", "vue-router": "^4.3.0", @@ -2089,18 +2088,6 @@ "node": ">=4.0.0" } }, - "node_modules/complex.js": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", - "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, "node_modules/computeds": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", @@ -2292,7 +2279,8 @@ "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true }, "node_modules/deep-eql": { "version": "4.1.3", @@ -2566,11 +2554,6 @@ "node": ">=12" } }, - "node_modules/escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3144,18 +3127,6 @@ "node": ">= 0.12" } }, - "node_modules/fraction.js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.4.tgz", - "integrity": "sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -3787,11 +3758,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" - }, "node_modules/jazz-midi": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/jazz-midi/-/jazz-midi-1.7.9.tgz", @@ -4263,28 +4229,6 @@ "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", "dev": true }, - "node_modules/mathjs": { - "version": "12.4.1", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-12.4.1.tgz", - "integrity": "sha512-welnW3khgwYjPYvECFHO+xkCxAx9IKIIPDDWPi8B5rKAvmgoEHnQX9slEmHKZTNaJiE+OS4qrJJcB4sfDn/4sw==", - "dependencies": { - "@babel/runtime": "^7.24.0", - "complex.js": "^2.1.1", - "decimal.js": "^10.4.3", - "escape-latex": "^1.2.0", - "fraction.js": "4.3.4", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^4.1.1" - }, - "bin": { - "mathjs": "bin/cli.js" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -5366,11 +5310,6 @@ "node": ">=v12.22.7" } }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -5502,9 +5441,9 @@ } }, "node_modules/sonic-weave": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/sonic-weave/-/sonic-weave-0.9.0.tgz", - "integrity": "sha512-dBtXA9CEo9/4CCNc5+9k1JMeegSijw3o+SACSzsxo7MpERheNVmpHlGtjjS0PeALoP8E6erwBzHqI83N+ylfqg==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/sonic-weave/-/sonic-weave-0.10.1.tgz", + "integrity": "sha512-gDKvywuHQcauy42joWmY6aOPvBr0wMMHoe5Npc+kexwzcaxUBRvrGEu6HI9izx8UGeJyOgGaTmB92VAf/WmQkg==", "dependencies": { "moment-of-symmetry": "^0.8.2", "xen-dev-utils": "^0.11.0" @@ -5815,32 +5754,6 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/temperaments": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/temperaments/-/temperaments-0.5.3.tgz", - "integrity": "sha512-+gVFX5vfYOv7fQzd9AKQpgMvaW+ww4bZfmOQ1vktXexfQgbhscC5SAQm1stfWg94ahDf7AkUwVkY2tLQFUj+rw==", - "dependencies": { - "mathjs": "^12.4.0", - "ts-geometric-algebra": "^0.5.1", - "xen-dev-utils": "^0.2.7" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/frostburn" - } - }, - "node_modules/temperaments/node_modules/xen-dev-utils": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/xen-dev-utils/-/xen-dev-utils-0.2.9.tgz", - "integrity": "sha512-ngs85djTa5MH9yMomyFg1ZK1eETgtaG6FN5ZvDazyBbGmShuK/gOEWGRemHqm+I2zp3lOGbXlEU/M4SwYl3HLA==", - "engines": { - "node": ">=10.6.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/frostburn" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5862,11 +5775,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, "node_modules/tinybench": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.7.0.tgz", @@ -5960,36 +5868,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-geometric-algebra": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/ts-geometric-algebra/-/ts-geometric-algebra-0.5.1.tgz", - "integrity": "sha512-7GoWXFQqY8N+RDL9WyD0IzH+XCdpVoaojw0ortWQqSkocHzCVlTTEOuafYS793GtIVcRoiUkbO8QlPykR04F9w==", - "dependencies": { - "mathjs": "^11.5.0" - } - }, - "node_modules/ts-geometric-algebra/node_modules/mathjs": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.12.0.tgz", - "integrity": "sha512-UGhVw8rS1AyedyI55DGz9q1qZ0p98kyKPyc9vherBkoueLntPfKtPBh14x+V4cdUWK0NZV2TBwqRFlvadscSuw==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "complex.js": "^2.1.1", - "decimal.js": "^10.4.3", - "escape-latex": "^1.2.0", - "fraction.js": "4.3.4", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^4.1.1" - }, - "bin": { - "mathjs": "bin/cli.js" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -6047,14 +5925,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-function": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.1.tgz", - "integrity": "sha512-Pq1DVubcvibmm8bYcMowjVnnMwPVMeh0DIdA8ad8NZY2sJgapANJmiigSUwlt+EgXxpfIv8MWrQXTIzkfYZLYQ==", - "engines": { - "node": ">= 14" - } - }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", diff --git a/package.json b/package.json index 3c3c8f4c..90e0c4a9 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,8 @@ "moment-of-symmetry": "^0.8.2", "pinia": "^2.1.7", "qs": "^6.12.0", - "sonic-weave": "0.9.0", + "sonic-weave": "^0.10.1", "sw-synth": "^0.1.0", - "temperaments": "^0.5.3", "values.js": "^2.1.1", "vue": "^3.3.4", "vue-router": "^4.3.0", diff --git a/src/__tests__/tempering.spec.ts b/src/__tests__/tempering.spec.ts deleted file mode 100644 index d2da4220..00000000 --- a/src/__tests__/tempering.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { describe, it, expect } from 'vitest' - -import { - makeRank2FromCommas, - makeRank2FromVals, - mosPatternsRank2FromCommas, - mosPatternsRank2FromVals, - toPrimeMapping -} from '../tempering' -import { arraysEqual, PRIME_CENTS, valueToCents } from 'xen-dev-utils' -import { Subgroup } from 'temperaments' -import { DEFAULT_NUMBER_OF_COMPONENTS } from '../constants' - -describe('Prime map converter', () => { - it('does (almost) nothing in 5-limit JI', () => { - const fiveLimit = PRIME_CENTS.slice(0, 3) - const mapping = toPrimeMapping(fiveLimit, new Subgroup(5)) - expect(mapping[0]).toBeCloseTo(fiveLimit[0]) - expect(mapping[1]).toBeCloseTo(fiveLimit[1]) - expect(mapping[2]).toBeCloseTo(fiveLimit[2]) - expect(mapping.length).toBe(DEFAULT_NUMBER_OF_COMPONENTS) - expect(mapping[3]).toBeCloseTo(PRIME_CENTS[3]) - }) - - it('converts a mapping in 2.3.13/5 to 2.3.5.7.11.13...', () => { - const original = [1200, 1901, 1654] - const mapping = toPrimeMapping(original, new Subgroup('2.3.13/5')) - expect(mapping[0]).toBeCloseTo(1200) - expect(mapping[1]).toBeCloseTo(1901) - expect(mapping[3]).toBeCloseTo(PRIME_CENTS[3]) - expect(mapping[4]).toBeCloseTo(PRIME_CENTS[4]) - expect(mapping[5] - mapping[2]).toBeCloseTo(1654) - expect(mapping[6]).toBeCloseTo(PRIME_CENTS[6]) - }) -}) - -describe('Tempered scale generation', () => { - it('generates CTE augmented from vals', () => { - const vals = [3, 12] - const limit = 5 - const options = { - constraints: ['2/1'] - } - const infos = mosPatternsRank2FromVals(vals, limit, undefined, 5, options) - const sizes = infos.map((info) => info.numberOfLargeSteps + info.numberOfSmallSteps) - expect(arraysEqual(sizes, [6, 9, 12, 15, 27])).toBeTruthy() - - const { generator, period, numPeriods } = makeRank2FromVals(vals, sizes[2], limit, options) - expect(period).toBeCloseTo(400) - expect(generator + period).toBeCloseTo(valueToCents(3 / 2)) - expect(numPeriods).toBe(3) - }) - - it('generates POTE meantone from a comma', () => { - const commas = ['81/80'] - const infos = mosPatternsRank2FromCommas(commas, undefined, undefined, 5) - const sizes = infos.map((info) => info.numberOfLargeSteps + info.numberOfSmallSteps) - expect(arraysEqual(sizes, [2, 3, 5, 7, 12])).toBeTruthy() - - const { generator, period, numPeriods } = makeRank2FromCommas(commas, sizes[3]) - expect(generator).toBeCloseTo(696.239) - expect(period).toBeCloseTo(1200) - expect(numPeriods).toBe(1) - }) - - it('generates frostburn from a comma', () => { - const commas = [[-5, 0, 7, -4, 0, 0, 0]] - const infos = mosPatternsRank2FromCommas(commas, new Subgroup('2.5.7'), undefined, 6) - const sizes = infos.map((info) => info.numberOfLargeSteps + info.numberOfSmallSteps) - expect(sizes).toEqual([2, 3, 4, 5, 6, 11]) - }) -}) diff --git a/src/components/PeriodCircle.vue b/src/components/PeriodCircle.vue index 9835b428..da78a6ba 100644 --- a/src/components/PeriodCircle.vue +++ b/src/components/PeriodCircle.vue @@ -109,7 +109,8 @@ const mosLabel = computed(() => { else if (step === M) countM++ else if (step === L) countL++ } - return `${countL}L ${countM}M ${countS}s` + const n = props.numPeriods + return `${n * countL}L ${n * countM}M ${n * countS}s` } // There are degenerate edgecases with step variety = 2. Show nothing to avoid implying MOS. return '' diff --git a/src/components/modals/generation/RankTwo.vue b/src/components/modals/generation/RankTwo.vue index 4b71233a..1226c09a 100644 --- a/src/components/modals/generation/RankTwo.vue +++ b/src/components/modals/generation/RankTwo.vue @@ -1,15 +1,15 @@ @@ -351,27 +300,7 @@ function generate(expand = true) { - -
+
-
+
@@ -410,7 +339,28 @@ function generate(expand = true) {
-
+
+
+ + + + + + + + + + + + + + +

Advanced options

-
-
- - - - - - - - - - - - - - -
- -
- - -
- -
- - -
+
+ + +
+

{{ rank2.error }}

+
+
+
+ MOS sizes + +
+
+
@@ -463,7 +407,7 @@ function generate(expand = true) { > Raw - + {{ rank2.previewMosPattern }}
@@ -492,4 +436,9 @@ function generate(expand = true) { .wide-range { width: 100%; } + +p.warning { + height: 3em; + overflow-y: hidden; +} diff --git a/src/components/modals/generation/SpanLattice.vue b/src/components/modals/generation/SpanLattice.vue index 62dc36f1..1c6fb63a 100644 --- a/src/components/modals/generation/SpanLattice.vue +++ b/src/components/modals/generation/SpanLattice.vue @@ -3,12 +3,12 @@ import { ref, watch } from 'vue' import Modal from '@/components/ModalDialog.vue' import ScaleLineInput from '@/components/ScaleLineInput.vue' import { OCTAVE } from '@/constants' -import Temperament from 'temperaments' import { useLatticeStore } from '@/stores/tempering' -import { setAndReportValidity } from '@/utils' +import { centString, setAndReportValidity } from '@/utils' import { useStateStore } from '@/stores/state' import { arrayToString, expandCode, parseInterval } from '@/utils' import { Interval, intervalValueAs } from 'sonic-weave' +import { mmod } from 'xen-dev-utils' defineProps<{ show: boolean @@ -20,33 +20,36 @@ const lattice = useLatticeStore() const state = useStateStore() const basisElement = ref(null) +const valsInput = ref(null) +const commasInput = ref(null) +const subgroupInput = ref(null) + watch( () => lattice.basisError, (newError) => setAndReportValidity(basisElement.value, newError) ) function calculateGenerators() { - let temperament: Temperament + const generators = lattice.temperament.generators + if (!generators.length) { + return + } if (lattice.method === 'vals') { - temperament = Temperament.fromVals(lattice.vals, lattice.subgroup) + lattice.comment = `vals = ${lattice.valsString}` + } else if (lattice.method === 'commas') { + lattice.comment = `commas = ${lattice.commasString}` } else { - temperament = Temperament.fromCommas(lattice.commas, lattice.subgroup) + lattice.comment = '' } - let periodGenerators: number[] - try { - periodGenerators = temperament.periodGenerators(lattice.options) - } catch { - alert('Unable to calculate generators.') - return + const period = generators.shift()! + for (let i = 0; i < generators.length; ++i) { + generators[i] = mmod(generators[i], period) } - lattice.equaveString = periodGenerators[0].toFixed(state.centsFractionDigits) + lattice.equaveString = centString(period, state.centsFractionDigits) lattice.equave = parseInterval(lattice.equaveString) - lattice.basisString = periodGenerators - .slice(1) - .map((g) => g.toFixed(state.centsFractionDigits)) - .join(' ') + lattice.basisString = generators.map((g) => centString(g, state.centsFractionDigits)).join(' ') lattice.method = 'generators' } @@ -71,28 +74,24 @@ function flip(index: number) { } function generate(expand = true) { - try { - const source = `parallelotope(${arrayToString(lattice.basis)}, ${arrayToString(lattice.ups)}, ${arrayToString(lattice.downs)}, ${lattice.equave.toString()})` - let name = `Parallelotope (${lattice.dimensions} of ${lattice.basisString}` - if (lattice.basis.length === 0) { - name = 'Parallelotope (unison' - } - if (!lattice.equave.equals(OCTAVE)) { - name += ` over ${lattice.equave.toString()}` - } - name += ')' - emit('update:scaleName', name) - if (expand) { - emit('update:source', expandCode(source)) - } else { - emit('update:source', source) - } - } catch (error) { - if (error instanceof Error) { - alert(error.message) - } else { - alert(error) - } + let source = '' + if (lattice.comment.length) { + source = `(* ${lattice.comment} *)\n` + } + source += `parallelotope(${arrayToString(lattice.basis)}, ${arrayToString(lattice.ups)}, ${arrayToString(lattice.downs)}, ${lattice.equave.toString()})` + let name = `Parallelotope (${lattice.dimensions} of ${lattice.basisString}` + if (lattice.basis.length === 0) { + name = 'Parallelotope (unison' + } + if (!lattice.equave.equals(OCTAVE)) { + name += ` over ${lattice.equave.toString()}` + } + name += ')' + emit('update:scaleName', name) + if (expand) { + emit('update:source', expandCode(source)) + } else { + emit('update:source', source) } } @@ -167,14 +166,22 @@ function generate(expand = true) { />
+
- +
-
-
-

- Advanced options -

-
-
- - - - - - - - - - - - - - -
- -
- - -
- -
- - -
-
+
+
+ + + + + + + + + + + + + + +
+

+ Advanced options +

+
+ +
+

{{ lattice.error }}

-
@@ -246,3 +255,10 @@ function generate(expand = true) { + + diff --git a/src/components/modals/modification/TemperScale.vue b/src/components/modals/modification/TemperScale.vue index 7bef62b7..836a7369 100644 --- a/src/components/modals/modification/TemperScale.vue +++ b/src/components/modals/modification/TemperScale.vue @@ -1,11 +1,10 @@