CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-panda3d

Panda3D is a framework for 3D rendering and game development for Python and C++ programs.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

audio.mddocs/

Audio and Sound

Panda3D provides a comprehensive audio system with support for 3D positional audio, sound effects, music playback, and spatial audio processing for immersive 3D environments.

Capabilities

Audio3DManager - 3D Positional Audio

The Audio3DManager handles 3D spatial audio with automatic distance attenuation, doppler effects, and positional sound placement.

class Audio3DManager:
    def __init__(self, 
                 audio_manager, 
                 listener_target: NodePath,
                 root: NodePath = None,
                 taskPriority: int = 51,
                 autoUpdate: bool = True) -> None:
        """
        Initialize 3D audio manager.
        
        Args:
            audio_manager: Base audio manager instance
            listener_target: NodePath representing listener position (usually camera)
            root: Root node for audio coordinate system
            taskPriority: Priority of audio update task
            autoUpdate: Whether to automatically update audio positions
        """
    
    def loadSfx(self, filename: str) -> AudioSound:
        """Load sound effect file."""
    
    def loadMusic(self, filename: str) -> AudioSound:
        """Load music file."""
    
    def attachSoundToObject(self, sound: AudioSound, object: NodePath) -> None:
        """Attach sound to 3D object for automatic positioning."""
    
    def detachSound(self, sound: AudioSound) -> None:
        """Detach sound from its 3D object."""
    
    def setListenerVelocity(self, velocity: Vec3) -> None:
        """Set listener velocity for doppler effects."""
    
    def setDistanceFactor(self, factor: float) -> None:
        """Set distance scaling factor."""
    
    def setDopplerFactor(self, factor: float) -> None:
        """Set doppler effect strength."""
    
    def setDropOffFactor(self, factor: float) -> None:
        """Set audio drop-off/attenuation factor."""
    
    def attachSoundToObject(self, 
                            sound: AudioSound, 
                            object: NodePath) -> None:
        """Attach sound to follow an object's position."""
    
    def detachSound(self, sound: AudioSound) -> None:
        """Detach sound from object."""
    
    def setSoundVelocity(self, sound: AudioSound, velocity: Vec3) -> None:
        """Set sound source velocity."""
    
    def setSoundVelocityAuto(self, sound: AudioSound) -> None:
        """Enable automatic velocity calculation."""
    
    def setSoundMinDistance(self, sound: AudioSound, distance: float) -> None:
        """Set minimum distance for audio attenuation."""
    
    def setSoundMaxDistance(self, sound: AudioSound, distance: float) -> None:
        """Set maximum distance for audio attenuation."""
    
    def update(self) -> None:
        """Manually update audio positions."""
    
    def disable(self) -> None:
        """Disable 3D audio processing."""
    
    def enable(self) -> None:
        """Enable 3D audio processing."""

AudioSound - Individual Sound Control

AudioSound objects provide detailed control over individual audio sources.

class AudioSound:
    def play(self) -> None:
        """Play the sound."""
    
    def stop(self) -> None:
        """Stop playing the sound."""
    
    def setLoop(self, loop: bool) -> None:
        """Set whether sound should loop."""
    
    def getLoop(self) -> bool:
        """Check if sound is set to loop."""
    
    def setLoopStart(self, start_time: float) -> None:
        """Set loop start time in seconds."""
    
    def setLoopEnd(self, end_time: float) -> None:
        """Set loop end time in seconds."""
    
    def setVolume(self, volume: float) -> None:
        """Set sound volume (0.0 to 1.0)."""
    
    def getVolume(self) -> float:
        """Get current volume."""
    
    def setBalance(self, balance: float) -> None:
        """Set left/right balance (-1.0 to 1.0)."""
    
    def getBalance(self) -> float:
        """Get current balance."""
    
    def setPlayRate(self, rate: float) -> None:
        """Set playback speed multiplier."""
    
    def getPlayRate(self) -> float:
        """Get current playback rate."""
    
    def length(self) -> float:
        """Get sound duration in seconds."""
    
    def getTime(self) -> float:
        """Get current playback position in seconds."""
    
    def setTime(self, time: float) -> None:
        """Set playback position."""
    
    def status(self) -> int:
        """Get playback status (READY, PLAYING, BAD)."""
    
    def finished(self) -> bool:
        """Check if sound has finished playing."""
    
    def ready(self) -> bool:
        """Check if sound is ready to play."""

Basic Audio Management

Simple audio playback without 3D positioning for music and UI sounds.

class AudioManager:
    def getSound(self, filename: str, positional: bool = False) -> AudioSound:
        """Load audio file and return AudioSound object."""
    
    def playSound(self, 
                  sound: AudioSound,
                  looping: bool = False,
                  interrupt: bool = True,
                  volume: float = 1.0,
                  node: NodePath = None) -> None:
        """Play sound with optional 3D positioning."""
    
    def stopSound(self, sound: AudioSound) -> None:
        """Stop playing sound."""
    
    def setVolume(self, volume: float) -> None:
        """Set global volume."""
    
    def getVolume(self) -> float:
        """Get global volume."""
    
    def setSfxVolume(self, volume: float) -> None:
        """Set sound effects volume."""
    
    def getSfxVolume(self) -> float:
        """Get sound effects volume."""
    
    def setMusicVolume(self, volume: float) -> None:
        """Set music volume."""
    
    def getMusicVolume(self) -> float:
        """Get music volume."""
    
    def stopAllSounds(self) -> None:
        """Stop all currently playing sounds."""
    
    def setSoundsActive(self, active: bool) -> None:
        """Enable or disable all sound playback."""
    
    def getSoundsActive(self) -> bool:
        """Check if sounds are active."""

