dsh-audiogen 0.3.0 → 0.3.3

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/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.0'
11
+ export const PLUGIN_VERSION = '0.3.3'
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
 
@@ -112,6 +114,10 @@ export interface GenerateAudioRequest {
112
114
  speed?: number
113
115
  /** Requested duration in seconds (music/sfx). */
114
116
  duration?: number
117
+ /** MiniMax 音乐生成歌词;music-3.0 / music-cover 在非纯音乐模式下必填。 */
118
+ lyrics?: string
119
+ /** MiniMax 是否生成纯音乐(无歌词/人声);true 时 lyrics 可为空。 */
120
+ isInstrumental?: boolean
115
121
  /** Output format, e.g. mp3, wav, pcm. */
116
122
  format?: string
117
123
  /** Channel this request targets (host falls back to default). */
@@ -120,6 +126,38 @@ export interface GenerateAudioRequest {
120
126
  channel?: string
121
127
  /** Upstream model id actually sent (host-filled from alias mapping). */
122
128
  upstream?: string
129
+
130
+ // ---- MiniMax TTS 专属字段(其他厂商渠道忽略)——————
131
+ /** MiniMax 音色情绪,如 happy/sad/angry/nervous/fearful/bored;默认按音色自身。 */
132
+ emotion?: string
133
+ /** MiniMax 音量,范围 0-10,默认 1。 */
134
+ vol?: number
135
+ /** MiniMax 音调偏移(半音),范围 -12~12,默认 0。 */
136
+ pitch?: number
137
+ /** MiniMax 文本归一化处理开关(默认 true)。 */
138
+ textNormalization?: boolean
139
+ /** MiniMax 数学公式朗读开关(默认 false)。 */
140
+ latexRead?: boolean
141
+ /** MiniMax 发音词典 tone 条目,元素形如 "处理/(chu3)(li3)" 或 "危险/dangerous"。 */
142
+ pronunciationTone?: string[]
143
+ /** MiniMax 采样率:16000/24000/32000/44100/48000,默认 32000。 */
144
+ sampleRate?: number
145
+ /** MiniMax 码率(bps):64000-320000,默认 128000。 */
146
+ bitrate?: number
147
+ /** MiniMax 声道数:1 或 2,默认 1。 */
148
+ audioChannel?: number
149
+ /** MiniMax 强制 CBR 编码(avoid VBR),默认 false。 */
150
+ forceCbr?: boolean
151
+ /** MiniMax 字幕开关:true 时响应携带字幕内容/文件。 */
152
+ subtitleEnable?: boolean
153
+ /** MiniMax AIGC 水印开关。 */
154
+ aigcWatermark?: boolean
155
+ /** MiniMax 语言增强(language_boost),如"中英混读",按模型支持情况。 */
156
+ languageBoost?: string
157
+ /** MiniMax 变声参数(voice_modify,speech-2.8 等支持)。 */
158
+ voiceModify?: { pitch?: number; intensity?: number; timbre?: number; soundEffects?: string }
159
+ /** MiniMax 双音色混合权重(timbre_weights)。 */
160
+ timbreWeights?: Array<{ voiceId: string; weight: number }>
123
161
  }
124
162
 
125
163
  /** One generated audio, normalized host-side to base64. */
package/src/routes.ts CHANGED
@@ -93,16 +93,55 @@ 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
- ...(typeof body.speed === 'number' ? { speed: body.speed } : {}),
103
- ...(typeof body.duration === 'number' ? { duration: body.duration } : {}),
123
+ ...(num(body.speed) !== undefined ? { speed: num(body.speed)! } : {}),
124
+ ...(num(body.duration) !== undefined ? { duration: num(body.duration)! } : {}),
125
+ ...(typeof body.lyrics === 'string' && body.lyrics.trim() !== '' ? { lyrics: body.lyrics.trim() } : {}),
126
+ ...(typeof body.isInstrumental === 'boolean' ? { isInstrumental: body.isInstrumental } : {}),
104
127
  ...(typeof body.format === 'string' && body.format.trim() !== '' ? { format: body.format.trim() } : {}),
105
128
  ...(typeof body.channelId === 'string' && body.channelId !== '' ? { channelId: body.channelId } : {}),
129
+ // ---- MiniMax TTS 专属字段(其他厂商忽略) ----
130
+ ...(str(body.emotion) !== undefined ? { emotion: str(body.emotion)! } : {}),
131
+ ...(num(body.vol) !== undefined ? { vol: num(body.vol)! } : {}),
132
+ ...(num(body.pitch) !== undefined ? { pitch: num(body.pitch)! } : {}),
133
+ ...(flag(body.textNormalization) !== undefined ? { textNormalization: flag(body.textNormalization)! } : {}),
134
+ ...(flag(body.latexRead) !== undefined ? { latexRead: flag(body.latexRead)! } : {}),
135
+ ...(tone !== undefined && tone.length > 0 ? { pronunciationTone: tone } : {}),
136
+ ...(num(body.sampleRate) !== undefined ? { sampleRate: num(body.sampleRate)! } : {}),
137
+ ...(num(body.bitrate) !== undefined ? { bitrate: num(body.bitrate)! } : {}),
138
+ ...(num(body.audioChannel) !== undefined ? { audioChannel: num(body.audioChannel)! } : {}),
139
+ ...(flag(body.forceCbr) !== undefined ? { forceCbr: flag(body.forceCbr)! } : {}),
140
+ ...(flag(body.subtitleEnable) !== undefined ? { subtitleEnable: flag(body.subtitleEnable)! } : {}),
141
+ ...(flag(body.aigcWatermark) !== undefined ? { aigcWatermark: flag(body.aigcWatermark)! } : {}),
142
+ ...(str(body.languageBoost) !== undefined ? { languageBoost: str(body.languageBoost)! } : {}),
143
+ ...(voiceModify !== undefined ? { voiceModify } : {}),
144
+ ...(timbreWeights !== undefined && timbreWeights.length > 0 ? { timbreWeights } : {}),
106
145
  }
107
146
  }
108
147
 
@@ -212,7 +251,9 @@ export function makeRoutes(deps: AudiogenRoutesDeps): WebRoute[] {
212
251
  ?? view.channels[0]
213
252
  const channel: AudioChannel = {
214
253
  id: stored?.id ?? 'preview',
215
- preset: stored?.preset ?? '',
254
+ // The draft's own preset wins (even when empty = custom): discovery
255
+ // must follow the vendor being configured, not the default channel.
256
+ preset: typeof body?.preset === 'string' ? body.preset.trim() : (stored?.preset ?? ''),
216
257
  name: stored?.name ?? '',
217
258
  apiUrl: typeof body?.apiUrl === 'string' && body.apiUrl.trim() !== '' ? body.apiUrl.trim() : (stored?.apiUrl ?? ''),
218
259
  apiKey: typeof body?.apiKey === 'string' && body.apiKey.trim() !== '' ? body.apiKey.trim() : (stored?.apiKey ?? ''),