summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Cully <bjc@spork.org>2025-07-28 08:52:51 -0400
committerBrian Cully <bjc@spork.org>2025-07-28 08:52:51 -0400
commit77f5e81d0a1adc497040eaa89095635f3de3924e (patch)
tree4ad6a00edf5cfe3c5c2bf0090ba80f8d13c143cc
parent2d12e407ccac850dd2e1ce133a2f335677a2ef29 (diff)
downloadchords-77f5e81d0a1adc497040eaa89095635f3de3924e.tar.gz
chords-77f5e81d0a1adc497040eaa89095635f3de3924e.zip
attach octave values to frets
sometimes just do the simple thing
-rw-r--r--fretboard.mjs9
-rw-r--r--history.mjs7
-rw-r--r--index.html2
-rw-r--r--main.mjs70
-rw-r--r--scale.mjs17
-rw-r--r--string.mjs7
-rw-r--r--tests.mjs10
7 files changed, 79 insertions, 43 deletions
diff --git a/fretboard.mjs b/fretboard.mjs
index 9c25ff6..cae94ec 100644
--- a/fretboard.mjs
+++ b/fretboard.mjs
@@ -82,6 +82,12 @@ export default class extends HTMLElement {
});
}
+ get tonics() {
+ return Array.from(this.querySelectorAll('x-string')).map(elt => {
+ return elt.getAttribute('tonic');
+ });
+ }
+
get octaves() {
return Array.from(this.querySelectorAll('x-string')).map(elt => {
return Number(elt.getAttribute('octave'));
@@ -140,8 +146,9 @@ export default class extends HTMLElement {
item.querySelectorAll('[slot="string"]').forEach(s => {
console.debug(' -- setting tonic', tonic, 'on', s);
s.setAttribute('tonic', tonic);
- s.setAttribute('octave', octave);
+ s.setAttribute('tonic-octave', octave);
s.setAttribute('value', tonic);
+ s.setAttribute('octave', octave);
s.setAttribute('frets', frets.toString());
})
tmpl.parentNode.insertBefore(item, tmpl);
diff --git a/history.mjs b/history.mjs
index b7d49ed..e09073a 100644
--- a/history.mjs
+++ b/history.mjs
@@ -18,7 +18,10 @@ export default class extends HTMLElement {
console.debug('History#constructor', this);
this.onClick = e => console.debug('history click', e);
+ }
+ connectedCallback() {
+ console.debug('History#connectedCallback', this);
this.template = this.querySelector('template');
this.list = this.querySelector('ol');
@@ -26,10 +29,6 @@ export default class extends HTMLElement {
this._internals = this.attachInternals();
}
- connectedCallback() {
- console.debug('History#connectedCallback', this);
- }
-
add(fretboard) {
console.debug('History#add', this, fretboard);
this._internals.states.add('fresh');
diff --git a/index.html b/index.html
index 9488e0c..3c02fa2 100644
--- a/index.html
+++ b/index.html
@@ -10,7 +10,7 @@
<body>
<x-fretboard strings='6' frets='7'>
<template class='string'>
- <x-string tonic='' frets='' slot='string' value=''>
+ <x-string tonic='' tonic-octave='' frets='' slot='string' value='' octave=''>
<form>
<input type='checkbox' name='muted'>
<li class='fret open'><slot name='open'></slot></li>
diff --git a/main.mjs b/main.mjs
index 931543a..6c71e9c 100644
--- a/main.mjs
+++ b/main.mjs
@@ -5,6 +5,41 @@ import String from './string.mjs';
import Player from './player.mjs';
import { Note, toCents } from './scale.mjs';
+let player = undefined;
+
+function save(e) {
+ document.querySelectorAll(History.name).forEach(h => {
+ console.debug('h is', h, 'e is', e.detail);
+ h.add(e.detail);
+ });
+}
+
+function play(e) {
+ console.debug('got playEvent', e, e.detail.notes);
+ if (player) {
+ player.stop();
+ }
+
+ const played = e.detail.notes.map((n, i) => {
+ if (n !== 'x') {
+ return [Note.fromString(n), e.detail.octaves[i]];
+ }
+ }).filter(n => n);
+
+ const a = Note.fromString('A');
+ player = new Player(played.map(([n, o]) => {
+ return toCents([a, 4], [n, o]);
+ }));
+ player.start();
+}
+
+function stop(e) {
+ console.debug('got stopEvent', e);
+ if (player) {
+ player.stop();
+ }
+}
+
function init() {
console.debug('init()', this);
@@ -13,40 +48,11 @@ function init() {
KeyPicker.register();
History.register();
- let player = undefined;
-
// todo: maybe just attach the listener to document?
document.querySelectorAll(Fretboard.name).forEach(f => {
- f.addEventListener(f.saveEvent, e => {
- document.querySelectorAll(History.name).forEach(h => {
- console.debug('h is', h, 'e is', e.detail);
- h.add(e.detail);
- });
- });
-
- f.addEventListener(f.playEvent, e => {
- console.debug('got playEvent', e, e.detail.notes);
- const a = Note.fromString('A');
- if (player) {
- player.stop();
- }
- const played = e.detail.notes.map((n, i) => {
- if (n !== 'x') {
- return [Note.fromString(n), e.detail.octaves[i]]
- }
- }).filter(n => n);
- player = new Player(played.map(([n, o]) => {
- return toCents([a, 4], [n, o]);
- }));
- player.start();
- });
-
- f.addEventListener(f.stopEvent, e => {
- console.debug('got stopEvent', e);
- if (player) {
- player.stop();
- }
- });
+ f.addEventListener(f.saveEvent, save);
+ f.addEventListener(f.playEvent, play);
+ f.addEventListener(f.stopEvent, stop);
});
}
document.addEventListener('DOMContentLoaded', init);
diff --git a/scale.mjs b/scale.mjs
index fbd3617..78c8433 100644
--- a/scale.mjs
+++ b/scale.mjs
@@ -1,4 +1,4 @@
-function notesBetween(a, b) {
+export function naturalNotesBetween(a, b) {
const [tonicA, tonicB] = [a[0], b[0]]
const abs = tonicB.charCodeAt() - tonicA.charCodeAt();
if (abs < 0) {
@@ -8,7 +8,20 @@ function notesBetween(a, b) {
}
}
+export function chromaticNotesBetween(a, b) {
+ console.log('chromaticNotesBetween', a, b);
+ const [aNote, bNote] = [a, b].map(x => Note.fromString(x));
+ console.log(' -- aNote', aNote);
+ console.log(' -- bNote', bNote);
+ const aIndex = Note.noteRange.findIndex(x => aNote.toString() == x.toString());
+ const bIndex = Note.noteRange.findIndex(x => bNote.toString() == x.toString());
+ console.log(' -- aNote', aIndex);
+ console.log(' -- bNote', bIndex);
+ return aIndex - bIndex;
+}
+
export function toCents([aNote, aChord], [bNote, bChord]) {
+ console.debug('toCents', [aNote, aChord], [bNote, bChord]);
console.debug('- a', [aNote, aChord]);
console.debug('- b', [bNote, bChord]);
const offset = (aNote.distanceTo(bNote)) * 100;
@@ -150,7 +163,7 @@ function scaleFromIntervals(tonic, intervals) {
const note = chromaticScale[scaleIndex + steps];
// don't display two base notes in a row by changing
// accidentals.
- const delta = notesBetween(lastBase, note);
+ const delta = naturalNotesBetween(lastBase, note);
if (delta === 0) {
const nextBase = chromaticScale[scaleIndex + steps + 1][0];
//console.log('0', 'prev', lastBase, 'next', nextBase, 'note', note)
diff --git a/string.mjs b/string.mjs
index d2f0461..03cf720 100644
--- a/string.mjs
+++ b/string.mjs
@@ -68,6 +68,7 @@ export default class extends HTMLElement {
console.debug('String#fretClicked', this, e);
console.debug(' -- note', e.target.note);
this.setAttribute('value', e.target.getAttribute('value'));
+ this.setAttribute('octave', e.target.getAttribute('octave'));
}
render() {
@@ -81,11 +82,13 @@ export default class extends HTMLElement {
this.zot();
const chromIndex = chromaticScale.indexOf(this.getAttribute('tonic'));
+ let octave = this.getAttribute('tonic-octave');
const tmpl = this.querySelector('template');
this.querySelectorAll('slot[name="open"]').forEach(s => {
const note = chromaticScale[chromIndex]
s.setAttribute('value', note);
+ s.setAttribute('octave', octave);
s.textContent = note;
s.parentNode.onclick = this.fretClicked.bind(this);
})
@@ -103,6 +106,10 @@ export default class extends HTMLElement {
} else {
s.checked = false;
}
+ if (value === 'C') {
+ octave++;
+ }
+ s.setAttribute('octave', octave);
s.parentNode.onclick = this.fretClicked.bind(this);
})
tmpl.parentNode.insertBefore(item, tmpl);
diff --git a/tests.mjs b/tests.mjs
index e7095f8..1dc4c09 100644
--- a/tests.mjs
+++ b/tests.mjs
@@ -5,12 +5,16 @@ customElements.define('x-string', class extends HTMLElement {
constructor() {
super();
console.debug('x-string#constructor', this);
- const template = this.querySelector('template').content;
- const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(template.cloneNode(true));
- console.debug('attached', template, 'to shadow root', shadowRoot);
this.muted.onclick = this.muteClicked.bind(this);
this.tonic.onclick = this.fretClicked.bind(this);
+ this.attachShadow({mode: 'open'});
+ }
+
+ connectedCallback() {
+ const template = this.querySelector('template').content;
+ this.shadowRoot.appendChild(template.cloneNode(true));
+ console.debug('attached', template, 'to shadow root', shadowRoot);
}
get tonic() {