From 2956ffa5be8c564274b3ba3eeb5ed765f2ee3ede Mon Sep 17 00:00:00 2001 From: degasus Date: Thu, 6 Feb 2014 06:38:04 +0100 Subject: [PATCH 1/5] audiocommon: remove 1:1 interpolation The usual one is 32->48 khz interpolation. So there is no need in a special 1:1 interpolation only for performance. --- Source/Core/AudioCommon/Mixer.cpp | 68 +++++++++---------------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index 788a3a29b5..a015f25f7f 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -56,59 +56,31 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) if (m_AIplaying) { numLeft = (numLeft > numSamples) ? numSamples : numLeft; - if (AudioInterface::GetAIDSampleRate() == m_sampleRate) // (1:1) - { -#if _M_SSE >= 0x301 - if (cpu_info.bSSSE3 && !((numLeft * 2) % 8)) - { - static const __m128i sr_mask = - _mm_set_epi32(0x0C0D0E0FL, 0x08090A0BL, - 0x04050607L, 0x00010203L); + //render numleft sample pairs to samples[] + //advance indexR with sample position + //remember fractional offset - for (unsigned int i = 0; i < numLeft * 2; i += 8) - { - _mm_storeu_si128((__m128i *)&samples[i], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&m_buffer[(indexR + i) & INDEX_MASK]), sr_mask)); - } - } - else -#endif - { - for (unsigned int i = 0; i < numLeft * 2; i+=2) - { - samples[i] = Common::swap16(m_buffer[(indexR + i + 1) & INDEX_MASK]); - samples[i+1] = Common::swap16(m_buffer[(indexR + i) & INDEX_MASK]); - } - } - indexR += numLeft * 2; - } - else //linear interpolation - { - //render numleft sample pairs to samples[] - //advance indexR with sample position - //remember fractional offset + static u32 frac = 0; + const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate ); - static u32 frac = 0; - const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate ); + for (u32 i = 0; i < numLeft * 2; i+=2) { + u32 indexR2 = indexR + 2; //next sample + if ((indexR2 & INDEX_MASK) == (indexW & INDEX_MASK)) //..if it exists + indexR2 = indexR; - for (u32 i = 0; i < numLeft * 2; i+=2) { - u32 indexR2 = indexR + 2; //next sample - if ((indexR2 & INDEX_MASK) == (indexW & INDEX_MASK)) //..if it exists - indexR2 = indexR; + 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; + samples[i+1] = sampleL; - 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; - samples[i+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; + samples[i] = sampleR; - 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; - samples[i] = sampleR; - - frac += ratio; - indexR += 2 * (u16)(frac >> 16); - frac &= 0xffff; - } + frac += ratio; + indexR += 2 * (u16)(frac >> 16); + frac &= 0xffff; } } else { From d20dbbc92fe78bd5e5005bb6512c688beb3df681 Mon Sep 17 00:00:00 2001 From: degasus Date: Thu, 6 Feb 2014 06:55:45 +0100 Subject: [PATCH 2/5] audiocommon: sync mixer by fifo instead of estimate values --- Source/Core/AudioCommon/Mixer.cpp | 115 +++++++++--------------------- Source/Core/AudioCommon/Mixer.h | 3 - 2 files changed, 34 insertions(+), 84 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index a015f25f7f..dd1e7ed386 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -32,16 +32,7 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) return numSamples; } - unsigned int numLeft = GetNumSamples(); - if (m_AIplaying) { - if (numLeft < numSamples)//cannot do much about this - m_AIplaying = false; - if (numLeft < MAX_SAMPLES/4)//low watermark - m_AIplaying = false; - } else { - if (numLeft > MAX_SAMPLES/2)//high watermark - m_AIplaying = true; - } + unsigned int currentSample = 0; // Cache access in non-volatile variable // This is the only function changing the read value, so it's safe to @@ -53,72 +44,55 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) u32 indexR = Common::AtomicLoad(m_indexR); u32 indexW = Common::AtomicLoad(m_indexW); - if (m_AIplaying) { - numLeft = (numLeft > numSamples) ? numSamples : numLeft; + //render numleft sample pairs to samples[] + //advance indexR with sample position + //remember fractional offset - //render numleft sample pairs to samples[] - //advance indexR with sample position - //remember fractional offset + static u32 frac = 0; + const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate ); - static u32 frac = 0; - const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate ); + if(ratio > 0x10000) + ERROR_LOG(AUDIO, "ratio out of range"); - for (u32 i = 0; i < numLeft * 2; i+=2) { - u32 indexR2 = indexR + 2; //next sample - if ((indexR2 & INDEX_MASK) == (indexW & INDEX_MASK)) //..if it exists - indexR2 = indexR; + 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; - samples[i+1] = sampleL; + 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; + 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; - samples[i] = sampleR; + 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; + samples[currentSample] = sampleR; - frac += ratio; - indexR += 2 * (u16)(frac >> 16); - frac &= 0xffff; - } - - } else { - numLeft = 0; + frac += ratio; + indexR += 2 * (u16)(frac >> 16); + frac &= 0xffff; } // Padding - if (numSamples > numLeft) + unsigned 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) { - unsigned short s[2]; - s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]); - s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]); - for (unsigned int i = numLeft*2; i < numSamples*2; i+=2) - *(u32*)(samples+i) = *(u32*)(s); -// memset(&samples[numLeft * 2], 0, (numSamples - numLeft) * 4); + samples[currentSample] = s[0]; + samples[currentSample+1] = s[1]; } // Flush cached variable Common::AtomicStore(m_indexR, indexR); - //when logging, also throttle HLE audio - if (m_logAudio) { - if (m_AIplaying) { - Premix(samples, numLeft); + // Add the DSPHLE sound, re-sampling is done inside + Premix(samples, numSamples); - AudioInterface::Callback_GetStreaming(samples, numLeft, m_sampleRate); - - g_wave_writer.AddStereoSamples(samples, numLeft); - } - } - else { //or mix as usual - // Add the DSPHLE sound, re-sampling is done inside - Premix(samples, numSamples); - - // Add the DTK Music - // Re-sampling is done inside - AudioInterface::Callback_GetStreaming(samples, numSamples, m_sampleRate); - } + // 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; } @@ -170,24 +144,3 @@ void CMixer::PushSamples(const short *samples, unsigned int num_samples) return; } -unsigned int CMixer::GetNumSamples() -{ - // Guess how many samples would be available after interpolation. - // As interpolation needs at least on sample from the future to - // linear interpolate between them, one sample less is available. - // We also can't say the current interpolation state (specially - // the frac), so to be sure, subtract one again to be sure not - // to underflow the fifo. - - u32 numSamples = ((Common::AtomicLoad(m_indexW) - Common::AtomicLoad(m_indexR)) & INDEX_MASK) / 2; - - if (AudioInterface::GetAIDSampleRate() == m_sampleRate) - ; //numSamples = numSamples; // 1:1 - else if (m_sampleRate == 48000 && AudioInterface::GetAIDSampleRate() == 32000) - numSamples = numSamples * 3 / 2 - 2; // most common case - else - numSamples = numSamples * m_sampleRate / AudioInterface::GetAIDSampleRate() - 2; - - return numSamples; -} - diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index 6b2c4b95f3..e38a64b067 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -24,7 +24,6 @@ public: , m_logAudio(0) , m_indexW(0) , m_indexR(0) - , m_AIplaying(true) { // 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 @@ -40,7 +39,6 @@ public: // Called from audio threads virtual unsigned int Mix(short* samples, unsigned int numSamples); virtual void Premix(short * /*samples*/, unsigned int /*numSamples*/) {} - unsigned int GetNumSamples(); // Called from main thread virtual void PushSamples(const short* samples, unsigned int num_samples); @@ -98,7 +96,6 @@ protected: volatile u32 m_indexW; volatile u32 m_indexR; - bool m_AIplaying; std::mutex m_csMixing; volatile float m_speed; // Current rate of the emulation (1.0 = 100% speed) From ca9fd64df916949518fe8dfd3ca9c0432eade388 Mon Sep 17 00:00:00 2001 From: degasus Date: Thu, 6 Feb 2014 13:03:40 +0100 Subject: [PATCH 3/5] controll the interpolation frac by the fifo size --- Source/Core/AudioCommon/Mixer.cpp | 8 +++++++- Source/Core/AudioCommon/Mixer.h | 10 ++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index dd1e7ed386..99c5a56176 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -44,12 +44,18 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) u32 indexR = Common::AtomicLoad(m_indexR); u32 indexW = Common::AtomicLoad(m_indexW); + float numLeft = ((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; + if(offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; + //render numleft sample pairs to samples[] //advance indexR with sample position //remember fractional offset static u32 frac = 0; - const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate ); + const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate + offset ); if(ratio > 0x10000) ERROR_LOG(AUDIO, "ratio out of range"); diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index e38a64b067..954aa7b99a 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -8,9 +8,13 @@ #include "StdMutex.h" // 16 bit Stereo -#define MAX_SAMPLES (1024 * 8) +#define MAX_SAMPLES (1024 * 2) // 64ms #define INDEX_MASK (MAX_SAMPLES * 2 - 1) -#define RESERVED_SAMPLES (256) + +#define LOW_WATERMARK 1280 // 40 ms +#define MAX_FREQ_SHIFT 0x0100 // of 0x10000 +#define CONTROL_FACTOR 0.3 // in freq_shift per fifo size offset +#define CONTROL_AVG 32 class CMixer { @@ -24,6 +28,7 @@ public: , m_logAudio(0) , m_indexW(0) , m_indexR(0) + , m_numLeftI(0.0f) { // 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 @@ -97,6 +102,7 @@ protected: 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: From bbd58b8f6af60ae7743dea4efc05107a53dd4abf Mon Sep 17 00:00:00 2001 From: degasus Date: Tue, 11 Feb 2014 14:53:53 +0100 Subject: [PATCH 4/5] change AI sampling rate based on framelimit --- Source/Core/AudioCommon/Mixer.cpp | 11 ++++++++++- Source/Core/AudioCommon/Mixer.h | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index 99c5a56176..31698a931d 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -7,6 +7,8 @@ #include "AudioCommon.h" #include "CPUDetect.h" #include "../Core/Host.h" +#include "ConfigManager.h" +#include "HW/VideoInterface.h" #include "../Core/HW/AudioInterface.h" @@ -54,8 +56,15 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) //advance indexR with sample position //remember fractional offset + u32 framelimit = SConfig::GetInstance().m_Framelimit; + float aid_sample_rate = AudioInterface::GetAIDSampleRate() + offset; + if (framelimit > 2) + { + aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate; + } + static u32 frac = 0; - const u32 ratio = (u32)( 65536.0f * (float)AudioInterface::GetAIDSampleRate() / (float)m_sampleRate + offset ); + const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_sampleRate ); if(ratio > 0x10000) ERROR_LOG(AUDIO, "ratio out of range"); diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index 954aa7b99a..4f31d66d7e 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -12,8 +12,8 @@ #define INDEX_MASK (MAX_SAMPLES * 2 - 1) #define LOW_WATERMARK 1280 // 40 ms -#define MAX_FREQ_SHIFT 0x0100 // of 0x10000 -#define CONTROL_FACTOR 0.3 // in freq_shift per fifo size offset +#define MAX_FREQ_SHIFT 200 // per 32000 Hz +#define CONTROL_FACTOR 0.2 // in freq_shift per fifo size offset #define CONTROL_AVG 32 class CMixer { From 9e56b1d3434c66316d974f324ed45a376d1d1fc0 Mon Sep 17 00:00:00 2001 From: degasus Date: Thu, 13 Feb 2014 13:22:29 +0100 Subject: [PATCH 5/5] Disable framerate correction for OpenAL OpenAL itself stretch the time on slowdowns, so the Mixer isn't allowed also to change the rate. --- Source/Core/AudioCommon/Mixer.cpp | 4 ++-- Source/Core/AudioCommon/Mixer.h | 2 +- Source/Core/AudioCommon/OpenALStream.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index 31698a931d..966b355dc0 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -20,7 +20,7 @@ #endif // Executed from sound stream thread -unsigned int CMixer::Mix(short* samples, unsigned int numSamples) +unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_framelimit) { if (!samples) return 0; @@ -58,7 +58,7 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) u32 framelimit = SConfig::GetInstance().m_Framelimit; float aid_sample_rate = AudioInterface::GetAIDSampleRate() + offset; - if (framelimit > 2) + if (consider_framelimit && framelimit > 2) { aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate; } diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index 4f31d66d7e..d5e82edf4b 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -42,7 +42,7 @@ public: virtual ~CMixer() {} // Called from audio threads - virtual unsigned int Mix(short* samples, unsigned int numSamples); + virtual unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); virtual void Premix(short * /*samples*/, unsigned int /*numSamples*/) {} // Called from main thread diff --git a/Source/Core/AudioCommon/OpenALStream.cpp b/Source/Core/AudioCommon/OpenALStream.cpp index ce59dc6c57..6e741e8e71 100644 --- a/Source/Core/AudioCommon/OpenALStream.cpp +++ b/Source/Core/AudioCommon/OpenALStream.cpp @@ -192,7 +192,7 @@ void OpenALStream::SoundLoop() unsigned int minSamples = surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION) numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples; - numSamples = m_mixer->Mix(realtimeBuffer, numSamples); + numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false); // Convert the samples from short to float float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS];