summaryrefslogtreecommitdiffstats
path: root/src/audio_core/sink/sink_stream.h
blob: 17ed6593fb6175b4bcc1cb05fe61d047efa97d09 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <atomic>
#include <memory>
#include <vector>

#include "audio_core/common/common.h"
#include "common/common_types.h"

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, always check IsBufferConsumed in the same order you appended the
 * buffers, skipping a buffer will result in 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:
    virtual ~SinkStream() = default;

    /**
     * Finalize the sink stream.
     */
    virtual void Finalize() = 0;

    /**
     * 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) = 0;

    /**
     * Stop the sink stream.
     */
    virtual void Stop() = 0;

    /**
     * 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::vector<s16>& samples) = 0;

    /**
     * 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) = 0;

    /**
     * Check if a certain buffer has been consumed (fully played).
     *
     * @param tag - Unique tag of a buffer to check for.
     * @return True if the buffer has been played, otherwise false.
     */
    virtual bool IsBufferConsumed(u64 tag) = 0;

    /**
     * Empty out the buffer queue.
     */
    virtual void ClearQueue() = 0;

    /**
     * Check if the stream is paused.
     *
     * @return True if paused, otherwise false.
     */
    bool IsPaused() {
        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 total number of samples played by this stream.
     *
     * @return Number of samples played.
     */
    u64 GetPlayedSampleCount() const {
        return played_sample_count;
    }

    /**
     * Set the number of samples played.
     * This is started and stopped on system start/stop.
     *
     * @param played_sample_count_ - Number of samples to set.
     */
    void SetPlayedSampleCount(u64 played_sample_count_) {
        played_sample_count = played_sample_count_;
    }

    /**
     * Add to the played sample count.
     *
     * @param num_samples - Number of samples to add.
     */
    void AddPlayedSampleCount(u64 num_samples) {
        played_sample_count += num_samples;
    }

    /**
     * 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() {
        return queued_buffers.load();
    }

protected:
    /// Number of buffers waiting to be played
    std::atomic<u32> queued_buffers{};
    /// Total samples played by this stream
    std::atomic<u64> played_sample_count{};
    /// 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};
    /// Set by the audio render/in/out systen 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};
    /// Was this stream previously playing?
    std::atomic<bool> was_playing{false};
};

using SinkStreamPtr = std::unique_ptr<SinkStream>;

} // namespace AudioCore::Sink