0
# Audio and Sound
1
2
Panda3D provides a comprehensive audio system with support for 3D positional audio, sound effects, music playback, and spatial audio processing for immersive 3D environments.
3
4
## Capabilities
5
6
### Audio3DManager - 3D Positional Audio
7
8
The Audio3DManager handles 3D spatial audio with automatic distance attenuation, doppler effects, and positional sound placement.
9
10
```python { .api }
11
class Audio3DManager:
12
def __init__(self,
13
audio_manager,
14
listener_target: NodePath,
15
root: NodePath = None,
16
taskPriority: int = 51,
17
autoUpdate: bool = True) -> None:
18
"""
19
Initialize 3D audio manager.
20
21
Args:
22
audio_manager: Base audio manager instance
23
listener_target: NodePath representing listener position (usually camera)
24
root: Root node for audio coordinate system
25
taskPriority: Priority of audio update task
26
autoUpdate: Whether to automatically update audio positions
27
"""
28
29
def loadSfx(self, filename: str) -> AudioSound:
30
"""Load sound effect file."""
31
32
def loadMusic(self, filename: str) -> AudioSound:
33
"""Load music file."""
34
35
def attachSoundToObject(self, sound: AudioSound, object: NodePath) -> None:
36
"""Attach sound to 3D object for automatic positioning."""
37
38
def detachSound(self, sound: AudioSound) -> None:
39
"""Detach sound from its 3D object."""
40
41
def setListenerVelocity(self, velocity: Vec3) -> None:
42
"""Set listener velocity for doppler effects."""
43
44
def setDistanceFactor(self, factor: float) -> None:
45
"""Set distance scaling factor."""
46
47
def setDopplerFactor(self, factor: float) -> None:
48
"""Set doppler effect strength."""
49
50
def setDropOffFactor(self, factor: float) -> None:
51
"""Set audio drop-off/attenuation factor."""
52
53
def attachSoundToObject(self,
54
sound: AudioSound,
55
object: NodePath) -> None:
56
"""Attach sound to follow an object's position."""
57
58
def detachSound(self, sound: AudioSound) -> None:
59
"""Detach sound from object."""
60
61
def setSoundVelocity(self, sound: AudioSound, velocity: Vec3) -> None:
62
"""Set sound source velocity."""
63
64
def setSoundVelocityAuto(self, sound: AudioSound) -> None:
65
"""Enable automatic velocity calculation."""
66
67
def setSoundMinDistance(self, sound: AudioSound, distance: float) -> None:
68
"""Set minimum distance for audio attenuation."""
69
70
def setSoundMaxDistance(self, sound: AudioSound, distance: float) -> None:
71
"""Set maximum distance for audio attenuation."""
72
73
def update(self) -> None:
74
"""Manually update audio positions."""
75
76
def disable(self) -> None:
77
"""Disable 3D audio processing."""
78
79
def enable(self) -> None:
80
"""Enable 3D audio processing."""
81
```
82
83
### AudioSound - Individual Sound Control
84
85
AudioSound objects provide detailed control over individual audio sources.
86
87
```python { .api }
88
class AudioSound:
89
def play(self) -> None:
90
"""Play the sound."""
91
92
def stop(self) -> None:
93
"""Stop playing the sound."""
94
95
def setLoop(self, loop: bool) -> None:
96
"""Set whether sound should loop."""
97
98
def getLoop(self) -> bool:
99
"""Check if sound is set to loop."""
100
101
def setLoopStart(self, start_time: float) -> None:
102
"""Set loop start time in seconds."""
103
104
def setLoopEnd(self, end_time: float) -> None:
105
"""Set loop end time in seconds."""
106
107
def setVolume(self, volume: float) -> None:
108
"""Set sound volume (0.0 to 1.0)."""
109
110
def getVolume(self) -> float:
111
"""Get current volume."""
112
113
def setBalance(self, balance: float) -> None:
114
"""Set left/right balance (-1.0 to 1.0)."""
115
116
def getBalance(self) -> float:
117
"""Get current balance."""
118
119
def setPlayRate(self, rate: float) -> None:
120
"""Set playback speed multiplier."""
121
122
def getPlayRate(self) -> float:
123
"""Get current playback rate."""
124
125
def length(self) -> float:
126
"""Get sound duration in seconds."""
127
128
def getTime(self) -> float:
129
"""Get current playback position in seconds."""
130
131
def setTime(self, time: float) -> None:
132
"""Set playback position."""
133
134
def status(self) -> int:
135
"""Get playback status (READY, PLAYING, BAD)."""
136
137
def finished(self) -> bool:
138
"""Check if sound has finished playing."""
139
140
def ready(self) -> bool:
141
"""Check if sound is ready to play."""
142
```
143
144
### Basic Audio Management
145
146
Simple audio playback without 3D positioning for music and UI sounds.
147
148
```python { .api }
149
class AudioManager:
150
def getSound(self, filename: str, positional: bool = False) -> AudioSound:
151
"""Load audio file and return AudioSound object."""
152
153
def playSound(self,
154
sound: AudioSound,
155
looping: bool = False,
156
interrupt: bool = True,
157
volume: float = 1.0,
158
node: NodePath = None) -> None:
159
"""Play sound with optional 3D positioning."""
160
161
def stopSound(self, sound: AudioSound) -> None:
162
"""Stop playing sound."""
163
164
def setVolume(self, volume: float) -> None:
165
"""Set global volume."""
166
167
def getVolume(self) -> float:
168
"""Get global volume."""
169
170
def setSfxVolume(self, volume: float) -> None:
171
"""Set sound effects volume."""
172
173
def getSfxVolume(self) -> float:
174
"""Get sound effects volume."""
175
176
def setMusicVolume(self, volume: float) -> None:
177
"""Set music volume."""
178
179
def getMusicVolume(self) -> float:
180
"""Get music volume."""
181
182
def stopAllSounds(self) -> None:
183
"""Stop all currently playing sounds."""
184
185
def setSoundsActive(self, active: bool) -> None:
186
"""Enable or disable all sound playback."""
187
188
def getSoundsActive(self) -> bool:
189
"""Check if sounds are active."""
190
```
191
192
## Usage Examples
193
194
### 3D Positional Audio
195
196
```python
197
from direct.showbase.ShowBase import ShowBase
198
from direct.showbase.Audio3DManager import Audio3DManager
199
from panda3d.core import Vec3
200
from direct.task import Task
201
202
class Audio3DDemo(ShowBase):
203
def __init__(self):
204
ShowBase.__init__(self)
205
206
# Initialize 3D audio manager
207
self.audio3d = Audio3DManager(base.sfxManagerList[0], self.camera)
208
209
# Load sounds
210
self.engine_sound = self.audio3d.loadSfx("sounds/engine.wav")
211
self.footsteps = self.audio3d.loadSfx("sounds/footsteps.wav")
212
self.ambient = self.audio3d.loadMusic("sounds/ambient.ogg")
213
214
# Create moving objects
215
self.createMovingObjects()
216
217
# Setup audio properties
218
self.setupAudioProperties()
219
220
# Start background music
221
self.audio3d.play(self.ambient, looping=True, volume=0.3)
222
223
# Setup controls
224
self.setupControls()
225
226
def createMovingObjects(self):
227
"""Create objects that will have attached sounds."""
228
# Car with engine sound
229
self.car = self.loader.loadModel("models/car")
230
self.car.reparentTo(self.render)
231
self.car.setPos(-10, 20, 0)
232
233
# Attach engine sound to car
234
self.audio3d.attachSoundToObject(self.engine_sound, self.car)
235
self.audio3d.play(self.engine_sound, looping=True)
236
237
# Set up car movement
238
self.taskMgr.add(self.moveCar, "move-car")
239
240
# Walking character
241
self.character = self.loader.loadModel("models/character")
242
self.character.reparentTo(self.render)
243
self.character.setPos(5, 15, 0)
244
245
# Attach footstep sound
246
self.audio3d.attachSoundToObject(self.footsteps, self.character)
247
248
def setupAudioProperties(self):
249
"""Configure 3D audio properties."""
250
# Set distance factors
251
self.audio3d.setDistanceFactor(1.0)
252
self.audio3d.setDopplerFactor(1.0)
253
self.audio3d.setDropOffFactor(0.1)
254
255
# Configure individual sounds
256
self.audio3d.setSoundMinDistance(self.engine_sound, 5.0)
257
self.audio3d.setSoundMaxDistance(self.engine_sound, 50.0)
258
259
self.audio3d.setSoundMinDistance(self.footsteps, 2.0)
260
self.audio3d.setSoundMaxDistance(self.footsteps, 20.0)
261
262
def moveCar(self, task):
263
"""Move car in circle for doppler demonstration."""
264
angle = task.time * 30 # 30 degrees per second
265
radius = 15
266
267
x = radius * math.cos(math.radians(angle))
268
y = 20 + radius * math.sin(math.radians(angle))
269
270
self.car.setPos(x, y, 0)
271
self.car.setH(angle + 90) # Face movement direction
272
273
return task.cont
274
275
def setupControls(self):
276
"""Setup keyboard controls."""
277
self.accept("1", self.toggleEngine)
278
self.accept("2", self.playFootsteps)
279
self.accept("3", self.adjustVolume, [0.1])
280
self.accept("4", self.adjustVolume, [-0.1])
281
282
# Movement controls for listener
283
self.accept("arrow_left", self.moveListener, [Vec3(-1, 0, 0)])
284
self.accept("arrow_right", self.moveListener, [Vec3(1, 0, 0)])
285
self.accept("arrow_up", self.moveListener, [Vec3(0, 1, 0)])
286
self.accept("arrow_down", self.moveListener, [Vec3(0, -1, 0)])
287
288
def toggleEngine(self):
289
"""Toggle engine sound."""
290
if self.engine_sound.status() == AudioSound.PLAYING:
291
self.audio3d.stop(self.engine_sound)
292
else:
293
self.audio3d.play(self.engine_sound, looping=True)
294
295
def playFootsteps(self):
296
"""Play footstep sound."""
297
self.audio3d.play(self.footsteps)
298
299
def adjustVolume(self, change):
300
"""Adjust global volume."""
301
current = base.musicManager.getVolume()
302
new_volume = max(0.0, min(1.0, current + change))
303
base.musicManager.setVolume(new_volume)
304
print(f"Volume: {new_volume:.1f}")
305
306
def moveListener(self, direction):
307
"""Move audio listener (camera)."""
308
current_pos = self.camera.getPos()
309
new_pos = current_pos + direction * 2
310
self.camera.setPos(new_pos)
311
print(f"Listener position: {new_pos}")
312
313
import math
314
app = Audio3DDemo()
315
app.run()
316
```
317
318
### Sound Manager Class
319
320
```python
321
from direct.showbase.DirectObject import DirectObject
322
from direct.showbase.Audio3DManager import Audio3DManager
323
324
class SoundManager(DirectObject):
325
"""Centralized sound management system."""
326
327
def __init__(self, listener_target):
328
DirectObject.__init__(self)
329
330
# Initialize audio systems
331
self.audio3d = Audio3DManager(base.sfxManagerList[0], listener_target)
332
333
# Sound libraries
334
self.sounds = {}
335
self.music = {}
336
self.current_music = None
337
338
# Volume settings
339
self.master_volume = 1.0
340
self.sfx_volume = 1.0
341
self.music_volume = 0.7
342
343
# Load sound libraries
344
self.loadSounds()
345
346
def loadSounds(self):
347
"""Load all game sounds."""
348
# Sound effects
349
sound_files = {
350
"button_click": "sounds/ui/button_click.wav",
351
"explosion": "sounds/fx/explosion.wav",
352
"pickup": "sounds/fx/pickup.wav",
353
"footstep": "sounds/character/footstep.wav",
354
"jump": "sounds/character/jump.wav"
355
}
356
357
for name, filename in sound_files.items():
358
try:
359
self.sounds[name] = self.audio3d.loadSfx(filename)
360
except:
361
print(f"Failed to load sound: {filename}")
362
363
# Music tracks
364
music_files = {
365
"menu": "music/menu_theme.ogg",
366
"level1": "music/level1_bg.ogg",
367
"victory": "music/victory.ogg"
368
}
369
370
for name, filename in music_files.items():
371
try:
372
self.music[name] = self.audio3d.loadMusic(filename)
373
except:
374
print(f"Failed to load music: {filename}")
375
376
def playSfx(self, name, volume=None, position=None, looping=False):
377
"""Play sound effect."""
378
if name not in self.sounds:
379
print(f"Sound not found: {name}")
380
return
381
382
sound = self.sounds[name]
383
384
# Set volume
385
if volume is None:
386
volume = self.sfx_volume
387
sound.setVolume(volume * self.master_volume)
388
389
# Set position if specified
390
if position:
391
self.audio3d.attachSoundToObject(sound, position)
392
393
# Play sound
394
self.audio3d.play(sound, looping=looping)
395
396
def playMusic(self, name, fade_in=False, fade_out_current=False):
397
"""Play background music."""
398
if name not in self.music:
399
print(f"Music not found: {name}")
400
return
401
402
# Stop current music
403
if self.current_music:
404
if fade_out_current:
405
self.fadeOutMusic(self.current_music, 1.0)
406
else:
407
self.audio3d.stop(self.current_music)
408
409
# Start new music
410
music = self.music[name]
411
music.setVolume(self.music_volume * self.master_volume)
412
413
if fade_in:
414
self.fadeInMusic(music, 2.0)
415
else:
416
self.audio3d.play(music, looping=True)
417
418
self.current_music = music
419
420
def stopMusic(self):
421
"""Stop current music."""
422
if self.current_music:
423
self.audio3d.stop(self.current_music)
424
self.current_music = None
425
426
def fadeInMusic(self, music, duration):
427
"""Fade in music over time."""
428
target_volume = self.music_volume * self.master_volume
429
music.setVolume(0.0)
430
self.audio3d.play(music, looping=True)
431
432
# Create fade task
433
def fadeTask(task):
434
progress = task.time / duration
435
if progress >= 1.0:
436
music.setVolume(target_volume)
437
return task.done
438
439
volume = target_volume * progress
440
music.setVolume(volume)
441
return task.cont
442
443
taskMgr.add(fadeTask, f"fade-in-{id(music)}")
444
445
def fadeOutMusic(self, music, duration):
446
"""Fade out music over time."""
447
start_volume = music.getVolume()
448
449
def fadeTask(task):
450
progress = task.time / duration
451
if progress >= 1.0:
452
self.audio3d.stop(music)
453
return task.done
454
455
volume = start_volume * (1.0 - progress)
456
music.setVolume(volume)
457
return task.cont
458
459
taskMgr.add(fadeTask, f"fade-out-{id(music)}")
460
461
def setMasterVolume(self, volume):
462
"""Set master volume level."""
463
self.master_volume = max(0.0, min(1.0, volume))
464
465
# Update current music volume
466
if self.current_music:
467
self.current_music.setVolume(self.music_volume * self.master_volume)
468
469
def setSfxVolume(self, volume):
470
"""Set sound effects volume."""
471
self.sfx_volume = max(0.0, min(1.0, volume))
472
473
def setMusicVolume(self, volume):
474
"""Set music volume."""
475
self.music_volume = max(0.0, min(1.0, volume))
476
477
# Update current music
478
if self.current_music:
479
self.current_music.setVolume(self.music_volume * self.master_volume)
480
481
def cleanup(self):
482
"""Clean up audio resources."""
483
self.stopMusic()
484
self.audio3d.disable()
485
self.ignoreAll()
486
```
487
488
## Types
489
490
```python { .api }
491
# Audio status constants
492
class AudioSound:
493
READY = 2 # Sound loaded and ready
494
PLAYING = 3 # Sound currently playing
495
BAD = 4 # Sound failed to load
496
497
# Audio file format support
498
SUPPORTED_AUDIO_FORMATS = [
499
".wav", # WAV (uncompressed)
500
".ogg", # Ogg Vorbis (compressed)
501
".mp3", # MP3 (compressed, if available)
502
".flac" # FLAC (lossless, if available)
503
]
504
```