@mingxy/ocosay 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +556 -0
  2. package/TECH_PLAN.md +352 -0
  3. package/__mocks__/@opencode-ai/plugin.ts +32 -0
  4. package/dist/config.d.ts +26 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/config.js +95 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/core/backends/afplay-backend.d.ts +33 -0
  9. package/dist/core/backends/afplay-backend.d.ts.map +1 -0
  10. package/dist/core/backends/afplay-backend.js +144 -0
  11. package/dist/core/backends/afplay-backend.js.map +1 -0
  12. package/dist/core/backends/aplay-backend.d.ts +33 -0
  13. package/dist/core/backends/aplay-backend.d.ts.map +1 -0
  14. package/dist/core/backends/aplay-backend.js +142 -0
  15. package/dist/core/backends/aplay-backend.js.map +1 -0
  16. package/dist/core/backends/base.d.ts +94 -0
  17. package/dist/core/backends/base.d.ts.map +1 -0
  18. package/dist/core/backends/base.js +6 -0
  19. package/dist/core/backends/base.js.map +1 -0
  20. package/dist/core/backends/index.d.ts +29 -0
  21. package/dist/core/backends/index.d.ts.map +1 -0
  22. package/dist/core/backends/index.js +114 -0
  23. package/dist/core/backends/index.js.map +1 -0
  24. package/dist/core/backends/naudiodon-backend.d.ts +52 -0
  25. package/dist/core/backends/naudiodon-backend.d.ts.map +1 -0
  26. package/dist/core/backends/naudiodon-backend.js +123 -0
  27. package/dist/core/backends/naudiodon-backend.js.map +1 -0
  28. package/dist/core/backends/powershell-backend.d.ts +34 -0
  29. package/dist/core/backends/powershell-backend.d.ts.map +1 -0
  30. package/dist/core/backends/powershell-backend.js +154 -0
  31. package/dist/core/backends/powershell-backend.js.map +1 -0
  32. package/dist/core/player.d.ts +97 -0
  33. package/dist/core/player.d.ts.map +1 -0
  34. package/dist/core/player.js +268 -0
  35. package/dist/core/player.js.map +1 -0
  36. package/dist/core/speaker.d.ts +97 -0
  37. package/dist/core/speaker.d.ts.map +1 -0
  38. package/dist/core/speaker.js +218 -0
  39. package/dist/core/speaker.js.map +1 -0
  40. package/dist/core/stream-player.d.ts +107 -0
  41. package/dist/core/stream-player.d.ts.map +1 -0
  42. package/dist/core/stream-player.js +272 -0
  43. package/dist/core/stream-player.js.map +1 -0
  44. package/dist/core/stream-reader.d.ts +86 -0
  45. package/dist/core/stream-reader.d.ts.map +1 -0
  46. package/dist/core/stream-reader.js +172 -0
  47. package/dist/core/stream-reader.js.map +1 -0
  48. package/dist/core/streaming-synthesizer.d.ts +51 -0
  49. package/dist/core/streaming-synthesizer.d.ts.map +1 -0
  50. package/dist/core/streaming-synthesizer.js +103 -0
  51. package/dist/core/streaming-synthesizer.js.map +1 -0
  52. package/dist/core/types.d.ts +141 -0
  53. package/dist/core/types.d.ts.map +1 -0
  54. package/dist/core/types.js +37 -0
  55. package/dist/core/types.js.map +1 -0
  56. package/dist/index.d.ts +40 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +179 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/plugin.d.ts +4 -0
  61. package/dist/plugin.d.ts.map +1 -0
  62. package/dist/plugin.js +151 -0
  63. package/dist/plugin.js.map +1 -0
  64. package/dist/providers/base.d.ts +55 -0
  65. package/dist/providers/base.d.ts.map +1 -0
  66. package/dist/providers/base.js +95 -0
  67. package/dist/providers/base.js.map +1 -0
  68. package/dist/providers/minimax.d.ts +84 -0
  69. package/dist/providers/minimax.d.ts.map +1 -0
  70. package/dist/providers/minimax.js +387 -0
  71. package/dist/providers/minimax.js.map +1 -0
  72. package/dist/tools/tts.d.ts +147 -0
  73. package/dist/tools/tts.d.ts.map +1 -0
  74. package/dist/tools/tts.js +232 -0
  75. package/dist/tools/tts.js.map +1 -0
  76. package/jest.config.js +15 -0
  77. package/package.json +49 -0
  78. package/src/config.ts +121 -0
  79. package/src/core/backends/afplay-backend.ts +162 -0
  80. package/src/core/backends/aplay-backend.ts +160 -0
  81. package/src/core/backends/base.ts +117 -0
  82. package/src/core/backends/index.ts +128 -0
  83. package/src/core/backends/naudiodon-backend.ts +164 -0
  84. package/src/core/backends/powershell-backend.ts +173 -0
  85. package/src/core/player.ts +322 -0
  86. package/src/core/speaker.ts +283 -0
  87. package/src/core/stream-player.ts +326 -0
  88. package/src/core/stream-reader.ts +190 -0
  89. package/src/core/streaming-synthesizer.ts +123 -0
  90. package/src/core/types.ts +185 -0
  91. package/src/index.ts +233 -0
  92. package/src/plugin.ts +166 -0
  93. package/src/providers/base.ts +150 -0
  94. package/src/providers/minimax.ts +515 -0
  95. package/src/tools/tts.ts +277 -0
  96. package/src/types/naudiodon.d.ts +19 -0
  97. package/tests/__mocks__/@opencode-ai/plugin.ts +32 -0
  98. package/tests/backends.test.ts +831 -0
  99. package/tests/index.test.ts +201 -0
  100. package/tests/integration-test.d.ts +6 -0
  101. package/tests/integration-test.d.ts.map +1 -0
  102. package/tests/integration-test.js +84 -0
  103. package/tests/integration-test.js.map +1 -0
  104. package/tests/integration-test.ts +93 -0
  105. package/tests/p1-fixes.test.ts +160 -0
  106. package/tests/plugin.test.ts +311 -0
  107. package/tests/provider.test.d.ts +2 -0
  108. package/tests/provider.test.d.ts.map +1 -0
  109. package/tests/provider.test.js +69 -0
  110. package/tests/provider.test.js.map +1 -0
  111. package/tests/provider.test.ts +87 -0
  112. package/tests/speaker.test.d.ts +2 -0
  113. package/tests/speaker.test.d.ts.map +1 -0
  114. package/tests/speaker.test.js +63 -0
  115. package/tests/speaker.test.js.map +1 -0
  116. package/tests/speaker.test.ts +232 -0
  117. package/tests/stream-player.test.ts +303 -0
  118. package/tests/stream-reader.test.ts +269 -0
  119. package/tests/streaming-synthesizer.test.ts +225 -0
  120. package/tests/tts-tools.test.ts +270 -0
  121. package/tests/types.test.d.ts +2 -0
  122. package/tests/types.test.d.ts.map +1 -0
  123. package/tests/types.test.js +61 -0
  124. package/tests/types.test.js.map +1 -0
  125. package/tests/types.test.ts +63 -0
  126. package/tsconfig.json +22 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * PowerShell Backend - Windows 平台音频播放后端
