@amaster.ai/asr-http-client 1.0.0-beta.2 → 1.0.0-beta.22

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 CHANGED
@@ -20,14 +20,109 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- createASRHttpClient: () => createASRHttpClient
23
+ createASRHttpClient: () => createASRHttpClient,
24
+ createAsrHttpClient: () => createASRHttpClient,
25
+ recognizeFile: () => recognizeFile,
26
+ record: () => record
24
27
  });
25
28
  module.exports = __toCommonJS(index_exports);
26
29
 
27
30
  // src/asr-http-client.ts
31
+ var ASR_HTTP_PATH = "/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions";
32
+ async function record(durationMs = 3e3) {
33
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
34
+ throw new Error("Microphone not supported. Requires HTTPS and browser with MediaDevices API.");
35
+ }
36
+ const sampleRate = 16e3;
37
+ const audioChunks = [];
38
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
39
+ audio: { sampleRate, channelCount: 1, echoCancellation: true }
40
+ });
41
+ const audioContext = new AudioContext({ sampleRate });
42
+ const source = audioContext.createMediaStreamSource(mediaStream);
43
+ const processor = audioContext.createScriptProcessor(4096, 1, 1);
44
+ processor.onaudioprocess = (e) => {
45
+ const input = e.inputBuffer.getChannelData(0);
46
+ const pcm = new Int16Array(input.length);
47
+ for (let i = 0; i < input.length; i++) {
48
+ const s = Math.max(-1, Math.min(1, input[i]));
49
+ pcm[i] = s < 0 ? s * 32768 : s * 32767;
50
+ }
51
+ audioChunks.push(pcm);
52
+ };
53
+ source.connect(processor);
54
+ processor.connect(audioContext.destination);
55
+ await new Promise((resolve) => setTimeout(resolve, durationMs));
56
+ mediaStream.getTracks().forEach((t) => t.stop());
57
+ processor.disconnect();
58
+ audioContext.close();
59
+ const totalLength = audioChunks.reduce((acc, chunk) => acc + chunk.length, 0);
60
+ const combined = new Int16Array(totalLength);
61
+ let offset = 0;
62
+ for (const chunk of audioChunks) {
63
+ combined.set(chunk, offset);
64
+ offset += chunk.length;
65
+ }
66
+ const wavBlob = createWavBlob(combined, sampleRate);
67
+ return recognizeBlob(wavBlob);
68
+ }
69
+ async function recognizeFile(file) {
70
+ return recognizeBlob(file);
71
+ }
72
+ async function recognizeBlob(blob) {
73
+ const base64 = await blobToBase64(blob);
74
+ const response = await fetch(ASR_HTTP_PATH, {
75
+ method: "POST",
76
+ headers: { "Content-Type": "application/json" },
77
+ body: JSON.stringify({
78
+ model: "qwen3-asr-flash",
79
+ messages: [{
80
+ role: "user",
81
+ content: [{
82
+ type: "input_audio",
83
+ input_audio: { data: `data:audio/wav;base64,${base64}` }
84
+ }]
85
+ }]
86
+ })
87
+ });
88
+ const data = await response.json();
89
+ return data.choices?.[0]?.message?.content || "";
90
+ }
91
+ function blobToBase64(blob) {
92
+ return new Promise((resolve, reject) => {
93
+ const reader = new FileReader();
94
+ reader.onloadend = () => {
95
+ const result = reader.result;
96
+ resolve(result.split(",")[1] || "");
97
+ };
98
+ reader.onerror = reject;
99
+ reader.readAsDataURL(blob);
100
+ });
101
+ }
102
+ function createWavBlob(pcm, sampleRate) {
103
+ const buffer = new ArrayBuffer(44 + pcm.length * 2);
104
+ const view = new DataView(buffer);
105
+ const writeString = (offset, str) => {
106
+ for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));
107
+ };
108
+ writeString(0, "RIFF");
109
+ view.setUint32(4, 36 + pcm.length * 2, true);
110
+ writeString(8, "WAVE");
111
+ writeString(12, "fmt ");
112
+ view.setUint32(16, 16, true);
113
+ view.setUint16(20, 1, true);
114
+ view.setUint16(22, 1, true);
115
+ view.setUint32(24, sampleRate, true);
116
+ view.setUint32(28, sampleRate * 2, true);
117
+ view.setUint16(32, 2, true);
118
+ view.setUint16(34, 16, true);
119
+ writeString(36, "data");
120
+ view.setUint32(40, pcm.length * 2, true);
121
+ for (let i = 0; i < pcm.length; i++) view.setInt16(44 + i * 2, pcm[i], true);
122
+ return new Blob([buffer], { type: "audio/wav" });
123
+ }
28
124
  function createASRHttpClient(config) {
29
125
  const {
30
- url,
31
126
  language = "zh",
32
127
  sampleRate = 16e3,
33
128
  onRecordingStart,
@@ -93,20 +188,20 @@ function createASRHttpClient(config) {
93
188
  offset += chunk.length;
94
189
  }
95
190
  audioChunks = [];
96
- const wavBlob = createWavBlob(combined, sampleRate);
97
- return recognizeBlob(wavBlob);
191
+ const wavBlob = createWavBlob2(combined, sampleRate);
192
+ return recognizeBlob2(wavBlob);
98
193
  }
99
194
  async function recordAndRecognize(durationMs) {
100
195
  await startRecording();
101
196
  await new Promise((resolve) => setTimeout(resolve, durationMs));
102
197
  return stopRecording();
103
198
  }
104
- async function recognizeFile(file) {
105
- return recognizeBlob(file);
199
+ async function recognizeFile2(file) {
200
+ return recognizeBlob2(file);
106
201
  }
107
202
  async function recognizeUrl(audioUrl) {
108
203
  try {
109
- const response = await fetch(url, {
204
+ const response = await fetch(ASR_HTTP_PATH, {
110
205
  method: "POST",
111
206
  headers: { "Content-Type": "application/json" },
112
207
  body: JSON.stringify({
@@ -129,7 +224,7 @@ function createASRHttpClient(config) {
129
224
  throw err;
130
225
  }
131
226
  }
132
- async function recognizeBlob(blob) {
227
+ async function recognizeBlob2(blob) {
133
228
  try {
134
229
  const arrayBuffer = await blob.arrayBuffer();
135
230
  const bytes = new Uint8Array(arrayBuffer);
@@ -139,7 +234,7 @@ function createASRHttpClient(config) {
139
234
  }
140
235
  const base64 = btoa(binary);
141
236
  const dataUrl = `data:audio/wav;base64,${base64}`;
142
- const response = await fetch(url, {
237
+ const response = await fetch(ASR_HTTP_PATH, {
143
238
  method: "POST",
144
239
  headers: { "Content-Type": "application/json" },
145
240
  body: JSON.stringify({
@@ -162,7 +257,7 @@ function createASRHttpClient(config) {
162
257
  throw err;
163
258
  }
164
259
  }
165
- function createWavBlob(samples, rate) {
260
+ function createWavBlob2(samples, rate) {
166
261
  const buffer = new ArrayBuffer(44 + samples.length * 2);
167
262
  const view = new DataView(buffer);
168
263
  const writeString = (offset2, str) => {
@@ -193,12 +288,15 @@ function createASRHttpClient(config) {
193
288
  startRecording,
194
289
  stopRecording,
195
290
  recordAndRecognize,
196
- recognizeFile,
291
+ recognizeFile: recognizeFile2,
197
292
  recognizeUrl
198
293
  };
199
294
  }
200
295
  // Annotate the CommonJS export names for ESM import in node:
201
296
  0 && (module.exports = {
202
- createASRHttpClient
297
+ createASRHttpClient,
298
+ createAsrHttpClient,
299
+ recognizeFile,
300
+ record
203
301
  });
204
302
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/asr-http-client.ts"],"sourcesContent":["export type { ASRHttpClient, ASRHttpClientConfig } from './asr-http-client';\nexport { createASRHttpClient } from './asr-http-client';\n","/**\n * HTTP ASR Client - Press-to-talk style speech recognition\n */\n\nexport interface ASRHttpClientConfig {\n /** API endpoint URL */\n url: string;\n /** Language, default 'zh' */\n language?: string;\n /** Sample rate, default 16000 */\n sampleRate?: number;\n /** Called when recording starts */\n onRecordingStart?: () => void;\n /** Called when recording stops */\n onRecordingStop?: () => void;\n /** Called with recognition result */\n onResult?: (text: string) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\nexport interface ASRHttpClient {\n /** Start recording (press-to-talk) */\n startRecording(): Promise<void>;\n /** Stop recording and get result */\n stopRecording(): Promise<string>;\n /** Record for specific duration then recognize */\n recordAndRecognize(durationMs: number): Promise<string>;\n /** Recognize audio file (File or Blob) */\n recognizeFile(file: File | Blob): Promise<string>;\n /** Recognize audio from URL */\n recognizeUrl(audioUrl: string): Promise<string>;\n}\n\nexport function createASRHttpClient(config: ASRHttpClientConfig): ASRHttpClient {\n const {\n url,\n language = 'zh',\n sampleRate = 16000,\n onRecordingStart,\n onRecordingStop,\n onResult,\n onError,\n } = config;\n\n let mediaStream: MediaStream | null = null;\n let audioContext: AudioContext | null = null;\n let processor: ScriptProcessorNode | null = null;\n let audioChunks: Int16Array[] = [];\n let isRecording = false;\n\n async function startRecording(): Promise<void> {\n if (isRecording) return;\n\n try {\n mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { sampleRate, channelCount: 1, echoCancellation: true }\n });\n\n audioContext = new AudioContext({ sampleRate });\n const source = audioContext.createMediaStreamSource(mediaStream);\n processor = audioContext.createScriptProcessor(4096, 1, 1);\n audioChunks = [];\n\n processor.onaudioprocess = (e) => {\n if (!isRecording) return;\n const inputData = e.inputBuffer.getChannelData(0);\n const pcm = new Int16Array(inputData.length);\n for (let i = 0; i < inputData.length; i++) {\n const s = Math.max(-1, Math.min(1, inputData[i]));\n pcm[i] = s < 0 ? s * 32768 : s * 32767;\n }\n audioChunks.push(pcm);\n };\n\n source.connect(processor);\n processor.connect(audioContext.destination);\n isRecording = true;\n onRecordingStart?.();\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function stopRecording(): Promise<string> {\n if (!isRecording) throw new Error('Not recording');\n\n isRecording = false;\n onRecordingStop?.();\n\n // Stop media\n if (mediaStream) {\n mediaStream.getTracks().forEach(t => t.stop());\n mediaStream = null;\n }\n if (processor) {\n processor.disconnect();\n processor = null;\n }\n if (audioContext) {\n await audioContext.close();\n audioContext = null;\n }\n\n // Combine audio chunks\n const totalLength = audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Int16Array(totalLength);\n let offset = 0;\n for (const chunk of audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n audioChunks = [];\n\n // Convert to WAV\n const wavBlob = createWavBlob(combined, sampleRate);\n return recognizeBlob(wavBlob);\n }\n\n async function recordAndRecognize(durationMs: number): Promise<string> {\n await startRecording();\n await new Promise(resolve => setTimeout(resolve, durationMs));\n return stopRecording();\n }\n\n async function recognizeFile(file: File | Blob): Promise<string> {\n return recognizeBlob(file);\n }\n\n async function recognizeUrl(audioUrl: string): Promise<string> {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { url: audioUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function recognizeBlob(blob: Blob): Promise<string> {\n try {\n // Convert to base64\n const arrayBuffer = await blob.arrayBuffer();\n const bytes = new Uint8Array(arrayBuffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n const dataUrl = `data:audio/wav;base64,${base64}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { data: dataUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n function createWavBlob(samples: Int16Array, rate: number): Blob {\n const buffer = new ArrayBuffer(44 + samples.length * 2);\n const view = new DataView(buffer);\n\n // WAV header\n const writeString = (offset: number, str: string) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n };\n\n writeString(0, 'RIFF');\n view.setUint32(4, 36 + samples.length * 2, true);\n writeString(8, 'WAVE');\n writeString(12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, rate, true);\n view.setUint32(28, rate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeString(36, 'data');\n view.setUint32(40, samples.length * 2, true);\n\n // Audio data\n const offset = 44;\n for (let i = 0; i < samples.length; i++) {\n view.setInt16(offset + i * 2, samples[i], true);\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n }\n\n return {\n startRecording,\n stopRecording,\n recordAndRecognize,\n recognizeFile,\n recognizeUrl,\n };\n}\n"],"mappings":"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","names":["offset"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/asr-http-client.ts"],"sourcesContent":["export { record, recognizeFile } from './asr-http-client';\nexport type { ASRHttpClient, ASRHttpClientConfig } from './asr-http-client';\nexport { createASRHttpClient, createASRHttpClient as createAsrHttpClient } from './asr-http-client';\n","/**\n * HTTP ASR Client - Press-to-talk style speech recognition\n */\n\nconst ASR_HTTP_PATH = '/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions';\n\n/**\n * Simple record and recognize\n * @param durationMs - Recording duration in milliseconds (default 3000)\n * @returns Recognized text string\n * @example const text = await record(3000) // record 3 seconds\n */\nexport async function record(durationMs: number = 3000): Promise<string> {\n // Check browser support\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n throw new Error('Microphone not supported. Requires HTTPS and browser with MediaDevices API.');\n }\n\n const sampleRate = 16000;\n const audioChunks: Int16Array[] = [];\n\n const mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { sampleRate, channelCount: 1, echoCancellation: true }\n });\n\n const audioContext = new AudioContext({ sampleRate });\n const source = audioContext.createMediaStreamSource(mediaStream);\n const processor = audioContext.createScriptProcessor(4096, 1, 1);\n\n processor.onaudioprocess = (e) => {\n const input = e.inputBuffer.getChannelData(0);\n const pcm = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n pcm[i] = s < 0 ? s * 32768 : s * 32767;\n }\n audioChunks.push(pcm);\n };\n\n source.connect(processor);\n processor.connect(audioContext.destination);\n\n // Wait for duration\n await new Promise(resolve => setTimeout(resolve, durationMs));\n\n // Stop\n mediaStream.getTracks().forEach(t => t.stop());\n processor.disconnect();\n audioContext.close();\n\n // Convert to WAV and send\n const totalLength = audioChunks.reduce((acc, chunk) => acc + chunk.length, 0);\n const combined = new Int16Array(totalLength);\n let offset = 0;\n for (const chunk of audioChunks) { combined.set(chunk, offset); offset += chunk.length; }\n\n const wavBlob = createWavBlob(combined, sampleRate);\n return recognizeBlob(wavBlob);\n}\n\n/** Recognize audio file */\nexport async function recognizeFile(file: File | Blob): Promise<string> {\n return recognizeBlob(file);\n}\n\nasync function recognizeBlob(blob: Blob): Promise<string> {\n const base64 = await blobToBase64(blob);\n const response = await fetch(ASR_HTTP_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{\n type: 'input_audio',\n input_audio: { data: `data:audio/wav;base64,${base64}` }\n }]\n }]\n })\n });\n const data = await response.json();\n return data.choices?.[0]?.message?.content || '';\n}\n\nfunction blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1] || '');\n };\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n}\n\nfunction createWavBlob(pcm: Int16Array, sampleRate: number): Blob {\n const buffer = new ArrayBuffer(44 + pcm.length * 2);\n const view = new DataView(buffer);\n const writeString = (offset: number, str: string) => {\n for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));\n };\n writeString(0, 'RIFF');\n view.setUint32(4, 36 + pcm.length * 2, true);\n writeString(8, 'WAVE');\n writeString(12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeString(36, 'data');\n view.setUint32(40, pcm.length * 2, true);\n for (let i = 0; i < pcm.length; i++) view.setInt16(44 + i * 2, pcm[i], true);\n return new Blob([buffer], { type: 'audio/wav' });\n}\n\nexport interface ASRHttpClientConfig {\n /** Language, default 'zh' */\n language?: string;\n /** Sample rate, default 16000 */\n sampleRate?: number;\n /** Called when recording starts */\n onRecordingStart?: () => void;\n /** Called when recording stops */\n onRecordingStop?: () => void;\n /** Called with recognition result */\n onResult?: (text: string) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\nexport interface ASRHttpClient {\n /** Start recording (press-to-talk) */\n startRecording(): Promise<void>;\n /** Stop recording and get result */\n stopRecording(): Promise<string>;\n /** Record for specific duration then recognize */\n recordAndRecognize(durationMs: number): Promise<string>;\n /** Recognize audio file (File or Blob) */\n recognizeFile(file: File | Blob): Promise<string>;\n /** Recognize audio from URL */\n recognizeUrl(audioUrl: string): Promise<string>;\n}\n\nexport function createASRHttpClient(config: ASRHttpClientConfig): ASRHttpClient {\n const {\n language = 'zh',\n sampleRate = 16000,\n onRecordingStart,\n onRecordingStop,\n onResult,\n onError,\n } = config;\n\n let mediaStream: MediaStream | null = null;\n let audioContext: AudioContext | null = null;\n let processor: ScriptProcessorNode | null = null;\n let audioChunks: Int16Array[] = [];\n let isRecording = false;\n\n async function startRecording(): Promise<void> {\n if (isRecording) return;\n\n try {\n mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { sampleRate, channelCount: 1, echoCancellation: true }\n });\n\n audioContext = new AudioContext({ sampleRate });\n const source = audioContext.createMediaStreamSource(mediaStream);\n processor = audioContext.createScriptProcessor(4096, 1, 1);\n audioChunks = [];\n\n processor.onaudioprocess = (e) => {\n if (!isRecording) return;\n const inputData = e.inputBuffer.getChannelData(0);\n const pcm = new Int16Array(inputData.length);\n for (let i = 0; i < inputData.length; i++) {\n const s = Math.max(-1, Math.min(1, inputData[i]));\n pcm[i] = s < 0 ? s * 32768 : s * 32767;\n }\n audioChunks.push(pcm);\n };\n\n source.connect(processor);\n processor.connect(audioContext.destination);\n isRecording = true;\n onRecordingStart?.();\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function stopRecording(): Promise<string> {\n if (!isRecording) throw new Error('Not recording');\n\n isRecording = false;\n onRecordingStop?.();\n\n // Stop media\n if (mediaStream) {\n mediaStream.getTracks().forEach(t => t.stop());\n mediaStream = null;\n }\n if (processor) {\n processor.disconnect();\n processor = null;\n }\n if (audioContext) {\n await audioContext.close();\n audioContext = null;\n }\n\n // Combine audio chunks\n const totalLength = audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Int16Array(totalLength);\n let offset = 0;\n for (const chunk of audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n audioChunks = [];\n\n // Convert to WAV\n const wavBlob = createWavBlob(combined, sampleRate);\n return recognizeBlob(wavBlob);\n }\n\n async function recordAndRecognize(durationMs: number): Promise<string> {\n await startRecording();\n await new Promise(resolve => setTimeout(resolve, durationMs));\n return stopRecording();\n }\n\n async function recognizeFile(file: File | Blob): Promise<string> {\n return recognizeBlob(file);\n }\n\n async function recognizeUrl(audioUrl: string): Promise<string> {\n try {\n const response = await fetch(ASR_HTTP_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { url: audioUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function recognizeBlob(blob: Blob): Promise<string> {\n try {\n // Convert to base64\n const arrayBuffer = await blob.arrayBuffer();\n const bytes = new Uint8Array(arrayBuffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n const dataUrl = `data:audio/wav;base64,${base64}`;\n\n const response = await fetch(ASR_HTTP_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { data: dataUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n function createWavBlob(samples: Int16Array, rate: number): Blob {\n const buffer = new ArrayBuffer(44 + samples.length * 2);\n const view = new DataView(buffer);\n\n // WAV header\n const writeString = (offset: number, str: string) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n };\n\n writeString(0, 'RIFF');\n view.setUint32(4, 36 + samples.length * 2, true);\n writeString(8, 'WAVE');\n writeString(12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, rate, true);\n view.setUint32(28, rate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeString(36, 'data');\n view.setUint32(40, samples.length * 2, true);\n\n // Audio data\n const offset = 44;\n for (let i = 0; i < samples.length; i++) {\n view.setInt16(offset + i * 2, samples[i], true);\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n }\n\n return {\n startRecording,\n stopRecording,\n recordAndRecognize,\n recognizeFile,\n recognizeUrl,\n };\n}\n"],"mappings":"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","names":["createWavBlob","recognizeBlob","recognizeFile","offset"]}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,16 @@
1
1
  /**
2
2
  * HTTP ASR Client - Press-to-talk style speech recognition
3
3
  */
4
+ /**
5
+ * Simple record and recognize
6
+ * @param durationMs - Recording duration in milliseconds (default 3000)
7
+ * @returns Recognized text string
8
+ * @example const text = await record(3000) // record 3 seconds
9
+ */
10
+ declare function record(durationMs?: number): Promise<string>;
11
+ /** Recognize audio file */
12
+ declare function recognizeFile(file: File | Blob): Promise<string>;
4
13
  interface ASRHttpClientConfig {
5
- /** API endpoint URL */
6
- url: string;
7
14
  /** Language, default 'zh' */
8
15
  language?: string;
9
16
  /** Sample rate, default 16000 */
@@ -31,4 +38,4 @@ interface ASRHttpClient {
31
38
  }
32
39
  declare function createASRHttpClient(config: ASRHttpClientConfig): ASRHttpClient;
33
40
 
34
- export { type ASRHttpClient, type ASRHttpClientConfig, createASRHttpClient };
41
+ export { type ASRHttpClient, type ASRHttpClientConfig, createASRHttpClient, createASRHttpClient as createAsrHttpClient, recognizeFile, record };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  /**
2
2
  * HTTP ASR Client - Press-to-talk style speech recognition
3
3
  */
4
+ /**
5
+ * Simple record and recognize
6
+ * @param durationMs - Recording duration in milliseconds (default 3000)
7
+ * @returns Recognized text string
8
+ * @example const text = await record(3000) // record 3 seconds
9
+ */
10
+ declare function record(durationMs?: number): Promise<string>;
11
+ /** Recognize audio file */
12
+ declare function recognizeFile(file: File | Blob): Promise<string>;
4
13
  interface ASRHttpClientConfig {
5
- /** API endpoint URL */
6
- url: string;
7
14
  /** Language, default 'zh' */
8
15
  language?: string;
9
16
  /** Sample rate, default 16000 */
@@ -31,4 +38,4 @@ interface ASRHttpClient {
31
38
  }
32
39
  declare function createASRHttpClient(config: ASRHttpClientConfig): ASRHttpClient;
33
40
 
34
- export { type ASRHttpClient, type ASRHttpClientConfig, createASRHttpClient };
41
+ export { type ASRHttpClient, type ASRHttpClientConfig, createASRHttpClient, createASRHttpClient as createAsrHttpClient, recognizeFile, record };
package/dist/index.js CHANGED
@@ -1,7 +1,99 @@
1
1
  // src/asr-http-client.ts
2
+ var ASR_HTTP_PATH = "/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions";
3
+ async function record(durationMs = 3e3) {
4
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
5
+ throw new Error("Microphone not supported. Requires HTTPS and browser with MediaDevices API.");
6
+ }
7
+ const sampleRate = 16e3;
8
+ const audioChunks = [];
9
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
10
+ audio: { sampleRate, channelCount: 1, echoCancellation: true }
11
+ });
12
+ const audioContext = new AudioContext({ sampleRate });
13
+ const source = audioContext.createMediaStreamSource(mediaStream);
14
+ const processor = audioContext.createScriptProcessor(4096, 1, 1);
15
+ processor.onaudioprocess = (e) => {
16
+ const input = e.inputBuffer.getChannelData(0);
17
+ const pcm = new Int16Array(input.length);
18
+ for (let i = 0; i < input.length; i++) {
19
+ const s = Math.max(-1, Math.min(1, input[i]));
20
+ pcm[i] = s < 0 ? s * 32768 : s * 32767;
21
+ }
22
+ audioChunks.push(pcm);
23
+ };
24
+ source.connect(processor);
25
+ processor.connect(audioContext.destination);
26
+ await new Promise((resolve) => setTimeout(resolve, durationMs));
27
+ mediaStream.getTracks().forEach((t) => t.stop());
28
+ processor.disconnect();
29
+ audioContext.close();
30
+ const totalLength = audioChunks.reduce((acc, chunk) => acc + chunk.length, 0);
31
+ const combined = new Int16Array(totalLength);
32
+ let offset = 0;
33
+ for (const chunk of audioChunks) {
34
+ combined.set(chunk, offset);
35
+ offset += chunk.length;
36
+ }
37
+ const wavBlob = createWavBlob(combined, sampleRate);
38
+ return recognizeBlob(wavBlob);
39
+ }
40
+ async function recognizeFile(file) {
41
+ return recognizeBlob(file);
42
+ }
43
+ async function recognizeBlob(blob) {
44
+ const base64 = await blobToBase64(blob);
45
+ const response = await fetch(ASR_HTTP_PATH, {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json" },
48
+ body: JSON.stringify({
49
+ model: "qwen3-asr-flash",
50
+ messages: [{
51
+ role: "user",
52
+ content: [{
53
+ type: "input_audio",
54
+ input_audio: { data: `data:audio/wav;base64,${base64}` }
55
+ }]
56
+ }]
57
+ })
58
+ });
59
+ const data = await response.json();
60
+ return data.choices?.[0]?.message?.content || "";
61
+ }
62
+ function blobToBase64(blob) {
63
+ return new Promise((resolve, reject) => {
64
+ const reader = new FileReader();
65
+ reader.onloadend = () => {
66
+ const result = reader.result;
67
+ resolve(result.split(",")[1] || "");
68
+ };
69
+ reader.onerror = reject;
70
+ reader.readAsDataURL(blob);
71
+ });
72
+ }
73
+ function createWavBlob(pcm, sampleRate) {
74
+ const buffer = new ArrayBuffer(44 + pcm.length * 2);
75
+ const view = new DataView(buffer);
76
+ const writeString = (offset, str) => {
77
+ for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));
78
+ };
79
+ writeString(0, "RIFF");
80
+ view.setUint32(4, 36 + pcm.length * 2, true);
81
+ writeString(8, "WAVE");
82
+ writeString(12, "fmt ");
83
+ view.setUint32(16, 16, true);
84
+ view.setUint16(20, 1, true);
85
+ view.setUint16(22, 1, true);
86
+ view.setUint32(24, sampleRate, true);
87
+ view.setUint32(28, sampleRate * 2, true);
88
+ view.setUint16(32, 2, true);
89
+ view.setUint16(34, 16, true);
90
+ writeString(36, "data");
91
+ view.setUint32(40, pcm.length * 2, true);
92
+ for (let i = 0; i < pcm.length; i++) view.setInt16(44 + i * 2, pcm[i], true);
93
+ return new Blob([buffer], { type: "audio/wav" });
94
+ }
2
95
  function createASRHttpClient(config) {
3
96
  const {
4
- url,
5
97
  language = "zh",
6
98
  sampleRate = 16e3,
7
99
  onRecordingStart,
@@ -67,20 +159,20 @@ function createASRHttpClient(config) {
67
159
  offset += chunk.length;
68
160
  }
69
161
  audioChunks = [];
70
- const wavBlob = createWavBlob(combined, sampleRate);
71
- return recognizeBlob(wavBlob);
162
+ const wavBlob = createWavBlob2(combined, sampleRate);
163
+ return recognizeBlob2(wavBlob);
72
164
  }
73
165
  async function recordAndRecognize(durationMs) {
74
166
  await startRecording();
75
167
  await new Promise((resolve) => setTimeout(resolve, durationMs));
76
168
  return stopRecording();
77
169
  }
78
- async function recognizeFile(file) {
79
- return recognizeBlob(file);
170
+ async function recognizeFile2(file) {
171
+ return recognizeBlob2(file);
80
172
  }
81
173
  async function recognizeUrl(audioUrl) {
82
174
  try {
83
- const response = await fetch(url, {
175
+ const response = await fetch(ASR_HTTP_PATH, {
84
176
  method: "POST",
85
177
  headers: { "Content-Type": "application/json" },
86
178
  body: JSON.stringify({
@@ -103,7 +195,7 @@ function createASRHttpClient(config) {
103
195
  throw err;
104
196
  }
105
197
  }
106
- async function recognizeBlob(blob) {
198
+ async function recognizeBlob2(blob) {
107
199
  try {
108
200
  const arrayBuffer = await blob.arrayBuffer();
109
201
  const bytes = new Uint8Array(arrayBuffer);
@@ -113,7 +205,7 @@ function createASRHttpClient(config) {
113
205
  }
114
206
  const base64 = btoa(binary);
115
207
  const dataUrl = `data:audio/wav;base64,${base64}`;
116
- const response = await fetch(url, {
208
+ const response = await fetch(ASR_HTTP_PATH, {
117
209
  method: "POST",
118
210
  headers: { "Content-Type": "application/json" },
119
211
  body: JSON.stringify({
@@ -136,7 +228,7 @@ function createASRHttpClient(config) {
136
228
  throw err;
137
229
  }
138
230
  }
139
- function createWavBlob(samples, rate) {
231
+ function createWavBlob2(samples, rate) {
140
232
  const buffer = new ArrayBuffer(44 + samples.length * 2);
141
233
  const view = new DataView(buffer);
142
234
  const writeString = (offset2, str) => {
@@ -167,11 +259,14 @@ function createASRHttpClient(config) {
167
259
  startRecording,
168
260
  stopRecording,
169
261
  recordAndRecognize,
170
- recognizeFile,
262
+ recognizeFile: recognizeFile2,
171
263
  recognizeUrl
172
264
  };
173
265
  }
174
266
  export {
175
- createASRHttpClient
267
+ createASRHttpClient,
268
+ createASRHttpClient as createAsrHttpClient,
269
+ recognizeFile,
270
+ record
176
271
  };
177
272
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/asr-http-client.ts"],"sourcesContent":["/**\n * HTTP ASR Client - Press-to-talk style speech recognition\n */\n\nexport interface ASRHttpClientConfig {\n /** API endpoint URL */\n url: string;\n /** Language, default 'zh' */\n language?: string;\n /** Sample rate, default 16000 */\n sampleRate?: number;\n /** Called when recording starts */\n onRecordingStart?: () => void;\n /** Called when recording stops */\n onRecordingStop?: () => void;\n /** Called with recognition result */\n onResult?: (text: string) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\nexport interface ASRHttpClient {\n /** Start recording (press-to-talk) */\n startRecording(): Promise<void>;\n /** Stop recording and get result */\n stopRecording(): Promise<string>;\n /** Record for specific duration then recognize */\n recordAndRecognize(durationMs: number): Promise<string>;\n /** Recognize audio file (File or Blob) */\n recognizeFile(file: File | Blob): Promise<string>;\n /** Recognize audio from URL */\n recognizeUrl(audioUrl: string): Promise<string>;\n}\n\nexport function createASRHttpClient(config: ASRHttpClientConfig): ASRHttpClient {\n const {\n url,\n language = 'zh',\n sampleRate = 16000,\n onRecordingStart,\n onRecordingStop,\n onResult,\n onError,\n } = config;\n\n let mediaStream: MediaStream | null = null;\n let audioContext: AudioContext | null = null;\n let processor: ScriptProcessorNode | null = null;\n let audioChunks: Int16Array[] = [];\n let isRecording = false;\n\n async function startRecording(): Promise<void> {\n if (isRecording) return;\n\n try {\n mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { sampleRate, channelCount: 1, echoCancellation: true }\n });\n\n audioContext = new AudioContext({ sampleRate });\n const source = audioContext.createMediaStreamSource(mediaStream);\n processor = audioContext.createScriptProcessor(4096, 1, 1);\n audioChunks = [];\n\n processor.onaudioprocess = (e) => {\n if (!isRecording) return;\n const inputData = e.inputBuffer.getChannelData(0);\n const pcm = new Int16Array(inputData.length);\n for (let i = 0; i < inputData.length; i++) {\n const s = Math.max(-1, Math.min(1, inputData[i]));\n pcm[i] = s < 0 ? s * 32768 : s * 32767;\n }\n audioChunks.push(pcm);\n };\n\n source.connect(processor);\n processor.connect(audioContext.destination);\n isRecording = true;\n onRecordingStart?.();\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function stopRecording(): Promise<string> {\n if (!isRecording) throw new Error('Not recording');\n\n isRecording = false;\n onRecordingStop?.();\n\n // Stop media\n if (mediaStream) {\n mediaStream.getTracks().forEach(t => t.stop());\n mediaStream = null;\n }\n if (processor) {\n processor.disconnect();\n processor = null;\n }\n if (audioContext) {\n await audioContext.close();\n audioContext = null;\n }\n\n // Combine audio chunks\n const totalLength = audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Int16Array(totalLength);\n let offset = 0;\n for (const chunk of audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n audioChunks = [];\n\n // Convert to WAV\n const wavBlob = createWavBlob(combined, sampleRate);\n return recognizeBlob(wavBlob);\n }\n\n async function recordAndRecognize(durationMs: number): Promise<string> {\n await startRecording();\n await new Promise(resolve => setTimeout(resolve, durationMs));\n return stopRecording();\n }\n\n async function recognizeFile(file: File | Blob): Promise<string> {\n return recognizeBlob(file);\n }\n\n async function recognizeUrl(audioUrl: string): Promise<string> {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { url: audioUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function recognizeBlob(blob: Blob): Promise<string> {\n try {\n // Convert to base64\n const arrayBuffer = await blob.arrayBuffer();\n const bytes = new Uint8Array(arrayBuffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n const dataUrl = `data:audio/wav;base64,${base64}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { data: dataUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n function createWavBlob(samples: Int16Array, rate: number): Blob {\n const buffer = new ArrayBuffer(44 + samples.length * 2);\n const view = new DataView(buffer);\n\n // WAV header\n const writeString = (offset: number, str: string) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n };\n\n writeString(0, 'RIFF');\n view.setUint32(4, 36 + samples.length * 2, true);\n writeString(8, 'WAVE');\n writeString(12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, rate, true);\n view.setUint32(28, rate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeString(36, 'data');\n view.setUint32(40, samples.length * 2, true);\n\n // Audio data\n const offset = 44;\n for (let i = 0; i < samples.length; i++) {\n view.setInt16(offset + i * 2, samples[i], true);\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n }\n\n return {\n startRecording,\n stopRecording,\n recordAndRecognize,\n recognizeFile,\n recognizeUrl,\n };\n}\n"],"mappings":"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","names":["offset"]}
1
+ {"version":3,"sources":["../src/asr-http-client.ts"],"sourcesContent":["/**\n * HTTP ASR Client - Press-to-talk style speech recognition\n */\n\nconst ASR_HTTP_PATH = '/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions';\n\n/**\n * Simple record and recognize\n * @param durationMs - Recording duration in milliseconds (default 3000)\n * @returns Recognized text string\n * @example const text = await record(3000) // record 3 seconds\n */\nexport async function record(durationMs: number = 3000): Promise<string> {\n // Check browser support\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n throw new Error('Microphone not supported. Requires HTTPS and browser with MediaDevices API.');\n }\n\n const sampleRate = 16000;\n const audioChunks: Int16Array[] = [];\n\n const mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { sampleRate, channelCount: 1, echoCancellation: true }\n });\n\n const audioContext = new AudioContext({ sampleRate });\n const source = audioContext.createMediaStreamSource(mediaStream);\n const processor = audioContext.createScriptProcessor(4096, 1, 1);\n\n processor.onaudioprocess = (e) => {\n const input = e.inputBuffer.getChannelData(0);\n const pcm = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n pcm[i] = s < 0 ? s * 32768 : s * 32767;\n }\n audioChunks.push(pcm);\n };\n\n source.connect(processor);\n processor.connect(audioContext.destination);\n\n // Wait for duration\n await new Promise(resolve => setTimeout(resolve, durationMs));\n\n // Stop\n mediaStream.getTracks().forEach(t => t.stop());\n processor.disconnect();\n audioContext.close();\n\n // Convert to WAV and send\n const totalLength = audioChunks.reduce((acc, chunk) => acc + chunk.length, 0);\n const combined = new Int16Array(totalLength);\n let offset = 0;\n for (const chunk of audioChunks) { combined.set(chunk, offset); offset += chunk.length; }\n\n const wavBlob = createWavBlob(combined, sampleRate);\n return recognizeBlob(wavBlob);\n}\n\n/** Recognize audio file */\nexport async function recognizeFile(file: File | Blob): Promise<string> {\n return recognizeBlob(file);\n}\n\nasync function recognizeBlob(blob: Blob): Promise<string> {\n const base64 = await blobToBase64(blob);\n const response = await fetch(ASR_HTTP_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{\n type: 'input_audio',\n input_audio: { data: `data:audio/wav;base64,${base64}` }\n }]\n }]\n })\n });\n const data = await response.json();\n return data.choices?.[0]?.message?.content || '';\n}\n\nfunction blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1] || '');\n };\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n}\n\nfunction createWavBlob(pcm: Int16Array, sampleRate: number): Blob {\n const buffer = new ArrayBuffer(44 + pcm.length * 2);\n const view = new DataView(buffer);\n const writeString = (offset: number, str: string) => {\n for (let i = 0; i < str.length; i++) view.setUint8(offset + i, str.charCodeAt(i));\n };\n writeString(0, 'RIFF');\n view.setUint32(4, 36 + pcm.length * 2, true);\n writeString(8, 'WAVE');\n writeString(12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeString(36, 'data');\n view.setUint32(40, pcm.length * 2, true);\n for (let i = 0; i < pcm.length; i++) view.setInt16(44 + i * 2, pcm[i], true);\n return new Blob([buffer], { type: 'audio/wav' });\n}\n\nexport interface ASRHttpClientConfig {\n /** Language, default 'zh' */\n language?: string;\n /** Sample rate, default 16000 */\n sampleRate?: number;\n /** Called when recording starts */\n onRecordingStart?: () => void;\n /** Called when recording stops */\n onRecordingStop?: () => void;\n /** Called with recognition result */\n onResult?: (text: string) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\nexport interface ASRHttpClient {\n /** Start recording (press-to-talk) */\n startRecording(): Promise<void>;\n /** Stop recording and get result */\n stopRecording(): Promise<string>;\n /** Record for specific duration then recognize */\n recordAndRecognize(durationMs: number): Promise<string>;\n /** Recognize audio file (File or Blob) */\n recognizeFile(file: File | Blob): Promise<string>;\n /** Recognize audio from URL */\n recognizeUrl(audioUrl: string): Promise<string>;\n}\n\nexport function createASRHttpClient(config: ASRHttpClientConfig): ASRHttpClient {\n const {\n language = 'zh',\n sampleRate = 16000,\n onRecordingStart,\n onRecordingStop,\n onResult,\n onError,\n } = config;\n\n let mediaStream: MediaStream | null = null;\n let audioContext: AudioContext | null = null;\n let processor: ScriptProcessorNode | null = null;\n let audioChunks: Int16Array[] = [];\n let isRecording = false;\n\n async function startRecording(): Promise<void> {\n if (isRecording) return;\n\n try {\n mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { sampleRate, channelCount: 1, echoCancellation: true }\n });\n\n audioContext = new AudioContext({ sampleRate });\n const source = audioContext.createMediaStreamSource(mediaStream);\n processor = audioContext.createScriptProcessor(4096, 1, 1);\n audioChunks = [];\n\n processor.onaudioprocess = (e) => {\n if (!isRecording) return;\n const inputData = e.inputBuffer.getChannelData(0);\n const pcm = new Int16Array(inputData.length);\n for (let i = 0; i < inputData.length; i++) {\n const s = Math.max(-1, Math.min(1, inputData[i]));\n pcm[i] = s < 0 ? s * 32768 : s * 32767;\n }\n audioChunks.push(pcm);\n };\n\n source.connect(processor);\n processor.connect(audioContext.destination);\n isRecording = true;\n onRecordingStart?.();\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function stopRecording(): Promise<string> {\n if (!isRecording) throw new Error('Not recording');\n\n isRecording = false;\n onRecordingStop?.();\n\n // Stop media\n if (mediaStream) {\n mediaStream.getTracks().forEach(t => t.stop());\n mediaStream = null;\n }\n if (processor) {\n processor.disconnect();\n processor = null;\n }\n if (audioContext) {\n await audioContext.close();\n audioContext = null;\n }\n\n // Combine audio chunks\n const totalLength = audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Int16Array(totalLength);\n let offset = 0;\n for (const chunk of audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n audioChunks = [];\n\n // Convert to WAV\n const wavBlob = createWavBlob(combined, sampleRate);\n return recognizeBlob(wavBlob);\n }\n\n async function recordAndRecognize(durationMs: number): Promise<string> {\n await startRecording();\n await new Promise(resolve => setTimeout(resolve, durationMs));\n return stopRecording();\n }\n\n async function recognizeFile(file: File | Blob): Promise<string> {\n return recognizeBlob(file);\n }\n\n async function recognizeUrl(audioUrl: string): Promise<string> {\n try {\n const response = await fetch(ASR_HTTP_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { url: audioUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n async function recognizeBlob(blob: Blob): Promise<string> {\n try {\n // Convert to base64\n const arrayBuffer = await blob.arrayBuffer();\n const bytes = new Uint8Array(arrayBuffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n const dataUrl = `data:audio/wav;base64,${base64}`;\n\n const response = await fetch(ASR_HTTP_PATH, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: 'qwen3-asr-flash',\n messages: [{\n role: 'user',\n content: [{ type: 'input_audio', input_audio: { data: dataUrl } }]\n }]\n })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const text = data.choices?.[0]?.message?.content || '';\n onResult?.(text);\n return text;\n } catch (err) {\n onError?.(err as Error);\n throw err;\n }\n }\n\n function createWavBlob(samples: Int16Array, rate: number): Blob {\n const buffer = new ArrayBuffer(44 + samples.length * 2);\n const view = new DataView(buffer);\n\n // WAV header\n const writeString = (offset: number, str: string) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n };\n\n writeString(0, 'RIFF');\n view.setUint32(4, 36 + samples.length * 2, true);\n writeString(8, 'WAVE');\n writeString(12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, rate, true);\n view.setUint32(28, rate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeString(36, 'data');\n view.setUint32(40, samples.length * 2, true);\n\n // Audio data\n const offset = 44;\n for (let i = 0; i < samples.length; i++) {\n view.setInt16(offset + i * 2, samples[i], true);\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n }\n\n return {\n startRecording,\n stopRecording,\n recordAndRecognize,\n recognizeFile,\n recognizeUrl,\n };\n}\n"],"mappings":"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","names":["createWavBlob","recognizeBlob","recognizeFile","offset"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amaster.ai/asr-http-client",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.22",
4
4
  "description": "HTTP ASR client with press-to-talk recording",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",