diff options
25 files changed, 765 insertions, 403 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 81121167d..827ab0ac7 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -1,9 +1,13 @@ add_library(audio_core STATIC audio_out.cpp audio_out.h + audio_renderer.cpp + audio_renderer.h buffer.h cubeb_sink.cpp cubeb_sink.h + codec.cpp + codec.h null_sink.h stream.cpp stream.h diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 3dfdf61f9..12632a95c 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp @@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) { return {}; } -StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, +StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, Stream::ReleaseCallback&& release_callback) { if (!sink) { const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); sink = sink_details.factory(Settings::values.audio_device_id); } - return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels), - std::move(release_callback), - sink->AcquireSinkStream(sample_rate, num_channels)); + return std::make_shared<Stream>( + sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback), + sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); } std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { @@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) { stream->Stop(); } -bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) { +bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) { return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data))); } diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h index 95e9b53fe..39b7e656b 100644 --- a/src/audio_core/audio_out.h +++ b/src/audio_core/audio_out.h @@ -5,6 +5,7 @@ #pragma once #include <memory> +#include <string> #include <vector> #include "audio_core/buffer.h" @@ -20,7 +21,7 @@ namespace AudioCore { class AudioOut { public: /// Opens a new audio stream - StreamPtr OpenStream(u32 sample_rate, u32 num_channels, + StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, Stream::ReleaseCallback&& release_callback); /// Returns a vector of recently released buffers specified by tag for the specified stream @@ -33,7 +34,7 @@ public: void StopStream(StreamPtr stream); /// Queues a buffer into the specified audio stream, returns true on success - bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data); + bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data); private: SinkPtr sink; diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp new file mode 100644 index 000000000..282f345c5 --- /dev/null +++ b/src/audio_core/audio_renderer.cpp @@ -0,0 +1,234 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "audio_core/audio_renderer.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/memory.h" + +namespace AudioCore { + +constexpr u32 STREAM_SAMPLE_RATE{48000}; +constexpr u32 STREAM_NUM_CHANNELS{2}; + +AudioRenderer::AudioRenderer(AudioRendererParameter params, + Kernel::SharedPtr<Kernel::Event> buffer_event) + : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { + + audio_core = std::make_unique<AudioCore::AudioOut>(); + stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", + [=]() { buffer_event->Signal(); }); + audio_core->StartStream(stream); + + QueueMixedBuffer(0); + QueueMixedBuffer(1); + QueueMixedBuffer(2); +} + +std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { + // Copy UpdateDataHeader struct + UpdateDataHeader config{}; + std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader)); + u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); + + // Copy MemoryPoolInfo structs + std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); + std::memcpy(mem_pool_info.data(), + input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, + memory_pool_count * sizeof(MemoryPoolInfo)); + + // Copy VoiceInfo structs + size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + + config.voice_resource_size}; + for (auto& voice : voices) { + std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); + offset += sizeof(VoiceInfo); + } + + // Update voices + for (auto& voice : voices) { + voice.UpdateState(); + if (!voice.GetInfo().is_in_use) { + continue; + } + if (voice.GetInfo().is_new) { + voice.SetWaveIndex(voice.GetInfo().wave_buffer_head); + } + } + + // Update memory pool state + std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); + for (size_t index = 0; index < memory_pool.size(); ++index) { + if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { + memory_pool[index].state = MemoryPoolStates::Attached; + } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { + memory_pool[index].state = MemoryPoolStates::Detached; + } + } + + // Release previous buffers and queue next ones for playback + ReleaseAndQueueBuffers(); + + // Copy output header + UpdateDataHeader response_data{worker_params}; + std::vector<u8> output_params(response_data.total_size); + std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); + + // Copy output memory pool entries + std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(), + response_data.memory_pools_size); + + // Copy output voice status + size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; + for (const auto& voice : voices) { + std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), + sizeof(VoiceOutStatus)); + voice_out_status_offset += sizeof(VoiceOutStatus); + } + + return output_params; +} + +void AudioRenderer::VoiceState::SetWaveIndex(size_t index) { + wave_index = index & 3; + is_refresh_pending = true; +} + +std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) { + if (!IsPlaying()) { + return {}; + } + + if (is_refresh_pending) { + RefreshBuffer(); + } + + const size_t max_size{samples.size() - offset}; + const size_t dequeue_offset{offset}; + size_t size{sample_count * STREAM_NUM_CHANNELS}; + if (size > max_size) { + size = max_size; + } + + out_status.played_sample_count += size / STREAM_NUM_CHANNELS; + offset += size; + + const auto& wave_buffer{info.wave_buffer[wave_index]}; + if (offset == samples.size()) { + offset = 0; + + if (!wave_buffer.is_looping) { + SetWaveIndex(wave_index + 1); + } + + out_status.wave_buffer_consumed++; + + if (wave_buffer.end_of_stream) { + info.play_state = PlayState::Paused; + } + } + + return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size}; +} + +void AudioRenderer::VoiceState::UpdateState() { + if (is_in_use && !info.is_in_use) { + // No longer in use, reset state + is_refresh_pending = true; + wave_index = 0; + offset = 0; + out_status = {}; + } + is_in_use = info.is_in_use; +} + +void AudioRenderer::VoiceState::RefreshBuffer() { + std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16)); + Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(), + info.wave_buffer[wave_index].buffer_sz); + + switch (static_cast<Codec::PcmFormat>(info.sample_format)) { + case Codec::PcmFormat::Int16: { + // PCM16 is played as-is + break; + } + case Codec::PcmFormat::Adpcm: { + // Decode ADPCM to PCM16 + Codec::ADPCM_Coeff coeffs; + Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); + new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()), + new_samples.size() * sizeof(s16), coeffs, adpcm_state); + break; + } + default: + LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format); + UNREACHABLE(); + break; + } + + switch (info.channel_count) { + case 1: + // 1 channel is upsampled to 2 channel + samples.resize(new_samples.size() * 2); + for (size_t index = 0; index < new_samples.size(); ++index) { + samples[index * 2] = new_samples[index]; + samples[index * 2 + 1] = new_samples[index]; + } + break; + case 2: { + // 2 channel is played as is + samples = std::move(new_samples); + break; + } + default: + LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count); + UNREACHABLE(); + break; + } + + is_refresh_pending = false; +} + +static constexpr s16 ClampToS16(s32 value) { + return static_cast<s16>(std::clamp(value, -32768, 32767)); +} + +void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { + constexpr size_t BUFFER_SIZE{512}; + std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); + + for (auto& voice : voices) { + if (!voice.IsPlaying()) { + continue; + } + + size_t offset{}; + s64 samples_remaining{BUFFER_SIZE}; + while (samples_remaining > 0) { + const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; + + if (samples.empty()) { + break; + } + + samples_remaining -= samples.size(); + + for (const auto& sample : samples) { + const s32 buffer_sample{buffer[offset]}; + buffer[offset++] = + ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume)); + } + } + } + audio_core->QueueBuffer(stream, tag, std::move(buffer)); +} + +void AudioRenderer::ReleaseAndQueueBuffers() { + const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)}; + for (const auto& tag : released_buffers) { + QueueMixedBuffer(tag); + } +} + +} // namespace AudioCore diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h new file mode 100644 index 000000000..6950a4681 --- /dev/null +++ b/src/audio_core/audio_renderer.h @@ -0,0 +1,206 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <vector> + +#include "audio_core/audio_out.h" +#include "audio_core/codec.h" +#include "audio_core/stream.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/kernel/event.h" + +namespace AudioCore { + +enum class PlayState : u8 { + Started = 0, + Stopped = 1, + Paused = 2, +}; + +struct AudioRendererParameter { + u32_le sample_rate; + u32_le sample_count; + u32_le unknown_8; + u32_le unknown_c; + u32_le voice_count; + u32_le sink_count; + u32_le effect_count; + u32_le unknown_1c; + u8 unknown_20; + INSERT_PADDING_BYTES(3); + u32_le splitter_count; + u32_le unknown_2c; + INSERT_PADDING_WORDS(1); + u32_le revision; +}; +static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size"); + +enum class MemoryPoolStates : u32 { // Should be LE + Invalid = 0x0, + Unknown = 0x1, + RequestDetach = 0x2, + Detached = 0x3, + RequestAttach = 0x4, + Attached = 0x5, + Released = 0x6, +}; + +struct MemoryPoolEntry { + MemoryPoolStates state; + u32_le unknown_4; + u32_le unknown_8; + u32_le unknown_c; +}; +static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size"); + +struct MemoryPoolInfo { + u64_le pool_address; + u64_le pool_size; + MemoryPoolStates pool_state; + INSERT_PADDING_WORDS(3); // Unknown +}; +static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size"); +struct BiquadFilter { + u8 enable; + INSERT_PADDING_BYTES(1); + std::array<s16_le, 3> numerator; + std::array<s16_le, 2> denominator; +}; +static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size"); + +struct WaveBuffer { + u64_le buffer_addr; + u64_le buffer_sz; + s32_le start_sample_offset; + s32_le end_sample_offset; + u8 is_looping; + u8 end_of_stream; + u8 sent_to_server; + INSERT_PADDING_BYTES(5); + u64 context_addr; + u64 context_sz; + INSERT_PADDING_BYTES(8); +}; +static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); + +struct VoiceInfo { + u32_le id; + u32_le node_id; + u8 is_new; + u8 is_in_use; + PlayState play_state; + u8 sample_format; + u32_le sample_rate; + u32_le priority; + u32_le sorting_order; + u32_le channel_count; + float_le pitch; + float_le volume; + std::array<BiquadFilter, 2> biquad_filter; + u32_le wave_buffer_count; + u32_le wave_buffer_head; + INSERT_PADDING_WORDS(1); + u64_le additional_params_addr; + u64_le additional_params_sz; + u32_le mix_id; + u32_le splitter_info_id; + std::array<WaveBuffer, 4> wave_buffer; + std::array<u32_le, 6> voice_channel_resource_ids; + INSERT_PADDING_BYTES(24); +}; +static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size"); + +struct VoiceOutStatus { + u64_le played_sample_count; + u32_le wave_buffer_consumed; + u32_le voice_drops_count; +}; +static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); + +struct UpdateDataHeader { + UpdateDataHeader() {} + + explicit UpdateDataHeader(const AudioRendererParameter& config) { + revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision + behavior_size = 0xb0; + memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; + voices_size = config.voice_count * 0x10; + voice_resource_size = 0x0; + effects_size = config.effect_count * 0x10; + mixes_size = 0x0; + sinks_size = config.sink_count * 0x20; + performance_manager_size = 0x10; + total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + + effects_size + sinks_size + performance_manager_size; + } + + u32_le revision; + u32_le behavior_size; + u32_le memory_pools_size; + u32_le voices_size; + u32_le voice_resource_size; + u32_le effects_size; + u32_le mixes_size; + u32_le sinks_size; + u32_le performance_manager_size; + INSERT_PADDING_WORDS(6); + u32_le total_size; +}; +static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); + +class AudioRenderer { +public: + AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); + std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); + void QueueMixedBuffer(Buffer::Tag tag); + void ReleaseAndQueueBuffers(); + +private: + class VoiceState { + public: + bool IsPlaying() const { + return is_in_use && info.play_state == PlayState::Started; + } + + const VoiceOutStatus& GetOutStatus() const { + return out_status; + } + + const VoiceInfo& GetInfo() const { + return info; + } + + VoiceInfo& Info() { + return info; + } + + void SetWaveIndex(size_t index); + std::vector<s16> DequeueSamples(size_t sample_count); + void UpdateState(); + void RefreshBuffer(); + + private: + bool is_in_use{}; + bool is_refresh_pending{}; + size_t wave_index{}; + size_t offset{}; + Codec::ADPCMState adpcm_state{}; + std::vector<s16> samples; + VoiceOutStatus out_status{}; + VoiceInfo info{}; + }; + + AudioRendererParameter worker_params; + Kernel::SharedPtr<Kernel::Event> buffer_event; + std::vector<VoiceState> voices; + std::unique_ptr<AudioCore::AudioOut> audio_core; + AudioCore::StreamPtr stream; +}; + +} // namespace AudioCore diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h index 4bf5fd58a..a323b23ec 100644 --- a/src/audio_core/buffer.h +++ b/src/audio_core/buffer.h @@ -18,11 +18,16 @@ class Buffer { public: using Tag = u64; - Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {} + Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {} /// Returns the raw audio data for the buffer - const std::vector<u8>& GetData() const { - return data; + std::vector<s16>& Samples() { + return samples; + } + + /// Returns the raw audio data for the buffer + const std::vector<s16>& GetSamples() const { + return samples; } /// Returns the buffer tag, this is provided by the game to the audout service @@ -32,7 +37,7 @@ public: private: Tag tag; - std::vector<u8> data; + std::vector<s16> samples; }; using BufferPtr = std::shared_ptr<Buffer>; diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp new file mode 100644 index 000000000..c3021403f --- /dev/null +++ b/src/audio_core/codec.cpp @@ -0,0 +1,77 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "audio_core/codec.h" + +namespace AudioCore::Codec { + +std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, + ADPCMState& state) { + // GC-ADPCM with scale factor and variable coefficients. + // Frames are 8 bytes long containing 14 samples each. + // Samples are 4 bits (one nibble) long. + + constexpr size_t FRAME_LEN = 8; + constexpr size_t SAMPLES_PER_FRAME = 14; + constexpr std::array<int, 16> SIGNED_NIBBLES = { + {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; + + const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; + const size_t ret_size = + sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. + std::vector<s16> ret(ret_size); + + int yn1 = state.yn1, yn2 = state.yn2; + + const size_t NUM_FRAMES = + (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. + for (size_t framei = 0; framei < NUM_FRAMES; framei++) { + const int frame_header = data[framei * FRAME_LEN]; + const int scale = 1 << (frame_header & 0xF); + const int idx = (frame_header >> 4) & 0x7; + + // Coefficients are fixed point with 11 bits fractional part. + const int coef1 = coeff[idx * 2 + 0]; + const int coef2 = coeff[idx * 2 + 1]; + + // Decodes an audio sample. One nibble produces one sample. + const auto decode_sample = [&](const int nibble) -> s16 { + const int xn = nibble * scale; + // We first transform everything into 11 bit fixed point, perform the second order + // digital filter, then transform back. + // 0x400 == 0.5 in 11 bit fixed point. + // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] + int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; + // Clamp to output range. + val = std::clamp<s32>(val, -32768, 32767); + // Advance output feedback. + yn2 = yn1; + yn1 = val; + return static_cast<s16>(val); + }; + + size_t outputi = framei * SAMPLES_PER_FRAME; + size_t datai = framei * FRAME_LEN + 1; + for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { + const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); + ret[outputi] = sample1; + outputi++; + + const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]); + ret[outputi] = sample2; + outputi++; + + datai++; + } + } + + state.yn1 = yn1; + state.yn2 = yn2; + + return ret; +} + +} // namespace AudioCore::Codec diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h new file mode 100644 index 000000000..3f845c42c --- /dev/null +++ b/src/audio_core/codec.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_types.h" + +namespace AudioCore::Codec { + +enum class PcmFormat : u32 { + Invalid = 0, + Int8 = 1, + Int16 = 2, + Int24 = 3, + Int32 = 4, + PcmFloat = 5, + Adpcm = 6, +}; + +/// See: Codec::DecodeADPCM +struct ADPCMState { + // Two historical samples from previous processed buffer, + // required for ADPCM decoding + s16 yn1; ///< y[n-1] + s16 yn2; ///< y[n-2] +}; + +using ADPCM_Coeff = std::array<s16, 16>; + +/** + * @param data Pointer to buffer that contains ADPCM data to decode + * @param size Size of buffer in bytes + * @param coeff ADPCM coefficients + * @param state ADPCM state, this is updated with new state + * @return Decoded stereo signed PCM16 data, sample_count in length + */ +std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, + ADPCMState& state); + +}; // namespace AudioCore::Codec diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 34ae5b062..1501ef1f4 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -13,20 +13,30 @@ namespace AudioCore { class SinkStreamImpl final : public SinkStream { public: - SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} { - cubeb_stream_params params; - params.rate = 48000; - params.channels = GetNumChannels(); + SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, + const std::string& name) + : ctx{ctx}, num_channels{num_channels_} { + + if (num_channels == 6) { + // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2 + // channel for now + is_6_channel = true; + num_channels = 2; + } + + cubeb_stream_params params{}; + params.rate = sample_rate; + params.channels = num_channels; params.format = CUBEB_SAMPLE_S16NE; - params.layout = CUBEB_LAYOUT_STEREO; + params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO; - u32 minimum_latency = 0; + u32 minimum_latency{}; if (cubeb_get_min_latency(ctx, ¶ms, &minimum_latency) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "Error getting minimum latency"); } - if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr, - output_device, ¶ms, std::max(512u, minimum_latency), + if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device, + ¶ms, std::max(512u, minimum_latency), &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback, this) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); @@ -51,33 +61,29 @@ public: cubeb_stream_destroy(stream_backend); } - void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override { + void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override { if (!ctx) { return; } - queue.reserve(queue.size() + sample_count * GetNumChannels()); + queue.reserve(queue.size() + samples.size() * GetNumChannels()); - if (num_channels == 2) { - // Copy as-is - std::copy(samples, samples + sample_count * GetNumChannels(), - std::back_inserter(queue)); - } else if (num_channels == 6) { + if (is_6_channel) { // Downsample 6 channels to 2 - const size_t sample_count_copy_size = sample_count * num_channels * 2; + const size_t sample_count_copy_size = samples.size() * 2; queue.reserve(sample_count_copy_size); - for (size_t i = 0; i < sample_count * num_channels; i += num_channels) { + for (size_t i = 0; i < samples.size(); i += num_channels) { queue.push_back(samples[i]); queue.push_back(samples[i + 1]); } } else { - ASSERT_MSG(false, "Unimplemented"); + // Copy as-is + std::copy(samples.begin(), samples.end(), std::back_inserter(queue)); } } u32 GetNumChannels() const { - // Only support 2-channel stereo output for now - return 2; + return num_channels; } private: @@ -85,6 +91,8 @@ private: cubeb* ctx{}; cubeb_stream* stream_backend{}; + u32 num_channels{}; + bool is_6_channel{}; std::vector<s16> queue; @@ -129,8 +137,10 @@ CubebSink::~CubebSink() { cubeb_destroy(ctx); } -SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) { - sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device)); +SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, + const std::string& name) { + sink_streams.push_back( + std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name)); return *sink_streams.back(); } diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h index d07113f1f..59cbf05e9 100644 --- a/src/audio_core/cubeb_sink.h +++ b/src/audio_core/cubeb_sink.h @@ -18,7 +18,8 @@ public: explicit CubebSink(std::string device_id); ~CubebSink() override; - SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override; + SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, + const std::string& name) override; private: cubeb* ctx{}; diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index 2e04438f7..f235d93e5 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h @@ -13,14 +13,14 @@ public: explicit NullSink(std::string){}; ~NullSink() override = default; - SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override { + SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, + const std::string& /*name*/) override { return null_sink_stream; } private: struct NullSinkStreamImpl final : SinkStream { - void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/, - size_t /*sample_count*/) override {} + void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} } null_sink_stream; }; diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h index d1bb98c3d..95c7b2b6e 100644 --- a/src/audio_core/sink.h +++ b/src/audio_core/sink.h @@ -5,6 +5,7 @@ #pragma once #include <memory> +#include <string> #include "audio_core/sink_stream.h" #include "common/common_types.h" @@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto"; class Sink { public: virtual ~Sink() = default; - virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0; + virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, + const std::string& name) = 0; }; using SinkPtr = std::unique_ptr<Sink>; diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h index e7a3f01b0..41b6736d8 100644 --- a/src/audio_core/sink_stream.h +++ b/src/audio_core/sink_stream.h @@ -5,6 +5,7 @@ #pragma once #include <memory> +#include <vector> #include "common/common_types.h" @@ -22,9 +23,8 @@ public: * Feed stereo samples to sink. * @param num_channels Number of channels used. * @param samples Samples in interleaved stereo PCM16 format. - * @param sample_count Number of samples. */ - virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0; + virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0; }; using SinkStreamPtr = std::unique_ptr<SinkStream>; diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index a0045b7a1..ad9e2915c 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -32,17 +32,13 @@ u32 Stream::GetNumChannels() const { return {}; } -u32 Stream::GetSampleSize() const { - return GetNumChannels() * 2; -} - Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, - SinkStream& sink_stream) + SinkStream& sink_stream, std::string&& name_) : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, - sink_stream{sink_stream} { + sink_stream{sink_stream}, name{std::move(name_)} { release_event = CoreTiming::RegisterEvent( - "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); + name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); } void Stream::Play() { @@ -55,17 +51,15 @@ void Stream::Stop() { } s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { - const size_t num_samples{buffer.GetData().size() / GetSampleSize()}; + const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); } -static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) { - std::vector<s16> samples(data.size() / sizeof(s16)); - std::memcpy(samples.data(), data.data(), data.size()); +static void VolumeAdjustSamples(std::vector<s16>& samples) { const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)}; if (volume == 1.0f) { - return samples; + return; } // Implementation of a volume slider with a dynamic range of 60 dB @@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) { for (auto& sample : samples) { sample = static_cast<s16>(sample * volume_scale_factor); } - - return samples; } void Stream::PlayNextBuffer() { @@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() { active_buffer = queued_buffers.front(); queued_buffers.pop(); - const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()}; - sink_stream.EnqueueSamples( - GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count); + VolumeAdjustSamples(active_buffer->Samples()); + sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); } void Stream::ReleaseActiveBuffer() { + ASSERT(active_buffer); released_buffers.push(std::move(active_buffer)); release_callback(); PlayNextBuffer(); diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 35253920e..049b92ca9 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -6,6 +6,7 @@ #include <functional> #include <memory> +#include <string> #include <vector> #include <queue> @@ -33,7 +34,7 @@ public: using ReleaseCallback = std::function<void()>; Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, - SinkStream& sink_stream); + SinkStream& sink_stream, std::string&& name_); /// Plays the audio stream void Play(); @@ -68,9 +69,6 @@ public: /// Gets the number of channels u32 GetNumChannels() const; - /// Gets the sample size in bytes - u32 GetSampleSize() const; - private: /// Current state of the stream enum class State { @@ -96,6 +94,7 @@ private: std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream SinkStream& sink_stream; ///< Output sink for the stream + std::string name; ///< Name of the stream, must be unique }; using StreamPtr = std::shared_ptr<Stream>; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a1b6f96f1..b2e3a495a 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user ForceExceptionCheck(cycles_into_future); event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { @@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) { // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -190,7 +190,7 @@ void MoveEvents() { for (Event ev; ts_queue.Pop(ev);) { ev.fifo_order = event_fifo_id++; event_queue.emplace_back(std::move(ev)); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -205,7 +205,7 @@ void Advance() { while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 7fe6380ad..5bbde47f4 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -23,6 +23,10 @@ namespace CoreTiming { +struct EventType; + +using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; + /** * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is * required to end slice -1 and start slice 0 before the first cycle of code is executed. @@ -30,8 +34,6 @@ namespace CoreTiming { void Init(); void Shutdown(); -typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; - /** * This should only be called from the emu thread, if you are calling it any other thread, you are * doing something evil @@ -40,8 +42,6 @@ u64 GetTicks(); u64 GetIdleTicks(); void AddTicks(u64 ticks); -struct EventType; - /** * Returns the event_type identifier. if name is not unique, it will assert. */ diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 75f6b8235..884e64e99 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -41,40 +41,42 @@ #include "core/loader/loader.h" #include "core/memory.h" -const int GDB_BUFFER_SIZE = 10000; +namespace GDBStub { +namespace { +constexpr int GDB_BUFFER_SIZE = 10000; -const char GDB_STUB_START = '$'; -const char GDB_STUB_END = '#'; -const char GDB_STUB_ACK = '+'; -const char GDB_STUB_NACK = '-'; +constexpr char GDB_STUB_START = '$'; +constexpr char GDB_STUB_END = '#'; +constexpr char GDB_STUB_ACK = '+'; +constexpr char GDB_STUB_NACK = '-'; #ifndef SIGTRAP -const u32 SIGTRAP = 5; +constexpr u32 SIGTRAP = 5; #endif #ifndef SIGTERM -const u32 SIGTERM = 15; +constexpr u32 SIGTERM = 15; #endif #ifndef MSG_WAITALL -const u32 MSG_WAITALL = 8; +constexpr u32 MSG_WAITALL = 8; #endif -const u32 LR_REGISTER = 30; -const u32 SP_REGISTER = 31; -const u32 PC_REGISTER = 32; -const u32 CPSR_REGISTER = 33; -const u32 UC_ARM64_REG_Q0 = 34; -const u32 FPSCR_REGISTER = 66; +constexpr u32 LR_REGISTER = 30; +constexpr u32 SP_REGISTER = 31; +constexpr u32 PC_REGISTER = 32; +constexpr u32 CPSR_REGISTER = 33; +constexpr u32 UC_ARM64_REG_Q0 = 34; +constexpr u32 FPSCR_REGISTER = 66; // TODO/WiP - Used while working on support for FPU -const u32 TODO_DUMMY_REG_997 = 997; -const u32 TODO_DUMMY_REG_998 = 998; +constexpr u32 TODO_DUMMY_REG_997 = 997; +constexpr u32 TODO_DUMMY_REG_998 = 998; // For sample XML files see the GDB source /gdb/features // GDB also wants the l character at the start // This XML defines what the registers are for this specific ARM device -static const char* target_xml = +constexpr char target_xml[] = R"(l<?xml version="1.0"?> <!DOCTYPE target SYSTEM "gdb-target.dtd"> <target version="1.0"> @@ -140,30 +142,28 @@ static const char* target_xml = </target> )"; -namespace GDBStub { - -static int gdbserver_socket = -1; +int gdbserver_socket = -1; -static u8 command_buffer[GDB_BUFFER_SIZE]; -static u32 command_length; +u8 command_buffer[GDB_BUFFER_SIZE]; +u32 command_length; -static u32 latest_signal = 0; -static bool memory_break = false; +u32 latest_signal = 0; +bool memory_break = false; -static Kernel::Thread* current_thread = nullptr; -static u32 current_core = 0; +Kernel::Thread* current_thread = nullptr; +u32 current_core = 0; // Binding to a port within the reserved ports range (0-1023) requires root permissions, // so default to a port outside of that range. -static u16 gdbstub_port = 24689; +u16 gdbstub_port = 24689; -static bool halt_loop = true; -static bool step_loop = false; -static bool send_trap = false; +bool halt_loop = true; +bool step_loop = false; +bool send_trap = false; // If set to false, the server will never be started and no // gdbstub-related functions will be executed. -static std::atomic<bool> server_enabled(false); +std::atomic<bool> server_enabled(false); #ifdef _WIN32 WSADATA InitData; @@ -171,23 +171,25 @@ WSADATA InitData; struct Breakpoint { bool active; - PAddr addr; + VAddr addr; u64 len; }; -static std::map<u64, Breakpoint> breakpoints_execute; -static std::map<u64, Breakpoint> breakpoints_read; -static std::map<u64, Breakpoint> breakpoints_write; +using BreakpointMap = std::map<VAddr, Breakpoint>; +BreakpointMap breakpoints_execute; +BreakpointMap breakpoints_read; +BreakpointMap breakpoints_write; struct Module { std::string name; - PAddr beg; - PAddr end; + VAddr beg; + VAddr end; }; -static std::vector<Module> modules; +std::vector<Module> modules; +} // Anonymous namespace -void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { +void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) { Module module; if (add_elf_ext) { Common::SplitPath(name, nullptr, &module.name, nullptr); @@ -418,11 +420,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) { } /** - * Get the list of breakpoints for a given breakpoint type. + * Get the map of breakpoints for a given breakpoint type. * - * @param type Type of breakpoint list. + * @param type Type of breakpoint map. */ -static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { +static BreakpointMap& GetBreakpointMap(BreakpointType type) { switch (type) { case BreakpointType::Execute: return breakpoints_execute; @@ -441,20 +443,22 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { * @param type Type of breakpoint. * @param addr Address of breakpoint. */ -static void RemoveBreakpoint(BreakpointType type, PAddr addr) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); +static void RemoveBreakpoint(BreakpointType type, VAddr addr) { + BreakpointMap& p = GetBreakpointMap(type); - auto bp = p.find(static_cast<u64>(addr)); - if (bp != p.end()) { - LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", - bp->second.len, bp->second.addr, static_cast<int>(type)); - p.erase(static_cast<u64>(addr)); + const auto bp = p.find(addr); + if (bp == p.end()) { + return; } + + LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", + bp->second.len, bp->second.addr, static_cast<int>(type)); + p.erase(addr); } -BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); - auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); +BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) { + const BreakpointMap& p = GetBreakpointMap(type); + const auto next_breakpoint = p.lower_bound(addr); BreakpointAddress breakpoint; if (next_breakpoint != p.end()) { @@ -468,36 +472,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) return breakpoint; } -bool CheckBreakpoint(PAddr addr, BreakpointType type) { +bool CheckBreakpoint(VAddr addr, BreakpointType type) { if (!IsConnected()) { return false; } - std::map<u64, Breakpoint>& p = GetBreakpointList(type); + const BreakpointMap& p = GetBreakpointMap(type); + const auto bp = p.find(addr); - auto bp = p.find(static_cast<u64>(addr)); - if (bp != p.end()) { - u64 len = bp->second.len; + if (bp == p.end()) { + return false; + } - // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints - // no matter if it's a 4-byte or 2-byte instruction. When you execute a - // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on - // two instructions instead of the single instruction you placed the breakpoint - // on. So, as a way to make sure that execution breakpoints are only breaking - // on the instruction that was specified, set the length of an execution - // breakpoint to 1. This should be fine since the CPU should never begin executing - // an instruction anywhere except the beginning of the instruction. - if (type == BreakpointType::Execute) { - len = 1; - } + u64 len = bp->second.len; - if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { - LOG_DEBUG(Debug_GDBStub, - "Found breakpoint type {} @ {:016X}, range: {:016X}" - " - {:016X} ({:X} bytes)", - static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); - return true; - } + // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints + // no matter if it's a 4-byte or 2-byte instruction. When you execute a + // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on + // two instructions instead of the single instruction you placed the breakpoint + // on. So, as a way to make sure that execution breakpoints are only breaking + // on the instruction that was specified, set the length of an execution + // breakpoint to 1. This should be fine since the CPU should never begin executing + // an instruction anywhere except the beginning of the instruction. + if (type == BreakpointType::Execute) { + len = 1; + } + + if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { + LOG_DEBUG(Debug_GDBStub, + "Found breakpoint type {} @ {:016X}, range: {:016X}" + " - {:016X} ({:X} bytes)", + static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); + return true; } return false; @@ -975,8 +981,8 @@ static void Continue() { * @param addr Address of breakpoint. * @param len Length of breakpoint. */ -static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); +static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { + BreakpointMap& p = GetBreakpointMap(type); Breakpoint breakpoint; breakpoint.active = true; @@ -1015,7 +1021,7 @@ static void AddBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; u64 len = @@ -1064,7 +1070,7 @@ static void RemoveBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index a6b50c26c..5a36524b2 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -22,7 +22,7 @@ enum class BreakpointType { }; struct BreakpointAddress { - PAddr address; + VAddr address; BreakpointType type; }; @@ -53,7 +53,7 @@ bool IsServerEnabled(); bool IsConnected(); /// Register module. -void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); +void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true); /** * Signal to the gdbstub server that it should halt CPU execution. @@ -74,7 +74,7 @@ void HandlePacket(); * @param addr Address to search from. * @param type Type of breakpoint. */ -BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); +BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type); /** * Check if a breakpoint of the specified type exists at the given address. @@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy * @param addr Address of breakpoint. * @param type Type of breakpoint. */ -bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); +bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type); /// If set to true, the CPU will halt at the beginning of the next CPU loop. bool GetCpuHaltFlag(); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 7a17ed162..03a954a9f 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -32,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) { } // Gets the threads waiting on an address. -static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, - VAddr address) { - auto RetrieveWaitingThreads = +static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { + const auto RetrieveWaitingThreads = [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); auto& thread_list = scheduler->GetThreadList(); @@ -45,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t } }; - // Retrieve a list of all threads that are waiting for this address. - RetrieveWaitingThreads(0, waiting_threads, address); - RetrieveWaitingThreads(1, waiting_threads, address); - RetrieveWaitingThreads(2, waiting_threads, address); - RetrieveWaitingThreads(3, waiting_threads, address); + // Retrieve all threads that are waiting for this address. + std::vector<SharedPtr<Thread>> threads; + RetrieveWaitingThreads(0, threads, address); + RetrieveWaitingThreads(1, threads, address); + RetrieveWaitingThreads(2, threads, address); + RetrieveWaitingThreads(3, threads, address); + // Sort them by priority, such that the highest priority ones come first. - std::sort(waiting_threads.begin(), waiting_threads.end(), + std::sort(threads.begin(), threads.end(), [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { return lhs->current_priority < rhs->current_priority; }); + + return threads; } // Wake up num_to_wake (or all) threads in a vector. @@ -76,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num // Signals an address being waited on. ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { - // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); + std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; @@ -110,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu } // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); + std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); // Determine the modified value depending on the waiting count. s32 updated_value; - if (waiting_threads.size() == 0) { + if (waiting_threads.empty()) { updated_value = value - 1; } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { updated_value = value + 1; diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp index 57b934dd6..bf8d40157 100644 --- a/src/core/hle/service/audio/audout_a.cpp +++ b/src/core/hle/service/audio/audout_a.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#pragma once - #include "core/hle/service/audio/audout_a.h" namespace Service::Audio { diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index b317027b6..108a7c6eb 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -4,6 +4,8 @@ #include <array> #include <vector> + +#include "audio_core/codec.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -48,7 +50,7 @@ public: buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, - [=]() { buffer_event->Signal(); }); + "IAudioOut", [=]() { buffer_event->Signal(); }); } private: @@ -111,10 +113,10 @@ private: std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); const u64 tag{rp.Pop<u64>()}; - std::vector<u8> data(audio_buffer.buffer_size); - Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size()); + std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); + Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); - if (!audio_core.QueueBuffer(stream, tag, std::move(data))) { + if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); } @@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u32>(DefaultSampleRate); rb.Push<u32>(params.channel_count); - rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); + rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); } diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index e5c2184d5..fd491f65d 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -38,16 +38,6 @@ private: void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); - - enum class PcmFormat : u32 { - Invalid = 0, - Int8 = 1, - Int16 = 2, - Int24 = 3, - Int32 = 4, - PcmFloat = 5, - Adpcm = 6, - }; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6aed9e2fa..f99304de5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -15,13 +15,10 @@ namespace Service::Audio { -/// TODO(bunnei): Find a proper value for the audio_ticks -constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)}; - class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(AudioRendererParameter audren_params) - : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { + explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) + : ServiceFramework("IAudioRenderer") { static const FunctionInfo functions[] = { {0, nullptr, "GetAudioRendererSampleRate"}, {1, nullptr, "GetAudioRendererSampleCount"}, @@ -39,21 +36,8 @@ public: RegisterHandlers(functions); system_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); - - // Register event callback to update the Audio Buffer - audio_event = CoreTiming::RegisterEvent( - "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) { - UpdateAudioCallback(); - CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); - }); - - // Start the audio event - CoreTiming::ScheduleEvent(audio_ticks, audio_event); - voice_status_list.resize(worker_params.voice_count); - } - ~IAudioRenderer() { - CoreTiming::UnscheduleEvent(audio_event, 0); + Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); + renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event); } private: @@ -62,60 +46,9 @@ private: } void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { - UpdateDataHeader config{}; - auto buf = ctx.ReadBuffer(); - std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader)); - u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); - - std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); - std::memcpy(mem_pool_info.data(), - buf.data() + sizeof(UpdateDataHeader) + config.behavior_size, - memory_pool_count * sizeof(MemoryPoolInfo)); - - std::vector<VoiceInfo> voice_info(worker_params.voice_count); - std::memcpy(voice_info.data(), - buf.data() + sizeof(UpdateDataHeader) + config.behavior_size + - config.memory_pools_size + config.voice_resource_size, - worker_params.voice_count * sizeof(VoiceInfo)); - - UpdateDataHeader response_data{worker_params}; - - ASSERT(ctx.GetWriteBufferSize() == response_data.total_size); - - std::vector<u8> output(response_data.total_size); - std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader)); - std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); - for (unsigned i = 0; i < memory_pool.size(); i++) { - if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach) - memory_pool[i].state = MemoryPoolStates::Attached; - else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach) - memory_pool[i].state = MemoryPoolStates::Detached; - } - std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(), - response_data.memory_pools_size); - - for (unsigned i = 0; i < voice_info.size(); i++) { - if (voice_info[i].is_new) { - voice_status_list[i].played_sample_count = 0; - voice_status_list[i].wave_buffer_consumed = 0; - } else if (voice_info[i].play_state == (u8)PlayStates::Started) { - for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) { - voice_status_list[i].played_sample_count += - (voice_info[i].wave_buffer[buff_idx].end_sample_offset - - voice_info[i].wave_buffer[buff_idx].start_sample_offset) / - 2; - voice_status_list[i].wave_buffer_consumed++; - } - } - } - std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size, - voice_status_list.data(), response_data.voices_size); - - ctx.WriteBuffer(output); - + ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_Audio, "(STUBBED) called"); } @@ -136,8 +69,6 @@ private: } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { - // system_event->Signal(); - IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(system_event); @@ -145,131 +76,8 @@ private: LOG_WARNING(Service_Audio, "(STUBBED) called"); } - enum class MemoryPoolStates : u32 { // Should be LE - Invalid = 0x0, - Unknown = 0x1, - RequestDetach = 0x2, - Detached = 0x3, - RequestAttach = 0x4, - Attached = 0x5, - Released = 0x6, - }; - - enum class PlayStates : u8 { - Started = 0, - Stopped = 1, - }; - - struct MemoryPoolEntry { - MemoryPoolStates state; - u32_le unknown_4; - u32_le unknown_8; - u32_le unknown_c; - }; - static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size"); - - struct MemoryPoolInfo { - u64_le pool_address; - u64_le pool_size; - MemoryPoolStates pool_state; - INSERT_PADDING_WORDS(3); // Unknown - }; - static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size"); - - struct UpdateDataHeader { - UpdateDataHeader() {} - - explicit UpdateDataHeader(const AudioRendererParameter& config) { - revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision - behavior_size = 0xb0; - memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; - voices_size = config.voice_count * 0x10; - voice_resource_size = 0x0; - effects_size = config.effect_count * 0x10; - mixes_size = 0x0; - sinks_size = config.sink_count * 0x20; - performance_manager_size = 0x10; - total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + - voices_size + effects_size + sinks_size + performance_manager_size; - } - - u32_le revision; - u32_le behavior_size; - u32_le memory_pools_size; - u32_le voices_size; - u32_le voice_resource_size; - u32_le effects_size; - u32_le mixes_size; - u32_le sinks_size; - u32_le performance_manager_size; - INSERT_PADDING_WORDS(6); - u32_le total_size; - }; - static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); - - struct BiquadFilter { - u8 enable; - INSERT_PADDING_BYTES(1); - s16_le numerator[3]; - s16_le denominator[2]; - }; - static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size"); - - struct WaveBuffer { - u64_le buffer_addr; - u64_le buffer_sz; - s32_le start_sample_offset; - s32_le end_sample_offset; - u8 loop; - u8 end_of_stream; - u8 sent_to_server; - INSERT_PADDING_BYTES(5); - u64 context_addr; - u64 context_sz; - INSERT_PADDING_BYTES(8); - }; - static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); - - struct VoiceInfo { - u32_le id; - u32_le node_id; - u8 is_new; - u8 is_in_use; - u8 play_state; - u8 sample_format; - u32_le sample_rate; - u32_le priority; - u32_le sorting_order; - u32_le channel_count; - float_le pitch; - float_le volume; - BiquadFilter biquad_filter[2]; - u32_le wave_buffer_count; - u16_le wave_buffer_head; - INSERT_PADDING_BYTES(6); - u64_le additional_params_addr; - u64_le additional_params_sz; - u32_le mix_id; - u32_le splitter_info_id; - WaveBuffer wave_buffer[4]; - u32_le voice_channel_resource_ids[6]; - INSERT_PADDING_BYTES(24); - }; - static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size"); - - struct VoiceOutStatus { - u64_le played_sample_count; - u32_le wave_buffer_consumed; - INSERT_PADDING_WORDS(1); - }; - static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); - - /// This is used to trigger the audio event callback. - CoreTiming::EventType* audio_event; - Kernel::SharedPtr<Kernel::Event> system_event; - AudioRendererParameter worker_params; - std::vector<VoiceOutStatus> voice_status_list; + std::unique_ptr<AudioCore::AudioRenderer> renderer; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { @@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioRendererParameter>(); + auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioRendererParameter>(); + auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); buffer_sz += params.unknown_c * 1024; diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index b9b81db4f..14907f8ae 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -4,6 +4,7 @@ #pragma once +#include "audio_core/audio_renderer.h" #include "core/hle/service/service.h" namespace Kernel { @@ -12,24 +13,6 @@ class HLERequestContext; namespace Service::Audio { -struct AudioRendererParameter { - u32_le sample_rate; - u32_le sample_count; - u32_le unknown_8; - u32_le unknown_c; - u32_le voice_count; - u32_le sink_count; - u32_le effect_count; - u32_le unknown_1c; - u8 unknown_20; - INSERT_PADDING_BYTES(3); - u32_le splitter_count; - u32_le unknown_2c; - INSERT_PADDING_WORDS(1); - u32_le revision; -}; -static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size"); - class AudRenU final : public ServiceFramework<AudRenU> { public: explicit AudRenU(); |