@focus8/expo-acapela-tts 0.1.11 → 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 -219
- 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/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,109 +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 voices = voicesList.map { voiceName ->
|
|
99
|
+
logInfo("Found ${voicesList.size} voices in ${paths.joinToString()}")
|
|
100
|
+
voicesList.map { voiceName ->
|
|
202
101
|
val info = tts!!.getVoiceInfo(voiceName) ?: emptyMap()
|
|
203
|
-
val genderValue = info["gender"] ?: "0"
|
|
204
102
|
mapOf(
|
|
205
103
|
"name" to voiceName,
|
|
206
104
|
"locale" to (info["locale"] ?: ""),
|
|
207
105
|
"language" to (info["language"] ?: ""),
|
|
208
|
-
"speaker" to (info["name"]
|
|
209
|
-
"gender" to if (
|
|
106
|
+
"speaker" to (info["name"] ?: ""),
|
|
107
|
+
"gender" to if ((info["gender"] ?: "0") == "1") "female" else "male",
|
|
210
108
|
"quality" to (info["quality"] ?: ""),
|
|
211
109
|
"age" to (info["age"] ?: ""),
|
|
212
110
|
"frequency" to (info["frequency"] ?: "")
|
|
213
111
|
)
|
|
214
112
|
}
|
|
215
|
-
|
|
216
|
-
// Reload the current voice since getVoicesList unloads any loaded voice
|
|
217
|
-
if (currentVoice != null) {
|
|
218
|
-
tts!!.load(currentVoice, "MODE=prep_full")
|
|
219
|
-
logInfo("Reloaded voice after listing: $currentVoice")
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
voices
|
|
223
113
|
} catch (e: Exception) {
|
|
224
114
|
logError("Failed to get voices", e)
|
|
225
115
|
emptyList<Map<String, Any>>()
|
|
@@ -228,12 +118,10 @@ class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
|
228
118
|
|
|
229
119
|
AsyncFunction("loadVoiceAsync") { voiceName: String ->
|
|
230
120
|
if (tts == null) {
|
|
231
|
-
logWarning("loadVoiceAsync: TTS not initialized")
|
|
232
121
|
return@AsyncFunction false
|
|
233
122
|
}
|
|
234
|
-
|
|
235
123
|
try {
|
|
236
|
-
val result = tts!!.load(voiceName, "
|
|
124
|
+
val result = tts!!.load(voiceName, "")
|
|
237
125
|
if (result == 0) {
|
|
238
126
|
currentVoice = voiceName
|
|
239
127
|
logInfo("Voice loaded: $voiceName")
|
|
@@ -250,125 +138,51 @@ class ExpoAcapelaTtsModule : Module(), iTTSEventsCallback {
|
|
|
250
138
|
|
|
251
139
|
AsyncFunction("speakAsync") { text: String ->
|
|
252
140
|
if (tts == null) {
|
|
253
|
-
logWarning("speakAsync: TTS not initialized")
|
|
254
141
|
return@AsyncFunction -1
|
|
255
142
|
}
|
|
256
|
-
|
|
257
143
|
try {
|
|
258
144
|
if (tts!!.isSpeaking) {
|
|
259
145
|
tts!!.stop()
|
|
260
146
|
}
|
|
261
|
-
|
|
262
|
-
if (result < 0) {
|
|
263
|
-
logWarning("speak returned error: $result")
|
|
264
|
-
}
|
|
265
|
-
result
|
|
147
|
+
tts!!.speak(text)
|
|
266
148
|
} catch (e: Exception) {
|
|
267
149
|
logError("Failed to speak", e)
|
|
268
150
|
-1
|
|
269
151
|
}
|
|
270
152
|
}
|
|
271
153
|
|
|
272
|
-
Function("stop") {
|
|
273
|
-
try {
|
|
274
|
-
tts?.stop()
|
|
275
|
-
} catch (e: Exception) {
|
|
276
|
-
logError("Failed to stop", e)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
154
|
+
Function("stop") { tts?.stop() }
|
|
279
155
|
|
|
280
|
-
Function("isSpeaking") {
|
|
281
|
-
try {
|
|
282
|
-
tts?.isSpeaking ?: false
|
|
283
|
-
} catch (e: Exception) {
|
|
284
|
-
false
|
|
285
|
-
}
|
|
286
|
-
}
|
|
156
|
+
Function("isSpeaking") { tts?.isSpeaking ?: false }
|
|
287
157
|
|
|
288
158
|
Function("setSpeechRate") { rate: Float ->
|
|
289
|
-
|
|
290
|
-
// JS sends 0.5-2.0, Acapela expects 30-400 (percentage)
|
|
291
|
-
val acapelaRate = rate * 100f
|
|
292
|
-
tts?.setSpeechRate(acapelaRate)
|
|
293
|
-
} catch (e: Exception) {
|
|
294
|
-
logError("Failed to set speech rate", e)
|
|
295
|
-
}
|
|
159
|
+
tts?.setSpeechRate(rate * 100f)
|
|
296
160
|
}
|
|
297
161
|
|
|
298
162
|
Function("setPitch") { pitch: Float ->
|
|
299
|
-
|
|
300
|
-
tts?.setPitch(pitch)
|
|
301
|
-
} catch (e: Exception) {
|
|
302
|
-
logError("Failed to set pitch", e)
|
|
303
|
-
}
|
|
163
|
+
tts?.setPitch(pitch)
|
|
304
164
|
}
|
|
305
165
|
|
|
306
166
|
Function("setAudioBoost") { boost: Int ->
|
|
307
|
-
|
|
308
|
-
tts?.setAudioBoost(boost)
|
|
309
|
-
} catch (e: Exception) {
|
|
310
|
-
logError("Failed to set audio boost", e)
|
|
311
|
-
}
|
|
167
|
+
tts?.setAudioBoost(boost)
|
|
312
168
|
}
|
|
313
169
|
|
|
314
170
|
AsyncFunction("shutdownAsync") {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
currentVoice = null
|
|
321
|
-
logInfo("Shutdown complete")
|
|
322
|
-
} catch (e: Exception) {
|
|
323
|
-
logError("Failed to shutdown", e)
|
|
324
|
-
}
|
|
171
|
+
tts?.stop()
|
|
172
|
+
tts?.shutdown()
|
|
173
|
+
tts = null
|
|
174
|
+
isInitialized = false
|
|
175
|
+
currentVoice = null
|
|
325
176
|
}
|
|
326
177
|
}
|
|
327
178
|
|
|
328
|
-
// iTTSEventsCallback implementation
|
|
329
179
|
override fun ttsevents(type: Long, param1: Long, param2: Long, param3: Long, param4: Long) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
logInfo("Speech started (text index: $param1)")
|
|
334
|
-
sendEvent("onSpeechStart", emptyMap<String, Any>())
|
|
335
|
-
}
|
|
336
|
-
acattsandroid.EVENT_AUDIO_END.toLong() -> {
|
|
337
|
-
logInfo("Speech ended (text index: $param1, samples: $param2)")
|
|
338
|
-
sendEvent("onSpeechEnd", emptyMap<String, Any>())
|
|
339
|
-
}
|
|
340
|
-
acattsandroid.EVENT_TEXT_START.toLong() -> {
|
|
341
|
-
logInfo("Text processing started (index: $param1)")
|
|
342
|
-
}
|
|
343
|
-
acattsandroid.EVENT_TEXT_END.toLong() -> {
|
|
344
|
-
logInfo("Text processing ended (index: $param1)")
|
|
345
|
-
}
|
|
346
|
-
acattsandroid.EVENT_WORD_POS.toLong() -> {
|
|
347
|
-
// Word position event - available for future text highlighting
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
} catch (e: Exception) {
|
|
351
|
-
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>())
|
|
352
183
|
}
|
|
353
184
|
}
|
|
354
185
|
|
|
355
|
-
private fun hasStorageAccess(): Boolean {
|
|
356
|
-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
357
|
-
Environment.isExternalStorageManager()
|
|
358
|
-
} else {
|
|
359
|
-
val context = appContext.reactContext ?: return false
|
|
360
|
-
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
private fun extractSpeakerName(voiceFileName: String): String {
|
|
365
|
-
// Voice file names like "non_kari_22k_ns.qvcu" -> "Kari"
|
|
366
|
-
// Or voice names like "hq-ref-Norwegian-Kari-22khz" -> "Kari"
|
|
367
|
-
val parts = voiceFileName.split("-", "_")
|
|
368
|
-
return parts.find { it.length > 2 && it[0].isUpperCase() && it.all { c -> c.isLetter() } }
|
|
369
|
-
?: voiceFileName
|
|
370
|
-
}
|
|
371
|
-
|
|
372
186
|
private fun logInfo(msg: String) = Log.i(TAG, msg)
|
|
373
187
|
private fun logWarning(msg: String) = Log.w(TAG, msg)
|
|
374
188
|
private fun logError(msg: String, e: Throwable) = Log.e(TAG, msg, e)
|
|
@@ -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> {
|