Files
hamori/docs/chord_format_spec.md
T
H1K0 4fd8ece170 refactor: replace fixed STYLE_user with open-ended style tag system
- STYLE_user renamed to STYLE_H1K0 in VOCAB (author's personal tag)
- Style field now accepts any [A-Za-z][A-Za-z0-9_]* identifier in .chord files
- Unknown styles fall back to STYLE_other at tokenization time with a log warning
- Test fixtures updated to style: other; drop closed _VALID_STYLES frozenset
- Spec bumped to v2.1: documents open style field, fallback behaviour, and §5.7
  guide on registering a new style token

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 00:29:52 +03:00

27 KiB
Raw Blame History

Спецификация формата данных hamori

Версия: 2.1 Дата: 2026-05-20


1. Что описывает этот документ

Формат хранения и кодирования гармонических периодов для обучения генеративной модели и для практического использования модели как творческого помощника.

Один файл в этом формате описывает один гармонический период (4–16 тактов) — замкнутую гармоническую фразу.


2. Архитектура формата

Формат двухуровневый:

  • Исходный (.chord) — то, что пишется руками. Близок к лид-шиту, человекочитаем, легко правится в любом текстовом редакторе.
  • Токенизированный — то, что подаётся в модель. Факторизованное представление с фиксированным словарём (81 токен).

Между уровнями стоит детерминированный парсер.


3. Исходный формат .chord

3.1 Пример

# title: Sea Glass - chorus
# key: D_major
# time: 4/4
# subdivision: 4
# style: H1K0
# function: chorus

| Gmaj7 . . . | A     . . . | F#m7  . . . | Bm7  . . . |
| Gmaj7 . . . | A     . . . | D     . . . | D    . . . |

3.2 Структура файла

Один файл — один период. Расширение .chord. Кодировка UTF-8.

Состоит из двух частей:

  1. Шапка — строки, начинающиеся с #, в любом порядке.
  2. Тело — последовательность тактов, каждый обрамлён |.

Пустые строки игнорируются. Комментарии (// ...) игнорируются парсером.

3.3 Поля шапки

Поле Обязательно Допустимые значения Назначение
title да свободная строка идентификация периода
key да <note>_major или <note>_minor для нормализации в C/Am перед обучением
time да 4/4, 3/4, 6/8, 2/4, 12/8 тактовый размер
subdivision да 4 или 8 сколько позиций в одном такте
style да любой идентификатор [A-Za-z][A-Za-z0-9_]* (например: H1K0, jpop, other) стилевой тег периода; при токенизации тег должен совпасть с токеном STYLE_<tag> в словаре, иначе используется STYLE_other
function нет verse, prechorus, chorus, bridge, intro, outro, interlude, other функциональная роль периода в исходной пьесе

Допустимые ноты для key: C, C#, D, D#, E, F, F#, G, G#, A, A#, B. Бемольные написания (Db, Eb, ...) принимаются и нормализуются к диезной форме.

subdivision определяет, сколько позиций в одном такте:

  • При subdivision: 4 в 4/4 — 4 позиции на такт (одна на четверть).
  • При subdivision: 8 в 4/4 — 8 позиций на такт (одна на восьмую).
  • Для 3/4 — соответственно 3 или 6 позиций.
  • Для 6/8 — 6 позиций при subdivision: 8, рекомендуемое значение.

3.4 Тело файла

Последовательность тактов. Такты записываются на одной строке через | или каждый на своей строке — парсер обрабатывает оба варианта одинаково.

Внутри такта — ровно subdivision позиций (или соответствующее число для нестандартных размеров), разделённых одним пробелом. На каждой позиции — один токен.

3.5 Что может стоять на позиции

Запись Значение
Аккордовый символ новый аккорд начинается здесь
. удержать предыдущий аккорд
NC no chord — пауза в гармонии
? unknown — для случаев неуверенности; парсер выпустит <UNK>

Первая позиция первого такта не может быть . (нечего удерживать).

3.6 Комментарии

Строки и хвосты строк, начинающиеся с //, игнорируются:

// здесь характерный приём с проходящим басом
| F . . . | F/E . . . | Dm . . . | Dm/C . . . |

4. Аккордовая нотация

4.1 Структура аккордового символа

<root><quality?><extension?>(/<bass>)?

Примеры:

  • C → root=C, quality=maj (по умолчанию), без extension, bass=root
  • Am7 → root=A, quality=m7, без extension, bass=root
  • Fmaj9 → root=F, quality=maj7, extension=9, bass=root
  • G7/B → root=G, quality=7, без extension, bass=B
  • Dm9/F → root=D, quality=m7, extension=9, bass=F

4.2 Корневой тон

Один из 12 классов: C, C#, D, D#, E, F, F#, G, G#, A, A#, B. Бемольная запись принимается.

4.3 Базовые качества (18)

Символ Альтернативы Состав от корня
maj или пусто 1, 3, 5
m, min - 1, ♭3, 5
dim ° 1, ♭3, ♭5
aug + 1, 3, ♯5
sus2 1, 2, 5
sus4 sus 1, 4, 5
maj7 M7, Δ7, Δ 1, 3, 5, 7
m7 min7, -7 1, ♭3, 5, ♭7
7 1, 3, 5, ♭7
m7b5 ø, ø7, m7♭5 1, ♭3, ♭5, ♭7
dim7 °7 1, ♭3, ♭5, ♭♭7
mM7 m(maj7), minMaj7 1, ♭3, 5, 7
7sus4 7sus 1, 4, 5, ♭7
aug7 7#5, +7 1, 3, ♯5, ♭7
6 maj6 1, 3, 5, 6
m6 min6 1, ♭3, 5, 6
add9 2 (в некоторых поп-нотациях) 1, 3, 5, 9
m(add9) madd9, m(add2) 1, ♭3, 5, 9

Если качество не указано (например, просто C), оно автоматически = maj.

4.4 Расширения (опционально)

Один опциональный слот после качества:

Символ Значение
9 нона натуральная
b9 ♭9
#9 ♯9
11 ундецима
#11 ♯11 (лидийская)
13 терцдецима
b13 ♭13

Условные сокращения, которые парсер расшифровывает автоматически:

Запись Расшифровка
C9 quality=7, extension=9
Cmaj9 quality=maj7, extension=9
Cm9 quality=m7, extension=9
C13 quality=7, extension=13
Cmaj13 quality=maj7, extension=13
Cm11 quality=m7, extension=11
C7b9 quality=7, extension=b9
Cmaj7#11 quality=maj7, extension=#11

Ограничение: ровно один слот расширения на аккорд. Если в исходной пьесе встречается аккорд с несколькими альтерациями (например, C7♯9♭13), нужно выбрать одну наиболее характерную и записать её. Это сознательное упрощение для уменьшения комбинаторики словаря.

4.5 Инверсии и слэш-аккорды

Стандартный формат <chord>/<bass>:

F/A      ← F, бас A (первое обращение)
G/B      ← G, бас B (первое обращение)
F/G      ← F с басом G (характерный on-аккорд из J-Pop)
Em7/G    ← Em7, бас G
D/F#     ← D, бас F♯

Бас может быть любой из 12 нот. Парсер не проверяет, входит ли бас в состав аккорда — это ответственность транскрибера. Если слэш не указан, считается bass = root.

Инверсии обязательны к точной записи. Они несут значительную часть стилистической информации (особенно для J-Pop материала с характерной on-нотацией).

4.6 Полные примеры разбора

Запись root quality extension bass
C C maj none root
Am A m none root
F#m7 F# m7 none root
Cmaj9 C maj7 9 root
G7sus4 G 7sus4 none root
F/G F maj none G
Bb7b9/D A# 7 b9 D
Em7b5 E m7b5 none root
D#dim7 D# dim7 none root

5. Токенизированное представление

5.1 Принцип нормализации тональности

Перед токенизацией каждый период транспонируется в каноническую тональность:

  • мажорные периоды → C major,
  • минорные периоды → A minor.

После транспозиции аккорды записываются абсолютными именами, но фактически отражают функциональные отношения относительно тоники. Тональность исходного периода в токенах не присутствует.

На инференсе пользователь задаёт желаемую тональность, модель генерирует в C/Am, и результат транспонируется обратно постпроцессингом.

5.2 Полный словарь

Служебные (4): <BOS>, <EOS>, <PAD>, <UNK>

Метаданные периода (выпускаются один раз в начале последовательности, после <BOS>):

  • MODE_major, MODE_minor — 2 токена (наследие тональности; нужны, потому что мажор и минор остаются разделёнными)
  • TIME_4/4, TIME_3/4, TIME_6/8, TIME_2/4, TIME_12/8 — 5 токенов
  • SUB_4, SUB_8 — 2 токена
  • STYLE_H1K0, STYLE_jpop, STYLE_classical, STYLE_jazz, STYLE_other — 5 токенов стиля (текущий словарь); неизвестный тег → STYLE_other
  • FUNC_verse, FUNC_prechorus, FUNC_chorus, FUNC_bridge, FUNC_intro, FUNC_outro, FUNC_interlude, FUNC_other, FUNC_unspecified — 9 токенов

Аккордовые слоты (новый аккорд = ровно 4 токена в порядке root, quality, extension, bass):

  • ROOT_C, ROOT_C#, ..., ROOT_B — 12 токенов
  • QUAL_maj, QUAL_m, QUAL_dim, QUAL_aug, QUAL_sus2, QUAL_sus4, QUAL_maj7, QUAL_m7, QUAL_7, QUAL_m7b5, QUAL_dim7, QUAL_mM7, QUAL_7sus4, QUAL_aug7, QUAL_6, QUAL_m6, QUAL_add9, QUAL_m_add9 — 18 токенов
  • EXT_none, EXT_9, EXT_b9, EXT_#9, EXT_11, EXT_#11, EXT_13, EXT_b13 — 8 токенов
  • BASS_root, BASS_C, BASS_C#, ..., BASS_B — 13 токенов

Временные/структурные:

  • HOLD — позиция продолжает предыдущий аккорд
  • NC — пауза в гармонии
  • BAR — конец такта

Итого: 4 + 2 + 5 + 2 + 5 + 9 + 12 + 18 + 8 + 13 + 3 = 81 токен.

5.3 Структура обучающей последовательности

<BOS>
MODE_<x>  TIME_<x>  SUB_<x>  STYLE_<x>  FUNC_<x>

ROOT_<x>  QUAL_<x>  EXT_<x>  BASS_<x>    ← новый аккорд = 4 токена
HOLD                                      ← удержание = 1 токен
HOLD
HOLD
BAR

ROOT_<x>  QUAL_<x>  EXT_<x>  BASS_<x>
HOLD
ROOT_<x>  QUAL_<x>  EXT_<x>  BASS_<x>
HOLD
BAR

...

<EOS>

5.4 Алгоритм токенизации (источник → токены)

  1. Прочитать шапку.
  2. Транспонировать все аккорды: если key = X_major, транспонировать так, чтобы X стало C; если key = X_minor — так, чтобы X стало A.
  3. Выпустить <BOS>.
  4. Выпустить метатокены: MODE_<major|minor>, TIME_<x>, SUB_<x>, STYLE_<x>, FUNC_<x> (если function не задан — выпустить FUNC_unspecified; если STYLE_<style> отсутствует в словаре — выпустить STYLE_other с предупреждением в лог).
  5. Для каждого такта:
    • Для каждой позиции в такте:
      • Если новый аккорд: разобрать на (root, quality, extension, bass), выпустить 4 токена в этом порядке.
      • Если .: выпустить HOLD.
      • Если NC: выпустить NC.
      • Если ?: выпустить <UNK>.
    • После последней позиции — BAR.
  6. Выпустить <EOS>.

5.5 Алгоритм детокенизации (токены → MIDI)

  1. Считать метатокены, восстановить параметры периода.
  2. Группировать аккордовые токены по 4 (root, quality, extension, bass).
  3. Развернуть в последовательность аккордов с длительностями, считая HOLD-ы как продолжение предыдущего.
  4. Транспонировать обратно из C/Am в целевую тональность (задаваемую пользователем на инференсе).
  5. Сгенерировать MIDI через pretty_midi: для каждого аккорда выложить ноты в один трек, бас — отдельной линией в нижней октаве.

5.6 Оценка длины последовательности

Период 8 тактов в 4/4 с subdivision=4, в среднем 2 смены аккорда на такт:

  • Метатокены: 1 (BOS) + 5 = 6 токенов
  • На такт: 2 аккорда × 4 + 2 HOLD-а + 1 BAR = 11 токенов
  • 8 тактов: ~88 токенов
  • EOS: 1
  • Итого: ~95 токенов на типичный период.

Длинный период (16 тактов с частой сменой): редко превышает 250 токенов. Контекстное окно 512 токенов более чем достаточно.

5.7 Добавление нового стилевого тега

Поле style в .chord файле принимает любой идентификатор — дополнительный код менять не нужно. Однако, чтобы модель кондиционировалась на новый стиль отдельным токеном (а не сваливала его в STYLE_other), нужно зарегистрировать токен в словаре:

  1. Открыть src/tokenizer.py, найти блок # Style (5) в списке VOCAB.
  2. Добавить "STYLE_<tag>" в этот блок. Пример: "STYLE_ALICE".
  3. Обновить счётчик в §5.2 этой спецификации и в итоговой строке («N токенов»).
  4. Переобучить модель с нуля — изменение размера словаря меняет размер матрицы эмбеддингов и выходного слоя; дообучить существующий чекпоинт не получится.
  5. Обновить все .chord файлы нужного корпуса, выставив новый тег в поле style.

Если токен не зарегистрирован, парсер принимает файл без ошибок, но токенизатор выдаёт STYLE_other и пишет предупреждение в лог. Это удобно для экспериментов до переобучения.


6. Полный пример

6.1 Исходный .chord файл

# title: Example period
# key: G_major
# time: 4/4
# subdivision: 4
# style: H1K0
# function: chorus

| Gmaj7 . . . | Bm7  . . . | Em7 . . . | C . D . |
| Cmaj7 . . .   | G/B  . . .  | Am7 . D .  | G    . . . |

6.2 Токенизация

Шаг 1: транспонировать из G major в C major (вниз на 7 полутонов или вверх на 5):

| Cmaj7 . . . | Em7 . . . | Am7 . . . | F . G . |
| Fmaj7 . . . | C/E . . . | Dm7 . G .  | C . . . |

Шаг 2: выпустить токены:

<BOS>
MODE_major  TIME_4/4  SUB_4  STYLE_H1K0  FUNC_chorus

ROOT_C  QUAL_maj7  EXT_none  BASS_root
HOLD  HOLD  HOLD
BAR

ROOT_E  QUAL_m7  EXT_none  BASS_root
HOLD  HOLD  HOLD
BAR

ROOT_A  QUAL_m7  EXT_none  BASS_root
HOLD  HOLD  HOLD
BAR

ROOT_F  QUAL_maj  EXT_none  BASS_root
HOLD
ROOT_G  QUAL_maj  EXT_none  BASS_root
HOLD
BAR

ROOT_F  QUAL_maj7  EXT_none  BASS_root
HOLD  HOLD  HOLD
BAR

ROOT_C  QUAL_maj  EXT_none  BASS_E
HOLD  HOLD  HOLD
BAR

ROOT_D  QUAL_m7  EXT_none  BASS_root
HOLD
ROOT_G  QUAL_maj  EXT_none  BASS_root
HOLD
BAR

ROOT_C  QUAL_maj  EXT_none  BASS_root
HOLD  HOLD  HOLD
BAR

<EOS>

(Переносы строк здесь для читаемости; в реальности — один поток.)


7. Краевые случаи

7.1 Анакруза (затакт)

Записывается как первый такт с NC на пустых позициях:

| NC . . D | G . . . | ...

7.2 Тонально нестабильные фрагменты

Если внутри периода невозможно однозначно определить тональность (например, секвенция через несколько тоник) — записать его в наиболее подходящей тональности по контексту исходной пьесы. Внутренние отклонения и тонизации записываются обычными функциональными аккордами; это не модуляция.

Истинная модуляция (смена тональности в середине периода) в текущей версии формата не поддерживается. Если в исходной пьесе период модулирует — разрезать его на два периода: до и после модуляции.

7.3 Смена тактового размера внутри периода

Не поддерживается. Если есть — разрезать на два периода.

7.4 Полиаккорды

Не поддерживаются. Записывать как слэш-аккорд или ближайший наиболее характерный single chord.

7.5 Микротональность

Не поддерживается. Округлять до ближайшего темперированного аккорда.

7.6 Аккорды короче subdivision

Если в пьесе систематически встречаются аккорды длительностью короче выбранного subdivision (например, восьмая нота при subdivision: 4) — переключить весь период на subdivision: 8. Не округлять и не выбрасывать аккорды.


8. Соглашения по именам файлов

Рекомендованный формат:

YYYY_NNN_<short-title>_<function>.chord

Где:

  • YYYY — год создания исходной пьесы
  • NNN — порядковый номер периода в датасете (000–999)
  • <short-title> — короткое имя в kebab-case
  • <function> — функциональная роль (опционально, для удобства поиска)

Примеры:

2024_001_sea-glass_chorus.chord
2024_002_sea-glass_verse.chord
2025_037_winter-light_bridge.chord

Это упростит хронологическую сортировку датасета и поиск по функции.


9. Что НЕ кодируется (сознательные пропуски)

  • Voicing (расположение голосов внутри аккорда выше баса). Бас передаётся, остальное — на ручную работу в DAW.
  • Ритмический паттерн внутри удержания аккорда (восьмые, синкопы, паттерны типа альбертиевых басов).
  • Динамика, артикуляция, тембр, аранжировка.
  • Мелодия. Это отдельная задача за пределами текущего проекта.
  • Полная форма пьесы. Юнит — период, не пьеса целиком.
  • Метроритмическая микроразметка (свинг, рубато, ramp'ы темпа).

10. Чек-лист транскрипции

При перекладывании периода из DAW в .chord файл:

  1. Прослушать пьесу целиком, определить границы периодов. Замкнутая фраза с возвратом к тонике или ясной полукаденцией — кандидат на период.
  2. Заполнить шапку: title, key, time, subdivision, style, function.
  3. Проверить subdivision. Если в выбранном периоде аккорды стабильно меняются не чаще четверти — subdivision: 4. Если есть систематические восьмые смены — subdivision: 8.
  4. Транскрибировать гармонию по позициям, аккуратно фиксируя инверсии.
  5. Прогнать парсер, проверить отсутствие <UNK> и предупреждений.
  6. Прогнать sanity-check утилиту (.chord → MIDI), воспроизвести в DAW параллельно с оригиналом, убедиться в совпадении.
  7. Сохранить файл под именем по схеме из §8.

11. История изменений

  • v2.1 (текущая)

    • Поле style теперь принимает любой идентификатор [A-Za-z][A-Za-z0-9_]*, а не фиксированный список.
    • STYLE_user заменён на STYLE_H1K0 (авторский тег) в словаре модели.
    • Неизвестный стиль при токенизации маппируется на STYLE_other с предупреждением в лог.
  • v2.0

    • Единицей датасета стал гармонический период, не пьеса целиком.
    • Введена нормализация тональности в C major / A minor. Поле key сохраняется в шапке для парсера, но в словаре модели нет — есть только MODE_major / MODE_minor.
    • Введён тег function (необязательный) как метаданные периода.
    • Удалены секционные теги (больше не нужны при периодной разбивке).
    • Удалён тег tempo_bucket (мало пользы при текущем объёме данных).
    • Словарь сокращён с ~100 до 81 токена.
  • v1.0 — первоначальная спецификация, единицей была целая пьеса, тональность входила в словарь, использовались секционные теги внутри последовательности.