stop audio from activating every tic

Hello,

i have a light machine gun that fires in true pulse mode.
i wanted to add a sound with:


import aud
        
sound = aud.Factory.file(logic.expandPath("//sounds/" + GD[own['carry_weapon']]['sound_file']))
sound.loop(-1)
aud.device().play(sound)

while this kinda works, it fires every tic. Is there a way to prevent it from activating every tic, so that it just loops,
and not play it like 10 times at any given time?

Check to see if the sound is playing, if not play it?

i tried this:


        import aud
        
        device = aud.device()
        sound = aud.Factory.file(logic.expandPath("//sounds/" + GD[own['carry_weapon']]['sound_file']))
        handle = device.play(sound)
        
        if handle.status is not aud.AUD_STATUS_PLAYING:
            handle.play()

or 

        import aud
        
        device = aud.device()
        sound = aud.Factory.file(logic.expandPath("//sounds/" + GD[own['carry_weapon']]['sound_file']))
        handle = device.play(sound)
        
        if not handle.status:
            handle.play()


No idea if i did it the right way, but it is still playing every tic.
every time handle is called(every tic) it just gonna play the sound 60 times a second.

Is there any reason why you start the audio again and again and again? Wouldn’t it be enough to start it once?

no, and yes that is my question

Is there a way to prevent it from activating every tic

till now unsuccessful.

this part (and more) gets imported by a script in true pulse


        if left_click and not 'mouse_ico' in ray[0] and own['carry_weapon'] and not own['hide_weapon']:
            
            #in true pulse mode
            if own['carry_weapon'] == 'lmg':
                print('Shoot')
                shoot(cont,ray)


            #normal mode    
            elif left_click == 1:
                print('Shoot')
                shoot(cont,ray)

then we get to the real deal and the place i want to play the audio:


def shoot(cont,ray):
    
    own     = cont.owner
    
    GD = script.check_GD('weapon_list')
    
    if not GD:
        return


    weapon_info = GD[own['carry_weapon']]


    damage          = weapon_info['damage']
    ammo            = weapon_info['ammo']
    bullet_use      = weapon_info['bullet_use']
 
    if not ammo > 0:
        print('* out of ammmo! reload or find more')
        return
    
    # play actions
    armature = script.get_object('Main', 'arms_armature')
    weapon_armature = script.get_object('Main',own['carry_weapon']+'_armature')
    
    if own['carry_weapon'] and own['weapon_equipped']:


        #firing weapon  
        # name, start, end, layer, priority, blendin, mode
        weapon_armature.playAction(own['carry_weapon']+'_firing', 0, 6, 1,  0, 0, 0)
        armature.playAction('aiming_with_'+own['carry_weapon'], 30, 36, 0,  2, 0, 0) 


        # subtract ammo
        GD[own['carry_weapon']]['ammo'] = ammo - bullet_use
        
        #audio
        device = aud.device()
        sound = aud.Factory.file(logic.expandPath("//sounds/" + GD[own['carry_weapon']]['sound_file']))
        sound.loop(-1)
        handle = device.play(sound)   
        
        #damage handling
        if 'health' in ray[0]:
            ray[0]['health'] -= damage
            
            if not ray[0]['health'] > 0:
                ray[0].endObject()
                print('killed: ',ray[0])

the order is wrong.

this line plays your audio

handle = device.play(sound)

and you run it every tic

here is a possible solution

audioclass.py


import aud
import os

class Audio:
    
    def __init__(self,path=None,folder="",audiofile=None):
        self.device = aud.device()
        self.handle = None
        self.sound = None
        
        if path:
           self.path = os.path.join(path,folder)
        else:
            self.path = os.path.join(os.getcwd(),folder)
        
        if audiofile:
            self.load(audiofile)
            
    def load(self,filename):
        if filename:
            filename = os.path.join(self.path,filename) 
            self.sound = aud.Factory(filename)    
    
    def play(self):
        if self.sound:
            self.handle = self.device.play(self.sound)
            
    def stop(self):
        if self.handle:
            self.handle.stop()
            
    def isplaying(self):
        if self.handle:
            return self.handle.status
        else:
            return False



test.py


import bge
from audioclass import Audio

cont = bge.logic.getCurrentController()

own = cont.owner

if "sound" not in own:
    own["sound"] = Audio()
    own["sound"].load("Clip.mp3")
    
