MpcdecDecoderPlugin.cxx 6.8 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 "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
#include <math.h>
36

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

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

45 46
static constexpr Domain mpcdec_domain("mpcdec");

47 48 49
static constexpr SampleFormat mpcdec_sample_format = SampleFormat::S24_P32;
typedef SampleTraits<mpcdec_sample_format> MpcdecSampleTraits;

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

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

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

65
	return data->is.LockSeek(offset, IgnoreError());
66 67
}

Max Kellermann's avatar
Max Kellermann committed
68
static mpc_int32_t
69
mpc_tell_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
70
{
71 72
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
73

74
	return (long)data->is.GetOffset();
75 76
}

Max Kellermann's avatar
Max Kellermann committed
77
static mpc_bool_t
78
mpc_canseek_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
79
{
80 81
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
82

83
	return data->is.IsSeekable();
84 85
}

Max Kellermann's avatar
Max Kellermann committed
86
static mpc_int32_t
87
mpc_getsize_cb(mpc_reader *reader)
Avuton Olrich's avatar
Avuton Olrich committed
88
{
89 90
	struct mpc_decoder_data *data =
		(struct mpc_decoder_data *)reader->data;
91

92 93 94
	if (!data->is.KnownSize())
		return -1;

95
	return data->is.GetSize();
96 97
}

98
/* this _looks_ performance-critical, don't de-inline -- eric */
99
static inline MpcdecSampleTraits::value_type
Max Kellermann's avatar
Max Kellermann committed
100
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
Avuton Olrich's avatar
Avuton Olrich committed
101
{
102
	/* only doing 16-bit audio for now */
103
	MpcdecSampleTraits::value_type val;
104

105 106 107
	constexpr int bits = MpcdecSampleTraits::BITS;
	constexpr auto clip_min = MpcdecSampleTraits::MIN;
	constexpr auto clip_max = MpcdecSampleTraits::MAX;
Avuton Olrich's avatar
Avuton Olrich committed
108

109
#ifdef MPC_FIXED_POINT
110
	const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
111

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

	val = sample * float_scale;
#endif

122
	return Clamp(val, clip_min, clip_max);
123 124
}

125
static void
126 127
mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest,
		  const MPC_SAMPLE_FORMAT *src,
128 129 130
		  unsigned num_samples)
{
	while (num_samples-- > 0)
Max Kellermann's avatar
Max Kellermann committed
131
		*dest++ = mpc_to_mpd_sample(*src++);
132 133
}

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

139
	mpc_decoder_data data(is, &mpd_decoder);
140

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

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

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

160
	Error error;
161 162
	AudioFormat audio_format;
	if (!audio_format_init_checked(audio_format, info.sample_freq,
163
				       mpcdec_sample_format,
164
				       info.channels, error)) {
165
		LogError(error);
166
		mpc_demux_exit(demux);
167 168 169
		return;
	}

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

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

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

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

198
		mpc_uint32_t vbr_update_bits = 0;
199

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

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

212
		mpc_uint32_t ret = frame.samples;
213
		ret *= info.channels;
214

215
		MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
216
		mpc_to_mpd_buffer(chunk, sample_buffer, ret);
217

218
		long bit_rate = vbr_update_bits * audio_format.sample_rate
219
			/ 1152 / 1000;
220

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

226
	mpc_demux_exit(demux);
227 228
}

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

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

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

246
	mpc_streaminfo info;
247 248
	mpc_demux_get_info(demux, &info);
	mpc_demux_exit(demux);
249

250
	return SongTime::FromS(mpc_streaminfo_get_length(&info));
251 252
}

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

261
	tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
262
	return true;
263 264
}

265
static const char *const mpcdec_suffixes[] = { "mpc", nullptr };
266

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