summaryrefslogtreecommitdiff
path: root/src/audio_core
diff options
context:
space:
mode:
authorGravatar David Marcec2020-08-14 21:04:28 +1000
committerGravatar David Marcec2020-08-14 21:04:28 +1000
commit1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d (patch)
tree1ee73fbd3c507806a57597e38eb0030e641a5489 /src/audio_core
parentmix buffer depopping (diff)
downloadyuzu-1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d.tar.gz
yuzu-1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d.tar.xz
yuzu-1b3d86c02fbc82db4dfd7b0ce908d02e48b5a35d.zip
Reworked ADPCM decoder to allow better streaming
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/command_generator.cpp119
-rw-r--r--src/audio_core/voice_context.h9
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};
86static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size"); 86static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
87 87
88struct ADPCMContext {
89 u16 header{};
90 s16 yn1{};
91 s16 yn2{};
92};
93static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
94
88struct VoiceState { 95struct 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{};