Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/
133 changes: 131 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,137 @@ Aces is a file format specifically designed for vocal synthesis that is simple,
- Introduce piece parameters that allow for the use of curves to represent pitch and energy across the entire piece


## Quick Start

Four steps to your first line of singing.

### 1. Get your credentials

Once your service is activated, your contact provides two values: `cooperator` (your caller name)
and `ace_token` (your request token). They are left blank in the sample below — fill them in and
it runs.

### 2. Pick a singer

Singer ids are listed in [Singer Information](/api/docs/singer_info.md). The sample uses `82`.

### 3. Prepare an aces file

The quickest option is `examples/xiaoxingxing_syllable.aces` — Chinese lyrics written as pinyin
syllables, so you do not have to look up phonemes. If you have a MIDI file, see
"Generating aces from MIDI" below.

### 4. Submit and download the audio

```python
import json, requests

URL = "https://api-lora-us.svsbusiness.com/engine/api/engine/2b_compose" # use the address assigned to you
COOPERATOR = "" # fill in after activation
ACE_TOKEN = "" # fill in after activation

resp = requests.post(
URL,
files=[("file", open("examples/xiaoxingxing_syllable.aces", "rb"))],
data={"cooperator": COOPERATOR, "ace_token": ACE_TOKEN, "speaker_id": "82"},
timeout=300,
)
body = resp.json()
assert body["code"] == 200, body["error"]

for piece in body["data"]:
audio = requests.get(piece["audio"], timeout=120).content
name = "piece_{}.{}".format(piece["sequence_index"], piece["output_format_suffix"])
open(name, "wb").write(audio)
print(name, "starts at", piece["pst"], "seconds")
```

`pst` is the absolute time, in seconds on your original project timeline, of sample 0 of that
audio — place the audio at that time and it lines up with your backing track. The full interface
reference is in [api_doc](/api/docs/api_doc_en.md).

## File Format Specification
For specifics regarding the file format please refer to [File Description](docs/aces_file_en.md). Examples illustrating various use-cases can be found in the `./examples/` directory.
For specifics regarding the file format please refer to [File Description](docs/aces_file_en.md). Every example under `./examples/` can be submitted to the synthesis API as-is:

| File | What it demonstrates |
|-------------------------------|--------------------------------------------------------------------------|
| `xiaoxingxing_syllable.aces` | The simplest form: Chinese syllable (pinyin) input, no phonemes needed |
| `xiaoxingxing.aces` | Chinese phoneme input, with multi-segment `pitch.user` editing the melody |
| `Iwannafly.aces` | English phoneme input, including `br` (breath) and `slur` notes |
| `はるをあい cl するひと_2b.aces` | Japanese phoneme input |
| `vibrato_example.aces` | Controlled vibrato via the `user` + `delta` layers of `pitch` |
| `param_example.aces` | `energy` / `air` / `falsetto` / `tension` parameter curves |


## Lyrics to phonemes

Chinese needs no extra tooling — put the pinyin (or a single Chinese character) in the note's
`syllable` field and the service converts it. **English, Japanese and Spanish must supply
`phone` themselves**, which is what `api/demo/lyrics2phone.py` is for:

```bash
cd api/demo
pip install -r requirements.txt # English needs cmudict; Japanese and Spanish have no deps
python lyrics2phone.py en "twinkle twinkle little star"
python lyrics2phone.py jp "さくら さくら"
python lyrics2phone.py spa "camino de la luz"
```

The output is one phone list per note, one syllable per note, ready to drop into `notes`:

```
[["t","w","ih","ng"], ["k","ah","l"], ["l","ih"], ["t","ah","l"], ["s","t","aa","r"]]
```

Add `--aces out.aces` to emit a submittable ACES skeleton (with evenly spaced placeholder
timings and pitches — adjust them to your score).

How each language works, and where it stops:

| Language | Approach | Main limitation |
|----|----|----|
| English `en` | CMUdict lookup → ARPAbet → engine phones (1:1), syllabified by maximal onset | Words not in the dictionary (new coinages, proper nouns, numbers) raise an error rather than being guessed; supply them via `extra_dict` |
| Japanese `jp` | Kana → romaji syllable → the engine's own dictionary (kana table embedded, no dependency) | Kana only — annotate kanji first; the particles `は`/`へ` are read literally |
| Spanish `spa` | Orthographic rules → phones, with diphthongs decided by stress and vowel strength | Seseo is fixed (`z`/`ce`/`ci` all read as `s`); peninsular distinción and Rioplatense readings are not supported |

