Разбор звука: эффекты через CBL как есть, музыка на AY из нот PC-спикера
Замеры по ассетам: 31 эффект уже 8-битным PCM на 11 000 Гц (у CBL есть 10 937,5 — расхождение 0,6 %, формат сэмпла совпадает байт в байт, то есть конверсии нет вовсе), 103 941 Б = 6,3 EMM-страницы. Вся музыка есть нотами PC-спикера — 7 КБ на 57 звуков, и нота там задана в ГЕРЦАХ напрямую (проверено по speaker_callback), а не делителем PIT, как кажется по числам. MIDI разбирать не нужно. Отдельный таймер не нужен: секвенсор музыки двигает CBL-callback раз в 11,7 мс, а короче 12 мс во всей музыке 2 ноты из 1469.
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
# Звук в порте PoP — разбор и план
|
||||
|
||||
Дата: 2026-08-20. Статус: **разбор, кода нет.**
|
||||
|
||||
Задача пользователя: добавить звук. Приоритет — эффекты; музыку, если
|
||||
найдётся способ. Ниже — что реально лежит в ассетах, что умеет железо, и
|
||||
почему получившийся план вышел проще, чем ожидалось.
|
||||
|
||||
## 1. Главный вывод
|
||||
|
||||
**Ни MIDI разбирать, ни ноты сочинять не придётся, и ресэмплировать тоже.**
|
||||
|
||||
- Эффекты уже лежат **8-битным беззнаковым PCM на 11 000 Гц**, а у CBL есть
|
||||
режим **10 937,5 Гц** — расхождение 0,6 %, на слух неразличимо. Формат
|
||||
сэмпла совпадает с нашим CBL байт в байт (`cbl.h`: 8 бит, беззнаковый,
|
||||
центр 0x80). То есть данные играются **как есть**, без конверсии.
|
||||
- Музыка есть в виде **списков нот PC-спикера — 7 КБ на всю игру**, а нота
|
||||
там задана прямо в ГЕРЦАХ. Пересчёт в делитель AY — одно деление.
|
||||
- AY и COVOX на Sp2000 сведены в **один ЦАП TDA1543** (док Ивана Мака,
|
||||
§5), значит музыка на AY и эффекты через CBL звучат ОДНОВРЕМЕННО, и
|
||||
смешивать их программно не надо.
|
||||
|
||||
## 2. Что лежит в ассетах (замерено, а не по памяти)
|
||||
|
||||
Звук в PoP адресуется как ресурс `10000 + N`, N = 0..56 — 57 звуков
|
||||
(`load_sound`, `seg009.c:2289`). Наборов три, и они ПАРАЛЛЕЛЬНЫЕ: один и
|
||||
тот же звук есть в нескольких видах.
|
||||
|
||||
| набор | что это | объём | покрытие |
|
||||
|---|---|---:|---|
|
||||
| `DIGISND1..3.DAT` | оцифровка, 8 бит PCM | 103 941 Б | **31 звук** (эффекты) |
|
||||
| `MIDISND1..2.DAT` | MIDI-музыка | 27 776 Б | музыка |
|
||||
| `IBM_SND1..2` (распакованы) | ноты PC-спикера | **7 КБ** | **все 57** |
|
||||
|
||||
### 2.1 Оцифровка (эффекты)
|
||||
|
||||
Разбор контейнера: индекс по 8 байт на запись (id, offset, size), **первый
|
||||
байт ресурса — контрольная сумма**, тело за ней (спецификация
|
||||
`POP-DAT-FormatSpecifications`, §3.1.2 — на этом я сначала споткнулся и
|
||||
читал мусор). Тело — `digi_type`: `word rate, word count, word unk,
|
||||
byte size`, дальше сэмплы.
|
||||
|
||||
- 31 звук, все 8-битные;
|
||||
- частоты: **28 звуков на 11 000 Гц**, по одному на 8 200 и 14 000;
|
||||
- 103 701 сэмпл = **9,4 секунды**, **103 941 Б ≈ 6,3 EMM-страницы**.
|
||||
|
||||
### 2.2 Ноты PC-спикера (и эффекты, и музыка)
|
||||
|
||||
Формат: `byte type(=0), word tempo`, дальше тройки `word frequency,
|
||||
byte length`; `frequency <= 1` — пауза, `0x12` — конец. Ключевое, что
|
||||
пришлось смотреть в `play_speaker_sound`/`speaker_callback` (`seg009.c`):
|
||||
|
||||
- **`frequency` — это ГЕРЦЫ напрямую** (`generate_square_wave(stream,
|
||||
(float)note->frequency, ...)`), а не делитель PIT, как кажется по
|
||||
маленьким числам;
|
||||
- длительность ноты = `length / tempo` СЕКУНД.
|
||||
|
||||
Замеры по всем 57 звукам: **2212 нот**, частоты 16..65507 Гц,
|
||||
длительности 1,74..2571 мс.
|
||||
|
||||
Из них 23 звука — те, у которых оцифровки НЕТ, то есть вся музыка:
|
||||
заставки, гимны уровней, смерть, победа, титры (id 24..43, 50..56).
|
||||
**1469 нот**, и вот их длительности:
|
||||
|
||||
| длительность ноты | нот | доля |
|
||||
|---|---:|---:|
|
||||
| < 3 мс | 0 | 0 % |
|
||||
| 3..12 мс | 2 | 0,1 % |
|
||||
| 12..25 мс | 140 | 9,5 % |
|
||||
| > 25 мс | 1327 | 90,3 % |
|
||||
|
||||
Это число решает вопрос про таймер — см. §4.
|
||||
|
||||
## 3. Что умеет железо (док Ивана Мака §5 + MAME)
|
||||
|
||||
- **AY-3-8910/8912** в ПЛМ, «программируется по стандартным описаниям» —
|
||||
то есть ZX-порты; в MAME он заведён как `AY8910(config, "ay8912",
|
||||
X_SP/24)`, то есть **тактовая 1,75 МГц**. Период канала = 109375 /
|
||||
частота(Гц), 12 бит (макс 4095) → снизу берутся частоты от ~27 Гц.
|
||||
Точные Z80-адреса портов идут через таблицу DCP, а не напрямую —
|
||||
**проверить артефактом до кодинга** (ожидаем ZX-стандарт 0xFFFD/0xBFFD).
|
||||
- **CBL** — COVOX с буфером 256 Б, две половины по 128; бит 7 порта 0xFE
|
||||
показывает играющую половину, порт управления 0x4E. Прерывание —
|
||||
когда половина сменилась.
|
||||
- **Бипер** (бит 5 порта 0xFE) — туда же в ЦАП. Нам не нужен.
|
||||
- Всё это **сведено в один ЦАП**, поэтому AY и CBL звучат вместе.
|
||||
|
||||
У нас уже есть готовая обвязка CBL (`libc/include/cbl.h`): callback
|
||||
`fill(n)`, выдача блока через `cbl_push_otir()` (порт 0x4F) или через
|
||||
акселератор, коды частот, счётчик недоливов. Своего кольца библиотека не
|
||||
держит — данные пропихиваются прямо из наших EMM-страниц.
|
||||
|
||||
## 4. Предлагаемая архитектура
|
||||
|
||||
```
|
||||
эффекты (31 шт, 8 бит 11 кГц) музыка (23 шт, ноты)
|
||||
│ │
|
||||
EMM-страницы (6,3) 7 КБ нот в банке
|
||||
│ │
|
||||
cbl_push_otir из fill() запись 3 регистров AY
|
||||
│ │
|
||||
CBL (10,9 кГц) ──────┐ ┌────────── AY (1,75 МГц)
|
||||
▼ ▼
|
||||
TDA1543 (аппаратное смешивание)
|
||||
```
|
||||
|
||||
**Один источник прерываний — CBL.** Его callback приходит каждые 128
|
||||
сэмплов = **11,7 мс** при 10,9 кГц, и он же двигает секвенсор музыки.
|
||||
Отдельный таймер (CTC) НЕ нужен: по таблице из §2.2 короче 12 мс всего
|
||||
2 ноты из 1469 — они растянутся на один тик, чего не слышно.
|
||||
|
||||
Почему это важно: `irq_ctc_install` сейчас требует кода в W2 (tiny/big),
|
||||
а roomtest — huge, и попадёт ли туда CTC-трамплин, зависит от раскладки.
|
||||
Обойтись без него — значит не открывать этот фронт вовсе.
|
||||
|
||||
**Пейсинг кадра при этом не страдает.** Наш темп считается ПО ЛУЧУ
|
||||
(`pop_pace.h`), а не по кадровым прерываниям, поэтому то, что CBL-ветка
|
||||
трамплина делает приватный RETI и съедает кадровые прерывания, нам
|
||||
безразлично. Если бы пейсинг остался на прерываниях — звук бы его сломал.
|
||||
|
||||
## 5. Объём работ
|
||||
|
||||
| фаза | что | оценка |
|
||||
|---|---|---|
|
||||
| **З1** | распаковщик `pop_pack_sound.py`: DAT → `.snd`-страницы EMM (эффекты) + `.not` (ноты музыки) | формат уже разобран |
|
||||
| **З2** | `pop_sfx.c`: `cbl_open` + `fill()`, таблица «звук → страница/смещение/длина», `pop_sfx_play(id)` | ядро |
|
||||
| **З3** | развесить вызовы: 66 мест `play_sound` в оригинале; у нас часть уже помечена TODO (`pop_ctrl.c:408`, `guards.c:992`, `pop_map.c:3035/3107`, …). **Плюс бесплатный кусок**: опкод `SEQ_SOUND` в seqtbl уже разбирается нашим `play_seq` (`pop_kid.c:326`) — шаги, приземления и прочее поедут сами | механическая |
|
||||
| **З4** | `pop_music.c`: секвенсор нот на AY, тик из CBL-callback | небольшая |
|
||||
| **З5** | приоритеты и вытеснение: у оригинала `play_sound` глушит предыдущий (`stop_sounds`), музыка и эффект — разные каналы | правила из seg009 |
|
||||
|
||||
## 6. Что проверить артефактом ДО кодинга
|
||||
|
||||
1. **Порты AY на Sprinter** — записать в 0xFFFD/0xBFFD и убедиться, что
|
||||
MAME отдаёт звук (порты идут через DCP-таблицу, «стандартные ZX» —
|
||||
это ожидание, а не факт).
|
||||
2. **CBL в режиме huge.** Шапка `cbl.h` говорит «код/данные в W2
|
||||
(tiny/big)», но это скорее всего устаревшая оговорка: IM2-трамплин
|
||||
давно переделан на all-modes, и наш кадровый путь в huge работает.
|
||||
Проверить `cbl_open` из roomtest.
|
||||
3. **Совместное владение портом 0xFE.** Бит 5 (луч) у нас держится через
|
||||
`_cbl_port_ref` «немым» кодом частоты; когда откроется НАСТОЯЩИЙ CBL,
|
||||
владение переходит к нему. Убедиться, что пейсинг переживает
|
||||
`cbl_open`/`cbl_close`.
|
||||
4. **Цена fill() в кадре.** 128 байт через `cbl_push_otir` раз в 11,7 мс
|
||||
— замерить тем же способом, что и остальное (брейкпоинт + totalcycles).
|
||||
|
||||
## 7. Про MIDI — почему не он
|
||||
|
||||
Музыка в MIDISND — настоящие MThd/MTrk чанки, 27 КБ. Чтобы играть их на
|
||||
AY, нужен разбор MIDI, раскладка каналов на три голоса и таблица
|
||||
инструментов — это отдельный проект, и звучать он будет НЕ так, как
|
||||
оригинал на PC. А набор PC-спикера — это ровно то, что слышал игрок на
|
||||
IBM PC 1989 года: один квадратный голос. Он у нас есть целиком, весит
|
||||
7 КБ и ложится на AY напрямую.
|
||||
|
||||
Если позже захочется богаче — материал уже будет разобран, и можно
|
||||
разложить те же мелодии на три канала AY (бас/мелодия/арпеджио), не трогая
|
||||
ни данные, ни секвенсор.
|
||||
@@ -233,6 +233,7 @@ fore-слоя, и клип (`clip_char`), и брызги; полоса HP и л
|
||||
| ✔ | [L1-SPEED](#l1-speed) | **закрыта 2026-08-19** режимами скорости (NORMAL = 4/5 растров, как оригинал) | — |
|
||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||
| — | [TUNE-2](#tune-2) | параметры СТРАЖЕЙ (6 таблиц по 12 градаций мастерства + HP) → cfg-файл | подбор сложности боя без пересборки |
|
||||
| — | [SND](#snd) | звук: эффекты через CBL, музыка на AY | разбор готов — `../docs/sound_plan.md` |
|
||||
| — | [MEM](#mem-next) | следующий шаг разгрузки W1/W2 | берётся по факту нехватки места |
|
||||
|
||||
Сделанное — в [`TASKS_CLOSED.md`](TASKS_CLOSED.md): L4-MIRROR, L3-CHOMP,
|
||||
@@ -1212,6 +1213,24 @@ N навык M» — отдельная надобность, если пона
|
||||
|
||||
**Оговорка по памяти** — та же, что в TUNE-1: парсер холодный, в банк.
|
||||
|
||||
### <a id="snd"></a>SND. Звук — эффекты через CBL, музыка на AY
|
||||
|
||||
Полный разбор с замерами — [`../docs/sound_plan.md`](../docs/sound_plan.md).
|
||||
Коротко, почему это оказалось дешевле, чем выглядело:
|
||||
|
||||
- эффекты уже лежат **8-битным PCM на 11 000 Гц**, а у CBL есть режим
|
||||
10 937,5 Гц (расхождение 0,6 %) и ровно тот же формат сэмпла —
|
||||
**играются как есть, без конверсии**; 31 звук, 103 941 Б ≈ 6,3
|
||||
EMM-страницы;
|
||||
- музыка есть **нотами PC-спикера, 7 КБ на всю игру**, причём нота задана
|
||||
прямо в ГЕРЦАХ — пересчёт в период AY это одно деление; MIDI разбирать
|
||||
не нужно;
|
||||
- AY и COVOX сведены в один ЦАП, поэтому играют одновременно;
|
||||
- отдельный таймер не нужен: секвенсор музыки двигает CBL-callback
|
||||
(11,7 мс), а короче 12 мс во всей музыке всего 2 ноты из 1469.
|
||||
|
||||
Фазы З1..З5 и список «проверить артефактом до кодинга» — в доке.
|
||||
|
||||
### <a id="mem-next"></a>MEM. Следующий шаг разгрузки W1/W2
|
||||
|
||||
`pop_ctrl.c` уехал в банк 5 ([MEM-BANK5](TASKS_CLOSED.md#mem-bank5), куча
|
||||
|
||||
Reference in New Issue
Block a user