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