Using Gstreamer with wxPython – Two MainLoops and the Law of Delimiter

(Original post from 19. May 2012)

Yesterday, while writing on the new version of my super duper honking RPG MusicPlayer, I came across a very interesting problem. Interesting, because it made me learn new things about Python in particular and programming in general.

Gstreamer uses the gobject.MainLoop() to communicate events via messages to its playbin instance. The example below is directly from the tutorial. Note the bus instance and the on_message method. This all runs via the gobject.MainLoop()

        # bus is used to report messages in playtime
        # Taken from tutorial, also the methods
        self.bus = self.player.get_bus()
        self.bus.add_signal_watch()
        self.bus.enable_sync_message_emission()
        self.bus.connect("message", self.on_message)

    def on_message(self, bus, message):
        """
        Taken from gstreamer tutorial.
        http://pygstdocs.berlios.de/pygst-tutorial
        
        Used in:
        - connects to bus in class.
        
        """
        
        t = message.type
        if t == gst.MESSAGE_EOS: # track is finished
            self.player.set_state(gst.STATE_NULL)
        elif t == gst.MESSAGE_ERROR:
            self.player.set_state(gst.STATE_NULL)
            err, debug = message.parse_error()
            print "Error: %s" % err, debug

So far, I had no problems not running the gobject.MainLoop, because the wx.App.MainLoop() did all the necessary things to keep my songs running. But at one point, I noticed a bug. When the song came naturally to an end, it would just stop and not play the next song. This was, of course, due to the fact, that the End of Song Message never reached the Playbin, because there was no system to relay said message

At this point, I had the uncomfortable situation of needing two MainLoops to run at the same time. Yes, I know, I should have learned to use pyGTK instead of sticking with wxPython, but I like wxPython and I was lazy. Also, that way I learned loads more funky stuff

Anyways, so how to solve that problem… Well, I used Python’s threading module to help me out here. Without threading, I was not able to start both MainLoops at the same time, because of Python’s procedural nature. Because I had no idea about threading, I had several mishaps on the way, but I came up with a minimal example such as shown at the end. I had to look for awhile, because I found most “simple” threading tutorials a bit over the top for my liking, but taking little bits and bobs from everywhere, I was able to come up with a solution.

Most of it really is just syntactic sugar. The key part about this, is the GobjectThread class. This class starts the MainLoop in a separate thread and after the wx.App.MainLoop is finished, the thread is stopped, too. Luckily, I only have to start and stop the gobject.MainLoop. I can imagine, these sort of things can very quickly become very confusing and cluttered. The only other important thing to note in this code is the close event. It is important to set the Gstreamer elements to gst.STATE_NULL, otherwise there are several Critical Errors thrown, when terminating the MainLoop

Also, as a little side note, while discussing this in the German Python Forum, I was told about Delimiter’s Law. I very often have things like self.player.player.get_state(), which according to Delimiter’s Law is a big “NoNo!”. Essentially, when disregarding Delimiter, too closely knit classes are a consequence. This can make it hard to debug or replace certain parts of the code, because the other classes take too much of the original architecture for granted. Knowing this now, I have a lot more work ahead of me 🙂

#!usr/bin/python

import os
import urllib
from threading import Thread

import wx
import pygst
pygst.require("0.10")
import gst
import gobject


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        
        ###############
        ## Threading ##
        ###############
        self.player = GstPlayer()
        
        # Close Event
        self.Bind(wx.EVT_CLOSE, self.on_close)

        self.__set_properties()
        self.__do_layout()

    def on_close(self, event):
        # If state not set to NULL, Critical Error occurs on gobject quit
        state = self.player.get_state()
        if state[1] == gst.STATE_PLAYING:
            self.player.set_state(gst.STATE_NULL)
        self.Destroy()

    def __set_properties(self):
        self.SetTitle("Test")

    def __do_layout(self):
        self.Layout()


