<commit_before>/*  XMMS2 - X Music Multiplexer System
 *  Copyright (C) 2003	<NAME>, <NAME>, <NAME>
 * 
 *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
 */




/** @file 
 * MPEG Layer 1/2/3 decoder plugin.
 *
 * Supports Xing VBR and id3v1/v2
 *
 * This code basicly sucks at some places. But hey, the
 * standard is fucked. Please convert to Ogg ;-)
 */


#include "xmms/xmms_defs.h"
#include "xmms/xmms_decoderplugin.h"
#include "xmms/xmms_log.h"
#include "id3.h"
#include "xing.h"
#include <mad.h>

#include <glib.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>

/*
 * Type definitions
 */

typedef struct xmms_mad_data_St {
	struct mad_stream stream;
	struct mad_frame frame;
	struct mad_synth synth;

	guchar buffer[4096];
	guint buffer_length;
	guint channels;
	guint bitrate;
	guint samplerate;
	guint64 fsize;
	
	xmms_xing_t *xing;
} xmms_mad_data_t;


/*
 * Function prototypes
 */

static gboolean xmms_mad_new (xmms_decoder_t *decoder);
static gboolean xmms_mad_decode_block (xmms_decoder_t *decoder);
static void xmms_mad_get_media_info (xmms_decoder_t *decoder);
static void xmms_mad_destroy (xmms_decoder_t *decoder);
static gboolean xmms_mad_init (xmms_decoder_t *decoder, gint mode);
static gboolean xmms_mad_seek (xmms_decoder_t *decoder, guint samples);

/*
 * Plugin header
 */

xmms_plugin_t *
xmms_plugin_get (void)
{
	xmms_plugin_t *plugin;

	plugin = xmms_plugin_new (XMMS_PLUGIN_TYPE_DECODER, 
	                          XMMS_DECODER_PLUGIN_API_VERSION,
	                          "mad",
	                          "MAD decoder " XMMS_VERSION,
	                          "MPEG Layer 1/2/3 decoder");
	
	if (!plugin) {
		return NULL;
	}

	xmms_plugin_info_add (plugin, "URL", "http://www.xmms.org/");
	xmms_plugin_info_add (plugin, "Author", "XMMS Team");
	xmms_plugin_info_add (plugin, "License", "GPL");

	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_NEW, xmms_mad_new);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_DECODE_BLOCK, xmms_mad_decode_block);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_DESTROY, xmms_mad_destroy);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_GET_MEDIAINFO, xmms_mad_get_media_info);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_INIT, xmms_mad_init);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_SEEK, xmms_mad_seek);

	xmms_plugin_properties_add (plugin, XMMS_PLUGIN_PROPERTY_FAST_FWD);
	xmms_plugin_properties_add (plugin, XMMS_PLUGIN_PROPERTY_REWIND);

	xmms_plugin_magic_add (plugin, "id3 header", "audio/mpeg",
	                       "0 string ID3", ">3 byte <0xff",
	                       ">>4 byte <0xff", NULL);
	xmms_plugin_magic_add (plugin, "mpeg header", "audio/mpeg",
	                       "0 beshort&0xfff6 0xfff6",
	                       "0 beshort&0xfff6 0xfff4",
	                       "0 beshort&0xffe6 0xffe2",
	                       NULL);

	return plugin;
}

static void
xmms_mad_destroy (xmms_decoder_t *decoder)
{
	xmms_mad_data_t *data;

	g_return_if_fail (decoder);

	data = xmms_decoder_private_data_get (decoder);
	g_return_if_fail (data);

	mad_stream_finish (&data->stream);
	mad_frame_finish (&data->frame);
	mad_synth_finish (&data->synth);

	g_free (data);

}

