mixers.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cstddef>
  5. #include "audio_core/hle/common.h"
  6. #include "audio_core/hle/dsp.h"
  7. #include "audio_core/hle/mixers.h"
  8. #include "common/assert.h"
  9. #include "common/logging/log.h"
  10. #include "common/math_util.h"
  11. namespace DSP {
  12. namespace HLE {
  13. void Mixers::Reset() {
  14. current_frame.fill({});
  15. state = {};
  16. }
  17. DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
  18. IntermediateMixSamples& write_samples,
  19. const std::array<QuadFrame32, 3>& input) {
  20. ParseConfig(config);
  21. AuxReturn(read_samples);
  22. AuxSend(write_samples, input);
  23. MixCurrentFrame();
  24. return GetCurrentStatus();
  25. }
  26. void Mixers::ParseConfig(DspConfiguration& config) {
  27. if (!config.dirty_raw) {
  28. return;
  29. }
  30. if (config.mixer1_enabled_dirty) {
  31. config.mixer1_enabled_dirty.Assign(0);
  32. state.mixer1_enabled = config.mixer1_enabled != 0;
  33. LOG_TRACE(Audio_DSP, "mixers mixer1_enabled = %hu", config.mixer1_enabled);
  34. }
  35. if (config.mixer2_enabled_dirty) {
  36. config.mixer2_enabled_dirty.Assign(0);
  37. state.mixer2_enabled = config.mixer2_enabled != 0;
  38. LOG_TRACE(Audio_DSP, "mixers mixer2_enabled = %hu", config.mixer2_enabled);
  39. }
  40. if (config.volume_0_dirty) {
  41. config.volume_0_dirty.Assign(0);
  42. state.intermediate_mixer_volume[0] = config.volume[0];
  43. LOG_TRACE(Audio_DSP, "mixers volume[0] = %f", config.volume[0]);
  44. }
  45. if (config.volume_1_dirty) {
  46. config.volume_1_dirty.Assign(0);
  47. state.intermediate_mixer_volume[1] = config.volume[1];
  48. LOG_TRACE(Audio_DSP, "mixers volume[1] = %f", config.volume[1]);
  49. }
  50. if (config.volume_2_dirty) {
  51. config.volume_2_dirty.Assign(0);
  52. state.intermediate_mixer_volume[2] = config.volume[2];
  53. LOG_TRACE(Audio_DSP, "mixers volume[2] = %f", config.volume[2]);
  54. }
  55. if (config.output_format_dirty) {
  56. config.output_format_dirty.Assign(0);
  57. state.output_format = config.output_format;
  58. LOG_TRACE(Audio_DSP, "mixers output_format = %zu",
  59. static_cast<size_t>(config.output_format));
  60. }
  61. if (config.headphones_connected_dirty) {
  62. config.headphones_connected_dirty.Assign(0);
  63. // Do nothing. (Note: Whether headphones are connected does affect coefficients used for
  64. // surround sound.)
  65. LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
  66. }
  67. if (config.dirty_raw) {
  68. LOG_DEBUG(Audio_DSP, "mixers remaining_dirty=%x", config.dirty_raw);
  69. }
  70. config.dirty_raw = 0;
  71. }
  72. static s16 ClampToS16(s32 value) {
  73. return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
  74. }
  75. static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a,
  76. const std::array<s16, 2>& b) {
  77. return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
  78. ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))};
  79. }
  80. void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
  81. // TODO(merry): Limiter. (Currently we're performing final mixing assuming a disabled limiter.)
  82. switch (state.output_format) {
  83. case OutputFormat::Mono:
  84. std::transform(
  85. current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
  86. [gain](const std::array<s16, 2>& accumulator,
  87. const std::array<s32, 4>& sample) -> std::array<s16, 2> {
  88. // Downmix to mono
  89. s16 mono = ClampToS16(static_cast<s32>(
  90. (gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) /
  91. 2));
  92. // Mix into current frame
  93. return AddAndClampToS16(accumulator, {mono, mono});
  94. });
  95. return;
  96. case OutputFormat::Surround:
  97. // TODO(merry): Implement surround sound.
  98. // fallthrough
  99. case OutputFormat::Stereo:
  100. std::transform(
  101. current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
  102. [gain](const std::array<s16, 2>& accumulator,
  103. const std::array<s32, 4>& sample) -> std::array<s16, 2> {
  104. // Downmix to stereo
  105. s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
  106. s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
  107. // Mix into current frame
  108. return AddAndClampToS16(accumulator, {left, right});
  109. });
  110. return;
  111. }
  112. UNREACHABLE_MSG("Invalid output_format %zu", static_cast<size_t>(state.output_format));
  113. }
  114. void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
  115. // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
  116. // QuadFrame32.
  117. if (state.mixer1_enabled) {
  118. for (size_t sample = 0; sample < samples_per_frame; sample++) {
  119. for (size_t channel = 0; channel < 4; channel++) {
  120. state.intermediate_mix_buffer[1][sample][channel] =
  121. read_samples.mix1.pcm32[channel][sample];
  122. }
  123. }
  124. }
  125. if (state.mixer2_enabled) {
  126. for (size_t sample = 0; sample < samples_per_frame; sample++) {
  127. for (size_t channel = 0; channel < 4; channel++) {
  128. state.intermediate_mix_buffer[2][sample][channel] =
  129. read_samples.mix2.pcm32[channel][sample];
  130. }
  131. }
  132. }
  133. }
  134. void Mixers::AuxSend(IntermediateMixSamples& write_samples,
  135. const std::array<QuadFrame32, 3>& input) {
  136. // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
  137. // QuadFrame32.
  138. state.intermediate_mix_buffer[0] = input[0];
  139. if (state.mixer1_enabled) {
  140. for (size_t sample = 0; sample < samples_per_frame; sample++) {
  141. for (size_t channel = 0; channel < 4; channel++) {
  142. write_samples.mix1.pcm32[channel][sample] = input[1][sample][channel];
  143. }
  144. }
  145. } else {
  146. state.intermediate_mix_buffer[1] = input[1];
  147. }
  148. if (state.mixer2_enabled) {
  149. for (size_t sample = 0; sample < samples_per_frame; sample++) {
  150. for (size_t channel = 0; channel < 4; channel++) {
  151. write_samples.mix2.pcm32[channel][sample] = input[2][sample][channel];
  152. }
  153. }
  154. } else {
  155. state.intermediate_mix_buffer[2] = input[2];
  156. }
  157. }
  158. void Mixers::MixCurrentFrame() {
  159. current_frame.fill({});
  160. for (size_t mix = 0; mix < 3; mix++) {
  161. DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
  162. state.intermediate_mix_buffer[mix]);
  163. }
  164. // TODO(merry): Compressor. (We currently assume a disabled compressor.)
  165. }
  166. DspStatus Mixers::GetCurrentStatus() const {
  167. DspStatus status;
  168. status.unknown = 0;
  169. status.dropped_frames = 0;
  170. return status;
  171. }
  172. } // namespace HLE
  173. } // namespace DSP