Browse Source

Rework the 'Punctuation level' (now 'Speak punctuation') preference to include a custom punctuation list.

master
Reece H. Dunn 11 years ago
parent
commit
1de60cdc15

+ 2
- 0
android/jni/Android.mk View File

@@ -1,6 +1,8 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS = -std=c++11

# ucd-tools wide-character compatibility support:

UCDTOOLS_SRC_PATH := ../../ucd-tools/src

+ 88
- 0
android/jni/jni/eSpeakService.cpp View File

@@ -31,6 +31,77 @@
#include <TtsEngine.h>
#include <Log.h>

/** @name Java to Wide String Helpers
* @brief These are helpers for converting a jstring to wchar_t*.
*
* This assumes that wchar_t is a 32-bit (UTF-32) value.
*/
//@{

const char *utf8_read(const char *in, wchar_t &c)
{
if (uint8_t(*in) < 0x80)
c = *in++;
else switch (uint8_t(*in) & 0xF0)
{
default:
c = uint8_t(*in++) & 0x1F;
c = (c << 6) + (uint8_t(*in++) & 0x3F);
break;
case 0xE0:
c = uint8_t(*in++) & 0x0F;
c = (c << 6) + (uint8_t(*in++) & 0x3F);
c = (c << 6) + (uint8_t(*in++) & 0x3F);
break;
case 0xF0:
c = uint8_t(*in++) & 0x07;
c = (c << 6) + (uint8_t(*in++) & 0x3F);
c = (c << 6) + (uint8_t(*in++) & 0x3F);
c = (c << 6) + (uint8_t(*in++) & 0x3F);
break;
}
return in;
}

class unicode_string
{
static_assert(sizeof(wchar_t) == 4, "wchar_t is not UTF-32");
public:
unicode_string(JNIEnv *env, jstring str);
~unicode_string();

const wchar_t *c_str() const { return mString; }
private:
wchar_t *mString;
};

unicode_string::unicode_string(JNIEnv *env, jstring str)
: mString(NULL)
{
if (str == NULL) return;

const char *utf8 = env->GetStringUTFChars(str, NULL);
mString = (wchar_t *)malloc(strlen(utf8) + 1);

const char *utf8_current = utf8;
wchar_t *utf32_current = mString;
while (*utf8_current)
{
utf8_current = utf8_read(utf8_current, *utf32_current);
++utf32_current;
}
*utf32_current = 0;

env->ReleaseStringUTFChars(str, utf8);
}

unicode_string::~unicode_string()
{
if (mString) free(mString);
}

//@}

#define LOG_TAG "eSpeakService"
#define DEBUG true

@@ -314,6 +385,23 @@ JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeGetParameter(
return espeak_GetParameter((espeak_PARAMETER)parameter, (int)current);
}

JNIEXPORT jboolean
JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeSetPunctuationCharacters(
JNIEnv *env, jobject object, jstring characters) {
if (DEBUG) LOGV("%s)", __FUNCTION__);

unicode_string list(env, characters);
const espeak_ERROR result = espeak_SetPunctuationList(list.c_str());
switch (result) {
case EE_OK: return JNI_TRUE;
case EE_INTERNAL_ERROR: LOGE("espeak_SetPunctuationList: internal error."); break;
case EE_BUFFER_FULL: LOGE("espeak_SetPunctuationList: buffer full."); break;
case EE_NOT_FOUND: LOGE("espeak_SetPunctuationList: not found."); break;
}

return JNI_FALSE;
}

