mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-20 03:24:59 +00:00
Core/AudioCommon: Move FIXED_SAMPLE_RATE_DIVIDEND from Mixer to AudioInterface.
This commit is contained in:
parent
61ab662733
commit
54e32dec66
10 changed files with 64 additions and 73 deletions
|
@ -66,7 +66,7 @@ void Mixer::MixerFifo::Mix(s16* samples, std::size_t num_samples)
|
|||
constexpr DT_s FADE_OUT_RC = DT_s(0.064);
|
||||
|
||||
const u64 out_sample_rate = m_mixer->m_output_sample_rate;
|
||||
u64 in_sample_rate = FIXED_SAMPLE_RATE_DIVIDEND / m_input_sample_rate_divisor;
|
||||
u64 in_sample_rate = m_input_sample_rate;
|
||||
|
||||
const float emulation_speed = m_mixer->m_config_emulation_speed;
|
||||
if (0 < emulation_speed && emulation_speed != 1.0)
|
||||
|
@ -225,10 +225,10 @@ void Mixer::PushSamples(const s16* samples, std::size_t num_samples)
|
|||
m_dma_mixer.PushSamples(samples, num_samples);
|
||||
if (m_log_dsp_audio)
|
||||
{
|
||||
const s32 sample_rate_divisor = m_dma_mixer.GetInputSampleRateDivisor();
|
||||
const s32 sample_rate = m_dma_mixer.GetInputSampleRate();
|
||||
auto volume = m_dma_mixer.GetVolume();
|
||||
m_wave_writer_dsp.AddStereoSamplesBE(samples, static_cast<u32>(num_samples),
|
||||
sample_rate_divisor, volume.first, volume.second);
|
||||
m_wave_writer_dsp.AddStereoSamplesBE(samples, static_cast<u32>(num_samples), sample_rate,
|
||||
volume.first, volume.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,15 +237,14 @@ void Mixer::PushStreamingSamples(const s16* samples, std::size_t num_samples)
|
|||
m_streaming_mixer.PushSamples(samples, num_samples);
|
||||
if (m_log_dtk_audio)
|
||||
{
|
||||
const s32 sample_rate_divisor = m_streaming_mixer.GetInputSampleRateDivisor();
|
||||
const s32 sample_rate = m_streaming_mixer.GetInputSampleRate();
|
||||
auto volume = m_streaming_mixer.GetVolume();
|
||||
m_wave_writer_dtk.AddStereoSamplesBE(samples, static_cast<u32>(num_samples),
|
||||
sample_rate_divisor, volume.first, volume.second);
|
||||
m_wave_writer_dtk.AddStereoSamplesBE(samples, static_cast<u32>(num_samples), sample_rate,
|
||||
volume.first, volume.second);
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_samples,
|
||||
u32 sample_rate_divisor)
|
||||
void Mixer::PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_samples, u32 sample_rate)
|
||||
{
|
||||
// Max 20 bytes/speaker report, may be 4-bit ADPCM so multiply by 2
|
||||
static constexpr std::size_t MAX_SPEAKER_SAMPLES = 20 * 2;
|
||||
|
@ -256,7 +255,7 @@ void Mixer::PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_sample
|
|||
MAX_SPEAKER_SAMPLES);
|
||||
if (num_samples <= MAX_SPEAKER_SAMPLES)
|
||||
{
|
||||
m_wiimote_speaker_mixer.SetInputSampleRateDivisor(sample_rate_divisor);
|
||||
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
|
||||
|
||||
for (std::size_t i = 0; i < num_samples; ++i)
|
||||
{
|
||||
|
@ -297,19 +296,19 @@ void Mixer::PushGBASamples(std::size_t device_number, const s16* samples, std::s
|
|||
m_gba_mixers[device_number].PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
void Mixer::SetDMAInputSampleRateDivisor(u32 rate_divisor)
|
||||
void Mixer::SetDMAInputSampleRate(u32 sample_rate)
|
||||
{
|
||||
m_dma_mixer.SetInputSampleRateDivisor(rate_divisor);
|
||||
m_dma_mixer.SetInputSampleRate(sample_rate);
|
||||
}
|
||||
|
||||
void Mixer::SetStreamInputSampleRateDivisor(u32 rate_divisor)
|
||||
void Mixer::SetStreamInputSampleRate(u32 sample_rate)
|
||||
{
|
||||
m_streaming_mixer.SetInputSampleRateDivisor(rate_divisor);
|
||||
m_streaming_mixer.SetInputSampleRate(sample_rate);
|
||||
}
|
||||
|
||||
void Mixer::SetGBAInputSampleRateDivisors(std::size_t device_number, u32 rate_divisor)
|
||||
void Mixer::SetGBAInputSampleRate(std::size_t device_number, u32 sample_rate)
|
||||
{
|
||||
m_gba_mixers[device_number].SetInputSampleRateDivisor(rate_divisor);
|
||||
m_gba_mixers[device_number].SetInputSampleRate(sample_rate);
|
||||
}
|
||||
|
||||
void Mixer::SetStreamingVolume(u32 lvolume, u32 rvolume)
|
||||
|
@ -332,7 +331,7 @@ void Mixer::StartLogDTKAudio(const std::string& filename)
|
|||
{
|
||||
if (!m_log_dtk_audio)
|
||||
{
|
||||
bool success = m_wave_writer_dtk.Start(filename, m_streaming_mixer.GetInputSampleRateDivisor());
|
||||
bool success = m_wave_writer_dtk.Start(filename, m_streaming_mixer.GetInputSampleRate());
|
||||
if (success)
|
||||
{
|
||||
m_log_dtk_audio = true;
|
||||
|
@ -369,7 +368,7 @@ void Mixer::StartLogDSPAudio(const std::string& filename)
|
|||
{
|
||||
if (!m_log_dsp_audio)
|
||||
{
|
||||
bool success = m_wave_writer_dsp.Start(filename, m_dma_mixer.GetInputSampleRateDivisor());
|
||||
bool success = m_wave_writer_dsp.Start(filename, m_dma_mixer.GetInputSampleRate());
|
||||
if (success)
|
||||
{
|
||||
m_log_dsp_audio = true;
|
||||
|
@ -411,19 +410,19 @@ void Mixer::RefreshConfig()
|
|||
|
||||
void Mixer::MixerFifo::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_input_sample_rate_divisor);
|
||||
p.Do(m_input_sample_rate);
|
||||
p.Do(m_LVolume);
|
||||
p.Do(m_RVolume);
|
||||
}
|
||||
|
||||
void Mixer::MixerFifo::SetInputSampleRateDivisor(u32 rate_divisor)
|
||||
void Mixer::MixerFifo::SetInputSampleRate(u32 sample_rate)
|
||||
{
|
||||
m_input_sample_rate_divisor = rate_divisor;
|
||||
m_input_sample_rate = sample_rate;
|
||||
}
|
||||
|
||||
u32 Mixer::MixerFifo::GetInputSampleRateDivisor() const
|
||||
u32 Mixer::MixerFifo::GetInputSampleRate() const
|
||||
{
|
||||
return m_input_sample_rate_divisor;
|
||||
return m_input_sample_rate;
|
||||
}
|
||||
|
||||
void Mixer::MixerFifo::SetVolume(u32 lvolume, u32 rvolume)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
|
@ -31,16 +30,15 @@ public:
|
|||
// Called from main thread
|
||||
void PushSamples(const s16* samples, std::size_t num_samples);
|
||||
void PushStreamingSamples(const s16* samples, std::size_t num_samples);
|
||||
void PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_samples,
|
||||
u32 sample_rate_divisor);
|
||||
void PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_samples, u32 sample_rate);
|
||||
void PushSkylanderPortalSamples(const u8* samples, std::size_t num_samples);
|
||||
void PushGBASamples(std::size_t device_number, const s16* samples, std::size_t num_samples);
|
||||
|
||||
u32 GetSampleRate() const { return m_output_sample_rate; }
|
||||
|
||||
void SetDMAInputSampleRateDivisor(u32 rate_divisor);
|
||||
void SetStreamInputSampleRateDivisor(u32 rate_divisor);
|
||||
void SetGBAInputSampleRateDivisors(std::size_t device_number, u32 rate_divisor);
|
||||
void SetDMAInputSampleRate(u32 sample_rate);
|
||||
void SetStreamInputSampleRate(u32 sample_rate);
|
||||
void SetGBAInputSampleRate(std::size_t device_number, u32 sample_rate);
|
||||
|
||||
void SetStreamingVolume(u32 lvolume, u32 rvolume);
|
||||
void SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume);
|
||||
|
@ -52,9 +50,6 @@ public:
|
|||
void StartLogDSPAudio(const std::string& filename);
|
||||
void StopLogDSPAudio();
|
||||
|
||||
// 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does
|
||||
static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2;
|
||||
|
||||
private:
|
||||
const std::size_t SURROUND_CHANNELS = 6;
|
||||
|
||||
|
@ -98,22 +93,21 @@ private:
|
|||
using Granule = std::array<StereoPair, GRANULE_SIZE>;
|
||||
|
||||
public:
|
||||
MixerFifo(Mixer* mixer, u32 sample_rate_divisor, bool little_endian)
|
||||
: m_mixer(mixer), m_input_sample_rate_divisor(sample_rate_divisor),
|
||||
m_little_endian(little_endian)
|
||||
MixerFifo(Mixer* mixer, u32 sample_rate, bool little_endian)
|
||||
: m_mixer(mixer), m_input_sample_rate(sample_rate), m_little_endian(little_endian)
|
||||
{
|
||||
}
|
||||
void DoState(PointerWrap& p);
|
||||
void PushSamples(const s16* samples, std::size_t num_samples);
|
||||
void Mix(s16* samples, std::size_t num_samples);
|
||||
void SetInputSampleRateDivisor(u32 rate_divisor);
|
||||
u32 GetInputSampleRateDivisor() const;
|
||||
void SetInputSampleRate(u32 sample_rate);
|
||||
u32 GetInputSampleRate() const;
|
||||
void SetVolume(u32 lvolume, u32 rvolume);
|
||||
std::pair<s32, s32> GetVolume() const;
|
||||
|
||||
private:
|
||||
Mixer* m_mixer;
|
||||
u32 m_input_sample_rate_divisor;
|
||||
u32 m_input_sample_rate;
|
||||
bool m_little_endian;
|
||||
|
||||
Granule m_next_buffer{};
|
||||
|
@ -141,14 +135,12 @@ private:
|
|||
|
||||
void RefreshConfig();
|
||||
|
||||
MixerFifo m_dma_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 32000, false};
|
||||
MixerFifo m_streaming_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, false};
|
||||
MixerFifo m_wiimote_speaker_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 3000, true};
|
||||
MixerFifo m_skylander_portal_mixer{this, FIXED_SAMPLE_RATE_DIVIDEND / 8000, true};
|
||||
std::array<MixerFifo, 4> m_gba_mixers{MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true},
|
||||
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true},
|
||||
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true},
|
||||
MixerFifo{this, FIXED_SAMPLE_RATE_DIVIDEND / 48000, true}};
|
||||
MixerFifo m_dma_mixer{this, 32000, false};
|
||||
MixerFifo m_streaming_mixer{this, 48000, false};
|
||||
MixerFifo m_wiimote_speaker_mixer{this, 3000, true};
|
||||
MixerFifo m_skylander_portal_mixer{this, 8000, true};
|
||||
std::array<MixerFifo, 4> m_gba_mixers{MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true},
|
||||
MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true}};
|
||||
u32 m_output_sample_rate;
|
||||
|
||||
AudioCommon::SurroundDecoder m_surround_decoder;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "AudioCommon/Mixer.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
|
@ -16,7 +15,6 @@
|
|||
#include "Common/StringUtil.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
constexpr size_t WaveFileWriter::BUFFER_SIZE;
|
||||
|
||||
|
@ -29,7 +27,7 @@ WaveFileWriter::~WaveFileWriter()
|
|||
Stop();
|
||||
}
|
||||
|
||||
bool WaveFileWriter::Start(const std::string& filename, u32 sample_rate_divisor)
|
||||
bool WaveFileWriter::Start(const std::string& filename, u32 sample_rate)
|
||||
{
|
||||
// Ask to delete file
|
||||
if (File::Exists(filename))
|
||||
|
@ -68,7 +66,7 @@ bool WaveFileWriter::Start(const std::string& filename, u32 sample_rate_divisor)
|
|||
if (basename.empty())
|
||||
SplitPath(filename, nullptr, &basename, nullptr);
|
||||
|
||||
current_sample_rate_divisor = sample_rate_divisor;
|
||||
current_sample_rate = sample_rate;
|
||||
|
||||
// -----------------
|
||||
// Write file header
|
||||
|
@ -81,7 +79,6 @@ bool WaveFileWriter::Start(const std::string& filename, u32 sample_rate_divisor)
|
|||
Write(16); // size of fmt block
|
||||
Write(0x00020001); // two channels, uncompressed
|
||||
|
||||
const u32 sample_rate = Mixer::FIXED_SAMPLE_RATE_DIVIDEND / sample_rate_divisor;
|
||||
Write(sample_rate);
|
||||
Write(sample_rate * 2 * 2); // two channels, 16bit
|
||||
|
||||
|
@ -117,8 +114,8 @@ void WaveFileWriter::Write4(const char* ptr)
|
|||
file.WriteBytes(ptr, 4);
|
||||
}
|
||||
|
||||
void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count,
|
||||
u32 sample_rate_divisor, int l_volume, int r_volume)
|
||||
void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count, u32 sample_rate,
|
||||
int l_volume, int r_volume)
|
||||
{
|
||||
if (!file)
|
||||
{
|
||||
|
@ -157,14 +154,14 @@ void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count,
|
|||
conv_buffer[2 * i + 1] = conv_buffer[2 * i + 1] * r_volume / 256;
|
||||
}
|
||||
|
||||
if (sample_rate_divisor != current_sample_rate_divisor)
|
||||
if (sample_rate != current_sample_rate)
|
||||
{
|
||||
Stop();
|
||||
file_index++;
|
||||
const std::string filename =
|
||||
fmt::format("{}{}{}.wav", File::GetUserPath(D_DUMPAUDIO_IDX), basename, file_index);
|
||||
Start(filename, sample_rate_divisor);
|
||||
current_sample_rate_divisor = sample_rate_divisor;
|
||||
Start(filename, sample_rate);
|
||||
current_sample_rate = sample_rate;
|
||||
}
|
||||
|
||||
file.WriteBytes(conv_buffer.data(), count * 4);
|
||||
|
|
|
@ -30,13 +30,13 @@ public:
|
|||
WaveFileWriter(WaveFileWriter&&) = delete;
|
||||
WaveFileWriter& operator=(WaveFileWriter&&) = delete;
|
||||
|
||||
bool Start(const std::string& filename, u32 sample_rate_divisor);
|
||||
bool Start(const std::string& filename, u32 sample_rate);
|
||||
void Stop();
|
||||
|
||||
void SetSkipSilence(bool skip) { skip_silence = skip; }
|
||||
// big endian
|
||||
void AddStereoSamplesBE(const short* sample_data, u32 count, u32 sample_rate_divisor,
|
||||
int l_volume, int r_volume);
|
||||
void AddStereoSamplesBE(const short* sample_data, u32 count, u32 sample_rate, int l_volume,
|
||||
int r_volume);
|
||||
u32 GetAudioSize() const { return audio_size; }
|
||||
|
||||
private:
|
||||
|
@ -50,7 +50,7 @@ private:
|
|||
u32 file_index = 0;
|
||||
u32 audio_size = 0;
|
||||
|
||||
u32 current_sample_rate_divisor;
|
||||
u32 current_sample_rate;
|
||||
std::array<short, BUFFER_SIZE> conv_buffer{};
|
||||
|
||||
bool skip_silence = false;
|
||||
|
|
|
@ -71,8 +71,8 @@ enum
|
|||
};
|
||||
|
||||
AudioInterfaceManager::AudioInterfaceManager(Core::System& system)
|
||||
: m_ais_sample_rate_divisor(Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 48000),
|
||||
m_aid_sample_rate_divisor(Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 32000), m_system(system)
|
||||
: m_ais_sample_rate_divisor(FIXED_SAMPLE_RATE_DIVIDEND / 48000),
|
||||
m_aid_sample_rate_divisor(FIXED_SAMPLE_RATE_DIVIDEND / 32000), m_system(system)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -125,8 +125,7 @@ void AudioInterfaceManager::IncreaseSampleCount(const u32 amount)
|
|||
int AudioInterfaceManager::GetAIPeriod() const
|
||||
{
|
||||
u64 period = m_cpu_cycles_per_sample * (m_interrupt_timing - m_sample_counter);
|
||||
u64 s_period =
|
||||
m_cpu_cycles_per_sample * Mixer::FIXED_SAMPLE_RATE_DIVIDEND / m_ais_sample_rate_divisor;
|
||||
u64 s_period = m_cpu_cycles_per_sample * FIXED_SAMPLE_RATE_DIVIDEND / m_ais_sample_rate_divisor;
|
||||
if (period == 0)
|
||||
return static_cast<int>(s_period);
|
||||
return static_cast<int>(std::min(period, s_period));
|
||||
|
@ -168,7 +167,8 @@ void AudioInterfaceManager::SetAIDSampleRate(SampleRate sample_rate)
|
|||
}
|
||||
|
||||
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||
sound_stream->GetMixer()->SetDMAInputSampleRateDivisor(m_aid_sample_rate_divisor);
|
||||
sound_stream->GetMixer()->SetDMAInputSampleRate(FIXED_SAMPLE_RATE_DIVIDEND /
|
||||
m_aid_sample_rate_divisor);
|
||||
}
|
||||
|
||||
void AudioInterfaceManager::SetAISSampleRate(SampleRate sample_rate)
|
||||
|
@ -185,9 +185,10 @@ void AudioInterfaceManager::SetAISSampleRate(SampleRate sample_rate)
|
|||
}
|
||||
|
||||
m_cpu_cycles_per_sample = static_cast<u64>(m_system.GetSystemTimers().GetTicksPerSecond()) *
|
||||
m_ais_sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
|
||||
m_ais_sample_rate_divisor / FIXED_SAMPLE_RATE_DIVIDEND;
|
||||
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||
sound_stream->GetMixer()->SetStreamInputSampleRateDivisor(m_ais_sample_rate_divisor);
|
||||
sound_stream->GetMixer()->SetStreamInputSampleRate(FIXED_SAMPLE_RATE_DIVIDEND /
|
||||
m_ais_sample_rate_divisor);
|
||||
}
|
||||
|
||||
void AudioInterfaceManager::Init()
|
||||
|
|
|
@ -23,6 +23,9 @@ class Mapping;
|
|||
|
||||
namespace AudioInterface
|
||||
{
|
||||
// 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does
|
||||
static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2;
|
||||
|
||||
enum class SampleRate
|
||||
{
|
||||
AI32KHz,
|
||||
|
|
|
@ -237,7 +237,7 @@ void DVDInterface::DTKStreamingCallback(DIInterruptType interrupt_type,
|
|||
// Read the next chunk of audio data asynchronously.
|
||||
s64 ticks_to_dtk = m_system.GetSystemTimers().GetTicksPerSecond() * s64(m_pending_blocks) *
|
||||
StreamADPCM::SAMPLES_PER_BLOCK * sample_rate_divisor /
|
||||
Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
|
||||
AudioInterface::FIXED_SAMPLE_RATE_DIVIDEND;
|
||||
ticks_to_dtk -= cycles_late;
|
||||
if (read_length > 0)
|
||||
{
|
||||
|
|
|
@ -407,8 +407,7 @@ void Core::SetSampleRates()
|
|||
blip_set_rates(m_core->getAudioChannel(m_core, 1), m_core->frequency(m_core), SAMPLE_RATE);
|
||||
|
||||
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||
sound_stream->GetMixer()->SetGBAInputSampleRateDivisors(
|
||||
m_device_number, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / SAMPLE_RATE);
|
||||
sound_stream->GetMixer()->SetGBAInputSampleRate(m_device_number, SAMPLE_RATE);
|
||||
}
|
||||
|
||||
void Core::AddCallbacks()
|
||||
|
|
|
@ -85,7 +85,7 @@ static int GetAudioDMACallbackPeriod(u32 cpu_core_clock, u32 aid_sample_rate_div
|
|||
{
|
||||
// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
|
||||
return static_cast<u64>(cpu_core_clock) * aid_sample_rate_divisor /
|
||||
(Mixer::FIXED_SAMPLE_RATE_DIVIDEND * 4 / 32);
|
||||
(AudioInterface::FIXED_SAMPLE_RATE_DIVIDEND * 4 / 32);
|
||||
}
|
||||
|
||||
void SystemTimersManager::AudioDMACallback(Core::System& system, u64 userdata, s64 cycles_late)
|
||||
|
|
|
@ -133,8 +133,8 @@ void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
|
|||
|
||||
// ADPCM sample rate is thought to be x2.(3000 x2 = 6000).
|
||||
const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate;
|
||||
sound_stream->GetMixer()->PushWiimoteSpeakerSamples(
|
||||
samples.data(), sample_length, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / (sample_rate * 2));
|
||||
sound_stream->GetMixer()->PushWiimoteSpeakerSamples(samples.data(), sample_length,
|
||||
sample_rate * 2);
|
||||
}
|
||||
|
||||
void SpeakerLogic::Reset()
|
||||
|
|
Loading…
Add table
Reference in a new issue