static gboolean
xmms_mad_seek (xmms_decoder_t *decoder, guint samples)
{
	xmms_mad_data_t *data;
	guint bytes;
	
	g_return_val_if_fail (decoder, FALSE);

	data = xmms_decoder_private_data_get (decoder);

	if (data->xing) {
		guint i;
		guint x_samples;

		x_samples = xmms_xing_get_frames (data->xing) * 1152;

		i = (guint) (100.0 * (gdouble) samples) / (gdouble) x_samples;

		bytes = xmms_xing_get_toc (data->xing, i) * xmms_xing_get_bytes (data->xing) / 256;
	} else {
		bytes = (guint)(((gdouble)samples) * data->bitrate / data->samplerate) / 8;
	}

	XMMS_DBG ("Try seek %d bytes", bytes);

	if (bytes > data->fsize) {
		xmms_log_error ("To big value %llu is filesize", data->fsize);
		return FALSE;
	}

	xmms_transport_seek (xmms_decoder_transport_get (decoder), bytes, 
			XMMS_TRANSPORT_SEEK_SET);

	return TRUE;
}

/** This function will calculate the duration in seconds.
  *
  * This is very easy, until someone thougth that VBR was
  * a good thing. That is a quite fucked up standard.
  *
  * We read the XING header and parse it accordingly to
  * xing.c, then calculate the duration of the next frame
  * and multiply it with the number of frames in the XING
  * header.
  *
  * Better than to stream the whole file I guess.
  */

