Skip to content

Commit

Permalink
Moved stuff around to consolidate.
Browse files Browse the repository at this point in the history
  • Loading branch information
kamiyo committed Dec 4, 2014
1 parent 5dae2b5 commit f4f260d
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 124 deletions.
162 changes: 55 additions & 107 deletions Source/Core/AudioCommon/Mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,73 +28,61 @@ const double CMixer::Resampler::LOWPASS_ROLLOFF = 0.9;
const double CMixer::Resampler::KAISER_BETA = 6.0;
const double CMixer::Resampler::BESSEL_EPSILON = 1e-21;

// 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
void CMixer::MixerFifo::MixLinear(std::vector<float>& samples, u32 numSamples, bool consider_framelimit)
void CMixer::LinearMixerFifo::Interpolate(u32 left_input_index, float* left_output, float* right_output)
{
u32 current_sample = 0;
*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];
}

// 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);
//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;

// 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);
// 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;

// adjust framerate with framelimit
u32 framelimit = SConfig::GetInstance().m_Framelimit;
float aid_sample_rate = m_input_sample_rate + offset;
if (consider_framelimit && framelimit > 1)
const Resampler& resampler = m_mixer->m_resampler;
u32 current_index = left_input_index;
while (left_wing_index < resampler.m_lowpass_filter.size())
{
aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate;
}
double impulse = resampler.m_lowpass_filter[left_wing_index];
impulse += resampler.m_lowpass_delta[left_wing_index] * left_wing_fraction;

// 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;
left_temp += (float) m_float_buffer[current_index & INDEX_MASK] * impulse;
right_temp += (float) m_float_buffer[(current_index + 1) & INDEX_MASK] * impulse;

float l_volume = (float) m_lvolume / 255.f;
float r_volume = (float) m_rvolume / 255.f;
left_wing_index += Resampler::SAMPLES_PER_CROSSING;
current_index -= 2;
}

// 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
for (; current_sample < numSamples * 2 && ((write_index - read_index) & INDEX_MASK) > 0; current_sample += 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())
{
float l_output = LinearInterpolate(m_float_buffer[read_index & INDEX_MASK],
m_float_buffer[(read_index + 2) & INDEX_MASK],
m_fraction);
float r_output = LinearInterpolate(m_float_buffer[(read_index + 1) & INDEX_MASK],
m_float_buffer[(read_index + 3) & INDEX_MASK],
m_fraction);
double impulse = resampler.m_lowpass_filter[right_wing_index];
impulse += resampler.m_lowpass_delta[right_wing_index] * right_wing_fraction;

samples[current_sample + 1] += l_volume * l_output;
samples[current_sample ] += r_volume * r_output;

m_fraction += ratio;
read_index += 2 * (s32) m_fraction;
m_fraction = m_fraction - (s32) m_fraction;
}
left_temp += (float) m_float_buffer[current_index & INDEX_MASK] * impulse;
right_temp += (float) m_float_buffer[(current_index + 1) & INDEX_MASK] * impulse;

// 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)
{
samples[current_sample ] += s[0];
samples[current_sample + 1] += s[1];
right_wing_index += Resampler::SAMPLES_PER_CROSSING;
current_index += 2;
}

// update read index
Common::AtomicStore(m_read_index, read_index);
*left_output = (float)left_temp;
*right_output = (float)right_temp;
}
// Executed from sound stream thread