> **Unknown words raise an error; nothing is guessed from spelling.** A wrong guess is hard for
> you to track down, while an error points straight at the word. Supply your own with
> `extra_dict={"word": ["phone", ...]}`.
>
> The phoneme inventory comes from the engine, and every phone the tool emits has been checked
> against it. The full list of known trade-offs is in the script's `CAVEATS` section.

## Generating aces from MIDI

`api/demo/midi2aces.py` converts a MIDI file with lyrics into aces and synthesizes it directly.
The repository ships `红昭愿.mid` as an example:

```bash
cd api/demo
pip install -r requirements.txt
python midi2aces.py # put your credentials in acel_svs_example.py
```

What the MIDI must provide:

- Track 0 must contain a `set_tempo` event; the script uses it to convert ticks to absolute seconds
- Lyrics go in `lyrics` meta events and **must be pinyin** (the script submits them as `syllable`,
so Chinese only for now)
- For a syllable held across several notes, write the lyric as `-` and the script emits a `slur` note
- The number of lyric events should match the number of notes; notes without a lyric fall back to `la`
- With multiple tracks the first one containing notes is used; pass
`midi2json(path, json_track_id=N)` to choose another

The script also does three things for you:

1. Drops notes shorter than 0.02s (too short to hold their phonemes)
2. Splits at gaps longer than 1.2s, then halves any piece still longer than 90s
3. Submits each piece and stitches the results into one wav using each piece's `pst`

With the bundled `红昭愿.mid`: 232 notes spanning 139.3 seconds, split into 4 pieces.
Note the script sends **one request per piece**, each consuming one credit; to spend fewer
credits, submit several pieces in a single request (see section 4 of
[api_doc](/api/docs/api_doc_en.md)).

## Contributing

