diff options
| author | 2022-08-01 02:58:13 +0100 | |
|---|---|---|
| committer | 2022-09-02 04:43:04 +0100 | |
| commit | ea9ff71725113b8dbb159917c57aa536bba0cb53 (patch) | |
| tree | 512cce0fea5eb511aa7803bc67f741815885bfcb /src/audio_core/device | |
| parent | Merge pull request #8752 from vonchenplus/rectangle_texture (diff) | |
| download | yuzu-ea9ff71725113b8dbb159917c57aa536bba0cb53.tar.gz yuzu-ea9ff71725113b8dbb159917c57aa536bba0cb53.tar.xz yuzu-ea9ff71725113b8dbb159917c57aa536bba0cb53.zip | |
Rework audio output, connecting AudioOut into coretiming to fix desync during heavy loads.
Diffstat (limited to 'src/audio_core/device')
| -rw-r--r-- | src/audio_core/device/audio_buffer.h | 4 | ||||
| -rw-r--r-- | src/audio_core/device/audio_buffers.h | 13 | ||||
| -rw-r--r-- | src/audio_core/device/device_session.cpp | 52 | ||||
| -rw-r--r-- | src/audio_core/device/device_session.h | 27 |
4 files changed, 75 insertions, 21 deletions
diff --git a/src/audio_core/device/audio_buffer.h b/src/audio_core/device/audio_buffer.h index cae7fa970..7128ef72a 100644 --- a/src/audio_core/device/audio_buffer.h +++ b/src/audio_core/device/audio_buffer.h | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | namespace AudioCore { | 8 | namespace AudioCore { |
| 9 | 9 | ||
| 10 | struct AudioBuffer { | 10 | struct AudioBuffer { |
| 11 | /// Timestamp this buffer started playing. | ||
| 12 | u64 start_timestamp; | ||
| 13 | /// Timestamp this buffer should finish playing. | ||
| 14 | u64 end_timestamp; | ||
| 11 | /// Timestamp this buffer completed playing. | 15 | /// Timestamp this buffer completed playing. |
| 12 | s64 played_timestamp; | 16 | s64 played_timestamp; |
| 13 | /// Game memory address for these samples. | 17 | /// Game memory address for these samples. |
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 5d1979ea0..57c78d439 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h | |||
| @@ -58,6 +58,7 @@ public: | |||
| 58 | if (index < 0) { | 58 | if (index < 0) { |
| 59 | index += N; | 59 | index += N; |
| 60 | } | 60 | } |
| 61 | |||
| 61 | out_buffers.push_back(buffers[index]); | 62 | out_buffers.push_back(buffers[index]); |
| 62 | registered_count++; | 63 | registered_count++; |
| 63 | registered_index = (registered_index + 1) % append_limit; | 64 | registered_index = (registered_index + 1) % append_limit; |
| @@ -100,7 +101,7 @@ public: | |||
| 100 | } | 101 | } |
| 101 | 102 | ||
| 102 | // Check with the backend if this buffer can be released yet. | 103 | // Check with the backend if this buffer can be released yet. |
| 103 | if (!session.IsBufferConsumed(buffers[index].tag)) { | 104 | if (!session.IsBufferConsumed(buffers[index])) { |
| 104 | break; | 105 | break; |
| 105 | } | 106 | } |
| 106 | 107 | ||
| @@ -280,6 +281,16 @@ public: | |||
| 280 | return true; | 281 | return true; |
| 281 | } | 282 | } |
| 282 | 283 | ||
| 284 | u64 GetNextTimestamp() const { | ||
| 285 | // Iterate backwards through the buffer queue, and take the most recent buffer's end | ||
| 286 | std::scoped_lock l{lock}; | ||
| 287 | auto index{appended_index - 1}; | ||
| 288 | if (index < 0) { | ||
| 289 | index += append_limit; | ||
| 290 | } | ||
| 291 | return buffers[index].end_timestamp; | ||
| 292 | } | ||
| 293 | |||
| 283 | private: | 294 | private: |
| 284 | /// Buffer lock | 295 | /// Buffer lock |
| 285 | mutable std::recursive_mutex lock{}; | 296 | mutable std::recursive_mutex lock{}; |
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 095fc96ce..c71c3a376 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp | |||
| @@ -7,11 +7,20 @@ | |||
| 7 | #include "audio_core/device/device_session.h" | 7 | #include "audio_core/device/device_session.h" |
| 8 | #include "audio_core/sink/sink_stream.h" | 8 | #include "audio_core/sink/sink_stream.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/core_timing.h" | ||
| 10 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 11 | 12 | ||
| 12 | namespace AudioCore { | 13 | namespace AudioCore { |
| 13 | 14 | ||
| 14 | DeviceSession::DeviceSession(Core::System& system_) : system{system_} {} | 15 | using namespace std::literals; |
| 16 | constexpr auto INCREMENT_TIME{5ms}; | ||
| 17 | |||
| 18 | DeviceSession::DeviceSession(Core::System& system_) | ||
| 19 | : system{system_}, thread_event{Core::Timing::CreateEvent( | ||
| 20 | "AudioOutSampleTick", | ||
| 21 | [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { | ||
| 22 | return ThreadFunc(); | ||
| 23 | })} {} | ||
| 15 | 24 | ||
| 16 | DeviceSession::~DeviceSession() { | 25 | DeviceSession::~DeviceSession() { |
| 17 | Finalize(); | 26 | Finalize(); |
| @@ -50,20 +59,21 @@ void DeviceSession::Finalize() { | |||
| 50 | } | 59 | } |
| 51 | 60 | ||
| 52 | void DeviceSession::Start() { | 61 | void DeviceSession::Start() { |
| 53 | stream->SetPlayedSampleCount(played_sample_count); | 62 | if (stream) { |
| 54 | stream->Start(); | 63 | stream->Start(); |
| 64 | system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds::zero(), INCREMENT_TIME, | ||
| 65 | thread_event); | ||
| 66 | } | ||
| 55 | } | 67 | } |
| 56 | 68 | ||
| 57 | void DeviceSession::Stop() { | 69 | void DeviceSession::Stop() { |
| 58 | if (stream) { | 70 | if (stream) { |
| 59 | played_sample_count = stream->GetPlayedSampleCount(); | ||
| 60 | stream->Stop(); | 71 | stream->Stop(); |
| 72 | system.CoreTiming().UnscheduleEvent(thread_event, {}); | ||
| 61 | } | 73 | } |
| 62 | } | 74 | } |
| 63 | 75 | ||
| 64 | void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | 76 | void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { |
| 65 | auto& memory{system.Memory()}; | ||
| 66 | |||
| 67 | for (size_t i = 0; i < buffers.size(); i++) { | 77 | for (size_t i = 0; i < buffers.size(); i++) { |
| 68 | Sink::SinkBuffer new_buffer{ | 78 | Sink::SinkBuffer new_buffer{ |
| 69 | .frames = buffers[i].size / (channel_count * sizeof(s16)), | 79 | .frames = buffers[i].size / (channel_count * sizeof(s16)), |
| @@ -77,7 +87,7 @@ void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | |||
| 77 | stream->AppendBuffer(new_buffer, samples); | 87 | stream->AppendBuffer(new_buffer, samples); |
| 78 | } else { | 88 | } else { |
| 79 | std::vector<s16> samples(buffers[i].size / sizeof(s16)); | 89 | std::vector<s16> samples(buffers[i].size / sizeof(s16)); |
| 80 | memory.ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size); | 90 | system.Memory().ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size); |
| 81 | stream->AppendBuffer(new_buffer, samples); | 91 | stream->AppendBuffer(new_buffer, samples); |
| 82 | } | 92 | } |
| 83 | } | 93 | } |
| @@ -85,17 +95,13 @@ void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | |||
| 85 | 95 | ||
| 86 | void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const { | 96 | void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const { |
| 87 | if (type == Sink::StreamType::In) { | 97 | if (type == Sink::StreamType::In) { |
| 88 | auto& memory{system.Memory()}; | ||
| 89 | auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; | 98 | auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; |
| 90 | memory.WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); | 99 | system.Memory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); |
| 91 | } | 100 | } |
| 92 | } | 101 | } |
| 93 | 102 | ||
| 94 | bool DeviceSession::IsBufferConsumed(u64 tag) const { | 103 | bool DeviceSession::IsBufferConsumed(AudioBuffer& buffer) const { |
| 95 | if (stream) { | 104 | return played_sample_count >= buffer.end_timestamp; |
| 96 | return stream->IsBufferConsumed(tag); | ||
| 97 | } | ||
| 98 | return true; | ||
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | void DeviceSession::SetVolume(f32 volume) const { | 107 | void DeviceSession::SetVolume(f32 volume) const { |
| @@ -105,10 +111,22 @@ void DeviceSession::SetVolume(f32 volume) const { | |||
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | u64 DeviceSession::GetPlayedSampleCount() const { | 113 | u64 DeviceSession::GetPlayedSampleCount() const { |
| 108 | if (stream) { | 114 | return played_sample_count; |
| 109 | return stream->GetPlayedSampleCount(); | 115 | } |
| 116 | |||
| 117 | std::optional<std::chrono::nanoseconds> DeviceSession::ThreadFunc() { | ||
| 118 | // Add 5ms of samples at a 48K sample rate. | ||
| 119 | played_sample_count += 48'000 * INCREMENT_TIME / 1s; | ||
| 120 | if (type == Sink::StreamType::Out) { | ||
| 121 | system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioOutManager, true); | ||
| 122 | } else { | ||
| 123 | system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioInManager, true); | ||
| 110 | } | 124 | } |
| 111 | return 0; | 125 | return std::nullopt; |
| 126 | } | ||
| 127 | |||
| 128 | void DeviceSession::SetRingSize(u32 ring_size) { | ||
| 129 | stream->SetRingSize(ring_size); | ||
| 112 | } | 130 | } |
| 113 | 131 | ||
| 114 | } // namespace AudioCore | 132 | } // namespace AudioCore |
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 4a031b765..3414e2c06 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <chrono> | ||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 6 | #include <span> | 9 | #include <span> |
| 7 | 10 | ||
| 8 | #include "audio_core/common/common.h" | 11 | #include "audio_core/common/common.h" |
| @@ -11,9 +14,13 @@ | |||
| 11 | 14 | ||
| 12 | namespace Core { | 15 | namespace Core { |
| 13 | class System; | 16 | class System; |
| 14 | } | 17 | namespace Timing { |
| 18 | struct EventType; | ||
| 19 | } // namespace Timing | ||
| 20 | } // namespace Core | ||
| 15 | 21 | ||
| 16 | namespace AudioCore { | 22 | namespace AudioCore { |
| 23 | |||
| 17 | namespace Sink { | 24 | namespace Sink { |
| 18 | class SinkStream; | 25 | class SinkStream; |
| 19 | struct SinkBuffer; | 26 | struct SinkBuffer; |
| @@ -70,7 +77,7 @@ public: | |||
| 70 | * @param tag - Unqiue tag of the buffer to check. | 77 | * @param tag - Unqiue tag of the buffer to check. |
| 71 | * @return true if the buffer has been consumed, otherwise false. | 78 | * @return true if the buffer has been consumed, otherwise false. |
| 72 | */ | 79 | */ |
| 73 | bool IsBufferConsumed(u64 tag) const; | 80 | bool IsBufferConsumed(AudioBuffer& buffer) const; |
| 74 | 81 | ||
| 75 | /** | 82 | /** |
| 76 | * Start this device session, starting the backend stream. | 83 | * Start this device session, starting the backend stream. |
| @@ -96,6 +103,16 @@ public: | |||
| 96 | */ | 103 | */ |
| 97 | u64 GetPlayedSampleCount() const; | 104 | u64 GetPlayedSampleCount() const; |
| 98 | 105 | ||
| 106 | /* | ||
| 107 | * CoreTiming callback to increment played_sample_count over time. | ||
| 108 | */ | ||
| 109 | std::optional<std::chrono::nanoseconds> ThreadFunc(); | ||
| 110 | |||
| 111 | /* | ||
| 112 | * Set the size of the ring buffer. | ||
| 113 | */ | ||
| 114 | void SetRingSize(u32 ring_size); | ||
| 115 | |||
| 99 | private: | 116 | private: |
| 100 | /// System | 117 | /// System |
| 101 | Core::System& system; | 118 | Core::System& system; |
| @@ -118,9 +135,13 @@ private: | |||
| 118 | /// Applet resource user id of this device session | 135 | /// Applet resource user id of this device session |
| 119 | u64 applet_resource_user_id{}; | 136 | u64 applet_resource_user_id{}; |
| 120 | /// Total number of samples played by this device session | 137 | /// Total number of samples played by this device session |
| 121 | u64 played_sample_count{}; | 138 | std::atomic<u64> played_sample_count{}; |
| 139 | /// Event increasing the played sample count every 5ms | ||
| 140 | std::shared_ptr<Core::Timing::EventType> thread_event; | ||
| 122 | /// Is this session initialised? | 141 | /// Is this session initialised? |
| 123 | bool initialized{}; | 142 | bool initialized{}; |
| 143 | /// Buffer queue | ||
| 144 | std::vector<AudioBuffer> buffer_queue{}; | ||
| 124 | }; | 145 | }; |
| 125 | 146 | ||
| 126 | } // namespace AudioCore | 147 | } // namespace AudioCore |