Spaces:
Running
on
A100
Running
on
A100
feat: add skills
Browse files- skills/acemusic/SKILL.md +269 -0
- skills/acemusic/scripts/acemusic.sh +623 -0
- skills/acemusic/scripts/config.json +11 -0
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 |
+
}
|