ConnectionToServer.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Atomic.h>
  8. #include <AK/Format.h>
  9. #include <AK/OwnPtr.h>
  10. #include <AK/Time.h>
  11. #include <AK/Types.h>
  12. #include <LibAudio/ConnectionToServer.h>
  13. #include <LibAudio/Queue.h>
  14. #include <LibAudio/UserSampleQueue.h>
  15. #include <LibCore/Event.h>
  16. #include <LibThreading/Mutex.h>
  17. #include <sched.h>
  18. #include <time.h>
  19. namespace Audio {
  20. ConnectionToServer::ConnectionToServer(NonnullOwnPtr<Core::LocalSocket> socket)
  21. : IPC::ConnectionToServer<AudioClientEndpoint, AudioServerEndpoint>(*this, move(socket))
  22. , m_buffer(make<AudioQueue>(MUST(AudioQueue::create())))
  23. , m_user_queue(make<UserSampleQueue>())
  24. , m_background_audio_enqueuer(Threading::Thread::construct([this]() {
  25. // All the background thread does is run an event loop.
  26. Core::EventLoop enqueuer_loop;
  27. m_enqueuer_loop = &enqueuer_loop;
  28. enqueuer_loop.exec();
  29. {
  30. Threading::MutexLocker const locker(m_enqueuer_loop_destruction);
  31. m_enqueuer_loop = nullptr;
  32. }
  33. return (intptr_t) nullptr;
  34. }))
  35. {
  36. async_pause_playback();
  37. set_buffer(*m_buffer);
  38. }
  39. ConnectionToServer::~ConnectionToServer()
  40. {
  41. die();
  42. }
  43. void ConnectionToServer::die()
  44. {
  45. {
  46. Threading::MutexLocker const locker(m_enqueuer_loop_destruction);
  47. // We're sometimes getting here after the other thread has already exited and its event loop does no longer exist.
  48. if (m_enqueuer_loop != nullptr) {
  49. m_enqueuer_loop->wake();
  50. m_enqueuer_loop->quit(0);
  51. }
  52. }
  53. if (m_background_audio_enqueuer->is_started())
  54. (void)m_background_audio_enqueuer->join();
  55. }
  56. ErrorOr<void> ConnectionToServer::async_enqueue(FixedArray<Sample>&& samples)
  57. {
  58. if (!m_background_audio_enqueuer->is_started()) {
  59. m_background_audio_enqueuer->start();
  60. while (!m_enqueuer_loop)
  61. usleep(1);
  62. TRY(m_background_audio_enqueuer->set_priority(THREAD_PRIORITY_MAX));
  63. }
  64. update_good_sleep_time();
  65. m_user_queue->append(move(samples));
  66. // Wake the background thread to make sure it starts enqueuing audio.
  67. m_enqueuer_loop->post_event(*this, make<Core::CustomEvent>(0));
  68. m_enqueuer_loop->wake();
  69. async_start_playback();
  70. return {};
  71. }
  72. void ConnectionToServer::clear_client_buffer()
  73. {
  74. m_user_queue->clear();
  75. }
  76. void ConnectionToServer::update_good_sleep_time()
  77. {
  78. auto sample_rate = static_cast<double>(get_sample_rate());
  79. auto buffer_play_time_ns = 1'000'000'000.0 / (sample_rate / static_cast<double>(AUDIO_BUFFER_SIZE));
  80. // A factor of 1 should be good for now.
  81. m_good_sleep_time = Time::from_nanoseconds(static_cast<unsigned>(buffer_play_time_ns)).to_timespec();
  82. }
  83. // Non-realtime audio writing loop
  84. void ConnectionToServer::custom_event(Core::CustomEvent&)
  85. {
  86. Array<Sample, AUDIO_BUFFER_SIZE> next_chunk;
  87. while (true) {
  88. if (m_user_queue->is_empty()) {
  89. dbgln("Reached end of provided audio data, going to sleep");
  90. break;
  91. }
  92. auto available_samples = min(AUDIO_BUFFER_SIZE, m_user_queue->size());
  93. for (size_t i = 0; i < available_samples; ++i)
  94. next_chunk[i] = (*m_user_queue)[i];
  95. m_user_queue->discard_samples(available_samples);
  96. // FIXME: Could we receive interrupts in a good non-IPC way instead?
  97. auto result = m_buffer->blocking_enqueue(next_chunk, [this]() {
  98. nanosleep(&m_good_sleep_time, nullptr);
  99. });
  100. if (result.is_error())
  101. dbgln("Error while writing samples to shared buffer: {}", result.error());
  102. }
  103. }
  104. ErrorOr<void, AudioQueue::QueueStatus> ConnectionToServer::realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples)
  105. {
  106. return m_buffer->enqueue(samples);
  107. }
  108. ErrorOr<void> ConnectionToServer::blocking_realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples, Function<void()> wait_function)
  109. {
  110. return m_buffer->blocking_enqueue(samples, move(wait_function));
  111. }
  112. unsigned ConnectionToServer::total_played_samples() const
  113. {
  114. return m_buffer->weak_tail() * AUDIO_BUFFER_SIZE;
  115. }
  116. unsigned ConnectionToServer::remaining_samples()
  117. {
  118. return static_cast<unsigned>(m_user_queue->remaining_samples());
  119. }
  120. size_t ConnectionToServer::remaining_buffers() const
  121. {
  122. return m_buffer->size() - m_buffer->weak_remaining_capacity();
  123. }
  124. void ConnectionToServer::main_mix_muted_state_changed(bool muted)
  125. {
  126. if (on_main_mix_muted_state_change)
  127. on_main_mix_muted_state_change(muted);
  128. }
  129. void ConnectionToServer::main_mix_volume_changed(double volume)
  130. {
  131. if (on_main_mix_volume_change)
  132. on_main_mix_volume_change(volume);
  133. }
  134. void ConnectionToServer::client_volume_changed(double volume)
  135. {
  136. if (on_client_volume_change)
  137. on_client_volume_change(volume);
  138. }
  139. }