Merge pull request #11633 from AdmiralCurtiss/ai-class
HW/AudioInterface: Refactor to class.
This commit is contained in:
commit
40ff9b25b7
|
@ -70,356 +70,277 @@ enum
|
||||||
AID_48KHz = 0
|
AID_48KHz = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SampleRate
|
AudioInterfaceManager::AudioInterfaceManager(Core::System& system)
|
||||||
{
|
: m_ais_sample_rate_divisor(Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 48000),
|
||||||
AI32KHz,
|
m_aid_sample_rate_divisor(Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 32000), m_system(system)
|
||||||
AI48KHz,
|
|
||||||
};
|
|
||||||
|
|
||||||
// AI Control Register
|
|
||||||
union AICR
|
|
||||||
{
|
|
||||||
AICR() = default;
|
|
||||||
explicit AICR(u32 hex_) : hex{hex_} {}
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u32 PSTAT : 1; // sample counter/playback enable
|
|
||||||
u32 AISFR : 1; // AIS Frequency (0=32khz 1=48khz)
|
|
||||||
u32 AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled
|
|
||||||
u32 AIINT : 1; // audio interrupt status
|
|
||||||
u32 AIINTVLD : 1; // This bit controls whether AIINT is affected by the Interrupt Timing
|
|
||||||
// register
|
|
||||||
// matching the sample counter. Once set, AIINT will hold its last value
|
|
||||||
u32 SCRESET : 1; // write to reset counter
|
|
||||||
u32 AIDFR : 1; // AID Frequency (0=48khz 1=32khz)
|
|
||||||
u32 : 25;
|
|
||||||
};
|
|
||||||
u32 hex = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// AI Volume Register
|
|
||||||
union AIVR
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u32 left : 8;
|
|
||||||
u32 right : 8;
|
|
||||||
u32 : 16;
|
|
||||||
};
|
|
||||||
u32 hex = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AudioInterfaceState::Data
|
|
||||||
{
|
|
||||||
// Registers
|
|
||||||
AICR control;
|
|
||||||
AIVR volume;
|
|
||||||
|
|
||||||
u32 sample_counter = 0;
|
|
||||||
u32 interrupt_timing = 0;
|
|
||||||
|
|
||||||
u64 last_cpu_time = 0;
|
|
||||||
u64 cpu_cycles_per_sample = 0;
|
|
||||||
|
|
||||||
u32 ais_sample_rate_divisor = Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 48000;
|
|
||||||
u32 aid_sample_rate_divisor = Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 32000;
|
|
||||||
|
|
||||||
CoreTiming::EventType* event_type_ai = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
AudioInterfaceState::AudioInterfaceState() : m_data(std::make_unique<Data>())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioInterfaceState::~AudioInterfaceState() = default;
|
AudioInterfaceManager::~AudioInterfaceManager() = default;
|
||||||
|
|
||||||
void DoState(PointerWrap& p)
|
void AudioInterfaceManager::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
p.Do(m_control);
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
p.Do(m_volume);
|
||||||
|
p.Do(m_sample_counter);
|
||||||
|
p.Do(m_interrupt_timing);
|
||||||
|
p.Do(m_last_cpu_time);
|
||||||
|
p.Do(m_ais_sample_rate_divisor);
|
||||||
|
p.Do(m_aid_sample_rate_divisor);
|
||||||
|
p.Do(m_cpu_cycles_per_sample);
|
||||||
|
|
||||||
p.Do(state.control);
|
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||||
p.Do(state.volume);
|
|
||||||
p.Do(state.sample_counter);
|
|
||||||
p.Do(state.interrupt_timing);
|
|
||||||
p.Do(state.last_cpu_time);
|
|
||||||
p.Do(state.ais_sample_rate_divisor);
|
|
||||||
p.Do(state.aid_sample_rate_divisor);
|
|
||||||
p.Do(state.cpu_cycles_per_sample);
|
|
||||||
|
|
||||||
SoundStream* sound_stream = system.GetSoundStream();
|
|
||||||
sound_stream->GetMixer()->DoState(p);
|
sound_stream->GetMixer()->DoState(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
void AudioInterfaceManager::UpdateInterrupts()
|
||||||
{
|
{
|
||||||
void UpdateInterrupts()
|
m_system.GetProcessorInterface().SetInterrupt(ProcessorInterface::INT_CAUSE_AI,
|
||||||
{
|
m_control.AIINT & m_control.AIINTMSK);
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
|
||||||
system.GetProcessorInterface().SetInterrupt(ProcessorInterface::INT_CAUSE_AI,
|
|
||||||
state.control.AIINT & state.control.AIINTMSK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateAudioInterrupt()
|
void AudioInterfaceManager::GenerateAudioInterrupt()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
m_control.AIINT = 1;
|
||||||
state.control.AIINT = 1;
|
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncreaseSampleCount(const u32 amount)
|
void AudioInterfaceManager::IncreaseSampleCount(const u32 amount)
|
||||||
{
|
{
|
||||||
if (!IsPlaying())
|
if (!IsPlaying())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
const u32 old_sample_counter = m_sample_counter + 1;
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
m_sample_counter += amount;
|
||||||
|
|
||||||
const u32 old_sample_counter = state.sample_counter + 1;
|
if ((m_interrupt_timing - old_sample_counter) <= (m_sample_counter - old_sample_counter))
|
||||||
state.sample_counter += amount;
|
|
||||||
|
|
||||||
if ((state.interrupt_timing - old_sample_counter) <= (state.sample_counter - old_sample_counter))
|
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE,
|
DEBUG_LOG_FMT(
|
||||||
"GenerateAudioInterrupt {:08x}:{:08x} at PC {:08x} control.AIINTVLD={}",
|
AUDIO_INTERFACE, "GenerateAudioInterrupt {:08x}:{:08x} at PC {:08x} control.AIINTVLD={}",
|
||||||
state.sample_counter, state.interrupt_timing, system.GetPPCState().pc,
|
m_sample_counter, m_interrupt_timing, m_system.GetPPCState().pc, m_control.AIINTVLD);
|
||||||
state.control.AIINTVLD);
|
|
||||||
GenerateAudioInterrupt();
|
GenerateAudioInterrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetAIPeriod()
|
int AudioInterfaceManager::GetAIPeriod() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
u64 period = m_cpu_cycles_per_sample * (m_interrupt_timing - m_sample_counter);
|
||||||
|
u64 s_period =
|
||||||
u64 period = state.cpu_cycles_per_sample * (state.interrupt_timing - state.sample_counter);
|
m_cpu_cycles_per_sample * Mixer::FIXED_SAMPLE_RATE_DIVIDEND / m_ais_sample_rate_divisor;
|
||||||
u64 s_period = state.cpu_cycles_per_sample * Mixer::FIXED_SAMPLE_RATE_DIVIDEND /
|
|
||||||
state.ais_sample_rate_divisor;
|
|
||||||
if (period == 0)
|
if (period == 0)
|
||||||
return static_cast<int>(s_period);
|
return static_cast<int>(s_period);
|
||||||
return static_cast<int>(std::min(period, s_period));
|
return static_cast<int>(std::min(period, s_period));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Update(Core::System& system, u64 userdata, s64 cycles_late)
|
void AudioInterfaceManager::GlobalUpdate(Core::System& system, u64 userdata, s64 cycles_late)
|
||||||
|
{
|
||||||
|
system.GetAudioInterface().Update(userdata, cycles_late);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInterfaceManager::Update(u64 userdata, s64 cycles_late)
|
||||||
{
|
{
|
||||||
if (!IsPlaying())
|
if (!IsPlaying())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
auto& core_timing = m_system.GetCoreTiming();
|
||||||
auto& core_timing = system.GetCoreTiming();
|
|
||||||
|
|
||||||
const u64 diff = core_timing.GetTicks() - state.last_cpu_time;
|
const u64 diff = core_timing.GetTicks() - m_last_cpu_time;
|
||||||
if (diff > state.cpu_cycles_per_sample)
|
if (diff > m_cpu_cycles_per_sample)
|
||||||
{
|
{
|
||||||
const u32 samples = static_cast<u32>(diff / state.cpu_cycles_per_sample);
|
const u32 samples = static_cast<u32>(diff / m_cpu_cycles_per_sample);
|
||||||
state.last_cpu_time += samples * state.cpu_cycles_per_sample;
|
m_last_cpu_time += samples * m_cpu_cycles_per_sample;
|
||||||
IncreaseSampleCount(samples);
|
IncreaseSampleCount(samples);
|
||||||
}
|
}
|
||||||
core_timing.ScheduleEvent(GetAIPeriod() - cycles_late, state.event_type_ai);
|
core_timing.ScheduleEvent(GetAIPeriod() - cycles_late, m_event_type_ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetAIDSampleRate(SampleRate sample_rate)
|
void AudioInterfaceManager::SetAIDSampleRate(SampleRate sample_rate)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
|
||||||
|
|
||||||
if (sample_rate == SampleRate::AI32KHz)
|
if (sample_rate == SampleRate::AI32KHz)
|
||||||
{
|
{
|
||||||
state.control.AIDFR = AID_32KHz;
|
m_control.AIDFR = AID_32KHz;
|
||||||
state.aid_sample_rate_divisor = Get32KHzSampleRateDivisor();
|
m_aid_sample_rate_divisor = Get32KHzSampleRateDivisor();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state.control.AIDFR = AID_48KHz;
|
m_control.AIDFR = AID_48KHz;
|
||||||
state.aid_sample_rate_divisor = Get48KHzSampleRateDivisor();
|
m_aid_sample_rate_divisor = Get48KHzSampleRateDivisor();
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundStream* sound_stream = Core::System::GetInstance().GetSoundStream();
|
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||||
sound_stream->GetMixer()->SetDMAInputSampleRateDivisor(state.aid_sample_rate_divisor);
|
sound_stream->GetMixer()->SetDMAInputSampleRateDivisor(m_aid_sample_rate_divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetAISSampleRate(SampleRate sample_rate)
|
void AudioInterfaceManager::SetAISSampleRate(SampleRate sample_rate)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
|
||||||
|
|
||||||
if (sample_rate == SampleRate::AI32KHz)
|
if (sample_rate == SampleRate::AI32KHz)
|
||||||
{
|
{
|
||||||
state.control.AISFR = AIS_32KHz;
|
m_control.AISFR = AIS_32KHz;
|
||||||
state.ais_sample_rate_divisor = Get32KHzSampleRateDivisor();
|
m_ais_sample_rate_divisor = Get32KHzSampleRateDivisor();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state.control.AISFR = AIS_48KHz;
|
m_control.AISFR = AIS_48KHz;
|
||||||
state.ais_sample_rate_divisor = Get48KHzSampleRateDivisor();
|
m_ais_sample_rate_divisor = Get48KHzSampleRateDivisor();
|
||||||
}
|
}
|
||||||
|
|
||||||
state.cpu_cycles_per_sample = static_cast<u64>(SystemTimers::GetTicksPerSecond()) *
|
m_cpu_cycles_per_sample = static_cast<u64>(SystemTimers::GetTicksPerSecond()) *
|
||||||
state.ais_sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
|
m_ais_sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
|
||||||
SoundStream* sound_stream = Core::System::GetInstance().GetSoundStream();
|
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||||
sound_stream->GetMixer()->SetStreamInputSampleRateDivisor(state.ais_sample_rate_divisor);
|
sound_stream->GetMixer()->SetStreamInputSampleRateDivisor(m_ais_sample_rate_divisor);
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void Init()
|
void AudioInterfaceManager::Init()
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
m_control.hex = 0;
|
||||||
auto& core_timing = system.GetCoreTiming();
|
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
|
||||||
|
|
||||||
state.control.hex = 0;
|
|
||||||
SetAISSampleRate(SampleRate::AI48KHz);
|
SetAISSampleRate(SampleRate::AI48KHz);
|
||||||
SetAIDSampleRate(SampleRate::AI32KHz);
|
SetAIDSampleRate(SampleRate::AI32KHz);
|
||||||
state.volume.hex = 0;
|
m_volume.hex = 0;
|
||||||
state.sample_counter = 0;
|
m_sample_counter = 0;
|
||||||
state.interrupt_timing = 0;
|
m_interrupt_timing = 0;
|
||||||
|
|
||||||
state.last_cpu_time = 0;
|
m_last_cpu_time = 0;
|
||||||
|
|
||||||
state.event_type_ai = core_timing.RegisterEvent("AICallback", Update);
|
m_event_type_ai = m_system.GetCoreTiming().RegisterEvent("AICallback", GlobalUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void AudioInterfaceManager::Shutdown()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
void AudioInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
|
||||||
|
|
||||||
mmio->Register(
|
mmio->Register(
|
||||||
base | AI_CONTROL_REGISTER, MMIO::DirectRead<u32>(&state.control.hex),
|
base | AI_CONTROL_REGISTER, MMIO::DirectRead<u32>(&m_control.hex),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
const AICR tmp_ai_ctrl(val);
|
const AICR tmp_ai_ctrl(val);
|
||||||
|
|
||||||
auto& core_timing = system.GetCoreTiming();
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state_ = system.GetAudioInterfaceState().GetData();
|
auto& ai = system.GetAudioInterface();
|
||||||
if (state_.control.AIINTMSK != tmp_ai_ctrl.AIINTMSK)
|
if (ai.m_control.AIINTMSK != tmp_ai_ctrl.AIINTMSK)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIINTMSK to {}", tmp_ai_ctrl.AIINTMSK);
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIINTMSK to {}", tmp_ai_ctrl.AIINTMSK);
|
||||||
state_.control.AIINTMSK = tmp_ai_ctrl.AIINTMSK;
|
ai.m_control.AIINTMSK = tmp_ai_ctrl.AIINTMSK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state_.control.AIINTVLD != tmp_ai_ctrl.AIINTVLD)
|
if (ai.m_control.AIINTVLD != tmp_ai_ctrl.AIINTVLD)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIINTVLD to {}", tmp_ai_ctrl.AIINTVLD);
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIINTVLD to {}", tmp_ai_ctrl.AIINTVLD);
|
||||||
state_.control.AIINTVLD = tmp_ai_ctrl.AIINTVLD;
|
ai.m_control.AIINTVLD = tmp_ai_ctrl.AIINTVLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set frequency of streaming audio
|
// Set frequency of streaming audio
|
||||||
if (tmp_ai_ctrl.AISFR != state_.control.AISFR)
|
if (tmp_ai_ctrl.AISFR != ai.m_control.AISFR)
|
||||||
{
|
{
|
||||||
// AISFR rates below are intentionally inverted wrt yagcd
|
// AISFR rates below are intentionally inverted wrt yagcd
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AISFR to {}",
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AISFR to {}",
|
||||||
tmp_ai_ctrl.AISFR ? "48khz" : "32khz");
|
tmp_ai_ctrl.AISFR ? "48khz" : "32khz");
|
||||||
SetAISSampleRate(tmp_ai_ctrl.AISFR ? SampleRate::AI48KHz : SampleRate::AI32KHz);
|
ai.SetAISSampleRate(tmp_ai_ctrl.AISFR ? SampleRate::AI48KHz : SampleRate::AI32KHz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set frequency of DMA
|
// Set frequency of DMA
|
||||||
if (tmp_ai_ctrl.AIDFR != state_.control.AIDFR)
|
if (tmp_ai_ctrl.AIDFR != ai.m_control.AIDFR)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIDFR to {}",
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Change AIDFR to {}",
|
||||||
tmp_ai_ctrl.AIDFR ? "32khz" : "48khz");
|
tmp_ai_ctrl.AIDFR ? "32khz" : "48khz");
|
||||||
SetAIDSampleRate(tmp_ai_ctrl.AIDFR ? SampleRate::AI32KHz : SampleRate::AI48KHz);
|
ai.SetAIDSampleRate(tmp_ai_ctrl.AIDFR ? SampleRate::AI32KHz : SampleRate::AI48KHz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streaming counter
|
// Streaming counter
|
||||||
if (tmp_ai_ctrl.PSTAT != state_.control.PSTAT)
|
if (tmp_ai_ctrl.PSTAT != ai.m_control.PSTAT)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "{} streaming audio",
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "{} streaming audio",
|
||||||
tmp_ai_ctrl.PSTAT ? "start" : "stop");
|
tmp_ai_ctrl.PSTAT ? "start" : "stop");
|
||||||
state_.control.PSTAT = tmp_ai_ctrl.PSTAT;
|
ai.m_control.PSTAT = tmp_ai_ctrl.PSTAT;
|
||||||
state_.last_cpu_time = core_timing.GetTicks();
|
ai.m_last_cpu_time = core_timing.GetTicks();
|
||||||
|
|
||||||
core_timing.RemoveEvent(state_.event_type_ai);
|
core_timing.RemoveEvent(ai.m_event_type_ai);
|
||||||
core_timing.ScheduleEvent(GetAIPeriod(), state_.event_type_ai);
|
core_timing.ScheduleEvent(ai.GetAIPeriod(), ai.m_event_type_ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI Interrupt
|
// AI Interrupt
|
||||||
if (tmp_ai_ctrl.AIINT)
|
if (tmp_ai_ctrl.AIINT)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Clear AIS Interrupt");
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Clear AIS Interrupt");
|
||||||
state_.control.AIINT = 0;
|
ai.m_control.AIINT = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample Count Reset
|
// Sample Count Reset
|
||||||
if (tmp_ai_ctrl.SCRESET)
|
if (tmp_ai_ctrl.SCRESET)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Reset AIS sample counter");
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Reset AIS sample counter");
|
||||||
state_.sample_counter = 0;
|
ai.m_sample_counter = 0;
|
||||||
|
|
||||||
state_.last_cpu_time = core_timing.GetTicks();
|
ai.m_last_cpu_time = core_timing.GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateInterrupts();
|
ai.UpdateInterrupts();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | AI_VOLUME_REGISTER, MMIO::DirectRead<u32>(&state.volume.hex),
|
mmio->Register(base | AI_VOLUME_REGISTER, MMIO::DirectRead<u32>(&m_volume.hex),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
auto& state_ = system.GetAudioInterfaceState().GetData();
|
auto& ai = system.GetAudioInterface();
|
||||||
state_.volume.hex = val;
|
ai.m_volume.hex = val;
|
||||||
SoundStream* sound_stream = system.GetSoundStream();
|
SoundStream* sound_stream = system.GetSoundStream();
|
||||||
sound_stream->GetMixer()->SetStreamingVolume(state_.volume.left,
|
sound_stream->GetMixer()->SetStreamingVolume(ai.m_volume.left,
|
||||||
state_.volume.right);
|
ai.m_volume.right);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | AI_SAMPLE_COUNTER, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
mmio->Register(base | AI_SAMPLE_COUNTER, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||||
auto& state_ = system.GetAudioInterfaceState().GetData();
|
auto& ai = system.GetAudioInterface();
|
||||||
const u64 cycles_streamed =
|
const u64 cycles_streamed =
|
||||||
IsPlaying() ? (system.GetCoreTiming().GetTicks() - state_.last_cpu_time) :
|
ai.IsPlaying() ? (system.GetCoreTiming().GetTicks() - ai.m_last_cpu_time) :
|
||||||
state_.last_cpu_time;
|
ai.m_last_cpu_time;
|
||||||
return state_.sample_counter +
|
return ai.m_sample_counter +
|
||||||
static_cast<u32>(cycles_streamed / state_.cpu_cycles_per_sample);
|
static_cast<u32>(cycles_streamed / ai.m_cpu_cycles_per_sample);
|
||||||
}),
|
}),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
auto& core_timing = system.GetCoreTiming();
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state_ = system.GetAudioInterfaceState().GetData();
|
auto& ai = system.GetAudioInterface();
|
||||||
state_.sample_counter = val;
|
ai.m_sample_counter = val;
|
||||||
state_.last_cpu_time = core_timing.GetTicks();
|
ai.m_last_cpu_time = core_timing.GetTicks();
|
||||||
core_timing.RemoveEvent(state_.event_type_ai);
|
core_timing.RemoveEvent(ai.m_event_type_ai);
|
||||||
core_timing.ScheduleEvent(GetAIPeriod(), state_.event_type_ai);
|
core_timing.ScheduleEvent(ai.GetAIPeriod(), ai.m_event_type_ai);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | AI_INTERRUPT_TIMING, MMIO::DirectRead<u32>(&state.interrupt_timing),
|
mmio->Register(base | AI_INTERRUPT_TIMING, MMIO::DirectRead<u32>(&m_interrupt_timing),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
auto& core_timing = system.GetCoreTiming();
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state_ = system.GetAudioInterfaceState().GetData();
|
auto& ai = system.GetAudioInterface();
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "AI_INTERRUPT_TIMING={:08x} at PC: {:08x}", val,
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "AI_INTERRUPT_TIMING={:08x} at PC: {:08x}", val,
|
||||||
system.GetPPCState().pc);
|
system.GetPPCState().pc);
|
||||||
state_.interrupt_timing = val;
|
ai.m_interrupt_timing = val;
|
||||||
core_timing.RemoveEvent(state_.event_type_ai);
|
core_timing.RemoveEvent(ai.m_event_type_ai);
|
||||||
core_timing.ScheduleEvent(GetAIPeriod(), state_.event_type_ai);
|
core_timing.ScheduleEvent(ai.GetAIPeriod(), ai.m_event_type_ai);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateAISInterrupt()
|
void AudioInterfaceManager::GenerateAISInterrupt()
|
||||||
{
|
{
|
||||||
GenerateAudioInterrupt();
|
GenerateAudioInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPlaying()
|
bool AudioInterfaceManager::IsPlaying() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
return (m_control.PSTAT == 1);
|
||||||
return (state.control.PSTAT == 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetAIDSampleRateDivisor()
|
u32 AudioInterfaceManager::GetAIDSampleRateDivisor() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
return m_aid_sample_rate_divisor;
|
||||||
return state.aid_sample_rate_divisor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetAISSampleRateDivisor()
|
u32 AudioInterfaceManager::GetAISSampleRateDivisor() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
return m_ais_sample_rate_divisor;
|
||||||
return state.ais_sample_rate_divisor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Get32KHzSampleRateDivisor()
|
u32 AudioInterfaceManager::Get32KHzSampleRateDivisor() const
|
||||||
{
|
{
|
||||||
return Get48KHzSampleRateDivisor() * 3 / 2;
|
return Get48KHzSampleRateDivisor() * 3 / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Get48KHzSampleRateDivisor()
|
u32 AudioInterfaceManager::Get48KHzSampleRateDivisor() const
|
||||||
{
|
{
|
||||||
return (SConfig::GetInstance().bWii ? 1125 : 1124) * 2;
|
return (SConfig::GetInstance().bWii ? 1125 : 1124) * 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,17 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
namespace CoreTiming
|
||||||
|
{
|
||||||
|
struct EventType;
|
||||||
|
}
|
||||||
namespace MMIO
|
namespace MMIO
|
||||||
{
|
{
|
||||||
class Mapping;
|
class Mapping;
|
||||||
|
@ -17,38 +23,98 @@ class Mapping;
|
||||||
|
|
||||||
namespace AudioInterface
|
namespace AudioInterface
|
||||||
{
|
{
|
||||||
class AudioInterfaceState
|
class AudioInterfaceManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioInterfaceState();
|
AudioInterfaceManager(Core::System& system);
|
||||||
AudioInterfaceState(const AudioInterfaceState&) = delete;
|
AudioInterfaceManager(const AudioInterfaceManager&) = delete;
|
||||||
AudioInterfaceState(AudioInterfaceState&&) = delete;
|
AudioInterfaceManager(AudioInterfaceManager&&) = delete;
|
||||||
AudioInterfaceState& operator=(const AudioInterfaceState&) = delete;
|
AudioInterfaceManager& operator=(const AudioInterfaceManager&) = delete;
|
||||||
AudioInterfaceState& operator=(AudioInterfaceState&&) = delete;
|
AudioInterfaceManager& operator=(AudioInterfaceManager&&) = delete;
|
||||||
~AudioInterfaceState();
|
~AudioInterfaceManager();
|
||||||
|
|
||||||
struct Data;
|
void Init();
|
||||||
Data& GetData() { return *m_data; }
|
void Shutdown();
|
||||||
|
void DoState(PointerWrap& p);
|
||||||
|
bool IsPlaying() const;
|
||||||
|
|
||||||
|
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||||
|
|
||||||
|
// Get the audio rate divisors (divisors for 48KHz or 32KHz only)
|
||||||
|
// Mixer::FIXED_SAMPLE_RATE_DIVIDEND will be the dividend used for these divisors
|
||||||
|
u32 GetAIDSampleRateDivisor() const;
|
||||||
|
u32 GetAISSampleRateDivisor() const;
|
||||||
|
|
||||||
|
u32 Get32KHzSampleRateDivisor() const;
|
||||||
|
u32 Get48KHzSampleRateDivisor() const;
|
||||||
|
|
||||||
|
void GenerateAISInterrupt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Data> m_data;
|
enum class SampleRate
|
||||||
|
{
|
||||||
|
AI32KHz,
|
||||||
|
AI48KHz,
|
||||||
|
};
|
||||||
|
|
||||||
|
// AI Control Register
|
||||||
|
union AICR
|
||||||
|
{
|
||||||
|
AICR() = default;
|
||||||
|
explicit AICR(u32 hex_) : hex{hex_} {}
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u32 PSTAT : 1; // sample counter/playback enable
|
||||||
|
u32 AISFR : 1; // AIS Frequency (0=32khz 1=48khz)
|
||||||
|
u32 AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled
|
||||||
|
u32 AIINT : 1; // audio interrupt status
|
||||||
|
u32 AIINTVLD : 1; // This bit controls whether AIINT is affected by the Interrupt Timing
|
||||||
|
// register
|
||||||
|
// matching the sample counter. Once set, AIINT will hold its last value
|
||||||
|
u32 SCRESET : 1; // write to reset counter
|
||||||
|
u32 AIDFR : 1; // AID Frequency (0=48khz 1=32khz)
|
||||||
|
u32 : 25;
|
||||||
|
};
|
||||||
|
u32 hex = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// AI Volume Register
|
||||||
|
union AIVR
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u32 left : 8;
|
||||||
|
u32 right : 8;
|
||||||
|
u32 : 16;
|
||||||
|
};
|
||||||
|
u32 hex = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void UpdateInterrupts();
|
||||||
|
void GenerateAudioInterrupt();
|
||||||
|
void IncreaseSampleCount(const u32 amount);
|
||||||
|
int GetAIPeriod() const;
|
||||||
|
void SetAIDSampleRate(SampleRate sample_rate);
|
||||||
|
void SetAISSampleRate(SampleRate sample_rate);
|
||||||
|
|
||||||
|
void Update(u64 userdata, s64 cycles_late);
|
||||||
|
static void GlobalUpdate(Core::System& system, u64 userdata, s64 cycles_late);
|
||||||
|
|
||||||
|
// Registers
|
||||||
|
AICR m_control;
|
||||||
|
AIVR m_volume;
|
||||||
|
|
||||||
|
u32 m_sample_counter = 0;
|
||||||
|
u32 m_interrupt_timing = 0;
|
||||||
|
|
||||||
|
u64 m_last_cpu_time = 0;
|
||||||
|
u64 m_cpu_cycles_per_sample = 0;
|
||||||
|
|
||||||
|
u32 m_ais_sample_rate_divisor = 0;
|
||||||
|
u32 m_aid_sample_rate_divisor = 0;
|
||||||
|
|
||||||
|
CoreTiming::EventType* m_event_type_ai = nullptr;
|
||||||
|
|
||||||
|
Core::System& m_system;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Init();
|
|
||||||
void Shutdown();
|
|
||||||
void DoState(PointerWrap& p);
|
|
||||||
bool IsPlaying();
|
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
|
||||||
|
|
||||||
// Get the audio rate divisors (divisors for 48KHz or 32KHz only)
|
|
||||||
// Mixer::FIXED_SAMPLE_RATE_DIVIDEND will be the dividend used for these divisors
|
|
||||||
u32 GetAIDSampleRateDivisor();
|
|
||||||
u32 GetAISSampleRateDivisor();
|
|
||||||
|
|
||||||
u32 Get32KHzSampleRateDivisor();
|
|
||||||
u32 Get48KHzSampleRateDivisor();
|
|
||||||
|
|
||||||
void GenerateAISInterrupt();
|
|
||||||
|
|
||||||
} // namespace AudioInterface
|
} // namespace AudioInterface
|
||||||
|
|
|
@ -311,11 +311,12 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
auto& system = Core::System::GetInstance();
|
||||||
auto& state = system.GetDVDInterfaceState().GetData();
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
|
auto& ai = system.GetAudioInterface();
|
||||||
|
|
||||||
// Actual games always set this to 48 KHz
|
// Actual games always set this to 48 KHz
|
||||||
// but let's make sure to use GetAISSampleRateDivisor()
|
// but let's make sure to use GetAISSampleRateDivisor()
|
||||||
// just in case it changes to 32 KHz
|
// just in case it changes to 32 KHz
|
||||||
const u32 sample_rate_divisor = AudioInterface::GetAISSampleRateDivisor();
|
const u32 sample_rate_divisor = ai.GetAISSampleRateDivisor();
|
||||||
|
|
||||||
// Determine which audio data to read next.
|
// Determine which audio data to read next.
|
||||||
|
|
||||||
|
@ -334,7 +335,7 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect
|
||||||
SoundStream* sound_stream = system.GetSoundStream();
|
SoundStream* sound_stream = system.GetSoundStream();
|
||||||
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), state.pending_samples);
|
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), state.pending_samples);
|
||||||
|
|
||||||
if (state.stream && AudioInterface::IsPlaying())
|
if (state.stream && ai.IsPlaying())
|
||||||
{
|
{
|
||||||
read_offset = state.audio_position;
|
read_offset = state.audio_position;
|
||||||
read_length = AdvanceDTK(maximum_samples, &state.pending_samples);
|
read_length = AdvanceDTK(maximum_samples, &state.pending_samples);
|
||||||
|
|
|
@ -40,7 +40,7 @@ void Init(const Sram* override_sram)
|
||||||
State::Init();
|
State::Init();
|
||||||
|
|
||||||
// Init the whole Hardware
|
// Init the whole Hardware
|
||||||
AudioInterface::Init();
|
system.GetAudioInterface().Init();
|
||||||
VideoInterface::Init();
|
VideoInterface::Init();
|
||||||
SerialInterface::Init();
|
SerialInterface::Init();
|
||||||
system.GetProcessorInterface().Init();
|
system.GetProcessorInterface().Init();
|
||||||
|
@ -80,7 +80,7 @@ void Shutdown()
|
||||||
system.GetHSP().Shutdown();
|
system.GetHSP().Shutdown();
|
||||||
ExpansionInterface::Shutdown();
|
ExpansionInterface::Shutdown();
|
||||||
SerialInterface::Shutdown();
|
SerialInterface::Shutdown();
|
||||||
AudioInterface::Shutdown();
|
system.GetAudioInterface().Shutdown();
|
||||||
|
|
||||||
State::Shutdown();
|
State::Shutdown();
|
||||||
system.GetCoreTiming().Shutdown();
|
system.GetCoreTiming().Shutdown();
|
||||||
|
@ -107,7 +107,7 @@ void DoState(PointerWrap& p)
|
||||||
p.DoMarker("GPFifo");
|
p.DoMarker("GPFifo");
|
||||||
ExpansionInterface::DoState(p);
|
ExpansionInterface::DoState(p);
|
||||||
p.DoMarker("ExpansionInterface");
|
p.DoMarker("ExpansionInterface");
|
||||||
AudioInterface::DoState(p);
|
system.GetAudioInterface().DoState(p);
|
||||||
p.DoMarker("AudioInterface");
|
p.DoMarker("AudioInterface");
|
||||||
system.GetHSP().DoState(p);
|
system.GetHSP().DoState(p);
|
||||||
p.DoMarker("HSP");
|
p.DoMarker("HSP");
|
||||||
|
|
|
@ -58,14 +58,14 @@ void MemoryManager::InitMMIO(bool is_wii)
|
||||||
DVDInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006000, false);
|
DVDInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006000, false);
|
||||||
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006400);
|
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006400);
|
||||||
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006800);
|
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006800);
|
||||||
AudioInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006C00);
|
system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C006C00);
|
||||||
if (is_wii)
|
if (is_wii)
|
||||||
{
|
{
|
||||||
IOS::RegisterMMIO(m_mmio_mapping.get(), 0x0D000000);
|
IOS::RegisterMMIO(m_mmio_mapping.get(), 0x0D000000);
|
||||||
DVDInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006000, true);
|
DVDInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006000, true);
|
||||||
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006400);
|
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006400);
|
||||||
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006800);
|
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006800);
|
||||||
AudioInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006C00);
|
system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006C00);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,8 @@ void DSPCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
int GetAudioDMACallbackPeriod()
|
int GetAudioDMACallbackPeriod()
|
||||||
{
|
{
|
||||||
// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
|
// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
|
||||||
return static_cast<u64>(s_cpu_core_clock) * AudioInterface::GetAIDSampleRateDivisor() /
|
auto& system = Core::System::GetInstance();
|
||||||
|
return static_cast<u64>(s_cpu_core_clock) * system.GetAudioInterface().GetAIDSampleRateDivisor() /
|
||||||
(Mixer::FIXED_SAMPLE_RATE_DIVIDEND * 4 / 32);
|
(Mixer::FIXED_SAMPLE_RATE_DIVIDEND * 4 / 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,8 @@ namespace Core
|
||||||
struct System::Impl
|
struct System::Impl
|
||||||
{
|
{
|
||||||
explicit Impl(System& system)
|
explicit Impl(System& system)
|
||||||
: m_core_timing(system), m_gp_fifo(system), m_ppc_state(PowerPC::ppcState)
|
: m_audio_interface(system), m_core_timing(system), m_gp_fifo(system),
|
||||||
|
m_ppc_state(PowerPC::ppcState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ struct System::Impl
|
||||||
bool m_sound_stream_running = false;
|
bool m_sound_stream_running = false;
|
||||||
bool m_audio_dump_started = false;
|
bool m_audio_dump_started = false;
|
||||||
|
|
||||||
AudioInterface::AudioInterfaceState m_audio_interface_state;
|
AudioInterface::AudioInterfaceManager m_audio_interface;
|
||||||
CoreTiming::CoreTimingManager m_core_timing;
|
CoreTiming::CoreTimingManager m_core_timing;
|
||||||
CommandProcessor::CommandProcessorManager m_command_processor;
|
CommandProcessor::CommandProcessorManager m_command_processor;
|
||||||
CPU::CPUManager m_cpu;
|
CPU::CPUManager m_cpu;
|
||||||
|
@ -112,9 +113,9 @@ void System::SetAudioDumpStarted(bool started)
|
||||||
m_impl->m_audio_dump_started = started;
|
m_impl->m_audio_dump_started = started;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioInterface::AudioInterfaceState& System::GetAudioInterfaceState() const
|
AudioInterface::AudioInterfaceManager& System::GetAudioInterface() const
|
||||||
{
|
{
|
||||||
return m_impl->m_audio_interface_state;
|
return m_impl->m_audio_interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPU::CPUManager& System::GetCPU() const
|
CPU::CPUManager& System::GetCPU() const
|
||||||
|
|
|
@ -13,7 +13,7 @@ class VertexShaderManager;
|
||||||
|
|
||||||
namespace AudioInterface
|
namespace AudioInterface
|
||||||
{
|
{
|
||||||
class AudioInterfaceState;
|
class AudioInterfaceManager;
|
||||||
};
|
};
|
||||||
namespace CPU
|
namespace CPU
|
||||||
{
|
{
|
||||||
|
@ -122,7 +122,7 @@ public:
|
||||||
bool IsAudioDumpStarted() const;
|
bool IsAudioDumpStarted() const;
|
||||||
void SetAudioDumpStarted(bool started);
|
void SetAudioDumpStarted(bool started);
|
||||||
|
|
||||||
AudioInterface::AudioInterfaceState& GetAudioInterfaceState() const;
|
AudioInterface::AudioInterfaceManager& GetAudioInterface() const;
|
||||||
CPU::CPUManager& GetCPU() const;
|
CPU::CPUManager& GetCPU() const;
|
||||||
CoreTiming::CoreTimingManager& GetCoreTiming() const;
|
CoreTiming::CoreTimingManager& GetCoreTiming() const;
|
||||||
CommandProcessor::CommandProcessorManager& GetCommandProcessor() const;
|
CommandProcessor::CommandProcessorManager& GetCommandProcessor() const;
|
||||||
|
|
Loading…
Reference in New Issue