This commit is contained in:
Jan Racek
2026-03-24 13:01:47 +01:00
parent 55c1d2c39b
commit e51c06b012
51 changed files with 8111 additions and 491 deletions

97
src/audiomanager.hh Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#include <stdint.h>
namespace psxsplash {
/// Maximum number of audio clips that can be loaded in a scene
static constexpr int MAX_AUDIO_CLIPS = 32;
/// Maximum SPU voices (hardware limit)
static constexpr int MAX_VOICES = 24;
/// SPU RAM is 512KB total (0x00000-0x7FFFF).
/// First 0x1000 bytes reserved for capture buffers.
/// psyqo places a 16-byte silent dummy sample at 0x1000.
/// User clips start at 0x1010.
///
/// Upper bound is 0x10000 (64KB) because psyqo::SPU::playADPCM()
/// takes a uint16_t for the SPU RAM address.
static constexpr uint32_t SPU_RAM_START = 0x1010;
static constexpr uint32_t SPU_RAM_END = 0x10000;
/// Default ADSR: instant attack, sustain at max, ~46ms linear release.
/// Lower 16-bit (AD): attack linear shift=0 step=0("+7"), decay shift=0,
/// sustain level=0xF (max -> decay skipped)
/// Upper 16-bit (SR): sustain linear increase shift=0 step=0("+7"),
/// release linear shift=10 (~46ms to zero)
static constexpr uint32_t DEFAULT_ADSR = 0x000A000F;
/// Descriptor for a loaded audio clip in SPU RAM
struct AudioClip {
uint32_t spuAddr; // Byte address in SPU RAM
uint32_t size; // Size of ADPCM data in bytes
uint16_t sampleRate; // Original sample rate in Hz
bool loop; // Whether this clip should loop
bool loaded; // Whether this slot is valid
};
/// Manages SPU voices and audio clip playback.
///
/// Uses psyqo::SPU for all hardware interaction: initialization,
/// DMA uploads, voice allocation (via currentVolume check), playback
/// (playADPCM), and silencing (silenceChannels).
///
/// init()
/// loadClip(index, data, size, rate, loop) -> bool
/// play(clipIndex) -> channel
/// play(clipIndex, volume, pan) -> channel
/// stopVoice(channel)
/// stopAll()
/// setVoiceVolume(channel, vol, pan)
///
/// Volume is 0-128 (0=silent, 128=max). Pan is 0-127 (64=center).
class AudioManager {
public:
/// Initialize SPU hardware and reset state
void init();
/// Upload ADPCM data to SPU RAM and register as clip index.
/// Data must be 16-byte aligned (SPU ADPCM block size). Returns true on success.
bool loadClip(int clipIndex, const uint8_t* adpcmData, uint32_t sizeBytes,
uint16_t sampleRate, bool loop);
/// Play a clip by index. Returns channel (0-23), or -1 if full.
/// Volume: 0-128 (128=max). Pan: 0 (left) to 127 (right), 64 = center.
int play(int clipIndex, int volume = 128, int pan = 64);
/// Stop a specific channel (returned from play())
void stopVoice(int channel);
/// Stop all playing channels
void stopAll();
/// Set volume/pan on a playing channel
void setVoiceVolume(int channel, int volume, int pan = 64);
/// Get total SPU RAM used by loaded clips (for visualization)
uint32_t getUsedSPURam() const { return m_nextAddr - SPU_RAM_START; }
/// Get total SPU RAM available
uint32_t getTotalSPURam() const { return SPU_RAM_END - SPU_RAM_START; }
/// Get number of loaded clips
int getLoadedClipCount() const;
/// Reset all clips and free SPU RAM (call on scene unload)
void reset();
private:
/// Convert 0-128 volume to hardware 0-0x3FFF (fixed-volume mode)
static uint16_t volToHw(int v);
AudioClip m_clips[MAX_AUDIO_CLIPS];
uint32_t m_nextAddr = SPU_RAM_START; // Bump allocator for SPU RAM
};
} // namespace psxsplash