react-native-davoice-tts 1.0.362 → 1.0.364

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/README.md +19 -0
  2. package/TTSRNBridge.podspec +1 -1
  3. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  4. package/android/.gradle/buildOutputCleanup/cache.properties +2 -2
  5. package/android/libs/com/davoice/tts/1.0.0/tts-1.0.0.aar +0 -0
  6. package/android/libs/com/davoice/tts/1.0.0/tts-1.0.0.aar.md5 +1 -1
  7. package/android/libs/com/davoice/tts/1.0.0/tts-1.0.0.aar.sha1 +1 -1
  8. package/android/src/main/java/com/davoice/stt/rn/STTModule.kt +3 -1
  9. package/ios/SpeechBridge/SpeechBridge.m +3 -2
  10. package/ios/TTSRNBridge/DaVoiceTTSBridge.m +144 -3
  11. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64/DavoiceTTS.framework/DavoiceTTS +0 -0
  12. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/arm64-apple-ios.abi.json +403 -368
  13. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/DavoiceTTS +0 -0
  14. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.abi.json +403 -368
  15. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.abi.json +403 -368
  16. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/_CodeSignature/CodeDirectory +0 -0
  17. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/_CodeSignature/CodeRequirements-1 +0 -0
  18. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/_CodeSignature/CodeResources +12 -12
  19. package/package.json +1 -1
  20. package/speech/index.ts +22 -7
package/README.md CHANGED
@@ -138,6 +138,7 @@ The unified API is intended for real voice flows where STT and TTS are part of t
138
138
  Common methods:
139
139
 
140
140
  - `initAll({ locale, model, timeoutMs?, onboardingJsonPath? })`
141
+ - `initTTS({ model })` — initialize only TTS; does not create or start STT
141
142
  - `destroyAll()`
142
143
  - `start(locale, options?)`
143
144
  - `stop()`
@@ -169,6 +170,24 @@ Unified events:
169
170
  - `onNewSpeechWAV`
170
171
  Android-only remote STT flow event.
171
172
 
173
+ ### TTS-Only Through the Speech API
174
+
175
+ Use `initTTS()` when the application needs synthesis/playback without speech
176
+ recognition:
177
+
178
+ ```ts
179
+ import Speech from 'react-native-davoice-tts/speech';
180
+
181
+ await Speech.setLicense('YOUR_LICENSE_KEY');
182
+ await Speech.initTTS({ model });
183
+ await Speech.speak('Hello world');
184
+ ```
185
+
186
+ On both iOS and Android this path initializes only the standalone TTS engine.
187
+ It does not construct or start the STT engine, request microphone permissions,
188
+ or begin microphone capture. `speak()`, `stopSpeaking()`, `playWav()`, and
189
+ `playBuffer()` remain on the standalone TTS bridge until `initAll()` is used.
190
+
172
191
  ### iOS Permission Helpers
173
192
 
174
193
  The unified API also exposes iOS permission helpers:
@@ -2,7 +2,7 @@ require 'json'
2
2
 
3
3
  Pod::Spec.new do |s|
4
4
  s.name = "TTSRNBridge"
5
- s.version = "1.0.288" # Update to your package version
5
+ s.version = "1.0.292" # Update to your package version
6
6
  s.summary = "TTS for React Native."
7
7
  s.description = <<-DESC
8
8
  A React Native module for tts .
@@ -1,2 +1,2 @@
1
- #Wed Jul 29 17:53:46 IDT 2026
2
- gradle.version=9.2.0
1
+ #Mon Aug 17 12:10:54 IDT 2026
2
+ gradle.version=8.9
@@ -1 +1 @@
1
- df8b1b6ae1a732a2ee67656b7879677d tts-1.0.0.aar
1
+ 20bac695ad8c44a85e5ca9ece89a26bc tts-1.0.0.aar
@@ -1 +1 @@
1
- 00a559125ed3cc276def548fdacf9661ddeebefb tts-1.0.0.aar
1
+ 0d733323fe3b29e2ac003a9e677fac17fee18c31 tts-1.0.0.aar
@@ -59,7 +59,9 @@ class STTModule(private val rc: ReactApplicationContext)
59
59
  promise.reject("invalid_args", "Missing licenseKey")
60
60
  return
61
61
  }
62
- promise.resolve(ensure().setLicenseKey(licenseKey))
62
+ // Licensing is process-global. Do not construct STT merely to store the
63
+ // key: Speech.initTTS() must remain a genuinely TTS-only path.
64
+ promise.resolve(LicenseManager.setLicenseKey(licenseKey))
63
65
  } catch (t: Throwable) {
64
66
  promise.reject("LicenseError", t.message, t)
65
67
  }
