Control.cxx 4.65 KB
Newer Older
1
/*
2
 * Copyright 2003-2018 The Music Player Daemon Project
3
 * http://www.musicpd.org
Max Kellermann's avatar
Max Kellermann committed
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.
Max Kellermann's avatar
Max Kellermann committed
18 19
 */

20
#include "Control.hxx"
21
#include "MusicPipe.hxx"
22
#include "song/DetachedSong.hxx"
23 24

#include <stdexcept>
25

26
#include <assert.h>
27

28
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond,
29
			       const AudioFormat _configured_audio_format,
30
			       const ReplayGainConfig &_replay_gain_config) noexcept
31 32
	:thread(BIND_THIS_METHOD(RunThread)),
	 mutex(_mutex), client_cond(_client_cond),
33
	 configured_audio_format(_configured_audio_format),
34
	 replay_gain_config(_replay_gain_config) {}
35

36
DecoderControl::~DecoderControl() noexcept
37
{
38
	ClearError();
39 40
}

41
void
42
DecoderControl::WaitForDecoder() noexcept
43 44 45 46 47 48 49 50 51 52
{
	assert(!client_is_waiting);
	client_is_waiting = true;

	client_cond.wait(mutex);

	assert(client_is_waiting);
	client_is_waiting = false;
}

53 54
void
DecoderControl::SetReady(const AudioFormat audio_format,
55
			 bool _seekable, SignedSongTime _duration) noexcept
56 57 58 59 60 61 62 63
{
	assert(state == DecoderState::START);
	assert(pipe != nullptr);
	assert(pipe->IsEmpty());
	assert(audio_format.IsDefined());
	assert(audio_format.IsValid());

	in_audio_format = audio_format;
64
	out_audio_format = audio_format.WithMask(configured_audio_format);
65 66 67 68 69 70 71 72

	seekable = _seekable;
	total_time = _duration;

	state = DecoderState::DECODE;
	client_cond.signal();
}

73
bool
74
DecoderControl::IsCurrentSong(const DetachedSong &_song) const noexcept
75
{
76
	switch (state) {
77 78
	case DecoderState::STOP:
	case DecoderState::ERROR:
79 80
		return false;

81 82
	case DecoderState::START:
	case DecoderState::DECODE:
83
		return song->IsSame(_song);
84 85 86
	}

	assert(false);
87
	gcc_unreachable();
88 89
}

90
void
91
DecoderControl::Start(std::unique_ptr<DetachedSong> _song,
92
		      SongTime _start_time, SongTime _end_time,
93
		      bool _initial_seek_essential,
94 95
		      MusicBuffer &_buffer,
		      std::shared_ptr<MusicPipe> _pipe) noexcept
Max Kellermann's avatar
Max Kellermann committed
96
{
97
	assert(_song != nullptr);
98
	assert(_pipe->IsEmpty());
Max Kellermann's avatar
Max Kellermann committed
99

100
	song = std::move(_song);
101 102
	start_time = _start_time;
	end_time = _end_time;
103
	initial_seek_essential = _initial_seek_essential;
104
	buffer = &_buffer;
105
	pipe = std::move(_pipe);
106

107 108
	ClearError();
	SynchronousCommandLocked(DecoderCommand::START);
Max Kellermann's avatar
Max Kellermann committed
109 110
}

111
void
112
DecoderControl::Stop() noexcept
Max Kellermann's avatar
Max Kellermann committed
113
{
114
	if (command != DecoderCommand::NONE)
115 116 117 118
		/* Attempt to cancel the current command.  If it's too
		   late and the decoder thread is already executing
		   the old command, we'll call STOP again in this
		   function (see below). */
119
		SynchronousCommandLocked(DecoderCommand::STOP);
120

121
	if (state != DecoderState::STOP && state != DecoderState::ERROR)
122
		SynchronousCommandLocked(DecoderCommand::STOP);
Max Kellermann's avatar
Max Kellermann committed
123 124
}

125 126
void
DecoderControl::Seek(SongTime t)
Max Kellermann's avatar
Max Kellermann committed
127
{
128
	assert(state != DecoderState::START);
129
	assert(state != DecoderState::ERROR);
Max Kellermann's avatar
Max Kellermann committed
130

131 132
	switch (state) {
	case DecoderState::START:
133
	case DecoderState::ERROR:
134 135 136
		gcc_unreachable();

	case DecoderState::STOP:
137 138
		/* TODO: if this happens, the caller should be given a
		   chance to restart the decoder */
139
		throw std::runtime_error("Decoder is dead");
140 141 142 143 144

	case DecoderState::DECODE:
		break;
	}

145 146
	if (!seekable)
		throw std::runtime_error("Not seekable");
Max Kellermann's avatar
Max Kellermann committed
147

148
	seek_time = t;
149
	seek_error = false;
150
	SynchronousCommandLocked(DecoderCommand::SEEK);
Max Kellermann's avatar
Max Kellermann committed
151

152 153 154 155 156 157 158 159 160 161 162 163
	while (state == DecoderState::START)
		/* If the decoder falls back to DecoderState::START,
		   this means that our SEEK command arrived too late,
		   and the decoder had meanwhile finished decoding and
		   went idle.  Our SEEK command is finished, but that
		   means only that the decoder thread has launched the
		   decoder.  To work around illegal states, we wait
		   until the decoder plugin has become ready.  This is
		   a kludge, built on top of the "late seek" kludge.
		   Not exactly elegant, sorry. */
		WaitForDecoder();

164 165
	if (seek_error)
		throw std::runtime_error("Decoder failed to seek");
Max Kellermann's avatar
Max Kellermann committed
166
}
167 168

void
169
DecoderControl::Quit() noexcept
170
{
171
	assert(thread.IsDefined());
172

173
	quit = true;
174
	LockAsynchronousCommand(DecoderCommand::STOP);
175

176
	thread.Join();
177
}
178 179

void
180
DecoderControl::CycleMixRamp() noexcept
181
{
182 183
	previous_mix_ramp = std::move(mix_ramp);
	mix_ramp.Clear();
184
}