1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package android.speech.tts;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.HashSet;
25import java.util.Locale;
26import java.util.Set;
27
28/**
29 * Characteristics and features of a Text-To-Speech Voice. Each TTS Engine can expose
30 * multiple voices for each locale, with different set of features.
31 */
32public class Voice implements Parcelable {
33    /** Very low, but still intelligible quality of speech synthesis */
34    public static final int QUALITY_VERY_LOW = 100;
35
36    /** Low, not human-like quality of speech synthesis */
37    public static final int QUALITY_LOW = 200;
38
39    /** Normal quality of speech synthesis */
40    public static final int QUALITY_NORMAL = 300;
41
42    /** High, human-like quality of speech synthesis */
43    public static final int QUALITY_HIGH = 400;
44
45    /** Very high, almost human-indistinguishable quality of speech synthesis */
46    public static final int QUALITY_VERY_HIGH = 500;
47
48    /** Very low expected synthesizer latency (< 20ms) */
49    public static final int LATENCY_VERY_LOW = 100;
50
51    /** Low expected synthesizer latency (~20ms) */
52    public static final int LATENCY_LOW = 200;
53
54    /** Normal expected synthesizer latency (~50ms) */
55    public static final int LATENCY_NORMAL = 300;
56
57    /** Network based expected synthesizer latency (~200ms) */
58    public static final int LATENCY_HIGH = 400;
59
60    /** Very slow network based expected synthesizer latency (> 200ms) */
61    public static final int LATENCY_VERY_HIGH = 500;
62
63    private final String mName;
64    private final Locale mLocale;
65    private final int mQuality;
66    private final int mLatency;
67    private final boolean mRequiresNetworkConnection;
68    private final Set<String> mFeatures;
69
70    public Voice(String name,
71            Locale locale,
72            int quality,
73            int latency,
74            boolean requiresNetworkConnection,
75            Set<String> features) {
76        this.mName = name;
77        this.mLocale = locale;
78        this.mQuality = quality;
79        this.mLatency = latency;
80        this.mRequiresNetworkConnection = requiresNetworkConnection;
81        this.mFeatures = features;
82    }
83
84    private Voice(Parcel in) {
85        this.mName = in.readString();
86        this.mLocale = (Locale)in.readSerializable();
87        this.mQuality = in.readInt();
88        this.mLatency = in.readInt();
89        this.mRequiresNetworkConnection = (in.readByte() == 1);
90        this.mFeatures = new HashSet<String>();
91        Collections.addAll(this.mFeatures, in.readStringArray());
92    }
93
94    @Override
95    public void writeToParcel(Parcel dest, int flags) {
96        dest.writeString(mName);
97        dest.writeSerializable(mLocale);
98        dest.writeInt(mQuality);
99        dest.writeInt(mLatency);
100        dest.writeByte((byte) (mRequiresNetworkConnection ? 1 : 0));
101        dest.writeStringList(new ArrayList<String>(mFeatures));
102    }
103
104    @Override
105    public int describeContents() {
106        return 0;
107    }
108
109    public static final Parcelable.Creator<Voice> CREATOR = new Parcelable.Creator<Voice>() {
110        @Override
111        public Voice createFromParcel(Parcel in) {
112            return new Voice(in);
113        }
114
115        @Override
116        public Voice[] newArray(int size) {
117            return new Voice[size];
118        }
119    };
120
121
122    /**
123     * @return The voice's locale
124     */
125    public Locale getLocale() {
126        return mLocale;
127    }
128
129    /**
130     * @return The voice's quality (higher is better)
131     * @see #QUALITY_VERY_HIGH
132     * @see #QUALITY_HIGH
133     * @see #QUALITY_NORMAL
134     * @see #QUALITY_LOW
135     * @see #QUALITY_VERY_LOW
136     */
137    public int getQuality() {
138        return mQuality;
139    }
140
141    /**
142     * @return The voice's latency (lower is better)
143     * @see #LATENCY_VERY_LOW
144     * @see #LATENCY_LOW
145     * @see #LATENCY_NORMAL
146     * @see #LATENCY_HIGH
147     * @see #LATENCY_VERY_HIGH
148     */
149    public int getLatency() {
150        return mLatency;
151    }
152
153    /**
154     * @return Does the Voice require a network connection to work.
155     */
156    public boolean isNetworkConnectionRequired() {
157        return mRequiresNetworkConnection;
158    }
159
160    /**
161     * @return Unique voice name.
162     */
163    public String getName() {
164        return mName;
165    }
166
167    /**
168     * Returns the set of features it supports for a given voice.
169     * Features can either be framework defined, e.g.
170     * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_TIMEOUT_MS} or engine specific.
171     * Engine specific keys must be prefixed by the name of the engine they
172     * are intended for. These keys can be used as parameters to
173     * {@link TextToSpeech#speak(String, int, java.util.HashMap)} and
174     * {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)}.
175     *
176     * Features values are strings and their values must met restrictions described in their
177     * documentation.
178     *
179     * @return Set instance. May return {@code null} on error.
180     */
181    public Set<String> getFeatures() {
182        return mFeatures;
183    }
184
185    @Override
186    public String toString() {
187        StringBuilder builder = new StringBuilder(64);
188        return builder.append("Voice[Name: ").append(mName)
189                .append(", locale: ").append(mLocale)
190                .append(", quality: ").append(mQuality)
191                .append(", latency: ").append(mLatency)
192                .append(", requiresNetwork: ").append(mRequiresNetworkConnection)
193                .append(", features: ").append(mFeatures.toString())
194                .append("]").toString();
195    }
196
197    @Override
198    public int hashCode() {
199        final int prime = 31;
200        int result = 1;
201        result = prime * result + ((mFeatures == null) ? 0 : mFeatures.hashCode());
202        result = prime * result + mLatency;
203        result = prime * result + ((mLocale == null) ? 0 : mLocale.hashCode());
204        result = prime * result + ((mName == null) ? 0 : mName.hashCode());
205        result = prime * result + mQuality;
206        result = prime * result + (mRequiresNetworkConnection ? 1231 : 1237);
207        return result;
208    }
209
210    @Override
211    public boolean equals(Object obj) {
212        if (this == obj) {
213            return true;
214        }
215        if (obj == null) {
216            return false;
217        }
218        if (getClass() != obj.getClass()) {
219            return false;
220        }
221        Voice other = (Voice) obj;
222        if (mFeatures == null) {
223            if (other.mFeatures != null) {
224                return false;
225            }
226        } else if (!mFeatures.equals(other.mFeatures)) {
227            return false;
228        }
229        if (mLatency != other.mLatency) {
230            return false;
231        }
232        if (mLocale == null) {
233            if (other.mLocale != null) {
234                return false;
235            }
236        } else if (!mLocale.equals(other.mLocale)) {
237            return false;
238        }
239        if (mName == null) {
240            if (other.mName != null) {
241                return false;
242            }
243        } else if (!mName.equals(other.mName)) {
244            return false;
245        }
246        if (mQuality != other.mQuality) {
247            return false;
248        }
249        if (mRequiresNetworkConnection != other.mRequiresNetworkConnection) {
250            return false;
251        }
252        return true;
253    }
254}
255