diff options
Diffstat (limited to 'src/audio_core')
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 118 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.h | 23 | ||||
| -rw-r--r-- | src/audio_core/common.h | 1 | ||||
| -rw-r--r-- | src/audio_core/stream.cpp | 25 | ||||
| -rw-r--r-- | src/audio_core/stream.h | 5 |
5 files changed, 147 insertions, 25 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index d18ef6940..d64452617 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -17,7 +17,7 @@ namespace AudioCore { constexpr u32 STREAM_SAMPLE_RATE{48000}; constexpr u32 STREAM_NUM_CHANNELS{2}; - +using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>; class AudioRenderer::VoiceState { public: bool IsPlaying() const { @@ -37,9 +37,10 @@ public: } void SetWaveIndex(std::size_t index); - std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory); + std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory, + const VoiceChannelHolder& voice_resources); void UpdateState(); - void RefreshBuffer(Core::Memory::Memory& memory); + void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources); private: bool is_in_use{}; @@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number) : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), - effects(params.effect_count), memory{memory_} { + voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} { behavior_info.SetUserRevision(params.revision); audio_out = std::make_unique<AudioCore::AudioOut>(); stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, @@ -127,6 +128,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, memory_pool_count * sizeof(MemoryPoolInfo)); + // Copy voice resources + const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size + + config.memory_pools_size}; + std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset, + sizeof(VoiceResourceInformation) * voice_resources.size()); + // Copy VoiceInfo structs std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + config.voice_resource_size}; @@ -173,11 +180,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< // Copy output header UpdateDataHeader response_data{worker_params}; - std::vector<u8> output_params(response_data.total_size); if (behavior_info.IsElapsedFrameCountSupported()) { - response_data.frame_count = 0x10; - response_data.total_size += 0x10; + response_data.render_info = sizeof(RendererInfo); + response_data.total_size += sizeof(RendererInfo); } + + std::vector<u8> output_params(response_data.total_size); std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); // Copy output memory pool entries @@ -212,6 +220,17 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< return Audren::ERR_INVALID_PARAMETERS; } + if (behavior_info.IsElapsedFrameCountSupported()) { + const std::size_t renderer_info_offset{ + sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + + response_data.effects_size + response_data.sinks_size + + response_data.performance_manager_size + response_data.behavior_size}; + RendererInfo renderer_info{}; + renderer_info.elasped_frame_count = elapsed_frame_count; + std::memcpy(output_params.data() + renderer_info_offset, &renderer_info, + sizeof(RendererInfo)); + } + return MakeResult(output_params); } @@ -220,14 +239,15 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { is_refresh_pending = true; } -std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count, - Core::Memory::Memory& memory) { +std::vector<s16> AudioRenderer::VoiceState::DequeueSamples( + std::size_t sample_count, Core::Memory::Memory& memory, + const VoiceChannelHolder& voice_resources) { if (!IsPlaying()) { return {}; } if (is_refresh_pending) { - RefreshBuffer(memory); + RefreshBuffer(memory, voice_resources); } const std::size_t max_size{samples.size() - offset}; @@ -271,7 +291,8 @@ void AudioRenderer::VoiceState::UpdateState() { is_in_use = info.is_in_use; } -void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { +void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory, + const VoiceChannelHolder& voice_resources) { const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); @@ -296,17 +317,77 @@ void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { } switch (info.channel_count) { - case 1: + case 1: { // 1 channel is upsampled to 2 channel samples.resize(new_samples.size() * 2); + for (std::size_t index = 0; index < new_samples.size(); ++index) { - samples[index * 2] = new_samples[index]; - samples[index * 2 + 1] = new_samples[index]; + auto sample = static_cast<float>(new_samples[index]); + if (voice_resources[0]->in_use) { + sample *= voice_resources[0]->mix_volumes[0]; + } + + samples[index * 2] = static_cast<s16>(sample * info.volume); + samples[index * 2 + 1] = static_cast<s16>(sample * info.volume); } break; + } case 2: { // 2 channel is played as is samples = std::move(new_samples); + const std::size_t sample_count = (samples.size() / 2); + for (std::size_t index = 0; index < sample_count; ++index) { + const std::size_t index_l = index * 2; + const std::size_t index_r = index * 2 + 1; + + auto sample_l = static_cast<float>(samples[index_l]); + auto sample_r = static_cast<float>(samples[index_r]); + + if (voice_resources[0]->in_use) { + sample_l *= voice_resources[0]->mix_volumes[0]; + } + + if (voice_resources[1]->in_use) { + sample_r *= voice_resources[1]->mix_volumes[1]; + } + + samples[index_l] = static_cast<s16>(sample_l * info.volume); + samples[index_r] = static_cast<s16>(sample_r * info.volume); + } + break; + } + case 6: { + samples.resize((new_samples.size() / 6) * 2); + const std::size_t sample_count = samples.size() / 2; + + for (std::size_t index = 0; index < sample_count; ++index) { + auto FL = static_cast<float>(new_samples[index * 6]); + auto FR = static_cast<float>(new_samples[index * 6 + 1]); + auto FC = static_cast<float>(new_samples[index * 6 + 2]); + auto BL = static_cast<float>(new_samples[index * 6 + 4]); + auto BR = static_cast<float>(new_samples[index * 6 + 5]); + + if (voice_resources[0]->in_use) { + FL *= voice_resources[0]->mix_volumes[0]; + } + if (voice_resources[1]->in_use) { + FR *= voice_resources[1]->mix_volumes[1]; + } + if (voice_resources[2]->in_use) { + FC *= voice_resources[2]->mix_volumes[2]; + } + if (voice_resources[4]->in_use) { + BL *= voice_resources[4]->mix_volumes[4]; + } + if (voice_resources[5]->in_use) { + BR *= voice_resources[5]->mix_volumes[5]; + } + + samples[index * 2] = + static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume); + samples[index * 2 + 1] = + static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume); + } break; } default: @@ -352,11 +433,17 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { if (!voice.IsPlaying()) { continue; } + VoiceChannelHolder resources{}; + for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) { + const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel]; + resources[channel] = &voice_resources[channel_resource_id]; + } std::size_t offset{}; s64 samples_remaining{BUFFER_SIZE}; while (samples_remaining > 0) { - const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)}; + const std::vector<s16> samples{ + voice.DequeueSamples(samples_remaining, memory, resources)}; if (samples.empty()) { break; @@ -372,6 +459,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { } } audio_out->QueueBuffer(stream, tag, std::move(buffer)); + elapsed_frame_count++; } void AudioRenderer::ReleaseAndQueueBuffers() { diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index b42770fae..f0b691a86 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -9,6 +9,7 @@ #include <vector> #include "audio_core/behavior_info.h" +#include "audio_core/common.h" #include "audio_core/stream.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -116,6 +117,14 @@ struct WaveBuffer { }; static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); +struct VoiceResourceInformation { + s32_le id{}; + std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{}; + bool in_use{}; + INSERT_PADDING_BYTES(11); +}; +static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size"); + struct VoiceInfo { u32_le id; u32_le node_id; @@ -187,6 +196,12 @@ struct EffectOutStatus { }; static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); +struct RendererInfo { + u64_le elasped_frame_count{}; + INSERT_PADDING_WORDS(2); +}; +static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size"); + struct UpdateDataHeader { UpdateDataHeader() {} @@ -200,7 +215,7 @@ struct UpdateDataHeader { mixes_size = 0x0; sinks_size = config.sink_count * 0x20; performance_manager_size = 0x10; - frame_count = 0; + render_info = 0; total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + effects_size + sinks_size + performance_manager_size; } @@ -214,8 +229,8 @@ struct UpdateDataHeader { u32_le mixes_size{}; u32_le sinks_size{}; u32_le performance_manager_size{}; - INSERT_PADDING_WORDS(1); - u32_le frame_count{}; + u32_le splitter_size{}; + u32_le render_info{}; INSERT_PADDING_WORDS(4); u32_le total_size{}; }; @@ -244,10 +259,12 @@ private: AudioRendererParameter worker_params; std::shared_ptr<Kernel::WritableEvent> buffer_event; std::vector<VoiceState> voices; + std::vector<VoiceResourceInformation> voice_resources; std::vector<EffectState> effects; std::unique_ptr<AudioOut> audio_out; StreamPtr stream; Core::Memory::Memory& memory; + std::size_t elapsed_frame_count{}; }; } // namespace AudioCore diff --git a/src/audio_core/common.h b/src/audio_core/common.h index 98478b66b..7bb145c53 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h @@ -14,6 +14,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; } constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); +constexpr std::size_t MAX_MIX_BUFFERS = 24; static constexpr u32 VersionFromRevision(u32_le rev) { // "REV7" -> 7 diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 4ca98f8ea..dfc4805d9 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -59,15 +59,24 @@ Stream::State Stream::GetState() const { return state; } -s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { +s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const { const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; - const auto us = - std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate); - return Core::Timing::usToCycles(us); + const auto ns = + std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate); + return ns.count(); +} + +s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const { + const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; + /// DSP signals before playing the last sample, in HLE we emulate this in this way + s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0); + const auto ns = + std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate); + return ns.count(); } static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { - const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)}; + const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)}; if (volume == 1.0f) { return; @@ -105,7 +114,11 @@ void Stream::PlayNextBuffer() { sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); - core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {}); + if (core_timing.IsHostTiming()) { + core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {}); + } else { + core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {}); + } } void Stream::ReleaseActiveBuffer() { diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 1708a4d98..e309d60fe 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -96,7 +96,10 @@ private: void ReleaseActiveBuffer(); /// Gets the number of core cycles when the specified buffer will be released - s64 GetBufferReleaseCycles(const Buffer& buffer) const; + s64 GetBufferReleaseNS(const Buffer& buffer) const; + + /// Gets the number of core cycles when the specified buffer will be released + s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const; u32 sample_rate; ///< Sample rate of the stream Format format; ///< Format of the stream |
