#pragma once #include "common/bitfield.h" #include "types.h" #include class AudioStream; class StateWrapper; class System; class DMA; class InterruptController; class SPU { public: using SampleFormat = s16; SPU(); ~SPU(); bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller); void Reset(); bool DoState(StateWrapper& sw); u16 ReadRegister(u32 offset); void WriteRegister(u32 offset, u16 value); u32 DMARead(); void DMAWrite(u32 value); void Execute(TickCount ticks); // Render statistics debug window. void DrawDebugWindow(); // Manipulating debug options. void DrawDebugMenu(); private: static constexpr u32 RAM_SIZE = 512 * 1024; static constexpr u32 RAM_MASK = RAM_SIZE - 1; static constexpr u32 SPU_BASE = 0x1F801C00; static constexpr u32 NUM_VOICES = 24; static constexpr u32 NUM_VOICE_REGISTERS = 8; static constexpr u32 VOICE_ADDRESS_SHIFT = 3; static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28; static constexpr u32 SAMPLE_RATE = 44100; static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = MASTER_CLOCK / SAMPLE_RATE; // 0x300 enum class RAMTransferMode : u8 { Stopped = 0, ManualWrite = 1, DMAWrite = 2, DMARead = 3 }; union SPUCNT { u16 bits; BitField enable; BitField mute; BitField noise_frequency_shift; BitField noise_frequency_step; BitField reverb_master_enable; BitField irq9_enable; BitField ram_transfer_mode; BitField external_audio_reverb; BitField cd_audio_reverb; BitField external_audio_enable; BitField cd_audio_enable; BitField mode; }; union SPUSTAT { u16 bits; BitField second_half_capture_buffer; BitField transfer_busy; BitField dma_read_request; BitField dma_write_request; BitField dma_read_write_request; BitField irq9_flag; BitField mode; }; union ADSRRegister { u32 bits; struct { u16 bits_low; u16 bits_high; }; BitField sustain_level; BitField decay_shift; BitField attack_step; BitField attack_shift; BitField attack_exponential; BitField release_shift; BitField release_exponential; BitField sustain_step; BitField sustain_shift; BitField sustain_direction_decrease; BitField sustain_exponential; }; union VolumeRegister { u16 bits; BitField sweep_mode; BitField fixed_volume; // divided by 2 BitField sweep_exponential; BitField sweep_direction_decrease; BitField sweep_phase_negative; BitField sweep_shift; BitField sweep_step; s16 GetVolume() { return fixed_volume * 2; } }; // organized so we can replace this with a u16 array in the future union VoiceRegisters { u16 index[NUM_VOICE_REGISTERS]; struct { VolumeRegister volume_left; VolumeRegister volume_right; u16 adpcm_sample_rate; // VxPitch u16 adpcm_start_address; // multiply by 8 ADSRRegister adsr; u16 adsr_volume; u16 adpcm_repeat_address; // multiply by 8 }; }; union VoiceCounter { // promoted to u32 because of overflow u32 bits; BitField interpolation_index; BitField sample_index; }; struct ADPCMBlock { union { u8 bits; BitField shift; BitField filter; } shift_filter; union { u8 bits; BitField loop_end; BitField loop_repeat; BitField loop_start; } flags; u8 data[NUM_SAMPLES_PER_ADPCM_BLOCK / 2]; // For both 4bit and 8bit ADPCM, reserved shift values 13..15 will act same as shift=9). u8 GetShift() const { const u8 shift = shift_filter.shift; return (shift > 12) ? 9 : shift; } u8 GetFilter() const { return std::min(shift_filter.filter, 4); } u8 GetNibble(u32 index) const { return (data[index / 2] >> ((index % 2) * 4)) & 0x0F; } }; struct Voice { u16 current_address; VoiceRegisters regs; VoiceCounter counter; ADPCMBlock current_block; // TODO Drop this after decoding std::array current_block_samples; std::array previous_block_last_samples; std::array adpcm_state; bool has_samples; bool key_on; void KeyOn(); void KeyOff(); void DecodeBlock(); SampleFormat SampleBlock(s32 index) const; s16 Interpolate() const; }; u16 ReadVoiceRegister(u32 offset); void WriteVoiceRegister(u32 offset, u16 value); void UpdateDMARequest(); u16 RAMTransferRead(); void RAMTransferWrite(u16 value); void ReadADPCMBlock(u16 address, ADPCMBlock* block); static void DecodeADPCMBlock(const ADPCMBlock& block, SampleFormat* out_samples, s32* state); std::tuple SampleVoice(u32 voice_index); void GenerateSample(); System* m_system = nullptr; DMA* m_dma = nullptr; InterruptController* m_interrupt_controller = nullptr; AudioStream* m_audio_stream = nullptr; bool m_debug_window_open = true; SPUCNT m_SPUCNT = {}; SPUSTAT m_SPUSTAT = {}; u16 m_transfer_address_reg = 0; u32 m_transfer_address = 0; u16 m_irq_address = 0; VolumeRegister m_main_volume_left = {}; VolumeRegister m_main_volume_right = {}; u32 m_key_on_register = 0; u32 m_key_off_register = 0; u32 m_endx_register = 0; u32 m_reverb_on_register = 0; TickCount m_ticks_carry = 0; std::array m_voices{}; std::array m_ram{}; };