Control.cxx 6.71 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
			     unsigned _buffer_chunks,
32
			     AudioFormat _configured_audio_format,
33
			     const ReplayGainConfig &_replay_gain_config) noexcept
34
	:listener(_listener), outputs(_outputs),
35
	 buffer_chunks(_buffer_chunks),
36
	 configured_audio_format(_configured_audio_format),
37
	 thread(BIND_THIS_METHOD(RunThread)),
38
	 replay_gain_config(_replay_gain_config)
39 40 41
{
}

42
PlayerControl::~PlayerControl() noexcept
43
{
44
	assert(!occupied);
45 46
}

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

	return result;
}

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

66
	assert(song != nullptr);
67

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

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

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

82
	LockSynchronousCommand(PlayerCommand::CANCEL);
83
	assert(next_song == nullptr);
84
}
85

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

92
	LockSynchronousCommand(PlayerCommand::CLOSE_AUDIO);
93
	assert(next_song == nullptr);
94 95

	idle_add(IDLE_PLAYER);
96 97
}

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

104
	LockSynchronousCommand(PlayerCommand::UPDATE_AUDIO);
105 106
}

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

113
	LockSynchronousCommand(PlayerCommand::EXIT);
114
	thread.Join();
115 116

	idle_add(IDLE_PLAYER);
117 118
}

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

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

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

141
	std::unique_lock<Mutex> lock(mutex);
142

143
	switch (state) {
144
	case PlayerState::STOP:
145 146
		break;

147
	case PlayerState::PLAY:
148
		if (pause_flag)
149
			PauseLocked(lock);
150
		break;
151

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

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

166
PlayerStatus
167
PlayerControl::LockGetStatus() noexcept
168
{
169
	PlayerStatus status;
170

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

175 176
	status.state = state;

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

184
	return status;
185 186
}

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

193
	error_type = type;
194
	error = std::move(_error);
195 196
}

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

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

void
213
PlayerControl::ClearTaggedSong() noexcept
214
{
215 216 217 218 219 220 221 222 223 224 225 226 227 228
	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();
229 230
}

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

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

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

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

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

259 260 261 262
	/* 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 */
263
	if (next_song != nullptr)
264
		SynchronousCommand(lock, PlayerCommand::CANCEL);
265 266 267

	assert(next_song == nullptr);

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

273
	assert(next_song == nullptr);
274

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

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

285
	assert(!error);
286 287
}

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

294 295
	assert(song != nullptr);

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

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

	idle_add(IDLE_OPTIONS);
306 307
}

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

	idle_add(IDLE_OPTIONS);
}

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

	idle_add(IDLE_OPTIONS);
}