@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
# include <limits>
# include <vector>
# include "audio_core/audio_out.h"
@ -14,6 +15,59 @@
# include "core/memory.h"
# include "core/settings.h"
namespace {
[[nodiscard]] static constexpr s16 ClampToS16 ( s32 value ) {
return static_cast < s16 > ( std : : clamp ( value , static_cast < s32 > ( std : : numeric_limits < s16 > : : min ( ) ) ,
static_cast < s32 > ( std : : numeric_limits < s16 > : : max ( ) ) ) ) ;
}
[[nodiscard]] static constexpr s16 Mix2To1 ( s16 l_channel , s16 r_channel ) {
// Mix 50% from left and 50% from right channel
constexpr float l_mix_amount = 50.0f / 100.0f ;
constexpr float r_mix_amount = 50.0f / 100.0f ;
return ClampToS16 ( static_cast < s32 > ( ( static_cast < float > ( l_channel ) * l_mix_amount ) +
( static_cast < float > ( r_channel ) * r_mix_amount ) ) ) ;
}
[[nodiscard]] static constexpr std : : tuple < s16 , s16 > Mix6To2 ( s16 fl_channel , s16 fr_channel ,
s16 fc_channel ,
[[maybe_unused]] s16 lf_channel ,
s16 bl_channel , s16 br_channel ) {
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
// are mixed to be 36.94%
constexpr float front_mix_amount = 36.94f / 100.0f ;
constexpr float center_mix_amount = 26.12f / 100.0f ;
constexpr float back_mix_amount = 36.94f / 100.0f ;
// Mix 50% from left and 50% from right channel
const auto left = front_mix_amount * static_cast < float > ( fl_channel ) +
center_mix_amount * static_cast < float > ( fc_channel ) +
back_mix_amount * static_cast < float > ( bl_channel ) ;
const auto right = front_mix_amount * static_cast < float > ( fr_channel ) +
center_mix_amount * static_cast < float > ( fc_channel ) +
back_mix_amount * static_cast < float > ( br_channel ) ;
return { ClampToS16 ( static_cast < s32 > ( left ) ) , ClampToS16 ( static_cast < s32 > ( right ) ) } ;
}
[[nodiscard]] static constexpr std : : tuple < s16 , s16 > Mix6To2WithCoefficients (
s16 fl_channel , s16 fr_channel , s16 fc_channel , s16 lf_channel , s16 bl_channel , s16 br_channel ,
const std : : array < float_le , 4 > & coeff ) {
const auto left =
static_cast < float > ( fl_channel ) * coeff [ 0 ] + static_cast < float > ( fc_channel ) * coeff [ 1 ] +
static_cast < float > ( lf_channel ) * coeff [ 2 ] + static_cast < float > ( bl_channel ) * coeff [ 0 ] ;
const auto right =
static_cast < float > ( fr_channel ) * coeff [ 0 ] + static_cast < float > ( fc_channel ) * coeff [ 1 ] +
static_cast < float > ( lf_channel ) * coeff [ 2 ] + static_cast < float > ( br_channel ) * coeff [ 0 ] ;
return { ClampToS16 ( static_cast < s32 > ( left ) ) , ClampToS16 ( static_cast < s32 > ( right ) ) } ;
}
} // namespace
namespace AudioCore {
AudioRenderer : : AudioRenderer ( Core : : Timing : : CoreTiming & core_timing , Core : : Memory : : Memory & memory_ ,
AudioCommon : : AudioRendererParameter params ,
@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const {
return stream - > GetState ( ) ;
}
static constexpr s16 ClampToS16 ( s32 value ) {
return static_cast < s16 > ( std : : clamp ( value , - 32768 , 32767 ) ) ;
}
ResultCode AudioRenderer : : UpdateAudioRenderer ( const std : : vector < u8 > & input_params ,
std : : vector < u8 > & output_params ) {
@ -104,8 +154,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
}
}
auto mix_result = info_updater . UpdateMixes ( mix_context , worker_params . mix_buffer_count ,
splitter_context , effect_context ) ;
const auto mix_result = info_updater . UpdateMixes ( mix_context , worker_params . mix_buffer_count ,
splitter_context , effect_context ) ;
if ( mix_result . IsError ( ) ) {
LOG_ERROR ( Audio , " Failed to update mix parameters " ) ;
@ -194,20 +244,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
for ( std : : size_t i = 0 ; i < BUFFER_SIZE ; i + + ) {
if ( channel_count = = 1 ) {
const auto sample = ClampToS16 ( mix_buffers [ 0 ] [ i ] ) ;
buffer [ i * stream_channel_count + 0 ] = sample ;
if ( stream_channel_count > 1 ) {
buffer [ i * stream_channel_count + 1 ] = sample ;
// Place sample in all channels
for ( u32 channel = 0 ; channel < stream_channel_count ; channel + + ) {
buffer [ i * stream_channel_count + channel ] = sample ;
}
if ( stream_channel_count = = 6 ) {
buffer [ i * stream_channel_count + 2 ] = sample ;
buffer [ i * stream_channel_count + 4 ] = sample ;
buffer [ i * stream_channel_count + 5 ] = sample ;
// Output stream has a LF channel, mute it!
buffer [ i * stream_channel_count + 3 ] = 0 ;
}
} else if ( channel_count = = 2 ) {
const auto l_sample = ClampToS16 ( mix_buffers [ 0 ] [ i ] ) ;
const auto r_sample = ClampToS16 ( mix_buffers [ 1 ] [ i ] ) ;
if ( stream_channel_count = = 1 ) {
buffer [ i * stream_channel_count + 0 ] = l_sample;
buffer [ i * stream_channel_count + 0 ] = Mix2To1( l_sample, r_sample ) ;
} else if ( stream_channel_count = = 2 ) {
buffer [ i * stream_channel_count + 0 ] = l_sample ;
buffer [ i * stream_channel_count + 1 ] = r_sample ;
@ -215,8 +267,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
buffer [ i * stream_channel_count + 0 ] = l_sample ;
buffer [ i * stream_channel_count + 1 ] = r_sample ;
buffer [ i * stream_channel_count + 2 ] =
ClampToS16 ( ( static_cast < s32 > ( l_sample ) + static_cast < s32 > ( r_sample ) ) / 2 ) ;
// Combine both left and right channels to the center channel
buffer [ i * stream_channel_count + 2 ] = Mix2To1 ( l_sample , r_sample ) ;
buffer [ i * stream_channel_count + 4 ] = l_sample ;
buffer [ i * stream_channel_count + 5 ] = r_sample ;
@ -231,17 +283,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
const auto br_sample = ClampToS16 ( mix_buffers [ 5 ] [ i ] ) ;
if ( stream_channel_count = = 1 ) {
buffer [ i * stream_channel_count + 0 ] = fc_sample ;
// Games seem to ignore the center channel half the time, we use the front left
// and right channel for mixing as that's where majority of the audio goes
buffer [ i * stream_channel_count + 0 ] = Mix2To1 ( fl_sample , fr_sample ) ;
} else if ( stream_channel_count = = 2 ) {
buffer [ i * stream_channel_count + 0 ] =
static_cast < s16 > ( 0.3694f * static_cast < float > ( fl_sample ) +
0.2612f * static_cast < float > ( fc_sample ) +
0.3694f * static_cast < float > ( bl_sample ) ) ;
buffer [ i * stream_channel_count + 1 ] =
static_cast < s16 > ( 0.3694f * static_cast < float > ( fr_sample ) +
0.2612f * static_cast < float > ( fc_sample ) +
0.3694f * static_cast < float > ( br_sample ) ) ;
// Mix all channels into 2 channels
if ( sink_context . HasDownMixingCoefficients ( ) ) {
const auto [ left , right ] = Mix6To2WithCoefficients (
fl_sample , fr_sample , fc_sample , lf_sample , bl_sample , br_sample ,
sink_context . GetDownmixCoefficients ( ) ) ;
buffer [ i * stream_channel_count + 0 ] = left ;
buffer [ i * stream_channel_count + 1 ] = right ;
} else {
const auto [ left , right ] = Mix6To2 ( fl_sample , fr_sample , fc_sample ,
lf_sample , bl_sample , br_sample ) ;
buffer [ i * stream_channel_count + 0 ] = left ;
buffer [ i * stream_channel_count + 1 ] = right ;
}
} else if ( stream_channel_count = = 6 ) {
// Pass through
buffer [ i * stream_channel_count + 0 ] = fl_sample ;
buffer [ i * stream_channel_count + 1 ] = fr_sample ;
buffer [ i * stream_channel_count + 2 ] = fc_sample ;
@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
}
void AudioRenderer : : ReleaseAndQueueBuffers ( ) {
const auto released_buffers { audio_out - > GetTagsAndReleaseBuffers ( stream , 2 )} ;
const auto released_buffers { audio_out - > GetTagsAndReleaseBuffers ( stream )} ;
for ( const auto & tag : released_buffers ) {
QueueMixedBuffer ( tag ) ;
}