Some issues with mp3 files

739 views
Skip to first unread message

Kobayashi

unread,
Mar 13, 2014, 11:13:37 AM3/13/14
to kivy-...@googlegroups.com
Hi everybody@kivy,

I'm trying to develop a very small app to load a mp3 file with a file chooser
and implement only "rewind" and "toogle play/pause" for the moment

Load works perfectly and return a kivy.core.audio.audio_gstplayer.SoundGstplayer object

Play works but sound.length remains to 0

I tried to code "rewind" with seek(0.0) but it raises :
      self._sound.seek(0.0)
   File ".../kivy/core/audio/audio_gstplayer.py", line 79, in seek
     self.player.seek(position / self.duration)
 AttributeError: 'SoundGstplayer' object has no attribute 'duration'

There is no pause method and it seems to be impossible to code it if seek fails ...

If someone has better ideas to achieve that, it would be great !

Regards, E.A.

Steve B

unread,
Mar 14, 2014, 1:01:40 AM3/14/14
to kivy-...@googlegroups.com
I have no doubt someone will come alone with the right answer for you. 

When I last checked there was only limited MP3 support.

I dont have a better idea, but I have an alternative idea that if all fails may help. I should point out this is NOT the kivy method and may clash with other kivy functions. And pygst is in a state of flux at the moment.
 
A year ago or so I wanted to stream my google music with a kivy and at the time kivy sound didn't play nicely in linux. So I came up with this little hack to provide old school pygst in kivy. You will need to tailor it to your playlist requirements. And if your running it in windows you will often get an annoying buffering stutter or audible thump at the start of the track, not ask why. 

If all else fails it goes something like this.

You will need...

import urllib     
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces

