Музыка: PCM-трек через CBL, первый экран истории озвучен
Путь C из sound_plan.md вместо ранее выбранного A (ноты спикера на AY). Причина смены: появились готовые записи DOS-версии, и строка «нужен синтезатор на хосте», из-за которой C считался дорогим, отпала — осталось `ffmpeg -ac 1 -ar 10937 -f u8`. Оцифровки музыки в самой игре нет вовсе: digisnd*.dat содержит только эффекты, музыка лежит MIDI под Adlib, так что конвертировать ресурс игры всё равно было не из чего. * toolchain/pop_pack_music.py — запись flac -> сырой u8 на 10 937,5 Гц (частота CBL, чтобы её не переключать никогда), нарезка на куски MUS\m<id>_<nn>.bin по 16 КБ: bank_load_file читает файл только целиком и только в одну страницу. Каталог -> pop_music_tbl.h; длина трека хранится ПОРЦИЯМИ по 128 байт — 169 КБ в uint16 не влезает, 1350 блоков влезает (32-бит арифметики на Z80 избегаем). * pop_music.c (банк 9) — загрузка трека в EMM и курсор; pop_sfx_fill (резидент) получил третий источник: эффект важнее музыки, музыка важнее тишины. Эффект музыку не сбрасывает — её курсор стоит, пока тот доигрывает, и она продолжается с места. * story_1_absence звучит на первом экране истории, где была тишина. Грузим ПОСЛЕ сборки картинки: чтение идёт через ESTEX, и при открытом CBL насос не успел бы долить блок. Проверено записью звука из MAME: корреляция огибающих с эталоном 0,836 при сдвиге 1,8 с, RMS 22,6 против 18,3 (разница — 8-битное квантование). Эффекты двери в PV-сцене после трека звучат по-прежнему. Бюджет: резидент +83 Б кода и +25 Б данных, запас W2 151 Б (кучи в приложении нет, malloc не слинкован); банк 9 занят на 88 %. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,7 @@ BANK8_SRCS := roomtest_cold.c pop_level_cold.c pop_kboot.c pop_guard_cold.c \
|
||||
# Банк 9 — оболочка: menu/settings/config/font, автомат, global timer,
|
||||
# title, attract-demo и таблица рекордов. Здесь остаются небольшие редкие
|
||||
# верхнеуровневые точки входа, но не рендеры крупных заставок.
|
||||
BANK9_SRCS := pop_menu.c pop_status.c pop_config.c pop_app.c pop_timer.c pop_ui.c pop_title.c pop_demo.c \
|
||||
BANK9_SRCS := pop_menu.c pop_status.c pop_music.c pop_config.c pop_app.c pop_timer.c pop_ui.c pop_title.c pop_demo.c \
|
||||
pop_flow.c pop_hof.c
|
||||
# Банк 10 — единый владелец экранных палитр/fade. Он намеренно отделён от UI:
|
||||
# его будут вызывать и игровой lifecycle, и все cutscene-ветки.
|
||||
@@ -173,6 +173,15 @@ SND_DIR := $(CURDIR)/../poc/res/sound
|
||||
# Список ОБЯЗАН совпадать: недостающий файл — это молча пропавшие эффекты.
|
||||
SND_DATA := $(foreach n,0 1 2 3 4 5 6 7 8 9,$(SND_DIR)/s$(n).bin)
|
||||
|
||||
# МУЗЫКА: записи DOS-версии, приведённые к формату CBL и порезанные на
|
||||
# страницы EMM (toolchain/pop_pack_music.py). Пока в игре звучит один
|
||||
# трек — story_1_absence на первом экране intro; список расширяется
|
||||
# добавлением номеров в MUS_IDS, имена кусков задаёт упаковщик.
|
||||
MUS_DIR := $(CURDIR)/../poc/res/music
|
||||
MUS_IDS := 55
|
||||
MUS_CHUNKS := 00 01 02 03 04 05 06 07 08 09 10
|
||||
MUS_DATA := $(foreach i,$(MUS_IDS),$(foreach c,$(MUS_CHUNKS),$(MUS_DIR)/m$(i)_$(c).bin))
|
||||
|
||||
# UI: два исходных шрифта SDLPoP в одном EMM-атласе. Файл короче страницы:
|
||||
# хвост использует menu для временного snapshot палитры (без расхода W2).
|
||||
FONT_DIR := $(CURDIR)/../poc/res/font
|
||||
@@ -203,7 +212,7 @@ PV_DATA := $(PV_DIR)/story.pal $(PV_DIR)/pv.pal \
|
||||
$(PV_ANIM)
|
||||
|
||||
EXTRA_DATA := $(BG_DATA) $(KID_DATA) $(KID_BIN) $(GUARD_DATA) $(SKEL_DATA) \
|
||||
$(VIZIER_DATA) $(SHADOW_DATA) $(SND_DATA) $(FONT_DATA) $(TITLE_DATA) $(PV_DATA) $(LVL_DATA)
|
||||
$(VIZIER_DATA) $(SHADOW_DATA) $(SND_DATA) $(MUS_DATA) $(FONT_DATA) $(TITLE_DATA) $(PV_DATA) $(LVL_DATA)
|
||||
# Образ HDD для MAME: ресурсы раскладываются ПО КАТАЛОГАМ (8.3, как в DSS),
|
||||
# чтобы корень диска не зарастал десятками .atl.
|
||||
# BG\ — фон (env/wall/fore/pot)
|
||||
@@ -221,6 +230,7 @@ HDD_PACK_ARGS := $(EXAMPLE).exe \
|
||||
$(foreach f,$(VIZIER_DATA),VIZIER:$(f)) \
|
||||
$(foreach f,$(SHADOW_DATA),SHADOW:$(f)) \
|
||||
$(foreach f,$(SND_DATA),SND:$(f)) \
|
||||
$(foreach f,$(MUS_DATA),MUS:$(f)) \
|
||||
$(foreach f,$(FONT_DATA),FONT:$(f)) \
|
||||
$(foreach f,$(TITLE_DATA),TITLE:$(f)) \
|
||||
$(foreach f,$(PV_DATA),PV:$(f)) \
|
||||
@@ -241,6 +251,7 @@ SKEL_STAMP := $(RESOURCE_STAMP_DIR)/skel
|
||||
VIZIER_STAMP := $(RESOURCE_STAMP_DIR)/vizier
|
||||
SHADOW_STAMP := $(RESOURCE_STAMP_DIR)/shadow
|
||||
SOUND_STAMP := $(RESOURCE_STAMP_DIR)/sound
|
||||
MUSIC_STAMP := $(RESOURCE_STAMP_DIR)/music
|
||||
KID_STAMP := $(RESOURCE_STAMP_DIR)/kid
|
||||
KID_BIN_STAMP := $(RESOURCE_STAMP_DIR)/kid-bin
|
||||
FONT_STAMP := $(RESOURCE_STAMP_DIR)/font
|
||||
@@ -286,6 +297,12 @@ $(SOUND_STAMP): $(TC)/pop_pack_sound.py | $(RESOURCE_STAMP_DIR)
|
||||
$(SND_DATA) pop_sound_tbl.h: $(SOUND_STAMP)
|
||||
@test -f $@ || { $(MAKE) -B $(SOUND_STAMP); test -f $@; }
|
||||
|
||||
$(MUSIC_STAMP): $(TC)/pop_pack_music.py | $(RESOURCE_STAMP_DIR)
|
||||
cd $(TC) && python3 pop_pack_music.py $(MUS_IDS)
|
||||
touch $@
|
||||
$(MUS_DATA) pop_music_tbl.h: $(MUSIC_STAMP)
|
||||
@test -f $@ || { $(MAKE) -B $(MUSIC_STAMP); test -f $@; }
|
||||
|
||||
$(KID_STAMP): $(TC)/pop_pack_kid.py $(TC)/pop_pack_bg.py | $(RESOURCE_STAMP_DIR)
|
||||
cd $(TC) && python3 pop_pack_kid.py
|
||||
touch $@
|
||||
@@ -349,11 +366,12 @@ resources-bg: $(BG_DATA)
|
||||
resources-kid: $(KID_DATA) $(KID_BIN)
|
||||
resources-actors: $(GUARD_DATA) $(SKEL_DATA) $(VIZIER_DATA) $(SHADOW_DATA)
|
||||
resources-sound: $(SND_DATA)
|
||||
resources-music: $(MUS_DATA)
|
||||
resources-font: $(FONT_DATA)
|
||||
resources-title: $(TITLE_DATA)
|
||||
resources-pv: $(PV_DATA)
|
||||
resources-levels: $(LVL_DATA)
|
||||
resources: resources-bg resources-kid resources-actors resources-sound resources-font resources-title resources-pv resources-levels
|
||||
resources: resources-bg resources-kid resources-actors resources-sound resources-music resources-font resources-title resources-pv resources-levels
|
||||
resources-rebuild:
|
||||
$(MAKE) -B resources
|
||||
|
||||
@@ -378,4 +396,4 @@ clean-resources:
|
||||
rm -rf $(RESOURCE_STAMP_DIR)
|
||||
|
||||
.PHONY: resources resources-rebuild resources-bg resources-kid resources-actors relink \
|
||||
resources-sound resources-font resources-title resources-pv resources-levels clean-resources
|
||||
resources-sound resources-music resources-font resources-title resources-pv resources-levels clean-resources
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "pop_bg.h"
|
||||
#include "pop_shadow.h"
|
||||
#include "pop_sfx.h"
|
||||
#include "pop_music.h"
|
||||
#include "pop_cutscene.h"
|
||||
#include "pop_intro.h"
|
||||
#include "pop_pal.h"
|
||||
@@ -1305,10 +1306,21 @@ uint8_t pop_intro_show(void) __banked
|
||||
if (intro_draw_new_palette(INTRO_STORY_ABSENCE) == 0)
|
||||
pop_pal_fade_in(INTRO_FADE);
|
||||
}
|
||||
/* Первый экран истории — единственное место, где музыка уже звучит.
|
||||
* Грузим ПОСЛЕ того, как картинка собрана: чтение кусков идёт через
|
||||
* ESTEX, и при открытом CBL насос не успел бы долить блок (скрежет,
|
||||
* см. pop_sfx_start). Порядок обязателен: load -> start -> play. */
|
||||
if (pop_music_load(POP_MUS_STORY_1) == 0) {
|
||||
(void)pop_sfx_start();
|
||||
pop_music_play();
|
||||
}
|
||||
if (intro_run(intro_before_pv)) {
|
||||
pop_music_free();
|
||||
intro_restore_game_palette();
|
||||
return 1;
|
||||
}
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
pv_result = intro_pv_animated();
|
||||
if (pv_result > 0) {
|
||||
intro_restore_game_palette();
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* pop_music.c — загрузка и запуск музыкального трека, БАНК 9.
|
||||
*
|
||||
* Здесь только холодная часть: выделить страницы EMM, вычитать в них куски
|
||||
* m<id>_<nn>.bin и выставить курсор. Сам вывод делает насос CBL в
|
||||
* резиденте (pop_sfx.c): когда эффект не звучит, он берёт байты отсюда, а
|
||||
* не из блока тишины. Контракт и мотивация — в pop_music.h.
|
||||
*
|
||||
* Имя файла собираем вручную, без sprintf: printf-семейство в резидент не
|
||||
* тянем, а тут и формат-то из двух чисел.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
#include "pop_music.h"
|
||||
#include "pop_music_tbl.h"
|
||||
#include "pop_sfx.h"
|
||||
|
||||
uint8_t pop_mus_page[POP_MUS_PAGES];
|
||||
static uint8_t mus_blk; /* блок EMM под трек */
|
||||
static uint8_t mus_pages; /* сколько страниц реально занято */
|
||||
static uint16_t mus_blocks; /* длина трека в порциях по 128 Б */
|
||||
|
||||
/* "MUS\mNN_KK.bin" — ровно 8.3, как требует DSS. */
|
||||
static void mus_name(char *buf, uint8_t id, uint8_t chunk)
|
||||
{
|
||||
buf[0] = 'M'; buf[1] = 'U'; buf[2] = 'S'; buf[3] = '\\';
|
||||
buf[4] = 'm';
|
||||
buf[5] = (char)('0' + id / 10);
|
||||
buf[6] = (char)('0' + id % 10);
|
||||
buf[7] = '_';
|
||||
buf[8] = (char)('0' + chunk / 10);
|
||||
buf[9] = (char)('0' + chunk % 10);
|
||||
buf[10] = '.'; buf[11] = 'b'; buf[12] = 'i'; buf[13] = 'n';
|
||||
buf[14] = 0;
|
||||
}
|
||||
|
||||
void pop_music_stop(void) __banked
|
||||
{
|
||||
pop_mus_left = 0;
|
||||
}
|
||||
|
||||
void pop_music_free(void) __banked
|
||||
{
|
||||
pop_mus_left = 0;
|
||||
if (mus_blk) {
|
||||
mem_free_block(mus_blk);
|
||||
mus_blk = 0;
|
||||
}
|
||||
mus_pages = 0;
|
||||
mus_blocks = 0;
|
||||
}
|
||||
|
||||
int8_t pop_music_load(uint8_t id) __banked
|
||||
{
|
||||
char name[15];
|
||||
uint8_t i, pages;
|
||||
|
||||
if (id >= POP_MUS_COUNT) return -1;
|
||||
pages = pop_mus_tbl[id].pages;
|
||||
if (!pages || pages > POP_MUS_PAGES) return -1;
|
||||
|
||||
pop_music_free();
|
||||
mus_blk = mem_alloc_pages(pages);
|
||||
if (!mus_blk) return -1;
|
||||
for (i = 0; i < pages; i++) {
|
||||
pop_mus_page[i] = mem_get_page(mus_blk, i);
|
||||
mus_name(name, id, i);
|
||||
/* Последний кусок короче страницы — читаем сколько есть; ошибку
|
||||
* ловим по отрицательному коду, а не по «не 16384». */
|
||||
if (!pop_mus_page[i] ||
|
||||
bank_load_file(pop_mus_page[i], 0, name, 16384) < 0) {
|
||||
pop_music_free();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
mus_pages = pages;
|
||||
mus_blocks = pop_mus_tbl[id].blocks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pop_music_play(void) __banked
|
||||
{
|
||||
if (!mus_pages) return;
|
||||
/* Порядок важен: сначала позиция, потом длина. Насос стартует ровно
|
||||
* с той порции, где left стал ненулевым, и промежуточное состояние
|
||||
* «играем, но курсор от прошлого трека» не возникает. */
|
||||
pop_mus_pg = 0;
|
||||
pop_mus_ptr = 0;
|
||||
pop_mus_left = mus_blocks;
|
||||
}
|
||||
|
||||
uint8_t pop_music_busy(void) __banked
|
||||
{
|
||||
return (uint8_t)(pop_mus_left != 0);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 — молчим */
|
||||
|
||||
/* Загрузить трек в 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 */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* pop_music_tbl.h — каталог музыкальных треков, СГЕНЕРИРОВАН.
|
||||
* Источник: applications/PoP/PoP1_DOS_music/flac (записи DOS-версии).
|
||||
* Генератор: toolchain/pop_pack_music.py. Руками не править.
|
||||
*
|
||||
* Нумерация id — оригинала (SDLPoP data/music/names.txt). pages —
|
||||
* сколько кусков m<id>_<nn>.bin лежит на диске, blocks — длина трека
|
||||
* в порциях насоса по 128 байт (в байтах не влезло бы в uint16).
|
||||
*/
|
||||
#ifndef POP_MUSIC_TBL_H
|
||||
#define POP_MUSIC_TBL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define POP_MUS_COUNT 56
|
||||
#define POP_MUS_BLOCK 128
|
||||
#define POP_MUS_MAX_PAGES 11
|
||||
|
||||
typedef struct {
|
||||
uint8_t pages; /* 0 — трека нет */
|
||||
uint16_t blocks; /* длина в порциях по 128 байт */
|
||||
} pop_mus_ent_t;
|
||||
|
||||
static const pop_mus_ent_t pop_mus_tbl[POP_MUS_COUNT] = {
|
||||
{ 0, 0 }, /* 0 */
|
||||
{ 0, 0 }, /* 1 */
|
||||
{ 0, 0 }, /* 2 */
|
||||
{ 0, 0 }, /* 3 */
|
||||
{ 0, 0 }, /* 4 */
|
||||
{ 0, 0 }, /* 5 */
|
||||
{ 0, 0 }, /* 6 */
|
||||
{ 0, 0 }, /* 7 */
|
||||
{ 0, 0 }, /* 8 */
|
||||
{ 0, 0 }, /* 9 */
|
||||
{ 0, 0 }, /* 10 */
|
||||
{ 0, 0 }, /* 11 */
|
||||
{ 0, 0 }, /* 12 */
|
||||
{ 0, 0 }, /* 13 */
|
||||
{ 0, 0 }, /* 14 */
|
||||
{ 0, 0 }, /* 15 */
|
||||
{ 0, 0 }, /* 16 */
|
||||
{ 0, 0 }, /* 17 */
|
||||
{ 0, 0 }, /* 18 */
|
||||
{ 0, 0 }, /* 19 */
|
||||
{ 0, 0 }, /* 20 */
|
||||
{ 0, 0 }, /* 21 */
|
||||
{ 0, 0 }, /* 22 */
|
||||
{ 0, 0 }, /* 23 */
|
||||
{ 0, 0 }, /* 24 */
|
||||
{ 0, 0 }, /* 25 */
|
||||
{ 0, 0 }, /* 26 */
|
||||
{ 0, 0 }, /* 27 */
|
||||
{ 0, 0 }, /* 28 */
|
||||
{ 0, 0 }, /* 29 */
|
||||
{ 0, 0 }, /* 30 */
|
||||
{ 0, 0 }, /* 31 */
|
||||
{ 0, 0 }, /* 32 */
|
||||
{ 0, 0 }, /* 33 */
|
||||
{ 0, 0 }, /* 34 */
|
||||
{ 0, 0 }, /* 35 */
|
||||
{ 0, 0 }, /* 36 */
|
||||
{ 0, 0 }, /* 37 */
|
||||
{ 0, 0 }, /* 38 */
|
||||
{ 0, 0 }, /* 39 */
|
||||
{ 0, 0 }, /* 40 */
|
||||
{ 0, 0 }, /* 41 */
|
||||
{ 0, 0 }, /* 42 */
|
||||
{ 0, 0 }, /* 43 */
|
||||
{ 0, 0 }, /* 44 */
|
||||
{ 0, 0 }, /* 45 */
|
||||
{ 0, 0 }, /* 46 */
|
||||
{ 0, 0 }, /* 47 */
|
||||
{ 0, 0 }, /* 48 */
|
||||
{ 0, 0 }, /* 49 */
|
||||
{ 0, 0 }, /* 50 */
|
||||
{ 0, 0 }, /* 51 */
|
||||
{ 0, 0 }, /* 52 */
|
||||
{ 0, 0 }, /* 53 */
|
||||
{ 0, 0 }, /* 54 */
|
||||
{ 11, 1350 }, /* 55 story_1_absence */
|
||||
};
|
||||
|
||||
#endif /* POP_MUSIC_TBL_H */
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <cbl.h>
|
||||
#include <irq.h> /* IRQ_DISABLE/IRQ_ENABLE вокруг курсора */
|
||||
#include "pop_sfx.h"
|
||||
#include "pop_music.h"
|
||||
#include "_pop_sfx.h"
|
||||
|
||||
/* Физические страницы набора; заполняет холодная половина. */
|
||||
@@ -42,6 +43,12 @@ uint8_t pop_snd_want;
|
||||
* Всё в 8/16 бит — 32-битной арифметики на Z80 избегаем (см. memory
|
||||
* avoid_32bit_arith_z80); поэтому и в таблице смещение уже разобрано на
|
||||
* страницу и адрес в окне. */
|
||||
/* Курсор МУЗЫКИ. Лежит здесь, а не в pop_music.c, по той же причине, что
|
||||
* и курсор эффектов: его читает насос из прерывания, то есть резидент. */
|
||||
volatile uint8_t pop_mus_pg;
|
||||
volatile uint16_t pop_mus_ptr;
|
||||
volatile uint16_t pop_mus_left;
|
||||
|
||||
static volatile uint8_t sfx_pg; /* индекс в pop_snd_page[] */
|
||||
static volatile uint16_t sfx_ptr; /* адрес в окне W0: 0x0000..0x3FFF */
|
||||
static volatile uint16_t sfx_left; /* сколько байт осталось (кратно 128) */
|
||||
@@ -66,15 +73,39 @@ static volatile uint16_t sfx_left; /* сколько байт осталось
|
||||
* тесном резиденте W2 отказала бы и уронила весь звук. */
|
||||
int pop_sfx_fill(uint16_t n)
|
||||
{
|
||||
uint8_t saved, pg;
|
||||
uint8_t saved, phys;
|
||||
uint16_t ptr;
|
||||
if (sfx_left) { pg = sfx_pg; ptr = sfx_ptr; }
|
||||
else { pg = POP_SND_SILENCE_PAGE; ptr = POP_SND_SILENCE_OFF; }
|
||||
|
||||
/* Три источника по убыванию важности: эффект, музыка, тишина. Эффект
|
||||
* перебивает музыку — как у оригинала, где play_sound глушит всё, что
|
||||
* звучало; голос-то один. Музыка при этом НЕ теряет позицию: её
|
||||
* курсор стоит, пока эффект доигрывает, и она продолжается с места. */
|
||||
if (sfx_left) {
|
||||
phys = pop_snd_page[sfx_pg]; ptr = sfx_ptr;
|
||||
} else if (pop_mus_left) {
|
||||
phys = pop_mus_page[pop_mus_pg]; ptr = pop_mus_ptr;
|
||||
} else {
|
||||
phys = pop_snd_page[POP_SND_SILENCE_PAGE];
|
||||
ptr = POP_SND_SILENCE_OFF;
|
||||
}
|
||||
saved = _io_page_w0;
|
||||
_io_page_w0 = pop_snd_page[pg];
|
||||
_io_page_w0 = phys;
|
||||
cbl_push_otir((const void *)ptr, n);
|
||||
_io_page_w0 = saved;
|
||||
if (!sfx_left) return 1; /* играли тишину — курсор не двигаем */
|
||||
if (!sfx_left) {
|
||||
if (!pop_mus_left) return 1; /* тишина — курсор не двигаем */
|
||||
/* Длину трека считаем ПОРЦИЯМИ насоса, а не байтами: 169 КБ в
|
||||
* uint16 не влезает, а 1350 блоков — легко (32-бит арифметики на
|
||||
* Z80 избегаем, тем более в прерывании). */
|
||||
pop_mus_left--;
|
||||
pop_mus_ptr += n;
|
||||
if (pop_mus_ptr >= 0x4000) {
|
||||
pop_mus_ptr = 0;
|
||||
if ((uint8_t)(pop_mus_pg + 1) < POP_MUS_PAGES) pop_mus_pg++;
|
||||
else pop_mus_left = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sfx_ptr += n;
|
||||
if (sfx_ptr >= 0x4000) { /* граница страницы — ровно по блоку */
|
||||
|
||||
Reference in New Issue
Block a user