3
+ * 使用 PowerShell 的 [System.Media.SoundPlayer] 异步 API
4
+ */
5
+ import { AudioBackend, BackendOptions } from './base';
6
+ /**
7
+ * PowerShellBackend - Windows 原生音频播放后端
8
+ * 使用 PowerShell 异步方式避免阻塞事件循环
9
+ * 不支持真正的流式播放
10
+ */
11
+ export declare class PowerShellBackend implements AudioBackend {
12
+ readonly name = "powershell";
13
+ readonly supportsStreaming = false;
14
+ private process?;
15
+ private tempFile?;
16
+ private events?;
17
+ private _started;
18
+ private _paused;
19
+ private _stopped;
20
+ private chunks;
21
+ private hasEnded;
22
+ constructor(options?: BackendOptions);
23
+ start(filePath: string): void;
24
+ write(chunk: Buffer): void;
25
+ end(): void;
26
+ pause(): void;
27
+ resume(): void;
28
+ stop(): void;
29
+ setVolume(_volume: number): void;
30
+ destroy(): void;
31
+ private cleanup;
32
+ private handleError;
33
+ }
34
+ //# sourceMappingURL=powershell-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"powershell-backend.d.ts","sourceRoot":"","sources":["../../../src/core/backends/powershell-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAsB,cAAc,EAAE,MAAM,QAAQ,CAAA;AAgBzE;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IACpD,QAAQ,CAAC,IAAI,gBAAe;IAC5B,QAAQ,CAAC,iBAAiB,SAAQ;IAElC,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAC,CAAQ;IACzB,OAAO,CAAC,MAAM,CAAC,CAAoB;IACnC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;IAExB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,OAAO,GAAE,cAAmB;IAIxC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAmD7B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM1B,GAAG,IAAI,IAAI;IAaX,KAAK,IAAI,IAAI;IAMb,MAAM,IAAI,IAAI;IAMd,IAAI,IAAI,IAAI;IAqBZ,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,WAAW;CAGpB"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * PowerShell Backend - Windows 平台音频播放后端
3
+ * 使用 PowerShell 的 [System.Media.SoundPlayer] 异步 API
4
+ */
5
+ import { spawn } from 'child_process';
6
+ import { tmpdir } from 'os';
7
+ import { join } from 'path';
8
+ import { writeFileSync, unlinkSync, existsSync } from 'fs';
9
+ // 白名单:Windows 路径格式(禁止 - 防止命令注入)
10
+ const SAFE_PATH_REGEX = /^[\w\:\\_.]+$/i;
11
+ // 定义不支持操作的错误类
12
+ class UnsupportedError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'UnsupportedError';
16
+ }
17
+ }
18
+ /**
19
+ * PowerShellBackend - Windows 原生音频播放后端
20
+ * 使用 PowerShell 异步方式避免阻塞事件循环
21
+ * 不支持真正的流式播放
22
+ */
23
+ export class PowerShellBackend {
24
+ name = 'powershell';
25
+ supportsStreaming = false;
26
+ process;
27
+ tempFile;
28
+ events;
29
+ _started = false;
30
+ _paused = false;
31
+ _stopped = false;
32
+ // P0-4: 缓冲所有chunk,等end()时一次性写入文件
33
+ chunks = [];
34
+ hasEnded = false;
35
+ constructor(options = {}) {
36
+ this.events = options.events;
37
+ }
38
+ start(filePath) {
39
+ if (this._started)
40
+ return;
41
+ if (!SAFE_PATH_REGEX.test(filePath)) {
42
+ throw new Error(`Invalid file path: ${filePath}`);
43
+ }
44
+ this.tempFile = filePath;
45
+ this._started = true;
46
+ this._stopped = false;
47
+ this.events?.onStart?.();
48
+ // 安全方案:将路径写入临时脚本文件,避免命令行注入
49
+ const escapedPath = filePath.replace(/'/g, "''");
50
+ const psScript = `$sound = New-Object System.Media.SoundPlayer('${escapedPath}'); $sound.PlayAsync()`;
51
+ const scriptFile = join(tmpdir(), `ocosay-${Date.now()}.ps1`);
52
+ writeFileSync(scriptFile, psScript, { encoding: 'utf8' });
53
+ this.process = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', scriptFile], {
54
+ stdio: 'ignore',
55
+ detached: false
56
+ });
57
+ // 清理脚本文件
58
+ this.process.on('exit', () => {
59
+ try {
60
+ if (existsSync(scriptFile)) {
61
+ unlinkSync(scriptFile);
62
+ }
63
+ }
64
+ catch (e) {
65
+ // 忽略清理错误
66
+ }
67
+ });
68
+ this.process.on('exit', (code) => {
69
+ if (this._stopped)
70
+ return;
71
+ if (code === 0 || code === null) {
72
+ this._started = false;
73
+ this.events?.onEnd?.();
74
+ }
75
+ else {
76
+ this.handleError(new Error(`PowerShell playback exited with code ${code}`));
77
+ }
78
+ });
79
+ this.process.on('error', (error) => {
80
+ this.handleError(error);
81
+ });
82
+ }
83
+ write(chunk) {
84
+ if (this._stopped)
85
+ return;
86
+ // P0-4: 缓冲所有chunk,等end()时一次性写入
87
+ this.chunks.push(chunk);
88
+ }
89
+ end() {
90
+ if (this._stopped || this.hasEnded)
91
+ return;
92
+ this.hasEnded = true;
93
+ if (this.chunks.length === 0)
94
+ return;
95
+ // P0-4: 所有chunk缓冲完毕后,一次性写入文件并播放
96
+ this.tempFile = join(tmpdir(), `ocosay-${Date.now()}.wav`);
97
+ writeFileSync(this.tempFile, Buffer.concat(this.chunks));
98
+ this.chunks = [];
99
+ this.start(this.tempFile);
100
+ }
101
+ pause() {
102
+ if (!this._started || this._paused || this._stopped)
103
+ return;
104
+ // PowerShell SoundPlayer.PlayAsync() 不支持暂停,抛错明确告知
105
+ throw new UnsupportedError('pause is not supported by PowerShell SoundPlayer');
106
+ }
107
+ resume() {
108
+ if (!this._paused || this._stopped)
109
+ return;
110
+ // PowerShell SoundPlayer.PlayAsync() 不支持恢复,抛错明确告知
111
+ throw new UnsupportedError('resume is not supported by PowerShell SoundPlayer');
112
+ }
113
+ stop() {
114
+ this._stopped = true;
115
+ this._started = false;
116
+ this._paused = false;
117
+ if (this.process) {
118
+ try {
119
+ this.process.kill('SIGTERM');
120
+ }
121
+ catch (e) {
122
+ // 忽略错误
123
+ }
124
+ this.process = undefined;
125
+ }
126
+ this.cleanup();
127
+ this.chunks = [];
128
+ this.hasEnded = false;
129
+ this.events?.onStop?.();
130
+ }
131
+ setVolume(_volume) {
132
+ // PowerShell SoundPlayer 不支持音量控制
133
+ }
134
+ destroy() {
135
+ this.stop();
136
+ }
137
+ cleanup() {
138
+ if (this.tempFile && this.tempFile.startsWith(tmpdir())) {
139
+ try {
140
+ if (existsSync(this.tempFile)) {
141
+ unlinkSync(this.tempFile);
142
+ }
143
+ }
144
+ catch (e) {
145
+ // 忽略清理错误
146
+ }
147
+ this.tempFile = undefined;
148
+ }
149
+ }
150
+ handleError(error) {
151
+ this.events?.onError?.(error);
152
+ }
153
+ }
154
+ //# sourceMappingURL=powershell-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"powershell-backend.js","sourceRoot":"","sources":["../../../src/core/backends/powershell-backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Audio Player Module
3
+ * 音频播放引擎 - 支持 pause/resume/stop 和流式播放
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ /**
7
+ * Player Events - 播放事件回调接口
8
+ */
9
+ export interface PlayerEvents {
10
+ onStart?: () => void;
11
+ onEnd?: () => void;
12
+ onError?: (error: Error) => void;
13
+ onProgress?: (progress: {
14
+ current: number;
15
+ total: number;
16
+ }) => void;
17
+ onPause?: () => void;
18
+ onResume?: () => void;
19
+ onStop?: () => void;
20
+ }
21
+ /**
22
+ * Player 接口 - 音频播放器标准接口
23
+ */
24
+ export interface Player {
25
+ play(audioData: Buffer | ReadableStream, format: string): Promise<void>;
26
+ pause(): void;
27
+ resume(): void;
28
+ stop(): void;
29
+ isPlaying(): boolean;
30
+ isPaused(): boolean;
31
+ }
32
+ /**
33
+ * AudioPlayer - 基于系统播放器的音频播放实现
34
+ * 支持 macOS (afplay), Linux (aplay), Windows (PowerShell)
35
+ */
36
+ export declare class AudioPlayer extends EventEmitter implements Player {
37
+ protected events?: PlayerEvents | undefined;
38
+ private _playing;
39
+ private _paused;
40
+ private currentProcess?;
41
+ private currentFile?;
42
+ constructor(events?: PlayerEvents | undefined);
43
+ /**
44
+ * 播放音频
45
+ * @param audioData 音频数据 (Buffer 或 ReadableStream)
46
+ * @param format 音频格式 (mp3, wav, etc.)
47
+ */
48
+ play(audioData: Buffer | ReadableStream, format: string): Promise<void>;
49
+ /**
50
+ * 播放音频文件
51
+ * 优先使用 afplay (macOS), aplay (Linux), 否则用 PowerShell (Windows)
52
+ */
53
+ private playFile;
54
+ /**
55
+ * 暂停播放
56
+ * 注意: 目前通过 SIGSTOP 实现,真正的 pause 需要支持暂停的音频库
57
+ */
58
+ pause(): void;
59
+ /**
60
+ * 恢复播放
61
+ */
62
+ resume(): void;
63
+ /**
64
+ * 停止播放
65
+ */
66
+ stop(): Promise<void>;
67
+ /**
68
+ * 是否正在播放
69
+ */
70
+ isPlaying(): boolean;
71
+ /**
72
+ * 是否暂停
73
+ */
74
+ isPaused(): boolean;
75
+ /**
76
+ * 清理临时文件
77
+ */
78
+ private cleanup;
79
+ }
80
+ /**
81
+ * StreamingPlayer - 流式播放器,支持边下载边播放
82
+ * 注意: 当前实现是下载完毕后再播放,onProgress 事件用于报告下载进度
83
+ */
84
+ export declare class StreamingPlayer extends AudioPlayer {
85
+ private chunks;
86
+ private downloadedSize;
87
+ constructor(events?: PlayerEvents);
88
+ /**
89
+ * 流式下载并播放
90
+ * @param stream 可读流
91
+ * @param format 音频格式
92
+ * @param expectedSize 预期大小(可选),用于进度报告
93
+ */
94
+ streamAndPlay(stream: ReadableStream, format: string, expectedSize?: number): Promise<void>;
95
+ }
96
+ export default AudioPlayer;
97
+ //# sourceMappingURL=player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../src/core/player.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAQrC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACnE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvE,KAAK,IAAI,IAAI,CAAA;IACb,MAAM,IAAI,IAAI,CAAA;IACd,IAAI,IAAI,IAAI,CAAA;IACZ,SAAS,IAAI,OAAO,CAAA;IACpB,QAAQ,IAAI,OAAO,CAAA;CACpB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,YAAa,YAAW,MAAM;IAMjD,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY;IAL3C,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,cAAc,CAAC,CAAc;IACrC,OAAO,CAAC,WAAW,CAAC,CAAQ;gBAEN,MAAM,CAAC,EAAE,YAAY,YAAA;IAI3C;;;;OAIG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmE7E;;;OAGG;IACH,OAAO,CAAC,QAAQ;IA2ChB;;;OAGG;IACH,KAAK,IAAI,IAAI;IAeb;;OAEG;IACH,MAAM,IAAI,IAAI;IAed;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,OAAO,CAAC,OAAO;CAYhB;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,cAAc,CAAI;gBAEd,MAAM,CAAC,EAAE,YAAY;IAIjC;;;;;OAKG;IACG,aAAa,CACjB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;CAoCjB;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Audio Player Module
3
+ * 音频播放引擎 - 支持 pause/resume/stop 和流式播放
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { spawn } from 'child_process';
7
+ import fs from 'fs';
8
+ import { createWriteStream } from 'fs';
9
+ import { tmpdir } from 'os';
10
+ import { join } from 'path';
11
+ import { TTSError, TTSErrorCode } from './types';
12
+ /**
13
+ * AudioPlayer - 基于系统播放器的音频播放实现
14
+ * 支持 macOS (afplay), Linux (aplay), Windows (PowerShell)
15
+ */
16
+ export class AudioPlayer extends EventEmitter {
17
+ events;
18
+ _playing = false;
19
+ _paused = false;
20
+ currentProcess;
21
+ currentFile;
22
+ constructor(events) {
23
+ super();
24
+ this.events = events;
25
+ }
26
+ /**
27
+ * 播放音频
28
+ * @param audioData 音频数据 (Buffer 或 ReadableStream)
29
+ * @param format 音频格式 (mp3, wav, etc.)
30
+ */
31
+ async play(audioData, format) {
32
+ // 如果正在播放,先停止
33
+ if (this._playing) {
34
+ await this.stop();
35
+ }
36
+ this._playing = true;
37
+ this._paused = false;
38
+ try {
39
+ // 将音频数据写入临时文件
40
+ const tempFile = join(tmpdir(), `ocosay-${Date.now()}.${format}`);
41
+ this.currentFile = tempFile;
42
+ if (Buffer.isBuffer(audioData)) {
43
+ fs.writeFileSync(tempFile, audioData);
44
+ }
45
+ else {
46
+ const writeStream = createWriteStream(tempFile);
47
+ const reader = audioData.getReader();
48
+ try {
49
+ while (true) {
50
+ const { done, value } = await reader.read();
51
+ if (done)
52
+ break;
53
+ if (value instanceof Uint8Array) {
54
+ writeStream.write(Buffer.from(value));
55
+ }
56
+ }
57
+ writeStream.end();
58
+ }
59
+ finally {
60
+ reader.releaseLock();
61
+ }
62
+ await new Promise((resolve, reject) => {
63
+ writeStream.on('finish', resolve);
64
+ writeStream.on('error', reject);
65
+ });
66
+ }
67
+ // 触发开始事件
68
+ this.events?.onStart?.();
69
+ this.emit('start');
70
+ // 根据格式选择播放器
71
+ await this.playFile(tempFile, format);
72
+ // 播放完成
73
+ this._playing = false;
74
+ this.events?.onEnd?.();
75
+ this.emit('end');
76
+ // 清理临时文件
77
+ this.cleanup();
78
+ }
79
+ catch (error) {
80
+ this._playing = false;
81
+ const ttsError = new TTSError(error.message || 'Playback failed', TTSErrorCode.PLAYER_ERROR, 'player');
82
+ this.events?.onError?.(ttsError);
83
+ this.emit('error', ttsError);
84
+ throw ttsError;
85
+ }
86
+ }
87
+ /**
88
+ * 播放音频文件
89
+ * 优先使用 afplay (macOS), aplay (Linux), 否则用 PowerShell (Windows)
90
+ */
91
+ playFile(filePath, format) {
92
+ const platform = process.platform;
93
+ return new Promise((resolve, reject) => {
94
+ let command;
95
+ let args;
96
+ if (platform === 'darwin') {
97
+ // macOS
98
+ command = 'afplay';
99
+ args = [filePath];
100
+ }
101
+ else if (platform === 'linux') {
102
+ // Linux
103
+ command = 'aplay';
104
+ args = [filePath];
105
+ }
106
+ else {
107
+ // Windows 或其他
108
+ command = 'powershell';
109
+ args = ['-c', `(New-Object System.Media.SoundPlayer('${filePath.replace(/\\/g, '\\\\')}')).PlaySync()`];
110
+ }
111
+ this.currentProcess = spawn(command, args, {
112
+ stdio: 'ignore',
113
+ detached: false
114
+ });
115
+ this.currentProcess.on('exit', (code, signal) => {
116
+ // 如果是被信号终止(如 SIGTERM),不当作错误
117
+ if (signal === 'SIGTERM' || signal === 'SIGINT') {
118
+ resolve();
119
+ }
120
+ else if (code === 0) {
121
+ resolve();
122
+ }
123
+ else {
124
+ reject(new Error(`Player exited with code ${code}`));
125
+ }
126
+ });
127
+ this.currentProcess.on('error', (error) => {
128
+ reject(error);
129
+ });
130
+ });
131
+ }
132
+ /**
133
+ * 暂停播放
134
+ * 注意: 目前通过 SIGSTOP 实现,真正的 pause 需要支持暂停的音频库
135
+ */
136
+ pause() {
137
+ if (!this._playing || this._paused)
138
+ return;
139
+ if (this.currentProcess) {
140
+ try {
141
+ this.currentProcess.kill('SIGSTOP');
142
+ this._paused = true;
143
+ this.events?.onPause?.();
144
+ this.emit('pause');
145
+ }
146
+ catch (e) {
147
+ // 如果 kill 失败,忽略
148
+ }
149
+ }
150
+ }
151
+ /**
152
+ * 恢复播放
153
+ */
154
+ resume() {
155
+ if (!this._playing || !this._paused)
156
+ return;
157
+ if (this.currentProcess) {
158
+ try {
159
+ this.currentProcess.kill('SIGCONT');
160
+ this._paused = false;
161
+ this.events?.onResume?.();
162
+ this.emit('resume');
163
+ }
164
+ catch (e) {
165
+ // 如果 kill 失败,忽略
166
+ }
167
+ }
168
+ }
169
+ /**
170
+ * 停止播放
171
+ */
172
+ async stop() {
173
+ this._playing = false;
174
+ this._paused = false;
175
+ if (this.currentProcess) {
176
+ try {
177
+ this.currentProcess.kill('SIGTERM');
178
+ }
179
+ catch (e) {
180
+ // 忽略错误
181
+ }
182
+ this.currentProcess = undefined;
183
+ }
184
+ this.cleanup();
185
+ this.events?.onStop?.();
186
+ this.emit('stop');
187
+ }
188
+ /**
189
+ * 是否正在播放
190
+ */
191
+ isPlaying() {
192
+ return this._playing;
193
+ }
194
+ /**
195
+ * 是否暂停
196
+ */
197
+ isPaused() {
198
+ return this._paused;
199
+ }
200
+ /**
201
+ * 清理临时文件
202
+ */
203
+ cleanup() {
204
+ if (this.currentFile) {
205
+ try {
206
+ if (fs.existsSync(this.currentFile)) {
207
+ fs.unlinkSync(this.currentFile);
208
+ }
209
+ }
210
+ catch (e) {
211
+ // 忽略清理错误
212
+ }
213
+ this.currentFile = undefined;
214
+ }
215
+ }
216
+ }
217
+ /**
218
+ * StreamingPlayer - 流式播放器,支持边下载边播放
219
+ * 注意: 当前实现是下载完毕后再播放,onProgress 事件用于报告下载进度
220
+ */
221
+ export class StreamingPlayer extends AudioPlayer {
222
+ chunks = [];
223
+ downloadedSize = 0;
224
+ constructor(events) {
225
+ super(events);
226
+ }
227
+ /**
228
+ * 流式下载并播放
229
+ * @param stream 可读流
230
+ * @param format 音频格式
231
+ * @param expectedSize 预期大小(可选),用于进度报告
232
+ */
233
+ async streamAndPlay(stream, format, expectedSize) {
234
+ const reader = stream.getReader();
235
+ try {
236
+ while (true) {
237
+ const { done, value } = await reader.read();
238
+ if (done)
239
+ break;
240
+ if (value instanceof Buffer) {
241
+ this.chunks.push(value);
242
+ this.downloadedSize += value.length;
243
+ // 触发下载进度
244
+ if (expectedSize) {
245
+ this.emit('progress', {
246
+ current: this.downloadedSize,
247
+ total: expectedSize
248
+ });
249
+ this.events?.onProgress?.({
250
+ current: this.downloadedSize,
251
+ total: expectedSize
252
+ });
253
+ }
254
+ }
255
+ }
256
+ // 合并所有 chunk 并播放
257
+ const fullAudio = Buffer.concat(this.chunks);
258
+ await this.play(fullAudio, format);
259
+ }
260
+ finally {
261
+ reader.releaseLock();
262
+ this.chunks = [];
263
+ this.downloadedSize = 0;
264
+ }
265
+ }
266
+ }
267
+ export default AudioPlayer;
268
+ //# sourceMappingURL=player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player.js","sourceRoot":"","sources":["../../src/core/player.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Speaker - TTS 统一调用入口
3
+ * 提供简洁的 API 和便捷函数
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { SpeakOptions, Voice, TTSEvent, SynthesisModel } from './types';
7
+ export interface SpeakerOptions {
8
+ defaultProvider?: string;
9
+ defaultModel?: SynthesisModel;
10
+ defaultVoice?: string;
11
+ onEvent?: (event: TTSEvent, data?: any) => void;
12
+ }
13
+ /**
14
+ * Speaker - TTS 统一调用入口类
15
+ * 封装 Provider 和 Player,提供简洁的 speak/pause/resume/stop API
16
+ */
17
+ export declare class Speaker extends EventEmitter {
18
+ private options;
19
+ private currentProvider?;
20
+ private player?;
21
+ private currentText?;
22
+ private isSpeaking;
23
+ private isPaused;
24
+ constructor(options?: SpeakerOptions);
25
+ /**
26
+ * 说话 - 核心方法
27
+ * @param text 要说的文本
28
+ * @param options 可选参数
29
+ */
30
+ speak(text: string, options?: SpeakOptions & {
31
+ provider?: string;
32
+ }): Promise<void>;
33
+ /**
34
+ * 暂停播放
35
+ */
36
+ pause(): void;
37
+ /**
38
+ * 恢复播放
39
+ */
40
+ resume(): void;
41
+ /**
42
+ * 停止播放
43
+ */
44
+ stop(): Promise<void>;
45
+ /**
46
+ * 销毁 Speaker,释放资源
47
+ */
48
+ destroy(): Promise<void>;
49
+ /**
50
+ * 列出可用音色
51
+ */
52
+ listVoices(providerName?: string): Promise<Voice[]>;
53
+ /**
54
+ * 获取 Provider 能力
55
+ */
56
+ getCapabilities(providerName?: string): import("./types").TTSCapabilities;
57
+ /**
58
+ * 获取所有已注册的 Provider
59
+ */
60
+ getProviders(): string[];
61
+ /**
62
+ * 是否正在播放
63
+ */
64
+ isPlaying(): boolean;
65
+ /**
66
+ * 是否暂停
67
+ */
68
+ isPausedState(): boolean;
69
+ }
70
+ /**
71
+ * 获取默认 Speaker 实例(单例)
72
+ */
73
+ export declare function getDefaultSpeaker(): Speaker;
74
+ /**
75
+ * 说话(便捷函数)
76
+ */
77
+ export declare function speak(text: string, options?: SpeakOptions & {
78
+ provider?: string;
79
+ }): Promise<void>;
80
+ /**
81
+ * 停止(便捷函数)
82
+ */
83
+ export declare function stop(): Promise<void>;
84
+ /**
85
+ * 暂停(便捷函数)
86
+ */
87
+ export declare function pause(): void;
88
+ /**
89
+ * 恢复(便捷函数)
90
+ */
91
+ export declare function resume(): void;
92
+ /**
93
+ * 列出音色(便捷函数)
94
+ */
95
+ export declare function listVoices(providerName?: string): Promise<Voice[]>;
96
+ export default Speaker;
97
+ //# sourceMappingURL=speaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speaker.d.ts","sourceRoot":"","sources":["../../src/core/speaker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAIL,YAAY,EAEZ,KAAK,EACL,QAAQ,EACR,cAAc,EACf,MAAM,SAAS,CAAA;AAIhB,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,cAAc,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;CAChD;AAED;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,YAAY;IAO3B,OAAO,CAAC,OAAO;IAN3B,OAAO,CAAC,eAAe,CAAC,CAAa;IACrC,OAAO,CAAC,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAQ;IAC5B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,QAAQ,CAAQ;gBAEJ,OAAO,GAAE,cAAmB;IA6BhD;;;;OAIG;IACG,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,YAAY,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GACjD,OAAO,CAAC,IAAI,CAAC;IA+DhB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,MAAM,IAAI,IAAI;IAMd;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B;;OAEG;IACG,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAMzD;;OAEG;IACH,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM;IAMrC;;OAEG;IACH,YAAY,IAAI,MAAM,EAAE;IAIxB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,aAAa,IAAI,OAAO;CAGzB;AAQD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAK3C;AAED;;GAEG;AACH,wBAAsB,KAAK,CACzB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1C;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,IAAI,CAG5B;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,IAAI,CAG7B;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAGxE;AAED,eAAe,OAAO,CAAA"}