univoice 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{base-R8jvH4Ko.d.ts → base-Bll64DMp.d.ts} +7 -1
- package/dist/{base--1Nj-ls3.d.ts → base-C2MmSV6s.d.ts} +1 -0
- package/dist/{chunk-QWT6LQKM.js → chunk-LVWPQ7DG.js} +2 -2
- package/dist/{chunk-QWT6LQKM.js.map → chunk-LVWPQ7DG.js.map} +1 -1
- package/dist/{chunk-J2ZVBKXK.js → chunk-TFXIXMXO.js} +544 -28
- package/dist/chunk-TFXIXMXO.js.map +1 -0
- package/dist/{chunk-NDNOETWJ.js → chunk-XEYV5Z5P.js} +12 -8
- package/dist/chunk-XEYV5Z5P.js.map +1 -0
- package/dist/{save-Cbjwiunv.d.ts → save-OBmee_PS.d.ts} +1 -1
- package/dist/src/asr/index.d.ts +2 -2
- package/dist/src/asr/index.js +1 -1
- package/dist/src/asr/providers/index.d.ts +2 -2
- package/dist/src/asr/providers/index.js +2 -2
- package/dist/src/asr/providers/index.js.map +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.js +3 -3
- package/dist/src/tts/index.d.ts +2 -2
- package/dist/src/tts/index.js +2 -2
- package/dist/src/tts/providers/index.d.ts +8 -1
- package/dist/src/tts/providers/index.js +1 -9
- package/dist/src/tts/providers/index.js.map +1 -1
- package/dist/{tee-DEtN5C9m.d.ts → tee-3KInEaCJ.d.ts} +1 -1
- package/examples/.env.example +3 -0
- package/examples/doubao-tts-speak-string.ts +1 -0
- package/examples/opus-packets-stream-to-asr-demo.ts +145 -0
- package/examples/opus-packets-to-asr-demo.ts +1 -1
- package/examples/qwen-tts-demo.ts +62 -0
- package/examples/qwen-tts-speak-stream-input.ts +120 -0
- package/examples/qwen-tts-speak-string.ts +91 -0
- package/examples/utils/ogg-muxer-stream.ts +413 -0
- package/package.json +18 -4
- package/dist/chunk-J2ZVBKXK.js.map +0 -1
- package/dist/chunk-NDNOETWJ.js.map +0 -1
package/README.md
CHANGED
|
@@ -414,8 +414,8 @@ const tts = createTTS({
|
|
|
414
414
|
const tts = createTTS({
|
|
415
415
|
provider: 'qwen',
|
|
416
416
|
apiKey: process.env.QWEN_API_KEY,
|
|
417
|
-
model: 'cosyvoice-
|
|
418
|
-
voice: '
|
|
417
|
+
model: 'cosyvoice-v3-flash',
|
|
418
|
+
voice: 'longxiaochun_v3',
|
|
419
419
|
format: 'mp3',
|
|
420
420
|
});
|
|
421
421
|
```
|
|
@@ -3,6 +3,8 @@ interface AudioFormat {
|
|
|
3
3
|
bits?: number;
|
|
4
4
|
channel?: number;
|
|
5
5
|
}
|
|
6
|
+
type AudioContainerFormat = 'pcm' | 'wav' | 'ogg' | 'mp3';
|
|
7
|
+
type AudioCodecFormat = 'raw' | 'opus';
|
|
6
8
|
interface ASROptions {
|
|
7
9
|
provider: string;
|
|
8
10
|
apiKey?: string;
|
|
@@ -16,6 +18,8 @@ interface ASROptions {
|
|
|
16
18
|
resourceId?: string;
|
|
17
19
|
mode?: 'streaming' | 'nostream' | 'async';
|
|
18
20
|
audioFormat?: AudioFormat;
|
|
21
|
+
format?: AudioContainerFormat;
|
|
22
|
+
codec?: AudioCodecFormat;
|
|
19
23
|
segmentDuration?: number;
|
|
20
24
|
enableItn?: boolean;
|
|
21
25
|
enablePunc?: boolean;
|
|
@@ -65,6 +69,8 @@ declare abstract class BaseASR {
|
|
|
65
69
|
language: string;
|
|
66
70
|
prompt: string;
|
|
67
71
|
responseFormat: 'json' | 'text' | 'srt' | 'vtt' | 'verbose_json';
|
|
72
|
+
format: AudioContainerFormat;
|
|
73
|
+
codec: AudioCodecFormat;
|
|
68
74
|
constructor(options: ASROptions);
|
|
69
75
|
abstract listenStream(audio: AudioStream): AsyncIterable<ASRStreamChunk>;
|
|
70
76
|
private isAudioStream;
|
|
@@ -81,4 +87,4 @@ declare abstract class BaseASR {
|
|
|
81
87
|
}): Promise<ASRResponse>;
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
export { type ASROptions as A, BaseASR as B, type ListenInstanceOptions as L, type ASRProvider as a, type ASRProviderType as b, type ASRRequest as c, type ASRResponse as d, type ASRSegment as e, type ASRStreamChunk as f, type
|
|
90
|
+
export { type ASROptions as A, BaseASR as B, type ListenInstanceOptions as L, type ASRProvider as a, type ASRProviderType as b, type ASRRequest as c, type ASRResponse as d, type ASRSegment as e, type ASRStreamChunk as f, type AudioCodecFormat as g, type AudioContainerFormat as h, type AudioFormat as i, type AudioStream as j, type AudioStreamInput as k };
|
|
@@ -130,5 +130,5 @@ async function teeAudio(response, options = {}) {
|
|
|
130
130
|
__name(teeAudio, "teeAudio");
|
|
131
131
|
|
|
132
132
|
export { collectAudio, playAudio, saveAudio, saveTTSResponse, teeAudio };
|
|
133
|
-
//# sourceMappingURL=chunk-
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
133
|
+
//# sourceMappingURL=chunk-LVWPQ7DG.js.map
|
|
134
|
+
//# sourceMappingURL=chunk-LVWPQ7DG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tts/utils/collect.ts","../src/tts/utils/play.ts","../src/tts/utils/save.ts","../src/tts/utils/save-audio.ts","../src/tts/utils/tee.ts"],"names":["Buffer","concatUint8Arrays","writeFile"],"mappings":"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","file":"chunk-QWT6LQKM.js","sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface CollectOptions {\n onChunk?: (chunk: Uint8Array) => void;\n onComplete?: (audio: Uint8Array) => void;\n onError?: (error: Error) => void;\n}\n\nexport async function collectAudio(\n response: TTSResponse,\n options: CollectOptions = {}\n): Promise<Uint8Array> {\n const { audio } = response;\n const chunks: Uint8Array[] = [];\n\n if (isUint8Array(audio)) {\n chunks.push(audio);\n } else if (isBuffer(audio)) {\n chunks.push(new Uint8Array(audio));\n }\n\n const result = concatUint8Arrays(chunks);\n\n if (options.onComplete) {\n options.onComplete(result);\n }\n\n return result;\n}\n\nfunction isUint8Array(value: unknown): value is Uint8Array {\n return value instanceof Uint8Array;\n}\n\nfunction isBuffer(value: unknown): value is Buffer {\n return Buffer.isBuffer(value);\n}\n\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n","import { spawn } from 'node:child_process';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface PlayOptions {\n player?: string;\n}\n\nexport async function playAudio(response: TTSResponse, options: PlayOptions = {}): Promise<void> {\n const player = options.player || 'afplay';\n\n let buffer: Buffer;\n if (response.audio instanceof Buffer) {\n buffer = response.audio;\n } else if (response.audio instanceof Uint8Array) {\n buffer = Buffer.from(response.audio);\n } else {\n throw new Error('Invalid audio data');\n }\n\n return new Promise((resolve, reject) => {\n const proc = spawn(player, [], { stdio: ['pipe', 'inherit', 'inherit'] });\n proc.stdin.write(buffer);\n proc.stdin.end();\n proc.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Player exited with code ${code}`));\n }\n });\n proc.on('error', reject);\n });\n}\n","import { writeFile } from 'node:fs/promises';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface SaveOptions {\n filename?: string;\n directory?: string;\n}\n\n/**\n * 保存 TTSResponse 到文件\n * 自动生成文件名,适合快速保存 TTS 响应\n *\n * @param response TTS 响应对象\n * @param options 保存选项\n * @returns 保存的文件路径\n */\nexport async function saveTTSResponse(\n response: TTSResponse,\n options: SaveOptions = {}\n): Promise<string> {\n const { format } = response;\n const timestamp = Date.now();\n const filename = options.filename || `tts_${timestamp}.${format}`;\n const filepath = options.directory ? `${options.directory}/${filename}` : filename;\n\n let buffer: Buffer;\n if (response.audio instanceof Buffer) {\n buffer = response.audio;\n } else if (response.audio instanceof Uint8Array) {\n buffer = Buffer.from(response.audio);\n } else {\n throw new Error('Invalid audio data');\n }\n\n await writeFile(filepath, buffer);\n return filepath;\n}\n","import { writeFile } from 'node:fs/promises';\nimport type { TTSStreamChunk } from '@/types/tts';\n\n/**\n * 判断是否为异步迭代器\n */\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n Symbol.asyncIterator in value &&\n typeof (value as AsyncIterable<unknown>)[Symbol.asyncIterator] === 'function'\n );\n}\n\n/**\n * 判断是否为 TTSStreamChunk 类型\n */\nfunction isTTSStreamChunk(value: unknown): value is TTSStreamChunk {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'audioChunk' in value &&\n (value as TTSStreamChunk).audioChunk instanceof Uint8Array\n );\n}\n\n/**\n * 合并多个 Uint8Array\n */\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\n/**\n * 保存音频数据到文件\n * 支持三种调用方式:\n * 1. saveAudio(filePath, chunks) - chunks 是 Uint8Array[]\n * 2. saveAudio(filePath, asyncIterable) - AsyncIterable<Uint8Array>\n * 3. saveAudio(filePath, asyncIterable) - AsyncIterable<TTSStreamChunk>\n *\n * @param filePath 目标文件路径\n * @param source 音频数据源,可以是 Uint8Array 数组或异步迭代器\n */\nexport async function saveAudio(\n filePath: string,\n source: Uint8Array[] | AsyncIterable<Uint8Array> | AsyncIterable<TTSStreamChunk>\n): Promise<void> {\n const chunks: Uint8Array[] = [];\n\n // 判断是否为异步迭代器\n if (isAsyncIterable(source)) {\n for await (const chunk of source) {\n // 自动检测并提取 audioChunk\n if (isTTSStreamChunk(chunk)) {\n chunks.push(chunk.audioChunk);\n } else if (chunk instanceof Uint8Array) {\n chunks.push(chunk);\n }\n }\n } else {\n chunks.push(...source);\n }\n\n // 合并并写入文件\n const audio = concatUint8Arrays(chunks);\n await writeFile(filePath, audio);\n}\n","import { collectAudio } from '@/tts/utils/collect';\nimport { playAudio } from '@/tts/utils/play';\nimport { saveTTSResponse } from '@/tts/utils/save';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface TeeOptions {\n save?: {\n filename?: string;\n directory?: string;\n };\n play?: {\n player?: string;\n };\n}\n\nexport async function teeAudio(\n response: TTSResponse,\n options: TeeOptions = {}\n): Promise<TTSResponse> {\n const audio = await collectAudio(response);\n\n if (options.save) {\n await saveTTSResponse({ ...response, audio }, options.save);\n }\n\n if (options.play) {\n await playAudio({ ...response, audio }, options.play);\n }\n\n return { ...response, audio };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/tts/utils/collect.ts","../src/tts/utils/play.ts","../src/tts/utils/save.ts","../src/tts/utils/save-audio.ts","../src/tts/utils/tee.ts"],"names":["Buffer","concatUint8Arrays","writeFile"],"mappings":"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","file":"chunk-LVWPQ7DG.js","sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface CollectOptions {\n onChunk?: (chunk: Uint8Array) => void;\n onComplete?: (audio: Uint8Array) => void;\n onError?: (error: Error) => void;\n}\n\nexport async function collectAudio(\n response: TTSResponse,\n options: CollectOptions = {}\n): Promise<Uint8Array> {\n const { audio } = response;\n const chunks: Uint8Array[] = [];\n\n if (isUint8Array(audio)) {\n chunks.push(audio);\n } else if (isBuffer(audio)) {\n chunks.push(new Uint8Array(audio));\n }\n\n const result = concatUint8Arrays(chunks);\n\n if (options.onComplete) {\n options.onComplete(result);\n }\n\n return result;\n}\n\nfunction isUint8Array(value: unknown): value is Uint8Array {\n return value instanceof Uint8Array;\n}\n\nfunction isBuffer(value: unknown): value is Buffer {\n return Buffer.isBuffer(value);\n}\n\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n","import { spawn } from 'node:child_process';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface PlayOptions {\n player?: string;\n}\n\nexport async function playAudio(response: TTSResponse, options: PlayOptions = {}): Promise<void> {\n const player = options.player || 'afplay';\n\n let buffer: Buffer;\n if (response.audio instanceof Buffer) {\n buffer = response.audio;\n } else if (response.audio instanceof Uint8Array) {\n buffer = Buffer.from(response.audio);\n } else {\n throw new Error('Invalid audio data');\n }\n\n return new Promise((resolve, reject) => {\n const proc = spawn(player, [], { stdio: ['pipe', 'inherit', 'inherit'] });\n proc.stdin.write(buffer);\n proc.stdin.end();\n proc.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Player exited with code ${code}`));\n }\n });\n proc.on('error', reject);\n });\n}\n","import { writeFile } from 'node:fs/promises';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface SaveOptions {\n filename?: string;\n directory?: string;\n}\n\n/**\n * 保存 TTSResponse 到文件\n * 自动生成文件名,适合快速保存 TTS 响应\n *\n * @param response TTS 响应对象\n * @param options 保存选项\n * @returns 保存的文件路径\n */\nexport async function saveTTSResponse(\n response: TTSResponse,\n options: SaveOptions = {}\n): Promise<string> {\n const { format } = response;\n const timestamp = Date.now();\n const filename = options.filename || `tts_${timestamp}.${format}`;\n const filepath = options.directory ? `${options.directory}/${filename}` : filename;\n\n let buffer: Buffer;\n if (response.audio instanceof Buffer) {\n buffer = response.audio;\n } else if (response.audio instanceof Uint8Array) {\n buffer = Buffer.from(response.audio);\n } else {\n throw new Error('Invalid audio data');\n }\n\n await writeFile(filepath, buffer);\n return filepath;\n}\n","import { writeFile } from 'node:fs/promises';\nimport type { TTSStreamChunk } from '@/types/tts';\n\n/**\n * 判断是否为异步迭代器\n */\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n Symbol.asyncIterator in value &&\n typeof (value as AsyncIterable<unknown>)[Symbol.asyncIterator] === 'function'\n );\n}\n\n/**\n * 判断是否为 TTSStreamChunk 类型\n */\nfunction isTTSStreamChunk(value: unknown): value is TTSStreamChunk {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'audioChunk' in value &&\n (value as TTSStreamChunk).audioChunk instanceof Uint8Array\n );\n}\n\n/**\n * 合并多个 Uint8Array\n */\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\n/**\n * 保存音频数据到文件\n * 支持三种调用方式:\n * 1. saveAudio(filePath, chunks) - chunks 是 Uint8Array[]\n * 2. saveAudio(filePath, asyncIterable) - AsyncIterable<Uint8Array>\n * 3. saveAudio(filePath, asyncIterable) - AsyncIterable<TTSStreamChunk>\n *\n * @param filePath 目标文件路径\n * @param source 音频数据源,可以是 Uint8Array 数组或异步迭代器\n */\nexport async function saveAudio(\n filePath: string,\n source: Uint8Array[] | AsyncIterable<Uint8Array> | AsyncIterable<TTSStreamChunk>\n): Promise<void> {\n const chunks: Uint8Array[] = [];\n\n // 判断是否为异步迭代器\n if (isAsyncIterable(source)) {\n for await (const chunk of source) {\n // 自动检测并提取 audioChunk\n if (isTTSStreamChunk(chunk)) {\n chunks.push(chunk.audioChunk);\n } else if (chunk instanceof Uint8Array) {\n chunks.push(chunk);\n }\n }\n } else {\n chunks.push(...source);\n }\n\n // 合并并写入文件\n const audio = concatUint8Arrays(chunks);\n await writeFile(filePath, audio);\n}\n","import { collectAudio } from '@/tts/utils/collect';\nimport { playAudio } from '@/tts/utils/play';\nimport { saveTTSResponse } from '@/tts/utils/save';\nimport type { TTSResponse } from '@/types/tts';\n\nexport interface TeeOptions {\n save?: {\n filename?: string;\n directory?: string;\n };\n play?: {\n player?: string;\n };\n}\n\nexport async function teeAudio(\n response: TTSResponse,\n options: TeeOptions = {}\n): Promise<TTSResponse> {\n const audio = await collectAudio(response);\n\n if (options.save) {\n await saveTTSResponse({ ...response, audio }, options.save);\n }\n\n if (options.play) {\n await playAudio({ ...response, audio }, options.play);\n }\n\n return { ...response, audio };\n}\n"]}
|