Fix XC2/VOEZ crashing, add audio looping and a few misc fixes
This commit is contained in:
		| @@ -129,7 +129,6 @@ Stream::State AudioRenderer::GetStreamState() const { | |||||||
|  |  | ||||||
| ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, | ||||||
|                                               std::vector<u8>& output_params) { |                                               std::vector<u8>& output_params) { | ||||||
|     { |  | ||||||
|     std::scoped_lock lock{mutex}; |     std::scoped_lock lock{mutex}; | ||||||
|     InfoUpdater info_updater{input_params, output_params, behavior_info}; |     InfoUpdater info_updater{input_params, output_params, behavior_info}; | ||||||
|  |  | ||||||
| @@ -166,8 +165,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         const auto mix_result = info_updater.UpdateMixes( |     const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, | ||||||
|             mix_context, worker_params.mix_buffer_count, splitter_context, effect_context); |                                                      splitter_context, effect_context); | ||||||
|  |  | ||||||
|     if (mix_result.IsError()) { |     if (mix_result.IsError()) { | ||||||
|         LOG_ERROR(Audio, "Failed to update mix parameters"); |         LOG_ERROR(Audio, "Failed to update mix parameters"); | ||||||
| @@ -210,7 +209,6 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param | |||||||
|         LOG_ERROR(Audio, "Audio buffers were not consumed!"); |         LOG_ERROR(Audio, "Audio buffers were not consumed!"); | ||||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|     return ResultSuccess; |     return ResultSuccess; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -234,10 +232,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||||||
|     command_generator.PostCommand(); |     command_generator.PostCommand(); | ||||||
|     // Base sample size |     // Base sample size | ||||||
|     std::size_t BUFFER_SIZE{worker_params.sample_count}; |     std::size_t BUFFER_SIZE{worker_params.sample_count}; | ||||||
|     // Samples |     // Samples, making sure to clear | ||||||
|     std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); |     std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0); | ||||||
|     // Make sure to clear our samples |  | ||||||
|     std::memset(buffer.data(), 0, buffer.size() * sizeof(s16)); |  | ||||||
|  |  | ||||||
|     if (sink_context.InUse()) { |     if (sink_context.InUse()) { | ||||||
|         const auto stream_channel_count = stream->GetNumChannels(); |         const auto stream_channel_count = stream->GetNumChannels(); | ||||||
|   | |||||||
| @@ -795,7 +795,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta | |||||||
|         state.lowpass_1 = 0.0f; |         state.lowpass_1 = 0.0f; | ||||||
|     } else { |     } else { | ||||||
|         const auto a = 1.0f - hf_gain; |         const auto a = 1.0f - hf_gain; | ||||||
|         const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference / |         const auto b = 2.0f * (2.0f - hf_gain * CosD(256.0f * info.hf_reference / | ||||||
|                                                      static_cast<f32>(info.sample_rate))); |                                                      static_cast<f32>(info.sample_rate))); | ||||||
|         const auto c = std::sqrt(b * b - 4.0f * a * a); |         const auto c = std::sqrt(b * b - 4.0f * a * a); | ||||||
|  |  | ||||||
| @@ -843,7 +843,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const auto max_early_delay = state.early_delay_line.GetMaxDelay(); |     const auto max_early_delay = state.early_delay_line.GetMaxDelay(); | ||||||
|     const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f); |     const auto reflection_time = 1000.0f * (0.9998f * info.reverb_delay + 0.02f); | ||||||
|     for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { |     for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { | ||||||
|         const auto length = AudioCommon::CalculateDelaySamples( |         const auto length = AudioCommon::CalculateDelaySamples( | ||||||
|             sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); |             sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); | ||||||
| @@ -1004,7 +1004,8 @@ void CommandGenerator::GenerateFinalMixCommand() { | |||||||
| } | } | ||||||
|  |  | ||||||
| s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | ||||||
|                                   s32 sample_count, s32 channel, std::size_t mix_offset) { |                                   s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, | ||||||
|  |                                   s32 channel, std::size_t mix_offset) { | ||||||
|     const auto& in_params = voice_info.GetInParams(); |     const auto& in_params = voice_info.GetInParams(); | ||||||
|     const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; |     const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | ||||||
|     if (wave_buffer.buffer_address == 0) { |     if (wave_buffer.buffer_address == 0) { | ||||||
| @@ -1013,14 +1014,12 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||||||
|     if (wave_buffer.buffer_size == 0) { |     if (wave_buffer.buffer_size == 0) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { |     if (sample_end_offset < sample_start_offset) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     const auto samples_remaining = |     const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; | ||||||
|         (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; |  | ||||||
|     const auto start_offset = |     const auto start_offset = | ||||||
|         ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) * |         ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(s16); | ||||||
|         sizeof(s16); |  | ||||||
|     const auto buffer_pos = wave_buffer.buffer_address + start_offset; |     const auto buffer_pos = wave_buffer.buffer_address + start_offset; | ||||||
|     const auto samples_processed = std::min(sample_count, samples_remaining); |     const auto samples_processed = std::min(sample_count, samples_remaining); | ||||||
|  |  | ||||||
| @@ -1044,8 +1043,8 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||||||
| } | } | ||||||
|  |  | ||||||
| s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | ||||||
|                                   s32 sample_count, [[maybe_unused]] s32 channel, |                                   s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, | ||||||
|                                   std::size_t mix_offset) { |                                   [[maybe_unused]] s32 channel, std::size_t mix_offset) { | ||||||
|     const auto& in_params = voice_info.GetInParams(); |     const auto& in_params = voice_info.GetInParams(); | ||||||
|     const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; |     const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | ||||||
|     if (wave_buffer.buffer_address == 0) { |     if (wave_buffer.buffer_address == 0) { | ||||||
| @@ -1054,7 +1053,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||||||
|     if (wave_buffer.buffer_size == 0) { |     if (wave_buffer.buffer_size == 0) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { |     if (sample_end_offset < sample_start_offset) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1079,10 +1078,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||||||
|     s32 coef1 = coeffs[idx * 2]; |     s32 coef1 = coeffs[idx * 2]; | ||||||
|     s32 coef2 = coeffs[idx * 2 + 1]; |     s32 coef2 = coeffs[idx * 2 + 1]; | ||||||
|  |  | ||||||
|     const auto samples_remaining = |     const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; | ||||||
|         (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; |  | ||||||
|     const auto samples_processed = std::min(sample_count, samples_remaining); |     const auto samples_processed = std::min(sample_count, samples_remaining); | ||||||
|     const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset; |     const auto sample_pos = dsp_state.offset + sample_start_offset; | ||||||
|  |  | ||||||
|     const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; |     const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; | ||||||
|     auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + |     auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + | ||||||
| @@ -1210,9 +1208,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::size_t temp_mix_offset{}; |     std::size_t temp_mix_offset{}; | ||||||
|     bool is_buffer_completed{false}; |  | ||||||
|     auto samples_remaining = sample_count; |     auto samples_remaining = sample_count; | ||||||
|     while (samples_remaining > 0 && !is_buffer_completed) { |     while (samples_remaining > 0) { | ||||||
|         const auto samples_to_output = std::min(samples_remaining, min_required_samples); |         const auto samples_to_output = std::min(samples_remaining, min_required_samples); | ||||||
|         const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; |         const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; | ||||||
|  |  | ||||||
| @@ -1229,24 +1226,38 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||||||
|             const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; |             const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | ||||||
|             // No more data can be read |             // No more data can be read | ||||||
|             if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { |             if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { | ||||||
|                 is_buffer_completed = true; |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && |             if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && | ||||||
|                 wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { |                 wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { | ||||||
|                 // TODO(ogniK): ADPCM loop context |                 memory.ReadBlock(wave_buffer.context_address, &dsp_state.context, | ||||||
|  |                                  sizeof(ADPCMContext)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             s32 samples_offset_start; | ||||||
|  |             s32 samples_offset_end; | ||||||
|  |             if (dsp_state.loop_count > 0 && wave_buffer.loop_start_sample != 0 && | ||||||
|  |                 wave_buffer.loop_end_sample != 0 && | ||||||
|  |                 wave_buffer.loop_start_sample <= wave_buffer.loop_end_sample) { | ||||||
|  |                 samples_offset_start = wave_buffer.loop_start_sample; | ||||||
|  |                 samples_offset_end = wave_buffer.loop_end_sample; | ||||||
|  |             } else { | ||||||
|  |                 samples_offset_start = wave_buffer.start_sample_offset; | ||||||
|  |                 samples_offset_end = wave_buffer.end_sample_offset; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             s32 samples_decoded{0}; |             s32 samples_decoded{0}; | ||||||
|             switch (in_params.sample_format) { |             switch (in_params.sample_format) { | ||||||
|             case SampleFormat::Pcm16: |             case SampleFormat::Pcm16: | ||||||
|                 samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read, |                 samples_decoded = | ||||||
|                                               channel, temp_mix_offset); |                     DecodePcm16(voice_info, dsp_state, samples_offset_start, samples_offset_end, | ||||||
|  |                                 samples_to_read - samples_read, channel, temp_mix_offset); | ||||||
|                 break; |                 break; | ||||||
|             case SampleFormat::Adpcm: |             case SampleFormat::Adpcm: | ||||||
|                 samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read, |                 samples_decoded = | ||||||
|                                               channel, temp_mix_offset); |                     DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end, | ||||||
|  |                                 samples_to_read - samples_read, channel, temp_mix_offset); | ||||||
|                 break; |                 break; | ||||||
|             default: |             default: | ||||||
|                 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); |                 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); | ||||||
| @@ -1257,15 +1268,19 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||||||
|             dsp_state.offset += samples_decoded; |             dsp_state.offset += samples_decoded; | ||||||
|             dsp_state.played_sample_count += samples_decoded; |             dsp_state.played_sample_count += samples_decoded; | ||||||
|  |  | ||||||
|             if (dsp_state.offset >= |             if (dsp_state.offset >= (samples_offset_end - samples_offset_start) || | ||||||
|                     (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) || |  | ||||||
|                 samples_decoded == 0) { |                 samples_decoded == 0) { | ||||||
|                 // Reset our sample offset |                 // Reset our sample offset | ||||||
|                 dsp_state.offset = 0; |                 dsp_state.offset = 0; | ||||||
|                 if (wave_buffer.is_looping) { |                 if (wave_buffer.is_looping) { | ||||||
|                     if (samples_decoded == 0) { |                     dsp_state.loop_count++; | ||||||
|  |                     if (wave_buffer.loop_count > 0 && | ||||||
|  |                         (dsp_state.loop_count > wave_buffer.loop_count || samples_decoded == 0)) { | ||||||
|                         // End of our buffer |                         // End of our buffer | ||||||
|                         is_buffer_completed = true; |                         voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (samples_decoded == 0) { | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
| @@ -1273,15 +1288,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||||||
|                         dsp_state.played_sample_count = 0; |                         dsp_state.played_sample_count = 0; | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|  |  | ||||||
|                     // Update our wave buffer states |                     // Update our wave buffer states | ||||||
|                     dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; |                     voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); | ||||||
|                     dsp_state.wave_buffer_consumed++; |  | ||||||
|                     dsp_state.wave_buffer_index = |  | ||||||
|                         (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; |  | ||||||
|                     if (wave_buffer.end_of_stream) { |  | ||||||
|                         dsp_state.played_sample_count = 0; |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -86,10 +86,10 @@ private: | |||||||
|                                std::vector<u8>& work_buffer); |                                std::vector<u8>& work_buffer); | ||||||
|     void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); |     void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); | ||||||
|     // DSP Code |     // DSP Code | ||||||
|     s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, |     s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, | ||||||
|                     s32 channel, std::size_t mix_offset); |                     s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); | ||||||
|     s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, |     s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, | ||||||
|                     s32 channel, std::size_t mix_offset); |                     s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); | ||||||
|     void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, |     void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, | ||||||
|                                s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); |                                s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -189,9 +189,6 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context, | |||||||
|         if (voice_in_params.is_new) { |         if (voice_in_params.is_new) { | ||||||
|             // Default our values for our voice |             // Default our values for our voice | ||||||
|             voice_info.Initialize(); |             voice_info.Initialize(); | ||||||
|             if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Zero out our voice states |             // Zero out our voice states | ||||||
|             for (std::size_t channel = 0; channel < channel_count; channel++) { |             for (std::size_t channel = 0; channel < channel_count; channel++) { | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ void ServerVoiceInfo::Initialize() { | |||||||
|     in_params.last_volume = 0.0f; |     in_params.last_volume = 0.0f; | ||||||
|     in_params.biquad_filter.fill({}); |     in_params.biquad_filter.fill({}); | ||||||
|     in_params.wave_buffer_count = 0; |     in_params.wave_buffer_count = 0; | ||||||
|     in_params.wave_bufffer_head = 0; |     in_params.wave_buffer_head = 0; | ||||||
|     in_params.mix_id = AudioCommon::NO_MIX; |     in_params.mix_id = AudioCommon::NO_MIX; | ||||||
|     in_params.splitter_info_id = AudioCommon::NO_SPLITTER; |     in_params.splitter_info_id = AudioCommon::NO_SPLITTER; | ||||||
|     in_params.additional_params_address = 0; |     in_params.additional_params_address = 0; | ||||||
| @@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() { | |||||||
|     out_params.played_sample_count = 0; |     out_params.played_sample_count = 0; | ||||||
|     out_params.wave_buffer_consumed = 0; |     out_params.wave_buffer_consumed = 0; | ||||||
|     in_params.voice_drop_flag = false; |     in_params.voice_drop_flag = false; | ||||||
|     in_params.buffer_mapped = false; |     in_params.buffer_mapped = true; | ||||||
|     in_params.wave_buffer_flush_request_count = 0; |     in_params.wave_buffer_flush_request_count = 0; | ||||||
|     in_params.was_biquad_filter_enabled.fill(false); |     in_params.was_biquad_filter_enabled.fill(false); | ||||||
|  |  | ||||||
| @@ -126,7 +126,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in, | |||||||
|     in_params.volume = voice_in.volume; |     in_params.volume = voice_in.volume; | ||||||
|     in_params.biquad_filter = voice_in.biquad_filter; |     in_params.biquad_filter = voice_in.biquad_filter; | ||||||
|     in_params.wave_buffer_count = voice_in.wave_buffer_count; |     in_params.wave_buffer_count = voice_in.wave_buffer_count; | ||||||
|     in_params.wave_bufffer_head = voice_in.wave_buffer_head; |     in_params.wave_buffer_head = voice_in.wave_buffer_head; | ||||||
|     if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { |     if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { | ||||||
|         const auto in_request_count = in_params.wave_buffer_flush_request_count; |         const auto in_request_count = in_params.wave_buffer_flush_request_count; | ||||||
|         const auto voice_request_count = voice_in.wave_buffer_flush_request_count; |         const auto voice_request_count = voice_in.wave_buffer_flush_request_count; | ||||||
| @@ -185,14 +185,16 @@ void ServerVoiceInfo::UpdateWaveBuffers( | |||||||
|             wave_buffer.buffer_size = 0; |             wave_buffer.buffer_size = 0; | ||||||
|             wave_buffer.context_address = 0; |             wave_buffer.context_address = 0; | ||||||
|             wave_buffer.context_size = 0; |             wave_buffer.context_size = 0; | ||||||
|  |             wave_buffer.loop_start_sample = 0; | ||||||
|  |             wave_buffer.loop_end_sample = 0; | ||||||
|             wave_buffer.sent_to_dsp = true; |             wave_buffer.sent_to_dsp = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Mark all our wave buffers as invalid |         // Mark all our wave buffers as invalid | ||||||
|         for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); |         for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); | ||||||
|              channel++) { |              channel++) { | ||||||
|             for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) { |             for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) { | ||||||
|                 is_valid = false; |                 voice_states[channel]->is_wave_buffer_valid[i] = false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -211,7 +213,7 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | |||||||
|                                        const WaveBuffer& in_wave_buffer, SampleFormat sample_format, |                                        const WaveBuffer& in_wave_buffer, SampleFormat sample_format, | ||||||
|                                        bool is_buffer_valid, |                                        bool is_buffer_valid, | ||||||
|                                        [[maybe_unused]] BehaviorInfo& behavior_info) { |                                        [[maybe_unused]] BehaviorInfo& behavior_info) { | ||||||
|     if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) { |     if (!is_buffer_valid && out_wavebuffer.sent_to_dsp && out_wavebuffer.buffer_address != 0) { | ||||||
|         out_wavebuffer.buffer_address = 0; |         out_wavebuffer.buffer_address = 0; | ||||||
|         out_wavebuffer.buffer_size = 0; |         out_wavebuffer.buffer_size = 0; | ||||||
|     } |     } | ||||||
| @@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | |||||||
|     if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { |     if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { | ||||||
|         // Validate sample offset sizings |         // Validate sample offset sizings | ||||||
|         if (sample_format == SampleFormat::Pcm16) { |         if (sample_format == SampleFormat::Pcm16) { | ||||||
|             const auto buffer_size = in_wave_buffer.buffer_size; |             const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); | ||||||
|             if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 || |             const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset; | ||||||
|                 (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) || |             const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset; | ||||||
|                 (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) { |             if (0 > start || start > buffer_size || 0 > end || end > buffer_size) { | ||||||
|                 // TODO(ogniK): Write error info |                 // TODO(ogniK): Write error info | ||||||
|  |                 LOG_ERROR(Audio, | ||||||
|  |                           "PCM16 wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " | ||||||
|  |                           "offsets were " | ||||||
|  |                           "{:08X} - 0x{:08X}", | ||||||
|  |                           buffer_size, sizeof(s16) * in_wave_buffer.start_sample_offset, | ||||||
|  |                           sizeof(s16) * in_wave_buffer.end_sample_offset); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } else if (sample_format == SampleFormat::Adpcm) { | ||||||
|  |             const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); | ||||||
|  |             const s64 start_frames = in_wave_buffer.start_sample_offset / 14; | ||||||
|  |             const s64 start_extra = in_wave_buffer.start_sample_offset % 14 == 0 | ||||||
|  |                                         ? 0 | ||||||
|  |                                         : (in_wave_buffer.start_sample_offset % 14) / 2 + 1 + | ||||||
|  |                                               (in_wave_buffer.start_sample_offset % 2); | ||||||
|  |             const s64 start = start_frames * 8 + start_extra; | ||||||
|  |             const s64 end_frames = in_wave_buffer.end_sample_offset / 14; | ||||||
|  |             const s64 end_extra = in_wave_buffer.end_sample_offset % 14 == 0 | ||||||
|  |                                       ? 0 | ||||||
|  |                                       : (in_wave_buffer.end_sample_offset % 14) / 2 + 1 + | ||||||
|  |                                             (in_wave_buffer.end_sample_offset % 2); | ||||||
|  |             const s64 end = end_frames * 8 + end_extra; | ||||||
|  |             if (in_wave_buffer.start_sample_offset < 0 || start > buffer_size || | ||||||
|  |                 in_wave_buffer.end_sample_offset < 0 || end > buffer_size) { | ||||||
|  |                 LOG_ERROR(Audio, | ||||||
|  |                           "ADPMC wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " | ||||||
|  |                           "offsets were " | ||||||
|  |                           "{:08X} - 0x{:08X}", | ||||||
|  |                           in_wave_buffer.buffer_size, start, end); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | |||||||
|         out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; |         out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; | ||||||
|         out_wavebuffer.context_address = in_wave_buffer.context_address; |         out_wavebuffer.context_address = in_wave_buffer.context_address; | ||||||
|         out_wavebuffer.context_size = in_wave_buffer.context_size; |         out_wavebuffer.context_size = in_wave_buffer.context_size; | ||||||
|  |         out_wavebuffer.loop_start_sample = in_wave_buffer.loop_start_sample; | ||||||
|  |         out_wavebuffer.loop_end_sample = in_wave_buffer.loop_end_sample; | ||||||
|         in_params.buffer_mapped = |         in_params.buffer_mapped = | ||||||
|             in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; |             in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; | ||||||
|         // TODO(ogniK): Pool mapper attachment |         // TODO(ogniK): Pool mapper attachment | ||||||
|         // TODO(ogniK): IsAdpcmLoopContextBugFixed |         // TODO(ogniK): IsAdpcmLoopContextBugFixed | ||||||
|  |         if (sample_format == SampleFormat::Adpcm && in_wave_buffer.context_address != 0 && | ||||||
|  |             in_wave_buffer.context_size != 0 && behavior_info.IsAdpcmLoopContextBugFixed()) { | ||||||
|  |         } else { | ||||||
|  |             out_wavebuffer.context_address = 0; | ||||||
|  |             out_wavebuffer.context_size = 0; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ServerVoiceInfo::WriteOutStatus( | void ServerVoiceInfo::WriteOutStatus( | ||||||
|     VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, |     VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, | ||||||
|     std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { |     std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { | ||||||
|     if (voice_in.is_new) { |     if (voice_in.is_new || in_params.is_new) { | ||||||
|         in_params.is_new = true; |         in_params.is_new = true; | ||||||
|         voice_out.wave_buffer_consumed = 0; |         voice_out.wave_buffer_consumed = 0; | ||||||
|         voice_out.played_sample_count = 0; |         voice_out.played_sample_count = 0; | ||||||
|         voice_out.voice_dropped = false; |         voice_out.voice_dropped = false; | ||||||
|     } else if (!in_params.is_new) { |  | ||||||
|         voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed; |  | ||||||
|         voice_out.played_sample_count = voice_states[0]->played_sample_count; |  | ||||||
|         voice_out.voice_dropped = in_params.voice_drop_flag; |  | ||||||
|     } else { |     } else { | ||||||
|         voice_out.wave_buffer_consumed = 0; |         const auto& state = voice_states[0]; | ||||||
|         voice_out.played_sample_count = 0; |         voice_out.wave_buffer_consumed = state->wave_buffer_consumed; | ||||||
|         voice_out.voice_dropped = false; |         voice_out.played_sample_count = state->played_sample_count; | ||||||
|  |         voice_out.voice_dropped = state->voice_dropped; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -283,7 +319,8 @@ ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() { | |||||||
|  |  | ||||||
| bool ServerVoiceInfo::ShouldSkip() const { | bool ServerVoiceInfo::ShouldSkip() const { | ||||||
|     // TODO(ogniK): Handle unmapped wave buffers or parameters |     // TODO(ogniK): Handle unmapped wave buffers or parameters | ||||||
|     return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag; |     return !in_params.in_use || in_params.wave_buffer_count == 0 || !in_params.buffer_mapped || | ||||||
|  |            in_params.voice_drop_flag; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { | bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { | ||||||
| @@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration( | |||||||
| void ServerVoiceInfo::FlushWaveBuffers( | void ServerVoiceInfo::FlushWaveBuffers( | ||||||
|     u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, |     u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, | ||||||
|     s32 channel_count) { |     s32 channel_count) { | ||||||
|     auto wave_head = in_params.wave_bufffer_head; |     auto wave_head = in_params.wave_buffer_head; | ||||||
|  |  | ||||||
|     for (u8 i = 0; i < flush_count; i++) { |     for (u8 i = 0; i < flush_count; i++) { | ||||||
|         in_params.wave_buffer[wave_head].sent_to_dsp = true; |         in_params.wave_buffer[wave_head].sent_to_dsp = true; | ||||||
| @@ -401,6 +438,17 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const { | |||||||
|     return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); |     return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ServerVoiceInfo::SetWaveBufferCompleted(VoiceState& dsp_state, | ||||||
|  |                                              const ServerWaveBuffer& wave_buffer) { | ||||||
|  |     dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; | ||||||
|  |     dsp_state.wave_buffer_consumed++; | ||||||
|  |     dsp_state.wave_buffer_index = (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; | ||||||
|  |     dsp_state.loop_count = 0; | ||||||
|  |     if (wave_buffer.end_of_stream) { | ||||||
|  |         dsp_state.played_sample_count = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { | VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { | ||||||
|     for (std::size_t i = 0; i < voice_count; i++) { |     for (std::size_t i = 0; i < voice_count; i++) { | ||||||
|         voice_channel_resources.emplace_back(static_cast<s32>(i)); |         voice_channel_resources.emplace_back(static_cast<s32>(i)); | ||||||
|   | |||||||
| @@ -60,10 +60,12 @@ struct WaveBuffer { | |||||||
|     u8 is_looping{}; |     u8 is_looping{}; | ||||||
|     u8 end_of_stream{}; |     u8 end_of_stream{}; | ||||||
|     u8 sent_to_server{}; |     u8 sent_to_server{}; | ||||||
|     INSERT_PADDING_BYTES(5); |     INSERT_PADDING_BYTES(1); | ||||||
|  |     s32 loop_count{}; | ||||||
|     u64 context_address{}; |     u64 context_address{}; | ||||||
|     u64 context_size{}; |     u64 context_size{}; | ||||||
|     INSERT_PADDING_BYTES(8); |     u32 loop_start_sample{}; | ||||||
|  |     u32 loop_end_sample{}; | ||||||
| }; | }; | ||||||
| static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); | static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); | ||||||
|  |  | ||||||
| @@ -76,6 +78,9 @@ struct ServerWaveBuffer { | |||||||
|     bool end_of_stream{}; |     bool end_of_stream{}; | ||||||
|     VAddr context_address{}; |     VAddr context_address{}; | ||||||
|     std::size_t context_size{}; |     std::size_t context_size{}; | ||||||
|  |     s32 loop_count{}; | ||||||
|  |     u32 loop_start_sample{}; | ||||||
|  |     u32 loop_end_sample{}; | ||||||
|     bool sent_to_dsp{true}; |     bool sent_to_dsp{true}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -108,6 +113,7 @@ struct VoiceState { | |||||||
|     u32 external_context_size; |     u32 external_context_size; | ||||||
|     bool is_external_context_used; |     bool is_external_context_used; | ||||||
|     bool voice_dropped; |     bool voice_dropped; | ||||||
|  |     s32 loop_count; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class VoiceChannelResource { | class VoiceChannelResource { | ||||||
| @@ -206,7 +212,7 @@ public: | |||||||
|         float last_volume{}; |         float last_volume{}; | ||||||
|         std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; |         std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; | ||||||
|         s32 wave_buffer_count{}; |         s32 wave_buffer_count{}; | ||||||
|         s16 wave_bufffer_head{}; |         s16 wave_buffer_head{}; | ||||||
|         INSERT_PADDING_BYTES(2); |         INSERT_PADDING_BYTES(2); | ||||||
|         BehaviorFlags behavior_flags{}; |         BehaviorFlags behavior_flags{}; | ||||||
|         VAddr additional_params_address{}; |         VAddr additional_params_address{}; | ||||||
| @@ -252,6 +258,7 @@ public: | |||||||
|     void FlushWaveBuffers(u8 flush_count, |     void FlushWaveBuffers(u8 flush_count, | ||||||
|                           std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, |                           std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, | ||||||
|                           s32 channel_count); |                           s32 channel_count); | ||||||
|  |     void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     std::vector<s16> stored_samples; |     std::vector<s16> stored_samples; | ||||||
|   | |||||||
| @@ -96,7 +96,7 @@ private: | |||||||
|     void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { |     void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { | ||||||
|         LOG_DEBUG(Service_Audio, "(STUBBED) called"); |         LOG_DEBUG(Service_Audio, "(STUBBED) called"); | ||||||
|  |  | ||||||
|         std::vector<u8> output_params(ctx.GetWriteBufferSize()); |         std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); | ||||||
|         auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); |         auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); | ||||||
|  |  | ||||||
|         if (result.IsSuccess()) { |         if (result.IsSuccess()) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user