JNIEXPORT jboolean
JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeSynthesize(
JNIEnv *env, jobject object, jstring text, jboolean isSsml) {

+ 39
- 0
android/res/layout/speak_punctuation_preference.xml View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<RadioGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false">

<RadioButton
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="@string/punctuation_all"
android:id="@+id/all"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="true"
android:checked="false" />

<RadioButton
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="@string/punctuation_custom"
android:id="@+id/custom"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="true" />
</RadioGroup>

<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/punctuation_characters"
android:hint="@string/punctuation_characters"
android:layout_marginLeft="32dp" />

</LinearLayout>

+ 0
- 6
android/res/values/donottranslate.xml View File

@@ -34,10 +34,4 @@
<item>female-old</item>
</string-array>

<string-array name="punctuation_level_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>

</resources>

+ 6
- 10
android/res/values/strings.xml View File

@@ -111,14 +111,10 @@
<string name="resetToDefault">Set to default</string>
<string name="espeak_pitch_range">Pitch variation</string>
<string name="espeak_variant">Voice variant</string>
<string name="espeak_punctuation_level">Punctuation level</string>
<!--
Source: Punctuation level preference labels.
Description: Labels for possible punctuation level values.
-->
<string-array name="punctuation_level_entries">
<item>None</item>
<item>All</item>
<item>Some</item>
</string-array>
<string name="espeak_speak_punctuation">Speak punctuation</string>
<string name="punctuation_all">All</string>
<string name="punctuation_custom">Custom</string>
<string name="punctuation_custom_fmt">Custom: %s</string>
<string name="punctuation_none">None</string>
<string name="punctuation_characters">Punctuation characters</string>
</resources>

+ 0
- 8
android/res/xml/preferences.xml View File

@@ -9,12 +9,4 @@
android:summary="%s"
android:title="@string/espeak_variant" />

<ListPreference
android:defaultValue="0"
android:entries="@array/punctuation_level_entries"
android:entryValues="@array/punctuation_level_values"
android:key="espeak_punctuation_level"
android:summary="%s"
android:title="@string/espeak_punctuation_level" />

</PreferenceScreen>

+ 129
- 0
android/src/com/reecedunn/espeak/SpeakPunctuationPreference.java View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2013 Reece H. Dunn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.reecedunn.espeak;

import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.preference.DialogPreference;
import android.text.Editable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.SeekBar;
import android.widget.TextView;

public class SpeakPunctuationPreference extends DialogPreference {
private RadioButton mAll;
private RadioButton mCustom;
private EditText mPunctuationCharacters;

private VoiceSettings mSettings;

public SpeakPunctuationPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setDialogLayoutResource(R.layout.speak_punctuation_preference);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
}

public SpeakPunctuationPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public SpeakPunctuationPreference(Context context) {
this(context, null);
}

public void setVoiceSettings(VoiceSettings settings) {
mSettings = settings;
onDataChanged(mSettings.getPunctuationLevel(), mSettings.getPunctuationCharacters());
}

private void onDataChanged(int level, String characters) {
switch (level) {
case SpeechSynthesis.PUNCT_ALL:
callChangeListener(getContext().getText(R.string.punctuation_all));
break;
case SpeechSynthesis.PUNCT_SOME:
if (characters == null || characters.isEmpty()) {
callChangeListener(getContext().getText(R.string.punctuation_none));
} else {
callChangeListener(String.format(getContext().getText(R.string.punctuation_custom_fmt).toString(), characters));
}
break;
case SpeechSynthesis.PUNCT_NONE:
callChangeListener(getContext().getText(R.string.punctuation_none));
break;
}
}

@Override
protected View onCreateDialogView() {
View root = super.onCreateDialogView();
mAll = (RadioButton)root.findViewById(R.id.all);
mCustom = (RadioButton)root.findViewById(R.id.custom);
mPunctuationCharacters = (EditText)root.findViewById(R.id.punctuation_characters);
return root;
}

@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);

if (mSettings.getPunctuationLevel() == SpeechSynthesis.PUNCT_ALL) {
mAll.toggle();
} else {
mCustom.toggle();
}

mPunctuationCharacters.setText(mSettings.getPunctuationCharacters());
}

@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
Editable text = mPunctuationCharacters.getText();
String characters = null;
int level;
if (text != null) {
characters = text.toString();
}
if (characters == null || characters.isEmpty()) {
level = mAll.isChecked() ? SpeechSynthesis.PUNCT_ALL : SpeechSynthesis.PUNCT_NONE;
} else {
level = mAll.isChecked() ? SpeechSynthesis.PUNCT_ALL : SpeechSynthesis.PUNCT_SOME;
}

