[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