Control.cxx 6.55 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
PlayerControl::WaitOutputConsumed(unsigned threshold) noexcept
49
{
50
	bool result = outputs.CheckPipe() < threshold;
51 52
	if (!result && command == PlayerCommand::NONE) {
		Wait();
53
		result = outputs.CheckPipe() < threshold;
54 55 56 57 58
	}

	return result;
}

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

65
	assert(song != nullptr);
66

67
	const std::lock_guard<Mutex> protect(mutex);
68
	SeekLocked(std::move(song), SongTime::zero());
69

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

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

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

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

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

	idle_add(IDLE_PLAYER);
95 96
}

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

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

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

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

	idle_add(IDLE_PLAYER);
116 117
}

118
void
119
PlayerControl::PauseLocked() noexcept
120
{
121 122
	if (state != PlayerState::STOP) {
		SynchronousCommand(PlayerCommand::PAUSE);
123 124 125 126
		idle_add(IDLE_PLAYER);
	}
}

127
void
128
PlayerControl::LockPause() noexcept
129
{
130
	const std::lock_guard<Mutex> protect(mutex);
131
	PauseLocked();
132 133
}

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

140
	const std::lock_guard<Mutex> protect(mutex);
141

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

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

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

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

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

170
	const std::lock_guard<Mutex> protect(mutex);
171
	if (!occupied && thread.IsDefined())
172
		SynchronousCommand(PlayerCommand::REFRESH);
173

174 175
	status.state = state;

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

183
	return status;
184 185
}

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

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

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

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

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

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

236
	const std::lock_guard<Mutex> protect(mutex);
237 238 239 240 241 242 243 244 245 246 247 248
	EnqueueSongLocked(std::move(song));
}

void
PlayerControl::EnqueueSongLocked(std::unique_ptr<DetachedSong> song) noexcept
{
	assert(song != nullptr);
	assert(next_song == nullptr);

	next_song = std::move(song);
	seek_time = SongTime::zero();
	SynchronousCommand(PlayerCommand::QUEUE);
249 250
}

251
void
252
PlayerControl::SeekLocked(std::unique_ptr<DetachedSong> song, SongTime t)
253
{
254
	assert(song != nullptr);
255

256 257 258 259
	/* 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 */
260 261 262 263 264
	if (next_song != nullptr)
		SynchronousCommand(PlayerCommand::CANCEL);

	assert(next_song == nullptr);

265
	ClearError();
266
	next_song = std::move(song);
267
	seek_time = t;
268
	SynchronousCommand(PlayerCommand::SEEK);
269

270
	assert(next_song == nullptr);
271

272 273 274 275 276
	/* the SEEK command is asynchronous; until completion, the
	   "seeking" flag is set */
	while (seeking)
		ClientWait();

277
	if (error_type != PlayerError::NONE) {
278 279
		assert(error);
		std::rethrow_exception(error);
280 281
	}

282
	assert(!error);
283 284
}

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

291 292
	assert(song != nullptr);

293
	const std::lock_guard<Mutex> protect(mutex);
294
	SeekLocked(std::move(song), t);
295 296
}

297
void
298
PlayerControl::SetCrossFade(FloatDuration duration) noexcept
299
{
300
	cross_fade.duration = std::max(duration, FloatDuration::zero());
301 302

	idle_add(IDLE_OPTIONS);
303 304
}

305
void
306
PlayerControl::SetMixRampDb(float _mixramp_db) noexcept
307
{
308
	cross_fade.mixramp_db = _mixramp_db;
309 310 311 312 313

	idle_add(IDLE_OPTIONS);
}

void
314
PlayerControl::SetMixRampDelay(FloatDuration _mixramp_delay) noexcept
315
{
316
	cross_fade.mixramp_delay = _mixramp_delay;
317 318 319

	idle_add(IDLE_OPTIONS);
}