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
|
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_AUDIO_OUTPUT_INTERFACE_HXX
#define MPD_AUDIO_OUTPUT_INTERFACE_HXX
#include <map>
#include <string>
#include <chrono>
struct AudioFormat;
struct Tag;
class AudioOutput {
const unsigned flags;
protected:
static constexpr unsigned FLAG_ENABLE_DISABLE = 0x1;
static constexpr unsigned FLAG_PAUSE = 0x2;
/**
* This output requires an "audio_format" setting which
* evaluates AudioFormat::IsFullyDefined().
*/
static constexpr unsigned FLAG_NEED_FULLY_DEFINED_AUDIO_FORMAT = 0x4;
public:
explicit AudioOutput(unsigned _flags) noexcept:flags(_flags) {}
virtual ~AudioOutput() noexcept = default;
AudioOutput(const AudioOutput &) = delete;
AudioOutput &operator=(const AudioOutput &) = delete;
bool SupportsEnableDisable() const noexcept {
return flags & FLAG_ENABLE_DISABLE;
}
bool SupportsPause() const noexcept {
return flags & FLAG_PAUSE;
}
bool GetNeedFullyDefinedAudioFormat() const noexcept {
return flags & FLAG_NEED_FULLY_DEFINED_AUDIO_FORMAT;
}
/**
* Returns a map of runtime attributes.
*
* This method must be thread-safe.
*/
virtual std::map<std::string, std::string> GetAttributes() const noexcept {
return {};
}
/**
* Manipulate a runtime attribute on client request.
*
* This method must be thread-safe.
*/
virtual void SetAttribute(std::string &&name, std::string &&value);
/**
* Enable the device. This may allocate resources, preparing
* for the device to be opened.
*
* Throws on error.
*/
virtual void Enable() {}
/**
* Disables the device. It is closed before this method is
* called.
*/
virtual void Disable() noexcept {}
/**
* Really open the device.
*
* Throws on error.
*
* @param audio_format the audio format in which data is going
* to be delivered; may be modified by the plugin
*/
virtual void Open(AudioFormat &audio_format) = 0;
/**
* Close the device.
*/
virtual void Close() noexcept = 0;
/**
* Attempt to change the #AudioFormat. After successful
* return, the caller may invoke Play() with the new format.
* If necessary, the method should drain old data from its
* buffers.
*
* If this method fails, the caller may then attempt to
* Close() and Open() the object instead.
*
* Throws on error. After such a failure, this object is in
* an undefined state, and it must be closed.
*
* @param audio_format the audio format in which data is going
* to be delivered; may be modified by the plugin
* @return true on success, false if the operation is not
* supported/implemented (no-op and the old format may still
* be used)
*/
virtual bool ChangeAudioFormat(AudioFormat &) {
return false;
}
/**
* Interrupt a blocking operation inside the plugin. This
* method will be called from outside the output thread (and
* therefore the method must be thread-safe), to make the
* output thread ready for receiving a command. For example,
* it will be called to prepare for an upcoming Close(),
* Cancel() or Pause() call.
*
* This method can be called any time, even if the output is
* not open or disabled.
*
* Implementations usually send some kind of message/signal to
* the output thread to wake it up and return to the output
* thread loop (e.g. by throwing #AudioOutputInterrupted),
* where the incoming command will be handled and dispatched.
*/
virtual void Interrupt() noexcept {}
/**
* Returns a positive number if the output thread shall further
* delay the next call to Play() or Pause(), which will happen
* until this function returns 0. This should be implemented
* instead of doing a sleep inside the plugin, because this
* allows MPD to listen to commands meanwhile.
*
* @return the duration to wait
*/
virtual std::chrono::steady_clock::duration Delay() const noexcept {
return std::chrono::steady_clock::duration::zero();
}
/**
* Display metadata for the next chunk. Optional method,
* because not all devices can display metadata.
*
* Throws on error.
*
* May throw #AudioOutputInterrupted after Interrupt() has
* been called.
*/
virtual void SendTag(const Tag &) {}
/**
* Play a chunk of audio data. The method blocks until at
* least one audio frame is consumed.
*
* Throws on error.
*
* May throw #AudioOutputInterrupted after Interrupt() has
* been called.
*
* @return the number of bytes played (must be a multiple of
* the frame size)
*/
virtual size_t Play(const void *chunk, size_t size) = 0;
/**
* Wait until the device has finished playing.
*
* Throws on error.
*/
virtual void Drain() {}
/**
* Try to cancel data which may still be in the device's
* buffers.
*/
virtual void Cancel() noexcept {}
/**
* Pause the device. If supported, it may perform a special
* action, which keeps the device open, but does not play
* anything. Output plugins like "shout" might want to play
* silence during pause, so their clients won't be
* disconnected. Plugins which do not support pausing will
* simply be closed, and have to be reopened when unpaused.
*
* May throw #AudioOutputInterrupted after Interrupt() has
* been called.
*
* @return false on error (output will be closed by caller),
* true for continue to pause
*
* Instead of returning false, the method may throw an
* exception, which will be logged.
*/
virtual bool Pause() {
/* fail because this method is not implemented */
return false;
}
};
#endif
|