2013-04-18 03:09:55 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// Refer to the license.txt file included.
|
2009-01-17 14:28:09 +00:00
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "AudioCommon/AudioCommon.h"
|
|
|
|
#include "AudioCommon/Mixer.h"
|
|
|
|
#include "Common/Atomic.h"
|
|
|
|
#include "Common/CPUDetect.h"
|
2014-04-11 01:28:19 +00:00
|
|
|
#include "Common/MathUtil.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/ConfigManager.h"
|
2014-02-16 22:13:01 +00:00
|
|
|
#include "Core/Core.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/HW/AudioInterface.h"
|
|
|
|
#include "Core/HW/VideoInterface.h"
|
2011-01-28 18:39:30 +00:00
|
|
|
|
|
|
|
// UGLINESS
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
2011-01-28 18:39:30 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
#ifndef M_PI
|
|
|
|
#define M_PI 3.14159265358979323846
|
2011-01-12 09:34:53 +00:00
|
|
|
#endif
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
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)
|
2008-12-08 05:25:12 +00:00
|
|
|
{
|
2015-01-30 08:29:49 +00:00
|
|
|
*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];
|
|
|
|
}
|
2010-09-28 21:43:38 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
//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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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;
|
2014-02-06 05:38:04 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
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<float>& 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
|
2014-02-11 13:53:53 +00:00
|
|
|
u32 framelimit = SConfig::GetInstance().m_Framelimit;
|
2014-04-11 01:28:19 +00:00
|
|
|
float aid_sample_rate = m_input_sample_rate + offset;
|
2014-07-24 12:16:17 +00:00
|
|
|
if (consider_framelimit && framelimit > 1)
|
2014-02-11 13:53:53 +00:00
|
|
|
{
|
|
|
|
aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate;
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
// 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),
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
float l_output, r_output;
|
2014-02-06 05:38:04 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
Interpolate(read_index, &l_output, &r_output);
|
2014-06-26 23:15:18 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
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;
|
2009-12-23 15:34:14 +00:00
|
|
|
}
|
2009-12-22 07:26:30 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
// 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)
|
2011-03-12 22:02:46 +00:00
|
|
|
{
|
2015-01-30 08:29:49 +00:00
|
|
|
samples[current_sample] += s[0];
|
|
|
|
samples[current_sample + 1] += s[1];
|
2011-03-12 22:02:46 +00:00
|
|
|
}
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
// update read index
|
|
|
|
Common::AtomicStore(m_read_index, read_index);
|
|
|
|
}
|
2009-12-23 15:34:14 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
// 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;
|
2009-12-23 15:34:14 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
u32 CMixer::Mix(s16* samples, u32 num_samples, bool consider_framelimit)
|
2014-04-11 01:28:19 +00:00
|
|
|
{
|
|
|
|
if (!samples)
|
|
|
|
return 0;
|
2009-12-23 15:34:14 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
std::lock_guard<std::mutex> lk(m_cs_mixing);
|
2014-04-11 01:28:19 +00:00
|
|
|
|
2014-07-07 03:30:06 +00:00
|
|
|
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
2014-04-11 01:28:19 +00:00
|
|
|
{
|
|
|
|
// Silence
|
2015-01-30 08:29:49 +00:00
|
|
|
memset(samples, 0, num_samples * 2 * sizeof(s16));
|
2014-04-11 01:28:19 +00:00
|
|
|
return num_samples;
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2014-04-11 01:28:19 +00:00
|
|
|
return num_samples;
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::MixerFifo::PushSamples(const s16* samples, u32 num_samples)
|
2009-12-23 15:34:14 +00:00
|
|
|
{
|
2013-07-11 19:22:38 +00:00
|
|
|
// 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.
|
2015-01-30 08:29:49 +00:00
|
|
|
u32 current_write_index = Common::AtomicLoad(m_write_index);
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2009-12-23 15:34:14 +00:00
|
|
|
// Check if we have enough free space
|
2013-07-11 19:22:38 +00:00
|
|
|
// indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW
|
2015-01-30 08:29:49 +00:00
|
|
|
if (num_samples * 2 + ((current_write_index - Common::AtomicLoad(m_read_index)) & INDEX_MASK) >= MAX_SAMPLES * 2)
|
2009-12-23 15:34:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// AyuanX: Actual re-sampling work has been moved to sound thread
|
2009-12-25 11:59:04 +00:00
|
|
|
// to alleviate the workload on main thread
|
2015-01-30 08:29:49 +00:00
|
|
|
// convert to float while copying to buffer
|
|
|
|
for (u32 i = 0; i < num_samples * 2; ++i)
|
2009-12-23 15:34:14 +00:00
|
|
|
{
|
2015-01-30 08:29:49 +00:00
|
|
|
m_float_buffer[(current_write_index + i) & INDEX_MASK] = Signed16ToFloat(Common::swap16(samples[i]));
|
2009-12-23 15:34:14 +00:00
|
|
|
}
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
Common::AtomicAdd(m_write_index, num_samples * 2);
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2009-12-23 15:34:14 +00:00
|
|
|
return;
|
2008-12-08 05:25:12 +00:00
|
|
|
}
|
2009-12-20 02:23:26 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::PushSamples(const s16* samples, u32 num_samples)
|
2014-04-11 01:28:19 +00:00
|
|
|
{
|
|
|
|
m_dma_mixer.PushSamples(samples, num_samples);
|
2014-10-04 07:28:01 +00:00
|
|
|
if (m_log_dsp_audio)
|
|
|
|
g_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples);
|
2014-04-11 01:28:19 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::PushStreamingSamples(const s16* samples, u32 num_samples)
|
2014-04-11 01:28:19 +00:00
|
|
|
{
|
|
|
|
m_streaming_mixer.PushSamples(samples, num_samples);
|
2014-10-04 07:28:01 +00:00
|
|
|
if (m_log_dtk_audio)
|
|
|
|
g_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples);
|
2014-04-11 01:28:19 +00:00
|
|
|
}
|
2014-06-26 23:15:18 +00:00
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::PushWiimoteSpeakerSamples(const s16* samples, u32 num_samples, u32 sample_rate)
|
2014-09-05 12:32:48 +00:00
|
|
|
{
|
2015-01-30 08:29:49 +00:00
|
|
|
s16 samples_stereo[MAX_SAMPLES * 2];
|
2014-09-05 12:32:48 +00:00
|
|
|
|
|
|
|
if (num_samples < MAX_SAMPLES)
|
|
|
|
{
|
|
|
|
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
for (u32 i = 0; i < num_samples; ++i)
|
2014-09-05 12:32:48 +00:00
|
|
|
{
|
2014-09-05 15:23:54 +00:00
|
|
|
samples_stereo[i * 2] = Common::swap16(samples[i]);
|
|
|
|
samples_stereo[i * 2 + 1] = Common::swap16(samples[i]);
|
2014-09-05 12:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_wiimote_speaker_mixer.PushSamples(samples_stereo, num_samples);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::SetDMAInputSampleRate(u32 rate)
|
2014-07-24 03:20:19 +00:00
|
|
|
{
|
|
|
|
m_dma_mixer.SetInputSampleRate(rate);
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::SetStreamInputSampleRate(u32 rate)
|
2014-07-24 03:20:19 +00:00
|
|
|
{
|
|
|
|
m_streaming_mixer.SetInputSampleRate(rate);
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::SetStreamingVolume(u32 lvolume, u32 rvolume)
|
2014-06-26 23:15:18 +00:00
|
|
|
{
|
|
|
|
m_streaming_mixer.SetVolume(lvolume, rvolume);
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::SetWiimoteSpeakerVolume(u32 lvolume, u32 rvolume)
|
2014-09-05 12:32:48 +00:00
|
|
|
{
|
|
|
|
m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume);
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::MixerFifo::SetInputSampleRate(u32 rate)
|
2014-07-24 03:20:19 +00:00
|
|
|
{
|
|
|
|
m_input_sample_rate = rate;
|
|
|
|
}
|
|
|
|
|
2015-01-30 08:29:49 +00:00
|
|
|
void CMixer::MixerFifo::SetVolume(u32 lvolume, u32 rvolume)
|
|
|
|
{
|
|
|
|
m_lvolume = lvolume;
|
|
|
|
m_rvolume = rvolume;
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2014-06-26 23:15:18 +00:00
|
|
|
{
|
2015-01-30 08:29:49 +00:00
|
|
|
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;
|
2014-06-26 23:15:18 +00:00
|
|
|
}
|
2015-01-30 08:29:49 +00:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
}
|