Sayoyo commited on
Commit
bc7cf7a
·
1 Parent(s): 4893237

feat: add skills

Browse files
skills/acemusic/SKILL.md ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: acemusic
3
+ description: 使用 ACE-Step API 生成音乐、编辑歌曲、remix 音乐。支持文本生成音乐、歌词生成、音频续写、音频重绘等功能。当用户提到生成音乐、创作歌曲、音乐制作、remix、续写音频时使用此技能。
4
+ allowed-tools: Read, Write, Bash
5
+ ---
6
+
7
+ # ACE Music 音乐生成技能
8
+
9
+ 使用 ACE-Step V1.5 API 进行音乐生成和编辑。
10
+
11
+ ## 输出文件
12
+
13
+ 生成完成后,脚本会自动保存结果到项目根目录的 `acemusic_output` 文件夹(与 `.claude` 同级):
14
+
15
+ ```
16
+ project_root/
17
+ ├── .claude/
18
+ │ └── skills/acemusic/...
19
+ ├── acemusic_output/ # 输出目录
20
+ │ ├── <job_id>.json # 完整任务结果 (JSON)
21
+ │ ├── <job_id>_1.mp3 # 第一个音频文件
22
+ │ ├── <job_id>_2.mp3 # 第二个音频文件 (如有 batch_size > 1)
23
+ │ └── ...
24
+ └── ...
25
+ ```
26
+
27
+ ## 配置说明
28
+
29
+ 脚本使用 `scripts/config.json` 管理默认配置。
30
+
31
+ ### 配置优先级规则
32
+
33
+ **重要**: 配置遵循以下优先级(从高到低):
34
+
35
+ 1. **命令行参数** > **config.json 默认值**
36
+ 2. 用户指定的参数会**临时覆盖**默认配置,但**不会修改** config.json
37
+ 3. 只有使用 `config --set` 命令时才会**永久修改** config.json
38
+
39
+ **示例**:
40
+ ```bash
41
+ # config.json 中 thinking=true
42
+
43
+ # 使用默认配置(thinking=true)
44
+ ./scripts/acemusic.sh generate "流行音乐"
45
+
46
+ # 临时覆盖(本次 thinking=false,但 config.json 不变)
47
+ ./scripts/acemusic.sh generate "流行音乐" --no-thinking
48
+
49
+ # 永久修改默认配置
50
+ ./scripts/acemusic.sh config --set generation.thinking false
51
+ ```
52
+
53
+ ### API 连接流程
54
+
55
+ 1. **加载配置**: 读取 `scripts/config.json`(不存在则使用内置默认值)
56
+ 2. **健康检查**: 请求 `/health` 端点验证服务可用性
57
+ 3. **连接失败处理**: 提示用户输入正确的 API 地址并保存到 config.json
58
+
59
+ ### 默认配置文件 (`scripts/config.json`)
60
+
61
+ ```json
62
+ {
63
+ "api_url": "http://127.0.0.1:8001",
64
+ "generation": {
65
+ "thinking": true,
66
+ "use_format": true,
67
+ "use_cot_caption": true,
68
+ "use_cot_language": true,
69
+ "batch_size": 1,
70
+ "audio_format": "mp3",
71
+ "vocal_language": "en"
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### 配置项说明
77
+
78
+ | 配置项 | 默认值 | 描述 |
79
+ |--------|--------|------|
80
+ | `api_url` | `http://127.0.0.1:8001` | API 服务地址 |
81
+ | `generation.thinking` | `true` | 启用 5Hz LM 模型(高质量模式) |
82
+ | `generation.use_format` | `true` | 使用 LM 增强 caption/lyrics |
83
+ | `generation.use_cot_caption` | `true` | 使用 CoT 增强 caption |
84
+ | `generation.use_cot_language` | `true` | 使用 CoT 增强语言检测 |
85
+ | `generation.audio_format` | `mp3` | 输出格式 |
86
+ | `generation.vocal_language` | `en` | 演唱语言 |
87
+
88
+ ## 脚本使用方法
89
+
90
+ ### 配置管理(永久修改 config.json)
91
+
92
+ ```bash
93
+ # 查看所有配置
94
+ ./scripts/acemusic.sh config
95
+
96
+ # 列出所有配置项及当前值
97
+ ./scripts/acemusic.sh config --list
98
+
99
+ # 获取单个配置值
100
+ ./scripts/acemusic.sh config --get generation.thinking
101
+
102
+ # 永久修改配置值(会写入 config.json)
103
+ ./scripts/acemusic.sh config --set generation.thinking false
104
+ ./scripts/acemusic.sh config --set api_url http://192.168.1.100:8001
105
+
106
+ # 重置为默认配置
107
+ ./scripts/acemusic.sh config --reset
108
+ ```
109
+
110
+ ### 生成音乐(命令行参数临时覆盖,不修改 config.json)
111
+
112
+ 支持两种生成模式:
113
+
114
+ **Caption 模式** - 直接指定音乐风格描述
115
+ ```bash
116
+ ./scripts/acemusic.sh generate "流行音乐,吉他伴奏"
117
+ ./scripts/acemusic.sh generate -c "抒情流行" -l "[Verse] 歌词内容"
118
+ ```
119
+
120
+ **Simple 模式** - 用简单描述,LM 自动生成 caption 和 lyrics
121
+ ```bash
122
+ ./scripts/acemusic.sh generate -d "一首关于春天的欢快歌曲"
123
+ ./scripts/acemusic.sh generate -d "A love song for February"
124
+ ```
125
+
126
+ **其他选项**
127
+ ```bash
128
+ # 临时禁用 thinking 模式(本次生效,不修改配置文件)
129
+ ./scripts/acemusic.sh generate "电子舞曲" --no-thinking
130
+
131
+ # 临时禁用 format 模式
132
+ ./scripts/acemusic.sh generate "古典钢琴" --no-format
133
+
134
+ # 临时指定其他参数
135
+ ./scripts/acemusic.sh generate "爵士乐" --steps 16 --guidance 8.0
136
+
137
+ # 随机生成
138
+ ./scripts/acemusic.sh random
139
+
140
+ # 查询任务状态(已完成的任务会自动下载音频)
141
+ ./scripts/acemusic.sh status <job_id>
142
+
143
+ # 列出可用模型
144
+ ./scripts/acemusic.sh models
145
+
146
+ # 检查 API 健康状态
147
+ ./scripts/acemusic.sh health
148
+ ```
149
+
150
+ ### Shell 脚本 (Linux/macOS,需要 curl + jq)
151
+
152
+ ```bash
153
+ # 配置管理
154
+ ./scripts/acemusic.sh config --list
155
+ ./scripts/acemusic.sh config --set generation.thinking false
156
+
157
+ # Caption 模式(完成后自动保存结果和下载音频)
158
+ ./scripts/acemusic.sh generate "流行音乐,吉他伴奏"
159
+ ./scripts/acemusic.sh generate -c "抒情流行" -l "[Verse] 歌词内容"
160
+
161
+ # Simple 模式(LM 自动生成 caption/lyrics)
162
+ ./scripts/acemusic.sh generate -d "一首关于春天的欢快歌曲"
163
+
164
+ # 随机生成
165
+ ./scripts/acemusic.sh random
166
+
167
+ # 其他命令
168
+ ./scripts/acemusic.sh status <job_id>
169
+ ./scripts/acemusic.sh models
170
+ ./scripts/acemusic.sh health
171
+ ```
172
+
173
+ ## 脚本依赖
174
+
175
+ | 脚本 | 依赖 | 平台 |
176
+ |------|------|------|
177
+ | `acemusic.sh` | curl | Linux/macOS/Git Bash |
178
+
179
+ ## API 端点
180
+
181
+ | 端点 | 方法 | 描述 |
182
+ |------|------|------|
183
+ | `/health` | GET | 健康检查 |
184
+ | `/v1/music/generate` | POST | 创建音乐生成任务 |
185
+ | `/v1/music/random` | POST | 创建随机采样任务 |
186
+ | `/v1/jobs/{job_id}` | GET | 查询任务状态和结果 |
187
+ | `/v1/models` | GET | 列出可用模型 |
188
+ | `/v1/audio?path={path}` | GET | 下载生成的音频文件 |
189
+
190
+ ## 主要参数
191
+
192
+ ### 基础参数
193
+
194
+ | 参数 | 类型 | 默认值 | 描述 |
195
+ |------|------|--------|------|
196
+ | `caption` | string | - | 音乐描述文本(Caption 模式) |
197
+ | `description` | string | - | 简单描述,LM 自动生成 caption/lyrics(Simple 模式) |
198
+ | `lyrics` | string | "" | 歌词内容 |
199
+ | `thinking` | bool | true | 启用 5Hz LM 模型(高质量) |
200
+ | `use_format` | bool | true | 使用 LM 增强输入 |
201
+ | `model` | string | - | 指定模型名称 |
202
+
203
+ ### 音乐属性
204
+
205
+ | 参数 | 类型 | 默认值 | 描述 |
206
+ |------|------|--------|------|
207
+ | `bpm` | int | - | 节奏速度 |
208
+ | `key_scale` | string | - | 调性 (如 "C Major") |
209
+ | `time_signature` | string | - | 拍号 (如 "4/4") |
210
+ | `vocal_language` | string | "en" | 演唱语言 (en/zh) |
211
+ | `audio_duration` | float | - | 音频时长(秒) |
212
+
213
+ ### 任务类型
214
+
215
+ | 参数值 | 描述 |
216
+ |--------|------|
217
+ | `text2music` | 文本生成音乐(默认) |
218
+ | `continuation` | 音频续写 |
219
+ | `repainting` | 音频重绘 |
220
+
221
+ ## 任务状态
222
+
223
+ | 状态 | 描述 |
224
+ |------|------|
225
+ | `queued` | 排队等待中 |
226
+ | `running` | 正在生成 |
227
+ | `succeeded` | 生成成功 |
228
+ | `failed` | 生成失败 |
229
+
230
+ ## 响应示例
231
+
232
+ ### 创建任务响应
233
+
234
+ ```json
235
+ {
236
+ "job_id": "abc123-def456",
237
+ "status": "queued",
238
+ "queue_position": 1
239
+ }
240
+ ```
241
+
242
+ ### 任务完成响应
243
+
244
+ ```json
245
+ {
246
+ "job_id": "abc123-def456",
247
+ "status": "succeeded",
248
+ "result": {
249
+ "audio_paths": ["/v1/audio?path=..."],
250
+ "bpm": 120,
251
+ "keyscale": "C Major",
252
+ "duration": 60.0,
253
+ "dit_model": "acestep-v15-turbo-rl"
254
+ }
255
+ }
256
+ ```
257
+
258
+ ## 注意事项
259
+
260
+ 1. **配置优先级**: 命令行参数 > config.json 默认值。用户指定的参数临时生效,不修改配置文件
261
+ 2. **修改默认配置**: 只有 `config --set` 命令才会永久修改 config.json
262
+ 3. **默认高质量模式**: `thinking=true`, `use_format=true`,可通过 `--no-thinking`/`--no-format` 临时禁用
263
+ 4. **异步任务**: 所有生成任务都是异步的,需要轮询 `/v1/jobs/{job_id}` 获取结果
264
+ 5. **自动下载**: 任务完成后会自动保存 JSON 结果并下载音频文件到 `acemusic_output/` 目录
265
+
266
+ ## 参考资源
267
+ - Shell 脚本: [scripts/acemusic.sh](scripts/acemusic.sh) (Linux/macOS/Git Bash)
268
+ - 默认配置: [scripts/config.json](scripts/config.json)
269
+ - 输出目录: 项目根目录下的 `acemusic_output/`
skills/acemusic/scripts/acemusic.sh ADDED
@@ -0,0 +1,623 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # ACE-Step Music Generation CLI (Bash + Curl)
4
+ #
5
+ # Requirements: curl (no jq needed)
6
+ #
7
+ # Usage:
8
+ # ./acemusic.sh generate "Music description" [options]
9
+ # ./acemusic.sh random [--no-thinking]
10
+ # ./acemusic.sh status <job_id>
11
+ # ./acemusic.sh models
12
+ # ./acemusic.sh health
13
+ # ./acemusic.sh config [--get|--set|--reset]
14
+ #
15
+ # Output:
16
+ # - Results saved to output/<job_id>.json
17
+ # - Audio files downloaded to output/<job_id>_1.mp3, output/<job_id>_2.mp3, ...
18
+
19
+ set -e
20
+
21
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22
+ CONFIG_FILE="${SCRIPT_DIR}/config.json"
23
+ # Output dir at same level as .claude (go up 4 levels from scripts/)
24
+ OUTPUT_DIR="$(cd "${SCRIPT_DIR}/../../../.." && pwd)/acemusic_output"
25
+ DEFAULT_API_URL="http://127.0.0.1:8001"
26
+
27
+ # Colors
28
+ RED='\033[0;31m'
29
+ GREEN='\033[0;32m'
30
+ YELLOW='\033[1;33m'
31
+ CYAN='\033[0;36m'
32
+ NC='\033[0m'
33
+
34
+ # Check dependencies
35
+ check_deps() {
36
+ if ! command -v curl &> /dev/null; then
37
+ echo -e "${RED}Error: curl is required but not installed.${NC}"
38
+ exit 1
39
+ fi
40
+ }
41
+
42
+ # Simple JSON value extractor (no jq needed)
43
+ # Usage: json_get "$json" "key"
44
+ json_get() {
45
+ local json="$1"
46
+ local key="$2"
47
+ # Handle nested keys like "generation.thinking"
48
+ if [[ "$key" == *.* ]]; then
49
+ local first="${key%%.*}"
50
+ local rest="${key#*.}"
51
+ # Extract the nested object
52
+ local nested=$(echo "$json" | sed 's/.*"'"$first"'"[[:space:]]*:[[:space:]]*{/{/' | sed 's/}.*/}/' | head -1)
53
+ json_get "$nested" "$rest"
54
+ else
55
+ # Extract string value
56
+ local result=$(echo "$json" | grep -o '"'"$key"'"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1)
57
+ if [ -z "$result" ]; then
58
+ # Try non-string value (number, boolean, null)
59
+ result=$(echo "$json" | grep -o '"'"$key"'"[[:space:]]*:[[:space:]]*[^,}]*' | sed 's/.*:[[:space:]]*//' | tr -d ' ' | head -1)
60
+ fi
61
+ echo "$result"
62
+ fi
63
+ }
64
+
65
+ # Extract array values (for audio_paths)
66
+ json_get_array() {
67
+ local json="$1"
68
+ local key="$2"
69
+ echo "$json" | grep -o '"'"$key"'"[[:space:]]*:[[:space:]]*\[[^]]*\]' | sed 's/.*\[//;s/\].*//' | tr ',' '\n' | sed 's/^[[:space:]]*"//;s/"[[:space:]]*$//'
70
+ }
71
+
72
+ # Escape string for JSON
73
+ json_escape() {
74
+ local str="$1"
75
+ # Escape backslashes, double quotes, and control characters
76
+ str="${str//\\/\\\\}"
77
+ str="${str//\"/\\\"}"
78
+ str="${str//$'\n'/\\n}"
79
+ str="${str//$'\r'/\\r}"
80
+ str="${str//$'\t'/\\t}"
81
+ echo "$str"
82
+ }
83
+
84
+ # Ensure output directory exists
85
+ ensure_output_dir() {
86
+ mkdir -p "$OUTPUT_DIR"
87
+ }
88
+
89
+ # Default config
90
+ DEFAULT_CONFIG='{
91
+ "api_url": "http://127.0.0.1:8001",
92
+ "generation": {
93
+ "thinking": true,
94
+ "use_format": true,
95
+ "use_cot_caption": true,
96
+ "use_cot_language": true,
97
+ "audio_format": "mp3",
98
+ "vocal_language": "en"
99
+ }
100
+ }'
101
+
102
+ # Ensure config file exists
103
+ ensure_config() {
104
+ if [ ! -f "$CONFIG_FILE" ]; then
105
+ echo "$DEFAULT_CONFIG" > "$CONFIG_FILE"
106
+ fi
107
+ }
108
+
109
+ # Get config value
110
+ get_config() {
111
+ local key="$1"
112
+ ensure_config
113
+ local json=$(cat "$CONFIG_FILE" | tr -d '\n')
114
+ json_get "$json" "$key"
115
+ }
116
+
117
+ # Set config value (simple implementation)
118
+ set_config() {
119
+ local key="$1"
120
+ local value="$2"
121
+ ensure_config
122
+
123
+ local tmp_file="${CONFIG_FILE}.tmp"
124
+
125
+ if [[ "$key" == *.* ]]; then
126
+ # Nested key like "generation.thinking"
127
+ local subkey="${key#*.}"
128
+
129
+ if [ "$value" = "true" ] || [ "$value" = "false" ]; then
130
+ sed "s/\"$subkey\"[[:space:]]*:[[:space:]]*[^,}]*/\"$subkey\": $value/" "$CONFIG_FILE" > "$tmp_file"
131
+ elif [[ "$value" =~ ^[0-9]+$ ]] || [[ "$value" =~ ^[0-9]+\.[0-9]+$ ]]; then
132
+ sed "s/\"$subkey\"[[:space:]]*:[[:space:]]*[^,}]*/\"$subkey\": $value/" "$CONFIG_FILE" > "$tmp_file"
133
+ else
134
+ sed "s/\"$subkey\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"$subkey\": \"$value\"/" "$CONFIG_FILE" > "$tmp_file"
135
+ fi
136
+ else
137
+ # Top-level key
138
+ if [ "$value" = "true" ] || [ "$value" = "false" ]; then
139
+ sed "s/\"$key\"[[:space:]]*:[[:space:]]*[^,}]*/\"$key\": $value/" "$CONFIG_FILE" > "$tmp_file"
140
+ elif [[ "$value" =~ ^[0-9]+$ ]] || [[ "$value" =~ ^[0-9]+\.[0-9]+$ ]]; then
141
+ sed "s/\"$key\"[[:space:]]*:[[:space:]]*[^,}]*/\"$key\": $value/" "$CONFIG_FILE" > "$tmp_file"
142
+ else
143
+ sed "s/\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"$key\": \"$value\"/" "$CONFIG_FILE" > "$tmp_file"
144
+ fi
145
+ fi
146
+
147
+ mv "$tmp_file" "$CONFIG_FILE"
148
+ echo "Set $key = $value"
149
+ }
150
+
151
+ # Load API URL
152
+ load_api_url() {
153
+ local url=$(get_config "api_url")
154
+ echo "${url:-$DEFAULT_API_URL}"
155
+ }
156
+
157
+ # Check API health
158
+ check_health() {
159
+ local url="$1"
160
+ local status
161
+ status=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "${url}/health" 2>/dev/null) || true
162
+ [ "$status" = "200" ]
163
+ }
164
+
165
+ # Prompt for URL
166
+ prompt_for_url() {
167
+ echo ""
168
+ echo -e "${YELLOW}API server is not responding.${NC}"
169
+ echo "Please enter the API URL (or press Enter for default):"
170
+ read -p "API URL [$DEFAULT_API_URL]: " user_input
171
+ echo "${user_input:-$DEFAULT_API_URL}"
172
+ }
173
+
174
+ # Ensure API connection
175
+ ensure_connection() {
176
+ ensure_config
177
+ local api_url=$(load_api_url)
178
+
179
+ if check_health "$api_url"; then
180
+ echo "$api_url"
181
+ return 0
182
+ fi
183
+
184
+ echo -e "${YELLOW}Cannot connect to: $api_url${NC}" >&2
185
+ local new_url=$(prompt_for_url)
186
+
187
+ if check_health "$new_url"; then
188
+ set_config "api_url" "$new_url" > /dev/null
189
+ echo -e "${GREEN}Saved API URL: $new_url${NC}" >&2
190
+ echo "$new_url"
191
+ return 0
192
+ fi
193
+
194
+ echo -e "${RED}Error: Cannot connect to $new_url${NC}" >&2
195
+ exit 1
196
+ }
197
+
198
+ # Download audio files from result
199
+ download_audios() {
200
+ local api_url="$1"
201
+ local job_id="$2"
202
+ local result_json="$3"
203
+
204
+ ensure_output_dir
205
+
206
+ local audio_format=$(get_config "generation.audio_format")
207
+ [ -z "$audio_format" ] && audio_format="mp3"
208
+
209
+ local count=1
210
+ for audio_path in $(json_get_array "$result_json" "audio_paths"); do
211
+ if [ -n "$audio_path" ]; then
212
+ local output_file="${OUTPUT_DIR}/${job_id}_${count}.${audio_format}"
213
+ local download_url="${api_url}${audio_path}"
214
+
215
+ echo -e " ${CYAN}Downloading audio $count...${NC}"
216
+ if curl -s -o "$output_file" "$download_url"; then
217
+ echo -e " ${GREEN}Saved: $output_file${NC}"
218
+ else
219
+ echo -e " ${RED}Failed to download: $download_url${NC}"
220
+ fi
221
+ count=$((count + 1))
222
+ fi
223
+ done
224
+ }
225
+
226
+ # Save result to JSON file
227
+ save_result() {
228
+ local job_id="$1"
229
+ local result_json="$2"
230
+
231
+ ensure_output_dir
232
+ local output_file="${OUTPUT_DIR}/${job_id}.json"
233
+ echo "$result_json" > "$output_file"
234
+ echo -e "${GREEN}Result saved: $output_file${NC}"
235
+ }
236
+
237
+ # Health command
238
+ cmd_health() {
239
+ check_deps
240
+ ensure_config
241
+ local api_url=$(load_api_url)
242
+
243
+ echo "Checking API at: $api_url"
244
+ if check_health "$api_url"; then
245
+ echo -e "${GREEN}Status: OK${NC}"
246
+ curl -s "${api_url}/health"
247
+ echo ""
248
+ else
249
+ echo -e "${RED}Status: FAILED${NC}"
250
+ exit 1
251
+ fi
252
+ }
253
+
254
+ # Config command
255
+ cmd_config() {
256
+ check_deps
257
+ ensure_config
258
+
259
+ local action=""
260
+ local key=""
261
+ local value=""
262
+
263
+ while [[ $# -gt 0 ]]; do
264
+ case $1 in
265
+ --get) action="get"; key="$2"; shift 2 ;;
266
+ --set) action="set"; key="$2"; value="$3"; shift 3 ;;
267
+ --reset) action="reset"; shift ;;
268
+ --list) action="list"; shift ;;
269
+ *) shift ;;
270
+ esac
271
+ done
272
+
273
+ case "$action" in
274
+ "get")
275
+ [ -z "$key" ] && { echo -e "${RED}Error: --get requires KEY${NC}"; exit 1; }
276
+ local result=$(get_config "$key")
277
+ [ -n "$result" ] && echo "$key = $result" || echo "Key not found: $key"
278
+ ;;
279
+ "set")
280
+ [ -z "$key" ] || [ -z "$value" ] && { echo -e "${RED}Error: --set requires KEY VALUE${NC}"; exit 1; }
281
+ set_config "$key" "$value"
282
+ ;;
283
+ "reset")
284
+ echo "$DEFAULT_CONFIG" > "$CONFIG_FILE"
285
+ echo -e "${GREEN}Configuration reset to defaults.${NC}"
286
+ cat "$CONFIG_FILE"
287
+ ;;
288
+ "list")
289
+ echo "Current configuration:"
290
+ cat "$CONFIG_FILE"
291
+ ;;
292
+ *)
293
+ echo "Config file: $CONFIG_FILE"
294
+ echo "Output dir: $OUTPUT_DIR"
295
+ echo "----------------------------------------"
296
+ cat "$CONFIG_FILE"
297
+ echo "----------------------------------------"
298
+ echo ""
299
+ echo "Usage:"
300
+ echo " config --list Show config"
301
+ echo " config --get <key> Get value"
302
+ echo " config --set <key> <val> Set value"
303
+ echo " config --reset Reset to defaults"
304
+ ;;
305
+ esac
306
+ }
307
+
308
+ # Models command
309
+ cmd_models() {
310
+ check_deps
311
+ local api_url=$(ensure_connection)
312
+
313
+ echo "Available Models:"
314
+ echo "----------------------------------------"
315
+ curl -s "${api_url}/v1/models"
316
+ echo ""
317
+ }
318
+
319
+ # Status command
320
+ cmd_status() {
321
+ check_deps
322
+ local job_id="$1"
323
+
324
+ [ -z "$job_id" ] && { echo -e "${RED}Error: job_id required${NC}"; echo "Usage: $0 status <job_id>"; exit 1; }
325
+
326
+ local api_url=$(ensure_connection)
327
+ local response=$(curl -s "${api_url}/v1/jobs/${job_id}")
328
+
329
+ local status=$(json_get "$response" "status")
330
+ echo "Job ID: $(json_get "$response" "job_id")"
331
+ echo "Status: $status"
332
+
333
+ if [ "$status" = "queued" ]; then
334
+ echo "Queue Position: $(json_get "$response" "queue_position")"
335
+ fi
336
+
337
+ if [ "$status" = "succeeded" ]; then
338
+ echo ""
339
+ echo "Result:"
340
+ echo " BPM: $(json_get "$response" "bpm")"
341
+ echo " Key: $(json_get "$response" "keyscale")"
342
+ echo " Duration: $(json_get "$response" "duration")s"
343
+
344
+ # Save and download
345
+ save_result "$job_id" "$response"
346
+ download_audios "$api_url" "$job_id" "$response"
347
+ fi
348
+
349
+ if [ "$status" = "failed" ]; then
350
+ echo ""
351
+ echo -e "${RED}Error: $(json_get "$response" "error")${NC}"
352
+ fi
353
+ }
354
+
355
+ # Wait for job and download results
356
+ wait_for_job() {
357
+ local api_url="$1"
358
+ local job_id="$2"
359
+
360
+ echo "Job created: $job_id"
361
+ echo "Output: $OUTPUT_DIR"
362
+ echo ""
363
+
364
+ while true; do
365
+ local response=$(curl -s "${api_url}/v1/jobs/${job_id}")
366
+ local status=$(json_get "$response" "status")
367
+
368
+ case "$status" in
369
+ "succeeded")
370
+ echo ""
371
+ echo -e "${GREEN}Generation completed!${NC}"
372
+ echo ""
373
+ echo "Metadata:"
374
+ echo " BPM: $(json_get "$response" "bpm")"
375
+ echo " Key: $(json_get "$response" "keyscale")"
376
+ echo " Duration: $(json_get "$response" "duration")s"
377
+ echo ""
378
+
379
+ # Save result JSON
380
+ save_result "$job_id" "$response"
381
+
382
+ # Download audio files
383
+ echo "Downloading audio files..."
384
+ download_audios "$api_url" "$job_id" "$response"
385
+
386
+ echo ""
387
+ echo -e "${GREEN}Done! Files saved to: $OUTPUT_DIR${NC}"
388
+ return 0
389
+ ;;
390
+ "failed")
391
+ echo ""
392
+ echo -e "${RED}Generation failed!${NC}"
393
+ echo "Error: $(json_get "$response" "error")"
394
+
395
+ # Save error result
396
+ save_result "$job_id" "$response"
397
+ return 1
398
+ ;;
399
+ "queued")
400
+ local pos=$(json_get "$response" "queue_position")
401
+ printf "\rQueued (position: %s)... " "${pos:-?}"
402
+ ;;
403
+ *)
404
+ printf "\rGenerating... "
405
+ ;;
406
+ esac
407
+ sleep 5
408
+ done
409
+ }
410
+
411
+ # Generate command
412
+ cmd_generate() {
413
+ check_deps
414
+ ensure_config
415
+
416
+ local caption="" lyrics="" description="" thinking="" use_format=""
417
+ local no_thinking=false no_format=false no_wait=false
418
+ local model="" language="" steps="" guidance="" seed="" duration="" bpm="" batch=""
419
+
420
+ while [[ $# -gt 0 ]]; do
421
+ case $1 in
422
+ --caption|-c) caption="$2"; shift 2 ;;
423
+ --lyrics|-l) lyrics="$2"; shift 2 ;;
424
+ --description|-d) description="$2"; shift 2 ;;
425
+ --thinking|-t) thinking="true"; shift ;;
426
+ --no-thinking) no_thinking=true; shift ;;
427
+ --use-format) use_format="true"; shift ;;
428
+ --no-format) no_format=true; shift ;;
429
+ --model|-m) model="$2"; shift 2 ;;
430
+ --language) language="$2"; shift 2 ;;
431
+ --steps) steps="$2"; shift 2 ;;
432
+ --guidance) guidance="$2"; shift 2 ;;
433
+ --seed) seed="$2"; shift 2 ;;
434
+ --duration) duration="$2"; shift 2 ;;
435
+ --bpm) bpm="$2"; shift 2 ;;
436
+ --batch) batch="$2"; shift 2 ;;
437
+ --no-wait) no_wait=true; shift ;;
438
+ *) [ -z "$caption" ] && caption="$1"; shift ;;
439
+ esac
440
+ done
441
+
442
+ # If no caption but has description, use simple mode
443
+ if [ -z "$caption" ] && [ -z "$description" ]; then
444
+ echo -e "${RED}Error: caption or description required${NC}"
445
+ echo "Usage: $0 generate \"Music description\" [options]"
446
+ echo " $0 generate -d \"Simple description\" [options]"
447
+ exit 1
448
+ fi
449
+
450
+ local api_url=$(ensure_connection)
451
+
452
+ # Get defaults
453
+ local def_thinking=$(get_config "generation.thinking")
454
+ local def_format=$(get_config "generation.use_format")
455
+ local def_cot_caption=$(get_config "generation.use_cot_caption")
456
+ local def_cot_language=$(get_config "generation.use_cot_language")
457
+ local def_language=$(get_config "generation.vocal_language")
458
+ local def_audio_format=$(get_config "generation.audio_format")
459
+
460
+ [ -z "$thinking" ] && thinking="${def_thinking:-true}"
461
+ [ -z "$use_format" ] && use_format="${def_format:-true}"
462
+ [ -z "$language" ] && language="${def_language:-en}"
463
+
464
+ [ "$no_thinking" = true ] && thinking="false"
465
+ [ "$no_format" = true ] && use_format="false"
466
+
467
+ # Build payload manually (no jq) - escape strings for JSON
468
+ local esc_caption=$(json_escape "$caption")
469
+ local esc_lyrics=$(json_escape "${lyrics:-}")
470
+ local esc_description=$(json_escape "${description:-}")
471
+
472
+ local payload="{"
473
+ payload+="\"caption\":\"${esc_caption}\","
474
+ payload+="\"lyrics\":\"${esc_lyrics}\","
475
+ payload+="\"sample_query\":\"${esc_description}\","
476
+ payload+="\"thinking\":${thinking},"
477
+ payload+="\"use_format\":${use_format},"
478
+ payload+="\"use_cot_caption\":${def_cot_caption:-true},"
479
+ payload+="\"use_cot_language\":${def_cot_language:-true},"
480
+ payload+="\"vocal_language\":\"${language}\","
481
+ payload+="\"audio_format\":\"${def_audio_format:-mp3}\","
482
+ payload+="\"use_random_seed\":true"
483
+
484
+ [ -n "$model" ] && payload+=",\"model\":\"$(json_escape "$model")\""
485
+ [ -n "$steps" ] && payload+=",\"inference_steps\":${steps}"
486
+ [ -n "$guidance" ] && payload+=",\"guidance_scale\":${guidance}"
487
+ [ -n "$seed" ] && payload+=",\"seed\":${seed},\"use_random_seed\":false"
488
+ [ -n "$duration" ] && payload+=",\"audio_duration\":${duration}"
489
+ [ -n "$bpm" ] && payload+=",\"bpm\":${bpm}"
490
+ [ -n "$batch" ] && payload+=",\"batch_size\":${batch}"
491
+
492
+ payload+="}"
493
+
494
+ echo "Generating music..."
495
+ if [ -n "$description" ]; then
496
+ echo " Mode: Simple (description)"
497
+ echo " Description: ${description:0:50}..."
498
+ else
499
+ echo " Mode: Caption"
500
+ echo " Caption: ${caption:0:50}..."
501
+ fi
502
+ echo " Thinking: $thinking, Format: $use_format"
503
+ echo " Output: $OUTPUT_DIR"
504
+ echo ""
505
+
506
+ # Write payload to temp file to ensure UTF-8 encoding
507
+ local temp_payload=$(mktemp)
508
+ printf "%s" "$payload" > "$temp_payload"
509
+
510
+ local response=$(curl -s -X POST "${api_url}/v1/music/generate" \
511
+ -H "Content-Type: application/json; charset=utf-8" \
512
+ --data-binary "@${temp_payload}")
513
+
514
+ rm -f "$temp_payload"
515
+
516
+ local job_id=$(json_get "$response" "job_id")
517
+
518
+ [ -z "$job_id" ] && { echo -e "${RED}Error: Failed to create job${NC}"; echo "$response"; exit 1; }
519
+
520
+ if [ "$no_wait" = true ]; then
521
+ echo "Job ID: $job_id"
522
+ echo "Use '$0 status $job_id' to check progress and download"
523
+ else
524
+ wait_for_job "$api_url" "$job_id"
525
+ fi
526
+ }
527
+
528
+ # Random command
529
+ cmd_random() {
530
+ check_deps
531
+ ensure_config
532
+
533
+ local thinking="" no_thinking=false no_wait=false
534
+
535
+ while [[ $# -gt 0 ]]; do
536
+ case $1 in
537
+ --thinking|-t) thinking="true"; shift ;;
538
+ --no-thinking) no_thinking=true; shift ;;
539
+ --no-wait) no_wait=true; shift ;;
540
+ *) shift ;;
541
+ esac
542
+ done
543
+
544
+ local api_url=$(ensure_connection)
545
+
546
+ local def_thinking=$(get_config "generation.thinking")
547
+ [ -z "$thinking" ] && thinking="${def_thinking:-true}"
548
+ [ "$no_thinking" = true ] && thinking="false"
549
+
550
+ echo "Generating random music..."
551
+ echo " Thinking: $thinking"
552
+ echo " Output: $OUTPUT_DIR"
553
+ echo ""
554
+
555
+ local temp_payload=$(mktemp)
556
+ printf "{\"thinking\": %s}" "$thinking" > "$temp_payload"
557
+
558
+ local response=$(curl -s -X POST "${api_url}/v1/music/random" \
559
+ -H "Content-Type: application/json; charset=utf-8" \
560
+ --data-binary "@${temp_payload}")
561
+
562
+ rm -f "$temp_payload"
563
+
564
+ local job_id=$(json_get "$response" "job_id")
565
+
566
+ [ -z "$job_id" ] && { echo -e "${RED}Error: Failed to create job${NC}"; echo "$response"; exit 1; }
567
+
568
+ if [ "$no_wait" = true ]; then
569
+ echo "Job ID: $job_id"
570
+ echo "Use '$0 status $job_id' to check progress and download"
571
+ else
572
+ wait_for_job "$api_url" "$job_id"
573
+ fi
574
+ }
575
+
576
+ # Help
577
+ show_help() {
578
+ echo "ACE-Step Music Generation CLI"
579
+ echo ""
580
+ echo "Requirements: curl"
581
+ echo ""
582
+ echo "Usage: $0 <command> [options]"
583
+ echo ""
584
+ echo "Commands:"
585
+ echo " generate Generate music from text"
586
+ echo " random Generate random music"
587
+ echo " status Check job status and download results"
588
+ echo " models List available models"
589
+ echo " health Check API health"
590
+ echo " config Manage configuration"
591
+ echo ""
592
+ echo "Output:"
593
+ echo " Results saved to: $OUTPUT_DIR/<job_id>.json"
594
+ echo " Audio files: $OUTPUT_DIR/<job_id>_1.mp3, ..."
595
+ echo ""
596
+ echo "Generate Options:"
597
+ echo " -c, --caption Music style/genre description (caption mode)"
598
+ echo " -d, --description Simple description, LM auto-generates caption/lyrics"
599
+ echo " -l, --lyrics Lyrics text"
600
+ echo " -t, --thinking Enable thinking mode (default: true)"
601
+ echo " --no-thinking Disable thinking mode"
602
+ echo " --no-format Disable format enhancement"
603
+ echo ""
604
+ echo "Examples:"
605
+ echo " $0 generate \"Pop music with guitar\" # Caption mode"
606
+ echo " $0 generate -d \"A February love song\" # Simple mode (LM generates)"
607
+ echo " $0 generate -c \"Jazz\" -l \"[Verse] Hello\" # With lyrics"
608
+ echo " $0 random"
609
+ echo " $0 status <job_id>"
610
+ echo " $0 config --set generation.thinking false"
611
+ }
612
+
613
+ # Main
614
+ case "$1" in
615
+ generate) shift; cmd_generate "$@" ;;
616
+ random) shift; cmd_random "$@" ;;
617
+ status) shift; cmd_status "$@" ;;
618
+ models) cmd_models ;;
619
+ health) cmd_health ;;
620
+ config) shift; cmd_config "$@" ;;
621
+ help|--help|-h) show_help ;;
622
+ *) show_help; exit 1 ;;
623
+ esac
skills/acemusic/scripts/config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "api_url": "http://127.0.0.1:8001",
3
+ "generation": {
4
+ "thinking": true,
5
+ "use_format": true,
6
+ "use_cot_caption": true,
7
+ "use_cot_language": true,
8
+ "audio_format": "mp3",
9
+ "vocal_language": "en"
10
+ }
11
+ }