Audio position does not change in kivy

Viewed 515

I am creating a music player in kivy using kivy.core.audio.Sounloader and I have come across a problem. When the user hits the start button I want them to be able to slide the slider and change the position of the song, however, the function just doesn't work. It also does not raise any errors, it just doesn't work. I also tried getting the position of the song with music_obj.get_pos() but it always returned 0. I did a couple of searches and others seem to have this problem too. How can I solve this? My code:

from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.widget import Widget
from kivy.lang import Builder


Builder.load_string('''
<MyLayout>:
    BoxLayout:
        orientation: "vertical"
        size: root.width, root.height

        Label:
            id: song_title
            text: "Song title!"
            text_size: self.size
            font_size: 32
            valign: "middle"
            halign: "center"

        Slider:
            id: slider
            min: 0
            max: 1
            step: 1
            value: 0
            on_value: root.change_pos(self.value)

        Button:
            text: "Animate!"
            font_size: 32
            on_release: root.start_song()''')


class MyLayout(Widget):
    music_file = "pitbull-feat-kesha-pitbull-feat-keshatimber.mp3"
    music_obj = None

    def start_song(self):
        self.music_obj = SoundLoader.load(self.music_file)
        if self.music_obj:
            print(self.music_obj.source)
            print(self.music_obj.length)
            self.ids.song_title.text = self.music_obj.source
            self.ids.slider.max = self.music_obj.length
            self.music_obj.play()

    def change_pos(self, value):
        if self.music_obj is not None:
            self.music_obj.seek(value)


class Awesome(App):
    def build(self):
        self.title = "Hello!"
        return MyLayout()


if __name__ == "__main__":
    Awesome().run()
2 Answers

I tried running your code and the seek bar works just fine as long as it's playing. Once the music stops, the seek bar stops working. If that is the problem you encountered, I suggest you play the audio again first before seeking.

    def change_pos(self, value):
        if self.music_obj is not None:
            self.music_obj.play()
            self.music_obj.seek(value)

I used Kivy 2.0.0 and ffpyplayer 4.3.2 by the way.

I am using kivy 2.0.0. The code of the question was not working until I installed ffpyplayer-4.3.2. Now, it works !

installing ffpyplayer-4.3.2

Here's an improved version of the code presented in the initial question. Now, as the mp3 file is played, the slider position is updated. This is done asynchronously in a separate thread.

from kivy.app import App
from kivy.core.audio import SoundLoader
from kivy.uix.widget import Widget
from kivy.lang import Builder

import os, time, threading

SLIDER_UPDATE_FRENQUENCY = 1

if os.name == 'posix':
    AUDIO_DIR = '/storage/emulated/0/Download/Audiobooks/Various/'
else:
    AUDIO_DIR = 'D:\\Users\\Jean-Pierre\\Downloads\\Audiobooks\\Various\\'

Builder.load_string('''
<MyLayout>:
    BoxLayout:
        orientation: "vertical"
        size: root.width, root.height

        Label:
            id: song_title
            text: "Song title!"
            text_size: self.size
            font_size: 32
            valign: "middle"
            halign: "center"

        Slider:
            id: slider
            min: 0
            max: 1
            step: 1
            value: 0
            on_value: root.change_pos(self.value)

        Button:
            text: "Animate!"
            font_size: 32
            on_release: root.start_song()''')


class AsynchSliderUpdater:
    def __init__(self, music_obj, slider):
        self.music_obj = music_obj
        self.slider = slider
    
    def updateSlider(self):
        music_length = self.music_obj.length
        stop = False
        
        while not stop:
            time.sleep(SLIDER_UPDATE_FRENQUENCY)
            music_pos = self.music_obj.get_pos()
            if music_pos < music_length:
                self.slider.value = music_pos
            else:
                stop = True

class MyLayout(Widget):
    """
    WARNING

    Only works if ffpyplayer is installed !
    (C:\Program Files\Python39> ./python -m pip install ffpyplayer
    Successfully installed ffpyplayer-4.3.2)
    
    NOT WORKING ON ANDROID SINCE INSTALLING FFPYPLAYER FAILS !
    """
    
    music_file = AUDIO_DIR + "Un résumé audio de ce qu'enseigne ' Le Cours en Miracles '.mp3"
    music_obj = None

    def start_song(self):
        self.music_obj = SoundLoader.load(self.music_file)
        if self.music_obj:
            print(self.music_obj.source)
            print(self.music_obj.length)
            self.ids.song_title.text = self.music_obj.source
            self.ids.slider.max = self.music_obj.length
            
            self.sliderAsynchUpdater = AsynchSliderUpdater(self.music_obj, self.ids.slider)
            t = threading.Thread(target=self.sliderAsynchUpdater.updateSlider, args=())
            t.daemon = True
            t.start()

            self.music_obj.play()

    def change_pos(self, value):
        if self.music_obj is not None:
            # test required to avoid mp3 playing perturbation
            if abs(self.music_obj.get_pos() - value) > SLIDER_UPDATE_FRENQUENCY:
                print(value)
                self.music_obj.seek(value)


class Awesome(App):
    def build(self):
        self.title = "Hello!"
        return MyLayout()


if __name__ == "__main__":
    Awesome().run()
Related