MpcdecDecoderPlugin.cxx 6.54 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 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 "tag/TagHandler.hxx"
26
#include "util/Error.hxx"
27
#include "util/Domain.hxx"
28
#include "util/Macros.hxx"
29
#include "Log.hxx"
30

31 32
#include <mpc/mpcdec.h>

33
#include <math.h>
34

Max Kellermann's avatar
Max Kellermann committed
35
struct mpc_decoder_data {
36
	InputStream &is;
37
	Decoder *decoder;
38 39 40

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

43 44
static constexpr Domain mpcdec_domain("mpcdec");

Max Kellermann's avatar
Max Kellermann committed
45
static mpc_int32_t
46
mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
Avuton Olrich's avatar
Avuton Olrich committed
47
{
48 49
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
Avuton Olrich's avatar
Avuton Olrich committed
50

Max Kellermann's avatar
Max Kellermann committed
51
	return decoder_read(data->decoder, data->is, ptr, size);
52 53
}

Max Kellermann's avatar
Max Kellermann committed
54
static mpc_bool_t
55
mpc_seek_cb(mpc_reader *reader, mpc_int32_t offset)
Avuton Olrich's avatar
Avuton Olrich committed
56
{
57 58
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
59

60
	return data->is.LockSeek(offset, IgnoreError());
61 62
}

Max Kellermann's avatar
Max Kellermann committed
63
static mpc_int32_t
64
mpc_tell_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
65
{
66 67
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
68

69
	return (long)data->is.GetOffset();
70 71
}

Max Kellermann's avatar
Max Kellermann committed
72
static mpc_bool_t
73
mpc_canseek_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
74
{
75 76
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
77

78
	return data->is.IsSeekable();
79 80
}

Max Kellermann's avatar
Max Kellermann committed
81
static mpc_int32_t
82
mpc_getsize_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
83
{
84 85
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
86

87 88 89
	if (!data->is.KnownSize())
		return -1;

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

93
/* this _looks_ performance-critical, don't de-inline -- eric */
Max Kellermann's avatar
Max Kellermann committed
94 95
static inline int32_t
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
Avuton Olrich's avatar
Avuton Olrich committed
96
{
97
	/* only doing 16-bit audio for now */
98
	int32_t val;
99

100
	enum {
101
		bits = 24,
102 103 104 105
	};

	const int clip_min = -1 << (bits - 1);
	const int clip_max = (1 << (bits - 1)) - 1;
Avuton Olrich's avatar
Avuton Olrich committed
106

107
#ifdef MPC_FIXED_POINT
108
	const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
109

110
	if (shift < 0)
111
		val = sample >> -shift;
112 113
	else
		val = sample << shift;
114
#else
115
	const int float_scale = 1 << (bits - 1);
116 117 118 119

	val = sample * float_scale;
#endif

Avuton Olrich's avatar
Avuton Olrich committed
120 121 122 123
	if (val < clip_min)
		val = clip_min;
	else if (val > clip_max)
		val = clip_max;
124 125 126 127

	return val;
}

128 129 130 131 132
static void
mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
		  unsigned num_samples)
{
	while (num_samples-- > 0)
Max Kellermann's avatar
Max Kellermann committed
133
		*dest++ = mpc_to_mpd_sample(*src++);
134 135
}

136
static void
137
mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
138
{
139
	MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
140

141
	mpc_decoder_data data(is, &mpd_decoder);
142

143
	mpc_reader reader;
144 145 146 147 148 149 150
	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;

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

159
	mpc_streaminfo info;
160
	mpc_demux_get_info(demux, &info);
Avuton Olrich's avatar
Avuton Olrich committed
161

162
	Error error;
163 164 165
	AudioFormat audio_format;
	if (!audio_format_init_checked(audio_format, info.sample_freq,
				       SampleFormat::S24_P32,
166
				       info.channels, error)) {
167
		LogError(error);
168
		mpc_demux_exit(demux);
169 170 171
		return;
	}

172
	ReplayGainInfo rgi;
173
	rgi.Clear();
174 175 176 177 178 179
	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
180

181
	decoder_initialized(mpd_decoder, audio_format,
182
			    is.IsSeekable(),
183
			    SongTime::FromS(mpc_streaminfo_get_length(&info)));
184

185
	DecoderCommand cmd = DecoderCommand::NONE;
186
	do {
187
		if (cmd == DecoderCommand::SEEK) {
188 189
			mpc_int64_t where =
				decoder_seek_where_frame(mpd_decoder);
190
			bool success;
191

192
			success = mpc_demux_seek_sample(demux, where)
193 194
				== MPC_STATUS_OK;
			if (success)
195
				decoder_command_finished(mpd_decoder);
196
			else
197
				decoder_seek_error(mpd_decoder);
198
		}
199

200
		mpc_uint32_t vbr_update_bits = 0;
201

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

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

214
		mpc_uint32_t ret = frame.samples;
215
		ret *= info.channels;
216

217
		int32_t chunk[ARRAY_SIZE(sample_buffer)];
218
		mpc_to_mpd_buffer(chunk, sample_buffer, ret);
219

220
		long bit_rate = vbr_update_bits * audio_format.sample_rate
221
			/ 1152 / 1000;
222

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

228
	mpc_demux_exit(demux);
229 230
}

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

236
	mpc_reader reader;
237 238 239 240 241 242 243
	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;

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

248
	mpc_streaminfo info;
249 250
	mpc_demux_get_info(demux, &info);
	mpc_demux_exit(demux);
251

252
	return SongTime::FromS(mpc_streaminfo_get_length(&info));
253 254
}

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

263
	tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
264
	return true;
265 266
}

267
static const char *const mpcdec_suffixes[] = { "mpc", nullptr };
268

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