class GstPlayer():
    def __init__(self):
        # This is almost straight out of the tutorial
        p = os.path.join(os.getcwd(), "tracks", "1. Shaent Blathanna.mp3")
        path = urllib.pathname2url(p)
        
        self.player = gst.element_factory_make("playbin2", "player")
        fakesink = gst.element_factory_make("fakesink", "fakesink")
        self.player.set_property("video-sink", fakesink)
        
        self.player.set_property("uri", "file:" + path)
        self.player.set_state(gst.STATE_PLAYING)
        
    def get_state(self):
        return self.player.get_state()
    
    def set_state(self, state):
        self.player.set_state(state)
        
class GobjectThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        ##############################
        self.loop = gobject.MainLoop() # The mainloop to be separated from wx
        ##############################
    
    def run(self): # Called on Thread.start()
        self.loop.run()
        
    def quit(self):
        # Stops the gobject Mainloop
        self.loop.quit()

if __name__ == "__main__":
    gobject.threads_init()
    
    t = GobjectThread()
    t.start()
    
    # wxGlade's my Friend here
    # Just basics
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame = MyFrame(None, -1, "")
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()
    
    t.quit() #End the gobject.MainLoop()
Advertisements

Using Gstreamer with Python2.6 in Windows XP – Pt. 4 Incorporating it all into wxPython

(Original post from 01. Nov. 2011)

Hi folks, back again for the next round. This time all I will do, is just putting Gstreamer and wxPython together. Nothing fancy at all. This post is based on the GStreamer Tutorial with the difference that I will only use the playbin instead of the pipeline, simply because I could not find a suitable mp3 decoder available for Windows at the time of writing this. I am sure there are suitable ones out there, but my measly skills couldn’t make them work. I could have tried the python ctypes library to get the winLame DLLs to work, but it just didn’t seem worth the hassle with mp3play available…

Anyhow, so essentially I just inserted the code from the previous post into a simply GUI with wxPython. Anyone who knows wxPython and read the previous post, will not see anything new. So here is the code and we’ll talk about it afterwards.

NOTE: I used an XRC file to represent the GUI. This is personal preference and the files can be downloaded here.

#!usr/bin/python

import os
import urllib

import pygst
pygst.require("0.10")
import gst

import wx
from wx import xrc

class SimpleAudio(wx.App):
    """
    Class definition of the GUI front end and its functionality
    """

    def OnInit(self):
       
        # Load GUI info frm xrc file
        self.res = xrc.XmlResource('simpleAudio.xrc')
        self.init_frame()
        return True

    def init_frame(self):
       
        self.frame = self.res.LoadFrame(None, 'simple_audio')
        self.panel = xrc.XRCCTRL(self.frame, "main_panel")
       
        self.init_panel()
       
        self.frame.Show()
       
    def init_panel(self):
       
        # Gstreamer player
        self.player = gst.element_factory_make("playbin2", "player")
        fakesink = gst.element_factory_make("fakesink", "fakesink")
        self.player.set_property("video-sink", fakesink)
       
        self.path_txt = xrc.XRCCTRL(self.panel, "path_txt")
        self.play_btn = xrc.XRCCTRL(self.panel, "play_btn")
        self.open_btn = xrc.XRCCTRL(self.panel, "open_btn")
       
        self.panel.Bind(wx.EVT_BUTTON, self.on_play, id=xrc.XRCID('play_btn'))
        self.panel.Bind(wx.EVT_BUTTON, self.on_open, id=xrc.XRCID('open_btn'))
   
    def on_play(self, event):
        if self.play_btn.GetLabel() == "Play":
            filepath = self.path_txt.GetValue()
            path = urllib.pathname2url(filepath)
           
            if os.path.isfile(filepath):
                self.play_btn.SetLabel("Stop")
                self.player.set_property("uri", "file:" + path)
                self.player.set_state(gst.STATE_PLAYING)
        else:
            self.player.set_state(gst.STATE_NULL)
            self.play_btn.SetLabel("Play")
           
    def on_open(self, event):
       
        dlg = wx.FileDialog(self.frame, message="Open an ogg or wav file",
                    wildcard = "OGG, WAV Files (.ogg;.wav;)|*.ogg;*.wav;",
                    defaultDir=os.getcwd(),
                    defaultFile="", style=wx.OPEN)
       
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.path_txt.SetValue(path)
                         
        dlg.Destroy()

if __name__ == '__main__':
    app = SimpleAudio(False)
    app.MainLoop()

