@ai-rpg-engine/audio-director 2.0.0 → 2.0.1
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 +72 -0
- package/dist/director.test.d.ts +2 -0
- package/dist/director.test.d.ts.map +1 -0
- package/dist/director.test.js +106 -0
- package/dist/director.test.js.map +1 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/mcp-tool-shop-org/brand/main/logos/ai-rpg-engine/readme.png" width="300" alt="AI RPG Engine">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@ai-rpg-engine/audio-director"><img src="https://img.shields.io/npm/v/@ai-rpg-engine/audio-director.svg" alt="npm"></a>
|
|
7
|
+
<a href="https://github.com/mcp-tool-shop-org/ai-rpg-engine/actions/workflows/ci.yml"><img src="https://github.com/mcp-tool-shop-org/ai-rpg-engine/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
8
|
+
<a href="https://github.com/mcp-tool-shop-org/ai-rpg-engine/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
# @ai-rpg-engine/audio-director
|
|
12
|
+
|
|
13
|
+
Deterministic audio cue scheduling engine for the [AI RPG Engine](https://github.com/mcp-tool-shop-org/ai-rpg-engine).
|
|
14
|
+
|
|
15
|
+
Part of the **Immersion Runtime** — converts narration plans into timed, prioritized audio commands.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @ai-rpg-engine/audio-director
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What It Does
|
|
24
|
+
|
|
25
|
+
The Audio Director takes a `NarrationPlan` and produces ordered `AudioCommand[]` — ready for any audio backend to execute. It handles:
|
|
26
|
+
|
|
27
|
+
- **Priority**: Voice > SFX > Music > Ambient (configurable)
|
|
28
|
+
- **Ducking**: Ambient/music automatically lower when voice plays
|
|
29
|
+
- **Cooldowns**: Prevents SFX spam (configurable per-resource)
|
|
30
|
+
- **Timing**: Sequences cues relative to speech duration
|
|
31
|
+
- **Layer tracking**: Knows which ambient layers are active
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { AudioDirector } from '@ai-rpg-engine/audio-director';
|
|
37
|
+
import type { NarrationPlan } from '@ai-rpg-engine/presentation';
|
|
38
|
+
|
|
39
|
+
const director = new AudioDirector({
|
|
40
|
+
defaultCooldownMs: 2000,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Schedule commands from a narration plan
|
|
44
|
+
const commands = director.schedule(plan);
|
|
45
|
+
|
|
46
|
+
// Execute commands through your audio backend
|
|
47
|
+
for (const cmd of commands) {
|
|
48
|
+
await audioBackend.execute(cmd);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check cooldowns
|
|
52
|
+
director.isOnCooldown('alert_warning'); // true if recently played
|
|
53
|
+
|
|
54
|
+
// Clear cooldowns on scene change
|
|
55
|
+
director.clearCooldowns();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Default Ducking Rules
|
|
59
|
+
|
|
60
|
+
| Trigger | Target | Duck Level |
|
|
61
|
+
|---------|--------|-----------|
|
|
62
|
+
| Voice | Ambient | 30% volume |
|
|
63
|
+
| Voice | Music | 40% volume |
|
|
64
|
+
| SFX | Ambient | 60% volume |
|
|
65
|
+
|
|
66
|
+
## Part of AI RPG Engine
|
|
67
|
+
|
|
68
|
+
This package is part of the [AI RPG Engine](https://github.com/mcp-tool-shop-org/ai-rpg-engine) monorepo. See the root README for the full architecture.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"director.test.d.ts","sourceRoot":"","sources":["../src/director.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { AudioDirector } from './director.js';
|
|
3
|
+
const makePlan = (overrides = {}) => ({
|
|
4
|
+
sceneText: 'You step into darkness.',
|
|
5
|
+
tone: 'dread',
|
|
6
|
+
urgency: 'normal',
|
|
7
|
+
sfx: [],
|
|
8
|
+
ambientLayers: [],
|
|
9
|
+
uiEffects: [],
|
|
10
|
+
interruptibility: 'free',
|
|
11
|
+
...overrides,
|
|
12
|
+
});
|
|
13
|
+
describe('AudioDirector', () => {
|
|
14
|
+
it('should schedule voice commands from plan with speaker', () => {
|
|
15
|
+
const director = new AudioDirector();
|
|
16
|
+
const plan = makePlan({
|
|
17
|
+
speaker: {
|
|
18
|
+
entityId: 'pilgrim',
|
|
19
|
+
voiceId: 'am_adam',
|
|
20
|
+
emotion: 'fearful',
|
|
21
|
+
speed: 0.9,
|
|
22
|
+
text: 'Turn back!',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
const commands = director.schedule(plan);
|
|
26
|
+
const voiceCmd = commands.find((c) => c.domain === 'voice');
|
|
27
|
+
expect(voiceCmd).toBeDefined();
|
|
28
|
+
expect(voiceCmd.params.text).toBe('Turn back!');
|
|
29
|
+
});
|
|
30
|
+
it('should schedule SFX commands', () => {
|
|
31
|
+
const director = new AudioDirector();
|
|
32
|
+
const plan = makePlan({
|
|
33
|
+
sfx: [{ effectId: 'alert_warning', timing: 'immediate', intensity: 0.8 }],
|
|
34
|
+
});
|
|
35
|
+
const commands = director.schedule(plan);
|
|
36
|
+
const sfxCmd = commands.find((c) => c.domain === 'sfx');
|
|
37
|
+
expect(sfxCmd).toBeDefined();
|
|
38
|
+
expect(sfxCmd.resourceId).toBe('alert_warning');
|
|
39
|
+
});
|
|
40
|
+
it('should schedule ambient commands', () => {
|
|
41
|
+
const director = new AudioDirector();
|
|
42
|
+
const plan = makePlan({
|
|
43
|
+
ambientLayers: [{ layerId: 'ambient_drone', action: 'start', volume: 0.3, fadeMs: 1000 }],
|
|
44
|
+
});
|
|
45
|
+
const commands = director.schedule(plan);
|
|
46
|
+
const ambientCmd = commands.find((c) => c.domain === 'ambient');
|
|
47
|
+
expect(ambientCmd).toBeDefined();
|
|
48
|
+
expect(ambientCmd.resourceId).toBe('ambient_drone');
|
|
49
|
+
});
|
|
50
|
+
it('should add ducking commands when voice is playing', () => {
|
|
51
|
+
const director = new AudioDirector();
|
|
52
|
+
const plan = makePlan({
|
|
53
|
+
speaker: {
|
|
54
|
+
entityId: 'npc',
|
|
55
|
+
voiceId: 'af_bella',
|
|
56
|
+
emotion: 'calm',
|
|
57
|
+
speed: 1.0,
|
|
58
|
+
text: 'Hello.',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
const commands = director.schedule(plan);
|
|
62
|
+
const duckCmd = commands.find((c) => c.action === 'duck');
|
|
63
|
+
expect(duckCmd).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
it('should apply SFX cooldown', () => {
|
|
66
|
+
const director = new AudioDirector();
|
|
67
|
+
const plan = makePlan({
|
|
68
|
+
sfx: [{ effectId: 'alert_warning', timing: 'immediate', intensity: 0.8 }],
|
|
69
|
+
});
|
|
70
|
+
director.schedule(plan);
|
|
71
|
+
expect(director.isOnCooldown('alert_warning')).toBe(true);
|
|
72
|
+
// Second schedule should filter out the cooled-down SFX
|
|
73
|
+
const commands2 = director.schedule(plan);
|
|
74
|
+
const sfxCmds = commands2.filter((c) => c.domain === 'sfx' && c.action === 'play');
|
|
75
|
+
expect(sfxCmds).toHaveLength(0);
|
|
76
|
+
});
|
|
77
|
+
it('should track active ambient layers', () => {
|
|
78
|
+
const director = new AudioDirector();
|
|
79
|
+
const plan = makePlan({
|
|
80
|
+
ambientLayers: [{ layerId: 'ambient_rain', action: 'start', volume: 0.4, fadeMs: 1000 }],
|
|
81
|
+
});
|
|
82
|
+
director.schedule(plan);
|
|
83
|
+
expect(director.getActiveLayers().has('ambient_rain')).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
it('should clear cooldowns', () => {
|
|
86
|
+
const director = new AudioDirector();
|
|
87
|
+
const plan = makePlan({
|
|
88
|
+
sfx: [{ effectId: 'ui_click', timing: 'immediate', intensity: 0.5 }],
|
|
89
|
+
});
|
|
90
|
+
director.schedule(plan);
|
|
91
|
+
expect(director.isOnCooldown('ui_click')).toBe(true);
|
|
92
|
+
director.clearCooldowns();
|
|
93
|
+
expect(director.isOnCooldown('ui_click')).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
it('should schedule music commands', () => {
|
|
96
|
+
const director = new AudioDirector();
|
|
97
|
+
const plan = makePlan({
|
|
98
|
+
musicCue: { action: 'intensify', fadeMs: 500 },
|
|
99
|
+
});
|
|
100
|
+
const commands = director.schedule(plan);
|
|
101
|
+
const musicCmd = commands.find((c) => c.domain === 'music');
|
|
102
|
+
expect(musicCmd).toBeDefined();
|
|
103
|
+
expect(musicCmd.action).toBe('intensify');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=director.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"director.test.js","sourceRoot":"","sources":["../src/director.test.ts"],"names":[],"mappings":"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"}
|