dsh-audiogen 0.3.0 → 0.3.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/lib/client.js +1046 -546
- package/lib/client.js.map +1 -1
- package/lib/index.js +445 -114
- package/package.json +1 -1
- package/skills/tts/SKILL.md +42 -4
- package/src/agent-audio-tools.ts +75 -2
- package/src/audio-engine.ts +165 -22
- package/src/audio-models.ts +132 -24
- package/src/audio-presets.ts +27 -33
- package/src/client/AudioGenPanel.tsx +69 -1
- package/src/client/SettingsCard.tsx +520 -176
- package/src/client/audio-panel.module.css +36 -0
- package/src/client/locales.ts +84 -16
- package/src/client/settings-card.module.css +424 -607
- package/src/protocol.ts +35 -1
- package/src/routes.ts +42 -3
package/src/protocol.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export const AUDIOGEN_SETTINGS_NAMESPACE = 'dsh-audiogen'
|
|
9
9
|
|
|
10
10
|
/** Published package version shared by the host updater and the client UI. */
|
|
11
|
-
export const PLUGIN_VERSION = '0.3.
|
|
11
|
+
export const PLUGIN_VERSION = '0.3.2'
|
|
12
12
|
|
|
13
13
|
/** Same-origin route family (loopback-only, mirroring dsh-imagegen). */
|
|
14
14
|
export const SETTINGS_API = {
|
|
@@ -94,6 +94,8 @@ export interface PresetProviderView {
|
|
|
94
94
|
name: string
|
|
95
95
|
apiUrl: string
|
|
96
96
|
hint: string
|
|
97
|
+
/** Official vendor website, shown as a link in the channel editor. */
|
|
98
|
+
site?: string
|
|
97
99
|
models: ModelMapping[]
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -120,6 +122,38 @@ export interface GenerateAudioRequest {
|
|
|
120
122
|
channel?: string
|
|
121
123
|
/** Upstream model id actually sent (host-filled from alias mapping). */
|
|
122
124
|
upstream?: string
|
|
125
|
+
|
|
126
|
+
// ---- MiniMax TTS 专属字段(其他厂商渠道忽略)——————
|
|
127
|
+
/** MiniMax 音色情绪,如 happy/sad/angry/nervous/fearful/bored;默认按音色自身。 */
|
|
128
|
+
emotion?: string
|
|
129
|
+
/** MiniMax 音量,范围 0-10,默认 1。 */
|
|
130
|
+
vol?: number
|
|
131
|
+
/** MiniMax 音调偏移(半音),范围 -12~12,默认 0。 */
|
|
132
|
+
pitch?: number
|
|
133
|
+
/** MiniMax 文本归一化处理开关(默认 true)。 */
|
|
134
|
+
textNormalization?: boolean
|
|
135
|
+
/** MiniMax 数学公式朗读开关(默认 false)。 */
|
|
136
|
+
latexRead?: boolean
|
|
137
|
+
/** MiniMax 发音词典 tone 条目,元素形如 "处理/(chu3)(li3)" 或 "危险/dangerous"。 */
|
|
138
|
+
pronunciationTone?: string[]
|
|
139
|
+
/** MiniMax 采样率:16000/24000/32000/44100/48000,默认 32000。 */
|
|
140
|
+
sampleRate?: number
|
|
141
|
+
/** MiniMax 码率(bps):64000-320000,默认 128000。 */
|
|
142
|
+
bitrate?: number
|
|
143
|
+
/** MiniMax 声道数:1 或 2,默认 1。 */
|
|
144
|
+
audioChannel?: number
|
|
145
|
+
/** MiniMax 强制 CBR 编码(avoid VBR),默认 false。 */
|
|
146
|
+
forceCbr?: boolean
|
|
147
|
+
/** MiniMax 字幕开关:true 时响应携带字幕内容/文件。 */
|
|
148
|
+
subtitleEnable?: boolean
|
|
149
|
+
/** MiniMax AIGC 水印开关。 */
|
|
150
|
+
aigcWatermark?: boolean
|
|
151
|
+
/** MiniMax 语言增强(language_boost),如"中英混读",按模型支持情况。 */
|
|
152
|
+
languageBoost?: string
|
|
153
|
+
/** MiniMax 变声参数(voice_modify,speech-2.8 等支持)。 */
|
|
154
|
+
voiceModify?: { pitch?: number; intensity?: number; timbre?: number; soundEffects?: string }
|
|
155
|
+
/** MiniMax 双音色混合权重(timbre_weights)。 */
|
|
156
|
+
timbreWeights?: Array<{ voiceId: string; weight: number }>
|
|
123
157
|
}
|
|
124
158
|
|
|
125
159
|
/** One generated audio, normalized host-side to base64. */
|
package/src/routes.ts
CHANGED
|
@@ -93,16 +93,53 @@ function parseGenerateRequest(body: Record<string, unknown>): GenerateAudioReque
|
|
|
93
93
|
const mode = body.mode === 'music' ? 'music' : body.mode === 'sfx' ? 'sfx' : body.mode === 'voice_design' ? 'voice_design' : 'tts'
|
|
94
94
|
const prompt = typeof body.prompt === 'string' ? body.prompt.trim() : ''
|
|
95
95
|
if (prompt === '') return undefined
|
|
96
|
+
const num = (value: unknown): number | undefined => typeof value === 'number' && Number.isFinite(value) ? value : undefined
|
|
97
|
+
const str = (value: unknown): string | undefined => typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined
|
|
98
|
+
const flag = (value: unknown): boolean | undefined => typeof value === 'boolean' ? value : undefined
|
|
99
|
+
const tone = Array.isArray(body.pronunciationTone)
|
|
100
|
+
? body.pronunciationTone.filter((item): item is string => typeof item === 'string' && item.trim() !== '').map(item => item.trim())
|
|
101
|
+
: undefined
|
|
102
|
+
const voiceModifyRaw = body.voiceModify
|
|
103
|
+
const voiceModify = typeof voiceModifyRaw === 'object' && voiceModifyRaw !== null
|
|
104
|
+
? {
|
|
105
|
+
...(num((voiceModifyRaw as Record<string, unknown>).pitch) !== undefined ? { pitch: num((voiceModifyRaw as Record<string, unknown>).pitch)! } : {}),
|
|
106
|
+
...(num((voiceModifyRaw as Record<string, unknown>).intensity) !== undefined ? { intensity: num((voiceModifyRaw as Record<string, unknown>).intensity)! } : {}),
|
|
107
|
+
...(num((voiceModifyRaw as Record<string, unknown>).timbre) !== undefined ? { timbre: num((voiceModifyRaw as Record<string, unknown>).timbre)! } : {}),
|
|
108
|
+
...(str((voiceModifyRaw as Record<string, unknown>).soundEffects) !== undefined ? { soundEffects: str((voiceModifyRaw as Record<string, unknown>).soundEffects)! } : {}),
|
|
109
|
+
}
|
|
110
|
+
: undefined
|
|
111
|
+
const timbreWeights = Array.isArray(body.timbreWeights)
|
|
112
|
+
? body.timbreWeights
|
|
113
|
+
.filter((item): item is Record<string, unknown> => typeof item === 'object' && item !== null && typeof (item as Record<string, unknown>).voiceId === 'string' && typeof (item as Record<string, unknown>).weight === 'number')
|
|
114
|
+
.map(item => ({ voiceId: (item.voiceId as string).trim(), weight: item.weight as number }))
|
|
115
|
+
.filter(item => item.voiceId !== '')
|
|
116
|
+
: undefined
|
|
96
117
|
return {
|
|
97
118
|
mode,
|
|
98
119
|
model: typeof body.model === 'string' ? body.model.trim() : '',
|
|
99
120
|
prompt,
|
|
100
121
|
...(typeof body.voice === 'string' && body.voice.trim() !== '' ? { voice: body.voice.trim() } : {}),
|
|
101
122
|
...(typeof body.previewText === 'string' && body.previewText.trim() !== '' ? { previewText: body.previewText.trim() } : {}),
|
|
102
|
-
...(
|
|
103
|
-
...(
|
|
123
|
+
...(num(body.speed) !== undefined ? { speed: num(body.speed)! } : {}),
|
|
124
|
+
...(num(body.duration) !== undefined ? { duration: num(body.duration)! } : {}),
|
|
104
125
|
...(typeof body.format === 'string' && body.format.trim() !== '' ? { format: body.format.trim() } : {}),
|
|
105
126
|
...(typeof body.channelId === 'string' && body.channelId !== '' ? { channelId: body.channelId } : {}),
|
|
127
|
+
// ---- MiniMax TTS 专属字段(其他厂商忽略) ----
|
|
128
|
+
...(str(body.emotion) !== undefined ? { emotion: str(body.emotion)! } : {}),
|
|
129
|
+
...(num(body.vol) !== undefined ? { vol: num(body.vol)! } : {}),
|
|
130
|
+
...(num(body.pitch) !== undefined ? { pitch: num(body.pitch)! } : {}),
|
|
131
|
+
...(flag(body.textNormalization) !== undefined ? { textNormalization: flag(body.textNormalization)! } : {}),
|
|
132
|
+
...(flag(body.latexRead) !== undefined ? { latexRead: flag(body.latexRead)! } : {}),
|
|
133
|
+
...(tone !== undefined && tone.length > 0 ? { pronunciationTone: tone } : {}),
|
|
134
|
+
...(num(body.sampleRate) !== undefined ? { sampleRate: num(body.sampleRate)! } : {}),
|
|
135
|
+
...(num(body.bitrate) !== undefined ? { bitrate: num(body.bitrate)! } : {}),
|
|
136
|
+
...(num(body.audioChannel) !== undefined ? { audioChannel: num(body.audioChannel)! } : {}),
|
|
137
|
+
...(flag(body.forceCbr) !== undefined ? { forceCbr: flag(body.forceCbr)! } : {}),
|
|
138
|
+
...(flag(body.subtitleEnable) !== undefined ? { subtitleEnable: flag(body.subtitleEnable)! } : {}),
|
|
139
|
+
...(flag(body.aigcWatermark) !== undefined ? { aigcWatermark: flag(body.aigcWatermark)! } : {}),
|
|
140
|
+
...(str(body.languageBoost) !== undefined ? { languageBoost: str(body.languageBoost)! } : {}),
|
|
141
|
+
...(voiceModify !== undefined ? { voiceModify } : {}),
|
|
142
|
+
...(timbreWeights !== undefined && timbreWeights.length > 0 ? { timbreWeights } : {}),
|
|
106
143
|
}
|
|
107
144
|
}
|
|
108
145
|
|
|
@@ -212,7 +249,9 @@ export function makeRoutes(deps: AudiogenRoutesDeps): WebRoute[] {
|
|
|
212
249
|
?? view.channels[0]
|
|
213
250
|
const channel: AudioChannel = {
|
|
214
251
|
id: stored?.id ?? 'preview',
|
|
215
|
-
|
|
252
|
+
// The draft's own preset wins (even when empty = custom): discovery
|
|
253
|
+
// must follow the vendor being configured, not the default channel.
|
|
254
|
+
preset: typeof body?.preset === 'string' ? body.preset.trim() : (stored?.preset ?? ''),
|
|
216
255
|
name: stored?.name ?? '',
|
|
217
256
|
apiUrl: typeof body?.apiUrl === 'string' && body.apiUrl.trim() !== '' ? body.apiUrl.trim() : (stored?.apiUrl ?? ''),
|
|
218
257
|
apiKey: typeof body?.apiKey === 'string' && body.apiKey.trim() !== '' ? body.apiKey.trim() : (stored?.apiKey ?? ''),
|