Fix XC2/VOEZ crashing, add audio looping and a few misc fixes
This commit is contained in:
		| @@ -129,87 +129,85 @@ Stream::State AudioRenderer::GetStreamState() const { | ||||
|  | ||||
| ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, | ||||
|                                               std::vector<u8>& output_params) { | ||||
|     { | ||||
|         std::scoped_lock lock{mutex}; | ||||
|         InfoUpdater info_updater{input_params, output_params, behavior_info}; | ||||
|     std::scoped_lock lock{mutex}; | ||||
|     InfoUpdater info_updater{input_params, output_params, behavior_info}; | ||||
|  | ||||
|         if (!info_updater.UpdateBehaviorInfo(behavior_info)) { | ||||
|             LOG_ERROR(Audio, "Failed to update behavior info input parameters"); | ||||
|     if (!info_updater.UpdateBehaviorInfo(behavior_info)) { | ||||
|         LOG_ERROR(Audio, "Failed to update behavior info input parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     if (!info_updater.UpdateMemoryPools(memory_pool_info)) { | ||||
|         LOG_ERROR(Audio, "Failed to update memory pool parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     if (!info_updater.UpdateVoiceChannelResources(voice_context)) { | ||||
|         LOG_ERROR(Audio, "Failed to update voice channel resource parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) { | ||||
|         LOG_ERROR(Audio, "Failed to update voice parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     // TODO(ogniK): Deal with stopped audio renderer but updates still taking place | ||||
|     if (!info_updater.UpdateEffects(effect_context, true)) { | ||||
|         LOG_ERROR(Audio, "Failed to update effect parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     if (behavior_info.IsSplitterSupported()) { | ||||
|         if (!info_updater.UpdateSplitterInfo(splitter_context)) { | ||||
|             LOG_ERROR(Audio, "Failed to update splitter parameters"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         if (!info_updater.UpdateMemoryPools(memory_pool_info)) { | ||||
|             LOG_ERROR(Audio, "Failed to update memory pool parameters"); | ||||
|     const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, | ||||
|                                                      splitter_context, effect_context); | ||||
|  | ||||
|     if (mix_result.IsError()) { | ||||
|         LOG_ERROR(Audio, "Failed to update mix parameters"); | ||||
|         return mix_result; | ||||
|     } | ||||
|  | ||||
|     // TODO(ogniK): Sinks | ||||
|     if (!info_updater.UpdateSinks(sink_context)) { | ||||
|         LOG_ERROR(Audio, "Failed to update sink parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     // TODO(ogniK): Performance buffer | ||||
|     if (!info_updater.UpdatePerformanceBuffer()) { | ||||
|         LOG_ERROR(Audio, "Failed to update performance buffer parameters"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     if (!info_updater.UpdateErrorInfo(behavior_info)) { | ||||
|         LOG_ERROR(Audio, "Failed to update error info"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|     if (behavior_info.IsElapsedFrameCountSupported()) { | ||||
|         if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) { | ||||
|             LOG_ERROR(Audio, "Failed to update renderer info"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|     } | ||||
|     // TODO(ogniK): Statistics | ||||
|  | ||||
|         if (!info_updater.UpdateVoiceChannelResources(voice_context)) { | ||||
|             LOG_ERROR(Audio, "Failed to update voice channel resource parameters"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|     if (!info_updater.WriteOutputHeader()) { | ||||
|         LOG_ERROR(Audio, "Failed to write output header"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|  | ||||
|         if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) { | ||||
|             LOG_ERROR(Audio, "Failed to update voice parameters"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|     // TODO(ogniK): Check when all sections are implemented | ||||
|  | ||||
|         // TODO(ogniK): Deal with stopped audio renderer but updates still taking place | ||||
|         if (!info_updater.UpdateEffects(effect_context, true)) { | ||||
|             LOG_ERROR(Audio, "Failed to update effect parameters"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|  | ||||
|         if (behavior_info.IsSplitterSupported()) { | ||||
|             if (!info_updater.UpdateSplitterInfo(splitter_context)) { | ||||
|                 LOG_ERROR(Audio, "Failed to update splitter parameters"); | ||||
|                 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         const auto mix_result = info_updater.UpdateMixes( | ||||
|             mix_context, worker_params.mix_buffer_count, splitter_context, effect_context); | ||||
|  | ||||
|         if (mix_result.IsError()) { | ||||
|             LOG_ERROR(Audio, "Failed to update mix parameters"); | ||||
|             return mix_result; | ||||
|         } | ||||
|  | ||||
|         // TODO(ogniK): Sinks | ||||
|         if (!info_updater.UpdateSinks(sink_context)) { | ||||
|             LOG_ERROR(Audio, "Failed to update sink parameters"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|  | ||||
|         // TODO(ogniK): Performance buffer | ||||
|         if (!info_updater.UpdatePerformanceBuffer()) { | ||||
|             LOG_ERROR(Audio, "Failed to update performance buffer parameters"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|  | ||||
|         if (!info_updater.UpdateErrorInfo(behavior_info)) { | ||||
|             LOG_ERROR(Audio, "Failed to update error info"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|  | ||||
|         if (behavior_info.IsElapsedFrameCountSupported()) { | ||||
|             if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) { | ||||
|                 LOG_ERROR(Audio, "Failed to update renderer info"); | ||||
|                 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|             } | ||||
|         } | ||||
|         // TODO(ogniK): Statistics | ||||
|  | ||||
|         if (!info_updater.WriteOutputHeader()) { | ||||
|             LOG_ERROR(Audio, "Failed to write output header"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|  | ||||
|         // TODO(ogniK): Check when all sections are implemented | ||||
|  | ||||
|         if (!info_updater.CheckConsumedSize()) { | ||||
|             LOG_ERROR(Audio, "Audio buffers were not consumed!"); | ||||
|             return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|         } | ||||
|     if (!info_updater.CheckConsumedSize()) { | ||||
|         LOG_ERROR(Audio, "Audio buffers were not consumed!"); | ||||
|         return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||||
|     } | ||||
|     return ResultSuccess; | ||||
| } | ||||
| @@ -234,10 +232,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | ||||
|     command_generator.PostCommand(); | ||||
|     // Base sample size | ||||
|     std::size_t BUFFER_SIZE{worker_params.sample_count}; | ||||
|     // Samples | ||||
|     std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); | ||||
|     // Make sure to clear our samples | ||||
|     std::memset(buffer.data(), 0, buffer.size() * sizeof(s16)); | ||||
|     // Samples, making sure to clear | ||||
|     std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0); | ||||
|  | ||||
|     if (sink_context.InUse()) { | ||||
|         const auto stream_channel_count = stream->GetNumChannels(); | ||||
|   | ||||
| @@ -795,7 +795,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta | ||||
|         state.lowpass_1 = 0.0f; | ||||
|     } else { | ||||
|         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))); | ||||
|         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 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++) { | ||||
|         const auto length = AudioCommon::CalculateDelaySamples( | ||||
|             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 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& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | ||||
|     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) { | ||||
|         return 0; | ||||
|     } | ||||
|     if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { | ||||
|     if (sample_end_offset < sample_start_offset) { | ||||
|         return 0; | ||||
|     } | ||||
|     const auto samples_remaining = | ||||
|         (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; | ||||
|     const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; | ||||
|     const auto start_offset = | ||||
|         ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) * | ||||
|         sizeof(s16); | ||||
|         ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(s16); | ||||
|     const auto buffer_pos = wave_buffer.buffer_address + start_offset; | ||||
|     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 sample_count, [[maybe_unused]] s32 channel, | ||||
|                                   std::size_t mix_offset) { | ||||
|                                   s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, | ||||
|                                   [[maybe_unused]] s32 channel, std::size_t mix_offset) { | ||||
|     const auto& in_params = voice_info.GetInParams(); | ||||
|     const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | ||||
|     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) { | ||||
|         return 0; | ||||
|     } | ||||
|     if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { | ||||
|     if (sample_end_offset < sample_start_offset) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -1079,10 +1078,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s | ||||
|     s32 coef1 = coeffs[idx * 2]; | ||||
|     s32 coef2 = coeffs[idx * 2 + 1]; | ||||
|  | ||||
|     const auto samples_remaining = | ||||
|         (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; | ||||
|     const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; | ||||
|     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; | ||||
|     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{}; | ||||
|     bool is_buffer_completed{false}; | ||||
|     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_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]; | ||||
|             // No more data can be read | ||||
|             if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { | ||||
|                 is_buffer_completed = true; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 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}; | ||||
|             switch (in_params.sample_format) { | ||||
|             case SampleFormat::Pcm16: | ||||
|                 samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read, | ||||
|                                               channel, temp_mix_offset); | ||||
|                 samples_decoded = | ||||
|                     DecodePcm16(voice_info, dsp_state, samples_offset_start, samples_offset_end, | ||||
|                                 samples_to_read - samples_read, channel, temp_mix_offset); | ||||
|                 break; | ||||
|             case SampleFormat::Adpcm: | ||||
|                 samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read, | ||||
|                                               channel, temp_mix_offset); | ||||
|                 samples_decoded = | ||||
|                     DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end, | ||||
|                                 samples_to_read - samples_read, channel, temp_mix_offset); | ||||
|                 break; | ||||
|             default: | ||||
|                 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.played_sample_count += samples_decoded; | ||||
|  | ||||
|             if (dsp_state.offset >= | ||||
|                     (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) || | ||||
|             if (dsp_state.offset >= (samples_offset_end - samples_offset_start) || | ||||
|                 samples_decoded == 0) { | ||||
|                 // Reset our sample offset | ||||
|                 dsp_state.offset = 0; | ||||
|                 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 | ||||
|                         is_buffer_completed = true; | ||||
|                         voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); | ||||
|                     } | ||||
|  | ||||
|                     if (samples_decoded == 0) { | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
| @@ -1273,15 +1288,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | ||||
|                         dsp_state.played_sample_count = 0; | ||||
|                     } | ||||
|                 } else { | ||||
|  | ||||
|                     // Update our wave buffer states | ||||
|                     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; | ||||
|                     if (wave_buffer.end_of_stream) { | ||||
|                         dsp_state.played_sample_count = 0; | ||||
|                     } | ||||
|                     voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -86,10 +86,10 @@ private: | ||||
|                                std::vector<u8>& work_buffer); | ||||
|     void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); | ||||
|     // DSP Code | ||||
|     s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, | ||||
|                     s32 channel, std::size_t mix_offset); | ||||
|     s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, | ||||
|                     s32 channel, std::size_t mix_offset); | ||||
|     s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_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_start_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, | ||||
|                                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) { | ||||
|             // Default our values for our voice | ||||
|             voice_info.Initialize(); | ||||
|             if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // Zero out our voice states | ||||
|             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.biquad_filter.fill({}); | ||||
|     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.splitter_info_id = AudioCommon::NO_SPLITTER; | ||||
|     in_params.additional_params_address = 0; | ||||
| @@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() { | ||||
|     out_params.played_sample_count = 0; | ||||
|     out_params.wave_buffer_consumed = 0; | ||||
|     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.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.biquad_filter = voice_in.biquad_filter; | ||||
|     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()) { | ||||
|         const auto in_request_count = in_params.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.context_address = 0; | ||||
|             wave_buffer.context_size = 0; | ||||
|             wave_buffer.loop_start_sample = 0; | ||||
|             wave_buffer.loop_end_sample = 0; | ||||
|             wave_buffer.sent_to_dsp = true; | ||||
|         } | ||||
|  | ||||
|         // Mark all our wave buffers as invalid | ||||
|         for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); | ||||
|              channel++) { | ||||
|             for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) { | ||||
|                 is_valid = false; | ||||
|             for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) { | ||||
|                 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, | ||||
|                                        bool is_buffer_valid, | ||||
|                                        [[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_size = 0; | ||||
|     } | ||||
| @@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | ||||
|     if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { | ||||
|         // Validate sample offset sizings | ||||
|         if (sample_format == SampleFormat::Pcm16) { | ||||
|             const auto buffer_size = in_wave_buffer.buffer_size; | ||||
|             if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 || | ||||
|                 (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) || | ||||
|                 (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) { | ||||
|             const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); | ||||
|             const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset; | ||||
|             const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset; | ||||
|             if (0 > start || start > buffer_size || 0 > end || end > buffer_size) { | ||||
|                 // 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; | ||||
|             } | ||||
|         } | ||||
| @@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | ||||
|         out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; | ||||
|         out_wavebuffer.context_address = in_wave_buffer.context_address; | ||||
|         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_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; | ||||
|         // TODO(ogniK): Pool mapper attachment | ||||
|         // 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( | ||||
|     VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, | ||||
|     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; | ||||
|         voice_out.wave_buffer_consumed = 0; | ||||
|         voice_out.played_sample_count = 0; | ||||
|         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 { | ||||
|         voice_out.wave_buffer_consumed = 0; | ||||
|         voice_out.played_sample_count = 0; | ||||
|         voice_out.voice_dropped = false; | ||||
|         const auto& state = voice_states[0]; | ||||
|         voice_out.wave_buffer_consumed = state->wave_buffer_consumed; | ||||
|         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 { | ||||
|     // 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) { | ||||
| @@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration( | ||||
| void ServerVoiceInfo::FlushWaveBuffers( | ||||
|     u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, | ||||
|     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++) { | ||||
|         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(); | ||||
| } | ||||
|  | ||||
| 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_} { | ||||
|     for (std::size_t i = 0; i < voice_count; i++) { | ||||
|         voice_channel_resources.emplace_back(static_cast<s32>(i)); | ||||
|   | ||||
| @@ -60,10 +60,12 @@ struct WaveBuffer { | ||||
|     u8 is_looping{}; | ||||
|     u8 end_of_stream{}; | ||||
|     u8 sent_to_server{}; | ||||
|     INSERT_PADDING_BYTES(5); | ||||
|     INSERT_PADDING_BYTES(1); | ||||
|     s32 loop_count{}; | ||||
|     u64 context_address{}; | ||||
|     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"); | ||||
|  | ||||
| @@ -76,6 +78,9 @@ struct ServerWaveBuffer { | ||||
|     bool end_of_stream{}; | ||||
|     VAddr context_address{}; | ||||
|     std::size_t context_size{}; | ||||
|     s32 loop_count{}; | ||||
|     u32 loop_start_sample{}; | ||||
|     u32 loop_end_sample{}; | ||||
|     bool sent_to_dsp{true}; | ||||
| }; | ||||
|  | ||||
| @@ -108,6 +113,7 @@ struct VoiceState { | ||||
|     u32 external_context_size; | ||||
|     bool is_external_context_used; | ||||
|     bool voice_dropped; | ||||
|     s32 loop_count; | ||||
| }; | ||||
|  | ||||
| class VoiceChannelResource { | ||||
| @@ -206,7 +212,7 @@ public: | ||||
|         float last_volume{}; | ||||
|         std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; | ||||
|         s32 wave_buffer_count{}; | ||||
|         s16 wave_bufffer_head{}; | ||||
|         s16 wave_buffer_head{}; | ||||
|         INSERT_PADDING_BYTES(2); | ||||
|         BehaviorFlags behavior_flags{}; | ||||
|         VAddr additional_params_address{}; | ||||
| @@ -252,6 +258,7 @@ public: | ||||
|     void FlushWaveBuffers(u8 flush_count, | ||||
|                           std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, | ||||
|                           s32 channel_count); | ||||
|     void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer); | ||||
|  | ||||
| private: | ||||
|     std::vector<s16> stored_samples; | ||||
|   | ||||
| @@ -96,7 +96,7 @@ private: | ||||
|     void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { | ||||
|         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); | ||||
|  | ||||
|         if (result.IsSuccess()) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user