Skip to content

Commit

Permalink
Add more parameters to chord builder
Browse files Browse the repository at this point in the history
  • Loading branch information
jangler committed May 1, 2024
1 parent bb921c5 commit 45de699
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 25 deletions.
16 changes: 10 additions & 6 deletions src/chord-builder/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ <h1>Chord builder</h1>
</header>

<main>
<section class="parameters">
<label for="integerLimit">Integer limit:</label>
<input type="number" id="integerLimit" value="27">
<label for="subgroup">Optional prime subgroup:</label>
<input type="text" id="subgroup">
<label for="octavesBelow">Octaves below:</label>
<input type="number" id="octavesBelow" value="0">
<label for="octavesAbove">Octaves above:</label>
<input type="number" id="octavesAbove" value="2">
</section>
<p>
Click intervals to move them into or out of the chord.
</p>
<section class="flex-row hidden">
<div class="field">
<label for="commas">Commas:</label>
<input type="text" id="commas">
</div>
</section>
<h2>Chord</h2>
<div id="chord" class="flex-row"></div>
<h2>Series</h2>
Expand Down
51 changes: 33 additions & 18 deletions src/chord-builder/script.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
// @ts-ignore
import { html, render } from 'https://unpkg.com/htm/preact/standalone.module.js';
import { gcd } from "../lib/limit.js";
import { gcd, inSubgroup } from "../lib/limit.js";

const commasInput = document.querySelector('#commas') as HTMLInputElement;
const integerLimitInput = document.querySelector('#integerLimit') as HTMLInputElement;
const subgroupInput = document.querySelector('#subgroup') as HTMLInputElement;
const octavesBelowInput = document.querySelector('#octavesBelow') as HTMLInputElement;
const octavesAboveInput = document.querySelector('#octavesAbove') as HTMLInputElement;
const seriesBox = document.querySelector('#series')!;
const dyadsBox = document.querySelector('#dyads')!;
const chordDiv = document.querySelector('#chord')!;
const suggestionsDiv = document.querySelector('#suggestions')!;

type Ratio = [number, number];

let commas: Ratio[] = [];
let integerLimit = integerLimitInput.valueAsNumber;
let subgroup = subgroupInput.value.match(/\d+/g)?.map(s => parseInt(s)) as number[];
let octavesBelow = octavesBelowInput.valueAsNumber;
let octavesAbove = octavesAboveInput.valueAsNumber;
let chordIntervals: Ratio[] = [[1, 1], [5, 4], [3, 2]];

function lcm(xs: number[]): number {
Expand All @@ -32,7 +38,6 @@ function simplify(r: Ratio): Ratio {
}

function dyad(a: Ratio, b: Ratio): Ratio {
// TODO: Simplify using commas.
if (a[0]/a[1] < b[0]/b[1]) {
const c = a;
a = b;
Expand Down Expand Up @@ -65,13 +70,13 @@ function removeInterval(r: Ratio) {
updateDiagnostics();
}

function integerLimitRatios(limit: number): Ratio[] {
function ratios(limit: number, subgroup: number[]): Ratio[] {
const s = new Set<number>();
const a = new Array<Ratio>();
for (let n = 1; n <= limit; n++) {
for (let d = Math.ceil(n / 4); d < n; d++) {
for (let d = Math.ceil(n / 2**octavesAbove); d < n * 2**octavesBelow; d++) {
const k = n/d;
if (!s.has(k)) {
if (!s.has(k) && (subgroup === undefined || inSubgroup(n, d, subgroup))) {
s.add(k);
a.push(simplify([n, d]));
}
Expand Down Expand Up @@ -112,7 +117,7 @@ function renderChord() {
html`<span class="ratio" onClick=${() => removeInterval(r)}>${r.join('/')}</span>`)
}`,
chordDiv);
const nonChordIntervals = integerLimitRatios(27)
const nonChordIntervals = ratios(integerLimit, subgroup)
.filter(r => !chordIntervals.some(x => x[0] == r[0] && x[1] == r[1]));
console.log(nonChordIntervals);
// TODO: Factor series height and critical band into this.
Expand All @@ -132,15 +137,25 @@ function updateDiagnostics() {
.map(r => r.join('/')).join(', ');
}

function updateCommas() {
const matches = commasInput.value.match(/\d+[/:]\d+/g);
if (matches) {
commas = matches.map(s => s.match(/\d+/g)!.map(s => parseInt(s)) as Ratio);
updateDiagnostics();
}
}

renderChord();
updateDiagnostics();
commasInput.addEventListener('input', updateCommas);
updateCommas();

integerLimitInput.addEventListener('change', () => {
integerLimit = integerLimitInput.valueAsNumber;
renderChord();
});

subgroupInput.addEventListener('change', () => {
subgroup = subgroupInput.value.match(/\d+/g)?.map(s => parseInt(s)) as number[];
renderChord();
});

octavesBelowInput.addEventListener('change', () => {
octavesBelow = octavesBelowInput.valueAsNumber;
renderChord();
});

octavesAboveInput.addEventListener('change', () => {
octavesAbove = octavesAboveInput.valueAsNumber;
renderChord();
});
2 changes: 1 addition & 1 deletion src/lib/limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function primeFactors(n: number): number[] {
}

/** Return true if all prime factors of n and d are included in subgroup. */
function inSubgroup(n: number, d: number, subgroup: number[]): boolean {
export function inSubgroup(n: number, d: number, subgroup: number[]): boolean {
if (d == 1) return primeFactors(n).every(f => subgroup.includes(f));
return inSubgroup(n, 1, subgroup) && inSubgroup(d, 1, subgroup);
}
Expand Down

0 comments on commit 45de699

Please sign in to comment.