=== added file 'src/common/sound.py'
--- /dev/null	
+++ src/common/sound.py	
@@ -0,0 +1,163 @@
+##	common/gst.py
+##
+## Contributors for this file:
+##	- Sami Haahtinen <ressu@ressukka.net>
+##
+## Copyright (C) 2006 Sami Haahtinen <ressu@ressukka.net>
+##
+## This program 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; version 2 only.
+##
+## This program 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.
+
+import gajim
+import os
+
+class GajimPlayer:
+  player = None
+
+  def __init__(self):
+    # For now, lets default to GStreamer, winsound, and then try External player
+    # FIXME: This really needs elegance!
+    if not gajim.config.get('sounds_on'):
+      raise NoSound
+      
+    try:
+      if not gajim.config.get('use_gstreamer'):
+        raise self.PlayerUnavailable
+      self.player = GajimGstPlayer()
+    except self.PlayerUnavailable:
+      try:
+        self.player = GajimWin32Player()
+      except self.PlayerUnavailable:
+        try:
+          self.player = GajimExtPlayer()
+        except self.PlayerUnavailable:
+          self.player = None # Make sure that player is still set to None
+          raise NoSound
+
+  class NoSound(Exception):
+    '''Notifies the system that we should be silent'''
+
+  class BeepOnly(Exception):
+    '''Notifies the system that we should just beep'''
+  
+  class PlayerUnavailable(Exception):
+    '''Notifies that the player is unavailable'''
+
+  def event_to_soundfile(self, event):
+    '''Returns the full file path for sound file of an event'''
+    # Check if we should play sound at all
+    if self.player is None:
+      raise NoSound
+
+    # Get soundevent
+    path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
+    
+    if path_to_soundfile is None or not os.path.exists(path_to_soundfile):
+      raise NoSound
+
+    return path_to_soundfile
+
+  def play(self, soundfile):
+    if soundfile == 'beep':
+      beep()
+    
+    if self.player is None:
+      return
+    
+    self.player.play_sound(soundfile)
+
+  def beep(self):
+    '''Play a single beep'''
+    # FIXME: There must be a better way
+    print "\a"
+
+# FIXME: This class hasn't gotten any testing!
+class GajimWin32Player:
+  '''User native Win32 layer to play sounds'''
+
+  def __init__(self):
+    try:
+      import winsound
+    except ImportError:
+      # gst module is not available...
+      raise GajimPlayer.PlayerUnavailable, "Winsound library not available"
+  
+  def play_sound(self,soundfile):
+    try:
+      winsound.PlaySound(soundfile, winsound.SND_FILENAME|winsound.SND_ASYNC)
+    except:
+      pass
+
+class GajimExtPlayer:
+  '''Use external application to play the sounds'''
+
+  def __init__(self):
+    self.player = gajim.config.get('soundplayer')
+    if self.player == '':
+      raise GajimPlayer.PlayerUnavailable, "External player not set"
+    # FIXME: We should check if the player exists!
+
+  def play_sound(self,soundfile):
+    soundfile = soundfile.replace('"', '\\"')
+    command = self.player + ' "' + soundfile + '" &'
+    os.system(command)
+
+class GajimGstPlayer:
+  '''Player that plays sound events through the GStreamer framework'''
+
+  def __init__(self):
+    try:
+      import gst
+    except ImportError:
+      # gst module is not available...
+      raise GajimPlayer.PlayerUnavailable, "Python Gstreamer modules not available"
+    self.gst = gst
+    self.GstBin = self.gst.parse_launch("playbin")
+
+    # Create a sink for video, if someone decides to use a video file ;)
+    fakesink = self.gst.element_factory_make("fakesink")
+    self.GstBin.set_property("video-sink", fakesink)
+
+    # Attach a message handler to the pipeline
+    bus = self.GstBin.get_bus()
+    bus.add_signal_watch()
+    bus.connect('message', self.gstevent)
+
+  def play_sound(self,soundfile):
+    self.GstBin.set_state(self.gst.STATE_NULL)
+
+    # since playbin takes input in URI format, add file:// prefix
+    sounduri = "file://" + os.path.abspath(soundfile)
+    self.GstBin.set_property('uri', sounduri)
+    
+    self.GstBin.set_state(self.gst.STATE_PLAYING)
+
+  def gstevent(self, bus, message):
+    t = message.type
+    if t == self.gst.MESSAGE_EOS:
+      self.reset_player()
+
+  def reset_player(self):
+    '''Simply Reset the player'''
+    self.GstBin.set_state(self.gst.STATE_NULL)
+
+def play_event(event):
+  global Player
+  try:
+    try:
+       soundfile = Player.event_to_soundfile(event)
+    except NameError:
+       Player = GajimPlayer()
+       soundfile = Player.event_to_soundfile(event)
+  except GajimPlayer.NoSound:
+    # No sound.
+    return
+
+  # Ok, we should be clear to play the sound
+  Player.play(soundfile)

