@elizaos/capacitor-talkmode 2.0.0-beta.1 → 2.0.3-beta.3
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/LICENSE +21 -0
- package/README.md +137 -0
- package/android/build.gradle +16 -2
- package/android/src/main/java/ai/eliza/plugins/talkmode/TalkModePlugin.kt +983 -65
- package/dist/esm/definitions.d.ts +149 -3
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/web.d.ts +6 -1
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +34 -5
- package/dist/esm/web.test.d.ts +2 -0
- package/dist/esm/web.test.d.ts.map +1 -0
- package/dist/esm/web.test.js +137 -0
- package/dist/plugin.cjs.js +34 -5
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +34 -5
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/TalkModePlugin/TalkModePlugin.swift +266 -16
- package/package.json +14 -11
package/dist/plugin.js
CHANGED
|
@@ -50,8 +50,13 @@ var capacitorTalkMode = (function (exports, core) {
|
|
|
50
50
|
this.recognition.interimResults = true;
|
|
51
51
|
this.recognition.onresult = (event) => {
|
|
52
52
|
const result = event.results[event.results.length - 1];
|
|
53
|
-
const
|
|
53
|
+
const first = result?.[0];
|
|
54
|
+
if (!first || typeof first.transcript !== "string")
|
|
55
|
+
return;
|
|
56
|
+
const transcript = first.transcript;
|
|
54
57
|
const isFinal = result.isFinal;
|
|
58
|
+
if (!transcript.trim())
|
|
59
|
+
return;
|
|
55
60
|
this.notifyListeners("transcript", { transcript, isFinal });
|
|
56
61
|
if (isFinal && transcript.trim()) ;
|
|
57
62
|
};
|
|
@@ -126,7 +131,9 @@ var capacitorTalkMode = (function (exports, core) {
|
|
|
126
131
|
// numbers in the wrong language (e.g., Chinese on a Chinese-locale system).
|
|
127
132
|
utterance.lang = options.directive?.language || "en-US";
|
|
128
133
|
// Apply directive settings if available
|
|
129
|
-
if (options.directive?.speed
|
|
134
|
+
if (typeof options.directive?.speed === "number" &&
|
|
135
|
+
Number.isFinite(options.directive.speed) &&
|
|
136
|
+
options.directive.speed > 0) {
|
|
130
137
|
utterance.rate = options.directive.speed;
|
|
131
138
|
}
|
|
132
139
|
utterance.onend = () => {
|
|
@@ -160,14 +167,32 @@ var capacitorTalkMode = (function (exports, core) {
|
|
|
160
167
|
async isSpeaking() {
|
|
161
168
|
return { speaking: this.synthesis?.speaking ?? false };
|
|
162
169
|
}
|
|
170
|
+
async startAudioFrames(_options) {
|
|
171
|
+
// Raw PCM frame capture is a native-only diarization path; on web the Web
|
|
172
|
+
// Speech API gives transcripts only, with no raw-PCM hook.
|
|
173
|
+
return {
|
|
174
|
+
started: false,
|
|
175
|
+
error: "audioFrame capture is not supported on web",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async stopAudioFrames() {
|
|
179
|
+
// no-op on web
|
|
180
|
+
}
|
|
181
|
+
async isCapturingAudioFrames() {
|
|
182
|
+
return { capturing: false };
|
|
183
|
+
}
|
|
163
184
|
async checkPermissions() {
|
|
164
185
|
// Check microphone permission
|
|
165
186
|
let microphone = "prompt";
|
|
166
187
|
try {
|
|
167
|
-
const result = await navigator.permissions
|
|
188
|
+
const result = await navigator.permissions?.query?.({
|
|
168
189
|
name: "microphone",
|
|
169
190
|
});
|
|
170
|
-
|
|
191
|
+
if (result?.state === "granted" ||
|
|
192
|
+
result?.state === "denied" ||
|
|
193
|
+
result?.state === "prompt") {
|
|
194
|
+
microphone = result.state;
|
|
195
|
+
}
|
|
171
196
|
}
|
|
172
197
|
catch {
|
|
173
198
|
// Permissions API may not support microphone query
|
|
@@ -181,7 +206,11 @@ var capacitorTalkMode = (function (exports, core) {
|
|
|
181
206
|
async requestPermissions() {
|
|
182
207
|
// Request microphone permission by attempting to get user media
|
|
183
208
|
try {
|
|
184
|
-
const stream = await navigator.mediaDevices
|
|
209
|
+
const stream = await navigator.mediaDevices?.getUserMedia?.({
|
|
210
|
+
audio: true,
|
|
211
|
+
});
|
|
212
|
+
if (!stream)
|
|
213
|
+
throw new Error("mediaDevices.getUserMedia unavailable");
|
|
185
214
|
stream.getTracks().forEach((track) => {
|
|
186
215
|
track.stop();
|
|
187
216
|
});
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.TalkModeWeb());\nexport const TalkMode = registerPlugin(\"TalkMode\", {\n web: loadWeb,\n});\n","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web implementation of TalkMode plugin\n *\n * Uses Web Speech API for TTS with limited functionality compared to native.\n * ElevenLabs streaming is not supported on web due to CORS limitations.\n */\nexport class TalkModeWeb extends WebPlugin {\n constructor() {\n super();\n this.config = {};\n this.state = \"idle\";\n this.statusText = \"Off\";\n this.synthesis = null;\n this.currentUtterance = null;\n this.recognition = null;\n this.enabled = false;\n if (typeof window !== \"undefined\" && window.speechSynthesis) {\n this.synthesis = window.speechSynthesis;\n }\n }\n async start(options) {\n if (options?.config) {\n this.config = { ...this.config, ...options.config };\n }\n // Check for Web Speech API support\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n if (!SpeechRecognitionAPI) {\n return {\n started: false,\n error: \"Speech recognition not supported on this browser\",\n };\n }\n if (!this.synthesis) {\n console.warn(\"[TalkMode] Speech synthesis not available on web\");\n }\n this.enabled = true;\n this.setState(\"listening\", \"Listening\");\n // Initialize speech recognition\n this.recognition = new SpeechRecognitionAPI();\n this.recognition.continuous = true;\n this.recognition.interimResults = true;\n this.recognition.onresult = (event) => {\n const result = event.results[event.results.length - 1];\n const transcript = result[0].transcript;\n const isFinal = result.isFinal;\n this.notifyListeners(\"transcript\", { transcript, isFinal });\n if (isFinal && transcript.trim()) {\n // Note: Full talk mode flow would need Gateway plugin integration\n // For web, we just emit the transcript\n }\n };\n this.recognition.onerror = (event) => {\n this.notifyListeners(\"error\", {\n code: event.error,\n message: event.message || event.error,\n recoverable: event.error !== \"not-allowed\",\n });\n };\n this.recognition.onend = () => {\n if (this.enabled && this.state === \"listening\") {\n // Restart recognition if still enabled\n try {\n this.recognition?.start();\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!msg.includes(\"already started\")) {\n console.warn(\"[TalkMode] Failed to restart recognition:\", msg);\n }\n }\n }\n };\n try {\n this.recognition.start();\n return { started: true };\n }\n catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to start\";\n return { started: false, error: message };\n }\n }\n async stop() {\n this.enabled = false;\n this.recognition?.stop();\n this.recognition = null;\n this.synthesis?.cancel();\n this.currentUtterance = null;\n this.setState(\"idle\", \"Off\");\n }\n async isEnabled() {\n return { enabled: this.enabled };\n }\n async getState() {\n return { state: this.state, statusText: this.statusText };\n }\n async updateConfig(options) {\n this.config = { ...this.config, ...options.config };\n }\n async speak(options) {\n if (!this.synthesis) {\n return {\n completed: false,\n interrupted: false,\n usedSystemTts: false,\n error: \"Speech synthesis not available\",\n };\n }\n // Web can only use system TTS (no ElevenLabs due to CORS)\n const text = options.text.trim();\n if (!text) {\n return { completed: true, interrupted: false, usedSystemTts: true };\n }\n this.setState(\"speaking\", \"Speaking\");\n this.notifyListeners(\"speaking\", { text, isSystemTts: true });\n return new Promise((resolve) => {\n const utterance = new SpeechSynthesisUtterance(text);\n this.currentUtterance = utterance;\n // Always set language — fallback to en-US if directive doesn't specify.\n // Without this, the browser uses the system locale, which may read\n // numbers in the wrong language (e.g., Chinese on a Chinese-locale system).\n utterance.lang = options.directive?.language || \"en-US\";\n // Apply directive settings if available\n if (options.directive?.speed) {\n utterance.rate = options.directive.speed;\n }\n utterance.onend = () => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: true });\n this.setState(\"listening\", \"Listening\");\n resolve({ completed: true, interrupted: false, usedSystemTts: true });\n };\n utterance.onerror = (event) => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: false });\n this.setState(\"idle\", \"Speech error\");\n resolve({\n completed: false,\n interrupted: event.error === \"interrupted\",\n usedSystemTts: true,\n error: event.error,\n });\n };\n this.synthesis?.speak(utterance);\n });\n }\n async stopSpeaking() {\n if (this.synthesis && this.currentUtterance) {\n this.synthesis.cancel();\n this.currentUtterance = null;\n return { interruptedAt: undefined };\n }\n return {};\n }\n async isSpeaking() {\n return { speaking: this.synthesis?.speaking ?? false };\n }\n async checkPermissions() {\n // Check microphone permission\n let microphone = \"prompt\";\n try {\n const result = await navigator.permissions.query({\n name: \"microphone\",\n });\n microphone = result.state;\n }\n catch {\n // Permissions API may not support microphone query\n }\n // Check if speech recognition is supported\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n const speechRecognition = SpeechRecognitionAPI ? \"prompt\" : \"not_supported\";\n return { microphone, speechRecognition };\n }\n async requestPermissions() {\n // Request microphone permission by attempting to get user media\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach((track) => {\n track.stop();\n });\n }\n catch {\n // Permission denied or error\n }\n return this.checkPermissions();\n }\n setState(state, statusText) {\n const previousState = this.state;\n this.state = state;\n this.statusText = statusText;\n this.notifyListeners(\"stateChange\", {\n state,\n previousState,\n statusText,\n usingSystemTts: true,\n });\n }\n}\n"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.TalkModeWeb());\nexport const TalkMode = registerPlugin(\"TalkMode\", {\n web: loadWeb,\n});\n","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web implementation of TalkMode plugin\n *\n * Uses Web Speech API for TTS with limited functionality compared to native.\n * ElevenLabs streaming is not supported on web due to CORS limitations.\n */\nexport class TalkModeWeb extends WebPlugin {\n constructor() {\n super();\n this.config = {};\n this.state = \"idle\";\n this.statusText = \"Off\";\n this.synthesis = null;\n this.currentUtterance = null;\n this.recognition = null;\n this.enabled = false;\n if (typeof window !== \"undefined\" && window.speechSynthesis) {\n this.synthesis = window.speechSynthesis;\n }\n }\n async start(options) {\n if (options?.config) {\n this.config = { ...this.config, ...options.config };\n }\n // Check for Web Speech API support\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n if (!SpeechRecognitionAPI) {\n return {\n started: false,\n error: \"Speech recognition not supported on this browser\",\n };\n }\n if (!this.synthesis) {\n console.warn(\"[TalkMode] Speech synthesis not available on web\");\n }\n this.enabled = true;\n this.setState(\"listening\", \"Listening\");\n // Initialize speech recognition\n this.recognition = new SpeechRecognitionAPI();\n this.recognition.continuous = true;\n this.recognition.interimResults = true;\n this.recognition.onresult = (event) => {\n const result = event.results[event.results.length - 1];\n const first = result?.[0];\n if (!first || typeof first.transcript !== \"string\")\n return;\n const transcript = first.transcript;\n const isFinal = result.isFinal;\n if (!transcript.trim())\n return;\n this.notifyListeners(\"transcript\", { transcript, isFinal });\n if (isFinal && transcript.trim()) {\n // Note: Full talk mode flow would need Gateway plugin integration\n // For web, we just emit the transcript\n }\n };\n this.recognition.onerror = (event) => {\n this.notifyListeners(\"error\", {\n code: event.error,\n message: event.message || event.error,\n recoverable: event.error !== \"not-allowed\",\n });\n };\n this.recognition.onend = () => {\n if (this.enabled && this.state === \"listening\") {\n // Restart recognition if still enabled\n try {\n this.recognition?.start();\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!msg.includes(\"already started\")) {\n console.warn(\"[TalkMode] Failed to restart recognition:\", msg);\n }\n }\n }\n };\n try {\n this.recognition.start();\n return { started: true };\n }\n catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to start\";\n return { started: false, error: message };\n }\n }\n async stop() {\n this.enabled = false;\n this.recognition?.stop();\n this.recognition = null;\n this.synthesis?.cancel();\n this.currentUtterance = null;\n this.setState(\"idle\", \"Off\");\n }\n async isEnabled() {\n return { enabled: this.enabled };\n }\n async getState() {\n return { state: this.state, statusText: this.statusText };\n }\n async updateConfig(options) {\n this.config = { ...this.config, ...options.config };\n }\n async speak(options) {\n if (!this.synthesis) {\n return {\n completed: false,\n interrupted: false,\n usedSystemTts: false,\n error: \"Speech synthesis not available\",\n };\n }\n // Web can only use system TTS (no ElevenLabs due to CORS)\n const text = options.text.trim();\n if (!text) {\n return { completed: true, interrupted: false, usedSystemTts: true };\n }\n this.setState(\"speaking\", \"Speaking\");\n this.notifyListeners(\"speaking\", { text, isSystemTts: true });\n return new Promise((resolve) => {\n const utterance = new SpeechSynthesisUtterance(text);\n this.currentUtterance = utterance;\n // Always set language — fallback to en-US if directive doesn't specify.\n // Without this, the browser uses the system locale, which may read\n // numbers in the wrong language (e.g., Chinese on a Chinese-locale system).\n utterance.lang = options.directive?.language || \"en-US\";\n // Apply directive settings if available\n if (typeof options.directive?.speed === \"number\" &&\n Number.isFinite(options.directive.speed) &&\n options.directive.speed > 0) {\n utterance.rate = options.directive.speed;\n }\n utterance.onend = () => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: true });\n this.setState(\"listening\", \"Listening\");\n resolve({ completed: true, interrupted: false, usedSystemTts: true });\n };\n utterance.onerror = (event) => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: false });\n this.setState(\"idle\", \"Speech error\");\n resolve({\n completed: false,\n interrupted: event.error === \"interrupted\",\n usedSystemTts: true,\n error: event.error,\n });\n };\n this.synthesis?.speak(utterance);\n });\n }\n async stopSpeaking() {\n if (this.synthesis && this.currentUtterance) {\n this.synthesis.cancel();\n this.currentUtterance = null;\n return { interruptedAt: undefined };\n }\n return {};\n }\n async isSpeaking() {\n return { speaking: this.synthesis?.speaking ?? false };\n }\n async startAudioFrames(_options) {\n // Raw PCM frame capture is a native-only diarization path; on web the Web\n // Speech API gives transcripts only, with no raw-PCM hook.\n return {\n started: false,\n error: \"audioFrame capture is not supported on web\",\n };\n }\n async stopAudioFrames() {\n // no-op on web\n }\n async isCapturingAudioFrames() {\n return { capturing: false };\n }\n async checkPermissions() {\n // Check microphone permission\n let microphone = \"prompt\";\n try {\n const result = await navigator.permissions?.query?.({\n name: \"microphone\",\n });\n if (result?.state === \"granted\" ||\n result?.state === \"denied\" ||\n result?.state === \"prompt\") {\n microphone = result.state;\n }\n }\n catch {\n // Permissions API may not support microphone query\n }\n // Check if speech recognition is supported\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n const speechRecognition = SpeechRecognitionAPI ? \"prompt\" : \"not_supported\";\n return { microphone, speechRecognition };\n }\n async requestPermissions() {\n // Request microphone permission by attempting to get user media\n try {\n const stream = await navigator.mediaDevices?.getUserMedia?.({\n audio: true,\n });\n if (!stream)\n throw new Error(\"mediaDevices.getUserMedia unavailable\");\n stream.getTracks().forEach((track) => {\n track.stop();\n });\n }\n catch {\n // Permission denied or error\n }\n return this.checkPermissions();\n }\n setState(state, statusText) {\n const previousState = this.state;\n this.state = state;\n this.statusText = statusText;\n this.notifyListeners(\"stateChange\", {\n state,\n previousState,\n statusText,\n usingSystemTts: true,\n });\n }\n}\n"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
private static let defaultModelId = "eleven_flash_v2_5"
|
|
26
|
+
private static let localInferenceTtsURL = "eliza-local-agent://ipc/api/tts/local-inference"
|
|
26
27
|
|
|
27
28
|
// MARK: - State
|
|
28
29
|
|
|
@@ -171,11 +172,18 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
let useSystemTts = call.getBool("useSystemTts") ?? false
|
|
175
|
+
let useLocalInferenceTts = call.getBool("useLocalInferenceTts") ?? false
|
|
174
176
|
let directive = call.getObject("directive")
|
|
175
177
|
|
|
176
178
|
speakTask?.cancel()
|
|
177
179
|
speakTask = Task { @MainActor in
|
|
178
|
-
await self.speakInternal(
|
|
180
|
+
await self.speakInternal(
|
|
181
|
+
text: text,
|
|
182
|
+
forceSystemTts: useSystemTts,
|
|
183
|
+
useLocalInferenceTts: useLocalInferenceTts,
|
|
184
|
+
directive: directive,
|
|
185
|
+
call: call
|
|
186
|
+
)
|
|
179
187
|
}
|
|
180
188
|
}
|
|
181
189
|
|
|
@@ -420,6 +428,7 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
420
428
|
private func speakInternal(
|
|
421
429
|
text: String,
|
|
422
430
|
forceSystemTts: Bool,
|
|
431
|
+
useLocalInferenceTts: Bool,
|
|
423
432
|
directive: [String: Any]?,
|
|
424
433
|
call: CAPPluginCall
|
|
425
434
|
) async {
|
|
@@ -452,13 +461,15 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
452
461
|
let effectiveFormat = Self.validatedOutputFormat(rawFormat) ?? "pcm_24000"
|
|
453
462
|
let effectiveApiKey = apiKey?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
454
463
|
|
|
455
|
-
let
|
|
464
|
+
let canUseLocalInference = useLocalInferenceTts && !forceSystemTts
|
|
465
|
+
let canUseElevenLabs = !canUseLocalInference
|
|
466
|
+
&& !forceSystemTts
|
|
456
467
|
&& !(effectiveApiKey ?? "").isEmpty
|
|
457
468
|
&& !(effectiveVoiceId ?? "").isEmpty
|
|
458
469
|
|
|
459
470
|
notifyListeners("speaking", data: [
|
|
460
471
|
"text": text,
|
|
461
|
-
"isSystemTts": !canUseElevenLabs
|
|
472
|
+
"isSystemTts": !(canUseLocalInference || canUseElevenLabs)
|
|
462
473
|
])
|
|
463
474
|
|
|
464
475
|
// Enable STT during playback for interrupt detection
|
|
@@ -474,7 +485,10 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
474
485
|
let language = Self.validatedLanguage(directive?["language"] as? String)
|
|
475
486
|
|
|
476
487
|
do {
|
|
477
|
-
if
|
|
488
|
+
if canUseLocalInference {
|
|
489
|
+
try await streamLocalInferenceTts(text: text, directive: directive)
|
|
490
|
+
interrupted = pcmStopRequested
|
|
491
|
+
} else if canUseElevenLabs {
|
|
478
492
|
do {
|
|
479
493
|
try await streamElevenLabsTts(
|
|
480
494
|
text: text,
|
|
@@ -527,6 +541,239 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
527
541
|
finishSpeaking()
|
|
528
542
|
}
|
|
529
543
|
|
|
544
|
+
// MARK: - Local Inference TTS
|
|
545
|
+
|
|
546
|
+
private struct LocalInferenceAudioResponse {
|
|
547
|
+
let status: Int
|
|
548
|
+
let statusText: String
|
|
549
|
+
let headers: [String: String]
|
|
550
|
+
let audioBase64: String
|
|
551
|
+
let bodyText: String
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
private struct WavPcmFormat {
|
|
555
|
+
let sampleRate: Int
|
|
556
|
+
let channels: Int
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private func streamLocalInferenceTts(text: String, directive: [String: Any]?) async throws {
|
|
560
|
+
let response = try await requestLocalInferenceTtsAudio(text: text, directive: directive)
|
|
561
|
+
guard (200..<300).contains(response.status) else {
|
|
562
|
+
let detail = response.bodyText.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
563
|
+
let suffix = detail.isEmpty ? response.statusText : detail
|
|
564
|
+
throw NSError(domain: "TalkMode", code: response.status, userInfo: [
|
|
565
|
+
NSLocalizedDescriptionKey: "Local inference TTS error: \(response.status) \(suffix)"
|
|
566
|
+
])
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
guard let audioData = Data(base64Encoded: response.audioBase64), !audioData.isEmpty else {
|
|
570
|
+
throw NSError(domain: "TalkMode", code: 2, userInfo: [
|
|
571
|
+
NSLocalizedDescriptionKey: "Local inference TTS returned empty audio"
|
|
572
|
+
])
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
let format = try Self.parseWavPcmFormat(audioData)
|
|
576
|
+
notifyListeners("playbackStart", data: [
|
|
577
|
+
"provider": "local-inference",
|
|
578
|
+
"sampleRate": format.sampleRate,
|
|
579
|
+
"channels": format.channels
|
|
580
|
+
])
|
|
581
|
+
try await playBufferedAudio(audioData)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private func requestLocalInferenceTtsAudio(
|
|
585
|
+
text: String,
|
|
586
|
+
directive: [String: Any]?
|
|
587
|
+
) async throws -> LocalInferenceAudioResponse {
|
|
588
|
+
guard let webView = bridge?.webView else {
|
|
589
|
+
throw NSError(domain: "TalkMode", code: 503, userInfo: [
|
|
590
|
+
NSLocalizedDescriptionKey: "iOS local-agent WebView bridge is unavailable"
|
|
591
|
+
])
|
|
592
|
+
}
|
|
593
|
+
guard #available(iOS 14.0, *) else {
|
|
594
|
+
throw NSError(domain: "TalkMode", code: 503, userInfo: [
|
|
595
|
+
NSLocalizedDescriptionKey: "Local inference TTS requires iOS 14 or later"
|
|
596
|
+
])
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
let payload = buildLocalInferenceTtsPayload(text: text, directive: directive)
|
|
600
|
+
let source = """
|
|
601
|
+
const response = await fetch(options.url, {
|
|
602
|
+
method: "POST",
|
|
603
|
+
headers: {
|
|
604
|
+
"Accept": "audio/wav",
|
|
605
|
+
"Content-Type": "application/json"
|
|
606
|
+
},
|
|
607
|
+
body: JSON.stringify(options.payload)
|
|
608
|
+
});
|
|
609
|
+
const headers = {};
|
|
610
|
+
response.headers.forEach((value, key) => { headers[key] = value; });
|
|
611
|
+
const buffer = await response.arrayBuffer();
|
|
612
|
+
const bytes = new Uint8Array(buffer);
|
|
613
|
+
let binary = "";
|
|
614
|
+
const chunkSize = 0x8000;
|
|
615
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
616
|
+
const chunk = bytes.subarray(i, i + chunkSize);
|
|
617
|
+
binary += String.fromCharCode.apply(null, Array.from(chunk));
|
|
618
|
+
}
|
|
619
|
+
let bodyText = "";
|
|
620
|
+
if (!response.ok && typeof TextDecoder !== "undefined") {
|
|
621
|
+
bodyText = new TextDecoder().decode(bytes.slice(0, 2048));
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
status: response.status,
|
|
625
|
+
statusText: response.statusText,
|
|
626
|
+
headers,
|
|
627
|
+
audioBase64: btoa(binary),
|
|
628
|
+
bodyText
|
|
629
|
+
};
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
return try await withCheckedThrowingContinuation { continuation in
|
|
633
|
+
DispatchQueue.main.async {
|
|
634
|
+
Task { @MainActor in
|
|
635
|
+
do {
|
|
636
|
+
let value = try await webView.callAsyncJavaScript(
|
|
637
|
+
source,
|
|
638
|
+
arguments: [
|
|
639
|
+
"options": [
|
|
640
|
+
"url": Self.localInferenceTtsURL,
|
|
641
|
+
"payload": payload
|
|
642
|
+
]
|
|
643
|
+
],
|
|
644
|
+
in: nil,
|
|
645
|
+
contentWorld: .page
|
|
646
|
+
)
|
|
647
|
+
continuation.resume(returning: try self.parseLocalInferenceAudioResponse(value))
|
|
648
|
+
} catch {
|
|
649
|
+
continuation.resume(throwing: error)
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private func buildLocalInferenceTtsPayload(text: String, directive: [String: Any]?) -> JSObject {
|
|
657
|
+
var payload: JSObject = ["text": text]
|
|
658
|
+
for key in ["voiceId", "voice", "modelId", "model"] {
|
|
659
|
+
if let value = directive?[key] as? String {
|
|
660
|
+
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
661
|
+
if !trimmed.isEmpty { payload[key] = trimmed }
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if let speed = directive?["speed"] as? Double, speed.isFinite, speed > 0 {
|
|
665
|
+
payload["speed"] = speed
|
|
666
|
+
} else if let speed = directive?["speed"] as? NSNumber, speed.doubleValue.isFinite, speed.doubleValue > 0 {
|
|
667
|
+
payload["speed"] = speed.doubleValue
|
|
668
|
+
}
|
|
669
|
+
if let outputFormat = directive?["outputFormat"] as? String {
|
|
670
|
+
let trimmed = outputFormat.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
671
|
+
if !trimmed.isEmpty { payload["format"] = trimmed }
|
|
672
|
+
}
|
|
673
|
+
return payload
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
private func parseLocalInferenceAudioResponse(_ value: Any?) throws -> LocalInferenceAudioResponse {
|
|
677
|
+
guard let payload = value as? JSObject else {
|
|
678
|
+
throw NSError(domain: "TalkMode", code: 502, userInfo: [
|
|
679
|
+
NSLocalizedDescriptionKey: "Local inference TTS returned an invalid bridge response"
|
|
680
|
+
])
|
|
681
|
+
}
|
|
682
|
+
let status = (payload["status"] as? Int)
|
|
683
|
+
?? (payload["status"] as? NSNumber)?.intValue
|
|
684
|
+
?? 0
|
|
685
|
+
let headers = payload["headers"] as? [String: String] ?? [:]
|
|
686
|
+
return LocalInferenceAudioResponse(
|
|
687
|
+
status: status,
|
|
688
|
+
statusText: payload["statusText"] as? String ?? "",
|
|
689
|
+
headers: headers,
|
|
690
|
+
audioBase64: payload["audioBase64"] as? String ?? "",
|
|
691
|
+
bodyText: payload["bodyText"] as? String ?? ""
|
|
692
|
+
)
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
private static func parseWavPcmFormat(_ data: Data) throws -> WavPcmFormat {
|
|
696
|
+
guard data.count >= 12,
|
|
697
|
+
asciiString(data, offset: 0, length: 4) == "RIFF",
|
|
698
|
+
asciiString(data, offset: 8, length: 4) == "WAVE" else {
|
|
699
|
+
throw NSError(domain: "TalkMode", code: 3, userInfo: [
|
|
700
|
+
NSLocalizedDescriptionKey: "Local inference TTS returned non-WAV audio"
|
|
701
|
+
])
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
var offset = 12
|
|
705
|
+
var parsedFormat: WavPcmFormat?
|
|
706
|
+
while offset + 8 <= data.count {
|
|
707
|
+
let id = asciiString(data, offset: offset, length: 4)
|
|
708
|
+
let size = Int(littleEndianUInt32(data, offset: offset + 4))
|
|
709
|
+
let payloadOffset = offset + 8
|
|
710
|
+
guard size >= 0, payloadOffset + size <= data.count else {
|
|
711
|
+
throw NSError(domain: "TalkMode", code: 4, userInfo: [
|
|
712
|
+
NSLocalizedDescriptionKey: "Invalid WAV chunk size"
|
|
713
|
+
])
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if id == "fmt " {
|
|
717
|
+
guard size >= 16 else {
|
|
718
|
+
throw NSError(domain: "TalkMode", code: 5, userInfo: [
|
|
719
|
+
NSLocalizedDescriptionKey: "Invalid WAV fmt chunk"
|
|
720
|
+
])
|
|
721
|
+
}
|
|
722
|
+
let audioFormat = littleEndianUInt16(data, offset: payloadOffset)
|
|
723
|
+
let channels = Int(littleEndianUInt16(data, offset: payloadOffset + 2))
|
|
724
|
+
let sampleRate = Int(littleEndianUInt32(data, offset: payloadOffset + 4))
|
|
725
|
+
let bitsPerSample = littleEndianUInt16(data, offset: payloadOffset + 14)
|
|
726
|
+
guard audioFormat == 1 else {
|
|
727
|
+
throw NSError(domain: "TalkMode", code: 6, userInfo: [
|
|
728
|
+
NSLocalizedDescriptionKey: "Only PCM WAV is supported"
|
|
729
|
+
])
|
|
730
|
+
}
|
|
731
|
+
guard bitsPerSample == 16 else {
|
|
732
|
+
throw NSError(domain: "TalkMode", code: 7, userInfo: [
|
|
733
|
+
NSLocalizedDescriptionKey: "Only 16-bit PCM WAV is supported"
|
|
734
|
+
])
|
|
735
|
+
}
|
|
736
|
+
guard channels >= 1, channels <= 2, sampleRate > 0 else {
|
|
737
|
+
throw NSError(domain: "TalkMode", code: 8, userInfo: [
|
|
738
|
+
NSLocalizedDescriptionKey: "Invalid WAV sample rate or channel count"
|
|
739
|
+
])
|
|
740
|
+
}
|
|
741
|
+
parsedFormat = WavPcmFormat(sampleRate: sampleRate, channels: channels)
|
|
742
|
+
} else if id == "data" {
|
|
743
|
+
guard let parsedFormat else {
|
|
744
|
+
throw NSError(domain: "TalkMode", code: 9, userInfo: [
|
|
745
|
+
NSLocalizedDescriptionKey: "WAV data arrived before fmt chunk"
|
|
746
|
+
])
|
|
747
|
+
}
|
|
748
|
+
return parsedFormat
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
offset = payloadOffset + size + (size % 2)
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
throw NSError(domain: "TalkMode", code: 10, userInfo: [
|
|
755
|
+
NSLocalizedDescriptionKey: "WAV data chunk was not found"
|
|
756
|
+
])
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
private static func asciiString(_ data: Data, offset: Int, length: Int) -> String {
|
|
760
|
+
guard offset >= 0, length >= 0, offset + length <= data.count else { return "" }
|
|
761
|
+
return String(data: data.subdata(in: offset..<(offset + length)), encoding: .ascii) ?? ""
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
private static func littleEndianUInt16(_ data: Data, offset: Int) -> UInt16 {
|
|
765
|
+
guard offset + 2 <= data.count else { return 0 }
|
|
766
|
+
return UInt16(data[offset]) | (UInt16(data[offset + 1]) << 8)
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
private static func littleEndianUInt32(_ data: Data, offset: Int) -> UInt32 {
|
|
770
|
+
guard offset + 4 <= data.count else { return 0 }
|
|
771
|
+
return UInt32(data[offset])
|
|
772
|
+
| (UInt32(data[offset + 1]) << 8)
|
|
773
|
+
| (UInt32(data[offset + 2]) << 16)
|
|
774
|
+
| (UInt32(data[offset + 3]) << 24)
|
|
775
|
+
}
|
|
776
|
+
|
|
530
777
|
/// Clean up after speech and restart recognition if talk mode is still enabled.
|
|
531
778
|
private func finishSpeaking() {
|
|
532
779
|
isSpeakingValue = false
|
|
@@ -751,17 +998,7 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
751
998
|
}
|
|
752
999
|
}
|
|
753
1000
|
|
|
754
|
-
|
|
755
|
-
private func downloadAndPlayAudio(request: URLRequest) async throws {
|
|
756
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
757
|
-
|
|
758
|
-
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
|
759
|
-
let msg = String(data: data.prefix(2048), encoding: .utf8) ?? "Unknown error"
|
|
760
|
-
throw NSError(domain: "TalkMode", code: 2, userInfo: [
|
|
761
|
-
NSLocalizedDescriptionKey: "ElevenLabs API error: \(msg)"
|
|
762
|
-
])
|
|
763
|
-
}
|
|
764
|
-
|
|
1001
|
+
private func playBufferedAudio(_ data: Data) async throws {
|
|
765
1002
|
mp3PlaybackStartTime = Date()
|
|
766
1003
|
|
|
767
1004
|
let player = try AVAudioPlayer(data: data)
|
|
@@ -772,7 +1009,6 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
772
1009
|
let delegate = AudioPlayerDelegate {
|
|
773
1010
|
continuation.resume()
|
|
774
1011
|
}
|
|
775
|
-
// Retain delegate for the lifetime of playback
|
|
776
1012
|
objc_setAssociatedObject(player, "delegate", delegate, .OBJC_ASSOCIATION_RETAIN)
|
|
777
1013
|
player.delegate = delegate
|
|
778
1014
|
player.play()
|
|
@@ -782,6 +1018,20 @@ public class TalkModePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
782
1018
|
mp3PlaybackStartTime = nil
|
|
783
1019
|
}
|
|
784
1020
|
|
|
1021
|
+
/// Download a full audio response (MP3 etc.) and play it with AVAudioPlayer.
|
|
1022
|
+
private func downloadAndPlayAudio(request: URLRequest) async throws {
|
|
1023
|
+
let (data, response) = try await URLSession.shared.data(for: request)
|
|
1024
|
+
|
|
1025
|
+
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
|
1026
|
+
let msg = String(data: data.prefix(2048), encoding: .utf8) ?? "Unknown error"
|
|
1027
|
+
throw NSError(domain: "TalkMode", code: 2, userInfo: [
|
|
1028
|
+
NSLocalizedDescriptionKey: "ElevenLabs API error: \(msg)"
|
|
1029
|
+
])
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
try await playBufferedAudio(data)
|
|
1033
|
+
}
|
|
1034
|
+
|
|
785
1035
|
// MARK: - System TTS
|
|
786
1036
|
|
|
787
1037
|
private func speakWithSystemTts(text: String, language: String? = nil) async throws {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/capacitor-talkmode",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3-beta.3",
|
|
4
4
|
"description": "Runs voice conversations with live transcription, chat orchestration, and spoken replies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voice",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./dist/esm/index.d.ts",
|
|
17
|
+
"bun": "./src/index.ts",
|
|
18
|
+
"development": "./src/index.ts",
|
|
17
19
|
"import": "./dist/esm/index.js",
|
|
18
20
|
"require": "./dist/plugin.cjs.js"
|
|
19
21
|
},
|
|
@@ -29,17 +31,17 @@
|
|
|
29
31
|
"dist"
|
|
30
32
|
],
|
|
31
33
|
"scripts": {
|
|
32
|
-
"build": "
|
|
34
|
+
"build": "node ../../packages/scripts/with-package-build-lock.mjs plugins/plugin-native-talkmode -- bun run build:unlocked",
|
|
33
35
|
"build:docs": "bun run clean && bun run docgen && tsc && bun --bun rollup -c rollup.config.mjs",
|
|
34
|
-
"clean": "node
|
|
36
|
+
"clean": "node ../../packages/scripts/rm-path-recursive.mjs dist",
|
|
37
|
+
"test": "vitest run",
|
|
35
38
|
"prepublishOnly": "bun run build",
|
|
36
|
-
"docgen": "docgen --api TalkModePlugin --output-readme README.md --output-json dist/docs.json"
|
|
39
|
+
"docgen": "docgen --api TalkModePlugin --output-readme README.md --output-json dist/docs.json",
|
|
40
|
+
"build:unlocked": "bun run clean && tsc && bunx rollup -c rollup.config.mjs"
|
|
37
41
|
},
|
|
38
42
|
"author": "elizaOS",
|
|
39
43
|
"license": "MIT",
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@elizaos/app-core": "2.0.0-beta.1"
|
|
42
|
-
},
|
|
44
|
+
"dependencies": {},
|
|
43
45
|
"repository": {
|
|
44
46
|
"type": "git",
|
|
45
47
|
"url": "https://github.com/elizaOS/eliza.git",
|
|
@@ -55,12 +57,12 @@
|
|
|
55
57
|
}
|
|
56
58
|
},
|
|
57
59
|
"devDependencies": {
|
|
58
|
-
"@capacitor/cli": "^8.0.0",
|
|
59
60
|
"@capacitor/core": "^8.3.1",
|
|
60
61
|
"@capacitor/docgen": "^0.3.0",
|
|
61
|
-
"
|
|
62
|
+
"@elizaos/native-plugin-shared-types": "2.0.3-beta.3",
|
|
62
63
|
"rollup": "^4.60.2",
|
|
63
|
-
"typescript": "^6.0.3"
|
|
64
|
+
"typescript": "^6.0.3",
|
|
65
|
+
"vitest": "^4.0.0"
|
|
64
66
|
},
|
|
65
67
|
"peerDependencies": {
|
|
66
68
|
"@capacitor/core": "^8.3.1"
|
|
@@ -80,5 +82,6 @@
|
|
|
80
82
|
"ios": true,
|
|
81
83
|
"android": true
|
|
82
84
|
}
|
|
83
|
-
}
|
|
85
|
+
},
|
|
86
|
+
"gitHead": "f54b0f4eaed317d59fa7dbcdce20f4cdb0734420"
|
|
84
87
|
}
|