@iternio/react-native-tts 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +268 -0
  2. package/TextToSpeech.podspec +21 -0
  3. package/android/build.gradle +41 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/java/net/no_mad/tts/TextToSpeechModule.java +538 -0
  6. package/android/src/main/java/net/no_mad/tts/TextToSpeechPackage.java +31 -0
  7. package/index.d.ts +118 -0
  8. package/index.js +127 -0
  9. package/ios/TextToSpeech/TextToSpeech.h +20 -0
  10. package/ios/TextToSpeech/TextToSpeech.m +285 -0
  11. package/ios/TextToSpeech.xcodeproj/project.pbxproj +276 -0
  12. package/ios/TextToSpeech.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  13. package/ios/TextToSpeech.xcodeproj/project.xcworkspace/xcuserdata/anton.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  14. package/ios/TextToSpeech.xcodeproj/xcuserdata/anton.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +5 -0
  15. package/ios/TextToSpeech.xcodeproj/xcuserdata/anton.xcuserdatad/xcschemes/TextToSpeech.xcscheme +80 -0
  16. package/ios/TextToSpeech.xcodeproj/xcuserdata/anton.xcuserdatad/xcschemes/xcschememanagement.plist +22 -0
  17. package/package.json +27 -0
  18. package/windows/README.md +25 -0
  19. package/windows/RNTTS/PropertySheet.props +16 -0
  20. package/windows/RNTTS/RNTTS.cpp +224 -0
  21. package/windows/RNTTS/RNTTS.def +3 -0
  22. package/windows/RNTTS/RNTTS.h +75 -0
  23. package/windows/RNTTS/RNTTS.vcxproj +163 -0
  24. package/windows/RNTTS/RNTTS.vcxproj.filters +33 -0
  25. package/windows/RNTTS/ReactPackageProvider.cpp +15 -0
  26. package/windows/RNTTS/ReactPackageProvider.h +16 -0
  27. package/windows/RNTTS/ReactPackageProvider.idl +9 -0
  28. package/windows/RNTTS/packages.config +4 -0
  29. package/windows/RNTTS/pch.cpp +1 -0
  30. package/windows/RNTTS/pch.h +11 -0
  31. package/windows/RNTTS62.sln +254 -0
  32. package/windows/RNTTS63.sln +226 -0
