cactus-react-native 0.2.10 → 0.2.11

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 (38) hide show
  1. package/README.md +52 -776
  2. package/android/src/main/CMakeLists.txt +2 -1
  3. package/android/src/main/java/com/cactus/CactusPackage.java +5 -5
  4. package/android/src/main/java/com/cactus/LlamaContext.java +1 -67
  5. package/android/src/main/jniLibs/arm64-v8a/libcactus.so +0 -0
  6. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8.so +0 -0
  7. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2.so +0 -0
  8. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod.so +0 -0
  9. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod_i8mm.so +0 -0
  10. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_i8mm.so +0 -0
  11. package/android/src/newarch/java/com/cactus/CactusModule.java +0 -2
  12. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Info.plist +0 -0
  13. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  14. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Info.plist +0 -0
  15. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
  16. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
  17. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Info.plist +0 -0
  18. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
  19. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
  20. package/lib/commonjs/index.js.map +1 -1
  21. package/lib/commonjs/projectId.js +2 -1
  22. package/lib/commonjs/projectId.js.map +1 -1
  23. package/lib/commonjs/tts.js +124 -12
  24. package/lib/commonjs/tts.js.map +1 -1
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/projectId.js +2 -1
  27. package/lib/module/projectId.js.map +1 -1
  28. package/lib/module/tts.js +123 -12
  29. package/lib/module/tts.js.map +1 -1
  30. package/lib/typescript/index.d.ts +1 -1
  31. package/lib/typescript/index.d.ts.map +1 -1
  32. package/lib/typescript/projectId.d.ts +1 -1
  33. package/lib/typescript/projectId.d.ts.map +1 -1
  34. package/lib/typescript/tts.d.ts +46 -2
  35. package/lib/typescript/tts.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/index.ts +1 -0
  38. package/src/tts.ts +209 -15
package/src/tts.ts CHANGED
@@ -4,39 +4,233 @@ import {
4
4
  getFormattedAudioCompletion,
5
5
  decodeAudioTokens,
6
6
  releaseVocoder,
7
+ getTTSType,
8
+ isVocoderEnabled,
7
9
  } from './index'
8
- import type { NativeAudioDecodeResult } from './index'
10
+ import type { NativeAudioDecodeResult, NativeTTSType } from './index'
11
+
12
+ export interface TTSOptions {
13
+ voiceId?: string
14
+ speed?: number
15
+ pitch?: number
16
+ volume?: number
17
+ format?: 'wav' | 'mp3' | 'ogg'
18
+ sampleRate?: number
19
+ }
20
+
21
+ export interface TTSGenerationResult extends NativeAudioDecodeResult {
22
+ metadata?: {
23
+ duration?: number
24
+ sampleRate?: number
25
+ channels?: number
26
+ format?: string
27
+ }
28
+ }
29
+
30
+ export interface TTSSpeaker {
31
+ id: string
32
+ name?: string
33
+ language?: string
34
+ gender?: 'male' | 'female' | 'neutral'
35
+ config: Record<string, any>
36
+ }
37
+
38
+ export class TTSError extends Error {
39
+ constructor(
40
+ message: string,
41
+ public code?: string,
42
+ public contextId?: number
43
+ ) {
44
+ super(message)
45
+ this.name = 'TTSError'
46
+ }
47
+ }
9
48
 