onDataChanged(level, characters);

if (shouldCommit()) {
SharedPreferences.Editor editor = getEditor();
if (editor != null) {
editor.putString(VoiceSettings.PREF_PUNCTUATION_CHARACTERS, characters);
editor.putString(VoiceSettings.PREF_PUNCTUATION_LEVEL, Integer.toString(level));
editor.commit();
}
}
break;
}
super.onClick(dialog, which);
}
}

+ 9
- 3
android/src/com/reecedunn/espeak/SpeechSynthesis.java View File

@@ -193,14 +193,18 @@ public class SpeechSynthesis {
}
}

public void setPunctuationCharacters(String characters) {
nativeSetPunctuationCharacters(characters);
}

/** Don't announce any punctuation characters. */
public static int PUNCT_NONE = 0;
public static final int PUNCT_NONE = 0;

/** Announce every punctuation character. */
public static int PUNCT_ALL = 1;
public static final int PUNCT_ALL = 1;

/** Announce some of the punctuation characters. */
public static int PUNCT_SOME = 2;
public static final int PUNCT_SOME = 2;

public enum UnitType {
Percentage,
@@ -345,6 +349,8 @@ public class SpeechSynthesis {

private native final int nativeGetParameter(int parameter, int current);

private native final boolean nativeSetPunctuationCharacters(String characters);

private native final boolean nativeSynthesize(String text, boolean isSsml);

private native final boolean nativeStop();

+ 1
- 0
android/src/com/reecedunn/espeak/TtsService.java View File

@@ -241,6 +241,7 @@ public class TtsService extends TextToSpeechService {
mEngine.PitchRange.setValue(settings.getPitchRange());
mEngine.Volume.setValue(settings.getVolume());
mEngine.Punctuation.setValue(settings.getPunctuationLevel());
mEngine.setPunctuationCharacters(settings.getPunctuationCharacters());
mEngine.synthesize(text, text.startsWith("<speak"));
}


+ 15
- 0
android/src/com/reecedunn/espeak/TtsSettingsActivity.java View File

@@ -97,6 +97,18 @@ public class TtsSettingsActivity extends PreferenceActivity {
}
}

private static Preference createSpeakPunctuationPreference(Context context, SpeechSynthesis engine, int titleRes) {
final String title = context.getString(titleRes);

final SpeakPunctuationPreference pref = new SpeakPunctuationPreference(context);
pref.setTitle(title);
pref.setDialogTitle(title);
pref.setOnPreferenceChangeListener(mOnPreferenceChanged);
pref.setPersistent(true);
pref.setVoiceSettings(new VoiceSettings(PreferenceManager.getDefaultSharedPreferences(context), engine));
return pref;
}

private static Preference createPreference(Context context, SpeechSynthesis.Parameter parameter, String key, int titleRes) {
final String title = context.getString(titleRes);
final int defaultValue = parameter.getDefaultValue();
@@ -165,6 +177,7 @@ public class TtsSettingsActivity extends PreferenceActivity {

SpeechSynthesis engine = new SpeechSynthesis(context, null);

group.addPreference(createSpeakPunctuationPreference(context, engine, R.string.espeak_speak_punctuation));
group.addPreference(createPreference(context, engine.Rate, "espeak_rate", R.string.setting_default_rate));
group.addPreference(createPreference(context, engine.Pitch, "espeak_pitch", R.string.setting_default_pitch));
group.addPreference(createPreference(context, engine.PitchRange, "espeak_pitch_range", R.string.espeak_pitch_range));
@@ -189,6 +202,8 @@ public class TtsSettingsActivity extends PreferenceActivity {
final SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
String formatter = seekBarPreference.getFormatter();
summary = String.format(formatter, (String)newValue);
} else {
summary = (String)newValue;
}
preference.setSummary(summary);
}

Loading…
Cancel
Save