@@ -403,8 +403,9 @@ RCT_EXPORT_METHOD(setLicense:(NSString *)licenseKey
403
403
  dispatch_async(dispatch_get_main_queue(), ^{
404
404
  self.lastLicenseKey = licenseKey;
405
405
  BOOL ttsOk = [DaVoiceTTS activateLicenseWithLicenseKey:licenseKey];
406
- [self ensureSTT];
407
- BOOL sttOk = [self.stt setLicenseWithLicenseKey:licenseKey];
406
+ // Keep licensing side-effect free. initAll()/the explicit STT APIs create
407
+ // STT through ensureSTT(), which reapplies lastLicenseKey at that point.
408
+ BOOL sttOk = self.stt ? [self.stt setLicenseWithLicenseKey:licenseKey] : YES;
408
409
  resolve(@(ttsOk && sttOk));
409
410
  });
410
411
  }
@@ -2,6 +2,62 @@
2
2
  #import "DaVoiceTTSBridge.h"
3
3
  #import <React/RCTLog.h>
4
4
  #import <React/RCTConvert.h>
5
+ #import <AVFAudio/AVFAudio.h>
6
+
7
+ static NSData *DTTSBase64Decode(NSString *base64) {
8
+ if (!base64 || (id)base64 == [NSNull null]) return nil;
9
+ return [[NSData alloc] initWithBase64EncodedString:base64 options:0];
10
+ }
11
+
12
+ static AVAudioPCMBuffer *DTTSMakeMonoF32Buffer(NSData *raw,
13
+ double sampleRate,
14
+ NSString *format,
15
+ NSInteger channels,
16
+ BOOL interleaved)
17
+ {
18
+ if (!raw || raw.length == 0 || sampleRate <= 0 || channels < 1) return nil;
19
+
20
+ AVAudioFormat *outputFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:sampleRate
21
+ channels:1];
22
+ NSString *normalizedFormat = format.lowercaseString;
23
+ NSUInteger bytesPerSample = [normalizedFormat isEqualToString:@"i16"] ? 2 :
24
+ ([normalizedFormat isEqualToString:@"f32"] ? 4 : 0);
25
+ if (bytesPerSample == 0 || raw.length % (bytesPerSample * channels) != 0) return nil;
26
+
27
+ NSUInteger frameCount = raw.length / (bytesPerSample * channels);
28
+ AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat
29
+ frameCapacity:(AVAudioFrameCount)frameCount];
30
+ buffer.frameLength = (AVAudioFrameCount)frameCount;
31
+ float *output = buffer.floatChannelData[0];
32
+
33
+ if ([normalizedFormat isEqualToString:@"i16"]) {
34
+ const int16_t *input = (const int16_t *)raw.bytes;
35
+ for (NSUInteger frame = 0; frame < frameCount; ++frame) {
36
+ int64_t sum = 0;
37
+ for (NSInteger channel = 0; channel < channels; ++channel) {
38
+ NSUInteger index = interleaved || channels == 1
39
+ ? frame * channels + channel
40
+ : channel * frameCount + frame;
41
+ sum += input[index];
42
+ }
43
+ output[frame] = (float)(sum / (double)channels) / 32768.0f;
44
+ }
45
+ } else {
46
+ const float *input = (const float *)raw.bytes;
47
+ for (NSUInteger frame = 0; frame < frameCount; ++frame) {
48
+ double sum = 0;
49
+ for (NSInteger channel = 0; channel < channels; ++channel) {
50
+ NSUInteger index = interleaved || channels == 1
51
+ ? frame * channels + channel
52
+ : channel * frameCount + frame;
53
+ sum += input[index];
54
+ }
55
+ output[frame] = (float)(sum / channels);
56
+ }
57
+ }
58
+
59
+ return buffer;
60
+ }
5
61
 
6
62
  @interface DaVoiceTTSBridge()
7
63
  @property (nonatomic, strong, nullable) DaVoiceTTS *ttsInstance;
@@ -56,6 +112,33 @@ RCT_EXPORT_MODULE();
56
112
  return self;
57
113
  }
58
114
 
115
+ - (NSURL *)resolveLocalURLFromPathOrURL:(NSString *)pathOrURL
116
+ {
117
+ if (!pathOrURL || (id)pathOrURL == [NSNull null] || pathOrURL.length == 0) return nil;
118
+
119
+ if ([pathOrURL hasPrefix:@"http://"] || [pathOrURL hasPrefix:@"https://"]) {
120
+ NSURL *remoteURL = [NSURL URLWithString:pathOrURL];
121
+ NSData *data = remoteURL ? [NSData dataWithContentsOfURL:remoteURL] : nil;
122
+ if (!data) return nil;
123
+ NSString *extension = remoteURL.pathExtension.length ? remoteURL.pathExtension : @"bin";
124
+ NSString *name = [NSString stringWithFormat:@"rn_asset_%f.%@",
125
+ [[NSDate date] timeIntervalSince1970], extension];
126
+ NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:name];
127
+ return [data writeToFile:path atomically:YES] ? [NSURL fileURLWithPath:path] : nil;
128
+ }
129
+
130
+ if ([pathOrURL hasPrefix:@"file://"]) return [NSURL URLWithString:pathOrURL];
131
+
132
+ if ([pathOrURL hasPrefix:@"asset:/"]) {
133
+ NSString *assetName = [pathOrURL substringFromIndex:@"asset:/".length];
134
+ NSString *bundlePath = [[NSBundle mainBundle] pathForResource:assetName.stringByDeletingPathExtension
135
+ ofType:assetName.pathExtension];
136
+ return bundlePath ? [NSURL fileURLWithPath:bundlePath] : nil;
137
+ }
138
+
139
+ return [NSURL fileURLWithPath:pathOrURL];
140
+ }
141
+
59
142
  RCT_EXPORT_METHOD(setLicense:(NSString *)licenseKey
60
143
  resolver:(RCTPromiseResolveBlock)resolve
61
144
  rejecter:(RCTPromiseRejectBlock)reject)