static void
xmms_mad_calc_duration (xmms_medialib_session_t *session,
                        xmms_decoder_t *decoder,
                        guchar *buf, gint len,
                        gint filesize,
                        xmms_medialib_entry_t entry)
{
	struct mad_frame frame;
	struct mad_stream stream;
	xmms_mad_data_t *data;
	guint bitrate=0;

	data = xmms_decoder_private_data_get (decoder);

	mad_stream_init (&stream);
	mad_frame_init (&frame);

	mad_stream_buffer (&stream, buf, len);

	while (mad_frame_decode (&frame, &stream) == -1) {
		if (!MAD_RECOVERABLE (stream.error)) {
			XMMS_DBG ("couldn't decode %02x %02x %02x %02x",buf[0],buf[1],buf[2],buf[3]);
			return;
		}
	}

	data->samplerate = frame.header.samplerate;
	data->channels = MAD_NCHANNELS (&frame.header);

	if (filesize == -1) {
		xmms_medialib_entry_property_set_int (session,
		                                      entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
		                                      -1);

		/* frame.header.bitrate might be wrong, but we cannot do it any
		 * better for streams
		 */
		xmms_medialib_entry_property_set_int (session, entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE,
		                                      frame.header.bitrate);
		return;
	}

	data->fsize = filesize;

	data->xing = xmms_xing_parse (stream.anc_ptr);
	if (data->xing) {
		/* @todo Hmm? This is SO strange. */
		while (42) {
			if (mad_frame_decode (&frame, &stream) == -1) {
				if (MAD_RECOVERABLE (stream.error))
					continue;
				break;
			}
		}

		xmms_medialib_entry_property_set_int (session,
		                                      entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_IS_VBR,
		                                      1);

		if (xmms_xing_has_flag (data->xing, XMMS_XING_FRAMES)) {
			guint duration;
			mad_timer_t timer;

			timer = frame.header.duration;
			mad_timer_multiply (&timer, xmms_xing_get_frames (data->xing));
			duration = mad_timer_count (timer, MAD_UNITS_MILLISECONDS);

			XMMS_DBG ("XING duration %d", duration);

			xmms_medialib_entry_property_set_int (session, entry,
			                                      XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
			                                      duration);

			if (xmms_xing_has_flag (data->xing, XMMS_XING_BYTES)) {
				guint tmp;

				tmp = xmms_xing_get_bytes (data->xing) * 8 / duration;
				XMMS_DBG ("XING bitrate %d", tmp);
				xmms_medialib_entry_property_set_int (session,
				                                      entry,
				                                      XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE,
				                                      tmp);
			}
		}

		return;
	}

	data->bitrate = bitrate = frame.header.bitrate;

	mad_frame_finish (&frame);
	mad_stream_finish (&stream);

	if (filesize == -1) {
		xmms_medialib_entry_property_set_int (session, entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
		                                      -1);<commit_msg>OTHER: Report VBR bitrate in bits not kbits<commit_after>/*  XMMS2 - X Music Multiplexer System
 *  Copyright (C) 2003	<NAME>, <NAME>, <NAME>
 * 
 *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
 */




/** @file 
 * MPEG Layer 1/2/3 decoder plugin.
 *
 * Supports Xing VBR and id3v1/v2
 *
 * This code basicly sucks at some places. But hey, the
 * standard is fucked. Please convert to Ogg ;-)
 */


#include "xmms/xmms_defs.h"
#include "xmms/xmms_decoderplugin.h"
#include "xmms/xmms_log.h"
#include "id3.h"
#include "xing.h"
#include <mad.h>

#include <glib.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>

/*
 * Type definitions
 */

typedef struct xmms_mad_data_St {
	struct mad_stream stream;
	struct mad_frame frame;
	struct mad_synth synth;

	guchar buffer[4096];
	guint buffer_length;
	guint channels;
	guint bitrate;
	guint samplerate;
	guint64 fsize;
	
	xmms_xing_t *xing;
} xmms_mad_data_t;


/*
 * Function prototypes
 */

static gboolean xmms_mad_new (xmms_decoder_t *decoder);
static gboolean xmms_mad_decode_block (xmms_decoder_t *decoder);
static void xmms_mad_get_media_info (xmms_decoder_t *decoder);
static void xmms_mad_destroy (xmms_decoder_t *decoder);
static gboolean xmms_mad_init (xmms_decoder_t *decoder, gint mode);
static gboolean xmms_mad_seek (xmms_decoder_t *decoder, guint samples);

/*
 * Plugin header
 */

xmms_plugin_t *
xmms_plugin_get (void)
{
	xmms_plugin_t *plugin;

	plugin = xmms_plugin_new (XMMS_PLUGIN_TYPE_DECODER, 
	                          XMMS_DECODER_PLUGIN_API_VERSION,
	                          "mad",
	                          "MAD decoder " XMMS_VERSION,
	                          "MPEG Layer 1/2/3 decoder");
	
	if (!plugin) {
		return NULL;
	}

	xmms_plugin_info_add (plugin, "URL", "http://www.xmms.org/");
	xmms_plugin_info_add (plugin, "Author", "XMMS Team");
	xmms_plugin_info_add (plugin, "License", "GPL");

	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_NEW, xmms_mad_new);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_DECODE_BLOCK, xmms_mad_decode_block);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_DESTROY, xmms_mad_destroy);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_GET_MEDIAINFO, xmms_mad_get_media_info);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_INIT, xmms_mad_init);
	xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_SEEK, xmms_mad_seek);

	xmms_plugin_properties_add (plugin, XMMS_PLUGIN_PROPERTY_FAST_FWD);
	xmms_plugin_properties_add (plugin, XMMS_PLUGIN_PROPERTY_REWIND);

	xmms_plugin_magic_add (plugin, "id3 header", "audio/mpeg",
	                       "0 string ID3", ">3 byte <0xff",
	                       ">>4 byte <0xff", NULL);
	xmms_plugin_magic_add (plugin, "mpeg header", "audio/mpeg",
	                       "0 beshort&0xfff6 0xfff6",
	                       "0 beshort&0xfff6 0xfff4",
	                       "0 beshort&0xffe6 0xffe2",
	                       NULL);

	return plugin;
}

static void
xmms_mad_destroy (xmms_decoder_t *decoder)
{
	xmms_mad_data_t *data;

	g_return_if_fail (decoder);

	data = xmms_decoder_private_data_get (decoder);
	g_return_if_fail (data);

	mad_stream_finish (&data->stream);
	mad_frame_finish (&data->frame);
	mad_synth_finish (&data->synth);

	g_free (data);

}

