From 3a5917e8efadd6f9825455faaa6fd2c6292e4d95 Mon Sep 17 00:00:00 2001
From: Lumi Pakkanen
Date: Tue, 2 Jan 2024 21:47:43 +0200
Subject: [PATCH] Use pinia to manage app state
Fix a bug where deactivated channel messages were passed to MIDI out from MIDI in.
ref #367
---
src/App.vue | 522 +++++-------------
src/components/ScaleBuilder.vue | 228 ++++----
.../modals/generation/HistoricalScale.vue | 6 +-
src/stores/midi.ts | 22 +
src/stores/state.ts | 236 ++++++++
src/views/AnalysisView.vue | 35 +-
src/views/LatticeView.vue | 17 +-
src/views/MidiView.vue | 83 ++-
src/views/NotFoundView.vue | 53 +-
src/views/PreferencesView.vue | 78 +--
src/views/ScaleView.vue | 45 +-
src/views/SynthView.vue | 99 +---
src/views/VirtualKeyboardView.vue | 41 +-
src/views/VirtualQwerty.vue | 52 +-
14 files changed, 640 insertions(+), 877 deletions(-)
create mode 100644 src/stores/midi.ts
create mode 100644 src/stores/state.ts
diff --git a/src/App.vue b/src/App.vue
index 49371c5c..c3ce5d5c 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,141 +1,22 @@
@@ -747,7 +536,7 @@ watch(degreeDownCode, (newValue) => window.localStorage.setItem('degreeDownCode'
Analysis
Lattice
Virtual Keyboard
-
+
Virtual QWERTY
Synth
@@ -756,7 +545,7 @@ watch(degreeDownCode, (newValue) => window.localStorage.setItem('degreeDownCode'
-
+
-
Key
@@ -770,70 +559,9 @@ watch(degreeDownCode, (newValue) => window.localStorage.setItem('degreeDownCode'
diff --git a/src/components/ScaleBuilder.vue b/src/components/ScaleBuilder.vue
index b520b8d5..ccde2f6c 100644
--- a/src/components/ScaleBuilder.vue
+++ b/src/components/ScaleBuilder.vue
@@ -11,6 +11,7 @@ import { presets, presetsByGroup } from '@/presets'
import { importFile, type ImporterKey } from '@/importers'
import { mtof } from 'xen-dev-utils'
import type { Scale } from 'scale-workshop-core'
+import { useStateStore } from '@/stores/state'
// Export
const KorgExportModal = defineAsyncComponent(
@@ -93,64 +94,40 @@ const TemperModal = defineAsyncComponent(
() => import('@/components/modals/modification/TemperScale.vue')
)
-const props = defineProps<{
- scaleName: string
- scaleLines: string[]
- baseMidiNote: number
- keyColors: string[]
-
- scale: Scale
- frequencies: number[]
-
- centsFractionDigits: number
- decimalFractionDigits: number
- newline: string
-
- midiOctaveOffset: number
-}>()
-
-const emit = defineEmits([
- 'update:scaleName',
- 'update:scaleLines',
- 'update:scale',
- 'update:baseFrequency',
- 'update:baseMidiNote',
- 'update:keyColors'
-])
+const state = useStateStore()
const joinedLines = computed({
get() {
- return props.scaleLines.join('\n')
+ return state.scaleLines.join('\n')
},
- set: debounce((newValue: string) => emit('update:scaleLines', newValue.split('\n')))
+ set: debounce((newValue: string) => {
+ state.scaleLines = newValue.split('\n')
+ })
})
const joinedKeyColors = computed({
get() {
- return props.keyColors.join(' ')
+ return state.keyColors.join(' ')
},
- set: debounce((newValue: string) => emit('update:keyColors', newValue.split(' ')))
-})
-
-const scaleName = computed({
- get: () => props.scaleName,
- set: (newValue: string) => emit('update:scaleName', newValue)
+ set: debounce((newValue: string) => {
+ state.keyColors = newValue.split(' ')
+ })
})
const baseFrequency = computed({
- get: () => props.scale.baseFrequency,
+ get: () => state.scale.baseFrequency,
set: debounce((newValue: number) => {
if (typeof newValue === 'number' && !isNaN(newValue) && isFinite(newValue)) {
- emit('update:baseFrequency', newValue)
+ state.scale.baseFrequency = newValue
}
})
})
const baseMidiNote = computed({
- get: () => props.baseMidiNote,
+ get: () => state.baseMidiNote,
set: debounce((newValue: number) => {
if (typeof newValue === 'number' && !isNaN(newValue)) {
- emit('update:baseMidiNote', newValue)
+ state.baseMidiNote = newValue
}
})
})
@@ -158,7 +135,7 @@ const baseMidiNote = computed({
const midiNoteNumber = ref(null)
function autoFrequency() {
- let baseMidiNote = props.baseMidiNote
+ let baseMidiNote = state.baseMidiNote
if (midiNoteNumber.value !== null) {
// Circumvent debouncing for this simple click
baseMidiNote = parseInt(midiNoteNumber.value.value)
@@ -167,20 +144,20 @@ function autoFrequency() {
}
}
- emit('update:baseFrequency', mtof(baseMidiNote))
+ state.scale.baseFrequency = mtof(baseMidiNote)
}
function doExport(exporter: ExporterKey) {
const params = {
- newline: props.newline,
- name: props.scaleName,
+ newline: state.newline,
+ name: state.scaleName,
scaleUrl: window.location.href,
- scale: props.scale,
- filename: sanitizeFilename(props.scaleName),
- baseMidiNote: props.baseMidiNote,
- midiOctaveOffset: props.midiOctaveOffset,
- description: props.scaleName,
- lines: props.scaleLines,
+ scale: state.scale,
+ filename: sanitizeFilename(state.scaleName),
+ baseMidiNote: state.baseMidiNote,
+ midiOctaveOffset: state.midiOctaveOffset,
+ description: state.scaleName,
+ lines: state.scaleLines,
appTitle: APP_TITLE,
date: new Date()
}
@@ -199,11 +176,11 @@ const presetSelect = ref(null)
const showPresetModal = ref(false)
function selectPreset() {
const preset = presets[presetSelect.value!.value]
- emit('update:scaleName', preset.name)
- emit('update:scaleLines', preset.lines)
- emit('update:baseFrequency', preset.baseFrequency)
- emit('update:baseMidiNote', preset.baseMidiNote)
- emit('update:keyColors', preset.keyColors)
+ state.scaleName = preset.name
+ state.scaleLines = preset.lines
+ state.scale.baseFrequency = preset.baseFrequency
+ state.baseMidiNote = preset.baseMidiNote
+ state.keyColors = preset.keyColors
}
const showEqualTemperamentModal = ref(false)
@@ -241,15 +218,15 @@ const exportTextClipboard = ref("Copy this scale's unique URL to clipboard")
async function doImport(importerKey: ImporterKey, event: Event) {
const result = await importFile(importerKey, event)
- emit('update:scaleLines', result.scale.toStrings())
+ state.scaleLines = result.scale.toStrings()
if (importerKey != 'scalascl') {
- emit('update:baseFrequency', result.scale.baseFrequency)
+ state.scale.baseFrequency = result.scale.baseFrequency
}
if (result.name !== undefined) {
- emit('update:scaleName', result.name)
+ state.scaleName = result.name
}
if (result.baseMidiNote !== undefined) {
- emit('update:baseMidiNote', result.baseMidiNote)
+ state.baseMidiNote = result.baseMidiNote
}
}
@@ -269,17 +246,17 @@ function clearScale() {
}
function sortAscending() {
- emit('update:scale', props.scale.sorted())
+ state.scale = state.scale.sorted()
scaleDataArea.value!.focus()
}
function clickReduce() {
- emit('update:scale', props.scale.reduce())
+ state.scale = state.scale.reduce()
scaleDataArea.value!.focus()
}
function clickInvert() {
- emit('update:scale', props.scale.invert())
+ state.scale = state.scale.invert()
scaleDataArea.value!.focus()
}
@@ -294,7 +271,7 @@ function clickShareUrl() {
}
function updateScaleAndHideModals(scale: Scale) {
- emit('update:scale', scale)
+ state.scale = scale
showRankTwoModal.value = false
showEqualTemperamentModal.value = false
showHarmonicSeriesModal.value = false
@@ -332,7 +309,7 @@ function confirmPreset() {
id="scale-name"
rows="1"
placeholder="Untitled scale"
- v-model="scaleName"
+ v-model="state.scaleName"
>
@@ -395,7 +372,7 @@ function confirmPreset() {
-
+
Tuning
@@ -424,7 +401,7 @@ function confirmPreset() {
step="1"
v-model="baseMidiNote"
/>
- {{ midiNoteNumberToName(baseMidiNote, props.midiOctaveOffset) }}
+ {{ midiNoteNumberToName(baseMidiNote, state.midiOctaveOffset) }}
@@ -451,16 +428,16 @@ function confirmPreset() {
-
+
Export current settings
@@ -559,134 +536,135 @@ function confirmPreset() {
v-if="showKorgExportModal"
@confirm="showKorgExportModal = false"
@cancel="showKorgExportModal = false"
- :newline="props.newline"
- :scaleName="scaleName"
+ :newline="state.newline"
+ :scaleName="state.scaleName"
:baseMidiNote="baseMidiNote"
- :midiOctaveOffset="midiOctaveOffset"
- :scale="scale"
+ :midiOctaveOffset="state.midiOctaveOffset"
+ :scale="state.scale"
/>
@@ -714,7 +692,7 @@ function confirmPreset() {
v-if="showRotateModal"
@update:scale="updateScaleAndHideModals"
@cancel="showRotateModal = false"
- :scale="scale"
+ :scale="state.scale"
/>
diff --git a/src/components/modals/generation/HistoricalScale.vue b/src/components/modals/generation/HistoricalScale.vue
index 05bc5fbe..73aded97 100644
--- a/src/components/modals/generation/HistoricalScale.vue
+++ b/src/components/modals/generation/HistoricalScale.vue
@@ -10,6 +10,7 @@ import { spineLabel as spineLabel_, type AccidentalStyle } from '@/utils'
const props = defineProps<{
centsFractionDigits: number
+ accidentalStyle: AccidentalStyle
}>()
const emit = defineEmits([
@@ -53,11 +54,8 @@ const SYNTONIC = new Interval(
'ratio'
)
-const ACCIDENTAL_STYLE =
- (localStorage.getItem('accidentalPreference') as AccidentalStyle) ?? 'double'
-
function spineLabel(up: number) {
- return spineLabel_(up, ACCIDENTAL_STYLE)
+ return spineLabel_(up, props.accidentalStyle)
}
const method = ref<'simple' | 'target' | 'well temperament'>('simple')
diff --git a/src/stores/midi.ts b/src/stores/midi.ts
new file mode 100644
index 00000000..10dae0e4
--- /dev/null
+++ b/src/stores/midi.ts
@@ -0,0 +1,22 @@
+import { ref } from 'vue'
+import { defineStore } from 'pinia'
+import type { Input, Output } from 'webmidi'
+
+export const useMidiStore = defineStore('midi', () => {
+ const input = ref
(null)
+ const output = ref
Accidentals
@@ -112,7 +64,7 @@ watch(accidentalPreference, (newValue) => localStorage.setItem('accidentalPrefer
type="radio"
id="accidentals-double"
value="double"
- v-model="accidentalPreference"
+ v-model="state.accidentalPreference"
/>
@@ -121,7 +73,7 @@ watch(accidentalPreference, (newValue) => localStorage.setItem('accidentalPrefer
type="radio"
id="accidentals-single"
value="single"
- v-model="accidentalPreference"
+ v-model="state.accidentalPreference"
/>
@@ -130,7 +82,7 @@ watch(accidentalPreference, (newValue) => localStorage.setItem('accidentalPrefer
type="radio"
id="accidentals-ascii"
value="ASCII"
- v-model="accidentalPreference"
+ v-model="state.accidentalPreference"
/>
@@ -142,7 +94,13 @@ watch(accidentalPreference, (newValue) => localStorage.setItem('accidentalPrefer
diff --git a/src/views/ScaleView.vue b/src/views/ScaleView.vue
index 31020db5..b967bc53 100644
--- a/src/views/ScaleView.vue
+++ b/src/views/ScaleView.vue
@@ -1,52 +1,9 @@
-
+
diff --git a/src/views/SynthView.vue b/src/views/SynthView.vue
index ed8ad656..1b77591f 100644
--- a/src/views/SynthView.vue
+++ b/src/views/SynthView.vue
@@ -4,39 +4,11 @@ import TimeDomainVisualizer from '@/components/TimeDomainVisualizer.vue'
import Modal from '@/components/ModalDialog.vue'
import { WAVEFORMS } from '@/synth'
import { useAudioStore } from '@/stores/audio'
+import { useStateStore } from '@/stores/state'
-const props = defineProps<{
- keyboardMode: 'isomorphic' | 'piano'
- pianoMode: 'Asdf' | 'QweZxc0' | 'QweZxc1'
- isomorphicHorizontal: number
- isomorphicVertical: number
- equaveShift: number
- degreeShift: number
- colorScheme: 'light' | 'dark'
- deactivationCode: string
- equaveUpCode: string
- equaveDownCode: string
- degreeUpCode: string
- degreeDownCode: string
-}>()
+const emit = defineEmits(['panic'])
-const emit = defineEmits([
- 'update:keyboardMode',
- 'update:pianoMode',
- 'update:isomorphicHorizontal',
- 'update:isomorphicVertical',
- 'update:equaveShift',
- 'update:degreeShift',
- 'update:deactivationCode',
- 'update:equaveUpCode',
- 'update:equaveDownCode',
- 'update:degreeUpCode',
- 'update:degreeDownCode',
- 'mapAsdf',
- 'mapZxcv0',
- 'mapZxcv1',
- 'panic'
-])
+const state = useStateStore()
const audio = useAudioStore()
@@ -71,30 +43,6 @@ const mainVolume = computed({
}
}
})
-const keyboardMode = computed({
- get: () => props.keyboardMode,
- set: (newValue: 'isomorphic' | 'piano') => emit('update:keyboardMode', newValue)
-})
-const pianoMode = computed({
- get: () => props.pianoMode,
- set: (newValue: 'Asdf' | 'QweZxc0' | 'QweZxc1') => emit('update:pianoMode', newValue)
-})
-const isomorphicVertical = computed({
- get: () => props.isomorphicVertical,
- set: (newValue: number) => emit('update:isomorphicVertical', newValue)
-})
-const isomorphicHorizontal = computed({
- get: () => props.isomorphicHorizontal,
- set: (newValue: number) => emit('update:isomorphicHorizontal', newValue)
-})
-const equaveShift = computed({
- get: () => props.equaveShift,
- set: (newValue: number) => emit('update:equaveShift', newValue)
-})
-const degreeShift = computed({
- get: () => props.degreeShift,
- set: (newValue: number) => emit('update:degreeShift', newValue)
-})
const attackTime = computed({
get: () => audio.attackTime,
@@ -194,7 +142,7 @@ const pingPongGain = computed({
const strokeStyle = computed(() => {
// Add dependency.
- props.colorScheme
+ state.colorScheme
// Fetch from document.
return getComputedStyle(document.documentElement).getPropertyValue('--color-text').trim()
})
@@ -236,7 +184,7 @@ function presetLong() {
function assignCode(event: KeyboardEvent) {
if (remappedKey.value.length && event.code.length) {
- emit(('update:' + remappedKey.value) as any, event.code)
+ ;(state as any)[remappedKey.value] = event.code
remappedKey.value = ''
}
}
@@ -412,26 +360,31 @@ onUnmounted(() => {
Keyboard degree shift
@@ -454,10 +407,10 @@ onUnmounted(() => {
-
and +
).
-
+
-
+
Isomorphic key mapping
Distance between adjacent keys on the horizontal/vertical axes, in scale degrees.
@@ -469,11 +422,11 @@ onUnmounted(() => {
>
-
+
-
+
@@ -481,23 +434,23 @@ onUnmounted(() => {
Shift
sustain currently held keys after release
- {{ deactivationCode }}
+ {{ state.deactivationCode }}
release sustain, stop all playing notes (click to reassign)
- {{ equaveDownCode }}
+ {{ state.equaveDownCode }}
equave shift down (click to reassign)
- {{ equaveUpCode }}
+ {{ state.equaveUpCode }}
equave shift up (click to reassign)
- {{ degreeDownCode }}
+ {{ state.degreeDownCode }}
degree shift down (click to reassign)
- {{ degreeUpCode }}
+ {{ state.degreeUpCode }}
degree shift up (click to reassign)
diff --git a/src/views/VirtualKeyboardView.vue b/src/views/VirtualKeyboardView.vue
index 2b8c1f3e..c41271d2 100644
--- a/src/views/VirtualKeyboardView.vue
+++ b/src/views/VirtualKeyboardView.vue
@@ -1,44 +1,37 @@
diff --git a/src/views/VirtualQwerty.vue b/src/views/VirtualQwerty.vue
index b61ffc35..f7bbf745 100644
--- a/src/views/VirtualQwerty.vue
+++ b/src/views/VirtualQwerty.vue
@@ -1,30 +1,16 @@