@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.
@@ -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("/sdcard/voices", "/system/media/voices", "/data/media/voices")
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
- if (!nativeLibLoaded) {
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: userId=0x${java.lang.Long.toHexString(licenseUserId!!)} password=0x${java.lang.Long.toHexString(licensePassword!!)}")
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
- // Use BuildConfig values as fallback if setLicense() was not called from JS
135
- val uid = licenseUserId ?: BuildConfig.ACAPELA_USER_ID
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("Cannot initialize: no license configured (neither via setLicense() nor BuildConfig)")
69
+ logWarning("No license configured")
141
70
  return@AsyncFunction false
142
71
  }
143
-
144
72
  try {
145
- val context = appContext.reactContext ?: run {
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("Failed to initialize", e)
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 voiceDirPaths = voicePaths.toTypedArray()
181
- logInfo("getVoicesAsync: scanning paths: ${voiceDirPaths.joinToString()}")
182
- for (path in voiceDirPaths) {
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
- for (v in voicesList) {
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 internalName,
216
- "gender" to if (genderValue == "1") "female" else "male",
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, "MODE=prep_full")
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
- val result = tts!!.speak(text)
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
- try {
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
- try {
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
- try {
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
- try {
323
- tts?.stop()
324
- tts?.shutdown()
325
- tts = null
326
- isInitialized = false
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
- try {
338
- when (type) {
339
- acattsandroid.EVENT_AUDIO_START.toLong() -> {
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,5 +1,4 @@
1
1
  export type AcapelaVoice = {
2
- id: string;
3
2
  name: string;
4
3
  locale: string;
5
4
  language: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAcapelaTts.types.d.ts","sourceRoot":"","sources":["../src/ExpoAcapelaTts.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,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
+ {"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 id: string;\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"]}
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,oBAAoB,IAAI,OAAO;IAC/B,6BAA6B,IAAI,OAAO,CAAC,OAAO,CAAC;IACjD,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,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IACzC,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
+ {"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;AAyBzD,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 hasStoragePermission(): boolean;\n requestStoragePermissionAsync(): Promise<boolean>;\n addVoicePath(path: string): void;\n setLicense(userId: string, password: string, key: string): void;\n initializeAsync(): Promise<boolean>;\n getVoicesAsync(): 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
+ {"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":";mBAEiB,OAAO;4BAGE,OAAO;qCAGQ,OAAO,CAAC,OAAO,CAAC;wBAGnC,MAAM;wBACN,MAAM,aAAa,MAAM,QAAQ,MAAM;uBAClC,OAAO,CAAC,OAAO,CAAC;;+BAMR,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;sBAGlC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;;kBAIlC,OAAO;yBAGA,MAAM;qBACV,MAAM;0BACD,MAAM;;;;;;;AA9B9B,wBAoCE"}
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,uCAAuC;AACvC,eAAe;IACb,WAAW;QACT,OAAO,KAAK,CAAC;IACf,CAAC;IACD,oBAAoB;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,KAAK,CAAC,6BAA6B;QACjC,OAAO,IAAI,CAAC;IACd,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;QAClB,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":["// No-op stub for non-Android platforms\nexport default {\n isAvailable(): boolean {\n return false;\n },\n hasStoragePermission(): boolean {\n return true;\n },\n async requestStoragePermissionAsync(): Promise<boolean> {\n return true;\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() {\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"]}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@focus8/expo-acapela-tts",
3
- "version": "0.1.12",
3
+ "version": "0.2.0",
4
4
  "description": "Acapela TTS integration for Expo on Android",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1,5 +1,4 @@
1
1
  export type AcapelaVoice = {
2
- id: string;
3
2
  name: string;
4
3
  locale: string;
5
4
  language: string;
@@ -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> {