Files
Sprinter-SDCC/applications/PoP/docs/sound_plan.md
T
snark13 9f9a8f26ae Звук: разобраны все наборы MS-DOS версии, включая mt32snd
Эффекты все 31 есть в WAV (digisnd, 8 бит PCM) — просьба «не спикер, а
wav» для них уже выполнена исходным планом.  mt32snd оказался НЕ музыкой,
а теми же эффектами в MIDI для Roland MT-32.

У музыки WAV нет ни в одном наборе: только ноты спикера (7 КБ) или MIDI
(27 КБ).  Посчитал третий путь — рендер MIDI в WAV на хосте: игровые
треки 74,7 с = 803 КБ = 50 EMM-страниц (влезает), заставки 248 с = 167
страниц (только стрим с диска).

Только спикером во всей игре остаётся один звук — blink (4 ноты).
2026-08-20 11:57:19 +03:00

15 KiB
Raw Blame History

Звук в порте PoP — разбор и план

Дата: 2026-08-20. Статус: разбор, кода нет.

Задача пользователя: добавить звук. Приоритет — эффекты; музыку, если найдётся способ. Эффекты — обязательно WAV, а не PC-спикер (уточнение 2026-08-20; см. §1а — оказалось, что они и так все в WAV). Ниже — что реально лежит в ассетах, что умеет железо, и почему получившийся план вышел проще, чем ожидалось.

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 звучат ОДНОВРЕМЕННО, и смешивать их программно не надо.

1а. Уточнение после разбора ВСЕХ наборов MS-DOS версии (2026-08-20)

Пользователь попросил, чтобы эффекты были не PC-спикером, а WAV, и заодно посмотреть mt32snd[1-2].dat. Разобрал все восемь .dat из MSDOS/. Ответ короткий: эффекты И ТАК все до одного есть в WAV, а вот у музыки WAV нет ни в одном наборе.

набор формат какие звуки сколько
digisnd1..3 WAV, 8 бит PCM эффекты 0..23, 44..49, 51 31
mt32snd1..2 MIDI для Roland MT-32 ТЕ ЖЕ эффекты 0..23, 44..51 31
midisnd1..2 MIDI (AdLib/GM) музыка 24..43, 50, 52..56 22
ibm_snd1..2 ноты PC-спикера всё подряд, 0..56 57

Здесь пряталась ловушка: mt32snd по имени похож на «музыку получше», а на деле это набор ЭФФЕКТОВ для владельцев MT-32 — те же id, что у digisnd. Музыки в нём нет вовсе.

Частоты WAV: 28 звуков на 11 000 Гц, по одному на 8 200, 14 000 и 2 750. Итого 112 922 сэмпла = 11,4 с, ~110 КБ ≈ 6,7 EMM-страниц.

Только PC-спикером, без альтернатив, остаются четыре id: 31, 34, 42 (пустые) и 38 blink — четыре ноты. То есть на весь звук игры спикер нужен ровно для одного писка.

Музыка: WAV нет, есть три пути

путь данные что получится цена
A. Ноты PC-спикера на AY 7 КБ один квадратный голос — ровно то, что слышали на IBM PC 1989 секвенсор на полсотни строк
B. MIDI -> AY, три голоса 27 КБ исходника богаче: бас + мелодия + арпеджио разбор MIDI + раскладка по каналам
C. MIDI -> WAV на хосте, стрим через CBL см. ниже настоящее звучание, любое нужен синтезатор на хосте + место

Про объём для пути C (замерено по длительностям треков):

группа треков длительность WAV 11 кГц
звучат ПО ХОДУ игры (гимн уровня, смерть, зелья, перо, победа) 12 74,7 с 803 КБ = 50 EMM-страниц
заставки и титры 10 248,1 с 2 665 КБ = 167 страниц

Игровая половина в EMM влезает (при ~215 свободных страницах), а заставочная — нет, её пришлось бы стримить с диска. Но заставки идут тогда, когда игра ничего не рисует, так что стрим там как раз уместен.

Предложение: начинать с A (7 КБ, работает сразу, ноль рисков), а C держать как отдельную фазу — она ортогональна: проигрыватель WAV для музыки это тот же cbl_push, что и для эффектов, только длиннее буфер. B имеет смысл только если C окажется неподъёмным по месту.

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 (бас/мелодия/арпеджио), не трогая ни данные, ни секвенсор.