@@ -0,0 +1,538 @@
1
+ package net.no_mad.tts;
2
+
3
+ import android.media.AudioManager;
4
+ import android.os.Build;
5
+ import android.os.Bundle;
6
+ import android.content.Intent;
7
+ import android.content.ActivityNotFoundException;
8
+ import android.app.Activity;
9
+ import android.net.Uri;
10
+ import android.speech.tts.TextToSpeech;
11
+ import android.speech.tts.UtteranceProgressListener;
12
+ import android.speech.tts.Voice;
13
+ import android.content.pm.PackageInfo;
14
+ import android.content.pm.PackageManager;
15
+ import android.content.pm.PackageManager.NameNotFoundException;
16
+ import com.facebook.react.bridge.*;
17
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
18
+
19
+ import java.util.ArrayList;
20
+ import java.util.HashMap;
21
+ import java.util.Locale;
22
+ import java.util.Map;
23
+
24
+ public class TextToSpeechModule extends ReactContextBaseJavaModule {
25
+
26
+ private TextToSpeech tts;
27
+ private Boolean ready = null;
28
+ private ArrayList<Promise> initStatusPromises;
29
+
30
+ private boolean ducking = false;
31
+ private AudioManager audioManager;
32
+ private AudioManager.OnAudioFocusChangeListener afChangeListener;
33
+
34
+ private Map<String, Locale> localeCountryMap;
35
+ private Map<String, Locale> localeLanguageMap;
36
+
37
+ public TextToSpeechModule(ReactApplicationContext reactContext) {
38
+ super(reactContext);
39
+ audioManager = (AudioManager) reactContext.getApplicationContext().getSystemService(reactContext.AUDIO_SERVICE);
40
+ initStatusPromises = new ArrayList<Promise>();
41
+ //initialize ISO3, ISO2 languague country code mapping.
42
+ initCountryLanguageCodeMapping();
43
+
44
+ tts = new TextToSpeech(getReactApplicationContext(), new TextToSpeech.OnInitListener() {
45
+ @Override
46
+ public void onInit(int status) {
47
+ synchronized(initStatusPromises) {
48
+ ready = (status == TextToSpeech.SUCCESS) ? Boolean.TRUE : Boolean.FALSE;
49
+ for(Promise p: initStatusPromises) {
50
+ resolveReadyPromise(p);
51
+ }
52
+ initStatusPromises.clear();
53
+ }
54
+ }
55
+ });
56
+
57
+ setUtteranceProgress();
58
+ }
59
+
60
+ private void setUtteranceProgress() {
61
+ if(tts != null)
62
+ {
63
+ tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
64
+ @Override
65
+ public void onStart(String utteranceId) {
66
+ sendEvent("tts-start", utteranceId);
67
+ }
68
+
69
+ @Override
70
+ public void onDone(String utteranceId) {
71
+ if(ducking) {
72
+ audioManager.abandonAudioFocus(afChangeListener);
73
+ }
74
+ sendEvent("tts-finish", utteranceId);
75
+ }
76
+
77
+ @Override
78
+ public void onError(String utteranceId) {
79
+ if(ducking) {
80
+ audioManager.abandonAudioFocus(afChangeListener);
81
+ }
82
+ sendEvent("tts-error", utteranceId);
83
+ }
84
+
85
+ @Override
86
+ public void onStop(String utteranceId, boolean interrupted) {
87
+ if(ducking) {
88
+ audioManager.abandonAudioFocus(afChangeListener);
89
+ }
90
+ sendEvent("tts-cancel", utteranceId);
91
+ }
92
+
93
+ @Override
94
+ public void onRangeStart (String utteranceId, int start, int end, int frame) {
95
+ WritableMap params = Arguments.createMap();
96
+ params.putString("utteranceId", utteranceId);
97
+ params.putInt("start", start);
98
+ params.putInt("end", end);
99
+ params.putInt("frame", frame);
100
+ sendEvent("tts-progress", params);
101
+ }
102
+ });
103
+ }
104
+ }
105
+
106
+ private void initCountryLanguageCodeMapping() {
107
+ String[] countries = Locale.getISOCountries();
108
+ localeCountryMap = new HashMap<String, Locale>(countries.length);
109
+ for (String country: countries) {
110
+ Locale locale = new Locale("", country);
111
+ localeCountryMap.put(locale.getISO3Country().toUpperCase(), locale);
112
+ }
113
+ String[] languages = Locale.getISOLanguages();
114
+ localeLanguageMap = new HashMap<String, Locale>(languages.length);
115
+ for (String language: languages) {
116
+ Locale locale = new Locale(language);
117
+ localeLanguageMap.put(locale.getISO3Language(), locale);
118
+ }
119
+ }
120
+
121
+ private String iso3CountryCodeToIso2CountryCode(String iso3CountryCode) {
122
+ return localeCountryMap.get(iso3CountryCode).getCountry();
123
+ }
124
+
125
+ private String iso3LanguageCodeToIso2LanguageCode(String iso3LanguageCode) {
126
+ return localeLanguageMap.get(iso3LanguageCode).getLanguage();
127
+ }
128
+
129
+ private void resolveReadyPromise(Promise promise) {
130
+ if (ready == Boolean.TRUE) {
131
+ promise.resolve("success");
132
+ }
133
+ else {
134
+ promise.reject("no_engine", "No TTS engine installed");
135
+ }
136
+ }
137
+
138
+ private static void resolvePromiseWithStatusCode(int statusCode, Promise promise) {
139
+ switch (statusCode) {
140
+ case TextToSpeech.SUCCESS:
141
+ promise.resolve("success");
142
+ break;
143
+ case TextToSpeech.LANG_COUNTRY_AVAILABLE:
144
+ promise.resolve("lang_country_available");
145
+ break;
146
+ case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE:
147
+ promise.resolve("lang_country_var_available");
148
+ break;
149
+ case TextToSpeech.ERROR_INVALID_REQUEST:
150
+ promise.reject("invalid_request", "Failure caused by an invalid request");
151
+ break;
152
+ case TextToSpeech.ERROR_NETWORK:
153
+ promise.reject("network_error", "Failure caused by a network connectivity problems");
154
+ break;
155
+ case TextToSpeech.ERROR_NETWORK_TIMEOUT:
156
+ promise.reject("network_timeout", "Failure caused by network timeout.");
157
+ break;
158
+ case TextToSpeech.ERROR_NOT_INSTALLED_YET:
159
+ promise.reject("not_installed_yet", "Unfinished download of voice data");
160
+ break;
161
+ case TextToSpeech.ERROR_OUTPUT:
162
+ promise.reject("output_error", "Failure related to the output (audio device or a file)");
163
+ break;
164
+ case TextToSpeech.ERROR_SERVICE:
165
+ promise.reject("service_error", "Failure of a TTS service");
166
+ break;
167
+ case TextToSpeech.ERROR_SYNTHESIS:
168
+ promise.reject("synthesis_error", "Failure of a TTS engine to synthesize the given input");
169
+ break;
170
+ case TextToSpeech.LANG_MISSING_DATA:
171
+ promise.reject("lang_missing_data", "Language data is missing");
172
+ break;
173
+ case TextToSpeech.LANG_NOT_SUPPORTED:
174
+ promise.reject("lang_not_supported", "Language is not supported");
175
+ break;
176
+ default:
177
+ promise.reject("error", "Unknown error code: " + statusCode);
178
+ break;
179
+ }
180
+ }
181
+
182
+ private boolean isPackageInstalled(String packageName) {
183
+ PackageManager pm = getReactApplicationContext().getPackageManager();
184
+ try {
185
+ PackageInfo pi = pm.getPackageInfo(packageName, 0);
186
+ return true;
187
+ } catch (NameNotFoundException e) {
188
+ return false;
189
+ }
190
+ }
191
+
192
+ @Override
193
+ public String getName() {
194
+ return "TextToSpeech";
195
+ }
196
+
197
+ @ReactMethod
198
+ public void getInitStatus(Promise promise) {
199
+ synchronized(initStatusPromises) {
200
+ if(ready == null) {
201
+ initStatusPromises.add(promise);
202
+ } else {
203
+ resolveReadyPromise(promise);
204
+ }
205
+ }
206
+ }
207
+
208
+ @ReactMethod
209
+ public void speak(String utterance, ReadableMap params, Promise promise) {
210
+ if(notReady(promise)) return;
211
+
212
+ if(ducking) {
213
+ // Request audio focus for playback
214
+ int amResult = audioManager.requestAudioFocus(afChangeListener,
215
+ // Use the music stream.
216
+ AudioManager.STREAM_MUSIC,
217
+ // Request permanent focus.
218
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
219
+
220
+ if(amResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
221
+ promise.reject("Android AudioManager error, failed to request audio focus");
222
+ return;
223
+ }
224
+ }
225
+
226
+ String utteranceId = Integer.toString(utterance.hashCode());
227
+
228
+ int speakResult = speak(utterance, utteranceId, params);
229
+ if(speakResult == TextToSpeech.SUCCESS) {
230
+ promise.resolve(utteranceId);
231
+ } else {
232
+ resolvePromiseWithStatusCode(speakResult, promise);
233
+ }
234
+ }
235
+
236
+ @ReactMethod
237
+ public void setDefaultLanguage(String language, Promise promise) {
238
+ if(notReady(promise)) return;
239
+
240
+ Locale locale = null;
241
+
242
+ if(language.indexOf("-") != -1) {
243
+ String[] parts = language.split("-");
244
+ locale = new Locale(parts[0], parts[1]);
245
+ } else {
246
+ locale = new Locale(language);
247
+ }
248
+
249
+ try {
250
+ int result = tts.setLanguage(locale);
251
+ resolvePromiseWithStatusCode(result, promise);
252
+ } catch (Exception e) {
253
+ promise.reject("error", "Unknown error code");
254
+ }
255
+ }
256
+
257
+ @ReactMethod
258
+ public void setDucking(Boolean ducking, Promise promise) {
259
+ if(notReady(promise)) return;
260
+ this.ducking = ducking;
261
+ promise.resolve("success");
262
+ }
263
+
264
+ @ReactMethod
265
+ public void setDefaultRate(Float rate, Boolean skipTransform, Promise promise) {
266
+ if(notReady(promise)) return;
267
+
268
+ if(skipTransform) {
269
+ int result = tts.setSpeechRate(rate);
270
+ resolvePromiseWithStatusCode(result, promise);
271
+ } else {
272
+ // normalize android rate
273
+ // rate value will be in the range 0.0 to 1.0
274
+ // let's convert it to the range of values Android platform expects,
275
+ // where 1.0 is no change of rate and 2.0 is the twice faster rate
276
+ float androidRate = rate.floatValue() < 0.5f ?
277
+ rate.floatValue() * 2 : // linear fit {0, 0}, {0.25, 0.5}, {0.5, 1}
278
+ rate.floatValue() * 4 - 1; // linear fit {{0.5, 1}, {0.75, 2}, {1, 3}}
279
+ int result = tts.setSpeechRate(androidRate);
280
+ resolvePromiseWithStatusCode(result, promise);
281
+ }
282
+ }
283
+
284
+ @ReactMethod
285
+ public void setDefaultPitch(Float pitch, Promise promise) {
286
+ if(notReady(promise)) return;
287
+ int result = tts.setPitch(pitch);
288
+ resolvePromiseWithStatusCode(result, promise);
289
+ }
290
+
291
+ @ReactMethod
292
+ public void setDefaultVoice(String voiceId, Promise promise) {
293
+ if(notReady(promise)) return;
294
+
295
+ if (Build.VERSION.SDK_INT >= 21) {
296
+ try {
297
+ for(Voice voice: tts.getVoices()) {
298
+ if(voice.getName().equals(voiceId)) {
299
+ int result = tts.setVoice(voice);
300
+ resolvePromiseWithStatusCode(result, promise);
301
+ return;
302
+ }
303
+ }
304
+ } catch (Exception e) {
305
+ // Purposefully ignore exceptions here due to some buggy TTS engines.
306
+ // See http://stackoverflow.com/questions/26730082/illegalargumentexception-invalid-int-os-with-samsung-tts
307
+ }
308
+ promise.reject("not_found", "The selected voice was not found");
309
+ } else {
310
+ promise.reject("not_available", "Android API 21 level or higher is required");
311
+ }
312
+ }
313
+
314
+ @ReactMethod
315
+ public void voices(Promise promise) {
316
+ if(notReady(promise)) return;
317
+
318
+ WritableArray voiceArray = Arguments.createArray();
319
+
320
+ if (Build.VERSION.SDK_INT >= 21) {
321
+ try {
322
+ for(Voice voice: tts.getVoices()) {
323
+ WritableMap voiceMap = Arguments.createMap();
324
+ voiceMap.putString("id", voice.getName());
325
+ voiceMap.putString("name", voice.getName());
326
+
327
+ String language = iso3LanguageCodeToIso2LanguageCode(voice.getLocale().getISO3Language());
328
+ String country = voice.getLocale().getISO3Country();
329
+ if(country != "") {
330
+ language += "-" + iso3CountryCodeToIso2CountryCode(country);
331
+ }
332
+
333
+ voiceMap.putString("language", language);
334
+ voiceMap.putInt("quality", voice.getQuality());
335
+ voiceMap.putInt("latency", voice.getLatency());
336
+ voiceMap.putBoolean("networkConnectionRequired", voice.isNetworkConnectionRequired());
337
+ voiceMap.putBoolean("notInstalled", voice.getFeatures().contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED));
338
+ voiceArray.pushMap(voiceMap);
339
+ }
340
+ } catch (Exception e) {
341
+ // Purposefully ignore exceptions here due to some buggy TTS engines.
342
+ // See http://stackoverflow.com/questions/26730082/illegalargumentexception-invalid-int-os-with-samsung-tts
343
+ }
344
+ }
345
+
346
+ promise.resolve(voiceArray);
347
+ }
348
+
349
+ @ReactMethod
350
+ public void setDefaultEngine(String engineName, final Promise promise) {
351
+ if(notReady(promise)) return;
352
+
353
+ if(isPackageInstalled(engineName)) {
354
+ ready = null;
355
+ onCatalystInstanceDestroy();
356
+ tts = new TextToSpeech(getReactApplicationContext(), new TextToSpeech.OnInitListener() {
357
+ @Override
358
+ public void onInit(int status) {
359
+ synchronized(initStatusPromises) {
360
+ ready = (status == TextToSpeech.SUCCESS) ? Boolean.TRUE : Boolean.FALSE;
361
+ for(Promise p: initStatusPromises) {
362
+ resolveReadyPromise(p);
363
+ }
364
+ initStatusPromises.clear();
365
+ promise.resolve(ready);
366
+ }
367
+ }
368
+ }, engineName);
369
+
370
+ setUtteranceProgress();
371
+ } else {
372
+ promise.reject("not_found", "The selected engine was not found");
373
+ }
374
+ }
375
+
376
+ @ReactMethod
377
+ public void engines(Promise promise) {
378
+ if(notReady(promise)) return;
379
+
380
+ WritableArray engineArray = Arguments.createArray();
381
+
382
+ if (Build.VERSION.SDK_INT >= 14) {
383
+ try {
384
+ String defaultEngineName = tts.getDefaultEngine();
385
+ for(TextToSpeech.EngineInfo engine: tts.getEngines()) {
386
+ WritableMap engineMap = Arguments.createMap();
387
+
388
+ engineMap.putString("name", engine.name);
389
+ engineMap.putString("label", engine.label);
390
+ engineMap.putBoolean("default", engine.name.equals(defaultEngineName));
391
+ engineMap.putInt("icon", engine.icon);
392
+
393
+ engineArray.pushMap(engineMap);
394
+ }
395
+ } catch (Exception e) {
396
+ promise.reject("error", "Unknown error code");
397
+ }
398
+ }
399
+
400
+ promise.resolve(engineArray);
401
+ }
402
+
403
+ @ReactMethod
404
+ public void stop(Promise promise) {
405
+ if(notReady(promise)) return;
406
+
407
+ int result = tts.stop();
408
+ boolean resultValue = (result == TextToSpeech.SUCCESS) ? Boolean.TRUE : Boolean.FALSE;
409
+ promise.resolve(resultValue);
410
+ }
411
+
412
+ @ReactMethod
413
+ private void requestInstallEngine(Promise promise) {
414
+ Intent intent = new Intent(Intent.ACTION_VIEW);
415
+ intent.setData(Uri.parse("market://details?id=com.google.android.tts"));
416
+ try {
417
+ getCurrentActivity().startActivity(intent);
418
+ promise.resolve("success");
419
+ } catch (Exception e) {
420
+ promise.reject("error", "Could not open Google Text to Speech App in the Play Store");
421
+ }
422
+ }
423
+
424
+ @ReactMethod
425
+ private void requestInstallData(Promise promise) {
426
+ Intent intent = new Intent();
427
+ intent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
428
+ try {
429
+ getCurrentActivity().startActivity(intent);
430
+ promise.resolve("success");
431
+ } catch (ActivityNotFoundException e) {
432
+ promise.reject("no_engine", "No TTS engine installed");
433
+ }
434
+ }
435
+
436
+ /**
437
+ * called on React Native Reloading JavaScript
438
+ * https://stackoverflow.com/questions/15563361/tts-leaked-serviceconnection
439
+ */
440
+ @Override
441
+ public void onCatalystInstanceDestroy() {
442
+ super.onCatalystInstanceDestroy();
443
+ if(tts != null) {
444
+ tts.stop();
445
+ tts.shutdown();
446
+ }
447
+ }
448
+
449
+ private boolean notReady(Promise promise) {
450
+ if(ready == null) {
451
+ promise.reject("not_ready", "TTS is not ready");
452
+ return true;
453
+ }
454
+ else if(ready != Boolean.TRUE) {
455
+ resolveReadyPromise(promise);
456
+ return true;
457
+ }
458
+ return false;
459
+ }
460
+
461
+ @SuppressWarnings("deprecation")
462
+ private int speak(String utterance, String utteranceId, ReadableMap inputParams) {
463
+ String audioStreamTypeString = inputParams.hasKey("KEY_PARAM_STREAM") ? inputParams.getString("KEY_PARAM_STREAM") : "";
464
+ float volume = inputParams.hasKey("KEY_PARAM_VOLUME") ? (float) inputParams.getDouble("KEY_PARAM_VOLUME") : 1.0f;
465
+ float pan = inputParams.hasKey("KEY_PARAM_PAN") ? (float) inputParams.getDouble("KEY_PARAM_PAN") : 0.0f;
466
+
467
+ int audioStreamType;
468
+ switch(audioStreamTypeString) {
469
+ /*
470
+ // This has been added in API level 26, commenting out for now
471
+
472
+ case "STREAM_ACCESSIBILITY":
473
+ audioStreamType = AudioManager.STREAM_ACCESSIBILITY;
474
+ break;
475
+ */
476
+ case "STREAM_ALARM":
477
+ audioStreamType = AudioManager.STREAM_ALARM;
478
+ break;
479
+ case "STREAM_DTMF":
480
+ audioStreamType = AudioManager.STREAM_DTMF;
481
+ break;
482
+ case "STREAM_MUSIC":
483
+ audioStreamType = AudioManager.STREAM_MUSIC;
484
+ break;
485
+ case "STREAM_NOTIFICATION":
486
+ audioStreamType = AudioManager.STREAM_NOTIFICATION;
487
+ break;
488
+ case "STREAM_RING":
489
+ audioStreamType = AudioManager.STREAM_RING;
490
+ break;
491
+ case "STREAM_SYSTEM":
492
+ audioStreamType = AudioManager.STREAM_SYSTEM;
493
+ break;
494
+ case "STREAM_VOICE_CALL":
495
+ audioStreamType = AudioManager.STREAM_VOICE_CALL;
496
+ break;
497
+ default:
498
+ audioStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
499
+ }
500
+
501
+ if (Build.VERSION.SDK_INT >= 21) {
502
+ Bundle params = new Bundle();
503
+ params.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, audioStreamType);
504
+ params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, volume);
505
+ params.putFloat(TextToSpeech.Engine.KEY_PARAM_PAN, pan);
506
+ return tts.speak(utterance, TextToSpeech.QUEUE_ADD, params, utteranceId);
507
+ } else {
508
+ HashMap<String, String> params = new HashMap();
509
+ params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
510
+ params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(audioStreamType));
511
+ params.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, String.valueOf(volume));
512
+ params.put(TextToSpeech.Engine.KEY_PARAM_PAN, String.valueOf(pan));
513
+ return tts.speak(utterance, TextToSpeech.QUEUE_ADD, params);
514
+ }
515
+ }
516
+
517
+ private void sendEvent(String eventName, String utteranceId) {
518
+ WritableMap params = Arguments.createMap();
519
+ params.putString("utteranceId", utteranceId);
520
+ sendEvent(eventName, params);
521
+ }
522
+
523
+ private void sendEvent(String eventName, WritableMap params) {
524
+ getReactApplicationContext()
525
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
526
+ .emit(eventName, params);
527
+ }
528
+
529
+ @ReactMethod
530
+ public void removeListeners(Integer count) {
531
+ // Keep: Required for RN built in Event Emitter Calls.
532
+ }
533
+
534
+ @ReactMethod
535
+ public void addListener(String eventName) {
536
+ // Keep: Required for RN built in Event Emitter Calls.
537
+ }
538
+ }
@@ -0,0 +1,31 @@
1
+ package net.no_mad.tts;
2
+
3
+ import com.facebook.react.ReactPackage;
4
+ import com.facebook.react.bridge.JavaScriptModule;
5
+ import com.facebook.react.bridge.NativeModule;
6
+ import com.facebook.react.bridge.ReactApplicationContext;
7
+ import com.facebook.react.uimanager.ViewManager;
8
+
9
+ import java.util.ArrayList;
10
+ import java.util.Collections;
11
+ import java.util.List;
12
+
13
+ public class TextToSpeechPackage implements ReactPackage {
14
+
15
+ @Override
16
+ public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
17
+ List<NativeModule> modules = new ArrayList<>();
18
+ modules.add(new TextToSpeechModule(reactContext));
19
+ return modules;
20
+ }
21
+
22
+ // Deprecated in RN 0.47
23
+ public List<Class<? extends JavaScriptModule>> createJSModules() {
24
+ return Collections.emptyList();
25
+ }
26
+
27
+ @Override
28
+ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
29
+ return Collections.emptyList();
30
+ }
31
+ }
package/index.d.ts ADDED
@@ -0,0 +1,118 @@
1
+ import * as RN from "react-native";
2
+
3
+ type SimpleEvents = "tts-start" | "tts-finish" | "tts-error" | "tts-cancel";
4
+ type SimpleEvent = {
5
+ utteranceId: string | number;
6
+ };
7
+
8
+ type ProgressEventName = "tts-progress";
9
+ type ProgressEvent = {
10
+ utteranceId: string | number;
11
+ location: number;
12
+ length: number;
13
+ };
14
+
15
+ export type TtsEvents = SimpleEvents | ProgressEventName;
16
+ export type TtsEvent<
17
+ T extends TtsEvents = TtsEvents
18
+ > = T extends ProgressEventName ? ProgressEvent : SimpleEvent;
19
+ export type TtsEventHandler<T extends TtsEvents = TtsEvents> = (
20
+ event: TtsEvent<T>
21
+ ) => any;
22
+
23
+ export type TtsError = {
24
+ code:
25
+ | "no_engine"
26
+ | "error"
27
+ | "not_ready"
28
+ | "invalid_request"
29
+ | "network_error"
30
+ | "network_timeout"
31
+ | "not_installed_yet"
32
+ | "output_error"
33
+ | "service_error"
34
+ | "synthesis_error"
35
+ | "lang_missing_data"
36
+ | "lang_not_supported"
37
+ | "Android AudioManager error"
38
+ | "not_available"
39
+ | "not_found"
40
+ | "bad_rate";
41
+ message: string;
42
+ };
43
+
44
+ export type IOSSilentSwitchBehavior = "inherit" | "ignore" | "obey"
45
+
46
+ export type Voice = {
47
+ id: string;
48
+ name: string;
49
+ language: string;
50
+ quality: number;
51
+ latency: number;
52
+ networkConnectionRequired: boolean;
53
+ notInstalled: boolean;
54
+ };
55
+
56
+ export type Engine = {
57
+ name: string;
58
+ label: string;
59
+ default: boolean;
60
+ icon: number;
61
+ };
62
+
63
+ export type AndroidOptions = {
64
+ /** Parameter key to specify the audio stream type to be used when speaking text or playing back a file */
65
+ KEY_PARAM_STREAM:
66
+ | "STREAM_VOICE_CALL"
67
+ | "STREAM_SYSTEM"
68
+ | "STREAM_RING"
69
+ | "STREAM_MUSIC"
70
+ | "STREAM_ALARM"
71
+ | "STREAM_NOTIFICATION"
72
+ | "STREAM_DTMF"
73
+ | "STREAM_ACCESSIBILITY";
74
+ /** Parameter key to specify the speech volume relative to the current stream type volume used when speaking text. Volume is specified as a float ranging from 0 to 1 where 0 is silence, and 1 is the maximum volume (the default behavior). */
75
+ KEY_PARAM_VOLUME: number;
76
+ /** Parameter key to specify how the speech is panned from left to right when speaking text. Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan, 0 to center (the default behavior), and +1 to hard-right. */
77
+ KEY_PARAM_PAN: number;
78
+ };
79
+
80
+ export type Options =
81
+ | string
82
+ | {
83
+ iosVoiceId: string;
84
+ rate: number;
85
+ androidParams: AndroidOptions;
86
+ };
87
+
88
+ export class ReactNativeTts extends RN.NativeEventEmitter {
89
+ getInitStatus: () => Promise<"success">;
90
+ requestInstallEngine: () => Promise<"success">;
91
+ requestInstallData: () => Promise<"success">;
92
+ setDucking: (enabled: boolean) => Promise<"success">;
93
+ setDefaultEngine: (engineName: string) => Promise<boolean>;
94
+ setDefaultVoice: (voiceId: string) => Promise<"success">;
95
+ setDefaultRate: (rate: number, skipTransform?: boolean) => Promise<"success">;
96
+ setDefaultPitch: (pitch: number) => Promise<"success">;
97
+ setDefaultLanguage: (language: string) => Promise<"success">;
98
+ setIgnoreSilentSwitch: (ignoreSilentSwitch: IOSSilentSwitchBehavior) => Promise<boolean>;
99
+ voices: () => Promise<Voice[]>;
100
+ engines: () => Promise<Engine[]>;
101
+ /** Read the sentence and return an id for the task. */
102
+ speak: (utterance: string, options?: Options) => string | number;
103
+ stop: (onWordBoundary?: boolean) => Promise<boolean>;
104
+ pause: (onWordBoundary?: boolean) => Promise<boolean>;
105
+ resume: () => Promise<boolean>;
106
+ addEventListener: <T extends TtsEvents>(
107
+ type: T,
108
+ handler: TtsEventHandler<T>
109
+ ) => void;
110
+ removeEventListener: <T extends TtsEvents>(
111
+ type: T,
112
+ handler: TtsEventHandler<T>
113
+ ) => void;
114
+ }
115
+
116
+ declare const Tts: ReactNativeTts;
117
+
118
+ export default Tts;