Control.cxx 4.56 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 94
		      MusicBuffer &_buffer,
		      std::shared_ptr<MusicPipe> _pipe) noexcept
Max Kellermann's avatar
Max Kellermann committed
95
{
96
	assert(_song != nullptr);
97
	assert(_pipe->IsEmpty());
Max Kellermann's avatar
Max Kellermann committed
98

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

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

109
void
110
DecoderControl::Stop() noexcept
Max Kellermann's avatar
Max Kellermann committed
111
{
112
	if (command != DecoderCommand::NONE)
113 114 115 116
		/* 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). */
117
		SynchronousCommandLocked(DecoderCommand::STOP);
118

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

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

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

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

	case DecoderState::DECODE:
		break;
	}

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

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

150 151 152 153 154 155 156 157 158 159 160 161
	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();

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

void
167
DecoderControl::Quit() noexcept
168
{
169
	assert(thread.IsDefined());
170

171
	quit = true;
172
	LockAsynchronousCommand(DecoderCommand::STOP);
173

174
	thread.Join();
175
}
176 177

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