@daugasauron/dolly-host-audio 0.1.4-rc.3 → 0.1.5-rc.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/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 = "bf9f97c6945174f9788f03091ca6e1c982f5b8c80488c1b5526804ceb84a1046";
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.packet);
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.4-rc.3",
2
+ "version": "0.1.5-rc.0",
3
3
  "type": "module",
4
4
  "name": "@daugasauron/dolly-host-audio",
5
5
  "license": "MIT",
6
6
  "dependencies": {
7
- "@daugasauron/dolly": "0.1.4-rc.3"
7
+ "@daugasauron/dolly": "0.1.5-rc.0"
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
- function dispatch(packet) {
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
- if (!(packet instanceof Uint8Array) || packet.byteLength < 32 || packet.byteLength > A.DOLLY_AUDIO_PACKET_BYTES) fail(E.EINVAL);
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 (packet.length !== 32) fail(E.EINVAL);
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.ENOSYS);
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 (packet.length < 40) fail(E.EINVAL);
77
- const frames = view.getUint32(32, true);
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(36, true) || packet.length !== 40 + frames * 8) fail(E.EINVAL);
80
- for (let at = 40; at < packet.length; at += 4) if (!Number.isFinite(view.getFloat32(at, true))) fail(E.EINVAL);
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(40 + (i * 2 + channel) * 4, true)));
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 (packet.length !== 32) fail(E.EINVAL);
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 (packet.length !== 32) fail(E.EINVAL);
102
+ if (body.length) fail(E.EINVAL);
106
103
  release(scope); result = response(0);
107
104
  } else fail(E.ENOSYS);
108
105
  }