MpcdecDecoderPlugin.cxx 6.85 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 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 "MpcdecDecoderPlugin.hxx"
22
#include "../DecoderAPI.hxx"
Max Kellermann's avatar
Max Kellermann committed
23
#include "input/InputStream.hxx"
24
#include "CheckAudioFormat.hxx"
25
#include "pcm/Traits.hxx"
26
#include "tag/TagHandler.hxx"
27
#include "util/Error.hxx"
28
#include "util/Domain.hxx"
29
#include "util/Macros.hxx"
30
#include "util/Clamp.hxx"
31
#include "Log.hxx"
32

33 34
#include <mpc/mpcdec.h>

35 36
#include <stdexcept>

37
#include <math.h>
38

Max Kellermann's avatar
Max Kellermann committed
39
struct mpc_decoder_data {
40
	InputStream &is;
41
	Decoder *decoder;
42 43 44

	mpc_decoder_data(InputStream &_is, Decoder *_decoder)
		:is(_is), decoder(_decoder) {}
Max Kellermann's avatar
Max Kellermann committed
45
};
46

47 48
static constexpr Domain mpcdec_domain("mpcdec");

49 50 51
static constexpr SampleFormat mpcdec_sample_format = SampleFormat::S24_P32;
typedef SampleTraits<mpcdec_sample_format> MpcdecSampleTraits;

Max Kellermann's avatar
Max Kellermann committed
52
static mpc_int32_t
53
mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
Avuton Olrich's avatar
Avuton Olrich committed
54
{
55 56
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
Avuton Olrich's avatar
Avuton Olrich committed
57

Max Kellermann's avatar
Max Kellermann committed
58
	return decoder_read(data->decoder, data->is, ptr, size);
59 60
}

Max Kellermann's avatar
Max Kellermann committed
61
static mpc_bool_t
62
mpc_seek_cb(mpc_reader *reader, mpc_int32_t offset)
Avuton Olrich's avatar
Avuton Olrich committed
63
{
64 65
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
66

67 68 69 70 71 72
	try {
		data->is.LockSeek(offset);
		return true;
	} catch (const std::runtime_error &) {
		return false;
	}
73 74
}

Max Kellermann's avatar
Max Kellermann committed
75
static mpc_int32_t
76
mpc_tell_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
77
{
78 79
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
80

81
	return (long)data->is.GetOffset();
82 83
}

Max Kellermann's avatar
Max Kellermann committed
84
static mpc_bool_t
85
mpc_canseek_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
86
{
87 88
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
89

90
	return data->is.IsSeekable();
91 92
}

Max Kellermann's avatar
Max Kellermann committed
93
static mpc_int32_t
94
mpc_getsize_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
95
{
96 97
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
98

99 100 101
	if (!data->is.KnownSize())
		return -1;

102
	return data->is.GetSize();
103 104
}

105
/* this _looks_ performance-critical, don't de-inline -- eric */
106
static inline MpcdecSampleTraits::value_type
Max Kellermann's avatar
Max Kellermann committed
107
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
Avuton Olrich's avatar
Avuton Olrich committed
108
{
109
	/* only doing 16-bit audio for now */
110
	MpcdecSampleTraits::value_type val;
111

112 113 114
	constexpr int bits = MpcdecSampleTraits::BITS;
	constexpr auto clip_min = MpcdecSampleTraits::MIN;
	constexpr auto clip_max = MpcdecSampleTraits::MAX;
Avuton Olrich's avatar
Avuton Olrich committed
115

116
#ifdef MPC_FIXED_POINT
117
	const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
118

119
	if (shift < 0)
120
		val = sample >> -shift;
121 122
	else
		val = sample << shift;
123
#else
124
	const int float_scale = 1 << (bits - 1);
125 126 127 128

	val = sample * float_scale;
#endif

129
	return Clamp(val, clip_min, clip_max);
130 131
}

132
static void
133 134
mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest,
		  const MPC_SAMPLE_FORMAT *src,
135 136 137
		  unsigned num_samples)
{
	while (num_samples-- > 0)
Max Kellermann's avatar
Max Kellermann committed
138
		*dest++ = mpc_to_mpd_sample(*src++);
139 140
}