So, as you can see, we’ve essentially transferred parts of the GStreamer syntax into the frame initialisation, so that it is ready.

        ...
        # Gstreamer player
        self.player = gst.element_factory_make("playbin2", "player")
        fakesink = gst.element_factory_make("fakesink", "fakesink")
        self.player.set_property("video-sink", fakesink)
        ...

This is a direct copy and paste job. The only thing that is a little step further is in the function “on_play”:

    ...
    def on_play(self, event):
        if self.play_btn.GetLabel() == "Play":
            filepath = self.path_txt.GetValue()
            path = urllib.pathname2url(filepath)
           
            if os.path.isfile(filepath):
                self.play_btn.SetLabel("Stop")
                self.player.set_property("uri", "file:" + path)
                self.player.set_state(gst.STATE_PLAYING)
        else:
            self.player.set_state(gst.STATE_NULL)
            self.play_btn.SetLabel("Play")
    ... 

here you will find the second part of the code from the last post with the difference that we now also give the option of “stopping” the song. Yes it may seem outlandish to want to stop a song, but I incorporated it anyway for all those non-conventional types 😉
Summing the code up: We get the path from the text control, check if the path is valid, convert to a url format and play it. That’s it really. No big deal.
Well, if I feel like it, I will try and expand on this player and include a song progress bar and a playlist, just to show you more of it, but we’ll see. Not sure yet, to be honest.

Using Gstreamer with Python2.6 in Windows XP – Pt. 3 Playing Sound Files – Which Formats Work?

(Original post from 30. Oct. 2011)

