# HG changeset patch # User nenolod # Date 1161658509 25200 # Node ID 4b31176c198a08c21591844db314cd6ac0b9f0d5 # Parent 117bc56d906b760c013cf64280009ba38a55ff15 [svn] - 1.1.2 compatible plugin (I HATE YOU FLAC PEOPLE) diff -r 117bc56d906b -r 4b31176c198a ChangeLog --- a/ChangeLog Mon Oct 23 19:51:39 2006 -0700 +++ b/ChangeLog Mon Oct 23 19:55:09 2006 -0700 @@ -1,3 +1,30 @@ +2006-10-24 02:51:39 +0000 William Pitcock + revision [206] + flac -> flac113 + + trunk/src/flac113/Makefile | 78 - + trunk/src/flac113/charset.c | 388 +++--- + trunk/src/flac113/charset.h | 112 - + trunk/src/flac113/configure.c | 1370 +++++++++++------------ + trunk/src/flac113/configure.h | 154 +- + trunk/src/flac113/fast_float_math_hack.h | 78 - + trunk/src/flac113/file.c | 284 ++-- + trunk/src/flac113/fileinfo.c | 1000 ++++++++--------- + trunk/src/flac113/grabbag.h | 66 - + trunk/src/flac113/http.c | 1794 +++++++++++++++---------------- + trunk/src/flac113/http.h | 52 + trunk/src/flac113/plugin.c | 1436 ++++++++++++------------ + trunk/src/flac113/plugin.h | 48 + trunk/src/flac113/replaygain.c | 1330 +++++++++++----------- + trunk/src/flac113/replaygain_analysis.c | 838 +++++++------- + trunk/src/flac113/replaygain_analysis.h | 116 +- + trunk/src/flac113/replaygain_synthesis.c | 930 ++++++++-------- + trunk/src/flac113/replaygain_synthesis.h | 102 - + trunk/src/flac113/tag.c | 298 ++--- + trunk/src/flac113/tag.h | 50 + 20 files changed, 5262 insertions(+), 5262 deletions(-) + + 2006-10-24 02:46:25 +0000 William Pitcock revision [204] - revert to r198 diff -r 117bc56d906b -r 4b31176c198a src/flac112/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/Makefile Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,39 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +SUBDIRS = plugin_common + +noinst_HEADERS = \ + charset.h \ + configure.h \ + http.h \ + plugin.h \ + tag.h \ + fast_float_math_hack.h \ + replaygain_analysis.h \ + grabbag.h \ + replaygain_synthesis.h + +OBJECTIVE_LIBS = libflac$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR) + +LIBADD = $(LIBFLAC_LIBS) -L./plugin_common -lplugin_common + +SOURCES = \ + charset.c \ + configure.c \ + fileinfo.c \ + http.c \ + plugin.c \ + tag.c \ + replaygain_synthesis.c \ + replaygain.c \ + replaygain_analysis.c \ + file.c + +OBJECTS = ${SOURCES:.c=.o} + +CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) -I../../intl -I../.. $(LIBFLAC_CFLAGS) + +include ../../mk/objective.mk diff -r 117bc56d906b -r 4b31176c198a src/flac112/Makefile.lite --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/Makefile.lite Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,39 @@ +# libxmms-flac - XMMS FLAC input plugin +# Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# +# GNU makefile +# + +topdir = ../.. + +LIB_NAME = libxmms-flac +INCLUDES = $(shell xmms-config --cflags) -I./include -I$(topdir)/include -I.. +# refer to the static libs explicitly +LIBS = $(topdir)/obj/$(BUILD)/lib/libFLAC.a $(topdir)/obj/$(BUILD)/lib/libplugin_common.a $(topdir)/obj/$(BUILD)/lib/libgrabbag.a $(topdir)/obj/$(BUILD)/lib/libreplaygain_analysis.a $(topdir)/obj/$(BUILD)/lib/libreplaygain_synthesis.a -L$(ICONV_LIB_DIR) -liconv -lstdc++ -lz + +SRCS_C = \ + charset.c \ + configure.c \ + plugin.c \ + fileinfo.c \ + http.c \ + tag.c + +include $(topdir)/build/lib.mk + +# DO NOT DELETE THIS LINE -- make depend depends on it. diff -r 117bc56d906b -r 4b31176c198a src/flac112/charset.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/charset.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,194 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Almost from charset.c + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 1999-2001 Håvard Kvålen + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "plugin_common/charset.h" +#include "plugin_common/locale_hack.h" +#include "charset.h" +#include "configure.h" + + +/**************** + * Declarations * + ****************/ + +#define CHARSET_TRANS_ARRAY_LEN ( sizeof(charset_trans_array) / sizeof((charset_trans_array)[0]) ) +const CharsetInfo charset_trans_array[] = { + {N_("Arabic (IBM-864)"), "IBM864" }, + {N_("Arabic (ISO-8859-6)"), "ISO-8859-6" }, + {N_("Arabic (Windows-1256)"), "windows-1256" }, + {N_("Baltic (ISO-8859-13)"), "ISO-8859-13" }, + {N_("Baltic (ISO-8859-4)"), "ISO-8859-4" }, + {N_("Baltic (Windows-1257)"), "windows-1257" }, + {N_("Celtic (ISO-8859-14)"), "ISO-8859-14" }, + {N_("Central European (IBM-852)"), "IBM852" }, + {N_("Central European (ISO-8859-2)"), "ISO-8859-2" }, + {N_("Central European (Windows-1250)"), "windows-1250" }, + {N_("Chinese Simplified (GB18030)"), "gb18030" }, + {N_("Chinese Simplified (GB2312)"), "GB2312" }, + {N_("Chinese Traditional (Big5)"), "Big5" }, + {N_("Chinese Traditional (Big5-HKSCS)"), "Big5-HKSCS" }, + {N_("Cyrillic (IBM-855)"), "IBM855" }, + {N_("Cyrillic (ISO-8859-5)"), "ISO-8859-5" }, + {N_("Cyrillic (ISO-IR-111)"), "ISO-IR-111" }, + {N_("Cyrillic (KOI8-R)"), "KOI8-R" }, + {N_("Cyrillic (Windows-1251)"), "windows-1251" }, + {N_("Cyrillic/Russian (CP-866)"), "IBM866" }, + {N_("Cyrillic/Ukrainian (KOI8-U)"), "KOI8-U" }, + {N_("English (US-ASCII)"), "us-ascii" }, + {N_("Greek (ISO-8859-7)"), "ISO-8859-7" }, + {N_("Greek (Windows-1253)"), "windows-1253" }, + {N_("Hebrew (IBM-862)"), "IBM862" }, + {N_("Hebrew (Windows-1255)"), "windows-1255" }, + {N_("Japanese (EUC-JP)"), "EUC-JP" }, + {N_("Japanese (ISO-2022-JP)"), "ISO-2022-JP" }, + {N_("Japanese (Shift_JIS)"), "Shift_JIS" }, + {N_("Korean (EUC-KR)"), "EUC-KR" }, + {N_("Nordic (ISO-8859-10)"), "ISO-8859-10" }, + {N_("South European (ISO-8859-3)"), "ISO-8859-3" }, + {N_("Thai (TIS-620)"), "TIS-620" }, + {N_("Turkish (IBM-857)"), "IBM857" }, + {N_("Turkish (ISO-8859-9)"), "ISO-8859-9" }, + {N_("Turkish (Windows-1254)"), "windows-1254" }, + {N_("Unicode (UTF-7)"), "UTF-7" }, + {N_("Unicode (UTF-8)"), "UTF-8" }, + {N_("Unicode (UTF-16BE)"), "UTF-16BE" }, + {N_("Unicode (UTF-16LE)"), "UTF-16LE" }, + {N_("Unicode (UTF-32BE)"), "UTF-32BE" }, + {N_("Unicode (UTF-32LE)"), "UTF-32LE" }, + {N_("Vietnamese (VISCII)"), "VISCII" }, + {N_("Vietnamese (Windows-1258)"), "windows-1258" }, + {N_("Visual Hebrew (ISO-8859-8)"), "ISO-8859-8" }, + {N_("Western (IBM-850)"), "IBM850" }, + {N_("Western (ISO-8859-1)"), "ISO-8859-1" }, + {N_("Western (ISO-8859-15)"), "ISO-8859-15" }, + {N_("Western (Windows-1252)"), "windows-1252" } + + /* + * From this point, character sets aren't supported by iconv + */ +#if 0 + {N_("Arabic (IBM-864-I)"), "IBM864i" }, + {N_("Arabic (ISO-8859-6-E)"), "ISO-8859-6-E" }, + {N_("Arabic (ISO-8859-6-I)"), "ISO-8859-6-I" }, + {N_("Arabic (MacArabic)"), "x-mac-arabic" }, + {N_("Armenian (ARMSCII-8)"), "armscii-8" }, + {N_("Central European (MacCE)"), "x-mac-ce" }, + {N_("Chinese Simplified (GBK)"), "x-gbk" }, + {N_("Chinese Simplified (HZ)"), "HZ-GB-2312" }, + {N_("Chinese Traditional (EUC-TW)"), "x-euc-tw" }, + {N_("Croatian (MacCroatian)"), "x-mac-croatian" }, + {N_("Cyrillic (MacCyrillic)"), "x-mac-cyrillic" }, + {N_("Cyrillic/Ukrainian (MacUkrainian)"), "x-mac-ukrainian" }, + {N_("Farsi (MacFarsi)"), "x-mac-farsi"}, + {N_("Greek (MacGreek)"), "x-mac-greek" }, + {N_("Gujarati (MacGujarati)"), "x-mac-gujarati" }, + {N_("Gurmukhi (MacGurmukhi)"), "x-mac-gurmukhi" }, + {N_("Hebrew (ISO-8859-8-E)"), "ISO-8859-8-E" }, + {N_("Hebrew (ISO-8859-8-I)"), "ISO-8859-8-I" }, + {N_("Hebrew (MacHebrew)"), "x-mac-hebrew" }, + {N_("Hindi (MacDevanagari)"), "x-mac-devanagari" }, + {N_("Icelandic (MacIcelandic)"), "x-mac-icelandic" }, + {N_("Korean (JOHAB)"), "x-johab" }, + {N_("Korean (UHC)"), "x-windows-949" }, + {N_("Romanian (MacRomanian)"), "x-mac-romanian" }, + {N_("Turkish (MacTurkish)"), "x-mac-turkish" }, + {N_("User Defined"), "x-user-defined" }, + {N_("Vietnamese (TCVN)"), "x-viet-tcvn5712" }, + {N_("Vietnamese (VPS)"), "x-viet-vps" }, + {N_("Western (MacRoman)"), "x-mac-roman" }, + /* charsets whithout posibly translatable names */ + {"T61.8bit", "T61.8bit" }, + {"x-imap4-modified-utf7", "x-imap4-modified-utf7"}, + {"x-u-escaped", "x-u-escaped" }, + {"windows-936", "windows-936" } +#endif +}; + +/************* + * Functions * + *************/ + +/* + * Commons conversion functions + */ +char *convert_from_utf8_to_user(const char *string) +{ + return FLAC_plugin__charset_convert_string(string, "UTF-8", flac_cfg.title.user_char_set); +} + +char *convert_from_user_to_utf8(const char *string) +{ + return FLAC_plugin__charset_convert_string(string, flac_cfg.title.user_char_set, "UTF-8"); +} + +GList *Charset_Create_List (void) +{ + GList *list = NULL; + guint i; + + for (i=0; i + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __CHARSET_H__ +#define __CHARSET_H__ + + +/*************** + * Declaration * + ***************/ + +typedef struct { + gchar *charset_title; + gchar *charset_name; +} CharsetInfo; + +/* translated charset titles */ +extern const CharsetInfo charset_trans_array[]; + +/************** + * Prototypes * + **************/ + +/* + * The returned strings are malloc()ed an must be free()d by the caller + */ +char *convert_from_utf8_to_user(const char *string); +char *convert_from_user_to_utf8(const char *string); + +GList *Charset_Create_List (void); +GList *Charset_Create_List_UTF8_Only (void); +gchar *Charset_Get_Name_From_Title (const gchar *charset_title); +gchar *Charset_Get_Title_From_Name (const gchar *charset_name); + +#endif /* __CHARSET_H__ */ + diff -r 117bc56d906b -r 4b31176c198a src/flac112/configure.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/configure.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,685 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Based on mpg123 plugin + * and prefs.c - 2000/05/06 + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 2000-2002 Jerome Couderc + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "plugin_common/locale_hack.h" +#include "replaygain_synthesis.h" /* for NOISE_SHAPING_LOW */ +#include "charset.h" +#include "configure.h" + +/* + * Initialize Global Valueable + */ +flac_config_t flac_cfg = { + /* title */ + { + FALSE, /* tag_override */ + NULL, /* tag_format */ + FALSE, /* convert_char_set */ + NULL /* user_char_set */ + }, + /* stream */ + { + 100 /* KB */, /* http_buffer_size */ + 50, /* http_prebuffer */ + FALSE, /* use_proxy */ + "", /* proxy_host */ + 0, /* proxy_port */ + FALSE, /* proxy_use_auth */ + "", /* proxy_user */ + "", /* proxy_pass */ + FALSE, /* save_http_stream */ + "", /* save_http_path */ + FALSE, /* cast_title_streaming */ + FALSE /* use_udp_channel */ + }, + /* output */ + { + /* replaygain */ + { + FALSE, /* enable */ + TRUE, /* album_mode */ + 0, /* preamp */ + FALSE /* hard_limit */ + }, + /* resolution */ + { + /* normal */ + { + TRUE /* dither_24_to_16 */ + }, + /* replaygain */ + { + TRUE, /* dither */ + NOISE_SHAPING_LOW, /* noise_shaping */ + 16 /* bps_out */ + } + } + } +}; + + +static GtkWidget *flac_configurewin = NULL; +static GtkWidget *vbox, *notebook; + +static GtkWidget *title_tag_override, *title_tag_box, *title_tag_entry, *title_desc; +static GtkWidget *convert_char_set, *fileCharacterSetEntry, *userCharacterSetEntry; +static GtkWidget *replaygain_enable, *replaygain_album_mode; +static GtkWidget *replaygain_preamp_hscale, *replaygain_preamp_label, *replaygain_hard_limit; +static GtkObject *replaygain_preamp; +static GtkWidget *resolution_normal_dither_24_to_16; +static GtkWidget *resolution_replaygain_dither; +static GtkWidget *resolution_replaygain_noise_shaping_frame; +static GtkWidget *resolution_replaygain_noise_shaping_radio_none; +static GtkWidget *resolution_replaygain_noise_shaping_radio_low; +static GtkWidget *resolution_replaygain_noise_shaping_radio_medium; +static GtkWidget *resolution_replaygain_noise_shaping_radio_high; +static GtkWidget *resolution_replaygain_bps_out_frame; +static GtkWidget *resolution_replaygain_bps_out_radio_16bps; +static GtkWidget *resolution_replaygain_bps_out_radio_24bps; + +static GtkObject *streaming_size_adj, *streaming_pre_adj; +static GtkWidget *streaming_save_use, *streaming_save_entry; +#ifdef FLAC_ICECAST +static GtkWidget *streaming_cast_title, *streaming_udp_title; +#endif +static GtkWidget *streaming_save_dirbrowser; +static GtkWidget *streaming_save_hbox; + +static const gchar *gtk_entry_get_text_1 (GtkWidget *widget); +static void flac_configurewin_ok(GtkWidget * widget, gpointer data); +static void configure_destroy(GtkWidget * w, gpointer data); + +static void flac_configurewin_ok(GtkWidget * widget, gpointer data) +{ + ConfigDb *db; + + (void)widget, (void)data; /* unused arguments */ + g_free(flac_cfg.title.tag_format); + flac_cfg.title.tag_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(title_tag_entry))); + flac_cfg.title.user_char_set = Charset_Get_Name_From_Title(gtk_entry_get_text_1(userCharacterSetEntry)); + + db = bmp_cfg_db_open(); + /* title */ + bmp_cfg_db_set_bool(db, "flac", "title.tag_override", flac_cfg.title.tag_override); + bmp_cfg_db_set_string(db, "flac", "title.tag_format", flac_cfg.title.tag_format); + bmp_cfg_db_set_bool(db, "flac", "title.convert_char_set", flac_cfg.title.convert_char_set); + bmp_cfg_db_set_string(db, "flac", "title.user_char_set", flac_cfg.title.user_char_set); + /* output */ + bmp_cfg_db_set_bool(db, "flac", "output.replaygain.enable", flac_cfg.output.replaygain.enable); + bmp_cfg_db_set_bool(db, "flac", "output.replaygain.album_mode", flac_cfg.output.replaygain.album_mode); + bmp_cfg_db_set_int(db, "flac", "output.replaygain.preamp", flac_cfg.output.replaygain.preamp); + bmp_cfg_db_set_bool(db, "flac", "output.replaygain.hard_limit", flac_cfg.output.replaygain.hard_limit); + bmp_cfg_db_set_bool(db, "flac", "output.resolution.normal.dither_24_to_16", flac_cfg.output.resolution.normal.dither_24_to_16); + bmp_cfg_db_set_bool(db, "flac", "output.resolution.replaygain.dither", flac_cfg.output.resolution.replaygain.dither); + bmp_cfg_db_set_int(db, "flac", "output.resolution.replaygain.noise_shaping", flac_cfg.output.resolution.replaygain.noise_shaping); + bmp_cfg_db_set_int(db, "flac", "output.resolution.replaygain.bps_out", flac_cfg.output.resolution.replaygain.bps_out); + /* streaming */ + flac_cfg.stream.http_buffer_size = (gint) GTK_ADJUSTMENT(streaming_size_adj)->value; + flac_cfg.stream.http_prebuffer = (gint) GTK_ADJUSTMENT(streaming_pre_adj)->value; + + flac_cfg.stream.save_http_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use)); + + if (flac_cfg.stream.save_http_path != NULL) + g_free(flac_cfg.stream.save_http_path); + + flac_cfg.stream.save_http_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_save_entry))); + +#ifdef FLAC_ICECAST + flac_cfg.stream.cast_title_streaming = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_cast_title)); + flac_cfg.stream.use_udp_channel = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_udp_title)); +#endif + + bmp_cfg_db_set_int(db, "flac", "stream.http_buffer_size", flac_cfg.stream.http_buffer_size); + bmp_cfg_db_set_int(db, "flac", "stream.http_prebuffer", flac_cfg.stream.http_prebuffer); + bmp_cfg_db_set_bool(db, "flac", "stream.save_http_stream", flac_cfg.stream.save_http_stream); + bmp_cfg_db_set_string(db, "flac", "stream.save_http_path", flac_cfg.stream.save_http_path); +#ifdef FLAC_ICECAST + bmp_cfg_db_set_bool(db, "flac", "stream.cast_title_streaming", flac_cfg.stream.cast_title_streaming); + bmp_cfg_db_set_bool(db, "flac", "stream.use_udp_channel", flac_cfg.stream.use_udp_channel); +#endif + + bmp_cfg_db_close(db); + gtk_widget_destroy(flac_configurewin); +} + +static void configure_destroy(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ +} + +static void title_tag_override_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.title.tag_override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_tag_override)); + + gtk_widget_set_sensitive(title_tag_box, flac_cfg.title.tag_override); + gtk_widget_set_sensitive(title_desc, flac_cfg.title.tag_override); + +} + +static void convert_char_set_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.title.convert_char_set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(convert_char_set)); + + gtk_widget_set_sensitive(fileCharacterSetEntry, FALSE); + gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set); +} + +static void replaygain_enable_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_enable)); + + gtk_widget_set_sensitive(replaygain_album_mode, flac_cfg.output.replaygain.enable); + gtk_widget_set_sensitive(replaygain_preamp_hscale, flac_cfg.output.replaygain.enable); + gtk_widget_set_sensitive(replaygain_hard_limit, flac_cfg.output.replaygain.enable); +} + +static void replaygain_album_mode_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.album_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_album_mode)); +} + +static void replaygain_hard_limit_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.hard_limit = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_hard_limit)); +} + +static void replaygain_preamp_cb(GtkWidget *widget, gpointer data) +{ + GString *gstring = g_string_new(""); + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.preamp = (int) floor(GTK_ADJUSTMENT(replaygain_preamp)->value + 0.5); + + g_string_sprintf(gstring, "%i dB", flac_cfg.output.replaygain.preamp); + gtk_label_set_text(GTK_LABEL(replaygain_preamp_label), _(gstring->str)); +} + +static void resolution_normal_dither_24_to_16_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.normal.dither_24_to_16 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_normal_dither_24_to_16)); +} + +static void resolution_replaygain_dither_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.replaygain.dither = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_dither)); + + gtk_widget_set_sensitive(resolution_replaygain_noise_shaping_frame, flac_cfg.output.resolution.replaygain.dither); +} + +static void resolution_replaygain_noise_shaping_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.replaygain.noise_shaping = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_none))? 0 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_low))? 1 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_medium))? 2 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_high))? 3 : + 0 + ; +} + +static void resolution_replaygain_bps_out_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.replaygain.bps_out = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_16bps))? 16 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_24bps))? 24 : + 16 + ; +} + +static void streaming_save_dirbrowser_cb(gchar * dir) +{ + gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), dir); +} + +static void streaming_save_browse_cb(GtkWidget * w, gpointer data) +{ + (void) w; + (void) data; + if (!streaming_save_dirbrowser) + { + streaming_save_dirbrowser = xmms_create_dir_browser(_("Select the directory where you want to store the MPEG streams:"), + flac_cfg.stream.save_http_path, GTK_SELECTION_SINGLE, streaming_save_dirbrowser_cb); + gtk_signal_connect(GTK_OBJECT(streaming_save_dirbrowser), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &streaming_save_dirbrowser); + gtk_window_set_transient_for(GTK_WINDOW(streaming_save_dirbrowser), GTK_WINDOW(flac_configurewin)); + gtk_widget_show(streaming_save_dirbrowser); + } +} + +static void streaming_save_use_cb(GtkWidget * w, gpointer data) +{ + gboolean save_stream; + (void) w; + (void) data; + + save_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use)); + + gtk_widget_set_sensitive(streaming_save_hbox, save_stream); +} + + +void FLAC_XMMS__configure(void) +{ + GtkWidget *title_frame, *title_tag_vbox, *title_tag_label; + GtkWidget *replaygain_frame, *resolution_frame, *output_vbox, *resolution_normal_frame, *resolution_replaygain_frame; + GtkWidget *replaygain_vbox, *resolution_hbox, *resolution_normal_vbox, *resolution_replaygain_vbox; + GtkWidget *resolution_replaygain_noise_shaping_vbox; + GtkWidget *resolution_replaygain_bps_out_vbox; + GtkWidget *label, *hbox; + GtkWidget *bbox, *ok, *cancel; + GList *list; + + GtkWidget *streaming_vbox; + GtkWidget *streaming_buf_frame, *streaming_buf_hbox; + GtkWidget *streaming_size_box, *streaming_size_label, *streaming_size_spin; + GtkWidget *streaming_pre_box, *streaming_pre_label, *streaming_pre_spin; + GtkWidget *streaming_save_frame, *streaming_save_vbox; + GtkWidget *streaming_save_label, *streaming_save_browse; +#ifdef FLAC_ICECAST + GtkWidget *streaming_cast_frame, *streaming_cast_vbox; +#endif + + if (flac_configurewin != NULL) { + gdk_window_raise(flac_configurewin->window); + return; + } + flac_configurewin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_signal_connect(GTK_OBJECT(flac_configurewin), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &flac_configurewin); + gtk_signal_connect(GTK_OBJECT(flac_configurewin), "destroy", GTK_SIGNAL_FUNC(configure_destroy), &flac_configurewin); + gtk_window_set_title(GTK_WINDOW(flac_configurewin), _("Flac Configuration")); + gtk_window_set_policy(GTK_WINDOW(flac_configurewin), FALSE, FALSE, FALSE); + gtk_container_border_width(GTK_CONTAINER(flac_configurewin), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(flac_configurewin), vbox); + + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + + /* Title config.. */ + + title_frame = gtk_frame_new(_("Tag Handling")); + gtk_container_border_width(GTK_CONTAINER(title_frame), 5); + + title_tag_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(title_tag_vbox), 5); + gtk_container_add(GTK_CONTAINER(title_frame), title_tag_vbox); + + /* Convert Char Set */ + + convert_char_set = gtk_check_button_new_with_label(_("Convert Character Set")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(convert_char_set), flac_cfg.title.convert_char_set); + gtk_signal_connect(GTK_OBJECT(convert_char_set), "clicked", (GCallback)convert_char_set_cb, NULL); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), convert_char_set, FALSE, FALSE, 0); + /* Combo boxes... */ + hbox = gtk_hbox_new(FALSE,4); + gtk_container_add(GTK_CONTAINER(title_tag_vbox),hbox); + label = gtk_label_new(_("Convert character set from :")); + gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); + fileCharacterSetEntry = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(hbox),fileCharacterSetEntry,TRUE,TRUE,0); + + label = gtk_label_new (_("to :")); + gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); + userCharacterSetEntry = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(hbox),userCharacterSetEntry,TRUE,TRUE,0); + + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(fileCharacterSetEntry)->entry),FALSE); + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(userCharacterSetEntry)->entry),FALSE); + gtk_combo_set_value_in_list(GTK_COMBO(fileCharacterSetEntry),TRUE,FALSE); + gtk_combo_set_value_in_list(GTK_COMBO(userCharacterSetEntry),TRUE,FALSE); + + list = Charset_Create_List(); + gtk_combo_set_popdown_strings(GTK_COMBO(fileCharacterSetEntry),Charset_Create_List_UTF8_Only()); + gtk_combo_set_popdown_strings(GTK_COMBO(userCharacterSetEntry),list); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(userCharacterSetEntry)->entry),Charset_Get_Title_From_Name(flac_cfg.title.user_char_set)); + gtk_widget_set_sensitive(fileCharacterSetEntry, FALSE); + gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set); + + /* Override Tagging Format */ + + title_tag_override = gtk_check_button_new_with_label(_("Override generic titles")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_tag_override), flac_cfg.title.tag_override); + gtk_signal_connect(GTK_OBJECT(title_tag_override), "clicked", (GCallback)title_tag_override_cb, NULL); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_tag_override, FALSE, FALSE, 0); + + title_tag_box = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(title_tag_box, flac_cfg.title.tag_override); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_tag_box, FALSE, FALSE, 0); + + title_tag_label = gtk_label_new(_("Title format:")); + gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_label, FALSE, FALSE, 0); + + title_tag_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(title_tag_entry), flac_cfg.title.tag_format); + gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_entry, TRUE, TRUE, 0); + + title_desc = xmms_titlestring_descriptions("pafFetnygc", 2); + gtk_widget_set_sensitive(title_desc, flac_cfg.title.tag_override); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_desc, FALSE, FALSE, 0); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), title_frame, gtk_label_new(_("Title"))); + + /* Output config.. */ + + output_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(output_vbox), 5); + + /* replaygain */ + + replaygain_frame = gtk_frame_new(_("ReplayGain")); + gtk_container_border_width(GTK_CONTAINER(replaygain_frame), 5); + gtk_box_pack_start(GTK_BOX(output_vbox), replaygain_frame, TRUE, TRUE, 0); + + replaygain_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(replaygain_vbox), 5); + gtk_container_add(GTK_CONTAINER(replaygain_frame), replaygain_vbox); + + replaygain_enable = gtk_check_button_new_with_label(_("Enable ReplayGain processing")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_enable), flac_cfg.output.replaygain.enable); + gtk_signal_connect(GTK_OBJECT(replaygain_enable), "clicked", (GCallback)replaygain_enable_cb, NULL); + gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_enable, FALSE, FALSE, 0); + + replaygain_album_mode = gtk_check_button_new_with_label(_("Album mode")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_album_mode), flac_cfg.output.replaygain.album_mode); + gtk_signal_connect(GTK_OBJECT(replaygain_album_mode), "clicked", (GCallback)replaygain_album_mode_cb, NULL); + gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_album_mode, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE,3); + gtk_container_add(GTK_CONTAINER(replaygain_vbox),hbox); + label = gtk_label_new(_("Preamp:")); + gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); + replaygain_preamp = gtk_adjustment_new(flac_cfg.output.replaygain.preamp, -24.0, +24.0, 1.0, 6.0, 0.0); + gtk_signal_connect(GTK_OBJECT(replaygain_preamp), "value-changed", (GCallback)replaygain_preamp_cb, NULL); + replaygain_preamp_hscale = gtk_hscale_new(GTK_ADJUSTMENT(replaygain_preamp)); + gtk_scale_set_draw_value(GTK_SCALE(replaygain_preamp_hscale), FALSE); + gtk_box_pack_start(GTK_BOX(hbox),replaygain_preamp_hscale,TRUE,TRUE,0); + replaygain_preamp_label = gtk_label_new(_("0 dB")); + gtk_box_pack_start(GTK_BOX(hbox),replaygain_preamp_label,FALSE,FALSE,0); + gtk_adjustment_value_changed(GTK_ADJUSTMENT(replaygain_preamp)); + + replaygain_hard_limit = gtk_check_button_new_with_label(_("6dB hard limiting")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_hard_limit), flac_cfg.output.replaygain.hard_limit); + gtk_signal_connect(GTK_OBJECT(replaygain_hard_limit), "clicked", (GCallback)replaygain_hard_limit_cb, NULL); + gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_hard_limit, FALSE, FALSE, 0); + + replaygain_enable_cb(replaygain_enable, NULL); + + /* resolution */ + + resolution_frame = gtk_frame_new(_("Resolution")); + gtk_container_border_width(GTK_CONTAINER(resolution_frame), 5); + gtk_box_pack_start(GTK_BOX(output_vbox), resolution_frame, TRUE, TRUE, 0); + + resolution_hbox = gtk_hbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(resolution_hbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_frame), resolution_hbox); + + resolution_normal_frame = gtk_frame_new(_("Without ReplayGain")); + gtk_container_border_width(GTK_CONTAINER(resolution_normal_frame), 5); + gtk_box_pack_start(GTK_BOX(resolution_hbox), resolution_normal_frame, TRUE, TRUE, 0); + + resolution_normal_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(resolution_normal_vbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_normal_frame), resolution_normal_vbox); + + resolution_normal_dither_24_to_16 = gtk_check_button_new_with_label(_("Dither 24bps to 16bps")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_normal_dither_24_to_16), flac_cfg.output.resolution.normal.dither_24_to_16); + gtk_signal_connect(GTK_OBJECT(resolution_normal_dither_24_to_16), "clicked", (GCallback)resolution_normal_dither_24_to_16_cb, NULL); + gtk_box_pack_start(GTK_BOX(resolution_normal_vbox), resolution_normal_dither_24_to_16, FALSE, FALSE, 0); + + resolution_replaygain_frame = gtk_frame_new(_("With ReplayGain")); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_frame), 5); + gtk_box_pack_start(GTK_BOX(resolution_hbox), resolution_replaygain_frame, TRUE, TRUE, 0); + + resolution_replaygain_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_vbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_frame), resolution_replaygain_vbox); + + resolution_replaygain_dither = gtk_check_button_new_with_label(_("Enable dithering")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_dither), flac_cfg.output.resolution.replaygain.dither); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_dither), "clicked", (GCallback)resolution_replaygain_dither_cb, NULL); + gtk_box_pack_start(GTK_BOX(resolution_replaygain_vbox), resolution_replaygain_dither, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(hbox), 5); + gtk_box_pack_start(GTK_BOX(resolution_replaygain_vbox), hbox, TRUE, TRUE, 0); + + resolution_replaygain_noise_shaping_frame = gtk_frame_new(_("Noise shaping")); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_noise_shaping_frame), 5); + gtk_box_pack_start(GTK_BOX(hbox), resolution_replaygain_noise_shaping_frame, TRUE, TRUE, 0); + + resolution_replaygain_noise_shaping_vbox = gtk_vbutton_box_new(); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_frame), resolution_replaygain_noise_shaping_vbox); + + resolution_replaygain_noise_shaping_radio_none = gtk_radio_button_new_with_label(NULL, _("none")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 0) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_none), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_none), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_none); + + resolution_replaygain_noise_shaping_radio_low = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("low")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 1) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_low), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_low), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_low); + + resolution_replaygain_noise_shaping_radio_medium = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("medium")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 2) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_medium), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_medium), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_medium); + + resolution_replaygain_noise_shaping_radio_high = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("high")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 3) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_high), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_high), "clicked", (GCallback)resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_high); + + resolution_replaygain_bps_out_frame = gtk_frame_new(_("Dither to")); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_bps_out_frame), 5); + gtk_box_pack_start(GTK_BOX(hbox), resolution_replaygain_bps_out_frame, FALSE, FALSE, 0); + + resolution_replaygain_bps_out_vbox = gtk_vbutton_box_new(); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), 0); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_frame), resolution_replaygain_bps_out_vbox); + + resolution_replaygain_bps_out_radio_16bps = gtk_radio_button_new_with_label(NULL, _("16 bps")); + if(flac_cfg.output.resolution.replaygain.bps_out == 16) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_16bps), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_bps_out_radio_16bps), "clicked", (GCallback)resolution_replaygain_bps_out_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), resolution_replaygain_bps_out_radio_16bps); + + resolution_replaygain_bps_out_radio_24bps = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_bps_out_radio_16bps), _("24 bps")); + if(flac_cfg.output.resolution.replaygain.bps_out == 24) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_24bps), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_bps_out_radio_24bps), "clicked", (GCallback)resolution_replaygain_bps_out_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), resolution_replaygain_bps_out_radio_24bps); + + resolution_replaygain_dither_cb(resolution_replaygain_dither, NULL); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), output_vbox, gtk_label_new(_("Output"))); + + /* Streaming */ + + streaming_vbox = gtk_vbox_new(FALSE, 0); + + streaming_buf_frame = gtk_frame_new(_("Buffering:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_buf_frame, FALSE, FALSE, 0); + + streaming_buf_hbox = gtk_hbox_new(TRUE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_hbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_buf_frame), streaming_buf_hbox); + + streaming_size_box = gtk_hbox_new(FALSE, 5); + /*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_size_box,0,1,0,1); */ + gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_size_box, TRUE, TRUE, 0); + streaming_size_label = gtk_label_new(_("Buffer size (kb):")); + gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_label, FALSE, FALSE, 0); + streaming_size_adj = gtk_adjustment_new(flac_cfg.stream.http_buffer_size, 4, 4096, 4, 4, 4); + streaming_size_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_size_adj), 8, 0); + gtk_widget_set_usize(streaming_size_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_spin, FALSE, FALSE, 0); + + streaming_pre_box = gtk_hbox_new(FALSE, 5); + /*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_pre_box,1,2,0,1); */ + gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_pre_box, TRUE, TRUE, 0); + streaming_pre_label = gtk_label_new(_("Pre-buffer (percent):")); + gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_label, FALSE, FALSE, 0); + streaming_pre_adj = gtk_adjustment_new(flac_cfg.stream.http_prebuffer, 0, 90, 1, 1, 1); + streaming_pre_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_pre_adj), 1, 0); + gtk_widget_set_usize(streaming_pre_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_spin, FALSE, FALSE, 0); + + /* + * Save to disk config. + */ + streaming_save_frame = gtk_frame_new(_("Save stream to disk:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_save_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_save_frame, FALSE, FALSE, 0); + + streaming_save_vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_save_vbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_save_frame), streaming_save_vbox); + + streaming_save_use = gtk_check_button_new_with_label(_("Save stream to disk")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_save_use), flac_cfg.stream.save_http_stream); + gtk_signal_connect(GTK_OBJECT(streaming_save_use), "clicked", GTK_SIGNAL_FUNC(streaming_save_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_use, FALSE, FALSE, 0); + + streaming_save_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_save_hbox, flac_cfg.stream.save_http_stream); + gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_hbox, FALSE, FALSE, 0); + + streaming_save_label = gtk_label_new(_("Path:")); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_label, FALSE, FALSE, 0); + + streaming_save_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), flac_cfg.stream.save_http_path); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_entry, TRUE, TRUE, 0); + + streaming_save_browse = gtk_button_new_with_label(_("Browse")); + gtk_signal_connect(GTK_OBJECT(streaming_save_browse), "clicked", GTK_SIGNAL_FUNC(streaming_save_browse_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_browse, FALSE, FALSE, 0); + +#ifdef FLAC_ICECAST + streaming_cast_frame = gtk_frame_new(_("SHOUT/Icecast:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_cast_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_cast_frame, FALSE, FALSE, 0); + + streaming_cast_vbox = gtk_vbox_new(5, FALSE); + gtk_container_add(GTK_CONTAINER(streaming_cast_frame), streaming_cast_vbox); + + streaming_cast_title = gtk_check_button_new_with_label(_("Enable SHOUT/Icecast title streaming")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_cast_title), flac_cfg.stream.cast_title_streaming); + gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_cast_title, FALSE, FALSE, 0); + + streaming_udp_title = gtk_check_button_new_with_label(_("Enable Icecast Metadata UDP Channel")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_udp_title), flac_cfg.stream.use_udp_channel); + gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_udp_title, FALSE, FALSE, 0); +#endif + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), streaming_vbox, gtk_label_new(_("Streaming"))); + + /* Buttons */ + + 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); + + ok = gtk_button_new_with_label(_("Ok")); + gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(flac_configurewin_ok), NULL); + GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0); + gtk_widget_grab_default(ok); + + cancel = gtk_button_new_with_label(_("Cancel")); + gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(flac_configurewin)); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + gtk_widget_show_all(flac_configurewin); +} + +void FLAC_XMMS__aboutbox() +{ + static GtkWidget *about_window; + + if (about_window) + gdk_window_raise(about_window->window); + else + { + about_window = xmms_show_message( + _("About Flac Plugin"), + _("Flac Plugin by Josh Coalson\n" + "contributions by\n" + "......\n" + "......\n" + "and\n" + "Daisuke Shimamura\n" + "Visit http://flac.sourceforge.net/"), + _("Ok"), FALSE, NULL, NULL); + gtk_signal_connect(GTK_OBJECT(about_window), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &about_window); + } +} + +/* + * Get text of an Entry or a ComboBox + */ +static const gchar *gtk_entry_get_text_1 (GtkWidget *widget) +{ + if (GTK_IS_COMBO(widget)) + { + return gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(widget)->entry)); + }else if (GTK_IS_ENTRY(widget)) + { + return gtk_entry_get_text(GTK_ENTRY(widget)); + }else + { + return NULL; + } +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/configure.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/configure.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,77 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Based on mpg123 plugin + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __CONFIGURE_H__ +#define __CONFIGURE_H__ + +#include + +typedef struct { + struct { + gboolean tag_override; + gchar *tag_format; + gboolean convert_char_set; + gchar *user_char_set; + } title; + + struct { + gint http_buffer_size; + gint http_prebuffer; + gboolean use_proxy; + gchar *proxy_host; + gint proxy_port; + gboolean proxy_use_auth; + gchar *proxy_user; + gchar *proxy_pass; + gboolean save_http_stream; + gchar *save_http_path; + gboolean cast_title_streaming; + gboolean use_udp_channel; + } stream; + + struct { + struct { + gboolean enable; + gboolean album_mode; + gint preamp; + gboolean hard_limit; + } replaygain; + struct { + struct { + gboolean dither_24_to_16; + } normal; + struct { + gboolean dither; + gint noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */ + gint bps_out; + } replaygain; + } resolution; + } output; +} flac_config_t; + +extern flac_config_t flac_cfg; + +extern void FLAC_XMMS__configure(void); +extern void FLAC_XMMS__aboutbox(); + +#endif + + + diff -r 117bc56d906b -r 4b31176c198a src/flac112/fast_float_math_hack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/fast_float_math_hack.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,39 @@ +# ifdef __ICL /* only Intel C compiler has fmath ??? */ + + #include + +/* Nearest integer, absolute value, etc. */ + + #define ceil ceilf + #define fabs fabsf + #define floor floorf + #define fmod fmodf + #define rint rintf + #define hypot hypotf + +/* Power functions */ + + #define pow powf + #define sqrt sqrtf + +/* Exponential and logarithmic functions */ + + #define exp expf + #define log logf + #define log10 log10f + +/* Trigonometric functions */ + + #define acos acosf + #define asin asinf + #define atan atanf + #define cos cosf + #define sin sinf + #define tan tanf + +/* Hyperbolic functions */ + #define cosh coshf + #define sinh sinhf + #define tanh tanhf + +# endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/file.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/file.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,142 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for utime() */ +#include /* for chmod(), _setmode(), unlink() */ +#include /* for _O_BINARY */ +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#endif +#ifdef __CYGWIN__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include /* for stat(), maybe chmod() */ +#if defined _WIN32 && !defined __CYGWIN__ +#else +#include /* for unlink() */ +#endif +#include +#include +#include /* for strrchr() */ +#include "grabbag.h" + + +void grabbag__file_copy_metadata(const char *srcpath, const char *destpath) +{ + struct stat srcstat; + struct utimbuf srctime; + + if(0 == stat(srcpath, &srcstat)) { + srctime.actime = srcstat.st_atime; + srctime.modtime = srcstat.st_mtime; + (void)chmod(destpath, srcstat.st_mode); + (void)utime(destpath, &srctime); + } +} + +off_t grabbag__file_get_filesize(const char *srcpath) +{ + struct stat srcstat; + + if(0 == stat(srcpath, &srcstat)) + return srcstat.st_size; + else + return -1; +} + +const char *grabbag__file_get_basename(const char *srcpath) +{ + const char *p; + + p = strrchr(srcpath, '/'); + if(0 == p) { + p = strrchr(srcpath, '\\'); + if(0 == p) + return srcpath; + } + return ++p; +} + +FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only) +{ + struct stat stats; + + if(0 == stat(filename, &stats)) { +#if !defined _MSC_VER && !defined __MINGW32__ + if(read_only) { + stats.st_mode &= ~S_IWUSR; + stats.st_mode &= ~S_IWGRP; + stats.st_mode &= ~S_IWOTH; + } + else { + stats.st_mode |= S_IWUSR; + } +#else + if(read_only) + stats.st_mode &= ~S_IWRITE; + else + stats.st_mode |= S_IWRITE; +#endif + if(0 != chmod(filename, stats.st_mode)) + return false; + } + else + return false; + + return true; +} + +FLAC__bool grabbag__file_remove_file(const char *filename) +{ + return grabbag__file_change_stats(filename, /*read_only=*/false) && 0 == unlink(filename); +} + +FILE *grabbag__file_get_binary_stdin() +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdin), _O_BINARY); +#endif + + return stdin; +} + +FILE *grabbag__file_get_binary_stdout() +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdout), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdout), _O_BINARY); +#endif + + return stdout; +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/fileinfo.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/fileinfo.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,433 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * Copyright (C) 1999,2000 Håvard Kvålen + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include /* for strlen() */ +#include +#include +#include +#include +#include + +#include "audacious/util.h" +#include "FLAC/metadata.h" +#include "charset.h" +#include "configure.h" +#include "plugin_common/tags.h" +#include "plugin_common/locale_hack.h" + +static GtkWidget *window = NULL; +static GList *genre_list = NULL; +static GtkWidget *filename_entry, *tag_frame; +static GtkWidget *title_entry, *artist_entry, *album_entry, *date_entry, *tracknum_entry, *comment_entry; +static GtkWidget *genre_combo; +static GtkWidget *flac_samplerate, *flac_channels, *flac_bits_per_sample, *flac_blocksize, *flac_filesize, *flac_samples, *flac_bitrate; + +static gchar *current_filename = NULL; +static FLAC__StreamMetadata *tags_ = NULL; + +static const gchar *vorbis_genres[] = +{ + N_("Blues"), N_("Classic Rock"), N_("Country"), N_("Dance"), + N_("Disco"), N_("Funk"), N_("Grunge"), N_("Hip-Hop"), + N_("Jazz"), N_("Metal"), N_("New Age"), N_("Oldies"), + N_("Other"), N_("Pop"), N_("R&B"), N_("Rap"), N_("Reggae"), + N_("Rock"), N_("Techno"), N_("Industrial"), N_("Alternative"), + N_("Ska"), N_("Death Metal"), N_("Pranks"), N_("Soundtrack"), + N_("Euro-Techno"), N_("Ambient"), N_("Trip-Hop"), N_("Vocal"), + N_("Jazz+Funk"), N_("Fusion"), N_("Trance"), N_("Classical"), + N_("Instrumental"), N_("Acid"), N_("House"), N_("Game"), + N_("Sound Clip"), N_("Gospel"), N_("Noise"), N_("Alt"), + N_("Bass"), N_("Soul"), N_("Punk"), N_("Space"), + N_("Meditative"), N_("Instrumental Pop"), + N_("Instrumental Rock"), N_("Ethnic"), N_("Gothic"), + N_("Darkwave"), N_("Techno-Industrial"), N_("Electronic"), + N_("Pop-Folk"), N_("Eurodance"), N_("Dream"), + N_("Southern Rock"), N_("Comedy"), N_("Cult"), + N_("Gangsta Rap"), N_("Top 40"), N_("Christian Rap"), + N_("Pop/Funk"), N_("Jungle"), N_("Native American"), + N_("Cabaret"), N_("New Wave"), N_("Psychedelic"), N_("Rave"), + N_("Showtunes"), N_("Trailer"), N_("Lo-Fi"), N_("Tribal"), + N_("Acid Punk"), N_("Acid Jazz"), N_("Polka"), N_("Retro"), + N_("Musical"), N_("Rock & Roll"), N_("Hard Rock"), N_("Folk"), + N_("Folk/Rock"), N_("National Folk"), N_("Swing"), + N_("Fast-Fusion"), N_("Bebob"), N_("Latin"), N_("Revival"), + N_("Celtic"), N_("Bluegrass"), N_("Avantgarde"), + N_("Gothic Rock"), N_("Progressive Rock"), + N_("Psychedelic Rock"), N_("Symphonic Rock"), N_("Slow Rock"), + N_("Big Band"), N_("Chorus"), N_("Easy Listening"), + N_("Acoustic"), N_("Humour"), N_("Speech"), N_("Chanson"), + N_("Opera"), N_("Chamber Music"), N_("Sonata"), N_("Symphony"), + N_("Booty Bass"), N_("Primus"), N_("Porn Groove"), + N_("Satire"), N_("Slow Jam"), N_("Club"), N_("Tango"), + N_("Samba"), N_("Folklore"), N_("Ballad"), N_("Power Ballad"), + N_("Rhythmic Soul"), N_("Freestyle"), N_("Duet"), + N_("Punk Rock"), N_("Drum Solo"), N_("A Cappella"), + N_("Euro-House"), N_("Dance Hall"), N_("Goa"), + N_("Drum & Bass"), N_("Club-House"), N_("Hardcore"), + N_("Terror"), N_("Indie"), N_("BritPop"), N_("Negerpunk"), + N_("Polsk Punk"), N_("Beat"), N_("Christian Gangsta Rap"), + N_("Heavy Metal"), N_("Black Metal"), N_("Crossover"), + N_("Contemporary Christian"), N_("Christian Rock"), + N_("Merengue"), N_("Salsa"), N_("Thrash Metal"), + N_("Anime"), N_("JPop"), N_("Synthpop") +}; + +static void label_set_text(GtkWidget * label, char *str, ...) +{ + va_list args; + gchar *tempstr; + + va_start(args, str); + tempstr = g_strdup_vprintf(str, args); + va_end(args); + + gtk_label_set_text(GTK_LABEL(label), tempstr); + g_free(tempstr); +} + +static void set_entry_tag(GtkEntry * entry, const char * utf8) +{ + if(utf8) { + if(flac_cfg.title.convert_char_set) { + char *text = convert_from_utf8_to_user(utf8); + gtk_entry_set_text(entry, text); + free(text); + } + else + gtk_entry_set_text(entry, utf8); + } + else + gtk_entry_set_text(entry, ""); +} + +static void get_entry_tag(GtkEntry * entry, const char *name) +{ + gchar *text; + char *utf8; + + text = g_strdup(gtk_entry_get_text(entry)); + if (!text || strlen(text) == 0) + { + g_free(text); + return; + } + if(flac_cfg.title.convert_char_set) + utf8 = convert_from_user_to_utf8(text); + else + utf8 = text; + + FLAC_plugin__tags_add_tag_utf8(tags_, name, utf8, /*separator=*/0); + + if(flac_cfg.title.convert_char_set) + free(utf8); + g_free(text); +} + +static void show_tag() +{ + set_entry_tag(GTK_ENTRY(title_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "TITLE")); + set_entry_tag(GTK_ENTRY(artist_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "ARTIST")); + set_entry_tag(GTK_ENTRY(album_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "ALBUM")); + set_entry_tag(GTK_ENTRY(date_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "DATE")); + set_entry_tag(GTK_ENTRY(tracknum_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "TRACKNUMBER")); + set_entry_tag(GTK_ENTRY(comment_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "DESCRIPTION")); + set_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), FLAC_plugin__tags_get_tag_utf8(tags_, "GENRE")); +} + +static void save_tag(GtkWidget * w, gpointer data) +{ + (void)w; + (void)data; + + FLAC_plugin__tags_delete_tag(tags_, "TITLE"); + FLAC_plugin__tags_delete_tag(tags_, "ARTIST"); + FLAC_plugin__tags_delete_tag(tags_, "ALBUM"); + FLAC_plugin__tags_delete_tag(tags_, "DATE"); + FLAC_plugin__tags_delete_tag(tags_, "TRACKNUMBER"); + FLAC_plugin__tags_delete_tag(tags_, "DESCRIPTION"); + FLAC_plugin__tags_delete_tag(tags_, "GENRE"); + + get_entry_tag(GTK_ENTRY(title_entry) , "TITLE"); + get_entry_tag(GTK_ENTRY(artist_entry) , "ARTIST"); + get_entry_tag(GTK_ENTRY(album_entry) , "ALBUM"); + get_entry_tag(GTK_ENTRY(date_entry) , "DATE"); + get_entry_tag(GTK_ENTRY(tracknum_entry) , "TRACKNUMBER"); + get_entry_tag(GTK_ENTRY(comment_entry) , "DESCRIPTION"); + get_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), "GENRE"); + + FLAC_plugin__tags_set(current_filename, tags_); + gtk_widget_destroy(window); +} + +static void remove_tag(GtkWidget * w, gpointer data) +{ + (void)w; + (void)data; + + FLAC_plugin__tags_delete_tag(tags_, "TITLE"); + FLAC_plugin__tags_delete_tag(tags_, "ARTIST"); + FLAC_plugin__tags_delete_tag(tags_, "ALBUM"); + FLAC_plugin__tags_delete_tag(tags_, "DATE"); + FLAC_plugin__tags_delete_tag(tags_, "TRACKNUMBER"); + FLAC_plugin__tags_delete_tag(tags_, "DESCRIPTION"); + FLAC_plugin__tags_delete_tag(tags_, "GENRE"); + + FLAC_plugin__tags_set(current_filename, tags_); + gtk_widget_destroy(window); +} + +static void show_file_info() +{ + FLAC__StreamMetadata streaminfo; + struct stat _stat; + + gtk_label_set_text(GTK_LABEL(flac_samplerate), ""); + gtk_label_set_text(GTK_LABEL(flac_channels), ""); + gtk_label_set_text(GTK_LABEL(flac_bits_per_sample), ""); + gtk_label_set_text(GTK_LABEL(flac_blocksize), ""); + gtk_label_set_text(GTK_LABEL(flac_filesize), ""); + gtk_label_set_text(GTK_LABEL(flac_samples), ""); + gtk_label_set_text(GTK_LABEL(flac_bitrate), ""); + + if(!FLAC__metadata_get_streaminfo(current_filename, &streaminfo)) { + return; + } + + label_set_text(flac_samplerate, _("Samplerate: %d Hz"), streaminfo.data.stream_info.sample_rate); + label_set_text(flac_channels, _("Channels: %d"), streaminfo.data.stream_info.channels); + label_set_text(flac_bits_per_sample, _("Bits/Sample: %d"), streaminfo.data.stream_info.bits_per_sample); + if(streaminfo.data.stream_info.min_blocksize == streaminfo.data.stream_info.max_blocksize) + label_set_text(flac_blocksize, _("Blocksize: %d"), streaminfo.data.stream_info.min_blocksize); + else + label_set_text(flac_blocksize, _("Blocksize: variable\n min/max: %d/%d"), streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize); + + if (streaminfo.data.stream_info.total_samples) + label_set_text(flac_samples, _("Samples: %llu\nLength: %d:%.2d"), + streaminfo.data.stream_info.total_samples, + (int)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate / 60), + (int)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate % 60)); + + if(!stat(current_filename, &_stat) && S_ISREG(_stat.st_mode)) { + label_set_text(flac_filesize, _("Filesize: %ld B"), _stat.st_size); + if (streaminfo.data.stream_info.total_samples) + label_set_text(flac_bitrate, _("Avg. bitrate: %.1f kb/s\nCompression ratio: %.1f%%"), + 8.0 * (float)(_stat.st_size) / (1000.0 * (float)streaminfo.data.stream_info.total_samples / (float)streaminfo.data.stream_info.sample_rate), + 100.0 * (float)_stat.st_size / (float)(streaminfo.data.stream_info.bits_per_sample / 8 * streaminfo.data.stream_info.channels * streaminfo.data.stream_info.total_samples)); + } +} + +void FLAC_XMMS__file_info_box(char *filename) +{ + unsigned i; + gchar *title; + gchar *filename_utf8; + + if (!window) + { + GtkWidget *vbox, *hbox, *left_vbox, *table; + GtkWidget *flac_frame, *flac_box; + GtkWidget *label, *filename_hbox; + GtkWidget *bbox, *save, *remove, *cancel; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &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); + + label = gtk_label_new(_("Filename:")); + 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_vbox_new(FALSE, 10); + gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0); + + tag_frame = gtk_frame_new(_("Tag:")); + gtk_box_pack_start(GTK_BOX(left_vbox), tag_frame, FALSE, FALSE, 0); + + table = gtk_table_new(5, 5, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + gtk_container_add(GTK_CONTAINER(tag_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(_("Date:")); + 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); + + date_entry = gtk_entry_new(); + gtk_widget_set_usize(date_entry, 40, -1); + gtk_table_attach(GTK_TABLE(table), date_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), TRUE); + + if (!genre_list) + { + for (i = 0; i < sizeof(vorbis_genres) / sizeof(*vorbis_genres) ; i++) + genre_list = g_list_prepend(genre_list, (char *)vorbis_genres[i]); + genre_list = g_list_prepend(genre_list, ""); + genre_list = g_list_sort(genre_list, (GCompareFunc)g_strcasecmp); + } + 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_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(left_vbox), bbox, FALSE, FALSE, 0); + + save = gtk_button_new_with_label(_("Save")); + gtk_signal_connect(GTK_OBJECT(save), "clicked", GTK_SIGNAL_FUNC(save_tag), NULL); + GTK_WIDGET_SET_FLAGS(save, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0); + gtk_widget_grab_default(save); + + remove= gtk_button_new_with_label(_("Remove Tag")); + gtk_signal_connect(GTK_OBJECT(remove), "clicked", GTK_SIGNAL_FUNC(remove_tag), NULL); + GTK_WIDGET_SET_FLAGS(remove, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), remove, TRUE, TRUE, 0); + + cancel = gtk_button_new_with_label(_("Cancel")); + gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + flac_frame = gtk_frame_new(_("FLAC Info:")); + gtk_box_pack_start(GTK_BOX(hbox), flac_frame, FALSE, FALSE, 0); + + flac_box = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(flac_frame), flac_box); + gtk_container_set_border_width(GTK_CONTAINER(flac_box), 10); + gtk_box_set_spacing(GTK_BOX(flac_box), 0); + + flac_samplerate = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_samplerate), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_samplerate), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_samplerate, FALSE, FALSE, 0); + + flac_channels = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_channels), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_channels), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_channels, FALSE, FALSE, 0); + + flac_bits_per_sample = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_bits_per_sample), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_bits_per_sample), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_bits_per_sample, FALSE, FALSE, 0); + + flac_blocksize = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_blocksize), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_blocksize), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_blocksize, FALSE, FALSE, 0); + + flac_filesize = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_filesize), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_filesize), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_filesize, FALSE, FALSE, 0); + + flac_samples = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_samples), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_samples), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_samples, FALSE, FALSE, 0); + + flac_bitrate = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_bitrate), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_bitrate), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_bitrate, FALSE, FALSE, 0); + + gtk_widget_show_all(window); + } + + if(current_filename) + g_free(current_filename); + if(!(current_filename = g_strdup(filename))) + return; + + filename_utf8 = filename_to_utf8(current_filename); + title = g_strdup_printf(_("File Info - %s"), g_basename(filename_utf8)); + gtk_window_set_title(GTK_WINDOW(window), title); + g_free(title); + + gtk_entry_set_text(GTK_ENTRY(filename_entry), filename_utf8); + gtk_editable_set_position(GTK_EDITABLE(filename_entry), -1); + + g_free(filename_utf8); + + if(tags_) + FLAC_plugin__tags_destroy(&tags_); + + FLAC_plugin__tags_get(current_filename, &tags_); + + show_tag(); + show_file_info(); + + gtk_widget_set_sensitive(tag_frame, TRUE); +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/grabbag.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/grabbag.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,28 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SHARE__GRABBAG_H +#define SHARE__GRABBAG_H + +/* These can't be included by themselves, only from within grabbag.h */ +#include "grabbag/cuesheet.h" +#include "grabbag/file.h" +#include "grabbag/replaygain.h" +#include "grabbag/seektable.h" + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/grabbag/cuesheet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/grabbag/cuesheet.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,42 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__CUESHEET_H +#define GRABBAG__CUESHEET_H + +#include +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames); +void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames); + +FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset); + +void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/grabbag/file.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/grabbag/file.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,54 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* Convenience routines for manipulating files */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABAG__FILE_H +#define GRABAG__FILE_H + +#include /* for off_t */ +#include /* for FILE */ +#include "FLAC/ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void grabbag__file_copy_metadata(const char *srcpath, const char *destpath); +off_t grabbag__file_get_filesize(const char *srcpath); +const char *grabbag__file_get_basename(const char *srcpath); + +/* read_only == false means "make file writable by user" + * read_only == true means "make file read-only for everyone" + */ +FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only); + +/* attempts to make writable before unlinking */ +FLAC__bool grabbag__file_remove_file(const char *filename); + +/* these will forcibly set stdin/stdout to binary mode (for OSes that require it) */ +FILE *grabbag__file_get_binary_stdin(); +FILE *grabbag__file_get_binary_stdout(); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/grabbag/replaygain.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/grabbag/replaygain.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,64 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This wraps the replaygain_analysis lib, which is LGPL. This wrapper + * allows analysis of different input resolutions by automatically + * scaling the input signal + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__REPLAYGAIN_H +#define GRABBAG__REPLAYGAIN_H + +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED; + +FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency); + +FLAC__bool grabbag__replaygain_init(unsigned sample_frequency); + +/* 'bps' must be valid for FLAC, i.e. >=4 and <= 32 */ +FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples); + +void grabbag__replaygain_get_album(float *gain, float *peak); +void grabbag__replaygain_get_title(float *gain, float *peak); + +/* These three functions return an error string on error, or NULL if successful */ +const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime); + +FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, double *gain, double *peak); +double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/grabbag/seektable.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/grabbag/seektable.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,38 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* Convenience routines for working with seek tables */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABAG__SEEKTABLE_H +#define GRABAG__SEEKTABLE_H + +#include "FLAC/format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +FLAC__bool grabbag__seektable_convert_specification_to_template(const char *spec, FLAC__bool only_explicit_placeholders, FLAC__uint64 total_samples_to_encode, unsigned sample_rate, FLAC__StreamMetadata *seektable_template, FLAC__bool *spec_has_real_points); + +#ifdef __cplusplus +} +#endif + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/http.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/http.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,897 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/* modified for FLAC support by Steven Richman (2003) */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "configure.h" +#include "plugin_common/locale_hack.h" +#include "FLAC/format.h" +#include "plugin.h" + +#define min(x,y) ((x)<(y)?(x):(y)) +#define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z)) +#define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w)) + +static gchar *icy_name = NULL; +static gint icy_metaint = 0; + +extern InputPlugin flac_ip; + +#undef DEBUG_UDP + +/* Static udp channel functions */ +static int udp_establish_listener (gint *sock); +static int udp_check_for_data(gint sock); + +static char *flac_http_get_title(char *url); + +static gboolean prebuffering, going, eof = FALSE; +static gint sock, rd_index, wr_index, buffer_length, prebuffer_length; +static guint64 buffer_read = 0; +static gchar *buffer; +static guint64 offset; +static GThread *thread; +static GtkWidget *error_dialog = NULL; + +static FILE *output_file = NULL; + +#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) + +/* Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. */ +static void base64_encode (const gchar *s, gchar *store, gint length) +{ + /* Conversion table. */ + static gchar tbl[64] = { + 'A','B','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','f', + 'g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3', + '4','5','6','7','8','9','+','/' + }; + gint i; + guchar *p = (guchar *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) + { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) + *(p - 1) = '='; + else if (i == length + 2) + *(p - 1) = *(p - 2) = '='; + /* ...and zero-terminate it. */ + *p = '\0'; +} + +/* Create the authentication header contents for the `Basic' scheme. + This is done by encoding the string `USER:PASS' in base64 and + prepending `HEADER: Basic ' to it. */ +static gchar *basic_authentication_encode (const gchar *user, const gchar *passwd, const gchar *header) +{ + gchar *t1, *t2, *res; + gint len1 = strlen (user) + 1 + strlen (passwd); + gint len2 = BASE64_LENGTH (len1); + + t1 = g_strdup_printf("%s:%s", user, passwd); + t2 = g_malloc0(len2 + 1); + base64_encode (t1, t2, len1); + res = g_strdup_printf("%s: Basic %s\r\n", header, t2); + g_free(t2); + g_free(t1); + + return res; +} + +static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename) +{ + gchar *h, *p, *pt, *f, *temp, *ptr; + + temp = g_strdup(url); + ptr = temp; + + if (!strncasecmp("http://", ptr, 7)) + ptr += 7; + h = strchr(ptr, '@'); + f = strchr(ptr, '/'); + if (h != NULL && (!f || h < f)) + { + *h = '\0'; + p = strchr(ptr, ':'); + if (p != NULL && p < h) + { + *p = '\0'; + p++; + *pass = g_strdup(p); + } + else + *pass = NULL; + *user = g_strdup(ptr); + h++; + ptr = h; + } + else + { + *user = NULL; + *pass = NULL; + h = ptr; + } + pt = strchr(ptr, ':'); + if (pt != NULL && (f == NULL || pt < f)) + { + *pt = '\0'; + *port = atoi(pt + 1); + } + else + { + if (f) + *f = '\0'; + *port = 80; + } + *host = g_strdup(h); + + if (f) + *filename = g_strdup(f + 1); + else + *filename = NULL; + g_free(temp); +} + +void flac_http_close(void) +{ + going = FALSE; + + g_thread_join(thread); + g_free(icy_name); + icy_name = NULL; +} + + +static gint http_used(void) +{ + if (wr_index >= rd_index) + return wr_index - rd_index; + return buffer_length - (rd_index - wr_index); +} + +static gint http_free(void) +{ + if (rd_index > wr_index) + return (rd_index - wr_index) - 1; + return (buffer_length - (wr_index - rd_index)) - 1; +} + +static void http_wait_for_data(gint bytes) +{ + while ((prebuffering || http_used() < bytes) && !eof && going) + xmms_usleep(10000); +} + +static void show_error_message(gchar *error) +{ + if(!error_dialog) + { + GDK_THREADS_ENTER(); + error_dialog = xmms_show_message(_("Error"), error, _("Ok"), FALSE, + NULL, NULL); + g_signal_connect(G_OBJECT(error_dialog), + "destroy", + G_CALLBACK(gtk_widget_destroyed), + &error_dialog); + GDK_THREADS_LEAVE(); + } +} + +int flac_http_read(gpointer data, gint length) +{ + gint len, cnt, off = 0, meta_len, meta_off = 0, i; + gchar *meta_data, **tags, *temp, *title; + if (length > buffer_length) { + length = buffer_length; + } + + http_wait_for_data(length); + + if (!going) + return 0; + len = min(http_used(), length); + + while (len && http_used()) + { + if ((flac_cfg.stream.cast_title_streaming) && (icy_metaint > 0) && (buffer_read % icy_metaint) == 0 && (buffer_read > 0)) + { + meta_len = *((guchar *) buffer + rd_index) * 16; + rd_index = (rd_index + 1) % buffer_length; + if (meta_len > 0) + { + http_wait_for_data(meta_len); + meta_data = g_malloc0(meta_len); + if (http_used() >= meta_len) + { + while (meta_len) + { + cnt = min(meta_len, buffer_length - rd_index); + memcpy(meta_data + meta_off, buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_length; + meta_len -= cnt; + meta_off += cnt; + } + tags = g_strsplit(meta_data, "';", 0); + + for (i = 0; tags[i]; i++) + { + if (!strncasecmp(tags[i], "StreamTitle=", 12)) + { + temp = g_strdup(tags[i] + 13); + title = g_strdup_printf("%s (%s)", temp, icy_name); + set_track_info(title, -1); + g_free(title); + g_free(temp); + } + + } + g_strfreev(tags); + + } + g_free(meta_data); + } + if (!http_used()) + http_wait_for_data(length - off); + cnt = min3(len, buffer_length - rd_index, http_used()); + } + else if ((icy_metaint > 0) && (flac_cfg.stream.cast_title_streaming)) + cnt = min4(len, buffer_length - rd_index, http_used(), icy_metaint - (gint) (buffer_read % icy_metaint)); + else + cnt = min3(len, buffer_length - rd_index, http_used()); + if (output_file) + fwrite(buffer + rd_index, 1, cnt, output_file); + + memcpy((gchar *)data + off, buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_length; + buffer_read += cnt; + len -= cnt; + off += cnt; + } + if (!off) { + fprintf(stderr, "returning zero\n"); + } + return off; +} + +static gboolean http_check_for_data(void) +{ + + fd_set set; + struct timeval tv; + gint ret; + + tv.tv_sec = 0; + tv.tv_usec = 20000; + FD_ZERO(&set); + FD_SET(sock, &set); + ret = select(sock + 1, &set, NULL, NULL, &tv); + if (ret > 0) + return TRUE; + return FALSE; +} + +gint flac_http_read_line(gchar * buf, gint size) +{ + gint i = 0; + + while (going && i < size - 1) + { + if (http_check_for_data()) + { + if (read(sock, buf + i, 1) <= 0) + return -1; + if (buf[i] == '\n') + break; + if (buf[i] != '\r') + i++; + } + } + if (!going) + return -1; + buf[i] = '\0'; + return i; +} + +/* returns the file descriptor of the socket, or -1 on error */ +static int http_connect (gchar *url_, gboolean head, guint64 offset) +{ + gchar line[1024], *user, *pass, *host, *filename, + *status, *url, *temp, *file; + gchar *chost; + gint cnt, error, port, cport; + guint err_len; + gboolean redirect; + int udp_sock = 0; + fd_set set; + struct hostent *hp; + struct sockaddr_in address; + struct timeval tv; + + url = g_strdup (url_); + + do + { + redirect=FALSE; + + g_strstrip(url); + + parse_url(url, &user, &pass, &host, &port, &filename); + + if ((!filename || !*filename) && url[strlen(url) - 1] != '/') + temp = g_strconcat(url, "/", NULL); + else + temp = g_strdup(url); + g_free(url); + url = temp; + + chost = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_host : host; + cport = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_port : port; + + sock = socket(AF_INET, SOCK_STREAM, 0); + fcntl(sock, F_SETFL, O_NONBLOCK); + address.sin_family = AF_INET; + + status = g_strdup_printf(_("LOOKING UP %s"), chost); + flac_ip.set_info_text(status); + g_free(status); + + if (!(hp = gethostbyname(chost))) + { + status = g_strdup_printf(_("Couldn't look up host %s"), chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + } + + if (!eof) + { + memcpy(&address.sin_addr.s_addr, *(hp->h_addr_list), sizeof (address.sin_addr.s_addr)); + address.sin_port = (gint) g_htons(cport); + + status = g_strdup_printf(_("CONNECTING TO %s:%d"), chost, cport); + flac_ip.set_info_text(status); + g_free(status); + if (connect(sock, (struct sockaddr *) &address, sizeof (struct sockaddr_in)) == -1) + { + if (errno != EINPROGRESS) + { + status = g_strdup_printf(_("Couldn't connect to host %s"), chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + } + } + while (going) + { + tv.tv_sec = 0; + tv.tv_usec = 10000; + FD_ZERO(&set); + FD_SET(sock, &set); + if (select(sock + 1, NULL, &set, NULL, &tv) > 0) + { + err_len = sizeof (error); + getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &err_len); + if (error) + { + status = g_strdup_printf(_("Couldn't connect to host %s"), + chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + + } + break; + } + } + if (!eof) + { + gchar *auth = NULL, *proxy_auth = NULL; + gchar udpspace[30]; + int udp_port; + + if (flac_cfg.stream.use_udp_channel) + { + udp_port = udp_establish_listener (&udp_sock); + if (udp_port > 0) + sprintf (udpspace, "x-audiocast-udpport: %d\r\n", udp_port); + else + udp_sock = 0; + } + + if(user && pass) + auth = basic_authentication_encode(user, pass, "Authorization"); + + if (flac_cfg.stream.use_proxy) + { + file = g_strdup(url); + if(flac_cfg.stream.proxy_use_auth && flac_cfg.stream.proxy_user && flac_cfg.stream.proxy_pass) + { + proxy_auth = basic_authentication_encode(flac_cfg.stream.proxy_user, + flac_cfg.stream.proxy_pass, + "Proxy-Authorization"); + } + } + else + file = g_strconcat("/", filename, NULL); + + temp = g_strdup_printf("GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: %s/%s\r\n" + "%s%s%s%s", + file, host, "Reference FLAC Player", FLAC__VERSION_STRING, + proxy_auth ? proxy_auth : "", auth ? auth : "", + flac_cfg.stream.cast_title_streaming ? "Icy-MetaData:1\r\n" : "", + flac_cfg.stream.use_udp_channel ? udpspace : ""); + if (offset && !head) { + gchar *temp_dead = temp; + temp = g_strconcat ("%sRange: %ll-\r\n", temp, offset, NULL); + fprintf (stderr, "%s", temp); + g_free (temp_dead); + } + + g_free(file); + if(proxy_auth) + g_free(proxy_auth); + if(auth) + g_free(auth); + write(sock, temp, strlen(temp)); + write(sock, "\r\n", 2); + g_free(temp); + flac_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY")); + while (going && !eof) + { + if (http_check_for_data()) + { + if (flac_http_read_line(line, 1024)) + { + status = strchr(line, ' '); + if (status) + { + if (status[1] == '2') + break; + else if(status[1] == '3' && status[2] == '0' && status[3] == '2') + { + while(going) + { + if(http_check_for_data()) + { + if((cnt = flac_http_read_line(line, 1024)) != -1) + { + if(!cnt) + break; + if(!strncmp(line, "Location:", 9)) + { + g_free(url); + url = g_strdup(line+10); + } + } + else + { + eof=TRUE; + flac_ip.set_info_text(NULL); + break; + } + } + } + redirect=TRUE; + break; + } + else + { + status = g_strdup_printf(_("Couldn't connect to host %s\nServer reported: %s"), chost, status); + show_error_message(status); + g_free(status); + break; + } + } + } + else + { + eof = TRUE; + flac_ip.set_info_text(NULL); + } + } + } + + while (going && !redirect) + { + if (http_check_for_data()) + { + if ((cnt = flac_http_read_line(line, 1024)) != -1) + { + if (!cnt) + break; + if (!strncmp(line, "icy-name:", 9)) + icy_name = g_strdup(line + 9); + else if (!strncmp(line, "x-audiocast-name:", 17)) + icy_name = g_strdup(line + 17); + if (!strncmp(line, "icy-metaint:", 12)) + icy_metaint = atoi(line + 12); + if (!strncmp(line, "x-audiocast-udpport:", 20)) { +#ifdef DEBUG_UDP + fprintf (stderr, "Server wants udp messages on port %d\n", atoi (line + 20)); +#endif + /*udp_serverport = atoi (line + 20);*/ + } + + } + else + { + eof = TRUE; + flac_ip.set_info_text(NULL); + break; + } + } + } + } + } + + if(redirect) + { + if (output_file) + { + fclose(output_file); + output_file = NULL; + } + close(sock); + } + + g_free(user); + g_free(pass); + g_free(host); + g_free(filename); + } while(redirect); + + g_free(url); + return eof ? -1 : sock; +} + +static void *http_buffer_loop(void *arg) +{ + gchar *status, *url, *temp, *file; + gint cnt, written; + int udp_sock = 0; + + url = (gchar *) arg; + sock = http_connect (url, false, offset); + + if (sock >= 0 && flac_cfg.stream.save_http_stream) { + gchar *output_name; + file = flac_http_get_title(url); + output_name = file; + if (!strncasecmp(output_name, "http://", 7)) + output_name += 7; + temp = strrchr(output_name, '.'); + if (temp && (!strcasecmp(temp, ".fla") || !strcasecmp(temp, ".flac"))) + *temp = '\0'; + + while ((temp = strchr(output_name, '/'))) + *temp = '_'; + output_name = g_strdup_printf("%s/%s.flac", flac_cfg.stream.save_http_path, output_name); + + g_free(file); + + output_file = fopen(output_name, "wb"); + g_free(output_name); + } + + while (going) + { + + if (!http_used() && !flac_ip.output->buffer_playing()) + { + prebuffering = TRUE; + flac_ip.set_status_buffering(TRUE); + } + if (http_free() > 0 && !eof) + { + if (http_check_for_data()) + { + cnt = min(http_free(), buffer_length - wr_index); + if (cnt > 1024) + cnt = 1024; + written = read(sock, buffer + wr_index, cnt); + if (written <= 0) + { + eof = TRUE; + if (prebuffering) + { + prebuffering = FALSE; + flac_ip.set_status_buffering(FALSE); + + flac_ip.set_info_text(NULL); + } + + } + else + wr_index = (wr_index + written) % buffer_length; + } + + if (prebuffering) + { + if (http_used() > prebuffer_length) + { + prebuffering = FALSE; + flac_ip.set_status_buffering(FALSE); + flac_ip.set_info_text(NULL); + } + else + { + status = g_strdup_printf(_("PRE-BUFFERING: %dKB/%dKB"), http_used() / 1024, prebuffer_length / 1024); + flac_ip.set_info_text(status); + g_free(status); + } + + } + } + else + xmms_usleep(10000); + + if (flac_cfg.stream.use_udp_channel && udp_sock != 0) + if (udp_check_for_data(udp_sock) < 0) + { + close(udp_sock); + udp_sock = 0; + } + } + if (output_file) + { + fclose(output_file); + output_file = NULL; + } + if (sock >= 0) { + close(sock); + } + if (udp_sock != 0) + close(udp_sock); + + g_free(buffer); + g_free(url); + + g_thread_exit(NULL); + return NULL; /* avoid compiler warning */ +} + +int flac_http_open(gchar * _url, guint64 _offset) +{ + gchar *url; + + url = g_strdup(_url); + + rd_index = 0; + wr_index = 0; + buffer_length = flac_cfg.stream.http_buffer_size * 1024; + prebuffer_length = (buffer_length * flac_cfg.stream.http_prebuffer) / 100; + buffer_read = 0; + icy_metaint = 0; + prebuffering = TRUE; + flac_ip.set_status_buffering(TRUE); + going = TRUE; + eof = FALSE; + buffer = g_malloc(buffer_length); + offset = _offset; + + thread = g_thread_create((GThreadFunc)http_buffer_loop, url, TRUE, NULL); + + return 0; +} + +char *flac_http_get_title(char *url) +{ + if (icy_name) + return g_strdup(icy_name); + if (g_basename(url) && strlen(g_basename(url)) > 0) + return g_strdup(g_basename(url)); + return g_strdup(url); +} + +/* Start UDP Channel specific stuff */ + +/* Find a good local udp port and bind udp_sock to it, return the port */ +static int udp_establish_listener(int *sock) +{ + struct sockaddr_in sin; + socklen_t sinlen = sizeof (struct sockaddr_in); + +#ifdef DEBUG_UDP + fprintf (stderr,"Establishing udp listener\n"); +#endif + + if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): unable to create socket"); + return -1; + } + + memset(&sin, 0, sinlen); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = g_htonl(INADDR_ANY); + + if (bind(*sock, (struct sockaddr *)&sin, sinlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to bind socket to localhost: %s", strerror(errno)); + close(*sock); + return -1; + } + if (fcntl(*sock, F_SETFL, O_NONBLOCK) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to set flags: %s", strerror(errno)); + close(*sock); + return -1; + } + + memset(&sin, 0, sinlen); + if (getsockname(*sock, (struct sockaddr *)&sin, &sinlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to retrieve socket info: %s", strerror(errno)); + close(*sock); + return -1; + } + +#ifdef DEBUG_UDP + fprintf (stderr,"Listening on local %s:%d\n", inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); +#endif + + return g_ntohs(sin.sin_port); +} + +static int udp_check_for_data(int sock) +{ + char buf[1025], **lines; + char *valptr; + gchar *title; + gint len, i; + struct sockaddr_in from; + socklen_t fromlen; + + fromlen = sizeof(struct sockaddr_in); + + if ((len = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen)) < 0) + { + if (errno != EAGAIN) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_read_data(): Error reading from socket: %s", strerror(errno)); + return -1; + } + return 0; + } + buf[len] = '\0'; +#ifdef DEBUG_UDP + fprintf (stderr,"Received: [%s]\n", buf); +#endif + lines = g_strsplit(buf, "\n", 0); + if (!lines) + return 0; + + for (i = 0; lines[i]; i++) + { + while ((lines[i][strlen(lines[i]) - 1] == '\n') || + (lines[i][strlen(lines[i]) - 1] == '\r')) + lines[i][strlen(lines[i]) - 1] = '\0'; + + valptr = strchr(lines[i], ':'); + + if (!valptr) + continue; + else + valptr++; + + g_strstrip(valptr); + if (!strlen(valptr)) + continue; + + if (strstr(lines[i], "x-audiocast-streamtitle") != NULL) + { + title = g_strdup_printf ("%s (%s)", valptr, icy_name); + if (going) + set_track_info(title, -1); + g_free (title); + } + +#if 0 + else if (strstr(lines[i], "x-audiocast-streamlength") != NULL) + { + if (atoi(valptr) != -1) + set_track_info(NULL, atoi(valptr)); + } +#endif + + else if (strstr(lines[i], "x-audiocast-streammsg") != NULL) + { + /* set_track_info(title, -1); */ +/* xmms_show_message(_("Message"), valptr, _("Ok"), */ +/* FALSE, NULL, NULL); */ + g_message("Stream_message: %s", valptr); + } + +#if 0 + /* Use this to direct your webbrowser.. yeah right.. */ + else if (strstr(lines[i], "x-audiocast-streamurl") != NULL) + { + if (lasturl && g_strcmp (valptr, lasturl)) + { + c_message (stderr, "Song URL: %s\n", valptr); + g_free (lasturl); + lasturl = g_strdup (valptr); + } + } +#endif + else if (strstr(lines[i], "x-audiocast-udpseqnr:") != NULL) + { + gchar obuf[60]; + sprintf(obuf, "x-audiocast-ack: %ld \r\n", atol(valptr)); + if (sendto(sock, obuf, strlen(obuf), 0, (struct sockaddr *) &from, fromlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_WARNING, + "udp_check_for_data(): Unable to send ack to server: %s", strerror(errno)); + } +#ifdef DEBUG_UDP + else + fprintf(stderr,"Sent ack: %s", obuf); + fprintf (stderr,"Remote: %s:%d\n", inet_ntoa(from.sin_addr), g_ntohs(from.sin_port)); +#endif + } + } + g_strfreev(lines); + return 0; +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/http.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/http.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,26 @@ +/* libxmms-flac - XMMS FLAC input plugin + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __HTTP_H__ +#define __HTTP_H__ + +extern int flac_http_open(gchar * url, guint64 offset); +extern void flac_http_close(void); +extern int flac_http_read(gpointer data, gint length); + + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,862 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "audacious/plugin.h" +#include "audacious/output.h" +#include "audacious/util.h" +#include "audacious/configdb.h" +#include "audacious/titlestring.h" +#include "audacious/vfs.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include +#endif + +#include "FLAC/all.h" +#include "plugin_common/all.h" +#include "grabbag.h" +#include "replaygain_synthesis.h" +#include "configure.h" +#include "charset.h" +#include "http.h" +#include "tag.h" + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +extern void FLAC_XMMS__file_info_box(char *filename); + +typedef struct { + FLAC__bool abort_flag; + FLAC__bool is_playing; + FLAC__bool eof; + FLAC__bool play_thread_open; /* if true, is_playing must also be true */ + unsigned total_samples; + unsigned bits_per_sample; + unsigned channels; + unsigned sample_rate; + unsigned length_in_msec; + gchar *title; + AFormat sample_format; + unsigned sample_format_bytes_per_sample; + int seek_to_in_sec; + FLAC__bool has_replaygain; + double replay_scale; + DitherContext dither_context; +} file_info_struct; + +typedef FLAC__StreamDecoderWriteStatus (*WriteCallback) (const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +typedef void (*MetadataCallback) (const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +typedef void (*ErrorCallback) (const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +typedef struct { + FLAC__bool seekable; + void* (*new_decoder) (void); + FLAC__bool (*set_md5_checking) (void *decoder, FLAC__bool value); + FLAC__bool (*set_source) (void *decoder, const char* source); + FLAC__bool (*set_metadata_ignore_all) (void *decoder); + FLAC__bool (*set_metadata_respond) (void *decoder, FLAC__MetadataType type); + FLAC__bool (*set_write_callback) (void *decoder, WriteCallback value); + FLAC__bool (*set_metadata_callback) (void *decoder, MetadataCallback value); + FLAC__bool (*set_error_callback) (void *decoder, ErrorCallback value); + FLAC__bool (*set_client_data) (void *decoder, void *value); + FLAC__bool (*decoder_init) (void *decoder); + void (*safe_decoder_finish) (void *decoder); + void (*safe_decoder_delete) (void *decoder); + FLAC__bool (*process_until_end_of_metadata) (void *decoder); + FLAC__bool (*process_single) (void *decoder); + FLAC__bool (*is_eof) (void *decoder); +} decoder_funcs_t; + +#define NUM_DECODER_TYPES 2 +typedef enum { + DECODER_FILE, + DECODER_HTTP +} decoder_t; + +static void FLAC_XMMS__init(); +static int FLAC_XMMS__is_our_file(char *filename); +static void FLAC_XMMS__play_file(char *filename); +static void FLAC_XMMS__stop(); +static void FLAC_XMMS__pause(short p); +static void FLAC_XMMS__seek(int time); +static int FLAC_XMMS__get_time(); +static void FLAC_XMMS__cleanup(); +static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length); + +static void *play_loop_(void *arg); + +static FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fnsp); +static void file_decoder_safe_decoder_finish_(void *decoder); +static void file_decoder_safe_decoder_delete_(void *decoder); +static FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +static void init_decoder_func_tables(); +static decoder_t source_to_decoder_type (const char *source); + +InputPlugin flac_ip = +{ + NULL, + NULL, + NULL, + FLAC_XMMS__init, + FLAC_XMMS__aboutbox, + FLAC_XMMS__configure, + FLAC_XMMS__is_our_file, + NULL, + FLAC_XMMS__play_file, + FLAC_XMMS__stop, + FLAC_XMMS__pause, + FLAC_XMMS__seek, + NULL, + FLAC_XMMS__get_time, + NULL, + NULL, + FLAC_XMMS__cleanup, + NULL, + NULL, + NULL, + NULL, + FLAC_XMMS__get_song_info, + FLAC_XMMS__file_info_box, + NULL, + flac_get_tuple +}; + +#define SAMPLES_PER_WRITE 512 +#define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8)) +static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE]; +static unsigned sample_buffer_first_, sample_buffer_last_; + +static void *decoder_ = 0, *decoder2 = 0; +static file_info_struct file_info_; +static GThread *decode_thread_; +static FLAC__bool audio_error_ = false; +static FLAC__bool is_big_endian_host_; + +#define BITRATE_HIST_SEGMENT_MSEC 500 +/* 500ms * 50 = 25s should be enough */ +#define BITRATE_HIST_SIZE 50 +static unsigned bitrate_history_[BITRATE_HIST_SIZE]; + +/* A table of sets of decoder functions, indexed by decoder_t */ +static const decoder_funcs_t* DECODER_FUNCS[NUM_DECODER_TYPES]; + +static decoder_funcs_t const * decoder_func_table_, * decoder_func_table2; + + +InputPlugin *get_iplugin_info() +{ + flac_ip.description = g_strdup_printf(_("FLAC Audio Plugin")); + return &flac_ip; +} + +void set_track_info(const char* title, int length_in_msec) +{ + if (file_info_.is_playing) { + flac_ip.set_info((char*) title, length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); + } +} + +static gchar* homedir() +{ + gchar *result; + char *env_home = getenv("HOME"); + if (env_home) { + result = g_strdup (env_home); + } else { + uid_t uid = getuid(); + struct passwd *pwent; + do { + pwent = getpwent(); + } while (pwent && pwent->pw_uid != uid); + result = pwent ? g_strdup (pwent->pw_dir) : NULL; + endpwent(); + } + return result; +} + +void FLAC_XMMS__init() +{ + ConfigDb *db; + FLAC__uint32 test = 1; + gchar *tmp = NULL; + + is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true; + + memset(&flac_cfg, 0, sizeof(flac_cfg)); + + db = bmp_cfg_db_open(); + + /* title */ + + bmp_cfg_db_get_bool(db, "flac", "title.tag_override", &flac_cfg.title.tag_override); + + if(!bmp_cfg_db_get_string(db, "flac", "title.tag_format", &flac_cfg.title.tag_format)) + flac_cfg.title.tag_format = g_strdup("%p - %t"); + + bmp_cfg_db_get_bool(db, "flac", "title.convert_char_set", &flac_cfg.title.convert_char_set); + + if(!bmp_cfg_db_get_string(db, "flac", "title.user_char_set", &flac_cfg.title.user_char_set)) + flac_cfg.title.user_char_set = FLAC_plugin__charset_get_current(); + + /* replaygain */ + + bmp_cfg_db_get_bool(db, "flac", "output.replaygain.enable", &flac_cfg.output.replaygain.enable); + + bmp_cfg_db_get_bool(db, "flac", "output.replaygain.album_mode", &flac_cfg.output.replaygain.album_mode); + + if(!bmp_cfg_db_get_int(db, "flac", "output.replaygain.preamp", &flac_cfg.output.replaygain.preamp)) + flac_cfg.output.replaygain.preamp = 0; + + bmp_cfg_db_get_bool(db, "flac", "output.replaygain.hard_limit", &flac_cfg.output.replaygain.hard_limit); + + bmp_cfg_db_get_bool(db, "flac", "output.resolution.normal.dither_24_to_16", &flac_cfg.output.resolution.normal.dither_24_to_16); + bmp_cfg_db_get_bool(db, "flac", "output.resolution.replaygain.dither", &flac_cfg.output.resolution.replaygain.dither); + + if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.noise_shaping", &flac_cfg.output.resolution.replaygain.noise_shaping)) + flac_cfg.output.resolution.replaygain.noise_shaping = 1; + + if(!bmp_cfg_db_get_int(db, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out)) + flac_cfg.output.resolution.replaygain.bps_out = 16; + + /* stream */ + + bmp_cfg_db_get_int(db, "flac", "stream.http_buffer_size", &flac_cfg.stream.http_buffer_size); + bmp_cfg_db_get_int(db, "flac", "stream.http_prebuffer", &flac_cfg.stream.http_prebuffer); + bmp_cfg_db_get_bool(db, "flac", "stream.save_http_stream", &flac_cfg.stream.save_http_stream); + if (!bmp_cfg_db_get_string(db, "flac", "stream.save_http_path", &flac_cfg.stream.save_http_path) || + ! *flac_cfg.stream.save_http_path) { + /* TODO: Is this a memory leak ?? */ + /* + if (flac_cfg.stream.save_http_path) + g_free (flac_cfg.stream.save_http_path); + */ + flac_cfg.stream.save_http_path = homedir(); + } + bmp_cfg_db_get_bool(db, "flac", "stream.cast_title_streaming", &flac_cfg.stream.cast_title_streaming); + bmp_cfg_db_get_bool(db, "flac", "stream.use_udp_channel", &flac_cfg.stream.use_udp_channel); + + init_decoder_func_tables(); + decoder_func_table_ = DECODER_FUNCS [DECODER_FILE]; + decoder_ = decoder_func_table_ -> new_decoder(); + + bmp_cfg_db_get_bool(db, NULL, "use_proxy", &flac_cfg.stream.use_proxy); + bmp_cfg_db_get_string(db, NULL, "proxy_host", &flac_cfg.stream.proxy_host); + bmp_cfg_db_get_string(db, NULL, "proxy_port", &tmp); + + if (tmp != NULL) + flac_cfg.stream.proxy_port = atoi(tmp); + + bmp_cfg_db_get_bool(db, NULL, "proxy_use_auth", &flac_cfg.stream.proxy_use_auth); + bmp_cfg_db_get_string(db, NULL, "proxy_user", &flac_cfg.stream.proxy_user); + bmp_cfg_db_get_string(db, NULL, "proxy_pass", &flac_cfg.stream.proxy_pass); + + bmp_cfg_db_close(db); +} + +int FLAC_XMMS__is_our_file(char *filename) +{ + FILE *f; + FLAC__StreamMetadata streaminfo; + + if (source_to_decoder_type (filename) == DECODER_FILE) { + if(0 == (f = fopen(filename, "r"))) + return 0; + fclose(f); + if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) + return 0; + return 1; + } + + if(!safe_decoder_init_(filename, &decoder2, &decoder_func_table2)) + return 0; + + decoder_func_table2 -> safe_decoder_finish(decoder2); + return 1; +} + +void FLAC_XMMS__play_file(char *filename) +{ + FILE *f; + + sample_buffer_first_ = sample_buffer_last_ = 0; + audio_error_ = false; + file_info_.abort_flag = false; + file_info_.is_playing = false; + file_info_.eof = false; + file_info_.play_thread_open = false; + file_info_.has_replaygain = false; + + if (source_to_decoder_type (filename) == DECODER_FILE) { + if(0 == (f = fopen(filename, "r"))) + return; + fclose(f); + } + + if(decoder_ == 0) + return; + + if(!safe_decoder_init_(filename, &decoder_, &decoder_func_table_)) + return; + + if(file_info_.has_replaygain && flac_cfg.output.replaygain.enable) { + if(flac_cfg.output.resolution.replaygain.bps_out == 8) { + file_info_.sample_format = FMT_U8; + file_info_.sample_format_bytes_per_sample = 1; + } + else if(flac_cfg.output.resolution.replaygain.bps_out == 16) { + file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; + file_info_.sample_format_bytes_per_sample = 2; + } + else { + /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ + fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out); + decoder_func_table_ -> safe_decoder_finish(decoder_); + return; + } + } + else { + if(file_info_.bits_per_sample == 8) { + file_info_.sample_format = FMT_U8; + file_info_.sample_format_bytes_per_sample = 1; + } + else if(file_info_.bits_per_sample == 16 || (file_info_.bits_per_sample == 24 && flac_cfg.output.resolution.normal.dither_24_to_16)) { + file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; + file_info_.sample_format_bytes_per_sample = 2; + } + else { + /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ + fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", file_info_.bits_per_sample); + decoder_func_table_ -> safe_decoder_finish(decoder_); + return; + } + } + FLAC__replaygain_synthesis__init_dither_context(&file_info_.dither_context, file_info_.sample_format_bytes_per_sample * 8, flac_cfg.output.resolution.replaygain.noise_shaping); + file_info_.is_playing = true; + + if(flac_ip.output->open_audio(file_info_.sample_format, file_info_.sample_rate, file_info_.channels) == 0) { + audio_error_ = true; + decoder_func_table_ -> safe_decoder_finish(decoder_); + return; + } + + file_info_.title = flac_format_song_title(filename); + flac_ip.set_info(file_info_.title, file_info_.length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); + + file_info_.seek_to_in_sec = -1; + file_info_.play_thread_open = true; + decode_thread_ = g_thread_create((GThreadFunc)play_loop_, NULL, TRUE, NULL); +} + +void FLAC_XMMS__stop() +{ + if(file_info_.is_playing) { + file_info_.is_playing = false; + if(file_info_.play_thread_open) { + file_info_.play_thread_open = false; + g_thread_join(decode_thread_); + } + flac_ip.output->close_audio(); + decoder_func_table_ -> safe_decoder_finish (decoder_); + } +} + +void FLAC_XMMS__pause(short p) +{ + flac_ip.output->pause(p); +} + +void FLAC_XMMS__seek(int time) +{ + if (decoder_func_table_->seekable) { + file_info_.seek_to_in_sec = time; + file_info_.eof = false; + + while(file_info_.seek_to_in_sec != -1) + xmms_usleep(10000); + } +} + +int FLAC_XMMS__get_time() +{ + if(audio_error_) + return -2; + if(!file_info_.is_playing || (file_info_.eof && !flac_ip.output->buffer_playing())) + return -1; + else + return flac_ip.output->output_time(); +} + +void FLAC_XMMS__cleanup() +{ + g_free(flac_ip.description); + flac_ip.description = NULL; + + if (flac_cfg.title.tag_format) { + free(flac_cfg.title.tag_format); + flac_cfg.title.tag_format = NULL; + } + + if (flac_cfg.title.user_char_set) { + free(flac_cfg.title.user_char_set); + flac_cfg.title.user_char_set = NULL; + } + + if (flac_cfg.stream.proxy_host) { + free(flac_cfg.stream.proxy_host); + flac_cfg.stream.proxy_host = NULL; + } + + if (flac_cfg.stream.proxy_user) { + free(flac_cfg.stream.proxy_user); + flac_cfg.stream.proxy_user = NULL; + + } + + if (flac_cfg.stream.proxy_pass) { + free(flac_cfg.stream.proxy_pass); + flac_cfg.stream.proxy_pass = NULL; + } + + decoder_func_table_ -> safe_decoder_delete(decoder_); + decoder_ = 0; +} + +void FLAC_XMMS__get_song_info(char *filename, char **title, int *length_in_msec) +{ + FLAC__StreamMetadata streaminfo; + + if(0 == filename) + filename = ""; + + if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) { + /* @@@ how to report the error? */ + if(title) { + if (source_to_decoder_type (filename) == DECODER_FILE) { + static const char *errtitle = "Invalid FLAC File: "; + *title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); + sprintf(*title, "%s\"%s\"", errtitle, filename); + } else { + *title = NULL; + } + } + if(length_in_msec) + *length_in_msec = -1; + return; + } + + if(title) { + *title = flac_format_song_title(filename); + } + if(length_in_msec) + *length_in_msec = (unsigned)((double)streaminfo.data.stream_info.total_samples / (double)streaminfo.data.stream_info.sample_rate * 1000.0 + 0.5); +} + +/*********************************************************************** + * local routines + **********************************************************************/ + +void *play_loop_(void *arg) +{ + unsigned written_time_last = 0, bh_index_last_w = 0, bh_index_last_o = BITRATE_HIST_SIZE, blocksize = 1; + FLAC__uint64 decode_position_last = 0, decode_position_frame_last = 0, decode_position_frame = 0; + + (void)arg; + + while(file_info_.is_playing) { + if(!file_info_.eof) { + while(sample_buffer_last_ - sample_buffer_first_ < SAMPLES_PER_WRITE) { + unsigned s; + + s = sample_buffer_last_ - sample_buffer_first_; + if(decoder_func_table_ -> is_eof(decoder_)) { + file_info_.eof = true; + break; + } + else if (!decoder_func_table_ -> process_single(decoder_)) { + /*@@@ this should probably be a dialog */ + fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); + file_info_.eof = true; + break; + } + blocksize = sample_buffer_last_ - sample_buffer_first_ - s; + decode_position_frame_last = decode_position_frame; + if(!decoder_func_table_->seekable || !FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) + decode_position_frame = 0; + } + if(sample_buffer_last_ - sample_buffer_first_ > 0) { + const unsigned n = min(sample_buffer_last_ - sample_buffer_first_, SAMPLES_PER_WRITE); + int bytes = n * file_info_.channels * file_info_.sample_format_bytes_per_sample; + FLAC__byte *sample_buffer_start = sample_buffer_ + sample_buffer_first_ * file_info_.channels * file_info_.sample_format_bytes_per_sample; + unsigned written_time, bh_index_w; + FLAC__uint64 decode_position; + + sample_buffer_first_ += n; + while(flac_ip.output->buffer_free() < (int)bytes && file_info_.is_playing && file_info_.seek_to_in_sec == -1) + xmms_usleep(10000); + if(file_info_.is_playing && file_info_.seek_to_in_sec == -1) + produce_audio(flac_ip.output->written_time(), file_info_.sample_format, + file_info_.channels, bytes, sample_buffer_start, NULL); + + /* compute current bitrate */ + + written_time = flac_ip.output->written_time(); + bh_index_w = written_time / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; + if(bh_index_w != bh_index_last_w) { + bh_index_last_w = bh_index_w; + decode_position = decode_position_frame - (double)(sample_buffer_last_ - sample_buffer_first_) * (double)(decode_position_frame - decode_position_frame_last) / (double)blocksize; + bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE - 1) % BITRATE_HIST_SIZE] = + decode_position > decode_position_last && written_time > written_time_last ? + 8000 * (decode_position - decode_position_last) / (written_time - written_time_last) : + file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample; + decode_position_last = decode_position; + written_time_last = written_time; + } + } + else { + file_info_.eof = true; + xmms_usleep(10000); + } + } + else + xmms_usleep(10000); + if(decoder_func_table_->seekable && file_info_.seek_to_in_sec != -1) { + const double distance = (double)file_info_.seek_to_in_sec * 1000.0 / (double)file_info_.length_in_msec; + unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples); + if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) { + flac_ip.output->flush(file_info_.seek_to_in_sec * 1000); + bh_index_last_w = bh_index_last_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; + if(!FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) + decode_position_frame = 0; + file_info_.seek_to_in_sec = -1; + file_info_.eof = false; + sample_buffer_first_ = sample_buffer_last_ = 0; + } + } + else { + /* display the right bitrate from history */ + unsigned bh_index_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; + if(bh_index_o != bh_index_last_o && bh_index_o != bh_index_last_w && bh_index_o != (bh_index_last_w + 1) % BITRATE_HIST_SIZE) { + bh_index_last_o = bh_index_o; + flac_ip.set_info(file_info_.title, file_info_.length_in_msec, bitrate_history_[bh_index_o], file_info_.sample_rate, file_info_.channels); + } + } + } + + decoder_func_table_ -> safe_decoder_finish(decoder_); + + /* are these two calls necessary? */ + flac_ip.output->buffer_free(); + flac_ip.output->buffer_free(); + + g_free(file_info_.title); + + g_thread_exit(NULL); + return 0; /* to silence the compiler warning about not returning a value */ +} + +/*********** File decoder functions */ + +static FLAC__bool file_decoder_init (void *decoder) +{ + return FLAC__file_decoder_init( (FLAC__FileDecoder*) decoder) == FLAC__FILE_DECODER_OK; +} + +static void file_decoder_safe_decoder_finish_(void *decoder) +{ + if(decoder && FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) != FLAC__FILE_DECODER_UNINITIALIZED) + FLAC__file_decoder_finish((FLAC__FileDecoder *) decoder); +} + +static void file_decoder_safe_decoder_delete_(void *decoder) +{ + if(decoder) { + file_decoder_safe_decoder_finish_(decoder); + FLAC__file_decoder_delete( (FLAC__FileDecoder *) decoder); + } +} + +static FLAC__bool file_decoder_is_eof(void *decoder) +{ + return FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) == FLAC__FILE_DECODER_END_OF_FILE; +} + +static const decoder_funcs_t FILE_DECODER_FUNCTIONS = { + true, + (void* (*) (void)) FLAC__file_decoder_new, + (FLAC__bool (*) (void *, FLAC__bool)) FLAC__file_decoder_set_md5_checking, + (FLAC__bool (*) (void *, const char*)) FLAC__file_decoder_set_filename, + (FLAC__bool (*) (void *)) FLAC__file_decoder_set_metadata_ignore_all, + (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__file_decoder_set_metadata_respond, + (FLAC__bool (*) (void *, WriteCallback)) FLAC__file_decoder_set_write_callback, + (FLAC__bool (*) (void *, MetadataCallback)) FLAC__file_decoder_set_metadata_callback, + (FLAC__bool (*) (void *, ErrorCallback)) FLAC__file_decoder_set_error_callback, + (FLAC__bool (*) (void *, void *)) FLAC__file_decoder_set_client_data, + (FLAC__bool (*) (void *)) file_decoder_init, + (void (*) (void *)) file_decoder_safe_decoder_finish_, + (void (*) (void *)) file_decoder_safe_decoder_delete_, + (FLAC__bool (*) (void *)) FLAC__file_decoder_process_until_end_of_metadata, + (FLAC__bool (*) (void *)) FLAC__file_decoder_process_single, + file_decoder_is_eof +}; + +/*********** HTTP decoder functions */ + +static gchar *url_; + +static FLAC__bool http_decoder_set_md5_checking (void *decoder, FLAC__bool value) +{ + (void) value; + // operation unsupported + return FLAC__stream_decoder_get_state ((const FLAC__StreamDecoder *) decoder) == + FLAC__STREAM_DECODER_UNINITIALIZED; +} + +static FLAC__bool http_decoder_set_url (void *decoder, const char* url) +{ + (void) decoder; + url_ = g_strdup (url); + return true; +} + +static FLAC__StreamDecoderReadStatus http_decoder_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) +{ + (void) decoder; + (void) client_data; + *bytes = flac_http_read (buffer, *bytes); + return *bytes ? FLAC__STREAM_DECODER_READ_STATUS_CONTINUE : FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; +} + +static FLAC__bool http_decoder_init (void *decoder) +{ + flac_http_open (url_, 0); + g_free (url_); + FLAC__stream_decoder_set_read_callback (decoder, http_decoder_read_callback); + return FLAC__stream_decoder_init( (FLAC__StreamDecoder*) decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; +} + +static void http_decoder_safe_decoder_finish_(void *decoder) +{ + if(decoder && FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) { + FLAC__stream_decoder_finish((FLAC__StreamDecoder *) decoder); + flac_http_close(); + } +} + +static void http_decoder_safe_decoder_delete_(void *decoder) +{ + if(decoder) { + http_decoder_safe_decoder_finish_(decoder); + FLAC__stream_decoder_delete( (FLAC__StreamDecoder *) decoder); + } +} + +static FLAC__bool http_decoder_is_eof(void *decoder) +{ + return FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) == FLAC__STREAM_DECODER_END_OF_STREAM; +} + +static const decoder_funcs_t HTTP_DECODER_FUNCTIONS = { + false, + (void* (*) (void)) FLAC__stream_decoder_new, + http_decoder_set_md5_checking, + (FLAC__bool (*) (void *, const char*)) http_decoder_set_url, + (FLAC__bool (*) (void *)) FLAC__stream_decoder_set_metadata_ignore_all, + (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__stream_decoder_set_metadata_respond, + (FLAC__bool (*) (void *, WriteCallback)) FLAC__stream_decoder_set_write_callback, + (FLAC__bool (*) (void *, MetadataCallback)) FLAC__stream_decoder_set_metadata_callback, + (FLAC__bool (*) (void *, ErrorCallback)) FLAC__stream_decoder_set_error_callback, + (FLAC__bool (*) (void *, void *)) FLAC__stream_decoder_set_client_data, + (FLAC__bool (*) (void *)) http_decoder_init, + (void (*) (void *)) http_decoder_safe_decoder_finish_, + (void (*) (void *)) http_decoder_safe_decoder_delete_, + (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_until_end_of_metadata, + (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_single, + http_decoder_is_eof +}; + +static decoder_funcs_t const *decoder_func_table_; + +static void init_decoder_func_tables() +{ + DECODER_FUNCS [DECODER_FILE] = & FILE_DECODER_FUNCTIONS; + DECODER_FUNCS [DECODER_HTTP] = & HTTP_DECODER_FUNCTIONS; +} + +static decoder_t source_to_decoder_type (const char *source) +{ + return strncasecmp(source, "http://", 7) ? DECODER_FILE : DECODER_HTTP; +} + +static void change_decoder_if_needed (decoder_t new_decoder_type, void **decoderp, decoder_funcs_t const ** fntabp) +{ + const decoder_funcs_t *new_fn_table = DECODER_FUNCS [new_decoder_type]; + if (*fntabp != new_fn_table) { + (*fntabp)->safe_decoder_delete(*decoderp); + *fntabp = new_fn_table; + *decoderp = new_fn_table -> new_decoder(); + } +} + +FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fntabp) +{ + if(decoderp == 0 || *decoderp == 0) + return false; + + (*fntabp)->safe_decoder_finish(*decoderp); + + change_decoder_if_needed(source_to_decoder_type(filename), decoderp, fntabp); + + { + decoder_funcs_t const *fntab = *fntabp; + void *decoder = *decoderp; + + decoder = *decoderp; + fntab = *fntabp; + + fntab -> set_md5_checking(decoder, false); + fntab -> set_source(decoder, filename); + fntab -> set_metadata_ignore_all(decoder); + fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); + fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + fntab -> set_write_callback(decoder, write_callback_); + fntab -> set_metadata_callback(decoder, metadata_callback_); + fntab -> set_error_callback(decoder, error_callback_); + fntab -> set_client_data(decoder, &file_info_); + if(!fntab -> decoder_init(decoder)) + return false; + + if(!fntab -> process_until_end_of_metadata(decoder)) + return false; + } + + return true; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize; + const unsigned bits_per_sample = file_info->bits_per_sample; + FLAC__byte *sample_buffer_start; + + (void)decoder; + + if(file_info->abort_flag) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + + if((sample_buffer_last_ + wide_samples) > (SAMPLE_BUFFER_SIZE / (channels * file_info->sample_format_bytes_per_sample))) { + memmove(sample_buffer_, sample_buffer_ + sample_buffer_first_ * channels * file_info->sample_format_bytes_per_sample, (sample_buffer_last_ - sample_buffer_first_) * channels * file_info->sample_format_bytes_per_sample); + sample_buffer_last_ -= sample_buffer_first_; + sample_buffer_first_ = 0; + } + sample_buffer_start = sample_buffer_ + sample_buffer_last_ * channels * file_info->sample_format_bytes_per_sample; + if(file_info->has_replaygain && flac_cfg.output.replaygain.enable) { + FLAC__replaygain_synthesis__apply_gain( + sample_buffer_start, + !is_big_endian_host_, + file_info->sample_format_bytes_per_sample == 1, /* unsigned_data_out */ + buffer, + wide_samples, + channels, + bits_per_sample, + file_info->sample_format_bytes_per_sample * 8, + file_info->replay_scale, + flac_cfg.output.replaygain.hard_limit, + flac_cfg.output.resolution.replaygain.dither, + &file_info->dither_context + ); + } + else if(is_big_endian_host_) { + FLAC__plugin_common__pack_pcm_signed_big_endian( + sample_buffer_start, + buffer, + wide_samples, + channels, + bits_per_sample, + file_info->sample_format_bytes_per_sample * 8 + ); + } + else { + FLAC__plugin_common__pack_pcm_signed_little_endian( + sample_buffer_start, + buffer, + wide_samples, + channels, + bits_per_sample, + file_info->sample_format_bytes_per_sample * 8 + ); + } + + sample_buffer_last_ += wide_samples; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + (void)decoder; + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + FLAC__ASSERT(metadata->data.stream_info.total_samples < FLAC__U64L(0x100000000)); /* this plugin can only handle < 4 gigasamples */ + file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff); + file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample; + file_info->channels = metadata->data.stream_info.channels; + file_info->sample_rate = metadata->data.stream_info.sample_rate; + file_info->length_in_msec = (unsigned)((double)file_info->total_samples / (double)file_info->sample_rate * 1000.0 + 0.5); + } + else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + double gain, peak; + if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, &gain, &peak)) { + file_info->has_replaygain = true; + file_info->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit); + } + } +} + +void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + (void)decoder; + if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + file_info->abort_flag = true; +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,24 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLAC__PLUGIN_XMMS__PLUGIN_H +#define FLAC__PLUGIN_XMMS__PLUGIN_H + +void set_track_info(const char* title, int length_in_msec); + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/Makefile Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,26 @@ +include ../../../mk/rules.mk +include ../../../mk/init.mk + +CFLAGS += $(PICFLAGS) -I.. -I../../.. + +OBJECTIVE_LIBS_NOINST = libplugin_common.a + +noinst_HEADERS = \ + all.h \ + charset.h \ + defs.h \ + dither.h \ + locale_hack.h \ + tags.h + +SOURCES = \ + charset.c \ + dither.c \ + tags.c + +OBJECTS = ${SOURCES:.c=.o} + +libplugin_common.a: $(OBJECTS) + $(AR) cq $@ $(OBJECTS) + +include ../../../mk/objective.mk diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/Makefile.lite --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/Makefile.lite Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,35 @@ +# plugin_common - Routines common to several plugins +# Copyright (C) 2002,2003,2004,2005 Josh Coalson +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# +# GNU makefile +# + +topdir = ../.. + +LIB_NAME = libplugin_common +INCLUDES = -I$(topdir)/include -I$(HOME)/local/include -I$(ICONV_INCLUDE_DIR) +DEFINES = + +SRCS_C = \ + charset.c \ + dither.c \ + tags.c + +include $(topdir)/build/lib.mk + +# DO NOT DELETE THIS LINE -- make depend depends on it. diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/README Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,2 @@ +This directory contains a convenience library of routines that are +common to the plugins. diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/all.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/all.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,27 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLAC__PLUGIN_COMMON__ALL_H +#define FLAC__PLUGIN_COMMON__ALL_H + +#include "charset.h" +#include "dither.h" +#include "locale_hack.h" +#include "tags.h" + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/charset.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/charset.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,156 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * Only slightly modified charset.c from: + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 1999-2001 Håvard Kvålen + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_ICONV +#include +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include +#endif + +#include "charset.h" + + +/************* + * Functions * + *************/ + +char* FLAC_plugin__charset_get_current (void) +{ + char *charset = getenv("CHARSET"); + +#ifdef HAVE_LANGINFO_CODESET + if (!charset) + charset = nl_langinfo(CODESET); +#endif + + if (charset) + return strdup(charset); + + return strdup("ISO-8859-1"); +} + + +#ifdef HAVE_ICONV +char* FLAC_plugin__charset_convert_string (const char *string, char *from, char *to) +{ + size_t outleft, outsize, length; + iconv_t cd; + char *out, *outptr; + const char *input = string; + + if (!string) + return NULL; + + length = strlen(string); + + if ((cd = iconv_open(to, from)) == (iconv_t)-1) + { +#ifdef DEBUG + fprintf(stderr, "convert_string(): Conversion not supported. Charsets: %s -> %s", from, to); +#endif + return strdup(string); + } + + /* Due to a GLIBC bug, round outbuf_size up to a multiple of 4 */ + /* + 1 for nul in case len == 1 */ + outsize = ((length + 3) & ~3) + 1; + out = (char*)malloc(outsize); + outleft = outsize - 1; + outptr = out; + +retry: +#if defined __OpenBSD__ || defined __NetBSD__ + if (iconv(cd, &input, &length, &outptr, &outleft) == (size_t)-1) +#else + if (iconv(cd, (char**)&input, &length, &outptr, &outleft) == (size_t)-1) +#endif + { + int used; + switch (errno) + { + case E2BIG: + used = outptr - out; + outsize = (outsize - 1) * 2 + 1; + out = realloc(out, outsize); + outptr = out + used; + outleft = outsize - 1 - used; + goto retry; + case EINVAL: + break; + case EILSEQ: + /* Invalid sequence, try to get the rest of the string */ + input++; + length = strlen(input); + goto retry; + default: +#ifdef DEBUG + fprintf(stderr, "convert_string(): Conversion failed. Inputstring: %s; Error: %s", string, strerror(errno)); +#endif + break; + } + } + *outptr = '\0'; + + iconv_close(cd); + return out; +} +#else +char* FLAC_plugin__charset_convert_string (const char *string, char *from, char *to) +{ + (void)from, (void)to; + if (!string) + return NULL; + return strdup(string); +} +#endif + +#ifdef HAVE_ICONV +int FLAC_plugin__charset_test_conversion (char *from, char *to) +{ + iconv_t cd; + + if ((cd=iconv_open(to,from)) == (iconv_t)-1) + { + /* Conversion not supported */ + return 0; + } + iconv_close(cd); + return 1; +} +#else +int FLAC_plugin__charset_test_conversion (char *from, char *to) +{ + (void)from, (void)to; + return 1; +} +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/charset.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/charset.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,39 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * Only slightly modified charset.h from: + * charset.h - 2001/12/04 + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 1999-2001 H蛆ard Kvè™±en + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef FLAC__PLUGIN_COMMON__CHARSET_H +#define FLAC__PLUGIN_COMMON__CHARSET_H + + +/************** + * Prototypes * + **************/ + +char *FLAC_plugin__charset_get_current(); +char *FLAC_plugin__charset_convert_string(const char *string, char *from, char *to); + +/* returns 1 for success, 0 for failure or no iconv */ +int FLAC_plugin__charset_test_conversion(char *from, char *to); + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/defs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/defs.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,24 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLAC__PLUGIN_COMMON__DEFS_H +#define FLAC__PLUGIN_COMMON__DEFS_H + +#define FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS 2 + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/dither.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/dither.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,260 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * dithering routine derived from (other GPLed source): + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "dither.h" +#include "FLAC/assert.h" + +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) + + +#if defined _MSC_VER +#define FLAC__INLINE __inline +#else +#define FLAC__INLINE +#endif + +/* 32-bit pseudo-random number generator + * + * @@@ According to Miroslav, this one is poor quality, the one from the + * @@@ original replaygain code is much better + */ +static FLAC__INLINE FLAC__uint32 prng(FLAC__uint32 state) +{ + return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; +} + +/* dither routine derived from MAD winamp plugin */ + +typedef struct { + FLAC__int32 error[3]; + FLAC__int32 random; +} dither_state; + +static FLAC__INLINE FLAC__int32 linear_dither(unsigned source_bps, unsigned target_bps, FLAC__int32 sample, dither_state *dither, const FLAC__int32 MIN, const FLAC__int32 MAX) +{ + unsigned scalebits; + FLAC__int32 output, mask, random; + + FLAC__ASSERT(source_bps < 32); + FLAC__ASSERT(target_bps <= 24); + FLAC__ASSERT(target_bps <= source_bps); + + /* noise shape */ + sample += dither->error[0] - dither->error[1] + dither->error[2]; + + dither->error[2] = dither->error[1]; + dither->error[1] = dither->error[0] / 2; + + /* bias */ + output = sample + (1L << (source_bps - target_bps - 1)); + + scalebits = source_bps - target_bps; + mask = (1L << scalebits) - 1; + + /* dither */ + random = (FLAC__int32)prng(dither->random); + output += (random & mask) - (dither->random & mask); + + dither->random = random; + + /* clip */ + if(output > MAX) { + output = MAX; + + if(sample > MAX) + sample = MAX; + } + else if(output < MIN) { + output = MIN; + + if(sample < MIN) + sample = MIN; + } + + /* quantize */ + output &= ~mask; + + /* error feedback */ + dither->error[0] = sample - output; + + /* scale */ + return output >> scalebits; +} + +size_t FLAC__plugin_common__pack_pcm_signed_big_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps) +{ + static dither_state dither[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS]; + FLAC__byte * const start = data; + FLAC__int32 sample; + const FLAC__int32 *input_; + unsigned samples, channel; + const unsigned bytes_per_sample = target_bps / 8; + const unsigned incr = bytes_per_sample * channels; + + FLAC__ASSERT(channels > 0 && channels <= FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS); + FLAC__ASSERT(source_bps < 32); + FLAC__ASSERT(target_bps <= 24); + FLAC__ASSERT(target_bps <= source_bps); + FLAC__ASSERT((source_bps & 7) == 0); + FLAC__ASSERT((target_bps & 7) == 0); + + if(source_bps != target_bps) { + const FLAC__int32 MIN = -(1L << (source_bps - 1)); + const FLAC__int32 MAX = ~MIN; /*(1L << (source_bps-1)) - 1 */ + + for(channel = 0; channel < channels; channel++) { + + samples = wide_samples; + data = start + bytes_per_sample * channel; + input_ = input[channel]; + + while(samples--) { + sample = linear_dither(source_bps, target_bps, *input_++, &dither[channel], MIN, MAX); + + switch(target_bps) { + case 8: + data[0] = sample ^ 0x80; + break; + case 16: + data[0] = (FLAC__byte)(sample >> 8); + data[1] = (FLAC__byte)sample; + break; + case 24: + data[0] = (FLAC__byte)(sample >> 16); + data[1] = (FLAC__byte)(sample >> 8); + data[2] = (FLAC__byte)sample; + break; + } + + data += incr; + } + } + } + else { + for(channel = 0; channel < channels; channel++) { + samples = wide_samples; + data = start + bytes_per_sample * channel; + input_ = input[channel]; + + while(samples--) { + sample = *input_++; + + switch(target_bps) { + case 8: + data[0] = sample ^ 0x80; + break; + case 16: + data[0] = (FLAC__byte)(sample >> 8); + data[1] = (FLAC__byte)sample; + break; + case 24: + data[0] = (FLAC__byte)(sample >> 16); + data[1] = (FLAC__byte)(sample >> 8); + data[2] = (FLAC__byte)sample; + break; + } + + data += incr; + } + } + } + + return wide_samples * channels * (target_bps/8); +} + +size_t FLAC__plugin_common__pack_pcm_signed_little_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps) +{ + static dither_state dither[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS]; + FLAC__byte * const start = data; + FLAC__int32 sample; + const FLAC__int32 *input_; + unsigned samples, channel; + const unsigned bytes_per_sample = target_bps / 8; + const unsigned incr = bytes_per_sample * channels; + + FLAC__ASSERT(channels > 0 && channels <= FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS); + FLAC__ASSERT(source_bps < 32); + FLAC__ASSERT(target_bps <= 24); + FLAC__ASSERT(target_bps <= source_bps); + FLAC__ASSERT((source_bps & 7) == 0); + FLAC__ASSERT((target_bps & 7) == 0); + + if(source_bps != target_bps) { + const FLAC__int32 MIN = -(1L << (source_bps - 1)); + const FLAC__int32 MAX = ~MIN; /*(1L << (source_bps-1)) - 1 */ + + for(channel = 0; channel < channels; channel++) { + + samples = wide_samples; + data = start + bytes_per_sample * channel; + input_ = input[channel]; + + while(samples--) { + sample = linear_dither(source_bps, target_bps, *input_++, &dither[channel], MIN, MAX); + + switch(target_bps) { + case 8: + data[0] = sample ^ 0x80; + break; + case 24: + data[2] = (FLAC__byte)(sample >> 16); + /* fall through */ + case 16: + data[1] = (FLAC__byte)(sample >> 8); + data[0] = (FLAC__byte)sample; + } + + data += incr; + } + } + } + else { + for(channel = 0; channel < channels; channel++) { + samples = wide_samples; + data = start + bytes_per_sample * channel; + input_ = input[channel]; + + while(samples--) { + sample = *input_++; + + switch(target_bps) { + case 8: + data[0] = sample ^ 0x80; + break; + case 24: + data[2] = (FLAC__byte)(sample >> 16); + /* fall through */ + case 16: + data[1] = (FLAC__byte)(sample >> 8); + data[0] = (FLAC__byte)sample; + } + + data += incr; + } + } + } + + return wide_samples * channels * (target_bps/8); +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/dither.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/dither.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,29 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLAC__PLUGIN_COMMON__DITHER_H +#define FLAC__PLUGIN_COMMON__DITHER_H + +#include /* for size_t */ +#include "defs.h" /* buy FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS for the caller */ +#include "FLAC/ordinals.h" + +size_t FLAC__plugin_common__pack_pcm_signed_big_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps); +size_t FLAC__plugin_common__pack_pcm_signed_little_endian(FLAC__byte *data, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, unsigned source_bps, unsigned target_bps); + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/locale_hack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/locale_hack.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,55 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * Based on: + * locale.h - 2000/05/05 13:10 Jerome Couderc + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 1999-2001 H蛆ard Kvè™±en + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/* + * Gettext support for EasyTAG + */ + + +#ifndef FLAC__PLUGIN_COMMON__LOCALE_HACK_H +#define FLAC__PLUGIN_COMMON__LOCALE_HACK_H + +#include + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include +# define _(String) gettext (String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +# define _(String) (String) +# define N_(String) (String) +#endif + + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/plugin_common_static.dsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/plugin_common_static.dsp Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,124 @@ +# Microsoft Developer Studio Project File - Name="plugin_common_static" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=plugin_common_static - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "plugin_common_static.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "plugin_common_static.mak" CFG="plugin_common_static - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "plugin_common_static - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "plugin_common_static - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "plugin_common" +# PROP Scc_LocalPath "..\.." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "plugin_common_static - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\obj\release\lib" +# PROP Intermediate_Dir "Release_static" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /WX /GX /Ox /Og /Oi /Os /Op /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /nodefaultlib + +!ELSEIF "$(CFG)" == "plugin_common_static - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\..\obj\debug\lib" +# PROP Intermediate_Dir "Debug_static" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "plugin_common_static - Win32 Release" +# Name "plugin_common_static - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp" +# Begin Source File + +SOURCE=.\charset.c +# End Source File +# Begin Source File + +SOURCE=.\dither.c +# End Source File +# Begin Source File + +SOURCE=.\tags.c +# End Source File +# End Group +# Begin Group "Public Header Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\all.h +# End Source File +# Begin Source File + +SOURCE=.\charset.h +# End Source File +# Begin Source File + +SOURCE=.\dither.h +# End Source File +# Begin Source File + +SOURCE=.\locale_hack.h +# End Source File +# Begin Source File + +SOURCE=.\tags.h +# End Source File +# End Group +# End Target +# End Project diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/tags.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/tags.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,307 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "tags.h" +#include "FLAC/assert.h" +#include "FLAC/metadata.h" + + +static __inline unsigned local__wide_strlen(const FLAC__uint16 *s) +{ + unsigned n = 0; + while(*s++) + n++; + return n; +} + +static __inline unsigned local__utf8len(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((utf8[0] & 0x80) == 0) + return 1; + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) + return 2; + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) + return 3; + else + return 0; +} + +static __inline unsigned local__utf8_to_ucs2(const FLAC__byte *utf8, FLAC__uint16 *ucs2) +{ + const unsigned len = local__utf8len(utf8); + + FLAC__ASSERT(0 != ucs2); + + if (len == 1) + *ucs2 = *utf8; + else if (len == 2) + *ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F); + else if (len == 3) + *ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F); + + return len; +} + +static FLAC__uint16 *local__convert_utf8_to_ucs2(const char *src, unsigned length) +{ + FLAC__uint16 *out; + unsigned chars = 0; + + FLAC__ASSERT(0 != src); + + /* calculate length */ + { + const char *s, *end; + for (s=src, end=src+length; s> 6); + utf8[1] = 0x80 | (ucs2 & 0x3f); + return 2; + } + else { + utf8[0] = 0xe0 | (ucs2 >> 12); + utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f); + utf8[2] = 0x80 | (ucs2 & 0x3f); + return 3; + } +} + +static char *local__convert_ucs2_to_utf8(const FLAC__uint16 *src, unsigned length) +{ + char *out; + unsigned len = 0; + + FLAC__ASSERT(0 != src); + + /* calculate length */ + { + unsigned i; + for (i = 0; i < length; i++) + len += local__ucs2len(src[i]); + } + + /* allocate */ + out = (char*)malloc(len * sizeof(char)); + if (0 == out) + return 0; + + /* convert */ + { + unsigned char *u = (unsigned char*) out; + for ( ; *src; src++) + u += local__ucs2_to_utf8(*src, u); + local__ucs2_to_utf8(*src, u); + } + + return out; +} + + +FLAC__bool FLAC_plugin__tags_get(const char *filename, FLAC__StreamMetadata **tags) +{ + if(!FLAC__metadata_get_tags(filename, tags)) + if(0 == (*tags = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT))) + return false; + return true; +} + +FLAC__bool FLAC_plugin__tags_set(const char *filename, const FLAC__StreamMetadata *tags) +{ + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Iterator *iterator; + FLAC__StreamMetadata *block; + FLAC__bool got_vorbis_comments = false; + FLAC__bool ok; + + if(0 == (chain = FLAC__metadata_chain_new())) + return false; + + if(!FLAC__metadata_chain_read(chain, filename)) { + FLAC__metadata_chain_delete(chain); + return false; + } + + if(0 == (iterator = FLAC__metadata_iterator_new())) { + FLAC__metadata_chain_delete(chain); + return false; + } + + FLAC__metadata_iterator_init(iterator, chain); + + do { + if(FLAC__metadata_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) + got_vorbis_comments = true; + } while(!got_vorbis_comments && FLAC__metadata_iterator_next(iterator)); + + if(0 == (block = FLAC__metadata_object_clone(tags))) { + FLAC__metadata_chain_delete(chain); + FLAC__metadata_iterator_delete(iterator); + return false; + } + + if(got_vorbis_comments) + ok = FLAC__metadata_iterator_set_block(iterator, block); + else + ok = FLAC__metadata_iterator_insert_block_after(iterator, block); + + FLAC__metadata_iterator_delete(iterator); + + if(ok) { + FLAC__metadata_chain_sort_padding(chain); + ok = FLAC__metadata_chain_write(chain, /*use_padding=*/true, /*preserve_file_stats=*/true); + } + + FLAC__metadata_chain_delete(chain); + + return ok; +} + +void FLAC_plugin__tags_destroy(FLAC__StreamMetadata **tags) +{ + FLAC__metadata_object_delete(*tags); + *tags = 0; +} + +const char *FLAC_plugin__tags_get_tag_utf8(const FLAC__StreamMetadata *tags, const char *name) +{ + const int i = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, /*offset=*/0, name); + return (i < 0? 0 : strchr((const char*)tags->data.vorbis_comment.comments[i].entry, '=')+1); +} + +FLAC__uint16 *FLAC_plugin__tags_get_tag_ucs2(const FLAC__StreamMetadata *tags, const char *name) +{ + const char *utf8 = FLAC_plugin__tags_get_tag_utf8(tags, name); + if(0 == utf8) + return 0; + return local__convert_utf8_to_ucs2(utf8, strlen(utf8)+1); /* +1 for terminating null */ +} + +int FLAC_plugin__tags_delete_tag(FLAC__StreamMetadata *tags, const char *name) +{ + return FLAC__metadata_object_vorbiscomment_remove_entries_matching(tags, name); +} + +int FLAC_plugin__tags_delete_all(FLAC__StreamMetadata *tags) +{ + int n = (int)tags->data.vorbis_comment.num_comments; + if(n > 0) { + if(!FLAC__metadata_object_vorbiscomment_resize_comments(tags, 0)) + n = -1; + } + return n; +} + +FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char *name, const char *value, const char *separator) +{ + int i; + + FLAC__ASSERT(0 != tags); + FLAC__ASSERT(0 != name); + FLAC__ASSERT(0 != value); + + if(separator && (i = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, /*offset=*/0, name)) >= 0) { + FLAC__StreamMetadata_VorbisComment_Entry *entry = tags->data.vorbis_comment.comments+i; + const size_t value_len = strlen(value); + const size_t separator_len = strlen(separator); + FLAC__byte *new_entry; + if(0 == (new_entry = (FLAC__byte*)realloc(entry->entry, entry->length + value_len + separator_len + 1))) + return false; + memcpy(new_entry+entry->length, separator, separator_len); + entry->length += separator_len; + memcpy(new_entry+entry->length, value, value_len); + entry->length += value_len; + new_entry[entry->length] = '\0'; + entry->entry = new_entry; + } + else { + FLAC__StreamMetadata_VorbisComment_Entry entry; + if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, value)) + return false; + FLAC__metadata_object_vorbiscomment_append_comment(tags, entry, /*copy=*/false); + } + return true; +} + +FLAC__bool FLAC_plugin__tags_set_tag_ucs2(FLAC__StreamMetadata *tags, const char *name, const FLAC__uint16 *value, FLAC__bool replace_all) +{ + FLAC__StreamMetadata_VorbisComment_Entry entry; + + FLAC__ASSERT(0 != tags); + FLAC__ASSERT(0 != name); + FLAC__ASSERT(0 != value); + + { + char *utf8 = local__convert_ucs2_to_utf8(value, local__wide_strlen(value)+1); /* +1 for the terminating null */ + if(0 == utf8) + return false; + if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, utf8)) { + free(utf8); + return false; + } + free(utf8); + } + if(!FLAC__metadata_object_vorbiscomment_replace_comment(tags, entry, replace_all, /*copy=*/false)) + return false; + return true; +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/plugin_common/tags.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/plugin_common/tags.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,74 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLAC__PLUGIN_COMMON__TAGS_H +#define FLAC__PLUGIN_COMMON__TAGS_H + +#include "FLAC/format.h" + +FLAC__bool FLAC_plugin__tags_get(const char *filename, FLAC__StreamMetadata **tags); +FLAC__bool FLAC_plugin__tags_set(const char *filename, const FLAC__StreamMetadata *tags); + +/* + * Deletes the tags object and sets '*tags' to NULL. + */ +void FLAC_plugin__tags_destroy(FLAC__StreamMetadata **tags); + +/* + * Gets the value (in UTF-8) of the first tag with the given name (NULL if no + * such tag exists). + */ +const char *FLAC_plugin__tags_get_tag_utf8(const FLAC__StreamMetadata *tags, const char *name); + +/* + * Gets the value (in UCS-2) of the first tag with the given name (NULL if no + * such tag exists). + * + * NOTE: the returned string is malloc()ed and must be free()d by the caller. + */ +FLAC__uint16 *FLAC_plugin__tags_get_tag_ucs2(const FLAC__StreamMetadata *tags, const char *name); + +/* + * Removes all tags with the given 'name'. Returns the number of tags removed, + * or -1 on memory allocation error. + */ +int FLAC_plugin__tags_delete_tag(FLAC__StreamMetadata *tags, const char *name); + +/* + * Removes all tags. Returns the number of tags removed, or -1 on memory + * allocation error. + */ +int FLAC_plugin__tags_delete_all(FLAC__StreamMetadata *tags); + +/* + * Adds a "name=value" tag to the tags. 'value' must be in UTF-8. If + * 'separator' is non-NULL and 'tags' already contains a tag for 'name', the + * first such tag's value is appended with separator, then value. + */ +FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char *name, const char *value, const char *separator); + +/* + * Adds a "name=value" tag to the tags. 'value' must be in UCS-2. If 'tags' + * already contains a tag or tags for 'name', then they will be replaced + * according to 'replace_all': if 'replace_all' is false, only the first such + * tag will be replaced; if true, all matching tags will be replaced by the one + * new tag. + */ +FLAC__bool FLAC_plugin__tags_set_tag_ucs2(FLAC__StreamMetadata *tags, const char *name, const FLAC__uint16 *value, FLAC__bool replace_all); + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/replaygain.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/replaygain.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,615 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "grabbag.h" +#include "replaygain_analysis.h" +#include "FLAC/assert.h" +#include "FLAC/file_decoder.h" +#include "FLAC/metadata.h" +#include +#include +#include +#include +#include +#if defined _MSC_VER || defined __MINGW32__ +#include /* for chmod() */ +#endif +#include /* for stat(), maybe chmod() */ + +#ifdef local_min +#undef local_min +#endif +#define local_min(a,b) ((a)<(b)?(a):(b)) + +#ifdef local_max +#undef local_max +#endif +#define local_max(a,b) ((a)>(b)?(a):(b)) + +static const FLAC__byte *tag_title_gain_ = (FLAC__byte*)"REPLAYGAIN_TRACK_GAIN"; +static const FLAC__byte *tag_title_peak_ = (FLAC__byte*)"REPLAYGAIN_TRACK_PEAK"; +static const FLAC__byte *tag_album_gain_ = (FLAC__byte*)"REPLAYGAIN_ALBUM_GAIN"; +static const FLAC__byte *tag_album_peak_ = (FLAC__byte*)"REPLAYGAIN_ALBUM_PEAK"; +static const char *peak_format_ = "%s=%1.8f"; +static const char *gain_format_ = "%s=%+2.2f dB"; + +static double album_peak_, title_peak_; + +const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED = 148; +/* + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 +*/ + + +static FLAC__bool get_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + return (0 == stat(filename, stats)); +} + +static void set_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + + (void)chmod(filename, stats->st_mode); +} + +static FLAC__bool append_tag_(FLAC__StreamMetadata *block, const char *format, const FLAC__byte *name, float value) +{ + char buffer[256]; + char *saved_locale; + FLAC__StreamMetadata_VorbisComment_Entry entry; + + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(0 != name); + FLAC__ASSERT(0 != value); + + buffer[sizeof(buffer)-1] = '\0'; + /* + * We need to save the old locale and switch to "C" because the locale + * influences the formatting of %f and we want it a certain way. + */ + saved_locale = setlocale(LC_ALL, 0); + setlocale(LC_ALL, "C"); +#if defined _MSC_VER || defined __MINGW32__ + _snprintf(buffer, sizeof(buffer)-1, format, name, value); +#else + snprintf(buffer, sizeof(buffer)-1, format, name, value); +#endif + setlocale(LC_ALL, saved_locale); + + entry.entry = (FLAC__byte *)buffer; + entry.length = strlen(buffer); + + return FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true); +} + +FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency) +{ + static const unsigned valid_sample_rates[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000 + }; + static const unsigned n_valid_sample_rates = sizeof(valid_sample_rates) / sizeof(valid_sample_rates[0]); + + unsigned i; + + for(i = 0; i < n_valid_sample_rates; i++) + if(sample_frequency == valid_sample_rates[i]) + return true; + return false; +} + +FLAC__bool grabbag__replaygain_init(unsigned sample_frequency) +{ + title_peak_ = album_peak_ = 0.0; + return InitGainAnalysis((long)sample_frequency) == INIT_GAIN_ANALYSIS_OK; +} + +FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples) +{ + /* using a small buffer improves data locality; we'd like it to fit easily in the dcache */ + static Float_t lbuffer[2048], rbuffer[2048]; + static const unsigned nbuffer = sizeof(lbuffer) / sizeof(lbuffer[0]); + FLAC__int32 block_peak = 0, s; + unsigned i, j; + + FLAC__ASSERT(bps >= 4 && bps <= FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE); + FLAC__ASSERT(FLAC__MIN_BITS_PER_SAMPLE == 4); + /* + * We use abs() on a FLAC__int32 which is undefined for the most negative value. + * If the reference codec ever handles 32bps we will have to write a special + * case here. + */ + FLAC__ASSERT(FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE < 32); + + if(bps == 16) { + if(is_stereo) { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)s; + s = abs(s); + block_peak = local_max(block_peak, s); + + s = input[1][j]; + rbuffer[i] = (Float_t)s; + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK) + return false; + } + } + else { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)s; + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK) + return false; + } + } + } + else { /* bps must be < 32 according to above assertion */ + const double scale = ( + (bps > 16)? + (double)1. / (double)(1u << (bps - 16)) : + (double)(1u << (16 - bps)) + ); + + if(is_stereo) { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)(scale * (double)s); + s = abs(s); + block_peak = local_max(block_peak, s); + + s = input[1][j]; + rbuffer[i] = (Float_t)(scale * (double)s); + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK) + return false; + } + } + else { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)(scale * (double)s); + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK) + return false; + } + } + } + + { + const double peak_scale = (double)(1u << (bps - 1)); + double peak = (double)block_peak / peak_scale; + if(peak > title_peak_) + title_peak_ = peak; + if(peak > album_peak_) + album_peak_ = peak; + } + + return true; +} + +void grabbag__replaygain_get_album(float *gain, float *peak) +{ + *gain = (float)GetAlbumGain(); + *peak = (float)album_peak_; + album_peak_ = 0.0; +} + +void grabbag__replaygain_get_title(float *gain, float *peak) +{ + *gain = (float)GetTitleGain(); + *peak = (float)title_peak_; + title_peak_ = 0.0; +} + + +typedef struct { + unsigned channels; + unsigned bits_per_sample; + unsigned sample_rate; + FLAC__bool error; +} DecoderInstance; + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + DecoderInstance *instance = (DecoderInstance*)client_data; + const unsigned bits_per_sample = frame->header.bits_per_sample; + const unsigned channels = frame->header.channels; + const unsigned sample_rate = frame->header.sample_rate; + const unsigned samples = frame->header.blocksize; + + (void)decoder; + + if( + !instance->error && + (channels == 2 || channels == 1) && + bits_per_sample == instance->bits_per_sample && + channels == instance->channels && + sample_rate == instance->sample_rate + ) { + instance->error = !grabbag__replaygain_analyze(buffer, channels==2, bits_per_sample, samples); + } + else { + instance->error = true; + } + + if(!instance->error) + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + else + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + DecoderInstance *instance = (DecoderInstance*)client_data; + + (void)decoder; + + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + instance->bits_per_sample = metadata->data.stream_info.bits_per_sample; + instance->channels = metadata->data.stream_info.channels; + instance->sample_rate = metadata->data.stream_info.sample_rate; + + if(instance->channels != 1 && instance->channels != 2) { + instance->error = true; + return; + } + + if(!grabbag__replaygain_is_valid_sample_frequency(instance->sample_rate)) { + instance->error = true; + return; + } + } +} + +static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + DecoderInstance *instance = (DecoderInstance*)client_data; + + (void)decoder, (void)status; + + instance->error = true; +} + +const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak) +{ + DecoderInstance instance; + FLAC__FileDecoder *decoder = FLAC__file_decoder_new(); + + if(0 == decoder) + return "memory allocation error"; + + instance.error = false; + + /* It does these three by default but lets be explicit: */ + FLAC__file_decoder_set_md5_checking(decoder, false); + FLAC__file_decoder_set_metadata_ignore_all(decoder); + FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); + + FLAC__file_decoder_set_filename(decoder, filename); + FLAC__file_decoder_set_write_callback(decoder, write_callback_); + FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_); + FLAC__file_decoder_set_error_callback(decoder, error_callback_); + FLAC__file_decoder_set_client_data(decoder, &instance); + + if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) { + FLAC__file_decoder_delete(decoder); + return "initializing decoder"; + } + + if(!FLAC__file_decoder_process_until_end_of_file(decoder) || instance.error) { + FLAC__file_decoder_delete(decoder); + return "decoding file"; + } + + FLAC__file_decoder_delete(decoder); + + grabbag__replaygain_get_title(title_gain, title_peak); + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak) +{ + const char *error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak) +{ + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if( + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_gain_) < 0 || + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_peak_) < 0 + ) + return "memory allocation error"; + + if( + !append_tag_(block, peak_format_, tag_album_peak_, album_peak) || + !append_tag_(block, gain_format_, tag_album_gain_, album_gain) + ) + return "memory allocation error"; + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak) +{ + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if( + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_gain_) < 0 || + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_peak_) < 0 + ) + return "memory allocation error"; + + if( + !append_tag_(block, peak_format_, tag_title_peak_, title_peak) || + !append_tag_(block, gain_format_, tag_title_gain_, title_gain) + ) + return "memory allocation error"; + + return 0; +} + +static const char *store_to_file_pre_(const char *filename, FLAC__Metadata_Chain **chain, FLAC__StreamMetadata **block) +{ + FLAC__Metadata_Iterator *iterator; + const char *error; + FLAC__bool found_vc_block = false; + + if(0 == (*chain = FLAC__metadata_chain_new())) + return "memory allocation error"; + + if(!FLAC__metadata_chain_read(*chain, filename)) { + error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)]; + FLAC__metadata_chain_delete(*chain); + return error; + } + + if(0 == (iterator = FLAC__metadata_iterator_new())) { + FLAC__metadata_chain_delete(*chain); + return "memory allocation error"; + } + + FLAC__metadata_iterator_init(iterator, *chain); + + do { + *block = FLAC__metadata_iterator_get_block(iterator); + if((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) + found_vc_block = true; + } while(!found_vc_block && FLAC__metadata_iterator_next(iterator)); + + if(!found_vc_block) { + /* create a new block */ + *block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 == *block) { + FLAC__metadata_chain_delete(*chain); + FLAC__metadata_iterator_delete(iterator); + return "memory allocation error"; + } + while(FLAC__metadata_iterator_next(iterator)) + ; + if(!FLAC__metadata_iterator_insert_block_after(iterator, *block)) { + error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)]; + FLAC__metadata_chain_delete(*chain); + FLAC__metadata_iterator_delete(iterator); + return error; + } + /* iterator is left pointing to new block */ + FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == *block); + } + + FLAC__metadata_iterator_delete(iterator); + + FLAC__ASSERT(0 != *block); + FLAC__ASSERT((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + return 0; +} + +static const char *store_to_file_post_(const char *filename, FLAC__Metadata_Chain *chain, FLAC__bool preserve_modtime) +{ + struct stat stats; + const FLAC__bool have_stats = get_file_stats_(filename, &stats); + + (void)grabbag__file_change_stats(filename, /*read_only=*/false); + + FLAC__metadata_chain_sort_padding(chain); + if(!FLAC__metadata_chain_write(chain, /*use_padding=*/true, preserve_modtime)) { + FLAC__metadata_chain_delete(chain); + return FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]; + } + + FLAC__metadata_chain_delete(chain); + + if(have_stats) + set_file_stats_(filename, &stats); + + return 0; +} + +const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment(block, album_gain, album_peak, title_gain, title_peak))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry *entry, double *val) +{ + char s[32], *end; + const char *p, *q; + double v; + + FLAC__ASSERT(0 != entry); + FLAC__ASSERT(0 != val); + + p = (const char *)entry->entry; + q = strchr(p, '='); + if(0 == q) + return false; + q++; + memset(s, 0, sizeof(s)-1); + strncpy(s, q, local_min(sizeof(s)-1, (size_t)(entry->length - (q-p)))); + + v = strtod(s, &end); + if(end == s) + return false; + + *val = v; + return true; +} + +FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, double *gain, double *peak) +{ + int gain_offset, peak_offset; + + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_gain_ : tag_title_gain_)))) + return false; + if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_peak_ : tag_title_peak_)))) + return false; + + if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain)) + return false; + if(!parse_double_(block->data.vorbis_comment.comments + peak_offset, peak)) + return false; + + return true; +} + +double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping) +{ + double scale; + FLAC__ASSERT(peak >= 0.0); + gain += preamp; + scale = (float) pow(10.0, gain * 0.05); + if(prevent_clipping && peak > 0.0) { + const double max_scale = (float)(1.0 / peak); + if(scale > max_scale) + scale = max_scale; + } + return scale; +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/replaygain_analysis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/replaygain_analysis.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,413 @@ +/* + * ReplayGainAnalysis - analyzes input samples and give the recommended dB change + * Copyright (C) 2001 David Robinson and Glen Sawyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * concept and filter values by David Robinson (David@Robinson.org) + * -- blame him if you think the idea is flawed + * original coding by Glen Sawyer (glensawyer@hotmail.com) + * -- blame him if you think this runs too slowly, or the coding is otherwise flawed + * + * lots of code improvements by Frank Klemm ( http://www.uni-jena.de/~pfk/mpp/ ) + * -- credit him for all the _good_ programming ;) + * + * minor cosmetic tweaks to integrate with FLAC by Josh Coalson + * + * + * For an explanation of the concepts and the basic algorithms involved, go to: + * http://www.replaygain.org/ + */ + +/* + * Here's the deal. Call + * + * InitGainAnalysis ( long samplefreq ); + * + * to initialize everything. Call + * + * AnalyzeSamples ( const Float_t* left_samples, + * const Float_t* right_samples, + * size_t num_samples, + * int num_channels ); + * + * as many times as you want, with as many or as few samples as you want. + * If mono, pass the sample buffer in through left_samples, leave + * right_samples NULL, and make sure num_channels = 1. + * + * GetTitleGain() + * + * will return the recommended dB level change for all samples analyzed + * SINCE THE LAST TIME you called GetTitleGain() OR InitGainAnalysis(). + * + * GetAlbumGain() + * + * will return the recommended dB level change for all samples analyzed + * since InitGainAnalysis() was called and finalized with GetTitleGain(). + * + * Pseudo-code to process an album: + * + * Float_t l_samples [4096]; + * Float_t r_samples [4096]; + * size_t num_samples; + * unsigned int num_songs; + * unsigned int i; + * + * InitGainAnalysis ( 44100 ); + * for ( i = 1; i <= num_songs; i++ ) { + * while ( ( num_samples = getSongSamples ( song[i], left_samples, right_samples ) ) > 0 ) + * AnalyzeSamples ( left_samples, right_samples, num_samples, 2 ); + * fprintf ("Recommended dB change for song %2d: %+6.2f dB\n", i, GetTitleGain() ); + * } + * fprintf ("Recommended dB change for whole album: %+6.2f dB\n", GetAlbumGain() ); + */ + +/* + * So here's the main source of potential code confusion: + * + * The filters applied to the incoming samples are IIR filters, + * meaning they rely on up to number of previous samples + * AND up to number of previous filtered samples. + * + * I set up the AnalyzeSamples routine to minimize memory usage and interface + * complexity. The speed isn't compromised too much (I don't think), but the + * internal complexity is higher than it should be for such a relatively + * simple routine. + * + * Optimization/clarity suggestions are welcome. + */ + +#include +#include +#include +#include + +#include "replaygain_analysis.h" + +typedef unsigned short Uint16_t; +typedef signed short Int16_t; +typedef unsigned int Uint32_t; +typedef signed int Int32_t; + +#define YULE_ORDER 10 +#define BUTTER_ORDER 2 +#define RMS_PERCENTILE 0.95 /* percentile which is louder than the proposed level */ +#define MAX_SAMP_FREQ 48000. /* maximum allowed sample frequency [Hz] */ +#define RMS_WINDOW_TIME 0.050 /* Time slice size [s] */ +#define STEPS_per_dB 100. /* Table entries per dB */ +#define MAX_dB 120. /* Table entries for 0...MAX_dB (normal max. values are 70...80 dB) */ + +#define MAX_ORDER (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER) +/* [JEC] the following was originally #defined as: + * (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME) + * but that seemed to fail to take into account the ceil() part of the + * sampleWindow calculation in ResetSampleFrequency(), and was causing + * buffer overflows for 48kHz analysis, hence the +1. + */ +#define MAX_SAMPLES_PER_WINDOW (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME + 1.) /* max. Samples per Time slice */ +#define PINK_REF 64.82 /* 298640883795 */ /* calibration value */ + +static Float_t linprebuf [MAX_ORDER * 2]; +static Float_t* linpre; /* left input samples, with pre-buffer */ +static Float_t lstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* lstep; /* left "first step" (i.e. post first filter) samples */ +static Float_t loutbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* lout; /* left "out" (i.e. post second filter) samples */ +static Float_t rinprebuf [MAX_ORDER * 2]; +static Float_t* rinpre; /* right input samples ... */ +static Float_t rstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* rstep; +static Float_t routbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* rout; +static unsigned int sampleWindow; /* number of samples required to reach number of milliseconds required for RMS window */ +static unsigned long totsamp; +static double lsum; +static double rsum; +static int freqindex; +static Uint32_t A [(size_t)(STEPS_per_dB * MAX_dB)]; +static Uint32_t B [(size_t)(STEPS_per_dB * MAX_dB)]; + +/* for each filter: + [0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz */ + +#ifdef WIN32 +#pragma warning ( disable : 4305 ) +#endif + +static const Float_t AYule [9] [11] = { + { 1., -3.84664617118067, 7.81501653005538,-11.34170355132042, 13.05504219327545,-12.28759895145294, 9.48293806319790, -5.87257861775999, 2.75465861874613, -0.86984376593551, 0.13919314567432 }, + { 1., -3.47845948550071, 6.36317777566148, -8.54751527471874, 9.47693607801280, -8.81498681370155, 6.85401540936998, -4.39470996079559, 2.19611684890774, -0.75104302451432, 0.13149317958808 }, + { 1., -2.37898834973084, 2.84868151156327, -2.64577170229825, 2.23697657451713, -1.67148153367602, 1.00595954808547, -0.45953458054983, 0.16378164858596, -0.05032077717131, 0.02347897407020 }, + { 1., -1.61273165137247, 1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906, 0.39120800788284, -0.22138138954925, 0.04500235387352, 0.02005851806501, 0.00302439095741 }, + { 1., -1.49858979367799, 0.87350271418188, 0.12205022308084, -0.80774944671438, 0.47854794562326, -0.12453458140019, -0.04067510197014, 0.08333755284107, -0.04237348025746, 0.02977207319925 }, + { 1., -0.62820619233671, 0.29661783706366, -0.37256372942400, 0.00213767857124, -0.42029820170918, 0.22199650564824, 0.00613424350682, 0.06747620744683, 0.05784820375801, 0.03222754072173 }, + { 1., -1.04800335126349, 0.29156311971249, -0.26806001042947, 0.00819999645858, 0.45054734505008, -0.33032403314006, 0.06739368333110, -0.04784254229033, 0.01639907836189, 0.01807364323573 }, + { 1., -0.51035327095184, -0.31863563325245, -0.20256413484477, 0.14728154134330, 0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053, 0.02442357316099, 0.01818801111503 }, + { 1., -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242, 0.26408300200955, 0.15113130533216, -0.17556493366449, -0.18823009262115, 0.05477720428674, 0.04704409688120 } +}; + +static const Float_t BYule [9] [11] = { + { 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619, 0.02161526843274, -0.02074045215285, 0.00594298065125, 0.00306428023191, 0.00012025322027, 0.00288463683916 }, + { 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936, 0.02245293253339, -0.02596338512915, 0.01624864962975, -0.00240879051584, 0.00674613682247, -0.00187763777362 }, + { 0.15457299681924, -0.09331049056315, -0.06247880153653, 0.02163541888798, -0.05588393329856, 0.04781476674921, 0.00222312597743, 0.03174092540049, -0.01390589421898, 0.00651420667831, -0.00881362733839 }, + { 0.30296907319327, -0.22613988682123, -0.08587323730772, 0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913, 0.06276101321749, -0.00000828086748, 0.00205861885564, -0.02950134983287 }, + { 0.33642304856132, -0.25572241425570, -0.11828570177555, 0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440, 0.05724228140351, 0.00832043980773, -0.01635381384540, -0.01760176568150 }, + { 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551, 0.04078262797139, -0.12398163381748, 0.04097565135648, 0.10478503600251, -0.01863887810927, -0.03193428438915, 0.00541907748707 }, + { 0.56619470757641, -0.75464456939302, 0.16242137742230, 0.16744243493672, -0.18901604199609, 0.30931782841830, -0.27562961986224, 0.00647310677246, 0.08647503780351, -0.03788984554840, -0.00588215443421 }, + { 0.58100494960553, -0.53174909058578, -0.14289799034253, 0.17520704835522, 0.02377945217615, 0.15558449135573, -0.25344790059353, 0.01628462406333, 0.06920467763959, -0.03721611395801, -0.00749618797172 }, + { 0.53648789255105, -0.42163034350696, -0.00275953611929, 0.04267842219415, -0.10214864179676, 0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000, 0.04788665548180, -0.02217936801134 } +}; + +static const Float_t AButter [9] [3] = { + { 1., -1.97223372919527, 0.97261396931306 }, + { 1., -1.96977855582618, 0.97022847566350 }, + { 1., -1.95835380975398, 0.95920349965459 }, + { 1., -1.95002759149878, 0.95124613669835 }, + { 1., -1.94561023566527, 0.94705070426118 }, + { 1., -1.92783286977036, 0.93034775234268 }, + { 1., -1.91858953033784, 0.92177618768381 }, + { 1., -1.91542108074780, 0.91885558323625 }, + { 1., -1.88903307939452, 0.89487434461664 } +}; + +static const Float_t BButter [9] [3] = { + { 0.98621192462708, -1.97242384925416, 0.98621192462708 }, + { 0.98500175787242, -1.97000351574484, 0.98500175787242 }, + { 0.97938932735214, -1.95877865470428, 0.97938932735214 }, + { 0.97531843204928, -1.95063686409857, 0.97531843204928 }, + { 0.97316523498161, -1.94633046996323, 0.97316523498161 }, + { 0.96454515552826, -1.92909031105652, 0.96454515552826 }, + { 0.96009142950541, -1.92018285901082, 0.96009142950541 }, + { 0.95856916599601, -1.91713833199203, 0.95856916599601 }, + { 0.94597685600279, -1.89195371200558, 0.94597685600279 } +}; + +#ifdef WIN32 +#pragma warning ( default : 4305 ) +#endif + +/* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */ + +static void +filter ( const Float_t* input, Float_t* output, size_t nSamples, const Float_t* a, const Float_t* b, size_t order ) +{ + double y; + size_t i; + size_t k; + + for ( i = 0; i < nSamples; i++ ) { + y = input[i] * b[0]; + for ( k = 1; k <= order; k++ ) + y += input[i-k] * b[k] - output[i-k] * a[k]; + output[i] = (Float_t)y; + } +} + +/* returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not */ + +int +ResetSampleFrequency ( long samplefreq ) { + int i; + + /* zero out initial values */ + for ( i = 0; i < MAX_ORDER; i++ ) + linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.; + + switch ( (int)(samplefreq) ) { + case 48000: freqindex = 0; break; + case 44100: freqindex = 1; break; + case 32000: freqindex = 2; break; + case 24000: freqindex = 3; break; + case 22050: freqindex = 4; break; + case 16000: freqindex = 5; break; + case 12000: freqindex = 6; break; + case 11025: freqindex = 7; break; + case 8000: freqindex = 8; break; + default: return INIT_GAIN_ANALYSIS_ERROR; + } + + sampleWindow = (int) ceil (samplefreq * RMS_WINDOW_TIME); + + lsum = 0.; + rsum = 0.; + totsamp = 0; + + memset ( A, 0, sizeof(A) ); + + return INIT_GAIN_ANALYSIS_OK; +} + +int +InitGainAnalysis ( long samplefreq ) +{ + if (ResetSampleFrequency(samplefreq) != INIT_GAIN_ANALYSIS_OK) { + return INIT_GAIN_ANALYSIS_ERROR; + } + + linpre = linprebuf + MAX_ORDER; + rinpre = rinprebuf + MAX_ORDER; + lstep = lstepbuf + MAX_ORDER; + rstep = rstepbuf + MAX_ORDER; + lout = loutbuf + MAX_ORDER; + rout = routbuf + MAX_ORDER; + + memset ( B, 0, sizeof(B) ); + + return INIT_GAIN_ANALYSIS_OK; +} + +/* returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not */ + +int +AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ) +{ + const Float_t* curleft; + const Float_t* curright; + long batchsamples; + long cursamples; + long cursamplepos; + int i; + + if ( num_samples == 0 ) + return GAIN_ANALYSIS_OK; + + cursamplepos = 0; + batchsamples = num_samples; + + switch ( num_channels) { + case 1: right_samples = left_samples; + case 2: break; + default: return GAIN_ANALYSIS_ERROR; + } + + if ( num_samples < MAX_ORDER ) { + memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) ); + memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) ); + } + else { + memcpy ( linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t) ); + memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t) ); + } + + while ( batchsamples > 0 ) { + cursamples = batchsamples > (long)(sampleWindow-totsamp) ? (long)(sampleWindow - totsamp) : batchsamples; + if ( cursamplepos < MAX_ORDER ) { + curleft = linpre+cursamplepos; + curright = rinpre+cursamplepos; + if (cursamples > MAX_ORDER - cursamplepos ) + cursamples = MAX_ORDER - cursamplepos; + } + else { + curleft = left_samples + cursamplepos; + curright = right_samples + cursamplepos; + } + + filter ( curleft , lstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER ); + filter ( curright, rstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER ); + + filter ( lstep + totsamp, lout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER ); + filter ( rstep + totsamp, rout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER ); + + for ( i = 0; i < cursamples; i++ ) { /* Get the squared values */ + lsum += lout [totsamp+i] * lout [totsamp+i]; + rsum += rout [totsamp+i] * rout [totsamp+i]; + } + + batchsamples -= cursamples; + cursamplepos += cursamples; + totsamp += cursamples; + if ( totsamp == sampleWindow ) { /* Get the Root Mean Square (RMS) for this set of samples */ + double val = STEPS_per_dB * 10. * log10 ( (lsum+rsum) / totsamp * 0.5 + 1.e-37 ); + int ival = (int) val; + if ( ival < 0 ) ival = 0; + if ( ival >= (int)(sizeof(A)/sizeof(*A)) ) ival = (int)(sizeof(A)/sizeof(*A)) - 1; + A [ival]++; + lsum = rsum = 0.; + memmove ( loutbuf , loutbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + memmove ( routbuf , routbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + memmove ( lstepbuf, lstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + memmove ( rstepbuf, rstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + totsamp = 0; + } + if ( totsamp > sampleWindow ) /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */ + return GAIN_ANALYSIS_ERROR; + } + if ( num_samples < MAX_ORDER ) { + memmove ( linprebuf, linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); + memmove ( rinprebuf, rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); + memcpy ( linprebuf + MAX_ORDER - num_samples, left_samples, num_samples * sizeof(Float_t) ); + memcpy ( rinprebuf + MAX_ORDER - num_samples, right_samples, num_samples * sizeof(Float_t) ); + } + else { + memcpy ( linprebuf, left_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); + memcpy ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); + } + + return GAIN_ANALYSIS_OK; +} + + +static Float_t +analyzeResult ( Uint32_t* Array, size_t len ) +{ + Uint32_t elems; + Int32_t upper; + size_t i; + + elems = 0; + for ( i = 0; i < len; i++ ) + elems += Array[i]; + if ( elems == 0 ) + return GAIN_NOT_ENOUGH_SAMPLES; + + upper = (Int32_t) ceil (elems * (1. - RMS_PERCENTILE)); + for ( i = len; i-- > 0; ) { + if ( (upper -= Array[i]) <= 0 ) + break; + } + + return (Float_t) ((Float_t)PINK_REF - (Float_t)i / (Float_t)STEPS_per_dB); +} + + +Float_t +GetTitleGain ( void ) +{ + Float_t retval; + unsigned int i; + + retval = analyzeResult ( A, sizeof(A)/sizeof(*A) ); + + for ( i = 0; i < sizeof(A)/sizeof(*A); i++ ) { + B[i] += A[i]; + A[i] = 0; + } + + for ( i = 0; i < MAX_ORDER; i++ ) + linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.f; + + totsamp = 0; + lsum = rsum = 0.; + return retval; +} + + +Float_t +GetAlbumGain ( void ) +{ + return analyzeResult ( B, sizeof(B)/sizeof(*B) ); +} + +/* end of replaygain_analysis.c */ diff -r 117bc56d906b -r 4b31176c198a src/flac112/replaygain_analysis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/replaygain_analysis.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,57 @@ +/* + * ReplayGainAnalysis - analyzes input samples and give the recommended dB change + * Copyright (C) 2001 David Robinson and Glen Sawyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * concept and filter values by David Robinson (David@Robinson.org) + * -- blame him if you think the idea is flawed + * coding by Glen Sawyer (glensawyer@hotmail.com) 442 N 700 E, Provo, UT 84606 USA + * -- blame him if you think this runs too slowly, or the coding is otherwise flawed + * minor cosmetic tweaks to integrate with FLAC by Josh Coalson + * + * For an explanation of the concepts and the basic algorithms involved, go to: + * http://www.replaygain.org/ + */ + +#ifndef GAIN_ANALYSIS_H +#define GAIN_ANALYSIS_H + +#include + +#define GAIN_NOT_ENOUGH_SAMPLES -24601 +#define GAIN_ANALYSIS_ERROR 0 +#define GAIN_ANALYSIS_OK 1 + +#define INIT_GAIN_ANALYSIS_ERROR 0 +#define INIT_GAIN_ANALYSIS_OK 1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef float Float_t; /* Type used for filtering */ + +int InitGainAnalysis ( long samplefreq ); +int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ); +int ResetSampleFrequency ( long samplefreq ); +Float_t GetTitleGain ( void ); +Float_t GetAlbumGain ( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* GAIN_ANALYSIS_H */ diff -r 117bc56d906b -r 4b31176c198a src/flac112/replaygain_synthesis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/replaygain_synthesis.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,465 @@ +/* replaygain_synthesis - Routines for applying ReplayGain to a signal + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/* + * This is an aggregation of pieces of code from John Edwards' WaveGain + * program. Mostly cosmetic changes were made; otherwise, the dithering + * code is almost untouched and the gain processing was converted from + * processing a whole file to processing chunks of samples. + * + * The original copyright notices for WaveGain's dither.c and wavegain.c + * appear below: + */ +/* + * (c) 2002 John Edwards + * mostly lifted from work by Frank Klemm + * random functions for dithering. + */ +/* + * Copyright (C) 2002 John Edwards + * Additional code by Magnus Holmgren and Gian-Carlo Pascutto + */ + +#include /* for memset() */ +#include +#include "fast_float_math_hack.h" +#include "replaygain_synthesis.h" +#include "FLAC/assert.h" + +#if defined _MSC_VER +#define FLAC__INLINE __inline +#else +#define FLAC__INLINE +#endif + +/* adjust for compilers that can't understand using LL suffix for int64_t literals */ +#ifdef _MSC_VER +#define FLAC__I64L(x) x +#else +#define FLAC__I64L(x) x##LL +#endif + + +/* + * the following is based on parts of dither.c + */ + + +/* + * This is a simple random number generator with good quality for audio purposes. + * It consists of two polycounters with opposite rotation direction and different + * periods. The periods are coprime, so the total period is the product of both. + * + * ------------------------------------------------------------------------------------------------- + * +-> |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| + * | ------------------------------------------------------------------------------------------------- + * | | | | | | | + * | +--+--+--+-XOR-+--------+ + * | | + * +--------------------------------------------------------------------------------------+ + * + * ------------------------------------------------------------------------------------------------- + * |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| <-+ + * ------------------------------------------------------------------------------------------------- | + * | | | | | + * +--+----XOR----+--+ | + * | | + * +----------------------------------------------------------------------------------------+ + * + * + * The first has an period of 3*5*17*257*65537, the second of 7*47*73*178481, + * which gives a period of 18.410.713.077.675.721.215. The result is the + * XORed values of both generators. + */ + +static unsigned int random_int_() +{ + static const unsigned char parity_[256] = { + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 + }; + static unsigned int r1_ = 1; + static unsigned int r2_ = 1; + + unsigned int t1, t2, t3, t4; + + /* Parity calculation is done via table lookup, this is also available + * on CPUs without parity, can be implemented in C and avoid unpredictable + * jumps and slow rotate through the carry flag operations. + */ + t3 = t1 = r1_; t4 = t2 = r2_; + t1 &= 0xF5; t2 >>= 25; + t1 = parity_[t1]; t2 &= 0x63; + t1 <<= 31; t2 = parity_[t2]; + + return (r1_ = (t3 >> 1) | t1 ) ^ (r2_ = (t4 + t4) | t2 ); +} + +/* gives a equal distributed random number */ +/* between -2^31*mult and +2^31*mult */ +static double random_equi_(double mult) +{ + return mult * (int) random_int_(); +} + +/* gives a triangular distributed random number */ +/* between -2^32*mult and +2^32*mult */ +static double random_triangular_(double mult) +{ + return mult * ( (double) (int) random_int_() + (double) (int) random_int_() ); +} + + +static const float F44_0 [16 + 32] = { + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0 +}; + + +static const float F44_1 [16 + 32] = { /* SNR(w) = 4.843163 dB, SNR = -3.192134 dB */ + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, + + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, + + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, +}; + + +static const float F44_2 [16 + 32] = { /* SNR(w) = 10.060213 dB, SNR = -12.766730 dB */ + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, + + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, + + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, +}; + + +static const float F44_3 [16 + 32] = { /* SNR(w) = 15.382598 dB, SNR = -29.402334 dB */ + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, + + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, + + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099 +}; + + +static double scalar16_(const float* x, const float* y) +{ + return + x[ 0]*y[ 0] + x[ 1]*y[ 1] + x[ 2]*y[ 2] + x[ 3]*y[ 3] + + x[ 4]*y[ 4] + x[ 5]*y[ 5] + x[ 6]*y[ 6] + x[ 7]*y[ 7] + + x[ 8]*y[ 8] + x[ 9]*y[ 9] + x[10]*y[10] + x[11]*y[11] + + x[12]*y[12] + x[13]*y[13] + x[14]*y[14] + x[15]*y[15]; +} + + +void FLAC__replaygain_synthesis__init_dither_context(DitherContext *d, int bits, int shapingtype) +{ + static unsigned char default_dither [] = { 92, 92, 88, 84, 81, 78, 74, 67, 0, 0 }; + static const float* F [] = { F44_0, F44_1, F44_2, F44_3 }; + + int index; + + if (shapingtype < 0) shapingtype = 0; + if (shapingtype > 3) shapingtype = 3; + d->ShapingType = (NoiseShaping)shapingtype; + index = bits - 11 - shapingtype; + if (index < 0) index = 0; + if (index > 9) index = 9; + + memset ( d->ErrorHistory , 0, sizeof (d->ErrorHistory ) ); + memset ( d->DitherHistory, 0, sizeof (d->DitherHistory) ); + + d->FilterCoeff = F [shapingtype]; + d->Mask = ((FLAC__uint64)-1) << (32 - bits); + d->Add = 0.5 * ((1L << (32 - bits)) - 1); + d->Dither = 0.01f*default_dither[index] / (((FLAC__int64)1) << bits); + d->LastHistoryIndex = 0; +} + +/* + * the following is based on parts of wavegain.c + */ + +static FLAC__INLINE FLAC__int64 dither_output_(DitherContext *d, FLAC__bool do_dithering, int shapingtype, int i, double Sum, int k) +{ + union { + double d; + FLAC__int64 i; + } doubletmp; + double Sum2; + FLAC__int64 val; + +#define ROUND64(x) ( doubletmp.d = (x) + d->Add + (FLAC__int64)FLAC__I64L(0x001FFFFD80000000), doubletmp.i - (FLAC__int64)FLAC__I64L(0x433FFFFD80000000) ) + + if(do_dithering) { + if(shapingtype == 0) { + double tmp = random_equi_(d->Dither); + Sum2 = tmp - d->LastRandomNumber [k]; + d->LastRandomNumber [k] = (int)tmp; + Sum2 = Sum += Sum2; + val = ROUND64(Sum2) & d->Mask; + } + else { + Sum2 = random_triangular_(d->Dither) - scalar16_(d->DitherHistory[k], d->FilterCoeff + i); + Sum += d->DitherHistory [k] [(-1-i)&15] = (float)Sum2; + Sum2 = Sum + scalar16_(d->ErrorHistory [k], d->FilterCoeff + i); + val = ROUND64(Sum2) & d->Mask; + d->ErrorHistory [k] [(-1-i)&15] = (float)(Sum - val); + } + return val; + } + else + return ROUND64(Sum); + +#undef ROUND64 +} + +#if 0 + float peak = 0.f, + new_peak, + factor_clip + double scale, + dB; + + ... + + peak is in the range -32768.0 .. 32767.0 + + /* calculate factors for ReplayGain and ClippingPrevention */ + *track_gain = GetTitleGain() + settings->man_gain; + scale = (float) pow(10., *track_gain * 0.05); + if(settings->clip_prev) { + factor_clip = (float) (32767./( peak + 1)); + if(scale < factor_clip) + factor_clip = 1.f; + else + factor_clip /= scale; + scale *= factor_clip; + } + new_peak = (float) peak * scale; + + dB = 20. * log10(scale); + *track_gain = (float) dB; + + const double scale = pow(10., (double)gain * 0.05); +#endif + + +size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context) +{ + static const FLAC__int32 conv_factors_[33] = { + -1, /* 0 bits-per-sample (not supported) */ + -1, /* 1 bits-per-sample (not supported) */ + -1, /* 2 bits-per-sample (not supported) */ + -1, /* 3 bits-per-sample (not supported) */ + 268435456, /* 4 bits-per-sample */ + 134217728, /* 5 bits-per-sample */ + 67108864, /* 6 bits-per-sample */ + 33554432, /* 7 bits-per-sample */ + 16777216, /* 8 bits-per-sample */ + 8388608, /* 9 bits-per-sample */ + 4194304, /* 10 bits-per-sample */ + 2097152, /* 11 bits-per-sample */ + 1048576, /* 12 bits-per-sample */ + 524288, /* 13 bits-per-sample */ + 262144, /* 14 bits-per-sample */ + 131072, /* 15 bits-per-sample */ + 65536, /* 16 bits-per-sample */ + 32768, /* 17 bits-per-sample */ + 16384, /* 18 bits-per-sample */ + 8192, /* 19 bits-per-sample */ + 4096, /* 20 bits-per-sample */ + 2048, /* 21 bits-per-sample */ + 1024, /* 22 bits-per-sample */ + 512, /* 23 bits-per-sample */ + 256, /* 24 bits-per-sample */ + 128, /* 25 bits-per-sample */ + 64, /* 26 bits-per-sample */ + 32, /* 27 bits-per-sample */ + 16, /* 28 bits-per-sample */ + 8, /* 29 bits-per-sample */ + 4, /* 30 bits-per-sample */ + 2, /* 31 bits-per-sample */ + 1 /* 32 bits-per-sample */ + }; + static const FLAC__int64 hard_clip_factors_[33] = { + 0, /* 0 bits-per-sample (not supported) */ + 0, /* 1 bits-per-sample (not supported) */ + 0, /* 2 bits-per-sample (not supported) */ + 0, /* 3 bits-per-sample (not supported) */ + -8, /* 4 bits-per-sample */ + -16, /* 5 bits-per-sample */ + -32, /* 6 bits-per-sample */ + -64, /* 7 bits-per-sample */ + -128, /* 8 bits-per-sample */ + -256, /* 9 bits-per-sample */ + -512, /* 10 bits-per-sample */ + -1024, /* 11 bits-per-sample */ + -2048, /* 12 bits-per-sample */ + -4096, /* 13 bits-per-sample */ + -8192, /* 14 bits-per-sample */ + -16384, /* 15 bits-per-sample */ + -32768, /* 16 bits-per-sample */ + -65536, /* 17 bits-per-sample */ + -131072, /* 18 bits-per-sample */ + -262144, /* 19 bits-per-sample */ + -524288, /* 20 bits-per-sample */ + -1048576, /* 21 bits-per-sample */ + -2097152, /* 22 bits-per-sample */ + -4194304, /* 23 bits-per-sample */ + -8388608, /* 24 bits-per-sample */ + -16777216, /* 25 bits-per-sample */ + -33554432, /* 26 bits-per-sample */ + -67108864, /* 27 bits-per-sample */ + -134217728, /* 28 bits-per-sample */ + -268435456, /* 29 bits-per-sample */ + -536870912, /* 30 bits-per-sample */ + -1073741824, /* 31 bits-per-sample */ + (FLAC__int64)(-1073741824) * 2 /* 32 bits-per-sample */ + }; + const FLAC__int32 conv_factor = conv_factors_[target_bps]; + const FLAC__int64 hard_clip_factor = hard_clip_factors_[target_bps]; + /* + * The integer input coming in has a varying range based on the + * source_bps. We want to normalize it to [-1.0, 1.0) so instead + * of doing two multiplies on each sample, we just multiple + * 'scale' by 1/(2^(source_bps-1)) + */ + const double multi_scale = scale / (double)(1u << (source_bps-1)); + + FLAC__byte * const start = data_out; + unsigned i, channel; + const FLAC__int32 *input_; + double sample; + const unsigned bytes_per_sample = target_bps / 8; + const unsigned last_history_index = dither_context->LastHistoryIndex; + NoiseShaping noise_shaping = dither_context->ShapingType; + FLAC__int64 val64; + FLAC__int32 val32; + FLAC__int32 uval32; + const FLAC__uint32 twiggle = 1u << (target_bps - 1); + + FLAC__ASSERT(channels > 0 && channels <= FLAC_SHARE__MAX_SUPPORTED_CHANNELS); + FLAC__ASSERT(source_bps >= 4); + FLAC__ASSERT(target_bps >= 4); + FLAC__ASSERT(source_bps <= 32); + FLAC__ASSERT(target_bps < 32); + FLAC__ASSERT((target_bps & 7) == 0); + + for(channel = 0; channel < channels; channel++) { + const unsigned incr = bytes_per_sample * channels; + data_out = start + bytes_per_sample * channel; + input_ = input[channel]; + for(i = 0; i < wide_samples; i++, data_out += incr) { + sample = (double)input_[i] * multi_scale; + + if(hard_limit) { + /* hard 6dB limiting */ + if(sample < -0.5) + sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5; + else if(sample > 0.5) + sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5; + } + sample *= 2147483647.f; + + val64 = dither_output_(dither_context, do_dithering, noise_shaping, (i + last_history_index) % 32, sample, channel) / conv_factor; + + val32 = (FLAC__int32)val64; + if(val64 >= -hard_clip_factor) + val32 = (FLAC__int32)(-(hard_clip_factor+1)); + else if(val64 < hard_clip_factor) + val32 = (FLAC__int32)hard_clip_factor; + + uval32 = (FLAC__uint32)val32; + if (unsigned_data_out) + uval32 ^= twiggle; + + if (little_endian_data_out) { + switch(target_bps) { + case 24: + data_out[2] = (FLAC__byte)(uval32 >> 16); + /* fall through */ + case 16: + data_out[1] = (FLAC__byte)(uval32 >> 8); + /* fall through */ + case 8: + data_out[0] = (FLAC__byte)uval32; + break; + } + } + else { + switch(target_bps) { + case 24: + data_out[0] = (FLAC__byte)(uval32 >> 16); + data_out[1] = (FLAC__byte)(uval32 >> 8); + data_out[2] = (FLAC__byte)uval32; + break; + case 16: + data_out[0] = (FLAC__byte)(uval32 >> 8); + data_out[1] = (FLAC__byte)uval32; + break; + case 8: + data_out[0] = (FLAC__byte)uval32; + break; + } + } + } + } + dither_context->LastHistoryIndex = (last_history_index + wide_samples) % 32; + + return wide_samples * channels * (target_bps/8); +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/replaygain_synthesis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/replaygain_synthesis.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,51 @@ +/* replaygain_synthesis - Routines for applying ReplayGain to a signal + * Copyright (C) 2002,2003,2004,2005 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H +#define FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H + +#include /* for size_t */ +#include "FLAC/ordinals.h" + +#define FLAC_SHARE__MAX_SUPPORTED_CHANNELS 2 + +typedef enum { + NOISE_SHAPING_NONE = 0, + NOISE_SHAPING_LOW = 1, + NOISE_SHAPING_MEDIUM = 2, + NOISE_SHAPING_HIGH = 3 +} NoiseShaping; + +typedef struct { + const float* FilterCoeff; + FLAC__uint64 Mask; + double Add; + float Dither; + float ErrorHistory [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16]; /* 16th order Noise shaping */ + float DitherHistory [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16]; + int LastRandomNumber [FLAC_SHARE__MAX_SUPPORTED_CHANNELS]; + unsigned LastHistoryIndex; + NoiseShaping ShapingType; +} DitherContext; + +void FLAC__replaygain_synthesis__init_dither_context(DitherContext *dither, int bits, int shapingtype); + +/* scale = (float) pow(10., (double)replaygain * 0.05); */ +size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context); + +#endif diff -r 117bc56d906b -r 4b31176c198a src/flac112/tag.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/tag.c Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,149 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Based on FLAC plugin.c and mpg123 plugin + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "FLAC/metadata.h" +#include "plugin_common/tags.h" +#include "charset.h" +#include "configure.h" + +/* + * Function local__extname (filename) + * + * Return pointer within filename to its extenstion, or NULL if + * filename has no extension. + * + */ +static char *local__extname(const char *filename) +{ + char *ext = strrchr(filename, '.'); + + if (ext != NULL) + ++ext; + + return ext; +} + +static char *local__getstr(char* str) +{ + if (str && strlen(str) > 0) + return g_strdup(str); + return NULL; +} + +static int local__getnum(char* str) +{ + if (str && strlen(str) > 0) + return atoi(str); + return 0; +} + +static char *local__getfield(const FLAC__StreamMetadata *tags, const char *name) +{ + if (0 != tags) { + const char *utf8 = FLAC_plugin__tags_get_tag_utf8(tags, name); + if (0 != utf8) { + if(flac_cfg.title.convert_char_set) + return convert_from_utf8_to_user(utf8); + else + return strdup(utf8); + } + } + + return 0; +} + +/* + * Function flac_format_song_title (tag, filename) + * + * Create song title according to `tag' and/or `filename' and + * return it. The title must be subsequently freed using g_free(). + * + */ +TitleInput *flac_get_tuple(char *filename) +{ + TitleInput *input = NULL; + FLAC__StreamMetadata *tags; + FLAC__StreamMetadata info; + char *title, *artist, *performer, *album, *date, *tracknumber, *genre, *description; + gchar *filename_proxy = g_strdup(filename); + + FLAC_plugin__tags_get(filename_proxy, &tags); + + title = local__getfield(tags, "TITLE"); + artist = local__getfield(tags, "ARTIST"); + performer = local__getfield(tags, "PERFORMER"); + album = local__getfield(tags, "ALBUM"); + date = local__getfield(tags, "DATE"); + tracknumber = local__getfield(tags, "TRACKNUMBER"); + genre = local__getfield(tags, "GENRE"); + description = local__getfield(tags, "DESCRIPTION"); + + input = bmp_title_input_new(); + + input->performer = local__getstr(performer); + if(!input->performer) + input->performer = local__getstr(artist); + input->album_name = local__getstr(album); + input->track_name = local__getstr(title); + input->track_number = local__getnum(tracknumber); + input->year = local__getnum(date); + input->genre = local__getstr(genre); + input->comment = local__getstr(description); + + input->file_name = g_path_get_basename(filename_proxy); + input->file_path = g_path_get_dirname(filename_proxy); + input->file_ext = local__extname(filename_proxy); + + FLAC__metadata_get_streaminfo(filename, &info); + + input->length = (unsigned)((double)info.data.stream_info.total_samples / (double)info.data.stream_info.sample_rate * 1000.0 + 0.5); + + return input; +} + +gchar *flac_format_song_title(gchar *filename) +{ + gchar *ret = NULL; + TitleInput *tuple = flac_get_tuple(filename); + + ret = xmms_get_titlestring(flac_cfg.title.tag_override ? flac_cfg.title.tag_format : xmms_get_gentitle_format(), tuple); + + if (!ret) { + /* + * Format according to filename. + */ + ret = g_strdup(g_basename(filename)); + if (local__extname(ret) != NULL) + *(local__extname(ret) - 1) = '\0'; /* removes period */ + } + + bmp_title_input_free(tuple); + + return ret; +} diff -r 117bc56d906b -r 4b31176c198a src/flac112/tag.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flac112/tag.h Mon Oct 23 19:55:09 2006 -0700 @@ -0,0 +1,25 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __FLAC__PLUGIN_XMMS__TAG_H__ +#define __FLAC__PLUGIN_XMMS__TAG_H__ + +TitleInput *flac_get_tuple(gchar * filename); +gchar *flac_format_song_title(gchar * filename); + +#endif