@@ -99,9 +182,11 @@ RCT_EXPORT_METHOD(initTTS:(NSDictionary *)configDict
99
182
  return;
100
183
  }
101
184
 
102
- NSURL *modelURL = [modelPath hasPrefix:@"file://"]
103
- ? [NSURL URLWithString:modelPath]
104
- : [NSURL fileURLWithPath:modelPath];
185
+ NSURL *modelURL = [self resolveLocalURLFromPathOrURL:modelPath];
186
+ if (!modelURL) {
187
+ reject(@"bad_model", [NSString stringWithFormat:@"Could not resolve model path: %@", modelPath], nil);
188
+ return;
189
+ }
105
190
 
106
191
  dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
107
192
  NSError *error = nil;
@@ -179,6 +264,62 @@ RCT_EXPORT_METHOD(initTTS:(NSDictionary *)configDict
179
264
  });
180
265
  }
181
266
 
267
+ RCT_EXPORT_METHOD(playWav:(NSString *)pathOrURL
268
+ markAsLast:(nonnull NSNumber *)markAsLast
269
+ resolver:(RCTPromiseResolveBlock)resolve
270
+ rejecter:(RCTPromiseRejectBlock)reject)
271
+ {
272
+ if (!self.ttsInstance) {
273
+ reject(@"no_instance", @"TTS not initialized", nil);
274
+ return;
275
+ }
276
+
277
+ NSURL *fileURL = [self resolveLocalURLFromPathOrURL:pathOrURL];
278
+ if (!fileURL || ![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) {
279
+ reject(@"file_missing", [NSString stringWithFormat:@"File missing: %@", fileURL.path ?: pathOrURL], nil);
280
+ return;
281
+ }
282
+
283
+ [self.ttsInstance playWav:fileURL markAsLastUtterance:markAsLast.boolValue];
284
+ resolve(@"queued");
285
+ }
286
+
287
+ RCT_EXPORT_METHOD(playBuffer:(NSDictionary *)description
288
+ resolver:(RCTPromiseResolveBlock)resolve
289
+ rejecter:(RCTPromiseRejectBlock)reject)
290
+ {
291
+ if (!self.ttsInstance) {
292
+ reject(@"no_instance", @"TTS not initialized", nil);
293
+ return;
294
+ }
295
+
296
+ NSString *base64 = [RCTConvert NSString:description[@"base64"]];
297
+ NSNumber *sampleRate = [RCTConvert NSNumber:description[@"sampleRate"]];
298
+ NSString *format = [RCTConvert NSString:description[@"format"]];
299
+ NSNumber *channels = description[@"channels"] ? [RCTConvert NSNumber:description[@"channels"]] : @(1);
300
+ NSNumber *interleaved = description[@"interleaved"] ? [RCTConvert NSNumber:description[@"interleaved"]] : @(YES);
301
+ NSNumber *markAsLast = description[@"markAsLast"] ? [RCTConvert NSNumber:description[@"markAsLast"]] : @(YES);
302
+
303
+ if (!base64 || !sampleRate || !format) {
304
+ reject(@"invalid_args", @"Missing one of base64/sampleRate/format", nil);
305
+ return;
306
+ }
307
+
308
+ NSData *raw = DTTSBase64Decode(base64);
309
+ AVAudioPCMBuffer *buffer = DTTSMakeMonoF32Buffer(raw,
310
+ sampleRate.doubleValue,
311
+ format,
312
+ channels.integerValue,
313
+ interleaved.boolValue);
314
+ if (!buffer) {
315
+ reject(@"bad_buffer", @"Unsupported PCM layout, invalid base64, or empty data", nil);
316
+ return;
317
+ }
318
+
319
+ [self.ttsInstance playBuffer:buffer markAsLastUtterance:markAsLast.boolValue];
320
+ resolve(@"queued");
321
+ }
322
+
182
323
  RCT_EXPORT_METHOD(destroy:(RCTPromiseResolveBlock)resolve
183
324
  rejecter:(RCTPromiseRejectBlock)reject)
184
325
  {