@daugasauron/dolly-host-microphone 0.1.4 → 0.1.5
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/abi.mjs +1 -1
- package/microphone.mjs +7 -12
- package/package.json +2 -2
- package/provider.mjs +11 -14
package/abi.mjs
CHANGED
|
@@ -17,4 +17,4 @@ export const DOLLY_MICROPHONE_CAPTURING = 2;
|
|
|
17
17
|
export const DOLLY_MICROPHONE_DENIED = 3;
|
|
18
18
|
export const DOLLY_MICROPHONE_UNAVAILABLE = 4;
|
|
19
19
|
export const DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
|
|
20
|
-
export const DOLLY_MICROPHONE_ABI_DIGEST = "
|
|
20
|
+
export const DOLLY_MICROPHONE_ABI_DIGEST = "0efad528c1809a3a87a0889c2e68145ace79da86af746de2800f2bc215dc2f86";
|
package/microphone.mjs
CHANGED
|
@@ -4,24 +4,19 @@ import { DOLLY_ERRNO as E } from "./abi.mjs";
|
|
|
4
4
|
export { DOLLY_MICROPHONE_ABI_DIGEST as digest } from "./abi.mjs";
|
|
5
5
|
|
|
6
6
|
// The device as the lease bridge sees it.
|
|
7
|
-
export const lease = { type: "microphone", slots: M.DOLLY_MICROPHONE_SLOTS, packetBytes: M.DOLLY_MICROPHONE_PACKET_BYTES,
|
|
7
|
+
export const lease = { type: "microphone", version: M.DOLLY_MICROPHONE_VERSION, slots: M.DOLLY_MICROPHONE_SLOTS, packetBytes: M.DOLLY_MICROPHONE_PACKET_BYTES,
|
|
8
8
|
replyBytes: M.DOLLY_MICROPHONE_REPLY_BYTES, open: M.DOLLY_MICROPHONE_OPEN, close: M.DOLLY_MICROPHONE_CLOSE };
|
|
9
9
|
|
|
10
|
-
export function check() {
|
|
11
|
-
return typeof navigator.mediaDevices?.getUserMedia === "function" && typeof globalThis.AudioWorkletNode === "function"
|
|
12
|
-
? null : "Microphone capture is unavailable in this browser";
|
|
13
|
-
}
|
|
14
|
-
|
|
15
10
|
// Trusted page DOM over the display, beside the browser's own recording mark:
|
|
16
11
|
// shown for as long as a program has asked for or holds the microphone.
|
|
17
|
-
function indicate(indicators, state) {
|
|
12
|
+
function indicate({ mount, indicators }, state) {
|
|
18
13
|
let element = document.querySelector("#microphone-status");
|
|
19
14
|
if (!element) {
|
|
20
|
-
element =
|
|
15
|
+
element = mount.appendChild(document.createElement("div"));
|
|
21
16
|
element.id = "microphone-status";
|
|
22
17
|
element.className = "page-indicator";
|
|
23
18
|
element.setAttribute("role", "status");
|
|
24
|
-
element.style.cssText = "position:
|
|
19
|
+
element.style.cssText = "position:absolute;left:0.5rem;bottom:2rem;z-index:8;max-width:calc(100% - 1rem);" +
|
|
25
20
|
"padding:0.15rem 0.45rem;background:#262626;border:1px solid #e98773;color:#e98773;font-size:13px;pointer-events:none";
|
|
26
21
|
}
|
|
27
22
|
const text = { waiting: "Microphone: waiting for permission", capturing: "Microphone on" }[state];
|
|
@@ -30,13 +25,13 @@ function indicate(indicators, state) {
|
|
|
30
25
|
indicators.hold(element, Boolean(text));
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
export function browser({ send, indicators }) {
|
|
34
|
-
const provider = createMicrophoneProvider(() => indicate(indicators, provider.status().state));
|
|
28
|
+
export function browser({ mount, send, indicators }) {
|
|
29
|
+
const provider = createMicrophoneProvider(() => indicate({ mount, indicators }, provider.status().state));
|
|
35
30
|
return {
|
|
36
31
|
page: { get microphone() { return provider.status(); } },
|
|
37
32
|
messages: {
|
|
38
33
|
"microphone-request"(message) {
|
|
39
|
-
const result = provider.dispatch(message
|
|
34
|
+
const result = provider.dispatch(message);
|
|
40
35
|
send({ type: "microphone-complete", scope: message.scope, sequence: message.sequence, ...result },
|
|
41
36
|
[result.bytes.buffer]);
|
|
42
37
|
},
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.1.
|
|
2
|
+
"version": "0.1.5",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"name": "@daugasauron/dolly-host-microphone",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@daugasauron/dolly": "0.1.
|
|
7
|
+
"@daugasauron/dolly": "0.1.5"
|
|
8
8
|
},
|
|
9
9
|
"description": "Dolly's microphone@0 host module: the default microphone, behind the browser's permission prompt.",
|
|
10
10
|
"repository": {
|
package/provider.mjs
CHANGED
|
@@ -72,20 +72,17 @@ export function createMicrophoneProvider(changed = () => {}) {
|
|
|
72
72
|
queuedFrames: capture?.length ?? 0, capturedFrames: capture?.captured ?? 0, droppedFrames: capture?.dropped ?? 0,
|
|
73
73
|
liveTracks: capture?.stream?.getTracks().filter(track => track.readyState === "live").length ?? 0 };
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
// A request the lease bridge checked: the packet's operation, scope and
|
|
76
|
+
// sequence (dolly_device_header) and its body.
|
|
77
|
+
function dispatch({ operation: op, scope, sequence, body }) {
|
|
76
78
|
try {
|
|
77
|
-
|
|
78
|
-
packet = packet.slice();
|
|
79
|
-
const view = new DataView(packet.buffer, packet.byteOffset, packet.byteLength);
|
|
80
|
-
const op = view.getUint32(4, true), scope = view.getUint32(8, true), sequence = view.getUint32(16, true);
|
|
81
|
-
if (view.getUint32(0, true) !== M.DOLLY_MICROPHONE_VERSION || view.getUint32(12, true) || view.getUint32(20, true) ||
|
|
82
|
-
!scope || !sequence || view.getUint32(24, true) !== packet.length - 32 || view.getUint32(28, true)) fail(E.EINVAL);
|
|
79
|
+
const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
83
80
|
let result;
|
|
84
81
|
if (op === M.DOLLY_MICROPHONE_OPEN) {
|
|
85
|
-
if (
|
|
82
|
+
if (body.length) fail(E.EINVAL);
|
|
86
83
|
if (scope <= generation) fail(E.ESTALE);
|
|
87
84
|
if (capture) fail(E.EBUSY);
|
|
88
|
-
if (typeof navigator.mediaDevices?.getUserMedia !== "function" || typeof AudioWorkletNode !== "function") fail(E.
|
|
85
|
+
if (typeof navigator.mediaDevices?.getUserMedia !== "function" || typeof AudioWorkletNode !== "function") fail(E.ENODEV);
|
|
89
86
|
capture = { scope, sequence, state: M.DOLLY_MICROPHONE_WAITING, released: false,
|
|
90
87
|
samples: new Float32Array(M.DOLLY_MICROPHONE_QUEUE_FRAMES), start: 0, length: 0, captured: 0, dropped: 0 };
|
|
91
88
|
generation = scope;
|
|
@@ -100,9 +97,9 @@ export function createMicrophoneProvider(changed = () => {}) {
|
|
|
100
97
|
if (sequence <= capture.sequence) fail(E.ESTALE);
|
|
101
98
|
capture.sequence = sequence;
|
|
102
99
|
if (op === M.DOLLY_MICROPHONE_READ) {
|
|
103
|
-
if (
|
|
104
|
-
const frames = view.getUint32(
|
|
105
|
-
if (!frames || frames > M.DOLLY_MICROPHONE_MAX_FRAMES || view.getUint32(
|
|
100
|
+
if (body.length !== 8) fail(E.EINVAL);
|
|
101
|
+
const frames = view.getUint32(0, true);
|
|
102
|
+
if (!frames || frames > M.DOLLY_MICROPHONE_MAX_FRAMES || view.getUint32(4, true)) fail(E.EINVAL);
|
|
106
103
|
const count = Math.min(frames, capture.length), size = capture.samples.length;
|
|
107
104
|
if (!count && capture.state === M.DOLLY_MICROPHONE_DENIED) fail(E.EACCES);
|
|
108
105
|
if (!count && capture.state === M.DOLLY_MICROPHONE_UNAVAILABLE) fail(E.ENODEV);
|
|
@@ -112,13 +109,13 @@ export function createMicrophoneProvider(changed = () => {}) {
|
|
|
112
109
|
for (let i = 0; i < count; ++i) output.setFloat32(8 + i * 4, capture.samples[(capture.start + i) % size], true);
|
|
113
110
|
capture.start = (capture.start + count) % size; capture.length -= count;
|
|
114
111
|
} else if (op === M.DOLLY_MICROPHONE_STATUS) {
|
|
115
|
-
if (
|
|
112
|
+
if (body.length) fail(E.EINVAL);
|
|
116
113
|
result = response(24);
|
|
117
114
|
const output = new DataView(result.buffer);
|
|
118
115
|
output.setUint32(0, capture.length, true); output.setUint32(4, capture.state, true);
|
|
119
116
|
output.setBigUint64(8, BigInt(capture.captured), true); output.setBigUint64(16, BigInt(capture.dropped), true);
|
|
120
117
|
} else if (op === M.DOLLY_MICROPHONE_CLOSE) {
|
|
121
|
-
if (
|
|
118
|
+
if (body.length) fail(E.EINVAL);
|
|
122
119
|
release(scope); result = response(0);
|
|
123
120
|
} else fail(E.ENOSYS);
|
|
124
121
|
}
|