10
49
  export class CactusTTS {
11
50
  private context: LlamaContext
51
+ private isInitialized: boolean = false
52
+ private isReleased: boolean = false
53
+ private vocoderPath?: string
54
+ private ttsType?: NativeTTSType
12
55
 
13
- private constructor(context: LlamaContext) {
56
+ private constructor(context: LlamaContext, vocoderPath: string) {
14
57
  this.context = context
58
+ this.vocoderPath = vocoderPath
15
59
  }
16
60
 
17
61
  static async init(
18
62
  context: LlamaContext,
19
63
  vocoderModelPath: string,
20
64
  ): Promise<CactusTTS> {
21
- await initVocoder(context.id, vocoderModelPath)
22
- return new CactusTTS(context)
65
+ if (!context) {
66
+ throw new TTSError('LlamaContext is required', 'INVALID_CONTEXT')
67
+ }
68
+
69
+ if (!vocoderModelPath || typeof vocoderModelPath !== 'string') {
70
+ throw new TTSError('Valid vocoder model path is required', 'INVALID_VOCODER_PATH')
71
+ }
72
+
73
+ try {
74
+ await initVocoder(context.id, vocoderModelPath)
75
+ const instance = new CactusTTS(context, vocoderModelPath)
76
+ instance.isInitialized = true
77
+ instance.ttsType = await getTTSType(context.id)
78
+ return instance
79
+ } catch (error) {
80
+ throw new TTSError(
81
+ `Failed to initialize TTS: ${error instanceof Error ? error.message : 'Unknown error'}`,
82
+ 'INITIALIZATION_FAILED',
83
+ context.id
84
+ )
85
+ }
23
86
  }
24
87
 
88
+
25
89
  async generate(
26
90
  textToSpeak: string,
27
- speakerJsonStr: string,
28
- ): Promise<NativeAudioDecodeResult> {
29
- const { formatted_prompt } = await getFormattedAudioCompletion(
30
- this.context.id,
31
- speakerJsonStr,
32
- textToSpeak,
33
- )
34
- // To-DO: Fix
35
- const tokens = (await this.context.tokenize(formatted_prompt)).tokens
36
- return decodeAudioTokens(this.context.id, tokens)
91
+ speaker: TTSSpeaker | string,
92
+ options?: TTSOptions
93
+ ): Promise<TTSGenerationResult> {
94
+ this.validateState()
95
+ this.validateInputs(textToSpeak, speaker)
96
+
97
+ const speakerConfig = typeof speaker === 'string' ? speaker : JSON.stringify(speaker.config)
98
+ const startTime = Date.now()
99
+
100
+ try {
101
+ const { formatted_prompt } = await getFormattedAudioCompletion(
102
+ this.context.id,
103
+ speakerConfig,
104
+ textToSpeak,
105
+ )
106
+
107
+ if (!formatted_prompt) {
108
+ throw new TTSError('Failed to format audio prompt', 'FORMATTING_FAILED', this.context.id)
109
+ }
110
+
111
+ const tokenizeResult = await this.context.tokenize(formatted_prompt)
112
+ if (!tokenizeResult?.tokens || tokenizeResult.tokens.length === 0) {
113
+ throw new TTSError('Failed to tokenize prompt', 'TOKENIZATION_FAILED', this.context.id)
114
+ }
115
+
116
+ const audioResult = await decodeAudioTokens(this.context.id, tokenizeResult.tokens)
117
+ const endTime = Date.now()
118
+
119
+ return {
120
+ ...audioResult,
121
+ metadata: {
122
+ duration: endTime - startTime,
123
+ sampleRate: options?.sampleRate,
124
+ format: options?.format || 'wav',
125
+ channels: 1, // Assuming mono output
126
+ }
127
+ }
128
+ } catch (error) {
129
+ if (error instanceof TTSError) {
130
+ throw error
131
+ }
132
+ throw new TTSError(
133
+ `Audio generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
134
+ 'GENERATION_FAILED',
135
+ this.context.id
136
+ )
137
+ }
138
+ }
139
+
140
+
141
+ async isVocoderReady(): Promise<boolean> {
142
+ if (!this.isInitialized || this.isReleased) {
143
+ return false
144
+ }
145
+
146
+ try {
147
+ return await isVocoderEnabled(this.context.id)
148
+ } catch {
149
+ return false
150
+ }
151
+ }
152
+
153
+
154
+ getTTSType(): NativeTTSType | undefined {
155
+ return this.ttsType
37
156
  }
38
157
 
158
+
159
+ getContextId(): number {
160
+ return this.context.id
161
+ }
162
+
163
+ getVocoderPath(): string | undefined {
164
+ return this.vocoderPath
165
+ }
166
+
167
+
168
+ isReady(): boolean {
169
+ return this.isInitialized && !this.isReleased
170
+ }
171
+
172
+ static createSpeaker(
173
+ id: string,
174
+ config: Record<string, any>,
175
+ metadata?: {
176
+ name?: string
177
+ language?: string
178
+ gender?: 'male' | 'female' | 'neutral'
179
+ }
180
+ ): TTSSpeaker {
181
+ return {
182
+ id,
183
+ config,
184
+ ...metadata
185
+ }
186
+ }
187
+
188
+
189
+ private validateState(): void {
190
+ if (this.isReleased) {
191
+ throw new TTSError('TTS instance has been released', 'INSTANCE_RELEASED', this.context.id)
192
+ }
193
+
194
+ if (!this.isInitialized) {
195
+ throw new TTSError('TTS instance not initialized', 'NOT_INITIALIZED', this.context.id)
196
+ }
197
+ }
198
+
199
+
200
+ private validateInputs(textToSpeak: string, speaker: TTSSpeaker | string): void {
201
+ if (!textToSpeak || typeof textToSpeak !== 'string' || textToSpeak.trim().length === 0) {
202
+ throw new TTSError('Valid text input is required', 'INVALID_TEXT_INPUT')
203
+ }
204
+
205
+ if (textToSpeak.length > 10000) { // Reasonable limit
206
+ throw new TTSError('Text input too long (max 10000 characters)', 'TEXT_TOO_LONG')
207
+ }
208
+
209
+ if (!speaker) {
210
+ throw new TTSError('Speaker configuration is required', 'INVALID_SPEAKER')
211
+ }
212
+
213
+ if (typeof speaker === 'object' && (!speaker.id || !speaker.config)) {
214
+ throw new TTSError('Speaker must have id and config properties', 'INVALID_SPEAKER_FORMAT')
215
+ }
216
+ }
217
+
218
+
39
219
  async release(): Promise<void> {
40
- return releaseVocoder(this.context.id)
220
+ if (this.isReleased) {
221
+ return // Already released, no-op
222
+ }
223
+
224
+ try {
225
+ await releaseVocoder(this.context.id)
226
+ this.isReleased = true
227
+ this.isInitialized = false
228
+ } catch (error) {
229
+ throw new TTSError(
230
+ `Failed to release TTS resources: ${error instanceof Error ? error.message : 'Unknown error'}`,
231
+ 'RELEASE_FAILED',
232
+ this.context.id
233
+ )
234
+ }
41
235
  }
42
236
  }