From e0b0ef38680365edd2c3eab6dabe65ed65d3192a Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Mon, 23 Feb 2015 18:43:13 +0100 Subject: [PATCH] Revert "High quality resampler" --- Source/Core/AudioCommon/Mixer.cpp | 313 +++++++++--------------------- Source/Core/AudioCommon/Mixer.h | 181 +++++------------ 2 files changed, 147 insertions(+), 347 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index 452e57c38c..90f771d3fa 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -15,89 +15,35 @@ // UGLINESS #include "Core/PowerPC/PowerPC.h" -#ifndef M_PI -#define M_PI 3.14159265358979323846 +#if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__) +#include #endif -const float CMixer::LOW_WATERMARK = 1280; -const float CMixer::MAX_FREQ_SHIFT = 200; -const float CMixer::CONTROL_FACTOR = 0.2f; -const float CMixer::CONTROL_AVG = 32; - -const double CMixer::Resampler::LOWPASS_ROLLOFF = 0.9; -const double CMixer::Resampler::KAISER_BETA = 6.0; -const double CMixer::Resampler::BESSEL_EPSILON = 1e-21; - -void CMixer::LinearMixerFifo::Interpolate(u32 left_input_index, float* left_output, float* right_output) +// Executed from sound stream thread +unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, bool consider_framelimit) { - *left_output = (1 - m_fraction) * m_float_buffer[left_input_index & INDEX_MASK] - + m_fraction * m_float_buffer[(left_input_index + 2) & INDEX_MASK]; - *right_output = (1 - m_fraction) * m_float_buffer[(left_input_index + 1) & INDEX_MASK] - + m_fraction * m_float_buffer[(left_input_index + 3) & INDEX_MASK]; -} + unsigned int currentSample = 0; -//see https://ccrma.stanford.edu/~jos/resample/Implementation.html -void CMixer::WindowedSincMixerFifo::Interpolate(u32 left_input_index, float* left_output, float* right_output) -{ - double left_temp = 0, right_temp = 0; + // Cache access in non-volatile variable + // This is the only function changing the read value, so it's safe to + // cache it locally although it's written here. + // The writing pointer will be modified outside, but it will only increase, + // so we will just ignore new written data while interpolating. + // Without this cache, the compiler wouldn't be allowed to optimize the + // interpolation loop. + u32 indexR = Common::AtomicLoad(m_indexR); + u32 indexW = Common::AtomicLoad(m_indexW); - // left wing of filter - double left_wing_fraction = (m_fraction * Resampler::SAMPLES_PER_CROSSING); - u32 left_wing_index = (u32) left_wing_fraction; - left_wing_fraction -= left_wing_index; + 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; + if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; - const Resampler& resampler = m_mixer->m_resampler; - u32 current_index = left_input_index; - while (left_wing_index < resampler.m_lowpass_filter.size()) - { - double impulse = resampler.m_lowpass_filter[left_wing_index]; - impulse += resampler.m_lowpass_delta[left_wing_index] * left_wing_fraction; + //render numleft sample pairs to samples[] + //advance indexR with sample position + //remember fractional offset - left_temp += (float) m_float_buffer[current_index & INDEX_MASK] * impulse; - right_temp += (float) m_float_buffer[(current_index + 1) & INDEX_MASK] * impulse; - - left_wing_index += Resampler::SAMPLES_PER_CROSSING; - current_index -= 2; - } - - // right wing of filter - double right_wing_fraction = (1 - m_fraction) * Resampler::SAMPLES_PER_CROSSING; - u32 right_wing_index = ((u32) right_wing_fraction) % Resampler::SAMPLES_PER_CROSSING; - right_wing_fraction -= right_wing_index; - - // we already used read_index for left wing - current_index = left_input_index + 2; - while (right_wing_index < resampler.m_lowpass_filter.size()) - { - double impulse = resampler.m_lowpass_filter[right_wing_index]; - impulse += resampler.m_lowpass_delta[right_wing_index] * right_wing_fraction; - - left_temp += (float) m_float_buffer[current_index & INDEX_MASK] * impulse; - right_temp += (float) m_float_buffer[(current_index + 1) & INDEX_MASK] * impulse; - - right_wing_index += Resampler::SAMPLES_PER_CROSSING; - current_index += 2; - } - - *left_output = (float) left_temp; - *right_output = (float) right_temp; -} - -void CMixer::MixerFifo::Mix(std::vector& samples, u32 numSamples, bool consider_framelimit) -{ - u32 current_sample = 0; - - // Cache access in non-volatile variable so interpolation loop can be optimized - u32 read_index = Common::AtomicLoad(m_read_index); - const u32 write_index = Common::AtomicLoad(m_write_index); - - // Sync input rate by fifo size - float num_left = (float) (((write_index - read_index) & INDEX_MASK) / 2); - m_num_left_i = (num_left + m_num_left_i * (CONTROL_AVG - 1)) / CONTROL_AVG; - float offset = (m_num_left_i - LOW_WATERMARK) * CONTROL_FACTOR; - MathUtil::Clamp(&offset, -MAX_FREQ_SHIFT, MAX_FREQ_SHIFT); - - // adjust framerate with framelimit u32 framelimit = SConfig::GetInstance().m_Framelimit; float aid_sample_rate = m_input_sample_rate + offset; if (consider_framelimit && framelimit > 1) @@ -105,146 +51,134 @@ void CMixer::MixerFifo::Mix(std::vector& samples, u32 numSamples, bool co aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate; } - // ratio = 1 / upscale_factor = stepsize for each sample - // e.g. going from 32khz to 48khz is 1 / (3 / 2) = 2 / 3 - // note because of syncing and framelimit, ratio will rarely be exactly 2 / 3 - float ratio = aid_sample_rate / (float) m_mixer->m_sample_rate; + const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate); - float l_volume = (float) m_lvolume / 255.f; - float r_volume = (float) m_rvolume / 255.f; + s32 lvolume = m_LVolume; + s32 rvolume = m_RVolume; - // for each output sample pair (left and right), - // linear interpolate between current and next sample - // increment output sample position - // increment input sample position by ratio, store fraction - // QUESTION: do we need to check for NUM_CROSSINGS samples before we interpolate? - // seems to work fine as is - for (; current_sample < numSamples * 2 && ((write_index - read_index) & INDEX_MASK) > 0; current_sample += 2) + // TODO: consider a higher-quality resampling algorithm. + for (; currentSample < numSamples * 2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample += 2) { - float l_output, r_output; + u32 indexR2 = indexR + 2; //next sample - Interpolate(read_index, &l_output, &r_output); + 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)m_frac) >> 16; + sampleL = (sampleL * lvolume) >> 8; + sampleL += samples[currentSample + 1]; + MathUtil::Clamp(&sampleL, -32767, 32767); + samples[currentSample + 1] = sampleL; - samples[current_sample + 1] += l_volume * l_output; - samples[current_sample] += r_volume * r_output; + 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)m_frac) >> 16; + sampleR = (sampleR * rvolume) >> 8; + sampleR += samples[currentSample]; + MathUtil::Clamp(&sampleR, -32767, 32767); + samples[currentSample] = sampleR; - m_fraction += ratio; - read_index += 2 * (s32) m_fraction; - m_fraction = m_fraction - (s32) m_fraction; + m_frac += ratio; + indexR += 2 * (u16)(m_frac >> 16); + m_frac &= 0xffff; } - // pad output if not enough input samples - float s[2]; - s[0] = m_float_buffer[(read_index - 1) & INDEX_MASK] * r_volume; - s[1] = m_float_buffer[(read_index - 2) & INDEX_MASK] * l_volume; - for (; current_sample < numSamples * 2; current_sample += 2) + // Padding + short s[2]; + s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]); + s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]); + s[0] = (s[0] * rvolume) >> 8; + s[1] = (s[1] * lvolume) >> 8; + for (; currentSample < numSamples * 2; currentSample += 2) { - samples[current_sample] += s[0]; - samples[current_sample + 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; } - // update read index - Common::AtomicStore(m_read_index, read_index); + // Flush cached variable + Common::AtomicStore(m_indexR, indexR); + + return numSamples; } -// we NEED dithering going from float -> 16bit -void CMixer::TriangleDither(float* l_sample, float* r_sample) -{ - float left_dither = DITHER_NOISE; - float right_dither = DITHER_NOISE; - *l_sample = (*l_sample) + left_dither - m_l_dither_prev; - *r_sample = (*r_sample) + right_dither - m_r_dither_prev; - m_l_dither_prev = left_dither; - m_r_dither_prev = right_dither; -} - -u32 CMixer::Mix(s16* samples, u32 num_samples, bool consider_framelimit) +unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit) { if (!samples) return 0; - std::lock_guard lk(m_cs_mixing); + std::lock_guard lk(m_csMixing); + + memset(samples, 0, num_samples * 2 * sizeof(short)); if (PowerPC::GetState() != PowerPC::CPU_RUNNING) { // Silence - memset(samples, 0, num_samples * 2 * sizeof(s16)); return num_samples; } - // reset float output buffer - m_output_buffer.resize(num_samples * 2); - std::fill_n(m_output_buffer.begin(), num_samples * 2, 0.f); - - m_dma_mixer.Mix(m_output_buffer, num_samples, consider_framelimit); - m_streaming_mixer.Mix(m_output_buffer, num_samples, consider_framelimit); - m_wiimote_speaker_mixer.Mix(m_output_buffer, num_samples, consider_framelimit); - - // dither and clamp - for (u32 i = 0; i < num_samples * 2; i += 2) - { - float l_output = m_output_buffer[i + 1]; - float r_output = m_output_buffer[i]; - TriangleDither(&m_output_buffer[i + 1], &m_output_buffer[i]); - - MathUtil::Clamp(&l_output, -1.f, 1.f); - samples[i + 1] = FloatToSigned16(l_output); - - MathUtil::Clamp(&r_output, -1.f, 1.f); - samples[i] = FloatToSigned16(r_output); - } - + m_dma_mixer.Mix(samples, num_samples, consider_framelimit); + m_streaming_mixer.Mix(samples, num_samples, consider_framelimit); + m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit); return num_samples; } -void CMixer::MixerFifo::PushSamples(const s16* samples, u32 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 current_write_index = Common::AtomicLoad(m_write_index); + u32 indexW = Common::AtomicLoad(m_indexW); // Check if we have enough free space // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW - if (num_samples * 2 + ((current_write_index - Common::AtomicLoad(m_read_index)) & INDEX_MASK) >= MAX_SAMPLES * 2) + if (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= MAX_SAMPLES * 2) return; // AyuanX: Actual re-sampling work has been moved to sound thread // to alleviate the workload on main thread - // convert to float while copying to buffer - for (u32 i = 0; i < num_samples * 2; ++i) + // and we simply store raw data here to make fast mem copy + int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short); + if (over_bytes > 0) { - m_float_buffer[(current_write_index + i) & INDEX_MASK] = Signed16ToFloat(Common::swap16(samples[i])); + memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes); + memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes); + } + else + { + memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4); } - Common::AtomicAdd(m_write_index, num_samples * 2); + Common::AtomicAdd(m_indexW, num_samples * 2); return; } -void CMixer::PushSamples(const s16* samples, u32 num_samples) +void CMixer::PushSamples(const short *samples, unsigned int num_samples) { m_dma_mixer.PushSamples(samples, num_samples); if (m_log_dsp_audio) g_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples); } -void CMixer::PushStreamingSamples(const s16* samples, u32 num_samples) +void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples) { m_streaming_mixer.PushSamples(samples, num_samples); if (m_log_dtk_audio) g_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples); } -void CMixer::PushWiimoteSpeakerSamples(const s16* samples, u32 num_samples, u32 sample_rate) +void CMixer::PushWiimoteSpeakerSamples(const short *samples, unsigned int num_samples, unsigned int sample_rate) { - s16 samples_stereo[MAX_SAMPLES * 2]; + short samples_stereo[MAX_SAMPLES * 2]; if (num_samples < MAX_SAMPLES) { m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate); - for (u32 i = 0; i < num_samples; ++i) + for (unsigned int i = 0; i < num_samples; ++i) { samples_stereo[i * 2] = Common::swap16(samples[i]); samples_stereo[i * 2 + 1] = Common::swap16(samples[i]); @@ -254,90 +188,33 @@ void CMixer::PushWiimoteSpeakerSamples(const s16* samples, u32 num_samples, u32 } } -void CMixer::SetDMAInputSampleRate(u32 rate) +void CMixer::SetDMAInputSampleRate(unsigned int rate) { m_dma_mixer.SetInputSampleRate(rate); } -void CMixer::SetStreamInputSampleRate(u32 rate) +void CMixer::SetStreamInputSampleRate(unsigned int rate) { m_streaming_mixer.SetInputSampleRate(rate); } -void CMixer::SetStreamingVolume(u32 lvolume, u32 rvolume) +void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume) { m_streaming_mixer.SetVolume(lvolume, rvolume); } -void CMixer::SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume) +void CMixer::SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume) { m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume); } -void CMixer::MixerFifo::SetInputSampleRate(u32 rate) +void CMixer::MixerFifo::SetInputSampleRate(unsigned int rate) { m_input_sample_rate = rate; } -void CMixer::MixerFifo::SetVolume(u32 lvolume, u32 rvolume) +void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume) { - m_lvolume = lvolume; - m_rvolume = rvolume; + m_LVolume = lvolume + (lvolume >> 7); + m_RVolume = rvolume + (rvolume >> 7); } - -void CMixer::MixerFifo::GetVolume(u32* lvolume, u32* rvolume) const -{ - *lvolume = m_lvolume; - *rvolume = m_rvolume; -} - -// I_0(x) = summation((((x/2)^k) / k!)^2) for k from 0 to Infinity -double CMixer::Resampler::ModBessel0th(const double x) -{ - double sum = 1; - s32 factorial_store = 1; - double half_x = x / 2.f; - double previous = 1; - do - { - double temp = half_x / (double) factorial_store; - temp *= temp; - previous *= temp; - sum += previous; - factorial_store++; - } while (previous >= BESSEL_EPSILON * sum); - return sum; -} - -// one wing of FIR by using sinc * Kaiser window -void CMixer::Resampler::PopulateFilterCoeff() -{ - // Generate sinc table - m_lowpass_filter[0] = LOWPASS_ROLLOFF; - for (u32 i = 1; i < m_lowpass_filter.size(); ++i) - { - double temp = M_PI * (double) i / SAMPLES_PER_CROSSING; - m_lowpass_filter[i] = sin(temp * LOWPASS_ROLLOFF) / temp; - } - - // use a Kaiser window - // https://ccrma.stanford.edu/~jos/sasp/Kaiser_Window.html - // - double I0_beta = 1.0 / ModBessel0th(KAISER_BETA); - double inside = 1.0 / (m_lowpass_filter.size() - 1); - for (u32 i = 1; i < m_lowpass_filter.size(); ++i) - { - double temp = (double) i * inside; - temp = 1.0 - temp * temp; - temp = (temp < 0) ? 0 : temp; - m_lowpass_filter[i] *= ModBessel0th(KAISER_BETA * sqrt(temp)) * I0_beta; - } - - // store deltas in delta table for faster lookup to interpolate impulse - for (u32 i = 0; i < m_lowpass_filter.size() - 1; ++i) - { - m_lowpass_delta[i] = m_lowpass_filter[i + 1] - m_lowpass_filter[i]; - } - m_lowpass_delta.back() = -1 * m_lowpass_filter.back(); - -} \ No newline at end of file diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index c4eb343f9f..ba9809d5bc 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -4,56 +4,50 @@ #pragma once -#include #include #include -#include #include "AudioCommon/WaveFile.h" -// Dither define -#define DITHER_NOISE (rand() / (float) RAND_MAX - 0.5f) +// 16 bit Stereo +#define MAX_SAMPLES (1024 * 2) // 64ms +#define INDEX_MASK (MAX_SAMPLES * 2 - 1) + +#define LOW_WATERMARK 1280 // 40 ms +#define MAX_FREQ_SHIFT 200 // per 32000 Hz +#define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset +#define CONTROL_AVG 32 + +class CMixer { -class CMixer -{ public: - CMixer(u32 BackendSampleRate) + CMixer(unsigned int BackendSampleRate) : m_dma_mixer(this, 32000) , m_streaming_mixer(this, 48000) , m_wiimote_speaker_mixer(this, 3000) - , m_sample_rate(BackendSampleRate) + , m_sampleRate(BackendSampleRate) , m_log_dtk_audio(0) , m_log_dsp_audio(0) , m_speed(0) - , m_l_dither_prev(0) - , m_r_dither_prev(0) { INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized"); - m_output_buffer.reserve(MAX_SAMPLES * 2); } - static const u32 MAX_SAMPLES = 2048; - static const u32 INDEX_MASK = MAX_SAMPLES * 2 - 1; - static const float LOW_WATERMARK; - static const float MAX_FREQ_SHIFT; - static const float CONTROL_FACTOR; - static const float CONTROL_AVG; - virtual ~CMixer() {} // Called from audio threads - u32 Mix(s16* samples, u32 numSamples, bool consider_framelimit = true); + virtual unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); // Called from main thread - virtual void PushSamples(const s16* samples, u32 num_samples); - virtual void PushStreamingSamples(const s16* samples, u32 num_samples); - virtual void PushWiimoteSpeakerSamples(const s16* samples, u32 num_samples, u32 sample_rate); - u32 GetSampleRate() const { return m_sample_rate; } + virtual void PushSamples(const short* samples, unsigned int num_samples); + virtual void PushStreamingSamples(const short* samples, unsigned int num_samples); + virtual void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, unsigned int sample_rate); + unsigned int GetSampleRate() const { return m_sampleRate; } - void SetDMAInputSampleRate(u32 rate); - void SetStreamInputSampleRate(u32 rate); - void SetStreamingVolume(u32 lvolume, u32 rvolume); - void SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume); + void SetDMAInputSampleRate(unsigned int rate); + void SetStreamInputSampleRate(unsigned int rate); + void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume); + void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume); virtual void StartLogDTKAudio(const std::string& filename) { @@ -113,98 +107,46 @@ public: } } - std::mutex& MixerCritical() { return m_cs_mixing; } + std::mutex& MixerCritical() { return m_csMixing; } float GetCurrentSpeed() const { return m_speed; } void UpdateSpeed(volatile float val) { m_speed = val; } protected: - class MixerFifo - { + class MixerFifo { public: - MixerFifo(CMixer* mixer, u32 sample_rate) + MixerFifo(CMixer *mixer, unsigned sample_rate) : m_mixer(mixer) , m_input_sample_rate(sample_rate) - , m_write_index(0) - , m_read_index(0) - , m_lvolume(255) - , m_rvolume(255) - , m_num_left_i(0.0f) - , m_fraction(0.0f) + , m_indexW(0) + , m_indexR(0) + , m_LVolume(256) + , m_RVolume(256) + , m_numLeftI(0.0f) + , m_frac(0) { - srand((u32) time(nullptr)); + memset(m_buffer, 0, sizeof(m_buffer)); } - virtual void Interpolate(u32 left_input_index, float* left_output, float* right_output) = 0; - void PushSamples(const s16* samples, u32 num_samples); - void Mix(std::vector& samples, u32 numSamples, bool consider_framelimit = true); - void SetInputSampleRate(u32 rate); - void SetVolume(u32 lvolume, u32 rvolume); - void GetVolume(u32* lvolume, u32* rvolume) const; - - protected: - CMixer* m_mixer; - u32 m_input_sample_rate; - - std::array m_float_buffer; - - volatile u32 m_write_index; - volatile u32 m_read_index; - - // Volume ranges from 0-255 - volatile u32 m_lvolume; - volatile u32 m_rvolume; - - float m_num_left_i; - float m_fraction; + void PushSamples(const short* samples, unsigned int num_samples); + unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); + void SetInputSampleRate(unsigned int rate); + 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; + u32 m_frac; }; - - class LinearMixerFifo : public MixerFifo - { - public: - LinearMixerFifo(CMixer* mixer, u32 sample_rate) : MixerFifo(mixer, sample_rate) {} - void Interpolate(u32 left_input_index, float* left_output, float* right_output) override; - }; - - class WindowedSincMixerFifo : public MixerFifo - { - public: - WindowedSincMixerFifo(CMixer* mixer, u32 sample_rate) : MixerFifo(mixer, sample_rate) {} - void Interpolate(u32 left_input_index, float* left_output, float* right_output) override; - }; - - class Resampler - { - static const double LOWPASS_ROLLOFF; - static const double KAISER_BETA; - static const double BESSEL_EPSILON; // acceptable delta for Kaiser Window calculation - - void PopulateFilterCoeff(); - double ModBessel0th(const double x); - public: - - static const u32 SAMPLES_PER_CROSSING = 4096; - static const u32 NUM_CROSSINGS = 35; - static const u32 WING_SIZE = SAMPLES_PER_CROSSING * (NUM_CROSSINGS - 1) / 2; - - Resampler() - { - PopulateFilterCoeff(); - } - - std::array m_lowpass_filter; - std::array m_lowpass_delta; - }; - - Resampler m_resampler; - - WindowedSincMixerFifo m_dma_mixer; - WindowedSincMixerFifo m_streaming_mixer; - - // Linear interpolation seems to be the best for Wiimote 3khz -> 48khz, for now. - // TODO: figure out why and make it work with the above FIR - LinearMixerFifo m_wiimote_speaker_mixer; - - u32 m_sample_rate; + MixerFifo m_dma_mixer; + MixerFifo m_streaming_mixer; + MixerFifo m_wiimote_speaker_mixer; + unsigned int m_sampleRate; WaveFileWriter g_wave_writer_dtk; WaveFileWriter g_wave_writer_dsp; @@ -212,26 +154,7 @@ protected: bool m_log_dtk_audio; bool m_log_dsp_audio; - std::mutex m_cs_mixing; + std::mutex m_csMixing; volatile float m_speed; // Current rate of the emulation (1.0 = 100% speed) - -private: - // converts [-32768, 32767] -> [-1.0, 1.0] - static inline float Signed16ToFloat(const s16 s) - { - return (s > 0) ? (float) (s / (float) 0x7fff) : (float) (s / (float) 0x8000); - } - - // converts [-1.0, 1.0] -> [-32768, 32767] - static inline s16 FloatToSigned16(const float f) - { - return (f > 0) ? (s16) (f * 0x7fff) : (s16) (f * 0x8000); - } - - void TriangleDither(float* l_sample, float* r_sample); - - std::vector m_output_buffer; - float m_l_dither_prev; - float m_r_dither_prev; -}; \ No newline at end of file +};