Control.cxx 5.55 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"
Max Kellermann's avatar
Max Kellermann committed
22
#include "Idle.hxx"
23
#include "DetachedSong.hxx"
24
#include "output/MultipleOutputs.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
			     AudioFormat _configured_audio_format,
35
			     const ReplayGainConfig &_replay_gain_config)
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()
46
{
47 48
	delete next_song;
	delete tagged_song;
49 50
}

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

	return result;
}

63 64
void
PlayerControl::Play(DetachedSong *song)
65
{
66
	assert(song != nullptr);
67

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

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

77
void
78
PlayerControl::LockCancel()
79
{
80
	LockSynchronousCommand(PlayerCommand::CANCEL);
81
	assert(next_song == nullptr);
82
}
83

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

	idle_add(IDLE_PLAYER);
91 92
}

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

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

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

	idle_add(IDLE_PLAYER);
108 109
}

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

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

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

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

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

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

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

154
player_status
155
PlayerControl::LockGetStatus()
156
{
157
	player_status status;
158

159
	const std::lock_guard<Mutex> protect(mutex);
160
	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)
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()
186
{
187
	const std::lock_guard<Mutex> protect(mutex);
188
	ClearError();
189 190
}

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

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

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

211
	const std::lock_guard<Mutex> protect(mutex);
212
	EnqueueSongLocked(song);
213 214
}

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

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

	assert(next_song == nullptr);

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

234
	assert(next_song == nullptr);
235 236

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

241
	assert(!error);
242 243
}

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

249
	{
250
		const std::lock_guard<Mutex> protect(mutex);
251
		SeekLocked(song, t);
252
	}
253

254
	idle_add(IDLE_PLAYER);
255 256
}

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

	idle_add(IDLE_OPTIONS);
265 266
}

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

	idle_add(IDLE_OPTIONS);
}

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

	idle_add(IDLE_OPTIONS);
}