@umicat/three-sdk 0.10.0 → 0.11.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.
@@ -58,6 +58,13 @@ export declare class GameAudio {
58
58
  private ctx;
59
59
  private master;
60
60
  private musicGain;
61
+ /** Everything that is not music, on its own knob.
62
+ *
63
+ * Effects used to connect straight to the master, which made "quieter
64
+ * music" and "quieter sound" the same lever — and they are the two things
65
+ * players most want to set separately. Someone who wants the track down
66
+ * wants to keep hearing what is shooting at them. */
67
+ private sfxGain;
61
68
  private musicEl;
62
69
  private musicName;
63
70
  private readonly buffers;
@@ -65,7 +72,10 @@ export declare class GameAudio {
65
72
  private readonly clips;
66
73
  private readonly base;
67
74
  private readonly ext;
68
- private readonly musicVolume;
75
+ /** Not readonly any more: it is the top of the music's range and the level
76
+ * `duck()` returns to, and both move when the player moves the slider. */
77
+ private musicVolume;
78
+ private sfxVolume;
69
79
  private muted;
70
80
  private readonly cleanups;
71
81
  constructor(opts: GameAudioOptions);
@@ -87,6 +97,19 @@ export declare class GameAudio {
87
97
  play(name: string): void;
88
98
  /** Duck the music for a moment — for an ending that should be heard over it. */
89
99
  duck(seconds?: number): void;
100
+ /**
101
+ * How loud the music is, 0 to 1, as a fraction of the mix it was given.
102
+ *
103
+ * Separate from mute, and both are kept: mute is a switch you flip on the way
104
+ * into a room and back on the way out, and losing the level you had set to
105
+ * find it back at 1 is a small betrayal. Muted, this still records what the
106
+ * player chose — unmuting restores it.
107
+ */
108
+ setMusicVolume(v: number): void;
109
+ get musicLevel(): number;
110
+ /** How loud everything that is not music is, 0 to 1. */
111
+ setSfxVolume(v: number): void;
112
+ get sfxLevel(): number;
90
113
  setMuted(on: boolean): void;
91
114
  get isMuted(): boolean;
92
115
  dispose(): void;
package/dist/GameAudio.js CHANGED
@@ -38,10 +38,18 @@ export class GameAudio {
38
38
  this.ctx = null;
39
39
  this.master = null;
40
40
  this.musicGain = null;
41
+ /** Everything that is not music, on its own knob.
42
+ *
43
+ * Effects used to connect straight to the master, which made "quieter
44
+ * music" and "quieter sound" the same lever — and they are the two things
45
+ * players most want to set separately. Someone who wants the track down
46
+ * wants to keep hearing what is shooting at them. */
47
+ this.sfxGain = null;
41
48
  this.musicEl = null;
42
49
  this.musicName = null;
43
50
  this.buffers = new Map();
44
51
  this.lastPlayed = new Map();
52
+ this.sfxVolume = 1;
45
53
  this.muted = false;
46
54
  this.cleanups = [];
47
55
  this.clips = opts.clips;
@@ -88,6 +96,9 @@ export class GameAudio {
88
96
  this.musicGain = this.ctx.createGain();
89
97
  this.musicGain.gain.value = this.musicVolume;
90
98
  this.musicGain.connect(this.master);
99
+ this.sfxGain = this.ctx.createGain();
100
+ this.sfxGain.gain.value = this.sfxVolume;
101
+ this.sfxGain.connect(this.master);
91
102
  void this.loadAll();
92
103
  }
93
104
  // Called inside the gesture's call stack, and awaited before anything asks
@@ -180,7 +191,10 @@ export class GameAudio {
180
191
  const g = ctx.createGain();
181
192
  g.gain.value = spec?.volume ?? 0.5;
182
193
  src.connect(g);
183
- g.connect(this.master);
194
+ // Through the effects knob, not straight to the master. The clip's own
195
+ // `volume` is still its balance against the others; this is the player's
196
+ // opinion about all of them at once.
197
+ g.connect(this.sfxGain ?? this.master);
184
198
  src.start();
185
199
  // Nodes disconnect themselves when they end; without this they pile up as
186
200
  // garbage the collector has to chase during play.
@@ -196,6 +210,40 @@ export class GameAudio {
196
210
  this.musicGain.gain.linearRampToValueAtTime(this.musicVolume * 0.25, t + 0.2);
197
211
  this.musicGain.gain.linearRampToValueAtTime(this.musicVolume, t + seconds);
198
212
  }
213
+ /**
214
+ * How loud the music is, 0 to 1, as a fraction of the mix it was given.
215
+ *
216
+ * Separate from mute, and both are kept: mute is a switch you flip on the way
217
+ * into a room and back on the way out, and losing the level you had set to
218
+ * find it back at 1 is a small betrayal. Muted, this still records what the
219
+ * player chose — unmuting restores it.
220
+ */
221
+ setMusicVolume(v) {
222
+ this.musicVolume = Math.max(0, Math.min(1, v));
223
+ if (this.musicGain && this.ctx) {
224
+ // Ramped, not assigned. A gain that jumps clicks, audibly, and a slider
225
+ // is a stream of tiny jumps.
226
+ this.musicGain.gain.setTargetAtTime(this.musicVolume, this.ctx.currentTime, 0.02);
227
+ }
228
+ // The fallback path, where the element could not be routed through the
229
+ // graph. Without this, the slider does nothing at all on those engines —
230
+ // and they are the ones nobody tests on.
231
+ if (this.musicEl && this.musicEl.volume !== undefined) {
232
+ try {
233
+ this.musicEl.volume = this.musicVolume;
234
+ }
235
+ catch { /* not fatal */ }
236
+ }
237
+ }
238
+ get musicLevel() { return this.musicVolume; }
239
+ /** How loud everything that is not music is, 0 to 1. */
240
+ setSfxVolume(v) {
241
+ this.sfxVolume = Math.max(0, Math.min(1, v));
242
+ if (this.sfxGain && this.ctx) {
243
+ this.sfxGain.gain.setTargetAtTime(this.sfxVolume, this.ctx.currentTime, 0.02);
244
+ }
245
+ }
246
+ get sfxLevel() { return this.sfxVolume; }
199
247
  setMuted(on) {
200
248
  this.muted = on;
201
249
  if (this.master && this.ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umicat/three-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Three.js runtime for Umicat games: the scene3d design format, its loader with physics, a kinematic character controller, and the Umicat platform via @umicat/platform-sdk.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",