_flac_common.c 11 KB
Newer Older
1 2 3
/*
 * Copyright (C) 2003-2009 The Music Player Daemon Project
 * 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 18 19 20 21
 *
 * 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.
 */

/*
 * Common data structures and functions used by FLAC and OggFLAC
22 23 24 25
 */

#include "_flac_common.h"

26 27
#include <glib.h>

28 29
#include <assert.h>

Max Kellermann's avatar
Max Kellermann committed
30 31 32
void
flac_data_init(struct flac_data *data, struct decoder * decoder,
	       struct input_stream *input_stream)
33 34 35
{
	data->time = 0;
	data->position = 0;
Max Kellermann's avatar
Max Kellermann committed
36
	data->bit_rate = 0;
Max Kellermann's avatar
Max Kellermann committed
37
	data->decoder = decoder;
Max Kellermann's avatar
Max Kellermann committed
38 39
	data->input_stream = input_stream;
	data->replay_gain_info = NULL;
40 41 42
	data->tag = NULL;
}

43 44 45 46 47 48 49 50 51 52
void
flac_data_deinit(struct flac_data *data)
{
	if (data->replay_gain_info != NULL)
		replay_gain_info_free(data->replay_gain_info);

	if (data->tag != NULL)
		tag_free(data->tag);
}

53
static void
Max Kellermann's avatar
Max Kellermann committed
54
flac_find_float_comment(const FLAC__StreamMetadata *block,
55
			const char *cmnt, float *fl, bool *found_r)
56
{
57 58 59 60
	int offset;
	size_t pos;
	int len;
	unsigned char tmp, *p;
61

62 63 64
	offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
								     cmnt);
	if (offset < 0)
65
		return;
66

67 68 69
	pos = strlen(cmnt) + 1; /* 1 is for '=' */
	len = block->data.vorbis_comment.comments[offset].length - pos;
	if (len <= 0)
70
		return;
71

72 73 74 75 76 77
	p = &block->data.vorbis_comment.comments[offset].entry[pos];
	tmp = p[len];
	p[len] = '\0';
	*fl = (float)atof((char *)p);
	p[len] = tmp;

78
	*found_r = true;
79 80
}

Max Kellermann's avatar
Max Kellermann committed
81 82 83
static void
flac_parse_replay_gain(const FLAC__StreamMetadata *block,
		       struct flac_data *data)
Avuton Olrich's avatar
Avuton Olrich committed
84
{
85
	bool found = false;
86

Max Kellermann's avatar
Max Kellermann committed
87 88
	if (data->replay_gain_info)
		replay_gain_info_free(data->replay_gain_info);
89

Max Kellermann's avatar
Max Kellermann committed
90
	data->replay_gain_info = replay_gain_info_new();
91

92 93 94 95 96 97 98 99 100 101 102 103
	flac_find_float_comment(block, "replaygain_album_gain",
				&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain,
				&found);
	flac_find_float_comment(block, "replaygain_album_peak",
				&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak,
				&found);
	flac_find_float_comment(block, "replaygain_track_gain",
				&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain,
				&found);
	flac_find_float_comment(block, "replaygain_track_peak",
				&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak,
				&found);
104 105

	if (!found) {
Max Kellermann's avatar
Max Kellermann committed
106 107
		replay_gain_info_free(data->replay_gain_info);
		data->replay_gain_info = NULL;
108 109 110
	}
}

111 112 113 114 115 116
/**
 * Checks if the specified name matches the entry's name, and if yes,
 * returns the comment value (not null-temrinated).
 */
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
117
		   const char *name, const char *char_tnum, size_t *length_r)
118 119
{
	size_t name_length = strlen(name);
120
	size_t char_tnum_length = 0;
121 122
	const char *comment = (const char*)entry->entry;

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	if (entry->length <= name_length ||
	    g_ascii_strncasecmp(comment, name, name_length) != 0)
		return NULL;

	if (char_tnum != NULL) {
		char_tnum_length = strlen(char_tnum);
		if (entry->length > name_length + char_tnum_length + 2 &&
		    comment[name_length] == '[' &&
		    g_ascii_strncasecmp(comment + name_length + 1,
					char_tnum, char_tnum_length) == 0 &&
		    comment[name_length + char_tnum_length + 1] == ']')
			name_length = name_length + char_tnum_length + 2;
		else if (entry->length > name_length + char_tnum_length &&
			 g_ascii_strncasecmp(comment + name_length,
					     char_tnum, char_tnum_length) == 0)
			name_length = name_length + char_tnum_length;
	}

	if (comment[name_length] == '=') {
		*length_r = entry->length - name_length - 1;
		return comment + name_length + 1;
144 145 146 147 148
	}

	return NULL;
}

149 150 151 152
/**
 * Check if the comment's name equals the passed name, and if so, copy
 * the comment value into the tag.
 */
