|
- import * as React from 'react'
- import { messages } from '../utils/midi'
-
- export type DeviceChannelActive = [
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean,
- boolean
- ]
-
- interface MidiMessageEvent extends Event {
- data: [number, number, number]
- }
-
- interface UseMidiReturn {
- midiAccess?: MIDIAccess
- lastStateChangeTimestamp?: number
- }
- interface ChannelData {
- channel: number
- key: number
- velocity: number
- }
-
- export const useMidi = (): UseMidiReturn => {
- const [lastStateChangeTimestamp, setLastStateChangeTimestamp] = React.useState<number>()
- const [midiAccess, setMidiAccess] = React.useState<MIDIAccess>()
-
- React.useEffect(() => {
- const stateChangeListener = (e: Event): void => {
- setLastStateChangeTimestamp(e.timeStamp)
- }
-
- window.navigator.requestMIDIAccess().then((midiAccess) => {
- setMidiAccess(midiAccess)
- setLastStateChangeTimestamp(Date.now())
- midiAccess.addEventListener('statechange', stateChangeListener)
- })
-
- return (): void => {
- midiAccess?.removeEventListener('statechange', stateChangeListener)
- }
- }, [])
-
- return {
- midiAccess,
- lastStateChangeTimestamp
- }
- }
-
- interface UseMidiActivityReturn {
- isChannelActive: DeviceChannelActive
- unaCorda: number
- sostenuto: number
- sustain: number
- keyChannels: ChannelData[]
- }
-
- export const useMidiActivity = (currentDevice?: MIDIInput): UseMidiActivityReturn => {
- const [isChannelActive, setIsChannelActive] = React.useState<DeviceChannelActive>([
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false
- ])
- const currentDeviceActiveTimeoutRef = React.useRef<number>()
- const [unaCorda, setUnaCorda] = React.useState(0)
- const [sostenuto, setSostenuto] = React.useState(0)
- const [sustain, setSustain] = React.useState(0)
-
- const [keyChannels, setKeyChannels] = React.useState<ChannelData[]>([])
-
- React.useEffect(() => {
- if (typeof currentDevice === 'undefined') {
- return
- }
-
- const addActivity = (channel: number): void => {
- setIsChannelActive(
- (oldCurrentDeviceActive) =>
- oldCurrentDeviceActive.map((state, i) =>
- i === channel ? true : state
- ) as DeviceChannelActive
- )
- window.clearTimeout(currentDeviceActiveTimeoutRef.current)
- currentDeviceActiveTimeoutRef.current = window.setTimeout(() => {
- setIsChannelActive(
- (oldCurrentDeviceActive) =>
- oldCurrentDeviceActive.map((state, i) =>
- i === channel ? false : state
- ) as DeviceChannelActive
- )
- }, 100)
- }
-
- const listener = (e: Event): void => {
- if (e.type !== 'midimessage') {
- return
- }
-
- const midiEvent = e as MidiMessageEvent
- const [messageType, param1, param2] = midiEvent.data
- const channel = messageType & messages.CHANNEL_BITMASK
- addActivity(channel)
-
- if (channel === messages.CHANNEL_INDEX_PERCUSSION) {
- return
- }
-
- switch (messageType & messages.TYPE_BITMASK) {
- case messages.types.CONTINUOUS_CONTROL: {
- const controlNumber = param1
- const value = param2
-
- switch (controlNumber) {
- case messages.params.continuousControl.sustain:
- setSustain(value)
- return
- case messages.params.continuousControl.sostenuto:
- setSostenuto(value)
- return
- case messages.params.continuousControl.unaCorda:
- setUnaCorda(value)
- return
- default:
- break
- }
- return
- }
- case messages.types.NOTE_ON: {
- const keyNumber = param1
- const velocity = param2
-
- setKeyChannels((oldKeyChannels) => {
- if (velocity > 0) {
- return [
- ...oldKeyChannels,
- {
- channel,
- key: keyNumber,
- velocity
- }
- ]
- }
-
- return oldKeyChannels.filter((c) => !(c.channel === channel && c.key === keyNumber))
- })
- return
- }
- case messages.types.NOTE_OFF: {
- const keyNumber = param1
-
- setKeyChannels((oldKeyChannels) => {
- return oldKeyChannels.filter((c) => !(c.channel === channel && c.key === keyNumber))
- })
- return
- }
- default:
- break
- }
- }
- currentDevice.addEventListener('midimessage', listener)
- return () => {
- currentDevice.removeEventListener('midimessage', listener)
- }
- }, [currentDevice])
-
- return {
- isChannelActive,
- unaCorda,
- sostenuto,
- sustain,
- keyChannels
- }
- }
|