Mercurial > audlegacy-plugins
changeset 610:862190d39e00 trunk
[svn] - add madplug. It is not yet hooked up, I'll do that later.
author | nenolod |
---|---|
date | Mon, 05 Feb 2007 12:28:01 -0800 |
parents | 9b73eb35f4ff |
children | 3f7a52adfe0e |
files | ChangeLog src/madplug/Makefile src/madplug/configure.c src/madplug/decoder.c src/madplug/dither.c src/madplug/fileinfo.c src/madplug/input.c src/madplug/input.h src/madplug/mp3.xpm src/madplug/plugin.c src/madplug/plugin.h src/madplug/replaygain.c src/madplug/replaygain.h src/madplug/xing.c src/madplug/xing.h |
diffstat | 15 files changed, 4261 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Sun Feb 04 16:43:27 2007 -0800 +++ b/ChangeLog Mon Feb 05 12:28:01 2007 -0800 @@ -1,3 +1,10 @@ +2007-02-05 00:43:27 +0000 Giacomo Lozito <james@develia.org> + revision [1304] + - aosd: avoid collision between playback start trigger and titlechange trigger, that occurred when the next file in playlist was played + trunk/src/aosd/aosd_trigger.c | 4 ++++ + 1 file changed, 4 insertions(+) + + 2007-02-04 21:40:53 +0000 Tony Vroon <chainsaw@gentoo.org> revision [1302] Obliterate empty tags, just like in mpg123.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/Makefile Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,25 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +OBJECTIVE_LIBS = libmadplug$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR) + +SOURCES = \ + configure.c \ + dither.c \ + input.c \ + replaygain.c \ + decoder.c \ + fileinfo.c \ + plugin.c \ + xing.c + +OBJECTS = ${SOURCES:.c=.o} + +CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(TAGLIB_CFLAGS) $(ARCH_DEFINES) $(LIBNMS_CFLAGS) -I../../intl -I../.. +CXXFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(TAGLIB_CFLAGS) $(ARCH_DEFINES) -I../../intl -I../.. + +LIBADD = -lmad -lid3tag $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(TAGLIB_LIBS) $(LIBNMS_LIBS) + +include ../../mk/objective.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/configure.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,251 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "plugin.h" + +#include <gtk/gtk.h> +#include <math.h> +#include <audacious/configdb.h> + +static GtkWidget *configure_win = NULL; +static GtkWidget *vbox; +static GtkWidget *fast_playback, *use_xing, *dither; +static GtkWidget *RG_enable, *RG_track_mode, *RG_default, *pregain, + *hard_limit; +static GtkWidget *title_id3_box, *title_tag_desc; +static GtkWidget *title_override, *title_id3_entry; + +static void configure_win_ok(GtkWidget * widget, gpointer data) +{ + ConfigDb *db; + const gchar *text = NULL; +#ifdef DEBUG + g_message("saving\n"); +#endif + + audmad_config.fast_play_time_calc = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fast_playback)); + audmad_config.use_xing = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_xing)); + audmad_config.dither = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dither)); + + audmad_config.replaygain.enable = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_enable)); + audmad_config.replaygain.track_mode = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_track_mode)); + audmad_config.hard_limit = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hard_limit)); + text = gtk_entry_get_text(GTK_ENTRY(RG_default)); + audmad_config.replaygain.default_db = g_strdup(text); + + text = gtk_entry_get_text(GTK_ENTRY(pregain)); + audmad_config.pregain_db = g_strdup(text); + + audmad_config_compute(&audmad_config); + + db = bmp_cfg_db_open(); + bmp_cfg_db_set_int(db, "MAD", "http_buffer_size", + audmad_config.http_buffer_size); + bmp_cfg_db_set_bool(db, "MAD", "fast_play_time_calc", + audmad_config.fast_play_time_calc); + bmp_cfg_db_set_bool(db, "MAD", "use_xing", audmad_config.use_xing); + bmp_cfg_db_set_bool(db, "MAD", "dither", audmad_config.dither); + bmp_cfg_db_set_bool(db, "MAD", "hard_limit", + audmad_config.hard_limit); + bmp_cfg_db_set_string(db, "MAD", "pregain_db", + audmad_config.pregain_db); + + bmp_cfg_db_set_bool(db, "MAD", "RG.enable", + audmad_config.replaygain.enable); + bmp_cfg_db_set_bool(db, "MAD", "RG.track_mode", + audmad_config.replaygain.track_mode); + bmp_cfg_db_set_string(db, "MAD", "RG.default_db", + audmad_config.replaygain.default_db); + + bmp_cfg_db_set_bool(db, "MAD", "title_override", audmad_config.title_override); + bmp_cfg_db_set_string(db, "MAD", "id3_format", audmad_config.id3_format); + + bmp_cfg_db_close(db); + gtk_widget_destroy(configure_win); +} + +static void configure_destroy(GtkWidget * w, gpointer data) +{ +} + +static void +title_override_cb(GtkWidget * w, gpointer data) +{ + gboolean override; + override = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_override)); + gtk_widget_set_sensitive(title_id3_box, override); + gtk_widget_set_sensitive(title_tag_desc, override); +} + +void audmad_configure(void) +{ + GtkWidget *bbox, *ok, *cancel; + GtkWidget *label, *RG_default_hbox, *pregain_hbox; + GtkWidget *notebook, *vbox2, *title_id3_label; + + if (configure_win != NULL) + { + gtk_widget_show(configure_win); + return; + } + + configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint(GTK_WINDOW(configure_win), GDK_WINDOW_TYPE_HINT_DIALOG); + + g_signal_connect(G_OBJECT(configure_win), "destroy", + G_CALLBACK(gtk_widget_destroyed), + &configure_win); + g_signal_connect(G_OBJECT(configure_win), "destroy", + G_CALLBACK(configure_destroy), &configure_win); + + gtk_window_set_title(GTK_WINDOW(configure_win), + _("MPEG Audio Plugin Configuration")); + gtk_window_set_policy(GTK_WINDOW(configure_win), FALSE, FALSE, FALSE); + gtk_container_border_width(GTK_CONTAINER(configure_win), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(configure_win), vbox); + + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + + vbox2 = gtk_vbox_new(FALSE, 5); + + fast_playback = + gtk_check_button_new_with_label(_("Enable fast play-length calculation")); + gtk_box_pack_start(GTK_BOX(vbox2), fast_playback, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_playback), + audmad_config.fast_play_time_calc); + + use_xing = gtk_check_button_new_with_label(_("Parse XING headers")); + gtk_box_pack_start(GTK_BOX(vbox2), use_xing, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(use_xing), + audmad_config.use_xing); + + dither = + gtk_check_button_new_with_label + (_("Dither output when rounding to 16-bit")); + gtk_box_pack_start(GTK_BOX(vbox2), dither, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dither), + audmad_config.dither); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(_("General"))); + + vbox2 = gtk_vbox_new(FALSE, 5); + + /* SKR added config : */ + RG_enable = gtk_check_button_new_with_label(_("Enable ReplayGain processing")); + gtk_box_pack_start(GTK_BOX(vbox2), RG_enable, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_enable), + audmad_config.replaygain.enable); + RG_track_mode = + gtk_check_button_new_with_label(_("Track mode")); + gtk_box_pack_start(GTK_BOX(vbox2), RG_track_mode, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_track_mode), + audmad_config.replaygain.track_mode); + + hard_limit = + gtk_check_button_new_with_label + (_("6dB hard limiting")); + gtk_box_pack_start(GTK_BOX(vbox2), hard_limit, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hard_limit), + audmad_config.hard_limit); + + label = gtk_label_new(_("Default gain (dB):")); + RG_default_hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox2), RG_default_hbox, TRUE, TRUE, 0); + RG_default = gtk_entry_new(); + gtk_widget_set_usize(RG_default, 80, -1); + gtk_entry_set_text(GTK_ENTRY(RG_default), + audmad_config.replaygain.default_db); + gtk_box_pack_start(GTK_BOX(RG_default_hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(RG_default_hbox), RG_default, FALSE, TRUE, + 0); + + label = gtk_label_new(_("Preamp (dB):")); + pregain_hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox2), pregain_hbox, TRUE, TRUE, 0); + pregain = gtk_entry_new(); + gtk_widget_set_usize(pregain, 80, -1); + gtk_entry_set_text(GTK_ENTRY(pregain), audmad_config.pregain_db); + gtk_box_pack_start(GTK_BOX(pregain_hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(pregain_hbox), pregain, FALSE, TRUE, 0); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new("ReplayGain")); + + vbox2 = gtk_vbox_new(FALSE, 5); + + title_override = + gtk_check_button_new_with_label(_("Override generic titles")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_override), + audmad_config.title_override); + g_signal_connect(G_OBJECT(title_override), "clicked", + G_CALLBACK(title_override_cb), NULL); + gtk_box_pack_start(GTK_BOX(vbox2), title_override, FALSE, + FALSE, 0); + + title_id3_box = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(title_id3_box, audmad_config.title_override); + gtk_box_pack_start(GTK_BOX(vbox2), title_id3_box, FALSE, + FALSE, 0); + + title_id3_label = gtk_label_new(_("ID3 format:")); + gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_label, FALSE, + FALSE, 0); + + title_id3_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(title_id3_entry), audmad_config.id3_format); + gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_entry, TRUE, TRUE, + 0); + + title_tag_desc = xmms_titlestring_descriptions("pafFetnygc", 2); + gtk_widget_set_sensitive(title_tag_desc, audmad_config.title_override); + gtk_box_pack_start(GTK_BOX(vbox2), title_tag_desc, FALSE, + FALSE, 0); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, + gtk_label_new(_("Title"))); + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + g_signal_connect_swapped(G_OBJECT(cancel), "clicked", + G_CALLBACK(gtk_widget_destroy), + G_OBJECT(configure_win)); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + ok = gtk_button_new_from_stock(GTK_STOCK_OK); + g_signal_connect(G_OBJECT(ok), "clicked", + G_CALLBACK(configure_win_ok), NULL); + gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0); + gtk_widget_grab_default(ok); + + gtk_widget_show_all(configure_win); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/decoder.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,567 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <math.h> +#include <assert.h> +#include <pthread.h> +#include <signal.h> + +#include <audacious/plugin.h> +#include <audacious/output.h> +#include <audacious/util.h> +#include "plugin.h" +#include "input.h" + +#define BUFFER_SIZE 16*1024 +#define N_AVERAGE_FRAMES 10 + +extern long triangular_dither_noise(int nbits); + +/** + * Scale PCM data + */ +static inline signed int +scale(mad_fixed_t sample, struct mad_info_t *file_info) +{ + /* replayGain by SamKR */ + gdouble scale = -1; + if (audmad_config.replaygain.enable) { + if (file_info->has_replaygain) { + scale = file_info->replaygain_track_scale; + if (file_info->replaygain_album_scale != -1 + && (scale == -1 || !audmad_config.replaygain.track_mode)) + { + scale = file_info->replaygain_album_scale; + } + } + if (scale == -1) + scale = audmad_config.replaygain.default_scale; + } + if (scale == -1) + scale = 1.0; + if (audmad_config.pregain_scale != 1) + scale = scale * audmad_config.pregain_scale; + + /* hard-limit (clipping-prevention) */ + if (audmad_config.hard_limit) { + /* convert to double before computation, to avoid mad_fixed_t wrapping */ + double x = mad_f_todouble(sample) * scale; + static const double k = 0.5; // -6dBFS + if (x > k) { + x = tanh((x - k) / (1 - k)) * (1 - k) + k; + } + else if (x < -k) { + x = tanh((x + k) / (1 - k)) * (1 - k) - k; + } + sample = x * (MAD_F_ONE); + } + else + sample *= scale; + + int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16; + + /* round */ + /* add half of the bits_to_loose range to round */ + sample += (1L << (n_bits_to_loose - 1)); + +#ifdef DEBUG_DITHER + mad_fixed_t no_dither = sample; +#endif + + /* dither one bit of actual output */ + if (audmad_config.dither) { + int dither = triangular_dither_noise(n_bits_to_loose + 1); + sample += dither; + } + + /* clip */ + /* make sure we are between -1 and 1 */ + if (sample >= MAD_F_ONE) { + sample = MAD_F_ONE - 1; + } + else if (sample < -MAD_F_ONE) { + sample = -MAD_F_ONE; + } + + /* quantize */ + /* + * Turn our mad_fixed_t into an integer. + * Shift all but 16-bits of the fractional part + * off the right hand side and shift an extra place + * to get the sign bit. + */ + sample >>= n_bits_to_loose; +#ifdef DEBUG_DITHER + static int n_zeros = 0; + no_dither >>= n_bits_to_loose; + if (no_dither - sample == 0) + n_zeros++; + else { + g_message("dither: %d %d", n_zeros, no_dither - sample); + n_zeros = 0; + } +#endif + return sample; +} + +void +write_output(struct mad_info_t *info, struct mad_pcm *pcm, + struct mad_header *header) +{ + unsigned int nsamples; + mad_fixed_t const *left_ch, *right_ch; + char *output; + int olen = 0; + int pos = 0; + + nsamples = pcm->length; + left_ch = pcm->samples[0]; + right_ch = pcm->samples[1]; + olen = nsamples * MAD_NCHANNELS(header) * 2; + output = (char *) g_malloc(olen * sizeof(char)); + + while (nsamples--) { + signed int sample; + /* output sample(s) in 16-bit signed little-endian PCM */ + sample = scale(*left_ch++, info); + output[pos++] = (sample >> 0) & 0xff; + output[pos++] = (sample >> 8) & 0xff; + + if (MAD_NCHANNELS(header) == 2) { + sample = scale(*right_ch++, info); + output[pos++] = (sample >> 0) & 0xff; + output[pos++] = (sample >> 8) & 0xff; + } + } + assert(pos == olen); + if (info->playback->playing == 0) + return; + produce_audio(mad_plugin->output->written_time(), + FMT_S16_LE, MAD_NCHANNELS(header), olen, output, NULL); + if (info->playback->playing == 0) + return; + g_free(output); +} + +/** + * Decode all headers in the file and fill in stats + * @return FALSE if scan failed. + */ +gboolean scan_file(struct mad_info_t * info, gboolean fast) +{ + struct mad_stream stream; + struct mad_header header; + int remainder = 0; + int data_used = 0; + int len = 0; + int tagsize = 0; + unsigned char buffer[BUFFER_SIZE]; + struct mad_frame frame; /* to read xing data */ + gboolean has_xing = FALSE; + + mad_stream_init(&stream); + mad_header_init(&header); + mad_frame_init(&frame); + xing_init(&info->xing); + + info->bitrate = 0; + info->pos = mad_timer_zero; + +#ifdef DEBUG + g_message("f: scan_file"); +#endif /* DEBUG */ + + while (1) { + remainder = stream.bufend - stream.next_frame; + + /* + if (remainder >= BUFFER_SIZE) + { + printf("oh dear.. remainder = %d\n", remainder); + } + */ + + memcpy(buffer, stream.this_frame, remainder); + len = input_get_data(info, buffer + remainder, + BUFFER_SIZE - remainder); + + if (len <= 0) + break; + + mad_stream_buffer(&stream, buffer, len + remainder); + + while (1) { + if (mad_header_decode(&header, &stream) == -1) { + if (stream.error == MAD_ERROR_BUFLEN) { + break; + } + if (!MAD_RECOVERABLE(stream.error)) { +#ifdef DEBUG + g_message("(fatal) error decoding header %d: %s", + info->frames, mad_stream_errorstr(&stream)); + g_message("remainder = %d", remainder); + g_message("len = %d", len); +#endif /* DEBUG */ + break; + } + if (stream.error == MAD_ERROR_LOSTSYNC) { + /* ignore LOSTSYNC due to ID3 tags */ + tagsize = id3_tag_query(stream.this_frame, + stream.bufend - + stream.this_frame); + if (tagsize > 0) { +#ifdef DEBUG + g_message("skipping id3_tag: %d", tagsize); +#endif /* DEBUG */ + mad_stream_skip(&stream, tagsize); + continue; + } + } +#ifdef DEBUG + g_message("(recovered) error decoding header %d: %s", + info->frames, mad_stream_errorstr(&stream)); + g_message("remainder = %d", remainder); + g_message("len = %d", len); +#endif /* DEBUG */ + continue; + } + info->frames++; +#ifdef DEBUG + g_message("duration = %lu", + mad_timer_count(header.duration, + MAD_UNITS_MILLISECONDS)); + g_message("size = %d", stream.next_frame - stream.this_frame); +#endif + mad_timer_add(&info->duration, header.duration); + data_used += stream.next_frame - stream.this_frame; + if (info->frames == 1) { + /* most of these *should* remain constant */ + info->bitrate = header.bitrate; + info->freq = header.samplerate; + info->channels = MAD_NCHANNELS(&header); + info->mpeg_layer = header.layer; + info->mode = header.mode; + + if (audmad_config.use_xing) { + frame.header = header; + if (mad_frame_decode(&frame, &stream) == -1) + break; + if (xing_parse + (&info->xing, stream.anc_ptr, + stream.anc_bitlen) == 0) { +#ifdef DEBUG + g_message("found xing header"); +#endif /* DEBUG */ + has_xing = TRUE; + info->vbr = TRUE; /* otherwise xing header would have been 'Info' */ + info->frames = info->xing.frames; + mad_timer_multiply(&info->duration, info->frames); + info->bitrate = + 8.0 * info->xing.bytes / + mad_timer_count(info->duration, + MAD_UNITS_SECONDS); + break; + } + } + + } + else { + /* perhaps we have a VRB file */ + if (info->bitrate != header.bitrate) + info->vbr = TRUE; + if (info->vbr) + info->bitrate += header.bitrate; + /* check for changin layer/samplerate/channels */ + if (info->mpeg_layer != header.layer) + g_warning("layer varies!!"); + if (info->freq != header.samplerate) + g_warning("samplerate varies!!"); + if (info->channels != MAD_NCHANNELS(&header)) + g_warning("number of channels varies!!"); + } + + if (fast && info->frames >= N_AVERAGE_FRAMES) { + float frame_size = ((double) data_used) / N_AVERAGE_FRAMES; + info->frames = (info->size - tagsize) / frame_size; + //int frame_frac = (info->size - tagsize) % frame_size; + info->duration.seconds /= N_AVERAGE_FRAMES; + info->duration.fraction /= N_AVERAGE_FRAMES; + mad_timer_multiply(&info->duration, info->frames); +#ifdef DEBUG + g_message("using fast playtime calculation"); + g_message("data used = %d [tagsize=%d framesize=%f]", + data_used, tagsize, frame_size); + //g_message ("frame_size = %f [frac=%d]", frame_size, frame_frac); + g_message("frames = %d, frequecy = %d, channels = %d", + info->frames, info->freq, info->channels); + long millis = mad_timer_count(info->duration, + MAD_UNITS_MILLISECONDS); + g_message("duration = %lu:%lu", millis / 1000 / 60, + (millis / 1000) % 60); +#endif /* DEBUG */ + break; + } + } + if (stream.error != MAD_ERROR_BUFLEN) + break; + } + + if (info->vbr && !has_xing) + info->bitrate = info->bitrate / info->frames; + + mad_frame_finish(&frame); + mad_header_finish(&header); + mad_stream_finish(&stream); + xing_finish(&info->xing); + +#ifdef DEBUG + g_message("e: scan_file"); +#endif /* DEBUG */ + return (info->frames != 0 || info->remote == TRUE); +} + +gpointer decode_loop(gpointer arg) +{ + unsigned char buffer[BUFFER_SIZE]; + int len; + int seek_skip = 0; + int remainder = 0; + gint tlen; + + /* mad structs */ + struct mad_stream stream; + struct mad_frame frame; + struct mad_synth synth; + + /* track info is passed in as thread argument */ + struct mad_info_t *info = (struct mad_info_t *) arg; + +#ifdef DEBUG + g_message("f: decode"); +#endif /* DEBUG */ + + /* init mad stuff */ + mad_frame_init(&frame); + mad_stream_init(&stream); + mad_synth_init(&synth); + + if (!mad_plugin->output-> + open_audio(info->fmt, info->freq, info->channels)) { + audmad_error("failed to open audio output: %s", + mad_plugin->output->description); + g_message("failed to open audio output: %s", + mad_plugin->output->description); + return NULL; + } + + /* set mainwin title */ + if (info->title) + g_free(info->title); + info->title = + xmms_get_titlestring(xmms_get_gentitle_format(), info->tuple); + + tlen = (gint) mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS), + + mad_plugin->set_info(info->title, + tlen == 0 ? -1 : tlen, + info->bitrate, info->freq, info->channels); + + /* main loop */ + do { + if (info->playback->playing == 0) { +#ifdef DEBUG + g_message("decode: stop signaled"); +#endif /* DEBUG */ + break; + } + if (seek_skip) + remainder = 0; + else { + remainder = stream.bufend - stream.next_frame; + memcpy(buffer, stream.this_frame, remainder); + } + len = input_get_data(info, buffer + remainder, + BUFFER_SIZE - remainder); + if (len <= 0) { +#ifdef DEBUG + g_message("finished decoding"); +#endif /* DEBUG */ + break; + } + len += remainder; + if (len < MAD_BUFFER_GUARD) { + int i; + for (i = len; i < MAD_BUFFER_GUARD; i++) + buffer[i] = 0; + len = MAD_BUFFER_GUARD; + } + + mad_stream_buffer(&stream, buffer, len); + + if (seek_skip) { +#ifdef DEBUG + g_message("skipping: %d", seek_skip); +#endif + int skip = 2; + do { + if (mad_frame_decode(&frame, &stream) == 0) { + mad_timer_add(&info->pos, frame.header.duration); + if (--skip == 0) + mad_synth_frame(&synth, &frame); + } + else if (!MAD_RECOVERABLE(stream.error)) + break; + } + while (skip); + seek_skip = 0; + } + + while (!info->playback->playing == 0) { + if (info->seek != -1 && !info->remote) { +#ifdef DEBUG + g_message("seeking: %d", info->seek); +#endif + int new_position; + int seconds = + mad_timer_count(info->duration, MAD_UNITS_SECONDS); + if (info->seek >= seconds) + info->seek = seconds; + + mad_timer_set(&info->pos, info->seek, 0, 0); + new_position = + ((double) info->seek / (double) seconds) * info->size; +#ifdef DEBUG + g_message("seeking to: %d bytes", new_position); +#endif + if (vfs_fseek(info->infile, new_position, SEEK_SET) == -1) + audmad_error("failed to seek to: %d", new_position); + mad_frame_mute(&frame); + mad_synth_mute(&synth); + stream.error = MAD_ERROR_BUFLEN; + mad_plugin->output->flush(mad_timer_count + (info->pos, + MAD_UNITS_MILLISECONDS)); + stream.sync = 0; + info->seek = -1; + seek_skip = 1; + break; + } + + if (mad_header_decode(&frame.header, &stream) == -1) { + if (!MAD_RECOVERABLE(stream.error)) + break; + if (stream.error == MAD_ERROR_LOSTSYNC) { + /* ignore LOSTSYNC due to ID3 tags */ + int tagsize = id3_tag_query(stream.this_frame, + stream.bufend - + stream.this_frame); + if (tagsize > 0) { + mad_stream_skip(&stream, tagsize); + continue; + } + } +#ifdef DEBUG + g_message("(recovered) error decoding header %d: %s", + info->current_frame, + mad_stream_errorstr(&stream)); +#endif /* DEBUG */ + continue; + } + + if (mad_frame_decode(&frame, &stream) == -1) { + if (!MAD_RECOVERABLE(stream.error)) + break; +#ifdef DEBUG + g_message("(recovered) error decoding frame %d: %s", + info->current_frame, + mad_stream_errorstr(&stream)); +#endif /* DEBUG */ + } + + info->current_frame++; + + if (info->freq != frame.header.samplerate + || info->channels != + (guint) MAD_NCHANNELS(&frame.header)) + { + tlen = mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS); +#ifdef DEBUG + g_message("re-opening audio due to change in audio type"); + g_message("old: frequency = %d, channels = %d", info->freq, + info->channels); + g_message("new: frequency = %d, channels = %d", + frame.header.samplerate, + (guint) MAD_NCHANNELS(&frame.header)); +#endif /* DEBUG */ + + info->freq = frame.header.samplerate; + info->channels = MAD_NCHANNELS(&frame.header); + mad_plugin->output->close_audio(); + if (!mad_plugin->output->open_audio(info->fmt, info->freq, + info->channels)) { + audmad_error("failed to re-open audio output: %s", + mad_plugin->output->description); + } + + mad_plugin->set_info(info->title, + tlen != 0 ? tlen : -1, + info->bitrate, info->freq, info->channels); + } + if (info->playback->playing == 0) + break; + mad_synth_frame(&synth, &frame); + mad_stream_sync(&stream); + write_output(info, &synth.pcm, &frame.header); + mad_timer_add(&info->pos, frame.header.duration); + } + } + while (stream.error == MAD_ERROR_BUFLEN); + + /* free mad stuff */ + mad_frame_finish(&frame); + mad_stream_finish(&stream); + mad_synth_finish(&synth); + + if (!info->playback->playing == 0) { + mad_plugin->output->buffer_free(); + mad_plugin->output->buffer_free(); + while (mad_plugin->output->buffer_playing()) { +#ifdef DEBUG + g_message("f: buffer_playing=%d", + mad_plugin->output->buffer_playing()); +#endif + xmms_usleep(10000); + if (info->playback->playing == 0) + break; + } + } +#ifdef DEBUG + g_message("e: decode"); +#endif /* DEBUG */ + + bmp_title_input_free(info->tuple); + info->tuple = NULL; + + mad_plugin->output->close_audio(); + info->playback->playing = 0; + g_thread_exit(0); + return NULL; /* dummy */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/dither.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,115 @@ +/* A C-program for MT19937: Integer version */ +/* genrand() generates one pseudorandom unsigned integer (32bit) */ +/* which is uniformly distributed among 0 to 2^32-1 for each */ +/* call. sgenrand(seed) set initial values to the working area */ +/* of 624 words. Before genrand(), sgenrand(seed) must be */ +/* called once. (seed is any 32-bit integer except for 0). */ +/* Coded by Takuji Nishimura, considering the suggestions by */ +/* Topher Cooper and Marc Rieffel in July-Aug. 1997. */ + +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later */ +/* version. */ +/* This library 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 Library General Public License for more details. */ +/* You should have received a copy of the GNU Library General */ +/* Public License along with this library; if not, write to the */ +/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ +/* 02111-1307 USA */ + +/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */ +/* Any feedback is very welcome. For any question, comments, */ +/* see http://www.math.keio.ac.jp/matumoto/emt.html or email */ +/* matumoto@math.keio.ac.jp */ + +#include <stdio.h> +#include <assert.h> + + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0df /* constant vector a */ +#define UPPER_MASK 0x80000000 /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffff /* least significant r bits */ + +/* Tempering parameters */ +#define TEMPERING_MASK_B 0x9d2c5680 +#define TEMPERING_MASK_C 0xefc60000 +#define TEMPERING_SHIFT_U(y) (y >> 11) +#define TEMPERING_SHIFT_S(y) (y << 7) +#define TEMPERING_SHIFT_T(y) (y << 15) +#define TEMPERING_SHIFT_L(y) (y >> 18) + +static unsigned long mt[N]; /* the array for the state vector */ +static int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */ + +/* initializing the array with a NONZERO seed */ +void sgenrand(seed) +unsigned long seed; +{ + /* setting initial seeds to mt[N] using */ + /* the generator Line 25 of Table 1 in */ + /* [KNUTH 1981, The Art of Computer Programming */ + /* Vol. 2 (2nd Ed.), pp102] */ + mt[0] = seed & 0xffffffff; + for (mti = 1; mti < N; mti++) + mt[mti] = (69069 * mt[mti - 1]) & 0xffffffff; +} + +unsigned long genrand() +{ + unsigned long y; + static unsigned long mag01[2] = { 0x0, MATRIX_A }; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (mti >= N) { /* generate N words at one time */ + int kk; + + if (mti == N + 1) /* if sgenrand() has not been called, */ + sgenrand(4357); /* a default initial seed is used */ + + for (kk = 0; kk < N - M; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1]; + } + y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); + mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1]; + + mti = 0; + } + + y = mt[mti++]; + y ^= TEMPERING_SHIFT_U(y); + y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; + y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; + y ^= TEMPERING_SHIFT_L(y); + + return y; +} + +long triangular_dither_noise(int nbits) +{ + // parameter nbits : the peak-to-peak amplitude desired (in bits) + // use with nbits set to 2 + nber of bits to be trimmed. + // (because triangular is made from two uniformly distributed processes, + // it starts at 2 bits peak-to-peak amplitude) + // see The Theory of Dithered Quantization by Robert Alexander Wannamaker + // for complete proof of why that's optimal + + long v = (genrand() / 2 - genrand() / 2); // in ]-2^31, 2^31[ + //int signe = (v>0) ? 1 : -1; + long P = 1 << (32 - nbits); // the power of 2 + v /= P; + // now v in ]-2^(nbits-1), 2^(nbits-1) [ + + return v; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/fileinfo.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,644 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include "plugin.h" +#include "input.h" + +#include "mp3.xpm" + +#include <math.h> +#include <audacious/util.h> +#include <gtk/gtk.h> + +/* yaz */ +#include <langinfo.h> + +#ifndef NOGUI +static GtkWidget *window = 0; +static GtkWidget *filename_entry, *id3_frame; +static GtkWidget *title_entry, *artist_entry, *album_entry; +static GtkWidget *year_entry, *tracknum_entry, *comment_entry; +static GtkWidget *genre_combo; +static GtkWidget *mpeg_level, *mpeg_bitrate, *mpeg_samplerate, + *mpeg_frames, *mpeg_duration, *mpeg_flags; +static GtkWidget *mpeg_fileinfo, *mpeg_replaygain, *mpeg_replaygain2, + *mpeg_replaygain3, *mpeg_replaygain4, *mp3gain1, *mp3gain2; +#endif /* !NOGUI */ + +static GList *genre_list = 0; +static struct mad_info_t info; +struct id3_frame *id3_frame_new(const char *str); + +#ifndef NOGUI +static void +update_id3_frame(struct id3_tag *tag, const char *frame_name, + const char *data) +{ + int res; + struct id3_frame *frame; + union id3_field *field; + id3_ucs4_t *ucs4; + + if (data == NULL) + return; + + /* + * An empty string removes the frame altogether. + */ + if (strlen(data) == 0) { + while ((frame = id3_tag_findframe(tag, frame_name, 0))) + id3_tag_detachframe(tag, frame); + return; + } + + frame = id3_tag_findframe(tag, frame_name, 0); + if (!frame) { + frame = id3_frame_new(frame_name); + id3_tag_attachframe(tag, frame); + } + + if (frame_name == ID3_FRAME_COMMENT) { + field = id3_frame_field(frame, 3); + field->type = ID3_FIELD_TYPE_STRINGFULL; + } + else { + field = id3_frame_field(frame, 1); + field->type = ID3_FIELD_TYPE_STRINGLIST; + } + + id3_field_settextencoding(field, ID3_FIELD_TEXTENCODING_UTF_8); + + ucs4 = id3_utf8_ucs4duplicate((const unsigned char *) data); + + if (frame_name == ID3_FRAME_GENRE) { + char *tmp; + int index = id3_genre_number(ucs4); + g_free(ucs4); + tmp = g_strdup_printf("%d", index); + ucs4 = id3_utf8_ucs4duplicate((unsigned char *) tmp); + } + + if (frame_name == ID3_FRAME_COMMENT) { + res = id3_field_setfullstring(field, ucs4); + } + else { + res = id3_field_setstrings(field, 1, &ucs4); + } + + if (res != 0) + g_print("error setting id3 field: %s\n", frame_name); +} + +static void close_window(GtkWidget * w, gpointer data) +{ + input_term(&info); + gtk_widget_destroy(window); +} + +static void save_cb(GtkWidget * w, gpointer data) +{ + gchar *text; + struct id3_file *id3file; + struct id3_tag *id3tag; + char *codeset; + + if (info.remote) + return; + + /* read tag from file */ + id3file = id3_file_open(info.filename, ID3_FILE_MODE_READWRITE); + if (!id3file) { + id3tag = id3_tag_new(); + id3_tag_clearframes(id3tag); + if (!id3file) { + xmms_show_message("File Info", "Couldn't open file!", "Ok", + FALSE, NULL, NULL); + return; + } + } + + id3tag = id3_file_tag(id3file); + if (!id3tag) { + id3tag = id3_tag_new(); + } + + text = gtk_editable_get_chars(GTK_EDITABLE(title_entry), 0, -1); + update_id3_frame(id3tag, ID3_FRAME_TITLE, text); + free(text); + + text = gtk_editable_get_chars(GTK_EDITABLE(artist_entry), 0, -1); + update_id3_frame(id3tag, ID3_FRAME_ARTIST, text); + free(text); + + text = gtk_editable_get_chars(GTK_EDITABLE(album_entry), 0, -1); + update_id3_frame(id3tag, ID3_FRAME_ALBUM, text); + free(text); + + text = gtk_editable_get_chars(GTK_EDITABLE(year_entry), 0, -1); + update_id3_frame(id3tag, ID3_FRAME_YEAR, text); + free(text); + + text = gtk_editable_get_chars(GTK_EDITABLE(comment_entry), 0, -1); + update_id3_frame(id3tag, ID3_FRAME_COMMENT, text); + free(text); + + text = gtk_editable_get_chars(GTK_EDITABLE(tracknum_entry), 0, -1); + update_id3_frame(id3tag, ID3_FRAME_TRACK, text); + free(text); + + text = + gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(genre_combo)->entry), + 0, -1); + update_id3_frame(id3tag, ID3_FRAME_GENRE, text); + free(text); + + if (id3_file_update(id3file) != 0) { + xmms_show_message("File Info", "Couldn't write tag!", "Ok", FALSE, + NULL, NULL); + } + id3_file_close(id3file); +} + +static void remove_id3_cb(GtkWidget * w, gpointer data) +{ + struct id3_file *id3file; + struct id3_tag *id3tag; + + id3file = id3_file_open(info.filename, ID3_FILE_MODE_READWRITE); + + if (id3file == NULL) + return; + + id3tag = id3_file_tag(id3file); + + if (id3tag == NULL) + { + id3_file_close(id3file); + return; + } + + id3_tag_clearframes(id3tag); + id3_file_update(id3file); + id3_file_close(id3file); + + gtk_entry_set_text(GTK_ENTRY(title_entry), ""); + gtk_entry_set_text(GTK_ENTRY(artist_entry), ""); + gtk_entry_set_text(GTK_ENTRY(album_entry), ""); + gtk_entry_set_text(GTK_ENTRY(comment_entry), ""); + gtk_entry_set_text(GTK_ENTRY(year_entry), ""); + gtk_entry_set_text(GTK_ENTRY(tracknum_entry), ""); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), ""); + gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); +} + +static void +change_buttons(GtkWidget * object) +{ + gtk_widget_set_sensitive(GTK_WIDGET(object), TRUE); +} + +void create_window() +{ + GtkWidget *vbox, *hbox, *left_vbox, *table; + GtkWidget *mpeg_frame, *mpeg_box; + GtkWidget *label, *filename_hbox; + GtkWidget *bbox, *save, *remove_id3, *cancel; + GtkWidget *pixmapwid; + GdkPixbuf *pixbuf; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint(GTK_WINDOW(window), + GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(close_window), &window); + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(window), vbox); + + filename_hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), filename_hbox, FALSE, TRUE, 0); + + pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **) + gnome_mime_audio_xpm); + pixmapwid = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + gtk_misc_set_alignment(GTK_MISC(pixmapwid), 0, 0); + gtk_box_pack_start(GTK_BOX(filename_hbox), pixmapwid, FALSE, FALSE, + 0); + + label = gtk_label_new("<b>Name:</b>"); + gtk_label_set_use_markup(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(filename_hbox), label, FALSE, TRUE, 0); + filename_entry = gtk_entry_new(); + gtk_editable_set_editable(GTK_EDITABLE(filename_entry), FALSE); + gtk_box_pack_start(GTK_BOX(filename_hbox), filename_entry, TRUE, TRUE, + 0); + + hbox = gtk_hbox_new(FALSE, 10); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + left_vbox = gtk_table_new(2, 4, FALSE); + gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0); + + mpeg_frame = gtk_frame_new(_(" MPEG Info ")); + gtk_table_attach(GTK_TABLE(left_vbox), mpeg_frame, 0, 2, 0, 1, + GTK_FILL, GTK_FILL, 5, 5); + + mpeg_box = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(mpeg_frame), mpeg_box); + gtk_container_set_border_width(GTK_CONTAINER(mpeg_box), 10); + gtk_box_set_spacing(GTK_BOX(mpeg_box), 0); + + mpeg_level = gtk_label_new(""); + gtk_widget_set_usize(mpeg_level, 120, -2); + gtk_misc_set_alignment(GTK_MISC(mpeg_level), 0, 0); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_level, FALSE, FALSE, 0); + + mpeg_bitrate = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_bitrate), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_bitrate), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_bitrate, FALSE, FALSE, 0); + + mpeg_samplerate = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_samplerate), 0, 0); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_samplerate, FALSE, FALSE, + 0); + + mpeg_flags = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_flags), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_flags), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_flags, FALSE, FALSE, 0); + + mpeg_frames = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_frames), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_frames), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_frames, FALSE, FALSE, 0); + + mpeg_duration = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_duration), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_duration), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_duration, FALSE, FALSE, 0); + + mpeg_replaygain = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_replaygain), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain, FALSE, FALSE, + 0); + mpeg_replaygain2 = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain2), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_replaygain2), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain2, FALSE, FALSE, + 0); + mpeg_replaygain3 = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain3), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_replaygain3), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain3, FALSE, FALSE, + 0); + mpeg_replaygain4 = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_replaygain4), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_replaygain4), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_replaygain4, FALSE, FALSE, + 0); + mp3gain1 = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mp3gain1), 0, 0); + gtk_label_set_justify(GTK_LABEL(mp3gain1), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mp3gain1, FALSE, FALSE, 0); + mp3gain2 = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mp3gain2), 0, 0); + gtk_label_set_justify(GTK_LABEL(mp3gain2), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mp3gain2, FALSE, FALSE, 0); + + mpeg_fileinfo = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(mpeg_fileinfo), 0, 0); + gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo, FALSE, FALSE, 0); + + id3_frame = gtk_frame_new(_(" ID3 Tag ")); + gtk_table_attach(GTK_TABLE(left_vbox), id3_frame, 2, 4, 0, 1, + GTK_FILL, GTK_FILL, 0, 5); + + table = gtk_table_new(5, 5, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + gtk_container_add(GTK_CONTAINER(id3_frame), table); + + label = gtk_label_new("Title:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, + GTK_FILL, 5, 5); + + title_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), title_entry, 1, 4, 0, 1, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new("Artist:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, + GTK_FILL, 5, 5); + + artist_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), artist_entry, 1, 4, 1, 2, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new("Album:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, + GTK_FILL, 5, 5); + + album_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), album_entry, 1, 4, 2, 3, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new("Comment:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, GTK_FILL, + GTK_FILL, 5, 5); + + comment_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), comment_entry, 1, 4, 3, 4, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new("Year:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, GTK_FILL, + GTK_FILL, 5, 5); + + year_entry = gtk_entry_new(); + gtk_widget_set_usize(year_entry, 40, -1); + gtk_table_attach(GTK_TABLE(table), year_entry, 1, 2, 4, 5, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new("Track number:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 4, 5, GTK_FILL, + GTK_FILL, 5, 5); + + tracknum_entry = gtk_entry_new(); + gtk_widget_set_usize(tracknum_entry, 40, -1); + gtk_table_attach(GTK_TABLE(table), tracknum_entry, 3, 4, 4, 5, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new("Genre:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6, GTK_FILL, + GTK_FILL, 5, 5); + + genre_combo = gtk_combo_new(); + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), + FALSE); + if (!genre_list) { + int i = 0; + const id3_ucs4_t *ucs4 = id3_genre_index(i); + while (ucs4) { + genre_list = + g_list_append(genre_list, id3_ucs4_utf8duplicate(ucs4)); + i++; + ucs4 = id3_genre_index(i); + } + } + gtk_combo_set_popdown_strings(GTK_COMBO(genre_combo), genre_list); + + gtk_table_attach(GTK_TABLE(table), genre_combo, 1, 4, 5, 6, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + bbox = gtk_hbutton_box_new(); + gtk_hbutton_box_set_layout_default(GTK_BUTTONBOX_SPREAD); + + save = gtk_button_new_from_stock(GTK_STOCK_SAVE); + g_signal_connect(G_OBJECT(save), "clicked", + G_CALLBACK(save_cb), NULL); + gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0); + gtk_widget_grab_default(save); + + remove_id3 = gtk_button_new_from_stock(GTK_STOCK_DELETE); + g_signal_connect(G_OBJECT(remove_id3), "clicked", + G_CALLBACK(remove_id3_cb), save); + gtk_box_pack_start(GTK_BOX(bbox), remove_id3, TRUE, TRUE, 0); + + g_signal_connect_swapped(G_OBJECT(title_entry), "changed", + G_CALLBACK(change_buttons), save); + g_signal_connect_swapped(G_OBJECT(artist_entry), "changed", + G_CALLBACK(change_buttons), save); + g_signal_connect_swapped(G_OBJECT(album_entry), "changed", + G_CALLBACK(change_buttons), save); + g_signal_connect_swapped(G_OBJECT(year_entry), "changed", + G_CALLBACK(change_buttons), save); + g_signal_connect_swapped(G_OBJECT(comment_entry), "changed", + G_CALLBACK(change_buttons), save); + g_signal_connect_swapped(G_OBJECT(tracknum_entry), "changed", + G_CALLBACK(change_buttons), save); + + g_signal_connect_swapped(G_OBJECT(GTK_COMBO(genre_combo)->entry), "changed", + G_CALLBACK(change_buttons), save); + + + gtk_table_attach(GTK_TABLE(table), bbox, 0, 5, 6, 7, GTK_FILL, 0, + 0, 8); + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_table_attach(GTK_TABLE(left_vbox), bbox, 0, 4, 1, 2, GTK_FILL, + 0, 0, 8); + + cancel = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + g_signal_connect_swapped(G_OBJECT(cancel), "clicked", + G_CALLBACK(gtk_widget_destroy), + G_OBJECT(window)); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + gtk_widget_show_all(window); +} + +static void id3_frame_to_entry(char *framename, GtkEntry * entry) +{ + gtk_entry_set_text(entry, ""); + + if (info.tag) { + gchar *text = input_id3_get_string(info.tag, framename); + if (text) { + gtk_entry_set_text(entry, text); + g_free(text); + } + } +} +#endif /* !NOGUI */ + +void audmad_get_file_info(char *filename) +{ +#ifndef NOGUI + gchar *title; + gchar message[128]; + static char const *const layer_str[3] = { "MPEG-1 Layer I", "MPEG-1 Layer II", "MPEG-1 Layer III" }; + static char const *const mode_str[4] = { + ("single channel"), ("dual channel"), "joint stereo", "stereo" + }; + char *utf_filename; + +#ifdef DEBUG + g_message("f: audmad_get_file_info: %s\n", filename); +#endif + utf_filename = str_to_utf8(filename); + create_window(); + + input_init(&info, filename); + input_get_info(&info, FALSE); + + title = g_strdup_printf("%s - Audacious", g_basename(filename)); + gtk_window_set_title(GTK_WINDOW(window), title); + g_free(title); + + gtk_entry_set_text(GTK_ENTRY(filename_entry), utf_filename); + gtk_editable_set_position(GTK_EDITABLE(filename_entry), -1); + + free(utf_filename); + + id3_frame_to_entry(ID3_FRAME_ARTIST, GTK_ENTRY(artist_entry)); + id3_frame_to_entry(ID3_FRAME_TITLE, GTK_ENTRY(title_entry)); + id3_frame_to_entry(ID3_FRAME_ALBUM, GTK_ENTRY(album_entry)); + +// year +// id3_frame_to_entry (ID3_FRAME_YEAR, GTK_ENTRY (year_entry)); +// to set year entry, we have to do manually because TYER is still used equally to TDRC. + gtk_entry_set_text(GTK_ENTRY(year_entry), ""); + if (info.tag) { + gchar *text = NULL; + text = input_id3_get_string(info.tag, "TDRC"); + if (!text) + text = input_id3_get_string(info.tag, "TYER"); + if (text) { + gtk_entry_set_text(GTK_ENTRY(year_entry), text); + g_free(text); + } + } + + id3_frame_to_entry(ID3_FRAME_TRACK, GTK_ENTRY(tracknum_entry)); + id3_frame_to_entry(ID3_FRAME_COMMENT, GTK_ENTRY(comment_entry)); + snprintf(message, 127, "%s", layer_str[info.mpeg_layer - 1]); + gtk_label_set_text(GTK_LABEL(mpeg_level), message); + if (info.vbr) { + snprintf(message, 127, "VBR (avg. %d kbps)", info.bitrate / 1000); + } + else { + snprintf(message, 127, "%d KBit/s", info.bitrate / 1000); + } + gtk_label_set_text(GTK_LABEL(mpeg_bitrate), message); + snprintf(message, 127, "%d Hz", info.freq); + gtk_label_set_text(GTK_LABEL(mpeg_samplerate), message); + if (info.frames != -1) { + snprintf(message, 127, "%d frames", info.frames); + gtk_label_set_text(GTK_LABEL(mpeg_frames), message); + } + else { + gtk_label_set_text(GTK_LABEL(mpeg_frames), ""); + } + gtk_label_set_text(GTK_LABEL(mpeg_flags), mode_str[info.mode]); + snprintf(message, 127, "%ld seconds", + mad_timer_count(info.duration, MAD_UNITS_SECONDS)); + gtk_label_set_text(GTK_LABEL(mpeg_duration), message); + + if (info.replaygain_album_str != NULL) { + snprintf(message, 127, "RG_album=%4s (x%4.2f)", + info.replaygain_album_str, info.replaygain_album_scale); + gtk_label_set_text(GTK_LABEL(mpeg_replaygain), message); + } + else + gtk_label_set_text(GTK_LABEL(mpeg_replaygain), ""); + + if (info.replaygain_track_str != NULL) { + snprintf(message, 127, "RG_track=%4s (x%4.2f)", + info.replaygain_track_str, info.replaygain_track_scale); + gtk_label_set_text(GTK_LABEL(mpeg_replaygain2), message); + } + else + gtk_label_set_text(GTK_LABEL(mpeg_replaygain2), ""); + + if (info.replaygain_album_peak_str != NULL) { + snprintf(message, 127, "Peak album=%4s (%+5.3fdBFS)", + info.replaygain_album_peak_str, + 20 * log10(info.replaygain_album_peak)); + gtk_label_set_text(GTK_LABEL(mpeg_replaygain3), message); + } + else + gtk_label_set_text(GTK_LABEL(mpeg_replaygain3), ""); + + if (info.replaygain_track_peak_str != NULL) { + snprintf(message, 127, "Peak track=%4s (%+5.3fdBFS)", + info.replaygain_track_peak_str, + 20 * log10(info.replaygain_track_peak)); + gtk_label_set_text(GTK_LABEL(mpeg_replaygain4), message); + } + else + gtk_label_set_text(GTK_LABEL(mpeg_replaygain3), ""); + + if (info.mp3gain_undo_str != NULL) { + snprintf(message, 127, "mp3gain undo=%4s (%+5.3fdB)", + info.mp3gain_undo_str, info.mp3gain_undo); + gtk_label_set_text(GTK_LABEL(mp3gain1), message); + } + else + gtk_label_set_text(GTK_LABEL(mp3gain1), ""); + + if (info.mp3gain_minmax_str != NULL) { + snprintf(message, 127, "mp3gain minmax=%4s (max-min=%+6.3fdB)", + info.mp3gain_minmax_str, info.mp3gain_minmax); + gtk_label_set_text(GTK_LABEL(mp3gain2), message); + } + else + gtk_label_set_text(GTK_LABEL(mp3gain2), ""); + + gtk_label_set_text(GTK_LABEL(mpeg_fileinfo), ""); + + + /* work out the index of the genre in the list */ + { + const id3_ucs4_t *string; + struct id3_frame *frame; + union id3_field *field; + frame = id3_tag_findframe(info.tag, ID3_FRAME_GENRE, 0); + if (frame) { + field = id3_frame_field(frame, 1); + string = id3_field_getstrings(field, 0); + string = id3_genre_name(string); + if (string) + gtk_list_select_item(GTK_LIST + (GTK_COMBO(genre_combo)->list), + id3_genre_number(string)); + } + } + + gtk_widget_set_sensitive(id3_frame, TRUE); + +#endif /* !NOGUI */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/input.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,518 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_ASSERT_H +#include <assert.h> +#endif /* HAVE_ASSERT_H */ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif /* HAVE_SYS_SOCKET_H */ + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif /* HAVE_NETDB_H */ + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif /* HAVE_SYS_TIME_H */ + +#include <fcntl.h> +#include <errno.h> +#include <audacious/util.h> + +#include "input.h" +#include "replaygain.h" + +#define DIR_SEPARATOR '/' +#define HEADER_SIZE 256 +#define LINE_LENGTH 256 + +extern gboolean scan_file(struct mad_info_t *info, gboolean fast); + +/** + * init the mad_info_t struct. + */ +gboolean input_init(struct mad_info_t * info, const char *url) +{ +#ifdef DEBUG + g_message("f: input_init"); +#endif + memset(info, 0, sizeof(struct mad_info_t)); + + info->fmt = FMT_S16_LE; + info->channels = -1; + info->mpeg_layer = -1; + info->size = -1; + info->freq = -1; + info->seek = -1; + info->duration = mad_timer_zero; + info->pos = mad_timer_zero; + info->url = g_strdup(url); + info->current_frame = 0; + info->frames = 0; + info->bitrate = 0; + info->vbr = 0; + info->mode = 0; + info->title = 0; + info->offset = 0; + + info->replaygain_album_str = 0; + info->replaygain_track_str = 0; + info->replaygain_album_peak_str = 0; + info->replaygain_track_peak_str = 0; + info->mp3gain_undo_str = 0; + info->mp3gain_minmax_str = 0; + + info->tuple = NULL; + info->filename = g_strdup(url); + + info->infile = vfs_fopen(info->filename, "rb"); + if (info->infile == NULL) + return FALSE; + + // obtain file size + vfs_fseek(info->infile, 0, SEEK_END); + info->size = vfs_ftell(info->infile); + vfs_fseek(info->infile, 0, SEEK_SET); + info->remote = info->size == 0 ? TRUE : FALSE; + +#ifdef DEBUG + g_message("i: info->size == %lu", info->size); + g_message("e: input_init"); +#endif + return TRUE; +} + +/* return length in letters */ +size_t mad_ucs4len(id3_ucs4_t *ucs) +{ + id3_ucs4_t *ptr = ucs; + size_t len = 0; + + while(*ptr++ != 0) + len++; + + return len; +} + +/* duplicate id3_ucs4_t string. new string will be terminated with 0. */ +id3_ucs4_t *mad_ucs4dup(id3_ucs4_t *org) +{ + id3_ucs4_t *new = NULL; + size_t len = mad_ucs4len(org); + + new = g_malloc0((len + 1) * sizeof(id3_ucs4_t)); + memcpy(new, org, len * sizeof(id3_ucs4_t)); + *(new + len) = 0; //terminate + + return new; +} + +#define BYTES(x) ((x) * sizeof(id3_ucs4_t)) + +id3_ucs4_t *mad_parse_genre(const id3_ucs4_t *string) +{ + id3_ucs4_t *ret = NULL; + id3_ucs4_t *tmp = NULL; + id3_ucs4_t *genre = NULL; + id3_ucs4_t *ptr, *end, *tail, *tp; + size_t ret_len = 0; //num of ucs4 char! + size_t tmp_len = 0; + gboolean is_num = TRUE; + + tail = (id3_ucs4_t *)string + mad_ucs4len((id3_ucs4_t *)string); + + ret = g_malloc0(1024); // realloc() is too picky + + for(ptr = (id3_ucs4_t *)string; *ptr != 0 && ptr <= tail; ptr++) { + if(*ptr == '(') { + if(*(++ptr) == '(') { // escaped text like: ((something) + for(end = ptr; *end != ')' && *end != 0;) { // copy "(something)" + end++; + } + end++; //include trailing ')' +// ret = g_realloc(ret, BYTES(end - ptr + 1)); + memcpy(ret, ptr, BYTES(end - ptr)); + ret_len += (end - ptr); + *(ret + ret_len) = 0; //terminate + ptr = end + 1; + } + else { + // reference to an id3v1 genre code + for(end = ptr; *end != ')' && *end != 0;) { + end++; + } + + tmp = g_malloc0(BYTES(end - ptr + 1)); + memcpy(tmp, ptr, BYTES(end - ptr)); + *(tmp + (end - ptr)) = 0; //terminate + ptr += end - ptr; + + genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); + + g_free(tmp); + tmp = NULL; + + tmp_len = mad_ucs4len(genre); + +// ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1)); + memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); + + ret_len += tmp_len; + *(ret + ret_len) = 0; //terminate + } + } + else { + for(end = ptr; *end != '(' && *end != 0; ) { + end++; + } + // scan string to determine whether a genre code number or not + tp = ptr; + is_num = TRUE; + while(tp < end) { + if(*tp < '0' || *tp > '9') { // anything else than number appears. + is_num = FALSE; + break; + } + tp++; + } + if(is_num) { +#ifdef DEBUG + printf("is_num!\n"); +#endif + tmp = g_malloc0(BYTES(end - ptr + 1)); + memcpy(tmp, ptr, BYTES(end - ptr)); + *(tmp + (end - ptr)) = 0; //terminate + ptr += end - ptr; + + genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); +#ifdef DEBUG + printf("genre length = %d\n", mad_ucs4len(genre)); +#endif + g_free(tmp); + tmp = NULL; + + tmp_len = mad_ucs4len(genre); + +// ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1)); + memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); + + ret_len += tmp_len; + *(ret + ret_len) = 0; //terminate + } + else { // plain text +// ret = g_realloc(ret, BYTES(end - ptr + 1)); +#ifdef DEBUG + printf("plain!\n"); + printf("ret_len = %d\n", ret_len); +#endif + memcpy(ret + BYTES(ret_len), ptr, BYTES(end - ptr)); + ret_len = ret_len + (end - ptr); + *(ret + ret_len) = 0; //terminate + ptr += (end - ptr); + } + } + } + return ret; +} + + +gchar *input_id3_get_string(struct id3_tag * tag, char *frame_name) +{ + gchar *rtn; + const id3_ucs4_t *string_const; + id3_ucs4_t *string; + struct id3_frame *frame; + union id3_field *field; + + frame = id3_tag_findframe(tag, frame_name, 0); + if (!frame) + return NULL; + + if (frame_name == ID3_FRAME_COMMENT) + field = id3_frame_field(frame, 3); + else + field = id3_frame_field(frame, 1); + + if (!field) + return NULL; + + if (frame_name == ID3_FRAME_COMMENT) + string_const = id3_field_getfullstring(field); + else + string_const = id3_field_getstrings(field, 0); + + if (!string_const) + return NULL; + + string = mad_ucs4dup((id3_ucs4_t *)string_const); + + if (frame_name == ID3_FRAME_GENRE) { + id3_ucs4_t *string2 = NULL; + string2 = mad_parse_genre(string); + g_free((void *)string); + string = string2; + } + + { + id3_utf8_t *string2 = id3_ucs4_utf8duplicate(string); + rtn = str_to_utf8(string2); + g_free(string2); + } + +#ifdef DEBUG + g_print("i: string = %s\n", rtn); +#endif + return rtn; +} + +/** + * read the ID3 tag + */ +static void input_read_tag(struct mad_info_t *info) +{ + gchar *string = NULL; + TitleInput *title_input; + + if (info->tuple == NULL) { + title_input = bmp_title_input_new(); + info->tuple = title_input; + } + else + title_input = info->tuple; + + info->id3file = id3_file_open(info->filename, ID3_FILE_MODE_READONLY); + if (!info->id3file) { + return; + } + + info->tag = id3_file_tag(info->id3file); + if (!info->tag) { + return; + } + + title_input->performer = + input_id3_get_string(info->tag, ID3_FRAME_ARTIST); + title_input->track_name = + input_id3_get_string(info->tag, ID3_FRAME_TITLE); + title_input->album_name = + input_id3_get_string(info->tag, ID3_FRAME_ALBUM); + title_input->genre = input_id3_get_string(info->tag, ID3_FRAME_GENRE); + title_input->comment = + input_id3_get_string(info->tag, ID3_FRAME_COMMENT); + string = input_id3_get_string(info->tag, ID3_FRAME_TRACK); + if (string) { + title_input->track_number = atoi(string); + g_free(string); + string = NULL; + } + // year + string = NULL; + string = input_id3_get_string(info->tag, ID3_FRAME_YEAR); //TDRC + if (!string) + string = input_id3_get_string(info->tag, "TYER"); + + if (string) { + title_input->year = atoi(string); + g_free(string); + string = NULL; + } + + title_input->file_name = g_strdup(g_basename(info->filename)); + title_input->file_path = g_path_get_dirname(info->filename); + if ((string = strrchr(title_input->file_name, '.'))) { + title_input->file_ext = string + 1; + *string = '\0'; // make filename end at dot. + } + + info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ? + audmad_config.id3_format : xmms_get_gentitle_format(), title_input); +} + +/** + * Retrieve meta-information about URL. + * For local files this means ID3 tag etc. + */ +gboolean input_get_info(struct mad_info_t *info, gboolean fast_scan) +{ +#ifdef DEBUG + g_message("f: input_get_info: %s", info->title); +#endif /* DEBUG */ + input_read_tag(info); + input_read_replaygain(info); + + /* scan mp3 file, decoding headers unless fast_scan is set */ + if (scan_file(info, fast_scan) == FALSE) + return FALSE; + + /* reset the input file to the start */ + vfs_rewind(info->infile); + info->offset = 0; + + if (info->remote) + { + gchar *stream_name = vfs_get_metadata(info->infile, "stream-name"); + gchar *track_name = vfs_get_metadata(info->infile, "track-name"); + gchar *tmp = NULL; + + g_free(info->title); + g_free(info->tuple->track_name); + g_free(info->tuple->album_name); + + info->title = g_strdup(track_name); + info->tuple->track_name = g_strdup(track_name); + info->tuple->album_name = g_strdup(stream_name); + tmp = g_strdup_printf("%s (%s)", track_name, stream_name); + mad_plugin->set_info(tmp, + -1, // indicates the stream is unseekable + info->bitrate, info->freq, info->channels); + g_free(tmp); g_free(stream_name); g_free(track_name); + } + + /* use the filename for the title as a last resort */ + if (!info->title) + { + char *pos = strrchr(info->filename, DIR_SEPARATOR); + if (pos) + info->title = g_strdup(pos + 1); + else + info->title = g_strdup(info->filename); + } + +#ifdef DEBUG + g_message("e: input_get_info"); +#endif /* DEBUG */ + return TRUE; +} + + +/** + * Read data from the source given my madinfo into the buffer + * provided. Return the number of bytes read. + * @return 0 on EOF + * @return -1 on error + */ +int +input_get_data(struct mad_info_t *madinfo, guchar * buffer, + int buffer_size) +{ + int len = 0; +#ifdef DEBUG +// g_message ("f: input_get_data: %d", buffer_size); +#endif + + /* simply read to data from the file */ + len = vfs_fread(buffer, 1, buffer_size, madinfo->infile); + + if (len == 0 && madinfo->playback) + madinfo->playback->eof = TRUE; + + if (madinfo->remote) + { + gchar *stream_name = vfs_get_metadata(madinfo->infile, "stream-name"); + gchar *track_name = vfs_get_metadata(madinfo->infile, "track-name"); + gchar *tmp = NULL; + + g_free(madinfo->title); + g_free(madinfo->tuple->track_name); + g_free(madinfo->tuple->album_name); + + madinfo->title = g_strdup(track_name); + madinfo->tuple->track_name = g_strdup(track_name); + madinfo->tuple->album_name = g_strdup(stream_name); + tmp = g_strdup_printf("%s (%s)", track_name, stream_name); + mad_plugin->set_info(tmp, + -1, // indicates the stream is unseekable + madinfo->bitrate, madinfo->freq, madinfo->channels); + g_free(tmp); g_free(stream_name); g_free(track_name); + } + +#ifdef DEBUG +// g_message ("e: input_get_data: size=%d offset=%d", len, madinfo->offset); +#endif + madinfo->offset += len; + return len; +} + +/** + * Free up all mad_info_t related resourses. + */ +gboolean input_term(struct mad_info_t * info) +{ +#ifdef DEBUG + g_message("f: input_term"); +#endif + + if (info->title) + g_free(info->title); + if (info->url) + g_free(info->url); + if (info->filename) + g_free(info->filename); + if (info->infile) + vfs_fclose(info->infile); + if (info->id3file) + id3_file_close(info->id3file); + + if (info->replaygain_album_str) + g_free(info->replaygain_album_str); + if (info->replaygain_track_str) + g_free(info->replaygain_track_str); + if (info->replaygain_album_peak_str) + g_free(info->replaygain_album_peak_str); + if (info->replaygain_track_peak_str) + g_free(info->replaygain_track_peak_str); + if (info->mp3gain_undo_str) + g_free(info->mp3gain_undo_str); + if (info->mp3gain_minmax_str) + g_free(info->mp3gain_minmax_str); + + if (info->tuple) { + bmp_title_input_free(info->tuple); + info->tuple = NULL; + } + + /* set everything to zero in case it gets used again. */ + memset(info, 0, sizeof(struct mad_info_t)); +#ifdef DEBUG + g_message("e: input_term"); +#endif + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/input.h Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,35 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef INPUT_H +#define INPUT_H + +#include "plugin.h" + +gboolean input_init(struct mad_info_t *songinfo, const gchar * url); +gboolean input_term(struct mad_info_t *songinfo); +gboolean input_get_info(struct mad_info_t *songinfo, gboolean fast_scan); +gint input_get_data(struct mad_info_t *songinfo, guchar * buffer, + gint buffer_size); +gint input_udp_read(); +gchar *input_id3_get_string(struct id3_tag *tag, char *frame_name); + +#endif /* ! INPUT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/mp3.xpm Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,963 @@ +/* XPM */ +static char * gnome_mime_audio_xpm[] = { +"48 52 908 2", +" c None", +". c #000000", +"+ c #010100", +"@ c #020201", +"# c #030201", +"$ c #232323", +"% c #1F1F1F", +"& c #DADADA", +"* c #FFFFFF", +"= c #F6F6F6", +"- c #CFCFCF", +"; c #707070", +"> c #FFFFFE", +", c #FEFEFE", +"' c #FBFBFB", +") c #EDEDED", +"! c #C0C0C0", +"~ c #FFFEFE", +"{ c #F8F5F2", +"] c #EBE7E1", +"^ c #ECEDE8", +"/ c #ECEFEA", +"( c #E6E9E4", +"_ c #CED0CE", +": c #BCBFBC", +"< c #F0F0EF", +"[ c #FEFEFD", +"} c #F7F7F7", +"| c #D7D7D7", +"1 c #F1F1F1", +"2 c #B7B7B7", +"3 c #EAEDEA", +"4 c #8EA299", +"5 c #697D70", +"6 c #546459", +"7 c #404D44", +"8 c #2C352F", +"9 c #4E5649", +"0 c #D6D8D3", +"a c #D2D2D2", +"b c #AEAEAE", +"c c #070707", +"d c #FCFCFB", +"e c #6D7C75", +"f c #5A5D4C", +"g c #6B735F", +"h c #6E7B63", +"i c #5B6052", +"j c #4A5042", +"k c #CED0CB", +"l c #FEFDFD", +"m c #FDFDFD", +"n c #B8B8B8", +"o c #DCDCDC", +"p c #A4A4A3", +"q c #0E0E0E", +"r c #8A928F", +"s c #6A7F68", +"t c #4D564E", +"u c #4E5950", +"v c #585E57", +"w c #333530", +"x c #9D9C9B", +"y c #FCFDFE", +"z c #FDFEFE", +"A c #FDFDFE", +"B c #F5F5F4", +"C c #B5B5B5", +"D c #F9F9F9", +"E c #FCFCFC", +"F c #CBCBCB", +"G c #A5A5A5", +"H c #CECDCB", +"I c #788976", +"J c #C6BDBB", +"K c #E2E2E2", +"L c #E2E0DF", +"M c #656A65", +"N c #4A4B49", +"O c #EAEDF1", +"P c #CED5DD", +"Q c #DBE0E9", +"R c #DDE1E9", +"S c #D3D7DD", +"T c #D9DCE1", +"U c #F4F4F4", +"V c #ACACAC", +"W c #ECECEC", +"X c #C3C3C3", +"Y c #C2C2C2", +"Z c #C9C9C8", +"` c #A8A8A8", +" . c #F6F8F6", +".. c #A7ADA5", +"+. c #6D7F71", +"@. c #ECECE9", +"#. c #FDFDFC", +"$. c #939C9A", +"%. c #1B2321", +"&. c #FCFBFA", +"*. c #F2F6F9", +"=. c #B4BFCC", +"-. c #7E8A9B", +";. c #515B6F", +">. c #50596A", +",. c #737D8D", +"'. c #BEC6CE", +"). c #C4CBD5", +"!. c #BFC5D1", +"~. c #F1F2F4", +"{. c #F4F4F3", +"]. c #ABABAB", +"^. c #515151", +"/. c #474747", +"(. c #464645", +"_. c #464646", +":. c #5D5D5C", +"<. c #A9A9A9", +"[. c #FAFAF9", +"}. c #EDEFF0", +"|. c #F1F5F5", +"1. c #636E62", +"2. c #636E5F", +"3. c #D4D5D3", +"4. c #FBF7F4", +"5. c #9BA595", +"6. c #657366", +"7. c #001514", +"8. c #FCFAF8", +"9. c #FDFCFC", +"0. c #FBF9F7", +"a. c #E3E6EA", +"b. c #9DACBF", +"c. c #515A6D", +"d. c #111828", +"e. c #131B29", +"f. c #0F1A2B", +"g. c #081125", +"h. c #0D1328", +"i. c #C2C9D1", +"j. c #C5CBD4", +"k. c #A6AEBB", +"l. c #F6F6F7", +"m. c #FAFAFA", +"n. c #F2F2F2", +"o. c #F3F3F3", +"p. c #AAAAAA", +"q. c #A0A09F", +"r. c #8A8A8A", +"s. c #7A7A7A", +"t. c #6C6C6C", +"u. c #454545", +"v. c #ABB3AF", +"w. c #A8B6A3", +"x. c #D0DBD0", +"y. c #F2F3F0", +"z. c #DEE1E0", +"A. c #66736A", +"B. c #354336", +"C. c #556254", +"D. c #C5C7C6", +"E. c #F2EDEA", +"F. c #CADAD9", +"G. c #9BA893", +"H. c #1B3128", +"I. c #FCF4EF", +"J. c #FDFBF9", +"K. c #DDDFE4", +"L. c #97A3B4", +"M. c #374152", +"N. c #0D131C", +"O. c #1C2026", +"P. c #262C3A", +"Q. c #242E43", +"R. c #202D41", +"S. c #172538", +"T. c #121523", +"U. c #BABCC3", +"V. c #9DA6B5", +"W. c #BABEC4", +"X. c #F8F8F7", +"Y. c #E7E7E7", +"Z. c #C6C6C5", +"`. c #BABAB9", +" + c #E7E9E9", +".+ c #63736C", +"++ c #464945", +"@+ c #5F7361", +"#+ c #CED4C3", +"$+ c #DBDEDA", +"%+ c #979999", +"&+ c #404342", +"*+ c #606E6B", +"=+ c #E5E5E3", +"-+ c #4B5F52", +";+ c #455845", +">+ c #2D362F", +",+ c #FCFAF9", +"'+ c #E4E4E7", +")+ c #97A8B9", +"!+ c #343C4A", +"~+ c #13181E", +"{+ c #1B2027", +"]+ c #1D232B", +"^+ c #1F2632", +"/+ c #212A3C", +"(+ c #242F45", +"_+ c #2A374D", +":+ c #121C2F", +"<+ c #525A67", +"[+ c #D9DDE5", +"}+ c #737C8C", +"|+ c #EAEBEB", +"1+ c #F7F7F6", +"2+ c #F6F6F5", +"3+ c #F5F5F5", +"4+ c #EDEDEB", +"5+ c #FBFBFA", +"6+ c #6C8175", +"7+ c #778673", +"8+ c #121613", +"9+ c #4C504A", +"0+ c #8FA695", +"a+ c #E2DCD8", +"b+ c #E5E6E7", +"c+ c #FBFAF9", +"d+ c #CAC6C1", +"e+ c #847F79", +"f+ c #FCFAFA", +"g+ c #ECE9E9", +"h+ c #A6B1C4", +"i+ c #232B37", +"j+ c #12171E", +"k+ c #191F27", +"l+ c #1D222B", +"m+ c #1E242B", +"n+ c #1E252F", +"o+ c #202937", +"p+ c #232F44", +"q+ c #283853", +"r+ c #253046", +"s+ c #0A1526", +"t+ c #D5D7DB", +"u+ c #727D90", +"v+ c #CFD0D2", +"w+ c #B1B1B1", +"x+ c #99A597", +"y+ c #7B9173", +"z+ c #65705D", +"A+ c #1F201B", +"B+ c #58655E", +"C+ c #F3F2F1", +"D+ c #AAB3C1", +"E+ c #525C6D", +"F+ c #0A0F17", +"G+ c #1C2129", +"H+ c #1E242A", +"I+ c #1E242C", +"J+ c #1F232D", +"K+ c #232E41", +"L+ c #2B3855", +"M+ c #2C3B57", +"N+ c #070D1F", +"O+ c #A3ABB6", +"P+ c #828EA0", +"Q+ c #B9BCC2", +"R+ c #F3F3F2", +"S+ c #F1EFEC", +"T+ c #72886D", +"U+ c #4E5849", +"V+ c #748268", +"W+ c #808080", +"X+ c #D6D9DE", +"Y+ c #5E6A7A", +"Z+ c #121621", +"`+ c #161D26", +" @ c #1D242C", +".@ c #1F242B", +"+@ c #1E2427", +"@@ c #212C3F", +"#@ c #283552", +"$@ c #2F405D", +"%@ c #0E162B", +"&@ c #59657B", +"*@ c #8A98AA", +"=@ c #AFB4BC", +"-@ c #F2F2F1", +";@ c #7D9074", +">@ c #494949", +",@ c #676D65", +"'@ c #C8CBC7", +")@ c #818B9B", +"!@ c #1B232F", +"~@ c #0F151F", +"{@ c #1B212B", +"]@ c #1E232D", +"^@ c #20252C", +"/@ c #24272C", +"(@ c #1F2429", +"_@ c #1C2228", +":@ c #1F2A3C", +"<@ c #1F2A40", +"[@ c #25344E", +"}@ c #314464", +"|@ c #15223A", +"1@ c #34445E", +"2@ c #939FB3", +"3@ c #ACB2BC", +"4@ c #F1F1F0", +"5@ c #ECEEEB", +"6@ c #B8BCB7", +"7@ c #647C65", +"8@ c #797E78", +"9@ c #C4C3C2", +"0@ c #919191", +"a@ c #DCE3EA", +"b@ c #242C3D", +"c@ c #0B101C", +"d@ c #1A212A", +"e@ c #1F242C", +"f@ c #21252C", +"g@ c #222629", +"h@ c #1C232A", +"i@ c #212B3D", +"j@ c #33415B", +"k@ c #27344B", +"l@ c #212F47", +"m@ c #374A68", +"n@ c #1A2840", +"o@ c #34445F", +"p@ c #96A3B5", +"q@ c #B3B7C0", +"r@ c #F2F2F0", +"s@ c #F0F0F0", +"t@ c #E2E7E2", +"u@ c #939E92", +"v@ c #50644E", +"w@ c #3D443E", +"x@ c #D4D2CF", +"y@ c #7F8898", +"z@ c #020A14", +"A@ c #171E28", +"B@ c #1C222A", +"C@ c #1E232B", +"D@ c #1F242D", +"E@ c #1F2228", +"F@ c #1D2328", +"G@ c #283449", +"H@ c #4A5C7B", +"I@ c #697D98", +"J@ c #4D5D79", +"K@ c #1F2D45", +"L@ c #394A6B", +"M@ c #1B2943", +"N@ c #425371", +"O@ c #95A3B5", +"P@ c #B8BDC2", +"Q@ c #EFEFEE", +"R@ c #EFEFED", +"S@ c #EDEEED", +"T@ c #EFF0F0", +"U@ c #F7FCF3", +"V@ c #313F30", +"W@ c #1C2120", +"X@ c #C9CBC9", +"Y@ c #F9F9F8", +"Z@ c #E9EEF2", +"`@ c #3C4352", +" # c #0C111D", +".# c #1E232E", +"+# c #1D212A", +"@# c #20242C", +"## c #1B1D20", +"$# c #263043", +"%# c #576886", +"&# c #8396B1", +"*# c #7A8FAD", +"=# c #677B99", +"-# c #28374F", +";# c #384A69", +"># c #182642", +",# c #5D708F", +"'# c #828FA3", +")# c #C5C7CB", +"!# c #F1F1EF", +"~# c #EEEEED", +"{# c #EEEEEC", +"]# c #9DA7A4", +"^# c #2A3B34", +"/# c #222725", +"(# c #80807E", +"_# c #EFEEEC", +":# c #F1F0EF", +"<# c #F4F3F2", +"[# c #F8F8F8", +"}# c #A8AFBB", +"|# c #0D1522", +"1# c #1A212E", +"2# c #1D232C", +"3# c #21252D", +"4# c #1E2327", +"5# c #171B20", +"6# c #637593", +"7# c #95A6BE", +"8# c #6D7F9C", +"9# c #617395", +"0# c #5A6E8B", +"a# c #2A354A", +"b# c #3D4E6D", +"c# c #1C2A46", +"d# c #748DAB", +"e# c #6D798A", +"f# c #DEDFE0", +"g# c #EEEEEE", +"h# c #EDEDEC", +"i# c #ECEAE7", +"j# c #ADB1B0", +"k# c #B5B9BA", +"l# c #F1F0F0", +"m# c #F9F8F8", +"n# c #F7F5F4", +"o# c #A7AAA4", +"p# c #B7BAB4", +"q# c #F7F6F6", +"r# c #ECEFF1", +"s# c #747F8E", +"t# c #0A121E", +"u# c #232C3A", +"v# c #1D232E", +"w# c #222932", +"x# c #1B1D21", +"y# c #1C273E", +"z# c #C1CFE1", +"A# c #8FA1B7", +"B# c #6D81A0", +"C# c #596D93", +"D# c #455674", +"E# c #293343", +"F# c #4A5C7D", +"G# c #304060", +"H# c #7690B8", +"I# c #5D6878", +"J# c #BBC0CC", +"K# c #ECECEB", +"L# c #F6F4F3", +"M# c #F4F1EF", +"N# c #F7F5F3", +"O# c #F9F8F7", +"P# c #BBBDB9", +"Q# c #4D5852", +"R# c #71746D", +"S# c #F0EAE7", +"T# c #DBDEE3", +"U# c #3E495B", +"V# c #1C2435", +"W# c #273041", +"X# c #283040", +"Y# c #293141", +"Z# c #262B38", +"`# c #1D212D", +" $ c #4A5B77", +".$ c #CFDBEB", +"+$ c #7A8FAC", +"@$ c #6C83A5", +"#$ c #4D6389", +"$$ c #35445E", +"%$ c #404D60", +"&$ c #506586", +"*$ c #4F6687", +"=$ c #6983AB", +"-$ c #343E50", +";$ c #4D5F7A", +">$ c #B8C0CC", +",$ c #F7F6F5", +"'$ c #D2D6D3", +")$ c #6C866F", +"!$ c #5A715E", +"~$ c #566758", +"{$ c #B5C1B4", +"]$ c #B2BAC8", +"^$ c #273245", +"/$ c #29354A", +"($ c #323D53", +"_$ c #364258", +":$ c #333F52", +"<$ c #343F52", +"[$ c #28354A", +"}$ c #6D7B92", +"|$ c #ACBDD2", +"1$ c #5B7297", +"2$ c #495F87", +"3$ c #3A4D6B", +"4$ c #252F44", +"5$ c #8292AC", +"6$ c #586E94", +"7$ c #6782A9", +"8$ c #37404D", +"9$ c #394860", +"0$ c #475977", +"a$ c #6E7E99", +"b$ c #EBEBEA", +"c$ c #BEC3BE", +"d$ c #CFD1CE", +"e$ c #F4F2F0", +"f$ c #7D8F84", +"g$ c #6A8667", +"h$ c #D5D8D4", +"i$ c #6B6B6B", +"j$ c #9E9E9F", +"k$ c #7A8EA8", +"l$ c #2B374D", +"m$ c #313E56", +"n$ c #34425B", +"o$ c #36425A", +"p$ c #354257", +"q$ c #39465D", +"r$ c #3B4963", +"s$ c #64738B", +"t$ c #768CAD", +"u$ c #354C72", +"v$ c #2E3D5B", +"w$ c #1D273A", +"x$ c #3D4655", +"y$ c #A7BACE", +"z$ c #607799", +"A$ c #617494", +"B$ c #191B1B", +"C$ c #445675", +"D$ c #3A4A65", +"E$ c #6C7A91", +"F$ c #ECECEA", +"G$ c #EAEAE9", +"H$ c #EAEAE8", +"I$ c #F7F6F4", +"J$ c #B5B9B6", +"K$ c #D7DCD9", +"L$ c #7B8982", +"M$ c #A9AEAD", +"N$ c #517257", +"O$ c #839284", +"P$ c #DEDEDD", +"Q$ c #D8D9D9", +"R$ c #F3F2EE", +"S$ c #51698C", +"T$ c #313F55", +"U$ c #35435B", +"V$ c #36445A", +"W$ c #35425C", +"X$ c #37465D", +"Y$ c #3D4A62", +"Z$ c #516079", +"`$ c #47536A", +" % c #354561", +".% c #212F46", +"+% c #0F1624", +"@% c #1A2231", +"#% c #99A5B7", +"$% c #97A8C3", +"%% c #6A7DA0", +"&% c #394559", +"*% c #1C1D26", +"=% c #35435C", +"-% c #344159", +";% c #8E96A2", +">% c #E9E9E8", +",% c #E9E9E7", +"'% c #F6F5F5", +")% c #70827F", +"!% c #C6D0C3", +"~% c #445344", +"{% c #637161", +"]% c #4B6C4C", +"^% c #CAC8C8", +"/% c #3D5271", +"(% c #3A4863", +"_% c #384862", +":% c #3E4C65", +"<% c #424D67", +"[% c #45536C", +"}% c #4D5A74", +"|% c #66768F", +"1% c #76869D", +"2% c #2A3243", +"3% c #151A27", +"4% c #252C3A", +"5% c #B2BBCE", +"6% c #C3D0E3", +"7% c #7B8CA9", +"8% c #51627E", +"9% c #171C22", +"0% c #141920", +"a% c #242C3A", +"b% c #3F4B60", +"c% c #BDC0C6", +"d% c #EBEBE9", +"e% c #D1D7D3", +"f% c #4F5753", +"g% c #27322D", +"h% c #57655B", +"i% c #7E8B86", +"j% c #F0EFED", +"k% c #3E516C", +"l% c #3F4E68", +"m% c #3C4A64", +"n% c #4C5A74", +"o% c #55637C", +"p% c #5A6881", +"q% c #707D95", +"r% c #8493A6", +"s% c #BFC8D8", +"t% c #D8E4F0", +"u% c #CCD5E4", +"v% c #E2E8F1", +"w% c #D2DAE9", +"x% c #8D9DB7", +"y% c #526380", +"z% c #323E53", +"A% c #080B11", +"B% c #0E1217", +"C% c #272E3A", +"D% c #747D8A", +"E% c #D6D7D8", +"F% c #E8E8E7", +"G% c #E8E8E6", +"H% c #D2D2D0", +"I% c #B3B7B4", +"J% c #BFC1BF", +"K% c #EBE8E7", +"L% c #5A718E", +"M% c #3F4C65", +"N% c #38475F", +"O% c #68768E", +"P% c #6E7C92", +"Q% c #78879B", +"R% c #959FB1", +"S% c #AAB4C1", +"T% c #C4CBD8", +"U% c #D7DCE6", +"V% c #D3D9E1", +"W% c #CCD3DC", +"X% c #8C9CB4", +"Y% c #52627B", +"Z% c #38475E", +"`% c #080E1A", +" & c #060910", +".& c #0E0F14", +"+& c #4F5869", +"@& c #B6B8BC", +"#& c #DDDDDC", +"$& c #E2E2E1", +"%& c #E5E5E4", +"&& c #E6E6E5", +"*& c #F2F1F1", +"=& c #F2F1F0", +"-& c #93A8C2", +";& c #2F3E56", +">& c #6C7A93", +",& c #939EAE", +"'& c #A9B3C0", +")& c #BDC3CB", +"!& c #C8CCD2", +"~& c #CDCFD5", +"{& c #CBD2DB", +"]& c #C7CFDC", +"^& c #A4B2C7", +"/& c #4C576D", +"(& c #262C3C", +"_& c #252D3B", +":& c #232C3D", +"<& c #323B4B", +"[& c #464B5A", +"}& c #959697", +"|& c #BDBDBC", +"1& c #C9C9C9", +"2& c #D6D6D5", +"3& c #E2E2E0", +"4& c #E7E7E6", +"5& c #E6E6E4", +"6& c #C8D6E7", +"7& c #515D76", +"8& c #33415A", +"9& c #66728A", +"0& c #C0C7D2", +"a& c #CACED7", +"b& c #CCCDD2", +"c& c #CBCED1", +"d& c #C8CED3", +"e& c #C2CDDB", +"f& c #A2B1C5", +"g& c #586377", +"h& c #181F27", +"i& c #363B40", +"j& c #575857", +"k& c #626261", +"l& c #656463", +"m& c #787877", +"n& c #929291", +"o& c #9F9F9D", +"p& c #B0B0AE", +"q& c #C3C3C2", +"r& c #D1D1D0", +"s& c #DCDCDB", +"t& c #DFE4EB", +"u& c #5F708B", +"v& c #717E92", +"w& c #4F5E78", +"x& c #8391A4", +"y& c #A9B4C2", +"z& c #B1BBC5", +"A& c #BAC3CE", +"B& c #D5DDE8", +"C& c #9EA7B9", +"D& c #444D63", +"E& c #1F242F", +"F& c #2D2F33", +"G& c #424242", +"H& c #4D4D4D", +"I& c #515150", +"J& c #575756", +"K& c #60605F", +"L& c #747473", +"M& c #838381", +"N& c #989897", +"O& c #B3B3B2", +"P& c #C6C6C4", +"Q& c #D3D3D2", +"R& c #F0EFEE", +"S& c #D0D1D3", +"T& c #959FB4", +"U& c #5B697E", +"V& c #9DA7B7", +"W& c #D8DEE4", +"X& c #D4DAE1", +"Y& c #DBE1E5", +"Z& c #DBDDE3", +"`& c #818894", +" * c #1E293A", +".* c #1B222B", +"+* c #222425", +"@* c #313131", +"#* c #343434", +"$* c #363635", +"%* c #3A3A3A", +"&* c #40403F", +"** c #484847", +"=* c #585858", +"-* c #6B6B6A", +";* c #838382", +">* c #A5A5A3", +",* c #BCBCBA", +"'* c #CFCFCE", +")* c #EFEFEF", +"!* c #E3E3E2", +"~* c #D9D9D8", +"{* c #BCBCBB", +"]* c #9DA0A4", +"^* c #616678", +"/* c #2F3A4F", +"(* c #2C3649", +"_* c #394250", +":* c #111923", +"<* c #010613", +"[* c #0A0F16", +"}* c #1F2225", +"|* c #262628", +"1* c #282828", +"2* c #292828", +"3* c #292929", +"4* c #2D2D2C", +"5* c #2F2F2F", +"6* c #343433", +"7* c #3D3D3C", +"8* c #5C5C5C", +"9* c #A1A1A0", +"0* c #B9B9B8", +"a* c #CECECC", +"b* c #E4E4E2", +"c* c #D7D7D5", +"d* c #989896", +"e* c #717174", +"f* c #464B51", +"g* c #2F353C", +"h* c #23272C", +"i* c #222529", +"j* c #25272A", +"k* c #2E2F31", +"l* c #313030", +"m* c #313130", +"n* c #333332", +"o* c #363636", +"p* c #3C3C3C", +"q* c #545454", +"r* c #686867", +"s* c #848483", +"t* c #A8A8A7", +"u* c #C0C0BE", +"v* c #D3D3D1", +"w* c #D9D9D7", +"x* c #C0C0BF", +"y* c #A3A3A2", +"z* c #888886", +"A* c #6E6D6C", +"B* c #5F6061", +"C* c #585859", +"D* c #525252", +"E* c #A3A3A1", +"F* c #A2A2A0", +"G* c #A2A1A0", +"H* c #A2A2A1", +"I* c #A3A2A1", +"J* c #A4A3A2", +"K* c #A4A4A2", +"L* c #A6A6A4", +"M* c #A9A8A6", +"N* c #ABAAA8", +"O* c #AFAEAB", +"P* c #B3B1AE", +"Q* c #B5B4B1", +"R* c #B8B6B2", +"S* c #BAB8B4", +"T* c #6E6B62", +"U* c #DEDEDC", +"V* c #D1D1CF", +"W* c #BFBFBE", +"X* c #AFAFAF", +"Y* c #9F9F9F", +"Z* c #919190", +"`* c #888888", +" = c #302E28", +".= c #2E2C27", +"+= c #2D2B26", +"@= c #312F29", +"#= c #33312B", +"$= c #36332D", +"%= c #38362F", +"&= c #3D3A33", +"*= c #413E36", +"== c #47433B", +"-= c #4D4940", +";= c #514D44", +">= c #535046", +",= c #565248", +"'= c #23211D", +")= c #EBEAE9", +"!= c #DADAD9", +"~= c #C9C9C7", +"{= c #B6B6B6", +"]= c #B2B0AD", +"^= c #423F37", +"/= c #E3E2E1", +"(= c #4E4B44", +"_= c #403D36", +":= c #413E37", +"<= c #908E8A", +"[= c #46433B", +"}= c #D8D7D5", +"|= c #75726A", +"1= c #ACAAA5", +"2= c #D7D6D3", +"3= c #DBDAD8", +"4= c #86837C", +"5= c #D5D5D4", +"6= c #B6B5B1", +"7= c #4E4A41", +"8= c #D5D4D2", +"9= c #514E46", +"0= c #5E5B52", +"a= c #504D43", +"b= c #545147", +"c= c #8A8780", +"d= c #E3E3E1", +"e= c #D2D1CE", +"f= c #7B7770", +"g= c #58544A", +"h= c #7C7971", +"i= c #E7E7E5", +"j= c #CCCBC8", +"k= c #C4C2BF", +"l= c #C4C3BF", +"m= c #FBFAFA", +"n= c #817E76", +"o= c #D1D0CD", +"p= c #67635A", +"q= c #F5F4F4", +"r= c #67645B", +"s= c #908E87", +"t= c #E5E4E3", +"u= c #6F6C63", +"v= c #8B8881", +"w= c #C0BEBA", +"x= c #8C8982", +"y= c #C8C7C4", +"z= c #656259", +"A= c #77736B", +"B= c #E4E4E3", +"C= c #D3D2CF", +"D= c #76736B", +"E= c #59554B", +"F= c #E7E6E5", +"G= c #AFADA8", +"H= c #DFDFDD", +"I= c #D0CFCC", +"J= c #736F67", +"K= c #A9A9A7", +"L= c #8D8D8D", +"M= c #E1E1DF", +"N= c #9A9A9A", +"O= c #8C8C8C", +" . . . . . . . . . . . . . + @ # + . . . . . . . . . . . . . . . . . $ % ", +". & * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * = - ; . ", +". * * * * * * * * * * * * * * * * * * * > * * , , , , , , , , , , , , ' = ) ! . ", +". * * * * * * ~ * * { ] ^ / ( _ : < * * * , [ [ , , , , , , , , , , , } | * 1 2 . ", +". * * * * * * ~ * * 3 4 5 6 7 8 9 0 * , , , , , , , , , , , , , , , , } a * * ) b c ", +". * * * * * * * * * d e f g h i j k , , , , , l , , , , , , , , , m m = n * m * o p q ", +". * * * * * * * * * * r s t u v w x , , , , , l , , y z A m m m m m m B C * D E * F G . ", +". * * * * * * * * * * H I J K L M N , , , , l m z O P Q R S T ' E E E U V W X F n Y Z ` . ", +". * * * * * * * * m ...+.@., #.$.%., , , , &.*.=.-.;.>.,.'.).!.~.E d {.].^./.(._._.:.<.. ", +". * * * * [. .m * }.|.1.2.3.4.5.6.7.8.m 9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.! p.q.r.s.t.u.. ", +". * * , n.v.w.x.y.z.A.B.C.D.E.F.G.H.I.m J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.D ' } } Y.Z.Z `.s.. ", +". * * , +.+++@+#+$+%+&+*+=+E -+;+>+,+,+'+)+!+~+{+]+^+/+(+_+:+<+[+}+|+} X.1+2+3+3+3+4+<.. ", +". * * , 5+6+7+8+9+0+1 a+b+c+E d+e+` f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+= } 3+3+B B o.o.w+. ", +". * , , 9.x+y+z+A+B+c+E E E E E E d C+D+E+F+G+l+m+H+I+J+K+L+M+N+O+P+Q+B B B U {.o.R+R+C . ", +". * m m m S+T+U+V+W+X.E E d ' ' ' ' X+Y+Z+`+ @I+.@.@+@^+@@#@$@%@&@*@=@o.U {.R+R+R+-@-@C . ", +". * m #.E ' ;@>@,@'@5+' ' ' ' 5+5+m.)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@{.R+R+n.n.-@4@4@C . ", +". * E ' 5@6@7@8@9@0@} ' 5+5+m.m.m.a@b@c@d@l+e@f@g@h@i@j@k@l@m@n@o@p@q@n.n.-@r@4@4@s@s@C . ", +". * d [.t@u@v@w@x@{.m.m.m.m.m.[.m.y@z@A@B@C@D@E@F@G@H@I@J@K@L@M@N@O@P@-@-@r@s@s@< Q@R@C . ", +". * ' S@T@U@V@W@X@m.m.[.[.[.D Y@Z@`@ #.#+#@#D@##$#%#&#*#=#-#;#>#,#'#)#4@!#< < Q@~#~#{#C . ", +". * 5+3+]#^#/#(#_#[.D :#<#D X.[#}#|#1#2#3#f@4#5#6#7#8#9#0#a#b#c#d#e#f#< < g#g#~#{#h#h#C . ", +". * m.X.i#j#k#l#m#D n#o#p#q#} r#s#t#u#v#w#^@x#y#z#A#B#C#D#E#F#G#H#I#J#|+~#~#{#h#h#W K#C . ", +". * [.L#M#N#[#O#O#1+P#Q#R#S#1+T#U#V#W#X#Y#Z#`# $.$+$@$#$$$%$&$*$=$-$;$>${#h#4+4+K#K#K#C . ", +". * D ,$X.Y@[#[#} '$)$!$~${$3+]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$7$8$9$0$a$4+4+K#K#b$b$b$C . ", +". * [#[#[#n#c$d$e$f$g$h$i$j${.k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$D$E$K#F$b$b$b$G$H$C . ", +". * X.1+I$J$K$L$M$N$O$< P$Q$R$S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%*%=%-%;%b$b$b$G$>%>%,%C . ", +". * = = '%)%!%~%{%]%^%o.R+R+{./%(%_%:%<%[%}%|%1%2%3%4%5%6%7%8%9%0%a%b%c%d%>%>%>%,%,%,%C . ", +". * 2+2+2+e%f%g%h%i%j%R+-@1 -@k%l%m%n%o%p%q%r%s%t%u%v%w%x%y%z%A%B%C%D%E%F%F%>%,%F%F%G%C . ", +". * 3+U {.{.H%I%J%K%-@4@4@4@4@L%M%N%O%P%Q%R%S%T%U%V%W%X%Y%Z%`% &.&+&@&#&$&%&&&F%G%G%G%C . ", +". * {.{.R+n.*&=&=&4@4@4@s@< Q@-&M%;&>&,&'&)&!&~&{&]&^&/&(&_&:&<&[&}&|&1&2&#&3&4&4&4&5&C . ", +". * n.-@-@-@-@4@4@s@< < < ~#b$6&7&8&9&0&a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&r&s&4&5&5&%&C . ", +". * -@-@-@4@< < < < < Q@R@G$%&t&u&v&w&x&y&z&A&B&C&D&E&F&G&H&I&J&K&L&M&N&O&P&Q&%&%&%&%&C . ", +". * 1 4@< < j%R&Q@Q@R@~#h#&&P$S&T&U&V&W&X&Y&Z&`& *.*+*@*#*$*%*&***=*-*;*>*,*'*%&=+=+=+C . ", +". * < < )*Q@Q@Q@R@~#~#K#G$!*~*{*]*^*/*(*_*:*<*[*}*|*1*2*3*4*5*6*7*>@8*m&9*0*a*=+=+=+b*C . ", +". * Q@Q@Q@Q@R@~#{#K#K#K#>%!*c*,*d*e*f*g*h*i*j*k*l*m*5*@*@*n*o*p*u.q*r*s*t*u*v*=+=+b*b*C . ", +". * Q@Q@~#~#{#K#K#K#K#b$>%!*w*x*y*z*A*B*C*D*E*F*F*F*G*F*H*I*J*K*L*M*N*O*P*Q*R*S*S*S*S*S*S*S*S*T*", +". * ~#~#4+K#K#F$b$b$b$b$H$5&U*V*W*X*Y*N&Z*`*N* =.=+=+=.= =@=#=$=%=&=*===-=;=>=,=,=,=,=,=,=,=,='=", +". * 4+K#F$)=)=)=b$d%d%G$H$G%b*!=H%~=x*{*0*{=]=^=* /=(=_=:=<=* * [=* * * m.}=|=,=1=2=D X.3=4=,='=", +". * F$b$b$b$d%G$G$G$G$H$H$G%5&3&#&~*5=Q&Q&H%6=7=* * 8=9=0={.* * a=* * b=c=* d=,=e=f=g=h=* R+,='=", +". * d%d%G$G$G$G$G$H$H$G%G%G%G%i=i=i=i=i=&&5&S*,=* j=* k=l=j=* * ,=* * ,=g=* m=,=,=,=,=n=* o=,='=", +". * G$G$G$H$H$>%>%F%G%G%G%i=i=i=i=i=5&5&=+=+S*,=* p={.* q=r=* * ,=* * ,=s=* o=,=,=,=* * t=u=,='=", +". * H$>%>%>%>%F%G%G%4&4&i=i=i=i=5&5&=+=+=+=+S*,=* ,=v=w=x=,=* * ,=* * * m#y=z=,=,=,=,=A=* )=,='=", +". * >%>%>%G%4&4&4&4&i=i=i=&&&&5&=+=+=+=+B=B=S*,=* ,=,=,=,=,=* * ,=* * ,=,=,=,=,=C=D=E=4=* F=,='=", +". * ,%4&4&4&4&i=i=i=&&&&&&5&=+=+=+B=B=b*b*b*S*,=* ,=,=,=,=,=* * ,=* * ,=,=,=,=,=G=H=m=q#I=J=,='=", +". * 4&i=i=i=&&&&&&&&5&5&=+B=B=B=b*b*b*b*b*b*S*,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,='=", +". * i=&&&&&&5&5&5&5&B=B=b*b*b*b*b*b*b*b*b*d=T*'='='='='='='='='='='='='='='='='='='='='='='='='=", +". * &&5&5&5&5&%&B=b*b*b*b*b*b*b*b*b*b*d=d=d=d=K=p p p y*y*y*y*E*E*E*E*E*E*E*E*E*E*E*E*L=. ", +". & 5&5&5&=+b*b*b*b*b*b*b*b*b*b*!*d=d=d=d=d=d=d=d=3&3&3&3&M=M=M=M=M=M=M=M=M=M=M=M=M=M=N=. ", +". Y C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C C N=O=. ", +" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +" ", +" ", +" "};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/plugin.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,568 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "plugin.h" +#include "input.h" + +#include <math.h> + +#include <gtk/gtk.h> +#include <audacious/util.h> +#include <audacious/configdb.h> +#include <stdarg.h> +#include <fcntl.h> +#include <audacious/vfs.h> +#include <sys/stat.h> + +/* + * Global variables + */ +struct audmad_config_t audmad_config; /**< global configuration */ +static GStaticMutex mutex; +InputPlugin *mad_plugin = NULL; + +/* + * static variables + */ +static GThread *decode_thread; /**< the single decoder thread */ +static struct mad_info_t info; /**< info for current track */ + +#ifndef NOGUI +static GtkWidget *error_dialog = 0; +#endif + +extern gboolean scan_file(struct mad_info_t *info, gboolean fast); + +/* + * Function extname (filename) + * + * Return pointer within filename to its extenstion, or NULL if + * filename has no extension. + * + */ +static gchar *extname(const char *filename) +{ + gchar *ext = strrchr(filename, '.'); + + if (ext != NULL) + ++ext; + + return ext; +} + +void audmad_config_compute(struct audmad_config_t *config) +{ + /* set some config parameters by parsing text fields + (RG default gain, etc..) + */ + const gchar *text; + gdouble x; + + text = config->pregain_db; + x = g_strtod(text, NULL); + config->pregain_scale = (x != 0) ? pow(10.0, x / 20) : 1; +#ifdef DEBUG + g_message("pregain=[%s] -> %g -> %g", text, x, config->pregain_scale); +#endif + text = config->replaygain.default_db; + x = g_strtod(text, NULL); + config->replaygain.default_scale = (x != 0) ? pow(10.0, x / 20) : 1; +#ifdef DEBUG + g_message("RG.default=[%s] -> %g -> %g", text, x, + config->replaygain.default_scale); +#endif +} + +static void audmad_init() +{ + ConfigDb *db = NULL; + + audmad_config.fast_play_time_calc = TRUE; + audmad_config.use_xing = TRUE; + audmad_config.dither = TRUE; + audmad_config.pregain_db = "+0.00"; + audmad_config.replaygain.enable = TRUE; + audmad_config.replaygain.track_mode = FALSE; + audmad_config.hard_limit = FALSE; + audmad_config.replaygain.default_db = "-9.00"; + + db = bmp_cfg_db_open(); + if (db) { + bmp_cfg_db_get_bool(db, "MAD", "fast_play_time_calc", + &audmad_config.fast_play_time_calc); + bmp_cfg_db_get_bool(db, "MAD", "use_xing", + &audmad_config.use_xing); + bmp_cfg_db_get_bool(db, "MAD", "dither", &audmad_config.dither); + bmp_cfg_db_get_bool(db, "MAD", "hard_limit", + &audmad_config.hard_limit); + bmp_cfg_db_get_string(db, "MAD", "pregain_db", + &audmad_config.pregain_db); + bmp_cfg_db_get_bool(db, "MAD", "RG.enable", + &audmad_config.replaygain.enable); + bmp_cfg_db_get_bool(db, "MAD", "RG.track_mode", + &audmad_config.replaygain.track_mode); + bmp_cfg_db_get_string(db, "MAD", "RG.default_db", + &audmad_config.replaygain.default_db); + bmp_cfg_db_get_bool(db, "MAD", "title_override", + &audmad_config.title_override); + bmp_cfg_db_get_string(db, "MAD", "id3_format", + &audmad_config.id3_format); + + bmp_cfg_db_close(db); + } + + g_static_mutex_init(&mutex); + audmad_config_compute(&audmad_config); + +} + +static void audmad_cleanup() +{ +} + +static gboolean mp3_head_check(guint32 head) +{ + /* + * First two bytes must be a sync header (11 bits all 1) + * http://www.mp3-tech.org/programmer/frame_header.html + */ + if ((head & 0xffe00000) != 0xffe00000) + return FALSE; + + /* check if bits 18 and 19 are set */ + if (!((head >> 17) & 3)) + return FALSE; + + /* check if bits 13 - 16 are all set */ + if (((head >> 12) & 0xf) == 0xf) + return FALSE; + + /* check if bits 13 - 16 are all not set */ + if (!((head >> 12) & 0xf)) + return FALSE; + + /* check if bit 11 and 12 are both set */ + if (((head >> 10) & 0x3) == 0x3) + return FALSE; + + /* check if bits 17 - 20 are all set */ + if (((head >> 19) & 1) == 1 && + ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1) + return FALSE; + + /* not sure why we check this, but ok! */ + if ((head & 0xffff0000) == 0xfffe0000) + return FALSE; + + return TRUE; +} + +static int mp3_head_convert(const guchar * hbuf) +{ + return ((unsigned long) hbuf[0] << 24) | + ((unsigned long) hbuf[1] << 16) | + ((unsigned long) hbuf[2] << 8) | (unsigned long) hbuf[3]; +} + +// audacious vfs fast version +static int audmad_is_our_fd(char *filename, VFSFile *fin) +{ + guint32 check; + gchar *ext = extname(filename); + gint cyc = 0; + guchar buf[4]; + guchar tmp[4096]; + gint ret, i; + + /* I've seen some flac files beginning with id3 frames.. + so let's exclude known non-mp3 filename extensions */ + if (!strcasecmp(".flac", ext) || !strcasecmp(".mpc", ext) || + !strcasecmp(".tta", ext)) + return 0; + + if (fin == NULL) + return FALSE; + + vfs_fread(buf, 1, 4, fin); + + check = mp3_head_convert(buf); + + if (memcmp(buf, "ID3", 3) == 0) + return 1; + else if (memcmp(buf, "RIFF", 4) == 0) + { + vfs_fseek(fin, 4, SEEK_CUR); + vfs_fread(buf, 1, 4, fin); + + if (memcmp(buf, "RMP3", 4) == 0) + return 1; + } + + while (!mp3_head_check(check)) + { + ret = vfs_fread(tmp, 1, 4096, fin); + if (ret == 0) + return 0; + + for (i = 0; i < ret; i++) + { + check <<= 8; + check |= tmp[i]; + + if (mp3_head_check(check)) + return 1; + } + + if (++cyc > 1024) + return 0; + } + + return 1; +} + +// audacious vfs version +static int audmad_is_our_file(char *filename) +{ + VFSFile *fin = NULL; + gint rtn; + + fin = vfs_fopen(filename, "rb"); + + if (fin == NULL) + return 0; + + rtn = audmad_is_our_fd(filename, fin); + vfs_fclose(fin); + + return rtn; +} + +static void audmad_stop(InputPlayback *playback) +{ +#ifdef DEBUG + g_message("f: audmad_stop"); +#endif /* DEBUG */ + g_static_mutex_lock(&mutex); + if (decode_thread) { + info.playback->playing = 0; +#ifdef DEBUG + g_message("waiting for thread"); +#endif /* DEBUG */ + g_thread_join(decode_thread); +#ifdef DEBUG + g_message("thread done"); +#endif /* DEBUG */ + input_term(&info); + decode_thread = NULL; + } + g_static_mutex_unlock(&mutex); +} + + +static void audmad_play_file(InputPlayback *playback) +{ + gboolean rtn; + gchar *url = playback->filename; + +#ifdef DEBUG + g_message("playing %s", url); +#endif /* DEBUG */ + + if (input_init(&info, url) == FALSE) { + g_message("error initialising input"); + return; + } + + rtn = input_get_info(&info, audmad_config.fast_play_time_calc); + + if (rtn == FALSE) { + g_message("error reading input info"); + return; + } + + info.playback = playback; + info.playback->playing = 1; + + decode_thread = g_thread_create(decode_loop, (void *) &info, TRUE, NULL); +} + +static void audmad_pause(InputPlayback *playback, short paused) +{ + mad_plugin->output->pause(paused); +} + +static void audmad_seek(InputPlayback *playback, int time) +{ + /* xmms gives us the desired seek time in seconds */ + info.seek = time; +} + +/** + * Scan the given file or URL. + * Fills in the title string and the track length in milliseconds. + */ +void audmad_get_song_info(char *url, char **title, int *length) +{ + struct mad_info_t myinfo; +#ifdef DEBUG + g_message("f: audmad_get_song_info: %s", url); +#endif /* DEBUG */ + + input_init(&myinfo, url); + + if (input_get_info(&myinfo, audmad_config.fast_play_time_calc) == TRUE) + { + *title = strdup(myinfo.title); + *length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); + } + else + { + *title = strdup(url); + *length = -1; + } + + input_term(&myinfo); + +#ifdef DEBUG + g_message("e: audmad_get_song_info"); +#endif /* DEBUG */ +} + +static void audmad_about() +{ + static GtkWidget *aboutbox; + gchar *scratch; + + if (aboutbox != NULL) + return; + + scratch = g_strdup_printf( + "Audacious MPEG Audio Plugin\n" + "\n" + "Compiled against libMAD version: %d.%d.%d%s\n" + "\n" + "Written by:\n" + " William Pitcock <nenolod@sacredspiral.co.uk>\n" + " Yoshiki Yazawa <yaz@cc.rim.or.jp>\n" + "\n" + "Portions derived from XMMS-MAD by:\n" + " Sam Clegg\n" + "\n" + "ReplayGain support by:\n" + " Samuel Krempp", + MAD_VERSION_MAJOR, MAD_VERSION_MINOR, MAD_VERSION_PATCH, + MAD_VERSION_EXTRA); + + aboutbox = xmms_show_message("About MPEG Audio Plugin", + scratch, + "Ok", FALSE, NULL, NULL); + + g_free(scratch); + + g_signal_connect(G_OBJECT(aboutbox), "destroy", + G_CALLBACK(gtk_widget_destroyed), &aboutbox); +} + +/** + * Display a GTK box containing the given error message. + * Taken from mpg123 plugin. + */ +void audmad_error(char *error, ...) +{ +#ifndef NOGUI + if (!error_dialog) { + va_list args; + char string[256]; + va_start(args, error); + vsnprintf(string, 256, error, args); + va_end(args); + GDK_THREADS_ENTER(); + error_dialog = + xmms_show_message("Error", string, "Ok", FALSE, 0, 0); + gtk_signal_connect(GTK_OBJECT(error_dialog), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &error_dialog); + GDK_THREADS_LEAVE(); + } +#endif /* !NOGUI */ +} + +extern void audmad_get_file_info(char *filename); +extern void audmad_configure(); + +// tuple stuff +static TitleInput *audmad_get_song_tuple(char *filename) +{ + TitleInput *tuple = NULL; + gchar *string = NULL; + VFSFile *file; + + struct id3_file *id3file = NULL; + struct id3_tag *tag = NULL; + +#ifdef DEBUG + string = str_to_utf8(filename); + g_message("f: mad: audmad_get_song_tuple: %s", string); + if (string) { + g_free(string); + string = NULL; + } +#endif + + if ((file = vfs_fopen(filename, "rb")) != NULL) { + tuple = bmp_title_input_new(); + + id3file = id3_file_open(filename, ID3_FILE_MODE_READONLY); + + if (id3file) { + tag = id3_file_tag(id3file); + + if (tag) { + tuple->performer = + input_id3_get_string(tag, ID3_FRAME_ARTIST); + tuple->album_name = + input_id3_get_string(tag, ID3_FRAME_ALBUM); + tuple->track_name = + input_id3_get_string(tag, ID3_FRAME_TITLE); + + // year + string = NULL; + string = input_id3_get_string(tag, ID3_FRAME_YEAR); //TDRC + if (!string) + string = input_id3_get_string(tag, "TYER"); + + if (string) { + tuple->year = atoi(string); + g_free(string); + string = NULL; + } + + tuple->file_name = g_path_get_basename(filename); + tuple->file_path = g_path_get_dirname(filename); + tuple->file_ext = extname(filename); + + // length + { + char *dummy = NULL; + int length = 0; + audmad_get_song_info(filename, &dummy, &length); + tuple->length = length; + g_free(dummy); + } + + // track number + string = input_id3_get_string(tag, ID3_FRAME_TRACK); + if (string) { + tuple->track_number = atoi(string); + g_free(string); + string = NULL; + } + // genre + tuple->genre = input_id3_get_string(tag, ID3_FRAME_GENRE); +#ifdef DEBUG + g_message("genre = %s\n", tuple->genre); +#endif + // comment + tuple->comment = + input_id3_get_string(tag, ID3_FRAME_COMMENT); + + // mtime +// tuple->mtime = audmad_get_mtime(filename); + + } + id3_file_close(id3file); + } + vfs_fclose(file); + } +#ifdef DEBUG + g_message("e: mad: audmad_get_song_tuple"); +#endif + tuple->formatter = NULL; //ensure + return tuple; + +} + +/** + * Retrieve meta-information about URL. + * For local files this means ID3 tag etc. + */ +gboolean mad_get_info(struct mad_info_t * info, gboolean fast_scan) +{ + TitleInput *tuple = NULL; + + g_message("f: mad_get_info: %s", info->filename); + + if (info->remote) + return TRUE; + + tuple = audmad_get_song_tuple(info->filename); + info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ? + audmad_config.id3_format : xmms_get_gentitle_format(), tuple); + + /* scan mp3 file, decoding headers unless fast_scan is set */ + if (scan_file(info, fast_scan) == FALSE) + return FALSE; + + /* reset the input file to the start */ + vfs_rewind(info->infile); + info->offset = 0; + + /* use the filename for the title as a last resort */ + if (!info->title) + { + char *pos = strrchr(info->filename, '/'); + if (pos) + info->title = g_strdup(pos + 1); + else + info->title = g_strdup(info->filename); + } + + g_message("e: mad_get_info"); + return TRUE; +} + +static gchar *fmts[] = { "mp3", "mp2", "mpg", NULL }; + +InputPlugin *get_iplugin_info(void) +{ + if (mad_plugin != NULL) + return mad_plugin; + + mad_plugin = g_new0(InputPlugin, 1); + mad_plugin->description = g_strdup(_("MPEG Audio Plugin")); + mad_plugin->init = audmad_init; + mad_plugin->about = audmad_about; + mad_plugin->configure = audmad_configure; + mad_plugin->is_our_file = audmad_is_our_file; + mad_plugin->play_file = audmad_play_file; + mad_plugin->stop = audmad_stop; + mad_plugin->pause = audmad_pause; + mad_plugin->seek = audmad_seek; + mad_plugin->cleanup = audmad_cleanup; + mad_plugin->get_song_info = audmad_get_song_info; + mad_plugin->file_info_box = audmad_get_file_info; + mad_plugin->get_song_tuple = audmad_get_song_tuple; + mad_plugin->is_our_file_from_vfs = audmad_is_our_fd; + mad_plugin->vfs_extensions = fmts; + + return mad_plugin; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/plugin.h Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,131 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef AUD_MAD_H +#define AUD_MAD_H + +//#define DEBUG 1 +#undef DEBUG + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "MADPlug" + +#include "config.h" + +#undef PACKAGE +#define PACKAGE "audacious-plugins" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <audacious/plugin.h> +#include <audacious/titlestring.h> +#include <audacious/strings.h> +#include <audacious/i18n.h> +#include <id3tag.h> +#include <mad.h> + +#include "xing.h" + +struct mad_info_t +{ + InputPlayback *playback; + int seek; + + /* state */ + guint current_frame;/**< current mp3 frame */ + mad_timer_t pos; /**< current play position */ + + /* song info */ + guint vbr; /**< bool: is vbr? */ + guint bitrate; /**< avg. bitrate */ + guint freq; /**< sample freq. */ + guint mpeg_layer; /**< mpeg layer */ + guint mode; /**< mpeg stereo mode */ + guint channels; + gint frames; /**< total mp3 frames or -1 */ + gint fmt; /**< sample format */ + gint size; /**< file size in bytes or -1 */ + gchar *title; /**< title for xmms */ + mad_timer_t duration; + /**< total play time */ + struct id3_tag *tag; + struct id3_file *id3file; + struct xing xing; + TitleInput *tuple; /* audacious tuple data */ + + /* replay parameters */ + gboolean has_replaygain; + double replaygain_album_scale; // -1 if not set + double replaygain_track_scale; + gchar *replaygain_album_str; + gchar *replaygain_track_str; + double replaygain_album_peak; // -1 if not set + double replaygain_track_peak; + gchar *replaygain_album_peak_str; + gchar *replaygain_track_peak_str; + double mp3gain_undo; // -1 if not set + double mp3gain_minmax; + gchar *mp3gain_undo_str; + gchar *mp3gain_minmax_str; + + /* data access */ + gchar *url; + gchar *filename; + VFSFile *infile; + gint offset; + + gint remote; + struct streamdata_t *sdata; + /**< stream data for remote connections */ +}; + +struct audmad_config_t +{ + gint http_buffer_size; + gboolean fast_play_time_calc; + gboolean use_xing; + gboolean dither; + gboolean hard_limit; + gchar *pregain_db; // gain applied to samples at decoding stage. + gdouble pregain_scale; // pow(10, pregain/20) + struct + { + gboolean enable; + gboolean track_mode; + gchar *default_db; // gain used if no RG. + gdouble default_scale; + } replaygain; + gboolean title_override; + gchar *id3_format; +}; + +void audmad_config_compute(struct audmad_config_t *config); +// compute scale values from "_db" strings + +extern gpointer decode_loop(gpointer arg); +extern void audmad_error(gchar * fmt, ...); +extern void audmad_configure(); +extern InputPlugin *mad_plugin; +extern struct audmad_config_t audmad_config; + +#endif /* !AUD_MAD_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/replaygain.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,254 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * Copyright (C) 2001-2007 Samuel Krempp + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "plugin.h" +#include <stdlib.h> +#include <math.h> +#include <ctype.h> +#include <assert.h> +#include "replaygain.h" + +static unsigned long Read_LE_Uint32(const unsigned char *p) +{ + return ((unsigned long) p[0] << 0) | + ((unsigned long) p[1] << 8) | + ((unsigned long) p[2] << 16) | ((unsigned long) p[3] << 24); +} + +static int uncase_strcmp(const char *s1, const char *s2) +{ + int l1 = strlen(s1); + int l2 = strlen(s2); + int i; + for (i = 0; i < l1 && i < l2; ++i) { + if (toupper(s1[i]) < toupper(s2[i])) + return -1; + } + if (l1 == l2) + return 0; + return (l1 < l2) ? -1 : +1; +} + +static gdouble strgain2double(gchar * s, int len) +{ + gdouble res = g_strtod(s, NULL); // gain, in dB. + if (res == 0) + return 1; + return pow(10, res / 20); +} + +// Reads APE v2.0 tag ending at current pos in fp + +static int ReadAPE2Tag(VFSFile * fp, struct mad_info_t *file) +{ + unsigned long vsize; + unsigned long isize; + unsigned long flags; + char *buff; + char *p; + char *end; + struct APETagFooterStruct T, *tp; + unsigned long TagLen; + unsigned long TagCount; + + tp = &T; + + if (vfs_fseek(fp, -sizeof(T), SEEK_CUR) != 0) + return 18; + if (vfs_fread(tp, 1, sizeof(T), fp) != sizeof T) + return 2; + if (memcmp(tp->ID, "APETAGEX", sizeof(tp->ID)) != 0) + return 3; + if (Read_LE_Uint32(tp->Version) != 2000) + return 4; + TagLen = Read_LE_Uint32(tp->Length); + if (TagLen < sizeof(T)) + return 5; + if (vfs_fseek(fp, -TagLen, SEEK_CUR) != 0) + return 6; + if ((buff = (char *) malloc(TagLen)) == NULL) { + return 7; + } + if (vfs_fread(buff, 1, TagLen - sizeof(T), fp) != TagLen - sizeof(T)) { + free(buff); + return 8; + } +#ifdef DEBUG + printf("ver = %ld\n", Read_LE_Uint32(tp->Version)); + printf("taglen = %ld\n", TagLen); +#endif + + TagCount = Read_LE_Uint32(tp->TagCount); + end = buff + TagLen - sizeof(T); + for (p = buff; p < end && TagCount--;) { + vsize = Read_LE_Uint32((unsigned char *)p); + p += 4; + flags = Read_LE_Uint32((unsigned char *)p); + p += 4; + isize = strlen((char *) p); + + if (isize > 0 && vsize > 0) { + gdouble *scale = NULL; + gchar **str = NULL; + if (uncase_strcmp(p, "REPLAYGAIN_ALBUM_GAIN") == 0) { + scale = &file->replaygain_album_scale; + str = &file->replaygain_album_str; + } + if (uncase_strcmp(p, "REPLAYGAIN_TRACK_GAIN") == 0) { + scale = &file->replaygain_track_scale; + str = &file->replaygain_track_str; + } + if (str != NULL) { + assert(scale != NULL); + *scale = strgain2double(p + isize + 1, vsize); + *str = g_strndup(p + isize + 1, vsize); + } + //* case of peak info tags : */ + str = NULL; + if (uncase_strcmp(p, "REPLAYGAIN_TRACK_PEAK") == 0) { + scale = &file->replaygain_track_peak; + str = &file->replaygain_track_peak_str; + } + if (uncase_strcmp(p, "REPLAYGAIN_ALBUM_PEAK") == 0) { + scale = &file->replaygain_album_peak; + str = &file->replaygain_album_peak_str; + } + if (str != NULL) { + *scale = g_strtod(p + isize + 1, NULL); + *str = g_strndup(p + isize + 1, vsize); + } + + /* mp3gain additional tags : + the gain tag translates to scale = 2^(gain/4), + i.e., in dB : 20*log(2)/log(10)*gain/4 + -> 1.501*gain dB + */ + if (uncase_strcmp(p, "MP3GAIN_UNDO") == 0) { + str = &file->mp3gain_undo_str; + scale = &file->mp3gain_undo; + assert(4 < vsize); /* this tag is +left,+right */ + *str = g_strndup(p + isize + 1, vsize); + *scale = 1.50515 * atoi(*str); + } + if (uncase_strcmp(p, "MP3GAIN_MINMAX") == 0) { + str = &file->mp3gain_minmax_str; + scale = &file->mp3gain_minmax; + *str = g_strndup(p + isize + 1, vsize); + assert(4 < vsize); /* this tag is min,max */ + *scale = 1.50515 * (atoi((*str) + 4) - atoi(*str)); + } + } + p += isize + 1 + vsize; + } + + free(buff); + + return 0; +} + +static int find_offset(VFSFile * fp) +{ + static const char *key = "APETAGEX"; + char buff[20000]; + int N = 0; + if (vfs_fseek(fp, -20000, SEEK_CUR) != 0); + if ((N = vfs_fread(buff, 1, 20000, fp)) < 16) + return 1; + int matched = 0; + int i, last_match = -1; + for (i = 0; i < N; ++i) { + if (buff[i] == key[matched]) + ++matched; + else { + if (matched == 5 && buff[i] == 'P') + matched = 2; // got "APET" + "AP" + else + matched = 0; + } + if (matched == 8) { + last_match = i; + matched = 0; + } + } + if (last_match == -1) + return 1; + return last_match + 1 - 8 + sizeof(struct APETagFooterStruct) - N; +} + +void input_read_replaygain(struct mad_info_t *file) +{ + file->has_replaygain = FALSE; + file->replaygain_album_scale = -1; + file->replaygain_track_scale = -1; + file->mp3gain_undo = -77; + file->mp3gain_minmax = -77; + + + VFSFile *fp; + + if ((fp = vfs_fopen(file->filename, "rb")) == NULL) + return; + + if (vfs_fseek(fp, 0L, SEEK_END) != 0) { + vfs_fclose(fp); + return; + } + long pos = vfs_ftell(fp); + int res = -1; + int try = 0; + while (res != 0 && try < 10) { + // try skipping an id3 tag + vfs_fseek(fp, pos, SEEK_SET); + vfs_fseek(fp, try * -128, SEEK_CUR); + res = ReadAPE2Tag(fp, file); + ++try; + } + if (res != 0) { + // try brute search (don't want to parse all possible kinds of tags..) + vfs_fseek(fp, pos, SEEK_SET); + int offs = find_offset(fp); + if (offs <= 0) { // found ! + vfs_fseek(fp, pos, SEEK_SET); + vfs_fseek(fp, offs, SEEK_CUR); + res = ReadAPE2Tag(fp, file); + if (res != 0) { + g_message + ("hmpf, was supposed to find a tag.. offs=%d, res=%d", + offs, res); + } + } + } +#ifdef DEBUG +//#if 1 + if (res == 0) { // got APE tags, show the result + printf("RG album scale= %g, RG track scale = %g, in %s \n", + file->replaygain_album_scale, + file->replaygain_track_scale, file->filename); + } +#endif + + if (file->replaygain_album_scale != -1 + || file->replaygain_track_scale != -1) + file->has_replaygain = TRUE; + + vfs_fclose(fp); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/replaygain.h Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,44 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * Copyright (C) 2001-2007 Samuel Krempp + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "plugin.h" +#include "stdlib.h" +#include "math.h" +#include "ctype.h" + +#ifndef __replaygain_h__ +#define __replaygain_h__ + +struct APETagFooterStruct +{ + unsigned char ID[8]; + unsigned char Version[4]; + unsigned char Length[4]; + unsigned char TagCount[4]; + unsigned char Flags[4]; + unsigned char Reserved[8]; +}; + +/* prototypes */ +void input_read_replaygain(struct mad_info_t *file_info); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/xing.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,91 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Derived from: mad - MPEG audio decoder + * Copyright (C) 2000-2001 Robert Leslie + * + * 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; under version 2 of the License. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <mad.h> + +#include "xing.h" + +#define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g') + +/* + * NAME: xing->init() + * DESCRIPTION: initialize Xing structure + */ +void xing_init(struct xing *xing) +{ + xing->flags = 0; +} + +/* + * NAME: xing->parse() + * DESCRIPTION: parse a Xing VBR header + */ +int xing_parse(struct xing *xing, struct mad_bitptr ptr, + unsigned int bitlen) +{ + if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC) + goto fail; + + xing->flags = mad_bit_read(&ptr, 32); + bitlen -= 64; + + if (xing->flags & XING_FRAMES) { + if (bitlen < 32) + goto fail; + + xing->frames = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + if (xing->flags & XING_BYTES) { + if (bitlen < 32) + goto fail; + + xing->bytes = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + if (xing->flags & XING_TOC) { + int i; + + if (bitlen < 800) + goto fail; + + for (i = 0; i < 100; ++i) + xing->toc[i] = mad_bit_read(&ptr, 8); + + bitlen -= 800; + } + + if (xing->flags & XING_SCALE) { + if (bitlen < 32) + goto fail; + + xing->scale = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + return 0; + + fail: + xing->flags = 0; + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/xing.h Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,48 @@ +/* + * mad - MPEG audio decoder + * Copyright (C) 2000-2001 Robert Leslie + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XING_H +#define XING_H + +#include "mad.h" + +struct xing +{ + long flags; /* valid fields (see below) */ + unsigned long frames; /* total number of frames */ + unsigned long bytes; /* total number of bytes */ + unsigned char toc[100]; /* 100-point seek table */ + long scale; /* ?? */ +}; + +enum +{ + XING_FRAMES = 0x00000001L, + XING_BYTES = 0x00000002L, + XING_TOC = 0x00000004L, + XING_SCALE = 0x00000008L +}; + +void xing_init(struct xing *); + +#define xing_finish(xing) /* nothing */ + +int xing_parse(struct xing *, struct mad_bitptr, unsigned int); + +#endif