141
static void
142
mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
143
{
144
	mpc_decoder_data data(is, &mpd_decoder);
145

146
	mpc_reader reader;
147 148 149 150 151 152 153
	reader.read = mpc_read_cb;
	reader.seek = mpc_seek_cb;
	reader.tell = mpc_tell_cb;
	reader.get_size = mpc_getsize_cb;
	reader.canseek = mpc_canseek_cb;
	reader.data = &data;

154
	mpc_demux *demux = mpc_demux_init(&reader);
155
	if (demux == nullptr) {
156
		if (decoder_get_command(mpd_decoder) != DecoderCommand::STOP)
157 158
			LogWarning(mpcdec_domain,
				   "Not a valid musepack stream");
159 160 161
		return;
	}

162
	mpc_streaminfo info;
163
	mpc_demux_get_info(demux, &info);
Avuton Olrich's avatar
Avuton Olrich committed
164

165
	Error error;
166 167
	AudioFormat audio_format;
	if (!audio_format_init_checked(audio_format, info.sample_freq,
168
				       mpcdec_sample_format,
169
				       info.channels, error)) {
170
		LogError(error);
171
		mpc_demux_exit(demux);
172 173 174
		return;
	}

175
	ReplayGainInfo rgi;
176
	rgi.Clear();
177 178 179 180 181 182
	rgi.tuples[REPLAY_GAIN_ALBUM].gain = MPC_OLD_GAIN_REF  - (info.gain_album  / 256.);
	rgi.tuples[REPLAY_GAIN_ALBUM].peak = pow(10, info.peak_album / 256. / 20) / 32767;
	rgi.tuples[REPLAY_GAIN_TRACK].gain = MPC_OLD_GAIN_REF  - (info.gain_title  / 256.);
	rgi.tuples[REPLAY_GAIN_TRACK].peak = pow(10, info.peak_title / 256. / 20) / 32767;

	decoder_replay_gain(mpd_decoder, &rgi);
Warren Dukes's avatar
Warren Dukes committed
183

184
	decoder_initialized(mpd_decoder, audio_format,
185
			    is.IsSeekable(),
186
			    SongTime::FromS(mpc_streaminfo_get_length(&info)));
187

188
	DecoderCommand cmd = DecoderCommand::NONE;
189
	do {
190
		if (cmd == DecoderCommand::SEEK) {
191 192
			mpc_int64_t where =
				decoder_seek_where_frame(mpd_decoder);
193
			bool success;
194

195
			success = mpc_demux_seek_sample(demux, where)
196 197
				== MPC_STATUS_OK;
			if (success)
198
				decoder_command_finished(mpd_decoder);
199
			else
200
				decoder_seek_error(mpd_decoder);
201
		}
202

203
		MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
204
		mpc_frame_info frame;
205
		frame.buffer = (MPC_SAMPLE_FORMAT *)sample_buffer;
206
		mpc_status status = mpc_demux_decode(demux, &frame);
207
		if (status != MPC_STATUS_OK) {
208 209
			LogWarning(mpcdec_domain,
				   "Failed to decode sample");
210 211 212 213 214 215
			break;
		}

		if (frame.bits == -1)
			break;

216
		mpc_uint32_t ret = frame.samples;
217
		ret *= info.channels;
218

219
		MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
220
		mpc_to_mpd_buffer(chunk, sample_buffer, ret);
221

222 223
		long bit_rate = unsigned(frame.bits) * audio_format.sample_rate
			/ (1000 * frame.samples);
224

Max Kellermann's avatar
Max Kellermann committed
225
		cmd = decoder_data(mpd_decoder, is,
226
				   chunk, ret * sizeof(chunk[0]),
227
				   bit_rate);
228
	} while (cmd != DecoderCommand::STOP);
229

230
	mpc_demux_exit(demux);
231 232
}

233
static SignedSongTime
234
mpcdec_get_file_duration(InputStream &is)
Avuton Olrich's avatar
Avuton Olrich committed
235
{
236
	mpc_decoder_data data(is, nullptr);
237

238
	mpc_reader reader;
239 240 241 242 243 244 245
	reader.read = mpc_read_cb;
	reader.seek = mpc_seek_cb;
	reader.tell = mpc_tell_cb;
	reader.get_size = mpc_getsize_cb;
	reader.canseek = mpc_canseek_cb;
	reader.data = &data;

246
	mpc_demux *demux = mpc_demux_init(&reader);
247
	if (demux == nullptr)
248
		return SignedSongTime::Negative();
249

250
	mpc_streaminfo info;
251 252
	mpc_demux_get_info(demux, &info);
	mpc_demux_exit(demux);
253

254
	return SongTime::FromS(mpc_streaminfo_get_length(&info));
255 256
}

257
static bool
258
mpcdec_scan_stream(InputStream &is,
259
		   const TagHandler &handler, void *handler_ctx)
Avuton Olrich's avatar
Avuton Olrich committed
260
{
261 262
	const auto duration = mpcdec_get_file_duration(is);
	if (duration.IsNegative())
263
		return false;
264

265
	tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
266
	return true;
267 268
}

269
static const char *const mpcdec_suffixes[] = { "mpc", nullptr };
270

271
const struct DecoderPlugin mpcdec_decoder_plugin = {
272 273 274 275 276 277 278 279 280 281
	"mpcdec",
	nullptr,
	nullptr,
	mpcdec_decode,
	nullptr,
	nullptr,
	mpcdec_scan_stream,
	nullptr,
	mpcdec_suffixes,
	nullptr,
282
};