Search code examples
pythonkivysdlffpyplayer

Python Kivy sound.seek works only with a weird twist


So here is my code, the problem is that the play function in my Player class doesn't work properly if I have a print statement between self.song.play() and self.song.seek(self.pos), then it works fine, else it doesn't work. Below I will show my console output because there is also a ffpyplayer warning by kivy and the time comparison of the actual time played and the current time of the song. You can ignore the s() and t() functions for the problem they are just for the console output to show which time the song actually should be at.

from kivy.app import App 
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.core.audio import SoundLoader

import os 
import random
from time import time

class Main(RelativeLayout):
    def __init__(self, **kw):
        super().__init__(**kw)
        self.player = Player()

        playbutton = Button(
            text = "Play",
            size_hint = (.1, .1),
            pos_hint = {"center_x": 0.4, "center_y": 0.3},
            on_press = self.player.play)

        stopbutton = Button(
            text = "Stop",
            size_hint = (.1, .1),
            pos_hint = {"center_x": 0.6, "center_y": 0.3},
            on_press = self.player.stop)
    
        self.add_widget(playbutton)
        self.add_widget(stopbutton)

class MyApp(App):
    def build(self):
        return Main()


class Player:
    def __init__(self):
        self.path = "C:/Users/Marc/Music/Lieder/"
        self.playlist = os.listdir(self.path)
        #self.mix_playlist()
        self.song = SoundLoader.load(self.path + self.playlist[0])
        self.pos = 0
        self.start_time = None
        self.stop_time = 0
        self.stopped_time = None

    def mix_playlist(self):
        mixedPlaylist = []
        while len(self.playlist) > 0:
            item = random.choice(self.playlist)
            mixedPlaylist.append(item)
            self.playlist.remove(item)
        self.playlist = mixedPlaylist

    def t(self):
        if self.start_time is None:
            self.start_time = time()

        return int(time() - self.start_time - self.stop_time)

    def s(self):
        if self.stopped_time:
            self.stop_time += time() - self.stopped_time

    def play(self, _):
        if self.song.state == "stop":
            self.s()
            self.song.play()
            if self.pos > 0:
                ################# IF HERE IS A PRINT STATEMENT -> seek works, else not #######################
                self.song.seek(self.pos)
                print("pos:", self.pos, "song.get_pos:",self.song.get_pos(), "play time:",self.t()) 


    def stop(self, _):
        self.pos = self.song.get_pos()
        self.song.stop()
        self.stopped_time = time()

app = MyApp()
app.run()

My Console Output

here as image

My thought was that through the print statement enough time passes so self.song.play() function counts for the self.song.seek(self.pos) function.


Solution

  • Quick update, i solved the problem. The problem was that seek() function only works when the song is playing and somehow seek() doesnt work if play() was called in the same frame of the kivy event loop like in my code. So the solution is to schedule seek() for the next frame like this: Clock.schedule_once(seek, 0) Sources: https://kivy.org/doc/stable/api-kivy.clock.html https://kivy.org/doc/stable/api-kivy.core.audio.html

    def play(self, _):
        if self.song.state == "stop":
            self.song.play()
            if self.pos > 0:
                Clock.schedule_once(self.unpause,0)
    
    def unpause(self, _):
        self.song.seek(self.pos)