| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #pragma once
- #include <array>
- #include <atomic>
- #include <chrono>
- #include <memory>
- #include <mutex>
- #include <span>
- #include <vector>
- #include "audio_core/common/common.h"
- #include "common/common_types.h"
- #include "common/polyfill_thread.h"
- #include "common/reader_writer_queue.h"
- #include "common/ring_buffer.h"
- #include "common/scratch_buffer.h"
- #include "common/thread.h"
- namespace Core {
- class System;
- } // namespace Core
- namespace AudioCore::Sink {
- enum class StreamType {
- Render,
- Out,
- In,
- };
- struct SinkBuffer {
- u64 frames;
- u64 frames_played;
- u64 tag;
- bool consumed;
- };
- /**
- * Contains a real backend stream for outputting samples to hardware,
- * created only via a Sink (See Sink::AcquireSinkStream).
- *
- * Accepts a SinkBuffer and samples in PCM16 format to be output (see AppendBuffer).
- * Appended buffers act as a FIFO queue, and will be held until played.
- * You should regularly call IsBufferConsumed with the unique SinkBuffer tag to check if the buffer
- * has been consumed.
- *
- * Since these are a FIFO queue, IsBufferConsumed must be checked in the same order buffers were
- * appended, skipping a buffer will result in the queue getting stuck, and all following buffers to
- * never release.
- *
- * If the buffers appear to be stuck, you can stop and re-open an IAudioIn/IAudioOut service (this
- * is what games do), or call ClearQueue to flush all of the buffers without a full restart.
- */
- class SinkStream {
- public:
- explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {}
- virtual ~SinkStream() {}
- /**
- * Finalize the sink stream.
- */
- virtual void Finalize() {}
- /**
- * Start the sink stream.
- *
- * @param resume - Set to true if this is resuming the stream a previously-active stream.
- * Default false.
- */
- virtual void Start(bool resume = false) {}
- /**
- * Stop the sink stream.
- */
- virtual void Stop() {}
- /**
- * Check if the stream is paused.
- *
- * @return True if paused, otherwise false.
- */
- bool IsPaused() const {
- return paused;
- }
- /**
- * Get the number of system channels in this stream.
- *
- * @return Number of system channels.
- */
- u32 GetSystemChannels() const {
- return system_channels;
- }
- /**
- * Set the number of channels the system expects.
- *
- * @param channels - New number of system channels.
- */
- void SetSystemChannels(u32 channels) {
- system_channels = channels;
- }
- /**
- * Get the number of channels the hardware supports.
- *
- * @return Number of channels supported.
- */
- u32 GetDeviceChannels() const {
- return device_channels;
- }
- /**
- * Get the system volume.
- *
- * @return The current system volume.
- */
- f32 GetSystemVolume() const {
- return system_volume;
- }
- /**
- * Get the device volume.
- *
- * @return The current device volume.
- */
- f32 GetDeviceVolume() const {
- return device_volume;
- }
- /**
- * Set the system volume.
- *
- * @param volume_ - The new system volume.
- */
- void SetSystemVolume(f32 volume_) {
- system_volume = volume_;
- }
- /**
- * Set the device volume.
- *
- * @param volume_ - The new device volume.
- */
- void SetDeviceVolume(f32 volume_) {
- device_volume = volume_;
- }
- /**
- * Get the number of queued audio buffers.
- *
- * @return The number of queued buffers.
- */
- u32 GetQueueSize() const {
- return queued_buffers.load();
- }
- /**
- * Set the maximum buffer queue size.
- */
- void SetRingSize(u32 ring_size) {
- max_queue_size = ring_size;
- }
- /**
- * Append a new buffer and its samples to a waiting queue to play.
- *
- * @param buffer - Audio buffer information to be queued.
- * @param samples - The s16 samples to be queue for playback.
- */
- virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples);
- /**
- * Release a buffer. Audio In only, will fill a buffer with recorded samples.
- *
- * @param num_samples - Maximum number of samples to receive.
- * @return Vector of recorded samples. May have fewer than num_samples.
- */
- virtual std::vector<s16> ReleaseBuffer(u64 num_samples);
- /**
- * Empty out the buffer queue.
- */
- void ClearQueue();
- /**
- * Callback for AudioIn.
- *
- * @param input_buffer - Input buffer to be filled with samples.
- * @param num_frames - Number of frames to be filled.
- */
- void ProcessAudioIn(std::span<const s16> input_buffer, std::size_t num_frames);
- /**
- * Callback for AudioOut and AudioRenderer.
- *
- * @param output_buffer - Output buffer to be filled with samples.
- * @param num_frames - Number of frames to be filled.
- */
- void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames);
- /**
- * Get the total number of samples expected to have been played by this stream.
- *
- * @return The number of samples.
- */
- u64 GetExpectedPlayedSampleCount();
- /**
- * Waits for free space in the sample ring buffer
- */
- void WaitFreeSpace(std::stop_token stop_token);
- protected:
- /// Core system
- Core::System& system;
- /// Type of this stream
- StreamType type;
- /// Set by the audio render/in/out system which uses this stream
- u32 system_channels{2};
- /// Channels supported by hardware
- u32 device_channels{2};
- /// Is this stream currently paused?
- std::atomic<bool> paused{true};
- /// Name of this stream
- std::string name{};
- private:
- /// Ring buffer of the samples waiting to be played or consumed
- Common::RingBuffer<s16, 0x10000> samples_buffer;
- /// Audio buffers queued and waiting to play
- Common::ReaderWriterQueue<SinkBuffer> queue;
- /// The currently-playing audio buffer
- SinkBuffer playing_buffer{};
- /// The last played (or received) frame of audio, used when the callback underruns
- std::array<s16, MaxChannels> last_frame{};
- /// Number of buffers waiting to be played
- 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::nanoseconds 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};
- /// Signalled when ring buffer entries are consumed
- std::condition_variable_any release_cv;
- std::mutex release_mutex;
- /// Temporary buffer for appending samples when upmixing
- Common::ScratchBuffer<s16> tmp_samples{};
- };
- using SinkStreamPtr = std::unique_ptr<SinkStream>;
- } // namespace AudioCore::Sink
|