@mudah-cli/audio 0.5.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/dist/backends/native.d.ts +15 -0
- package/dist/backends/native.js +52 -0
- package/dist/backends/native.js.map +1 -0
- package/dist/backends/silent.d.ts +7 -0
- package/dist/backends/silent.js +9 -0
- package/dist/backends/silent.js.map +1 -0
- package/dist/backends/spawn-command.d.ts +10 -0
- package/dist/backends/spawn-command.js +23 -0
- package/dist/backends/spawn-command.js.map +1 -0
- package/dist/backends/spawn.d.ts +14 -0
- package/dist/backends/spawn.js +48 -0
- package/dist/backends/spawn.js.map +1 -0
- package/dist/detect.d.ts +10 -0
- package/dist/detect.js +52 -0
- package/dist/detect.js.map +1 -0
- package/dist/device.d.ts +35 -0
- package/dist/device.js +172 -0
- package/dist/device.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wav.d.ts +7 -0
- package/dist/wav.js +76 -0
- package/dist/wav.js.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AudioBackend, AudioFormat } from '../types.js';
|
|
2
|
+
export declare class NativeBackend implements AudioBackend {
|
|
3
|
+
readonly kind: 'native';
|
|
4
|
+
private readonly rt;
|
|
5
|
+
private closed;
|
|
6
|
+
private constructor();
|
|
7
|
+
static tryOpen(options: {
|
|
8
|
+
sampleRate: number;
|
|
9
|
+
channels: number;
|
|
10
|
+
format: AudioFormat;
|
|
11
|
+
framesPerBuffer: number;
|
|
12
|
+
}): Promise<NativeBackend | undefined>;
|
|
13
|
+
write(bytes: Uint8Array): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export class NativeBackend {
|
|
2
|
+
kind = 'native';
|
|
3
|
+
rt;
|
|
4
|
+
closed = false;
|
|
5
|
+
constructor(rt) {
|
|
6
|
+
this.rt = rt;
|
|
7
|
+
}
|
|
8
|
+
static async tryOpen(options) {
|
|
9
|
+
let mod;
|
|
10
|
+
try {
|
|
11
|
+
mod = (await import('audify'));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (typeof mod.RtAudio !== 'function' || mod.RtAudioFormat === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
try {
|
|
19
|
+
const rt = new mod.RtAudio();
|
|
20
|
+
const format = options.format === 'f32' ? mod.RtAudioFormat.RTAUDIO_FLOAT32 : mod.RtAudioFormat.RTAUDIO_SINT16;
|
|
21
|
+
rt.openStream({ deviceId: rt.getDefaultOutputDevice(), nChannels: options.channels, firstChannel: 0 }, null, format, options.sampleRate, options.framesPerBuffer, 'mudah-audio');
|
|
22
|
+
rt.start();
|
|
23
|
+
return new NativeBackend(rt);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
write(bytes) {
|
|
30
|
+
if (this.closed)
|
|
31
|
+
return;
|
|
32
|
+
this.rt.write(Buffer.from(bytes));
|
|
33
|
+
}
|
|
34
|
+
dispose() {
|
|
35
|
+
if (this.closed)
|
|
36
|
+
return;
|
|
37
|
+
this.closed = true;
|
|
38
|
+
try {
|
|
39
|
+
this.rt.stop();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
/* already stopped */
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
this.rt.closeStream();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* already closed */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../../src/backends/native.ts"],"names":[],"mappings":"AAuBA,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,QAAiB,CAAC;IACjB,EAAE,CAAc;IACzB,MAAM,GAAG,KAAK,CAAC;IAEvB,YAAoB,EAAe;QACjC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAKpB;QACC,IAAI,GAAiB,CAAC;QACtB,IAAI,CAAC;YACH,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAA4B,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,IAAI,GAAG,CAAC,aAAa,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAE3F,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAC7B,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,cAAc,CAAC;YAClG,EAAE,CAAC,UAAU,CACX,EAAE,QAAQ,EAAE,EAAE,CAAC,sBAAsB,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,EAAE,EACvF,IAAI,EACJ,MAAM,EACN,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,eAAe,EACvB,aAAa,CACd,CAAC;YACF,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,aAAa,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAiB;QACrB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACpC,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;QACtB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"silent.js","sourceRoot":"","sources":["../../src/backends/silent.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,QAAiB,CAAC;IAClC,YAAY,GAAG,CAAC,CAAC;IAEjB,KAAK,CAAC,KAAiB;QACrB,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC,UAAU,CAAC;IACxC,CAAC;IAED,OAAO,KAAU,CAAC;CACnB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SpawnTool } from '../types.js';
|
|
2
|
+
export interface SpawnTarget {
|
|
3
|
+
readonly sampleRate: number;
|
|
4
|
+
readonly channels: number;
|
|
5
|
+
}
|
|
6
|
+
/** argv for a raw s16le stdin player. Tests assert this without opening a device. */
|
|
7
|
+
export declare function spawnCommand(tool: SpawnTool, target: SpawnTarget): {
|
|
8
|
+
command: string;
|
|
9
|
+
args: string[];
|
|
10
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** argv for a raw s16le stdin player. Tests assert this without opening a device. */
|
|
2
|
+
export function spawnCommand(tool, target) {
|
|
3
|
+
const rate = String(target.sampleRate);
|
|
4
|
+
const channels = String(target.channels);
|
|
5
|
+
switch (tool) {
|
|
6
|
+
case 'pw-play':
|
|
7
|
+
return {
|
|
8
|
+
command: 'pw-play',
|
|
9
|
+
args: ['--raw', `--rate=${rate}`, `--channels=${channels}`, '--format=s16', '-'],
|
|
10
|
+
};
|
|
11
|
+
case 'paplay':
|
|
12
|
+
return {
|
|
13
|
+
command: 'paplay',
|
|
14
|
+
args: ['--raw', `--rate=${rate}`, `--channels=${channels}`, '--format=s16le', '-'],
|
|
15
|
+
};
|
|
16
|
+
case 'aplay':
|
|
17
|
+
return {
|
|
18
|
+
command: 'aplay',
|
|
19
|
+
args: ['-t', 'raw', '-f', 'S16_LE', '-c', channels, '-r', rate, '-'],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=spawn-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-command.js","sourceRoot":"","sources":["../../src/backends/spawn-command.ts"],"names":[],"mappings":"AAOA,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,IAAe,EAAE,MAAmB;IAC/D,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO;gBACL,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,EAAE,cAAc,QAAQ,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC;aACjF,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,EAAE,cAAc,QAAQ,EAAE,EAAE,gBAAgB,EAAE,GAAG,CAAC;aACnF,CAAC;QACJ,KAAK,OAAO;YACV,OAAO;gBACL,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC;aACrE,CAAC;IACN,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AudioBackend, SpawnFn, SpawnTool } from '../types.js';
|
|
2
|
+
export declare class SpawnBackend implements AudioBackend {
|
|
3
|
+
readonly kind: 'spawn';
|
|
4
|
+
readonly spawnTool: SpawnTool;
|
|
5
|
+
private readonly child;
|
|
6
|
+
private readonly onUnderrun;
|
|
7
|
+
private dead;
|
|
8
|
+
constructor(tool: SpawnTool, target: {
|
|
9
|
+
sampleRate: number;
|
|
10
|
+
channels: number;
|
|
11
|
+
}, spawnFn: SpawnFn | undefined, onUnderrun?: () => void);
|
|
12
|
+
write(bytes: Uint8Array): void;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
2
|
+
import { spawnCommand } from './spawn-command.js';
|
|
3
|
+
export class SpawnBackend {
|
|
4
|
+
kind = 'spawn';
|
|
5
|
+
spawnTool;
|
|
6
|
+
child;
|
|
7
|
+
onUnderrun;
|
|
8
|
+
dead = false;
|
|
9
|
+
constructor(tool, target, spawnFn, onUnderrun) {
|
|
10
|
+
this.spawnTool = tool;
|
|
11
|
+
this.onUnderrun = onUnderrun;
|
|
12
|
+
const { command, args } = spawnCommand(tool, target);
|
|
13
|
+
const run = spawnFn ?? defaultSpawn;
|
|
14
|
+
this.child = run(command, args, { stdio: ['pipe', 'ignore', 'ignore'] });
|
|
15
|
+
this.child.on?.('exit', () => {
|
|
16
|
+
this.dead = true;
|
|
17
|
+
});
|
|
18
|
+
this.child.on?.('error', () => {
|
|
19
|
+
this.dead = true;
|
|
20
|
+
this.onUnderrun?.();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
write(bytes) {
|
|
24
|
+
if (this.dead || this.child.stdin === null) {
|
|
25
|
+
this.onUnderrun?.();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
this.child.stdin.write(bytes);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
this.dead = true;
|
|
33
|
+
this.onUnderrun?.();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
dispose() {
|
|
37
|
+
this.dead = true;
|
|
38
|
+
try {
|
|
39
|
+
this.child.stdin?.end();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
/* already closed */
|
|
43
|
+
}
|
|
44
|
+
this.child.kill();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const defaultSpawn = (command, args, options) => nodeSpawn(command, [...args], options);
|
|
48
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/backends/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,OAAO,YAAY;IACd,IAAI,GAAG,OAAgB,CAAC;IACxB,SAAS,CAAY;IACb,KAAK,CAAgB;IACrB,UAAU,CAA2B;IAC9C,IAAI,GAAG,KAAK,CAAC;IAErB,YACE,IAAe,EACf,MAAgD,EAChD,OAA4B,EAC5B,UAAuB;QAEvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,OAAO,IAAI,YAAY,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACjB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAiB;QACrB,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YAC3C,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACjB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;QACtB,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;CACF;AAED,MAAM,YAAY,GAAY,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CACvD,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,OAAO,CAAkB,CAAC"}
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AudioDetection, SpawnTool } from './types.js';
|
|
2
|
+
export declare function silentRequested(env?: NodeJS.ProcessEnv): boolean;
|
|
3
|
+
export declare function canResolveAudify(): boolean;
|
|
4
|
+
export declare function pathDirsFrom(env?: NodeJS.ProcessEnv, override?: readonly string[]): readonly string[];
|
|
5
|
+
export declare function findSpawnTool(dirs?: readonly string[]): SpawnTool | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Probe what `AudioOut.open({ backend: 'auto' })` would pick, without opening a stream.
|
|
8
|
+
* Native is "audify is resolvable", not "the addon loaded".
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectAudio(env?: NodeJS.ProcessEnv, pathDirs?: readonly string[]): AudioDetection;
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const requireFromHere = createRequire(fileURLToPath(import.meta.url));
|
|
6
|
+
const SPAWN_TOOLS = ['pw-play', 'paplay', 'aplay'];
|
|
7
|
+
export function silentRequested(env = process.env) {
|
|
8
|
+
if (env['MUDAH_NO_AUDIO'] === '1')
|
|
9
|
+
return true;
|
|
10
|
+
const ci = env['CI'];
|
|
11
|
+
return ci !== undefined && ci !== '' && ci !== 'false';
|
|
12
|
+
}
|
|
13
|
+
export function canResolveAudify() {
|
|
14
|
+
try {
|
|
15
|
+
requireFromHere.resolve('audify');
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function pathDirsFrom(env = process.env, override) {
|
|
23
|
+
if (override !== undefined)
|
|
24
|
+
return override;
|
|
25
|
+
return (env['PATH'] ?? '').split(':').filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
export function findSpawnTool(dirs = pathDirsFrom()) {
|
|
28
|
+
for (const tool of SPAWN_TOOLS) {
|
|
29
|
+
for (const dir of dirs) {
|
|
30
|
+
if (existsSync(join(dir, tool)))
|
|
31
|
+
return tool;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Probe what `AudioOut.open({ backend: 'auto' })` would pick, without opening a stream.
|
|
38
|
+
* Native is "audify is resolvable", not "the addon loaded".
|
|
39
|
+
*/
|
|
40
|
+
export function detectAudio(env = process.env, pathDirs) {
|
|
41
|
+
const spawnTool = findSpawnTool(pathDirsFrom(env, pathDirs));
|
|
42
|
+
const native = canResolveAudify();
|
|
43
|
+
if (silentRequested(env)) {
|
|
44
|
+
return { backend: 'silent', native, spawnTool };
|
|
45
|
+
}
|
|
46
|
+
if (native)
|
|
47
|
+
return { backend: 'native', native, spawnTool };
|
|
48
|
+
if (spawnTool !== undefined)
|
|
49
|
+
return { backend: 'spawn', native, spawnTool };
|
|
50
|
+
return { backend: 'silent', native, spawnTool };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtE,MAAM,WAAW,GAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAEzE,MAAM,UAAU,eAAe,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG;IAClE,IAAI,GAAG,CAAC,gBAAgB,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAE,QAA4B;IAC7F,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAI,GAAsB,YAAY,EAAE;IACpE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,GAAG,GAAsB,OAAO,CAAC,GAAG,EACpC,QAA4B;IAE5B,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC5D,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC5E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAClD,CAAC"}
|
package/dist/device.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AudioBackendKind, AudioFormat, AudioOutOptions, PlaySource, SpawnTool } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* PCM output through the OS mixer. Kitty cannot carry audio; this never
|
|
4
|
+
* writes to the terminal.
|
|
5
|
+
*
|
|
6
|
+
* Apps that never import `@mudah-cli/audio` never load a backend.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AudioOut {
|
|
9
|
+
readonly sampleRate: number;
|
|
10
|
+
readonly channels: number;
|
|
11
|
+
readonly format: AudioFormat;
|
|
12
|
+
readonly backendKind: Exclude<AudioBackendKind, 'auto'>;
|
|
13
|
+
readonly spawnTool: SpawnTool | undefined;
|
|
14
|
+
bytesWritten: number;
|
|
15
|
+
private readonly backend;
|
|
16
|
+
private readonly clips;
|
|
17
|
+
private wroteOnce;
|
|
18
|
+
private disposed;
|
|
19
|
+
private constructor();
|
|
20
|
+
static open(options?: AudioOutOptions): Promise<AudioOut>;
|
|
21
|
+
/**
|
|
22
|
+
* Push one interleaved buffer. Caller owns timing. Underrun does not throw.
|
|
23
|
+
*/
|
|
24
|
+
write(pcm: Int16Array | Float32Array): void;
|
|
25
|
+
/**
|
|
26
|
+
* One-shot clip. Mixes into later `write()` calls once the stream is pumping.
|
|
27
|
+
* Before the first `write()`, the samples go to the device immediately.
|
|
28
|
+
*/
|
|
29
|
+
play(source: PlaySource): Promise<void>;
|
|
30
|
+
dispose(): void;
|
|
31
|
+
private normalize;
|
|
32
|
+
private toDeviceS16;
|
|
33
|
+
private mixClips;
|
|
34
|
+
private push;
|
|
35
|
+
}
|
package/dist/device.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { SilentBackend } from './backends/silent.js';
|
|
2
|
+
import { NativeBackend } from './backends/native.js';
|
|
3
|
+
import { SpawnBackend } from './backends/spawn.js';
|
|
4
|
+
import { findSpawnTool, pathDirsFrom, detectAudio, silentRequested } from './detect.js';
|
|
5
|
+
import { decodeWav } from './wav.js';
|
|
6
|
+
/**
|
|
7
|
+
* PCM output through the OS mixer. Kitty cannot carry audio; this never
|
|
8
|
+
* writes to the terminal.
|
|
9
|
+
*
|
|
10
|
+
* Apps that never import `@mudah-cli/audio` never load a backend.
|
|
11
|
+
*/
|
|
12
|
+
export class AudioOut {
|
|
13
|
+
sampleRate;
|
|
14
|
+
channels;
|
|
15
|
+
format;
|
|
16
|
+
backendKind;
|
|
17
|
+
spawnTool;
|
|
18
|
+
bytesWritten = 0;
|
|
19
|
+
backend;
|
|
20
|
+
clips = [];
|
|
21
|
+
wroteOnce = false;
|
|
22
|
+
disposed = false;
|
|
23
|
+
constructor(backend, sampleRate, channels, format) {
|
|
24
|
+
this.backend = backend;
|
|
25
|
+
this.sampleRate = sampleRate;
|
|
26
|
+
this.channels = channels;
|
|
27
|
+
this.format = format;
|
|
28
|
+
this.backendKind = backend.kind;
|
|
29
|
+
this.spawnTool = backend.spawnTool;
|
|
30
|
+
}
|
|
31
|
+
static async open(options = {}) {
|
|
32
|
+
const sampleRate = options.sampleRate ?? 44100;
|
|
33
|
+
const channels = options.channels ?? 2;
|
|
34
|
+
const format = options.format ?? 's16';
|
|
35
|
+
const framesPerBuffer = options.framesPerBuffer ?? 1024;
|
|
36
|
+
const env = options.env ?? process.env;
|
|
37
|
+
const requested = options.backend ?? 'auto';
|
|
38
|
+
const dirs = pathDirsFrom(env, options.pathDirs);
|
|
39
|
+
if (requested === 'silent' || (requested === 'auto' && silentRequested(env))) {
|
|
40
|
+
return new AudioOut(new SilentBackend(), sampleRate, channels, format);
|
|
41
|
+
}
|
|
42
|
+
const detected = detectAudio(env, options.pathDirs);
|
|
43
|
+
const tryNative = requested === 'native' || (requested === 'auto' && detected.backend === 'native');
|
|
44
|
+
if (tryNative) {
|
|
45
|
+
const native = await NativeBackend.tryOpen({ sampleRate, channels, format, framesPerBuffer });
|
|
46
|
+
if (native !== undefined)
|
|
47
|
+
return new AudioOut(native, sampleRate, channels, format);
|
|
48
|
+
if (requested === 'native') {
|
|
49
|
+
throw new Error('[audio] Native backend failed to load. Install audify (optional peer) and a C++ toolchain if no prebuild exists for this Node version.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const trySpawn = requested === 'spawn' || requested === 'auto';
|
|
53
|
+
if (trySpawn) {
|
|
54
|
+
const tool = options.spawnTool ?? findSpawnTool(dirs);
|
|
55
|
+
if (tool !== undefined) {
|
|
56
|
+
return new AudioOut(new SpawnBackend(tool, { sampleRate, channels }, options.spawn, options.onUnderrun), sampleRate, channels, format);
|
|
57
|
+
}
|
|
58
|
+
if (requested === 'spawn') {
|
|
59
|
+
throw new Error('[audio] Spawn backend needs pw-play, paplay, or aplay on PATH.');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (requested === 'auto') {
|
|
63
|
+
return new AudioOut(new SilentBackend(), sampleRate, channels, format);
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`[audio] Backend ${requested} is not available.`);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Push one interleaved buffer. Caller owns timing. Underrun does not throw.
|
|
69
|
+
*/
|
|
70
|
+
write(pcm) {
|
|
71
|
+
if (this.disposed)
|
|
72
|
+
return;
|
|
73
|
+
const samples = this.toDeviceS16(pcm);
|
|
74
|
+
this.mixClips(samples);
|
|
75
|
+
this.push(samples);
|
|
76
|
+
this.wroteOnce = true;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* One-shot clip. Mixes into later `write()` calls once the stream is pumping.
|
|
80
|
+
* Before the first `write()`, the samples go to the device immediately.
|
|
81
|
+
*/
|
|
82
|
+
async play(source) {
|
|
83
|
+
if (this.disposed)
|
|
84
|
+
return;
|
|
85
|
+
const clip = this.normalize(source);
|
|
86
|
+
if (this.wroteOnce) {
|
|
87
|
+
this.clips.push({ samples: clip, offset: 0 });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.push(clip);
|
|
91
|
+
}
|
|
92
|
+
dispose() {
|
|
93
|
+
if (this.disposed)
|
|
94
|
+
return;
|
|
95
|
+
this.disposed = true;
|
|
96
|
+
this.clips.length = 0;
|
|
97
|
+
this.backend.dispose();
|
|
98
|
+
}
|
|
99
|
+
normalize(source) {
|
|
100
|
+
const clip = source instanceof Uint8Array ? decodeWav(source) : source;
|
|
101
|
+
return remapChannels(clip.samples, clip.channels, this.channels);
|
|
102
|
+
}
|
|
103
|
+
toDeviceS16(pcm) {
|
|
104
|
+
if (pcm instanceof Int16Array)
|
|
105
|
+
return Int16Array.from(pcm);
|
|
106
|
+
const out = new Int16Array(pcm.length);
|
|
107
|
+
for (let i = 0; i < pcm.length; i++) {
|
|
108
|
+
const x = pcm[i] ?? 0;
|
|
109
|
+
out[i] = Math.max(-32768, Math.min(32767, Math.round(x * 32767)));
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
mixClips(into) {
|
|
114
|
+
for (const clip of this.clips) {
|
|
115
|
+
const remaining = clip.samples.length - clip.offset;
|
|
116
|
+
const n = Math.min(into.length, remaining);
|
|
117
|
+
for (let i = 0; i < n; i++) {
|
|
118
|
+
const mixed = (into[i] ?? 0) + (clip.samples[clip.offset + i] ?? 0);
|
|
119
|
+
into[i] = Math.max(-32768, Math.min(32767, mixed));
|
|
120
|
+
}
|
|
121
|
+
clip.offset += n;
|
|
122
|
+
}
|
|
123
|
+
for (let i = this.clips.length - 1; i >= 0; i--) {
|
|
124
|
+
const clip = this.clips[i];
|
|
125
|
+
if (clip !== undefined && clip.offset >= clip.samples.length)
|
|
126
|
+
this.clips.splice(i, 1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
push(samples) {
|
|
130
|
+
const bytes = this.format === 'f32' && this.backendKind === 'native' ? f32le(samples) : s16le(samples);
|
|
131
|
+
this.backend.write(bytes);
|
|
132
|
+
this.bytesWritten += bytes.byteLength;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function s16le(samples) {
|
|
136
|
+
const out = new Uint8Array(samples.length * 2);
|
|
137
|
+
const view = new DataView(out.buffer);
|
|
138
|
+
for (let i = 0; i < samples.length; i++)
|
|
139
|
+
view.setInt16(i * 2, samples[i] ?? 0, true);
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
function f32le(samples) {
|
|
143
|
+
const out = new Uint8Array(samples.length * 4);
|
|
144
|
+
const view = new DataView(out.buffer);
|
|
145
|
+
for (let i = 0; i < samples.length; i++)
|
|
146
|
+
view.setFloat32(i * 4, (samples[i] ?? 0) / 32768, true);
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
function remapChannels(samples, from, to) {
|
|
150
|
+
if (from === to)
|
|
151
|
+
return Int16Array.from(samples);
|
|
152
|
+
const frames = Math.floor(samples.length / from);
|
|
153
|
+
const out = new Int16Array(frames * to);
|
|
154
|
+
for (let f = 0; f < frames; f++) {
|
|
155
|
+
if (from === 1 && to === 2) {
|
|
156
|
+
const s = samples[f] ?? 0;
|
|
157
|
+
out[f * 2] = s;
|
|
158
|
+
out[f * 2 + 1] = s;
|
|
159
|
+
}
|
|
160
|
+
else if (from === 2 && to === 1) {
|
|
161
|
+
const l = samples[f * 2] ?? 0;
|
|
162
|
+
const r = samples[f * 2 + 1] ?? 0;
|
|
163
|
+
out[f] = (l + r) >> 1;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
for (let c = 0; c < to; c++)
|
|
167
|
+
out[f * to + c] = samples[f * from + Math.min(c, from - 1)] ?? 0;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AudioOut, } from './device.js';
|
|
2
|
+
export { detectAudio, canResolveAudify, findSpawnTool, silentRequested, } from './detect.js';
|
|
3
|
+
export { decodeWav, encodeWav, } from './wav.js';
|
|
4
|
+
export { spawnCommand, } from './backends/spawn-command.js';
|
|
5
|
+
export type { AudioBackendKind, AudioClip, AudioDetection, AudioFormat, AudioOutOptions, PlaySource, SpawnTool, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AudioOut, } from './device.js';
|
|
2
|
+
export { detectAudio, canResolveAudify, findSpawnTool, silentRequested, } from './detect.js';
|
|
3
|
+
export { decodeWav, encodeWav, } from './wav.js';
|
|
4
|
+
export { spawnCommand, } from './backends/spawn-command.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,SAAS,EACT,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,GACb,MAAM,6BAA6B,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type AudioFormat = 's16' | 'f32';
|
|
2
|
+
export type AudioBackendKind = 'auto' | 'native' | 'spawn' | 'silent';
|
|
3
|
+
export type SpawnTool = 'pw-play' | 'paplay' | 'aplay';
|
|
4
|
+
export interface AudioOutOptions {
|
|
5
|
+
sampleRate?: number;
|
|
6
|
+
channels?: number;
|
|
7
|
+
/** Interleaved little-endian. Default `s16`. Spawn always sends s16. */
|
|
8
|
+
format?: AudioFormat;
|
|
9
|
+
/** Hint for native RtAudio and for callers that pump a fixed block. Default 1024. */
|
|
10
|
+
framesPerBuffer?: number;
|
|
11
|
+
backend?: AudioBackendKind;
|
|
12
|
+
/** Called when a write cannot reach the device. Never throws. */
|
|
13
|
+
onUnderrun?: () => void;
|
|
14
|
+
/** Environment for `CI` / `MUDAH_NO_AUDIO` (tests pass a map). */
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
16
|
+
/** Override PATH lookup (tests). */
|
|
17
|
+
pathDirs?: readonly string[];
|
|
18
|
+
/** Inject spawn (tests). */
|
|
19
|
+
spawn?: SpawnFn;
|
|
20
|
+
/** Skip PATH lookup (tests). */
|
|
21
|
+
spawnTool?: SpawnTool;
|
|
22
|
+
}
|
|
23
|
+
export type SpawnFn = (command: string, args: readonly string[], options: {
|
|
24
|
+
stdio: ['pipe', 'ignore', 'ignore'];
|
|
25
|
+
}) => SpawnedPlayer;
|
|
26
|
+
export interface SpawnedPlayer {
|
|
27
|
+
stdin: {
|
|
28
|
+
write(data: Uint8Array): boolean;
|
|
29
|
+
end(): void;
|
|
30
|
+
} | null;
|
|
31
|
+
kill(): void;
|
|
32
|
+
on?(event: 'error' | 'exit', listener: (...args: unknown[]) => void): unknown;
|
|
33
|
+
}
|
|
34
|
+
export interface AudioClip {
|
|
35
|
+
readonly samples: Int16Array;
|
|
36
|
+
readonly sampleRate: number;
|
|
37
|
+
readonly channels: number;
|
|
38
|
+
}
|
|
39
|
+
export type PlaySource = Uint8Array | AudioClip;
|
|
40
|
+
export interface AudioDetection {
|
|
41
|
+
readonly backend: Exclude<AudioBackendKind, 'auto'>;
|
|
42
|
+
readonly native: boolean;
|
|
43
|
+
readonly spawnTool: SpawnTool | undefined;
|
|
44
|
+
}
|
|
45
|
+
export interface AudioBackend {
|
|
46
|
+
readonly kind: Exclude<AudioBackendKind, 'auto'>;
|
|
47
|
+
readonly spawnTool?: SpawnTool;
|
|
48
|
+
write(bytes: Uint8Array): void;
|
|
49
|
+
dispose(): void;
|
|
50
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/wav.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AudioClip } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decode a PCM 16-bit little-endian WAV. Other encodings throw.
|
|
4
|
+
*/
|
|
5
|
+
export declare function decodeWav(bytes: Uint8Array): AudioClip;
|
|
6
|
+
/** Encode PCM 16-bit little-endian WAV. Used by tests and one-shot helpers. */
|
|
7
|
+
export declare function encodeWav(clip: AudioClip): Uint8Array;
|
package/dist/wav.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const RIFF = 0x46464952;
|
|
2
|
+
const WAVE = 0x45564157;
|
|
3
|
+
const FMT = 0x20746d66;
|
|
4
|
+
const DATA = 0x61746164;
|
|
5
|
+
/**
|
|
6
|
+
* Decode a PCM 16-bit little-endian WAV. Other encodings throw.
|
|
7
|
+
*/
|
|
8
|
+
export function decodeWav(bytes) {
|
|
9
|
+
const buf = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
10
|
+
const view = new DataView(buf);
|
|
11
|
+
if (bytes.byteLength < 12 || view.getUint32(0, true) !== RIFF || view.getUint32(8, true) !== WAVE) {
|
|
12
|
+
throw new Error('[audio] WAV payload is not a RIFF/WAVE file.');
|
|
13
|
+
}
|
|
14
|
+
let offset = 12;
|
|
15
|
+
let channels = 0;
|
|
16
|
+
let sampleRate = 0;
|
|
17
|
+
let bits = 0;
|
|
18
|
+
let audioFormat = 0;
|
|
19
|
+
let data;
|
|
20
|
+
while (offset + 8 <= bytes.byteLength) {
|
|
21
|
+
const id = view.getUint32(offset, true);
|
|
22
|
+
const size = view.getUint32(offset + 4, true);
|
|
23
|
+
const start = offset + 8;
|
|
24
|
+
const end = start + size;
|
|
25
|
+
if (end > bytes.byteLength)
|
|
26
|
+
throw new Error('[audio] WAV chunk overruns the buffer.');
|
|
27
|
+
if (id === FMT) {
|
|
28
|
+
audioFormat = view.getUint16(start, true);
|
|
29
|
+
channels = view.getUint16(start + 2, true);
|
|
30
|
+
sampleRate = view.getUint32(start + 4, true);
|
|
31
|
+
bits = view.getUint16(start + 14, true);
|
|
32
|
+
}
|
|
33
|
+
else if (id === DATA) {
|
|
34
|
+
data = new Uint8Array(buf, start, size);
|
|
35
|
+
}
|
|
36
|
+
offset = end + (size % 2);
|
|
37
|
+
}
|
|
38
|
+
if (audioFormat !== 1)
|
|
39
|
+
throw new Error('[audio] WAV encoding is not PCM (format 1).');
|
|
40
|
+
if (bits !== 16)
|
|
41
|
+
throw new Error(`[audio] WAV bits-per-sample is ${bits}, need 16.`);
|
|
42
|
+
if (channels < 1)
|
|
43
|
+
throw new Error('[audio] WAV has no channels.');
|
|
44
|
+
if (data === undefined)
|
|
45
|
+
throw new Error('[audio] WAV has no data chunk.');
|
|
46
|
+
const samples = new Int16Array(data.byteLength / 2);
|
|
47
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
48
|
+
for (let i = 0; i < samples.length; i++)
|
|
49
|
+
samples[i] = dataView.getInt16(i * 2, true);
|
|
50
|
+
return { samples, sampleRate, channels };
|
|
51
|
+
}
|
|
52
|
+
/** Encode PCM 16-bit little-endian WAV. Used by tests and one-shot helpers. */
|
|
53
|
+
export function encodeWav(clip) {
|
|
54
|
+
const dataBytes = clip.samples.byteLength;
|
|
55
|
+
const buffer = new ArrayBuffer(44 + dataBytes);
|
|
56
|
+
const view = new DataView(buffer);
|
|
57
|
+
const out = new Uint8Array(buffer);
|
|
58
|
+
const blockAlign = clip.channels * 2;
|
|
59
|
+
const byteRate = clip.sampleRate * blockAlign;
|
|
60
|
+
view.setUint32(0, RIFF, true);
|
|
61
|
+
view.setUint32(4, 36 + dataBytes, true);
|
|
62
|
+
view.setUint32(8, WAVE, true);
|
|
63
|
+
view.setUint32(12, FMT, true);
|
|
64
|
+
view.setUint32(16, 16, true);
|
|
65
|
+
view.setUint16(20, 1, true);
|
|
66
|
+
view.setUint16(22, clip.channels, true);
|
|
67
|
+
view.setUint32(24, clip.sampleRate, true);
|
|
68
|
+
view.setUint32(28, byteRate, true);
|
|
69
|
+
view.setUint16(32, blockAlign, true);
|
|
70
|
+
view.setUint16(34, 16, true);
|
|
71
|
+
view.setUint32(36, DATA, true);
|
|
72
|
+
view.setUint32(40, dataBytes, true);
|
|
73
|
+
out.set(new Uint8Array(clip.samples.buffer, clip.samples.byteOffset, dataBytes), 44);
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=wav.js.map
|
package/dist/wav.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wav.js","sourceRoot":"","sources":["../src/wav.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mudah-cli/audio",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Play PCM through the OS mixer from a Mudah app (streaming output and one-shot WAV). Optional: apps that never import it never load an audio backend.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=26"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"audify": "^1.10.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependenciesMeta": {
|
|
24
|
+
"audify": {
|
|
25
|
+
"optional": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|