Skip to content

Commit

Permalink
Add support for monzo syntax inside chords and comma lists
Browse files Browse the repository at this point in the history
ref #364
  • Loading branch information
frostburn committed Nov 26, 2023
1 parent a300c73 commit f809b0b
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 16 deletions.
22 changes: 22 additions & 0 deletions src/__tests__/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
formatExponential,
formatHertz,
gapKeyColors,
parseChordInput,
} from "../utils";
import { DEFAULT_NUMBER_OF_COMPONENTS } from "../constants";

function naiveExponential(x: number, fractionDigits = 3) {
if (Math.abs(x) < 10000) {
Expand Down Expand Up @@ -97,3 +99,23 @@ describe("Gap key color algorithm", () => {
);
});
});

describe("Chord input parser", () => {
it("parses many types of intervals with many separators supported", () => {
const text = "3:2400.&11/3|1\\5;[-1,1> [0 0 1>-4/1";
const intervals = parseChordInput(text);
expect(intervals[0].monzo.vector.length).toBe(DEFAULT_NUMBER_OF_COMPONENTS);
expect(intervals[0].type).toBe("ratio");
expect(intervals[1].type).toBe("cents");
expect(intervals[2].type).toBe("ratio");
expect(intervals[3].type).toBe("equal temperament");
expect(intervals[4].type).toBe("monzo");

expect(intervals[0].totalCents()).toBeCloseTo(1901.955);
expect(intervals[1].totalCents()).toBeCloseTo(2400);
expect(intervals[2].totalCents()).toBeCloseTo(2249.36);
expect(intervals[3].totalCents()).toBeCloseTo(240);
expect(intervals[4].totalCents()).toBeCloseTo(701.955);
expect(intervals[5].totalCents()).toBeCloseTo(386.31);
});
});
2 changes: 1 addition & 1 deletion src/components/modals/generation/EqualTemperament.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { DEFAULT_NUMBER_OF_COMPONENTS } from "@/constants";
import { computed, ref, watch } from "vue";
import Modal from "@/components/ModalDialog.vue";
import ScaleLineInput from "@/components/ScaleLineInput.vue";
import { splitText } from "@/components/modals/tempering-state";
import { clamp } from "xen-dev-utils";
import { ExtendedMonzo, Interval, Scale } from "scale-workshop-core";
import { splitText } from "@/utils";
const props = defineProps<{
centsFractionDigits: number;
Expand Down
6 changes: 4 additions & 2 deletions src/components/modals/modification/TemperScale.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import { Mapping, stretchToEdo, toPrimeMapping } from "@/tempering";
import { computed, ref, watch } from "vue";
import Modal from "@/components/ModalDialog.vue";
import { makeState, splitText } from "@/components/modals/tempering-state";
import { makeState } from "@/components/modals/tempering-state";
import { add, Fraction, PRIME_CENTS } from "xen-dev-utils";
import { mapByVal, resolveMonzo, tenneyVals, vanishCommas } from "temperaments";
import {
Expand All @@ -15,6 +15,7 @@ import {
Scale,
type IntervalOptions,
} from "scale-workshop-core";
import { splitText } from "@/utils";
const props = defineProps<{
scale: Scale;
Expand Down Expand Up @@ -46,6 +47,7 @@ const constraintsString = state.constraintsString;
// === Computed state ===
const vals = state.vals;
const rawCommas = state.rawCommas;
const commas = state.commas;
const subgroup = state.subgroup;
const options = state.options;
Expand Down Expand Up @@ -168,7 +170,7 @@ function modify() {
options.value.temperEquaves && tempering.value !== "CTE";
const weights = options.value.weights;
const jip = subgroup.value.jip();
const commaMonzos = commas.value.map(
const commaMonzos = rawCommas.value.map(
(comma) => subgroup.value.toMonzoAndResidual(comma)[0]
);
const mappingVector = vanishCommas(
Expand Down
20 changes: 9 additions & 11 deletions src/components/modals/tempering-state.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
// Component state used by RankOne, RankTwo and TemperScale

import { computedAndError } from "@/utils";
import { computedAndError, parseChordInput, splitText } from "@/utils";
import { fractionToString } from "scale-workshop-core";
import { Subgroup, type TuningOptions } from "temperaments";
import { computed, ref, watch, type Ref } from "vue";

// Split text values separated by whitespace, pipes, amps, semicolons or commas.
export function splitText(text: string) {
return text
.replace(/\s/g, ",")
.replace(/\|/g, ",")
.replace(/&/g, ",")
.replace(/;/g, ",")
.split(",")
.filter((token) => token.length);
// Split text into (non-extended) monzos
function splitCommas(text: string) {
return parseChordInput(text).map((interval) =>
interval.monzo.vector.map((component) => component.valueOf())
);
}

export function makeState(method: Ref, subgroupStringDefault = "") {
Expand All @@ -32,7 +28,8 @@ export function makeState(method: Ref, subgroupStringDefault = "") {
// === Computed state ===
const vals = computed(() => splitText(valsString.value));

const commas = computed(() => splitText(commasString.value));
const rawCommas = computed(() => splitText(commasString.value));
const commas = computed(() => splitCommas(commasString.value));

const subgroupDefault = new Subgroup(subgroupStringDefault || []);
const [subgroup, subgroupError] = computedAndError(() => {
Expand Down Expand Up @@ -105,6 +102,7 @@ export function makeState(method: Ref, subgroupStringDefault = "") {
tempering,
constraintsString,
vals,
rawCommas,
commas,
subgroup,
weights,
Expand Down
10 changes: 8 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { computed, type ComputedRef } from "vue";
import { gcd, mmod } from "xen-dev-utils";
import { DEFAULT_NUMBER_OF_COMPONENTS } from "./constants";

// Split at whitespace, pipes, amps, colons, semicolons and commas
export const SEPARATOR_RE = /\s|\||&|:|;|,/;

export function splitText(text: string) {
return text.split(SEPARATOR_RE).filter((token) => token.length);
}

export function parseChordInput(input: string) {
const separator = input.includes(":") ? ":" : /\s/;
return parseChord(input, DEFAULT_NUMBER_OF_COMPONENTS, separator);
return parseChord(input, DEFAULT_NUMBER_OF_COMPONENTS, SEPARATOR_RE);
}

export function debounce(func: (...args: any[]) => void, timeout = 300) {
Expand Down

0 comments on commit f809b0b

Please sign in to comment.