@focus8/expo-acapela-tts 0.1.12 → 0.2.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/android/src/main/java/expo/modules/acapelatts/ExpoAcapelaTtsModule.kt +33 -226
- package/build/ExpoAcapelaTts.types.d.ts +0 -1
- package/build/ExpoAcapelaTts.types.d.ts.map +1 -1
- package/build/ExpoAcapelaTts.types.js.map +1 -1
- package/build/ExpoAcapelaTtsModule.android.d.ts +1 -3
- package/build/ExpoAcapelaTtsModule.android.d.ts.map +1 -1
- package/build/ExpoAcapelaTtsModule.android.js.map +1 -1
- package/build/ExpoAcapelaTtsModule.d.ts +1 -3
- package/build/ExpoAcapelaTtsModule.d.ts.map +1 -1
- package/build/ExpoAcapelaTtsModule.js +1 -8
- package/build/ExpoAcapelaTtsModule.js.map +1 -1
- package/package.json +1 -1
- package/src/ExpoAcapelaTts.types.ts +0 -1
- package/src/ExpoAcapelaTtsModule.android.ts +1 -3
- package/src/ExpoAcapelaTtsModule.ts +1 -8
|
@@ -1,33 +1,23 @@
|
|
|
1
1
|
package expo.modules.acapelatts
|
|
2
2
|
|
|
3
|
-
import android.Manifest
|
|
4
|
-
import android.content.Intent
|
|
5
|
-
import android.content.pm.PackageManager
|
|
6
|
-
import android.net.Uri
|
|
7
|
-
import android.os.Build
|
|
8
|
-
import android.os.Environment
|
|
9
|
-
import android.provider.Settings
|
|
10
3
|
import android.util.Log
|
|
11
|
-
import androidx.core.content.ContextCompat
|
|
12
4
|
import com.acapelagroup.android.tts.acattsandroid
|
|
13
5
|
import com.acapelagroup.android.tts.acattsandroid.iTTSEventsCallback
|
|
14
6
|
import expo.modules.kotlin.modules.Module
|
|
15
7
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
16
|
-
import java.io.File
|
|
17
8
|
|
|
18
9
|
class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
19
10
|
|
|
20
11
|
private var tts: acattsandroid? = null
|
|
21
12
|
private var isInitialized = false
|
|
22
13
|
private var currentVoice: String? = null
|
|
23
|
-
private var voicePaths = mutableListOf(
|
|
14
|
+
private var voicePaths = mutableListOf<String>()
|
|
24
15
|
private var licenseUserId: Long? = null
|
|
25
16
|
private var licensePassword: Long? = null
|
|
26
17
|
private var licenseKey: String? = null
|
|
27
18
|
|
|
28
19
|
companion object {
|
|
29
20
|
private const val TAG = "ExpoAcapelaTts"
|
|
30
|
-
|
|
31
21
|
private var nativeLibLoaded = false
|
|
32
22
|
|
|
33
23
|
init {
|
|
@@ -47,63 +37,8 @@ class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
|
47
37
|
|
|
48
38
|
Events("onSpeechStart", "onSpeechEnd", "onError")
|
|
49
39
|
|
|
50
|
-
Function("hasStoragePermission") {
|
|
51
|
-
hasStorageAccess()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
AsyncFunction("requestStoragePermissionAsync") {
|
|
55
|
-
val context = appContext.reactContext ?: return@AsyncFunction false
|
|
56
|
-
|
|
57
|
-
if (hasStorageAccess()) {
|
|
58
|
-
logInfo("Storage permission already granted")
|
|
59
|
-
return@AsyncFunction true
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
63
|
-
// Android 11+ needs MANAGE_EXTERNAL_STORAGE via Settings intent
|
|
64
|
-
try {
|
|
65
|
-
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
|
|
66
|
-
data = Uri.fromParts("package", context.packageName, null)
|
|
67
|
-
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
68
|
-
}
|
|
69
|
-
context.startActivity(intent)
|
|
70
|
-
logInfo("Launched MANAGE_EXTERNAL_STORAGE settings for ${context.packageName}")
|
|
71
|
-
} catch (e: Exception) {
|
|
72
|
-
// Fallback for devices that don't support the per-app intent
|
|
73
|
-
try {
|
|
74
|
-
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION).apply {
|
|
75
|
-
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
76
|
-
}
|
|
77
|
-
context.startActivity(intent)
|
|
78
|
-
} catch (e2: Exception) {
|
|
79
|
-
logError("Failed to launch storage permission settings", e2)
|
|
80
|
-
return@AsyncFunction false
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// User needs to grant in settings and come back - we can't wait for result
|
|
84
|
-
false
|
|
85
|
-
} else {
|
|
86
|
-
// Pre-Android 11: READ_EXTERNAL_STORAGE is enough, granted via manifest on enrolled AOSP
|
|
87
|
-
logInfo("Pre-Android 11: storage permissions handled by manifest")
|
|
88
|
-
true
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
40
|
Function("isAvailable") {
|
|
93
|
-
|
|
94
|
-
return@Function false
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
val available = voicePaths.any { path ->
|
|
98
|
-
val dir = File(path)
|
|
99
|
-
dir.exists() && dir.isDirectory && (dir.list()?.isNotEmpty() == true)
|
|
100
|
-
}
|
|
101
|
-
logInfo("isAvailable: $available (paths: ${voicePaths.joinToString()})")
|
|
102
|
-
available
|
|
103
|
-
} catch (e: Exception) {
|
|
104
|
-
logError("isAvailable check failed", e)
|
|
105
|
-
false
|
|
106
|
-
}
|
|
41
|
+
nativeLibLoaded && voicePaths.isNotEmpty()
|
|
107
42
|
}
|
|
108
43
|
|
|
109
44
|
Function("addVoicePath") { path: String ->
|
|
@@ -117,116 +52,64 @@ class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
|
117
52
|
licenseUserId = java.lang.Long.decode(userIdStr)
|
|
118
53
|
licensePassword = java.lang.Long.decode(passwordStr)
|
|
119
54
|
licenseKey = key
|
|
120
|
-
logInfo("License set
|
|
55
|
+
logInfo("License set")
|
|
121
56
|
}
|
|
122
57
|
|
|
123
58
|
AsyncFunction("initializeAsync") {
|
|
124
59
|
if (!nativeLibLoaded) {
|
|
125
|
-
logWarning("Cannot initialize: native library not loaded")
|
|
126
60
|
return@AsyncFunction false
|
|
127
61
|
}
|
|
128
|
-
|
|
129
62
|
if (isInitialized && tts != null) {
|
|
130
|
-
logInfo("Already initialized")
|
|
131
63
|
return@AsyncFunction true
|
|
132
64
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
val
|
|
136
|
-
val pwd = licensePassword ?: BuildConfig.ACAPELA_PASSWORD
|
|
137
|
-
val key = licenseKey ?: BuildConfig.ACAPELA_LICENSE_KEY
|
|
138
|
-
|
|
65
|
+
val uid = licenseUserId ?: 0L
|
|
66
|
+
val pwd = licensePassword ?: 0L
|
|
67
|
+
val key = licenseKey
|
|
139
68
|
if (uid == 0L || key.isNullOrEmpty()) {
|
|
140
|
-
logWarning("
|
|
69
|
+
logWarning("No license configured")
|
|
141
70
|
return@AsyncFunction false
|
|
142
71
|
}
|
|
143
|
-
|
|
144
72
|
try {
|
|
145
|
-
val context = appContext.reactContext ?:
|
|
146
|
-
logWarning("ReactContext not available")
|
|
147
|
-
return@AsyncFunction false
|
|
148
|
-
}
|
|
149
|
-
|
|
73
|
+
val context = appContext.reactContext ?: return@AsyncFunction false
|
|
150
74
|
tts = acattsandroid(context, this@ExpoAcapelaTtsModule, null)
|
|
151
75
|
tts!!.setLog(true)
|
|
152
|
-
logInfo("setLicense userId=0x${java.lang.Long.toHexString(uid)} password=0x${java.lang.Long.toHexString(pwd)} keyLength=${key?.length}")
|
|
153
76
|
tts!!.setLicense(uid, pwd, key)
|
|
154
|
-
|
|
155
|
-
// Store for re-use after getVoicesList
|
|
156
|
-
licenseUserId = uid
|
|
157
|
-
licensePassword = pwd
|
|
158
|
-
licenseKey = key
|
|
159
|
-
|
|
160
|
-
val version = tts!!.version
|
|
161
|
-
logInfo("Initialized, SDK version: $version")
|
|
162
|
-
|
|
77
|
+
logInfo("Initialized, version: ${tts!!.version}")
|
|
163
78
|
isInitialized = true
|
|
164
79
|
true
|
|
165
80
|
} catch (e: Exception) {
|
|
166
|
-
logError("
|
|
81
|
+
logError("Init failed", e)
|
|
167
82
|
tts = null
|
|
168
83
|
isInitialized = false
|
|
169
84
|
false
|
|
170
85
|
}
|
|
171
86
|
}
|
|
172
87
|
|
|
173
|
-
AsyncFunction("getVoicesAsync") {
|
|
88
|
+
AsyncFunction("getVoicesAsync") { paths: List<String> ->
|
|
174
89
|
if (tts == null) {
|
|
175
|
-
logWarning("getVoicesAsync: TTS not initialized")
|
|
176
90
|
return@AsyncFunction emptyList<Map<String, Any>>()
|
|
177
91
|
}
|
|
178
|
-
|
|
179
92
|
try {
|
|
180
|
-
val
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
val dir = java.io.File(path)
|
|
184
|
-
if (dir.exists()) {
|
|
185
|
-
val files = dir.list() ?: emptyArray()
|
|
186
|
-
logInfo(" Path $path: exists=${dir.exists()}, canRead=${dir.canRead()}, entries=${files.size}")
|
|
187
|
-
} else {
|
|
188
|
-
logInfo(" Path $path: does not exist")
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
val voicesList = tts!!.getVoicesList(voiceDirPaths) ?: emptyArray()
|
|
192
|
-
// Re-set license after getVoicesList as it may reset internal state
|
|
93
|
+
val dirPaths = paths.toTypedArray()
|
|
94
|
+
val voicesList = tts!!.getVoicesList(dirPaths) ?: emptyArray()
|
|
95
|
+
// Re-set license after getVoicesList
|
|
193
96
|
if (licenseUserId != null && licensePassword != null && licenseKey != null) {
|
|
194
97
|
tts!!.setLicense(licenseUserId!!, licensePassword!!, licenseKey)
|
|
195
98
|
}
|
|
196
|
-
logInfo("Found ${voicesList.size} voices")
|
|
197
|
-
|
|
198
|
-
logInfo(" Voice: $v")
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
val seen = mutableMapOf<String, Int>()
|
|
202
|
-
val voices = voicesList.mapIndexed { index, voiceName ->
|
|
99
|
+
logInfo("Found ${voicesList.size} voices in ${paths.joinToString()}")
|
|
100
|
+
voicesList.map { voiceName ->
|
|
203
101
|
val info = tts!!.getVoiceInfo(voiceName) ?: emptyMap()
|
|
204
|
-
val genderValue = info["gender"] ?: "0"
|
|
205
|
-
val internalName = info["name"] ?: "voice_$index"
|
|
206
|
-
// Track duplicates and create unique id
|
|
207
|
-
val count = seen.getOrDefault(voiceName, 0)
|
|
208
|
-
seen[voiceName] = count + 1
|
|
209
|
-
val uniqueId = if (count == 0) voiceName else "${voiceName}_${count}"
|
|
210
102
|
mapOf(
|
|
211
|
-
"id" to uniqueId,
|
|
212
103
|
"name" to voiceName,
|
|
213
104
|
"locale" to (info["locale"] ?: ""),
|
|
214
105
|
"language" to (info["language"] ?: ""),
|
|
215
|
-
"speaker" to
|
|
216
|
-
"gender" to if (
|
|
106
|
+
"speaker" to (info["name"] ?: ""),
|
|
107
|
+
"gender" to if ((info["gender"] ?: "0") == "1") "female" else "male",
|
|
217
108
|
"quality" to (info["quality"] ?: ""),
|
|
218
109
|
"age" to (info["age"] ?: ""),
|
|
219
110
|
"frequency" to (info["frequency"] ?: "")
|
|
220
111
|
)
|
|
221
112
|
}
|
|
222
|
-
|
|
223
|
-
// Reload the current voice since getVoicesList unloads any loaded voice
|
|
224
|
-
if (currentVoice != null) {
|
|
225
|
-
tts!!.load(currentVoice, "MODE=prep_full")
|
|
226
|
-
logInfo("Reloaded voice after listing: $currentVoice")
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
voices
|
|
230
113
|
} catch (e: Exception) {
|
|
231
114
|
logError("Failed to get voices", e)
|
|
232
115
|
emptyList<Map<String, Any>>()
|
|
@@ -235,12 +118,10 @@ class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
|
235
118
|
|
|
236
119
|
AsyncFunction("loadVoiceAsync") { voiceName: String ->
|
|
237
120
|
if (tts == null) {
|
|
238
|
-
logWarning("loadVoiceAsync: TTS not initialized")
|
|
239
121
|
return@AsyncFunction false
|
|
240
122
|
}
|
|
241
|
-
|
|
242
123
|
try {
|
|
243
|
-
val result = tts!!.load(voiceName, "
|
|
124
|
+
val result = tts!!.load(voiceName, "")
|
|
244
125
|
if (result == 0) {
|
|
245
126
|
currentVoice = voiceName
|
|
246
127
|
logInfo("Voice loaded: $voiceName")
|
|
@@ -257,125 +138,51 @@ class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
|
257
138
|
|
|
258
139
|
AsyncFunction("speakAsync") { text: String ->
|
|
259
140
|
if (tts == null) {
|
|
260
|
-
logWarning("speakAsync: TTS not initialized")
|
|
261
141
|
return@AsyncFunction -1
|
|
262
142
|
}
|
|
263
|
-
|
|
264
143
|
try {
|
|
265
144
|
if (tts!!.isSpeaking) {
|
|
266
145
|
tts!!.stop()
|
|
267
146
|
}
|
|
268
|
-
|
|
269
|
-
if (result < 0) {
|
|
270
|
-
logWarning("speak returned error: $result")
|
|
271
|
-
}
|
|
272
|
-
result
|
|
147
|
+
tts!!.speak(text)
|
|
273
148
|
} catch (e: Exception) {
|
|
274
149
|
logError("Failed to speak", e)
|
|
275
150
|
-1
|
|
276
151
|
}
|
|
277
152
|
}
|
|
278
153
|
|
|
279
|
-
Function("stop") {
|
|
280
|
-
try {
|
|
281
|
-
tts?.stop()
|
|
282
|
-
} catch (e: Exception) {
|
|
283
|
-
logError("Failed to stop", e)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
154
|
+
Function("stop") { tts?.stop() }
|
|
286
155
|
|
|
287
|
-
Function("isSpeaking") {
|
|
288
|
-
try {
|
|
289
|
-
tts?.isSpeaking ?: false
|
|
290
|
-
} catch (e: Exception) {
|
|
291
|
-
false
|
|
292
|
-
}
|
|
293
|
-
}
|
|
156
|
+
Function("isSpeaking") { tts?.isSpeaking ?: false }
|
|
294
157
|
|
|
295
158
|
Function("setSpeechRate") { rate: Float ->
|
|
296
|
-
|
|
297
|
-
// JS sends 0.5-2.0, Acapela expects 30-400 (percentage)
|
|
298
|
-
val acapelaRate = rate * 100f
|
|
299
|
-
tts?.setSpeechRate(acapelaRate)
|
|
300
|
-
} catch (e: Exception) {
|
|
301
|
-
logError("Failed to set speech rate", e)
|
|
302
|
-
}
|
|
159
|
+
tts?.setSpeechRate(rate * 100f)
|
|
303
160
|
}
|
|
304
161
|
|
|
305
162
|
Function("setPitch") { pitch: Float ->
|
|
306
|
-
|
|
307
|
-
tts?.setPitch(pitch)
|
|
308
|
-
} catch (e: Exception) {
|
|
309
|
-
logError("Failed to set pitch", e)
|
|
310
|
-
}
|
|
163
|
+
tts?.setPitch(pitch)
|
|
311
164
|
}
|
|
312
165
|
|
|
313
166
|
Function("setAudioBoost") { boost: Int ->
|
|
314
|
-
|
|
315
|
-
tts?.setAudioBoost(boost)
|
|
316
|
-
} catch (e: Exception) {
|
|
317
|
-
logError("Failed to set audio boost", e)
|
|
318
|
-
}
|
|
167
|
+
tts?.setAudioBoost(boost)
|
|
319
168
|
}
|
|
320
169
|
|
|
321
170
|
AsyncFunction("shutdownAsync") {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
currentVoice = null
|
|
328
|
-
logInfo("Shutdown complete")
|
|
329
|
-
} catch (e: Exception) {
|
|
330
|
-
logError("Failed to shutdown", e)
|
|
331
|
-
}
|
|
171
|
+
tts?.stop()
|
|
172
|
+
tts?.shutdown()
|
|
173
|
+
tts = null
|
|
174
|
+
isInitialized = false
|
|
175
|
+
currentVoice = null
|
|
332
176
|
}
|
|
333
177
|
}
|
|
334
178
|
|
|
335
|
-
// iTTSEventsCallback implementation
|
|
336
179
|
override fun ttsevents(type: Long, param1: Long, param2: Long, param3: Long, param4: Long) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
logInfo("Speech started (text index: $param1)")
|
|
341
|
-
sendEvent("onSpeechStart", emptyMap<String, Any>())
|
|
342
|
-
}
|
|
343
|
-
acattsandroid.EVENT_AUDIO_END.toLong() -> {
|
|
344
|
-
logInfo("Speech ended (text index: $param1, samples: $param2)")
|
|
345
|
-
sendEvent("onSpeechEnd", emptyMap<String, Any>())
|
|
346
|
-
}
|
|
347
|
-
acattsandroid.EVENT_TEXT_START.toLong() -> {
|
|
348
|
-
logInfo("Text processing started (index: $param1)")
|
|
349
|
-
}
|
|
350
|
-
acattsandroid.EVENT_TEXT_END.toLong() -> {
|
|
351
|
-
logInfo("Text processing ended (index: $param1)")
|
|
352
|
-
}
|
|
353
|
-
acattsandroid.EVENT_WORD_POS.toLong() -> {
|
|
354
|
-
// Word position event - available for future text highlighting
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
} catch (e: Exception) {
|
|
358
|
-
logError("Error in ttsevents callback", e)
|
|
180
|
+
when (type) {
|
|
181
|
+
acattsandroid.EVENT_AUDIO_START.toLong() -> sendEvent("onSpeechStart", emptyMap<String, Any>())
|
|
182
|
+
acattsandroid.EVENT_AUDIO_END.toLong() -> sendEvent("onSpeechEnd", emptyMap<String, Any>())
|
|
359
183
|
}
|
|
360
184
|
}
|
|
361
185
|
|
|
362
|
-
private fun hasStorageAccess(): Boolean {
|
|
363
|
-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
364
|
-
Environment.isExternalStorageManager()
|
|
365
|
-
} else {
|
|
366
|
-
val context = appContext.reactContext ?: return false
|
|
367
|
-
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private fun extractSpeakerName(voiceFileName: String): String {
|
|
372
|
-
// Voice file names like "non_kari_22k_ns.qvcu" -> "Kari"
|
|
373
|
-
// Or voice names like "hq-ref-Norwegian-Kari-22khz" -> "Kari"
|
|
374
|
-
val parts = voiceFileName.split("-", "_")
|
|
375
|
-
return parts.find { it.length > 2 && it[0].isUpperCase() && it.all { c -> c.isLetter() } }
|
|
376
|
-
?: voiceFileName
|
|
377
|
-
}
|
|
378
|
-
|
|
379
186
|
private fun logInfo(msg: String) = Log.i(TAG, msg)
|
|
380
187
|
private fun logWarning(msg: String) = Log.w(TAG, msg)
|
|
381
188
|
private fun logError(msg: String, e: Throwable) = Log.e(TAG, msg, e)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAcapelaTts.types.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTts.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,
|
|
1
|
+
{"version":3,"file":"ExpoAcapelaTts.types.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTts.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC7D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAcapelaTts.types.js","sourceRoot":"","sources":["../src/ExpoAcapelaTts.types.ts"],"names":[],"mappings":"","sourcesContent":["export type AcapelaVoice = {\n
|
|
1
|
+
{"version":3,"file":"ExpoAcapelaTts.types.js","sourceRoot":"","sources":["../src/ExpoAcapelaTts.types.ts"],"names":[],"mappings":"","sourcesContent":["export type AcapelaVoice = {\n name: string;\n locale: string;\n language: string;\n speaker: string;\n gender: 'male' | 'female';\n quality: string;\n age: string;\n frequency: string;\n};\n\nexport type ExpoAcapelaTtsModuleEvents = {\n onSpeechStart: () => void;\n onSpeechEnd: () => void;\n onError: (event: { message: string; code: number }) => void;\n};\n"]}
|
|
@@ -2,12 +2,10 @@ import { NativeModule } from 'expo';
|
|
|
2
2
|
import { AcapelaVoice, ExpoAcapelaTtsModuleEvents } from './ExpoAcapelaTts.types';
|
|
3
3
|
declare class ExpoAcapelaTtsModule extends NativeModule<ExpoAcapelaTtsModuleEvents> {
|
|
4
4
|
isAvailable(): boolean;
|
|
5
|
-
hasStoragePermission(): boolean;
|
|
6
|
-
requestStoragePermissionAsync(): Promise<boolean>;
|
|
7
5
|
addVoicePath(path: string): void;
|
|
8
6
|
setLicense(userId: string, password: string, key: string): void;
|
|
9
7
|
initializeAsync(): Promise<boolean>;
|
|
10
|
-
getVoicesAsync(): Promise<AcapelaVoice[]>;
|
|
8
|
+
getVoicesAsync(paths: string[]): Promise<AcapelaVoice[]>;
|
|
11
9
|
loadVoiceAsync(voiceName: string): Promise<boolean>;
|
|
12
10
|
speakAsync(text: string): Promise<number>;
|
|
13
11
|
stop(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAcapelaTtsModule.android.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.android.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,OAAO,oBAAqB,SAAQ,YAAY,CAAC,0BAA0B,CAAC;IACjF,WAAW,IAAI,OAAO;IACtB,
|
|
1
|
+
{"version":3,"file":"ExpoAcapelaTtsModule.android.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.android.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,OAAO,oBAAqB,SAAQ,YAAY,CAAC,0BAA0B,CAAC;IACjF,WAAW,IAAI,OAAO;IACtB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAChC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAC/D,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IACnC,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IACxD,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IACnD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACzC,IAAI,IAAI,IAAI;IACZ,UAAU,IAAI,OAAO;IACrB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC7B,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAClC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B;;AAED,wBAA2E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAcapelaTtsModule.android.js","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.android.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoAcapelaTtsModule.android.js","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.android.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAuBzD,eAAe,mBAAmB,CAAuB,gBAAgB,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport {\n AcapelaVoice,\n ExpoAcapelaTtsModuleEvents,\n} from './ExpoAcapelaTts.types';\n\ndeclare class ExpoAcapelaTtsModule extends NativeModule<ExpoAcapelaTtsModuleEvents> {\n isAvailable(): boolean;\n addVoicePath(path: string): void;\n setLicense(userId: string, password: string, key: string): void;\n initializeAsync(): Promise<boolean>;\n getVoicesAsync(paths: string[]): Promise<AcapelaVoice[]>;\n loadVoiceAsync(voiceName: string): Promise<boolean>;\n speakAsync(text: string): Promise<number>;\n stop(): void;\n isSpeaking(): boolean;\n setSpeechRate(rate: number): void;\n setPitch(pitch: number): void;\n setAudioBoost(boost: number): void;\n shutdownAsync(): Promise<void>;\n}\n\nexport default requireNativeModule<ExpoAcapelaTtsModule>('ExpoAcapelaTts');\n"]}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
2
|
isAvailable(): boolean;
|
|
3
|
-
hasStoragePermission(): boolean;
|
|
4
|
-
requestStoragePermissionAsync(): Promise<boolean>;
|
|
5
3
|
addVoicePath(_path: string): void;
|
|
6
4
|
setLicense(_userId: string, _password: string, _key: string): void;
|
|
7
5
|
initializeAsync(): Promise<boolean>;
|
|
8
|
-
getVoicesAsync(): Promise<never[]>;
|
|
6
|
+
getVoicesAsync(_paths: string[]): Promise<never[]>;
|
|
9
7
|
loadVoiceAsync(_voiceName: string): Promise<boolean>;
|
|
10
8
|
speakAsync(_text: string): Promise<number>;
|
|
11
9
|
stop(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAcapelaTtsModule.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"ExpoAcapelaTtsModule.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.ts"],"names":[],"mappings":";mBACiB,OAAO;wBAGF,MAAM;wBACN,MAAM,aAAa,MAAM,QAAQ,MAAM;uBAClC,OAAO,CAAC,OAAO,CAAC;2BAGZ,MAAM,EAAE;+BAGJ,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;sBAGlC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;;kBAIlC,OAAO;yBAGA,MAAM;qBACV,MAAM;0BACD,MAAM;;;;;;;AAxB9B,wBA8BE"}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
// No-op stub for non-Android platforms
|
|
2
1
|
export default {
|
|
3
2
|
isAvailable() {
|
|
4
3
|
return false;
|
|
5
4
|
},
|
|
6
|
-
hasStoragePermission() {
|
|
7
|
-
return true;
|
|
8
|
-
},
|
|
9
|
-
async requestStoragePermissionAsync() {
|
|
10
|
-
return true;
|
|
11
|
-
},
|
|
12
5
|
addVoicePath(_path) { },
|
|
13
6
|
setLicense(_userId, _password, _key) { },
|
|
14
7
|
async initializeAsync() {
|
|
15
8
|
return false;
|
|
16
9
|
},
|
|
17
|
-
async getVoicesAsync() {
|
|
10
|
+
async getVoicesAsync(_paths) {
|
|
18
11
|
return [];
|
|
19
12
|
},
|
|
20
13
|
async loadVoiceAsync(_voiceName) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAcapelaTtsModule.js","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"ExpoAcapelaTtsModule.js","sourceRoot":"","sources":["../src/ExpoAcapelaTtsModule.ts"],"names":[],"mappings":"AAAA,eAAe;IACb,WAAW;QACT,OAAO,KAAK,CAAC;IACf,CAAC;IACD,YAAY,CAAC,KAAa,IAAG,CAAC;IAC9B,UAAU,CAAC,OAAe,EAAE,SAAiB,EAAE,IAAY,IAAG,CAAC;IAC/D,KAAK,CAAC,eAAe;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,MAAgB;QACnC,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,UAAkB;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,KAAa;QAC5B,OAAO,CAAC,CAAC,CAAC;IACZ,CAAC;IACD,IAAI,KAAI,CAAC;IACT,UAAU;QACR,OAAO,KAAK,CAAC;IACf,CAAC;IACD,aAAa,CAAC,KAAa,IAAG,CAAC;IAC/B,QAAQ,CAAC,MAAc,IAAG,CAAC;IAC3B,aAAa,CAAC,MAAc,IAAG,CAAC;IAChC,KAAK,CAAC,aAAa,KAAI,CAAC;IACxB,WAAW;QACT,OAAO,EAAE,MAAM,KAAI,CAAC,EAAE,CAAC;IACzB,CAAC;IACD,eAAe,KAAI,CAAC;CACrB,CAAC","sourcesContent":["export default {\n isAvailable(): boolean {\n return false;\n },\n addVoicePath(_path: string) {},\n setLicense(_userId: string, _password: string, _key: string) {},\n async initializeAsync(): Promise<boolean> {\n return false;\n },\n async getVoicesAsync(_paths: string[]) {\n return [];\n },\n async loadVoiceAsync(_voiceName: string): Promise<boolean> {\n return false;\n },\n async speakAsync(_text: string): Promise<number> {\n return -1;\n },\n stop() {},\n isSpeaking(): boolean {\n return false;\n },\n setSpeechRate(_rate: number) {},\n setPitch(_pitch: number) {},\n setAudioBoost(_boost: number) {},\n async shutdownAsync() {},\n addListener() {\n return { remove() {} };\n },\n removeListeners() {},\n};\n"]}
|
package/package.json
CHANGED
|
@@ -7,12 +7,10 @@ import {
|
|
|
7
7
|
|
|
8
8
|
declare class ExpoAcapelaTtsModule extends NativeModule<ExpoAcapelaTtsModuleEvents> {
|
|
9
9
|
isAvailable(): boolean;
|
|
10
|
-
hasStoragePermission(): boolean;
|
|
11
|
-
requestStoragePermissionAsync(): Promise<boolean>;
|
|
12
10
|
addVoicePath(path: string): void;
|
|
13
11
|
setLicense(userId: string, password: string, key: string): void;
|
|
14
12
|
initializeAsync(): Promise<boolean>;
|
|
15
|
-
getVoicesAsync(): Promise<AcapelaVoice[]>;
|
|
13
|
+
getVoicesAsync(paths: string[]): Promise<AcapelaVoice[]>;
|
|
16
14
|
loadVoiceAsync(voiceName: string): Promise<boolean>;
|
|
17
15
|
speakAsync(text: string): Promise<number>;
|
|
18
16
|
stop(): void;
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
// No-op stub for non-Android platforms
|
|
2
1
|
export default {
|
|
3
2
|
isAvailable(): boolean {
|
|
4
3
|
return false;
|
|
5
4
|
},
|
|
6
|
-
hasStoragePermission(): boolean {
|
|
7
|
-
return true;
|
|
8
|
-
},
|
|
9
|
-
async requestStoragePermissionAsync(): Promise<boolean> {
|
|
10
|
-
return true;
|
|
11
|
-
},
|
|
12
5
|
addVoicePath(_path: string) {},
|
|
13
6
|
setLicense(_userId: string, _password: string, _key: string) {},
|
|
14
7
|
async initializeAsync(): Promise<boolean> {
|
|
15
8
|
return false;
|
|
16
9
|
},
|
|
17
|
-
async getVoicesAsync() {
|
|
10
|
+
async getVoicesAsync(_paths: string[]) {
|
|
18
11
|
return [];
|
|
19
12
|
},
|
|
20
13
|
async loadVoiceAsync(_voiceName: string): Promise<boolean> {
|