Control.cxx 5.35 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 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"
Max Kellermann's avatar
Max Kellermann committed
22
#include "Idle.hxx"
23
#include "DetachedSong.hxx"
24
#include "util/Error.hxx"
25

26
#include <algorithm>
27

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

30 31
PlayerControl::PlayerControl(PlayerListener &_listener,
			     MultipleOutputs &_outputs,
32
			     unsigned _buffer_chunks,
33
			     unsigned _buffered_before_play)
34
	:listener(_listener), outputs(_outputs),
35
	 buffer_chunks(_buffer_chunks),
36
	 buffered_before_play(_buffered_before_play),
37 38 39
	 command(PlayerCommand::NONE),
	 state(PlayerState::STOP),
	 error_type(PlayerError::NONE),
40
	 tagged_song(nullptr),
41
	 next_song(nullptr),
42 43
	 total_play_time(0),
	 border_pause(false)
44 45 46
{
}

47
PlayerControl::~PlayerControl()
48
{
49 50
	delete next_song;
	delete tagged_song;
51 52
}

53 54
void
PlayerControl::Play(DetachedSong *song)
55
{
56
	assert(song != nullptr);
57

58
	const ScopeLock protect(mutex);
59
	SeekLocked(song, SongTime::zero());
60

61
	if (state == PlayerState::PAUSE)
62 63 64
		/* if the player was paused previously, we need to
		   unpause it */
		PauseLocked();
65 66
}

67
void
68
PlayerControl::LockCancel()
69
{
70
	LockSynchronousCommand(PlayerCommand::CANCEL);
71
	assert(next_song == nullptr);
72
}
73

74
void
75
PlayerControl::LockStop()
76
{
77
	LockSynchronousCommand(PlayerCommand::CLOSE_AUDIO);
78
	assert(next_song == nullptr);
79 80

	idle_add(IDLE_PLAYER);
81 82
}

83
void
84
PlayerControl::LockUpdateAudio()
85
{
86
	LockSynchronousCommand(PlayerCommand::UPDATE_AUDIO);
87 88
}

89
void
90
PlayerControl::Kill()
91
{
92
	assert(thread.IsDefined());
93

94
	LockSynchronousCommand(PlayerCommand::EXIT);
95
	thread.Join();
96 97

	idle_add(IDLE_PLAYER);
98 99
}

100
void
101
PlayerControl::PauseLocked()
102
{
103 104
	if (state != PlayerState::STOP) {
		SynchronousCommand(PlayerCommand::PAUSE);
105 106 107 108
		idle_add(IDLE_PLAYER);
	}
}

109
void
110
PlayerControl::LockPause()
111
{
112
	const ScopeLock protect(mutex);
113
	PauseLocked();
114 115
}

116
void
117
PlayerControl::LockSetPause(bool pause_flag)
118
{
119
	const ScopeLock protect(mutex);
120

121
	switch (state) {
122
	case PlayerState::STOP:
123 124
		break;

125
	case PlayerState::PLAY:
126
		if (pause_flag)
127
			PauseLocked();
128
		break;
129

130
	case PlayerState::PAUSE:
131
		if (!pause_flag)
132
			PauseLocked();
133 134 135 136
		break;
	}
}

137
void
138
PlayerControl::LockSetBorderPause(bool _border_pause)
139
{
140
	const ScopeLock protect(mutex);
141
	border_pause = _border_pause;
142 143
}

144
player_status
145
PlayerControl::LockGetStatus()
146
{
147
	player_status status;
148

149
	const ScopeLock protect(mutex);
150
	SynchronousCommand(PlayerCommand::REFRESH);
151

152 153
	status.state = state;

154
	if (state != PlayerState::STOP) {
155 156 157 158
		status.bit_rate = bit_rate;
		status.audio_format = audio_format;
		status.total_time = total_time;
		status.elapsed_time = elapsed_time;
159
	}
160

161
	return status;
162 163
}

164
void
165
PlayerControl::SetError(PlayerError type, std::exception_ptr &&_error)
166
{
167
	assert(type != PlayerError::NONE);
168
	assert(_error);
169

170
	error_type = type;
171
	error = std::move(_error);
172 173
}

174 175 176 177 178 179 180 181 182
void
PlayerControl::SetError(PlayerError type, Error &&_error)
{
	assert(type != PlayerError::NONE);
	assert(_error.IsDefined());

	SetError(type, std::make_exception_ptr(std::move(_error)));
}

183
void
184
PlayerControl::LockClearError()
185
{
186
	const ScopeLock protect(mutex);
187
	ClearError();
188 189
}

190
void
191
PlayerControl::LockSetTaggedSong(const DetachedSong &song)
192
{
193
	const ScopeLock protect(mutex);
194 195
	delete tagged_song;
	tagged_song = new DetachedSong(song);
196 197 198
}

void
199
PlayerControl::ClearTaggedSong()
200
{
201 202
	delete tagged_song;
	tagged_song = nullptr;
203 204
}

205
void
206
PlayerControl::LockEnqueueSong(DetachedSong *song)
207
{
208
	assert(song != nullptr);
209

210
	const ScopeLock protect(mutex);
211
	EnqueueSongLocked(song);
212 213
}

214 215
void
PlayerControl::SeekLocked(DetachedSong *song, SongTime t)
216
{
217
	assert(song != nullptr);
218

219 220 221 222
	/* 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 */
223 224 225 226 227
	if (next_song != nullptr)
		SynchronousCommand(PlayerCommand::CANCEL);

	assert(next_song == nullptr);

228
	ClearError();
229
	next_song = song;
230
	seek_time = t;
231
	SynchronousCommand(PlayerCommand::SEEK);
232

233
	assert(next_song == nullptr);
234 235

	if (error_type != PlayerError::NONE) {
236 237
		assert(error);
		std::rethrow_exception(error);
238 239
	}

240
	assert(!error);
241 242
}

243 244
void
PlayerControl::LockSeek(DetachedSong *song, SongTime t)
245 246 247
{
	assert(song != nullptr);

248 249
	{
		const ScopeLock protect(mutex);
250
		SeekLocked(song, t);
251
	}
252

253
	idle_add(IDLE_PLAYER);
254 255
}

256
void
257
PlayerControl::SetCrossFade(float _cross_fade_seconds)
258
{
259 260
	if (_cross_fade_seconds < 0)
		_cross_fade_seconds = 0;
261
	cross_fade.duration = _cross_fade_seconds;
262 263

	idle_add(IDLE_OPTIONS);
264 265
}

266
void
267
PlayerControl::SetMixRampDb(float _mixramp_db)
268
{
269
	cross_fade.mixramp_db = _mixramp_db;
270 271 272 273 274

	idle_add(IDLE_OPTIONS);
}

void
275
PlayerControl::SetMixRampDelay(float _mixramp_delay_seconds)
276
{
277
	cross_fade.mixramp_delay = _mixramp_delay_seconds;
278 279 280

	idle_add(IDLE_OPTIONS);
}