aboutsummaryrefslogtreecommitdiff
path: root/src/audio_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/audio_renderer.cpp118
-rw-r--r--src/audio_core/audio_renderer.h23
-rw-r--r--src/audio_core/common.h1
-rw-r--r--src/audio_core/stream.cpp25
-rw-r--r--src/audio_core/stream.h5
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