#include "libs/kaja/kaja.h" #include "game/pf.h" #include "defconv.h" #ifdef __cplusplus extern "C" { #endif extern char snd_interrupt_if_midi; extern bool snd_midi_possible; #if GAME <= 3 typedef enum { SND_BGM_OFF, SND_BGM_FM, SND_BGM_MIDI } snd_bgm_mode_t; extern bool snd_active; extern bool snd_midi_active; extern bool snd_fm_possible; #ifdef __cplusplus static inline bool snd_bgm_active() { return snd_active; } static inline bool snd_se_active() { return snd_fm_possible; } #endif #define snd_bgm_is_fm() \ (snd_midi_active != true) #endif // Purely returns whether the PMD driver is resident at its interrupt. The // check for an actual installed FM sound source is done by // snd_determine_mode(). bool16 snd_pmd_resident(void); #if (GAME != 3) // Returns whether the MMD driver is resident at its interrupt. If it is, // ≤TH03 sets [snd_midi_active] to true. bool16 snd_mmd_resident(void); #endif // Checks whether an FM sound source is active, and sets [snd_fm_possible] // and [snd_active]. In the absence of an FM source, [snd_active] is set to // [snd_midi_active]. Returns the new value of [snd_active]. bool16 snd_determine_mode(void); // Calls the interrupt handler of the installed sound driver with AX = [ax], // if any. If BGM is disabled, the return value is undefined. int16_t DEFCONV snd_kaja_interrupt(int16_t ax); // Blocks until the active sound driver reports the given [volume] via // KAJA_GET_VOLUME. The behavior is undefined if no sound driver is active. void snd_delay_until_volume(uint8_t volume); #if (GAME == 2) #define SND_FALLBACK_DELAY_FRAMES 100 // Blocks until the active sound driver has played back the current BGM for // the *total* given number of full measures. Does *not* correspond to a // specific measure of the song; a [measure] within the looping section // would still only be hit once, during the first loop. // If no sound driver is active, the delay is replaced with // frame_delay([SND_FALLBACK_DELAY_FRAMES]). // ZUN landmine: Neither PMD nor MMD reset the internal measure when // stopping playback. If no BGM is playing and the previous one hasn't been // played back for at least the given number of [measures], the function // will deadlock. void snd_delay_until_measure(int measure); #endif #define snd_kaja_func(func, param) ( \ snd_kaja_interrupt(((func) << 8) + (param)) \ ) typedef enum { SND_LOAD_SONG = (KAJA_GET_SONG_ADDRESS << 8), SND_LOAD_SE = (PMD_GET_SE_ADDRESS << 8), } snd_load_func_t; #if (GAME <= 3) // Loads a song in .M format ([func] == SND_LOAD_SONG) or a sound effect // bank in EFC format ([func] == SND_LOAD_SE) into the respective work // buffer of the sound driver. If MIDI is used, 'md' is appended to the // file name. // [fn] still needs to be null-terminated, despite its fixed length. // // ZUN landmine: The function doesn't stop any currently playing song // before loading the a new one. This can cause glitches when loading from // a slow storage device: If it takes longer than a single period of the // OPN timer to write the full new song to the driver's song buffer, the // driver will play back garbage in the meantime. void snd_load(const char fn[PF_FN_LEN], snd_load_func_t func); #endif void snd_se_reset(void); void DEFCONV snd_se_play(int new_se); void snd_se_update(void); // Cancels any currently playing sound effect to play the given one. #define snd_se_play_force(new_se) { \ snd_se_reset(); \ snd_se_play(new_se); \ snd_se_update(); \ } #ifdef __cplusplus } #endif