Control.cxx 4.57 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2020 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 <cassert>
25
#include <stdexcept>
26

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

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

42 43
void
DecoderControl::SetReady(const AudioFormat audio_format,
44
			 bool _seekable, SignedSongTime _duration) noexcept
45 46 47 48 49 50 51 52
{
	assert(state == DecoderState::START);
	assert(pipe != nullptr);
	assert(pipe->IsEmpty());
	assert(audio_format.IsDefined());
	assert(audio_format.IsValid());

	in_audio_format = audio_format;
53
	out_audio_format = audio_format.WithMask(configured_audio_format);
54 55 56 57 58

	seekable = _seekable;
	total_time = _duration;

	state = DecoderState::DECODE;
59
	client_cond.notify_one();
60 61
}

62
bool
63
DecoderControl::IsCurrentSong(const DetachedSong &_song) const noexcept
64
{
65
	switch (state) {
66 67
	case DecoderState::STOP:
	case DecoderState::ERROR:
68 69
		return false;

70 71
	case DecoderState::START:
	case DecoderState::DECODE:
72
		return song->IsSame(_song);
73 74 75
	}

	assert(false);
76
	gcc_unreachable();
77 78
}

79
void
80 81
DecoderControl::Start(std::unique_lock<Mutex> &lock,
		      std::unique_ptr<DetachedSong> _song,
82
		      SongTime _start_time, SongTime _end_time,
83 84
		      MusicBuffer &_buffer,
		      std::shared_ptr<MusicPipe> _pipe) noexcept
Max Kellermann's avatar
Max Kellermann committed
85
{
86
	assert(_song != nullptr);
87
	assert(_pipe->IsEmpty());
Max Kellermann's avatar
Max Kellermann committed
88

89
	song = std::move(_song);
90 91
	start_time = _start_time;
	end_time = _end_time;
92
	buffer = &_buffer;
93
	pipe = std::move(_pipe);
94

95
	ClearError();
96
	SynchronousCommandLocked(lock, DecoderCommand::START);
Max Kellermann's avatar
Max Kellermann committed
97 98
}

99
void
100
DecoderControl::Stop(std::unique_lock<Mutex> &lock) noexcept
Max Kellermann's avatar
Max Kellermann committed
101
{
102
	if (command != DecoderCommand::NONE)
103 104 105 106
		/* 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). */
107
		SynchronousCommandLocked(lock, DecoderCommand::STOP);
108

109
	if (state != DecoderState::STOP && state != DecoderState::ERROR)
110
		SynchronousCommandLocked(lock, DecoderCommand::STOP);
Max Kellermann's avatar
Max Kellermann committed
111 112
}

113
void
114
DecoderControl::Seek(std::unique_lock<Mutex> &lock, SongTime t)
Max Kellermann's avatar
Max Kellermann committed
115
{
116
	assert(state != DecoderState::START);
117
	assert(state != DecoderState::ERROR);
Max Kellermann's avatar
Max Kellermann committed
118

119 120
	switch (state) {
	case DecoderState::START:
121
	case DecoderState::ERROR:
122 123 124
		gcc_unreachable();

	case DecoderState::STOP:
125 126
		/* TODO: if this happens, the caller should be given a
		   chance to restart the decoder */
127
		throw std::runtime_error("Decoder is dead");
128 129 130 131 132

	case DecoderState::DECODE:
		break;
	}

133 134
	if (!seekable)
		throw std::runtime_error("Not seekable");
Max Kellermann's avatar
Max Kellermann committed
135

136
	seek_time = t;
137
	seek_error = false;
138
	SynchronousCommandLocked(lock, DecoderCommand::SEEK);
Max Kellermann's avatar
Max Kellermann committed
139

140 141 142 143 144 145 146 147 148 149
	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. */
Max Kellermann's avatar
Max Kellermann committed
150
		WaitForDecoder(lock);
151

152 153
	if (seek_error)
		throw std::runtime_error("Decoder failed to seek");
Max Kellermann's avatar
Max Kellermann committed
154
}
155 156

void
157
DecoderControl::Quit() noexcept
158
{
159
	assert(thread.IsDefined());
160

161
	quit = true;
162
	LockAsynchronousCommand(DecoderCommand::STOP);
163

164
	thread.Join();
165
}
166 167

void
168
DecoderControl::CycleMixRamp() noexcept
169
{
170 171
	previous_mix_ramp = std::move(mix_ramp);
	mix_ramp.Clear();
172
}