// Audio: 4-channel Protracker .MOD music with digital one-shot SFX. // // Authors compose music as .MOD modules. The host-side asset pipeline // (tools/joeymod) converts each module into the runtime form the // target platform expects: // // Apple IIgs -- NinjaTrackerPlus .NTP, played by Ninjaforce's // 65816 replayer linked into the binary. // Amiga -- raw .MOD, played by Frank Wille's PTPlayer. // DOS -- raw .MOD, played by libxmp-lite over SB DMA. // Atari ST -- raw .MOD, played by libxmp-lite (parser only) plus // a hand-rolled 68k 4-channel mixer that outputs via // YM2149 4-bit PWM at ~12.5 kHz. // // Game code always calls the same five entry points; the per-port // HAL hides the engine choice. A failed audio init is non-fatal -- // joeyInit still succeeds and audio calls become no-ops. #ifndef JOEYLIB_AUDIO_H #define JOEYLIB_AUDIO_H #include "platform.h" #include "types.h" #define JOEY_AUDIO_SFX_SLOTS 4 // Initialize audio. Returns true if the platform has a working audio // engine and was able to start it. Returns false silently otherwise; // the rest of the API stays callable but produces no sound. bool joeyAudioInit(void); // Tear down the engine. Safe to call when audio is not initialized. void joeyAudioShutdown(void); // Begin module playback. data points at the platform-native module // blob (.MOD on most platforms, .NTP on IIgs); the asset pipeline // produces the right form for each target. If a module is already // playing, it is replaced. // // loop=true plays forever. loop=false stops at song end, but Amiga // requires the module to contain an E8FF effect at song end for the // stop to fire (PTPlayer has no native song-end signal). The // `joeymod` tool's .amod output extension injects that marker // automatically; ship .amod for Amiga and .mod for the other ports. // loop=false on a .mod (no E8FF) loops anyway on Amiga. void joeyAudioPlayMod(const uint8_t *data, uint32_t length, bool loop); // Stop the current module (if any). The playhead is reset so the next // joeyAudioPlayMod starts from the top. void joeyAudioStopMod(void); // True if a module is currently producing output (false during silence // after StopMod or before the first PlayMod, and on platforms where // audio init failed). bool joeyAudioIsPlayingMod(void); // Trigger a one-shot digital SFX on the given slot (0..JOEY_AUDIO_SFX_SLOTS-1). // sample points at raw signed 8-bit PCM. rateHz is the playback rate // the sample was recorded at; the engine pitches as needed for its // own output rate. If the slot is currently playing, the new sample // replaces it. void joeyAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz); // Stop a SFX slot early. No-op if the slot is already idle. void joeyAudioStopSfx(uint8_t slot); // Hook the engine into the game loop. Most platforms drive their // engines off a hardware IRQ and ignore this call, but it's safe to // invoke once per frame regardless and required for any port that // runs its mixer in user-thread context. void joeyAudioFrameTick(void); #endif