diff --git a/DroidFish/res/values/strings.xml b/DroidFish/res/values/strings.xml
index e7ef65f..c5eb2bc 100644
--- a/DroidFish/res/values/strings.xml
+++ b/DroidFish/res/values/strings.xml
@@ -704,10 +704,12 @@ you are not actively using the program.\
- off
- sound
- speech_en
-
+ - speech_de
+
- Off
- Play sound
- English Speech
-
+ - German Speech
+
diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java
index 9892a59..4627fa2 100644
--- a/DroidFish/src/org/petero/droidfish/DroidFish.java
+++ b/DroidFish/src/org/petero/droidfish/DroidFish.java
@@ -3776,7 +3776,7 @@ public class DroidFish extends Activity
/** Initialize text to speech if enabled in settings. */
private void initSpeech() {
if (moveAnnounceType.startsWith("speech_"))
- speech.initialize(this);
+ speech.initialize(this, moveAnnounceType.substring(7));
}
@Override
@@ -3793,7 +3793,7 @@ public class DroidFish extends Activity
}
}
} else if (moveAnnounceType.startsWith("speech_")) {
- speech.say(pos, move, moveAnnounceType.substring(7));
+ speech.say(pos, move);
}
if (vibrateEnabled) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
diff --git a/DroidFish/src/org/petero/droidfish/Speech.java b/DroidFish/src/org/petero/droidfish/Speech.java
index 5ec901a..87507ff 100644
--- a/DroidFish/src/org/petero/droidfish/Speech.java
+++ b/DroidFish/src/org/petero/droidfish/Speech.java
@@ -33,11 +33,33 @@ import android.widget.Toast;
/** Handles text to speech translation. */
public class Speech {
private TextToSpeech tts;
- boolean initialized = false;
- boolean supported = false;
- String toSpeak = null;
+ private boolean initialized = false;
+ private String toSpeak = null;
- public void initialize(final Context context) {
+ public enum Language {
+ EN, // English
+ DE, // German
+ NONE; // Not supported
+
+ public static Language fromString(String langStr) {
+ if ("en".equals(langStr))
+ return EN;
+ if ("de".equals(langStr))
+ return DE;
+ return NONE;
+ }
+ }
+ private Language lang;
+
+
+ /** Initialize the text to speech engine for a given language. */
+ public void initialize(final Context context, final String langStr) {
+ Language newLang = Language.fromString(langStr);
+ if (newLang != lang)
+ shutdown();
+ final Locale loc = getLocale(newLang);
+ if (loc == null)
+ initialized = true;
if (initialized)
return;
tts = new TextToSpeech(context, new OnInitListener() {
@@ -46,12 +68,12 @@ public class Speech {
initialized = true;
int toast = -1;
if (status == TextToSpeech.SUCCESS) {
- int code = tts.setLanguage(Locale.US);
+ int code = tts.setLanguage(loc);
switch (code) {
case TextToSpeech.LANG_AVAILABLE:
case TextToSpeech.LANG_COUNTRY_AVAILABLE:
case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE:
- supported = true;
+ lang = Language.fromString(langStr);
say(toSpeak);
break;
case TextToSpeech.LANG_MISSING_DATA:
@@ -75,7 +97,7 @@ public class Speech {
@SuppressWarnings("deprecation")
public void say(String text) {
if (initialized) {
- if (supported && text != null)
+ if (lang != Language.NONE && text != null)
tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
toSpeak = null;
} else {
@@ -95,22 +117,22 @@ public class Speech {
if (tts != null) {
tts.shutdown();
tts = null;
+ lang = Language.NONE;
initialized = false;
- supported = false;
}
}
/** Convert move "move" in position "pos" to a sentence and speak it. */
- public void say(Position pos, Move move, String langStr) {
- String s = moveToText(pos, move, langStr);
+ public void say(Position pos, Move move) {
+ String s = moveToText(pos, move, lang);
// System.out.printf("%.3f Speech.say(): %s\n", System.currentTimeMillis() * 1e-3, s);
if (!s.isEmpty())
say(s);
}
/** Convert move "move" in position "pos" to a sentence that can be spoken. */
- public static String moveToText(Position pos, Move move, String langStr) {
- if (move == null || !langStr.equals("en"))
+ public static String moveToText(Position pos, Move move, Language lang) {
+ if (move == null)
return "";
String moveStr = TextIO.moveToString(pos, move, false, false);
@@ -120,12 +142,15 @@ public class Speech {
boolean check = moveStr.endsWith("+");
boolean checkMate = moveStr.endsWith("#");
boolean castle = false;
+ boolean enPassant = false;
if (piece == Piece.WPAWN && !capture) {
int fx = Position.getX(move.from);
int tx = Position.getX(move.to);
- if (fx != tx)
+ if (fx != tx) {
capture = true; // En passant
+ enPassant = true;
+ }
}
StringBuilder sentence = new StringBuilder();
@@ -134,10 +159,10 @@ public class Speech {
int fx = Position.getX(move.from);
int tx = Position.getX(move.to);
if (fx == 4 && tx == 6) {
- sentence.append("Short castle");
+ addWord(sentence, castleToString(true, lang));
castle = true;
} else if (fx == 4 && (tx == 2)) {
- sentence.append("Long castle");
+ addWord(sentence, castleToString(false, lang));
castle = true;
}
}
@@ -145,65 +170,178 @@ public class Speech {
if (!castle) {
boolean pawnMove = piece == Piece.WPAWN;
if (!pawnMove)
- sentence.append(pieceName(piece)).append(' ');
+ addWord(sentence, pieceName(piece, lang));
if (capture) {
int i = moveStr.indexOf("x");
String from = moveStr.substring(pawnMove ? 0 : 1, i);
if (!from.isEmpty())
- sentence.append(getFromWord(from)).append(' ');
+ addWord(sentence, fromToString(from, lang));
String to = moveStr.substring(i + 1, i + 3);
- sentence.append(to.startsWith("e") ? "take " : "takes ");
- sentence.append(to).append(' ');
+ addWord(sentence, captureToString(to, lang));
+ addWord(sentence, toToString(to, lang));
+ if (enPassant)
+ addWord(sentence, epToString(lang));
} else {
int nSkip = (promotion ? 1 : 0) + ((check | checkMate) ? 1 : 0);
int i = moveStr.length() - nSkip;
String from = moveStr.substring(pawnMove ? 0 : 1, i - 2);
if (!from.isEmpty())
- sentence.append(from).append(' ');
+ addWord(sentence, fromToString(from, lang));
String to = moveStr.substring(i - 2, i);
- sentence.append(to).append(' ');
+ addWord(sentence, toToString(to, lang));
}
if (promotion)
- sentence.append(pieceName(move.promoteTo)).append(' ');
+ addWord(sentence, promToString(move.promoteTo, lang));
}
if (checkMate) {
- removeLastSpace(sentence);
- sentence.append(". Check mate!");
+ addWord(sentence, checkMateToString(lang));
} else if (check) {
- removeLastSpace(sentence);
- sentence.append(". Check!");
+ addWord(sentence, checkToString(lang));
}
return sentence.toString().trim();
}
+ /** Return the locale corresponding to a language string,
+ * or null if language not supported. */
+ private static Locale getLocale(Language lang) {
+ switch (lang) {
+ case EN:
+ return Locale.US;
+ case DE:
+ return Locale.GERMAN;
+ case NONE:
+ return null;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ /** Add zero or more words to the string builder.
+ * If anything was added, an extra space is also added at the end. */
+ private static void addWord(StringBuilder sb, String words) {
+ if (!words.isEmpty())
+ sb.append(words).append(' ');
+ }
+
/** Get the name of a non-pawn piece. Return empty string if no such piece. */
- private static String pieceName(int piece) {
+ private static String pieceName(int piece, Language lang) {
piece = Piece.makeWhite(piece);
- switch (piece) {
- case Piece.WKING: return "King";
- case Piece.WQUEEN: return "Queen";
- case Piece.WROOK: return "Rook";
- case Piece.WBISHOP: return "Bishop";
- case Piece.WKNIGHT: return "Knight";
- default: return "";
- }
+ switch (lang) {
+ case EN:
+ switch (piece) {
+ case Piece.WKING: return "King";
+ case Piece.WQUEEN: return "Queen";
+ case Piece.WROOK: return "Rook";
+ case Piece.WBISHOP: return "Bishop";
+ case Piece.WKNIGHT: return "Knight";
+ default: return "";
+ }
+ case DE:
+ switch (piece) {
+ case Piece.WKING: return "König";
+ case Piece.WQUEEN: return "Dame";
+ case Piece.WROOK: return "Turm";
+ case Piece.WBISHOP: return "Läufer";
+ case Piece.WKNIGHT: return "Springer";
+ default: return "";
+ }
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
}
- /** Transform a "from" file or file+rank to a word. */
- private static String getFromWord(String from) {
- if ("a".equals(from))
- return "ae";
- return from;
+ private static String fromToString(String from, Language lang) {
+ switch (lang) {
+ case EN:
+ if ("a".equals(from))
+ return "ae";
+ return from;
+ case DE:
+ return from;
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
}
- /** If the last character in the StringBuilder is a space, remove it. */
- private static void removeLastSpace(StringBuilder sb) {
- int len = sb.length();
- if (len > 0 && sb.charAt(len - 1) == ' ')
- sb.setLength(len - 1);
+ private static String toToString(String to, Language lang) {
+ return to;
+ }
+
+ private static String captureToString(String to, Language lang) {
+ switch (lang) {
+ case EN:
+ return to.startsWith("e") ? "take" : "takes";
+ case DE:
+ return "schlägt";
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private static String castleToString(boolean kingSide, Language lang) {
+ switch (lang) {
+ case EN:
+ return kingSide ? "Short castle" : "Long castle";
+ case DE:
+ return kingSide ? "Kleine Rochade" : "Große Rochade";
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private static String epToString(Language lang) {
+ switch (lang) {
+ case EN:
+ return "";
+ case DE:
+ return "en passant";
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private static String promToString(int piece, Language lang) {
+ String pn = pieceName(piece, lang);
+ switch (lang) {
+ case EN:
+ return pn;
+ case DE:
+ return "Umwandlung zu " + pn;
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private static String checkToString(Language lang) {
+ switch (lang) {
+ case EN:
+ return "check!";
+ case DE:
+ return "Schach!";
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private static String checkMateToString(Language lang) {
+ switch (lang) {
+ case EN:
+ return "check mate!";
+ case DE:
+ return "Schach matt!";
+ case NONE:
+ return "";
+ }
+ throw new IllegalArgumentException();
}
}
diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/SpeechTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/SpeechTest.java
index 24c3c0a..c4303c5 100644
--- a/DroidFishTest/src/org/petero/droidfish/gamelogic/SpeechTest.java
+++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/SpeechTest.java
@@ -26,130 +26,183 @@ public class SpeechTest extends TestCase {
public SpeechTest() {
}
+ private String[] moveToText(Position pos, Move move, String[] langStrs) {
+ String[] ret = new String[langStrs.length];
+ for (int i = 0; i < langStrs.length; i++) {
+ Speech.Language lang = Speech.Language.fromString(langStrs[i]);
+ assertTrue(lang != null);
+ ret[i] = Speech.moveToText(pos, move, lang);
+ }
+ return ret;
+ }
+
+ private static void assertEquals(String[] expected, String[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; i++)
+ assertEquals(expected[i], actual[i]);
+ }
+
public void testEnglish() {
- String lang = "en";
+ String[] lang = {"en", "de"};
{
Game game = new Game(null, new TimeControlData());
Pair res = game.processString("e4");
- assertEquals("e4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"e4", "e4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("d5");
- assertEquals("d5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"d5", "d5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("exd5");
- assertEquals("e takes d5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"e takes d5", "e schlägt d5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Qxd5");
- assertEquals("Queen takes d5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Queen takes d5", "Dame schlägt d5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Ne2");
- assertEquals("Knight e2", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Knight e2", "Springer e2"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Nf6");
- assertEquals("Knight f6", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Knight f6", "Springer f6"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Nbc3");
- assertEquals("Knight b c3", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Knight b c3", "Springer b c3"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("e5");
- assertEquals("e5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"e5", "e5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("b4");
- assertEquals("b4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"b4", "b4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("a5");
- assertEquals("a5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"a5", "a5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("a3");
- assertEquals("a3", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"a3", "a3"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("axb4");
- assertEquals("ae takes b4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"ae takes b4", "a schlägt b4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("axb4");
- assertEquals("ae takes b4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"ae takes b4", "a schlägt b4"},
+ moveToText(game.prevPos(), res.second, lang));
}
{
Game game = new Game(null, new TimeControlData());
Pair res = game.processString("d4");
- assertEquals("d4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"d4", "d4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("e5");
- assertEquals("e5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"e5", "e5"}, moveToText(game.prevPos(), res.second, lang));
res = game.processString("dxe5");
- assertEquals("d take e5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"d take e5", "d schlägt e5"}, moveToText(game.prevPos(), res.second, lang));
res = game.processString("f6");
- assertEquals("f6", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"f6", "f6"}, moveToText(game.prevPos(), res.second, lang));
res = game.processString("exf6");
- assertEquals("e takes f6", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"e takes f6", "e schlägt f6"}, moveToText(game.prevPos(), res.second, lang));
res = game.processString("Bb4");
- assertEquals("Bishop b4. Check!", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Bishop b4 check!", "Läufer b4 Schach!"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("c3");
- assertEquals("c3", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"c3", "c3"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Ne7");
- assertEquals("Knight e7", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Knight e7", "Springer e7"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("cxb4");
- assertEquals("c takes b4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"c takes b4", "c schlägt b4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("O-O");
- assertEquals("Short castle", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Short castle", "Kleine Rochade"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("fxg7");
- assertEquals("f takes g7", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"f takes g7", "f schlägt g7"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("h6");
- assertEquals("h6", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"h6", "h6"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("gxf8Q+");
- assertEquals("g takes f8 Queen. Check!", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"g takes f8 Queen check!", "g schlägt f8 Umwandlung zu Dame Schach!"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Kxf8");
- assertEquals("King takes f8", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"King takes f8", "König schlägt f8"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("b5");
- assertEquals("b5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"b5", "b5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("a5");
- assertEquals("a5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"a5", "a5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("bxa6");
- assertEquals("b takes a6", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"b takes a6", "b schlägt a6 en passant"},
+ moveToText(game.prevPos(), res.second, lang));
}
{
Game game = new Game(null, new TimeControlData());
Pair res = game.processString("f4");
- assertEquals("f4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"f4", "f4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("e5");
- assertEquals("e5", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"e5", "e5"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("g4");
- assertEquals("g4", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"g4", "g4"},
+ moveToText(game.prevPos(), res.second, lang));
res = game.processString("Qh4");
- assertEquals("Queen h4. Check mate!", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Queen h4 check mate!", "Dame h4 Schach matt!"},
+ moveToText(game.prevPos(), res.second, lang));
}
{
Game game = new Game(null, new TimeControlData());
playMoves(game, "d4 d5 Nc3 Nc6 Bf4 Bf5 Qd2 Qd7");
Pair res = game.processString("O-O-O");
- assertEquals("Long castle", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Long castle", "Große Rochade"},
+ moveToText(game.prevPos(), res.second, lang));
playMoves(game, "Nxd4 Nxd5 Qxd5 Qxd4 Qxd4 Nf3 Qxd1 Kxd1");
res = game.processString("O-O-O");
- assertEquals("Long castle. Check!", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Long castle check!", "Große Rochade Schach!"},
+ moveToText(game.prevPos(), res.second, lang));
+ playMoves(game, "Kc1");
+ res = game.processString("Rd7");
+ assertEquals(new String[]{"Rook d7", "Turm d7"},
+ moveToText(game.prevPos(), res.second, lang));
}
{
Game game = new Game(null, new TimeControlData());
playMoves(game, "e4 e5 h3 Bb4 Ne2 Bc3");
Pair res = game.processString("Nexc3");
- assertEquals("Knight e takes c3", Speech.moveToText(game.prevPos(), res.second, lang));
+ assertEquals(new String[]{"Knight e takes c3", "Springer e schlägt c3"},
+ moveToText(game.prevPos(), res.second, lang));
}
}