@mingxy/ocosay 1.1.0 → 1.1.2
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 +43 -653
- package/dist/config.js +2 -1
- package/dist/core/logger.d.ts +46 -0
- package/dist/core/logger.js +126 -0
- package/dist/core/notification.d.ts +27 -0
- package/dist/core/notification.js +86 -0
- package/dist/core/speaker.js +5 -35
- package/dist/core/stream-reader.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/package.json +7 -4
- package/dist/plugin.js +220 -197
- package/dist/tools/tts.js +2 -1
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +42 -6
- package/package.json +4 -2
- package/scripts/compile-native.cjs +48 -0
- package/scripts/install-portaudio.cjs +117 -0
- package/.idea/UniappTool.xml +0 -10
- package/.idea/inspectionProfiles/profiles_settings.xml +0 -5
- package/.idea/modules.xml +0 -8
- package/.idea/ocosay.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/.sisyphus/boulder.json +0 -23
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/core/backends/afplay-backend.d.ts.map +0 -1
- package/dist/core/backends/afplay-backend.js.map +0 -1
- package/dist/core/backends/aplay-backend.d.ts.map +0 -1
- package/dist/core/backends/aplay-backend.js.map +0 -1
- package/dist/core/backends/base.d.ts.map +0 -1
- package/dist/core/backends/base.js.map +0 -1
- package/dist/core/backends/howler-backend.d.ts.map +0 -1
- package/dist/core/backends/howler-backend.js.map +0 -1
- package/dist/core/backends/index.d.ts.map +0 -1
- package/dist/core/backends/index.js.map +0 -1
- package/dist/core/backends/naudiodon-backend.d.ts.map +0 -1
- package/dist/core/backends/naudiodon-backend.js.map +0 -1
- package/dist/core/backends/powershell-backend.d.ts.map +0 -1
- package/dist/core/backends/powershell-backend.js.map +0 -1
- package/dist/core/player.d.ts.map +0 -1
- package/dist/core/player.js.map +0 -1
- package/dist/core/speaker.d.ts.map +0 -1
- package/dist/core/speaker.js.map +0 -1
- package/dist/core/stream-player.d.ts.map +0 -1
- package/dist/core/stream-player.js.map +0 -1
- package/dist/core/stream-reader.d.ts.map +0 -1
- package/dist/core/stream-reader.js.map +0 -1
- package/dist/core/streaming-synthesizer.d.ts.map +0 -1
- package/dist/core/streaming-synthesizer.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js.map +0 -7
- package/dist/providers/base.d.ts.map +0 -1
- package/dist/providers/base.js.map +0 -1
- package/dist/providers/minimax.d.ts.map +0 -1
- package/dist/providers/minimax.js.map +0 -1
- package/dist/services/notification-service.d.ts.map +0 -1
- package/dist/services/notification-service.js.map +0 -1
- package/dist/services/speaker-service.d.ts.map +0 -1
- package/dist/services/speaker-service.js.map +0 -1
- package/dist/services/streaming-service.d.ts.map +0 -1
- package/dist/services/streaming-service.js.map +0 -1
- package/dist/tools/tts.d.ts.map +0 -1
- package/dist/tools/tts.js.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/tsconfig.jest.json +0 -21
package/README.md
CHANGED
|
@@ -1,693 +1,83 @@
|
|
|
1
|
-
# ocosay
|
|
1
|
+
# @mingxy/ocosay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenCode MiniMax TTS 插件,支持语音合成与播放。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](LICENSE)
|
|
5
|
+
## 安装
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
**解决的问题:**
|
|
13
|
-
- 长文本等待焦虑 — 流式播放,首包延迟低
|
|
14
|
-
- 缺乏情感反馈 — 多种音色选择,支持语速/音调调节
|
|
15
|
-
- 交互体验单一 — 从"看"升级到"听"
|
|
16
|
-
|
|
17
|
-
## Features
|
|
18
|
-
|
|
19
|
-
- 🎙️ **多 TTS 模型支持** - MiniMax 作为第一个实现,可扩展其他提供商
|
|
20
|
-
- 🔊 **多种合成模式** - 同步、异步、流式三种模式可选
|
|
21
|
-
- 🎭 **音色克隆** - 支持音色快速复刻
|
|
22
|
-
- 🎛️ **播放控制** - 暂停、恢复、停止
|
|
23
|
-
- 📋 **音色列表** - 查询可用音色
|
|
24
|
-
- 🔌 **OpenCode Plugin** - 无缝集成 OpenCode
|
|
25
|
-
- 📡 **豆包模式** - autoRead + TuiEventBus,边接收边朗读
|
|
26
|
-
- 📝 **日志系统** - 使用 pino 统一日志格式,写入 `~/.ocosay/ocosay.log`
|
|
27
|
-
- 🔔 **Toast 提示** - 播放成功/失败显示 Toast 通知
|
|
28
|
-
|
|
29
|
-
## Platform Support
|
|
30
|
-
|
|
31
|
-
| 平台 | 支持状态 | 说明 |
|
|
32
|
-
|------|----------|------|
|
|
33
|
-
| macOS | ✅ 完全支持 | howler.js 跨平台音频后端 |
|
|
34
|
-
| Linux | ✅ 完全支持 | howler.js 跨平台音频后端 |
|
|
35
|
-
| Windows | ✅ 完全支持 | howler.js 跨平台音频后端 |
|
|
36
|
-
|
|
37
|
-
### 音频后端
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @mingxy/ocosay
|
|
9
|
+
```
|
|
38
10
|
|
|
39
|
-
|
|
11
|
+
> ✅ 安装过程自动完成 PortAudio 依赖检测与编译
|
|
40
12
|
|
|
41
|
-
|
|
42
|
-
|------|------|------|
|
|
43
|
-
| **howler** | 跨平台 | 基于 howler.js,纯 JavaScript 实现,npm 直接安装无需编译 |
|
|
44
|
-
| naudiodon | 流式 | native 模块,支持真正的流式播放(需 npm install) |
|
|
45
|
-
| afplay | macOS | 系统命令 afplay |
|
|
46
|
-
| aplay | Linux | 系统命令 aplay |
|
|
47
|
-
| powershell | Windows | PowerShell PlaySync |
|
|
13
|
+
---
|
|
48
14
|
|
|
49
|
-
|
|
15
|
+
## ⚠️ 自动安装失败时
|
|
50
16
|
|
|
51
|
-
|
|
17
|
+
如果终端出现 PortAudio 相关错误,请手动安装:
|
|
52
18
|
|
|
53
|
-
###
|
|
19
|
+
### macOS
|
|
54
20
|
|
|
55
21
|
```bash
|
|
56
|
-
|
|
22
|
+
brew install portaudio
|
|
57
23
|
```
|
|
58
24
|
|
|
59
|
-
###
|
|
25
|
+
### Windows WSL
|
|
60
26
|
|
|
61
27
|
```bash
|
|
62
|
-
|
|
28
|
+
sudo apt-get install -y libportaudio-dev portaudio
|
|
63
29
|
```
|
|
64
30
|
|
|
65
|
-
|
|
31
|
+
### Windows 原生
|
|
66
32
|
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
"plugin": [
|
|
70
|
-
"@mingxy/ocosay"
|
|
71
|
-
]
|
|
72
|
-
}
|
|
33
|
+
```powershell
|
|
34
|
+
choco install portaudio
|
|
73
35
|
```
|
|
74
36
|
|
|
75
|
-
|
|
76
|
-
> 请编辑该文件,填写您的 `apiKey` 和 `baseURL`。
|
|
37
|
+
安装完成后重新运行:
|
|
77
38
|
|
|
78
|
-
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g @mingxy/ocosay
|
|
41
|
+
```
|
|
79
42
|
|
|
80
|
-
|
|
43
|
+
---
|
|
81
44
|
|
|
82
|
-
|
|
45
|
+
## 验证
|
|
83
46
|
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
"plugin": [
|
|
87
|
-
"@mingxy/ocosay"
|
|
88
|
-
]
|
|
89
|
-
}
|
|
47
|
+
```bash
|
|
48
|
+
node -e "require('naudiodon')"
|
|
90
49
|
```
|
|
91
50
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
插件会自动下载并初始化。
|
|
51
|
+
无报错即成功。
|
|
95
52
|
|
|
96
|
-
|
|
53
|
+
---
|
|
97
54
|
|
|
98
|
-
|
|
55
|
+
## 配置
|
|
99
56
|
|
|
100
|
-
|
|
57
|
+
在 OpenCode 配置文件中添加:
|
|
101
58
|
|
|
102
|
-
```
|
|
59
|
+
```json
|
|
103
60
|
{
|
|
104
|
-
"
|
|
105
|
-
"minimax": {
|
|
106
|
-
"apiKey": "您的API_KEY", // ⚠️ 必填
|
|
107
|
-
"baseURL": "https://api.minimaxi.com" // ⚠️ 必填
|
|
108
|
-
}
|
|
109
|
-
}
|
|
61
|
+
"plugins": ["@mingxy/ocosay"]
|
|
110
62
|
}
|
|
111
63
|
```
|
|
112
64
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
## 快速开始
|
|
116
|
-
|
|
117
|
-
### 初始化
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { initialize } from 'ocosay'
|
|
121
|
-
|
|
122
|
-
await initialize({
|
|
123
|
-
providers: {
|
|
124
|
-
minimax: {
|
|
125
|
-
apiKey: 'your-api-key',
|
|
126
|
-
voiceId: 'male-qn-qingse',
|
|
127
|
-
model: 'stream'
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 基本使用
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { speak, stop, pause, resume } from 'ocosay'
|
|
137
|
-
|
|
138
|
-
// 说话
|
|
139
|
-
await speak('你好,世界!')
|
|
140
|
-
|
|
141
|
-
// 暂停
|
|
142
|
-
pause()
|
|
143
|
-
|
|
144
|
-
// 恢复
|
|
145
|
-
resume()
|
|
146
|
-
|
|
147
|
-
// 停止
|
|
148
|
-
await stop()
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 指定音色和模式
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
await speak('你好,世界!', {
|
|
155
|
-
provider: 'minimax',
|
|
156
|
-
voice: 'female-shaonv',
|
|
157
|
-
model: 'sync',
|
|
158
|
-
speed: 1.0,
|
|
159
|
-
volume: 80
|
|
160
|
-
})
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## 豆包模式
|
|
164
|
-
|
|
165
|
-
豆包模式是 ocosay 的核心特性 — 开启后,AI 助手的回复会**边接收边朗读**,实现类似豆包 App 的流畅播报体验。
|
|
166
|
-
|
|
167
|
-
### 核心组件
|
|
168
|
-
|
|
169
|
-
| 组件 | 说明 |
|
|
170
|
-
|------|------|
|
|
171
|
-
| `StreamReader` | 文本缓冲器,收集并缓冲流式文本 |
|
|
172
|
-
| `StreamingSynthesizer` | 流式合成器,边接收边合成音频 |
|
|
173
|
-
| `StreamPlayer` | 流式播放器,边收边播 |
|
|
174
|
-
| `TuiEventBus` | 事件总线,监听 OpenCode 事件流 |
|
|
175
|
-
|
|
176
|
-
> **注意**:`autoRead` 是 `initialize()` 的配置选项(`autoRead: true`),而非独立组件。
|
|
177
|
-
|
|
178
|
-
### 启用豆包模式
|
|
179
|
-
|
|
180
|
-
豆包模式通过 `initialize()` 的 `autoRead: true` 配置选项启用:
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
import { initialize } from 'ocosay'
|
|
184
|
-
|
|
185
|
-
// 启用豆包模式
|
|
186
|
-
await initialize({
|
|
187
|
-
providers: {
|
|
188
|
-
minimax: {
|
|
189
|
-
apiKey: 'your-api-key',
|
|
190
|
-
voiceId: 'male-qn-qingse'
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
autoRead: true // 开启边接收边朗读
|
|
194
|
-
})
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### 数据流与事件
|
|
198
|
-
|
|
199
|
-
豆包模式的数据流如下:
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
TuiEventBus (message.part.delta)
|
|
203
|
-
→ StreamReader (缓冲文本)
|
|
204
|
-
→ StreamingSynthesizer (流式合成)
|
|
205
|
-
→ StreamPlayer (边收边播)
|
|
206
|
-
→ TuiEventBus (message.part.end)
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
| 事件 | 说明 |
|
|
210
|
-
|------|------|
|
|
211
|
-
| `message.part.delta` | AI回复文本增量事件,携带 `delta` 字段 |
|
|
212
|
-
| `message.part.end` | AI回复片段结束事件 |
|
|
213
|
-
|
|
214
|
-
### TuiEventBus 事件监听
|
|
215
|
-
|
|
216
|
-
TuiEventBus 负责监听 OpenCode 的事件流,实现边接收边朗读:
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
import { TuiEventBus } from 'ocosay'
|
|
220
|
-
|
|
221
|
-
const bus = new TuiEventBus()
|
|
222
|
-
|
|
223
|
-
// 监听文本增量事件
|
|
224
|
-
bus.on('message.part.delta', (event) => {
|
|
225
|
-
// event.properties.delta 包含新增的文本
|
|
226
|
-
console.log('收到文本:', event.properties.delta)
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
// 监听回复片段结束
|
|
230
|
-
bus.on('message.part.end', () => {
|
|
231
|
-
console.log('回复片段结束')
|
|
232
|
-
})
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## 工具列表
|
|
236
|
-
|
|
237
|
-
ocosay 提供 10 个工具用于 OpenCode 集成:
|
|
238
|
-
|
|
239
|
-
| 工具名 | 描述 | 参数 |
|
|
240
|
-
|--------|------|------|
|
|
241
|
-
| `tts_speak` | 将文本转换为语音并播放 | `text` (必填),其他参数从配置文件读取 |
|
|
242
|
-
| `tts_stop` | 停止当前 TTS 播放 | - |
|
|
243
|
-
| `tts_pause` | 暂停当前 TTS 播放 | - |
|
|
244
|
-
| `tts_resume` | 恢复暂停的 TTS 播放 | - |
|
|
245
|
-
| `tts_list_voices` | 列出可用的音色 | `provider` |
|
|
246
|
-
| `tts_list_providers` | 列出所有已注册的 TTS 提供商 | - |
|
|
247
|
-
| `tts_status` | 获取当前 TTS 播放状态 | - |
|
|
248
|
-
| `tts_stream_speak` | 启动流式朗读(豆包模式) | `text`, `voice`, `model` |
|
|
249
|
-
| `tts_stream_stop` | 停止当前流式朗读 | - |
|
|
250
|
-
| `tts_stream_status` | 获取当前流式朗读状态 | - |
|
|
251
|
-
|
|
252
|
-
### tts_speak
|
|
253
|
-
|
|
254
|
-
将文本转换为语音并播放。其他参数(provider、voice、model、speed等)从配置文件读取。
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
// 工具调用
|
|
258
|
-
await tts_speak({
|
|
259
|
-
text: '这是要播放的文本内容'
|
|
260
|
-
})
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### tts_stop
|
|
264
|
-
|
|
265
|
-
停止当前 TTS 播放。
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
// 工具调用
|
|
269
|
-
await tts_stop()
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### tts_pause
|
|
273
|
-
|
|
274
|
-
暂停当前 TTS 播放。
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
// 工具调用
|
|
278
|
-
await tts_pause()
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### tts_resume
|
|
282
|
-
|
|
283
|
-
恢复暂停的 TTS 播放。
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
// 工具调用
|
|
287
|
-
await tts_resume()
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### tts_list_voices
|
|
291
|
-
|
|
292
|
-
列出可用的音色。
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
// 工具调用
|
|
296
|
-
const result = await tts_list_voices({ provider: 'minimax' })
|
|
297
|
-
// 返回: { success: true, voices: [...] }
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### tts_list_providers
|
|
301
|
-
|
|
302
|
-
列出所有已注册的 TTS 提供商。
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
// 工具调用
|
|
306
|
-
const result = await tts_list_providers()
|
|
307
|
-
// 返回: { success: true, providers: ['minimax'] }
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### tts_status
|
|
311
|
-
|
|
312
|
-
获取当前 TTS 播放状态。
|
|
313
|
-
|
|
314
|
-
```typescript
|
|
315
|
-
// 工具调用
|
|
316
|
-
const status = await tts_status()
|
|
317
|
-
// 返回: { success: true, isPlaying: boolean, isPaused: boolean }
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### tts_stream_speak
|
|
321
|
-
|
|
322
|
-
启动流式朗读(豆包模式),订阅AI回复并边生成边朗读。
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
// 工具调用
|
|
326
|
-
await tts_stream_speak({
|
|
327
|
-
text: '初始文本(可选)',
|
|
328
|
-
voice: 'female-shaonv',
|
|
329
|
-
model: 'stream'
|
|
330
|
-
})
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### tts_stream_stop
|
|
334
|
-
|
|
335
|
-
停止当前流式朗读。
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
// 工具调用
|
|
339
|
-
await tts_stream_stop()
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### tts_stream_status
|
|
343
|
-
|
|
344
|
-
获取当前流式朗读状态。
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
// 工具调用
|
|
348
|
-
const status = await tts_stream_status()
|
|
349
|
-
// 返回: { success: true, isActive: boolean, bytesWritten: number, state: string }
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
## API 参考
|
|
65
|
+
---
|
|
353
66
|
|
|
354
|
-
|
|
67
|
+
## 故障排除
|
|
355
68
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
| 参数 | 类型 | 说明 |
|
|
359
|
-
|------|------|------|
|
|
360
|
-
| config.defaultProvider | string | 默认 TTS 提供商,默认 minimax |
|
|
361
|
-
| config.defaultModel | 'sync' \| 'async' \| 'stream' | 默认合成模式,默认 stream |
|
|
362
|
-
| config.defaultVoice | string | 默认音色 ID |
|
|
363
|
-
| config.providers.minimax | MiniMaxConfig | MiniMax 提供商配置 |
|
|
364
|
-
| config.autoRead | boolean | 启用豆包模式(边接收边朗读) |
|
|
365
|
-
| config.streamBufferSize | number | 流式缓冲大小,默认 30 |
|
|
366
|
-
| config.streamBufferTimeout | number | 流式缓冲超时(ms),默认 2000 |
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
import { initialize } from 'ocosay'
|
|
370
|
-
|
|
371
|
-
await initialize({
|
|
372
|
-
providers: {
|
|
373
|
-
minimax: {
|
|
374
|
-
apiKey: 'your-api-key',
|
|
375
|
-
voiceId: 'male-qn-qingse',
|
|
376
|
-
model: 'stream'
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
autoRead: true // 启用豆包模式
|
|
380
|
-
})
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### destroy()
|
|
384
|
-
|
|
385
|
-
释放所有资源,清理插件状态。应在插件卸载或会话结束时调用。
|
|
386
|
-
|
|
387
|
-
```typescript
|
|
388
|
-
import { destroy } from 'ocosay'
|
|
389
|
-
|
|
390
|
-
await destroy()
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### TuiEventBus
|
|
394
|
-
|
|
395
|
-
事件总线类,用于监听 OpenCode 事件流。
|
|
396
|
-
|
|
397
|
-
| 方法 | 说明 |
|
|
69
|
+
| 问题 | 解决 |
|
|
398
70
|
|------|------|
|
|
399
|
-
|
|
|
400
|
-
|
|
|
401
|
-
|
|
402
|
-
**可用事件:**
|
|
71
|
+
| Cannot find module 'naudiodon' | 运行 `npm rebuild naudiodon` |
|
|
72
|
+
| 音频播放无声 | 检查系统音频设备 |
|
|
73
|
+
| 其他错误 | 运行诊断:`node scripts/verify-audio.js` |
|
|
403
74
|
|
|
404
|
-
|
|
405
|
-
|--------|------|----------|
|
|
406
|
-
| `message.part.delta` | AI回复文本增量 | `sessionId`, `messageId`, `partId`, `properties.delta` |
|
|
407
|
-
| `message.part.end` | AI回复片段结束 | - |
|
|
408
|
-
|
|
409
|
-
### speak(text, options?)
|
|
410
|
-
|
|
411
|
-
将文本转换为语音并播放。
|
|
412
|
-
|
|
413
|
-
| 参数 | 类型 | 说明 |
|
|
414
|
-
|------|------|------|
|
|
415
|
-
| text | string | 要转换的文本 |
|
|
416
|
-
| options.provider | string | TTS 提供商,默认 minimax |
|
|
417
|
-
| options.voice | string | 音色 ID |
|
|
418
|
-
| options.model | 'sync' \| 'async' \| 'stream' | 合成模式,默认 stream |
|
|
419
|
-
| options.speed | number | 语速 0.5-2.0 |
|
|
420
|
-
| options.volume | number | 音量 0-100 |
|
|
421
|
-
| options.pitch | number | 音调 0.5-2.0 |
|
|
422
|
-
|
|
423
|
-
### stop()
|
|
424
|
-
|
|
425
|
-
停止当前播放。
|
|
426
|
-
|
|
427
|
-
### pause()
|
|
428
|
-
|
|
429
|
-
暂停当前播放。
|
|
430
|
-
|
|
431
|
-
### resume()
|
|
432
|
-
|
|
433
|
-
恢复暂停的播放。
|
|
434
|
-
|
|
435
|
-
### listVoices(provider?)
|
|
436
|
-
|
|
437
|
-
列出可用的音色。
|
|
438
|
-
|
|
439
|
-
```typescript
|
|
440
|
-
const voices = await listVoices('minimax')
|
|
441
|
-
console.log(voices)
|
|
442
|
-
// [{ id: 'male-qn-qingse', name: '青年清澈', ... }]
|
|
443
|
-
```
|
|
75
|
+
---
|
|
444
76
|
|
|
445
|
-
|
|
77
|
+
## 平台支持
|
|
446
78
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
import { isAutoReadEnabled } from 'ocosay'
|
|
451
|
-
|
|
452
|
-
const enabled = isAutoReadEnabled() // 返回: boolean
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### isStreamEnabled()
|
|
456
|
-
|
|
457
|
-
检查流式组件是否已初始化。
|
|
458
|
-
|
|
459
|
-
```typescript
|
|
460
|
-
import { isStreamEnabled } from 'ocosay'
|
|
461
|
-
|
|
462
|
-
const enabled = isStreamEnabled() // 返回: boolean
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### getStreamStatus()
|
|
466
|
-
|
|
467
|
-
获取流式播放状态。
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
import { getStreamStatus } from 'ocosay'
|
|
471
|
-
|
|
472
|
-
const status = getStreamStatus()
|
|
473
|
-
// 返回: { isActive: boolean, bytesWritten: number, state: string }
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## MiniMax 音色列表
|
|
477
|
-
|
|
478
|
-
| ID | 名称 | 语言 | 性别 |
|
|
479
|
-
|----|------|------|------|
|
|
480
|
-
| male-qn-qingse | 青年清澈 | zh-CN | male |
|
|
481
|
-
| male-qn-qingse_2 | 青年清澈v2 | zh-CN | male |
|
|
482
|
-
| female-shaonv | 少女 | zh-CN | female |
|
|
483
|
-
| male-baiming | 成熟男声 | zh-CN | male |
|
|
484
|
-
| female-tianmei | 甜美女声 | zh-CN | female |
|
|
485
|
-
|
|
486
|
-
## 合成模式
|
|
487
|
-
|
|
488
|
-
| 模式 | 说明 | 适用场景 |
|
|
489
|
-
|------|----------|----------|
|
|
490
|
-
| stream | 流式合成,边生成边播放 | 长文本,首包延迟低 |
|
|
491
|
-
| sync | 同步合成,等待完整音频 | 短文本,一体化返回 |
|
|
492
|
-
| async | 异步合成,轮询获取结果 | 长文本,需要任务队列 |
|
|
493
|
-
|
|
494
|
-
## 架构设计
|
|
495
|
-
|
|
496
|
-
```
|
|
497
|
-
┌─────────────────────────────────────────────────────────────────────┐
|
|
498
|
-
│ OpenCode │
|
|
499
|
-
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|
500
|
-
│ │ Plugin (ocosay) │ │
|
|
501
|
-
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
|
|
502
|
-
│ │ │ Speaker │ │ │
|
|
503
|
-
│ │ │ ┌───────────────────────┐ ┌───────────────────────────┐ │ │ │
|
|
504
|
-
│ │ │ │ StreamReader │ │ StreamingSynthesizer │ │ │ │
|
|
505
|
-
│ │ │ │ (文本缓冲) │ │ (流式合成) │ │ │ │
|
|
506
|
-
│ │ │ └───────────────────────┘ └───────────────────────────┘ │ │ │
|
|
507
|
-
│ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │
|
|
508
|
-
│ │ │ │ StreamPlayer (边收边播) │ │ │ │
|
|
509
|
-
│ │ │ └─────────────────────────────────────────────────────┘ │ │ │
|
|
510
|
-
│ │ │ │ │ │ │
|
|
511
|
-
│ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │
|
|
512
|
-
│ │ │ │ TuiEventBus │ │ │ │
|
|
513
|
-
│ │ │ │ (监听 OpenCode 事件流) │ │ │ │
|
|
514
|
-
│ │ │ └─────────────────────────────────────────────────────┘ │ │ │
|
|
515
|
-
│ │ └───────────────────────────────────────────────────────────┘ │ │
|
|
516
|
-
│ └─────────────────────────────────────────────────────────────────┘ │
|
|
517
|
-
│ │ │
|
|
518
|
-
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|
519
|
-
│ │ TTS Providers (可扩展) │ │
|
|
520
|
-
│ │ ┌──────────────────┐ ┌────────────────────────────────────┐ │ │
|
|
521
|
-
│ │ │ MiniMax │ │ (Future) │ │ │
|
|
522
|
-
│ │ └──────────────────┘ └────────────────────────────────────┘ │ │
|
|
523
|
-
│ └─────────────────────────────────────────────────────────────────┘ │
|
|
524
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
## 错误处理
|
|
528
|
-
|
|
529
|
-
```typescript
|
|
530
|
-
import { speak } from 'ocosay'
|
|
531
|
-
import { TTSError, TTSErrorCode } from 'ocosay'
|
|
532
|
-
|
|
533
|
-
try {
|
|
534
|
-
await speak('你好')
|
|
535
|
-
} catch (error) {
|
|
536
|
-
if (error instanceof TTSError) {
|
|
537
|
-
console.error(`[${error.code}] ${error.message}`)
|
|
538
|
-
console.error(`Provider: ${error.provider}`)
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
### 错误码
|
|
544
|
-
|
|
545
|
-
| 错误码 | 说明 |
|
|
546
|
-
|--------|------|
|
|
547
|
-
| NETWORK | 网络错误 |
|
|
548
|
-
| AUTH | 认证失败 |
|
|
549
|
-
| QUOTA | 配额超限 |
|
|
550
|
-
| INVALID_VOICE | 无效音色 |
|
|
551
|
-
| INVALID_PARAMS | 无效参数 |
|
|
552
|
-
| PLAYER_ERROR | 播放错误 |
|
|
553
|
-
| UNKNOWN | 未知错误 |
|
|
554
|
-
|
|
555
|
-
## Troubleshooting / FAQ
|
|
556
|
-
|
|
557
|
-
### Q: 首包延迟太长怎么办?
|
|
558
|
-
|
|
559
|
-
**A:** 切换到 `stream` 模式,并选择较短的音色如 `female-shaonv`。
|
|
560
|
-
|
|
561
|
-
### Q: 播放出现卡顿?
|
|
562
|
-
|
|
563
|
-
**A:** 检查网络状况,或降低语速 `speed: 0.8`。
|
|
564
|
-
|
|
565
|
-
### Q: 如何切换不同音色?
|
|
566
|
-
|
|
567
|
-
**A:** 使用 `listVoices()` 查看可用音色,通过 `speak(text, { voice: 'voice-id' })` 指定。
|
|
568
|
-
|
|
569
|
-
### Q: 豆包模式不工作?
|
|
570
|
-
|
|
571
|
-
**A:** 确认 TuiEventBus 已正确初始化,并检查 `autoRead` 开关状态。
|
|
572
|
-
|
|
573
|
-
### Q: 报错 AUTH?
|
|
574
|
-
|
|
575
|
-
**A:** 检查 API Key 是否正确配置,或密钥是否过期。
|
|
576
|
-
|
|
577
|
-
## 日志系统
|
|
578
|
-
|
|
579
|
-
ocosay 使用 pino 作为日志库,提供统一的日志格式。
|
|
580
|
-
|
|
581
|
-
### 日志位置
|
|
582
|
-
|
|
583
|
-
```
|
|
584
|
-
~/.ocosay/ocosay.log
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### 日志级别
|
|
588
|
-
|
|
589
|
-
| 级别 | 说明 |
|
|
79
|
+
| 平台 | 状态 |
|
|
590
80
|
|------|------|
|
|
591
|
-
|
|
|
592
|
-
|
|
|
593
|
-
|
|
|
594
|
-
| `debug` | 调试信息 |
|
|
595
|
-
|
|
596
|
-
### 示例
|
|
597
|
-
|
|
598
|
-
```bash
|
|
599
|
-
# 查看实时日志
|
|
600
|
-
tail -f ~/.ocosay/ocosay.log
|
|
601
|
-
|
|
602
|
-
# 查看错误日志
|
|
603
|
-
grep "error" ~/.ocosay/ocosay.log
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
## 开发
|
|
607
|
-
|
|
608
|
-
```bash
|
|
609
|
-
# 安装依赖
|
|
610
|
-
npm install
|
|
611
|
-
|
|
612
|
-
# 构建
|
|
613
|
-
npm run build
|
|
614
|
-
|
|
615
|
-
# 运行测试
|
|
616
|
-
npm test
|
|
617
|
-
|
|
618
|
-
# 监听模式
|
|
619
|
-
npm run watch
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
## Changelog
|
|
623
|
-
|
|
624
|
-
### v1.0.13 (2026-04-06)
|
|
625
|
-
**New Features:**
|
|
626
|
-
- ✨ Toast通知:插件加载成功/失败显示Toast (success/error)
|
|
627
|
-
- ✨ Toast通知:语音播放成功/失败显示Toast (success/error)
|
|
628
|
-
- 🔄 初始化重试机制:session创建时自动重试初始化
|
|
629
|
-
- 🌐 所有Toast内容改为英文
|
|
630
|
-
- 🔒 错误信息脱敏,不暴露内部细节
|
|
631
|
-
|
|
632
|
-
**Bug Fix:**
|
|
633
|
-
- 🐛 修复showToast调用方式(添加延迟+防御性检查)
|
|
634
|
-
|
|
635
|
-
### v1.0.10 (2026-04-05)
|
|
636
|
-
**Bug Fix:**
|
|
637
|
-
- 🐛 修复 showToast 时序问题(初始化成功后才显示 toast)
|
|
638
|
-
- 🐛 添加初始化失败时 error toast 提示
|
|
639
|
-
|
|
640
|
-
### v1.0.8 (2026-04-05)
|
|
641
|
-
**New Features:**
|
|
642
|
-
- ✨ 新增 OpenCode 启动成功弹窗,显示插件版本号和 autoRead 模式状态
|
|
643
|
-
|
|
644
|
-
### v1.0.4 (2026-04-05)
|
|
645
|
-
**Bug Fix & Improvements:**
|
|
646
|
-
- 🐛 修复 npm 包包含源码和测试文件问题(添加 .npmignore)
|
|
647
|
-
- 🎵 默认 voiceId 改为 `female-chengshu`(成熟女性音色)
|
|
648
|
-
- 🔧 默认 baseURL: `https://api.minimaxi.com`
|
|
649
|
-
|
|
650
|
-
### v1.0.5 (2026-04-05)
|
|
651
|
-
**Bug Fix:**
|
|
652
|
-
- 🐛 修复 OpenCode 加载失败 `Plugin export is not a function`
|
|
653
|
-
- 根因:`export default { server }` 让 module.default 是对象不是函数
|
|
654
|
-
- 修复:使用 named export `export const server = OcosayPlugin; export default server`
|
|
655
|
-
|
|
656
|
-
### v1.0.3 (2026-04-05)
|
|
657
|
-
**Bug Fix:**
|
|
658
|
-
- 🐛 修复 OpenCode 加载失败 `Plugin export is not a function`
|
|
659
|
-
- 根因:TypeScript ESM 导入缺少 .js 扩展名
|
|
660
|
-
- 修复:使用 esbuild 打包 plugin.ts
|
|
661
|
-
|
|
662
|
-
### v1.0.2 (2026-04-05)
|
|
663
|
-
**Bug Fix:**
|
|
664
|
-
- 🐛 修复 OpenCode 加载失败 `Plugin export is not a function`
|
|
665
|
-
- 根因:OpenCode 插件加载器期望 `{ server: Plugin }` 格式导出
|
|
666
|
-
- 修复:`export default { server: OcosayPlugin }`
|
|
667
|
-
- 🎵 默认 TTS 模型改为 `speech-2.8-hd`
|
|
668
|
-
|
|
669
|
-
### v1.0.1 (2026-04-05)
|
|
670
|
-
- 📚 多平台 AudioBackend 架构(6 种后端)
|
|
671
|
-
- 🔌 OpenCode 插件标准集成
|
|
672
|
-
- 📋 完整测试覆盖(304 测试)
|
|
673
|
-
|
|
674
|
-
### v1.0.0 (2026-04-05)
|
|
675
|
-
- 🎙️ 初始版本
|
|
676
|
-
- 🔊 多 TTS 模型支持
|
|
677
|
-
- 🎭 多种音色选择
|
|
678
|
-
- 🎛️ 播放控制(暂停、恢复、停止)
|
|
679
|
-
- 📡 豆包模式(边接收边朗读)
|
|
680
|
-
|
|
681
|
-
## Contributing
|
|
682
|
-
|
|
683
|
-
欢迎提交 Issue 和 PR!
|
|
684
|
-
|
|
685
|
-
1. Fork 本仓库
|
|
686
|
-
2. 创建特性分支 (`git checkout -b feature/amazing`)
|
|
687
|
-
3. 提交更改 (`git commit -m 'Add amazing feature'`)
|
|
688
|
-
4. 推送分支 (`git push origin feature/amazing`)
|
|
689
|
-
5. 提交 Pull Request
|
|
690
|
-
|
|
691
|
-
## License
|
|
692
|
-
|
|
693
|
-
MIT
|
|
81
|
+
| macOS | ✅ |
|
|
82
|
+
| Windows WSL | ✅ |
|
|
83
|
+
| Windows 原生 | ⚠️ |
|