=== modified file 'src/common/config.py'
--- src/common/config.py	
+++ src/common/config.py	
@@ -202,6 +202,7 @@
 		'chat_merge_consecutive_nickname': [opt_bool, False, _('Merge consecutive nickname in chat window')],
 		'chat_merge_consecutive_nickname_indent': [opt_str, '  ', _('Indentation when using merge consecutive nickame')],
 		'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#38995d:#519938:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors that will be used to color nicknames in groupchats'), True ],
+		'use_gstreamer': [opt_bool, False, _('Try to use gstreamer as the sound system'), True],
 	}
 
 	__options_per_key = {

=== modified file 'src/common/helpers.py'
--- src/common/helpers.py	
+++ src/common/helpers.py	
@@ -30,7 +30,6 @@
 from xmpp_stringprep import nodeprep, resourceprep, nameprep
 
 try:
-	import winsound # windows-only built-in module for playing wav
 	import win32api
 	import win32con
 except:
@@ -414,31 +413,6 @@
 		except:
 			pass
 
-def play_sound(event):
-	if not gajim.config.get('sounds_on'):
-		return
-	path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
-	if path_to_soundfile == 'beep':
-		print '\a' # make a speaker beep
-		return
-	if path_to_soundfile is None or not os.path.exists(path_to_soundfile):
-		return
-	if os.name == 'nt':
-		try:
-			winsound.PlaySound(path_to_soundfile,
-				winsound.SND_FILENAME|winsound.SND_ASYNC)
-		except:
-			pass
-	elif os.name == 'posix':
-		if gajim.config.get('soundplayer') == '':
-			return
-		player = gajim.config.get('soundplayer')
-		# we add the path in "" so we have good parsing from shell
-		path_to_soundfile = path_to_soundfile.replace('"', '\\"') # escape "
-		command = player + ' "' + path_to_soundfile + '" &'
-		#FIXME: when we require 2.4+ use subprocess module
-		os.system(command)
-
 def get_file_path_from_dnd_dropped_uri(uri):
 	path = urllib.url2pathname(uri) # escape special chars
 	path = path.strip('\r\n\x00') # remove \r\n and NULL

=== modified file 'src/config.py'
--- src/config.py	
+++ src/config.py	
@@ -37,6 +37,7 @@
 from common import helpers
 from common import gajim
 from common import connection
+from common import sound
 
 #---------- PreferencesWindow class -------------#
 class PreferencesWindow:
@@ -978,7 +979,7 @@
 		if not iter:
 			return
 		snd_event_config_name = model[iter][3]
-		helpers.play_sound(snd_event_config_name)
+		sound.play_event(snd_event_config_name)
 
 	def on_open_advanced_editor_button_clicked(self, widget, data = None):
 		if gajim.interface.instances.has_key('advanced_config'):

=== modified file 'src/gajim.py'
--- src/gajim.py	
+++ src/gajim.py	
@@ -40,6 +40,7 @@
 from chat_control import ChatControlBase
 
 from common import exceptions
+from common import sound
 
 try:
 	import gtk
@@ -617,7 +618,7 @@
 		msg = array[1]
 		# do not play sound when standalone chatstate message (eg no msg)
 		if msg and gajim.config.get_per('soundevents', 'message_sent', 'enabled'):
-			helpers.play_sound('message_sent')
+			sound.play_event('message_sent')
 		
 	def handle_event_subscribe(self, account, array):
 		#('SUBSCRIBE', account, (jid, text, user_nick)) user_nick is JEP-0172

=== modified file 'src/groupchat_control.py'
--- src/groupchat_control.py	
+++ src/groupchat_control.py	
@@ -35,6 +35,7 @@
 
 from common import gajim
 from common import helpers
+from common import sound
 
 from chat_control import ChatControl
 from chat_control import ChatControlBase
@@ -530,7 +531,7 @@
 
 		if kind == 'incoming': # it's a message NOT from us
 			# highlighting and sounds
-			(highlight, sound) = self.highlighting_for_message(text, tim)
+			(highlight, sound_event) = self.highlighting_for_message(text, tim)
 			gc_class=self.__class__
 			if gc_class.gc_custom_colors.has_key(contact):
 				other_tags_for_name.append('gc_nickname_color_' + \
@@ -548,10 +549,10 @@
 				self.parent_win.redraw_tab(self, 'attention')
 				other_tags_for_name.append('bold')
 				other_tags_for_text.append('marked')
-			if sound == 'received':
-				helpers.play_sound('muc_message_received')
-			elif sound == 'highlight':
-				helpers.play_sound('muc_message_highlight')
+			if sound_event == 'received':
+				sound.play_event('muc_message_received')
+			elif sound_event == 'highlight':
+				sound.play_event('muc_message_highlight')
 
 			self.check_and_possibly_add_focus_out_line()
 
@@ -561,25 +562,25 @@
 	def highlighting_for_message(self, text, tim):
 		'''Returns a 2-Tuple. The first says whether or not to highlight the
 		text, the second, what sound to play.'''
-		highlight, sound = (None, None)
+		highlight, sound_event = (None, None)
 
 		# Do we play a sound on every muc message?
 		if gajim.config.get_per('soundevents', 'muc_message_received', 'enabled'):
 			if gajim.config.get('notify_on_all_muc_messages'):
-				sound = 'received'
+				sound_event = 'received'
 
 		# Are any of the defined highlighting words in the text?
 		if self.needs_visual_notification(text):
 			highlight = True
 			if gajim.config.get_per('soundevents', 'muc_message_highlight',
 									'enabled'):
-				sound = 'highlight'
+				sound_event = 'highlight'
 
 		# Is it a history message? Don't want sound-floods when we join.
 		if tim != time.localtime():
-			sound = None
-
-		return (highlight, sound)
+			sound_event = None
+
+		return (highlight, sound_event)
 
 	def check_and_possibly_add_focus_out_line(self):
 		'''checks and possibly adds focus out line for room_jid if it needs it

=== modified file 'src/notify.py'
--- src/notify.py	
+++ src/notify.py	
@@ -24,6 +24,7 @@
 
 from common import gajim
 from common import helpers
+from common import sound
 
 import dbus_support
 if dbus_support.supported:
@@ -164,11 +165,11 @@
 	if (do_sound):
 		if (event == 'new_message'):
 			if first:
-				 helpers.play_sound('first_message_received')
+				 sound.play_event('first_message_received')
 			else:
-				 helpers.play_sound('next_message_received')
+				 sound.play_event('next_message_received')
 		elif (event == 'contact_connected' or event == 'contact_disconnected'):
-			helpers.play_sound(event)	
+			sound.play_event(event)	
 	 
 
 def popup(event_type, jid, account, msg_type = '', path_to_image = None,

