@ai-ecoverse/pi-audio 1.22.0 → 1.24.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 +4 -2
- package/dist/custom-llm.js +32 -0
- package/dist/custom-llm.js.map +7 -0
- package/dist/elevenlabs-agents/index.js +2 -0
- package/dist/elevenlabs-agents/index.js.map +2 -2
- package/dist/elevenlabs-agents/provision.js +2 -8
- package/dist/elevenlabs-agents/provision.js.map +2 -2
- package/dist/fish-agents/index.js +2 -0
- package/dist/fish-agents/index.js.map +2 -2
- package/dist/fish-agents/provision.js +2 -14
- package/dist/fish-agents/provision.js.map +2 -2
- package/dist/index.js +10 -0
- package/dist/index.js.map +2 -2
- package/dist/mixer.js +337 -0
- package/dist/mixer.js.map +7 -0
- package/dist/node-host.js +132 -0
- package/dist/node-host.js.map +7 -0
- package/dist/types/custom-llm.d.ts +4 -0
- package/dist/types/elevenlabs-agents/index.d.ts +1 -0
- package/dist/types/fish-agents/index.d.ts +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/mixer.d.ts +15 -0
- package/dist/types/node-host.d.ts +34 -0
- package/dist/types/worklet/convert.d.ts +9 -6
- package/dist/types/worklet/release.d.ts +13 -0
- package/dist/types/worklet/tap.d.ts +25 -0
- package/dist/worklet/convert.js +23 -0
- package/dist/worklet/convert.js.map +2 -2
- package/dist/worklet/release.js +42 -0
- package/dist/worklet/release.js.map +7 -0
- package/dist/worklet/tap.js +97 -0
- package/dist/worklet/tap.js.map +7 -0
- package/package.json +12 -2
|
@@ -6,22 +6,25 @@ export interface AudioConvertProcessorOptions {
|
|
|
6
6
|
sampleRate?: number;
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
+
interface WorkletPort {
|
|
10
|
+
postMessage(message: unknown): void;
|
|
11
|
+
onmessage: ((event: {
|
|
12
|
+
data?: unknown;
|
|
13
|
+
}) => void) | null;
|
|
14
|
+
}
|
|
9
15
|
interface WorkletScope {
|
|
10
16
|
AudioWorkletProcessor?: new (options?: AudioConvertProcessorOptions) => {
|
|
11
|
-
port?:
|
|
12
|
-
postMessage(message: unknown): void;
|
|
13
|
-
};
|
|
17
|
+
port?: WorkletPort;
|
|
14
18
|
};
|
|
15
19
|
registerProcessor?: (name: string, processor: new (options?: AudioConvertProcessorOptions) => object) => void;
|
|
16
20
|
}
|
|
17
21
|
declare const WorkletBase: new (options?: AudioConvertProcessorOptions) => {
|
|
18
|
-
port?:
|
|
19
|
-
postMessage(message: unknown): void;
|
|
20
|
-
};
|
|
22
|
+
port?: WorkletPort;
|
|
21
23
|
};
|
|
22
24
|
export declare class AudioConvertProcessor extends WorkletBase {
|
|
23
25
|
#private;
|
|
24
26
|
constructor(options?: AudioConvertProcessorOptions);
|
|
27
|
+
release(): void;
|
|
25
28
|
process(inputs: Array<ArrayLike<Float32Array | undefined> | undefined>): boolean;
|
|
26
29
|
take(): Uint8Array[];
|
|
27
30
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const WORKLET_RELEASE = "pi-audio-release";
|
|
2
|
+
export interface WorkletPort {
|
|
3
|
+
postMessage(message: unknown): void;
|
|
4
|
+
onmessage: ((event: {
|
|
5
|
+
data?: unknown;
|
|
6
|
+
}) => void) | null;
|
|
7
|
+
}
|
|
8
|
+
export interface WorkletNodeLike {
|
|
9
|
+
disconnect(): void;
|
|
10
|
+
port: WorkletPort;
|
|
11
|
+
}
|
|
12
|
+
export declare function isWorkletRelease(data: unknown): boolean;
|
|
13
|
+
export declare function bindWorkletNode(node: WorkletNodeLike): () => void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const AUDIO_TAP_PROCESSOR = "pi-audio-tap";
|
|
2
|
+
interface TapPort {
|
|
3
|
+
postMessage(message: unknown): void;
|
|
4
|
+
onmessage: ((event: {
|
|
5
|
+
data?: unknown;
|
|
6
|
+
}) => void) | null;
|
|
7
|
+
}
|
|
8
|
+
interface WorkletScope {
|
|
9
|
+
AudioWorkletProcessor?: new () => {
|
|
10
|
+
port?: TapPort;
|
|
11
|
+
};
|
|
12
|
+
registerProcessor?: (name: string, processor: new () => object) => void;
|
|
13
|
+
}
|
|
14
|
+
interface TapInputs {
|
|
15
|
+
inputs: Array<ArrayLike<Float32Array | undefined> | undefined>;
|
|
16
|
+
outputs: Array<ArrayLike<Float32Array | undefined> | undefined>;
|
|
17
|
+
}
|
|
18
|
+
export declare function copyTap(inputs: TapInputs['inputs'], outputs: TapInputs['outputs']): void;
|
|
19
|
+
export declare function tapSamples(inputs: TapInputs['inputs']): Float32Array | undefined;
|
|
20
|
+
export declare function registerAudioTapWorklet(target?: WorkletScope): void;
|
|
21
|
+
export declare function audioTapWorkletUrl(): string;
|
|
22
|
+
export declare function loadAudioTap(worklet: {
|
|
23
|
+
addModule(module: unknown): Promise<void>;
|
|
24
|
+
}, url?: string): Promise<void>;
|
|
25
|
+
export {};
|
package/dist/worklet/convert.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { convertAudio } from "../convert.js";
|
|
2
|
+
import { isWorkletRelease, WORKLET_RELEASE } from "./release.js";
|
|
2
3
|
const AUDIO_CONVERT_PROCESSOR = "pi-audio-convert";
|
|
3
4
|
const scope = globalThis;
|
|
4
5
|
const WorkletBase = scope.AudioWorkletProcessor ?? class {
|
|
@@ -8,6 +9,8 @@ class AudioConvertProcessor extends WorkletBase {
|
|
|
8
9
|
#format;
|
|
9
10
|
#sampleRate;
|
|
10
11
|
#frames = 0;
|
|
12
|
+
#released = false;
|
|
13
|
+
#posted = false;
|
|
11
14
|
#pending = [];
|
|
12
15
|
constructor(options) {
|
|
13
16
|
super(options);
|
|
@@ -18,8 +21,21 @@ class AudioConvertProcessor extends WorkletBase {
|
|
|
18
21
|
};
|
|
19
22
|
const sampleRate = options?.processorOptions?.sampleRate;
|
|
20
23
|
this.#sampleRate = sampleRate !== void 0 && Number.isFinite(sampleRate) && sampleRate > 0 ? sampleRate : 48e3;
|
|
24
|
+
const port = this.port;
|
|
25
|
+
if (port) {
|
|
26
|
+
port.onmessage = (event) => {
|
|
27
|
+
if (isWorkletRelease(event.data)) this.release();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
release() {
|
|
32
|
+
this.#released = true;
|
|
21
33
|
}
|
|
22
34
|
process(inputs) {
|
|
35
|
+
if (this.#released) {
|
|
36
|
+
this.#finish();
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
23
39
|
const input = inputs[0];
|
|
24
40
|
const channels = [];
|
|
25
41
|
if (input) {
|
|
@@ -59,6 +75,13 @@ class AudioConvertProcessor extends WorkletBase {
|
|
|
59
75
|
});
|
|
60
76
|
return true;
|
|
61
77
|
}
|
|
78
|
+
#finish() {
|
|
79
|
+
if (this.#posted) return;
|
|
80
|
+
this.#posted = true;
|
|
81
|
+
const port = this.port;
|
|
82
|
+
if (!port) return;
|
|
83
|
+
port.postMessage({ type: WORKLET_RELEASE });
|
|
84
|
+
}
|
|
62
85
|
take() {
|
|
63
86
|
return this.#pending.splice(0);
|
|
64
87
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/worklet/convert.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AudioFormat } from '../audio.ts';\nimport { convertAudio } from '../convert.ts';\n\nexport const AUDIO_CONVERT_PROCESSOR = 'pi-audio-convert';\n\nexport interface AudioConvertProcessorOptions {\n processorOptions?: {\n format?: AudioFormat;\n sampleRate?: number;\n };\n}\n\ninterface WorkletScope {\n AudioWorkletProcessor?: new (\n options?: AudioConvertProcessorOptions\n ) => {\n port?:
|
|
5
|
-
"mappings": "AACA,SAAS,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import type { AudioFormat } from '../audio.ts';\nimport { convertAudio } from '../convert.ts';\nimport { isWorkletRelease, WORKLET_RELEASE } from './release.ts';\n\nexport const AUDIO_CONVERT_PROCESSOR = 'pi-audio-convert';\n\nexport interface AudioConvertProcessorOptions {\n processorOptions?: {\n format?: AudioFormat;\n sampleRate?: number;\n };\n}\n\ninterface WorkletPort {\n postMessage(message: unknown): void;\n onmessage: ((event: { data?: unknown }) => void) | null;\n}\n\ninterface WorkletScope {\n AudioWorkletProcessor?: new (\n options?: AudioConvertProcessorOptions\n ) => {\n port?: WorkletPort;\n };\n registerProcessor?: (\n name: string,\n processor: new (options?: AudioConvertProcessorOptions) => object\n ) => void;\n}\n\nconst scope = globalThis as WorkletScope;\n\nconst WorkletBase =\n scope.AudioWorkletProcessor ??\n class {\n port?: WorkletPort;\n };\n\nexport class AudioConvertProcessor extends WorkletBase {\n readonly #format: AudioFormat;\n readonly #sampleRate: number;\n #frames = 0;\n #released = false;\n #posted = false;\n readonly #pending: Uint8Array[] = [];\n\n constructor(options?: AudioConvertProcessorOptions) {\n super(options);\n this.#format = options?.processorOptions?.format ?? {\n encoding: 'pcm16',\n sampleRate: 24000,\n channels: 1,\n };\n const sampleRate = options?.processorOptions?.sampleRate;\n this.#sampleRate =\n sampleRate !== undefined && Number.isFinite(sampleRate) && sampleRate > 0\n ? sampleRate\n : 48000;\n const port = this.port;\n if (port) {\n port.onmessage = (event) => {\n if (isWorkletRelease(event.data)) this.release();\n };\n }\n }\n\n release(): void {\n this.#released = true;\n }\n\n process(inputs: Array<ArrayLike<Float32Array | undefined> | undefined>): boolean {\n if (this.#released) {\n this.#finish();\n return true;\n }\n const input = inputs[0];\n const channels: Float32Array[] = [];\n if (input) {\n for (let index = 0; index < input.length; index++) {\n const channel = input[index];\n if (channel) channels.push(channel);\n }\n }\n const frames = channels[0]?.length ?? 0;\n if (channels.length === 0 || frames === 0) return true;\n const count = Math.min(channels.length, 2);\n const data = new Uint8Array(frames * count * 4);\n const view = new DataView(data.buffer);\n for (let frame = 0; frame < frames; frame++) {\n for (let channel = 0; channel < count; channel++) {\n view.setFloat32((frame * count + channel) * 4, channels[channel]?.[frame] ?? 0, true);\n }\n }\n const converted = convertAudio(\n {\n data,\n format: {\n encoding: 'pcmf32',\n sampleRate: this.#sampleRate,\n channels: count === 1 ? 1 : 2,\n },\n timestamp: (this.#frames / this.#sampleRate) * 1000,\n },\n this.#format\n );\n this.#frames += frames;\n this.#pending.push(converted.data);\n this.port?.postMessage({\n data: converted.data,\n format: converted.format,\n timestamp: converted.timestamp,\n });\n return true;\n }\n\n #finish(): void {\n if (this.#posted) return;\n this.#posted = true;\n const port = this.port;\n if (!port) return;\n port.postMessage({ type: WORKLET_RELEASE });\n }\n\n take(): Uint8Array[] {\n return this.#pending.splice(0);\n }\n}\n\nexport function registerAudioConvertWorklet(target: WorkletScope = scope): void {\n if (typeof target.registerProcessor !== 'function') return;\n target.registerProcessor(AUDIO_CONVERT_PROCESSOR, AudioConvertProcessor);\n}\n\nexport function audioConvertWorkletUrl(): string {\n return new URL(import.meta.url).href;\n}\n\nregisterAudioConvertWorklet();\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB,uBAAuB;AAE3C,MAAM,0BAA0B;AA0BvC,MAAM,QAAQ;AAEd,MAAM,cACJ,MAAM,yBACN,MAAM;AAAA,EACJ;AACF;AAEK,MAAM,8BAA8B,YAAY;AAAA,EAC5C;AAAA,EACA;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AAAA,EACD,WAAyB,CAAC;AAAA,EAEnC,YAAY,SAAwC;AAClD,UAAM,OAAO;AACb,SAAK,UAAU,SAAS,kBAAkB,UAAU;AAAA,MAClD,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AACA,UAAM,aAAa,SAAS,kBAAkB;AAC9C,SAAK,cACH,eAAe,UAAa,OAAO,SAAS,UAAU,KAAK,aAAa,IACpE,aACA;AACN,UAAM,OAAO,KAAK;AAClB,QAAI,MAAM;AACR,WAAK,YAAY,CAAC,UAAU;AAC1B,YAAI,iBAAiB,MAAM,IAAI,EAAG,MAAK,QAAQ;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAQ,QAAyE;AAC/E,QAAI,KAAK,WAAW;AAClB,WAAK,QAAQ;AACb,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,WAA2B,CAAC;AAClC,QAAI,OAAO;AACT,eAAS,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;AACjD,cAAM,UAAU,MAAM,KAAK;AAC3B,YAAI,QAAS,UAAS,KAAK,OAAO;AAAA,MACpC;AAAA,IACF;AACA,UAAM,SAAS,SAAS,CAAC,GAAG,UAAU;AACtC,QAAI,SAAS,WAAW,KAAK,WAAW,EAAG,QAAO;AAClD,UAAM,QAAQ,KAAK,IAAI,SAAS,QAAQ,CAAC;AACzC,UAAM,OAAO,IAAI,WAAW,SAAS,QAAQ,CAAC;AAC9C,UAAM,OAAO,IAAI,SAAS,KAAK,MAAM;AACrC,aAAS,QAAQ,GAAG,QAAQ,QAAQ,SAAS;AAC3C,eAAS,UAAU,GAAG,UAAU,OAAO,WAAW;AAChD,aAAK,YAAY,QAAQ,QAAQ,WAAW,GAAG,SAAS,OAAO,IAAI,KAAK,KAAK,GAAG,IAAI;AAAA,MACtF;AAAA,IACF;AACA,UAAM,YAAY;AAAA,MAChB;AAAA,QACE;AAAA,QACA,QAAQ;AAAA,UACN,UAAU;AAAA,UACV,YAAY,KAAK;AAAA,UACjB,UAAU,UAAU,IAAI,IAAI;AAAA,QAC9B;AAAA,QACA,WAAY,KAAK,UAAU,KAAK,cAAe;AAAA,MACjD;AAAA,MACA,KAAK;AAAA,IACP;AACA,SAAK,WAAW;AAChB,SAAK,SAAS,KAAK,UAAU,IAAI;AACjC,SAAK,MAAM,YAAY;AAAA,MACrB,MAAM,UAAU;AAAA,MAChB,QAAQ,UAAU;AAAA,MAClB,WAAW,UAAU;AAAA,IACvB,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM;AACX,SAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAAA,EAC5C;AAAA,EAEA,OAAqB;AACnB,WAAO,KAAK,SAAS,OAAO,CAAC;AAAA,EAC/B;AACF;AAEO,SAAS,4BAA4B,SAAuB,OAAa;AAC9E,MAAI,OAAO,OAAO,sBAAsB,WAAY;AACpD,SAAO,kBAAkB,yBAAyB,qBAAqB;AACzE;AAEO,SAAS,yBAAiC;AAC/C,SAAO,IAAI,IAAI,YAAY,GAAG,EAAE;AAClC;AAEA,4BAA4B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const WORKLET_RELEASE = "pi-audio-release";
|
|
2
|
+
function isWorkletRelease(data) {
|
|
3
|
+
if (typeof data !== "object" || data === null) return false;
|
|
4
|
+
if (!("type" in data)) return false;
|
|
5
|
+
return data.type === WORKLET_RELEASE;
|
|
6
|
+
}
|
|
7
|
+
function bindWorkletNode(node) {
|
|
8
|
+
let disconnected = false;
|
|
9
|
+
const disconnect = () => {
|
|
10
|
+
if (disconnected) return;
|
|
11
|
+
disconnected = true;
|
|
12
|
+
node.disconnect();
|
|
13
|
+
closePort(node.port);
|
|
14
|
+
};
|
|
15
|
+
const previous = node.port.onmessage;
|
|
16
|
+
node.port.onmessage = (event) => {
|
|
17
|
+
if (previous) previous(event);
|
|
18
|
+
if (isWorkletRelease(event.data)) disconnect();
|
|
19
|
+
};
|
|
20
|
+
let stopped = false;
|
|
21
|
+
return () => {
|
|
22
|
+
if (stopped) return;
|
|
23
|
+
stopped = true;
|
|
24
|
+
node.port.postMessage({ type: WORKLET_RELEASE });
|
|
25
|
+
disconnect();
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function closePort(port) {
|
|
29
|
+
const closer = port.close;
|
|
30
|
+
if (typeof closer !== "function") return;
|
|
31
|
+
try {
|
|
32
|
+
closer.call(port);
|
|
33
|
+
} catch {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
WORKLET_RELEASE,
|
|
39
|
+
bindWorkletNode,
|
|
40
|
+
isWorkletRelease
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=release.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/worklet/release.ts"],
|
|
4
|
+
"sourcesContent": ["export const WORKLET_RELEASE = 'pi-audio-release';\n\nexport interface WorkletPort {\n postMessage(message: unknown): void;\n onmessage: ((event: { data?: unknown }) => void) | null;\n}\n\nexport interface WorkletNodeLike {\n disconnect(): void;\n port: WorkletPort;\n}\n\nexport function isWorkletRelease(data: unknown): boolean {\n if (typeof data !== 'object' || data === null) return false;\n if (!('type' in data)) return false;\n return data.type === WORKLET_RELEASE;\n}\n\nexport function bindWorkletNode(node: WorkletNodeLike): () => void {\n let disconnected = false;\n const disconnect = () => {\n if (disconnected) return;\n disconnected = true;\n node.disconnect();\n closePort(node.port);\n };\n const previous = node.port.onmessage;\n node.port.onmessage = (event) => {\n if (previous) previous(event);\n if (isWorkletRelease(event.data)) disconnect();\n };\n let stopped = false;\n return () => {\n if (stopped) return;\n stopped = true;\n node.port.postMessage({ type: WORKLET_RELEASE });\n disconnect();\n };\n}\n\nfunction closePort(port: WorkletPort): void {\n const closer = (port as WorkletPort & { close?: () => void }).close;\n if (typeof closer !== 'function') return;\n try {\n closer.call(port);\n } catch {\n return;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,kBAAkB;AAYxB,SAAS,iBAAiB,MAAwB;AACvD,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,MAAI,EAAE,UAAU,MAAO,QAAO;AAC9B,SAAO,KAAK,SAAS;AACvB;AAEO,SAAS,gBAAgB,MAAmC;AACjE,MAAI,eAAe;AACnB,QAAM,aAAa,MAAM;AACvB,QAAI,aAAc;AAClB,mBAAe;AACf,SAAK,WAAW;AAChB,cAAU,KAAK,IAAI;AAAA,EACrB;AACA,QAAM,WAAW,KAAK,KAAK;AAC3B,OAAK,KAAK,YAAY,CAAC,UAAU;AAC/B,QAAI,SAAU,UAAS,KAAK;AAC5B,QAAI,iBAAiB,MAAM,IAAI,EAAG,YAAW;AAAA,EAC/C;AACA,MAAI,UAAU;AACd,SAAO,MAAM;AACX,QAAI,QAAS;AACb,cAAU;AACV,SAAK,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC/C,eAAW;AAAA,EACb;AACF;AAEA,SAAS,UAAU,MAAyB;AAC1C,QAAM,SAAU,KAA8C;AAC9D,MAAI,OAAO,WAAW,WAAY;AAClC,MAAI;AACF,WAAO,KAAK,IAAI;AAAA,EAClB,QAAQ;AACN;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { isWorkletRelease, WORKLET_RELEASE } from "./release.js";
|
|
2
|
+
const AUDIO_TAP_PROCESSOR = "pi-audio-tap";
|
|
3
|
+
const scope = globalThis;
|
|
4
|
+
const registered = /* @__PURE__ */ new WeakSet();
|
|
5
|
+
function copyTap(inputs, outputs) {
|
|
6
|
+
const input = inputs[0] ?? [];
|
|
7
|
+
const output = outputs[0] ?? [];
|
|
8
|
+
for (let channel = 0; channel < output.length; channel += 1) {
|
|
9
|
+
const dst = output[channel];
|
|
10
|
+
if (dst === void 0) continue;
|
|
11
|
+
writeChannel(dst, input[channel] ?? input[0]);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function tapSamples(inputs) {
|
|
15
|
+
const heard = inputs[0]?.[0];
|
|
16
|
+
if (heard === void 0 || heard.length === 0) return void 0;
|
|
17
|
+
const copy = new Float32Array(heard.length);
|
|
18
|
+
copy.set(heard);
|
|
19
|
+
return copy;
|
|
20
|
+
}
|
|
21
|
+
function registerAudioTapWorklet(target = scope) {
|
|
22
|
+
if (typeof target.registerProcessor !== "function") return;
|
|
23
|
+
if (registered.has(target)) return;
|
|
24
|
+
registered.add(target);
|
|
25
|
+
const Base = target.AudioWorkletProcessor ?? class {
|
|
26
|
+
port;
|
|
27
|
+
};
|
|
28
|
+
const processor = class AudioTapProcessor extends Base {
|
|
29
|
+
released = false;
|
|
30
|
+
#posted = false;
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
const port = this.port;
|
|
34
|
+
if (port) {
|
|
35
|
+
port.onmessage = (event) => {
|
|
36
|
+
if (isWorkletRelease(event.data)) this.released = true;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
process(inputs, outputs) {
|
|
41
|
+
if (this.released) {
|
|
42
|
+
this.#finish();
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
copyTap(inputs, outputs);
|
|
46
|
+
const samples = tapSamples(inputs);
|
|
47
|
+
if (samples) this.port?.postMessage({ samples });
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
#finish() {
|
|
51
|
+
if (this.#posted) return;
|
|
52
|
+
this.#posted = true;
|
|
53
|
+
const port = this.port;
|
|
54
|
+
if (!port) return;
|
|
55
|
+
port.postMessage({ type: WORKLET_RELEASE });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
target.registerProcessor(AUDIO_TAP_PROCESSOR, processor);
|
|
59
|
+
}
|
|
60
|
+
function audioTapWorkletUrl() {
|
|
61
|
+
return new URL(import.meta.url).href;
|
|
62
|
+
}
|
|
63
|
+
async function loadAudioTap(worklet, url = audioTapWorkletUrl()) {
|
|
64
|
+
try {
|
|
65
|
+
await worklet.addModule(url);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
try {
|
|
68
|
+
await worklet.addModule((next) => {
|
|
69
|
+
registerAudioTapWorklet(next);
|
|
70
|
+
});
|
|
71
|
+
} catch {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function writeChannel(dst, src) {
|
|
77
|
+
if (src === void 0) {
|
|
78
|
+
dst.fill(0);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (src.length === dst.length) {
|
|
82
|
+
dst.set(src);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const count = Math.min(src.length, dst.length);
|
|
86
|
+
for (let frame = 0; frame < count; frame += 1) dst[frame] = src[frame];
|
|
87
|
+
}
|
|
88
|
+
registerAudioTapWorklet();
|
|
89
|
+
export {
|
|
90
|
+
AUDIO_TAP_PROCESSOR,
|
|
91
|
+
audioTapWorkletUrl,
|
|
92
|
+
copyTap,
|
|
93
|
+
loadAudioTap,
|
|
94
|
+
registerAudioTapWorklet,
|
|
95
|
+
tapSamples
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=tap.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/worklet/tap.ts"],
|
|
4
|
+
"sourcesContent": ["import { isWorkletRelease, WORKLET_RELEASE } from './release.ts';\n\nexport const AUDIO_TAP_PROCESSOR = 'pi-audio-tap';\n\ninterface TapPort {\n postMessage(message: unknown): void;\n onmessage: ((event: { data?: unknown }) => void) | null;\n}\n\ninterface WorkletScope {\n AudioWorkletProcessor?: new () => { port?: TapPort };\n registerProcessor?: (name: string, processor: new () => object) => void;\n}\n\ninterface TapInputs {\n inputs: Array<ArrayLike<Float32Array | undefined> | undefined>;\n outputs: Array<ArrayLike<Float32Array | undefined> | undefined>;\n}\n\nconst scope = globalThis as WorkletScope;\nconst registered = new WeakSet<object>();\n\nexport function copyTap(inputs: TapInputs['inputs'], outputs: TapInputs['outputs']): void {\n const input = inputs[0] ?? [];\n const output = outputs[0] ?? [];\n for (let channel = 0; channel < output.length; channel += 1) {\n const dst = output[channel];\n if (dst === undefined) continue;\n writeChannel(dst, input[channel] ?? input[0]);\n }\n}\n\nexport function tapSamples(inputs: TapInputs['inputs']): Float32Array | undefined {\n const heard = inputs[0]?.[0];\n if (heard === undefined || heard.length === 0) return undefined;\n const copy = new Float32Array(heard.length);\n copy.set(heard);\n return copy;\n}\n\nexport function registerAudioTapWorklet(target: WorkletScope = scope): void {\n if (typeof target.registerProcessor !== 'function') return;\n if (registered.has(target)) return;\n registered.add(target);\n const Base =\n target.AudioWorkletProcessor ??\n class {\n port?: TapPort;\n };\n const processor = class AudioTapProcessor extends Base {\n released = false;\n #posted = false;\n\n constructor() {\n super();\n const port = this.port;\n if (port) {\n port.onmessage = (event) => {\n if (isWorkletRelease(event.data)) this.released = true;\n };\n }\n }\n\n process(inputs: TapInputs['inputs'], outputs: TapInputs['outputs']): boolean {\n if (this.released) {\n this.#finish();\n return true;\n }\n copyTap(inputs, outputs);\n const samples = tapSamples(inputs);\n if (samples) this.port?.postMessage({ samples });\n return true;\n }\n\n #finish(): void {\n if (this.#posted) return;\n this.#posted = true;\n const port = this.port;\n if (!port) return;\n port.postMessage({ type: WORKLET_RELEASE });\n }\n };\n target.registerProcessor(AUDIO_TAP_PROCESSOR, processor);\n}\n\nexport function audioTapWorkletUrl(): string {\n return new URL(import.meta.url).href;\n}\n\nexport async function loadAudioTap(\n worklet: { addModule(module: unknown): Promise<void> },\n url = audioTapWorkletUrl()\n): Promise<void> {\n try {\n await worklet.addModule(url);\n } catch (error) {\n try {\n await worklet.addModule((next: WorkletScope) => {\n registerAudioTapWorklet(next);\n });\n } catch {\n throw error;\n }\n }\n}\n\nfunction writeChannel(dst: Float32Array, src: Float32Array | undefined): void {\n if (src === undefined) {\n dst.fill(0);\n return;\n }\n if (src.length === dst.length) {\n dst.set(src);\n return;\n }\n const count = Math.min(src.length, dst.length);\n for (let frame = 0; frame < count; frame += 1) dst[frame] = src[frame] as number;\n}\n\nregisterAudioTapWorklet();\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB,uBAAuB;AAE3C,MAAM,sBAAsB;AAiBnC,MAAM,QAAQ;AACd,MAAM,aAAa,oBAAI,QAAgB;AAEhC,SAAS,QAAQ,QAA6B,SAAqC;AACxF,QAAM,QAAQ,OAAO,CAAC,KAAK,CAAC;AAC5B,QAAM,SAAS,QAAQ,CAAC,KAAK,CAAC;AAC9B,WAAS,UAAU,GAAG,UAAU,OAAO,QAAQ,WAAW,GAAG;AAC3D,UAAM,MAAM,OAAO,OAAO;AAC1B,QAAI,QAAQ,OAAW;AACvB,iBAAa,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC,CAAC;AAAA,EAC9C;AACF;AAEO,SAAS,WAAW,QAAuD;AAChF,QAAM,QAAQ,OAAO,CAAC,IAAI,CAAC;AAC3B,MAAI,UAAU,UAAa,MAAM,WAAW,EAAG,QAAO;AACtD,QAAM,OAAO,IAAI,aAAa,MAAM,MAAM;AAC1C,OAAK,IAAI,KAAK;AACd,SAAO;AACT;AAEO,SAAS,wBAAwB,SAAuB,OAAa;AAC1E,MAAI,OAAO,OAAO,sBAAsB,WAAY;AACpD,MAAI,WAAW,IAAI,MAAM,EAAG;AAC5B,aAAW,IAAI,MAAM;AACrB,QAAM,OACJ,OAAO,yBACP,MAAM;AAAA,IACJ;AAAA,EACF;AACF,QAAM,YAAY,MAAM,0BAA0B,KAAK;AAAA,IACrD,WAAW;AAAA,IACX,UAAU;AAAA,IAEV,cAAc;AACZ,YAAM;AACN,YAAM,OAAO,KAAK;AAClB,UAAI,MAAM;AACR,aAAK,YAAY,CAAC,UAAU;AAC1B,cAAI,iBAAiB,MAAM,IAAI,EAAG,MAAK,WAAW;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAAA,IAEA,QAAQ,QAA6B,SAAwC;AAC3E,UAAI,KAAK,UAAU;AACjB,aAAK,QAAQ;AACb,eAAO;AAAA,MACT;AACA,cAAQ,QAAQ,OAAO;AACvB,YAAM,UAAU,WAAW,MAAM;AACjC,UAAI,QAAS,MAAK,MAAM,YAAY,EAAE,QAAQ,CAAC;AAC/C,aAAO;AAAA,IACT;AAAA,IAEA,UAAgB;AACd,UAAI,KAAK,QAAS;AAClB,WAAK,UAAU;AACf,YAAM,OAAO,KAAK;AAClB,UAAI,CAAC,KAAM;AACX,WAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,SAAO,kBAAkB,qBAAqB,SAAS;AACzD;AAEO,SAAS,qBAA6B;AAC3C,SAAO,IAAI,IAAI,YAAY,GAAG,EAAE;AAClC;AAEA,eAAsB,aACpB,SACA,MAAM,mBAAmB,GACV;AACf,MAAI;AACF,UAAM,QAAQ,UAAU,GAAG;AAAA,EAC7B,SAAS,OAAO;AACd,QAAI;AACF,YAAM,QAAQ,UAAU,CAAC,SAAuB;AAC9C,gCAAwB,IAAI;AAAA,MAC9B,CAAC;AAAA,IACH,QAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,aAAa,KAAmB,KAAqC;AAC5E,MAAI,QAAQ,QAAW;AACrB,QAAI,KAAK,CAAC;AACV;AAAA,EACF;AACA,MAAI,IAAI,WAAW,IAAI,QAAQ;AAC7B,QAAI,IAAI,GAAG;AACX;AAAA,EACF;AACA,QAAM,QAAQ,KAAK,IAAI,IAAI,QAAQ,IAAI,MAAM;AAC7C,WAAS,QAAQ,GAAG,QAAQ,OAAO,SAAS,EAAG,KAAI,KAAK,IAAI,IAAI,KAAK;AACvE;AAEA,wBAAwB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/pi-audio",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "Voice bindings for pi: speech-to-text, text-to-speech and realtime speech for pi-coding-agent plugins and slicc-agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -105,6 +105,8 @@
|
|
|
105
105
|
"@biomejs/biome": "2.5.15",
|
|
106
106
|
"@earendil-works/pi-ai": "1.1.0",
|
|
107
107
|
"@earendil-works/pi-durable": "1.1.0",
|
|
108
|
+
"node-web-audio-api": "2.2.0",
|
|
109
|
+
"web-audio-api": "1.6.0",
|
|
108
110
|
"@types/node": "24.19.1",
|
|
109
111
|
"esbuild": "0.28.2",
|
|
110
112
|
"lefthook": "2.2.1",
|
|
@@ -114,11 +116,19 @@
|
|
|
114
116
|
},
|
|
115
117
|
"peerDependencies": {
|
|
116
118
|
"@earendil-works/pi-ai": "1.1.0",
|
|
117
|
-
"@earendil-works/pi-durable": "^1.1.0"
|
|
119
|
+
"@earendil-works/pi-durable": "^1.1.0",
|
|
120
|
+
"node-web-audio-api": "^2.2.0",
|
|
121
|
+
"web-audio-api": "^1.6.0"
|
|
118
122
|
},
|
|
119
123
|
"peerDependenciesMeta": {
|
|
120
124
|
"@earendil-works/pi-durable": {
|
|
121
125
|
"optional": true
|
|
126
|
+
},
|
|
127
|
+
"node-web-audio-api": {
|
|
128
|
+
"optional": true
|
|
129
|
+
},
|
|
130
|
+
"web-audio-api": {
|
|
131
|
+
"optional": true
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
}
|