mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-17 22:25:37 +00:00
SPU: Convert to namespace
This commit is contained in:
parent
6c69cf33c2
commit
5b2c18e27c
|
@ -1267,22 +1267,22 @@ ALWAYS_INLINE static TickCount DoAccessSPU(u32 offset, u32& value)
|
|||
case MemoryAccessSize::Word:
|
||||
{
|
||||
// 32-bit reads are read as two 16-bit accesses.
|
||||
const u16 lsb = g_spu.ReadRegister(offset);
|
||||
const u16 msb = g_spu.ReadRegister(offset + 2);
|
||||
const u16 lsb = SPU::ReadRegister(offset);
|
||||
const u16 msb = SPU::ReadRegister(offset + 2);
|
||||
value = ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
{
|
||||
value = ZeroExtend32(g_spu.ReadRegister(offset));
|
||||
value = ZeroExtend32(SPU::ReadRegister(offset));
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::Byte:
|
||||
default:
|
||||
{
|
||||
const u16 value16 = g_spu.ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
|
||||
const u16 value16 = SPU::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
|
||||
value = FIXUP_HALFWORD_READ_VALUE(size, offset, value16);
|
||||
}
|
||||
break;
|
||||
|
@ -1299,21 +1299,21 @@ ALWAYS_INLINE static TickCount DoAccessSPU(u32 offset, u32& value)
|
|||
case MemoryAccessSize::Word:
|
||||
{
|
||||
DebugAssert(Common::IsAlignedPow2(offset, 2));
|
||||
g_spu.WriteRegister(offset, Truncate16(value));
|
||||
g_spu.WriteRegister(offset + 2, Truncate16(value >> 16));
|
||||
SPU::WriteRegister(offset, Truncate16(value));
|
||||
SPU::WriteRegister(offset + 2, Truncate16(value >> 16));
|
||||
break;
|
||||
}
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
{
|
||||
DebugAssert(Common::IsAlignedPow2(offset, 2));
|
||||
g_spu.WriteRegister(offset, Truncate16(value));
|
||||
SPU::WriteRegister(offset, Truncate16(value));
|
||||
break;
|
||||
}
|
||||
|
||||
case MemoryAccessSize::Byte:
|
||||
{
|
||||
g_spu.WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset),
|
||||
SPU::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset),
|
||||
Truncate16(FIXUP_HALFWORD_READ_VALUE(size, offset, value)));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -641,7 +641,7 @@ void CDROM::WriteRegister(u32 offset, u8 value)
|
|||
{
|
||||
if (HasPendingDiscEvent())
|
||||
m_drive_event->InvokeEarly();
|
||||
g_spu.GeneratePendingSamples();
|
||||
SPU::GeneratePendingSamples();
|
||||
}
|
||||
|
||||
m_adpcm_muted = adpcm_muted;
|
||||
|
@ -2583,7 +2583,7 @@ void CDROM::ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannel
|
|||
if (m_muted || m_adpcm_muted || g_settings.cdrom_mute_cd_audio)
|
||||
return;
|
||||
|
||||
g_spu.GeneratePendingSamples();
|
||||
SPU::GeneratePendingSamples();
|
||||
|
||||
if (m_last_sector_subheader.codinginfo.IsStereo())
|
||||
{
|
||||
|
@ -2689,7 +2689,7 @@ void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ&
|
|||
if (m_muted || g_settings.cdrom_mute_cd_audio)
|
||||
return;
|
||||
|
||||
g_spu.GeneratePendingSamples();
|
||||
SPU::GeneratePendingSamples();
|
||||
|
||||
constexpr bool is_stereo = true;
|
||||
constexpr u32 num_samples = CDImage::RAW_SECTOR_SIZE / sizeof(s16) / (is_stereo ? 2 : 1);
|
||||
|
|
|
@ -530,7 +530,7 @@ TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 incremen
|
|||
break;
|
||||
|
||||
case Channel::SPU:
|
||||
g_spu.DMAWrite(src_pointer, word_count);
|
||||
SPU::DMAWrite(src_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::MDECin:
|
||||
|
@ -591,7 +591,7 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
|||
break;
|
||||
|
||||
case Channel::SPU:
|
||||
g_spu.DMARead(dest_pointer, word_count);
|
||||
SPU::DMARead(dest_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::MDECout:
|
||||
|
|
1260
src/core/spu.cpp
1260
src/core/spu.cpp
File diff suppressed because it is too large
Load diff
479
src/core/spu.h
479
src/core/spu.h
|
@ -6,445 +6,56 @@
|
|||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
// Enable to dump all voices of the SPU audio individually.
|
||||
// #define SPU_DUMP_ALL_VOICES 1
|
||||
|
||||
class StateWrapper;
|
||||
|
||||
class AudioStream;
|
||||
|
||||
namespace Common {
|
||||
class WAVWriter;
|
||||
}
|
||||
namespace SPU {
|
||||
|
||||
class TimingEvent;
|
||||
|
||||
class SPU
|
||||
enum : u32
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
RAM_SIZE = 512 * 1024,
|
||||
RAM_MASK = RAM_SIZE - 1,
|
||||
SAMPLE_RATE = 44100,
|
||||
};
|
||||
|
||||
SPU();
|
||||
~SPU();
|
||||
|
||||
void Initialize();
|
||||
void CPUClockChanged();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
u16 ReadRegister(u32 offset);
|
||||
void WriteRegister(u32 offset, u16 value);
|
||||
|
||||
void DMARead(u32* words, u32 word_count);
|
||||
void DMAWrite(const u32* words, u32 word_count);
|
||||
|
||||
// Render statistics debug window.
|
||||
void DrawDebugStateWindow();
|
||||
|
||||
// Executes the SPU, generating any pending samples.
|
||||
void GeneratePendingSamples();
|
||||
|
||||
/// Returns true if currently dumping audio.
|
||||
ALWAYS_INLINE bool IsDumpingAudio() const { return static_cast<bool>(m_dump_writer); }
|
||||
|
||||
/// Starts dumping audio to file.
|
||||
bool StartDumpingAudio(const char* filename);
|
||||
|
||||
/// Stops dumping audio to file, if started.
|
||||
bool StopDumpingAudio();
|
||||
|
||||
/// Access to SPU RAM.
|
||||
const std::array<u8, RAM_SIZE>& GetRAM() const { return m_ram; }
|
||||
std::array<u8, RAM_SIZE>& GetRAM() { return m_ram; }
|
||||
|
||||
/// Change output stream - used for runahead.
|
||||
// TODO: Make it use system "running ahead" flag
|
||||
ALWAYS_INLINE bool IsAudioOutputMuted() const { return m_audio_output_muted; }
|
||||
void SetAudioOutputMuted(bool muted) { m_audio_output_muted = muted; }
|
||||
|
||||
ALWAYS_INLINE AudioStream* GetOutputStream() const { return m_audio_stream.get(); }
|
||||
void RecreateOutputStream();
|
||||
|
||||
private:
|
||||
static constexpr u32 SPU_BASE = 0x1F801C00;
|
||||
static constexpr u32 NUM_CHANNELS = 2;
|
||||
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 NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK = 3;
|
||||
static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = System::MASTER_CLOCK / SAMPLE_RATE; // 0x300
|
||||
static constexpr s16 ENVELOPE_MIN_VOLUME = 0;
|
||||
static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF;
|
||||
static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400;
|
||||
static constexpr u32 MINIMUM_TICKS_BETWEEN_KEY_ON_OFF = 2;
|
||||
static constexpr u32 NUM_REVERB_REGS = 32;
|
||||
static constexpr u32 FIFO_SIZE_IN_HALFWORDS = 32;
|
||||
static constexpr TickCount TRANSFER_TICKS_PER_HALFWORD = 16;
|
||||
|
||||
enum class RAMTransferMode : u8
|
||||
{
|
||||
Stopped = 0,
|
||||
ManualWrite = 1,
|
||||
DMAWrite = 2,
|
||||
DMARead = 3
|
||||
};
|
||||
|
||||
union SPUCNT
|
||||
{
|
||||
u16 bits;
|
||||
|
||||
BitField<u16, bool, 15, 1> enable;
|
||||
BitField<u16, bool, 14, 1> mute_n;
|
||||
BitField<u16, u8, 8, 6> noise_clock;
|
||||
BitField<u16, bool, 7, 1> reverb_master_enable;
|
||||
BitField<u16, bool, 6, 1> irq9_enable;
|
||||
BitField<u16, RAMTransferMode, 4, 2> ram_transfer_mode;
|
||||
BitField<u16, bool, 3, 1> external_audio_reverb;
|
||||
BitField<u16, bool, 2, 1> cd_audio_reverb;
|
||||
BitField<u16, bool, 1, 1> external_audio_enable;
|
||||
BitField<u16, bool, 0, 1> cd_audio_enable;
|
||||
|
||||
BitField<u16, u8, 0, 6> mode;
|
||||
};
|
||||
|
||||
union SPUSTAT
|
||||
{
|
||||
u16 bits;
|
||||
|
||||
BitField<u16, bool, 11, 1> second_half_capture_buffer;
|
||||
BitField<u16, bool, 10, 1> transfer_busy;
|
||||
BitField<u16, bool, 9, 1> dma_write_request;
|
||||
BitField<u16, bool, 8, 1> dma_read_request;
|
||||
BitField<u16, bool, 7, 1> dma_request;
|
||||
BitField<u16, bool, 6, 1> irq9_flag;
|
||||
BitField<u16, u8, 0, 6> mode;
|
||||
};
|
||||
|
||||
union TransferControl
|
||||
{
|
||||
u16 bits;
|
||||
|
||||
BitField<u8, u8, 1, 3> mode;
|
||||
};
|
||||
|
||||
union ADSRRegister
|
||||
{
|
||||
u32 bits;
|
||||
struct
|
||||
{
|
||||
u16 bits_low;
|
||||
u16 bits_high;
|
||||
};
|
||||
|
||||
BitField<u32, u8, 0, 4> sustain_level;
|
||||
BitField<u32, u8, 4, 4> decay_rate_shr2;
|
||||
BitField<u32, u8, 8, 7> attack_rate;
|
||||
BitField<u32, bool, 15, 1> attack_exponential;
|
||||
|
||||
BitField<u32, u8, 16, 5> release_rate_shr2;
|
||||
BitField<u32, bool, 21, 1> release_exponential;
|
||||
BitField<u32, u8, 22, 7> sustain_rate;
|
||||
BitField<u32, bool, 30, 1> sustain_direction_decrease;
|
||||
BitField<u32, bool, 31, 1> sustain_exponential;
|
||||
};
|
||||
|
||||
union VolumeRegister
|
||||
{
|
||||
u16 bits;
|
||||
|
||||
BitField<u16, bool, 15, 1> sweep_mode;
|
||||
BitField<u16, s16, 0, 15> fixed_volume_shr1; // divided by 2
|
||||
|
||||
BitField<u16, bool, 14, 1> sweep_exponential;
|
||||
BitField<u16, bool, 13, 1> sweep_direction_decrease;
|
||||
BitField<u16, bool, 12, 1> sweep_phase_negative;
|
||||
BitField<u16, u8, 0, 7> sweep_rate;
|
||||
};
|
||||
|
||||
// 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;
|
||||
s16 adsr_volume;
|
||||
|
||||
u16 adpcm_repeat_address; // multiply by 8
|
||||
};
|
||||
};
|
||||
|
||||
union VoiceCounter
|
||||
{
|
||||
// promoted to u32 because of overflow
|
||||
u32 bits;
|
||||
|
||||
BitField<u32, u8, 4, 8> interpolation_index;
|
||||
BitField<u32, u8, 12, 5> sample_index;
|
||||
};
|
||||
|
||||
union ADPCMFlags
|
||||
{
|
||||
u8 bits;
|
||||
|
||||
BitField<u8, bool, 0, 1> loop_end;
|
||||
BitField<u8, bool, 1, 1> loop_repeat;
|
||||
BitField<u8, bool, 2, 1> loop_start;
|
||||
};
|
||||
|
||||
struct ADPCMBlock
|
||||
{
|
||||
union
|
||||
{
|
||||
u8 bits;
|
||||
|
||||
BitField<u8, u8, 0, 4> shift;
|
||||
BitField<u8, u8, 4, 3> filter;
|
||||
} shift_filter;
|
||||
ADPCMFlags 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<u8>(shift_filter.filter, 4); }
|
||||
|
||||
u8 GetNibble(u32 index) const { return (data[index / 2] >> ((index % 2) * 4)) & 0x0F; }
|
||||
};
|
||||
|
||||
struct VolumeEnvelope
|
||||
{
|
||||
s32 counter;
|
||||
u8 rate;
|
||||
bool decreasing;
|
||||
bool exponential;
|
||||
|
||||
void Reset(u8 rate_, bool decreasing_, bool exponential_);
|
||||
s16 Tick(s16 current_level);
|
||||
};
|
||||
|
||||
struct VolumeSweep
|
||||
{
|
||||
VolumeEnvelope envelope;
|
||||
bool envelope_active;
|
||||
s16 current_level;
|
||||
|
||||
void Reset(VolumeRegister reg);
|
||||
void Tick();
|
||||
};
|
||||
|
||||
enum class ADSRPhase : u8
|
||||
{
|
||||
Off = 0,
|
||||
Attack = 1,
|
||||
Decay = 2,
|
||||
Sustain = 3,
|
||||
Release = 4
|
||||
};
|
||||
|
||||
struct Voice
|
||||
{
|
||||
u16 current_address;
|
||||
VoiceRegisters regs;
|
||||
VoiceCounter counter;
|
||||
ADPCMFlags current_block_flags;
|
||||
bool is_first_block;
|
||||
std::array<s16, NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK + NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples;
|
||||
std::array<s16, 2> adpcm_last_samples;
|
||||
s32 last_volume;
|
||||
|
||||
VolumeSweep left_volume;
|
||||
VolumeSweep right_volume;
|
||||
|
||||
VolumeEnvelope adsr_envelope;
|
||||
ADSRPhase adsr_phase;
|
||||
s16 adsr_target;
|
||||
bool has_samples;
|
||||
bool ignore_loop_address;
|
||||
|
||||
bool IsOn() const { return adsr_phase != ADSRPhase::Off; }
|
||||
|
||||
void KeyOn();
|
||||
void KeyOff();
|
||||
void ForceOff();
|
||||
|
||||
void DecodeBlock(const ADPCMBlock& block);
|
||||
s32 Interpolate() const;
|
||||
|
||||
// Switches to the specified phase, filling in target.
|
||||
void UpdateADSREnvelope();
|
||||
|
||||
// Updates the ADSR volume/phase.
|
||||
void TickADSR();
|
||||
};
|
||||
|
||||
struct ReverbRegisters
|
||||
{
|
||||
s16 vLOUT;
|
||||
s16 vROUT;
|
||||
u16 mBASE;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u16 FB_SRC_A;
|
||||
u16 FB_SRC_B;
|
||||
s16 IIR_ALPHA;
|
||||
s16 ACC_COEF_A;
|
||||
s16 ACC_COEF_B;
|
||||
s16 ACC_COEF_C;
|
||||
s16 ACC_COEF_D;
|
||||
s16 IIR_COEF;
|
||||
s16 FB_ALPHA;
|
||||
s16 FB_X;
|
||||
u16 IIR_DEST_A[2];
|
||||
u16 ACC_SRC_A[2];
|
||||
u16 ACC_SRC_B[2];
|
||||
u16 IIR_SRC_A[2];
|
||||
u16 IIR_DEST_B[2];
|
||||
u16 ACC_SRC_C[2];
|
||||
u16 ACC_SRC_D[2];
|
||||
u16 IIR_SRC_B[2];
|
||||
u16 MIX_DEST_A[2];
|
||||
u16 MIX_DEST_B[2];
|
||||
s16 IN_COEF[2];
|
||||
};
|
||||
|
||||
u16 rev[NUM_REVERB_REGS];
|
||||
};
|
||||
};
|
||||
|
||||
static constexpr s32 Clamp16(s32 value) { return (value < -0x8000) ? -0x8000 : (value > 0x7FFF) ? 0x7FFF : value; }
|
||||
|
||||
static constexpr s32 ApplyVolume(s32 sample, s16 volume) { return (sample * s32(volume)) >> 15; }
|
||||
|
||||
static ADSRPhase GetNextADSRPhase(ADSRPhase phase);
|
||||
|
||||
ALWAYS_INLINE bool IsVoiceReverbEnabled(u32 i) const
|
||||
{
|
||||
return ConvertToBoolUnchecked((m_reverb_on_register >> i) & u32(1));
|
||||
}
|
||||
ALWAYS_INLINE bool IsVoiceNoiseEnabled(u32 i) const
|
||||
{
|
||||
return ConvertToBoolUnchecked((m_noise_mode_register >> i) & u32(1));
|
||||
}
|
||||
ALWAYS_INLINE bool IsPitchModulationEnabled(u32 i) const
|
||||
{
|
||||
return ((i > 0) && ConvertToBoolUnchecked((m_pitch_modulation_enable_register >> i) & u32(1)));
|
||||
}
|
||||
ALWAYS_INLINE s16 GetVoiceNoiseLevel() const { return static_cast<s16>(static_cast<u16>(m_noise_level)); }
|
||||
|
||||
u16 ReadVoiceRegister(u32 offset);
|
||||
void WriteVoiceRegister(u32 offset, u16 value);
|
||||
|
||||
ALWAYS_INLINE bool IsRAMIRQTriggerable() const { return m_SPUCNT.irq9_enable && !m_SPUSTAT.irq9_flag; }
|
||||
ALWAYS_INLINE bool CheckRAMIRQ(u32 address) const { return ((ZeroExtend32(m_irq_address) * 8) == address); }
|
||||
void TriggerRAMIRQ();
|
||||
void CheckForLateRAMIRQs();
|
||||
|
||||
void WriteToCaptureBuffer(u32 index, s16 value);
|
||||
void IncrementCaptureBufferPosition();
|
||||
|
||||
void ReadADPCMBlock(u16 address, ADPCMBlock* block);
|
||||
std::tuple<s32, s32> SampleVoice(u32 voice_index);
|
||||
|
||||
void UpdateNoise();
|
||||
|
||||
u32 ReverbMemoryAddress(u32 address) const;
|
||||
s16 ReverbRead(u32 address, s32 offset = 0);
|
||||
void ReverbWrite(u32 address, s16 data);
|
||||
void ProcessReverb(s16 left_in, s16 right_in, s32* left_out, s32* right_out);
|
||||
|
||||
void Execute(TickCount ticks);
|
||||
void UpdateEventInterval();
|
||||
|
||||
void ExecuteFIFOWriteToRAM(TickCount& ticks);
|
||||
void ExecuteFIFOReadFromRAM(TickCount& ticks);
|
||||
void ExecuteTransfer(TickCount ticks);
|
||||
void ManualTransferWrite(u16 value);
|
||||
void UpdateTransferEvent();
|
||||
void UpdateDMARequest();
|
||||
|
||||
void CreateOutputStream();
|
||||
|
||||
std::unique_ptr<TimingEvent> m_tick_event;
|
||||
std::unique_ptr<TimingEvent> m_transfer_event;
|
||||
std::unique_ptr<Common::WAVWriter> m_dump_writer;
|
||||
std::unique_ptr<AudioStream> m_audio_stream;
|
||||
std::unique_ptr<AudioStream> m_null_audio_stream;
|
||||
bool m_audio_output_muted = false;
|
||||
|
||||
TickCount m_ticks_carry = 0;
|
||||
TickCount m_cpu_ticks_per_spu_tick = 0;
|
||||
TickCount m_cpu_tick_divider = 0;
|
||||
|
||||
SPUCNT m_SPUCNT = {};
|
||||
SPUSTAT m_SPUSTAT = {};
|
||||
|
||||
TransferControl m_transfer_control = {};
|
||||
u16 m_transfer_address_reg = 0;
|
||||
u32 m_transfer_address = 0;
|
||||
|
||||
u16 m_irq_address = 0;
|
||||
u16 m_capture_buffer_position = 0;
|
||||
|
||||
VolumeRegister m_main_volume_left_reg = {};
|
||||
VolumeRegister m_main_volume_right_reg = {};
|
||||
VolumeSweep m_main_volume_left = {};
|
||||
VolumeSweep m_main_volume_right = {};
|
||||
|
||||
s16 m_cd_audio_volume_left = 0;
|
||||
s16 m_cd_audio_volume_right = 0;
|
||||
|
||||
s16 m_external_volume_left = 0;
|
||||
s16 m_external_volume_right = 0;
|
||||
|
||||
u32 m_key_on_register = 0;
|
||||
u32 m_key_off_register = 0;
|
||||
u32 m_endx_register = 0;
|
||||
u32 m_pitch_modulation_enable_register = 0;
|
||||
|
||||
u32 m_noise_mode_register = 0;
|
||||
u32 m_noise_count = 0;
|
||||
u32 m_noise_level = 0;
|
||||
|
||||
u32 m_reverb_on_register = 0;
|
||||
u32 m_reverb_base_address = 0;
|
||||
u32 m_reverb_current_address = 0;
|
||||
ReverbRegisters m_reverb_registers{};
|
||||
std::array<std::array<s16, 128>, 2> m_reverb_downsample_buffer;
|
||||
std::array<std::array<s16, 64>, 2> m_reverb_upsample_buffer;
|
||||
s32 m_reverb_resample_buffer_position = 0;
|
||||
|
||||
std::array<Voice, NUM_VOICES> m_voices{};
|
||||
|
||||
InlineFIFOQueue<u16, FIFO_SIZE_IN_HALFWORDS> m_transfer_fifo;
|
||||
|
||||
std::array<u8, RAM_SIZE> m_ram{};
|
||||
|
||||
#ifdef SPU_DUMP_ALL_VOICES
|
||||
// +1 for reverb output
|
||||
std::array<std::unique_ptr<Common::WAVWriter>, NUM_VOICES + 1> m_voice_dump_writers;
|
||||
#endif
|
||||
RAM_SIZE = 512 * 1024,
|
||||
RAM_MASK = RAM_SIZE - 1,
|
||||
SAMPLE_RATE = 44100,
|
||||
};
|
||||
|
||||
extern SPU g_spu;
|
||||
void Initialize();
|
||||
void CPUClockChanged();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
u16 ReadRegister(u32 offset);
|
||||
void WriteRegister(u32 offset, u16 value);
|
||||
|
||||
void DMARead(u32* words, u32 word_count);
|
||||
void DMAWrite(const u32* words, u32 word_count);
|
||||
|
||||
// Render statistics debug window.
|
||||
void DrawDebugStateWindow();
|
||||
|
||||
// Executes the SPU, generating any pending samples.
|
||||
void GeneratePendingSamples();
|
||||
|
||||
/// Returns true if currently dumping audio.
|
||||
bool IsDumpingAudio();
|
||||
|
||||
/// Starts dumping audio to file.
|
||||
bool StartDumpingAudio(const char* filename);
|
||||
|
||||
/// Stops dumping audio to file, if started.
|
||||
bool StopDumpingAudio();
|
||||
|
||||
/// Access to SPU RAM.
|
||||
const std::array<u8, RAM_SIZE>& GetRAM();
|
||||
std::array<u8, RAM_SIZE>& GetWritableRAM();
|
||||
|
||||
/// Change output stream - used for runahead.
|
||||
// TODO: Make it use system "running ahead" flag
|
||||
bool IsAudioOutputMuted();
|
||||
void SetAudioOutputMuted(bool muted);
|
||||
|
||||
AudioStream* GetOutputStream();
|
||||
void RecreateOutputStream();
|
||||
|
||||
}; // namespace SPU
|
||||
|
|
|
@ -270,7 +270,7 @@ void System::UpdateOverclock()
|
|||
{
|
||||
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
|
||||
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
|
||||
g_spu.CPUClockChanged();
|
||||
SPU::CPUClockChanged();
|
||||
g_cdrom.CPUClockChanged();
|
||||
g_gpu->CPUClockChanged();
|
||||
g_timers.CPUClocksChanged();
|
||||
|
@ -934,7 +934,7 @@ void System::PauseSystem(bool paused)
|
|||
return;
|
||||
|
||||
SetState(paused ? State::Paused : State::Running);
|
||||
g_spu.GetOutputStream()->SetPaused(paused);
|
||||
SPU::GetOutputStream()->SetPaused(paused);
|
||||
|
||||
if (paused)
|
||||
{
|
||||
|
@ -1216,7 +1216,7 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
s_state =
|
||||
(g_settings.start_paused || parameters.override_start_paused.value_or(false)) ? State::Paused : State::Running;
|
||||
UpdateSoftwareCursor();
|
||||
g_spu.GetOutputStream()->SetPaused(false);
|
||||
SPU::GetOutputStream()->SetPaused(false);
|
||||
Host::OnSystemStarted();
|
||||
|
||||
if (s_state == State::Paused)
|
||||
|
@ -1332,7 +1332,7 @@ bool System::Initialize(bool force_software_renderer)
|
|||
g_cdrom.Initialize();
|
||||
g_pad.Initialize();
|
||||
g_timers.Initialize();
|
||||
g_spu.Initialize();
|
||||
SPU::Initialize();
|
||||
g_mdec.Initialize();
|
||||
g_sio.Initialize();
|
||||
|
||||
|
@ -1392,7 +1392,7 @@ void System::DestroySystem()
|
|||
|
||||
g_sio.Shutdown();
|
||||
g_mdec.Shutdown();
|
||||
g_spu.Shutdown();
|
||||
SPU::Shutdown();
|
||||
g_timers.Shutdown();
|
||||
g_pad.Shutdown();
|
||||
g_cdrom.Shutdown();
|
||||
|
@ -1594,7 +1594,7 @@ bool System::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool u
|
|||
if (!sw.DoMarker("Timers") || !g_timers.DoState(sw))
|
||||
return false;
|
||||
|
||||
if (!sw.DoMarker("SPU") || !g_spu.DoState(sw))
|
||||
if (!sw.DoMarker("SPU") || !SPU::DoState(sw))
|
||||
return false;
|
||||
|
||||
if (!sw.DoMarker("MDEC") || !g_mdec.DoState(sw))
|
||||
|
@ -1678,7 +1678,7 @@ void System::InternalReset()
|
|||
g_cdrom.Reset();
|
||||
g_pad.Reset();
|
||||
g_timers.Reset();
|
||||
g_spu.Reset();
|
||||
SPU::Reset();
|
||||
g_mdec.Reset();
|
||||
g_sio.Reset();
|
||||
s_frame_number = 1;
|
||||
|
@ -1858,7 +1858,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
|
|||
if (s_state == State::Starting)
|
||||
s_state = State::Running;
|
||||
|
||||
g_spu.GetOutputStream()->EmptyBuffer();
|
||||
SPU::GetOutputStream()->EmptyBuffer();
|
||||
ResetPerformanceCounters();
|
||||
ResetThrottler();
|
||||
return true;
|
||||
|
@ -1977,7 +1977,7 @@ void System::SingleStepCPU()
|
|||
|
||||
CPU::SingleStep();
|
||||
|
||||
g_spu.GeneratePendingSamples();
|
||||
SPU::GeneratePendingSamples();
|
||||
|
||||
if (s_frame_number != old_frame_number && s_cheat_list)
|
||||
s_cheat_list->Apply();
|
||||
|
@ -2017,7 +2017,7 @@ void System::DoRunFrame()
|
|||
}
|
||||
|
||||
// Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns.
|
||||
g_spu.GeneratePendingSamples();
|
||||
SPU::GeneratePendingSamples();
|
||||
|
||||
if (s_cheat_list)
|
||||
s_cheat_list->Apply();
|
||||
|
@ -2218,7 +2218,7 @@ void System::UpdateSpeedLimiterState()
|
|||
|
||||
if (IsValid())
|
||||
{
|
||||
AudioStream* stream = g_spu.GetOutputStream();
|
||||
AudioStream* stream = SPU::GetOutputStream();
|
||||
if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume)
|
||||
stream->SetOutputVolume(GetAudioOutputVolume());
|
||||
|
||||
|
@ -2779,7 +2779,7 @@ bool System::DumpSPURAM(const char* filename)
|
|||
if (!IsValid())
|
||||
return false;
|
||||
|
||||
return FileSystem::WriteBinaryFile(filename, g_spu.GetRAM().data(), SPU::RAM_SIZE);
|
||||
return FileSystem::WriteBinaryFile(filename, SPU::GetRAM().data(), SPU::RAM_SIZE);
|
||||
}
|
||||
|
||||
bool System::HasMedia()
|
||||
|
@ -3060,15 +3060,15 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||
Settings::GetAudioBackendName(g_settings.audio_backend));
|
||||
}
|
||||
|
||||
g_spu.RecreateOutputStream();
|
||||
SPU::RecreateOutputStream();
|
||||
}
|
||||
if (g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
|
||||
g_spu.GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode);
|
||||
SPU::GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode);
|
||||
if (g_settings.audio_buffer_ms != old_settings.audio_buffer_ms ||
|
||||
g_settings.audio_output_latency_ms != old_settings.audio_output_latency_ms ||
|
||||
g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
|
||||
{
|
||||
g_spu.RecreateOutputStream();
|
||||
SPU::RecreateOutputStream();
|
||||
UpdateSpeedLimiterState();
|
||||
}
|
||||
|
||||
|
@ -3099,7 +3099,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||
CPU::ClearICache();
|
||||
}
|
||||
|
||||
g_spu.GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
|
||||
SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
|
||||
|
||||
if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale ||
|
||||
g_settings.gpu_multisamples != old_settings.gpu_multisamples ||
|
||||
|
@ -3480,7 +3480,7 @@ void System::DoRunahead()
|
|||
const s32 temp = frames_to_run;
|
||||
#endif
|
||||
|
||||
g_spu.SetAudioOutputMuted(true);
|
||||
SPU::SetAudioOutputMuted(true);
|
||||
|
||||
while (frames_to_run > 0)
|
||||
{
|
||||
|
@ -3489,7 +3489,7 @@ void System::DoRunahead()
|
|||
frames_to_run--;
|
||||
}
|
||||
|
||||
g_spu.SetAudioOutputMuted(false);
|
||||
SPU::SetAudioOutputMuted(false);
|
||||
|
||||
#ifdef PROFILE_MEMORY_SAVE_STATES
|
||||
Log_VerbosePrintf("Running %d frames to catch up took %.2f ms", temp, timer2.GetTimeMilliseconds());
|
||||
|
@ -3626,12 +3626,12 @@ void System::UpdateVolume()
|
|||
if (!IsValid())
|
||||
return;
|
||||
|
||||
g_spu.GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
|
||||
SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
|
||||
}
|
||||
|
||||
bool System::IsDumpingAudio()
|
||||
{
|
||||
return g_spu.IsDumpingAudio();
|
||||
return SPU::IsDumpingAudio();
|
||||
}
|
||||
|
||||
bool System::StartDumpingAudio(const char* filename)
|
||||
|
@ -3657,7 +3657,7 @@ bool System::StartDumpingAudio(const char* filename)
|
|||
filename = auto_filename.c_str();
|
||||
}
|
||||
|
||||
if (g_spu.StartDumpingAudio(filename))
|
||||
if (SPU::StartDumpingAudio(filename))
|
||||
{
|
||||
Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Started dumping audio to '%s'."), filename);
|
||||
return true;
|
||||
|
@ -3672,7 +3672,7 @@ bool System::StartDumpingAudio(const char* filename)
|
|||
|
||||
void System::StopDumpingAudio()
|
||||
{
|
||||
if (System::IsShutdown() || !g_spu.StopDumpingAudio())
|
||||
if (System::IsShutdown() || !SPU::StopDumpingAudio())
|
||||
return;
|
||||
|
||||
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Stopped dumping audio."), 5.0f);
|
||||
|
|
|
@ -405,7 +405,7 @@ void ImGuiManager::RenderDebugWindows()
|
|||
if (g_settings.debugging.show_timers_state)
|
||||
g_timers.DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_spu_state)
|
||||
g_spu.DrawDebugStateWindow();
|
||||
SPU::DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_mdec_state)
|
||||
g_mdec.DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_dma_state)
|
||||
|
@ -908,7 +908,7 @@ DEFINE_HOTKEY("AudioMute", TRANSLATABLE("Hotkeys", "Audio"), TRANSLATABLE("Hotke
|
|||
{
|
||||
g_settings.audio_output_muted = !g_settings.audio_output_muted;
|
||||
const s32 volume = System::GetAudioOutputVolume();
|
||||
g_spu.GetOutputStream()->SetOutputVolume(volume);
|
||||
SPU::GetOutputStream()->SetOutputVolume(volume);
|
||||
if (g_settings.audio_output_muted)
|
||||
{
|
||||
Host::AddIconOSDMessage("AudioControlHotkey", ICON_FA_VOLUME_MUTE,
|
||||
|
@ -942,7 +942,7 @@ DEFINE_HOTKEY("AudioVolumeUp", TRANSLATABLE("Hotkeys", "Audio"), TRANSLATABLE("H
|
|||
const s32 volume = std::min<s32>(System::GetAudioOutputVolume() + 10, 100);
|
||||
g_settings.audio_output_volume = volume;
|
||||
g_settings.audio_fast_forward_volume = volume;
|
||||
g_spu.GetOutputStream()->SetOutputVolume(volume);
|
||||
SPU::GetOutputStream()->SetOutputVolume(volume);
|
||||
Host::AddIconOSDMessage("AudioControlHotkey", ICON_FA_VOLUME_UP,
|
||||
fmt::format(Host::TranslateString("OSDMessage", "Volume: {}%").GetCharArray(), volume),
|
||||
5.0f);
|
||||
|
@ -957,7 +957,7 @@ DEFINE_HOTKEY("AudioVolumeDown", TRANSLATABLE("Hotkeys", "Audio"), TRANSLATABLE(
|
|||
const s32 volume = std::max<s32>(System::GetAudioOutputVolume() - 10, 0);
|
||||
g_settings.audio_output_volume = volume;
|
||||
g_settings.audio_fast_forward_volume = volume;
|
||||
g_spu.GetOutputStream()->SetOutputVolume(volume);
|
||||
SPU::GetOutputStream()->SetOutputVolume(volume);
|
||||
Host::AddIconOSDMessage(
|
||||
"AudioControlHotkey", ICON_FA_VOLUME_DOWN,
|
||||
fmt::format(Host::TranslateString("OSDMessage", "Volume: {}%").GetCharArray(), volume), 5.0f);
|
||||
|
|
Loading…
Reference in a new issue