diff options
| -rw-r--r-- | src/audio_core/device/device_session.cpp | 3 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/audio_renderer.cpp | 2 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/audio_renderer.h | 1 | ||||
| -rw-r--r-- | src/audio_core/renderer/system_manager.cpp | 19 | ||||
| -rw-r--r-- | src/audio_core/renderer/system_manager.h | 7 | ||||
| -rw-r--r-- | src/audio_core/sink/cubeb_sink.cpp | 4 | ||||
| -rw-r--r-- | src/audio_core/sink/sdl2_sink.cpp | 2 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_stream.cpp | 67 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_stream.h | 29 | 
9 files changed, 55 insertions, 79 deletions
| diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 043ce8875..b5c0ef0e6 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -121,8 +121,7 @@ u64 DeviceSession::GetPlayedSampleCount() const {  }  std::optional<std::chrono::nanoseconds> DeviceSession::ThreadFunc() { -    // Add 5ms of samples at a 48K sample rate. -    played_sample_count += 48'000 * INCREMENT_TIME / 1s; +    played_sample_count = stream->GetExpectedPlayedSampleCount();      if (type == Sink::StreamType::Out) {          system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioOutManager, true);      } else { diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp index 42b4b167a..503f40349 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp @@ -189,6 +189,8 @@ void AudioRenderer::ThreadFunc() {                      max_time = std::min(command_buffer.time_limit, max_time);                      command_list_processor.SetProcessTimeMax(max_time); +                    streams[index]->WaitFreeSpace(); +                      // Process the command list                      {                          MICROPROFILE_SCOPE(Audio_Renderer); diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h index 151f38c1b..85ce6a269 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.h +++ b/src/audio_core/renderer/adsp/audio_renderer.h @@ -10,6 +10,7 @@  #include "audio_core/renderer/adsp/command_buffer.h"  #include "audio_core/renderer/adsp/command_list_processor.h"  #include "common/common_types.h" +#include "common/polyfill_thread.h"  #include "common/reader_writer_queue.h"  #include "common/thread.h" diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index ce631f810..07d8ed093 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp @@ -15,14 +15,9 @@ MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager",                      MP_RGB(60, 19, 97));  namespace AudioCore::AudioRenderer { -constexpr std::chrono::nanoseconds RENDER_TIME{5'000'000UL};  SystemManager::SystemManager(Core::System& core_) -    : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()}, -      thread_event{Core::Timing::CreateEvent( -          "AudioRendererSystemManager", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { -              return ThreadFunc2(time); -          })} {} +    : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()} {}  SystemManager::~SystemManager() {      Stop(); @@ -33,8 +28,6 @@ bool SystemManager::InitializeUnsafe() {          if (adsp.Start()) {              active = true;              thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); }); -            core.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), RENDER_TIME, -                                                   thread_event);          }      } @@ -45,7 +38,6 @@ void SystemManager::Stop() {      if (!active) {          return;      } -    core.CoreTiming().UnscheduleEvent(thread_event, {});      active = false;      update.store(true);      update.notify_all(); @@ -111,16 +103,7 @@ void SystemManager::ThreadFunc() {          adsp.Signal();          adsp.Wait(); - -        update.wait(false); -        update.store(false);      }  } -std::optional<std::chrono::nanoseconds> SystemManager::ThreadFunc2(s64 time) { -    update.store(true); -    update.notify_all(); -    return std::nullopt; -} -  } // namespace AudioCore::AudioRenderer diff --git a/src/audio_core/renderer/system_manager.h b/src/audio_core/renderer/system_manager.h index 415ddb74f..1f0bbd8b4 100644 --- a/src/audio_core/renderer/system_manager.h +++ b/src/audio_core/renderer/system_manager.h @@ -68,11 +68,6 @@ private:       */      void ThreadFunc(); -    /** -     * Signalling core timing thread to run ThreadFunc. -     */ -    std::optional<std::chrono::nanoseconds> ThreadFunc2(s64 time); -      enum class StreamState {          Filling,          Steady, @@ -95,8 +90,6 @@ private:      ADSP::ADSP& adsp;      /// AudioRenderer mailbox for communication      ADSP::AudioRenderer_Mailbox* mailbox{}; -    /// Core timing event to signal main thread -    std::shared_ptr<Core::Timing::EventType> thread_event;      /// Atomic for main thread to wait on      std::atomic<bool> update{};  }; diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 9133f5388..9a0801888 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -101,8 +101,6 @@ public:      ~CubebSinkStream() override {          LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name); -        Unstall(); -          if (!ctx) {              return;          } @@ -143,8 +141,6 @@ public:       * Stop the sink stream.       */      void Stop() override { -        Unstall(); -          if (!ctx || paused) {              return;          } diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index c138dc628..ee1a0652f 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp @@ -88,7 +88,6 @@ public:       * Finalize the sink stream.       */      void Finalize() override { -        Unstall();          if (device == 0) {              return;          } @@ -116,7 +115,6 @@ public:       * Stop the sink stream.       */      void Stop() override { -        Unstall();          if (device == 0 || paused) {              return;          } diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 39a21b0f0..f99dbd8ec 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -14,6 +14,8 @@  #include "common/fixed_point.h"  #include "common/settings.h"  #include "core/core.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h"  namespace AudioCore::Sink { @@ -149,10 +151,6 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n          return;      } -    if (queued_buffers > max_queue_size) { -        Stall(); -    } -      while (frames_written < num_frames) {          // If the playing buffer has been consumed or has no frames, we need a new one          if (playing_buffer.consumed || playing_buffer.frames == 0) { @@ -187,10 +185,6 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n      }      std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes); - -    if (queued_buffers <= max_queue_size) { -        Unstall(); -    }  }  void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames) { @@ -198,10 +192,15 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz      const std::size_t frame_size = num_channels;      const std::size_t frame_size_bytes = frame_size * sizeof(s16);      size_t frames_written{0}; +    size_t actual_frames_written{0};      // If we're paused or going to shut down, we don't want to consume buffers as coretiming is      // paused and we'll desync, so just play silence.      if (system.IsPaused() || system.IsShuttingDown()) { +        if (system.IsShuttingDown()) { +            release_cv.notify_one(); +        } +          static constexpr std::array<s16, 6> silence{};          for (size_t i = frames_written; i < num_frames; i++) {              std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes); @@ -209,20 +208,6 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz          return;      } -    // Due to many frames being queued up with nvdec (5 frames or so?), a lot of buffers also get -    // queued up (30+) but not all at once, which causes constant stalling here, so just let the -    // video play out without attempting to stall. -    // Can hopefully remove this later with a more complete NVDEC implementation. -    const auto nvdec_active{system.AudioCore().IsNVDECActive()}; - -    // Core timing cannot be paused in single-core mode, so Stall ends up being called over and over -    // and never recovers to a normal state, so just skip attempting to sync things on single-core. -    if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) { -        Stall(); -    } else if (system.IsMulticore() && queued_buffers <= max_queue_size) { -        Unstall(); -    } -      while (frames_written < num_frames) {          // If the playing buffer has been consumed or has no frames, we need a new one          if (playing_buffer.consumed || playing_buffer.frames == 0) { @@ -237,6 +222,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz              }              // Successfully dequeued a new buffer.              queued_buffers--; + +            { std::unique_lock lk{release_mutex}; } + +            release_cv.notify_one();          }          // Get the minimum frames available between the currently playing buffer, and the @@ -248,6 +237,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz                             frames_available * frame_size);          frames_written += frames_available; +        actual_frames_written += frames_available;          playing_buffer.frames_played += frames_available;          // If that's all the frames in the current buffer, add its samples and mark it as @@ -260,26 +250,29 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz      std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size],                  frame_size_bytes); -    if (system.IsMulticore() && queued_buffers <= max_queue_size) { -        Unstall(); +    { +        std::scoped_lock lk{sample_count_lock}; +        last_sample_count_update_time = +            Core::Timing::CyclesToUs(system.CoreTiming().GetClockTicks()); +        min_played_sample_count = max_played_sample_count; +        max_played_sample_count += actual_frames_written;      }  } -void SinkStream::Stall() { -    std::scoped_lock lk{stall_guard}; -    if (stalled_lock) { -        return; -    } -    stalled_lock = system.StallApplication(); +u64 SinkStream::GetExpectedPlayedSampleCount() { +    std::scoped_lock lk{sample_count_lock}; +    auto cur_time{Core::Timing::CyclesToUs(system.CoreTiming().GetClockTicks())}; +    auto time_delta{cur_time - last_sample_count_update_time}; +    auto exp_played_sample_count{min_played_sample_count + +                                 (TargetSampleRate * time_delta) / std::chrono::seconds{1}}; + +    return std::min<u64>(exp_played_sample_count, max_played_sample_count);  } -void SinkStream::Unstall() { -    std::scoped_lock lk{stall_guard}; -    if (!stalled_lock) { -        return; -    } -    system.UnstallApplication(); -    stalled_lock.unlock(); +void SinkStream::WaitFreeSpace() { +    std::unique_lock lk{release_mutex}; +    release_cv.wait( +        lk, [this]() { return queued_buffers < max_queue_size || system.IsShuttingDown(); });  }  } // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 5fea72ab7..23e289c7b 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h @@ -5,6 +5,7 @@  #include <array>  #include <atomic> +#include <chrono>  #include <memory>  #include <mutex>  #include <span> @@ -14,6 +15,7 @@  #include "common/common_types.h"  #include "common/reader_writer_queue.h"  #include "common/ring_buffer.h" +#include "common/thread.h"  namespace Core {  class System; @@ -53,9 +55,7 @@ struct SinkBuffer {  class SinkStream {  public:      explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {} -    virtual ~SinkStream() { -        Unstall(); -    } +    virtual ~SinkStream() {}      /**       * Finalize the sink stream. @@ -201,14 +201,16 @@ public:      void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames);      /** -     * Stall core processes if the audio thread falls too far behind. +     * Get the total number of samples expected to have been played by this stream. +     * +     * @return The number of samples.       */ -    void Stall(); +    u64 GetExpectedPlayedSampleCount();      /** -     * Unstall core processes. +     * Waits for free space in the sample ring buffer       */ -    void Unstall(); +    void WaitFreeSpace();  protected:      /// Core system @@ -237,12 +239,21 @@ private:      std::atomic<u32> queued_buffers{};      /// The ring size for audio out buffers (usually 4, rarely 2 or 8)      u32 max_queue_size{}; +    /// Locks access to sample count tracking info +    std::mutex sample_count_lock; +    /// Minimum number of total samples that have been played since the last callback +    u64 min_played_sample_count{}; +    /// Maximum number of total samples that can be played since the last callback +    u64 max_played_sample_count{}; +    /// The time the two above tracking variables were last written to +    std::chrono::microseconds last_sample_count_update_time{};      /// Set by the audio render/in/out system which uses this stream      f32 system_volume{1.0f};      /// Set via IAudioDevice service calls      f32 device_volume{1.0f}; -    std::mutex stall_guard; -    std::unique_lock<std::mutex> stalled_lock; +    /// Signalled when ring buffer entries are consumed +    std::condition_variable release_cv; +    std::mutex release_mutex;  };  using SinkStreamPtr = std::unique_ptr<SinkStream>; | 
