Implement MIDI permission detection and user prompting
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { Device } from '$lib/image/device'
|
||||
import { getContext, setContext, untrack } from 'svelte'
|
||||
import { getContext, setContext } from 'svelte'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import type { MidiContext } from './midi.svelte'
|
||||
import { effect } from '$lib/utils.svelte'
|
||||
|
||||
function isInkclip(port: MIDIPort): boolean {
|
||||
return !!(
|
||||
@@ -10,54 +12,59 @@ function isInkclip(port: MIDIPort): boolean {
|
||||
}
|
||||
|
||||
export class DeviceContext {
|
||||
private midi: MidiContext
|
||||
private input: MIDIInput | null = $state(null)
|
||||
private output: MIDIOutput | null = $state(null)
|
||||
device: Device | null = $state(null)
|
||||
|
||||
constructor() {
|
||||
$effect(() => {
|
||||
if (untrack(() => this.device == null)) {
|
||||
if (this.input == null || this.output == null) return
|
||||
this.device = new Device(this.input, this.output)
|
||||
toast.info('Device connected')
|
||||
} else {
|
||||
if (this.input != null && this.output != null) return
|
||||
this.device = null
|
||||
toast.info('Device disconnected')
|
||||
}
|
||||
})
|
||||
constructor(midi: MidiContext) {
|
||||
this.midi = midi
|
||||
effect(
|
||||
() => [this.midi.midi],
|
||||
() => this.initialize(),
|
||||
)
|
||||
effect(
|
||||
() => [this.input, this.output],
|
||||
() => this.update(),
|
||||
)
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
try {
|
||||
const midi = await navigator.requestMIDIAccess({ sysex: true })
|
||||
const inputs = Array.from(midi.inputs.values())
|
||||
const outputs = Array.from(midi.outputs.values())
|
||||
console.log(
|
||||
'MIDI inputs:',
|
||||
Array.from(midi.inputs.values()),
|
||||
'outputs:',
|
||||
Array.from(midi.outputs.values()),
|
||||
)
|
||||
|
||||
this.input = inputs.find(isInkclip) ?? null
|
||||
this.output = outputs.find(isInkclip) ?? null
|
||||
|
||||
midi.addEventListener('statechange', e => {
|
||||
console.log(`MIDI port statechange:`, e.port)
|
||||
if (!e.port || !isInkclip(e.port)) return
|
||||
if (e.port?.state === 'connected') {
|
||||
if (e.port.type === 'input' && !this.input) this.input = e.port as MIDIInput
|
||||
else if (e.port.type === 'output' && !this.output) this.output = e.port as MIDIOutput
|
||||
} else {
|
||||
if (e.port.type === 'input' && e.port === this.input) this.input = null
|
||||
else if (e.port.type === 'output' && e.port === this.output) this.output = null
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
toast.error(`Failed to acquire MIDI access: ${e}`)
|
||||
private update() {
|
||||
if (this.device == null) {
|
||||
if (this.input == null || this.output == null) return
|
||||
this.device = new Device(this.input, this.output)
|
||||
toast.info('Device connected')
|
||||
} else {
|
||||
if (this.input != null && this.output != null) return
|
||||
this.device = null
|
||||
toast.info('Device disconnected')
|
||||
}
|
||||
}
|
||||
|
||||
private async initialize() {
|
||||
if (!this.midi.midi) return
|
||||
const midi = this.midi.midi
|
||||
|
||||
const inputs = Array.from(midi.inputs.values())
|
||||
const outputs = Array.from(midi.outputs.values())
|
||||
console.log('MIDI inputs:', Array.from(midi.inputs.values()))
|
||||
console.log('MIDI outputs:', Array.from(midi.outputs.values()))
|
||||
|
||||
this.input = inputs.find(isInkclip) ?? null
|
||||
this.output = outputs.find(isInkclip) ?? null
|
||||
|
||||
midi.addEventListener('statechange', e => {
|
||||
console.log(`MIDI port statechange:`, e.port)
|
||||
if (!e.port || !isInkclip(e.port)) return
|
||||
if (e.port?.state === 'connected') {
|
||||
if (e.port.type === 'input' && !this.input) this.input = e.port as MIDIInput
|
||||
else if (e.port.type === 'output' && !this.output) this.output = e.port as MIDIOutput
|
||||
} else {
|
||||
if (e.port.type === 'input' && e.port === this.input) this.input = null
|
||||
else if (e.port.type === 'output' && e.port === this.output) this.output = null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const DeviceContextToken = Symbol('device')
|
||||
@@ -66,7 +73,7 @@ export function getDeviceContext(): DeviceContext {
|
||||
return getContext(DeviceContextToken)
|
||||
}
|
||||
|
||||
export function createDeviceContext(): DeviceContext {
|
||||
let ctx = new DeviceContext()
|
||||
export function createDeviceContext(midi: MidiContext): DeviceContext {
|
||||
let ctx = new DeviceContext(midi)
|
||||
return setContext(DeviceContextToken, ctx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user