diff --git a/Makefile b/Makefile index f7d8bc4..e5eea0a 100644 --- a/Makefile +++ b/Makefile @@ -111,11 +111,6 @@ CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) -#ifneq ($(strip $(MUSIC)),) -# export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) -# BINFILES += soundbank.bin -#endif - #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C #--------------------------------------------------------------------------------- @@ -136,6 +131,12 @@ export OFILES_GRAPHICS := $(PNGFILES:.png=.o) export OFILES := $(OFILES_SOURCES) $(OFILES_GRAPHICS) +ifneq ($(strip $(MUSIC)),) + export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) + BINFILES += soundbank.bin + OFILES += soundbank.bin.o +endif + export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(PNGFILES:.png=.h) export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ @@ -228,9 +229,9 @@ $(OFILES_SOURCES) : $(HFILES) #--------------------------------------------------------------------------------- # rule to build soundbank from music files #--------------------------------------------------------------------------------- -#soundbank.bin soundbank.h : $(AUDIOFILES) +soundbank.bin soundbank.h : $(AUDIOFILES) #--------------------------------------------------------------------------------- -# @mmutil $^ -osoundbank.bin -hsoundbank.h + @mmutil $^ -osoundbank.bin -hsoundbank.h #--------------------------------------------------------------------------------- # This rule links in binary data with the .bin extension diff --git a/audio/main_menu.xm b/audio/main_menu.xm new file mode 100644 index 0000000..5ca0021 Binary files /dev/null and b/audio/main_menu.xm differ diff --git a/include/sound.h b/include/sound.h new file mode 100644 index 0000000..d94c2fd --- /dev/null +++ b/include/sound.h @@ -0,0 +1,94 @@ +#ifndef _SOUND_H +#define _SOUND_H + +#include + +#include "soundbank.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* PTGBSFXHandle; + +/** + * @brief The API's defined here are a thin abstraction layer over the sound engine functions. + * This allows the underlying sound engine to be swapped out without affecting the rest of the codebase. + */ + +/** + * @brief This function initializes the sound engine and sets up a VBLANK handler + * to process audio every frame. It should be called during the initialization phase of the program. + */ +bool sound_init(void); + +/** + * @brief This function starts playing the song at the given index. + * If loop is true, the song will loop indefinitely until stopped or another song is played. + * + * @param song_index The index of the song to play. + * @param loop Whether the song should loop indefinitely. + */ +void play_song(u32 song_index, bool loop); + +/** + * @brief This function checks if a song is currently playing. + * + * @return true if a song is playing, false otherwise. + */ +bool is_song_playing(void); + +/** + * @brief This function stops the currently playing song. + */ +void stop_song(void); + +/** + * @brief This function plays the sound effect at the given index. + * Sound effects are typically short audio clips that play in response to specific events in the game, + * such as button presses or character actions. + * These are usually raw PCM samples (e.g., WAV files) + * + * To play a MOD- S3M- XM- or IT format based sound effect, + * use the play_jingle function instead. + */ +PTGBSFXHandle play_sound_effect(u32 sound_effect_index); + +/** + * @brief This function stops the given sound effect from playing and invalidates the handle + */ +void stop_sound_effect(PTGBSFXHandle handle); + +/** + * @brief This function stops all currently playing sound effects and resets + */ +void stop_all_sound_effects(void); + +/** + * @brief This function marks the sound effect as unimportant, + * allowing the sound engine to stop it if it needs to free up channels for new sound effects. + * + * It also invalidates the handle, so it should not be used after calling this function. + */ +void release_sound_effect(PTGBSFXHandle handle); + +/** + * @brief This function plays a jingle at the given index. + * A jingle is a sound effect that is based on a tracker format like MOD, S3M, XM, or IT. + * They can be played simultaneously with songs. + * If you want to play PCM-based sound effects (e.g., WAV files), use the play_sound_effect function instead. + * + * Note that jingles must be limited to 4 channels only. (https://blocksds.skylyrac.net/maxmod/group__gba__jingle__playback.html) + */ +void play_jingle(u32 jingle_index); + +/** + * @brief Checks if a jingle is actively playing. + */ +bool is_jingle_playing(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index c86d003..0de8f91 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,6 +1,5 @@ #include #include -// #include //Music #include "libstd_replacements.h" #include "flash_mem.h" #include "interrupt.h" @@ -30,6 +29,7 @@ #include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h" #include "text_data_table.h" #include "custom_malloc.h" +#include "sound.h" /* @@ -53,36 +53,6 @@ bool skip = true; rom_data curr_GBA_rom; Button_Menu yes_no_menu(1, 2, 40, 24, false); -/* -int test_main(void) Music -{ - - irq_init(NULL); - // Initialize maxmod with default settings - // pass soundbank address, and allocate 8 channels. - - irq_set(II_VBLANK, mmVBlank, 0); - irq_enable(II_VBLANK); - - mmInitDefault((mm_addr)soundbank_bin, 8); - - mmStart(MOD_FLATOUTLIES, MM_PLAY_LOOP); - // Song is playing now (well... almost) - while (1) - { - // ..process game logic.. - - // Update Maxmod - mmFrame(); - - // Wait for new frame (SWI 5) - VBlankIntrWait(); - - // ..update graphical data.. - } -} -*/ - // (R + G*32 + B*1024) #define RGB(r, g, b) (r + (g * 32) + (b * 1024)) @@ -106,8 +76,6 @@ void initalization_script(void) { // Initalizations REG_DISPCNT = DCNT_BLANK | DCNT_MODE0 | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_BG3 | DCNT_OBJ | DCNT_OBJ_1D; - irq_init(NULL); - irq_enable(II_VBLANK); // Disable for save data read/write REG_IME = 0; @@ -116,9 +84,7 @@ void initalization_script(void) // Sound bank init irq_init(NULL); irq_enable(II_VBLANK); - // irq_set(II_VBLANK, mmVBlank, 0); //Music - // mmInitDefault((mm_addr)soundbank_bin, 8); //Music - // mmStart(MOD_FLATOUTLIES, MM_PLAY_LOOP); //Music + sound_init(); // Graphics init oam_init(obj_buffer, 128); @@ -331,6 +297,7 @@ int main_menu_loop() general_text.decompress(get_compressed_general_table()); + play_song(MOD_MAIN_MENU, true); while (true) { if (update) diff --git a/source/sound.c b/source/sound.c new file mode 100644 index 0000000..29bf420 --- /dev/null +++ b/source/sound.c @@ -0,0 +1,68 @@ +#include +#include "sound.h" +#include "soundbank_bin.h" + +static void sound_irq_handler(void) +{ + mmVBlank(); + mmFrame(); +} + +bool sound_init(void) +{ + irq_add(II_VBLANK, sound_irq_handler); + + mm_addr soundbank = (mm_addr)soundbank_bin; + if (!soundbank) + { + return false; + } + + mmInitDefault(soundbank, 16); + return true; +} + +void play_song(u32 song_index, bool loop) +{ + mmStart(song_index, loop ? MM_PLAY_LOOP : MM_PLAY_ONCE); +} + +bool is_song_playing(void) +{ + return mmActive(); +} + +void stop_song(void) +{ + mmStop(); +} + +PTGBSFXHandle play_sound_effect(u32 sound_effect_index) +{ + return (PTGBSFXHandle)mmEffect(sound_effect_index); +} + +void stop_sound_effect(PTGBSFXHandle handle) +{ + mmEffectCancel((mm_sfxhand)handle); +} + +void stop_all_sound_effects(void) +{ + mmEffectCancelAll(); +} + +void release_sound_effect(PTGBSFXHandle handle) +{ + mmEffectRelease((mm_sfxhand)handle); +} + +void play_jingle(u32 jingle_index) +{ + mmJingle(jingle_index); +} + +bool is_jingle_playing(void) +{ + return mmActiveSub(); +} \ No newline at end of file