153
static bool
154 155
flac_copy_comment(struct tag *tag,
		  const FLAC__StreamMetadata_VorbisComment_Entry *entry,
156 157
		  const char *name, enum tag_type tag_type,
		  const char *char_tnum)
158
{
159 160
	const char *value;
	size_t value_length;
Eric Wong's avatar
Eric Wong committed
161

162
	value = flac_comment_value(entry, name, char_tnum, &value_length);
163
	if (value != NULL) {
164
		tag_add_item_n(tag, tag_type, value, value_length);
165
		return true;
166
	}
Avuton Olrich's avatar
Avuton Olrich committed
167

168
	return false;
169
}
Avuton Olrich's avatar
Avuton Olrich committed
170

171 172 173 174 175 176
/* tracknumber is used in VCs, MPD uses "track" ..., all the other
 * tag names match */
static const char *VORBIS_COMMENT_TRACK_KEY = "tracknumber";
static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";

static void
177
flac_parse_comment(struct tag *tag, const char *char_tnum,
178 179 180 181 182
		   const FLAC__StreamMetadata_VorbisComment_Entry *entry)
{
	assert(tag != NULL);

	if (flac_copy_comment(tag, entry, VORBIS_COMMENT_TRACK_KEY,
183
			      TAG_TRACK, char_tnum) ||
184
	    flac_copy_comment(tag, entry, VORBIS_COMMENT_DISC_KEY,
185
			      TAG_DISC, char_tnum) ||
186
	    flac_copy_comment(tag, entry, "album artist",
187
			      TAG_ALBUM_ARTIST, char_tnum))
188 189 190 191
		return;

	for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
		if (flac_copy_comment(tag, entry,
192
				      tag_item_names[i], i, char_tnum))
193 194 195
			return;
}

196
void
197
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
198
			    const FLAC__StreamMetadata *block)
199
{
200 201
	FLAC__StreamMetadata_VorbisComment_Entry *comments =
		block->data.vorbis_comment.comments;
Avuton Olrich's avatar
Avuton Olrich committed
202

203
	for (unsigned i = block->data.vorbis_comment.num_comments; i > 0; --i)
204
		flac_parse_comment(tag, char_tnum, comments++);
205 206
}

Avuton Olrich's avatar
Avuton Olrich committed
207
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
Max Kellermann's avatar
Max Kellermann committed
208
			     struct flac_data *data)
209 210 211
{
	const FLAC__StreamMetadata_StreamInfo *si = &(block->data.stream_info);

Avuton Olrich's avatar
Avuton Olrich committed
212
	switch (block->type) {
213
	case FLAC__METADATA_TYPE_STREAMINFO:
214 215
		audio_format_init(&data->audio_format, si->sample_rate,
				  si->bits_per_sample, si->channels);
216
		data->total_time = ((float)si->total_samples) / (si->sample_rate);
217 218
		break;
	case FLAC__METADATA_TYPE_VORBIS_COMMENT:
Max Kellermann's avatar
Max Kellermann committed
219
		flac_parse_replay_gain(block, data);
Max Kellermann's avatar
Max Kellermann committed
220 221

		if (data->tag != NULL)
222
			flac_vorbis_comments_to_tag(data->tag, NULL, block);
Max Kellermann's avatar
Max Kellermann committed
223

Avuton Olrich's avatar
Avuton Olrich committed
224 225
	default:
		break;
226 227 228
	}
}

Avuton Olrich's avatar
Avuton Olrich committed
229 230
void flac_error_common_cb(const char *plugin,
			  const FLAC__StreamDecoderErrorStatus status,
Max Kellermann's avatar
Max Kellermann committed
231
			  struct flac_data *data)
232
{
233
	if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP)
Avuton Olrich's avatar
Avuton Olrich committed
234
		return;
235

Avuton Olrich's avatar
Avuton Olrich committed
236
	switch (status) {
237
	case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
238
		g_warning("%s lost sync\n", plugin);
239 240
		break;
	case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
241
		g_warning("bad %s header\n", plugin);
242 243
		break;
	case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
244
		g_warning("%s crc mismatch\n", plugin);
245 246
		break;
	default:
247
		g_warning("unknown %s error\n", plugin);
248 249 250
	}
}

251
static void flac_convert_stereo16(int16_t *dest,
252 253 254 255
				  const FLAC__int32 * const buf[],
				  unsigned int position, unsigned int end)
{
	for (; position < end; ++position) {
256 257
		*dest++ = buf[0][position];
		*dest++ = buf[1][position];
258 259 260
	}
}

261 262 263 264 265 266 267 268 269 270 271 272 273
static void
flac_convert_16(int16_t *dest,
		unsigned int num_channels,
		const FLAC__int32 * const buf[],
		unsigned int position, unsigned int end)
{
	unsigned int c_chan;

	for (; position < end; ++position)
		for (c_chan = 0; c_chan < num_channels; c_chan++)
			*dest++ = buf[c_chan][position];
}

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
/**
 * Note: this function also handles 24 bit files!
 */
