effect-web-midi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/src/EMIDIAccess.js +68 -0
- package/dist/src/EMIDIAccess.js.map +1 -0
- package/dist/src/EMIDIInput.js +226 -0
- package/dist/src/EMIDIInput.js.map +1 -0
- package/dist/src/EMIDIOutput.js +225 -0
- package/dist/src/EMIDIOutput.js.map +1 -0
- package/dist/src/EMIDIPort.js +213 -0
- package/dist/src/EMIDIPort.js.map +1 -0
- package/dist/src/MIDIErrors.js +4 -0
- package/dist/src/MIDIErrors.js.map +1 -0
- package/dist/src/Parsing.js +2 -0
- package/dist/src/Parsing.js.map +1 -0
- package/dist/src/StreamMaker.js +1 -0
- package/dist/src/StreamMaker.js.map +1 -0
- package/dist/src/Util.js +4 -0
- package/dist/src/Util.js.map +1 -0
- package/dist/src/index.js +9 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/internal/EMIDIAccess.js +409 -0
- package/dist/src/internal/EMIDIAccess.js.map +1 -0
- package/dist/src/internal/EMIDIInput.js +69 -0
- package/dist/src/internal/EMIDIInput.js.map +1 -0
- package/dist/src/internal/EMIDIOutput.js +120 -0
- package/dist/src/internal/EMIDIOutput.js.map +1 -0
- package/dist/src/internal/EMIDIPort.js +125 -0
- package/dist/src/internal/EMIDIPort.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPort.js +27 -0
- package/dist/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPort.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdAndAccess.js +19 -0
- package/dist/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdInContext.js +16 -0
- package/dist/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdInContext.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/actOnPort.js +18 -0
- package/dist/src/internal/MIDIPortMethodCalls/actOnPort.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPort.js +27 -0
- package/dist/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPort.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdAndAccess.js +18 -0
- package/dist/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdInContext.js +16 -0
- package/dist/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdInContext.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/makeMIDIPortMethodCallerFactory.js +21 -0
- package/dist/src/internal/MIDIPortMethodCalls/makeMIDIPortMethodCallerFactory.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPort.js +30 -0
- package/dist/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPort.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdAndAccess.js +18 -0
- package/dist/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdInContext.js +16 -0
- package/dist/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdInContext.js.map +1 -0
- package/dist/src/internal/StreamMaker.js +101 -0
- package/dist/src/internal/StreamMaker.js.map +1 -0
- package/dist/src/internal/errors.js +161 -0
- package/dist/src/internal/errors.js.map +1 -0
- package/dist/src/internal/getPortByPortId/getPortByPortIdAndAccess.js +53 -0
- package/dist/src/internal/getPortByPortId/getPortByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/getPortByPortId/getPortByPortIdInContext.js +18 -0
- package/dist/src/internal/getPortByPortId/getPortByPortIdInContext.js.map +1 -0
- package/dist/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPort.js +52 -0
- package/dist/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPort.js.map +1 -0
- package/dist/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdAndAccess.js +13 -0
- package/dist/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdInContext.js +18 -0
- package/dist/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdInContext.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPort.js +66 -0
- package/dist/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPort.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdAndAccess.js +61 -0
- package/dist/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdInContext.js +61 -0
- package/dist/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdInContext.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPort.js +42 -0
- package/dist/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPort.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdAndAccess.js +42 -0
- package/dist/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdInContext.js +29 -0
- package/dist/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdInContext.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/getValueInRawPortFieldUnsafe.js +6 -0
- package/dist/src/internal/mutablePropertyTools/getValueInRawPortFieldUnsafe.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPort.js +46 -0
- package/dist/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPort.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdAndAccess.js +25 -0
- package/dist/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdAndAccess.js.map +1 -0
- package/dist/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdInContext.js +31 -0
- package/dist/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdInContext.js.map +1 -0
- package/dist/src/internal/parsing.js +117 -0
- package/dist/src/internal/parsing.js.map +1 -0
- package/dist/src/internal/util.js +59 -0
- package/dist/src/internal/util.js.map +1 -0
- package/dist-types/index.d.ts +2 -0
- package/dist-types/index.d.ts.map +1 -0
- package/dist-types/src/EMIDIAccess.d.ts +27 -0
- package/dist-types/src/EMIDIAccess.d.ts.map +1 -0
- package/dist-types/src/EMIDIInput.d.ts +26 -0
- package/dist-types/src/EMIDIInput.d.ts.map +1 -0
- package/dist-types/src/EMIDIOutput.d.ts +26 -0
- package/dist-types/src/EMIDIOutput.d.ts.map +1 -0
- package/dist-types/src/EMIDIPort.d.ts +26 -0
- package/dist-types/src/EMIDIPort.d.ts.map +1 -0
- package/dist-types/src/MIDIErrors.d.ts +2 -0
- package/dist-types/src/MIDIErrors.d.ts.map +1 -0
- package/dist-types/src/Parsing.d.ts +2 -0
- package/dist-types/src/Parsing.d.ts.map +1 -0
- package/dist-types/src/StreamMaker.d.ts +2 -0
- package/dist-types/src/StreamMaker.d.ts.map +1 -0
- package/dist-types/src/Util.d.ts +2 -0
- package/dist-types/src/Util.d.ts.map +1 -0
- package/dist-types/src/index.d.ts +9 -0
- package/dist-types/src/index.d.ts.map +1 -0
- package/dist-types/src/internal/EMIDIAccess.d.ts +339 -0
- package/dist-types/src/internal/EMIDIAccess.d.ts.map +1 -0
- package/dist-types/src/internal/EMIDIInput.d.ts +48 -0
- package/dist-types/src/internal/EMIDIInput.d.ts.map +1 -0
- package/dist-types/src/internal/EMIDIOutput.d.ts +93 -0
- package/dist-types/src/internal/EMIDIOutput.d.ts.map +1 -0
- package/dist-types/src/internal/EMIDIPort.d.ts +87 -0
- package/dist-types/src/internal/EMIDIPort.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPort.d.ts +16 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPort.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdAndAccess.d.ts +15 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdInContext.d.ts +13 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/actOnPort.d.ts +11 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/actOnPort.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPort.d.ts +23 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPort.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdAndAccess.d.ts +15 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdInContext.d.ts +13 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/makeMIDIPortMethodCallerFactory.d.ts +15 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/makeMIDIPortMethodCallerFactory.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPort.d.ts +21 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPort.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdAndAccess.d.ts +15 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdInContext.d.ts +13 -0
- package/dist-types/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/StreamMaker.d.ts +202 -0
- package/dist-types/src/internal/StreamMaker.d.ts.map +1 -0
- package/dist-types/src/internal/errors.d.ts +260 -0
- package/dist-types/src/internal/errors.d.ts.map +1 -0
- package/dist-types/src/internal/getPortByPortId/getPortByPortIdAndAccess.d.ts +27 -0
- package/dist-types/src/internal/getPortByPortId/getPortByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/getPortByPortId/getPortByPortIdInContext.d.ts +20 -0
- package/dist-types/src/internal/getPortByPortId/getPortByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPort.d.ts +71 -0
- package/dist-types/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPort.d.ts.map +1 -0
- package/dist-types/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdAndAccess.d.ts +4 -0
- package/dist-types/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdInContext.d.ts +20 -0
- package/dist-types/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPort.d.ts +62 -0
- package/dist-types/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPort.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdAndAccess.d.ts +16 -0
- package/dist-types/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdInContext.d.ts +16 -0
- package/dist-types/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPort.d.ts +36 -0
- package/dist-types/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPort.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdAndAccess.d.ts +22 -0
- package/dist-types/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdInContext.d.ts +17 -0
- package/dist-types/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/getValueInRawPortFieldUnsafe.d.ts +7 -0
- package/dist-types/src/internal/mutablePropertyTools/getValueInRawPortFieldUnsafe.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPort.d.ts +71 -0
- package/dist-types/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPort.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdAndAccess.d.ts +7 -0
- package/dist-types/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdAndAccess.d.ts.map +1 -0
- package/dist-types/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdInContext.d.ts +28 -0
- package/dist-types/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdInContext.d.ts.map +1 -0
- package/dist-types/src/internal/parsing.d.ts +72 -0
- package/dist-types/src/internal/parsing.d.ts.map +1 -0
- package/dist-types/src/internal/util.d.ts +33 -0
- package/dist-types/src/internal/util.d.ts.map +1 -0
- package/index.ts +1 -0
- package/package.json +83 -0
- package/src/EMIDIAccess.ts +271 -0
- package/src/EMIDIInput.ts +270 -0
- package/src/EMIDIOutput.ts +269 -0
- package/src/EMIDIPort.ts +267 -0
- package/src/MIDIErrors.ts +13 -0
- package/src/Parsing.ts +12 -0
- package/src/StreamMaker.ts +12 -0
- package/src/Util.ts +19 -0
- package/src/index.ts +8 -0
- package/src/internal/EMIDIAccess.ts +893 -0
- package/src/internal/EMIDIInput.ts +114 -0
- package/src/internal/EMIDIOutput.ts +228 -0
- package/src/internal/EMIDIPort.ts +233 -0
- package/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPort.ts +43 -0
- package/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdAndAccess.ts +39 -0
- package/src/internal/MIDIPortMethodCalls/acquireReleasePortConnection/acquireReleasePortConnectionByPortIdInContext.ts +27 -0
- package/src/internal/MIDIPortMethodCalls/actOnPort.ts +66 -0
- package/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPort.ts +38 -0
- package/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdAndAccess.ts +38 -0
- package/src/internal/MIDIPortMethodCalls/closePortConnection/closePortConnectionByPortIdInContext.ts +27 -0
- package/src/internal/MIDIPortMethodCalls/makeMIDIPortMethodCallerFactory.ts +73 -0
- package/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPort.ts +44 -0
- package/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdAndAccess.ts +38 -0
- package/src/internal/MIDIPortMethodCalls/openPortConnection/openPortConnectionByPortIdInContext.ts +27 -0
- package/src/internal/StreamMaker.ts +414 -0
- package/src/internal/errors.ts +220 -0
- package/src/internal/getPortByPortId/getPortByPortIdAndAccess.ts +116 -0
- package/src/internal/getPortByPortId/getPortByPortIdInContext.ts +26 -0
- package/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPort.ts +148 -0
- package/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdAndAccess.ts +14 -0
- package/src/internal/makePortStateChangesStream/makePortStateChangesStreamByPortIdInContext.ts +51 -0
- package/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPort.ts +127 -0
- package/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdAndAccess.ts +74 -0
- package/src/internal/mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPortIdInContext.ts +74 -0
- package/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPort.ts +63 -0
- package/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdAndAccess.ts +110 -0
- package/src/internal/mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPortIdInContext.ts +37 -0
- package/src/internal/mutablePropertyTools/getValueInRawPortFieldUnsafe.ts +12 -0
- package/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPort.ts +183 -0
- package/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdAndAccess.ts +29 -0
- package/src/internal/mutablePropertyTools/matchMutablePortProperty/matchMutablePortPropertyByPortIdInContext.ts +86 -0
- package/src/internal/parsing.ts +219 -0
- package/src/internal/util.ts +152 -0
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/useShorthandFunctionType: It's a nice way to
|
|
2
|
+
* preserve JSDoc comments attached to the function signature */
|
|
3
|
+
|
|
4
|
+
import * as EArray from 'effect/Array'
|
|
5
|
+
import * as Context from 'effect/Context'
|
|
6
|
+
import * as Effect from 'effect/Effect'
|
|
7
|
+
import * as Equal from 'effect/Equal'
|
|
8
|
+
import * as EFunction from 'effect/Function'
|
|
9
|
+
import * as Hash from 'effect/Hash'
|
|
10
|
+
import * as Inspectable from 'effect/Inspectable'
|
|
11
|
+
import * as Iterable from 'effect/Iterable'
|
|
12
|
+
import * as Layer from 'effect/Layer'
|
|
13
|
+
import * as Option from 'effect/Option'
|
|
14
|
+
import * as Order from 'effect/Order'
|
|
15
|
+
import * as Pipeable from 'effect/Pipeable'
|
|
16
|
+
import * as Record from 'effect/Record'
|
|
17
|
+
import * as Ref from 'effect/Ref'
|
|
18
|
+
import * as SortedMap from 'effect/SortedMap'
|
|
19
|
+
import type * as Types from 'effect/Types'
|
|
20
|
+
import * as Unify from 'effect/Unify'
|
|
21
|
+
import * as EMIDIInput from './EMIDIInput.ts'
|
|
22
|
+
import * as EMIDIOutput from './EMIDIOutput.ts'
|
|
23
|
+
import type * as EMIDIPort from './EMIDIPort.ts'
|
|
24
|
+
import * as Errors from './errors.ts'
|
|
25
|
+
import * as GetPort from './getPortByPortId/getPortByPortIdInContext.ts'
|
|
26
|
+
import { isOutputConnectionOpenByPort } from './mutablePropertyTools/doesMutablePortPropertyHaveSpecificValue/doesMutablePortPropertyHaveSpecificValueByPort.ts'
|
|
27
|
+
import { getOutputDeviceStateByPort } from './mutablePropertyTools/getMutablePortProperty/getMutablePortPropertyByPort.ts'
|
|
28
|
+
import * as StreamMaker from './StreamMaker.ts'
|
|
29
|
+
import * as Util from './util.ts'
|
|
30
|
+
|
|
31
|
+
// TODO: add stream of messages sent from this device to target midi device
|
|
32
|
+
|
|
33
|
+
// TODO: fat service APIs, where all the methods are attached to instance and
|
|
34
|
+
// where you don't have to constantly write the prefix
|
|
35
|
+
|
|
36
|
+
// TODO: implement scoping of midi access that will clean up all message queues
|
|
37
|
+
// and streams, and remove listeners
|
|
38
|
+
|
|
39
|
+
// TODO: implement scope inheritance
|
|
40
|
+
|
|
41
|
+
// TODO: make a Ref with a port map that would be automatically updated by
|
|
42
|
+
// listening to the stream of connection events?
|
|
43
|
+
|
|
44
|
+
// TODO: add a stream to listen for all messages of all currently
|
|
45
|
+
// connected inputs, all present inputs, specific input
|
|
46
|
+
|
|
47
|
+
// TODO: add sinks that will accept command streams to redirect midi commands
|
|
48
|
+
// from something into an actual API
|
|
49
|
+
|
|
50
|
+
// TODO: add effect to wait until connected by port ID
|
|
51
|
+
|
|
52
|
+
// TODO: reflect sysex and software flags in type-system
|
|
53
|
+
|
|
54
|
+
// TODO: make matchers that support returning effects from the callback instead of plain values
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Unique symbol used for distinguishing {@linkcode EMIDIAccessInstance}
|
|
58
|
+
* instances from other objects at both runtime and type-level
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
const TypeId: unique symbol = Symbol.for('effect-web-midi/EMIDIAccessInstance')
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Unique symbol used for distinguishing {@linkcode EMIDIAccessInstance}
|
|
65
|
+
* instances from other objects at both runtime and type-level
|
|
66
|
+
*/
|
|
67
|
+
export type TypeId = typeof TypeId
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A tag that allows to provide
|
|
71
|
+
* {@linkcode EMIDIAccessInstance|access instance} once with e.g.
|
|
72
|
+
* {@linkcode layer}, {@linkcode layerSystemExclusiveSupported}, etc. and reuse
|
|
73
|
+
* it anywhere, instead of repeatedly {@linkcode request}ing it.
|
|
74
|
+
*
|
|
75
|
+
* The downside of using DI might be that in different places of the app it
|
|
76
|
+
* would be harder to maintain tight MIDI permission scopes.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { EMIDIAccess } from 'effect-web-midi'
|
|
81
|
+
* import * as Effect from 'effect/Effect'
|
|
82
|
+
*
|
|
83
|
+
* const program = Effect.gen(function* () {
|
|
84
|
+
* // ^ Effect.Effect<
|
|
85
|
+
* // void,
|
|
86
|
+
* // | AbortError
|
|
87
|
+
* // | UnderlyingSystemError
|
|
88
|
+
* // | MIDIAccessNotAllowedError
|
|
89
|
+
* // | MIDIAccessNotSupportedError
|
|
90
|
+
* // never
|
|
91
|
+
* // >
|
|
92
|
+
*
|
|
93
|
+
* const access = yield* EMIDIAccess.EMIDIAccess
|
|
94
|
+
* // ^ EMIDIAccessInstance
|
|
95
|
+
*
|
|
96
|
+
* console.log(access.sysexEnabled)
|
|
97
|
+
* // ^ true
|
|
98
|
+
* }).pipe(Effect.provide(EMIDIAccess.layerSystemExclusiveSupported))
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @see `navigator.requestMIDIAccess` {@link https://www.w3.org/TR/webmidi/#dom-navigator-requestmidiaccess|Web MIDI spec}, {@link https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMIDIAccess|MDN reference}
|
|
102
|
+
*/
|
|
103
|
+
export class EMIDIAccess extends Context.Tag('effect-web-midi/EMIDIAccess')<
|
|
104
|
+
EMIDIAccess,
|
|
105
|
+
EMIDIAccessInstance
|
|
106
|
+
>() {}
|
|
107
|
+
|
|
108
|
+
export interface RequestMIDIAccessOptions {
|
|
109
|
+
/**
|
|
110
|
+
* This field informs the system whether the ability to send and receive
|
|
111
|
+
* `System Exclusive` messages is requested or allowed on a given
|
|
112
|
+
* {@linkcode EMIDIAccessInstance} object.
|
|
113
|
+
*
|
|
114
|
+
* If this field is set to `true`, but `System Exclusive` support is denied
|
|
115
|
+
* (either by policy or by user action), the access request will fail with a
|
|
116
|
+
* {@linkcode Errors.MIDIAccessNotAllowedError} error.
|
|
117
|
+
*
|
|
118
|
+
* If this support is not requested (and allowed), the system will throw
|
|
119
|
+
* exceptions if the user tries to send `System Exclusive` messages, and will
|
|
120
|
+
* silently mask out any `System Exclusive` messages received on the port.
|
|
121
|
+
*
|
|
122
|
+
* @default false
|
|
123
|
+
* @see {@link https://www.w3.org/TR/webmidi/#dom-midioptions-sysex|Web MIDI spec}, {@link https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMIDIAccess#sysex|MDN reference}
|
|
124
|
+
*/
|
|
125
|
+
readonly sysex?: boolean
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* This field informs the system whether the ability to utilize any software
|
|
129
|
+
* synthesizers installed in the host system is requested or allowed on a
|
|
130
|
+
* given {@linkcode EMIDIAccessInstance} object.
|
|
131
|
+
*
|
|
132
|
+
* If this field is set to `true`, but software synthesizer support is denied
|
|
133
|
+
* (either by policy or by user action), the access request will fail with a
|
|
134
|
+
* {@linkcode Errors.MIDIAccessNotAllowedError} error.
|
|
135
|
+
*
|
|
136
|
+
* If this support is not requested, {@linkcode AllPortsRecord},
|
|
137
|
+
* {@linkcode getInputsRecord}, {@linkcode OutputsRecord}, etc. would
|
|
138
|
+
* not include any software synthesizers.
|
|
139
|
+
*
|
|
140
|
+
* Note that may result in a two-step request procedure if software
|
|
141
|
+
* synthesizer support is desired but not required - software synthesizers may
|
|
142
|
+
* be disabled when MIDI hardware device access is allowed.
|
|
143
|
+
*
|
|
144
|
+
* @default false
|
|
145
|
+
* @see {@link https://www.w3.org/TR/webmidi/#dom-midioptions-software|Web MIDI spec}, {@link https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMIDIAccess#software|MDN reference}
|
|
146
|
+
*/
|
|
147
|
+
readonly software?: boolean
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Prototype of all {@linkcode EMIDIAccessInstance} instances
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
154
|
+
const Proto = {
|
|
155
|
+
_tag: 'EMIDIAccess' as const,
|
|
156
|
+
[TypeId]: TypeId,
|
|
157
|
+
[Hash.symbol](this: EMIDIAccessImplementationInstance) {
|
|
158
|
+
return Hash.structure(this._config)
|
|
159
|
+
},
|
|
160
|
+
[Equal.symbol](that: Equal.Equal) {
|
|
161
|
+
return this === that
|
|
162
|
+
},
|
|
163
|
+
pipe() {
|
|
164
|
+
// biome-ignore lint/complexity/noArguments: Effect's tradition
|
|
165
|
+
return Pipeable.pipeArguments(this, arguments)
|
|
166
|
+
},
|
|
167
|
+
toString(this: EMIDIAccessImplementationInstance) {
|
|
168
|
+
return Inspectable.format(this.toJSON())
|
|
169
|
+
},
|
|
170
|
+
toJSON(this: EMIDIAccessImplementationInstance) {
|
|
171
|
+
return { _id: 'EMIDIAccess', config: this._config }
|
|
172
|
+
},
|
|
173
|
+
[Inspectable.NodeInspectSymbol](this: EMIDIAccessImplementationInstance) {
|
|
174
|
+
return this.toJSON()
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
get sysexEnabled() {
|
|
178
|
+
return assumeImpl(this)._access.sysexEnabled
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
get softwareSynthEnabled() {
|
|
182
|
+
return !!assumeImpl(this)._config.software
|
|
183
|
+
},
|
|
184
|
+
} satisfies EMIDIAccessInstance
|
|
185
|
+
|
|
186
|
+
// !!! DOCUMENTATION CURSOR !!!
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Thin wrapper around {@linkcode MIDIAccess} instance. Will be seen in all the
|
|
190
|
+
* external code.
|
|
191
|
+
*/
|
|
192
|
+
export interface EMIDIAccessInstance
|
|
193
|
+
extends Equal.Equal,
|
|
194
|
+
Pipeable.Pipeable,
|
|
195
|
+
Inspectable.Inspectable {
|
|
196
|
+
readonly [TypeId]: TypeId
|
|
197
|
+
readonly _tag: 'EMIDIAccess'
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The **`sysexEnabled`** read-only property of the MIDIAccess interface indicates whether system exclusive support is enabled on the current MIDIAccess instance.
|
|
201
|
+
*
|
|
202
|
+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/MIDIAccess/sysexEnabled)
|
|
203
|
+
*/
|
|
204
|
+
readonly sysexEnabled: boolean
|
|
205
|
+
|
|
206
|
+
readonly softwareSynthEnabled: boolean
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Thin wrapper around {@linkcode MIDIAccess} instance giving access to the
|
|
211
|
+
* actual field storing it.
|
|
212
|
+
* @internal
|
|
213
|
+
*/
|
|
214
|
+
interface EMIDIAccessImplementationInstance extends EMIDIAccessInstance {
|
|
215
|
+
readonly _access: MIDIAccess
|
|
216
|
+
readonly _config: Readonly<RequestMIDIAccessOptions>
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
*
|
|
221
|
+
*
|
|
222
|
+
* @internal
|
|
223
|
+
*/
|
|
224
|
+
const makeImpl = (
|
|
225
|
+
rawAccess: MIDIAccess,
|
|
226
|
+
config?: Readonly<RequestMIDIAccessOptions>,
|
|
227
|
+
): EMIDIAccessImplementationInstance => {
|
|
228
|
+
const instance = Object.create(Proto)
|
|
229
|
+
instance._access = rawAccess
|
|
230
|
+
// TODO: set individual software and sysex flags instead
|
|
231
|
+
instance._config = config ?? {}
|
|
232
|
+
return instance
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Asserts an object to be valid `EMIDIAccess` and casts it to internal
|
|
237
|
+
* implementation type
|
|
238
|
+
*
|
|
239
|
+
* @internal
|
|
240
|
+
*/
|
|
241
|
+
const assertImpl = (access: unknown) => {
|
|
242
|
+
if (!isImpl(access)) throw new Error('Failed to cast to EMIDIAccess')
|
|
243
|
+
return access
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Asserts an object to be valid `EMIDIAccess`
|
|
248
|
+
*
|
|
249
|
+
* @internal
|
|
250
|
+
*/
|
|
251
|
+
export const assert: (access: unknown) => EMIDIAccessInstance = assertImpl
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @internal
|
|
255
|
+
*/
|
|
256
|
+
export const assumeImpl = (access: EMIDIAccessInstance) =>
|
|
257
|
+
access as EMIDIAccessImplementationInstance
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
*
|
|
261
|
+
*
|
|
262
|
+
* @internal
|
|
263
|
+
*/
|
|
264
|
+
const make: (
|
|
265
|
+
rawAccess: MIDIAccess,
|
|
266
|
+
config?: Readonly<RequestMIDIAccessOptions>,
|
|
267
|
+
) => EMIDIAccessInstance = makeImpl
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
*
|
|
271
|
+
*
|
|
272
|
+
* @internal
|
|
273
|
+
*/
|
|
274
|
+
const isImpl = (access: unknown): access is EMIDIAccessImplementationInstance =>
|
|
275
|
+
typeof access === 'object' &&
|
|
276
|
+
access !== null &&
|
|
277
|
+
Object.getPrototypeOf(access) === Proto &&
|
|
278
|
+
TypeId in access &&
|
|
279
|
+
'_access' in access &&
|
|
280
|
+
typeof access._access === 'object' &&
|
|
281
|
+
'_config' in access &&
|
|
282
|
+
typeof access._config === 'object' &&
|
|
283
|
+
access._config !== null &&
|
|
284
|
+
access._access instanceof MIDIAccess
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
*
|
|
288
|
+
*
|
|
289
|
+
*/
|
|
290
|
+
export const is: (access: unknown) => access is EMIDIAccessInstance = isImpl
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
*
|
|
294
|
+
* @internal
|
|
295
|
+
*/
|
|
296
|
+
export const resolve = <E = never, R = never>(
|
|
297
|
+
polymorphicAccess: PolymorphicAccessInstance<E, R>,
|
|
298
|
+
) => Util.fromPolymorphic(polymorphicAccess, is)
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
*
|
|
302
|
+
*
|
|
303
|
+
*/
|
|
304
|
+
export type PolymorphicAccessInstance<E, R> = Util.PolymorphicEffect<
|
|
305
|
+
EMIDIAccessInstance,
|
|
306
|
+
E,
|
|
307
|
+
R
|
|
308
|
+
>
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
*
|
|
312
|
+
*
|
|
313
|
+
*/
|
|
314
|
+
export type PolymorphicAccessInstanceClean = PolymorphicAccessInstance<
|
|
315
|
+
never,
|
|
316
|
+
never
|
|
317
|
+
>
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
*
|
|
321
|
+
*
|
|
322
|
+
*/
|
|
323
|
+
type ValueOfReadonlyMap<T> = T extends ReadonlyMap<unknown, infer V> ? V : never
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
*
|
|
327
|
+
* @internal
|
|
328
|
+
*/
|
|
329
|
+
const getPortEntriesFromRawAccess =
|
|
330
|
+
<
|
|
331
|
+
const TMIDIPortType extends MIDIPortType,
|
|
332
|
+
const TMIDIAccessObjectKey extends `${TMIDIPortType}s`,
|
|
333
|
+
TRawMIDIPort extends ValueOfReadonlyMap<MIDIAccess[TMIDIAccessObjectKey]>,
|
|
334
|
+
>(
|
|
335
|
+
key: TMIDIAccessObjectKey,
|
|
336
|
+
make: (port: TRawMIDIPort) => EMIDIPort.EMIDIPort<TMIDIPortType>,
|
|
337
|
+
) =>
|
|
338
|
+
(rawAccess: MIDIAccess) =>
|
|
339
|
+
Iterable.map(
|
|
340
|
+
rawAccess[key] as ReadonlyMap<EMIDIPort.Id<TMIDIPortType>, TRawMIDIPort>,
|
|
341
|
+
([id, raw]) =>
|
|
342
|
+
[id as EMIDIPort.Id<TMIDIPortType>, make(raw)] satisfies Types.TupleOf<
|
|
343
|
+
2,
|
|
344
|
+
unknown
|
|
345
|
+
>,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
*
|
|
350
|
+
* @internal
|
|
351
|
+
*/
|
|
352
|
+
const getInputEntriesFromRaw = getPortEntriesFromRawAccess(
|
|
353
|
+
'inputs',
|
|
354
|
+
EMIDIInput.make,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
*
|
|
359
|
+
* @internal
|
|
360
|
+
*/
|
|
361
|
+
const getOutputEntriesFromRaw = getPortEntriesFromRawAccess(
|
|
362
|
+
'outputs',
|
|
363
|
+
EMIDIOutput.make,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
*
|
|
368
|
+
* @internal
|
|
369
|
+
*/
|
|
370
|
+
const getAllPortsEntriesFromRaw = (rawAccess: MIDIAccess) =>
|
|
371
|
+
Iterable.appendAll(
|
|
372
|
+
getInputEntriesFromRaw(rawAccess),
|
|
373
|
+
getOutputEntriesFromRaw(rawAccess),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
*
|
|
378
|
+
* @param accessor
|
|
379
|
+
* @returns
|
|
380
|
+
* @internal
|
|
381
|
+
*/
|
|
382
|
+
const decorateToTakePolymorphicAccessAndReturnRecord =
|
|
383
|
+
<T extends [string, unknown]>(
|
|
384
|
+
accessor: (rawAccess: MIDIAccess) => Iterable<T>,
|
|
385
|
+
) =>
|
|
386
|
+
<E = never, R = never>(polymorphicAccess: PolymorphicAccessInstance<E, R>) =>
|
|
387
|
+
Effect.map(
|
|
388
|
+
resolve(polymorphicAccess),
|
|
389
|
+
EFunction.flow(assumeImpl, e => e._access, accessor, Record.fromEntries),
|
|
390
|
+
) as Effect.Effect<
|
|
391
|
+
Types.UnionToIntersection<T extends unknown ? Record<T[0], T[1]> : never>,
|
|
392
|
+
E,
|
|
393
|
+
R
|
|
394
|
+
>
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Because `MIDIInputMap` can potentially be a mutable object, meaning new
|
|
398
|
+
* devices can be added or removed at runtime, it is effectful.
|
|
399
|
+
*
|
|
400
|
+
* The **`inputs`** read-only property of the MIDIAccess interface provides
|
|
401
|
+
* access to any available MIDI input ports.
|
|
402
|
+
*
|
|
403
|
+
* [MDN
|
|
404
|
+
* Reference](https://developer.mozilla.org/docs/Web/API/MIDIAccess/inputs)
|
|
405
|
+
*/
|
|
406
|
+
export const getInputsRecord = decorateToTakePolymorphicAccessAndReturnRecord(
|
|
407
|
+
getInputEntriesFromRaw,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Because `MIDIOutputMap` can potentially be a mutable object, meaning new
|
|
412
|
+
* devices can be added or removed at runtime, it is effectful.
|
|
413
|
+
*
|
|
414
|
+
* The **`outputs`** read-only property of the MIDIAccess interface provides
|
|
415
|
+
* access to any available MIDI output ports.
|
|
416
|
+
*
|
|
417
|
+
* [MDN
|
|
418
|
+
* Reference](https://developer.mozilla.org/docs/Web/API/MIDIAccess/outputs)
|
|
419
|
+
*/
|
|
420
|
+
export const getOutputsRecord = decorateToTakePolymorphicAccessAndReturnRecord(
|
|
421
|
+
getOutputEntriesFromRaw,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
*
|
|
426
|
+
*
|
|
427
|
+
*/
|
|
428
|
+
export const getAllPortsRecord = decorateToTakePolymorphicAccessAndReturnRecord(
|
|
429
|
+
getAllPortsEntriesFromRaw,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
*
|
|
434
|
+
*
|
|
435
|
+
*/
|
|
436
|
+
export const InputsRecord = getInputsRecord(EMIDIAccess)
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
*
|
|
440
|
+
*
|
|
441
|
+
*/
|
|
442
|
+
export const OutputsRecord = getOutputsRecord(EMIDIAccess)
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
*
|
|
446
|
+
*
|
|
447
|
+
*/
|
|
448
|
+
export const AllPortsRecord = getAllPortsRecord(EMIDIAccess)
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* [MIDIConnectionEvent MDN
|
|
452
|
+
* Reference](https://developer.mozilla.org/docs/Web/API/MIDIConnectionEvent)
|
|
453
|
+
*/
|
|
454
|
+
export const makeAllPortsStateChangesStream =
|
|
455
|
+
StreamMaker.createStreamMakerFrom<MIDIPortEventMap>()(
|
|
456
|
+
is,
|
|
457
|
+
access => ({
|
|
458
|
+
tag: 'MIDIPortStateChange',
|
|
459
|
+
eventListener: {
|
|
460
|
+
target: assumeImpl(access)._access,
|
|
461
|
+
type: 'statechange',
|
|
462
|
+
},
|
|
463
|
+
spanAttributes: {
|
|
464
|
+
spanTargetName: 'MIDI access handle',
|
|
465
|
+
requestedAccessConfig: assumeImpl(access)._config,
|
|
466
|
+
},
|
|
467
|
+
nullableFieldName: 'port',
|
|
468
|
+
}),
|
|
469
|
+
rawPort =>
|
|
470
|
+
({
|
|
471
|
+
newState: rawPort
|
|
472
|
+
? ({
|
|
473
|
+
ofDevice: rawPort.state,
|
|
474
|
+
ofConnection: rawPort.connection,
|
|
475
|
+
} as const)
|
|
476
|
+
: null,
|
|
477
|
+
port:
|
|
478
|
+
rawPort instanceof globalThis.MIDIInput
|
|
479
|
+
? EMIDIInput.make(rawPort)
|
|
480
|
+
: rawPort instanceof globalThis.MIDIOutput
|
|
481
|
+
? EMIDIOutput.make(rawPort)
|
|
482
|
+
: null,
|
|
483
|
+
}) as const,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* beware that it's not possible to ensure the messages will either be all
|
|
488
|
+
* delivered, or all not delivered, as in ACID transactions. There's not even a
|
|
489
|
+
* mechanism to remove a specific message (not all) from the sending queue
|
|
490
|
+
*/
|
|
491
|
+
export const send: DualSendMIDIMessageFromAccess = EFunction.dual<
|
|
492
|
+
SendMIDIMessageAccessLast,
|
|
493
|
+
SendMIDIMessageAccessFirst
|
|
494
|
+
>(
|
|
495
|
+
Util.polymorphicCheckInDual(is),
|
|
496
|
+
Effect.fn('EMIDIAccess.send')(
|
|
497
|
+
function* (polymorphicAccess, target, midiMessage, timestamp) {
|
|
498
|
+
const access = yield* resolve(polymorphicAccess)
|
|
499
|
+
|
|
500
|
+
const outputs = yield* getOutputsRecord(access)
|
|
501
|
+
|
|
502
|
+
if (target === 'all existing outputs at effect execution')
|
|
503
|
+
return yield* EFunction.pipe(
|
|
504
|
+
Record.values(outputs),
|
|
505
|
+
Effect.forEach(EMIDIOutput.send(midiMessage, timestamp)),
|
|
506
|
+
Effect.as(access),
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if (target === 'all open connections at effect execution')
|
|
510
|
+
return yield* EFunction.pipe(
|
|
511
|
+
Record.values(outputs),
|
|
512
|
+
// TODO: maybe also do something about pending?
|
|
513
|
+
Effect.filter(isOutputConnectionOpenByPort),
|
|
514
|
+
Effect.flatMap(
|
|
515
|
+
Effect.forEach(EMIDIOutput.send(midiMessage, timestamp)),
|
|
516
|
+
),
|
|
517
|
+
Effect.as(access),
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
// TODO: maybe since deviceState returns always connected devices we can
|
|
521
|
+
// simplify this check by applying intersections and comparing lengths
|
|
522
|
+
|
|
523
|
+
const portsIdsToSend: EMIDIOutput.Id[] = EArray.ensure(target)
|
|
524
|
+
|
|
525
|
+
const deviceStatusesEffect = portsIdsToSend.map(id =>
|
|
526
|
+
EFunction.pipe(
|
|
527
|
+
Record.get(outputs, id),
|
|
528
|
+
Option.match({
|
|
529
|
+
onNone: () => Effect.succeed('disconnected' as const),
|
|
530
|
+
onSome: EFunction.flow(getOutputDeviceStateByPort),
|
|
531
|
+
}),
|
|
532
|
+
effect => Unify.unify(effect),
|
|
533
|
+
Effect.map(state => ({ id, state })),
|
|
534
|
+
),
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
const disconnectedDevice = EArray.findFirst(
|
|
538
|
+
yield* Effect.all(deviceStatusesEffect),
|
|
539
|
+
_ => _.state === 'disconnected',
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
if (Option.isSome(disconnectedDevice))
|
|
543
|
+
return yield* new Errors.DisconnectedPortError({
|
|
544
|
+
portId: disconnectedDevice.value.id,
|
|
545
|
+
cause: new DOMException(
|
|
546
|
+
// TODO: make an experiment and paste the error text here
|
|
547
|
+
'TODO: imitate there an error thats thrown when the port is disconnected',
|
|
548
|
+
'InvalidStateError',
|
|
549
|
+
) as DOMException & { name: 'InvalidStateError' },
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
const sendToSome = (predicate: (id: EMIDIOutput.Id) => boolean) =>
|
|
553
|
+
Effect.all(
|
|
554
|
+
Record.reduce(
|
|
555
|
+
outputs,
|
|
556
|
+
[] as EMIDIOutput.SentMessageEffectFromPort<never, never>[],
|
|
557
|
+
// TODO: investigate what the fuck is going on, why the fuck can't I
|
|
558
|
+
// make it a simple expression without either nesting it in
|
|
559
|
+
// curly-braced function body or adding manual type-annotation
|
|
560
|
+
(acc, port, id) =>
|
|
561
|
+
predicate(id)
|
|
562
|
+
? [
|
|
563
|
+
...acc,
|
|
564
|
+
EMIDIOutput.send(
|
|
565
|
+
port,
|
|
566
|
+
midiMessage,
|
|
567
|
+
timestamp,
|
|
568
|
+
) as EMIDIOutput.SentMessageEffectFromPort,
|
|
569
|
+
]
|
|
570
|
+
: acc,
|
|
571
|
+
),
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
yield* sendToSome(id => portsIdsToSend.includes(id))
|
|
575
|
+
|
|
576
|
+
return access
|
|
577
|
+
},
|
|
578
|
+
),
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* @param options Passing a value of a `boolean` type is equivalent to setting
|
|
583
|
+
* `options.capture` property
|
|
584
|
+
*/
|
|
585
|
+
export const makeMessagesStreamByInputId = <
|
|
586
|
+
const TOnNullStrategy extends StreamMaker.OnNullStrategy = undefined,
|
|
587
|
+
>(
|
|
588
|
+
id: EMIDIInput.Id,
|
|
589
|
+
options?: StreamMaker.StreamMakerOptions<TOnNullStrategy>,
|
|
590
|
+
) =>
|
|
591
|
+
EMIDIInput.makeMessagesStreamByPort(
|
|
592
|
+
GetPort.getInputByPortIdInContext(id),
|
|
593
|
+
options,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
// TODO: makeMessagesStreamByInputIdAndAccess
|
|
597
|
+
export const makeMessagesStreamByInputIdAndAccess = () => {
|
|
598
|
+
throw new Error('not implemented')
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
*
|
|
603
|
+
*/
|
|
604
|
+
export const sendToPortById = (
|
|
605
|
+
id: EMIDIOutput.Id,
|
|
606
|
+
...args: EMIDIOutput.SendFromPortArgs
|
|
607
|
+
) =>
|
|
608
|
+
Effect.asVoid(
|
|
609
|
+
EMIDIOutput.send(GetPort.getOutputByPortIdInContext(id), ...args),
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
*
|
|
614
|
+
*/
|
|
615
|
+
export const clearPortById = EFunction.flow(
|
|
616
|
+
GetPort.getOutputByPortIdInContext,
|
|
617
|
+
EMIDIOutput.clear,
|
|
618
|
+
Effect.asVoid,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* @param options Passing a value of a `boolean` type is equivalent to setting
|
|
623
|
+
* `options.capture` property
|
|
624
|
+
*/
|
|
625
|
+
export const makeAllPortsStateChangesStreamInContext = <
|
|
626
|
+
const TOnNullStrategy extends StreamMaker.OnNullStrategy = undefined,
|
|
627
|
+
>(
|
|
628
|
+
options?: StreamMaker.StreamMakerOptions<TOnNullStrategy>,
|
|
629
|
+
) => makeAllPortsStateChangesStream(EMIDIAccess, options)
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
*
|
|
633
|
+
*
|
|
634
|
+
*/
|
|
635
|
+
export const sendInContext = (...args: SendFromAccessArgs) =>
|
|
636
|
+
Effect.asVoid(send(EMIDIAccess, ...args))
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @param options
|
|
640
|
+
*
|
|
641
|
+
* @returns An Effect representing a request for access to MIDI devices on a
|
|
642
|
+
* user's system. Available only in secure contexts.
|
|
643
|
+
*/
|
|
644
|
+
export const request = Effect.fn('EMIDIAccess.request')(function* (
|
|
645
|
+
options?: RequestMIDIAccessOptions,
|
|
646
|
+
) {
|
|
647
|
+
yield* Effect.annotateCurrentSpan({ options })
|
|
648
|
+
|
|
649
|
+
const rawAccess = yield* Effect.tryPromise({
|
|
650
|
+
try: () => navigator.requestMIDIAccess(options),
|
|
651
|
+
catch: Errors.remapErrorByName(
|
|
652
|
+
{
|
|
653
|
+
AbortError: Errors.AbortError,
|
|
654
|
+
|
|
655
|
+
InvalidStateError: Errors.UnderlyingSystemError,
|
|
656
|
+
|
|
657
|
+
NotAllowedError: Errors.MIDIAccessNotAllowedError,
|
|
658
|
+
// SecurityError is kept for compatibility reason
|
|
659
|
+
// (https://github.com/WebAudio/web-midi-api/pull/267):
|
|
660
|
+
SecurityError: Errors.MIDIAccessNotAllowedError,
|
|
661
|
+
|
|
662
|
+
NotSupportedError: Errors.MIDIAccessNotSupportedError,
|
|
663
|
+
// For case when navigator doesn't exist
|
|
664
|
+
ReferenceError: Errors.MIDIAccessNotSupportedError,
|
|
665
|
+
// For case when navigator.requestMIDIAccess is undefined
|
|
666
|
+
TypeError: Errors.MIDIAccessNotSupportedError,
|
|
667
|
+
},
|
|
668
|
+
'EMIDIAccess.request error handling absurd',
|
|
669
|
+
{ whileAskingForPermissions: options ?? {} },
|
|
670
|
+
),
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
// TODO: finish this
|
|
674
|
+
|
|
675
|
+
const ref = yield* Ref.make(
|
|
676
|
+
SortedMap.empty<EMIDIPort.BothId, MIDIPortType>(Order.string),
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
// return make(rawAccess, options, ref)
|
|
680
|
+
return make(rawAccess, options)
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
// TODO: clear all outputs
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
*
|
|
687
|
+
* **Errors:**
|
|
688
|
+
*
|
|
689
|
+
* - {@linkcode Errors.AbortError} Argument x must be non-zero
|
|
690
|
+
* - {@linkcode Errors.UnderlyingSystemError} Argument x must be non-zero
|
|
691
|
+
* - {@linkcode Errors.MIDIAccessNotSupportedError} Argument x must be non-zero
|
|
692
|
+
* - {@linkcode Errors.MIDIAccessNotAllowedError} Argument x must be non-zero
|
|
693
|
+
*
|
|
694
|
+
* @param config
|
|
695
|
+
* @returns
|
|
696
|
+
*/
|
|
697
|
+
export const layer = (config?: RequestMIDIAccessOptions) =>
|
|
698
|
+
Layer.effect(EMIDIAccess, request(config))
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
*
|
|
702
|
+
*/
|
|
703
|
+
export const layerMostRestricted = layer()
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
*
|
|
707
|
+
*/
|
|
708
|
+
export const layerSystemExclusiveSupported = layer({ sysex: true })
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
*
|
|
712
|
+
*/
|
|
713
|
+
export const layerSoftwareSynthSupported = layer({ software: true })
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
*
|
|
717
|
+
*/
|
|
718
|
+
export const layerSystemExclusiveAndSoftwareSynthSupported = layer({
|
|
719
|
+
software: true,
|
|
720
|
+
sysex: true,
|
|
721
|
+
})
|
|
722
|
+
|
|
723
|
+
export interface SentMessageEffectFromAccess<E = never, R = never>
|
|
724
|
+
extends Util.SentMessageEffectFrom<EMIDIAccessInstance, E, R> {}
|
|
725
|
+
|
|
726
|
+
export type TargetPortSelector =
|
|
727
|
+
| 'all existing outputs at effect execution'
|
|
728
|
+
| 'all open connections at effect execution'
|
|
729
|
+
| EMIDIOutput.Id
|
|
730
|
+
| EMIDIOutput.Id[]
|
|
731
|
+
|
|
732
|
+
export interface DualSendMIDIMessageFromAccess
|
|
733
|
+
extends SendMIDIMessageAccessFirst,
|
|
734
|
+
SendMIDIMessageAccessLast {}
|
|
735
|
+
|
|
736
|
+
export type SendFromAccessArgs = [
|
|
737
|
+
targetPortSelector: TargetPortSelector,
|
|
738
|
+
...args: EMIDIOutput.SendFromPortArgs,
|
|
739
|
+
]
|
|
740
|
+
|
|
741
|
+
export interface SendMIDIMessageAccessFirst {
|
|
742
|
+
/**
|
|
743
|
+
*
|
|
744
|
+
*
|
|
745
|
+
*/
|
|
746
|
+
<E = never, R = never>(
|
|
747
|
+
polymorphicAccess: PolymorphicAccessInstance<E, R>,
|
|
748
|
+
...args: SendFromAccessArgs
|
|
749
|
+
): SentMessageEffectFromAccess<E, R>
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
export interface SendMIDIMessageAccessLast {
|
|
753
|
+
/**
|
|
754
|
+
*
|
|
755
|
+
*
|
|
756
|
+
*/
|
|
757
|
+
(
|
|
758
|
+
...args: SendFromAccessArgs
|
|
759
|
+
): {
|
|
760
|
+
/**
|
|
761
|
+
*
|
|
762
|
+
*
|
|
763
|
+
*/
|
|
764
|
+
<E = never, R = never>(
|
|
765
|
+
polymorphicAccess: PolymorphicAccessInstance<E, R>,
|
|
766
|
+
): SentMessageEffectFromAccess<E, R>
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
export interface GetThingByPortId<
|
|
771
|
+
TSuccess,
|
|
772
|
+
TTypeOfPortId extends MIDIPortType,
|
|
773
|
+
TAccessGettingFallbackError,
|
|
774
|
+
TAccessGettingFallbackRequirement,
|
|
775
|
+
TAdditionalError,
|
|
776
|
+
TAdditionalRequirement,
|
|
777
|
+
> extends GetThingByPortIdAccessFirst<
|
|
778
|
+
TSuccess,
|
|
779
|
+
TTypeOfPortId,
|
|
780
|
+
TAccessGettingFallbackError,
|
|
781
|
+
TAccessGettingFallbackRequirement,
|
|
782
|
+
TAdditionalError,
|
|
783
|
+
TAdditionalRequirement
|
|
784
|
+
>,
|
|
785
|
+
GetThingByPortIdAccessLast<
|
|
786
|
+
TSuccess,
|
|
787
|
+
TTypeOfPortId,
|
|
788
|
+
TAccessGettingFallbackError,
|
|
789
|
+
TAccessGettingFallbackRequirement,
|
|
790
|
+
TAdditionalError,
|
|
791
|
+
TAdditionalRequirement
|
|
792
|
+
> {}
|
|
793
|
+
|
|
794
|
+
export interface GetThingByPortIdAccessFirst<
|
|
795
|
+
TSuccess,
|
|
796
|
+
TTypeOfPortId extends MIDIPortType,
|
|
797
|
+
TAccessGettingFallbackError,
|
|
798
|
+
TAccessGettingFallbackRequirement,
|
|
799
|
+
TAdditionalError,
|
|
800
|
+
TAdditionalRequirement,
|
|
801
|
+
> {
|
|
802
|
+
/**
|
|
803
|
+
*
|
|
804
|
+
*
|
|
805
|
+
*/
|
|
806
|
+
<TAccessGettingError = never, TAccessGettingRequirement = never>(
|
|
807
|
+
polymorphicAccess: PolymorphicAccessInstance<
|
|
808
|
+
TAccessGettingError,
|
|
809
|
+
TAccessGettingRequirement
|
|
810
|
+
>,
|
|
811
|
+
id: EMIDIPort.Id<TTypeOfPortId>,
|
|
812
|
+
): AcquiredThing<
|
|
813
|
+
TSuccess,
|
|
814
|
+
TAccessGettingError,
|
|
815
|
+
TAccessGettingRequirement,
|
|
816
|
+
TAccessGettingFallbackError,
|
|
817
|
+
TAccessGettingFallbackRequirement,
|
|
818
|
+
TAdditionalError,
|
|
819
|
+
TAdditionalRequirement
|
|
820
|
+
>
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export interface GetThingByPortIdAccessLast<
|
|
824
|
+
TSuccess,
|
|
825
|
+
TTypeOfPortId extends MIDIPortType,
|
|
826
|
+
TAccessGettingFallbackError,
|
|
827
|
+
TAccessGettingFallbackRequirement,
|
|
828
|
+
TAdditionalError,
|
|
829
|
+
TAdditionalRequirement,
|
|
830
|
+
> {
|
|
831
|
+
/**
|
|
832
|
+
*
|
|
833
|
+
*
|
|
834
|
+
*/
|
|
835
|
+
(
|
|
836
|
+
id: EMIDIPort.Id<TTypeOfPortId>,
|
|
837
|
+
): GetThingByPortIdAccessLastSecondHalf<
|
|
838
|
+
TSuccess,
|
|
839
|
+
TAccessGettingFallbackError,
|
|
840
|
+
TAccessGettingFallbackRequirement,
|
|
841
|
+
TAdditionalError,
|
|
842
|
+
TAdditionalRequirement
|
|
843
|
+
>
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
export interface GetThingByPortIdAccessLastSecondHalf<
|
|
847
|
+
TSuccess,
|
|
848
|
+
TAccessGettingFallbackError,
|
|
849
|
+
TAccessGettingFallbackRequirement,
|
|
850
|
+
TAdditionalError,
|
|
851
|
+
TAdditionalRequirement,
|
|
852
|
+
> {
|
|
853
|
+
/**
|
|
854
|
+
*
|
|
855
|
+
*
|
|
856
|
+
*/
|
|
857
|
+
<TAccessGettingError = never, TAccessGettingRequirement = never>(
|
|
858
|
+
polymorphicAccess: PolymorphicAccessInstance<
|
|
859
|
+
TAccessGettingError,
|
|
860
|
+
TAccessGettingRequirement
|
|
861
|
+
>,
|
|
862
|
+
): AcquiredThing<
|
|
863
|
+
TSuccess,
|
|
864
|
+
TAccessGettingError,
|
|
865
|
+
TAccessGettingRequirement,
|
|
866
|
+
TAccessGettingFallbackError,
|
|
867
|
+
TAccessGettingFallbackRequirement,
|
|
868
|
+
TAdditionalError,
|
|
869
|
+
TAdditionalRequirement
|
|
870
|
+
>
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
export interface AcquiredThing<
|
|
874
|
+
TSuccess,
|
|
875
|
+
TAccessGettingError,
|
|
876
|
+
TAccessGettingRequirement,
|
|
877
|
+
TAccessGettingFallbackError,
|
|
878
|
+
TAccessGettingFallbackRequirement,
|
|
879
|
+
TAdditionalError,
|
|
880
|
+
TAdditionalRequirement,
|
|
881
|
+
> extends Effect.Effect<
|
|
882
|
+
TSuccess,
|
|
883
|
+
| Util.FallbackOnUnknownOrAny<
|
|
884
|
+
TAccessGettingError,
|
|
885
|
+
TAccessGettingFallbackError
|
|
886
|
+
>
|
|
887
|
+
| TAdditionalError,
|
|
888
|
+
| Util.FallbackOnUnknownOrAny<
|
|
889
|
+
TAccessGettingRequirement,
|
|
890
|
+
TAccessGettingFallbackRequirement
|
|
891
|
+
>
|
|
892
|
+
| TAdditionalRequirement
|
|
893
|
+
> {}
|