@@ -15,28 +15,87 @@ export default defineCommand({
1515 description : 'Generate a song (music-2.5)' ,
1616 usage : 'minimax music generate --prompt <text> [--lyrics <text>] [--out <path>] [flags]' ,
1717 options : [
18- { flag : '--prompt <text>' , description : 'Music style description' } ,
19- { flag : '--lyrics <text>' , description : 'Song lyrics' } ,
20- { flag : '--lyrics-file <path>' , description : 'Read lyrics from file (use - for stdin)' } ,
18+ { flag : '--prompt <text>' , description : 'Music style description (can be detailed — see examples)' } ,
19+ { flag : '--lyrics <text>' , description : 'Song lyrics with structure tags. Use "无歌词" for instrumental music. Cannot be used with --instrumental.' } ,
20+ { flag : '--lyrics-file <path>' , description : 'Read lyrics from file. Use "无歌词" for instrumental. (use - for stdin)' } ,
21+ { flag : '--vocals <text>' , description : 'Vocal style, e.g. "warm male baritone", "bright female soprano", "duet with harmonies"' } ,
22+ { flag : '--genre <text>' , description : 'Music genre, e.g. folk, pop, jazz' } ,
23+ { flag : '--mood <text>' , description : 'Mood or emotion, e.g. warm, melancholic, uplifting' } ,
24+ { flag : '--instruments <text>' , description : 'Instruments to feature, e.g. "acoustic guitar, piano"' } ,
25+ { flag : '--tempo <text>' , description : 'Tempo description, e.g. fast, slow, moderate' } ,
26+ { flag : '--bpm <number>' , description : 'Exact tempo in beats per minute' , type : 'number' } ,
27+ { flag : '--key <text>' , description : 'Musical key, e.g. C major, A minor, G sharp' } ,
28+ { flag : '--avoid <text>' , description : 'Elements to avoid in the generated music' } ,
29+ { flag : '--use-case <text>' , description : 'Use case context, e.g. "background music for video", "theme song"' } ,
30+ { flag : '--structure <text>' , description : 'Song structure, e.g. "verse-chorus-verse-bridge-chorus"' } ,
31+ { flag : '--references <text>' , description : 'Reference tracks or artists, e.g. "similar to Ed Sheeran, Taylor Swift"' } ,
32+ { flag : '--extra <text>' , description : 'Additional fine-grained requirements not covered above' } ,
33+ { flag : '--instrumental' , description : 'Generate instrumental music (no vocals)' } ,
34+ { flag : '--aigc-watermark' , description : 'Embed AI-generated content watermark in audio for content provenance' } ,
2135 { flag : '--format <fmt>' , description : 'Audio format (default: mp3)' } ,
2236 { flag : '--sample-rate <hz>' , description : 'Sample rate (default: 44100)' , type : 'number' } ,
2337 { flag : '--bitrate <bps>' , description : 'Bitrate (default: 256000)' , type : 'number' } ,
2438 { flag : '--stream' , description : 'Stream raw audio to stdout' } ,
2539 { flag : '--out <path>' , description : 'Save audio to file (uses hex decoding)' } ,
2640 ] ,
2741 examples : [
28- 'minimax music generate --prompt "Upbeat pop" --lyrics "La la la..."' ,
29- 'minimax music generate --prompt "Indie folk, melancholic" --lyrics-file song.txt --out my_song.mp3' ,
3042 'minimax music generate --prompt "Upbeat pop" --lyrics "La la la..." --out summer.mp3' ,
43+ 'minimax music generate --prompt "Indie folk, melancholic" --lyrics-file song.txt --out my_song.mp3' ,
44+ '# Detailed prompt with vocal characteristics — music-2.5 responds well to rich descriptions:' ,
45+ 'minimax music generate --prompt "Warm morning folk" --vocals "male and female duet, harmonies in chorus" --instruments "acoustic guitar, piano" --bpm 95 --lyrics-file song.txt --out duet.mp3' ,
46+ '# Instrumental (use --instrumental flag):' ,
47+ 'minimax music generate --prompt "Cinematic orchestral, building tension" --instrumental --out bgm.mp3' ,
48+ '# Or specify "无歌词" in lyrics:' ,
49+ 'minimax music generate --prompt "Cinematic orchestral" --lyrics "无歌词" --out bgm.mp3' ,
3150 ] ,
3251 async run ( config : Config , flags : GlobalFlags ) {
33- const prompt = flags . prompt as string | undefined ;
52+ let prompt = flags . prompt as string | undefined ;
3453 let lyrics = flags . lyrics as string | undefined ;
3554
3655 if ( flags . lyricsFile ) {
3756 lyrics = readTextFromPathOrStdin ( flags . lyricsFile as string ) ;
3857 }
3958
59+ // Check for conflicting flags: --instrumental and --lyrics/--lyrics-file
60+ if ( flags . instrumental && ( lyrics || flags . lyricsFile ) ) {
61+ throw new CLIError (
62+ 'Cannot use --instrumental with --lyrics or --lyrics-file. For instrumental music, simply use --instrumental without --lyrics.' ,
63+ ExitCode . USAGE ,
64+ 'minimax music generate --prompt <style> --instrumental' ,
65+ ) ;
66+ }
67+
68+ // Build structured prompt from optional music characteristic flags.
69+ // music-2.5 interprets rich natural-language prompts — these flags make it
70+ // easy to describe vocal style, genre, mood, and instrumentation without
71+ // needing to hand-craft a long --prompt string.
72+ const structuredParts : string [ ] = [ ] ;
73+ if ( flags . vocals ) structuredParts . push ( `Vocals: ${ flags . vocals as string } ` ) ;
74+ if ( flags . genre ) structuredParts . push ( `Genre: ${ flags . genre as string } ` ) ;
75+ if ( flags . mood ) structuredParts . push ( `Mood: ${ flags . mood as string } ` ) ;
76+ if ( flags . instruments ) structuredParts . push ( `Instruments: ${ flags . instruments as string } ` ) ;
77+ if ( flags . tempo ) structuredParts . push ( `Tempo: ${ flags . tempo as string } ` ) ;
78+ if ( flags . bpm ) structuredParts . push ( `BPM: ${ flags . bpm as number } ` ) ;
79+ if ( flags . key ) structuredParts . push ( `Key: ${ flags . key as string } ` ) ;
80+ if ( flags . avoid ) structuredParts . push ( `Avoid: ${ flags . avoid as string } ` ) ;
81+ if ( flags . useCase ) structuredParts . push ( `Use case: ${ flags . useCase as string } ` ) ;
82+ if ( flags . structure ) structuredParts . push ( `Structure: ${ flags . structure as string } ` ) ;
83+ if ( flags . references ) structuredParts . push ( `References: ${ flags . references as string } ` ) ;
84+ if ( flags . extra ) structuredParts . push ( `Extra: ${ flags . extra as string } ` ) ;
85+
86+ // Handle "无歌词" as instrumental request
87+ if ( lyrics === '无歌词' || lyrics === 'no lyrics' ) {
88+ lyrics = '[intro] [outro]' ;
89+ structuredParts . push ( 'Style: instrumental, no vocals, pure music' ) ;
90+ }
91+
92+ // Handle --instrumental: music-2.5 has no is_instrumental flag,
93+ // so we use the empty-structure lyrics workaround.
94+ if ( flags . instrumental ) {
95+ lyrics = '[intro] [outro]' ;
96+ structuredParts . push ( 'Style: instrumental, no vocals, pure music' ) ;
97+ }
98+
4099 if ( ! prompt && ! lyrics ) {
41100 throw new CLIError (
42101 'At least one of --prompt or --lyrics is required.' ,
@@ -49,6 +108,11 @@ export default defineCommand({
49108 process . stderr . write ( 'Warning: No lyrics provided. Use --lyrics or --lyrics-file to include lyrics.\n' ) ;
50109 }
51110
111+ if ( structuredParts . length > 0 ) {
112+ const structured = structuredParts . join ( '. ' ) ;
113+ prompt = prompt ? `${ prompt } . ${ structured } ` : structured ;
114+ }
115+
52116 const outPath = flags . out as string | undefined ;
53117 const outFormat = outPath ? 'hex' : 'url' ;
54118 const format = detectOutputFormat ( config . output ) ;
@@ -66,6 +130,10 @@ export default defineCommand({
66130 stream : flags . stream === true ,
67131 } ;
68132
133+ if ( flags . aigcWatermark ) {
134+ body . aigc_watermark = true ;
135+ }
136+
69137 if ( config . dryRun ) {
70138 console . log ( formatOutput ( { request : body } , format ) ) ;
71139 return ;
0 commit comments