As Panda’s distribution maintainer, I’d love to see a free audio library connected to panda. Unfortunately, I can’t do it myself.

I’m interested in the development of these tutorials. Perhaps someday we’ll integrate these into the distribution.

I’m certainly wrong but i though Fmod was free as long as you don’t sell your game?

It is. He mentioned that if you want to sell your game FMOD starts to get expensive.

I modified IPKNIGHTLY’s code so that it now runs in 1.3.2

#                                                                            #
# File: musicBox.py                                                          #
#                                                                            #
# Description: This module incorporates PyGame.Mixer with Pand3D code.       #
#                                                                            #
# Needed: Panda 1.0.5 w/ Python2.4, models\MusicBox.egg, models\box.jpg      #
#         models\panda.jpg, music\musicbox.mp3, music\openclose.mp3          #
#         music\close.wav, music\open.wav, music\musicbox.wav                #
#         pygame-1.7.1release.win32-py2.4.exe                                #
#                                                                            #
#                                                                            #
#                                                                            #
#                                                                            #
# PyGame specific and misc code written by David Lettier. The rest was       # 
# written by the authors listed below.                                       #
#                                                                            #
# musicbox.wav, open.wav, and close.wav converted and edited by David        #
# Lettier. Audacity was used.                                                #
#                                                                            #
#                                                                            #
#                                                                            #
# All files provided "as is" without warranties expressed or implied.        #
#                                                                            #
# (C) 2005 David Lettier.                                                    #
#                                                                            #

# Author: Shao Zhang, Phil Saltzman, and Elan Ruskin
# Last Updated: 4/20/2005
# This tutorial shows how to load, play, and manipulate sounds
# and sound intervals in a panda project.
import direct
import sys , os
from direct.showbase.DirectObject import DirectObject
from direct.gui.DirectSlider import DirectSlider
import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.gui.DirectGui import *

import pygame # import the pygame module
from pygame.locals import *

# here we check if the SDL_Mixer is available

if not pygame.mixer: print 'Warning, sound disabled'

# want to use PyGame.Mixer instead of FMOD?

usePyGame = True # yes 

