Initial UI hacking
authorKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Tue, 29 Dec 2009 15:06:46 +0000 (16:06 +0100)
committerKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Sat, 2 Jan 2010 23:37:44 +0000 (00:37 +0100)
jamaui/__init__.py
jamaui/dbus.py [new file with mode: 0644]
jamaui/player.py [new file with mode: 0644]
jamaui/ui.py [new file with mode: 0644]
jamaui/util.py [new file with mode: 0644]
scripts/player [new file with mode: 0644]
scripts/query

index e69de29..40fb3e8 100644 (file)
@@ -0,0 +1,3 @@
+import logging
+
+logging.basicConfig(level=logging.DEBUG, format="%(name)-15s: [%(lineno)4d] %(levelname)-8s %(message)s")
diff --git a/jamaui/dbus.py b/jamaui/dbus.py
new file mode 100644 (file)
index 0000000..196e250
--- /dev/null
@@ -0,0 +1,5 @@
+# Media player controls
+#dbus-send --print-reply --dest=com.nokia.mediaplayer /com/nokia/mediaplayer com.nokia.mediaplayer.mime_open string:"file:///$1"
+#dbus-send --dest=com.nokia.osso_media_server /com/nokia/osso_media_server com.nokia.osso_media_server.music.pause
+import dbus
+
diff --git a/jamaui/player.py b/jamaui/player.py
new file mode 100644 (file)
index 0000000..07b89b4
--- /dev/null
@@ -0,0 +1,221 @@
+# Implements playback controls
+# Gstreamer stuff mostly snibbed from Panucci
+#
+# This file is part of Panucci.
+# Copyright (c) 2008-2009 The Panucci Audiobook and Podcast Player Project
+#
+# Panucci is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Panucci is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Panucci.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import logging
+import pygst
+pygst.require('0.10')
+import gst
+import util
+
+log = logging.getLogger(__name__)
+
+
+class GStreamer(object):
+    """Wraps GStreamer"""
+    STATES = { gst.STATE_NULL    : 'stopped',
+               gst.STATE_PAUSED  : 'paused',
+               gst.STATE_PLAYING : 'playing' }
+
+    def __init__(self):
+        self.time_format = gst.Format(gst.FORMAT_TIME)
+        self.player = None
+        self.filesrc = None
+        self.filesrc_property = None
+        self.volume_control = None
+        self.volume_multiplier = 1
+        self.volume_property = None
+        self.eos_callback = lambda: self.stop()
+
+    def setup(self, filetype, uri):
+        if None in (filetype, uri):
+            self.player = None
+            return False
+
+        # On maemo use software decoding to workaround some bugs their gst:
+        # 1. Weird volume bugs in playbin when playing ogg or wma files
+        # 2. When seeking the DSPs sometimes lie about the real position info
+        if util.platform == 'maemo':
+            if not self._maemo_setup_hardware_player(filetype):
+                self._maemo_setup_software_player()
+                log.debug( 'Using software decoding (maemo)' )
+            else:
+                log.debug( 'Using hardware decoding (maemo)' )
+        else:
+            # This is for *ahem* "normal" versions of gstreamer
+            self._setup_playbin_player()
+            log.debug( 'Using playbin (non-maemo)' )
+
+        self._set_uri_to_be_played(uri)
+
+        bus = self._player.get_bus()
+        bus.add_signal_watch()
+        bus.connect('message', self._on_message)
+        return True
+
+    def get_state(self):
+        if self.player:
+            state = self._player.get_state()[1]
+            return self.STATES.get(state, 'none')
+        return 'none'
+
+    def playing(self):
+        return self.get_state() == 'playing'
+
+    def play(self):
+        if self.player:
+            self.player.set_state(gst.STATE_PLAYING)
+
+    def pause(self):
+        if self.player:
+            self.player.set_state(gst.STATE_PAUSED)
+
+    def play_pause_toggle(self):
+        self.pause() if self.playing() else self.play()
+
+    def stop(self):
+        if self.player:
+            self.player.set_state(gst.STATE_NULL)
+            self.player = None
+
+    def _maemo_setup_hardware_player( self, filetype ):
+        """ Setup a hardware player for mp3 or aac audio using
+        dspaacsink or dspmp3sink """
+
+        if filetype in [ 'mp3', 'aac', 'mp4', 'm4a' ]:
+            self.player = gst.element_factory_make('playbin', 'player')
+            self.filesrc = self.player
+            self.filesrc_property = 'uri'
+            self.volume_control = self.player
+            self.volume_multiplier = 10.
+            self.volume_property = 'volume'
+            return True
+        else:
+            return False
+
+    def _maemo_setup_software_player( self ):
+        """
+        Setup a software decoding player for maemo, this is the only choice
+        for decoding wma and ogg or if audio is to be piped to a bluetooth
+        headset (this is because the audio must first be decoded only to be
+        re-encoded using sbcenc.
+        """
+
+        self.player = gst.Pipeline('player')
+        src = gst.element_factory_make('gnomevfssrc', 'src')
+        decoder = gst.element_factory_make('decodebin', 'decoder')
+        convert = gst.element_factory_make('audioconvert', 'convert')
+        resample = gst.element_factory_make('audioresample', 'resample')
+        sink = gst.element_factory_make('dsppcmsink', 'sink')
+
+        self.filesrc = src # pointer to the main source element
+        self.filesrc_property = 'location'
+        self.volume_control = sink
+        self.volume_multiplier = 1
+        self.volume_property = 'fvolume'
+
+        # Add the various elements to the player pipeline
+        self.player.add( src, decoder, convert, resample, sink )
+
+        # Link what can be linked now, the decoder->convert happens later
+        gst.element_link_many( src, decoder )
+        gst.element_link_many( convert, resample, sink )
+
+        # We can't link the two halves of the pipeline until it comes
+        # time to start playing, this singal lets us know when it's time.
+        # This is because the output from decoder can't be determined until
+        # decoder knows what it's decoding.
+        decoder.connect('pad-added',
+                        self._on_decoder_pad_added,
+                        convert.get_pad('sink') )
+
+    def _setup_playbin_player( self ):
+        """ This is for situations where we have a normal (read: non-maemo)
+        version of gstreamer like on a regular linux distro. """
+        self.player = gst.element_factory_make('playbin2', 'player')
+        self.filesrc = self.player
+        self.filesrc_property = 'uri'
+        self.volume_control = self.player
+        self.volume_multiplier = 1.
+        self.volume_property = 'volume'
+
+    def _on_decoder_pad_added(self, decoder, src_pad, sink_pad):
+        # link the decoder's new "src_pad" to "sink_pad"
+        src_pad.link( sink_pad )
+
+    def _get_volume_level(self):
+        if self.volume_control is not None:
+            vol = self.volume_control.get_property( self.volume_property )
+            return  vol / float(self.volume_multiplier)
+
+    def _set_volume_level(self, value):
+        assert  0 <= value <= 1
+
+        if self.volume_control is not None:
+            vol = value * self.volume_multiplier
+            self.volume_control.set_property( self.volume_property, vol )
+
+    def _set_uri_to_be_played(self, uri):
+        # Sets the right property depending on the platform of self.filesrc
+        if self.player is not None:
+            self.filesrc.set_property(self.filesrc_property, uri)
+
+    def _on_message(self, bus, message):
+        t = message.type
+
+        if t == gst.MESSAGE_EOS:
+            self.eos_callback()
+
+        elif t == gst.MESSAGE_ERROR:
+            err, debug = message.parse_error()
+            log.critical( 'Error: %s %s', err, debug )
+            self.stop()
+
+    def set_eos_callback(self, cb):
+        self.eos_callback = cb
+
+class Playlist(object):
+    def __init__(self):
+        self.items = []
+
+    def add(self, item):
+        self.items.append(item)
+
+class Player(Playlist):
+    def __init__(self):
+        self.gstreamer = GStreamer()
+        self.gstreamer.set_eos_callback(self._on_eos)
+
+    def play(self, item=None):
+        pass
+
+    def pause(self):
+        pass
+
+    def stop(self):
+        pass
+
+    def next(self):
+        pass
+
+    def prev(self):
+        pass
+
+    def _on_eos(self):
+        pass
diff --git a/jamaui/ui.py b/jamaui/ui.py
new file mode 100644 (file)
index 0000000..e0bb84b
--- /dev/null
@@ -0,0 +1,28 @@
+import gtk
+import gobject
+import util
+
+log = logging.getLogger(__name__)
+
+try:
+    import hildon
+except:
+    if util.platform == 'maemo':
+        log.critical( 'Using GTK widgets, install "python2.5-hildon" '
+            'for this to work properly.' )
+
+class Jamaui(object):
+    def __init__(self):
+        self.app = None
+        self.window = None
+        if util.platform == 'maemo':
+            self.app = hildon.Program()
+            self.window = hildon.Window()
+            self.app.add_window(window)
+        else:
+            self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+
+    def run(self):
+        self.window.show()
+        gtk.main()
+
diff --git a/jamaui/util.py b/jamaui/util.py
new file mode 100644 (file)
index 0000000..6bff67f
--- /dev/null
@@ -0,0 +1,21 @@
+import os
+
+def string_in_file( filepath, string ):
+    try:
+        f = open( filepath, 'r' )
+        found = f.read().find( string ) != -1
+        f.close()
+    except:
+        found = False
+
+    return found
+
+def get_platform():
+    if ( os.path.exists('/etc/osso_software_version') or
+         os.path.exists('/proc/component_version') or
+         string_in_file('/etc/issue', 'maemo') ):
+        return 'maemo'
+    else:
+        return 'linux'
+
+platform = get_platform()
diff --git a/scripts/player b/scripts/player
new file mode 100644 (file)
index 0000000..706819f
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+# debugging hack - add . to path
+import os, sys
+local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
+if os.path.isdir(local_module_dir):
+    sys.path.append(local_module_dir)
+
+def main():
+    from jamaui.ui import JamaUI
+    player = JamaUI()
+    player.run()
+
+if __name__=="__main__":
+    main()
index ebf97d6..a8e94cf 100755 (executable)
@@ -1,6 +1,13 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
+
+# debugging hack - add . to path
+import os, sys
+local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
+if os.path.isdir(local_module_dir):
+    sys.path.append(local_module_dir)
+
 from jamaendo.api import LocalDB, Queries, refresh_dump
 from jamaendo.api import LocalDB, Queries, refresh_dump
-import sys, time
+import time
 
 class Refresher(object):
     def __init__(self):
 
 class Refresher(object):
     def __init__(self):