@amaster.ai/asr-http-client 1.0.0-beta.15 → 1.0.0-beta.16
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 +8 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
30
|
// src/asr-http-client.ts
|
|
31
31
|
var ASR_HTTP_PATH = "/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions";
|
|
32
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
|
+
}
|
|
33
36
|
const sampleRate = 16e3;
|
|
34
37
|
const audioChunks = [];
|
|
35
38
|
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
@@ -72,13 +75,13 @@ async function recognizeBlob(blob) {
|
|
|
72
75
|
method: "POST",
|
|
73
76
|
headers: { "Content-Type": "application/json" },
|
|
74
77
|
body: JSON.stringify({
|
|
75
|
-
model: "
|
|
78
|
+
model: "qwen3-asr-flash",
|
|
76
79
|
messages: [{
|
|
77
80
|
role: "user",
|
|
78
|
-
content: [
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
]
|
|
81
|
+
content: [{
|
|
82
|
+
type: "input_audio",
|
|
83
|
+
input_audio: { data: `data:audio/wav;base64,${base64}` }
|
|
84
|
+
}]
|
|
82
85
|
}]
|
|
83
86
|
})
|
|
84
87
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 * @example const text = await record(3000) // record 3 seconds\n */\nexport async function record(durationMs: number = 3000): Promise<string> {\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: 'qwen2-audio-instruct',\n messages: [{\n role: 'user',\n content: [\n { type: 'input_audio', input_audio: { data: base64, format: 'wav' } },\n { type: 'text', text: '请将音频转为文字' }\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"]}
|
|
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
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* Simple record and recognize
|
|
6
|
+
* @param durationMs - Recording duration in milliseconds (default 3000)
|
|
7
|
+
* @returns Recognized text string
|
|
6
8
|
* @example const text = await record(3000) // record 3 seconds
|
|
7
9
|
*/
|
|
8
10
|
declare function record(durationMs?: number): Promise<string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* Simple record and recognize
|
|
6
|
+
* @param durationMs - Recording duration in milliseconds (default 3000)
|
|
7
|
+
* @returns Recognized text string
|
|
6
8
|
* @example const text = await record(3000) // record 3 seconds
|
|
7
9
|
*/
|
|
8
10
|
declare function record(durationMs?: number): Promise<string>;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/asr-http-client.ts
|
|
2
2
|
var ASR_HTTP_PATH = "/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions";
|
|
3
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
|
+
}
|
|
4
7
|
const sampleRate = 16e3;
|
|
5
8
|
const audioChunks = [];
|
|
6
9
|
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
@@ -43,13 +46,13 @@ async function recognizeBlob(blob) {
|
|
|
43
46
|
method: "POST",
|
|
44
47
|
headers: { "Content-Type": "application/json" },
|
|
45
48
|
body: JSON.stringify({
|
|
46
|
-
model: "
|
|
49
|
+
model: "qwen3-asr-flash",
|
|
47
50
|
messages: [{
|
|
48
51
|
role: "user",
|
|
49
|
-
content: [
|
|
50
|
-
|
|
51
|
-
{
|
|
52
|
-
]
|
|
52
|
+
content: [{
|
|
53
|
+
type: "input_audio",
|
|
54
|
+
input_audio: { data: `data:audio/wav;base64,${base64}` }
|
|
55
|
+
}]
|
|
53
56
|
}]
|
|
54
57
|
})
|
|
55
58
|
});
|
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\nconst ASR_HTTP_PATH = '/api/proxy/builtin/platform/qwen-asr/compatible-mode/v1/chat/completions';\n\n/**\n * Simple record and recognize\n * @example const text = await record(3000) // record 3 seconds\n */\nexport async function record(durationMs: number = 3000): Promise<string> {\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: 'qwen2-audio-instruct',\n messages: [{\n role: 'user',\n content: [\n { type: 'input_audio', input_audio: { data: base64, format: 'wav' } },\n { type: 'text', text: '请将音频转为文字' }\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"]}
|
|
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"]}
|