Skip to content

Commit

Permalink
refactor keyboard rows as 4 objects
Browse files Browse the repository at this point in the history
  • Loading branch information
TeemuKoivisto committed Mar 27, 2024
1 parent 7fb1ca6 commit 9ddac9a
Show file tree
Hide file tree
Showing 11 changed files with 1,454 additions and 1,372 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
export let rowIndex: number, keyIndex: number
$: value = $rows[rowIndex][keyIndex]
$: value = $rows[rowIndex].keys[keyIndex]
$: octave = value.note
? getOctave({ midi: value.note.semitones + $midiRange[0], flats: 0, sharps: 0 })
: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
</button>
{/if}
</li>
{#each row as _, idx}
{#each row.keys as _, idx}
<VirtualKey rowIndex={ridx} keyIndex={idx} />
{/each}
{/each}
Expand Down
10 changes: 5 additions & 5 deletions packages/client/src/stores/keyboard/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ export const keyMap = derived(
keyboard,
kbd =>
new Map<string, KeyboardKey>([
...kbd.rows[0].map(c => [c.code, c] as [string, KeyboardKey]),
...kbd.rows[1].map(c => [c.code, c] as [string, KeyboardKey]),
...kbd.rows[2].map(c => [c.code, c] as [string, KeyboardKey]),
...kbd.rows[3].map(c => [c.code, c] as [string, KeyboardKey])
...kbd.rows[0].keys.map(c => [c.code, c] as [string, KeyboardKey]),
...kbd.rows[1].keys.map(c => [c.code, c] as [string, KeyboardKey]),
...kbd.rows[2].keys.map(c => [c.code, c] as [string, KeyboardKey]),
...kbd.rows[3].keys.map(c => [c.code, c] as [string, KeyboardKey])
])
)
// @TODO duplicate keys in keyMap???
Expand Down Expand Up @@ -168,7 +168,7 @@ export const keyboardActions = {
}
const newRows = get(rows)
if (next) {
newRows[cpt.rowIndex][index] = next.key
newRows[cpt.rowIndex].keys[index] = next.key
rows.set(newRows)
capturingHotkeys.update(v =>
v
Expand Down
3 changes: 2 additions & 1 deletion packages/keyboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"scripts": {
"build": "vite build",
"watch": "rimraf dist && vite build --watch",
"test": "vitest run"
"test": "vitest run",
"test:u": "vitest run --update"
},
"devDependencies": {
"vitest": "^1.0.4"
Expand Down
72 changes: 28 additions & 44 deletions packages/keyboard/src/Keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,31 @@ import { getNote, setNotes } from './setNotes'
import { parseLayout } from './importLayout'

import type { ScaleNote } from '@/chords-and-scales'
import { KeyboardKey, KeyboardOptions, LayoutImport, Rows } from './types'
import { KeyboardKey, KeyboardOptions, LayoutImport, Row, Rows } from './types'
import { rowsFromImport } from 'rowsFromImport'

interface SetCustomRow {
row: KeyboardKey[]
row: Row
rowIndex: number
startKeyIndex: number
nextKeyIdx: number
nextNoteOffset: number
whiteKeys: boolean
}

const EMPTY_ROWS: Rows = [
{ keyType: undefined, startNoteOffset: 0, availableNotes: 0, keys: [] },
{ keyType: undefined, startNoteOffset: 0, availableNotes: 0, keys: [] },
{ keyType: undefined, startNoteOffset: 0, availableNotes: 0, keys: [] },
{ keyType: undefined, startNoteOffset: 0, availableNotes: 0, keys: [] }
]

export class Keyboard {
opts: Required<KeyboardOptions>
rows: Rows = [[], [], [], []]
rows: Rows = EMPTY_ROWS
setCustomRow: SetCustomRow = {
row: [],
row: EMPTY_ROWS[0],
rowIndex: 0,
startKeyIndex: 0,
nextKeyIdx: -1,
nextNoteOffset: 0,
whiteKeys: false
nextNoteOffset: 0
}

constructor(opts?: KeyboardOptions) {
Expand All @@ -47,7 +50,7 @@ export class Keyboard {
}

loadRowsFromImport(imported: LayoutImport) {
this.rows = rowsFromImport(imported)
this.rows = rowsFromImport(imported, this.opts.hotkeydRows)
return this
}

Expand All @@ -58,52 +61,37 @@ export class Keyboard {

setNotes(notes: ScaleNote[]) {
if (this.opts.hotkeydRows === 'middle-row') {
const { firstIndex } = setNotes(this.rows[2], notes, true)
// In ISO keyboard, the Q key is to the left of A -> start black keys from W instead
setNotes(this.rows[1], notes, false, 1 + firstIndex)
const { firstIndex } = setNotes(this.rows[2], notes)
setNotes(this.rows[1], notes, 1 + firstIndex)
} else {
const { firstIndex: bottomFirstWhite, lastIndex } = setNotes(this.rows[3], notes, true)
// In ISO, the middle row is to the right of bottom row -> no offset needed EXCEPT
// of course the shift from the first white key (incase it's empty)
setNotes(this.rows[2], notes, false, bottomFirstWhite)
const { firstIndex: topFirstWhite } = setNotes(this.rows[1], notes, true, 0, lastIndex + 1)
// For the top row, there's 2 keys (§ and 1) that are to the left of Q -> offset by 2
setNotes(this.rows[0], notes, false, 1 + topFirstWhite, lastIndex + 1)
const { firstIndex: bottomFirstWhite, lastIndex } = setNotes(this.rows[3], notes)
setNotes(this.rows[2], notes, bottomFirstWhite)
const { firstIndex: topFirstWhite } = setNotes(this.rows[1], notes, 0, lastIndex + 1)
setNotes(this.rows[0], notes, 1 + topFirstWhite, lastIndex + 1)
}
}

startSetCustomRow(rowIndex: number) {
let first = -1
let count = 0
const row = this.rows[rowIndex].map(r => ({ ...r }))
for (let i = 0; i < row.length; i += 1) {
const isSpecial = row[i].key.charAt(0) === '{' && row[i].key !== '{empty}'
if (!isSpecial && first === -1) {
first = i
}
count += isSpecial ? 0 : 1
}
if (first === -1) {
console.error(row)
throw Error('No valid keyboard key found!')
const row = {
...this.rows[rowIndex],
keys: this.rows[rowIndex].keys.map(r => ({ ...r }))
}
const { hotkeydRows } = this.opts
const whiteKeys = hotkeydRows === 'two-rows' ? rowIndex === 1 || rowIndex === 3 : rowIndex === 2
const { availableNotes: count, startNoteOffset: first } = row
this.setCustomRow = {
row,
rowIndex,
startKeyIndex: first,
nextKeyIdx: first,
nextNoteOffset: 0,
whiteKeys
nextNoteOffset: 0
}
console.log('this.setCustomRow', this.setCustomRow)
return { first, count }
}

setNextCustomNote(key: string, code: string, notes: ScaleNote[]) {
let rowKey
const { startKeyIndex, nextKeyIdx, nextNoteOffset, whiteKeys } = this.setCustomRow
const note = getNote(nextKeyIdx - nextNoteOffset - startKeyIndex, notes, whiteKeys)
const { nextKeyIdx, nextNoteOffset, row } = this.setCustomRow
const index = nextKeyIdx - nextNoteOffset - row.startNoteOffset
const note = getNote(row, notes, index)
if (note) {
rowKey = { key, code, note }
} else {
Expand All @@ -124,8 +112,4 @@ export class Keyboard {
key: { code: 'EMPTY', key: '{empty}' }
}
}

static createWithRows(opts: KeyboardOptions, rows: Rows) {
return new Keyboard(opts).setRows(rows)
}
}
Loading

0 comments on commit 9ddac9a

Please sign in to comment.