void CMixer::MixerFifo::Mix(std::vector<float>& samples, u32 numSamples, bool consider_framelimit)
{
u32 current_sample = 0;
Expand All @@ -108,7 +96,7 @@ void CMixer::MixerFifo::Mix(std::vector<float>& samples, u32 numSamples, bool co
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;
Expand All @@ -117,73 +105,33 @@ void CMixer::MixerFifo::Mix(std::vector<float>& 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;

float l_volume = (float) m_lvolume / 255.f;
float r_volume = (float) m_rvolume / 255.f;

// for each output sample pair (left and right),
// since filter table is one-sided,
// convolve input samples to the left with filter
// then convolve input samples to the right
// linear interpolate between current and next sample
// increment output sample position
// increment input sample position by ratio, store fraction
//
// see https://ccrma.stanford.edu/~jos/resample/Implementation.html
//
for (; current_sample < numSamples * 2 && ((write_index - read_index) & INDEX_MASK) > 0; current_sample += 2)
{
double left_output = 0, right_output = 0;

// 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 l_output, r_output;

const Resampler& resampler = m_mixer->m_resampler;
u32 current_index = read_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;

left_output += (float) m_float_buffer[ current_index & INDEX_MASK] * impulse;
right_output += (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 = read_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_output += (float) m_float_buffer[ current_index & INDEX_MASK] * impulse;
right_output += (float) m_float_buffer[(current_index + 1) & INDEX_MASK] * impulse;

right_wing_index += Resampler::SAMPLES_PER_CROSSING;
current_index += 2;
}

left_output = left_output * l_volume;
samples[current_sample + 1] += (float)left_output;

right_output = right_output * r_volume;
samples[current_sample] += (float) right_output;
Interpolate(read_index, &l_output, &r_output);

samples[current_sample + 1] += l_volume * l_output;
samples[current_sample ] += r_volume * r_output;

m_fraction += ratio;
read_index += 2 * (s32) m_fraction;
m_fraction = m_fraction - (s32) m_fraction;
}


// 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;
Expand All @@ -193,7 +141,7 @@ void CMixer::MixerFifo::Mix(std::vector<float>& samples, u32 numSamples, bool co
samples[current_sample + 1] += s[1];
}

// Flush cached variable
// update read index
Common::AtomicStore(m_read_index, read_index);
}

Expand Down Expand Up @@ -228,7 +176,7 @@ u32 CMixer::Mix(s16* samples, u32 num_samples, bool consider_framelimit)

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.MixLinear(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)
Expand Down
38 changes: 21 additions & 17 deletions Source/Core/AudioCommon/Mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@

#include "AudioCommon/WaveFile.h"

// 16 bit Stereo

//#define LOW_WATERMARK 1280 // 40 ms
//#define MAX_FREQ_SHIFT 200.0f // per 32000 Hz
//#define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset
//#define CONTROL_AVG 32

// Dither define
#define DITHER_NOISE (rand() / (float) RAND_MAX - 0.5f)

Expand Down Expand Up @@ -138,14 +131,14 @@ class CMixer
{
srand((u32) time(nullptr));
}
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<float>& samples, u32 numSamples, bool consider_framelimit = true);
void MixLinear(std::vector<float>& samples, u32 numSamples, bool consider_framelimit);
void SetInputSampleRate(u32 rate);
void SetVolume(u32 lvolume, u32 rvolume);
void GetVolume(u32* lvolume, u32* rvolume) const;

private:
protected:
CMixer* m_mixer;
u32 m_input_sample_rate;

Expand All @@ -159,7 +152,18 @@ class CMixer
// Volume ranges from 0-255
volatile u32 m_rvolume;
volatile u32 m_lvolume;
};

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);
};

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);
};

class Resampler
Expand All @@ -171,6 +175,7 @@ class CMixer
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;
Expand All @@ -186,9 +191,13 @@ class CMixer

Resampler m_resampler;

MixerFifo m_dma_mixer;
MixerFifo m_streaming_mixer;
MixerFifo m_wiimote_speaker_mixer;
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;

WaveFileWriter g_wave_writer_dtk;
Expand All @@ -214,11 +223,6 @@ class CMixer
return (f > 0) ? (s16) (f * 0x7fff) : (s16) (f * 0x8000);
}

static inline float LinearInterpolate(const float s0, const float s1, const float t)
{
return (1 - t) * s0 + t * s1;
}

void TriangleDither(float* l_sample, float* r_sample);

std::vector<float> m_output_buffer;
Expand Down

0 comments on commit f4f260d

Please sign in to comment.