Files
Sprinter-SDCC/applications/PoP/roomtest/pop_music.h
T
snark13 ba3aca07bf Архивы ресурсов PBA1: звук одним файлом, музыка потоком; ALLOCS 6000
ЗАМЕР, из которого всё выросло (MAME, 2026-08-25): `open` стоит 51,4 мс,
а чтение 16 КБ из уже открытого файла — 32,6 мс.  То есть 61 % времени
загрузки уходит на ОТКРЫТИЕ, а не на данные, и чистая скорость чтения —
491 КБ/с, а не 260.  На диске игры 343 файла; одна только стартовая пачка
(kid+shadow+bg+sound+guard, ~142 файла) — это 7,3 секунды чистого open.

* toolchain/pop_pack_arc.py + pop_arc.c/h (банк 8) — формат PBA1:
  заголовок 512 Б, магия, count, записи по 4 байта (offset в СЕКТОРАХ —
  архив бывает больше 64 КБ; size в байтах — ресурс всегда <= 16 КБ,
  он обязан лезть в EMM-страницу).  Таблицу держит вызывающий на своём
  стеке: в W2 её класть нельзя, а стека в точке загрузки израсходовано
  75 байт из 1279 (замер там же).
* libc: bank_read_page — чтение из УЖЕ ОТКРЫТОГО файла в EMM-страницу.
  bank_load_file переписана через неё, поэтому дублирования работы с W3
  не осталось и резидент почти не вырос.
* Набор эффектов: 10 файлов -> SND\snd.arc, один open вместо десяти.
* Музыка: трек лежит ОДНИМ файлом и читается порциями по странице —
  нарезка на куски больше не нужна.  Этот же путь понадобится финалу
  `won` (1,2 МБ), который в память целиком не влезает.
* Менеджер звука приведён к оригиналу: играющая музыка участвует в
  таблицах приоритета наравне с эффектами (seg000:1672 + data.h:433).
  Почти вся музыка НЕперебиваема, поэтому поверх неё эффект не стартует —
  ровно как в SDLPoP; раньше эффект её перебивал.
* ALLOCS 3000 -> 6000 (решение пользователя): сборка +20 с, резидент
  -161 байт.  Куча 87 -> 248 Б.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 17:13:25 +03:00

62 lines
4.1 KiB
C

/*
* pop_music.h — музыкальный трек через тот же CBL, что и эффекты.
*
* ПОЧЕМУ PCM, А НЕ AY. Оцифровки музыки в игре нет вовсе: digisnd*.dat
* содержит только эффекты, музыка лежит MIDI под Adlib. Зато есть готовые
* записи DOS-версии, и путь «запись -> сырой поток CBL» не требует ни
* синтезатора на хосте, ни аранжировки под три канала AY (разбор вариантов
* — ../docs/sound_plan.md §1). Плата известна: CBL один, поэтому пока
* играет музыка, эффектов нет. Оригинал ведёт себя так же — play_sound
* там глушит предыдущий звук.
*
* ГДЕ УМЕСТНО. Заставки и титры: эффектов там не бывает, картинка
* статична, и загрузка сотни-другой килобайт с диска никому не мешает.
* Музыка ПО ХОДУ игры (джинглы смерти, зелья) — отдельный вопрос, её
* придётся либо гасить эффектами, либо уводить на AY.
*
* ФОРМАТ на диске — пачка кусков m<id>_<nn>.bin по 16 КБ (bank_load_file
* читает файл только целиком и только в одну страницу). Каталог с
* длинами генерирует toolchain/pop_pack_music.py в pop_music_tbl.h.
*/
#ifndef POP_MUSIC_H
#define POP_MUSIC_H
#include <stdint.h>
/* Сколько страниц EMM держим под трек. Двадцать = 320 КБ, это все
* заставочные треки, кроме финального «won» (115 с, 1,2 МБ): тот придётся
* либо резать, либо стримить, и это отдельная задача. */
#define POP_MUS_PAGES 20
/* Номера треков — нумерация оригинала (SDLPoP data/music/names.txt). */
#define POP_MUS_STORY_1 55 /* «In the absence of the Sultan...» */
/* Курсор проигрывания — читает НАСОС (pop_sfx.c, резидент), пишет
* загрузчик. Отдельный от эффектов: они звучат поверх музыки, а не
* вместо неё, и у каждого свой источник. */
extern uint8_t pop_mus_page[POP_MUS_PAGES]; /* физические страницы EMM */
extern volatile uint8_t pop_mus_pg; /* индекс текущей страницы */
extern volatile uint16_t pop_mus_ptr; /* адрес в окне W0 */
extern volatile uint16_t pop_mus_left; /* порций до конца, 0 — молчим */
/* Номер играющего трека (нумерация оригинала). Нужен менеджеру звука:
* музыка участвует в таблицах приоритета наравне с эффектами. */
extern uint8_t pop_mus_id;
/* Загрузить трек в EMM (НЕ запускает). Звать там, где допустима пауза на
* диск: CBL в это время обязан быть закрыт, иначе насос не успевает долить
* блок и железо повторяет старый — на слух скрежет (pop_sfx.h).
* 0 — готово, -1 — нет трека/памяти/файла. */
int8_t pop_music_load(uint8_t id) __banked;
/* Отпустить страницы. Останавливает проигрывание, если оно шло. */
void pop_music_free(void) __banked;
/* Пустить загруженный трек с начала / оборвать. */
void pop_music_play(void) __banked;
void pop_music_stop(void) __banked;
/* Трек ещё звучит? 0 — доиграл или не запускался. */
uint8_t pop_music_busy(void) __banked;
#endif /* POP_MUSIC_H */