Control.cxx 6.45 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 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 "config.h"
21
#include "Control.hxx"
22
#include "Outputs.hxx"
Max Kellermann's avatar
Max Kellermann committed
23
#include "Idle.hxx"
24
#include "song/DetachedSong.hxx"
25

26
#include <algorithm>
27

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

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

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

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

	return result;
}

62
void
63
PlayerControl::Play(std::unique_ptr<DetachedSong> song)
64
{
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
	LockSynchronousCommand(PlayerCommand::CANCEL);
80
	assert(next_song == nullptr);
81
}
82

83
void
84
PlayerControl::LockStop() noexcept
85
{
86
	LockSynchronousCommand(PlayerCommand::CLOSE_AUDIO);
87
	assert(next_song == nullptr);
88 89

	idle_add(IDLE_PLAYER);
90 91
}

92
void
93
PlayerControl::LockUpdateAudio() noexcept
94
{
95
	LockSynchronousCommand(PlayerCommand::UPDATE_AUDIO);
96 97
}

98
void
99
PlayerControl::Kill() noexcept
100
{
101
	assert(thread.IsDefined());
102

103
	LockSynchronousCommand(PlayerCommand::EXIT);
104
	thread.Join();
105 106

	idle_add(IDLE_PLAYER);
107 108
}

109
void
110
PlayerControl::PauseLocked() noexcept
111
{
112 113
	if (state != PlayerState::STOP) {
		SynchronousCommand(PlayerCommand::PAUSE);
114 115 116 117
		idle_add(IDLE_PLAYER);
	}
}

118
void
119
PlayerControl::LockPause() noexcept
120
{
121
	const std::lock_guard<Mutex> protect(mutex);
122
	PauseLocked();
123 124
}

125
void
126
PlayerControl::LockSetPause(bool pause_flag) noexcept
127
{
128
	const std::lock_guard<Mutex> protect(mutex);
129

130
	switch (state) {
131
	case PlayerState::STOP:
132 133
		break;

134
	case PlayerState::PLAY:
135
		if (pause_flag)
136
			PauseLocked();
137
		break;
138

139
	case PlayerState::PAUSE:
140
		if (!pause_flag)
141
			PauseLocked();
142 143 144 145
		break;
	}
}

146
void
147
PlayerControl::LockSetBorderPause(bool _border_pause) noexcept
148
{
149
	const std::lock_guard<Mutex> protect(mutex);
150
	border_pause = _border_pause;
151 152
}

153
PlayerStatus
154
PlayerControl::LockGetStatus() noexcept
155
{
156
	PlayerStatus status;
157

158
	const std::lock_guard<Mutex> protect(mutex);
159 160
	if (!occupied)
		SynchronousCommand(PlayerCommand::REFRESH);
161

162 163
	status.state = state;

164
	if (state != PlayerState::STOP) {
165 166 167 168
		status.bit_rate = bit_rate;
		status.audio_format = audio_format;
		status.total_time = total_time;
		status.elapsed_time = elapsed_time;
169
	}
170

171
	return status;
172 173
}

174
void
175
PlayerControl::SetError(PlayerError type, std::exception_ptr &&_error) noexcept
176
{
177
	assert(type != PlayerError::NONE);
178
	assert(_error);
179

180
	error_type = type;
181
	error = std::move(_error);
182 183
}

184
void
185
PlayerControl::LockClearError() noexcept
186
{
187
	const std::lock_guard<Mutex> protect(mutex);
188
	ClearError();
189 190
}

191
void
192
PlayerControl::LockSetTaggedSong(const DetachedSong &song) noexcept
193
{
194
	const std::lock_guard<Mutex> protect(mutex);
195 196
	tagged_song.reset();
	tagged_song = std::make_unique<DetachedSong>(song);
197 198 199
}

void
200
PlayerControl::ClearTaggedSong() noexcept
201
{
202 203 204 205 206 207 208 209 210 211 212 213 214 215
	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();
216 217
}

218
void
219
PlayerControl::LockEnqueueSong(std::unique_ptr<DetachedSong> song) noexcept
220
{
221
	assert(song != nullptr);
222

223
	const std::lock_guard<Mutex> protect(mutex);
224 225 226 227 228 229 230 231 232 233 234 235
	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);
236 237
}

238
void
239
PlayerControl::SeekLocked(std::unique_ptr<DetachedSong> song, SongTime t)
240
{
241
	assert(song != nullptr);
242

243 244 245 246
	/* 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 */
247 248 249 250 251
	if (next_song != nullptr)
		SynchronousCommand(PlayerCommand::CANCEL);

	assert(next_song == nullptr);

252
	ClearError();
253
	next_song = std::move(song);
254
	seek_time = t;
255
	SynchronousCommand(PlayerCommand::SEEK);
256

257
	assert(next_song == nullptr);
258

259 260 261 262 263
	/* the SEEK command is asynchronous; until completion, the
	   "seeking" flag is set */
	while (seeking)
		ClientWait();

264
	if (error_type != PlayerError::NONE) {
265 266
		assert(error);
		std::rethrow_exception(error);
267 268
	}

269
	assert(!error);
270 271
}

272
void
273
PlayerControl::LockSeek(std::unique_ptr<DetachedSong> song, SongTime t)
274 275 276
{
	assert(song != nullptr);

277
	{
278
		const std::lock_guard<Mutex> protect(mutex);
279
		SeekLocked(std::move(song), t);
280
	}
281

282
	idle_add(IDLE_PLAYER);
283 284
}

285
void
286
PlayerControl::SetCrossFade(float _cross_fade_seconds) noexcept
287
{
288 289
	if (_cross_fade_seconds < 0)
		_cross_fade_seconds = 0;
290
	cross_fade.duration = _cross_fade_seconds;
291 292

	idle_add(IDLE_OPTIONS);
293 294
}

295
void
296
PlayerControl::SetMixRampDb(float _mixramp_db) noexcept
297
{
298
	cross_fade.mixramp_db = _mixramp_db;
299 300 301 302 303

	idle_add(IDLE_OPTIONS);
}

void
304
PlayerControl::SetMixRampDelay(float _mixramp_delay_seconds) noexcept
305
{
306
	cross_fade.mixramp_delay = _mixramp_delay_seconds;
307 308 309

	idle_add(IDLE_OPTIONS);
}