mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-25 07:09:48 -06:00
@ -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
|
||||
@ -45,7 +34,7 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
||||
u32 indexR = Common::AtomicLoad(m_indexR);
|
||||
u32 indexW = Common::AtomicLoad(m_indexW);
|
||||
|
||||
float numLeft = ((indexW - indexR) & INDEX_MASK) / 2;
|
||||
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
|
||||
m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG-1)) / CONTROL_AVG;
|
||||
float offset = (m_numLeftI - LOW_WATERMARK) * CONTROL_FACTOR;
|
||||
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT;
|
||||
@ -56,29 +45,36 @@ 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");
|
||||
s32 lvolume = m_LVolume;
|
||||
s32 rvolume = m_RVolume;
|
||||
|
||||
// TODO: consider a higher-quality resampling algorithm.
|
||||
for (; currentSample < numSamples*2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample+=2) {
|
||||
u32 indexR2 = indexR + 2; //next sample
|
||||
|
||||
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 = (sampleL * lvolume) >> 8;
|
||||
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 = (sampleR * rvolume) >> 8;
|
||||
sampleR += samples[currentSample];
|
||||
MathUtil::Clamp(&sampleR, -32767, 32767);
|
||||
samples[currentSample] = sampleR;
|
||||
|
||||
frac += ratio;
|
||||
@ -87,36 +83,57 @@ 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)
|
||||
s[0] = (s[0] * lvolume) >> 8;
|
||||
s[1] = (s[1] * rvolume) >> 8;
|
||||
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 +172,23 @@ 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);
|
||||
}
|
||||
|
||||
void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume)
|
||||
{
|
||||
m_streaming_mixer.SetVolume(lvolume, rvolume);
|
||||
}
|
||||
|
||||
void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume)
|
||||
{
|
||||
m_LVolume = lvolume + (lvolume >> 7);
|
||||
m_RVolume = rvolume + (rvolume >> 7);
|
||||
}
|
||||
|
@ -15,29 +15,21 @@
|
||||
|
||||
#define LOW_WATERMARK 1280 // 40 ms
|
||||
#define MAX_FREQ_SHIFT 200 // per 32000 Hz
|
||||
#define CONTROL_FACTOR 0.2 // in freq_shift per fifo size offset
|
||||
#define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset
|
||||
#define CONTROL_AVG 32
|
||||
|
||||
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,9 @@ 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 SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
|
||||
void SetThrottle(bool use) { m_throttle = use;}
|
||||
|
||||
@ -87,11 +81,36 @@ 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)
|
||||
, m_LVolume(256)
|
||||
, m_RVolume(256)
|
||||
{
|
||||
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);
|
||||
void SetVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
private:
|
||||
CMixer *m_mixer;
|
||||
unsigned m_input_sample_rate;
|
||||
short m_buffer[MAX_SAMPLES * 2];
|
||||
volatile u32 m_indexW;
|
||||
volatile u32 m_indexR;
|
||||
// Volume ranges from 0-256
|
||||
volatile s32 m_LVolume;
|
||||
volatile s32 m_RVolume;
|
||||
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 +118,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