From a88810c8a70c8e3a7533a266774ab61a84a9adf4 Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Sun, 2 Jul 2023 21:31:19 +0100 Subject: Add sound support --- data/death_efx.wav | Bin 0 -> 65506 bytes data/gold_efx.wav | Bin 0 -> 25202 bytes data/hit_efx.wav | Bin 0 -> 16510 bytes data/jump_efx.wav | Bin 0 -> 27328 bytes data/music.it | Bin 0 -> 81629 bytes data/pickup_efx.wav | Bin 0 -> 56048 bytes data/time_efx.wav | Bin 0 -> 45908 bytes data/warp_efx.wav | Bin 0 -> 45908 bytes src/Makefile | 16 ++++++-- src/data.h | 19 +++++++++ src/game.c | 3 ++ src/main.c | 11 +++++ src/pickup.c | 6 +++ src/player.c | 9 ++++ src/sound.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sound.h | 24 +++++++++++ src/timer.c | 9 ++++ src/timer.h | 2 + tools/raw.py | 58 ++++++++++++++++++++++++++ 19 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 data/death_efx.wav create mode 100644 data/gold_efx.wav create mode 100644 data/hit_efx.wav create mode 100644 data/jump_efx.wav create mode 100644 data/music.it create mode 100644 data/pickup_efx.wav create mode 100644 data/time_efx.wav create mode 100644 data/warp_efx.wav create mode 100644 src/sound.c create mode 100644 src/sound.h create mode 100755 tools/raw.py diff --git a/data/death_efx.wav b/data/death_efx.wav new file mode 100644 index 0000000..7bed12a Binary files /dev/null and b/data/death_efx.wav differ diff --git a/data/gold_efx.wav b/data/gold_efx.wav new file mode 100644 index 0000000..1dc970a Binary files /dev/null and b/data/gold_efx.wav differ diff --git a/data/hit_efx.wav b/data/hit_efx.wav new file mode 100644 index 0000000..483d544 Binary files /dev/null and b/data/hit_efx.wav differ diff --git a/data/jump_efx.wav b/data/jump_efx.wav new file mode 100644 index 0000000..82673e4 Binary files /dev/null and b/data/jump_efx.wav differ diff --git a/data/music.it b/data/music.it new file mode 100644 index 0000000..373a4e1 Binary files /dev/null and b/data/music.it differ diff --git a/data/pickup_efx.wav b/data/pickup_efx.wav new file mode 100644 index 0000000..d9a1253 Binary files /dev/null and b/data/pickup_efx.wav differ diff --git a/data/time_efx.wav b/data/time_efx.wav new file mode 100644 index 0000000..df6f317 Binary files /dev/null and b/data/time_efx.wav differ diff --git a/data/warp_efx.wav b/data/warp_efx.wav new file mode 100644 index 0000000..027bc4d Binary files /dev/null and b/data/warp_efx.wav differ diff --git a/src/Makefile b/src/Makefile index 4764364..1631723 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,7 +5,8 @@ endif BIN := ../game.exe CC := i586-pc-msdosdjgpp-gcc CFLAGS := -I. -I$(LIBMIKMOD_BASE)/include -c -Wall -Werror -pedantic -O2 -march=i386 -DDEBUG -LDFLAGS := -s -L$(LIBMIKMOD_BASE)/dos -lmikmod +LDFLAGS := -s -L$(LIBMIKMOD_BASE)/dos +LIBS := -lmikmod IMGS := $(wildcard ../data/*.png) IMG_OBJS := $(IMGS:../data/%.png=%.o) @@ -13,13 +14,16 @@ IMG_OBJS := $(IMGS:../data/%.png=%.o) MAPS := ../data/stage.json MAP_OBJS := $(MAPS:../data/%.json=%.o) +WAVS := $(wildcard ../data/*.wav) +WAV_OBJS := $(WAVS:../data/%.wav=%.o) + SRCS := $(wildcard *.c) -OBJS := $(SRCS:.c=.o) $(IMG_OBJS) palette.o $(MAP_OBJS) +OBJS := $(SRCS:.c=.o) $(IMG_OBJS) palette.o $(MAP_OBJS) $(WAV_OBJS) music.o all: $(BIN) $(BIN): $(OBJS) - $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ $(LIBS) .c.o: $(CC) $(CFLAGS) $< -o $@ @@ -33,6 +37,12 @@ $(IMG_OBJS): %.o: ../data/%.png $(MAP_OBJS): %.o: ../data/%.json ../tools/map.py $< $@ +$(WAV_OBJS): %.o: ../data/%.wav + ../tools/raw.py $< $@ + +music.o: ../data/music.it + ../tools/raw.py $< $@ + clean: rm -f $(BIN) *.o Makefile.deps diff --git a/src/data.h b/src/data.h index 78a1f60..0abdd22 100644 --- a/src/data.h +++ b/src/data.h @@ -10,4 +10,23 @@ extern const uint8_t binary_title_start[]; extern const uint8_t binary_stage_start[]; +extern const uint8_t binary_music_start[]; +extern const uint8_t binary_music_size; + +extern const uint8_t binary_hit_efx_start[]; +extern const uint8_t binary_death_efx_start[]; +extern const uint8_t binary_jump_efx_start[]; +extern const uint8_t binary_time_efx_start[]; +extern const uint8_t binary_gold_efx_start[]; +extern const uint8_t binary_pickup_efx_start[]; +extern const uint8_t binary_warp_efx_start[]; + +extern const uint8_t binary_hit_efx_size; +extern const uint8_t binary_death_efx_size; +extern const uint8_t binary_jump_efx_size; +extern const uint8_t binary_time_efx_size; +extern const uint8_t binary_gold_efx_size; +extern const uint8_t binary_pickup_efx_size; +extern const uint8_t binary_warp_efx_size; + #endif /* _DATA_H */ diff --git a/src/game.c b/src/game.c index b45989b..60a986a 100644 --- a/src/game.c +++ b/src/game.c @@ -3,6 +3,7 @@ #include "keyb.h" #include "vga.h" +#include "sound.h" #include "text.h" #include "map.h" #include "data.h" @@ -85,6 +86,8 @@ static void hud_render() { sprintf(b, "%02d", time); put_text(172, 4, b, time > 10 ? 1 : 15); + if (time <= 10) + sound_play_efx(EFX_TIME); } if (hud & HUD_STAGE) diff --git a/src/main.c b/src/main.c index 6207b35..d5b9df8 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #include "timer.h" #include "keyb.h" +#include "sound.h" #include "vga.h" #include "data.h" #include "menu.h" @@ -18,14 +19,24 @@ void free_all() { timer_free(); keyb_free(); + sound_free(); } int main(int argc, char *argv[]) { + if (!sound_init()) + { + fprintf(stderr, "ERROR: failed to init sound\n"); + return 1; + } + timer_init(); keyb_init(); atexit(free_all); + /* to update mikmod */ + timer_user_fn(sound_update); + /* set VGA 320x200, 256 col */ if (!set_mode(0x13)) { diff --git a/src/pickup.c b/src/pickup.c index 044e3ef..7036891 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -2,6 +2,7 @@ #include #include "vga.h" +#include "sound.h" #include "entities.h" #include "game.h" @@ -133,7 +134,10 @@ void pickup_pickaxe_init(Entity *e) void pickup_wait_update(Entity *e) { if (e->counter-- == 0) + { e->update = pickup_in_update; + sound_play_efx(EFX_WARP); + } } void pickup_in_update(Entity *e) @@ -170,6 +174,7 @@ void pickup_update(Entity *e) if (e->counter++ == MAX_TTL) { effect_out_init(e); + sound_play_efx(EFX_WARP); return; } @@ -203,5 +208,6 @@ void pickup_update(Entity *e) break; } e->used = 0; + sound_play_efx(EFX_PICKUP); } } diff --git a/src/player.c b/src/player.c index a8a75cf..8d80923 100644 --- a/src/player.c +++ b/src/player.c @@ -2,6 +2,7 @@ #include "keyb.h" #include "vga.h" +#include "sound.h" #include "map.h" #include "data.h" #include "game.h" @@ -164,6 +165,7 @@ void player_update() momentum = 0; frame = FRAME_JUMPING; gravity = GRAVITY_UP; + sound_play_efx(EFX_JUMP); } } else @@ -274,7 +276,10 @@ void player_update() } if (map_update_gold(x + (dir == DIR_LEFT ? 7 : 8), y + 15)) + { add_score(10); + sound_play_efx(EFX_GOLD); + } } void player_erase() @@ -318,11 +323,15 @@ uint8_t player_collision_pickup(Entity *e) void player_hit() { if (use_pickaxe()) + { invuln = INVULN_TIME; + sound_play_efx(EFX_HIT); + } else { dying = 1; frame = FRAME_DYING; gravity = GRAVITY_UP; + sound_play_efx(EFX_DEATH); } } diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..f57c133 --- /dev/null +++ b/src/sound.c @@ -0,0 +1,116 @@ +#include + +#include "data.h" + +#include "mikmod.h" + +typedef struct +{ + SAMPLE *s; + const char *data; + size_t len; +} Efx; + +#define EFX_CNT 7 + +static Efx efx[EFX_CNT] = +{ + { NULL, (const char *)binary_gold_efx_start, (size_t)&binary_gold_efx_size}, + { NULL, (const char *)binary_jump_efx_start, (size_t)&binary_jump_efx_size}, + { NULL, (const char *)binary_pickup_efx_start, (size_t)&binary_pickup_efx_size}, + { NULL, (const char *)binary_warp_efx_start, (size_t)&binary_warp_efx_size}, + { NULL, (const char *)binary_time_efx_start, (size_t)&binary_time_efx_size}, + { NULL, (const char *)binary_hit_efx_start, (size_t)&binary_hit_efx_size}, + { NULL, (const char *)binary_death_efx_start, (size_t)&binary_death_efx_size}, +}; + +static uint8_t cur = 0; +static uint32_t voice = 0; + +static MODULE *music = NULL; + +uint8_t sound_init() +{ + MikMod_RegisterAllDrivers(); + MikMod_RegisterLoader(&load_it); + + md_mode |= DMODE_SOFT_SNDFX | DMODE_SOFT_MUSIC; + md_volume = 128; + md_sndfxvolume = 128; + md_pansep = 0; + md_mixfreq = 22050; + + if (MikMod_Init(NULL)) + return 0; + + for (uint8_t i = 0; i < EFX_CNT; i++) + { + efx[i].s = Sample_LoadMem(efx[i].data, efx[i].len); + if (!efx[i].s) + return 0; + } + + /* 1 voice for effects */ + MikMod_SetNumVoices(-1, 1); + + /* needs a song playing to get effects; have a "silence" pattern on 0 */ + music = Player_LoadMem((const char *)binary_music_start, (size_t)&binary_music_size, 8, 0); + if (!music) + return 0; + + Player_Start(music); + MikMod_EnableOutput(); + + return 1; +} + +volatile static uint8_t divider = 0; + +/* To be called from the timer int, so we update every 540ms approx by using a + * counter and updating 1 in 10 interrupts */ +void sound_update() +{ + if (++divider == 10) + { + divider = 0; + MikMod_Update(); + } +} + +void sound_mute() +{ + MikMod_DisableOutput(); +} + +void sound_unmute() +{ + MikMod_EnableOutput(); +} + +void sound_free() +{ + MikMod_DisableOutput(); + + if (music) + Player_Free(music); + + for (uint8_t i = 0; i < EFX_CNT; i++) + if (!efx[i].s) + Sample_Free(efx[i].s); + + MikMod_Exit(); +} + +void sound_music_pattern(uint16_t pat) +{ + Player_SetPosition(pat); +} + +void sound_play_efx(uint8_t efxno) +{ + if (cur > efxno && !Voice_Stopped(voice)) + return; + + cur = efxno; + voice = Sample_Play(efx[cur].s, 0, 0); +} diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..506531b --- /dev/null +++ b/src/sound.h @@ -0,0 +1,24 @@ +#ifndef _SOUND_H +#define _SOUND_H + +enum { + EFX_GOLD = 0, + EFX_JUMP, + EFX_PICKUP, + EFX_WARP, + EFX_TIME, + EFX_HIT, + EFX_DEATH, +}; + +uint8_t sound_init(); +void sound_free(); +void sound_update(); + +void sound_music_pattern(uint16_t pat); +void sound_play_efx(uint8_t efxno); + +void sound_mute(); +void sound_unmute(); + +#endif /* _SOUND_H */ diff --git a/src/timer.c b/src/timer.c index f3c5898..8702233 100644 --- a/src/timer.c +++ b/src/timer.c @@ -8,6 +8,7 @@ volatile uint32_t clock = 0; volatile uint8_t clock_secs = 0; volatile uint8_t clock_enabled = 0; volatile uint8_t *clock_updated = NULL; +volatile void (*user_fn)(void) = NULL; static _go32_dpmi_seginfo old_handler, new_handler; @@ -29,6 +30,9 @@ static void timer_handler() } } } + + if (user_fn) + user_fn(); } void timer_init() @@ -39,6 +43,11 @@ void timer_init() _go32_dpmi_chain_protected_mode_interrupt_vector(0x1c, &new_handler); } +void timer_user_fn(void (*fn)(void)) +{ + user_fn = fn; +} + void timer_free() { if (_go32_dpmi_set_protected_mode_interrupt_vector(0x1c, &old_handler) == -1) diff --git a/src/timer.h b/src/timer.h index 8207e5b..8f636df 100644 --- a/src/timer.h +++ b/src/timer.h @@ -7,6 +7,8 @@ extern volatile uint32_t ticks; void timer_init(); void timer_free(); +void timer_user_fn(void (*fn)(void)); + /* countdown clock */ void timer_start(uint8_t secs, volatile uint8_t *updated); uint8_t timer_value(); diff --git a/tools/raw.py b/tools/raw.py new file mode 100755 index 0000000..755a64c --- /dev/null +++ b/tools/raw.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +import subprocess +import os + +__version__ = "1.0" + +ld = os.environ.get("LD", "i586-pc-msdosdjgpp-ld") +strip = os.environ.get("STRIP", "i586-pc-msdosdjgpp-strip") + + +def main(): + parser = ArgumentParser( + description="RAW to .o", + epilog="Copyright (C) 2023 Juan J Martinez ", + ) + + parser.add_argument( + "--version", action="version", version="%(prog)s " + __version__ + ) + parser.add_argument("file", help="file to convert") + parser.add_argument("output", help="object name for the map data") + + args = parser.parse_args() + + with open(args.file, "rb") as fd: + data = fd.read() + + tmp = args.output.rstrip(".o") + with open(tmp, "wb") as fd: + fd.write(bytearray(data)) + fd.flush() + + # create an object file from the binary + rc = subprocess.call( + [ + ld, + "-r", + "-b", + "binary", + "-o", + args.output, + tmp, + ] + ) + os.unlink(tmp) + if rc != 0: + parser.error("Failed to run %s" % ld) + + # strip unwanted symbols + rc = subprocess.call([strip, "-w", "-K", "*_%s_*" % tmp, args.output]) + if rc != 0: + parser.error("Failed to run %s" % ld) + + +if __name__ == "__main__": + main() -- cgit v1.2.3