class World( DirectObject ):
  def __init__( self ):
    #Our standard title and instructions text
    self.title = OnscreenText( text = "Panda3D: Tutorial - Musicbox(sounds)", style = 1, fg = ( 1, 1, 1, 1), pos = ( 0.8, -0.95 ), scale = .07 )
    self.escapeEventText = OnscreenText( text = "ESC: Quit", style = 1, fg = ( 1, 1, 1, 1 ), pos = ( -1.3, 0.95 ), align = TextNode.ALeft, scale = .05 )
    #Set up the key input 
    self.accept( 'escape', sys.exit )
    # set the background color to black
    base.setBackgroundColor( 0, 0, 0 )
    # create a point light
    plight = PointLight( 'plight' ) # new point light
    plight.setColor( VBase4( 0.8, 0.8, 0.8, 1 ) ) # set the color
    plnp = render.attachNewNode( plight.upcastToPandaNode( ) ) # attach it to the render 
    plnp.setPos( 0, -70, 10 ) # set it's position 10 units before the music box and 
    render.setLight( plnp ) # turn on the light    
    if usePyGame: # if we are using pygame
        self.pyGameInit( ) # initialize pygame so we can use it
        print "Using PyGame."
        print "Using Panda3D default sound library."

    #Fix the camera position
    base.disableMouse( )

    #Loading sounds is done in a similar way to loading other things
    #Loading the main music box song
    if usePyGame: # if we are using pygame
        pygame.mixer.music.load( 'music/musicbox.wav' ) # load the music box song
                                                        # pygame needs it to be wav instead of mp3
        pygame.mixer.music.set_volume( .5 ) # set the volume to half way
        pygame.mixer.music.play( -1 ) # loop it for ever
        pygame.mixer.music.pause( ) # pause for now so we can resume later
        self.musicBoxSound = base.loadMusic( 'music/musicbox.mp3' )
        self.musicBoxSound.setVolume( .5 )   #Volume is a percentage from 0 to 1
        self.musicBoxSound.setLoopCount( 0 ) #0 means loop forever, 1 (default) means
                                             #play once. 2 or higher means play that
                                             #many times

    #Sound objects do not have a pause function, just play and stop. So we will
    #Use this variable to keep track of where the sound is at when it was stoped
    #to impliment pausing
    self.musicTime = 0
    if usePyGame: # if we are using pygame       
        self.lidSfxOpen = self.pyGameLoadSound( 'music/open.wav' ) # load the open sound
                                                                   # i broke up the openclose sound
                                                                   # because pygame.mixer didn't have a 
                                                                   # start pos argument
                                                                   # plus this is just easier and makes sense IMO
                                                                   # also it needed to be wav instead of mp3
        self.lidSfxClose = self.pyGameLoadSound( 'music/close.wav' ) # same
        # on an interesting side note: this sound effect seems to be the same
        # one used in the game 'The Elder Scrolls 3: Morrowind'
        #Loading the open/close effect
        #loadSFX and loadMusic are identical. They are often used for organization
        #(loadMusic is used for background music, loadSfx is used for other effects)
        self.lidSfx = base.loadSfx( 'music/openclose.mp3' )
        #The open/close file has both effects in it. Fortunatly we can use intervals
        #to easily define parts of a sound file to play
        self.lidOpenSfx = SoundInterval( self.lidSfx, duration = 2, startTime = 0 )
        self.lidCloseSfx = SoundInterval( self.lidSfx, startTime = 5 )

    #For this tutorial, it seemed appropriate to have on screen controls. The
    #following code creates them
    #The slider itself. It calls self.setMusicBoxVolume when changed
    self.slider = DirectSlider( pos = Vec3( 0, 0, .7 ), value = .50, command = self.setMusicBoxVolume )
    #This is a label for a slider
    # here i changed it a bit to display what the actual volume is on screen
    self.sliderText = OnscreenText( "Volume " + str( self.slider.guiItem.getValue( ) ), style = 1, fg = ( 1, 1, 1, 1 ), shadow = ( .9, .9, .9, .5 ), pos = (0, 0.8 ), scale = .07 )
    #A button that calls self.toggleMusicBox when pressed
    self.button = DirectButton( pos = Vec3( .7, 0, .7 ), text = "Open Box", scale = .1, pad = ( .5, .5 ), rolloverSound = None, clickSound = None, command = self.toggleMusicBox )

    #A variable to represent the state of the simulation. It starts closed
    self.boxOpen = False

    #Here we load and set up the music box. It was modeled in a complex way, so
    #setting it up will be complicated
    self.musicBox = loader.loadModel( 'models/MusicBox' )
    self.musicBox.setPos( 0, 60, -10 )
    self.musicBox.reparentTo( render )
    #Just like the scene graph contains hierarchies of nodes, so can
    #models. You can get the NodePath for the node using the find
    #function, and then you can animate the model by moving its parts
    #To see the hierarchy of a model, use, the ls function
    #self.musicBox.ls() prints out the entire hierarchy of the model

    #Finding pieces of the model
    self.Lid   = self.musicBox.find( '**/lid' )
    self.Panda = self.musicBox.find( '**/turningthing' )  

    #This model was made with the hinge in the wrong place
    #this is here so we have something to turn
    self.HingeNode = self.musicBox.find( '**/box' ).attachNewNode( 'nHingeNode' )
    self.HingeNode.setPos( .8659, 6.5, 5.4 )
    #WRT - ie with respect to. Reparents the object without changing
    #its position, size, or orientation
    self.Lid.wrtReparentTo( self.HingeNode )
    self.HingeNode.setHpr( 0, 90, 0 )
    if usePyGame: # if we are using pygame
        # here we are not using 'self.lidClose/OpenSfx' so we don't need to use a Parallel

        #This sets up an interval to play the close sound and actually close the box
        #at the same time.
        self.lidClose = LerpFunc( self.HingeNode.setP, duration = 2, fromData = 0, toData = 90, blendType = 'easeInOut' )
        #Same thing for opening the box
        self.lidOpen = LerpFunc( self.HingeNode.setP, duration = 2, fromData = 90, toData = 0, blendType = 'easeInOut' )
        #This sets up an interval to play the close sound and actually close the box
        #at the same time.
        self.lidClose = Parallel(
          LerpFunc( self.HingeNode.setP, duration = 2, fromData = 0, toData = 90, blendType = 'easeInOut' )
        #Same thing for opening the box
        self.lidOpen = Parallel(
          LerpFunc( self.HingeNode.setP, duration = 2, fromData = 90, toData = 0, blendType ='easeInOut' )

    #The interval for turning the panda
    self.PandaTurn = self.Panda.hprInterval( 7, Vec3( 360, 0, 0 ) )
    #Do a quick loop and pause to set it as a looping interval so it can be
    #started with resume and loop properly
    self.PandaTurn.loop( )
    self.PandaTurn.pause( )
  # begin the pyGameInit member function
  # this intializes pygame for us
  def pyGameInit( self ):
    pygame.init( )
    # end pyGameInit
  # begin the pyGameLoadSound member function
  def pyGameLoadSound( self, filename ): # accept filename as an argument
    class NoneSound: # dummy class
        def play( self ): pass # so there is no error is we call .play( )
                               # this is because sdl mixer may not exist
    # here we check if pygame.mixer was initialized
    if not pygame.mixer or not pygame.mixer.get_init( ):
        return NoneSound( ) # if not then return the dummy class
    try: # try to load the sound file
        sound = pygame.mixer.Sound( filename )
    except pygame.error, message: # can't do it
        print 'Cannot load sound:', filename # print the file we can't load
        raise SystemExit, message # there is a problem so exit
    return sound # everything is fine so return the new sound object

    # end pyGameLoadSound
  def setMusicBoxVolume( self ):
    if usePyGame: # if we are using pygame
        # same as for default Panda3D sound library
        newVol = self.slider.guiItem.getValue( ) # get value
        pygame.mixer.music.set_volume( newVol ) # set the new volume
        #Simply reads the current value from the slider and sets it in the sound
        newVol = self.slider.guiItem.getValue( )
        self.musicBoxSound.setVolume( newVol )
    # here we update the volume level on screen
    self.sliderText.destroy( ) # must destroy what was there
                               # or it would just write over it making it unreadable
    self.sliderText = OnscreenText( "Volume " + str( self.slider.guiItem.getValue() ), style = 1, fg = ( 1, 1, 1, 1 ), shadow = ( .9, .9, .9, .5 ), pos = (0, 0.8 ), scale = .07 )

  def toggleMusicBox( self ):
    if self.boxOpen:
        #close the box
        self.lidClose.start()               #Start the close box interval
        self.PandaTurn.pause()              #Pause the figurine turning
        if usePyGame: # if we are using pygame.mixer
            self.lidSfxClose.play(  ) # play the close sound effect
            # here is what you could do to resemble the original tutorial
            #self.musicTime = pygame.mixer.music.get_pos( )
            pygame.mixer.music.pause( ) # pause the streaming music
                                        # fortunately pygame music module
                                        # has pause and unpause functions
                                        # however you can only stream
                                        # one piece of music at one time
                                        # unless you queue them
            #Save the current time of the music box song
            self.musicTime = self.musicBoxSound.getTime( ) 
            self.musicBoxSound.stop( )           #Stop the music box song
        self.button[ 'text' ] = "Open Box"    #Prepare to change button label
        #open the box
        self.lidOpen.start( )                #Start the open box interval
        self.PandaTurn.resume( )             #Resume the figuring turning
        if usePyGame: # if we are using pygame.mixer
            self.lidSfxOpen.play(  ) # play the open sound

            pygame.mixer.music.unpause(  ) # resume the streamed music
            #Reset the time of the music box song so it starts where it left off
            self.musicBoxSound.setTime( self.musicTime )
            self.musicBoxSound.play( ) #Play the music box song
        self.button[ 'text' ] = "Close Box"   #Prepare to change button label
    self.button.setText( )                 #Actually change the button label
    self.boxOpen = not self.boxOpen        #Set our state to opposite what it was
                                           #(closed to open or open to closed)

# and we can run!

w = World()


# In the end the player doesn't know the difference, however, we do.
# Now we don't need to use FMOD (which is what Panda3D uses) so we can
# save money, which is important if your an independent game developer with
# a low budget (or none for that matter). Hopefully, Panda3D will latter support
# sdl_mixer or OpenAL in future versions. It already supports Miles and FMOD.

Hi guys,

Sorry for bumping into an old thread, but since it’s among P3d’s sample projects in the documentation, I was wondering if anyone has still got a copy of lettier’s project around, and if updated to latest V 1.54 even better! :wink:

There aren’t many audio samples for the engine so I think this exapmple shouldn’t be left into oblivion.

regards, and thanks for any help.


It’s a bit unnecessary now that we have the OpenAL library to use in addition to FMOD, although OpenAL on Linux seems to be fraught with difficulties as well.

Yeah there is no need for this thing any more.

However, I do agree that there could be more audio samples/tutorials. As I am a sound designer, it’s one thing I’ve been focusing on, so I’ve collected a good amount of info on it. I’ve been thinking about writing a tutorial (or a few) on it, if anyone thinks they could use help with it, as I’d love to finally have a way to give back to the community that has helped me so much.

On the subject, what would be the best form for a tutorial. I feel that just throwing a heavily commented .py file with necessary media into a zip file can be pretty limited. HTML feels like it might be overkill though, and not as convenient, but maybe I’m wrong there. Any suggestions for a tutorial medium?

I’m interested in your tutorial :wink:

An HTML page sounds good for me…

Sweet, at least one person will benefit, I’ll get to work on it tonight then. :slight_smile:
It’ll be nice to have a reference I can go back to too, once my audio code is obscured by the rest of my game. :laughing: Might even be an opportunity to brush up on some HTML, I’ll try and make it look pretty.

I haven’t yet used it myself, so I’m not speaking from experience, but I thought I read that Panda’s OpenAl implementation is slow and a resource hog, even on Windows. Is this not true?

Its not without its problems but i would not say its a resource hog and super slow.

The 1.6.0 release will speed OpenAL a bit up and fix a few bugs here and there. Although I tried it without much problems, I still recommend FMOD for non-commercial games.

I agree completely. OpenAL is fine for basic sound needs, and it’s nice to be able to release the game commercially if that’s what you’re going for, but FMOD is better in a lot of ways, and is a standard in game audio.

Also, I only get DSP with FMOD. Is this simply a limitation of OpenAL? I’ve never used it before with anything else, so I don’t know if OpenAL does any DSP at all.

Cool to see all these new replies. :wink:
And I’m glad the news about P3d 1.60 let foresee a better future on the sound part. Let’s hope they include some updated examples on OpenAL, I’m really looking forward to testing this new release out.

@AamesxDavid: I’d also be interested in your tutorials. From your perspective (as a sound designer), you must be doing some interesting things in the audio field. Please let us know if you ever release them.

regards, and thanks for all the interesting ideas posted.

More then one person. IN FACT if its printable (margins not over the page e.t.c) I will convert it to a pdf and send it back to you for posting.

That would be great, thanks. :slight_smile:

I appreciate all of the responses; the tutorial-making is in full swing now, and will be finished in a couple of days.

Does HTML count as printable? There aren’t really margins for that, by default anyway. I got a response saying that it was a good format for a tutorial. I can put it in .doc format also if that would work better for pdf-ing.

Also, any input on more things to include would be great. As of now I have:

Playing/Looping Sound
Adding DSP
Making Sounds 3D
Moving 3D Sounds

And one example that mixes them all together. There are subdivisions for a lot of things, but those are the overarching ideas. Anything in particular you’d like focus on, or a new category you’d like, I’d be happy to add it in. Thanks again for the interest! :slight_smile:

Sounds very good!