Python - Audio module using aud library

Hey,

I wrote this module to deal with all of the in-game sound-effects:
Module All_versions

The issue that I’m having with it is that it opens sound files, but doesn’t close them properly.
After about a half hour, this causes it to stop loading any files (Images, sounds, etc.)
I used Activity Monitor to confirm that it is opening lots of sounds files.

I know that there are solutions that use logic bricks, but I would prefer to not resort to them.

I’ve tried a couple different solutions, like keeping the handles from the sounds after they’re played, saving the factories/devices used, etc.
But nothing has worked.

Documentation on the aud library:
http://www.blender.org/documentation/blender_python_api_2_62_0/aud.html

Does anyone know more about the aud library/Know how to handle closing the sound files (“With” doesn’t work because there are no exit methods)

Thanks!

i just noticed that with “normal use” cause a big stress for the CPU each time the file is read/start.(reading the API seem that this happen since the file is on the HD, not in the ram)

this happen using both , aud module or actuator.

the device shoud have a method to stop all sounds (device.stopAll() if not wrong)

you can keep the handle to stop just that sound:

handle = device.play()
logic.handles[“x”] = handle

(…)

logic.handles[“x”].stop()

i also want make a class to manage the sounds(store all sounds on the ram) , but not done yet

I’m pretty sure that I solved my own issue:

This new version buffers the sounds, stores each one individually, then plays it back whenever it is needed.
Activity Monitor shows Blender having no sound files open.

Success?

Also, if anyone wants to use soundControl, feel free.

the difference using buffer is pretty giant, (in the same stress-blend(triggering a lot of sounds) the logic change from 6.00ms to 0.20)

The original problem was that in the play() function, you were constantly creating a new factory to play. You should create the factory once and store that reference, and then use that reference to play the sound as many times as necessary. Your updated solution should work fine, but you can only have one instance of a sound playing at a time this way because you’re storing sound handles (a “play” of a sound) rather than the factories (the ability to create “plays” of a sound). This may or may not be a big deal depending on your application.

Thanks for the responses,

I can store unbuffered handles, then stop them as the game exits, but for my case, I’d rather just buffer them because I will use the same couple 100s of times, and they don’t take long to buffer.
Plus, even if I stop the handles before the game exits, one of each sound file is left open each time I exit/play game (At least, according to my Monitor.) This is fairly minor, it just seems like good practice to avoid a solution that leaves some files open.
If storing handles unbuffered, run this at game exit:


# Stop any sound handles to prevent memory leak
# Called by exit.py as game exits
def exit():
        for sound in storedSounds.values():
                sound.stop()

@ SolarLune
Thanks for the suggestion to use Factories instead of handles, in most cases I would do it that way, but for this game, I’d rather only have 1 sound effect playing at a time :slight_smile:

Hey, i done the class, to me seem pretty cool .
in old version i tried to solve all path inside the class as your module , but i noticed is not so flexible , can be a problem when for example the object Monster has a sound “argh” and also a Goblin has “argh”, so i decided to keep all problem about the path and name file to the “end user” (out of class).

all other is managed from the class , volume 3D , cleaning of the handles , and validity of the gameObject .
in fact there a argument not default , that is the gameobject source (to make 3D sound).

that is the class:


import bge, aud


device = aud.device()
device.distance_model = aud.AUD_DISTANCE_MODEL_INVERSE_CLAMPED


KX_GameObject = bge.types.KX_GameObject






class Audio:
    def __init__(self):
        self.buffered = {}
        self.handles = []
        self.scene = bge.logic.getCurrentScene()
        self.music = None
        
        if not self.update in self.scene.pre_draw:
            self.scene.pre_draw.append(self.update)
        
    def play(self, file, gob, loop=0):
        d = self.buffered
        if not file in d:
            d[file] = aud.Factory.buffer(aud.Factory(file))
        buffered = d[file]
        if isinstance(gob, KX_GameObject):
            handle = device.play(buffered)
            handle.relative = False
            handle.loop_count = int(loop)
            self.handles.append((handle, gob))
        
    def update(self):
        cam = self.scene.active_camera
        device.listener_location       = cam.worldPosition 
        device.listener_orientation    = cam.worldOrientation.to_quaternion()


        self.handles = [i for i in self.handles if i[0].status and not i[1].invalid]
        for handle,gob in self.handles:
            handle.location = gob.worldPosition
            
    def playMusic(self, file, vol=1.0):
        if self.music: 
            self.music.stop()
        handle = device.play(aud.Factory(file))
        handle.volume = vol
        handle.loop_count = -1
        self.music = handle
        
        
audio = Audio()

this is the code of the end user:


import bge
from audio_module import audio
#audio = audio_module.audio


audio.playMusic(bge.logic.expandPath("//audio/musics/") + "01-Look Who's Talking Now-MaRJuaNa.mp3", vol=0.5)


def main(cont):
    own = cont.owner
    if not "init" in own:
        own["init"] = 1
        sounds = ["punch", "kick"]
        path = bge.logic.expandPath("//audio/sounds/") 
        own["sounds"] = {sound: path + sound + ".ogg" for sound in sounds}
    
    sens = cont.sensors
    if sens[0].positive:
        sounds = own["sounds"]
        
        if sens[1].positive:
            audio.play(sounds["kick"], own)
            
        elif sens[2].positive:
            audio.play(sounds["punch"], own)

lack a check to avoid by mistake of buffering file big , that can be played from playMusic()

:wink: