4a939e42f8
ТАЙМИНГИ. Треки заставок между уровнями длиннее самих сцен (27 — 10,7 с против 2,6 с картинки), и оригинал их не обрывает: load_intro гасит экран и возвращает управление, а звук продолжает играть поверх загрузки уровня и первых секунд игры (seg001:690). Мы глушили CBL на выходе — музыка обрывалась на полуслове. Больше не глушим. «ВРЕМЯ ВЫШЛО». Оригинал (seg001:04D3) не зовёт ни одного init_*: на экране пустая комната и ОСТАНОВИВШИЕСЯ часы (state 7, струйки песка нет). Мы показывали готовую композицию с принцессой и полными часами — противоположное по смыслу. Теперь это живая сцена без персонажей, 102 кадра, и трек 36 дослушивается под чёрным экраном, как в оригинале. ПЕСОЧНЫЕ ЧАСЫ. Сцена «времени мало» (cutscene_12) шла статической композицией, куда упаковщик запёк res953 — ПОЛНЫЕ часы. Отсюда и наблюдение «чем меньше времени, тем полнее часы»: картинка была одна и та же независимо от таймера. Сделана живой (cut_scene_12_short: принцесса стоит правее, через два кадра оборачивается), часы берутся от pre_hourglass_state. Заодно: при state 7 струйка песка не рисуется. МУЗЫКА — СВОЙ ТУМБЛЕР. Settings -> MUSIC, Ctrl+M и статус в debug bar. Флаг pop_mus_want отдельный от звукового: музыка у нас поток с диска, и выключают её по другим причинам, чем эффекты; выключение обрывает текущий трек, эффекты продолжают звучать. Строка-заглушка GAMEPLAY PROFILE («VANILLA ONLY») уступила место MUSIC — на десятую строку экрана не хватает. Подписи тумблеров в debug bar сокращены до S:/M:/I:. БАГИ. После меню персонаж оставался невидимым до первого движения: снимок слота продолжал утверждать, что кадр на странице уже нарисован. Общая pop_cd_forget() теперь зовётся после меню, QuickLoad и переворота экрана. «QUICKLOAD» мигал через кадр — pop_status_show_now снимал бит видимой страницы, а её успевало переписать восстановление комнаты. Host-тесты: 15 наборов. Проверено в MAME: debug bar, выход из меню. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
98 lines
6.7 KiB
C
98 lines
6.7 KiB
C
/*
|
|
* pop_sfx.h — звуковые ЭФФЕКТЫ PoP через COVOX-Blaster.
|
|
*
|
|
* Данные — оригинальная оцифровка игры (`digisnd1..3.dat`), приведённая
|
|
* упаковщиком к 10 937,5 Гц. Конвертировать в рантайме нечего: у PoP
|
|
* сэмплы 8 бит БЕЗЗНАКОВЫЕ МОНО, и это ровно формат CBL (см. <cbl.h> и
|
|
* ../docs/sound_plan.md).
|
|
*
|
|
* ОДИН ГОЛОС. Оригинал тоже одноголосый: `play_sound` глушит
|
|
* предыдущий звук (`stop_sounds`, seg009.c), поэтому новый эффект просто
|
|
* перебивает играющий, и никакого микшера не нужно.
|
|
*
|
|
* ПОЧЕМУ ЭТО НЕ ЛОМАЕТ КАДР. Насос живёт в прерывании CBL и отдаёт 128
|
|
* байт раз в ~11,7 мс. Кадровые прерывания при этом теряются (ветка CBL
|
|
* в трамплине делает приватный RETI), но нам всё равно: темп кадра
|
|
* считается ПО ЛУЧУ (pop_pace.h), а не по прерываниям. Если бы пейсинг
|
|
* остался на них — звук сломал бы игру.
|
|
*/
|
|
#ifndef POP_SFX_H
|
|
#define POP_SFX_H
|
|
|
|
#include <stdint.h>
|
|
|
|
/* Поднять набор: занять 8 EMM-страниц и вычитать в них данные. ВЫВОД НЕ
|
|
* ВКЛЮЧАЕТ — см. pop_sfx_start. 0 — OK, -1 — не получилось (тогда всё
|
|
* дальнейшее молча ничего не делает, игра идёт без звука). */
|
|
int pop_sfx_init(void) __banked;
|
|
|
|
/* Включить вывод (открыть CBL). Звать ПОСЛЕ всей загрузки: пока идёт
|
|
* чтение файлов, ESTEX уходит в диск надолго, насос не успевает долить
|
|
* блок, и железо крутит хвост своего буфера — на слух сплошной скрежет.
|
|
* Идемпотентно. */
|
|
int pop_sfx_start(void) __banked;
|
|
|
|
/* Заглушить вывод на время загрузки (закрывает CBL). Парная к start. */
|
|
void pop_sfx_pause(void) __banked;
|
|
|
|
/* Закрыть CBL и отпустить страницы; звать на выходе. */
|
|
void pop_sfx_close(void) __banked;
|
|
|
|
/* Вкл/выкл звук по воле пользователя (Ctrl+S, порт SDLPoP seg000:657).
|
|
* Правит pop_snd_want и приводит вывод в соответствие. */
|
|
void pop_sfx_toggle(void) __banked;
|
|
|
|
/* ХОЧЕТ ЛИ ЗВУКА ПОЛЬЗОВАТЕЛЬ. Не то же самое, что «CBL открыт»: вывод
|
|
* ещё гасит служебная пауза на время загрузки уровня. Читать для
|
|
* индикации; писать — только через pop_sfx_toggle. 0 и в случае, когда
|
|
* набор вообще не поднялся (нет файлов, нет страниц) — то есть флаг
|
|
* честно means «звука не будет». */
|
|
extern uint8_t pop_snd_want;
|
|
|
|
/* ЧАСЫ ДЛЯ СЦЕН: +1 на каждый блок, отданный CBL, то есть ровно 85,4 Гц
|
|
* (10 937,5 / 128), пока звук открыт. Идут от расхода буфера железом, а
|
|
* не от нашего кода, поэтому не врут ни на долгих блитах, ни на чтении с
|
|
* диска — в отличие от счёта ожиданий vsync (тот считает НАШИ ожидания) и
|
|
* от кадровых прерываний (те теряются в di-окнах акселератора, pop_pace.h).
|
|
* Переполняется каждые 3 с — разности брать в uint8_t и накапливать.
|
|
* Когда CBL закрыт (звук выключён), НЕ ИДУТ: у пейсинга обязан быть
|
|
* запасной путь по кадрам луча. */
|
|
extern volatile uint8_t pop_snd_tick;
|
|
|
|
/* ЗАЯВКА НА МУЗЫКУ. pop_sfx_play(id) для звука без оцифровки (24..43 —
|
|
* в оригинале это Adlib-музыка) кладёт сюда номер трека, а разбирает
|
|
* заявку главный цикл: чтение с диска ни в прерывании, ни в горячем коде
|
|
* недопустимо. 0 — заявки нет. Снимать обязан тот, кто обработал. */
|
|
extern uint8_t pop_mus_req;
|
|
|
|
/* Тумблер МУЗЫКИ (Settings / Ctrl+M), независимый от тумблера эффектов:
|
|
* музыка у нас — поток с диска, и выключают её по другим причинам.
|
|
* 0 — заявки не принимаются, играющий трек глушится (pop_music_service). */
|
|
extern uint8_t pop_mus_want;
|
|
|
|
/* Заявить эффект с номером id (0..56 — нумерация оригинала, см.
|
|
* sound_plan.md). НЕ запускает его немедленно и НЕ перебивает играющий:
|
|
* это порт play_sound (seg000:12C5), который лишь НОМИНИРУЕТ кандидата на
|
|
* текущий кадр. Из нескольких заявок за кадр выживает важнейшая, а решение
|
|
* «запускать или нет» принимает pop_sfx_tick. Звуки без оцифровки (музыка)
|
|
* игнорируются.
|
|
*
|
|
* НЕ банковая и НЕ __banked: зовётся из горячих мест (play_seq в
|
|
* pop_kid.c, физика, боёвка), и трамплин на каждый шаг Кида не нужен. */
|
|
void pop_sfx_play(uint8_t id);
|
|
|
|
/* Раз в кадр: запустить номинанта, если можно (порт play_next_sound,
|
|
* seg000:1304). Оригинал зовёт его в конце отрисовки кадра — зовём там же.
|
|
* Не запустили — номинант ВЫБРАСЫВАЕТСЯ, очереди у оригинала нет. */
|
|
void pop_sfx_tick(void);
|
|
|
|
/* Перебиваемость съезжающей створки двери уровня (звук 15) — единственная
|
|
* запись таблицы, которую оригинал правит на ходу (seg007:442/464):
|
|
* 1 при закрытии двери, 0 при открытии. */
|
|
extern uint8_t pop_sfx_slide_intr;
|
|
|
|
/* Оборвать текущий эффект (порт stop_sounds). */
|
|
void pop_sfx_stop(void);
|
|
|
|
#endif
|