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.
- package/README.md +52 -776
- package/android/src/main/CMakeLists.txt +2 -1
- package/android/src/main/java/com/cactus/CactusPackage.java +5 -5
- package/android/src/main/java/com/cactus/LlamaContext.java +1 -67
- package/android/src/main/jniLibs/arm64-v8a/libcactus.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libcactus_v8.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod_i8mm.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_i8mm.so +0 -0
- package/android/src/newarch/java/com/cactus/CactusModule.java +0 -2
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Info.plist +0 -0
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
- package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Info.plist +0 -0
- package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
- package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
- package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Info.plist +0 -0
- package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
- package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/projectId.js +2 -1
- package/lib/commonjs/projectId.js.map +1 -1
- package/lib/commonjs/tts.js +124 -12
- package/lib/commonjs/tts.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/projectId.js +2 -1
- package/lib/module/projectId.js.map +1 -1
- package/lib/module/tts.js +123 -12
- package/lib/module/tts.js.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/projectId.d.ts +1 -1
- package/lib/typescript/projectId.d.ts.map +1 -1
- package/lib/typescript/tts.d.ts +46 -2
- package/lib/typescript/tts.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- 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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
}
|