expo-speech 11.3.0 → 11.5.0
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.
- package/CHANGELOG.md +18 -0
- package/android/build.gradle +11 -8
- package/android/src/main/java/expo/modules/speech/LanguageUtils.kt +8 -8
- package/android/src/main/java/expo/modules/speech/SpeechInputIsToLongException.kt +8 -0
- package/android/src/main/java/expo/modules/speech/SpeechModule.kt +70 -89
- package/android/src/main/java/expo/modules/speech/SpeechOptions.kt +7 -54
- package/android/src/main/java/expo/modules/speech/VoiceRecord.kt +17 -0
- package/build/Speech.d.ts.map +1 -1
- package/build/Speech.js +3 -9
- package/build/Speech.js.map +1 -1
- package/expo-module.config.json +3 -0
- package/package.json +2 -2
- package/src/Speech/Speech.ts +3 -9
- package/tsconfig.json +1 -1
- package/android/src/main/java/expo/modules/speech/SpeechPackage.kt +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 11.5.0 — 2023-09-04
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Added support for React Native 0.73. ([#24018](https://github.com/expo/expo/pull/24018) by [@kudo](https://github.com/kudo))
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug fixes
|
|
20
|
+
|
|
21
|
+
- Use new `EventEmitter` instead of `NativeEventEmitter`. ([#24221](https://github.com/expo/expo/pull/24221) by [@alanjhughes](https://github.com/alanjhughes))
|
|
22
|
+
|
|
23
|
+
### 💡 Others
|
|
24
|
+
|
|
25
|
+
- Migrated Android codebase to use Expo modules API. ([#23862](https://github.com/expo/expo/pull/23862) by [@lukmccall](https://github.com/lukmccall))
|
|
26
|
+
|
|
27
|
+
## 11.4.0 — 2023-08-02
|
|
28
|
+
|
|
29
|
+
_This version does not introduce any user-facing changes._
|
|
30
|
+
|
|
13
31
|
## 11.3.0 — 2023-06-21
|
|
14
32
|
|
|
15
33
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
|
|
3
3
|
apply plugin: 'maven-publish'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '11.
|
|
6
|
+
version = '11.5.0'
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
9
9
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
@@ -53,13 +53,16 @@ afterEvaluate {
|
|
|
53
53
|
android {
|
|
54
54
|
compileSdkVersion safeExtGet("compileSdkVersion", 33)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
|
|
57
|
+
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
|
58
|
+
compileOptions {
|
|
59
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
60
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
61
|
+
}
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
kotlinOptions {
|
|
64
|
+
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
|
65
|
+
}
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
namespace "expo.modules.speech"
|
|
@@ -67,7 +70,7 @@ android {
|
|
|
67
70
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
68
71
|
targetSdkVersion safeExtGet("targetSdkVersion", 33)
|
|
69
72
|
versionCode 18
|
|
70
|
-
versionName "11.
|
|
73
|
+
versionName "11.5.0"
|
|
71
74
|
}
|
|
72
75
|
lintOptions {
|
|
73
76
|
abortOnError false
|
|
@@ -5,16 +5,16 @@ import java.util.*
|
|
|
5
5
|
// Lazy load the ISO codes into a Map then transform the codes to match other localization patterns in Expo
|
|
6
6
|
object LanguageUtils {
|
|
7
7
|
private val countryISOCodes: Map<String, Locale> by lazy {
|
|
8
|
-
Locale.getISOCountries().
|
|
8
|
+
Locale.getISOCountries().associate { country ->
|
|
9
9
|
val locale = Locale("", country)
|
|
10
|
-
locale.
|
|
11
|
-
}
|
|
10
|
+
locale.isO3Country.uppercase(locale) to locale
|
|
11
|
+
}
|
|
12
12
|
}
|
|
13
13
|
private val languageISOCodes: Map<String, Locale> by lazy {
|
|
14
|
-
Locale.getISOLanguages().
|
|
14
|
+
Locale.getISOLanguages().associate { language ->
|
|
15
15
|
val locale = Locale(language)
|
|
16
|
-
locale.
|
|
17
|
-
}
|
|
16
|
+
locale.isO3Language to locale
|
|
17
|
+
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// NOTE: These helpers are null-unsafe and should be called ONLY with codes
|
|
@@ -23,8 +23,8 @@ object LanguageUtils {
|
|
|
23
23
|
private fun transformLanguageISO(code: String) = languageISOCodes[code]!!.language
|
|
24
24
|
|
|
25
25
|
fun getISOCode(locale: Locale): String {
|
|
26
|
-
var language = transformLanguageISO(locale.
|
|
27
|
-
val country = locale.
|
|
26
|
+
var language = transformLanguageISO(locale.isO3Language)
|
|
27
|
+
val country = locale.isO3Country
|
|
28
28
|
if (country != "") {
|
|
29
29
|
val countryCode = transformCountryISO(country)
|
|
30
30
|
language += "-$countryCode"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
package expo.modules.speech
|
|
2
|
+
|
|
3
|
+
import android.speech.tts.TextToSpeech
|
|
4
|
+
import expo.modules.kotlin.exception.CodedException
|
|
5
|
+
|
|
6
|
+
class SpeechInputIsToLongException : CodedException(
|
|
7
|
+
message = "Speech input text is too long! Limit of input length is: ${TextToSpeech.getMaxSpeechInputLength()}"
|
|
8
|
+
)
|
|
@@ -1,94 +1,87 @@
|
|
|
1
1
|
package expo.modules.speech
|
|
2
2
|
|
|
3
|
-
import android.content.Context
|
|
4
3
|
import android.os.Bundle
|
|
5
4
|
import android.speech.tts.TextToSpeech
|
|
6
5
|
import android.speech.tts.UtteranceProgressListener
|
|
7
6
|
import android.speech.tts.Voice
|
|
8
|
-
import expo.modules.
|
|
9
|
-
import expo.modules.
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
) : ExportedModule(context), LifecycleEventListener {
|
|
22
|
-
|
|
23
|
-
private inline fun <reified T> moduleRegistry() =
|
|
24
|
-
moduleRegistryDelegate.getFromModuleRegistry<T>()
|
|
25
|
-
|
|
26
|
-
private val uiManager: UIManager by moduleRegistry()
|
|
7
|
+
import expo.modules.kotlin.modules.Module
|
|
8
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
9
|
+
import java.util.ArrayDeque
|
|
10
|
+
import java.util.Locale
|
|
11
|
+
import java.util.Queue
|
|
12
|
+
|
|
13
|
+
const val speakingStartedEvent = "Exponent.speakingStarted"
|
|
14
|
+
const val speakingWillSayNextStringEvent = "Exponent.speakingWillSayNextString"
|
|
15
|
+
const val speakingDoneEvent = "Exponent.speakingDone"
|
|
16
|
+
const val speakingStoppedEvent = "Exponent.speakingStopped"
|
|
17
|
+
const val speakingErrorEvent = "Exponent.speakingError"
|
|
18
|
+
|
|
19
|
+
class SpeechModule : Module() {
|
|
27
20
|
private val delayedUtterances: Queue<Utterance> = ArrayDeque()
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
override fun getConstants() = mapOf(
|
|
32
|
-
"maxSpeechInputLength" to TextToSpeech.getMaxSpeechInputLength()
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
// Module methods
|
|
22
|
+
override fun definition() = ModuleDefinition {
|
|
23
|
+
Name("ExpoSpeech")
|
|
36
24
|
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
Events(
|
|
26
|
+
speakingStartedEvent,
|
|
27
|
+
speakingWillSayNextStringEvent,
|
|
28
|
+
speakingDoneEvent,
|
|
29
|
+
speakingStoppedEvent,
|
|
30
|
+
speakingErrorEvent
|
|
31
|
+
)
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
fun getVoices(promise: Promise) {
|
|
42
|
-
var nativeVoices: List<Voice> = emptyList()
|
|
43
|
-
try {
|
|
44
|
-
nativeVoices = textToSpeech.voices.toList()
|
|
45
|
-
} catch (e: Exception) {}
|
|
33
|
+
Constants("maxSpeechInputLength" to TextToSpeech.getMaxSpeechInputLength())
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} else {
|
|
51
|
-
"Default"
|
|
52
|
-
}
|
|
35
|
+
OnActivityDestroys {
|
|
36
|
+
textToSpeech.shutdown()
|
|
37
|
+
}
|
|
53
38
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
putString("name", it.name)
|
|
57
|
-
putString("quality", quality)
|
|
58
|
-
putString("language", LanguageUtils.getISOCode(it.locale))
|
|
59
|
-
}
|
|
39
|
+
AsyncFunction("isSpeaking") {
|
|
40
|
+
textToSpeech.isSpeaking
|
|
60
41
|
}
|
|
61
42
|
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
AsyncFunction("getVoices") {
|
|
44
|
+
val nativeVoices = try {
|
|
45
|
+
textToSpeech.voices.toList()
|
|
46
|
+
} catch (_: Exception) {
|
|
47
|
+
emptyList()
|
|
48
|
+
}
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
50
|
+
return@AsyncFunction nativeVoices.map {
|
|
51
|
+
val quality = if (it.quality > Voice.QUALITY_NORMAL) {
|
|
52
|
+
VoiceQuality.ENHANCED
|
|
53
|
+
} else {
|
|
54
|
+
VoiceQuality.DEFAULT
|
|
55
|
+
}
|
|
70
56
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
VoiceRecord(
|
|
58
|
+
identifier = it.name,
|
|
59
|
+
name = it.name,
|
|
60
|
+
quality = quality,
|
|
61
|
+
language = LanguageUtils.getISOCode(it.locale)
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"ERR_SPEECH_INPUT_LENGTH",
|
|
78
|
-
"Speech input text is too long! Limit of input length is: " + TextToSpeech.getMaxSpeechInputLength()
|
|
79
|
-
)
|
|
80
|
-
return
|
|
66
|
+
AsyncFunction("stop") {
|
|
67
|
+
textToSpeech.stop()
|
|
81
68
|
}
|
|
82
69
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
AsyncFunction("speak") { id: String, text: String, options: SpeechOptions ->
|
|
71
|
+
if (text.length > TextToSpeech.getMaxSpeechInputLength()) {
|
|
72
|
+
throw SpeechInputIsToLongException()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (isTextToSpeechReady) {
|
|
76
|
+
speakOut(id, text, options)
|
|
77
|
+
} else {
|
|
78
|
+
delayedUtterances.add(Utterance(id, text, options))
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
// init TTS, speaking will be available only after onInit
|
|
81
|
+
textToSpeech
|
|
82
|
+
}
|
|
83
|
+
Unit
|
|
90
84
|
}
|
|
91
|
-
promise.resolve(null)
|
|
92
85
|
}
|
|
93
86
|
|
|
94
87
|
private fun speakOut(id: String, text: String, options: SpeechOptions) {
|
|
@@ -129,37 +122,36 @@ class SpeechModule(
|
|
|
129
122
|
get() = _ttsReady
|
|
130
123
|
|
|
131
124
|
private val textToSpeech: TextToSpeech by lazy {
|
|
132
|
-
val newTtsInstance = TextToSpeech(
|
|
125
|
+
val newTtsInstance = TextToSpeech(appContext.reactContext) { status: Int ->
|
|
133
126
|
if (status == TextToSpeech.SUCCESS) {
|
|
134
127
|
// synchronize because in some cases this runs on another thread and _textToSpeech is null
|
|
135
128
|
synchronized(this@SpeechModule) {
|
|
136
129
|
_ttsReady = true
|
|
137
130
|
_textToSpeech!!.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
|
|
138
|
-
private val emitter by moduleRegistry<EventEmitter>()
|
|
139
131
|
|
|
140
132
|
override fun onStart(utteranceId: String) {
|
|
141
|
-
|
|
133
|
+
sendEvent(speakingStartedEvent, idToMap(utteranceId))
|
|
142
134
|
}
|
|
143
135
|
|
|
144
136
|
override fun onRangeStart(utteranceId: String, start: Int, end: Int, frame: Int) {
|
|
145
|
-
|
|
137
|
+
val map = Bundle().apply {
|
|
146
138
|
putString("id", utteranceId)
|
|
147
139
|
putInt("charIndex", start)
|
|
148
140
|
putInt("charLength", end - start)
|
|
149
141
|
}
|
|
150
|
-
|
|
142
|
+
sendEvent(speakingWillSayNextStringEvent, map)
|
|
151
143
|
}
|
|
152
144
|
|
|
153
145
|
override fun onDone(utteranceId: String) {
|
|
154
|
-
|
|
146
|
+
sendEvent(speakingDoneEvent, idToMap(utteranceId))
|
|
155
147
|
}
|
|
156
148
|
|
|
157
149
|
override fun onStop(utteranceId: String, interrupted: Boolean) {
|
|
158
|
-
|
|
150
|
+
sendEvent(speakingStoppedEvent, idToMap(utteranceId))
|
|
159
151
|
}
|
|
160
152
|
|
|
161
153
|
override fun onError(utteranceId: String) {
|
|
162
|
-
|
|
154
|
+
sendEvent(speakingErrorEvent, idToMap(utteranceId))
|
|
163
155
|
}
|
|
164
156
|
})
|
|
165
157
|
for ((id, text, options) in delayedUtterances) {
|
|
@@ -172,17 +164,6 @@ class SpeechModule(
|
|
|
172
164
|
newTtsInstance
|
|
173
165
|
}
|
|
174
166
|
|
|
175
|
-
// Lifecycle methods
|
|
176
|
-
override fun onCreate(moduleRegistry: ModuleRegistry) {
|
|
177
|
-
moduleRegistryDelegate.onCreate(moduleRegistry)
|
|
178
|
-
uiManager.registerLifecycleEventListener(this)
|
|
179
|
-
}
|
|
180
|
-
override fun onHostPause() {}
|
|
181
|
-
override fun onHostResume() {}
|
|
182
|
-
override fun onHostDestroy() {
|
|
183
|
-
textToSpeech.shutdown()
|
|
184
|
-
}
|
|
185
|
-
|
|
186
167
|
// Helpers
|
|
187
168
|
private fun idToMap(id: String) = Bundle().apply {
|
|
188
169
|
putString("id", id)
|
|
@@ -1,58 +1,11 @@
|
|
|
1
1
|
package expo.modules.speech
|
|
2
2
|
|
|
3
|
-
import expo.modules.
|
|
4
|
-
import
|
|
3
|
+
import expo.modules.kotlin.records.Field
|
|
4
|
+
import expo.modules.kotlin.records.Record
|
|
5
5
|
|
|
6
6
|
data class SpeechOptions(
|
|
7
|
-
val language: String?,
|
|
8
|
-
val pitch: Float?,
|
|
9
|
-
val rate: Float?,
|
|
10
|
-
val voice: String?
|
|
11
|
-
)
|
|
12
|
-
companion object {
|
|
13
|
-
fun optionsFromMap(options: Map<String, Any?>?, promise: Promise): SpeechOptions? {
|
|
14
|
-
|
|
15
|
-
if (options == null) {
|
|
16
|
-
return SpeechOptions(null, null, null, null)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
val language = options["language"]?.let {
|
|
20
|
-
if (it is String) {
|
|
21
|
-
return@let it
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
promise.reject("ERR_INVALID_OPTION", "Language must be a string")
|
|
25
|
-
return null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
val pitch = options["pitch"]?.let {
|
|
29
|
-
if (it is Number) {
|
|
30
|
-
return@let it.toFloat()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
promise.reject("ERR_INVALID_OPTION", "Pitch must be a number")
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
val rate = options["rate"]?.let {
|
|
38
|
-
if (it is Number) {
|
|
39
|
-
return@let it.toFloat()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
promise.reject("ERR_INVALID_OPTION", "Rate must be a number")
|
|
43
|
-
return null
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
val voice = options["voice"]?.let {
|
|
47
|
-
if (it is String) {
|
|
48
|
-
return@let it
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
promise.reject("ERR_INVALID_OPTION", "Voice name must be a string")
|
|
52
|
-
return null
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return SpeechOptions(language, pitch, rate, voice)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
7
|
+
@Field val language: String?,
|
|
8
|
+
@Field val pitch: Float?,
|
|
9
|
+
@Field val rate: Float?,
|
|
10
|
+
@Field val voice: String?
|
|
11
|
+
) : Record
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package expo.modules.speech
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.records.Field
|
|
4
|
+
import expo.modules.kotlin.records.Record
|
|
5
|
+
import expo.modules.kotlin.types.Enumerable
|
|
6
|
+
|
|
7
|
+
enum class VoiceQuality(val value: String) : Enumerable {
|
|
8
|
+
ENHANCED("Enhanced"),
|
|
9
|
+
DEFAULT("Default")
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
data class VoiceRecord(
|
|
13
|
+
@Field val identifier: String,
|
|
14
|
+
@Field val name: String,
|
|
15
|
+
@Field val quality: VoiceQuality,
|
|
16
|
+
@Field val language: String
|
|
17
|
+
) : Record
|
package/build/Speech.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Speech.d.ts","sourceRoot":"","sources":["../src/Speech/Speech.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Speech.d.ts","sourceRoot":"","sources":["../src/Speech/Speech.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAInG,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AA8D7E;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,QAK9D;AAGD;;;GAGG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAKhE;AAGD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAExD;AAGD;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;AAGD;;GAEG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3C;AAGD;;;GAGG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAM5C;AAeD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAgE,CAAC"}
|
package/build/Speech.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
|
-
import { NativeEventEmitter } from 'react-native';
|
|
1
|
+
import { UnavailabilityError, EventEmitter } from 'expo-modules-core';
|
|
3
2
|
import ExponentSpeech from './ExponentSpeech';
|
|
4
3
|
import { VoiceQuality } from './Speech.types';
|
|
5
|
-
const SpeechEventEmitter =
|
|
4
|
+
const SpeechEventEmitter = new EventEmitter(ExponentSpeech);
|
|
6
5
|
export { VoiceQuality };
|
|
7
6
|
const _CALLBACKS = {};
|
|
8
7
|
let _nextCallbackId = 1;
|
|
@@ -123,12 +122,7 @@ export async function resume() {
|
|
|
123
122
|
return ExponentSpeech.resume();
|
|
124
123
|
}
|
|
125
124
|
function setSpeakingListener(eventName, callback) {
|
|
126
|
-
|
|
127
|
-
const listenerCount = SpeechEventEmitter.listenerCount
|
|
128
|
-
? // @ts-ignore: this is available since 0.64
|
|
129
|
-
SpeechEventEmitter.listenerCount(eventName)
|
|
130
|
-
: // @ts-ignore: this is available in older versions
|
|
131
|
-
SpeechEventEmitter.listeners(eventName).length;
|
|
125
|
+
const listenerCount = SpeechEventEmitter._listenerCount;
|
|
132
126
|
if (listenerCount > 0) {
|
|
133
127
|
SpeechEventEmitter.removeAllListeners(eventName);
|
|
134
128
|
}
|
package/build/Speech.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Speech.js","sourceRoot":"","sources":["../src/Speech/Speech.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"Speech.js","sourceRoot":"","sources":["../src/Speech/Speech.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtE,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAsC,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AAEnG,MAAM,kBAAkB,GAAG,IAAI,YAAY,CAAC,cAAc,CAAC,CAAC;AAE5D,OAAO,EAAsC,YAAY,EAAmB,CAAC;AAE7E,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,IAAI,eAAe,GAAG,CAAC,CAAC;AACxB,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B,SAAS,4BAA4B;IACnC,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;QACxC,sBAAsB,CAAC,0BAA0B,CAAC,CAAC;QACnD,sBAAsB,CAAC,oCAAoC,CAAC,CAAC;QAC7D,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;QAChD,sBAAsB,CAAC,0BAA0B,CAAC,CAAC;QACnD,sBAAsB,CAAC,wBAAwB,CAAC,CAAC;QACjD,gBAAgB,GAAG,KAAK,CAAC;KAC1B;AACH,CAAC;AAED,SAAS,0BAA0B;IACjC,IAAI,gBAAgB;QAAE,OAAO;IAC7B,gBAAgB,GAAG,IAAI,CAAC;IACxB,mBAAmB,CAAC,0BAA0B,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACzD,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE;YAC9B,OAAO,CAAC,OAAO,EAAE,CAAC;SACnB;IACH,CAAC,CAAC,CAAC;IACH,mBAAmB,CAAC,oCAAoC,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE;QAC1F,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,OAAO,IAAI,OAAO,CAAC,UAAU,EAAE;YACjC,OAAO,CAAC,UAAU,CAAC;gBACjB,SAAS;gBACT,UAAU;aACX,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IACH,mBAAmB,CAAC,uBAAuB,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACtD,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE;YAC7B,OAAO,CAAC,MAAM,EAAE,CAAC;SAClB;QACD,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;QACtB,4BAA4B,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IACH,mBAAmB,CAAC,0BAA0B,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACzD,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE;YAChC,OAAO,CAAC,SAAS,EAAE,CAAC;SACrB;QACD,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;QACtB,4BAA4B,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IACH,mBAAmB,CAAC,wBAAwB,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC9D,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE;YAC9B,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;SACnC;QACD,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;QACtB,4BAA4B,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,cAAc;AACd;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,UAAyB,EAAE;IAC7D,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IACzB,0BAA0B,EAAE,CAAC;IAC7B,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED,cAAc;AACd;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE;QAC7B,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;KACtD;IACD,OAAO,cAAc,CAAC,SAAS,EAAE,CAAC;AACpC,CAAC;AAED,aAAa;AACb;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,OAAO,cAAc,CAAC,UAAU,EAAE,CAAC;AACrC,CAAC;AAED,cAAc;AACd;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,OAAO,cAAc,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,cAAc;AACd;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE;QACzB,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;KAClD;IACD,OAAO,cAAc,CAAC,KAAK,EAAE,CAAC;AAChC,CAAC;AAED,cAAc;AACd;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE;QAC1B,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;KACnD;IAED,OAAO,cAAc,CAAC,MAAM,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,mBAAmB,CAAC,SAAS,EAAE,QAAQ;IAC9C,MAAM,aAAa,GAAG,kBAAkB,CAAC,cAAc,CAAC;IACxD,IAAI,aAAa,GAAG,CAAC,EAAE;QACrB,kBAAkB,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;KAClD;IACD,kBAAkB,CAAC,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,sBAAsB,CAAC,SAAS;IACvC,kBAAkB,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC;AAED,cAAc;AACd;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAW,cAAc,CAAC,oBAAoB,IAAI,MAAM,CAAC,SAAS,CAAC","sourcesContent":["import { UnavailabilityError, EventEmitter } from 'expo-modules-core';\n\nimport ExponentSpeech from './ExponentSpeech';\nimport { SpeechOptions, SpeechEventCallback, VoiceQuality, Voice, WebVoice } from './Speech.types';\n\nconst SpeechEventEmitter = new EventEmitter(ExponentSpeech);\n\nexport { SpeechOptions, SpeechEventCallback, VoiceQuality, Voice, WebVoice };\n\nconst _CALLBACKS = {};\nlet _nextCallbackId = 1;\nlet _didSetListeners = false;\n\nfunction _unregisterListenersIfNeeded() {\n if (Object.keys(_CALLBACKS).length === 0) {\n removeSpeakingListener('Exponent.speakingStarted');\n removeSpeakingListener('Exponent.speakingWillSayNextString');\n removeSpeakingListener('Exponent.speakingDone');\n removeSpeakingListener('Exponent.speakingStopped');\n removeSpeakingListener('Exponent.speakingError');\n _didSetListeners = false;\n }\n}\n\nfunction _registerListenersIfNeeded() {\n if (_didSetListeners) return;\n _didSetListeners = true;\n setSpeakingListener('Exponent.speakingStarted', ({ id }) => {\n const options = _CALLBACKS[id];\n if (options && options.onStart) {\n options.onStart();\n }\n });\n setSpeakingListener('Exponent.speakingWillSayNextString', ({ id, charIndex, charLength }) => {\n const options = _CALLBACKS[id];\n if (options && options.onBoundary) {\n options.onBoundary({\n charIndex,\n charLength,\n });\n }\n });\n setSpeakingListener('Exponent.speakingDone', ({ id }) => {\n const options = _CALLBACKS[id];\n if (options && options.onDone) {\n options.onDone();\n }\n delete _CALLBACKS[id];\n _unregisterListenersIfNeeded();\n });\n setSpeakingListener('Exponent.speakingStopped', ({ id }) => {\n const options = _CALLBACKS[id];\n if (options && options.onStopped) {\n options.onStopped();\n }\n delete _CALLBACKS[id];\n _unregisterListenersIfNeeded();\n });\n setSpeakingListener('Exponent.speakingError', ({ id, error }) => {\n const options = _CALLBACKS[id];\n if (options && options.onError) {\n options.onError(new Error(error));\n }\n delete _CALLBACKS[id];\n _unregisterListenersIfNeeded();\n });\n}\n\n// @needsAudit\n/**\n * Speak out loud the text given options. Calling this when another text is being spoken adds\n * an utterance to queue.\n * @param text The text to be spoken. Cannot be longer than [`Speech.maxSpeechInputLength`](#speechmaxspeechinputlength).\n * @param options A `SpeechOptions` object.\n */\nexport function speak(text: string, options: SpeechOptions = {}) {\n const id = _nextCallbackId++;\n _CALLBACKS[id] = options;\n _registerListenersIfNeeded();\n ExponentSpeech.speak(String(id), text, options);\n}\n\n// @needsAudit\n/**\n * Returns list of all available voices.\n * @return List of `Voice` objects.\n */\nexport async function getAvailableVoicesAsync(): Promise<Voice[]> {\n if (!ExponentSpeech.getVoices) {\n throw new UnavailabilityError('Speech', 'getVoices');\n }\n return ExponentSpeech.getVoices();\n}\n\n//@needsAudit\n/**\n * Determine whether the Text-to-speech utility is currently speaking. Will return `true` if speaker\n * is paused.\n * @return Returns a Promise that fulfils with a boolean, `true` if speaking, `false` if not.\n */\nexport async function isSpeakingAsync(): Promise<boolean> {\n return ExponentSpeech.isSpeaking();\n}\n\n// @needsAudit\n/**\n * Interrupts current speech and deletes all in queue.\n */\nexport async function stop(): Promise<void> {\n return ExponentSpeech.stop();\n}\n\n// @needsAudit\n/**\n * Pauses current speech. This method is not available on Android.\n */\nexport async function pause(): Promise<void> {\n if (!ExponentSpeech.pause) {\n throw new UnavailabilityError('Speech', 'pause');\n }\n return ExponentSpeech.pause();\n}\n\n// @needsAudit\n/**\n * Resumes speaking previously paused speech or does nothing if there's none. This method is not\n * available on Android.\n */\nexport async function resume(): Promise<void> {\n if (!ExponentSpeech.resume) {\n throw new UnavailabilityError('Speech', 'resume');\n }\n\n return ExponentSpeech.resume();\n}\n\nfunction setSpeakingListener(eventName, callback) {\n const listenerCount = SpeechEventEmitter._listenerCount;\n if (listenerCount > 0) {\n SpeechEventEmitter.removeAllListeners(eventName);\n }\n SpeechEventEmitter.addListener(eventName, callback);\n}\n\nfunction removeSpeakingListener(eventName) {\n SpeechEventEmitter.removeAllListeners(eventName);\n}\n\n// @needsAudit\n/**\n * Maximum possible text length acceptable by `Speech.speak()` method. It is platform-dependent.\n * On iOS, this returns `Number.MAX_VALUE`.\n */\nexport const maxSpeechInputLength: number = ExponentSpeech.maxSpeechInputLength || Number.MAX_VALUE;\n"]}
|
package/expo-module.config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-speech",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.5.0",
|
|
4
4
|
"description": "Provides text-to-speech functionality.",
|
|
5
5
|
"main": "build/Speech.js",
|
|
6
6
|
"types": "build/Speech.d.ts",
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"expo": "*"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "79607a7325f47aa17c36d266100d09a4ff2cc544"
|
|
46
46
|
}
|
package/src/Speech/Speech.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
|
-
import { NativeEventEmitter } from 'react-native';
|
|
1
|
+
import { UnavailabilityError, EventEmitter } from 'expo-modules-core';
|
|
3
2
|
|
|
4
3
|
import ExponentSpeech from './ExponentSpeech';
|
|
5
4
|
import { SpeechOptions, SpeechEventCallback, VoiceQuality, Voice, WebVoice } from './Speech.types';
|
|
6
5
|
|
|
7
|
-
const SpeechEventEmitter =
|
|
6
|
+
const SpeechEventEmitter = new EventEmitter(ExponentSpeech);
|
|
8
7
|
|
|
9
8
|
export { SpeechOptions, SpeechEventCallback, VoiceQuality, Voice, WebVoice };
|
|
10
9
|
|
|
@@ -136,12 +135,7 @@ export async function resume(): Promise<void> {
|
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
function setSpeakingListener(eventName, callback) {
|
|
139
|
-
|
|
140
|
-
const listenerCount = SpeechEventEmitter.listenerCount
|
|
141
|
-
? // @ts-ignore: this is available since 0.64
|
|
142
|
-
SpeechEventEmitter.listenerCount(eventName)
|
|
143
|
-
: // @ts-ignore: this is available in older versions
|
|
144
|
-
SpeechEventEmitter.listeners(eventName).length;
|
|
138
|
+
const listenerCount = SpeechEventEmitter._listenerCount;
|
|
145
139
|
if (listenerCount > 0) {
|
|
146
140
|
SpeechEventEmitter.removeAllListeners(eventName);
|
|
147
141
|
}
|
package/tsconfig.json
CHANGED