mirror of https://github.com/yuzu-emu/yuzu
381 lines
14 KiB
C++
381 lines
14 KiB
C++
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <bitset>
|
|
|
|
#include "audio_core/common/common.h"
|
|
#include "audio_core/common/wave_buffer.h"
|
|
#include "audio_core/renderer/behavior/behavior_info.h"
|
|
#include "audio_core/renderer/memory/address_info.h"
|
|
#include "common/common_types.h"
|
|
|
|
namespace AudioCore::AudioRenderer {
|
|
class PoolMapper;
|
|
class VoiceContext;
|
|
struct VoiceState;
|
|
|
|
/**
|
|
* Represents one voice. Voices are essentially noises, and they can be further mixed and have
|
|
* effects applied to them, but voices are the basis of all sounds.
|
|
*/
|
|
class VoiceInfo {
|
|
public:
|
|
enum class ServerPlayState {
|
|
Started,
|
|
Stopped,
|
|
RequestStop,
|
|
Paused,
|
|
};
|
|
|
|
struct Flags {
|
|
u8 IsVoicePlayedSampleCountResetAtLoopPointSupported : 1;
|
|
u8 IsVoicePitchAndSrcSkippedSupported : 1;
|
|
};
|
|
|
|
/**
|
|
* A wavebuffer contains information on the data source buffers.
|
|
*/
|
|
struct WaveBuffer {
|
|
void Copy(WaveBufferVersion1& other) {
|
|
other.buffer = buffer_address.GetReference(true);
|
|
other.buffer_size = buffer_address.GetSize();
|
|
other.start_offset = start_offset;
|
|
other.end_offset = end_offset;
|
|
other.loop = loop;
|
|
other.stream_ended = stream_ended;
|
|
|
|
if (context_address.GetCpuAddr()) {
|
|
other.context = context_address.GetReference(true);
|
|
other.context_size = context_address.GetSize();
|
|
} else {
|
|
other.context = CpuAddr(0);
|
|
other.context_size = 0;
|
|
}
|
|
}
|
|
|
|
void Copy(WaveBufferVersion2& other) {
|
|
other.buffer = buffer_address.GetReference(true);
|
|
other.buffer_size = buffer_address.GetSize();
|
|
other.start_offset = start_offset;
|
|
other.end_offset = end_offset;
|
|
other.loop_start_offset = loop_start_offset;
|
|
other.loop_end_offset = loop_end_offset;
|
|
other.loop = loop;
|
|
other.loop_count = loop_count;
|
|
other.stream_ended = stream_ended;
|
|
|
|
if (context_address.GetCpuAddr()) {
|
|
other.context = context_address.GetReference(true);
|
|
other.context_size = context_address.GetSize();
|
|
} else {
|
|
other.context = CpuAddr(0);
|
|
other.context_size = 0;
|
|
}
|
|
}
|
|
|
|
void Initialize() {
|
|
buffer_address.Setup(0, 0);
|
|
context_address.Setup(0, 0);
|
|
start_offset = 0;
|
|
end_offset = 0;
|
|
loop = false;
|
|
stream_ended = false;
|
|
sent_to_DSP = true;
|
|
loop_start_offset = 0;
|
|
loop_end_offset = 0;
|
|
loop_count = 0;
|
|
}
|
|
/// Game memory address of the wavebuffer data
|
|
AddressInfo buffer_address{0, 0};
|
|
/// Context for decoding, used for ADPCM
|
|
AddressInfo context_address{0, 0};
|
|
/// Starting offset for the wavebuffer
|
|
u32 start_offset{};
|
|
/// Ending offset the wavebuffer
|
|
u32 end_offset{};
|
|
/// Should this wavebuffer loop?
|
|
bool loop{};
|
|
/// Has this wavebuffer ended?
|
|
bool stream_ended{};
|
|
/// Has this wavebuffer been sent to the AudioRenderer?
|
|
bool sent_to_DSP{true};
|
|
/// Starting offset when looping, can differ from start_offset
|
|
u32 loop_start_offset{};
|
|
/// Ending offset when looping, can differ from end_offset
|
|
u32 loop_end_offset{};
|
|
/// Number of times to loop this wavebuffer
|
|
s32 loop_count{};
|
|
};
|
|
|
|
struct WaveBufferInternal {
|
|
/* 0x00 */ CpuAddr address;
|
|
/* 0x08 */ u64 size;
|
|
/* 0x10 */ s32 start_offset;
|
|
/* 0x14 */ s32 end_offset;
|
|
/* 0x18 */ bool loop;
|
|
/* 0x19 */ bool stream_ended;
|
|
/* 0x1A */ bool sent_to_DSP;
|
|
/* 0x1C */ s32 loop_count;
|
|
/* 0x20 */ CpuAddr context_address;
|
|
/* 0x28 */ u64 context_size;
|
|
/* 0x30 */ u32 loop_start;
|
|
/* 0x34 */ u32 loop_end;
|
|
};
|
|
static_assert(sizeof(WaveBufferInternal) == 0x38,
|
|
"VoiceInfo::WaveBufferInternal has the wrong size!");
|
|
|
|
struct BiquadFilterParameter {
|
|
/* 0x00 */ bool enabled;
|
|
/* 0x02 */ std::array<s16, 3> b;
|
|
/* 0x08 */ std::array<s16, 2> a;
|
|
};
|
|
static_assert(sizeof(BiquadFilterParameter) == 0xC,
|
|
"VoiceInfo::BiquadFilterParameter has the wrong size!");
|
|
|
|
struct InParameter {
|
|
/* 0x000 */ u32 id;
|
|
/* 0x004 */ u32 node_id;
|
|
/* 0x008 */ bool is_new;
|
|
/* 0x009 */ bool in_use;
|
|
/* 0x00A */ PlayState play_state;
|
|
/* 0x00B */ SampleFormat sample_format;
|
|
/* 0x00C */ u32 sample_rate;
|
|
/* 0x010 */ s32 priority;
|
|
/* 0x014 */ s32 sort_order;
|
|
/* 0x018 */ u32 channel_count;
|
|
/* 0x01C */ f32 pitch;
|
|
/* 0x020 */ f32 volume;
|
|
/* 0x024 */ std::array<BiquadFilterParameter, MaxBiquadFilters> biquads;
|
|
/* 0x03C */ u32 wave_buffer_count;
|
|
/* 0x040 */ u16 wave_buffer_index;
|
|
/* 0x042 */ char unk042[0x6];
|
|
/* 0x048 */ CpuAddr src_data_address;
|
|
/* 0x050 */ u64 src_data_size;
|
|
/* 0x058 */ u32 mix_id;
|
|
/* 0x05C */ u32 splitter_id;
|
|
/* 0x060 */ std::array<WaveBufferInternal, MaxWaveBuffers> wave_buffer_internal;
|
|
/* 0x140 */ std::array<u32, MaxChannels> channel_resource_ids;
|
|
/* 0x158 */ bool clear_voice_drop;
|
|
/* 0x159 */ u8 flush_buffer_count;
|
|
/* 0x15A */ char unk15A[0x2];
|
|
/* 0x15C */ Flags flags;
|
|
/* 0x15D */ char unk15D[0x1];
|
|
/* 0x15E */ SrcQuality src_quality;
|
|
/* 0x15F */ char unk15F[0x11];
|
|
};
|
|
static_assert(sizeof(InParameter) == 0x170, "VoiceInfo::InParameter has the wrong size!");
|
|
|
|
struct OutStatus {
|
|
/* 0x00 */ u64 played_sample_count;
|
|
/* 0x08 */ u32 wave_buffers_consumed;
|
|
/* 0x0C */ bool voice_dropped;
|
|
};
|
|
static_assert(sizeof(OutStatus) == 0x10, "OutStatus::InParameter has the wrong size!");
|
|
|
|
VoiceInfo();
|
|
|
|
/**
|
|
* Initialize this voice.
|
|
*/
|
|
void Initialize();
|
|
|
|
/**
|
|
* Does this voice need an update?
|
|
*
|
|
* @param params - Input parameters to check matching.
|
|
*
|
|
* @return True if this voice needs an update, otherwise false.
|
|
*/
|
|
bool ShouldUpdateParameters(const InParameter& params) const;
|
|
|
|
/**
|
|
* Update the parameters of this voice.
|
|
*
|
|
* @param error_info - Output error code.
|
|
* @param params - Input parameters to update from.
|
|
* @param pool_mapper - Used to map buffers.
|
|
* @param behavior - behavior to check supported features.
|
|
*/
|
|
void UpdateParameters(BehaviorInfo::ErrorInfo& error_info, const InParameter& params,
|
|
const PoolMapper& pool_mapper, const BehaviorInfo& behavior);
|
|
|
|
/**
|
|
* Update the current play state.
|
|
*
|
|
* @param state - New play state for this voice.
|
|
*/
|
|
void UpdatePlayState(PlayState state);
|
|
|
|
/**
|
|
* Update the current sample rate conversion quality.
|
|
*
|
|
* @param quality - New quality.
|
|
*/
|
|
void UpdateSrcQuality(SrcQuality quality);
|
|
|
|
/**
|
|
* Update all wavebuffers.
|
|
*
|
|
* @param error_infos - Output 2D array of errors, 2 per wavebuffer.
|
|
* @param error_count - Number of errors provided. Unused.
|
|
* @param params - Input parameters to be used for the update.
|
|
* @param voice_states - The voice states for each channel in this voice to be updated.
|
|
* @param pool_mapper - Used to map the wavebuffers.
|
|
* @param behavior - Used to check for supported features.
|
|
*/
|
|
void UpdateWaveBuffers(std::span<std::array<BehaviorInfo::ErrorInfo, 2>> error_infos,
|
|
u32 error_count, const InParameter& params,
|
|
std::span<VoiceState*> voice_states, const PoolMapper& pool_mapper,
|
|
const BehaviorInfo& behavior);
|
|
|
|
/**
|
|
* Update a wavebuffer.
|
|
*
|
|
* @param error_info - Output array of errors.
|
|
* @param wave_buffer - The wavebuffer to be updated.
|
|
* @param wave_buffer_internal - Input parameters to be used for the update.
|
|
* @param sample_format - Sample format of the wavebuffer.
|
|
* @param valid - Is this wavebuffer valid?
|
|
* @param pool_mapper - Used to map the wavebuffers.
|
|
* @param behavior - Used to check for supported features.
|
|
*/
|
|
void UpdateWaveBuffer(std::span<BehaviorInfo::ErrorInfo> error_info, WaveBuffer& wave_buffer,
|
|
const WaveBufferInternal& wave_buffer_internal,
|
|
SampleFormat sample_format, bool valid, const PoolMapper& pool_mapper,
|
|
const BehaviorInfo& behavior);
|
|
|
|
/**
|
|
* Check if the input wavebuffer needs an update.
|
|
*
|
|
* @param wave_buffer_internal - Input wavebuffer parameters to check.
|
|
* @return True if the given wavebuffer needs an update, otherwise false.
|
|
*/
|
|
bool ShouldUpdateWaveBuffer(const WaveBufferInternal& wave_buffer_internal) const;
|
|
|
|
/**
|
|
* Write the number of played samples, number of consumed wavebuffers and if this voice was
|
|
* dropped, to the given out_status.
|
|
*
|
|
* @param out_status - Output status to be written to.
|
|
* @param in_params - Input parameters to check if the wavebuffer is new.
|
|
* @param voice_states - Current host voice states for this voice, source of the output.
|
|
*/
|
|
void WriteOutStatus(OutStatus& out_status, const InParameter& in_params,
|
|
std::span<VoiceState*> voice_states);
|
|
|
|
/**
|
|
* Check if this voice should be skipped for command generation.
|
|
* Checks various things such as usage state, whether data is mapped etc.
|
|
*
|
|
* @return True if this voice should not be generated, otherwise false.
|
|
*/
|
|
bool ShouldSkip() const;
|
|
|
|
/**
|
|
* Check if this voice has any mixing connections.
|
|
*
|
|
* @return True if this voice participates in mixing, otherwise false.
|
|
*/
|
|
bool HasAnyConnection() const;
|
|
|
|
/**
|
|
* Flush flush_count wavebuffers, marking them as consumed.
|
|
*
|
|
* @param flush_count - Number of wavebuffers to flush.
|
|
* @param voice_states - Voice states for these wavebuffers.
|
|
* @param channel_count - Number of active channels.
|
|
*/
|
|
void FlushWaveBuffers(u32 flush_count, std::span<VoiceState*> voice_states, s8 channel_count);
|
|
|
|
/**
|
|
* Update this voice's parameters on command generation,
|
|
* updating voice states and flushing if needed.
|
|
*
|
|
* @param voice_states - Voice states for these wavebuffers.
|
|
* @return True if this voice should be generated, otherwise false.
|
|
*/
|
|
bool UpdateParametersForCommandGeneration(std::span<VoiceState*> voice_states);
|
|
|
|
/**
|
|
* Update this voice on command generation.
|
|
*
|
|
* @param voice_context - Voice context for these wavebuffers.
|
|
*
|
|
* @return True if this voice should be generated, otherwise false.
|
|
*/
|
|
bool UpdateForCommandGeneration(VoiceContext& voice_context);
|
|
|
|
/**
|
|
* Reset the AudioRenderer-side voice states, and the channel resources for this voice.
|
|
*
|
|
* @param voice_context - Context from which to get the resources.
|
|
*/
|
|
void ResetResources(VoiceContext& voice_context) const;
|
|
|
|
/// Is this voice in use?
|
|
bool in_use{};
|
|
/// Is this voice new?
|
|
bool is_new{};
|
|
/// Was this voice last playing? Used for depopping
|
|
bool was_playing{};
|
|
/// Sample format of the wavebuffers in this voice
|
|
SampleFormat sample_format{};
|
|
/// Sample rate of the wavebuffers in this voice
|
|
u32 sample_rate{};
|
|
/// Number of channels in this voice
|
|
s8 channel_count{};
|
|
/// Id of this voice
|
|
u32 id{};
|
|
/// Node id of this voice
|
|
u32 node_id{};
|
|
/// Mix id this voice is mixed to
|
|
u32 mix_id{};
|
|
/// Play state of this voice
|
|
ServerPlayState current_play_state{ServerPlayState::Stopped};
|
|
/// Last play state of this voice
|
|
ServerPlayState last_play_state{ServerPlayState::Started};
|
|
/// Priority of this voice, lower is higher
|
|
s32 priority{};
|
|
/// Sort order of this voice, used when same priority
|
|
s32 sort_order{};
|
|
/// Pitch of this voice (for sample rate conversion)
|
|
f32 pitch{};
|
|
/// Current volume of this voice
|
|
f32 volume{};
|
|
/// Previous volume of this voice
|
|
f32 prev_volume{};
|
|
/// Biquad filters for generating filter commands on this voice
|
|
std::array<BiquadFilterParameter, MaxBiquadFilters> biquads{};
|
|
/// Number of active wavebuffers
|
|
u32 wave_buffer_count{};
|
|
/// Current playing wavebuffer index
|
|
u16 wave_buffer_index{};
|
|
/// Flags controlling decode behavior
|
|
u16 flags{};
|
|
/// Game memory for ADPCM coefficients
|
|
AddressInfo data_address{0, 0};
|
|
/// Wavebuffers
|
|
std::array<WaveBuffer, MaxWaveBuffers> wavebuffers{};
|
|
/// Channel resources for this voice
|
|
std::array<u32, MaxChannels> channel_resource_ids{};
|
|
/// Splitter id this voice is connected with
|
|
s32 splitter_id{UnusedSplitterId};
|
|
/// Sample rate conversion quality
|
|
SrcQuality src_quality{SrcQuality::Medium};
|
|
/// Was this voice dropped due to limited time?
|
|
bool voice_dropped{};
|
|
/// Is this voice's coefficient (data_address) unmapped?
|
|
bool data_unmapped{};
|
|
/// Is this voice's buffers (wavebuffer data and ADPCM context) unmapped?
|
|
bool buffer_unmapped{};
|
|
/// Initialisation state of the biquads
|
|
std::array<bool, MaxBiquadFilters> biquad_initialized{};
|
|
/// Number of wavebuffers to flush
|
|
u8 flush_buffer_count{};
|
|
};
|
|
|
|
} // namespace AudioCore::AudioRenderer
|