mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
Rewrite handling of DTK (streaming) audio.
The primary motivation here is to make sure we submit samples from the CPU thread. This makes sure the timing of related interrupts accurate, and generally keeps the different kinds of audio synchronized. This will also allow improvements to audio dumping functionality. The new code is also more concise because it gets rid of some duplicated audio mixing code.
This commit is contained in:
@ -28,10 +28,7 @@ namespace AudioCommon
|
||||
{
|
||||
SoundStream *InitSoundStream(void *hWnd)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
delete soundStream;
|
||||
CMixer *mixer = new CMixer(AISampleRate, DACSampleRate, 48000);
|
||||
CMixer *mixer = new CMixer(48000);
|
||||
|
||||
// TODO: possible memleak with mixer
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "AudioCommon/Mixer.h"
|
||||
#include "Common/Atomic.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/AudioInterface.h"
|
||||
@ -19,20 +20,8 @@
|
||||
#endif
|
||||
|
||||
// Executed from sound stream thread
|
||||
unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_framelimit)
|
||||
unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, bool consider_framelimit)
|
||||
{
|
||||
if (!samples)
|
||||
return 0;
|
||||
|
||||
std::lock_guard<std::mutex> lk(m_csMixing);
|
||||
|
||||
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
||||
{
|
||||
// Silence
|
||||
memset(samples, 0, numSamples * 4);
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
unsigned int currentSample = 0;
|
||||
|
||||
// Cache access in non-volatile variable
|
||||
@ -56,14 +45,14 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
||||
//remember fractional offset
|
||||
|
||||
u32 framelimit = SConfig::GetInstance().m_Framelimit;
|
||||
float aid_sample_rate = AudioInterface::GetAIDSampleRate() + offset;
|
||||
float aid_sample_rate = m_input_sample_rate + offset;
|
||||
if (consider_framelimit && framelimit > 2)
|
||||
{
|
||||
aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate;
|
||||
}
|
||||
|
||||
static u32 frac = 0;
|
||||
const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_sampleRate );
|
||||
const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate );
|
||||
|
||||
if (ratio > 0x10000)
|
||||
ERROR_LOG(AUDIO, "ratio out of range");
|
||||
@ -74,11 +63,15 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
||||
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current
|
||||
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next
|
||||
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16;
|
||||
sampleL += samples[currentSample + 1];
|
||||
MathUtil::Clamp(&sampleL, -32767, 32767);
|
||||
samples[currentSample+1] = sampleL;
|
||||
|
||||
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current
|
||||
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next
|
||||
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16;
|
||||
sampleR += samples[currentSample];
|
||||
MathUtil::Clamp(&sampleR, -32767, 32767);
|
||||
samples[currentSample] = sampleR;
|
||||
|
||||
frac += ratio;
|
||||
@ -87,36 +80,55 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
||||
}
|
||||
|
||||
// Padding
|
||||
unsigned short s[2];
|
||||
short s[2];
|
||||
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
|
||||
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
|
||||
for (; currentSample < numSamples*2; currentSample+=2)
|
||||
{
|
||||
samples[currentSample] = s[0];
|
||||
samples[currentSample+1] = s[1];
|
||||
int sampleR = s[0] + samples[currentSample];
|
||||
MathUtil::Clamp(&sampleR, -32767, 32767);
|
||||
samples[currentSample] = sampleR;
|
||||
int sampleL = s[1] + samples[currentSample + 1];
|
||||
MathUtil::Clamp(&sampleL, -32767, 32767);
|
||||
samples[currentSample + 1] = sampleL;
|
||||
}
|
||||
|
||||
// Flush cached variable
|
||||
Common::AtomicStore(m_indexR, indexR);
|
||||
|
||||
// Add the DTK Music
|
||||
// Re-sampling is done inside
|
||||
AudioInterface::Callback_GetStreaming(samples, numSamples, m_sampleRate);
|
||||
if (m_logAudio)
|
||||
g_wave_writer.AddStereoSamples(samples, numSamples);
|
||||
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit)
|
||||
{
|
||||
if (!samples)
|
||||
return 0;
|
||||
|
||||
void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
std::lock_guard<std::mutex> lk(m_csMixing);
|
||||
|
||||
memset(samples, 0, num_samples * 2 * sizeof(short));
|
||||
|
||||
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
||||
{
|
||||
// Silence
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
m_dma_mixer.Mix(samples, num_samples, consider_framelimit);
|
||||
m_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
|
||||
if (m_logAudio)
|
||||
g_wave_writer.AddStereoSamples(samples, num_samples);
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
void CMixer::MixerFifo::PushSamples(const short *samples, unsigned int num_samples)
|
||||
{
|
||||
// Cache access in non-volatile variable
|
||||
// indexR isn't allowed to cache in the audio throttling loop as it
|
||||
// needs to get updates to not deadlock.
|
||||
u32 indexW = Common::AtomicLoad(m_indexW);
|
||||
|
||||
if (m_throttle)
|
||||
if (m_mixer->m_throttle)
|
||||
{
|
||||
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
||||
while (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= MAX_SAMPLES * 2)
|
||||
@ -155,3 +167,12 @@ void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
return;
|
||||
}
|
||||
|
||||
void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
{
|
||||
m_dma_mixer.PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples)
|
||||
{
|
||||
m_streaming_mixer.PushSamples(samples, num_samples);
|
||||
}
|
||||
|
@ -21,23 +21,15 @@
|
||||
class CMixer {
|
||||
|
||||
public:
|
||||
CMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000, unsigned int BackendSampleRate = 32000)
|
||||
: m_aiSampleRate(AISampleRate)
|
||||
, m_dacSampleRate(DACSampleRate)
|
||||
, m_bits(16)
|
||||
, m_channels(2)
|
||||
CMixer(unsigned int BackendSampleRate)
|
||||
: m_dma_mixer(this, 32000)
|
||||
, m_streaming_mixer(this, 48000)
|
||||
, m_sampleRate(BackendSampleRate)
|
||||
, m_logAudio(0)
|
||||
, m_indexW(0)
|
||||
, m_indexR(0)
|
||||
, m_numLeftI(0.0f)
|
||||
, m_throttle(false)
|
||||
, m_speed(0)
|
||||
{
|
||||
// AyuanX: The internal (Core & DSP) sample rate is fixed at 32KHz
|
||||
// So when AI/DAC sample rate differs than 32KHz, we have to do re-sampling
|
||||
m_sampleRate = BackendSampleRate;
|
||||
|
||||
memset(m_buffer, 0, sizeof(m_buffer));
|
||||
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized (AISampleRate:%i, DACSampleRate:%i)", AISampleRate, DACSampleRate);
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
|
||||
}
|
||||
|
||||
virtual ~CMixer() {}
|
||||
@ -47,7 +39,8 @@ public:
|
||||
|
||||
// Called from main thread
|
||||
virtual void PushSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int GetSampleRate() const {return m_sampleRate;}
|
||||
virtual void PushStreamingSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int GetSampleRate() const { return m_sampleRate; }
|
||||
|
||||
void SetThrottle(bool use) { m_throttle = use;}
|
||||
|
||||
@ -87,11 +80,30 @@ public:
|
||||
void UpdateSpeed(volatile float val) { m_speed = val; }
|
||||
|
||||
protected:
|
||||
class MixerFifo {
|
||||
public:
|
||||
MixerFifo(CMixer *mixer, unsigned sample_rate)
|
||||
: m_mixer(mixer)
|
||||
, m_input_sample_rate(sample_rate)
|
||||
, m_indexW(0)
|
||||
, m_indexR(0)
|
||||
, m_numLeftI(0.0f)
|
||||
{
|
||||
memset(m_buffer, 0, sizeof(m_buffer));
|
||||
}
|
||||
void PushSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
|
||||
private:
|
||||
CMixer *m_mixer;
|
||||
unsigned m_input_sample_rate;
|
||||
short m_buffer[MAX_SAMPLES * 2];
|
||||
volatile u32 m_indexW;
|
||||
volatile u32 m_indexR;
|
||||
float m_numLeftI;
|
||||
};
|
||||
MixerFifo m_dma_mixer;
|
||||
MixerFifo m_streaming_mixer;
|
||||
unsigned int m_sampleRate;
|
||||
unsigned int m_aiSampleRate;
|
||||
unsigned int m_dacSampleRate;
|
||||
int m_bits;
|
||||
int m_channels;
|
||||
|
||||
WaveFileWriter g_wave_writer;
|
||||
|
||||
@ -99,14 +111,7 @@ protected:
|
||||
|
||||
bool m_throttle;
|
||||
|
||||
short m_buffer[MAX_SAMPLES * 2];
|
||||
volatile u32 m_indexW;
|
||||
volatile u32 m_indexR;
|
||||
|
||||
std::mutex m_csMixing;
|
||||
float m_numLeftI;
|
||||
|
||||
volatile float m_speed; // Current rate of the emulation (1.0 = 100% speed)
|
||||
private:
|
||||
|
||||
};
|
||||
|
Reference in New Issue
Block a user