whisper-coreml 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.
package/dist/index.cjs ADDED
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ WHISPER_MODEL: () => WHISPER_MODEL,
24
+ WhisperAsrEngine: () => WhisperAsrEngine,
25
+ downloadModel: () => downloadModel,
26
+ formatBytes: () => formatBytes,
27
+ getDefaultModelDir: () => getDefaultModelDir,
28
+ getLoadError: () => getLoadError,
29
+ getModelPath: () => getModelPath,
30
+ isAvailable: () => isAvailable,
31
+ isModelDownloaded: () => isModelDownloaded
32
+ });
33
+ module.exports = __toCommonJS(src_exports);
34
+
35
+ // src/download.ts
36
+ var import_node_fs = require("fs");
37
+ var import_node_os = require("os");
38
+ var import_node_path = require("path");
39
+ var WHISPER_MODEL = {
40
+ name: "large-v3-turbo",
41
+ size: "1.5 GB",
42
+ languages: "99 languages",
43
+ url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin"
44
+ };
45
+ function getDefaultModelDir() {
46
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), ".cache", "whisper-coreml", "models");
47
+ }
48
+ function getModelPath(modelDir) {
49
+ const dir = modelDir ?? getDefaultModelDir();
50
+ return (0, import_node_path.join)(dir, `ggml-${WHISPER_MODEL.name}.bin`);
51
+ }
52
+ function isModelDownloaded(modelDir) {
53
+ const modelPath = getModelPath(modelDir);
54
+ return (0, import_node_fs.existsSync)(modelPath);
55
+ }
56
+ async function downloadModel(options = {}) {
57
+ const modelDir = options.modelDir ?? getDefaultModelDir();
58
+ const modelPath = getModelPath(modelDir);
59
+ if (!options.force && (0, import_node_fs.existsSync)(modelPath)) {
60
+ return modelPath;
61
+ }
62
+ if ((0, import_node_fs.existsSync)(modelPath)) {
63
+ (0, import_node_fs.rmSync)(modelPath);
64
+ }
65
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(modelPath), { recursive: true });
66
+ console.log(`Downloading Whisper ${WHISPER_MODEL.name} (${WHISPER_MODEL.size})...`);
67
+ console.log(`Source: ${WHISPER_MODEL.url}`);
68
+ console.log(`Target: ${modelPath}`);
69
+ const response = await fetch(WHISPER_MODEL.url);
70
+ if (!response.ok) {
71
+ throw new Error(`Failed to download model: ${response.statusText}`);
72
+ }
73
+ const contentLength = response.headers.get("content-length");
74
+ const totalBytes = contentLength ? parseInt(contentLength, 10) : 0;
75
+ const reader = response.body?.getReader();
76
+ if (!reader) {
77
+ throw new Error("Failed to get response body reader");
78
+ }
79
+ const chunks = [];
80
+ let downloadedBytes = 0;
81
+ while (true) {
82
+ const result = await reader.read();
83
+ if (result.done) {
84
+ break;
85
+ }
86
+ const chunk = result.value;
87
+ chunks.push(chunk);
88
+ downloadedBytes += chunk.length;
89
+ const percent = totalBytes > 0 ? Math.round(downloadedBytes / totalBytes * 100) : 0;
90
+ if (options.onProgress) {
91
+ options.onProgress({
92
+ downloadedBytes,
93
+ totalBytes,
94
+ percent
95
+ });
96
+ }
97
+ process.stdout.write(
98
+ `\rProgress: ${String(percent)}% (${formatBytes(downloadedBytes)}/${formatBytes(totalBytes)})`
99
+ );
100
+ }
101
+ const buffer = Buffer.concat(chunks);
102
+ (0, import_node_fs.writeFileSync)(modelPath, buffer);
103
+ console.log("\n\u2713 Model downloaded successfully!");
104
+ return modelPath;
105
+ }
106
+ function formatBytes(bytes) {
107
+ if (bytes < 1024) {
108
+ return `${String(bytes)} B`;
109
+ }
110
+ if (bytes < 1024 * 1024) {
111
+ return `${(bytes / 1024).toFixed(1)} KB`;
112
+ }
113
+ if (bytes < 1024 * 1024 * 1024) {
114
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
115
+ }
116
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
117
+ }
118
+
119
+ // src/index.ts
120
+ var bindingsModule = require("bindings");
121
+ function loadAddon() {
122
+ if (process.platform !== "darwin") {
123
+ throw new Error("whisper-coreml is only supported on macOS");
124
+ }
125
+ try {
126
+ return bindingsModule("whisper_asr");
127
+ } catch (error) {
128
+ const message = error instanceof Error ? error.message : String(error);
129
+ throw new Error(`Failed to load Whisper ASR native addon: ${message}`);
130
+ }
131
+ }
132
+ var addon = null;
133
+ var loadError = null;
134
+ function getAddon() {
135
+ if (!addon) {
136
+ try {
137
+ addon = loadAddon();
138
+ } catch (error) {
139
+ loadError = error instanceof Error ? error : new Error(String(error));
140
+ throw error;
141
+ }
142
+ }
143
+ return addon;
144
+ }
145
+ function isAvailable() {
146
+ return process.platform === "darwin" && process.arch === "arm64";
147
+ }
148
+ function getLoadError() {
149
+ return loadError;
150
+ }
151
+ var WhisperAsrEngine = class {
152
+ options;
153
+ initialized = false;
154
+ constructor(options) {
155
+ this.options = options;
156
+ }
157
+ /* v8 ignore start - native addon calls, tested via E2E */
158
+ /**
159
+ * Initialize the Whisper engine
160
+ * This loads the model into memory - may take a few seconds.
161
+ */
162
+ initialize() {
163
+ if (this.initialized) {
164
+ return Promise.resolve();
165
+ }
166
+ const nativeAddon = getAddon();
167
+ const success = nativeAddon.initialize({
168
+ modelPath: this.options.modelPath,
169
+ language: this.options.language ?? "auto",
170
+ translate: this.options.translate ?? false,
171
+ threads: this.options.threads ?? 0
172
+ });
173
+ if (!success) {
174
+ return Promise.reject(new Error("Failed to initialize Whisper engine"));
175
+ }
176
+ this.initialized = true;
177
+ return Promise.resolve();
178
+ }
179
+ /**
180
+ * Check if the engine is ready for transcription
181
+ */
182
+ isReady() {
183
+ if (!this.initialized) {
184
+ return false;
185
+ }
186
+ try {
187
+ return getAddon().isInitialized();
188
+ } catch {
189
+ return false;
190
+ }
191
+ }
192
+ /**
193
+ * Transcribe audio samples
194
+ *
195
+ * @param samples - Float32Array of audio samples (mono, 16kHz)
196
+ * @param sampleRate - Sample rate in Hz (default: 16000)
197
+ * @returns Transcription result with text and segments
198
+ */
199
+ transcribe(samples, sampleRate = 16e3) {
200
+ if (!this.initialized) {
201
+ return Promise.reject(new Error("Whisper engine not initialized. Call initialize() first."));
202
+ }
203
+ const result = getAddon().transcribe(samples, sampleRate);
204
+ return Promise.resolve({
205
+ text: result.text,
206
+ language: result.language,
207
+ durationMs: result.durationMs,
208
+ segments: result.segments
209
+ });
210
+ }
211
+ /**
212
+ * Clean up resources and unload the model
213
+ */
214
+ cleanup() {
215
+ if (this.initialized) {
216
+ try {
217
+ getAddon().cleanup();
218
+ } catch {
219
+ }
220
+ this.initialized = false;
221
+ }
222
+ }
223
+ /**
224
+ * Get version information
225
+ */
226
+ getVersion() {
227
+ return getAddon().getVersion();
228
+ }
229
+ /* v8 ignore stop */
230
+ };
231
+ // Annotate the CommonJS export names for ESM import in node:
232
+ 0 && (module.exports = {
233
+ WHISPER_MODEL,
234
+ WhisperAsrEngine,
235
+ downloadModel,
236
+ formatBytes,
237
+ getDefaultModelDir,
238
+ getLoadError,
239
+ getModelPath,
240
+ isAvailable,
241
+ isModelDownloaded
242
+ });
243
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/download.ts"],"sourcesContent":["/**\n * whisper-coreml\n *\n * OpenAI Whisper ASR for Node.js with CoreML/ANE acceleration on Apple Silicon.\n * Based on whisper.cpp with Apple Neural Engine support.\n *\n * Uses the large-v3-turbo model exclusively, as it offers the best speed/quality\n * ratio and is the main reason to choose Whisper over Parakeet.\n */\n\n// Dynamic require for loading native addon (works in both ESM and CJS)\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst bindingsModule = require(\"bindings\") as (name: string) => unknown\n\n/**\n * Native addon interface\n */\ninterface NativeAddon {\n initialize(options: {\n modelPath: string\n language?: string\n translate?: boolean\n threads?: number\n }): boolean\n isInitialized(): boolean\n transcribe(samples: Float32Array, sampleRate: number): NativeTranscriptionResult\n cleanup(): void\n getVersion(): { addon: string; whisper: string; coreml: string }\n}\n\ninterface NativeTranscriptionResult {\n text: string\n language: string\n durationMs: number\n segments: {\n startMs: number\n endMs: number\n text: string\n confidence: number\n }[]\n}\n\n/* v8 ignore start - platform checks and native addon loading */\n\n/**\n * Load the native addon\n */\nfunction loadAddon(): NativeAddon {\n if (process.platform !== \"darwin\") {\n throw new Error(\"whisper-coreml is only supported on macOS\")\n }\n\n try {\n return bindingsModule(\"whisper_asr\") as NativeAddon\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Failed to load Whisper ASR native addon: ${message}`)\n }\n}\n\n/* v8 ignore stop */\n\nlet addon: NativeAddon | null = null\nlet loadError: Error | null = null\n\nfunction getAddon(): NativeAddon {\n if (!addon) {\n try {\n addon = loadAddon()\n } catch (error) {\n loadError = error instanceof Error ? error : new Error(String(error))\n throw error\n }\n }\n return addon\n}\n\n/**\n * Check if Whisper ASR is available on this platform\n */\nexport function isAvailable(): boolean {\n return process.platform === \"darwin\" && process.arch === \"arm64\"\n}\n\n/**\n * Get the load error if the addon failed to load\n */\nexport function getLoadError(): Error | null {\n return loadError\n}\n\n/**\n * Transcription segment with timestamps\n */\nexport interface TranscriptionSegment {\n /** Start time in milliseconds */\n startMs: number\n /** End time in milliseconds */\n endMs: number\n /** Transcribed text for this segment */\n text: string\n /** Confidence score (0-1) */\n confidence: number\n}\n\n/**\n * Transcription result\n */\nexport interface TranscriptionResult {\n /** Full transcribed text */\n text: string\n /** Detected or specified language (ISO code) */\n language: string\n /** Processing time in milliseconds */\n durationMs: number\n /** Individual segments with timestamps */\n segments: TranscriptionSegment[]\n}\n\n/**\n * Whisper ASR engine options\n */\nexport interface WhisperAsrOptions {\n /** Path to the Whisper model file (ggml format) */\n modelPath: string\n /** Language code (e.g., \"en\", \"de\", \"fr\") or \"auto\" for auto-detection */\n language?: string\n /** Translate to English (default: false) */\n translate?: boolean\n /** Number of threads (0 = auto) */\n threads?: number\n}\n\n/**\n * Whisper ASR Engine with CoreML acceleration\n *\n * Uses the large-v3-turbo model for best speed/quality balance.\n *\n * @example\n * ```typescript\n * import { WhisperAsrEngine, getModelPath } from \"whisper-coreml\"\n *\n * const engine = new WhisperAsrEngine({\n * modelPath: getModelPath()\n * })\n *\n * await engine.initialize()\n * const result = await engine.transcribe(audioSamples, 16000)\n * console.log(result.text)\n * ```\n */\nexport class WhisperAsrEngine {\n private options: WhisperAsrOptions\n private initialized = false\n\n constructor(options: WhisperAsrOptions) {\n this.options = options\n }\n\n /* v8 ignore start - native addon calls, tested via E2E */\n\n /**\n * Initialize the Whisper engine\n * This loads the model into memory - may take a few seconds.\n */\n initialize(): Promise<void> {\n if (this.initialized) {\n return Promise.resolve()\n }\n\n const nativeAddon = getAddon()\n const success = nativeAddon.initialize({\n modelPath: this.options.modelPath,\n language: this.options.language ?? \"auto\",\n translate: this.options.translate ?? false,\n threads: this.options.threads ?? 0\n })\n\n if (!success) {\n return Promise.reject(new Error(\"Failed to initialize Whisper engine\"))\n }\n\n this.initialized = true\n return Promise.resolve()\n }\n\n /**\n * Check if the engine is ready for transcription\n */\n isReady(): boolean {\n if (!this.initialized) {\n return false\n }\n try {\n return getAddon().isInitialized()\n } catch {\n return false\n }\n }\n\n /**\n * Transcribe audio samples\n *\n * @param samples - Float32Array of audio samples (mono, 16kHz)\n * @param sampleRate - Sample rate in Hz (default: 16000)\n * @returns Transcription result with text and segments\n */\n transcribe(samples: Float32Array, sampleRate = 16000): Promise<TranscriptionResult> {\n if (!this.initialized) {\n return Promise.reject(new Error(\"Whisper engine not initialized. Call initialize() first.\"))\n }\n\n const result = getAddon().transcribe(samples, sampleRate)\n\n return Promise.resolve({\n text: result.text,\n language: result.language,\n durationMs: result.durationMs,\n segments: result.segments\n })\n }\n\n /**\n * Clean up resources and unload the model\n */\n cleanup(): void {\n if (this.initialized) {\n try {\n getAddon().cleanup()\n } catch {\n // Ignore cleanup errors\n }\n this.initialized = false\n }\n }\n\n /**\n * Get version information\n */\n getVersion(): { addon: string; whisper: string; coreml: string } {\n return getAddon().getVersion()\n }\n\n /* v8 ignore stop */\n}\n\n// Re-export download utilities\nexport {\n downloadModel,\n formatBytes,\n getDefaultModelDir,\n getModelPath,\n isModelDownloaded,\n WHISPER_MODEL,\n type DownloadOptions\n} from \"./download.js\"\n","/**\n * Model download functionality for whisper-coreml\n *\n * Note: We only support large-v3-turbo as it's the only Whisper model\n * that offers better quality than Parakeet while maintaining reasonable speed.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, rmSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join, dirname } from \"node:path\"\n\n/**\n * Whisper large-v3-turbo model info\n * This is the only model we support as it offers the best speed/quality ratio\n * and is the main reason to choose Whisper over Parakeet.\n */\nexport const WHISPER_MODEL = {\n name: \"large-v3-turbo\",\n size: \"1.5 GB\",\n languages: \"99 languages\",\n url: \"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin\"\n} as const\n\n/**\n * Default model directory in user's cache\n */\nexport function getDefaultModelDir(): string {\n return join(homedir(), \".cache\", \"whisper-coreml\", \"models\")\n}\n\n/**\n * Get the path to the model\n */\nexport function getModelPath(modelDir?: string): string {\n const dir = modelDir ?? getDefaultModelDir()\n return join(dir, `ggml-${WHISPER_MODEL.name}.bin`)\n}\n\n/**\n * Check if the model is downloaded\n */\nexport function isModelDownloaded(modelDir?: string): boolean {\n const modelPath = getModelPath(modelDir)\n return existsSync(modelPath)\n}\n\ninterface DownloadProgress {\n downloadedBytes: number\n totalBytes: number\n percent: number\n}\n\nexport interface DownloadOptions {\n /** Target directory for model (default: ~/.cache/whisper-coreml/models) */\n modelDir?: string\n\n /** Progress callback */\n onProgress?: (progress: DownloadProgress) => void\n\n /** Force re-download even if model exists */\n force?: boolean\n}\n\n/* v8 ignore start - network I/O */\n\n/**\n * Download the Whisper large-v3-turbo model from Hugging Face\n */\nexport async function downloadModel(options: DownloadOptions = {}): Promise<string> {\n const modelDir = options.modelDir ?? getDefaultModelDir()\n const modelPath = getModelPath(modelDir)\n\n if (!options.force && existsSync(modelPath)) {\n return modelPath\n }\n\n // Clean up partial downloads\n if (existsSync(modelPath)) {\n rmSync(modelPath)\n }\n\n mkdirSync(dirname(modelPath), { recursive: true })\n\n console.log(`Downloading Whisper ${WHISPER_MODEL.name} (${WHISPER_MODEL.size})...`)\n console.log(`Source: ${WHISPER_MODEL.url}`)\n console.log(`Target: ${modelPath}`)\n\n const response = await fetch(WHISPER_MODEL.url)\n if (!response.ok) {\n throw new Error(`Failed to download model: ${response.statusText}`)\n }\n\n const contentLength = response.headers.get(\"content-length\")\n const totalBytes = contentLength ? parseInt(contentLength, 10) : 0\n\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error(\"Failed to get response body reader\")\n }\n\n const chunks: Uint8Array[] = []\n let downloadedBytes = 0\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const result = await reader.read()\n if (result.done) {\n break\n }\n\n const chunk = result.value as Uint8Array\n chunks.push(chunk)\n downloadedBytes += chunk.length\n\n const percent = totalBytes > 0 ? Math.round((downloadedBytes / totalBytes) * 100) : 0\n\n if (options.onProgress) {\n options.onProgress({\n downloadedBytes,\n totalBytes,\n percent\n })\n }\n\n // Progress indicator\n process.stdout.write(\n `\\rProgress: ${String(percent)}% (${formatBytes(downloadedBytes)}/${formatBytes(totalBytes)})`\n )\n }\n\n // Combine chunks and write to file\n const buffer = Buffer.concat(chunks)\n writeFileSync(modelPath, buffer)\n\n console.log(\"\\n✓ Model downloaded successfully!\")\n return modelPath\n}\n\n/* v8 ignore stop */\n\n/**\n * Format bytes to human readable string\n * @internal Exported for testing\n */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${String(bytes)} B`\n }\n if (bytes < 1024 * 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`\n }\n if (bytes < 1024 * 1024 * 1024) {\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`\n }\n return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Model download functionality for whisper-coreml
3
+ *
4
+ * Note: We only support large-v3-turbo as it's the only Whisper model
5
+ * that offers better quality than Parakeet while maintaining reasonable speed.
6
+ */
7
+ /**
8
+ * Whisper large-v3-turbo model info
9
+ * This is the only model we support as it offers the best speed/quality ratio
10
+ * and is the main reason to choose Whisper over Parakeet.
11
+ */
12
+ declare const WHISPER_MODEL: {
13
+ readonly name: "large-v3-turbo";
14
+ readonly size: "1.5 GB";
15
+ readonly languages: "99 languages";
16
+ readonly url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin";
17
+ };
18
+ /**
19
+ * Default model directory in user's cache
20
+ */
21
+ declare function getDefaultModelDir(): string;
22
+ /**
23
+ * Get the path to the model
24
+ */
25
+ declare function getModelPath(modelDir?: string): string;
26
+ /**
27
+ * Check if the model is downloaded
28
+ */
29
+ declare function isModelDownloaded(modelDir?: string): boolean;
30
+ interface DownloadProgress {
31
+ downloadedBytes: number;
32
+ totalBytes: number;
33
+ percent: number;
34
+ }
35
+ interface DownloadOptions {
36
+ /** Target directory for model (default: ~/.cache/whisper-coreml/models) */
37
+ modelDir?: string;
38
+ /** Progress callback */
39
+ onProgress?: (progress: DownloadProgress) => void;
40
+ /** Force re-download even if model exists */
41
+ force?: boolean;
42
+ }
43
+ /**
44
+ * Download the Whisper large-v3-turbo model from Hugging Face
45
+ */
46
+ declare function downloadModel(options?: DownloadOptions): Promise<string>;
47
+ /**
48
+ * Format bytes to human readable string
49
+ * @internal Exported for testing
50
+ */
51
+ declare function formatBytes(bytes: number): string;
52
+
53
+ /**
54
+ * whisper-coreml
55
+ *
56
+ * OpenAI Whisper ASR for Node.js with CoreML/ANE acceleration on Apple Silicon.
57
+ * Based on whisper.cpp with Apple Neural Engine support.
58
+ *
59
+ * Uses the large-v3-turbo model exclusively, as it offers the best speed/quality
60
+ * ratio and is the main reason to choose Whisper over Parakeet.
61
+ */
62
+ /**
63
+ * Check if Whisper ASR is available on this platform
64
+ */
65
+ declare function isAvailable(): boolean;
66
+ /**
67
+ * Get the load error if the addon failed to load
68
+ */
69
+ declare function getLoadError(): Error | null;
70
+ /**
71
+ * Transcription segment with timestamps
72
+ */
73
+ interface TranscriptionSegment {
74
+ /** Start time in milliseconds */
75
+ startMs: number;
76
+ /** End time in milliseconds */
77
+ endMs: number;
78
+ /** Transcribed text for this segment */
79
+ text: string;
80
+ /** Confidence score (0-1) */
81
+ confidence: number;
82
+ }
83
+ /**
84
+ * Transcription result
85
+ */
86
+ interface TranscriptionResult {
87
+ /** Full transcribed text */
88
+ text: string;
89
+ /** Detected or specified language (ISO code) */
90
+ language: string;
91
+ /** Processing time in milliseconds */
92
+ durationMs: number;
93
+ /** Individual segments with timestamps */
94
+ segments: TranscriptionSegment[];
95
+ }
96
+ /**
97
+ * Whisper ASR engine options
98
+ */
99
+ interface WhisperAsrOptions {
100
+ /** Path to the Whisper model file (ggml format) */
101
+ modelPath: string;
102
+ /** Language code (e.g., "en", "de", "fr") or "auto" for auto-detection */
103
+ language?: string;
104
+ /** Translate to English (default: false) */
105
+ translate?: boolean;
106
+ /** Number of threads (0 = auto) */
107
+ threads?: number;
108
+ }
109
+ /**
110
+ * Whisper ASR Engine with CoreML acceleration
111
+ *
112
+ * Uses the large-v3-turbo model for best speed/quality balance.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * import { WhisperAsrEngine, getModelPath } from "whisper-coreml"
117
+ *
118
+ * const engine = new WhisperAsrEngine({
119
+ * modelPath: getModelPath()
120
+ * })
121
+ *
122
+ * await engine.initialize()
123
+ * const result = await engine.transcribe(audioSamples, 16000)
124
+ * console.log(result.text)
125
+ * ```
126
+ */
127
+ declare class WhisperAsrEngine {
128
+ private options;
129
+ private initialized;
130
+ constructor(options: WhisperAsrOptions);
131
+ /**
132
+ * Initialize the Whisper engine
133
+ * This loads the model into memory - may take a few seconds.
134
+ */
135
+ initialize(): Promise<void>;
136
+ /**
137
+ * Check if the engine is ready for transcription
138
+ */
139
+ isReady(): boolean;
140
+ /**
141
+ * Transcribe audio samples
142
+ *
143
+ * @param samples - Float32Array of audio samples (mono, 16kHz)
144
+ * @param sampleRate - Sample rate in Hz (default: 16000)
145
+ * @returns Transcription result with text and segments
146
+ */
147
+ transcribe(samples: Float32Array, sampleRate?: number): Promise<TranscriptionResult>;
148
+ /**
149
+ * Clean up resources and unload the model
150
+ */
151
+ cleanup(): void;
152
+ /**
153
+ * Get version information
154
+ */
155
+ getVersion(): {
156
+ addon: string;
157
+ whisper: string;
158
+ coreml: string;
159
+ };
160
+ }
161
+
162
+ export { type DownloadOptions, type TranscriptionResult, type TranscriptionSegment, WHISPER_MODEL, WhisperAsrEngine, type WhisperAsrOptions, downloadModel, formatBytes, getDefaultModelDir, getLoadError, getModelPath, isAvailable, isModelDownloaded };
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Model download functionality for whisper-coreml
3
+ *
4
+ * Note: We only support large-v3-turbo as it's the only Whisper model
5
+ * that offers better quality than Parakeet while maintaining reasonable speed.
6
+ */
7
+ /**
8
+ * Whisper large-v3-turbo model info
9
+ * This is the only model we support as it offers the best speed/quality ratio
10
+ * and is the main reason to choose Whisper over Parakeet.
11
+ */
12
+ declare const WHISPER_MODEL: {
13
+ readonly name: "large-v3-turbo";
14
+ readonly size: "1.5 GB";
15
+ readonly languages: "99 languages";
16
+ readonly url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin";
17
+ };
18
+ /**
19
+ * Default model directory in user's cache
20
+ */
21
+ declare function getDefaultModelDir(): string;
22
+ /**
23
+ * Get the path to the model
24
+ */
25
+ declare function getModelPath(modelDir?: string): string;
26
+ /**
27
+ * Check if the model is downloaded
28
+ */
29
+ declare function isModelDownloaded(modelDir?: string): boolean;
30
+ interface DownloadProgress {
31
+ downloadedBytes: number;
32
+ totalBytes: number;
33
+ percent: number;
34
+ }
35
+ interface DownloadOptions {
36
+ /** Target directory for model (default: ~/.cache/whisper-coreml/models) */
37
+ modelDir?: string;
38
+ /** Progress callback */
39
+ onProgress?: (progress: DownloadProgress) => void;
40
+ /** Force re-download even if model exists */
41
+ force?: boolean;
42
+ }
43
+ /**
44
+ * Download the Whisper large-v3-turbo model from Hugging Face
45
+ */
46
+ declare function downloadModel(options?: DownloadOptions): Promise<string>;
47
+ /**
48
+ * Format bytes to human readable string
49
+ * @internal Exported for testing
50
+ */
51
+ declare function formatBytes(bytes: number): string;
52
+
53
+ /**
54
+ * whisper-coreml
55
+ *
56
+ * OpenAI Whisper ASR for Node.js with CoreML/ANE acceleration on Apple Silicon.
57
+ * Based on whisper.cpp with Apple Neural Engine support.
58
+ *
59
+ * Uses the large-v3-turbo model exclusively, as it offers the best speed/quality
60
+ * ratio and is the main reason to choose Whisper over Parakeet.
61
+ */
62
+ /**
63
+ * Check if Whisper ASR is available on this platform
64
+ */
65
+ declare function isAvailable(): boolean;
66
+ /**
67
+ * Get the load error if the addon failed to load
68
+ */
69
+ declare function getLoadError(): Error | null;
70
+ /**
71
+ * Transcription segment with timestamps
72
+ */
73
+ interface TranscriptionSegment {
74
+ /** Start time in milliseconds */
75
+ startMs: number;
76
+ /** End time in milliseconds */
77
+ endMs: number;
78
+ /** Transcribed text for this segment */
79
+ text: string;
80
+ /** Confidence score (0-1) */
81
+ confidence: number;
82
+ }
83
+ /**
84
+ * Transcription result
85
+ */
86
+ interface TranscriptionResult {
87
+ /** Full transcribed text */
88
+ text: string;
89
+ /** Detected or specified language (ISO code) */
90
+ language: string;
91
+ /** Processing time in milliseconds */
92
+ durationMs: number;
93
+ /** Individual segments with timestamps */
94
+ segments: TranscriptionSegment[];
95
+ }
96
+ /**
97
+ * Whisper ASR engine options
98
+ */
99
+ interface WhisperAsrOptions {
100
+ /** Path to the Whisper model file (ggml format) */
101
+ modelPath: string;
102
+ /** Language code (e.g., "en", "de", "fr") or "auto" for auto-detection */
103
+ language?: string;
104
+ /** Translate to English (default: false) */
105
+ translate?: boolean;
106
+ /** Number of threads (0 = auto) */
107
+ threads?: number;
108
+ }
109
+ /**
110
+ * Whisper ASR Engine with CoreML acceleration
111
+ *
112
+ * Uses the large-v3-turbo model for best speed/quality balance.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * import { WhisperAsrEngine, getModelPath } from "whisper-coreml"
117
+ *
118
+ * const engine = new WhisperAsrEngine({
119
+ * modelPath: getModelPath()
120
+ * })
121
+ *
122
+ * await engine.initialize()
123
+ * const result = await engine.transcribe(audioSamples, 16000)
124
+ * console.log(result.text)
125
+ * ```
126
+ */
127
+ declare class WhisperAsrEngine {
128
+ private options;
129
+ private initialized;
130
+ constructor(options: WhisperAsrOptions);
131
+ /**
132
+ * Initialize the Whisper engine
133
+ * This loads the model into memory - may take a few seconds.
134
+ */
135
+ initialize(): Promise<void>;
136
+ /**
137
+ * Check if the engine is ready for transcription
138
+ */
139
+ isReady(): boolean;
140
+ /**
141
+ * Transcribe audio samples
142
+ *
143
+ * @param samples - Float32Array of audio samples (mono, 16kHz)
144
+ * @param sampleRate - Sample rate in Hz (default: 16000)
145
+ * @returns Transcription result with text and segments
146
+ */
147
+ transcribe(samples: Float32Array, sampleRate?: number): Promise<TranscriptionResult>;
148
+ /**
149
+ * Clean up resources and unload the model
150
+ */
151
+ cleanup(): void;
152
+ /**
153
+ * Get version information
154
+ */
155
+ getVersion(): {
156
+ addon: string;
157
+ whisper: string;
158
+ coreml: string;
159
+ };
160
+ }
161
+
162
+ export { type DownloadOptions, type TranscriptionResult, type TranscriptionSegment, WHISPER_MODEL, WhisperAsrEngine, type WhisperAsrOptions, downloadModel, formatBytes, getDefaultModelDir, getLoadError, getModelPath, isAvailable, isModelDownloaded };
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import {
2
+ WHISPER_MODEL,
3
+ WhisperAsrEngine,
4
+ downloadModel,
5
+ formatBytes,
6
+ getDefaultModelDir,
7
+ getLoadError,
8
+ getModelPath,
9
+ isAvailable,
10
+ isModelDownloaded
11
+ } from "./chunk-MOQMN4DX.js";
12
+ export {
13
+ WHISPER_MODEL,
14
+ WhisperAsrEngine,
15
+ downloadModel,
16
+ formatBytes,
17
+ getDefaultModelDir,
18
+ getLoadError,
19
+ getModelPath,
20
+ isAvailable,
21
+ isModelDownloaded
22
+ };
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}