[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdegames
From:       Wolfgang Rohdewald <wolfgang () rohdewald ! de>
Date:       2012-03-09 4:48:09
Message-ID: 20120309044809.B4E2EAC899 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1284540 by wrohdewald:

use predefined voices from /usr/share/kd4/apps/kajongg/voices

language preference is the same as for the kajongg application

 M  +41 -11    doc/kajongg/index.docbook  
 M  +13 -7     kajongg/src/game.py  
 M  +4 -3      kajongg/src/message.py  
 M  +68 -21    kajongg/src/sound.py  


--- trunk/KDE/kdegames/doc/kajongg/index.docbook #1284539:1284540
@@ -628,31 +628,61 @@
 
 	<sect2><title>Sound</title>
 	<para>
-	It is possible to record sound files for your announcements which will be heard by the other players. \
There is some manual work needed: +		Playing gives you a much nicer experience if you hear players claim \
tiles and announce discards. It also makes playing +		easier because you can concentrate on your own \
tiles, you do not have to watch what others discard.  </para>
+	<para>
+		&kajongg; comes with voices for different players. It automatically assigns voices to players. For \
every language there should be +		four voices, preferably two male and two female. &kajongg; tries to use \
voices in the same language &kajongg; runs in. If it +		finds no voices in that language, &kajongg; will \
fallback to other languages, using the same fallback languages you defined for +		&kajongg; (see \
<menuchoice><guimenu>Help</guimenu><guimenuitem>Switch Application Language</guimenuitem></menuchoice>). \
If no +		voice files exist yet for your language, you are invited to record and contribute them to \
&kajongg;. +	</para>
+	<para>
+		</para><itemizedlist><listitem><para>
+		This is how you can generate voices yourself. There is a separate directory for every voice. There are \
two groups of voices +		with different places in the file system:
             <itemizedlist><listitem><para>
-		First you need to find out where to store your voice files. This folder normally is \
                ~/.kde/share/apps/kajongg. So if your login name is joe,
-		this might be /home/joe/.kde/share/apps/kajongg. In there you will also find the database kajongg.db.
+			Predefined voices for every language. They live in a directory like <filename \
class="directory">/usr/share/kde4/apps/kajongg/voices/LANG</filename> where +			LANG is the code for your \
language. The voice directories directly under <filename class="directory">.../kajongg/voices/</filename> \
are the US-english +			versions. Predefined voices are randomly assigned to players. Those voices have \
names like <literal>"male1 male2 female1 female2"</literal> but those names do not matter, the user will \
never see them. Also, it makes no sense to define more than four predefined voices per language.  \
                </para></listitem><listitem><para>
-	        In the folder you just located, generate a new subfolder with the name <filename \
class="directory">voices</filename>. +			User voices can be defined for a specific user.
+			If &kajongg; makes use of such a voice, it can automatically be transferred to other players
+			over the internet.
+			Those voices live in a directory like <filename \
class="directory">~/.kde/share/apps/kajongg/voices/</filename>. +			So if your login name is joe, this \
might be <filename class="directory">/home/joe/.kde/share/apps/kajongg/voices/joe/</filename>. +			The \
directory <filename class="directory">voices/</filename> might not yet exist - if so, create it. \
+		</para></listitem></itemizedlist>  </para></listitem><listitem><para>
-	        In the folder <filename class="directory">voices</filename> generate another subfolder with \
                your player name. This is the name you use when
-	        connecting to a game server. You may also want to define voices for the players <filename>ROBOT \
1</filename>, <filename>ROBOT 2</filename>, <filename>ROBOT 3</filename>. +		Now that you know where to \
place your new voice, generate a new directory for it.  </para></listitem><listitem><para>
 	        In that subfolder, you can place the sound files. They have to be encoded with the <ulink \
url="http://xiph.org/vorbis/">Ogg Vorbis</ulink> audio compressor, the file names need to  have the \
extension <filename class="extension">.ogg</filename>. +			A single file has a name like \
<filename>s3.ogg</filename> - this is Stone 3. You should define such files for all tiles and +			for all \
claims and announcements. But if a sound file is missing, nothing bad happens - you just will not hear \
that sound. Fallback +			to other voices or languages only works for entire voices, not for single sound \
files.  </para></listitem><listitem><para>
-	        A single file has a name like <filename>s3.ogg</filename> - this is Stone 3. You should define \
                such files for all tiles and
-	        for all claims and announcements.
+		After having defined all sound files for a predefined voice, please make sure that the &kajongg; \
process has write permission in +		the new voice directory. Start &kajongg; and test the voice. After \
everything is OK, you can revoke write permission (and you should +		do so for predefined voices). \
&kajongg; will automatically generate a file named <filename>md5sum</filename> containing a checksum \
+		over all sound files. Whenever you change, add or delete a sound file for a voice, &kajongg; will try \
to rewrite that file.  </para></listitem></itemizedlist>
-            <para>Please make the length of the sound short but speak clearly - this game can be played \
very fast when you get used to it. It is easier if you first write down a list with everything that \
should be spoken. Do not read it as a sequence of items, try to pronounce each item as if it were the \
                only one and as you would when playing. Reading this needs a little practicing.</para>
-            <para>This is just one possibility to create the sound files:
+	<para>
+		Please make the length of the sound short but speak clearly - this game can be played very fast when \
you get used to it. It is easier +		if you first write down a list with everything that should be spoken. \
Do not read it as a sequence of items, try to pronounce each +		item as if it were the only one and as \
you would when playing. Leave room between the items. Reading this needs a little practicing.</para> \
+	<para> +		This is just one possibility to create the sound files:
 	    </para><para><itemizedlist><listitem><para>
 	      Record a sound file with the program <command>qarecord</command>, save it in the \
<literal>WAV</literal> format.  In this single sound file, speak all of the sounds.
 	    </para></listitem><listitem><para>
 	     Edit that file with the sound editor <command>audacity</command>. In that editor, use the mouse to \
                mark a range of the sound waves.
-	The editor will play that part. Repeat until you have the correct part selected. This may be easier if \
you <menuchoice><guimenu>View</guimenu><guimenuitem>Zoom In</guimenuitem></menuchoice>. Use the menu \
command <menuchoice><guimenu>File</guimenu><guimenuitem>Export Selection</guimenuitem></menuchoice> and \
save in the ogg format. Under <guilabel>Options</guilabel>, choose low quality - this is sufficient and \
even +		The editor will play that part. Repeat until you have the correct part selected.
+		This may be easier if you <menuchoice><guimenu>View</guimenu><guimenuitem>Zoom \
In</guimenuitem></menuchoice>. +		Use the menu command \
<menuchoice><guimenu>File</guimenu><guimenuitem>Export Selection</guimenuitem></menuchoice> and save in \
the ogg format. +		Under <guilabel>Options</guilabel>, choose low quality - this is sufficient and even
 	     wanted because this reduces the time to transfer the sounds to the other players.
             </para></listitem></itemizedlist></para>
             <para>The following are the names of the files that you should generate. Always append \
                <filename>.ogg</filename> to them:</para>
--- trunk/KDE/kdegames/kajongg/src/game.py #1284539:1284540
@@ -29,7 +29,7 @@
 from tile import Tile
 from meld import tileKey
 from scoringengine import HandContent
-from sound import Voice, Sound
+from sound import Voice
 from wall import Wall
 from move import Move
 from animation import Animated
@@ -264,8 +264,6 @@
     def assignVoices(self):
         """now we have all remote user voices"""
         assert self.belongsToHumanPlayer()
-        if not Sound.enabled:
-            return
         available = Voice.availableVoices()[:]
         # available is without transferred human voices
         for player in self.players:
@@ -278,11 +276,19 @@
                 if player.voice:
                     if Debug.sound:
                         logDebug('%s has own local voice %s' % (player.name, player.voice))
-            if player.voice in available:
-                available.remove(player.voice)
+            if player.voice:
+                for voice in Voice.availableVoices():
+                    if voice in available and voice.md5sum == player.voice.md5sum:
+                        # if the local voice is also predefined,
+                        # make sure we do not use both
+                        available.remove(voice)
+        # for the other players use predefined voices in preferred language. Only if
+        # we do not have enough predefined voices, look again in locally defined voices
+        predefined = [x for x in available if x.language() != 'local']
+        predefined.extend(available)
         for player in self.players:
-            if player.voice is None and available:
-                player.voice = available.pop()
+            if player.voice is None and predefined:
+                player.voice = predefined.pop(0)
                 if Debug.sound:
                     logDebug('%s gets one of the still available voices %s' % (player.name, \
player.voice))  
--- trunk/KDE/kdegames/kajongg/src/message.py #1284539:1284540
@@ -400,14 +400,15 @@
     """we got voice sounds from the server, assign them to the player voice"""
     def clientAction(self, dummyClient, move):
         """server sent us voice sounds about somebody else"""
+        move.player.voice = Voice(move.md5sum, move.source)
         if Debug.sound:
-            logDebug('%s gets voice data %s from server' % (
-                move.player, move.player.voice))
-        move.player.voice = Voice(move.md5sum, move.source)
+            logDebug('%s gets voice data %s from server, language=%s' % (
+                move.player, move.player.voice, move.player.voice.language()))
 
 class MessageAssignVoices(ServerMessage):
     """The server tells us that we now got all voice data available"""
     def clientAction(self, client, move):
+        if Sound.enabled:
         client.game.assignVoices()
 
 class MessageClientWantsVoiceData(ClientMessage):
--- trunk/KDE/kdegames/kajongg/src/sound.py #1284539:1284540
@@ -24,8 +24,15 @@
     import winsound # pylint: disable=F0401
 
 import common
-from util import which, logWarning, m18n, appdataDir, cacheDir, logDebug, \
-    removeIfExists
+from util import which, logWarning, m18n, cacheDir, logDebug, \
+    removeIfExists, logException
+
+try:
+    from kde import KGlobal, KConfigGroup
+    HAVE_KDE = True
+except BaseException:
+    HAVE_KDE = False
+
 from common import Debug
 from meld import Meld
 
@@ -138,29 +145,59 @@
     def __repr__(self):
         return "<Voice: %s>" % self
 
+    def language(self):
+        """the language code of this voice. Locally defined voices
+        have no language code and return 'local'.
+        remote voices received from other clients return 'remote',
+        they always get predecence."""
+        if len(self.directory) == 32:
+            if os.path.split(self.directory)[1] == self.md5sum:
+                # TODO: test this. Needs separate computers for server and client.
+                return 'remote'
+        if self.directory.startswith(os.environ['HOME']):
+            # TODO: how is this on Windows?
+            return 'local'
+        result = os.path.split(self.directory)[0]
+        result = os.path.split(result)[1]
+        if result == 'voices':
+            result = 'en_US'
+        return result
+
+
     @staticmethod
     def availableVoices():
         """a list of all voice directories"""
-        if not Voice.__availableVoices:
-            ownVoices = os.path.join(appdataDir(), 'voices')
-            if not os.path.exists(ownVoices):
-                # happens if we use an empty $HOME for testing
-                os.makedirs(ownVoices)
-            voices = [os.path.join(ownVoices, x) for x in sorted(os.listdir(ownVoices))]
-            voices = [x for x in voices if os.path.exists(os.path.join(x, 's1.ogg'))]
-            Voice.__availableVoices = list(Voice(x) for x in voices)
+        if not Voice.__availableVoices and HAVE_KDE:
+            result = []
+            for parentDirectory in KGlobal.dirs().findDirs("appdata", "voices"):
+                parentDirectory = unicode(parentDirectory)
+                for (dirpath, _, _) in os.walk(parentDirectory, followlinks=True):
+                    if os.path.exists(os.path.join(dirpath, 's1.ogg')):
+                        result.append(Voice(dirpath))
+            config = KGlobal.config()
+            group = KConfigGroup(config, 'Locale')
+            prefLanguages = str(group.readEntry('Language')).split(':')
+            prefLanguages.insert(0, 'local')
+            if 'en_US' not in prefLanguages:
+                prefLanguages.append('en_US')
+            prefLanguages = dict((x[1], x[0]) for x in enumerate(prefLanguages))
+            result = sorted(result, key=lambda x: prefLanguages.get(x.language(), 9999))
+            if Debug.sound:
+                logDebug('found voices:%s' % [str(x) for x in result])
+            Voice.__availableVoices = result
         return Voice.__availableVoices
 
     @staticmethod
     def locate(name):
-        """returns Voice or None if no voice matches"""
+        """returns Voice or None if no foreign or local voice matches.
+        In other words never return a predefined voice"""
         for voice in Voice.availableVoices():
             dirname = os.path.split(voice.directory)[-1]
             if name == voice.md5sum:
                 if Debug.sound:
                     logDebug('locate found %s by md5sum in %s' % (name, voice.directory))
                 return voice
-            elif name == dirname:
+            elif name == dirname and voice.language() == 'local':
                 if Debug.sound:
                     logDebug('locate found %s by name in %s' % (name, voice.directory))
                 return voice
@@ -205,19 +242,20 @@
             md5sum.update(open(os.path.join(self.directory, oggFile)).read())
         # the md5 stamp goes into the old archive directory 'username'
         self.__md5sum = md5sum.hexdigest()
-        if os.path.exists(md5FileName):
-            existingMd5sum = open(md5FileName, 'r').readlines()[0].strip()
-        else:
-            existingMd5sum = None
+        existingMd5sum = self.savedmd5Sum()
+        md5Name = self.md5FileName()
         if self.__md5sum != existingMd5sum:
             if Debug.sound:
-                if not os.path.exists(md5FileName):
-                    logDebug('creating new %s' % md5FileName)
+                if not os.path.exists(md5Name):
+                    logDebug('creating new %s' % md5Name)
                 else:
-                    logDebug('md5sum %s changed, rewriting %s with %s' % (existingMd5sum, md5FileName, \
                self.__md5sum))
-            open(md5FileName, 'w').write('%s\n' % self.__md5sum)
+                    logDebug('md5sum %s changed, rewriting %s with %s' % (existingMd5sum, md5Name, \
self.__md5sum)) +            try:
+                open(md5Name, 'w').write('%s\n' % self.__md5sum)
+            except BaseException, exception:
+                logException(m18n('cannot write <filename>%1</filename>: %2', md5Name, str(exception)))
         if archiveExists:
-            archiveIsOlder = os.path.getmtime(md5FileName) > os.path.getmtime(self.archiveName())
+            archiveIsOlder = os.path.getmtime(md5Name) > os.path.getmtime(self.archiveName())
             if self.__md5sum != existingMd5sum or archiveIsOlder:
                 os.remove(self.archiveName())
 
@@ -234,6 +272,15 @@
         """ the full path of the archive file"""
         return os.path.join(self.directory, 'content.tbz')
 
+    def md5FileName(self):
+        """the name of the md5sum file"""
+        return os.path.join(self.directory, 'md5sum')
+
+    def savedmd5Sum(self):
+        """returns the current value of the md5sum file"""
+        if os.path.exists(self.md5FileName()):
+            return open(self.md5FileName(), 'r').readlines()[0].strip()
+
     @apply
     def md5sum():
         """the current checksum over all ogg files"""


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic