Control.cxx 4.65 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
		      bool _initial_seek_essential,
84 85
		      MusicBuffer &_buffer,
		      std::shared_ptr<MusicPipe> _pipe) noexcept
Max Kellermann's avatar
Max Kellermann committed
86
{
87
	assert(_song != nullptr);
88
	assert(_pipe->IsEmpty());
Max Kellermann's avatar
Max Kellermann committed
89

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

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

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

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

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

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

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

	case DecoderState::DECODE:
		break;
	}

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

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

142 143 144 145 146 147 148 149 150 151
	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
152
		WaitForDecoder(lock);
153

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

void
159
DecoderControl::Quit() noexcept
160
{
161
	assert(thread.IsDefined());
162

163
	quit = true;
164
	LockAsynchronousCommand(DecoderCommand::STOP);
165

166
	thread.Join();
167
}
168 169

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