Expand All @@ -29,4 +158,4 @@ We offer an API service for singing voice synthesis, please refer to the [api_do

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
This project is licensed under the MIT License - see the [LICENCE](LICENCE) file for details.
124 changes: 122 additions & 2 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,128 @@ Aces 是一个专为歌声合成设计的文件格式,它简单、开放、用
- 引入片段参数,允许使用曲线来表示整个片段的音高和能量


## 快速开始

四步听到第一句歌声。

### 1. 取得凭据

开通服务后,对接人员会提供两个值:`cooperator`(请求方名称)与 `ace_token`(请求令牌)。
下面示例中留空,填入即可运行。

### 2. 选一个歌手

歌手 id 见 [歌手信息列表](/api/docs/singer_info.md)。示例用 `82`。

### 3. 准备一个 aces 文件

最省事的办法是直接用 `examples/xiaoxingxing_syllable.aces` —— 中文拼音歌词,不必查音素。
手里是 MIDI 的话见下面「从 MIDI 生成 aces」。

### 4. 提交并下载音频

```python
import json, requests

URL = "https://api-lora-us.svsbusiness.com/engine/api/engine/2b_compose" # 请用对接时分配给您的地址
COOPERATOR = "" # 开通后填入
ACE_TOKEN = "" # 开通后填入

resp = requests.post(
URL,
files=[("file", open("examples/xiaoxingxing_syllable.aces", "rb"))],
data={"cooperator": COOPERATOR, "ace_token": ACE_TOKEN, "speaker_id": "82"},
timeout=300,
)
body = resp.json()
assert body["code"] == 200, body["error"]

for piece in body["data"]:
audio = requests.get(piece["audio"], timeout=120).content
name = "piece_{}.{}".format(piece["sequence_index"], piece["output_format_suffix"])
open(name, "wb").write(audio)
print(name, "起始时间", piece["pst"], "秒")
```

`pst` 是这段音频第 0 个采样点在原工程时间轴上的绝对秒数——按它把音频摆到时间轴上,
就能与伴奏对齐。接口的完整说明见 [api_doc](/api/docs/api_doc.md)。

## 文件格式规范
有关文件格式的具体信息,请参考 [文件描述](docs/aces_file.md)。在 `./examples/` 目录中可以找到各种用例的示例。
有关文件格式的具体信息,请参考 [文件描述](docs/aces_file.md)。`./examples/` 目录下的示例都可以直接提交给合成接口:

| 文件 | 演示内容 |
|-------------------------------|-------------------------------------------------|
| `xiaoxingxing_syllable.aces` | 最简形态:中文音节(拼音)输入,不必自己查音素 |
| `xiaoxingxing.aces` | 中文音素输入,并用多段 `pitch.user` 编辑整句音高 |
| `Iwannafly.aces` | 英文音素输入,含 `br`(换气)与 `slur`(延音)音符 |
| `はるをあい cl するひと_2b.aces` | 日文音素输入 |
| `vibrato_example.aces` | 用 `pitch` 的 `user` + `delta` 做可控颤音 |
| `param_example.aces` | `energy` / `air` / `falsetto` / `tension` 参数曲线 |


## 歌词转音素

中文不需要额外工具——直接在音符上写 `syllable`(拼音或单个汉字),服务端会转换。
**英语 / 日语 / 西班牙语必须自己给 `phone`**,`api/demo/lyrics2phone.py` 就是干这个的:

```bash
cd api/demo
pip install -r requirements.txt # 英语需要 cmudict;日语与西语无依赖
python lyrics2phone.py en "twinkle twinkle little star"
python lyrics2phone.py jp "さくら さくら"
python lyrics2phone.py spa "camino de la luz"
```

输出是逐音符的音素列表,一个音节一个音符,直接填进 `notes` 即可:

```
[["t","w","ih","ng"], ["k","ah","l"], ["l","ih"], ["t","ah","l"], ["s","t","aa","r"]]
```

加 `--aces out.aces` 可以直接生成一个可提交的 ACES 骨架(时值与音高是等分占位值,
请按您的曲谱调整)。

各语言的做法与边界:

| 语言 | 做法 | 主要限制 |
|----|----|----|
| 英语 `en` | CMUdict 查词 → ARPAbet → 引擎音素(1:1),按最大起首原则切音节 | 词典查不到的词(新词、专名、数字)会报错而不是猜,需自己用 `extra_dict` 补 |
| 日语 `jp` | 假名 → 罗马字音节 → 引擎自带词典(内嵌五十音表,无依赖) | 只认假名,汉字请先注音;助词 `は`/`へ` 按字面读 |
| 西语 `spa` | 正字法规则 → 音素,按重音与强弱元音判定二合元音 | 固定 seseo(`z`/`ce`/`ci` 一律读 `s`),不支持半岛 distinción 与阿根廷读法 |

> **查不到的词一律报错,不按拼写猜。** 猜错的发音客户很难排查,报错至少能立刻定位。
> 需要自己补词时传 `extra_dict={"word": ["音素", ...]}`。
>
> 音素表以引擎为准,工具产出的音素已逐一核对在表内。完整的已知取舍见脚本内的
> `CAVEATS` 说明。

## 从 MIDI 生成 aces

`api/demo/midi2aces.py` 能把带歌词的 MIDI 转成 aces 并直接合成。仓库自带 `红昭愿.mid`:

```bash
cd api/demo
pip install -r requirements.txt
python midi2aces.py # 凭据填在 acel_svs_example.py 里
```

对 MIDI 的要求:

- 轨道 0 必须含 `set_tempo` 事件,脚本据此把 tick 换算成绝对秒
- 歌词写在 `lyrics` 元事件里,且**必须是拼音**(脚本按 `syllable` 提交,因此目前只支持中文)
- 拖腔(一个字唱过多个音)把歌词写成 `-`,脚本会转成 `slur` 音符
- 歌词事件数量应与音符数一致;没有对应歌词的音符会退化成 `la`
- 多轨时默认取第一个含音符的轨道,需要指定用 `midi2json(path, json_track_id=N)`

脚本还会替你做三件事:

1. 丢掉短于 0.02s 的音符(太短装不下音素)
2. 在超过 1.2s 的空隙处切片,仍超过 90s 的片段再对半切
3. 逐片提交,并按各片的 `pst` 拼成一个完整 wav

以自带的 `红昭愿.mid` 为例:232 个音符、跨度 139.3 秒,切成 4 片。
注意脚本是**一片一个请求**,各扣 1 个额度;若要省额度,可改为一次请求提交多片
(上限见 [api_doc](/api/docs/api_doc.md) 第 4 节)。

## 贡献

Expand All @@ -28,4 +148,4 @@ Aces 是一个专为歌声合成设计的文件格式,它简单、开放、用

## 许可证

该项目在 MIT 许可证下授权 - 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。
该项目在 MIT 许可证下授权 - 有关详细信息,请参阅 [LICENCE](LICENCE) 文件。
8 changes: 2 additions & 6 deletions api/demo/acel_svs_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,9 @@ def download_and_open_audio(url):

def one_piece_compose(aces_json):

# 声线混合: mel 是唯一的混合维度, 权重自动归一化。
# 传 duration/pitch/air/falsetto/tension/energy 会返回 400, 详见 api_doc。
# mix_str = json.dumps({
# "duration": [[82, 0.7], [1, 0.3]],
# "pitch": [[82, 0.7], [1, 0.3]],
# "air": [[82, 0.7], [1, 0.3]],
# "falsetto": [[82, 0.7], [1, 0.3]],
# "tension": [[82, 0.7], [1, 0.3]],
# "energy": [[82, 0.7], [1, 0.3]],
# "mel": [[82, 0.7], [1, 0.3]],
# })

Expand Down
Loading