if "sound" in own:
    
    if not own["sound"].isplaying():
        own["sound"].play()

hope it helps

here is a possible solution
hope it helps

Thanks allot, with a few adjustments i got it working.

#edit
it works but slows animations down a bit so it’s not working for me

If "Sound"  not in own:
    own['Sound']=own.scene.addObject("soundMakerObject",own,0)
    own|'Sound'].setParent(own)
    own['Sound']['Timer'] = 60
else:
    if own['Sound']['Timer'] >=1:
        own['SoundTimer']-=1
    else:
        own['Sound'].endObject()
        del own['Sound']

No fancy classes required.

depends on what you want.

your code can reduces to one line

scene.addObject(“soundMakerObject”,own,60)

My question was more intended towards: Do you really need to call play() at each single frame (as this is what your original code is doing)?

I just want it to be in that spot of the script, so it can handle everything for every weapon i equip.
i’m not like most guys here who scripting 5 lines a code a page and get a mess with 1000+ scripts, 1 script to handle all weapons is what i want/have. I just need a way to get the sound in there as well.

if there is no good solution for it tell me and i build another script to handle the sound, but as i said i prefer to have it there.
and to be honest it looked the right way.

(if i run a brick in true pulse, it wont play the sound every tick, i thought python would/could do the same)

just add a object from a hidden layer per weapon, or even per projectile

(projectile adds noise object to scene)

I use 1 script for every weapon and it’s like 75 lines including reload and animate weapon etc.

for barrel in gun:
    if own['Hand']['Equip']['Clip']>=1:
        own.scene.addObject(own['Hand']['Equip']['Projectile'])
        own['Hand']['Equip']['Clip']-=1

I think this is more a design question. Remember the code does exactly what you tell it to do. This is not necessarily what you expect it to do.

In your case you run the code at each single frame. The code is supposed to start playing a sound. This is what it does. That is why I asked. You request and the description what you did, do not match. You want the sound to be played once.

Solution -> the code to play the sound should run once (not at each single frame).

There are several options. The easiest is to get rid of the True Level Triggering, because this causes the repeated playing.

Another option is to check if a sound is already played. This wastes a lot of time because the majority of checks will tell it is playing Finally it is a good method to measure the event when playing done (there is not brick to measure the end of such sounds).

To check if the sound is already playing you can (as suggested above) store the playback object (called aud.Handle) somewhere e.g. in a property. This allows you to access it at a later stage e.g. to check the status similar to your post#3.

sample: setup and play sound:


import aud
import bge 

path = bge.logic.expandPath("//sounds/")
sound = aud.Factory(path+"smb_mariodie.wav")

device = aud.device()
playback = device.play(sound)

owner = bge.logic.getCurrentController().owner
owner["playback"] = playback

check sound:


import bge 

owner = bge.logic.getCurrentController().owner

playback = owner["playback"]

isPlaying = playback.status (in 2.78 it is boolean True=playing or paused, False=stopped or ended)

I think this is more a design question

Indeed pff the solution was so simple, dunno why i didnt thought of this earlier (no need to check if sound is playing).

I kept trying/checking in there (tunnel vision), then i looked at bpr code and thought… timer… haha thats not going to work, but the del function could be usefull.

solved by doing:


        if left_click and not 'mouse_ico' in ray[0] and own['carry_weapon'] and not own['hide_weapon']:
            
            #in true pulse mode
            if own['carry_weapon'] == 'lmg':
                #print('Shoot')
                shoot(cont,ray)
            #normal mode    
            elif left_click == 1:
                #print('Shoot')
                shoot(cont,ray)
                
<b>        elif 'play_sound' in own:</b>
<b>            own['play_sound'].stop()</b>
<b>            del own['play_sound']    </b>
                            
        elif reload and own['carry_weapon'] and own['weapon_equipped']:
            #print('- Reload')
            reload_weapon(own['carry_weapon']) 

and this on the spot i wanted:


        if not 'play_sound' in own:
            device = aud.device()
            sound = aud.Factory.file(logic.expandPath("//sounds/" + GD[own['carry_weapon']]['sound_file']))


            own['play_sound'] = device.play(sound)
    
            if own['carry_weapon'] == 'lmg':
                own['play_sound'].loop_count = -1

now it plays once in loop mode for the lmg and the single shot weapons just calling it once every click.
no slowdowns anymore and everything works.

should be a good solution right?