1 package org.daisy.pipeline.tts;
2
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Locale;
9 import java.util.Map;
10 import java.util.Optional;
11
12 import com.google.common.collect.Collections2;
13
14 import org.daisy.pipeline.tts.VoiceInfo.Gender;
15 import org.daisy.pipeline.tts.VoiceInfo.LanguageRange;
16
17
18
19
20 public class Voice {
21
22
23
24 private final String engine;
25 private final String name;
26 private final MarkSupport markSupport;
27 private final Collection<LanguageRange> locale;
28 private final Optional<Gender> gender;
29 private final String id;
30
31 public enum MarkSupport {
32 DEFAULT,
33 MARK_SUPPORTED,
34 MARK_NOT_SUPPORTED
35 }
36
37 public Voice(String engine, String name) {
38 this(engine, name, Collections.emptyList(), null, MarkSupport.DEFAULT);
39 }
40
41
42
43
44 public Voice(String engine, String name, Locale locale, Gender gender) throws IllegalArgumentException {
45 this(engine,
46 name,
47 locale != null ? new LanguageRange(locale) : null,
48 gender);
49 }
50
51 public Voice(String engine, String name, LanguageRange locale, Gender gender) throws IllegalArgumentException {
52 this(engine,
53 name,
54 Optional.ofNullable(locale).map(x -> listOf(x)).orElseGet(Collections::emptyList),
55 gender,
56 MarkSupport.DEFAULT);
57 }
58
59 public Voice(String engine, String name, Collection<Locale> locale, Gender gender) throws IllegalArgumentException {
60 this(engine, name, Collections2.transform(locale, LanguageRange::new), gender, MarkSupport.DEFAULT);
61 }
62
63 public Voice(String engine, String name, Collection<LanguageRange> locale, Gender gender, MarkSupport markSupport)
64 throws IllegalArgumentException {
65 if (engine == null || engine.isEmpty() || name == null || name.isEmpty())
66 throw new IllegalArgumentException();
67
68 this.engine = engine;
69 this.name = name;
70 this.locale = Collections.unmodifiableCollection(locale);
71 this.gender = Optional.ofNullable(gender);
72 this.markSupport = markSupport;
73 synchronized (ids) {
74 if (ids.containsKey(this))
75 id = ids.get(this);
76 else {
77 Integer i = voiceCounter.get(engine.toLowerCase());
78 if (i == null)
79 i = 1;
80 else
81 i++;
82 id = engine.toLowerCase() + "-" + i;
83 ids.put(this, id);
84 voiceCounter.put(engine.toLowerCase(), i);
85 }
86 }
87 }
88
89 private final static Map<Voice,String> ids = new HashMap<>();
90 private final static Map<String,Integer> voiceCounter = new HashMap<>();
91
92
93 private static <E> List<E> listOf(E element) {
94 if (element == null)
95 throw new NullPointerException();
96 return Collections.unmodifiableList(Arrays.asList(element));
97 }
98
99 public String getName() {
100 return name;
101 }
102
103 public String getEngine() {
104 return engine;
105 }
106
107 public MarkSupport getMarkSupport() {
108 return markSupport;
109 }
110
111
112
113
114 public Collection<LanguageRange> getLocale() {
115 return locale;
116 }
117
118
119
120
121 public Optional<Gender> getGender() {
122 return gender;
123 }
124
125
126
127
128
129
130
131 public String getID() {
132 return id;
133 }
134
135 public int hashCode() {
136 return engine.toLowerCase().hashCode() ^ name.toLowerCase().hashCode();
137 }
138
139 public boolean equals(Object other) {
140 if (other == null)
141 return false;
142 Voice v2 = (Voice)other;
143 return engine.equalsIgnoreCase(v2.engine) && name.equalsIgnoreCase(v2.name);
144 }
145
146 public String toString() {
147 StringBuilder s = new StringBuilder();
148 s.append("{");
149 s.append("engine: ").append(engine);
150 s.append(", ").append("name: ").append(name);
151 if (!locale.isEmpty()) {
152 s.append(", ").append("locale: ");
153 if (locale.size() > 1) s.append("\"");
154 s.append(LanguageRange.toString(locale));
155 if (locale.size() > 1) s.append("\"");
156 }
157 if (gender.isPresent()) {
158 s.append(", ").append("gender:").append(gender.get());
159 }
160 s.append("}");
161 return s.toString();
162 }
163 }