Ok, so, the last time, I managed to write a very minimalistic script that gave off a static noise and nothing more. This time, I want to use the same core script, but manage to play a music file and while doing that test the waters for format compatibility. With open source libraries, there is always an issue with mp3, so I was not too hopeful.
So, here goes. This time, I will try to use the current GStreamer documentation (http://pygstdocs.berlios.de/pygst-tutorial/playbin.html ) and extract the basic functionality from the gtk clutter. What I came up with was the following:

import os
import urllib

import pygst
pygst.require("0.10")
import gst
import gobject
gobject.threads_init()

p = os.path.join("My", "path", "to", "my", "file")
path = urllib.pathname2url(p)

player = gst.element_factory_make("playbin2", "player")
fakesink = gst.element_factory_make("fakesink", "fakesink")
player.set_property("video-sink", fakesink)

player.set_property("uri", "file:" + path)
player.set_state(gst.STATE_PLAYING)

gobject.MainLoop().run()

Two things that have to be said about the above code.

[h3]1. Why urllib?[/h3]
I had a problem, feeding gstreamer a proper uri and I always had a horrible error

0:00:00.109375000 1472 00A55488 ERROR baesrc gstbasesrc.c:2974:gst_base_src_activate_pull:(source) Failed to start in pull mode

Converting the path to a url seemed to do the trick. I am assuming this has something to do with the way Windows marks paths as opposed to Linux.

[h3]2. Playbin2 – What’s that?[/h3]
Yea, that is from the tutorial. Apparently, just throw anything in and it tries to play it. If you want to only play Audio, you need to blank out the videosink with an in-build fakesink (as above).

Now when testing it, I checked the formats .ogg, .wav, and .mp3. Unfortunately, as I suspected, while ogg and wav work very well, mp3s are not played by gstreamer. There is Fluendo (http://www.fluendo.com/shop/product/fluendo-mp3-decoder/) but that does not supply for any Windows Versions… unfortunately. If anyone, finds a way to use gstreamer for mp3s, please let me know.

As an alternative, I found mp3play ( http://code.google.com/p/mp3play/ ) , which looks very good and seems to be able to do all the things I want it to do.

The last thing I wanted to test was, whether gstreamer can play more than one file at the same time. This is essential to what I am trying to achieve in the long run. Building on the above code, I just repeated what I did and created two instances. This worked as wanted. If I have any problems later on, i am sure, I will find out in the process.

import os
import urllib

import pygst
pygst.require("0.10")
import gst
import gobject
gobject.threads_init()

# First audio
p1 = os.path.join("My", "path", "to", "my", "file")
path1 = urllib.pathname2url(p1)

player1 = gst.element_factory_make("playbin2", "player1")
fakesink1 = gst.element_factory_make("fakesink", "fakesink")
player1.set_property("video-sink", fakesink1)

player1.set_property("uri", "file:" + path1)
player1.set_state(gst.STATE_PLAYING)

# Second audio
p2 = os.path.join("My", "path", "to", "my", "other", "file")
path2 = urllib.pathname2url(p2)

player2 = gst.element_factory_make("playbin2", "player2")
fakesink2 = gst.element_factory_make("fakesink", "fakesink")
player2.set_property("video-sink", fakesink2)

player2.set_property("uri", "file:" + path2)
player2.set_state(gst.STATE_PLAYING)

gobject.MainLoop().run()

Using Gstreamer with Python2.6 in Windows XP – Pt. 2 A Minimal Example

(Original post from 30. Oct. 2011)

Right folks, so first things first. Let’s try and get something easy going. You know, nothing fancy. Just a simple output. No gui nothing. For that, as a starting point, I chose the tutorial from the following website:

http://www.jonobacon.org/2006/08/28/getting-started-with-gstreamer-with-python/

In this tutorial (feel free to browse, it’s good), the author writes a very good introduction to what GStreamer is all about and explains the mechanics (Sinks, Pipelines etc.)… Unfortunately for me, for Linux. He gives a script that plays an audible tone, just to test if everything is working fine. Of course, taking it over to Windows is an issue.
Here is the code as quoted on JonoBacon’s website (many thanks for it):

#!/usr/bin/python

import pygst
pygst.require("0.10")
import gst
import pygtk
import gtk

class Main:
    def __init__(self):
        self.pipeline = gst.Pipeline("mypipeline")

        self.audiotestsrc = gst.element_factory_make("audiotestsrc", "audio")
        self.pipeline.add(self.audiotestsrc)

        self.sink = gst.element_factory_make("alsasink", "sink")
        self.pipeline.add(self.sink)

        self.audiotestsrc.link(self.sink)

        self.pipeline.set_state(gst.STATE_PLAYING)

start=Main()
gtk.main()

If you try the same code as stated on the website, you will receive a

gst.ElementNotFoundError: alsasink

in windows. After some browsing the web and learning about the combination of cygwin and the gst-inspect and gst-launch commands (google it, if you want to know more), I finally found the answer. First though, here are the website, I had that were useful to me in this instance:

http://www.jonobacon.org/2006/08/28/getting-started-with-gstreamer-with-python/

http://gstreamer.freedesktop.org/data/doc/gstreamer/head/faq/html/chapter-using.html#using-sound

The problem lies with using “alsasink”. ALSA is Linux’ way of playing sound. The equivalent of DirectSound for Windows if you want. Therefore, you need to find the right sink, to play audio on Windows. Alsasink won’t cut it, as it quite clearly is made for Linux. After trying several sinks via trial and error (osssink, esdsink, artsdsink, etc.) and no joy, I tried cygwin and gst-inspect to figure out which one I had, but this was also not successful. Finally, after a while, I came across a totally unrelated site, that mentions “autoaudiosink” as the auto-detect for your sound. Replacing “alsasink” with “autoaudiosink” and voilá, the sound was there when the script was run. Just careful and don’t turn the volume up too high. I did after a million and two failed tries and it almost blew me off my chair :D.

One last thing that I wanted to address was the pseudo-dependency on gtk. I say, pseudo, because the grand majority of examples were given with pygtk and thus most people have used gtk with it. Again a little bit of searching around, I found that the need for a gtk.main() call, was simply because GStreamer uses the Gobject module and its threads (see among others, this forum post: http://ubuntuforums.org/showthread.php?t=1168455 ). Once I found that, I was able to remove the gtk and pygtk import and replace it with the gobject import and the code still ran.
Thus, to some things up, the minimal example that works for Windows is as follows:

#!/usr/bin/python

import pygst
pygst.require("0.10")
import gst

# replaces the call to gtk and pygtk
import gobject
gobject.threads_init()

class Main:
def __init__(self):
self.pipeline = gst.Pipeline("mypipeline")

self.audiotestsrc = gst.element_factory_make("audiotestsrc", "audio")
self.pipeline.add(self.audiotestsrc)

self.sink = gst.element_factory_make("autoaudiosink", "sink")
self.pipeline.add(self.sink)

self.audiotestsrc.link(self.sink)

self.pipeline.set_state(gst.STATE_PLAYING)

start=Main()
gobject.MainLoop().run() # instead of gtk.main()

Having this in hand, I will next look at playing a simple audio file and testing the scope, i.e. which common formats work and if I can play several at the same time. Since I was able to remove gtk and replace it with gobject, I can now use wxpython to serve as my GUI of choice.

Using Gstreamer with Python2.6 in Windows XP – Pt. 5 – A Simple Gstreamer Wrapper

(Original post from 31. March 2012)

Hello loyal audience, whoever you may be. As you can deduct from the last post about Gstreamer, I have decided to go back on board with it from a lack of option and the fact that it seems to support mp3 files now on both Windows (!!!) and Linux.

I haven’t quite got a hold of the guys responsible for the Gstreamer Ossbuild, but I will thank them without end… and then ask them about some strange things I encountered when trying to install the Gstreamer Python bindings.

Anyhow, enough of that idle chitchat. Since everything is working fine, I have been trawling through the Gstreamer Tutorial a bit more and Jonobacon’s Tutorial (My thanks to both authors, fantastic tutorials) to get all the necessary functionality together and I have to my surprise been able to write an entire wrapper for an Audiofile class, which I will append to the end of this text, because it is rather long. I will, however, highlight the odd bits about it and if I have a reason for it, I shall explain, if not (the majority) then you will have some working code and I may come back to this text, once I have found the answer.

First of all, I have to say that I stuck with the standard playbin2 as it does all the things I need and it is fantastic and simple to use. Why reinvent the wheel with pipelines and ghostpads (and spend hours upon hours in the process) when you can have it easy 🙂 If some things are unclear and I haven’t mentioned them, chances are they are in any of the entries, I have posted before on the topic (Check the tags for that).

I have already discussed the basics like play and stop. The message system isn’t spectacular and is also taken one to one from the tutorial (thank you, again, oh dear author of this fine piece of work).

One thing that is important to mention at the start is the role of the two commands

gobject.threads_init()

and

gobject.MainLoop().run()

As far, as I am aware, these are necessary for some, but not all processes. If you want your file to only play for say 10 seconds, you might just as well use the built-in time.sleep function. However, these commands provide the endless loop of the application which means that when the song ends this does not mean the script has ended, so always end your script explicitly.

And also, I am assuming, some processes need another thread to be started, so I suggest once it becomes more complex than play, pause, stop, you call these commands.

Two very straight forward methods are the volume handling. There really is no rocket science about this. Volume is a float between 1.0 and 0.0, the rest is self-explanatory, I would assume. Let’s move on.

    def set_volume(self, new_volume):
        """
        Sets new volume of self.audiofile instance.
        new_volume must be float between 0.0 and 1.0
        """
        
        self.player.set_property("volume", new_volume)
        
    def get_volume(self):
        """
        Gets volume of self.audiofile instance.
        new_volume must be float between 0.0 and 1.0
        """
        
        return self.player.get_property("volume")

The other important function for an audiofile is being able to fast forward are rewind. This is done by the set_position() and get_position() methods. Gstreamer handles the time in nanoseconds and as such, it is necessary to convert them accordingly. As I wanted to use the numbers in a more useful manner (and also more humanly readable), I decided to write two functions converting nanoseconds to a tuple of hour, minutes and seconds and back. This should also allow me to give a regular time update on the user interface once I managed to get that one.

Regarding the interesting gst.SEEK_FLAG_FLUSH, I rather quote the official guide than mincing my own words. I hereby quote:

Seeks with the GST_SEEK_FLAG_FLUSH should be done when the pipeline is in PAUSED or PLAYING state. The pipeline will automatically go to preroll state until the new data after the seek will cause the pipeline to preroll again. After the pipeline is prerolled, it will go back to the state (PAUSED or PLAYING) it was in when the seek was executed. You can wait (blocking) for the seek to complete with gst_element_get_state() or by waiting for the ASYNC_DONE message to appear on the bus.

Seeks without the GST_SEEK_FLAG_FLUSH should only be done when the pipeline is in the PLAYING state. Executing a non-flushing seek in the PAUSED state might deadlock because the pipeline streaming threads might be blocked in the sinks.

Don’t say, you weren’t told…

A thing that struck me as very odd was the fact that I had to add a time.sleep call at the end of it, because otherwise I would receive a gst.QueryError when calling get_position() or get_volume(). Why this is, I am not exactly certain about, but it did the job, so I am not arguing.

    def get_position(self):
        """
        Returns the position of the currently played audiofile as
        a tuple of (hour, min, sec).
        """
        
        try:
            pos_dur = self.player.query_position(gst.FORMAT_TIME, None)[0]
            pos = self.convert_ns(pos_dur)
            return pos
        
        except gst.QueryError:
            return (0, 0, 0)
        
    def set_position(self, new_pos):
        """
        Sets a new position of the currently played audiofile as 
        a tuple of (hour, min, sec)
        
        new_pos format is (hour, min, sec)
        """
        
        seek_pos = self.convert2ns(new_pos)
        self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, seek_pos)
        # time.sleep seemed to be necessary for action right after to work
        # correctly. test if it can be removed when the GUI is up and running.
        time.sleep(0.2)
        
    def convert_ns(self, time):
        """
        Taken from gstreamer tutorial. Thank you very much to Sam Mason
        http://pygstdocs.berlios.de/pygst-tutorial/seeking.html
        adjusted to return a tuple (hour, min, sec)
        
        Converts nanoseconds to tuple (hour, min, sec).
        
        Used in:
        - method get_length
        """
        
        sec, nanosec = divmod(time, 1000000000)
        minute, sec = divmod(sec, 60)

        if minute < 60:
            return (0, int(minute), int(sec))
        else:
            hour, minute = divmod(minute, 60)
            return (int(hour), int(minute), int(sec))
        
    def convert2ns(self, time):
        """
        Converts a tuple of (hour, min, sec) to nanoseconds.
        """
        
        nanosec = 0
        hour, minute, sec = time
        
        if hour != 0:
            total_sec = (((hour*60)+minute)*60)+sec
            nanosec += total_sec*1000000000
            
        else:
            total_sec = (minute*60)+sec
            nanosec += total_sec*1000000000
            
        return nanosec

Ah well, that’s it. Maybe a rather boring post for you, but I enjoyed doing it. The more I use Gstreamer, the more it grows on me. This is probably, the last article on this topic, since I will now aim to incorporate the functionality into my GUI and create a working audioplayer. check out the posts tagged as “Music’N’SFX”. There you should find more. I hope you enjoyed it. Please find below the code for the entire Audiofile class.

—————————————————————————————————————–

A brief disclaimer up front. If there any major blooping mistakes then I apologise and if you also tell me about them and how to remedy them, I am more than happy to do so, because I may also learn something in the process.

#!usr/bin/python

# Standard libraries
import os
import time
import urllib

import pygst
pygst.require("0.10")
import gst
import gobject

class AudioFile:
    """
    A wrapper class for the gstreamer library.
    """
    
    def __init__(self, path, volume=1.0):
        
        self.path = path
        self.volume = volume # value between 0.0 and 1.0
        
        # The Gstreamer playbin - out of box audio/video functionality
        self.player = gst.element_factory_make("playbin2", "player")
        self.fakesink = gst.element_factory_make("fakesink", "fakesink")
        self.player.set_property("video-sink", self.fakesink)
        
        # set_current file
        uri_path = urllib.pathname2url(self.path)
        self.player.set_property("uri", "file:" + uri_path)
        
        # bus is used to report messages in playtime
        # Taken from tutorial, also the methods
        bus = self.player.get_bus()
        bus.add_signal_watch()
        bus.enable_sync_message_emission()
        bus.connect("message", self.on_message)
        
    def __str__(self):
        pathsplit = os.path.split(self.path)
        return """Properties of current audiofile class:
        Filename: {0}
        Path: {1}
        Volume: {2}
        """.format(pathsplit[-1], pathsplit[:-1], self.volume)
        
    def reload(self, path):
        self.__init__(path, self.volume, self.repeat)
        
    def play(self):
        self.player.set_state(gst.STATE_PLAYING)
        
    def stop(self):
        self.player.set_state(gst.STATE_NULL)
        
    def pause(self):
        self.player.set_state(gst.STATE_PAUSED)
        
    def reset(self):
        self.player.stop()
        self.player.play()
        
    def set_volume(self, new_volume):
        """
        Sets new volume of self.audiofile instance.
        new_volume must be float between 0.0 and 1.0
        """
        
        self.player.set_property("volume", new_volume)
        
    def get_volume(self):
        """
        Gets volume of self.audiofile instance.
        new_volume must be float between 0.0 and 1.0
        """
        
        return self.player.get_property("volume")
        
    def get_length(self):
        """
        Returns the length of the currently played audiofile as
        a tuple of (hour, min, sec).
        """
        
        try:
            time.sleep(0.2) # from tutorial. Why necessary?
            int_dur = self.player.query_duration(gst.FORMAT_TIME, None)[0]
            length = self.convert_ns(int_dur)
            
            return length
            
        except gst.QueryError:
            return (0, 0, 0) # hour, min, sec
        
    def get_position(self):
        """
        Returns the position of the currently played audiofile as
        a tuple of (hour, min, sec).
        """
        
        try:
            pos_dur = self.player.query_position(gst.FORMAT_TIME, None)[0]
            pos = self.convert_ns(pos_dur)
            return pos
        
        except gst.QueryError:
            return (0, 0, 0)
        
    def set_position(self, new_pos):
        """
        Sets a new position of the currently played audiofile as 
        a tuple of (hour, min, sec)
        
        new_pos format is (hour, min, sec)
        """
        
        seek_pos = self.convert2ns(new_pos)
        self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, seek_pos)
        # time.sleep seemed to be necessary for action right after to work
        # correctly. test if it can be removed when the GUI is up and running.
        time.sleep(0.2)
        
    
    def on_message(self, bus, message):
        """
        Taken from gstreamer tutorial.
        http://pygstdocs.berlios.de/pygst-tutorial
        
        Used in:
        - connects to bus in class.
        """
        
        t = message.type
        if t == gst.MESSAGE_EOS: # track is finished
            self.player.set_state(gst.STATE_NULL) # change this to call next
        elif t == gst.MESSAGE_ERROR:
            self.player.set_state(gst.STATE_NULL)
            err, debug = message.parse_error()
            print "Error: %s" % err, debug
            
    def convert_ns(self, time):
        """
        Taken from gstreamer tutorial. Thank you very much to Sam Mason
        http://pygstdocs.berlios.de/pygst-tutorial/seeking.html
        adjusted to return a tuple (hour, min, sec)
        
        Converts nanoseconds to tuple (hour, min, sec).
        
        Used in:
        - method get_length
        """
        
        sec, nanosec = divmod(time, 1000000000)
        minute, sec = divmod(sec, 60)

        if minute < 60:
            return (0, int(minute), int(sec))
        else:
            hour, minute = divmod(minute, 60)
            return (int(hour), int(minute), int(sec))
        
    def convert2ns(self, time):
        """
        Converts a tuple of (hour, min, sec) to nanoseconds.
        """
        
        nanosec = 0
        hour, minute, sec = time
        
        if hour != 0:
            total_sec = (((hour*60)+minute)*60)+sec
            nanosec += total_sec*1000000000
            
        else:
            total_sec = (minute*60)+sec
            nanosec += total_sec*1000000000
            
        return nanosec

if __name__ == "__main__":
    
    gobject.threads_init()
    
    p = "/a/path/of/your/choosing"
    
    audio = AudioFile(p)
    
    audio.play()
    
    time.sleep(5)
    print audio.get_position()
    audio.set_position((0, 0, 10))
    
    print audio.get_position()
    print audio.get_length()
    print audio.get_volume()
    
    audio.set_volume(0.5)
    time.sleep(5)
    audio.set_volume(1.0)
    time.sleep(5)
    audio.pause()
    time.sleep(5)
    audio.play()
    time.sleep(5)
    audio.stop()
    
    gobject.MainLoop().run()