static gboolean
xmms_mad_seek (xmms_decoder_t *decoder, guint samples)
{
	xmms_mad_data_t *data;
	guint bytes;
	
	g_return_val_if_fail (decoder, FALSE);

	data = xmms_decoder_private_data_get (decoder);

	if (data->xing) {
		guint i;
		guint x_samples;

		x_samples = xmms_xing_get_frames (data->xing) * 1152;

		i = (guint) (100.0 * (gdouble) samples) / (gdouble) x_samples;

		bytes = xmms_xing_get_toc (data->xing, i) * xmms_xing_get_bytes (data->xing) / 256;
	} else {
		bytes = (guint)(((gdouble)samples) * data->bitrate / data->samplerate) / 8;
	}

	XMMS_DBG ("Try seek %d bytes", bytes);

	if (bytes > data->fsize) {
		xmms_log_error ("To big value %llu is filesize", data->fsize);
		return FALSE;
	}

	xmms_transport_seek (xmms_decoder_transport_get (decoder), bytes, 
			XMMS_TRANSPORT_SEEK_SET);

	return TRUE;
}

/** This function will calculate the duration in seconds.
  *
  * This is very easy, until someone thougth that VBR was
  * a good thing. That is a quite fucked up standard.
  *
  * We read the XING header and parse it accordingly to
  * xing.c, then calculate the duration of the next frame
  * and multiply it with the number of frames in the XING
  * header.
  *
  * Better than to stream the whole file I guess.
  */

static void
xmms_mad_calc_duration (xmms_medialib_session_t *session,
                        xmms_decoder_t *decoder,
                        guchar *buf, gint len,
                        gint filesize,
                        xmms_medialib_entry_t entry)
{
	struct mad_frame frame;
	struct mad_stream stream;
	xmms_mad_data_t *data;
	guint bitrate=0;

	data = xmms_decoder_private_data_get (decoder);

	mad_stream_init (&stream);
	mad_frame_init (&frame);

	mad_stream_buffer (&stream, buf, len);

	while (mad_frame_decode (&frame, &stream) == -1) {
		if (!MAD_RECOVERABLE (stream.error)) {
			XMMS_DBG ("couldn't decode %02x %02x %02x %02x",buf[0],buf[1],buf[2],buf[3]);
			return;
		}
	}

	data->samplerate = frame.header.samplerate;
	data->channels = MAD_NCHANNELS (&frame.header);

	if (filesize == -1) {
		xmms_medialib_entry_property_set_int (session,
		                                      entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
		                                      -1);

		/* frame.header.bitrate might be wrong, but we cannot do it any
		 * better for streams
		 */
		xmms_medialib_entry_property_set_int (session, entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE,
		                                      frame.header.bitrate);
		return;
	}

	data->fsize = filesize;

	data->xing = xmms_xing_parse (stream.anc_ptr);
	if (data->xing) {
		/* @todo Hmm? This is SO strange. */
		while (42) {
			if (mad_frame_decode (&frame, &stream) == -1) {
				if (MAD_RECOVERABLE (stream.error))
					continue;
				break;
			}
		}

		xmms_medialib_entry_property_set_int (session,
		                                      entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_IS_VBR,
		                                      1);

		if (xmms_xing_has_flag (data->xing, XMMS_XING_FRAMES)) {
			guint duration;
			mad_timer_t timer;

			timer = frame.header.duration;
			mad_timer_multiply (&timer, xmms_xing_get_frames (data->xing));
			duration = mad_timer_count (timer, MAD_UNITS_MILLISECONDS);

			XMMS_DBG ("XING duration %d", duration);

			xmms_medialib_entry_property_set_int (session, entry,
			                                      XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
			                                      duration);

			if (xmms_xing_has_flag (data->xing, XMMS_XING_BYTES)) {
				guint tmp;

				tmp = xmms_xing_get_bytes (data->xing) * 8 / duration * 1000;
				XMMS_DBG ("XING bitrate %d", tmp);
				xmms_medialib_entry_property_set_int (session,
				                                      entry,
				                                      XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE,
				                                      tmp);
			}
		}

		return;
	}

	data->bitrate = bitrate = frame.header.bitrate;

	mad_frame_finish (&frame);
	mad_stream_finish (&stream);

	if (filesize == -1) {
		xmms_medialib_entry_property_set_int (session, entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION,
		                                      -1);
{'fraction_non_alphanumeric': 0.09866121479918222, 'fraction_numerical': 0.010552001582800237, 'mean_word_length': 2.7003416300634457, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2092563', 'n_tokens_mistral': 6091, 'n_tokens_neox': 5499, 'n_words': 1159}