diff options
Diffstat (limited to 'src/audio_core/hle/source.cpp')
| -rw-r--r-- | src/audio_core/hle/source.cpp | 86 |
1 files changed, 51 insertions, 35 deletions
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp index 30552fe26..2bbf7146e 100644 --- a/src/audio_core/hle/source.cpp +++ b/src/audio_core/hle/source.cpp | |||
| @@ -4,21 +4,19 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | 6 | #include <array> |
| 7 | |||
| 8 | #include "audio_core/codec.h" | 7 | #include "audio_core/codec.h" |
| 9 | #include "audio_core/hle/common.h" | 8 | #include "audio_core/hle/common.h" |
| 10 | #include "audio_core/hle/source.h" | 9 | #include "audio_core/hle/source.h" |
| 11 | #include "audio_core/interpolate.h" | 10 | #include "audio_core/interpolate.h" |
| 12 | |||
| 13 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 14 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 15 | |||
| 16 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 17 | 14 | ||
| 18 | namespace DSP { | 15 | namespace DSP { |
| 19 | namespace HLE { | 16 | namespace HLE { |
| 20 | 17 | ||
| 21 | SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { | 18 | SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, |
| 19 | const s16_le (&adpcm_coeffs)[16]) { | ||
| 22 | ParseConfig(config, adpcm_coeffs); | 20 | ParseConfig(config, adpcm_coeffs); |
| 23 | 21 | ||
| 24 | if (state.enabled) { | 22 | if (state.enabled) { |
| @@ -47,7 +45,8 @@ void Source::Reset() { | |||
| 47 | state = {}; | 45 | state = {}; |
| 48 | } | 46 | } |
| 49 | 47 | ||
| 50 | void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { | 48 | void Source::ParseConfig(SourceConfiguration::Configuration& config, |
| 49 | const s16_le (&adpcm_coeffs)[16]) { | ||
| 51 | if (!config.dirty_raw) { | 50 | if (!config.dirty_raw) { |
| 52 | return; | 51 | return; |
| 53 | } | 52 | } |
| @@ -82,7 +81,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | |||
| 82 | LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); | 81 | LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); |
| 83 | 82 | ||
| 84 | if (state.rate_multiplier <= 0) { | 83 | if (state.rate_multiplier <= 0) { |
| 85 | LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); | 84 | LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", |
| 85 | source_id, state.rate_multiplier); | ||
| 86 | state.rate_multiplier = 1.0f; | 86 | state.rate_multiplier = 1.0f; |
| 87 | // Note: Actual firmware starts producing garbage if this occurs. | 87 | // Note: Actual firmware starts producing garbage if this occurs. |
| 88 | } | 88 | } |
| @@ -90,37 +90,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | |||
| 90 | 90 | ||
| 91 | if (config.adpcm_coefficients_dirty) { | 91 | if (config.adpcm_coefficients_dirty) { |
| 92 | config.adpcm_coefficients_dirty.Assign(0); | 92 | config.adpcm_coefficients_dirty.Assign(0); |
| 93 | std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), | 93 | std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), |
| 94 | [](const auto& coeff) { return static_cast<s16>(coeff); }); | 94 | state.adpcm_coeffs.begin(), |
| 95 | [](const auto& coeff) { return static_cast<s16>(coeff); }); | ||
| 95 | LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); | 96 | LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | if (config.gain_0_dirty) { | 99 | if (config.gain_0_dirty) { |
| 99 | config.gain_0_dirty.Assign(0); | 100 | config.gain_0_dirty.Assign(0); |
| 100 | std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), | 101 | std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), |
| 101 | [](const auto& coeff) { return static_cast<float>(coeff); }); | 102 | [](const auto& coeff) { return static_cast<float>(coeff); }); |
| 102 | LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); | 103 | LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | if (config.gain_1_dirty) { | 106 | if (config.gain_1_dirty) { |
| 106 | config.gain_1_dirty.Assign(0); | 107 | config.gain_1_dirty.Assign(0); |
| 107 | std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), | 108 | std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), |
| 108 | [](const auto& coeff) { return static_cast<float>(coeff); }); | 109 | [](const auto& coeff) { return static_cast<float>(coeff); }); |
| 109 | LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); | 110 | LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); |
| 110 | } | 111 | } |
| 111 | 112 | ||
| 112 | if (config.gain_2_dirty) { | 113 | if (config.gain_2_dirty) { |
| 113 | config.gain_2_dirty.Assign(0); | 114 | config.gain_2_dirty.Assign(0); |
| 114 | std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), | 115 | std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), |
| 115 | [](const auto& coeff) { return static_cast<float>(coeff); }); | 116 | [](const auto& coeff) { return static_cast<float>(coeff); }); |
| 116 | LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); | 117 | LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 119 | if (config.filters_enabled_dirty) { | 120 | if (config.filters_enabled_dirty) { |
| 120 | config.filters_enabled_dirty.Assign(0); | 121 | config.filters_enabled_dirty.Assign(0); |
| 121 | state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); | 122 | state.filters.Enable(config.simple_filter_enabled.ToBool(), |
| 122 | LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", | 123 | config.biquad_filter_enabled.ToBool()); |
| 123 | source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); | 124 | LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id, |
| 125 | config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); | ||
| 124 | } | 126 | } |
| 125 | 127 | ||
| 126 | if (config.simple_filter_dirty) { | 128 | if (config.simple_filter_dirty) { |
| @@ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | |||
| 138 | if (config.interpolation_dirty) { | 140 | if (config.interpolation_dirty) { |
| 139 | config.interpolation_dirty.Assign(0); | 141 | config.interpolation_dirty.Assign(0); |
| 140 | state.interpolation_mode = config.interpolation_mode; | 142 | state.interpolation_mode = config.interpolation_mode; |
| 141 | LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); | 143 | LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, |
| 144 | static_cast<size_t>(state.interpolation_mode)); | ||
| 142 | } | 145 | } |
| 143 | 146 | ||
| 144 | if (config.format_dirty || config.embedded_buffer_dirty) { | 147 | if (config.format_dirty || config.embedded_buffer_dirty) { |
| 145 | config.format_dirty.Assign(0); | 148 | config.format_dirty.Assign(0); |
| 146 | state.format = config.format; | 149 | state.format = config.format; |
| 147 | LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); | 150 | LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, |
| 151 | static_cast<size_t>(state.format)); | ||
| 148 | } | 152 | } |
| 149 | 153 | ||
| 150 | if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { | 154 | if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { |
| 151 | config.mono_or_stereo_dirty.Assign(0); | 155 | config.mono_or_stereo_dirty.Assign(0); |
| 152 | state.mono_or_stereo = config.mono_or_stereo; | 156 | state.mono_or_stereo = config.mono_or_stereo; |
| 153 | LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); | 157 | LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, |
| 158 | static_cast<size_t>(state.mono_or_stereo)); | ||
| 154 | } | 159 | } |
| 155 | 160 | ||
| 156 | if (config.embedded_buffer_dirty) { | 161 | if (config.embedded_buffer_dirty) { |
| @@ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | |||
| 159 | config.physical_address, | 164 | config.physical_address, |
| 160 | config.length, | 165 | config.length, |
| 161 | static_cast<u8>(config.adpcm_ps), | 166 | static_cast<u8>(config.adpcm_ps), |
| 162 | { config.adpcm_yn[0], config.adpcm_yn[1] }, | 167 | {config.adpcm_yn[0], config.adpcm_yn[1]}, |
| 163 | config.adpcm_dirty.ToBool(), | 168 | config.adpcm_dirty.ToBool(), |
| 164 | config.is_looping.ToBool(), | 169 | config.is_looping.ToBool(), |
| 165 | config.buffer_id, | 170 | config.buffer_id, |
| 166 | state.mono_or_stereo, | 171 | state.mono_or_stereo, |
| 167 | state.format, | 172 | state.format, |
| 168 | false | 173 | false, |
| 169 | }); | 174 | }); |
| 170 | LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id); | 175 | LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", |
| 176 | config.physical_address, config.length, config.buffer_id); | ||
| 171 | } | 177 | } |
| 172 | 178 | ||
| 173 | if (config.buffer_queue_dirty) { | 179 | if (config.buffer_queue_dirty) { |
| @@ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l | |||
| 179 | b.physical_address, | 185 | b.physical_address, |
| 180 | b.length, | 186 | b.length, |
| 181 | static_cast<u8>(b.adpcm_ps), | 187 | static_cast<u8>(b.adpcm_ps), |
| 182 | { b.adpcm_yn[0], b.adpcm_yn[1] }, | 188 | {b.adpcm_yn[0], b.adpcm_yn[1]}, |
| 183 | b.adpcm_dirty != 0, | 189 | b.adpcm_dirty != 0, |
| 184 | b.is_looping != 0, | 190 | b.is_looping != 0, |
| 185 | b.buffer_id, | 191 | b.buffer_id, |
| 186 | state.mono_or_stereo, | 192 | state.mono_or_stereo, |
| 187 | state.format, | 193 | state.format, |
| 188 | true | 194 | true, |
| 189 | }); | 195 | }); |
| 190 | LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id); | 196 | LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, |
| 197 | b.physical_address, b.length, b.buffer_id); | ||
| 191 | } | 198 | } |
| 192 | } | 199 | } |
| 193 | config.buffers_dirty = 0; | 200 | config.buffers_dirty = 0; |
| @@ -218,10 +225,13 @@ void Source::GenerateFrame() { | |||
| 218 | break; | 225 | break; |
| 219 | } | 226 | } |
| 220 | 227 | ||
| 221 | const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); | 228 | const size_t size_to_copy = |
| 229 | std::min(state.current_buffer.size(), current_frame.size() - frame_position); | ||
| 222 | 230 | ||
| 223 | std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); | 231 | std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, |
| 224 | state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); | 232 | current_frame.begin() + frame_position); |
| 233 | state.current_buffer.erase(state.current_buffer.begin(), | ||
| 234 | state.current_buffer.begin() + size_to_copy); | ||
| 225 | 235 | ||
| 226 | frame_position += size_to_copy; | 236 | frame_position += size_to_copy; |
| 227 | state.next_sample_number += static_cast<u32>(size_to_copy); | 237 | state.next_sample_number += static_cast<u32>(size_to_copy); |
| @@ -230,9 +240,9 @@ void Source::GenerateFrame() { | |||
| 230 | state.filters.ProcessFrame(current_frame); | 240 | state.filters.ProcessFrame(current_frame); |
| 231 | } | 241 | } |
| 232 | 242 | ||
| 233 | |||
| 234 | bool Source::DequeueBuffer() { | 243 | bool Source::DequeueBuffer() { |
| 235 | ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); | 244 | ASSERT_MSG(state.current_buffer.empty(), |
| 245 | "Shouldn't dequeue; we still have data in current_buffer"); | ||
| 236 | 246 | ||
| 237 | if (state.input_queue.empty()) | 247 | if (state.input_queue.empty()) |
| 238 | return false; | 248 | return false; |
| @@ -261,29 +271,34 @@ bool Source::DequeueBuffer() { | |||
| 261 | break; | 271 | break; |
| 262 | case Format::ADPCM: | 272 | case Format::ADPCM: |
| 263 | DEBUG_ASSERT(num_channels == 1); | 273 | DEBUG_ASSERT(num_channels == 1); |
| 264 | state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); | 274 | state.current_buffer = |
| 275 | Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); | ||
| 265 | break; | 276 | break; |
| 266 | default: | 277 | default: |
| 267 | UNIMPLEMENTED(); | 278 | UNIMPLEMENTED(); |
| 268 | break; | 279 | break; |
| 269 | } | 280 | } |
| 270 | } else { | 281 | } else { |
| 271 | LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", | 282 | LOG_WARNING(Audio_DSP, |
| 272 | source_id, buf.buffer_id, buf.length, buf.physical_address); | 283 | "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", |
| 284 | source_id, buf.buffer_id, buf.length, buf.physical_address); | ||
| 273 | state.current_buffer.clear(); | 285 | state.current_buffer.clear(); |
| 274 | return true; | 286 | return true; |
| 275 | } | 287 | } |
| 276 | 288 | ||
| 277 | switch (state.interpolation_mode) { | 289 | switch (state.interpolation_mode) { |
| 278 | case InterpolationMode::None: | 290 | case InterpolationMode::None: |
| 279 | state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); | 291 | state.current_buffer = |
| 292 | AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); | ||
| 280 | break; | 293 | break; |
| 281 | case InterpolationMode::Linear: | 294 | case InterpolationMode::Linear: |
| 282 | state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | 295 | state.current_buffer = |
| 296 | AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | ||
| 283 | break; | 297 | break; |
| 284 | case InterpolationMode::Polyphase: | 298 | case InterpolationMode::Polyphase: |
| 285 | // TODO(merry): Implement polyphase interpolation | 299 | // TODO(merry): Implement polyphase interpolation |
| 286 | state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | 300 | state.current_buffer = |
| 301 | AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); | ||
| 287 | break; | 302 | break; |
| 288 | default: | 303 | default: |
| 289 | UNIMPLEMENTED(); | 304 | UNIMPLEMENTED(); |
| @@ -296,7 +311,8 @@ bool Source::DequeueBuffer() { | |||
| 296 | state.buffer_update = buf.from_queue; | 311 | state.buffer_update = buf.from_queue; |
| 297 | 312 | ||
| 298 | LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", | 313 | LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", |
| 299 | source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); | 314 | source_id, buf.buffer_id, buf.from_queue ? "true" : "false", |
| 315 | state.current_buffer.size()); | ||
| 300 | return true; | 316 | return true; |
| 301 | } | 317 | } |
| 302 | 318 | ||