Control.cxx 5.26 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

25
#include <algorithm>
26

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

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

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

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

57
	const ScopeLock protect(mutex);
58
	SeekLocked(song, SongTime::zero(), IgnoreError());
59 60 61 62 63

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

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

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

	idle_add(IDLE_PLAYER);
80 81
}

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

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

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

	idle_add(IDLE_PLAYER);
97 98
}

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

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

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

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

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

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

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

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

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

151 152
	status.state = state;

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

160
	return status;
161 162
}

163
void
164
PlayerControl::SetError(PlayerError type, Error &&_error)
165
{
166
	assert(type != PlayerError::NONE);
167
	assert(_error.IsDefined());
168

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

173
void
174
PlayerControl::LockClearError()
175
{
176
	const ScopeLock protect(mutex);
177
	ClearError();
178 179
}

180
void
181
PlayerControl::LockSetTaggedSong(const DetachedSong &song)
182
{
183
	const ScopeLock protect(mutex);
184 185
	delete tagged_song;
	tagged_song = new DetachedSong(song);
186 187 188
}

void
189
PlayerControl::ClearTaggedSong()
190
{
191 192
	delete tagged_song;
	tagged_song = nullptr;
193 194
}

195
void
196
PlayerControl::LockEnqueueSong(DetachedSong *song)
197
{
198
	assert(song != nullptr);
199

200
	const ScopeLock protect(mutex);
201
	EnqueueSongLocked(song);
202 203
}

204 205
bool
PlayerControl::SeekLocked(DetachedSong *song, SongTime t, Error &error_r)
206
{
207
	assert(song != nullptr);
208

209 210 211 212
	/* 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 */
213 214 215 216 217
	if (next_song != nullptr)
		SynchronousCommand(PlayerCommand::CANCEL);

	assert(next_song == nullptr);

218
	ClearError();
219
	next_song = song;
220
	seek_time = t;
221
	SynchronousCommand(PlayerCommand::SEEK);
222

223
	assert(next_song == nullptr);
224 225 226 227 228 229 230 231 232

	if (error_type != PlayerError::NONE) {
		assert(error.IsDefined());
		error_r.Set(error);
		return false;
	}

	assert(!error.IsDefined());
	return true;
233 234 235
}

bool
236
PlayerControl::LockSeek(DetachedSong *song, SongTime t, Error &error_r)
237 238 239
{
	assert(song != nullptr);

240 241
	{
		const ScopeLock protect(mutex);
242 243
		if (!SeekLocked(song, t, error_r))
			return false;
244
	}
245

246
	idle_add(IDLE_PLAYER);
247

248
	return true;
249 250
}

251
void
252
PlayerControl::SetCrossFade(float _cross_fade_seconds)
253
{
254 255
	if (_cross_fade_seconds < 0)
		_cross_fade_seconds = 0;
256
	cross_fade.duration = _cross_fade_seconds;
257 258

	idle_add(IDLE_OPTIONS);
259 260
}

261
void
262
PlayerControl::SetMixRampDb(float _mixramp_db)
263
{
264
	cross_fade.mixramp_db = _mixramp_db;
265 266 267 268 269

	idle_add(IDLE_OPTIONS);
}

void
270
PlayerControl::SetMixRampDelay(float _mixramp_delay_seconds)
271
{
272
	cross_fade.mixramp_delay = _mixramp_delay_seconds;
273 274 275

	idle_add(IDLE_OPTIONS);
}