Python-newbie
Python-newbie

Reputation: 41

Kivy and in-game sounds: Game update loop waits for sound to finish before continuing [FPS issues using SoundLoader in Kivy]

I'm learning to program Python by making a game using Kivy, but I'm having trouble implementing sounds for different events (eg. shield_on.play() when shield-item is picked up.) because the game update loop appears to halt for a short while until the sound has finished playing. I've made a short version of the relevant code here...

shield_on = soundLoader('shield_on.wav')
class game(Widget):
#...loads of other stuff...

    def update_loop(foo):
        self.player_one.update()
        self.player_two.update()
        self.player_item_collision_detector()
        if "game_file_says_player_one's_shields_are on":
            self.player_one.drawShield()
            shield_on.play()

Presently, I simply load my sounds globally. I know that's bad, but they're also my only globals. Then there is a Widget containing the game itself which has a lot of stuff and an update loop... it updates the player positions, checks for collisions with items - and on collision the item, here the shield, is registered as "on" in a game-file. Then the update-loop checks that game-file for the status of "shields", sees they are on and should play the sound.

The sound plays just fine, however the loop appears to halt until its finished playing the sound. Essentially, the players stop for a microsecond. How can I make the update loop not wait for the sounds to finish...?

Upvotes: 1

Views: 828

Answers (2)

Python-newbie
Python-newbie

Reputation: 41

Explanation and workaround with PyGame:

The cause of the problem is explained here: github.com/kivy/kivy/issues/2728 Essentially, the SoundLoader.load() function should return the class most suited to play the soundfile you pass it. It ends up not doing exactly that, and as I understand it the fault doesn't lie with Kivy but with GStreamer. This causes a significant temporary framerate drop for the app - regardless of where you call the .play() method.

Two possible solutions to this is offerend in the github-thread; 1) Either ensure a suitable class is returned directly - using SoundSDL2 2) Use PyGame instead

I implemented the latter, and it works fine.

# Initialize files and PyGame mixer:
import pygame
pygame.init()
pygame.mixer.pre_init(44100, 16, 2, 4096) # Frequency, size, channels and buffersize    
shield_on = pygame.mixer.Sound("shield_on.wav")

class game(Widget):
    ...
    def update_loop(self):
        ...
        if "game_file_says_shield_is_on":
            shield_on.play()

Hopefully this is of some help to others!

I'd like to say the above answer was useful as well because it enabled me to identify the real problem. I'd give it a vote, but I don't have the reputation here yet.

Upvotes: 3

kiok46
kiok46

Reputation: 1714

The sound plays just fine, however the loop appears to halt until its finished playing the sound.

Multithreading could be your solution here.

import threading
...
class foo(something):
    ...
    def update_loop(self,foo):
        ...
        if "game_file_says_player_one's_shields_are on":
            #starting new thread, which will run parallely with the main loop
            threading.Thread(target=self.first_thread).start()

    def first_thread(self):
        self.player_one.drawShield()
        shield_on.play()

Upvotes: 0

Related Questions