diff options
| author | 2020-08-14 21:04:28 +1000 | |
|---|---|---|
| committer | 2020-08-14 21:04:28 +1000 | |
| commit | 1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d (patch) | |
| tree | 1ee73fbd3c507806a57597e38eb0030e641a5489 | |
| parent | mix buffer depopping (diff) | |
| download | yuzu-1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d.tar.gz yuzu-1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d.tar.xz yuzu-1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d.zip | |
Reworked ADPCM decoder to allow better streaming
| -rw-r--r-- | src/audio_core/command_generator.cpp | 119 | ||||
| -rw-r--r-- | src/audio_core/voice_context.h | 9 |
2 files changed, 95 insertions, 33 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 8f89292be..157140d68 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp | |||
| @@ -546,46 +546,101 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||
| 546 | return 0; | 546 | return 0; |
| 547 | } | 547 | } |
| 548 | 548 | ||
| 549 | const auto samples_remaining = | 549 | constexpr std::array<int, 16> SIGNED_NIBBLES = { |
| 550 | (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; | 550 | {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; |
| 551 | const auto samples_processed = std::min(sample_count, samples_remaining); | ||
| 552 | const auto start_offset = | ||
| 553 | ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count); | ||
| 554 | const auto end_offset = start_offset + samples_processed; | ||
| 555 | 551 | ||
| 556 | constexpr std::size_t FRAME_LEN = 8; | 552 | constexpr std::size_t FRAME_LEN = 8; |
| 553 | constexpr std::size_t NIBBLES_PER_SAMPLE = 16; | ||
| 557 | constexpr std::size_t SAMPLES_PER_FRAME = 14; | 554 | constexpr std::size_t SAMPLES_PER_FRAME = 14; |
| 558 | 555 | ||
| 559 | // Base buffer position | 556 | auto frame_header = dsp_state.context.header; |
| 560 | const auto start_frame_index = start_offset / SAMPLES_PER_FRAME; | 557 | s32 idx = (frame_header >> 4) & 0xf; |
| 561 | const auto start_frame_buffer = start_frame_index * FRAME_LEN; | 558 | s32 scale = frame_header & 0xf; |
| 562 | 559 | s16 yn1 = dsp_state.context.yn1; | |
| 563 | const auto end_frame_index = end_offset / SAMPLES_PER_FRAME; | 560 | s16 yn2 = dsp_state.context.yn2; |
| 564 | const auto end_frame_buffer = end_frame_index * FRAME_LEN; | ||
| 565 | |||
| 566 | const auto position_in_frame = start_offset % SAMPLES_PER_FRAME; | ||
| 567 | |||
| 568 | const auto buffer_size = (1 + (end_frame_index - start_frame_index)) * FRAME_LEN; | ||
| 569 | 561 | ||
| 570 | Codec::ADPCM_Coeff coeffs; | 562 | Codec::ADPCM_Coeff coeffs; |
| 571 | memory.ReadBlock(in_params.additional_params_address, coeffs.data(), | 563 | memory.ReadBlock(in_params.additional_params_address, coeffs.data(), |
| 572 | sizeof(Codec::ADPCM_Coeff)); | 564 | sizeof(Codec::ADPCM_Coeff)); |
| 573 | std::vector<u8> buffer(buffer_size); | 565 | |
| 574 | memory.ReadBlock(wave_buffer.buffer_address + start_frame_buffer, buffer.data(), buffer.size()); | 566 | s32 coef1 = coeffs[idx * 2]; |
| 575 | const auto adpcm_samples = | 567 | s32 coef2 = coeffs[idx * 2 + 1]; |
| 576 | std::move(Codec::DecodeADPCM(buffer.data(), buffer.size(), coeffs, dsp_state.context)); | 568 | |
| 577 | 569 | const auto samples_remaining = | |
| 578 | for (std::size_t i = 0; i < samples_processed; i++) { | 570 | (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; |
| 579 | const auto sample_offset = position_in_frame + i * in_params.channel_count + channel; | 571 | const auto samples_processed = std::min(sample_count, samples_remaining); |
| 580 | const auto sample = adpcm_samples[sample_offset]; | 572 | const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset; |
| 581 | sample_buffer[mix_offset + i] = sample; | 573 | |
| 582 | } | 574 | const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; |
| 583 | 575 | auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + | |
| 584 | // Manually set our context | 576 | samples_remaining_in_frame + (samples_remaining_in_frame != 0 ? 2 : 0); |
| 585 | const auto frame_before_final = (end_frame_index - start_frame_index) - 1; | 577 | |
| 586 | const auto frame_before_final_off = frame_before_final * SAMPLES_PER_FRAME; | 578 | const auto decode_sample = [&](const int nibble) -> s16 { |
| 587 | dsp_state.context.yn2 = adpcm_samples[frame_before_final_off + 12]; | 579 | const int xn = nibble * (1 << scale); |
| 588 | dsp_state.context.yn1 = adpcm_samples[frame_before_final_off + 13]; | 580 | // We first transform everything into 11 bit fixed point, perform the second order |
| 581 | // digital filter, then transform back. | ||
| 582 | // 0x400 == 0.5 in 11 bit fixed point. | ||
| 583 | // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] | ||
| 584 | int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; | ||
| 585 | // Clamp to output range. | ||
| 586 | val = std::clamp<s32>(val, -32768, 32767); | ||
| 587 | // Advance output feedback. | ||
| 588 | yn2 = yn1; | ||
| 589 | yn1 = val; | ||
| 590 | return static_cast<s16>(val); | ||
| 591 | }; | ||
| 592 | |||
| 593 | std::size_t buffer_offset{}; | ||
| 594 | std::vector<u8> buffer( | ||
| 595 | std::max((samples_processed / FRAME_LEN) * SAMPLES_PER_FRAME, FRAME_LEN)); | ||
| 596 | memory.ReadBlock(wave_buffer.buffer_address + (position_in_frame / 2), buffer.data(), | ||
| 597 | buffer.size()); | ||
| 598 | std::size_t cur_mix_offset = mix_offset; | ||
| 599 | |||
| 600 | auto remaining_samples = samples_processed; | ||
| 601 | while (remaining_samples > 0) { | ||
| 602 | if (position_in_frame % NIBBLES_PER_SAMPLE == 0) { | ||
| 603 | // Read header | ||
| 604 | frame_header = buffer[buffer_offset++]; | ||
| 605 | idx = (frame_header >> 4) & 0xf; | ||
| 606 | scale = frame_header & 0xf; | ||
| 607 | coef1 = coeffs[idx * 2]; | ||
| 608 | coef2 = coeffs[idx * 2 + 1]; | ||
| 609 | position_in_frame += 2; | ||
| 610 | |||
| 611 | // Decode entire frame | ||
| 612 | if (remaining_samples >= SAMPLES_PER_FRAME) { | ||
| 613 | for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) { | ||
| 614 | |||
| 615 | // Sample 1 | ||
| 616 | const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4]; | ||
| 617 | const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf]; | ||
| 618 | const s16 sample_1 = decode_sample(s0); | ||
| 619 | const s16 sample_2 = decode_sample(s1); | ||
| 620 | sample_buffer[cur_mix_offset++] = sample_1; | ||
| 621 | sample_buffer[cur_mix_offset++] = sample_2; | ||
| 622 | } | ||
| 623 | remaining_samples -= SAMPLES_PER_FRAME; | ||
| 624 | position_in_frame += SAMPLES_PER_FRAME; | ||
| 625 | continue; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | // Decode mid frame | ||
| 629 | s32 current_nibble = buffer[buffer_offset]; | ||
| 630 | if (position_in_frame++ & 0x1) { | ||
| 631 | current_nibble &= 0xf; | ||
| 632 | buffer_offset++; | ||
| 633 | } else { | ||
| 634 | current_nibble >>= 4; | ||
| 635 | } | ||
| 636 | const s16 sample = decode_sample(SIGNED_NIBBLES[current_nibble]); | ||
| 637 | sample_buffer[cur_mix_offset++] = sample; | ||
| 638 | remaining_samples--; | ||
| 639 | } | ||
| 640 | |||
| 641 | dsp_state.context.header = frame_header; | ||
| 642 | dsp_state.context.yn1 = yn1; | ||
| 643 | dsp_state.context.yn2 = yn2; | ||
| 589 | 644 | ||
| 590 | return samples_processed; | 645 | return samples_processed; |
| 591 | } | 646 | } |
diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h index 13b0a7f0f..59d3d7dfb 100644 --- a/src/audio_core/voice_context.h +++ b/src/audio_core/voice_context.h | |||
| @@ -85,6 +85,13 @@ struct BehaviorFlags { | |||
| 85 | }; | 85 | }; |
| 86 | static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size"); | 86 | static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size"); |
| 87 | 87 | ||
| 88 | struct ADPCMContext { | ||
| 89 | u16 header{}; | ||
| 90 | s16 yn1{}; | ||
| 91 | s16 yn2{}; | ||
| 92 | }; | ||
| 93 | static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size"); | ||
| 94 | |||
| 88 | struct VoiceState { | 95 | struct VoiceState { |
| 89 | s64 played_sample_count{}; | 96 | s64 played_sample_count{}; |
| 90 | s32 offset{}; | 97 | s32 offset{}; |
| @@ -95,7 +102,7 @@ struct VoiceState { | |||
| 95 | s32 fraction{}; | 102 | s32 fraction{}; |
| 96 | VAddr context_address{}; | 103 | VAddr context_address{}; |
| 97 | Codec::ADPCM_Coeff coeff{}; | 104 | Codec::ADPCM_Coeff coeff{}; |
| 98 | Codec::ADPCMState context{}; | 105 | ADPCMContext context{}; |
| 99 | std::array<s64, 2> biquad_filter_state{}; | 106 | std::array<s64, 2> biquad_filter_state{}; |
| 100 | std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{}; | 107 | std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{}; |
| 101 | u32 external_context_size{}; | 108 | u32 external_context_size{}; |