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 May 21, 2023
1 parent c75afb2 commit e9e540b
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 46 deletions.
45 changes: 15 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"vue-router": "^4.1.5",
"webmidi": "^3.0.21",
"xen-dev-utils": "^0.1.2",
"scale-workshop-core": "github:xenharmonic-devs/scale-workshop-core#v0.0.1"
"scale-workshop-core": "github:xenharmonic-devs/scale-workshop-core#71722b7a6f12c8e967817e88422006858636671e"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
Expand Down
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 @@ -85,3 +87,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 emit = defineEmits(["update:scale", "update:scaleName", "cancel"]);
Expand Down
3 changes: 2 additions & 1 deletion src/components/modals/modification/TemperScale.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { DEFAULT_NUMBER_OF_COMPONENTS } from "@/constants";
import { Mapping } from "@/tempering";
import { 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 { PRIME_CENTS } from "xen-dev-utils";
import type { Scale } from "scale-workshop-core";
import { splitText } from "@/utils";
const props = defineProps<{
scale: Scale;
Expand Down
18 changes: 7 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,7 @@ export function makeState(method: Ref, subgroupStringDefault = "") {
// === Computed state ===
const vals = computed(() => splitText(valsString.value));

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

const subgroupDefault = new Subgroup(subgroupStringDefault || []);
const [subgroup, subgroupError] = computedAndError(() => {
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 e9e540b

Please sign in to comment.