and...

    def __init__(self, **kwargs):
        super(yourclass,self).__init__(**kwargs) 
        self.player = gst.element_factory_make("playbin2", "player")
        fakesink = gst.element_factory_make("fakesink", "fakesink")
        self.bus = self.player.get_bus()
        self.bus.set_sync_handler(self.on_message)

    def on_message(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_EOS:
            self.player.set_state(gst.STATE_NULL)
            Clock.schedule_once(self.Autoplay_Next, 2)  
        elif t == gst.MESSAGE_ERROR:
            self.player.set_state(gst.STATE_NULL)
            err, debug = message.parse_error()
            print "Player Error: %s" % err, debug
        return gst.BUS_PASS

Use the standard gst commands like gst.STATE_PLAYING

            self.player.set_property('uri',"file:///mymusic.mp3")     # I used the google music stream url here
            self.player.set_state(gst.STATE_PLAYING)

Hope this get you started if all else fails with your codecs.

Kobayashi

unread,
Mar 14, 2014, 6:37:06 AM3/14/14
to kivy-...@googlegroups.com
Hi Steve, thanks a lot for your answer !

I used you code to play mp3 on linux and indeed it works ...
I found a full example at
http://mylinuxtechcorner.blogspot.fr/2012/05/small-audio-player-using-gstreamer-gtk.html

but unfortunately, it does not work on android using Kivy Launcher.

My tests with  kivy.core.audio.SoundLoader with a mp3 file are :

o linux : load and play work, sound.get_pos() works, but sound.length is 0, seek(0.0) fails
o android with Kivy Launcher : load and play work, sound.get_pos() fails, sound.length is 0, seek(0.0) works (!)
o windows with Kivy 1.8.0 fails with
[WARNING           ] [AudioGstplayer] No decoder available for type 'audio/mpeg, mpegversion=(int)1, mpegaudioversion=(int)1, layer=(int)3, rate=(int)44100, channels=(int)2, parsed=(boolean)true'.
[ERROR             ] [AudioGstplayer] Your GStreamer installation is missing a plug-in.
[ERROR             ] [AudioGstplayer] Your GStreamer installation is missing a plug-in.
[ERROR             ] [AudioGstplayer] GStreamer encountered a general stream error.

But, my opinion is kivy is really a very promise tool. When knowing already pyqt, writing
an application with kivy is very easy. Personally, I prefer writing everything in python and not
use kv file.

Regards, K.

Steve B

unread,
Mar 14, 2014, 7:33:31 AM3/14/14
to kivy-...@googlegroups.com
Sadly MP3 is not supported in the Android port over. Probably due to reasons of licencing, who knows?
Thanks for the link there is some handy points of reference on that site.

Kobayashi

unread,
Mar 14, 2014, 9:41:41 AM3/14/14
to kivy-...@googlegroups.com
Indeed, mp3 files can play on Android but get_pos() and length remain to 0.
Maybe in the future ...

Kobayashi

unread,
Mar 14, 2014, 11:10:21 AM3/14/14
to kivy-...@googlegroups.com
Sorry to answer myself but with v1.8.1-dev of today, get_pos and length work
perfectly on linux (fedora 20) ! Cool, I can continue my tests


Le vendredi 14 mars 2014 11:37:06 UTC+1, Kobayashi a écrit :
Hi Steve, thanks a lot for your answer !

...

ZenCODE

unread,
Mar 16, 2014, 5:05:26 AM3/16/14
to kivy-...@googlegroups.com
Hi

Thanks massive Steve + Kobayashi, that exactly what I was looking for.

I'm going to play with that a bit, but if it works well, I was thinking of building an interface compatible version of this against the SoundLoader class (http://kivy.org/docs/api-kivy.core.audio.html). The intent is to be able to easily swapout the standard Kivy SoundLoader for this gst "mp3 capable" class. Even if it's not perfect, just getting the audio is great as long as the limitations/shortcomings are well documented.

If possible/legal, it could be added to the next kivy version, making kivy even more awesome? Any thoughts/warning/suggestions on that? Comments from the peanut gallery welcome...

Peace

Kobayashi

unread,
Mar 17, 2014, 10:08:33 AM3/17/14
to kivy-...@googlegroups.com
If someone is interested, here is a piece of code in which you can :
o by clicking on "...", navigate on your file system, select a mp3 file
and click "Ok", it should play the file, display the elapsed and remaining
time and display the name
o a click on ">||" toogle play/pause
o a click on "<<" goes to the beginning

It has been tested on :
o fedora 20 with kivy 1.8.1 dev (gstreamer1-devel must be installed as rpm
and Cython-0.20.1 must be installed manually before compiling kivy)
o android as apk using the construction with VirtualBox Image

With kivy launcher, it works but the times remains to 0.0 ...

Here is the code :

# ----------------------------------------------------------------------
# Server and client part
# Music player object
# ----------------------------------------------------------------------

from kivy.event import EventDispatcher

class MuwssysPlayer(EventDispatcher):
   
    from kivy.properties import StringProperty
    current_track_informations = StringProperty('')
    from kivy.properties import NumericProperty
    current_track_total_time = NumericProperty(0)
    current_track_elapsed_time = NumericProperty(0)
   
    def __init__(self):
        self._single_file_player = MuwssysSingleFilePlayer()
        self._single_file_player.bind(informations=self.onCurrentTrackInformationsChanged)
        self._single_file_player.bind(total_time=self.onCurrentTrackTotalTimeChanged)
        self._single_file_player.bind(elapsed_time=self.onCurrentTrackElapsedTimeChanged)
        return
   
    # --
    # Methods to single file player
    # --
   
    def load(self, url):
        # --
        # Temporary : for the moment, load one file at once
        # --
        self._single_file_player.load(url)
        return
   
    def rewind(self):
        self._single_file_player.rewind()
        return
   
    def togglePlayPause(self):
        self._single_file_player.togglePlayPause()
        return
   
    # ----------------------
   
    def getCurrentTrackInformations(self):
        return self.current_track_informations
   
    def getCurrentTrackTotalTime(self):
        return self.current_track_total_time
   
    def getCurrentTrackElapsedTime(self):
        return self.current_track_elapsed_time
   
    # -------
   
    def onCurrentTrackInformationsChanged(self, instance, value):
        self.current_track_informations = value
        return
   
    def onCurrentTrackTotalTimeChanged(self, instance, value):
        self.current_track_total_time = value
        return
   
    def onCurrentTrackElapsedTimeChanged(self, instance, value):
        self.current_track_elapsed_time = value
        return
   
    # -------
   
    pass

from kivy.event import EventDispatcher

class MuwssysSingleFilePlayer(EventDispatcher):
   
    # ------------------------------
   
    from kivy.properties import StringProperty
    informations = StringProperty('')
    from kivy.properties import NumericProperty
    total_time = NumericProperty(0)
    elapsed_time = NumericProperty(0)
   
    # ------------------------------
   
    def __init__(self):
        self._sound = None
        self._interval = 0.5
        from kivy.clock import Clock
        Clock.schedule_once(self._onClock, self._interval)
        return
   
    def _onClock(self, dt, work=False):
        # -----
        if not work:
            try:
                self._onClock(dt, True)
            finally:
                from kivy.clock import Clock
                Clock.schedule_once(self._onClock, self._interval)
                pass
            return
        # -----
        sound = self._sound
        if not sound:
            return
        # -----
        if hasattr(sound, "_paused"):
            self.total_time = max(int(sound._current_length), 0)
            self.elapsed_time = max(int(sound._current_pos), 0)
        else:
            self.total_time = max(int(sound.length), 0)
            self.elapsed_time = max(int(sound.get_pos()), 0)
            pass
        # -----
        return
   
    # ------------------------------
   
    def load(self, url):
        if self._sound:
            self._sound.unload()
            self._sound = None
            self.informations = ''
            pass
        from kivy.core.audio import SoundLoader
        sound = SoundLoader.load(url)
        if not sound:
            return
        sound.play()
        # --
        from os.path import basename
        infos = basename(sound.source)
        self.informations = infos
        # --
        self._sound = sound
        return
   
    def rewind(self):
        sound = self._sound
        if not sound:
            return
        sound.seek(0.0)
        if hasattr(sound, "_paused"):
            sound._current_pos = 0.0
            pass
        return
   
    def togglePlayPause(self):
        sound = self._sound
        if not sound:
            return
        if sound.state == "play":
            sound._paused = True
            sound._current_length = sound.length
            sound._current_pos = sound.get_pos()
            sound.stop()
        elif sound.state == "stop":
            delattr(sound, "_paused")
            sound.play()
            if sound._current_length > 1.0:
                while sound.length < sound._current_length/2:
                    pass
                pass
            sound.seek(sound._current_pos)
            pass
        return
   
    pass

# ----------------------------------------------------------------------
# Client GUI part
# ----------------------------------------------------------------------

from kivy.uix.boxlayout import BoxLayout

class MuwssysBoxLayout(BoxLayout):
   
    def __init__(self):
        # -------
        super(MuwssysBoxLayout, self).__init__()
        self.player = MuwssysPlayer()
        self.orientation = "vertical"
        # -------
        bl = self.addBoxLayoutToParent(self)
        bl.size_hint_y = None
        self.addButtonToParent(bl, "...", self.onChooseButtonRelease)
        # -------
        bl = self.addBoxLayoutToParent(self)
        from kivy.uix.label import Label
        bl.add_widget(Label())
        label = bl.children[0]
        label.text = "0:00"
        self._label_time_elapsed = label
        bl.add_widget(Label())
        label = bl.children[0]
        label.text = "0:00"
        self._label_time_remaining = label
        # -------
        bl = self.addBoxLayoutToParent(self)
        bl.size_hint_y = None
        self.addButtonToParent(bl, "<<", self.onRewind)
        self.addButtonToParent(bl, ">||", self.onTogglePlayPause)
        # -------
        bl = self.addBoxLayoutToParent(self)
        from kivy.uix.label import Label
        bl.add_widget(Label())
        label = bl.children[0]
        label.text = ""
        self._label_current = label
        # -----
        self.player.bind(current_track_informations=self.onCurrentLabelUpdate)
        self.player.bind(current_track_total_time=self.onTimeUpdate)
        self.player.bind(current_track_elapsed_time=self.onTimeUpdate)
        # -----
        return
   
    def addBoxLayoutToParent(self, parent):
        from kivy.uix.boxlayout import BoxLayout
        parent.add_widget(BoxLayout())
        bl = parent.children[0]
        return bl
   
    def addButtonToParent(self, parent, text, target):
        from kivy.uix.button import Button
        parent.add_widget(Button())
        btn = parent.children[0]
        btn.size_hint_y = None
        btn.text = text
        btn.bind(on_release=target)
        return btn
   
    # -----------------------------
   
    def onChooseButtonRelease(self, button):
        from kivy.uix.popup import Popup
        popup = Popup()
        popup.title = "Choose a file"
        from kivy.uix.boxlayout import BoxLayout
        popup.content = BoxLayout()
        # ---
        boxlayout = popup.content
        boxlayout.orientation = "vertical"
        # ---------
        from kivy.uix.filechooser import FileChooserListView
        boxlayout.add_widget(FileChooserListView())
        # ---------
        file_chooser = boxlayout.children[0]
        try:
            file_chooser.path = self._previous_file_chooser_dir
        except AttributeError:
            from os import getcwd
            file_chooser.path = getcwd()
            pass
        # --------
        bl = self.addBoxLayoutToParent(boxlayout)
        bl.size_hint_y = None
        btn = self.addButtonToParent(bl, "Ok", self.onButtonOkRelease)
        btn.popup = popup
        btn.file_chooser = file_chooser
        btn = self.addButtonToParent(bl, "Cancel", self.onButtonCancelRelease)
        btn.popup = popup
        # -------
        popup.open()
        # ----
        return
   
    def onButtonOkRelease(self, button):
        fc = button.file_chooser
        if fc.selection:
            url = fc.selection[0]
            from os.path import dirname
            d = dirname(url)
            self._previous_file_chooser_dir = d
            self.onChooseTrack(url)
            pass
        button.popup.dismiss()
        return
   
    def onButtonCancelRelease(self, button):
        button.popup.dismiss()
        return
   
    # ---------------------------------------------------
   
    def onChooseTrack(self, url):
        self.player.load(url)
        return
   
    def onRewind(self, button):
        self.player.rewind()
        return
   
    def onTogglePlayPause(self, button):
        self.player.togglePlayPause()
        return
   
    # ---------------------------------------------------
   
    def onTimeUpdate(self, instance, value):
        total = self.player.getCurrentTrackTotalTime()
        elapsed = self.player.getCurrentTrackElapsedTime()
        remaining = total - elapsed
        self._label_time_elapsed.text = "%i:%2.2i"%(elapsed/60, elapsed%60)
        self._label_time_remaining.text = "%i:%2.2i"%(remaining/60, remaining%60)
        return
   
    def onCurrentLabelUpdate(self, instance, value):
        infos = self.player.getCurrentTrackInformations()
        self._label_current.text = infos
        return
   
    # ---------------------------------------------------
   
    pass

# ----

from kivy.app import App

class MuwssysApp(App):
   
    def build(self):
        return MuwssysBoxLayout()
   
    pass

# ----------

if __name__ == '__main__':
    MuwssysApp().run()
    pass

Zach Dunlap

unread,
Oct 13, 2014, 10:35:11 AM10/13/14
to kivy-...@googlegroups.com
Thank you for sharing this! It's really well done. I was wondering if you figured out why when running this on Android the times remain at zero? Could it be the need to specific android permissions?
Reply all
Reply to author
Forward
0 new messages