main
dart 253 lines 4.95 KB
Raw
1 import '../constants/app_constants.dart';
2
3 enum EngineId { system, kitten, piper }
4
5 class EngineMeta {
6 final String name;
7 final String tagline;
8 final int sizeMb;
9 final int ramMb;
10 final int voiceCount;
11 final int accentColor;
12
13 const EngineMeta({
14 required this.name,
15 required this.tagline,
16 required this.sizeMb,
17 required this.ramMb,
18 required this.voiceCount,
19 required this.accentColor,
20 });
21
22 static const system = EngineMeta(
23 name: 'System TTS',
24 tagline: 'Built-in device engine',
25 sizeMb: 0,
26 ramMb: 0,
27 voiceCount: 0,
28 accentColor: 0xFF9E9E9E,
29 );
30
31 static const kitten = EngineMeta(
32 name: 'Kitten TTS',
33 tagline: 'High-speed neural TTS',
34 sizeMb: 57,
35 ramMb: 120,
36 voiceCount: 8,
37 accentColor: 0xFFFF9800,
38 );
39
40 static const piper = EngineMeta(
41 name: 'Sherpa ONNX VITS',
42 tagline: 'Offline Piper voices',
43 sizeMb: 50,
44 ramMb: 100,
45 voiceCount: 2,
46 accentColor: 0xFF4CAF50,
47 );
48
49 static EngineMeta forEngine(EngineId engine) {
50 switch (engine) {
51 case EngineId.kitten:
52 return kitten;
53 case EngineId.piper:
54 return piper;
55 default:
56 return system;
57 }
58 }
59 }
60
61 class Voice {
62 final String id;
63 final String name;
64 final EngineId engine;
65 final String family;
66 final String? language;
67 final String gender;
68
69 const Voice({
70 required this.id,
71 required this.name,
72 required this.engine,
73 required this.family,
74 this.language,
75 required this.gender,
76 });
77 }
78
79 List<Voice> voicesForEngine(EngineId engine) {
80 switch (engine) {
81 case EngineId.kitten:
82 return kittenVoices;
83 case EngineId.piper:
84 return piperVoices;
85 default:
86 return [];
87 }
88 }
89
90 Map<String, List<Voice>> voicesGroupedByFamily(EngineId engine) {
91 final result = <String, List<Voice>>{};
92 for (final voice in voicesForEngine(engine)) {
93 result.putIfAbsent(voice.family, () => <Voice>[]).add(voice);
94 }
95 return result;
96 }
97
98 const List<Voice> kittenVoices = [
99 Voice(
100 id: 'Jasper',
101 name: 'Jasper',
102 engine: EngineId.kitten,
103 family: 'Kitten',
104 language: 'en',
105 gender: 'm',
106 ),
107 Voice(
108 id: 'Bella',
109 name: 'Bella',
110 engine: EngineId.kitten,
111 family: 'Kitten',
112 language: 'en',
113 gender: 'f',
114 ),
115 Voice(
116 id: 'Bruno',
117 name: 'Bruno',
118 engine: EngineId.kitten,
119 family: 'Kitten',
120 language: 'en',
121 gender: 'm',
122 ),
123 Voice(
124 id: 'Luna',
125 name: 'Luna',
126 engine: EngineId.kitten,
127 family: 'Kitten',
128 language: 'en',
129 gender: 'f',
130 ),
131 Voice(
132 id: 'Hugo',
133 name: 'Hugo',
134 engine: EngineId.kitten,
135 family: 'Kitten',
136 language: 'en',
137 gender: 'm',
138 ),
139 Voice(
140 id: 'Rosie',
141 name: 'Rosie',
142 engine: EngineId.kitten,
143 family: 'Kitten',
144 language: 'en',
145 gender: 'f',
146 ),
147 Voice(
148 id: 'Leo',
149 name: 'Leo',
150 engine: EngineId.kitten,
151 family: 'Kitten',
152 language: 'en',
153 gender: 'm',
154 ),
155 Voice(
156 id: 'Kiki',
157 name: 'Kiki',
158 engine: EngineId.kitten,
159 family: 'Kitten',
160 language: 'en',
161 gender: 'f',
162 ),
163 ];
164
165 const List<Voice> piperVoices = [
166 Voice(
167 id: 'en_US-lessac-medium',
168 name: 'Lessac (US)',
169 engine: EngineId.piper,
170 family: 'Piper',
171 language: 'en',
172 gender: 'f',
173 ),
174 Voice(
175 id: 'en_US-ryan-medium',
176 name: 'Ryan (US)',
177 engine: EngineId.piper,
178 family: 'Piper',
179 language: 'en',
180 gender: 'm',
181 ),
182 ];
183
184 EngineId engineIdFromString(String value) {
185 switch (value) {
186 case 'kitten':
187 return EngineId.kitten;
188 case 'piper':
189 return EngineId.piper;
190 default:
191 return EngineId.system;
192 }
193 }
194
195 Voice? voiceFromSettings(String? voiceId, EngineId engine) {
196 if (voiceId == null || voiceId.isEmpty) return null;
197 final voices = voicesForEngine(engine);
198 try {
199 return voices.firstWhere((v) => v.id == voiceId);
200 } catch (_) {
201 return null;
202 }
203 }
204
205 enum ServerType {
206 lmStudio,
207 openAICompatible,
208 ollama,
209 ollamaCloud,
210 openRouter,
211 onDevice,
212 requesty,
213 }
214
215 extension ServerTypeLogoExtension on ServerType {
216 String? get logoAsset {
217 switch (this) {
218 case ServerType.lmStudio:
219 return AppConstants.lmStudioLogoAsset;
220 case ServerType.ollama:
221 case ServerType.ollamaCloud:
222 return AppConstants.ollamaLogoAsset;
223 case ServerType.openRouter:
224 return AppConstants.openRouterLogoAsset;
225 case ServerType.requesty:
226 return AppConstants.requestyLogoAsset;
227 case ServerType.openAICompatible:
228 case ServerType.onDevice:
229 return null;
230 }
231 }
232
233 bool get hasLogo => logoAsset != null;
234 }
235
236 enum ConnectionStatus { connected, disconnected, checking, error }
237
238 enum MessageRole { user, assistant, system, tool }
239
240 enum MessageStatus { sending, streaming, complete, error, cancelled }
241
242 enum ModelStatus { unloaded, loading, loaded, preloaded, thinking }
243
244 enum OnDeviceEngineStatus { notLoaded, loading, loaded, error }
245
246 enum OnDeviceModelState {
247 notDownloaded,
248 downloading,
249 downloaded,
250 loading,
251 loaded,
252 error,
253 }