Usage Examples

3D Positional Audio

from direct.showbase.ShowBase import ShowBase
from direct.showbase.Audio3DManager import Audio3DManager
from panda3d.core import Vec3
from direct.task import Task

class Audio3DDemo(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        # Initialize 3D audio manager
        self.audio3d = Audio3DManager(base.sfxManagerList[0], self.camera)
        
        # Load sounds
        self.engine_sound = self.audio3d.loadSfx("sounds/engine.wav")
        self.footsteps = self.audio3d.loadSfx("sounds/footsteps.wav")
        self.ambient = self.audio3d.loadMusic("sounds/ambient.ogg")
        
        # Create moving objects
        self.createMovingObjects()
        
        # Setup audio properties
        self.setupAudioProperties()
        
        # Start background music
        self.audio3d.play(self.ambient, looping=True, volume=0.3)
        
        # Setup controls
        self.setupControls()
    
    def createMovingObjects(self):
        """Create objects that will have attached sounds."""
        # Car with engine sound
        self.car = self.loader.loadModel("models/car")
        self.car.reparentTo(self.render)
        self.car.setPos(-10, 20, 0)
        
        # Attach engine sound to car
        self.audio3d.attachSoundToObject(self.engine_sound, self.car)
        self.audio3d.play(self.engine_sound, looping=True)
        
        # Set up car movement
        self.taskMgr.add(self.moveCar, "move-car")
        
        # Walking character
        self.character = self.loader.loadModel("models/character")
        self.character.reparentTo(self.render)
        self.character.setPos(5, 15, 0)
        
        # Attach footstep sound
        self.audio3d.attachSoundToObject(self.footsteps, self.character)
    
    def setupAudioProperties(self):
        """Configure 3D audio properties."""
        # Set distance factors
        self.audio3d.setDistanceFactor(1.0)
        self.audio3d.setDopplerFactor(1.0)
        self.audio3d.setDropOffFactor(0.1)
        
        # Configure individual sounds
        self.audio3d.setSoundMinDistance(self.engine_sound, 5.0)
        self.audio3d.setSoundMaxDistance(self.engine_sound, 50.0)
        
        self.audio3d.setSoundMinDistance(self.footsteps, 2.0)
        self.audio3d.setSoundMaxDistance(self.footsteps, 20.0)
    
    def moveCar(self, task):
        """Move car in circle for doppler demonstration."""
        angle = task.time * 30  # 30 degrees per second
        radius = 15
        
        x = radius * math.cos(math.radians(angle))
        y = 20 + radius * math.sin(math.radians(angle))
        
        self.car.setPos(x, y, 0)
        self.car.setH(angle + 90)  # Face movement direction
        
        return task.cont
    
    def setupControls(self):
        """Setup keyboard controls."""
        self.accept("1", self.toggleEngine)
        self.accept("2", self.playFootsteps)
        self.accept("3", self.adjustVolume, [0.1])
        self.accept("4", self.adjustVolume, [-0.1])
        
        # Movement controls for listener
        self.accept("arrow_left", self.moveListener, [Vec3(-1, 0, 0)])
        self.accept("arrow_right", self.moveListener, [Vec3(1, 0, 0)])
        self.accept("arrow_up", self.moveListener, [Vec3(0, 1, 0)])
        self.accept("arrow_down", self.moveListener, [Vec3(0, -1, 0)])
    
    def toggleEngine(self):
        """Toggle engine sound."""
        if self.engine_sound.status() == AudioSound.PLAYING:
            self.audio3d.stop(self.engine_sound)
        else:
            self.audio3d.play(self.engine_sound, looping=True)
    
    def playFootsteps(self):
        """Play footstep sound."""
        self.audio3d.play(self.footsteps)
    
    def adjustVolume(self, change):
        """Adjust global volume."""
        current = base.musicManager.getVolume()
        new_volume = max(0.0, min(1.0, current + change))
        base.musicManager.setVolume(new_volume)
        print(f"Volume: {new_volume:.1f}")
    
    def moveListener(self, direction):
        """Move audio listener (camera)."""
        current_pos = self.camera.getPos()
        new_pos = current_pos + direction * 2
        self.camera.setPos(new_pos)
        print(f"Listener position: {new_pos}")

import math
app = Audio3DDemo()
app.run()

Sound Manager Class

from direct.showbase.DirectObject import DirectObject
from direct.showbase.Audio3DManager import Audio3DManager

class SoundManager(DirectObject):
    """Centralized sound management system."""
    
    def __init__(self, listener_target):
        DirectObject.__init__(self)
        
        # Initialize audio systems
        self.audio3d = Audio3DManager(base.sfxManagerList[0], listener_target)
        
        # Sound libraries
        self.sounds = {}
        self.music = {}
        self.current_music = None
        
        # Volume settings
        self.master_volume = 1.0
        self.sfx_volume = 1.0
        self.music_volume = 0.7
        
        # Load sound libraries
        self.loadSounds()
    
    def loadSounds(self):
        """Load all game sounds."""
        # Sound effects
        sound_files = {
            "button_click": "sounds/ui/button_click.wav",
            "explosion": "sounds/fx/explosion.wav",
            "pickup": "sounds/fx/pickup.wav",
            "footstep": "sounds/character/footstep.wav",
            "jump": "sounds/character/jump.wav"
        }
        
        for name, filename in sound_files.items():
            try:
                self.sounds[name] = self.audio3d.loadSfx(filename)
            except:
                print(f"Failed to load sound: {filename}")
        
        # Music tracks
        music_files = {
            "menu": "music/menu_theme.ogg",
            "level1": "music/level1_bg.ogg",
            "victory": "music/victory.ogg"
        }
        
        for name, filename in music_files.items():
            try:
                self.music[name] = self.audio3d.loadMusic(filename)
            except:
                print(f"Failed to load music: {filename}")
    
    def playSfx(self, name, volume=None, position=None, looping=False):
        """Play sound effect."""
        if name not in self.sounds:
            print(f"Sound not found: {name}")
            return
        
        sound = self.sounds[name]
        
        # Set volume
        if volume is None:
            volume = self.sfx_volume
        sound.setVolume(volume * self.master_volume)
        
        # Set position if specified
        if position:
            self.audio3d.attachSoundToObject(sound, position)
        
        # Play sound
        self.audio3d.play(sound, looping=looping)
    
    def playMusic(self, name, fade_in=False, fade_out_current=False):
        """Play background music."""
        if name not in self.music:
            print(f"Music not found: {name}")
            return
        
        # Stop current music
        if self.current_music:
            if fade_out_current:
                self.fadeOutMusic(self.current_music, 1.0)
            else:
                self.audio3d.stop(self.current_music)
        
        # Start new music
        music = self.music[name]
        music.setVolume(self.music_volume * self.master_volume)
        
        if fade_in:
            self.fadeInMusic(music, 2.0)
        else:
            self.audio3d.play(music, looping=True)
        
        self.current_music = music
    
    def stopMusic(self):
        """Stop current music."""
        if self.current_music:
            self.audio3d.stop(self.current_music)
            self.current_music = None
    
    def fadeInMusic(self, music, duration):
        """Fade in music over time."""
        target_volume = self.music_volume * self.master_volume
        music.setVolume(0.0)
        self.audio3d.play(music, looping=True)
        
        # Create fade task
        def fadeTask(task):
            progress = task.time / duration
            if progress >= 1.0:
                music.setVolume(target_volume)
                return task.done
            
            volume = target_volume * progress
            music.setVolume(volume)
            return task.cont
        
        taskMgr.add(fadeTask, f"fade-in-{id(music)}")
    
    def fadeOutMusic(self, music, duration):
        """Fade out music over time."""
        start_volume = music.getVolume()
        
        def fadeTask(task):
            progress = task.time / duration
            if progress >= 1.0:
                self.audio3d.stop(music)
                return task.done
            
            volume = start_volume * (1.0 - progress)
            music.setVolume(volume)
            return task.cont
        
        taskMgr.add(fadeTask, f"fade-out-{id(music)}")
    
    def setMasterVolume(self, volume):
        """Set master volume level."""
        self.master_volume = max(0.0, min(1.0, volume))
        
        # Update current music volume
        if self.current_music:
            self.current_music.setVolume(self.music_volume * self.master_volume)
    
    def setSfxVolume(self, volume):
        """Set sound effects volume."""
        self.sfx_volume = max(0.0, min(1.0, volume))
    
    def setMusicVolume(self, volume):
        """Set music volume."""
        self.music_volume = max(0.0, min(1.0, volume))
        
        # Update current music
        if self.current_music:
            self.current_music.setVolume(self.music_volume * self.master_volume)
    
    def cleanup(self):
        """Clean up audio resources."""
        self.stopMusic()
        self.audio3d.disable()
        self.ignoreAll()

Types

# Audio status constants
class AudioSound:
    READY = 2      # Sound loaded and ready
    PLAYING = 3    # Sound currently playing  
    BAD = 4        # Sound failed to load

# Audio file format support
SUPPORTED_AUDIO_FORMATS = [
    ".wav",        # WAV (uncompressed)
    ".ogg",        # Ogg Vorbis (compressed)
    ".mp3",        # MP3 (compressed, if available)
    ".flac"        # FLAC (lossless, if available)
]

Install with Tessl CLI

npx tessl i tessl/pypi-panda3d

docs

animation.md

application-framework.md

asset-loading.md

audio.md

index.md

input.md

mathematics.md

scene-graph.md

task-event.md

user-interface.md

tile.json