PlaybackStreamSerenity.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /*
  2. * Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "PlaybackStreamSerenity.h"
  7. #include <LibCore/ThreadedPromise.h>
  8. namespace Audio {
  9. ErrorOr<NonnullRefPtr<PlaybackStream>> PlaybackStreamSerenity::create(OutputState initial_state, u32 sample_rate, u8 channels, [[maybe_unused]] u32 target_latency_ms, AudioDataRequestCallback&& data_request_callback)
  10. {
  11. // ConnectionToServer can only handle stereo audio currently. If it is able to accept mono audio
  12. // later, this can be removed.
  13. VERIFY(channels == 2);
  14. VERIFY(data_request_callback);
  15. auto connection = TRY(ConnectionToServer::try_create());
  16. if (auto result = connection->try_set_self_sample_rate(sample_rate); result.is_error())
  17. return Error::from_string_literal("Failed to set sample rate");
  18. auto polling_timer = TRY(Core::Timer::try_create());
  19. auto implementation = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PlaybackStreamSerenity(connection, move(polling_timer), move(data_request_callback))));
  20. if (initial_state == OutputState::Playing)
  21. connection->async_start_playback();
  22. return implementation;
  23. }
  24. PlaybackStreamSerenity::PlaybackStreamSerenity(NonnullRefPtr<ConnectionToServer> stream, NonnullRefPtr<Core::Timer> polling_timer, AudioDataRequestCallback&& data_request_callback)
  25. : m_connection(move(stream))
  26. , m_polling_timer(move(polling_timer))
  27. , m_data_request_callback(move(data_request_callback))
  28. {
  29. // Ensure that our audio buffers are filled when they are more than 3/4 empty.
  30. // FIXME: Add an event to ConnectionToServer track the sample rate and update this interval, or
  31. // implement the data request into ConnectionToServer so each client doesn't need to poll
  32. // on a timer with an arbitrary interval.
  33. m_polling_timer->set_interval(static_cast<int>((AUDIO_BUFFERS_COUNT * 3 / 4) * AUDIO_BUFFER_SIZE * 1000 / m_connection->get_self_sample_rate()));
  34. m_polling_timer->on_timeout = [this]() {
  35. fill_buffers();
  36. };
  37. m_polling_timer->start();
  38. }
  39. void PlaybackStreamSerenity::fill_buffers()
  40. {
  41. while (m_connection->can_enqueue()) {
  42. Array<Sample, AUDIO_BUFFER_SIZE> buffer;
  43. buffer.fill({ 0.0f, 0.0f });
  44. auto written_data = m_data_request_callback(Bytes { reinterpret_cast<u8*>(buffer.data()), sizeof(buffer) }, PcmSampleFormat::Float32, AUDIO_BUFFER_SIZE);
  45. // FIXME: The buffer we are enqueuing here is a fixed size, meaning that the server will not be
  46. // aware of exactly how many samples we have written here. We should allow the server to
  47. // consume sized buffers to allow us to obtain sample-accurate timing information even
  48. // when we run out of samples on a sample count that is not a multiple of AUDIO_BUFFER_SIZE.
  49. m_number_of_samples_enqueued += written_data.size() / sizeof(Sample);
  50. MUST(m_connection->realtime_enqueue(buffer));
  51. }
  52. }
  53. void PlaybackStreamSerenity::set_underrun_callback(Function<void()> callback)
  54. {
  55. // FIXME: Implement underrun callback in AudioServer
  56. (void)callback;
  57. }
  58. NonnullRefPtr<Core::ThreadedPromise<Duration>> PlaybackStreamSerenity::resume()
  59. {
  60. auto promise = Core::ThreadedPromise<Duration>::create();
  61. // FIXME: We need to get the time played at the correct time from the server. If a message to
  62. // start playback is sent while there is any other message being processed, this may end
  63. // up being inaccurate.
  64. auto time = MUST(total_time_played());
  65. fill_buffers();
  66. m_connection->async_start_playback();
  67. m_polling_timer->start();
  68. promise->resolve(move(time));
  69. return promise;
  70. }
  71. NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamSerenity::drain_buffer_and_suspend()
  72. {
  73. // FIXME: Play back all samples on the server before pausing. This can be achieved by stopping
  74. // enqueuing samples and receiving a message that a buffer underrun has occurred.
  75. auto promise = Core::ThreadedPromise<void>::create();
  76. m_connection->async_pause_playback();
  77. m_polling_timer->stop();
  78. promise->resolve();
  79. return promise;
  80. }
  81. NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamSerenity::discard_buffer_and_suspend()
  82. {
  83. auto promise = Core::ThreadedPromise<void>::create();
  84. m_connection->async_clear_buffer();
  85. m_connection->async_pause_playback();
  86. m_polling_timer->stop();
  87. promise->resolve();
  88. return promise;
  89. }
  90. ErrorOr<Duration> PlaybackStreamSerenity::total_time_played()
  91. {
  92. return Duration::from_milliseconds(m_number_of_samples_enqueued * 1000 / m_connection->get_self_sample_rate());
  93. }
  94. NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamSerenity::set_volume(double volume)
  95. {
  96. auto promise = Core::ThreadedPromise<void>::create();
  97. m_connection->async_set_self_volume(volume);
  98. promise->resolve();
  99. return promise;
  100. }
  101. }