Control.cxx 6.78 KB
Newer Older
1
/*
2
 * Copyright 2003-2018 The Music Player Daemon Project
3
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
14 15 16 17
 *
 * 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.
18 19
 */

20
#include "Control.hxx"
21
#include "Outputs.hxx"
Max Kellermann's avatar
Max Kellermann committed
22
#include "Idle.hxx"
23
#include "song/DetachedSong.hxx"
24

25
#include <algorithm>
26

Max Kellermann's avatar
Max Kellermann committed
27
#include <assert.h>
28

29
PlayerControl::PlayerControl(PlayerListener &_listener,
30
			     PlayerOutputs &_outputs,
31
			     InputCacheManager *_input_cache,
32
			     unsigned _buffer_chunks,
33
			     AudioFormat _configured_audio_format,
34
			     const ReplayGainConfig &_replay_gain_config) noexcept
35
	:listener(_listener), outputs(_outputs),
36
	 input_cache(_input_cache),
37
	 buffer_chunks(_buffer_chunks),
38
	 configured_audio_format(_configured_audio_format),
39
	 thread(BIND_THIS_METHOD(RunThread)),
40
	 replay_gain_config(_replay_gain_config)
41 42 43
{
}

44
PlayerControl::~PlayerControl() noexcept
45
{
46
	assert(!occupied);
47 48
}

49
bool
50 51
PlayerControl::WaitOutputConsumed(std::unique_lock<Mutex> &lock,
				  unsigned threshold) noexcept
52
{
53
	bool result = outputs.CheckPipe() < threshold;
54
	if (!result && command == PlayerCommand::NONE) {
55
		Wait(lock);
56
		result = outputs.CheckPipe() < threshold;
57 58 59 60 61
	}

	return result;
}

62
void
63
PlayerControl::Play(std::unique_ptr<DetachedSong> song)
64
{
65 66 67
	if (!thread.IsDefined())
		thread.Start();

68
	assert(song != nullptr);
69

70 71
	std::unique_lock<Mutex> lock(mutex);
	SeekLocked(lock, std::move(song), SongTime::zero());
72

73
	if (state == PlayerState::PAUSE)
74 75
		/* if the player was paused previously, we need to
		   unpause it */
76
		PauseLocked(lock);
77 78
}

79
void
80
PlayerControl::LockCancel() noexcept
81
{
82 83
	assert(thread.IsDefined());

84
	LockSynchronousCommand(PlayerCommand::CANCEL);
85
	assert(next_song == nullptr);
86
}
87

88
void
89
PlayerControl::LockStop() noexcept
90
{
91 92 93
	if (!thread.IsDefined())
		return;

94
	LockSynchronousCommand(PlayerCommand::CLOSE_AUDIO);
95
	assert(next_song == nullptr);
96 97

	idle_add(IDLE_PLAYER);
98 99
}

100
void
101
PlayerControl::LockUpdateAudio() noexcept
102
{
103 104 105
	if (!thread.IsDefined())
		return;

106
	LockSynchronousCommand(PlayerCommand::UPDATE_AUDIO);
107 108
}

109
void
110
PlayerControl::Kill() noexcept
111
{
112 113
	if (!thread.IsDefined())
		return;
114

115
	LockSynchronousCommand(PlayerCommand::EXIT);
116
	thread.Join();
117 118

	idle_add(IDLE_PLAYER);
119 120
}

121
void
122
PlayerControl::PauseLocked(std::unique_lock<Mutex> &lock) noexcept
123
{
124
	if (state != PlayerState::STOP) {
125
		SynchronousCommand(lock, PlayerCommand::PAUSE);
126 127 128 129
		idle_add(IDLE_PLAYER);
	}
}

130
void
131
PlayerControl::LockPause() noexcept
132
{
133 134
	std::unique_lock<Mutex> lock(mutex);
	PauseLocked(lock);
135 136
}

137
void
138
PlayerControl::LockSetPause(bool pause_flag) noexcept
139
{
140 141 142
	if (!thread.IsDefined())
		return;

143
	std::unique_lock<Mutex> lock(mutex);
144

145
	switch (state) {
146
	case PlayerState::STOP:
147 148
		break;

149
	case PlayerState::PLAY:
150
		if (pause_flag)
151
			PauseLocked(lock);
152
		break;
153

154
	case PlayerState::PAUSE:
155
		if (!pause_flag)
156
			PauseLocked(lock);
157 158 159 160
		break;
	}
}

161
void
162
PlayerControl::LockSetBorderPause(bool _border_pause) noexcept
163
{
164
	const std::lock_guard<Mutex> protect(mutex);
165
	border_pause = _border_pause;
166 167
}