static void
flac_convert_32(int32_t *dest,
		unsigned int num_channels,
		const FLAC__int32 * const buf[],
		unsigned int position, unsigned int end)
{
	unsigned int c_chan;

	for (; position < end; ++position)
		for (c_chan = 0; c_chan < num_channels; c_chan++)
			*dest++ = buf[c_chan][position];
}

static void
flac_convert_8(int8_t *dest,
	       unsigned int num_channels,
	       const FLAC__int32 * const buf[],
	       unsigned int position, unsigned int end)
{
	unsigned int c_chan;

	for (; position < end; ++position)
		for (c_chan = 0; c_chan < num_channels; c_chan++)
			*dest++ = buf[c_chan][position];
}

303 304 305 306 307
static void
flac_convert(void *dest,
	     unsigned int num_channels, unsigned sample_format,
	     const FLAC__int32 *const buf[],
	     unsigned int position, unsigned int end)
308
{
309 310
	switch (sample_format) {
	case 16:
311 312 313 314 315 316 317
		if (num_channels == 2)
			flac_convert_stereo16((int16_t*)dest, buf,
					      position, end);
		else
			flac_convert_16((int16_t*)dest, num_channels, buf,
					position, end);
		break;
318

319 320
	case 24:
	case 32:
321 322 323 324
		flac_convert_32((int32_t*)dest, num_channels, buf,
				position, end);
		break;

325
	case 8:
326 327 328
		flac_convert_8((int8_t*)dest, num_channels, buf,
			       position, end);
		break;
329 330 331 332
	}
}

FLAC__StreamDecoderWriteStatus
Max Kellermann's avatar
Max Kellermann committed
333
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
334 335 336
		  const FLAC__int32 *const buf[])
{
	unsigned int c_samp;
337
	const unsigned sample_format = data->audio_format.bits;
338 339 340 341 342 343 344
	const unsigned int num_channels = frame->header.channels;
	const unsigned int bytes_per_sample =
		audio_format_sample_size(&data->audio_format);
	const unsigned int bytes_per_channel =
		bytes_per_sample * frame->header.channels;
	const unsigned int max_samples = FLAC_CHUNK_SIZE / bytes_per_channel;
	unsigned int num_samples;
345
	enum decoder_command cmd;
346 347 348 349 350 351 352

	for (c_samp = 0; c_samp < frame->header.blocksize;
	     c_samp += num_samples) {
		num_samples = frame->header.blocksize - c_samp;
		if (num_samples > max_samples)
			num_samples = max_samples;

353
		flac_convert(data->chunk,
354
			     num_channels, sample_format, buf,
355 356
			     c_samp, c_samp + num_samples);

Max Kellermann's avatar
Max Kellermann committed
357
		cmd = decoder_data(data->decoder, data->input_stream,
358
				   data->chunk,
359
				   num_samples * bytes_per_channel,
Max Kellermann's avatar
Max Kellermann committed
360 361
				   data->time, data->bit_rate,
				   data->replay_gain_info);
362 363 364 365 366 367
		switch (cmd) {
		case DECODE_COMMAND_NONE:
		case DECODE_COMMAND_START:
			break;

		case DECODE_COMMAND_STOP:
368 369
			return
				FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
370 371

		case DECODE_COMMAND_SEEK:
372 373 374 375 376 377 378
			return
				FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
		}
	}

	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
379 380 381 382 383 384 385

#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7

char*
flac_cue_track(	const char* pathname,
		const unsigned int tnum)
{
386 387
	FLAC__bool success;
	FLAC__StreamMetadata* cs;
388

389 390
	success = FLAC__metadata_get_cuesheet(pathname, &cs);
	if (!success)
391 392
		return NULL;

393 394
	assert(cs != NULL);

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
	if (cs->data.cue_sheet.num_tracks <= 1)
	{
		FLAC__metadata_object_delete(cs);
		return NULL;
	}

	if (tnum > 0 && tnum < cs->data.cue_sheet.num_tracks)
	{
		char* track = g_strdup_printf("track_%03u.flac", tnum);

		FLAC__metadata_object_delete(cs);

		return track;
	}
	else
	{
		FLAC__metadata_object_delete(cs);
		return NULL;
	}
}

unsigned int
flac_vtrack_tnum(const char* fname)
{
	/* find last occurrence of '_' in fname
	 * which is hopefully something like track_xxx.flac
	 * another/better way would be to use tag struct
	 */
	char* ptr = strrchr(fname, '_');

	// copy ascii tracknumber to int
	char vtrack[4];
	g_strlcpy(vtrack, ++ptr, 4);
	return (unsigned int)strtol(vtrack, NULL, 10);
}

#endif /* FLAC_API_VERSION_CURRENT >= 7 */