@daugasauron/dolly-host-audio 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/audio.mjs +2 -7
- package/package.json +2 -2
- package/provider.mjs +13 -16
package/abi.mjs
CHANGED
|
@@ -15,4 +15,4 @@ export const DOLLY_AUDIO_WRITE = 2;
|
|
|
15
15
|
export const DOLLY_AUDIO_STATUS = 3;
|
|
16
16
|
export const DOLLY_AUDIO_CLOSE = 4;
|
|
17
17
|
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 });
|
|
18
|
-
export const DOLLY_AUDIO_ABI_DIGEST = "
|
|
18
|
+
export const DOLLY_AUDIO_ABI_DIGEST = "fb2dac188156936432d9e25bb6350b3cf5a3226bd1ee26aa799b323b41e53592";
|
package/audio.mjs
CHANGED
|
@@ -4,24 +4,19 @@ import { DOLLY_ERRNO as E } from "./abi.mjs";
|
|
|
4
4
|
export { DOLLY_AUDIO_ABI_DIGEST as digest } from "./abi.mjs";
|
|
5
5
|
|
|
6
6
|
// The device as the lease bridge sees it.
|
|
7
|
-
export const lease = { type: "audio", slots: A.DOLLY_AUDIO_SLOTS, packetBytes: A.DOLLY_AUDIO_PACKET_BYTES,
|
|
7
|
+
export const lease = { type: "audio", version: A.DOLLY_AUDIO_VERSION, slots: A.DOLLY_AUDIO_SLOTS, packetBytes: A.DOLLY_AUDIO_PACKET_BYTES,
|
|
8
8
|
replyBytes: A.DOLLY_AUDIO_REPLY_BYTES, open: A.DOLLY_AUDIO_OPEN, close: A.DOLLY_AUDIO_CLOSE };
|
|
9
9
|
|
|
10
|
-
export function check() {
|
|
11
|
-
return typeof globalThis.AudioContext === "function" ? null : "Web Audio playback is unavailable in this browser";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
10
|
export function browser({ send }) {
|
|
15
11
|
const provider = createAudioProvider();
|
|
16
12
|
const resume = event => { if (event.isTrusted) provider.resume(); };
|
|
17
13
|
window.addEventListener("keydown", resume, { capture: true });
|
|
18
14
|
window.addEventListener("pointerdown", resume, { capture: true });
|
|
19
15
|
return {
|
|
20
|
-
get status() { return provider.status(); },
|
|
21
16
|
page: { get audio() { return provider.status(); } },
|
|
22
17
|
messages: {
|
|
23
18
|
"audio-request"(message) {
|
|
24
|
-
const result = provider.dispatch(message
|
|
19
|
+
const result = provider.dispatch(message);
|
|
25
20
|
send({ type: "audio-complete", scope: message.scope, sequence: message.sequence, ...result },
|
|
26
21
|
[result.bytes.buffer]);
|
|
27
22
|
},
|
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-audio",
|
|
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 audio@0 host module: stereo PCM playback.",
|
|
10
10
|
"repository": {
|
package/provider.mjs
CHANGED
|
@@ -42,22 +42,19 @@ export function createAudioProvider() {
|
|
|
42
42
|
for (const node of stream.nodes) { node.source.onended = null; node.source.stop(); node.source.disconnect(); }
|
|
43
43
|
stream.nodes.clear();
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
// A request the lease bridge checked: the packet's operation, scope and
|
|
46
|
+
// sequence (dolly_device_header) and its body.
|
|
47
|
+
function dispatch({ operation: op, scope, sequence, body }) {
|
|
46
48
|
try {
|
|
47
|
-
|
|
48
|
-
packet = packet.slice();
|
|
49
|
-
const view = new DataView(packet.buffer, packet.byteOffset, packet.byteLength);
|
|
50
|
-
const op = view.getUint32(4, true), scope = view.getUint32(8, true), sequence = view.getUint32(16, true);
|
|
51
|
-
if (view.getUint32(0, true) !== A.DOLLY_AUDIO_VERSION || view.getUint32(12, true) || view.getUint32(20, true) ||
|
|
52
|
-
!scope || !sequence || view.getUint32(24, true) !== packet.length - 32 || view.getUint32(28, true)) fail(E.EINVAL);
|
|
49
|
+
const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
53
50
|
const slot = (scope - 1) % A.DOLLY_AUDIO_SLOTS;
|
|
54
51
|
let stream = streams.get(scope), result;
|
|
55
52
|
if (op === A.DOLLY_AUDIO_OPEN) {
|
|
56
|
-
if (
|
|
53
|
+
if (body.length) fail(E.EINVAL);
|
|
57
54
|
if (scope <= generations[slot]) fail(E.ESTALE);
|
|
58
55
|
if ([...streams.keys()].some(key => (key - 1) % A.DOLLY_AUDIO_SLOTS === slot)) fail(E.EBUSY);
|
|
59
56
|
if (!context) {
|
|
60
|
-
if (typeof AudioContext !== "function") fail(E.
|
|
57
|
+
if (typeof AudioContext !== "function") fail(E.ENODEV);
|
|
61
58
|
context = new AudioContext({sampleRate: A.DOLLY_AUDIO_RATE, latencyHint: "interactive"});
|
|
62
59
|
}
|
|
63
60
|
if (context.state === "closed") fail(E.EIO);
|
|
@@ -73,17 +70,17 @@ export function createAudioProvider() {
|
|
|
73
70
|
if (sequence <= stream.sequence) fail(E.ESTALE);
|
|
74
71
|
stream.sequence = sequence;
|
|
75
72
|
if (op === A.DOLLY_AUDIO_WRITE) {
|
|
76
|
-
if (
|
|
77
|
-
const frames = view.getUint32(
|
|
73
|
+
if (body.length < 8) fail(E.EINVAL);
|
|
74
|
+
const frames = view.getUint32(0, true);
|
|
78
75
|
if (frames < A.DOLLY_AUDIO_MIN_FRAMES || frames > A.DOLLY_AUDIO_MAX_FRAMES ||
|
|
79
|
-
view.getUint32(
|
|
80
|
-
for (let at =
|
|
76
|
+
view.getUint32(4, true) || body.length !== 8 + frames * 8) fail(E.EINVAL);
|
|
77
|
+
for (let at = 8; at < body.length; at += 4) if (!Number.isFinite(view.getFloat32(at, true))) fail(E.EINVAL);
|
|
81
78
|
const pending = queued(stream);
|
|
82
79
|
if (pending + frames > A.DOLLY_AUDIO_QUEUE_FRAMES || stream.nodes.size >= A.DOLLY_AUDIO_MAX_BUFFERS) fail(E.EAGAIN);
|
|
83
80
|
const buffer = context.createBuffer(A.DOLLY_AUDIO_CHANNELS, frames, A.DOLLY_AUDIO_RATE);
|
|
84
81
|
for (let channel = 0; channel < A.DOLLY_AUDIO_CHANNELS; ++channel) {
|
|
85
82
|
const data = buffer.getChannelData(channel);
|
|
86
|
-
for (let i = 0; i < frames; ++i) data[i] = Math.max(-1, Math.min(1, view.getFloat32(
|
|
83
|
+
for (let i = 0; i < frames; ++i) data[i] = Math.max(-1, Math.min(1, view.getFloat32(8 + (i * 2 + channel) * 4, true)));
|
|
87
84
|
}
|
|
88
85
|
const source = context.createBufferSource(); source.buffer = buffer;
|
|
89
86
|
source.connect(context.destination);
|
|
@@ -93,7 +90,7 @@ export function createAudioProvider() {
|
|
|
93
90
|
source.start(start); stream.end = node.end; stream.nodes.add(node);
|
|
94
91
|
result = response(4); new DataView(result.buffer).setUint32(0, frames, true);
|
|
95
92
|
} else if (op === A.DOLLY_AUDIO_STATUS) {
|
|
96
|
-
if (
|
|
93
|
+
if (body.length) fail(E.EINVAL);
|
|
97
94
|
const pending = queued(stream);
|
|
98
95
|
let played = stream.played;
|
|
99
96
|
for (const node of stream.nodes) played += Math.min(node.frames, Math.max(0, Math.floor((context.currentTime - node.start) * A.DOLLY_AUDIO_RATE)));
|
|
@@ -102,7 +99,7 @@ export function createAudioProvider() {
|
|
|
102
99
|
output.setUint32(0, pending, true); output.setUint32(4, context.state === "running" ? 1 : 2, true);
|
|
103
100
|
output.setBigUint64(8, BigInt(played), true);
|
|
104
101
|
} else if (op === A.DOLLY_AUDIO_CLOSE) {
|
|
105
|
-
if (
|
|
102
|
+
if (body.length) fail(E.EINVAL);
|
|
106
103
|
release(scope); result = response(0);
|
|
107
104
|
} else fail(E.ENOSYS);
|
|
108
105
|
}
|