@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 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=director.test.d.ts.map
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-rpg-engine/audio-director",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Audio cue scheduling, priority, ducking, and cooldown engine for AI RPG Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",