168
PlayerStatus
169
PlayerControl::LockGetStatus() noexcept
170
{
171
	PlayerStatus status;
172

173
	std::unique_lock<Mutex> lock(mutex);
174
	if (!occupied && thread.IsDefined())
175
		SynchronousCommand(lock, PlayerCommand::REFRESH);
176

177 178
	status.state = state;

179
	if (state != PlayerState::STOP) {
180 181 182 183
		status.bit_rate = bit_rate;
		status.audio_format = audio_format;
		status.total_time = total_time;
		status.elapsed_time = elapsed_time;
184
	}
185

186
	return status;
187 188
}

189
void
190
PlayerControl::SetError(PlayerError type, std::exception_ptr &&_error) noexcept
191
{
192
	assert(type != PlayerError::NONE);
193
	assert(_error);
194

195
	error_type = type;
196
	error = std::move(_error);
197 198
}

199
void
200
PlayerControl::LockClearError() noexcept
201
{
202
	const std::lock_guard<Mutex> protect(mutex);
203
	ClearError();
204 205
}

206
void
207
PlayerControl::LockSetTaggedSong(const DetachedSong &song) noexcept
208
{
209
	const std::lock_guard<Mutex> protect(mutex);
210 211
	tagged_song.reset();
	tagged_song = std::make_unique<DetachedSong>(song);
212 213 214
}

void
215
PlayerControl::ClearTaggedSong() noexcept
216
{
217 218 219 220 221 222 223 224 225 226 227 228 229 230
	tagged_song.reset();
}

std::unique_ptr<DetachedSong>
PlayerControl::ReadTaggedSong() noexcept
{
	return std::exchange(tagged_song, nullptr);
}

std::unique_ptr<DetachedSong>
PlayerControl::LockReadTaggedSong() noexcept
{
	const std::lock_guard<Mutex> protect(mutex);
	return ReadTaggedSong();
231 232
}

233
void
234
PlayerControl::LockEnqueueSong(std::unique_ptr<DetachedSong> song) noexcept
235
{
236
	assert(thread.IsDefined());
237
	assert(song != nullptr);
238

239 240
	std::unique_lock<Mutex> lock(mutex);
	EnqueueSongLocked(lock, std::move(song));
241 242 243
}

void
244 245
PlayerControl::EnqueueSongLocked(std::unique_lock<Mutex> &lock,
				 std::unique_ptr<DetachedSong> song) noexcept
246 247 248 249 250 251
{
	assert(song != nullptr);
	assert(next_song == nullptr);

	next_song = std::move(song);
	seek_time = SongTime::zero();
252
	SynchronousCommand(lock, PlayerCommand::QUEUE);
253 254
}

255
void
256 257
PlayerControl::SeekLocked(std::unique_lock<Mutex> &lock,
			  std::unique_ptr<DetachedSong> song, SongTime t)
258
{
259
	assert(song != nullptr);
260

261 262 263 264
	/* to issue the SEEK command below, we need to clear the
	   "next_song" attribute with the CANCEL command */
	/* optimization TODO: if the decoder happens to decode that
	   song already, don't cancel that */
265
	if (next_song != nullptr)
266
		SynchronousCommand(lock, PlayerCommand::CANCEL);
267 268 269

	assert(next_song == nullptr);

270
	ClearError();
271
	next_song = std::move(song);
272
	seek_time = t;
273
	SynchronousCommand(lock, PlayerCommand::SEEK);
274

275
	assert(next_song == nullptr);
276

277 278 279
	/* the SEEK command is asynchronous; until completion, the
	   "seeking" flag is set */
	while (seeking)
280
		ClientWait(lock);
281

282
	if (error_type != PlayerError::NONE) {
283 284
		assert(error);
		std::rethrow_exception(error);
285 286
	}

287
	assert(!error);
288 289
}

290
void
291
PlayerControl::LockSeek(std::unique_ptr<DetachedSong> song, SongTime t)
292
{
293 294 295
	if (!thread.IsDefined())
		thread.Start();

296 297
	assert(song != nullptr);

298 299
	std::unique_lock<Mutex> lock(mutex);
	SeekLocked(lock, std::move(song), t);
300 301
}

302
void
303
PlayerControl::SetCrossFade(FloatDuration duration) noexcept
304
{
305
	cross_fade.duration = std::max(duration, FloatDuration::zero());
306 307

	idle_add(IDLE_OPTIONS);
308 309
}

310
void
311
PlayerControl::SetMixRampDb(float _mixramp_db) noexcept
312
{
313
	cross_fade.mixramp_db = _mixramp_db;
314 315 316 317 318

	idle_add(IDLE_OPTIONS);
}

void
319
PlayerControl::SetMixRampDelay(FloatDuration _mixramp_delay) noexcept
320
{
321
	cross_fade.mixramp_delay = _mixramp_delay;
322 323 324

	idle_add(IDLE_OPTIONS);
}