major plugin revamp based on x-fixer's code
authorJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 24 Sep 2004 13:57:40 +0000 (13:57 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 24 Sep 2004 13:57:40 +0000 (13:57 +0000)
30 files changed:
build/config.mk
configure.in
src/plugin_common/canonical_tag.c
src/plugin_common/canonical_tag.h
src/plugin_common/id3v1.c
src/plugin_common/id3v1.h
src/plugin_common/id3v2.c
src/plugin_common/id3v2.h
src/plugin_common/plugin_common_static.dsp
src/plugin_common/vorbiscomment.c
src/plugin_common/vorbiscomment.h
src/plugin_winamp2/config.c
src/plugin_winamp2/config.h
src/plugin_winamp2/in_flac.c
src/plugin_winamp2/in_flac.dsp
src/plugin_winamp2/infobox.c
src/plugin_winamp2/infobox.h
src/plugin_winamp2/playback.c [new file with mode: 0644]
src/plugin_winamp2/playback.h [new file with mode: 0644]
src/plugin_winamp2/resource.h
src/plugin_winamp2/resource.rc
src/plugin_winamp2/tagz.cpp
src/plugin_winamp2/tagz.h
src/plugin_xmms/charset.c
src/plugin_xmms/charset.h
src/plugin_xmms/configure.c
src/plugin_xmms/configure.h
src/plugin_xmms/fileinfo.c
src/plugin_xmms/plugin.c
src/plugin_xmms/wrap_id3.c

index 2cae934..eaba182 100644 (file)
@@ -37,4 +37,4 @@ all default: $(DEFAULT_BUILD)
 
 VERSION=\"1.1.1\"
 
-CONFIG_CFLAGS=-DHAVE_INTTYPES_H
+CONFIG_CFLAGS=-D_GNU_SOURCE -DHAVE_INTTYPES_H -DHAVE_WCSDUP -DHAVE_WCSCASECMP
index 5cb7e77..2ecf9a2 100644 (file)
@@ -147,10 +147,16 @@ AM_PROG_AS
 AC_PROG_CXX
 AC_PROG_MAKE_SET
 
+dnl In order to have access to any available wcsdup() and wcscasecmp()
+AC_GNU_SOURCE
+
 dnl check for getopt in standard library
 dnl AC_CHECK_FUNCS(getopt_long , , [LIBOBJS="$LIBOBJS getopt.o getopt1.o"] )
 AC_CHECK_FUNCS(getopt_long, [], [])
 
+dnl Check for uncommon wide char functions
+AC_CHECK_FUNCS(wcsdup, wcscasecmp, [], [])
+
 AC_CANONICAL_HOST
 case "$host_cpu" in
        i*86)   cpu_ia32=true ; AC_DEFINE(FLAC__CPU_IA32) ;;
index fe07214..7195119 100644 (file)
-/* plugin_common - Routines common to several plugins
- * Copyright (C) 2002,2003,2004  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "canonical_tag.h"
-#include "id3v2.h"
-#include "vorbiscomment.h"
-#include "FLAC/assert.h"
-#include "FLAC/metadata.h"
-
-static void local__safe_free(void *object)
-{
-       if(0 != object)
-               free(object);
-}
-
-static void local__copy_field(char **dest, const char *src, unsigned n)
-{
-       if(n > 0) {
-               const char *p = src + n;
-               while(p > src && *(--p) == ' ')
-                       ;
-               n = p - src + 1;
-               if(0 != (*dest = malloc(n+1))) {
-                       memcpy(*dest, src, n);
-                       (*dest)[n] = '\0';
-               }
-       }
-       else
-               *dest = 0;
-}
-
-static FLAC__bool local__get_id3v1_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag)
-{
-       FLAC_Plugin__Id3v1_Tag id3v1_tag;
-
-       if(FLAC_plugin__id3v1_tag_get(filename, &id3v1_tag)) {
-               FLAC_plugin__canonical_tag_convert_from_id3v1(tag, &id3v1_tag);
-               return true;
-       }
-       return false;
-}
-
-FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new()
-{
-       FLAC_Plugin__CanonicalTag *object = (FLAC_Plugin__CanonicalTag*)malloc(sizeof(FLAC_Plugin__CanonicalTag));
-       if(0 != object)
-               FLAC_plugin__canonical_tag_init(object);
-       return object;
-}
-
-void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *object)
-{
-       FLAC__ASSERT(0 != object);
-       FLAC_plugin__canonical_tag_clear(object);
-       free(object);
-}
-
-void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *object)
-{
-       FLAC__ASSERT(0 != object);
-       object->title = 0;
-       object->composer = 0;
-       object->performer = 0;
-       object->album = 0;
-       object->year_recorded = 0;
-       object->year_performed = 0;
-       object->track_number = 0;
-       object->tracks_in_album = 0;
-       object->genre = 0;
-       object->comment = 0;
-}
-
-void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *object)
-{
-       FLAC__ASSERT(0 != object);
-       local__safe_free(object->title);
-       local__safe_free(object->composer);
-       local__safe_free(object->performer);
-       local__safe_free(object->album);
-       local__safe_free(object->year_recorded);
-       local__safe_free(object->year_performed);
-       local__safe_free(object->track_number);
-       local__safe_free(object->tracks_in_album);
-       local__safe_free(object->genre);
-       local__safe_free(object->comment);
-       FLAC_plugin__canonical_tag_init(object);
-}
-
-static void local__grab(char **dest, char **src)
-{
-       if(0 == *dest) {
-               *dest = *src;
-               *src = 0;
-       }
-}
-
-void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, FLAC_Plugin__CanonicalTag *src)
-{
-       local__grab(&dest->title, &src->title);
-       local__grab(&dest->composer, &src->composer);
-       local__grab(&dest->performer, &src->performer);
-       local__grab(&dest->album, &src->album);
-       local__grab(&dest->year_recorded, &src->year_recorded);
-       local__grab(&dest->year_performed, &src->year_performed);
-       local__grab(&dest->track_number, &src->track_number);
-       local__grab(&dest->tracks_in_album, &src->tracks_in_album);
-       local__grab(&dest->genre, &src->genre);
-       local__grab(&dest->comment, &src->comment);
-}
-
-void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v1_Tag *id3v1_tag)
-{
-       local__copy_field(&object->title, id3v1_tag->title, 30);
-       local__copy_field(&object->composer, id3v1_tag->artist, 30);
-       local__copy_field(&object->performer, id3v1_tag->artist, 30);
-       local__copy_field(&object->album, id3v1_tag->album, 30);
-       local__copy_field(&object->year_performed, id3v1_tag->year, 4);
-
-       /* Check for v1.1 tags. */
-       if (id3v1_tag->comment.v1_1.zero == 0) {
-               if(0 != (object->track_number = malloc(4)))
-                       sprintf(object->track_number, "%u", (unsigned)id3v1_tag->comment.v1_1.track);
-               local__copy_field(&object->comment, id3v1_tag->comment.v1_1.comment, 28);
-       }
-       else {
-               object->track_number = strdup("0");
-               local__copy_field(&object->comment, id3v1_tag->comment.v1_0.comment, 30);
-       }
-
-       object->genre = strdup(FLAC_plugin__id3v1_tag_get_genre_as_string(id3v1_tag->genre));
-}
-
-void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag)
-{
-       FLAC_Plugin__CanonicalTag id3v1_tag, id3v2_tag;
-
-       FLAC_plugin__vorbiscomment_get(filename, tag);
-
-       FLAC_plugin__canonical_tag_init(&id3v2_tag);
-       (void)FLAC_plugin__id3v2_tag_get(filename, &id3v2_tag);
-
-       FLAC_plugin__canonical_tag_init(&id3v1_tag);
-       (void)local__get_id3v1_tag_as_canonical(filename, &id3v1_tag);
-
-       /* merge tags, preferring, in order: vorbis comments, id3v2, id3v1 */
-       FLAC_plugin__canonical_tag_merge(tag, &id3v2_tag);
-       FLAC_plugin__canonical_tag_merge(tag, &id3v1_tag);
-
-       FLAC_plugin__canonical_tag_clear(&id3v1_tag);
-       FLAC_plugin__canonical_tag_clear(&id3v2_tag);
-}
+/* plugin_common - Routines common to several plugins\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include <config.h>\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+\r
+#include "canonical_tag.h"\r
+#include "id3v2.h"\r
+#include "vorbiscomment.h"\r
+#include "FLAC/assert.h"\r
+#include "FLAC/metadata.h"\r
+\r
+#if 0\r
+#define __USE_GNU /*@@@@@@ needed on glibc systems to get wcsdup() and wcscasecmp() */\r
+#endif\r
+#include <wchar.h>\r
+\r
+/*\r
+ * Here lies hackage to get any missing wide character string functions we\r
+ * need.  The fallback implementations here are from glibc.\r
+ */\r
+\r
+#if !defined(_MSC_VER) && !defined(HAVE_WCSDUP)\r
+/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.\r
+   This file is part of the GNU C Library.\r
+   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.\r
+\r
+   The GNU C Library is free software; you can redistribute it and/or\r
+   modify it under the terms of the GNU Lesser General Public\r
+   License as published by the Free Software Foundation; either\r
+   version 2.1 of the License, or (at your option) any later version.\r
+\r
+   The GNU C Library is distributed in the hope that it will be useful,\r
+   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+   Lesser General Public License for more details.\r
+\r
+   You should have received a copy of the GNU Lesser General Public\r
+   License along with the GNU C Library; if not, write to the Free\r
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\r
+   02111-1307 USA.  */\r
+\r
+#include <wchar.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+\r
+\r
+/* Duplicate S, returning an identical malloc'd string.         */\r
+wchar_t *\r
+wcsdup (s)\r
+     const wchar_t *s;\r
+{\r
+  size_t len = (__wcslen (s) + 1) * sizeof (wchar_t);\r
+  void *new = malloc (len);\r
+\r
+  if (new == NULL)\r
+    return NULL;\r
+\r
+  return (wchar_t *) memcpy (new, (void *) s, len);\r
+}\r
+#endif\r
+\r
+#if !defined(_MSC_VER) && !defined(HAVE_WCSCASECMP)\r
+/* Copyright (C) 1991, 1992, 1995, 1996, 1997 Free Software Foundation, Inc.\r
+   This file is part of the GNU C Library.\r
+\r
+   The GNU C Library is free software; you can redistribute it and/or\r
+   modify it under the terms of the GNU Lesser General Public\r
+   License as published by the Free Software Foundation; either\r
+   version 2.1 of the License, or (at your option) any later version.\r
+\r
+   The GNU C Library is distributed in the hope that it will be useful,\r
+   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+   Lesser General Public License for more details.\r
+\r
+   You should have received a copy of the GNU Lesser General Public\r
+   License along with the GNU C Library; if not, write to the Free\r
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\r
+   02111-1307 USA.  */\r
+\r
+#include <wctype.h>\r
+#include <wchar.h>\r
+\r
+#ifndef weak_alias\r
+# define __wcscasecmp wcscasecmp\r
+# define TOLOWER(Ch) towlower (Ch)\r
+#else\r
+# ifdef USE_IN_EXTENDED_LOCALE_MODEL\r
+#  define __wcscasecmp __wcscasecmp_l\r
+#  define TOLOWER(Ch) __towlower_l ((Ch), loc)\r
+# else\r
+#  define TOLOWER(Ch) towlower (Ch)\r
+# endif\r
+#endif\r
+\r
+#ifdef USE_IN_EXTENDED_LOCALE_MODEL\r
+# define LOCALE_PARAM , loc\r
+# define LOCALE_PARAM_DECL __locale_t loc;\r
+#else\r
+# define LOCALE_PARAM\r
+# define LOCALE_PARAM_DECL\r
+#endif\r
+\r
+/* Compare S1 and S2, ignoring case, returning less than, equal to or\r
+   greater than zero if S1 is lexicographically less than,\r
+   equal to or greater than S2.  */\r
+int\r
+__wcscasecmp (s1, s2 LOCALE_PARAM)\r
+     const wchar_t *s1;\r
+     const wchar_t *s2;\r
+     LOCALE_PARAM_DECL\r
+{\r
+  wint_t c1, c2;\r
+\r
+  if (s1 == s2)\r
+    return 0;\r
+\r
+  do\r
+    {\r
+      c1 = TOLOWER (*s1++);\r
+      c2 = TOLOWER (*s2++);\r
+      if (c1 == L'\0')\r
+       break;\r
+    }\r
+  while (c1 == c2);\r
+\r
+  return c1 - c2;\r
+}\r
+#ifndef __wcscasecmp\r
+weak_alias (__wcscasecmp, wcscasecmp)\r
+#endif\r
+#endif\r
+\r
+#ifndef _MSC_VER\r
+/* @@@ cheesy and does base 10 only */\r
+wchar_t *local__itow(int value, wchar_t *string)\r
+{\r
+       if (value == 0) {\r
+               string[0] = (wchar_t)'0';\r
+               string[1] = (wchar_t)0;\r
+       }\r
+       else {\r
+               /* convert backwards, then reverse string */\r
+               wchar_t *start = string, *s;\r
+               if (value < 0) {\r
+                       *start++ = (wchar_t)'-';\r
+                       value = -value; /* @@@ overflow at INT_MIN */\r
+               }\r
+               s = start;\r
+               while (value > 0) {\r
+                       *s++ = (wchar_t)((value % 10) + '0');\r
+                       value /= 10;\r
+               }\r
+               *s-- = (wchar_t)0;\r
+               while (s > start) {\r
+                       wchar_t tmp = *s;\r
+                       *s-- = *start;\r
+                       *start++ = tmp;\r
+               }\r
+       }\r
+\r
+       return string;\r
+}\r
+#endif\r
+\r
+/*\r
+ *  helpers\r
+ */\r
+\r
+/* TODO: should be moved out somewhere? @@@ */\r
+\r
+wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src)\r
+{\r
+       int len;\r
+       wchar_t *dest;\r
+\r
+       FLAC__ASSERT(0 != src);\r
+\r
+       len = strlen(src) + 1;\r
+       /* copy */\r
+       dest = malloc(len*sizeof(wchar_t));\r
+       if (dest) mbstowcs(dest, src, len);\r
+       return dest;\r
+}\r
+\r
+/* TODO: more validation? @@@ */\r
+static __inline int utf8len(const FLAC__byte *utf8)\r
+{\r
+       FLAC__ASSERT(0 != utf8);\r
+       if ((*utf8 & 0x80) == 0)\r
+               return 1;\r
+       else if ((*utf8 & 0xE0) == 0xC0)\r
+               return 2;\r
+       else if ((*utf8 & 0xF0) == 0xE0)\r
+               return 3;\r
+       else return 0;\r
+}\r
+\r
+/* TODO: validation? @@@ */\r
+static __inline int utf8_to_ucs2(const FLAC__byte *utf8, wchar_t *ucs2)\r
+{\r
+       int len;\r
+       FLAC__ASSERT(utf8!=0 && *utf8!=0 && ucs2!=0);\r
+\r
+       if (!(len = utf8len(utf8))) return 0;\r
+\r
+       if (len == 1)\r
+               *ucs2 = *utf8;\r
+       else if (len == 2)\r
+               *ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F);\r
+       else if (len == 3)\r
+               *ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F);\r
+       else {\r
+               FLAC__ASSERT(len == 0);\r
+       }\r
+\r
+       return len;\r
+}\r
+\r
+wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length)\r
+{\r
+       wchar_t *out, *p;\r
+       const char *s;\r
+       int len = 0;\r
+       /* calculate length */\r
+       for (s=src; length && *s; len++)\r
+       {\r
+               int l = utf8len(s);\r
+               if (!l) break;\r
+               s += l;\r
+               length -= l;\r
+       }\r
+       /* allocate */\r
+       len++;\r
+       p = out = (wchar_t*)malloc(len * sizeof(wchar_t));\r
+       if (!out) return NULL;\r
+       /* convert */\r
+       for (s=src; --len; p++)\r
+       {\r
+               int l = utf8_to_ucs2(s, p);\r
+               /* l==0 is possible, because real conversion */\r
+               /* might do more careful validation */\r
+               if (!l) break;\r
+               s += l;\r
+       }\r
+       *p = 0;\r
+\r
+       return out;\r
+}\r
+\r
+static __inline int ucs2len(wchar_t ucs2)\r
+{\r
+       if (ucs2 < 0x0080)\r
+               return 1;\r
+       else if (ucs2 < 0x0800)\r
+               return 2;\r
+       else return 3;\r
+}\r
+\r
+static __inline int ucs2_to_utf8(wchar_t ucs2, FLAC__byte *utf8)\r
+{\r
+       if (ucs2 < 0x080)\r
+       {\r
+               utf8[0] = (FLAC__byte)ucs2;\r
+               return 1;\r
+       }\r
+       else if (ucs2 < 0x800)\r
+       {\r
+               utf8[0] = 0xc0 | (ucs2 >> 6);\r
+               utf8[1] = 0x80 | (ucs2 & 0x3f);\r
+               return 2;\r
+       }\r
+       else\r
+       {\r
+               utf8[0] = 0xe0 | (ucs2 >> 12);\r
+               utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f);\r
+               utf8[2] = 0x80 | (ucs2 & 0x3f);\r
+               return 3;\r
+       }\r
+}\r
+\r
+char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src)\r
+{\r
+       const wchar_t *s;\r
+       char *out, *p;\r
+       int len = 0;\r
+       FLAC__ASSERT(0 != src);\r
+       /* calculate length */\r
+       for (s=src; *s; s++)\r
+               len += ucs2len(*s);\r
+       /* allocate */\r
+       len++;\r
+       p = out = malloc(len);\r
+       if (!out) return NULL;\r
+       /* convert */\r
+       for (s=src; *s; s++)\r
+       {\r
+               int l = ucs2_to_utf8(*s, p);\r
+               p += l;\r
+       }\r
+       *p = 0;\r
+\r
+       return out;\r
+}\r
+\r
+/*\r
+ *  init/clear/delete\r
+ */\r
+\r
+FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new()\r
+{\r
+       FLAC_Plugin__CanonicalTag *object = (FLAC_Plugin__CanonicalTag*)malloc(sizeof(FLAC_Plugin__CanonicalTag));\r
+       if (object != 0)\r
+               FLAC_plugin__canonical_tag_init(object);\r
+       return object;\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *object)\r
+{\r
+       FLAC_plugin__canonical_tag_clear(object);\r
+       free(object);\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *object)\r
+{\r
+       object->head = object->tail = 0;\r
+       object->count = 0;\r
+}\r
+\r
+static void FLAC_plugin__canonical_tag_clear_entry(FLAC__tag_entry *entry)\r
+{\r
+       free(entry->name);\r
+       free(entry->value);\r
+       free(entry);\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *object)\r
+{\r
+       FLAC__tag_entry *entry = object->head;\r
+\r
+       while (entry)\r
+       {\r
+               FLAC__tag_entry *next = entry->next;\r
+               FLAC_plugin__canonical_tag_clear_entry(entry);\r
+               entry = next;\r
+       }\r
+\r
+       FLAC_plugin__canonical_tag_init(object);\r
+}\r
+\r
+/*\r
+ *  internal\r
+ */\r
+\r
+static FLAC__tag_entry *FLAC_plugin__canonical_find(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)\r
+{\r
+       FLAC__tag_entry *entry = tag->head;\r
+\r
+       while (entry)\r
+       {\r
+#if defined _MSC_VER || defined __MINGW32__\r
+#define FLAC__WCSCASECMP wcsicmp\r
+#else\r
+#define FLAC__WCSCASECMP wcscasecmp\r
+#endif\r
+               if (!FLAC__WCSCASECMP(name, entry->name))\r
+#undef FLAC__WCSCASECMP\r
+                       break;\r
+               entry = entry->next;\r
+       }\r
+\r
+       return entry;\r
+}\r
+\r
+/* NOTE: does NOT copy strings. takes ownership over passed strings. */\r
+static void FLAC_plugin__canonical_add_tail(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value)\r
+{\r
+       FLAC__tag_entry *entry = (FLAC__tag_entry*)malloc(sizeof(FLAC__tag_entry));\r
+       if (!entry)\r
+       {\r
+               free(name);\r
+               free(value);\r
+               return;\r
+       }\r
+       /* init */\r
+       entry->name = name;\r
+       entry->value = value;\r
+       /* add */\r
+       entry->prev = tag->tail;\r
+       if (tag->tail)\r
+               tag->tail->next = entry;\r
+       tag->tail = entry;\r
+       if (!tag->head)\r
+               tag->head = entry;\r
+       entry->next = 0;\r
+       tag->count++;\r
+}\r
+\r
+static void FLAC_plugin__canonical_add_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value)\r
+{\r
+       FLAC_plugin__canonical_add_tail(tag, wcsdup(name), wcsdup(value));\r
+}\r
+\r
+/* NOTE: does NOT copy value, but copies name */\r
+static void FLAC_plugin__canonical_set_nc(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, wchar_t *value)\r
+{\r
+       FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);\r
+\r
+       if (entry)\r
+       {\r
+               free(entry->value);\r
+               entry->value = value;\r
+       }\r
+       else FLAC_plugin__canonical_add_tail(tag, wcsdup(name), value);\r
+}\r
+\r
+/* NOTE: does NOT copy strings. takes ownership over passed strings. (except sep!) */\r
+static void FLAC_plugin__canonical_add_nc(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value, const wchar_t *sep)\r
+{\r
+       FLAC__tag_entry *entry;\r
+\r
+       if (sep && (entry = FLAC_plugin__canonical_find(tag, name)))\r
+       {\r
+               unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1;\r
+               wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t));\r
+\r
+               if (newvalue)\r
+               {\r
+                       entry->value = newvalue;\r
+                       wcscat(entry->value, sep);\r
+                       wcscat(entry->value, value);\r
+               }\r
+\r
+               free(name);\r
+               free(value);\r
+       }\r
+       else FLAC_plugin__canonical_add_tail(tag, name, value);\r
+}\r
+\r
+/*\r
+ *  manipulation\r
+ */\r
+\r
+void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value)\r
+{\r
+       FLAC_plugin__canonical_set_nc(tag, name, wcsdup(value));\r
+}\r
+\r
+void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value)\r
+{\r
+       FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);\r
+       if (!entry) FLAC_plugin__canonical_add_new(tag, name, value);\r
+}\r
+\r
+void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value)\r
+{\r
+       wchar_t *val = FLAC_plugin__convert_ansi_to_wide(value);\r
+       if (val) FLAC_plugin__canonical_set_nc(tag, name, val);\r
+}\r
+\r
+void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep)\r
+{\r
+       FLAC__tag_entry *entry;\r
+\r
+       if (sep && (entry = FLAC_plugin__canonical_find(tag, name)))\r
+       {\r
+               unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1;\r
+               wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t));\r
+\r
+               if (newvalue)\r
+               {\r
+                       entry->value = newvalue;\r
+                       wcscat(entry->value, sep);\r
+                       wcscat(entry->value, value);\r
+               }\r
+       }\r
+       else FLAC_plugin__canonical_add_new(tag, name, value);\r
+}\r
+\r
+void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep)\r
+{\r
+       wchar_t *n = FLAC_plugin__convert_utf8_to_ucs2(name, namelen);\r
+       wchar_t *v = FLAC_plugin__convert_utf8_to_ucs2(value, vallen);\r
+       wchar_t *s = sep ? FLAC_plugin__convert_utf8_to_ucs2(sep, -1) : 0;\r
+\r
+       if (n && v)\r
+       {\r
+               FLAC_plugin__canonical_add_nc(tag, n, v, s);\r
+       }\r
+       else\r
+       {\r
+               if (n) free(n);\r
+               if (v) free(v);\r
+       }\r
+       if (s) free(s);\r
+}\r
+\r
+const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)\r
+{\r
+       FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);\r
+       return entry ? entry->value : 0;\r
+}\r
+\r
+FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)\r
+{\r
+       FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);\r
+\r
+       if (entry)\r
+       {\r
+               if (entry->prev)\r
+                       entry->prev->next = entry->next;\r
+               else tag->head = entry->next;\r
+\r
+               if (entry->next)\r
+                       entry->next->prev = entry->prev;\r
+               else tag->tail = entry->prev;\r
+\r
+               FLAC_plugin__canonical_tag_clear_entry(entry);\r
+               tag->count--;\r
+               return true;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)\r
+{\r
+       while (FLAC_plugin__canonical_remove(tag, name));\r
+}\r
+\r
+char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it)\r
+{\r
+       int len1 = wcslen(it->name);\r
+       int len2 = wcslen(it->value);\r
+       int len  = len1 + len2 + 1;\r
+       wchar_t *val = malloc((len+1) * sizeof(wchar_t));\r
+\r
+       if (val)\r
+       {\r
+               char *res;\r
+\r
+               memcpy(val, it->name, len1 * sizeof(wchar_t));\r
+               val[len1] = '=';\r
+               memcpy(val+len1+1, it->value, len2 * sizeof(wchar_t));\r
+               val[len] = 0;\r
+\r
+               res = FLAC_plugin__convert_ucs2_to_utf8(val);\r
+               free(val);\r
+               return res;\r
+       }\r
+\r
+       return NULL;\r
+}\r
+\r
+/*\r
+ *  merging\r
+ */\r
+\r
+void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src)\r
+{\r
+       FLAC__tag_entry *entry = src->head;\r
+\r
+       while (entry)\r
+       {\r
+               FLAC_plugin__canonical_set_new(dest, entry->name, entry->value);\r
+               entry = entry->next;\r
+       }\r
+}\r
+\r
+static wchar_t *local__copy_field(const char *src, unsigned n)\r
+{\r
+       const char *p = src + n;\r
+       wchar_t *dest;\r
+       FLAC__ASSERT(n > 0);\r
+\r
+       while (p>src && *(--p)==' ');\r
+\r
+       n = p - src + 1;\r
+       if (!n) return NULL;\r
+\r
+       if ((dest = malloc((n+1)*sizeof(wchar_t))) != 0)\r
+       {\r
+               mbstowcs(dest, src, n);\r
+               dest[n] = 0;\r
+       }\r
+       return dest;\r
+}\r
+\r
+static void local__add_id3_field(FLAC_Plugin__CanonicalTag *object, const char *value, size_t length, const wchar_t *new_name)\r
+{\r
+       wchar_t *tmp;\r
+       if (0 != value && length > 0) {\r
+               tmp = local__copy_field(value, length);\r
+               if (tmp)\r
+                       FLAC_plugin__canonical_add_tail(object, wcsdup(new_name), tmp);\r
+       }\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v1_Tag *id3v1_tag)\r
+{\r
+       wchar_t *tmp;\r
+       FLAC_plugin__canonical_tag_clear(object);\r
+\r
+       local__add_id3_field(object, id3v1_tag->title, 30, L"TITLE");\r
+       local__add_id3_field(object, id3v1_tag->artist, 30, L"ARTIST");\r
+       local__add_id3_field(object, id3v1_tag->album, 30, L"ALBUM");\r
+       local__add_id3_field(object, id3v1_tag->year, 4, L"YEAR");\r
+\r
+       /* check for v1.1 tags */\r
+       if (id3v1_tag->zero == 0)\r
+       {\r
+               if (id3v1_tag->track && (tmp=(wchar_t*)malloc(sizeof(id3v1_tag->track)*4*sizeof(wchar_t)))!=0)\r
+               {\r
+#ifdef _MSC_VER\r
+                       _itow(id3v1_tag->track, tmp, 10);\r
+#else\r
+                       local__itow(id3v1_tag->track, tmp);\r
+#endif\r
+                       FLAC_plugin__canonical_add_tail(object, wcsdup(L"TRACKNUMBER"), tmp);\r
+               }\r
+               local__add_id3_field(object, id3v1_tag->comment, 28, L"DESCRIPTION");\r
+       }\r
+       else\r
+       {\r
+               local__add_id3_field(object, id3v1_tag->comment, 30, L"DESCRIPTION");\r
+       }\r
+\r
+       tmp = FLAC_plugin__convert_ansi_to_wide(FLAC_plugin__id3v1_tag_get_genre_as_string(id3v1_tag->genre));\r
+       if (tmp) FLAC_plugin__canonical_add_tail(object, wcsdup(L"GENRE"), tmp);\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_convert_from_id3v2(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v2_Tag *id3v2_tag)\r
+{\r
+       FLAC_plugin__canonical_tag_clear(object);\r
+\r
+       local__add_id3_field(object, id3v2_tag->title          , strlen(id3v2_tag->title)          , L"TITLE");\r
+       local__add_id3_field(object, id3v2_tag->composer       , strlen(id3v2_tag->composer)       , L"ARTIST");\r
+       local__add_id3_field(object, id3v2_tag->performer      , strlen(id3v2_tag->performer)      , L"PERFORMER");\r
+       local__add_id3_field(object, id3v2_tag->album          , strlen(id3v2_tag->album)          , L"ALBUM");\r
+       local__add_id3_field(object, id3v2_tag->year_recorded  , strlen(id3v2_tag->year_recorded)  , L"YEAR_RECORDED");\r
+       local__add_id3_field(object, id3v2_tag->year_performed , strlen(id3v2_tag->year_performed) , L"YEAR_PERFORMED");\r
+       local__add_id3_field(object, id3v2_tag->track_number   , strlen(id3v2_tag->track_number)   , L"TRACKNUMBER");\r
+       local__add_id3_field(object, id3v2_tag->tracks_in_album, strlen(id3v2_tag->tracks_in_album), L"TRACKS_IN_ALBUM");\r
+       local__add_id3_field(object, id3v2_tag->genre          , strlen(id3v2_tag->genre)          , L"GENRE");\r
+       local__add_id3_field(object, id3v2_tag->comment        , strlen(id3v2_tag->comment)        , L"DESCRIPTION");\r
+}\r
+\r
+static FLAC__bool local__get_id3v1_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       FLAC_Plugin__Id3v1_Tag id3v1_tag;\r
+\r
+       if (FLAC_plugin__id3v1_tag_get(filename, &id3v1_tag))\r
+       {\r
+               FLAC_plugin__canonical_tag_convert_from_id3v1(tag, &id3v1_tag);\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+static FLAC__bool local__get_id3v2_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       FLAC_Plugin__Id3v2_Tag id3v2_tag;\r
+\r
+       if (FLAC_plugin__id3v2_tag_get(filename, &id3v2_tag))\r
+       {\r
+               FLAC_plugin__canonical_tag_convert_from_id3v2(tag, &id3v2_tag);\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_add_id3v1(const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       FLAC_Plugin__CanonicalTag id3v1_tag;\r
+\r
+       FLAC_plugin__canonical_tag_init(&id3v1_tag);\r
+       (void)local__get_id3v1_tag_as_canonical(filename, &id3v1_tag);\r
+       FLAC_plugin__canonical_tag_merge(tag, &id3v1_tag);\r
+\r
+       FLAC_plugin__canonical_tag_clear(&id3v1_tag);\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_add_id3v2(const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       FLAC_Plugin__CanonicalTag id3v2_tag;\r
+\r
+       FLAC_plugin__canonical_tag_init(&id3v2_tag);\r
+       (void)local__get_id3v2_tag_as_canonical(filename, &id3v2_tag);\r
+       FLAC_plugin__canonical_tag_merge(tag, &id3v2_tag);\r
+\r
+       FLAC_plugin__canonical_tag_clear(&id3v2_tag);\r
+}\r
+\r
+void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep)\r
+{\r
+       FLAC_plugin__vorbiscomment_get(filename, tag, sep);\r
+       FLAC_plugin__canonical_tag_add_id3v2(filename, tag);\r
+       FLAC_plugin__canonical_tag_add_id3v1(filename, tag);\r
+}\r
index 6ba2be0..3133c5a 100644 (file)
-/* plugin_common - Routines common to several plugins
- * Copyright (C) 2002,2003,2004  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#ifndef FLAC__PLUGIN_COMMON__CANONICAL_TAG_H
-#define FLAC__PLUGIN_COMMON__CANONICAL_TAG_H
-
-#include "id3v1.h"
-
-typedef struct {
-       char *title;
-       char *composer;
-       char *performer;
-       char *album;
-       char *year_recorded;
-       char *year_performed;
-       char *track_number;
-       char *tracks_in_album;
-       char *genre;
-       char *comment;
-} FLAC_Plugin__CanonicalTag;
-
-FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new();
-void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *);
-void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *);
-void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *);
-
-/* For each null field in dest, move the corresponding field from src
- * WATCHOUT: note that src is not-const, because fields are 'moved' from
- * src to dest and the src field is set to null.
- */
-void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, FLAC_Plugin__CanonicalTag *src);
-
-void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v1_Tag *);
-
-/* Returns a merged tag based on any Vorbis comments, id3v2 tag, and id3v1.
- * In case of overlaps the preceding precedence applies.
- */
-void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag);
-
-#endif
+/* plugin_common - Routines common to several plugins\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#ifndef FLAC__PLUGIN_COMMON__CANONICAL_TAG_H\r
+#define FLAC__PLUGIN_COMMON__CANONICAL_TAG_H\r
+\r
+#include "id3v1.h"\r
+#include "id3v2.h"\r
+\r
+/* TODO: splay tree? */\r
+typedef struct tagFLAC__tag_entry FLAC__tag_entry;\r
+struct tagFLAC__tag_entry\r
+{\r
+       FLAC__tag_entry *next, *prev;\r
+       /* TODO: name in ascii? */\r
+       wchar_t *name;\r
+       wchar_t *value;\r
+};\r
+\r
+typedef struct {\r
+       FLAC__tag_entry *head, *tail;\r
+       unsigned count;\r
+} FLAC_Plugin__CanonicalTag;\r
+\r
+\r
+typedef FLAC__tag_entry *FLAC__tag_iterator;\r
+\r
+FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new();\r
+void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *);\r
+void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *);\r
+void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *);\r
+\r
+/* note that multiple fields with the same name are allowed.\r
+ * set - adds field if it's not present yet, or replaces\r
+ * existing field if it's present.\r
+ */\r
+void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value);\r
+void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value);\r
+/* set_new - only adds field if it's not present yet. */\r
+void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value);\r
+/* add - adds field if it's not present yet, or merges value with existing\r
+ * field, if it's present. (sep - separator string to use when merging;\r
+ * if sep==NULL no merging occurs - always adds new field)\r
+ */\r
+void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep);\r
+void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep); /* 'namelen'/'vallen' may be (unsigned)(-1) if 'name'/'value' is NUL-terminated */\r
+\r
+/* gets value of the first field with the given name (NULL if field not found) */\r
+const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name);\r
+/* removes first field with the given name.\r
+ * (returns 'true' if deleted, 'false' if not found)\r
+ */\r
+FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name);\r
+/* removes all fields with the given name. */\r
+void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name);\r
+\r
+/* enumeration */\r
+static __inline unsigned FLAC_plugin__canonical_get_count(FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       return tag->count;\r
+}\r
+static __inline FLAC__tag_iterator FLAC_plugin__canonical_first(FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       return tag->head;\r
+}\r
+static __inline FLAC__tag_iterator FLAC_plugin__canonical_next(FLAC__tag_iterator it)\r
+{\r
+       return it->next;\r
+}\r
+static __inline wchar_t *FLAC_plugin__canonical_get_name(FLAC__tag_iterator it)\r
+{\r
+       return it->name;\r
+}\r
+static __inline wchar_t *FLAC_plugin__canonical_get_value(FLAC__tag_iterator it)\r
+{\r
+       return it->value;\r
+}\r
+\r
+/* returns a new string containing the current entry in UTF-8 in "NAME=VALUE" form */\r
+char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it);\r
+\r
+void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src);\r
+void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v1_Tag *);\r
+void FLAC_plugin__canonical_tag_convert_from_id3v2(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v2_Tag *);\r
+\r
+void FLAC_plugin__canonical_tag_add_id3v1(const char *filename, FLAC_Plugin__CanonicalTag *tag);\r
+void FLAC_plugin__canonical_tag_add_id3v2(const char *filename, FLAC_Plugin__CanonicalTag *tag);\r
+\r
+/* Returns a merged tag based on any Vorbis comments, id3v2 tag, and id3v1.\r
+ * In case of overlaps the preceding precedence applies.\r
+ *\r
+ * sep - separator to use when merging fields with same name (in VorbisComment).\r
+ * should be in UTF-8. if sep==NULL, no merging occurs, so multiple fields\r
+ * with the same name can exist.\r
+ */\r
+void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep);\r
+\r
+/* helpers */\r
+wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src);\r
+wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length); /* 'length' may be (unsigned)(-1) if 'src' is NUL-terminated */\r
+char    *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src);\r
+\r
+#endif\r
index aa5fc68..274cc9b 100644 (file)
-/* plugin_common - Routines common to several plugins
- * Copyright (C) 2002,2003,2004  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <stdio.h>
-
-#include "FLAC/assert.h"
-#include "id3v1.h"
-#include "locale_hack.h"
-
-
-/* 
- * Do not sort genres!!
- * Last Update: 2000/04/30
- */
-static const char * const FLAC_plugin__id3v1_tag_genre_table[] =
-{
-       "Blues",                /* 0 */
-       "Classic Rock",
-       "Country",
-       "Dance",
-       "Disco",
-       "Funk",                 /* 5 */
-       "Grunge",
-       "Hip-Hop", 
-       "Jazz",
-       "Metal",
-       "New Age",              /* 10 */                
-       "Oldies",
-       "Other", 
-       "Pop",
-       "R&B",
-       "Rap",                  /* 15 */
-       "Reggae", 
-       "Rock",
-       "Techno",
-       "Industrial",
-       "Alternative",          /* 20 */
-       "Ska",
-       "Death Metal", 
-       "Pranks",
-       "Soundtrack",
-       "Euro-Techno",          /* 25 */
-       "Ambient",
-       "Trip-Hop", 
-       "Vocal",
-       "Jazz+Funk", 
-       "Fusion",               /* 30 */
-       "Trance",
-       "Classical",
-       "Instrumental", 
-       "Acid",
-       "House",                /* 35 */
-       "Game",
-       "Sound Clip", 
-       "Gospel",
-       "Noise",
-       "Altern Rock",          /* 40 */
-       "Bass",
-       "Soul",
-       "Punk",
-       "Space",
-       "Meditative",           /* 45 */
-       "Instrumental Pop",
-       "Instrumental Rock", 
-       "Ethnic",
-       "Gothic",
-       "Darkwave",             /* 50 */
-       "Techno-Industrial", 
-       "Electronic", 
-       "Pop-Folk",
-       "Eurodance", 
-       "Dream",                /* 55 */
-       "Southern Rock", 
-       "Comedy", 
-       "Cult",
-       "Gangsta",
-       "Top 40",               /* 60 */
-       "Christian Rap", 
-       "Pop/Funk", 
-       "Jungle",
-       "Native American", 
-       "Cabaret",              /* 65 */
-       "New Wave",
-       "Psychadelic", 
-       "Rave",
-       "Showtunes", 
-       "Trailer",              /* 70 */
-       "Lo-Fi",
-       "Tribal",
-       "Acid Punk",
-       "Acid Jazz", 
-       "Polka",                /* 75 */
-       "Retro",
-       "Musical",
-       "Rock & Roll", 
-       "Hard Rock", 
-       "Folk",                 /* 80 */
-       "Folk/Rock",
-       "National Folk", 
-       "Fast Fusion",
-       "Swing",
-       "Bebob",                /* 85 */
-       "Latin",
-       "Revival",
-       "Celtic",
-       "Bluegrass",
-       "Avantgarde",           /* 90 */
-       "Gothic Rock",
-       "Progressive Rock",
-       "Psychedelic Rock", 
-       "Symphonic Rock", 
-       "Slow Rock",            /* 95 */
-       "Big Band", 
-       "Chorus",
-       "Easy Listening", 
-       "Acoustic", 
-       "Humour",               /* 100 */
-       "Speech",
-       "Chanson", 
-       "Opera",
-       "Chamber Music", 
-       "Sonata",               /* 105 */
-       "Symphony",
-       "Booty Bass", 
-       "Primus",
-       "Porn Groove", 
-       "Satire",               /* 110 */
-       "Slow Jam", 
-       "Club",
-       "Tango",
-       "Samba",
-       "Folklore",             /* 115 */
-       "Ballad",
-       "Power Ballad",
-       "Rhythmic Soul",
-       "Freestyle",
-       "Duet",                 /* 120 */
-       "Punk Rock",
-       "Drum Solo",
-       "A Capella",
-       "Euro-House",
-       "Dance Hall",           /* 125 */
-       "Goa",
-       "Drum & Bass",
-       "Club-House",
-       "Hardcore",
-       "Terror",               /* 130 */
-       "Indie",
-       "BritPop",
-       "Negerpunk",
-       "Polsk Punk",
-       "Beat",                 /* 135 */
-       "Christian Gangsta Rap",
-       "Heavy Metal",
-       "Black Metal",
-       "Crossover",
-       "Contemporary Christian",/* 140 */
-       "Christian Rock",
-       "Merengue",
-       "Salsa",
-       "Thrash Metal",
-       "Anime",                /* 145 */
-       "JPop",
-       "Synthpop"
-};
-
-
-FLAC__bool FLAC_plugin__id3v1_tag_get(const char *filename, FLAC_Plugin__Id3v1_Tag *tag)
-{
-       char raw[128];
-       FILE *f;
-
-       FLAC__ASSERT(0 != filename);
-       FLAC__ASSERT(0 != tag);
-
-       memset(tag, 0, sizeof(FLAC_Plugin__Id3v1_Tag));
-
-       if(0 == (f = fopen(filename, "rb")))
-               return false;
-       if(-1 == fseek(f, -128, SEEK_END)) {
-               fclose(f);
-               return false;
-       }
-       if(fread(raw, 1, 128, f) < 128) {
-               fclose(f);
-               return false;
-       }
-       fclose(f);
-       if(strncmp(raw, "TAG", 3))
-               return false;
-       else {
-               memcpy(tag->tag, raw, 3);
-               memcpy(tag->title, raw+3, 30);
-               memcpy(tag->artist, raw+33, 30);
-               memcpy(tag->album, raw+63, 30);
-               memcpy(tag->year, raw+93, 4);
-               memcpy(tag->comment.v1_0.comment, raw+97, 30);
-               tag->genre = raw[127];
-               return true;
-       }
-}
-
-const char *FLAC_plugin__id3v1_tag_get_genre_as_string(unsigned char genre_code)
-{
-       if (genre_code < FLAC_plugin__id3v1_tag_genre_table_max())
-               return gettext(FLAC_plugin__id3v1_tag_genre_table[genre_code]);
-
-       return "";
-}
-
-unsigned FLAC_plugin__id3v1_tag_genre_table_max()
-{
-       return sizeof(FLAC_plugin__id3v1_tag_genre_table) / sizeof(FLAC_plugin__id3v1_tag_genre_table[0]) - 1;
-}
+/* plugin_common - Routines common to several plugins\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#include <stdio.h>\r
+\r
+#include "FLAC/assert.h"\r
+#include "id3v1.h"\r
+#include "locale_hack.h"\r
+\r
+\r
+/*\r
+ * Do not sort genres!!\r
+ * Last Update: 2000/04/30\r
+ */\r
+static const char * const FLAC_plugin__id3v1_tag_genre_table[] =\r
+{\r
+       "Blues",                /* 0 */\r
+       "Classic Rock",\r
+       "Country",\r
+       "Dance",\r
+       "Disco",\r
+       "Funk",                 /* 5 */\r
+       "Grunge",\r
+       "Hip-Hop",\r
+       "Jazz",\r
+       "Metal",\r
+       "New Age",              /* 10 */                \r
+       "Oldies",\r
+       "Other",\r
+       "Pop",\r
+       "R&B",\r
+       "Rap",                  /* 15 */\r
+       "Reggae",\r
+       "Rock",\r
+       "Techno",\r
+       "Industrial",\r
+       "Alternative",          /* 20 */\r
+       "Ska",\r
+       "Death Metal",\r
+       "Pranks",\r
+       "Soundtrack",\r
+       "Euro-Techno",          /* 25 */\r
+       "Ambient",\r
+       "Trip-Hop",\r
+       "Vocal",\r
+       "Jazz+Funk",\r
+       "Fusion",               /* 30 */\r
+       "Trance",\r
+       "Classical",\r
+       "Instrumental",\r
+       "Acid",\r
+       "House",                /* 35 */\r
+       "Game",\r
+       "Sound Clip",\r
+       "Gospel",\r
+       "Noise",\r
+       "Altern Rock",          /* 40 */\r
+       "Bass",\r
+       "Soul",\r
+       "Punk",\r
+       "Space",\r
+       "Meditative",           /* 45 */\r
+       "Instrumental Pop",\r
+       "Instrumental Rock",\r
+       "Ethnic",\r
+       "Gothic",\r
+       "Darkwave",             /* 50 */\r
+       "Techno-Industrial",\r
+       "Electronic",\r
+       "Pop-Folk",\r
+       "Eurodance",\r
+       "Dream",                /* 55 */\r
+       "Southern Rock",\r
+       "Comedy",\r
+       "Cult",\r
+       "Gangsta",\r
+       "Top 40",               /* 60 */\r
+       "Christian Rap",\r
+       "Pop/Funk",\r
+       "Jungle",\r
+       "Native American",\r
+       "Cabaret",              /* 65 */\r
+       "New Wave",\r
+       "Psychadelic",\r
+       "Rave",\r
+       "Showtunes",\r
+       "Trailer",              /* 70 */\r
+       "Lo-Fi",\r
+       "Tribal",\r
+       "Acid Punk",\r
+       "Acid Jazz",\r
+       "Polka",                /* 75 */\r
+       "Retro",\r
+       "Musical",\r
+       "Rock & Roll",\r
+       "Hard Rock",\r
+       "Folk",                 /* 80 */\r
+       "Folk/Rock",\r
+       "National Folk",\r
+       "Fast Fusion",\r
+       "Swing",\r
+       "Bebob",                /* 85 */\r
+       "Latin",\r
+       "Revival",\r
+       "Celtic",\r
+       "Bluegrass",\r
+       "Avantgarde",           /* 90 */\r
+       "Gothic Rock",\r
+       "Progressive Rock",\r
+       "Psychedelic Rock",\r
+       "Symphonic Rock",\r
+       "Slow Rock",            /* 95 */\r
+       "Big Band",\r
+       "Chorus",\r
+       "Easy Listening",\r
+       "Acoustic",\r
+       "Humour",               /* 100 */\r
+       "Speech",\r
+       "Chanson",\r
+       "Opera",\r
+       "Chamber Music",\r
+       "Sonata",               /* 105 */\r
+       "Symphony",\r
+       "Booty Bass",\r
+       "Primus",\r
+       "Porn Groove",\r
+       "Satire",               /* 110 */\r
+       "Slow Jam",\r
+       "Club",\r
+       "Tango",\r
+       "Samba",\r
+       "Folklore",             /* 115 */\r
+       "Ballad",\r
+       "Power Ballad",\r
+       "Rhythmic Soul",\r
+       "Freestyle",\r
+       "Duet",                 /* 120 */\r
+       "Punk Rock",\r
+       "Drum Solo",\r
+       "A Capella",\r
+       "Euro-House",\r
+       "Dance Hall",           /* 125 */\r
+       "Goa",\r
+       "Drum & Bass",\r
+       "Club-House",\r
+       "Hardcore",\r
+       "Terror",               /* 130 */\r
+       "Indie",\r
+       "BritPop",\r
+       "Negerpunk",\r
+       "Polsk Punk",\r
+       "Beat",                 /* 135 */\r
+       "Christian Gangsta Rap",\r
+       "Heavy Metal",\r
+       "Black Metal",\r
+       "Crossover",\r
+       "Contemporary Christian",/* 140 */\r
+       "Christian Rock",\r
+       "Merengue",\r
+       "Salsa",\r
+       "Thrash Metal",\r
+       "Anime",                /* 145 */\r
+       "JPop",\r
+       "Synthpop"\r
+};\r
+\r
+\r
+FLAC__bool FLAC_plugin__id3v1_tag_get(const char *filename, FLAC_Plugin__Id3v1_Tag *tag)\r
+{\r
+       FILE *f;\r
+       int res;\r
+\r
+       FLAC__ASSERT(0 != filename);\r
+       FLAC__ASSERT(0 != tag);\r
+\r
+       memset(tag, 0, sizeof(FLAC_Plugin__Id3v1_Tag));\r
+\r
+       if(0 == (f = fopen(filename, "rb")))\r
+               return false;\r
+       if(-1 == fseek(f, -128, SEEK_END)) {\r
+               fclose(f);\r
+               return false;\r
+       }\r
+       res = fread(tag, 128, 1, f);\r
+       fclose(f);\r
+       return res==1 && !strncmp(tag->tag, "TAG", 3);\r
+}\r
+\r
+const char *FLAC_plugin__id3v1_tag_get_genre_as_string(unsigned char genre_code)\r
+{\r
+       if (genre_code < (sizeof(FLAC_plugin__id3v1_tag_genre_table)/sizeof(FLAC_plugin__id3v1_tag_genre_table[0])))\r
+               return gettext(FLAC_plugin__id3v1_tag_genre_table[genre_code]);\r
+       return "Unknown";\r
+}\r
+\r
+unsigned FLAC_plugin__id3v1_tag_genre_table_max()\r
+{\r
+       return sizeof(FLAC_plugin__id3v1_tag_genre_table) / sizeof(FLAC_plugin__id3v1_tag_genre_table[0]) - 1;\r
+}\r
index 46e070d..28d361f 100644 (file)
 
 #include "FLAC/ordinals.h"
 
+#pragma pack(push, 1)
+
 typedef struct {
        char tag[3];
        char title[30];
        char artist[30];
        char album[30];
        char year[4];
-       union {
-               struct {
-                       char comment[30];
-               } v1_0;
-               struct {
-                       char comment[28];
-                       char zero;
-                       unsigned char track;
-               } v1_1;
-       } comment;
+    /* always use layout of id3 v1.1 */
+       char comment[28];
+       char zero;
+       unsigned char track;
        unsigned char genre;
 } FLAC_Plugin__Id3v1_Tag;
 
+#pragma pack(pop)
+
 FLAC__bool FLAC_plugin__id3v1_tag_get(const char *filename, FLAC_Plugin__Id3v1_Tag *tag);
 
 
index b18a1da..219a808 100644 (file)
 #include <config.h>
 #endif
 
+#include "FLAC/assert.h"
+
+#include <stdlib.h> /* for free() */
+#include <string.h> /* for memset() */
+
 #ifdef FLAC__HAS_ID3LIB
 #include <id3.h>
 #include <stdio.h>
@@ -32,8 +37,6 @@
 #include <ctype.h>
 #include <unistd.h>
 
-#include "FLAC/assert.h"
-
 #include "id3v1.h" /* for genre stuff */
 #include "locale_hack.h"
 
@@ -113,25 +116,25 @@ static size_t local__ID3Field_GetASCII_wrapper(const ID3Field *field, char *buff
         *  = 3.7.13 : first item num is 0 for ID3Field_GetASCII
         * >= 3.8.0  : doesn't need item num for ID3Field_GetASCII
         */
-#    if (ID3LIB_MAJOR >= 3)
+#      if (ID3LIB_MAJOR >= 3)
                 /* (>= 3.x.x) */
-#        if (ID3LIB_MINOR <= 7)
+#              if (ID3LIB_MINOR <= 7)
                         /* (3.0.0 to 3.7.x) */
-#            if (ID3LIB_PATCH >= 13)
+#                      if (ID3LIB_PATCH >= 13)
                                 /* (>= 3.7.13) */
                                 return ID3Field_GetASCII(field, buffer, maxChars, itemNum);
-#            else
+#                      else
                                 return ID3Field_GetASCII(field, buffer, maxChars, itemNum+1);
-#            endif
-#        else
+#                      endif
+#              else
                         /* (>= to 3.8.0) */
                         /*return ID3Field_GetASCII(field, buffer, maxChars); */
                         return ID3Field_GetASCIIItem(field, buffer, maxChars, itemNum);
-#        endif
-#    else
+#              endif
+#      else
                 /* Not tested (< 3.x.x) */
                 return ID3Field_GetASCII(field, buffer, maxChars, itemNum+1);
-#    endif
+#      endif
 }
 
 
@@ -161,7 +164,7 @@ static const char *local__genre_to_string(unsigned genre_code)
  * Returns true on success, else false.
  * If a tag entry exists (ex: title), we allocate memory, else value stays to NULL
  */
-static FLAC__bool local__get_tag(const char *filename, FLAC_Plugin__CanonicalTag *tag)
+static FLAC__bool local__get_tag(const char *filename, FLAC_Plugin__Id3v2_Tag *tag)
 {
        FILE *file;
        ID3Tag *id3_tag = 0; /* Tag defined by id3lib */
@@ -368,8 +371,22 @@ static FLAC__bool local__get_tag(const char *filename, FLAC_Plugin__CanonicalTag
 }
 #endif /* ifdef FLAC__HAS_ID3LIB */
 
-FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__CanonicalTag *tag)
+FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__Id3v2_Tag *tag)
 {
+       FLAC__ASSERT(0 != tag);
+       if(
+               0 != tag->title ||
+               0 != tag->composer ||
+               0 != tag->performer ||
+               0 != tag->album ||
+               0 != tag->year_recorded ||
+               0 != tag->year_performed ||
+               0 != tag->track_number ||
+               0 != tag->tracks_in_album ||
+               0 != tag->genre ||
+               0 != tag->comment
+       )
+               return false;
 #ifdef FLAC__HAS_ID3LIB
        return local__get_tag(filename, tag);
 #else
@@ -377,3 +394,29 @@ FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__Canonic
        return false;
 #endif
 }
+
+void FLAC_plugin__id3v2_tag_clear(FLAC_Plugin__Id3v2_Tag *tag)
+{
+       FLAC__ASSERT(0 != tag);
+       if(0 != tag->title)
+               free(tag->title);
+       if(0 != tag->composer)
+               free(tag->composer);
+       if(0 != tag->performer)
+               free(tag->performer);
+       if(0 != tag->album)
+               free(tag->album);
+       if(0 != tag->year_recorded)
+               free(tag->year_recorded);
+       if(0 != tag->year_performed)
+               free(tag->year_performed);
+       if(0 != tag->track_number)
+               free(tag->track_number);
+       if(0 != tag->tracks_in_album)
+               free(tag->tracks_in_album);
+       if(0 != tag->genre)
+               free(tag->genre);
+       if(0 != tag->comment)
+               free(tag->comment);
+       memset(tag, 0, sizeof(*tag));
+}
index 4d03fef..dbb766c 100644 (file)
 #ifndef FLAC__PLUGIN_COMMON__ID3V2_H
 #define FLAC__PLUGIN_COMMON__ID3V2_H
 
-#include "canonical_tag.h"
+#include "FLAC/ordinals.h"
 
-FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__CanonicalTag *tag);
+/*
+ * This is a simple structure that holds pointers to field values (in ASCII)
+ * for fields we care about.
+ */
+typedef struct {
+       char *title;
+       char *composer;
+       char *performer;
+       char *album;
+       char *year_recorded;
+       char *year_performed;
+       char *track_number;
+       char *tracks_in_album;
+       char *genre;
+       char *comment;
+} FLAC_Plugin__Id3v2_Tag;
+
+/* Fills up an existing FLAC_Plugin__Id3v2_Tag.  All pointers must be NULL on
+ * entry or the function will return false.  For any field for which there is
+ * no corresponding ID3 frame, it's pointer will be NULL.
+ *
+ * If loading fails, all pointers will be cleared and the function will return
+ * false.
+ *
+ * If the function returns true, be sure to call FLAC_plugin__id3v2_tag_clear()
+ * when you are done with 'tag'.
+ */
+FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__Id3v2_Tag *tag);
+
+/* free()s any non-NULL pointers in 'tag'.  Does NOT free(tag).
+ */
+void FLAC_plugin__id3v2_tag_clear(FLAC_Plugin__Id3v2_Tag *tag);
 
 #endif
index 9562a87..cafa6ff 100644 (file)
@@ -41,7 +41,7 @@ RSC=rc.exe
 # PROP Intermediate_Dir "Release_static"\r
 # PROP Target_Dir ""\r
 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c\r
-# ADD CPP /nologo /MD /W3 /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\r
+# 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\r
 # ADD BASE RSC /l 0x409 /d "NDEBUG"\r
 # ADD RSC /l 0x409 /d "NDEBUG"\r
 BSC32=bscmake.exe\r
index 278c2a7..934b4f8 100644 (file)
-/* plugin_common - Routines common to several plugins
- * Copyright (C) 2002,2003,2004  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "vorbiscomment.h"
-#include "FLAC/metadata.h"
-
-static int local__vcentry_matches(const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry *entry)
-{
-#if defined _MSC_VER || defined __MINGW32__
-#define FLAC__STRNCASECMP strnicmp
-#else
-#define FLAC__STRNCASECMP strncasecmp
-#endif
-       const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
-       const unsigned field_name_length = strlen(field_name);
-       return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry->entry, field_name_length));
-}
-
-static void local__vcentry_parse_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, char **dest)
-{
-       const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
-
-       if(0 == eq)
-               return;
-       else {
-               const unsigned value_length = entry->length - (unsigned)((++eq) - entry->entry);
-
-               *dest = malloc(value_length + 1);
-               if(0 != *dest) {
-                       memcpy(*dest, eq, value_length);
-                       (*dest)[value_length] = '\0';
-               }
-       }
-}
-
-static void local__vc_change_field(FLAC__StreamMetadata *block, const char *name, const char *value)
-{
-       int i, l;
-       char *s;
-
-       /* find last */
-       for (l = -1; (i = FLAC__metadata_object_vorbiscomment_find_entry_from(block, l + 1, name)) != -1; l = i)
-               ;
-                       
-       if(!value || !strlen(value)) {
-               if (l != -1)
-                       FLAC__metadata_object_vorbiscomment_delete_comment(block, l);
-               return;
-       }
-
-       s = malloc(strlen(name) + strlen(value) + 2);
-       if(s) {
-               FLAC__StreamMetadata_VorbisComment_Entry entry;
-
-               sprintf(s, "%s=%s", name, value);
-
-               entry.length = strlen(s);
-               entry.entry = (FLAC__byte *)s;
-               
-               if(l == -1)
-                       FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true);
-               else
-                       FLAC__metadata_object_vorbiscomment_set_comment(block, l, entry, /*copy=*/true);
-               free(s);
-       }
-}
-
-void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag)
-{
-       FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
-       if(0 != iterator) {
-               if(FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) {
-                       FLAC__bool got_vorbis_comments = false;
-                       do {
-                               if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
-                                       FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator);
-                                       if(0 != block) {
-                                               unsigned i;
-                                               const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;
-                                               for(i = 0; i < vc->num_comments; i++) {
-                                                       if(local__vcentry_matches("artist", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->composer);
-                                                       else if(local__vcentry_matches("performer", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->performer);
-                                                       else if(local__vcentry_matches("album", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->album);
-                                                       else if(local__vcentry_matches("title", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->title);
-                                                       else if(local__vcentry_matches("tracknumber", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->track_number);
-                                                       else if(local__vcentry_matches("genre", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->genre);
-                                                       else if(local__vcentry_matches("description", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->comment);
-                                                       else if(local__vcentry_matches("date", &vc->comments[i]))
-                                                               local__vcentry_parse_value(&vc->comments[i], &tag->year_recorded);
-                                               }
-                                               FLAC__metadata_object_delete(block);
-                                               got_vorbis_comments = true;
-                                       }
-                               }
-                       } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
-               }
-               FLAC__metadata_simple_iterator_delete(iterator);
-       }
-}
-
-FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag)
-{
-       FLAC__bool got_vorbis_comments = false;
-       FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
-       FLAC__StreamMetadata *block;
-               
-       if(!iterator || !FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/false, /*preserve_file_stats=*/true))
-               return false;
-
-       do {
-               if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT)
-                       got_vorbis_comments = true;
-       } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
-
-       if(!got_vorbis_comments) {
-               /* create a new block */
-               block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
-
-               if(!block) {
-                       FLAC__metadata_simple_iterator_delete(iterator);
-                       return false;
-               }
-       }
-       else
-               block = FLAC__metadata_simple_iterator_get_block(iterator);
-
-       local__vc_change_field(block, "ARTIST", tag->composer);
-       local__vc_change_field(block, "PERFORMER", tag->performer);
-       local__vc_change_field(block, "ALBUM", tag->album);
-       local__vc_change_field(block, "TITLE", tag->title);
-       local__vc_change_field(block, "TRACKNUMBER", tag->track_number);
-       local__vc_change_field(block, "GENRE", tag->genre);
-       local__vc_change_field(block, "DESCRIPTION", tag->comment);
-       local__vc_change_field(block, "DATE", tag->year_recorded);
-
-       if(!got_vorbis_comments) {
-               if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, block, /*use_padding=*/true)) {
-                       FLAC__metadata_object_delete(block);
-                       FLAC__metadata_simple_iterator_delete(iterator);
-                       return false;
-               }
-       }
-       else {
-               if(!FLAC__metadata_simple_iterator_set_block(iterator, block, /*use_padding=*/true)) {
-                       FLAC__metadata_object_delete(block);
-                       FLAC__metadata_simple_iterator_delete(iterator);
-                       return false;
-               }
-       }
-
-       FLAC__metadata_object_delete(block);
-       FLAC__metadata_simple_iterator_delete(iterator);
-       return true;
-}
+/* plugin_common - Routines common to several plugins\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+\r
+#include "vorbiscomment.h"\r
+#include "FLAC/metadata.h"\r
+\r
+\r
+static void local__add_vcentry(FLAC_Plugin__CanonicalTag *tag, FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *sep)\r
+{\r
+       FLAC__byte *value = memchr(entry->entry, '=', entry->length);\r
+       int len;\r
+       if (!value) return;\r
+       len = value - entry->entry;\r
+       value++;\r
+       FLAC_plugin__canonical_add_utf8(tag, entry->entry, value, len, entry->length-len-1, sep);\r
+}\r
+\r
+void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep)\r
+{\r
+       FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();\r
+       if(0 != iterator) {\r
+               if(FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) {\r
+                       FLAC__bool got_vorbis_comments = false;\r
+                       do {\r
+                               if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) {\r
+                                       FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator);\r
+                                       if(0 != block) {\r
+                                               unsigned i;\r
+                                               const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;\r
+\r
+                                               for(i = 0; i < vc->num_comments; i++)\r
+                                                       local__add_vcentry(tag, vc->comments+i, sep);\r
+\r
+                                               FLAC__metadata_object_delete(block);\r
+                                               got_vorbis_comments = true;\r
+                                       }\r
+                               }\r
+                       } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));\r
+               }\r
+               FLAC__metadata_simple_iterator_delete(iterator);\r
+       }\r
+}\r
+\r
+FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
+{\r
+       FLAC__bool got_vorbis_comments = false, result;\r
+       FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();\r
+       FLAC__StreamMetadata *block;\r
+       FLAC__tag_iterator it;\r
+       unsigned position = 0;\r
+               \r
+       if (!iterator || !FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/false, /*preserve_file_stats=*/true))\r
+               return false;\r
+\r
+       do {\r
+               if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT)\r
+                       got_vorbis_comments = true;\r
+       } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));\r
+\r
+       if(!got_vorbis_comments) {\r
+               /* create a new block */\r
+               block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);\r
+\r
+               if(!block) {\r
+                       FLAC__metadata_simple_iterator_delete(iterator);\r
+                       return false;\r
+               }\r
+       }\r
+       else\r
+               block = FLAC__metadata_simple_iterator_get_block(iterator);\r
+\r
+       FLAC__metadata_object_vorbiscomment_resize_comments(block, FLAC_plugin__canonical_get_count(tag));\r
+\r
+       for (it=FLAC_plugin__canonical_first(tag); it; it=FLAC_plugin__canonical_next(it))\r
+       {\r
+               FLAC__StreamMetadata_VorbisComment_Entry entry;\r
+               /* replace entry */\r
+               entry.entry = FLAC_plugin__canonical_get_formatted(it);\r
+               entry.length = strlen(entry.entry);\r
+               FLAC__metadata_object_vorbiscomment_set_comment(block, position++, entry, /*copy=*/false);\r
+       }\r
+\r
+       if (!got_vorbis_comments)\r
+               result = FLAC__metadata_simple_iterator_insert_block_after(iterator, block, /*use_padding=*/true);\r
+       else\r
+               result = FLAC__metadata_simple_iterator_set_block(iterator, block, /*use_padding=*/true);\r
+\r
+       FLAC__metadata_object_delete(block);\r
+       FLAC__metadata_simple_iterator_delete(iterator);\r
+       return result;\r
+}\r
index 84c6447..f02b5ca 100644 (file)
@@ -22,7 +22,7 @@
 #include "canonical_tag.h"
 #include "FLAC/ordinals.h"
 
-void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag);
+void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep);
 FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag);
 
 #endif
index ff894e0..c9b0362 100644 (file)
@@ -1,3 +1,21 @@
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
 #include <windows.h>\r
 #include <commctrl.h>\r
 #include <stdio.h>\r
 \r
 \r
 static char buffer[256];\r
+static char ini_name[MAX_PATH];\r
 \r
-//\r
-//  Read/write config\r
-//\r
+/*\r
+ *  Read/write config\r
+ */\r
 \r
 #define RI(x, def)          (x = GetPrivateProfileInt("FLAC", #x, def, ini_name))\r
 #define WI(x)               WritePrivateProfileString("FLAC", #x, itoa(x, buffer, 10), ini_name)\r
@@ -18,38 +37,65 @@ static char buffer[256];
 #define WS(x)               WritePrivateProfileString("FLAC", #x, x, ini_name)\r
 \r
 static const char default_format[] = "[%artist% - ]$if2(%title%,%filename%)";\r
+static const char default_sep[] = ", ";\r
+\r
+void InitConfig()\r
+{\r
+       char *p;\r
+\r
+       GetModuleFileName(NULL, ini_name, sizeof(ini_name));\r
+       p = strrchr(ini_name, '.');\r
+       if (!p) p = ini_name + strlen(ini_name);\r
+       strcpy(p, ".ini");\r
+\r
+       flac_cfg.title.tag_format_w = NULL;\r
+}\r
 \r
 void ReadConfig()\r
 {\r
-    RS(flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format), default_format);\r
-\r
-    RI(flac_cfg.output.replaygain.enable, 1);\r
-    RI(flac_cfg.output.replaygain.album_mode, 0);\r
-    RI(flac_cfg.output.replaygain.hard_limit, 0);\r
-    RI(flac_cfg.output.replaygain.preamp, 0);\r
-    RI(flac_cfg.output.resolution.normal.dither_24_to_16, 0);\r
-    RI(flac_cfg.output.resolution.replaygain.dither, 0);\r
-    RI(flac_cfg.output.resolution.replaygain.noise_shaping, 1);\r
-    RI(flac_cfg.output.resolution.replaygain.bps_out, 16);\r
+       RS(flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format), default_format);\r
+       if (flac_cfg.title.tag_format_w)\r
+               free(flac_cfg.title.tag_format_w);\r
+       flac_cfg.title.tag_format_w = FLAC_plugin__convert_ansi_to_wide(flac_cfg.title.tag_format);\r
+       /* @@@ FIXME: trailing spaces */\r
+       RS(flac_cfg.title.sep, sizeof(flac_cfg.title.sep), default_sep);\r
+       RI(flac_cfg.title.read_v1, 1);\r
+       RI(flac_cfg.tag.reserve_space, 1);\r
+\r
+       RI(flac_cfg.display.show_bps, 1);\r
+       RI(flac_cfg.output.misk.stop_err, 0);\r
+       RI(flac_cfg.output.replaygain.enable, 1);\r
+       RI(flac_cfg.output.replaygain.album_mode, 0);\r
+       RI(flac_cfg.output.replaygain.hard_limit, 0);\r
+       RI(flac_cfg.output.replaygain.preamp, 0);\r
+       RI(flac_cfg.output.resolution.normal.dither_24_to_16, 0);\r
+       RI(flac_cfg.output.resolution.replaygain.dither, 0);\r
+       RI(flac_cfg.output.resolution.replaygain.noise_shaping, 1);\r
+       RI(flac_cfg.output.resolution.replaygain.bps_out, 16);\r
 }\r
 \r
 void WriteConfig()\r
 {\r
-    WS(flac_cfg.title.tag_format);\r
-\r
-    WI(flac_cfg.output.replaygain.enable);\r
-    WI(flac_cfg.output.replaygain.album_mode);\r
-    WI(flac_cfg.output.replaygain.hard_limit);\r
-    WI(flac_cfg.output.replaygain.preamp);\r
-    WI(flac_cfg.output.resolution.normal.dither_24_to_16);\r
-    WI(flac_cfg.output.resolution.replaygain.dither);\r
-    WI(flac_cfg.output.resolution.replaygain.noise_shaping);\r
-    WI(flac_cfg.output.resolution.replaygain.bps_out);\r
+       WS(flac_cfg.title.tag_format);\r
+       WI(flac_cfg.title.read_v1);\r
+       WI(flac_cfg.tag.reserve_space);\r
+       WS(flac_cfg.title.sep);\r
+\r
+       WI(flac_cfg.display.show_bps);\r
+       WI(flac_cfg.output.misk.stop_err);\r
+       WI(flac_cfg.output.replaygain.enable);\r
+       WI(flac_cfg.output.replaygain.album_mode);\r
+       WI(flac_cfg.output.replaygain.hard_limit);\r
+       WI(flac_cfg.output.replaygain.preamp);\r
+       WI(flac_cfg.output.resolution.normal.dither_24_to_16);\r
+       WI(flac_cfg.output.resolution.replaygain.dither);\r
+       WI(flac_cfg.output.resolution.replaygain.noise_shaping);\r
+       WI(flac_cfg.output.resolution.replaygain.bps_out);\r
 }\r
 \r
-//\r
-//  Dialog\r
-//\r
+/*\r
+ *  Dialog\r
+ */\r
 \r
 #define PREAMP_RANGE            24\r
 \r
@@ -59,284 +105,308 @@ void WriteConfig()
 #define GetPos(x)               SendDlgItemMessage(hwnd, x, TBM_GETPOS, 0, 0)\r
 #define Enable(x,y)             EnableWindow(GetDlgItem(hwnd, x), y)\r
 \r
-\r
 static INT_PTR CALLBACK GeneralProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
 {\r
-    switch (msg)\r
-    {\r
-    // init\r
-    case WM_INITDIALOG:\r
-        SetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format);\r
-        return TRUE;\r
-    // commands\r
-    case WM_COMMAND:\r
-        switch (LOWORD(wParam))\r
-        {\r
-        // ok\r
-        case IDOK:\r
-            GetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format));\r
-            break;\r
-        // reset\r
-        case IDC_RESET:\r
-            SetDlgItemText(hwnd, IDC_TITLE, default_format);\r
-            break;\r
-        // help\r
-        case IDC_TAGZ_HELP:\r
-            MessageBox(hwnd, tagz_manual, "Help", 0);\r
-            break;\r
-        }\r
-        break;\r
-    }\r
-\r
-    return 0;\r
+       switch (msg)\r
+       {\r
+       /* init */\r
+       case WM_INITDIALOG:\r
+               SendDlgItemMessage(hwnd, IDC_TITLE, EM_LIMITTEXT, 255, 0);\r
+               SendDlgItemMessage(hwnd, IDC_SEP, EM_LIMITTEXT, 15, 0);\r
+\r
+               SetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format);\r
+               SetDlgItemText(hwnd, IDC_SEP, flac_cfg.title.sep);\r
+               Check(IDC_ID3V1, flac_cfg.title.read_v1);\r
+/*!            Check(IDC_RESERVE, flac_cfg.tag.reserve_space); */\r
+               Check(IDC_BPS, flac_cfg.display.show_bps);\r
+               Check(IDC_ERRORS, flac_cfg.output.misk.stop_err);\r
+               return TRUE;\r
+       /* commands */\r
+       case WM_COMMAND:\r
+               switch (LOWORD(wParam))\r
+               {\r
+               /* ok */\r
+               case IDOK:\r
+                       GetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format));\r
+                       if (flac_cfg.title.tag_format_w)\r
+                               free(flac_cfg.title.tag_format_w);\r
+                       GetDlgItemText(hwnd, IDC_SEP, flac_cfg.title.sep, sizeof(flac_cfg.title.sep));\r
+                       flac_cfg.title.tag_format_w = FLAC_plugin__convert_ansi_to_wide(flac_cfg.title.tag_format);\r
+\r
+                       flac_cfg.title.read_v1 = GetCheck(IDC_ID3V1);\r
+/*!                    flac_cfg.tag.reserve_space = GetCheck(IDC_RESERVE); */\r
+                       flac_cfg.display.show_bps = GetCheck(IDC_BPS);\r
+                       flac_cfg.output.misk.stop_err = GetCheck(IDC_ERRORS);\r
+                       break;\r
+               /* reset */\r
+               case IDC_RESET:\r
+                       Check(IDC_ID3V1, 1);\r
+                       Check(IDC_RESERVE, 1);\r
+                       Check(IDC_BPS, 1);\r
+                       Check(IDC_ERRORS, 0);\r
+                       /* fall throught */\r
+               /* default */\r
+               case IDC_TAGZ_DEFAULT:\r
+                       SetDlgItemText(hwnd, IDC_TITLE, default_format);\r
+                       break;\r
+               /* help */\r
+               case IDC_TAGZ_HELP:\r
+                       MessageBox(hwnd, tagz_manual, "Help", 0);\r
+                       break;\r
+               }\r
+               break;\r
+       }\r
+\r
+       return 0;\r
 }\r
 \r
 \r
 static void UpdatePreamp(HWND hwnd, HWND hamp)\r
 {\r
-    int pos = SendMessage(hamp, TBM_GETPOS, 0, 0) - PREAMP_RANGE;\r
-    sprintf(buffer, "%d dB", pos);\r
-    SetDlgItemText(hwnd, IDC_PA, buffer);\r
+       int pos = SendMessage(hamp, TBM_GETPOS, 0, 0) - PREAMP_RANGE;\r
+       sprintf(buffer, "%d dB", pos);\r
+       SetDlgItemText(hwnd, IDC_PA, buffer);\r
 }\r
 \r
 static void UpdateRG(HWND hwnd)\r
 {\r
-    int on = GetCheck(IDC_ENABLE);\r
-    Enable(IDC_ALBUM, on);\r
-    Enable(IDC_LIMITER, on);\r
-    Enable(IDC_PREAMP, on);\r
-    Enable(IDC_PA, on);\r
+       int on = GetCheck(IDC_ENABLE);\r
+       Enable(IDC_ALBUM, on);\r
+       Enable(IDC_LIMITER, on);\r
+       Enable(IDC_PREAMP, on);\r
+       Enable(IDC_PA, on);\r
 }\r
 \r
 static void UpdateDither(HWND hwnd)\r
 {\r
-    int on = GetCheck(IDC_DITHERRG);\r
-    Enable(IDC_SHAPE, on);\r
+       int on = GetCheck(IDC_DITHERRG);\r
+       Enable(IDC_SHAPE, on);\r
 }\r
 \r
 static INT_PTR CALLBACK OutputProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
 {\r
-    switch (msg)\r
-    {\r
-    // init\r
-    case WM_INITDIALOG:\r
-        Check(IDC_ENABLE, flac_cfg.output.replaygain.enable);\r
-        Check(IDC_ALBUM, flac_cfg.output.replaygain.album_mode);\r
-        Check(IDC_LIMITER, flac_cfg.output.replaygain.hard_limit);\r
-        Check(IDC_DITHER, flac_cfg.output.resolution.normal.dither_24_to_16);\r
-        Check(IDC_DITHERRG, flac_cfg.output.resolution.replaygain.dither);\r
-        // prepare preamp slider\r
-        {\r
-            HWND hamp = GetDlgItem(hwnd, IDC_PREAMP);\r
-            SendMessage(hamp, TBM_SETRANGE, 1, MAKELONG(0, PREAMP_RANGE*2));\r
-            SendMessage(hamp, TBM_SETPOS, 1, flac_cfg.output.replaygain.preamp+PREAMP_RANGE);\r
-            UpdatePreamp(hwnd, hamp);\r
-        }\r
-        // fill comboboxes\r
-        {\r
-            HWND hlist = GetDlgItem(hwnd, IDC_TO);\r
-            SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"16 bps");\r
-            SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"24 bps");\r
-            SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.bps_out/8 - 2, 0);\r
-  \r
-            hlist = GetDlgItem(hwnd, IDC_SHAPE);\r
-            SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"None");\r
-            SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Low");\r
-            SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Medium");\r
-            SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"High");\r
-            SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.noise_shaping, 0);\r
-        }\r
-        UpdateRG(hwnd);\r
-        UpdateDither(hwnd);\r
-        return TRUE;\r
-    // commands\r
-    case WM_COMMAND:\r
-        switch (LOWORD(wParam))\r
-        {\r
-        // ok\r
-        case IDOK:\r
-            flac_cfg.output.replaygain.enable = GetCheck(IDC_ENABLE);\r
-            flac_cfg.output.replaygain.album_mode = GetCheck(IDC_ALBUM);\r
-            flac_cfg.output.replaygain.hard_limit = GetCheck(IDC_LIMITER);\r
-            flac_cfg.output.replaygain.preamp = GetPos(IDC_PREAMP) - PREAMP_RANGE;\r
-            flac_cfg.output.resolution.normal.dither_24_to_16 = GetCheck(IDC_DITHER);\r
-            flac_cfg.output.resolution.replaygain.dither = GetCheck(IDC_DITHERRG);\r
-            flac_cfg.output.resolution.replaygain.noise_shaping = GetSel(IDC_SHAPE);\r
-            flac_cfg.output.resolution.replaygain.bps_out = (GetSel(IDC_TO)+2)*8;\r
-            break;\r
-        // reset\r
-        case IDC_RESET:\r
-            Check(IDC_ENABLE, 1);\r
-            Check(IDC_ALBUM, 0);\r
-            Check(IDC_LIMITER, 0);\r
-            Check(IDC_DITHER, 0);\r
-            Check(IDC_DITHERRG, 0);\r
-\r
-            SendDlgItemMessage(hwnd, IDC_PREAMP, TBM_SETPOS, 1, PREAMP_RANGE);\r
-            SendDlgItemMessage(hwnd, IDC_TO, CB_SETCURSEL, 0, 0);\r
-            SendDlgItemMessage(hwnd, IDC_SHAPE, CB_SETCURSEL, 1, 0);\r
-\r
-            UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP));\r
-            UpdateRG(hwnd);\r
-            UpdateDither(hwnd);\r
-            break;\r
-        // active check-boxes\r
-        case IDC_ENABLE:\r
-            UpdateRG(hwnd);\r
-            break;\r
-        case IDC_DITHERRG:\r
-            UpdateDither(hwnd);\r
-            break;\r
-        }\r
-        break;\r
-    // scroller\r
-    case WM_HSCROLL:\r
-        if (GetDlgCtrlID((HWND)lParam)==IDC_PREAMP)\r
-            UpdatePreamp(hwnd, (HWND)lParam);\r
-        return 0;\r
-    }\r
-\r
-    return 0;\r
+       switch (msg)\r
+       {\r
+       /* init */\r
+       case WM_INITDIALOG:\r
+               Check(IDC_ENABLE, flac_cfg.output.replaygain.enable);\r
+               Check(IDC_ALBUM, flac_cfg.output.replaygain.album_mode);\r
+               Check(IDC_LIMITER, flac_cfg.output.replaygain.hard_limit);\r
+               Check(IDC_DITHER, flac_cfg.output.resolution.normal.dither_24_to_16);\r
+               Check(IDC_DITHERRG, flac_cfg.output.resolution.replaygain.dither);\r
+               /* prepare preamp slider */\r
+               {\r
+                       HWND hamp = GetDlgItem(hwnd, IDC_PREAMP);\r
+                       SendMessage(hamp, TBM_SETRANGE, 1, MAKELONG(0, PREAMP_RANGE*2));\r
+                       SendMessage(hamp, TBM_SETPOS, 1, flac_cfg.output.replaygain.preamp+PREAMP_RANGE);\r
+                       UpdatePreamp(hwnd, hamp);\r
+               }\r
+               /* fill comboboxes */\r
+               {\r
+                       HWND hlist = GetDlgItem(hwnd, IDC_TO);\r
+                       SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"16 bps");\r
+                       SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"24 bps");\r
+                       SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.bps_out/8 - 2, 0);\r
+\r
+                       hlist = GetDlgItem(hwnd, IDC_SHAPE);\r
+                       SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"None");\r
+                       SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Low");\r
+                       SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Medium");\r
+                       SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"High");\r
+                       SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.noise_shaping, 0);\r
+               }\r
+               UpdateRG(hwnd);\r
+               UpdateDither(hwnd);\r
+               return TRUE;\r
+       /* commands */\r
+       case WM_COMMAND:\r
+               switch (LOWORD(wParam))\r
+               {\r
+               /* ok */\r
+               case IDOK:\r
+                       flac_cfg.output.replaygain.enable = GetCheck(IDC_ENABLE);\r
+                       flac_cfg.output.replaygain.album_mode = GetCheck(IDC_ALBUM);\r
+                       flac_cfg.output.replaygain.hard_limit = GetCheck(IDC_LIMITER);\r
+                       flac_cfg.output.replaygain.preamp = GetPos(IDC_PREAMP) - PREAMP_RANGE;\r
+                       flac_cfg.output.resolution.normal.dither_24_to_16 = GetCheck(IDC_DITHER);\r
+                       flac_cfg.output.resolution.replaygain.dither = GetCheck(IDC_DITHERRG);\r
+                       flac_cfg.output.resolution.replaygain.noise_shaping = GetSel(IDC_SHAPE);\r
+                       flac_cfg.output.resolution.replaygain.bps_out = (GetSel(IDC_TO)+2)*8;\r
+                       break;\r
+               /* reset */\r
+               case IDC_RESET:\r
+                       Check(IDC_ENABLE, 1);\r
+                       Check(IDC_ALBUM, 0);\r
+                       Check(IDC_LIMITER, 0);\r
+                       Check(IDC_DITHER, 0);\r
+                       Check(IDC_DITHERRG, 0);\r
+\r
+                       SendDlgItemMessage(hwnd, IDC_PREAMP, TBM_SETPOS, 1, PREAMP_RANGE);\r
+                       SendDlgItemMessage(hwnd, IDC_TO, CB_SETCURSEL, 0, 0);\r
+                       SendDlgItemMessage(hwnd, IDC_SHAPE, CB_SETCURSEL, 1, 0);\r
+\r
+                       UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP));\r
+                       UpdateRG(hwnd);\r
+                       UpdateDither(hwnd);\r
+                       break;\r
+               /* active check-boxes */\r
+               case IDC_ENABLE:\r
+                       UpdateRG(hwnd);\r
+                       break;\r
+               case IDC_DITHERRG:\r
+                       UpdateDither(hwnd);\r
+                       break;\r
+               }\r
+               break;\r
+       /* scroller */\r
+       case WM_HSCROLL:\r
+               if (GetDlgCtrlID((HWND)lParam)==IDC_PREAMP)\r
+                       UpdatePreamp(hwnd, (HWND)lParam);\r
+               return 0;\r
+       }\r
+\r
+       return 0;\r
 }\r
 \r
 #define NUM_PAGES       2\r
 \r
 typedef struct\r
 {\r
-    HWND htab;\r
-    HWND hdlg;\r
-    RECT r;\r
-    HWND all[NUM_PAGES];\r
+       HWND htab;\r
+       HWND hdlg;\r
+       RECT r;\r
+       HWND all[NUM_PAGES];\r
 } LOCALDATA;\r
 \r
 static void ScreenToClientRect(HWND hwnd, RECT *rect)\r
 {\r
-    POINT pt = { rect->left, rect->top };\r
-    ScreenToClient(hwnd, &pt);\r
-    rect->left = pt.x;\r
-    rect->top  = pt.y;\r
-\r
-    pt.x = rect->right;\r
-    pt.y = rect->bottom;\r
-    ScreenToClient(hwnd, &pt);\r
-    rect->right  = pt.x;\r
-    rect->bottom = pt.y;\r
+       POINT pt = { rect->left, rect->top };\r
+       ScreenToClient(hwnd, &pt);\r
+       rect->left = pt.x;\r
+       rect->top  = pt.y;\r
+\r
+       pt.x = rect->right;\r
+       pt.y = rect->bottom;\r
+       ScreenToClient(hwnd, &pt);\r
+       rect->right  = pt.x;\r
+       rect->bottom = pt.y;\r
 }\r
 \r
 static void SendCommand(HWND hwnd, int command)\r
 {\r
-    LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
-    SendMessage(data->hdlg, WM_COMMAND, command, 0);\r
+       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+       SendMessage(data->hdlg, WM_COMMAND, command, 0);\r
 }\r
 \r
 static void BroadcastCommand(HWND hwnd, int command)\r
 {\r
-    LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
-    int i;\r
+       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+       int i;\r
 \r
-    for (i=0; i<NUM_PAGES; i++)\r
-        SendMessage(data->all[i], WM_COMMAND, command, 0);\r
+       for (i=0; i<NUM_PAGES; i++)\r
+               SendMessage(data->all[i], WM_COMMAND, command, 0);\r
 }\r
 \r
 static void OnSelChange(HWND hwnd)\r
 {\r
-    LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
-    int index = TabCtrl_GetCurSel(data->htab);\r
-    if (index < 0) return;\r
-    // hide previous\r
-    if (data->hdlg)\r
-        ShowWindow(data->hdlg, SW_HIDE);\r
-    // display\r
-    data->hdlg = data->all[index];\r
-    SetWindowPos(data->hdlg, HWND_TOP, data->r.left, data->r.top, data->r.right-data->r.left, data->r.bottom-data->r.top, SWP_SHOWWINDOW);\r
+       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+       int index = TabCtrl_GetCurSel(data->htab);\r
+       if (index < 0) return;\r
+       /* hide previous */\r
+       if (data->hdlg)\r
+               ShowWindow(data->hdlg, SW_HIDE);\r
+       /* display */\r
+       data->hdlg = data->all[index];\r
+       SetWindowPos(data->hdlg, HWND_TOP, data->r.left, data->r.top, data->r.right-data->r.left, data->r.bottom-data->r.top, SWP_SHOWWINDOW);\r
+       SetFocus(hwnd);\r
 }\r
 \r
 static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
 {\r
-    static activePage = 0;\r
-\r
-    switch (msg)\r
-    {\r
-    // init\r
-    case WM_INITDIALOG:\r
-        {\r
-            LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
-            HINSTANCE inst = (HINSTANCE)lParam;\r
-            TCITEM item;\r
-\r
-            // init\r
-            SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
-            data->htab = GetDlgItem(hwnd, IDC_TABS);\r
-            data->hdlg = NULL;\r
-            // add pages\r
-            item.mask = TCIF_TEXT;\r
-            data->all[0] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_GENERAL), hwnd, GeneralProc);\r
-            item.pszText = "General";\r
-            TabCtrl_InsertItem(data->htab, 0, &item);\r
-\r
-            data->all[1] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_OUTPUT), hwnd, OutputProc);\r
-            item.pszText = "Output";\r
-            TabCtrl_InsertItem(data->htab, 1, &item);\r
-            // get rect (after adding pages)\r
-            GetWindowRect(data->htab, &data->r);\r
-            ScreenToClientRect(hwnd, &data->r);\r
-            TabCtrl_AdjustRect(data->htab, 0, &data->r);\r
-            // simulate item change\r
-            TabCtrl_SetCurSel(data->htab, activePage);\r
-            OnSelChange(hwnd);\r
-        }\r
-        return TRUE;\r
-    // destory\r
-    case WM_DESTROY:\r
-        {\r
-            LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
-            int i;\r
-\r
-            activePage = TabCtrl_GetCurSel(data->htab);\r
-\r
-            for (i=0; i<NUM_PAGES; i++)\r
-                DestroyWindow(data->all[i]);\r
-\r
-            LocalFree(data);\r
-        }\r
-        break;\r
-    // commands\r
-    case WM_COMMAND:\r
-        switch (LOWORD(wParam))\r
-        {\r
-        // ok/cancel\r
-        case IDOK:\r
-            BroadcastCommand(hwnd, IDOK);\r
-            /* fall through */\r
-        case IDCANCEL:\r
-            EndDialog(hwnd, LOWORD(wParam));\r
-            return TRUE;\r
-        case IDC_RESET:\r
-            SendCommand(hwnd, IDC_RESET);\r
-            break;\r
-        }\r
-        break;\r
-    // notification\r
-    case WM_NOTIFY:\r
-        if (LOWORD(wParam) == IDC_TABS)\r
-        {\r
-            NMHDR *hdr = (NMHDR*)lParam;\r
-\r
-            switch (hdr->code)\r
-            {\r
-            case TCN_SELCHANGE:\r
-                OnSelChange(hwnd);\r
-                break;\r
-            }\r
-        }\r
-        break;\r
-    }\r
-\r
-    return 0;\r
+       static activePage = 0;\r
+\r
+       switch (msg)\r
+       {\r
+       /* init */\r
+       case WM_INITDIALOG:\r
+               {\r
+                       LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
+                       HINSTANCE inst = (HINSTANCE)lParam;\r
+                       TCITEM item;\r
+\r
+                       /* init */\r
+                       SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
+                       data->htab = GetDlgItem(hwnd, IDC_TABS);\r
+                       data->hdlg = NULL;\r
+                       /* add pages */\r
+                       item.mask = TCIF_TEXT;\r
+                       data->all[0] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_GENERAL), hwnd, GeneralProc);\r
+                       item.pszText = "General";\r
+                       TabCtrl_InsertItem(data->htab, 0, &item);\r
+\r
+                       data->all[1] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_OUTPUT), hwnd, OutputProc);\r
+                       item.pszText = "Output";\r
+                       TabCtrl_InsertItem(data->htab, 1, &item);\r
+                       /* get rect (after adding pages) */\r
+                       GetWindowRect(data->htab, &data->r);\r
+                       ScreenToClientRect(hwnd, &data->r);\r
+                       TabCtrl_AdjustRect(data->htab, 0, &data->r);\r
+                       /* simulate item change */\r
+                       TabCtrl_SetCurSel(data->htab, activePage);\r
+                       OnSelChange(hwnd);\r
+               }\r
+               return TRUE;\r
+       /* destory */\r
+       case WM_DESTROY:\r
+               {\r
+                       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+                       int i;\r
+\r
+                       activePage = TabCtrl_GetCurSel(data->htab);\r
+\r
+                       for (i=0; i<NUM_PAGES; i++)\r
+                               DestroyWindow(data->all[i]);\r
+\r
+                       LocalFree(data);\r
+               }\r
+               break;\r
+       /* commands */\r
+       case WM_COMMAND:\r
+               switch (LOWORD(wParam))\r
+               {\r
+               /* ok/cancel */\r
+               case IDOK:\r
+                       BroadcastCommand(hwnd, IDOK);\r
+                       /* fall through */\r
+               case IDCANCEL:\r
+                       EndDialog(hwnd, LOWORD(wParam));\r
+                       return TRUE;\r
+               case IDC_RESET:\r
+                       SendCommand(hwnd, IDC_RESET);\r
+                       break;\r
+               }\r
+               break;\r
+       /* notification */\r
+       case WM_NOTIFY:\r
+               if (LOWORD(wParam) == IDC_TABS)\r
+               {\r
+                       NMHDR *hdr = (NMHDR*)lParam;\r
+\r
+                       switch (hdr->code)\r
+                       {\r
+                       case TCN_SELCHANGE:\r
+                               OnSelChange(hwnd);\r
+                               break;\r
+                       }\r
+               }\r
+               break;\r
+       }\r
+\r
+       return 0;\r
 }\r
 \r
 \r
 int DoConfig(HINSTANCE inst, HWND parent)\r
 {\r
-    return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc, (LONG)inst) == IDOK;\r
+       return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc, (LONG)inst) == IDOK;\r
 }\r
index 60e5efa..11ec20b 100644 (file)
@@ -1,41 +1,50 @@
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
 \r
-//\r
-//  common stuff\r
-//\r
+#include "playback.h"\r
 \r
-typedef struct {\r
-       struct {\r
-               BOOL enable;\r
-               BOOL album_mode;\r
-               INT  preamp;\r
-               BOOL hard_limit;\r
-       } replaygain;\r
-       struct {\r
-               struct {\r
-                       BOOL dither_24_to_16;\r
-               } normal;\r
-               struct {\r
-                       BOOL dither;\r
-                       INT  noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */\r
-                       INT  bps_out;\r
-               } replaygain;\r
-       } resolution;\r
-} output_config_t;\r
+/*\r
+ *  common stuff\r
+ */\r
 \r
 typedef struct {\r
        struct {\r
                char tag_format[256];\r
+               char sep[16];\r
+               WCHAR *tag_format_w;\r
+               BOOL read_v1;\r
        } title;\r
+       struct {\r
+               BOOL reserve_space;\r
+       } tag;\r
+       struct {\r
+               FLAC__bool show_bps;\r
+       } display;\r
        output_config_t output;\r
 } flac_config_t;\r
 \r
 extern flac_config_t flac_cfg;\r
-extern char ini_name[MAX_PATH];\r
 \r
-//\r
-//  prototypes\r
-//\r
+/*\r
+ *  prototypes\r
+ */\r
 \r
+void InitConfig();\r
 void ReadConfig();\r
 void WriteConfig();\r
-int DoConfig(HINSTANCE inst, HWND parent);\r
+int  DoConfig(HINSTANCE inst, HWND parent);\r
index 4458ce5..3ba942a 100644 (file)
-/* in_flac - Winamp2 FLAC input plugin
- * Copyright (C) 2000,2001,2002,2003,2004  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <windows.h>
-#include <mmreg.h>
-#include <msacm.h>
-#include <math.h>
-#include <stdio.h>
-
-#include "winamp2/in2.h"
-#include "FLAC/all.h"
-#include "plugin_common/all.h"
-#include "share/grabbag.h"
-#include "share/replaygain_synthesis.h"
-#include "config.h"
-#include "infobox.h"
-#include "tagz.h"
-
-
-typedef struct {
-       FLAC__bool abort_flag;
-       int seek_to;
-       int paused;
-       unsigned total_samples;
-       unsigned bits_per_sample;
-       unsigned output_bits_per_sample;
-       unsigned channels;
-       unsigned sample_rate;
-       unsigned length_in_msec;
-       FLAC__bool has_replaygain;
-       double replay_scale;
-       DitherContext dither_context;
-} file_info_struct;
-
-
-static FLAC__bool safe_decoder_init_(const char *infilename, FLAC__FileDecoder *decoder);
-static void safe_decoder_finish_(FLAC__FileDecoder *decoder);
-static void safe_decoder_delete_(FLAC__FileDecoder *decoder);
-static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
-static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
-static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
-static void get_description_(const char *filename, char *description, unsigned max_size);
-
-char ini_name[MAX_PATH];
-flac_config_t flac_cfg;
-static output_config_t cfg; /* local copy */
-
-static In_Module mod_; /* the input module (declared near the bottom of this file) */
-static char lastfn_[MAX_PATH]; /* currently playing file (used for getting info on the current file) */
-static int decode_pos_ms_; /* current decoding position, in milliseconds */
-
-#define SAMPLES_PER_WRITE 576
-static FLAC__int32 reservoir_[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS][FLAC__MAX_BLOCK_SIZE * 2/*for overflow*/];
-static FLAC__int32 *reservoir__[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS] = { reservoir_[0], reservoir_[1] }; /*@@@ kind of a hard-coded hack */
-static char sample_buffer_[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8) * 2]; /* (24/8) for max bytes per sample, and 2 for who knows what */
-static unsigned wide_samples_in_reservoir_;
-static file_info_struct file_info_;
-static FLAC__FileDecoder *decoder_;
-
-static volatile int killDecodeThread = 0; /* the kill switch for the decode thread */
-static HANDLE thread_handle = NULL;       /* the handle to the decode thread */
-
-static DWORD WINAPI DecodeThread(void *b); /* the decode thread procedure */
-
-
-static void show_error(const char *message,...)
-{
-       char foo[512];
-       va_list args;
-       va_start(args, message);
-       vsprintf(foo, message, args);
-       va_end(args);
-       MessageBox(mod_.hMainWindow, foo, "FLAC Plug-in Error", MB_ICONSTOP);
-}
-
-void config(HWND hwndParent)
-{
-       if (DoConfig(mod_.hDllInstance, hwndParent))
-               WriteConfig();
-}
-
-void about(HWND hwndParent)
-{
-       MessageBox(hwndParent, "Winamp FLAC Plugin v" VERSION ", by Josh Coalson\nSee http://flac.sourceforge.net/", "About FLAC Plugin", MB_OK);
-}
-
-void init()
-{
-       char *p;
-
-       decoder_ = FLAC__file_decoder_new();
-       strcpy(lastfn_, "");
-       /* read config */
-       GetModuleFileName(NULL, ini_name, sizeof(ini_name));
-       p = strrchr(ini_name, '.');
-       if (!p) p = ini_name + strlen(ini_name);
-       strcpy(p, ".ini");
-
-       ReadConfig();
-}
-
-void quit()
-{
-       WriteConfig();
-       safe_decoder_delete_(decoder_);
-       decoder_ = 0;
-}
-
-int isourfile(char *fn) { return 0; }
-
-
-int play(char *fn)
-{
-       int maxlatency;
-       int thread_id;
-       HANDLE input_file;
-       DWORD  file_size;       /*@@@ fixme 64-bit */
-
-       if (decoder_ == 0)
-               return 1;
-
-       input_file = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-       if(input_file == INVALID_HANDLE_VALUE)
-               return -1;
-       file_size = GetFileSize(input_file, NULL);
-       CloseHandle(input_file);
-
-       file_info_.abort_flag = false;
-       file_info_.has_replaygain = false;
-       if (!safe_decoder_init_(fn, decoder_))
-               return 1;
-
-       cfg = flac_cfg.output;
-       strcpy(lastfn_, fn);
-       wide_samples_in_reservoir_ = 0;
-       file_info_.output_bits_per_sample = file_info_.has_replaygain && cfg.replaygain.enable ?
-               cfg.resolution.replaygain.bps_out :
-               cfg.resolution.normal.dither_24_to_16 ? min(file_info_.bits_per_sample, 16) : file_info_.bits_per_sample;
-
-       if (file_info_.has_replaygain && cfg.replaygain.enable && cfg.resolution.replaygain.dither)
-               FLAC__replaygain_synthesis__init_dither_context(&file_info_.dither_context, file_info_.bits_per_sample, cfg.resolution.replaygain.noise_shaping);
-
-       maxlatency = mod_.outMod->Open(file_info_.sample_rate, file_info_.channels, file_info_.output_bits_per_sample, -1, -1);
-       if (maxlatency < 0) /* error opening device */
-               return 1;
-
-       /* dividing by 1000 for the first parameter of setinfo makes it */
-       /* display 'H'... for hundred.. i.e. 14H Kbps. */
-       mod_.SetInfo((int)(file_size/(125.*file_info_.total_samples/file_info_.sample_rate)), file_info_.sample_rate/1000, file_info_.channels, 1);
-
-       /* initialize vis stuff */
-       mod_.SAVSAInit(maxlatency, file_info_.sample_rate);
-       mod_.VSASetInfo(file_info_.sample_rate, file_info_.channels);
-       /* set the output plug-ins default volume */
-       mod_.outMod->SetVolume(-666);
-
-       file_info_.paused = 0;
-       file_info_.seek_to = -1;
-       decode_pos_ms_ = 0;
-       killDecodeThread = 0;
-       thread_handle = CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id);
-       if (!thread_handle)
-               return 1;
-
-       return 0;
-}
-
-void pause()
-{
-       file_info_.paused = 1;
-       mod_.outMod->Pause(1);
-}
-
-void unpause()
-{
-       file_info_.paused = 0;
-       mod_.outMod->Pause(0);
-}
-
-int ispaused()
-{
-       return file_info_.paused;
-}
-
-void stop()
-{
-       if (thread_handle) {
-               killDecodeThread = 1;
-               if(WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT) {
-                       show_error("Error while stopping decoding thread.");
-                       TerminateThread(thread_handle, 0);
-               }
-               CloseHandle(thread_handle);
-               thread_handle = NULL;
-       }
-       safe_decoder_finish_(decoder_);
-
-       mod_.outMod->Close();
-
-       mod_.SAVSADeInit();
-}
-
-int getlength()
-{
-       return (int)file_info_.length_in_msec;
-}
-
-int getoutputtime()
-{
-       return decode_pos_ms_ + (mod_.outMod->GetOutputTime() - mod_.outMod->GetWrittenTime());
-}
-
-void setoutputtime(int time_in_ms)
-{
-       file_info_.seek_to = time_in_ms;
-}
-
-void setvolume(int volume) { mod_.outMod->SetVolume(volume); }
-void setpan(int pan) { mod_.outMod->SetPan(pan); }
-
-int infoDlg(char *fn, HWND hwnd)
-{
-       DoInfoBox(mod_.hDllInstance, hwnd, fn);
-       return 0;
-}
-
-void getfileinfo(char *filename, char *title, int *length_in_msec)
-{
-       FLAC__StreamMetadata streaminfo;
-
-       if (!filename || !*filename) {
-               filename = lastfn_;
-               if (length_in_msec) {
-                       *length_in_msec = getlength();
-                       length_in_msec = 0; /* force skip in following code */
-               }
-       }
-
-       if (!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
-               if (title)
-                       sprintf(title, "Invalid FLAC: %s", filename);
-               if (length_in_msec)
-                       *length_in_msec = -1;
-               return;
-       }
-
-       if (title)
-               get_description_(filename, title, 400);
-
-       if (length_in_msec)
-               *length_in_msec = (int)(streaminfo.data.stream_info.total_samples * 10 / (streaminfo.data.stream_info.sample_rate / 100));
-}
-
-void eq_set(int on, char data[10], int preamp) {}
-
-static void do_vis(char *data, int nch, int resolution, int position, unsigned samples)
-{
-       static char vis_buffer[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
-       char *ptr;
-       int size, count;
-
-       /*
-        * Winamp visuals may have problems accepting sample sizes larger than
-        * 16 bits, so we reduce the sample size here if necessary.
-        */
-
-       switch(resolution) {
-               case 32:
-               case 24:
-                       size  = resolution / 8;
-                       count = samples * nch;
-                       data += size - 1;
-
-                       ptr = vis_buffer;
-                       while(count--) {
-                               *ptr++ = data[0] ^ 0x80;
-                               data += size;
-                       }
-
-                       data = vis_buffer;
-                       resolution = 8;
-
-                       /* fall through */
-               case 16:
-               case 8:
-               default:
-                       mod_.SAAddPCMData(data, nch, resolution, position);
-                       mod_.VSAAddPCMData(data, nch, resolution, position);
-       }
-}
-
-static DWORD WINAPI DecodeThread(void *unused)
-{
-       int done = 0;
-
-       (void)unused;
-
-       while(!killDecodeThread) {
-               const unsigned channels = file_info_.channels;
-               const unsigned bits_per_sample = file_info_.bits_per_sample;
-               const unsigned target_bps = file_info_.output_bits_per_sample;
-               const unsigned sample_rate = file_info_.sample_rate;
-
-               if(file_info_.seek_to != -1) {
-                       const double distance = (double)file_info_.seek_to / (double)getlength();
-                       const unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples);
-                       if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) {
-                               decode_pos_ms_ = (int)(distance * (double)getlength());
-                               file_info_.seek_to = -1;
-                               done = 0;
-                               mod_.outMod->Flush(decode_pos_ms_);
-                       }
-               }
-               if(done) {
-                       if(!mod_.outMod->IsPlaying()) {
-                               PostMessage(mod_.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
-                               return 0;
-                       }
-                       Sleep(10);
-               }
-               else if(mod_.outMod->CanWrite() >= ((int)(SAMPLES_PER_WRITE*channels*((target_bps+7)/8)) << (mod_.dsp_isactive()?1:0))) {
-                       while(wide_samples_in_reservoir_ < SAMPLES_PER_WRITE) {
-                               if(FLAC__file_decoder_get_state(decoder_) == FLAC__FILE_DECODER_END_OF_FILE) {
-                                       done = 1;
-                                       break;
-                               }
-                               else if(!FLAC__file_decoder_process_single(decoder_)) {
-                                       show_error("Error while processing frame (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder_)]);
-                                       done = 1;
-                                       break;
-                               }
-                       }
-
-                       if(wide_samples_in_reservoir_ == 0) {
-                               done = 1;
-                       }
-                       else {
-                               const unsigned n = min(wide_samples_in_reservoir_, SAMPLES_PER_WRITE);
-                               int bytes;
-                               unsigned i;
-
-                               if(cfg.replaygain.enable && file_info_.has_replaygain) {
-                                       bytes = (int)FLAC__replaygain_synthesis__apply_gain(
-                                               sample_buffer_,
-                                               true, /* little_endian_data_out */
-                                               target_bps == 8, /* unsigned_data_out */
-                                               reservoir__,
-                                               n,
-                                               channels,
-                                               bits_per_sample,
-                                               target_bps,
-                                               file_info_.replay_scale,
-                                               cfg.replaygain.hard_limit,
-                                               cfg.resolution.replaygain.dither,
-                                               &file_info_.dither_context
-                                       );
-                               }
-                               else {
-                                       bytes = (int)FLAC__plugin_common__pack_pcm_signed_little_endian(
-                                               sample_buffer_,
-                                               reservoir__,
-                                               n,
-                                               channels,
-                                               bits_per_sample,
-                                               target_bps
-                                       );
-                               }
-
-                               for (i = 0; i < channels; i++)
-                                       memmove(&reservoir_[i][0], &reservoir_[i][n], sizeof(reservoir_[0][0]) * (wide_samples_in_reservoir_ - n));
-                               wide_samples_in_reservoir_ -= n;
-
-                               do_vis(sample_buffer_, channels, target_bps, decode_pos_ms_, n);
-                               decode_pos_ms_ += (n*1000 + sample_rate/2)/sample_rate;
-                               if(mod_.dsp_isactive())
-                                       bytes = mod_.dsp_dosamples((short *)sample_buffer_, n, target_bps, channels, sample_rate) * (channels*target_bps/8);
-                               mod_.outMod->Write(sample_buffer_, bytes);
-                       }
-               }
-               else Sleep(20);
-       }
-       return 0;
-}
-
-
-
-In_Module mod_ =
-{
-       IN_VER,
-       "FLAC Reference Player v" VERSION,
-       0,      /* hMainWindow */
-       0,  /* hDllInstance */
-       "FLAC\0FLAC Audio File (*.FLAC)\0"
-       ,
-       1,      /* is_seekable */
-       1, /* uses output */
-       config,
-       about,
-       init,
-       quit,
-       getfileinfo,
-       infoDlg,
-       isourfile,
-       play,
-       pause,
-       unpause,
-       ispaused,
-       stop,
-
-       getlength,
-       getoutputtime,
-       setoutputtime,
-
-       setvolume,
-       setpan,
-
-       0,0,0,0,0,0,0,0,0, /* vis stuff */
-
-
-       0,0, /* dsp */
-
-       eq_set,
-
-       NULL,           /* setinfo */
-
-       0 /* out_mod */
-
-};
-
-__declspec(dllexport) In_Module *winampGetInModule2()
-{
-       return &mod_;
-}
-
-BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
-{
-       return TRUE;
-}
-
-/***********************************************************************
- * local routines
- **********************************************************************/
-FLAC__bool safe_decoder_init_(const char *filename, FLAC__FileDecoder *decoder)
-{
-       FLAC__ASSERT(0 != decoder);
-
-       safe_decoder_finish_(decoder);
-
-       FLAC__file_decoder_set_md5_checking(decoder, false);
-       FLAC__file_decoder_set_filename(decoder, filename);
-       FLAC__file_decoder_set_metadata_ignore_all(decoder);
-       FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
-       FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
-       FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
-       FLAC__file_decoder_set_write_callback(decoder, write_callback_);
-       FLAC__file_decoder_set_error_callback(decoder, error_callback_);
-       FLAC__file_decoder_set_client_data(decoder, &file_info_);
-
-       if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) {
-               show_error("Error while initializing decoder (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);
-               return false;
-       }
-
-       if(!FLAC__file_decoder_process_until_end_of_metadata(decoder)) {
-               show_error("Error while processing metadata (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);
-               return false;
-       }
-
-       if (file_info_.abort_flag)
-               return false; /* metadata callback already popped up the error dialog */
-
-       return true;
-}
-
-void safe_decoder_finish_(FLAC__FileDecoder *decoder)
-{
-       if(decoder && FLAC__file_decoder_get_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
-               FLAC__file_decoder_finish(decoder);
-}
-
-void safe_decoder_delete_(FLAC__FileDecoder *decoder)
-{
-       if(decoder) {
-               safe_decoder_finish_(decoder);
-               FLAC__file_decoder_delete(decoder);
-       }
-}
-
-FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *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;
-       unsigned channel;
-
-       (void)decoder;
-
-       if(file_info->abort_flag)
-               return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
-
-       for(channel = 0; channel < channels; channel++)
-               memcpy(&reservoir_[channel][wide_samples_in_reservoir_], buffer[channel], sizeof(buffer[0][0]) * wide_samples);
-
-       wide_samples_in_reservoir_ += wide_samples;
-
-       return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-}
-
-void metadata_callback_(const FLAC__FileDecoder *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 < 0x100000000); /* this plugin can only handle < 4 gigasamples */
-               file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xfffffffful);
-               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;
-
-               if(file_info->bits_per_sample!=8 && file_info->bits_per_sample!=16 && file_info->bits_per_sample!=24) {
-                       show_error("This plugin can only handle 8/16/24-bit samples.");
-                       file_info->abort_flag = true;
-                       return;
-               }
-               file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100);
-       }
-       else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
-               double gain, peak;
-               if(grabbag__replaygain_load_from_vorbiscomment(metadata, cfg.replaygain.album_mode, &gain, &peak)) {
-                       file_info_.has_replaygain = true;
-                       file_info_.replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)cfg.replaygain.preamp, !cfg.replaygain.hard_limit);
-               }
-       }
-}
-
-void error_callback_(const FLAC__FileDecoder *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;
-}
-
-/*
- *  title formatting
- */
-
-typedef struct
-{
-       FLAC_Plugin__CanonicalTag t;
-       const char *filename;
-} tag_param_t;
-
-
-static __inline char *GetFileName(const char *fullname)
-{
-       const char *c = fullname + strlen(fullname) - 1;
-
-       while (c > fullname)
-       {
-               if (*c=='\\' || *c=='/')
-               {
-                       c++;
-                       break;
-               }
-               c--;
-       }
-
-       return (char*)c;
-}
-
-static const T_CHAR *get_tag(const T_CHAR *tag, void *param)
-{
-       tag_param_t *p = (tag_param_t*)param;
-
-       if (!stricmp(tag, "path") || !stricmp(tag, "filepath") || !stricmp(tag, "url"))
-               return p->filename;
-       else if (!stricmp(tag, "filename"))
-       {
-               static char foo[MAX_PATH];
-               char *c;
-
-               strcpy(foo, GetFileName(p->filename));
-               if (c = strrchr(foo, '.')) *c = 0;
-
-               return foo;
-       }
-       else if (!stricmp(tag, "title"))
-               return p->t.title;
-       else if (!stricmp(tag, "artist"))
-               return p->t.performer ? p->t.performer : p->t.composer;
-       else if (!stricmp(tag, "composer"))
-               return p->t.composer;
-       else if (!stricmp(tag, "performer"))
-               return p->t.performer;
-       else if (!stricmp(tag, "album"))
-               return p->t.album;
-       else if (!stricmp(tag, "year") || !stricmp(tag, "date"))
-               return p->t.year_recorded ? p->t.year_recorded : p->t.year_performed;
-       else if (!stricmp(tag, "year_recorded"))
-               return p->t.year_recorded;
-       else if (!stricmp(tag, "year_performed"))
-               return p->t.year_performed;
-       else if (!stricmp(tag, "track_number"))
-               return p->t.track_number;
-       else if (!stricmp(tag, "tracks_in_album"))
-               return p->t.tracks_in_album;
-       else if (!stricmp(tag, "genre"))
-               return p->t.genre;
-       else if (!stricmp(tag, "comment") || !stricmp(tag, "description"))
-               return p->t.comment;
-       else return NULL;
-}
-
-void get_description_(const char *filename, char *description, unsigned max_size)
-{
-       tag_param_t param;
-
-       FLAC_plugin__canonical_tag_init(&param.t);
-       FLAC_plugin__canonical_tag_get_combined(filename, &param.t);
-       param.filename = filename;
-
-       tagz_format(flac_cfg.title.tag_format, get_tag, NULL, &param, description, max_size);
-
-       FLAC_plugin__canonical_tag_clear(&param.t);
-}
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2000,2001,2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#include <windows.h>\r
+#include <stdio.h>\r
+\r
+#include "winamp2/in2.h"\r
+#include "config.h"\r
+#include "infobox.h"\r
+#include "tagz.h"\r
+\r
+#define PLUGIN_VERSION          "1.1.1"\r
+\r
+static In_Module mod_;                      /* the input module (declared near the bottom of this file) */\r
+static char lastfn_[MAX_PATH];              /* currently playing file (used for getting info on the current file) */\r
+flac_config_t flac_cfg;\r
+\r
+static file_info_struct file_info_;\r
+static int paused;\r
+static FLAC__FileDecoder *decoder_;\r
+static char sample_buffer_[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8) * 2];\r
+/* (24/8) for max bytes per sample, and 2 for DSPs */\r
+\r
+static HANDLE thread_handle = NULL;         /* the handle to the decode thread */\r
+static DWORD WINAPI DecodeThread(void *b);  /* the decode thread procedure */\r
+\r
+/*\r
+ *  init/quit\r
+ */\r
+\r
+static void init()\r
+{\r
+       decoder_ = FLAC__file_decoder_new();\r
+       strcpy(lastfn_, "");\r
+\r
+       InitConfig();\r
+       ReadConfig();\r
+       InitInfobox();\r
+}\r
+\r
+static void quit()\r
+{\r
+       WriteConfig();\r
+       DeinitInfobox();\r
+       FLAC_plugin__decoder_delete(decoder_);\r
+       decoder_ = 0;\r
+}\r
+\r
+/*\r
+ *  open/close\r
+ */\r
+\r
+static int isourfile(char *fn) { return 0; }\r
+\r
+static int play(char *fn)\r
+{\r
+       LONGLONG filesize;\r
+       DWORD thread_id;\r
+       int   maxlatency;\r
+       /* checks */\r
+       if (decoder_ == 0) return 1;\r
+       if (!(filesize = FileSize(fn))) return -1;\r
+       /* init decoder */\r
+       if (!FLAC_plugin__decoder_init(decoder_, fn, filesize, &file_info_, &flac_cfg.output))\r
+               return 1;\r
+       strcpy(lastfn_, fn);\r
+       /* open output */\r
+       maxlatency = mod_.outMod->Open(file_info_.sample_rate, file_info_.channels, file_info_.output_bits_per_sample, -1, -1);\r
+       if (maxlatency < 0)\r
+       {\r
+               FLAC_plugin__decoder_finish(decoder_);\r
+               return 1;\r
+       }\r
+       /* set defaults */\r
+       mod_.outMod->SetVolume(-666);\r
+       mod_.outMod->SetPan(0);\r
+       /* initialize vis stuff */\r
+       mod_.SAVSAInit(maxlatency, file_info_.sample_rate);\r
+       mod_.VSASetInfo(file_info_.sample_rate, file_info_.channels);\r
+       /* set info */\r
+       mod_.SetInfo(file_info_.average_bps, file_info_.sample_rate/1000, file_info_.channels, 1);\r
+       /* start playing thread */\r
+       paused = 0;\r
+       thread_handle = CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id);\r
+       if (!thread_handle)     return 1;\r
+\r
+       return 0;\r
+}\r
+\r
+static void stop()\r
+{\r
+       if (thread_handle)\r
+       {\r
+               file_info_.is_playing = false;\r
+               if (WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT)\r
+               {\r
+                       FLAC_plugin__show_error("Error while stopping decoding thread.");\r
+                       TerminateThread(thread_handle, 0);\r
+               }\r
+               CloseHandle(thread_handle);\r
+               thread_handle = NULL;\r
+       }\r
+\r
+       FLAC_plugin__decoder_finish(decoder_);\r
+       mod_.outMod->Close();\r
+       mod_.SAVSADeInit();\r
+}\r
+\r
+/*\r
+ *  play control\r
+ */\r
+\r
+static void pause()\r
+{\r
+       paused = 1;\r
+       mod_.outMod->Pause(1);\r
+}\r
+\r
+static void unpause()\r
+{\r
+       paused = 0;\r
+       mod_.outMod->Pause(0);\r
+}\r
+\r
+static int ispaused()\r
+{\r
+       return paused;\r
+}\r
+\r
+static int getlength()\r
+{\r
+       return file_info_.length_in_msec;\r
+}\r
+\r
+static int getoutputtime()\r
+{\r
+       return mod_.outMod->GetOutputTime();\r
+}\r
+\r
+static void setoutputtime(int time_in_ms)\r
+{\r
+       file_info_.seek_to = time_in_ms;\r
+}\r
+\r
+static void setvolume(int volume)\r
+{\r
+       mod_.outMod->SetVolume(volume);\r
+}\r
+\r
+static void setpan(int pan)\r
+{\r
+       mod_.outMod->SetPan(pan);\r
+}\r
+\r
+static void eq_set(int on, char data[10], int preamp) {}\r
+\r
+/*\r
+ *  playing loop\r
+ */\r
+\r
+static void do_vis(char *data, int nch, int resolution, int position, unsigned samples)\r
+{\r
+       static char vis_buffer[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];\r
+       char *ptr;\r
+       int size, count;\r
+\r
+       /*\r
+        * Winamp visuals may have problems accepting sample sizes larger than\r
+        * 16 bits, so we reduce the sample size here if necessary.\r
+        */\r
+\r
+       switch(resolution) {\r
+               case 32:\r
+               case 24:\r
+                       size  = resolution / 8;\r
+                       count = samples * nch;\r
+                       data += size - 1;\r
+\r
+                       ptr = vis_buffer;\r
+                       while(count--) {\r
+                               *ptr++ = data[0] ^ 0x80;\r
+                               data += size;\r
+                       }\r
+\r
+                       data = vis_buffer;\r
+                       resolution = 8;\r
+                       /* fall through */\r
+               case 16:\r
+               case 8:\r
+                       mod_.SAAddPCMData(data, nch, resolution, position);\r
+                       mod_.VSAAddPCMData(data, nch, resolution, position);\r
+       }\r
+}\r
+\r
+static DWORD WINAPI DecodeThread(void *unused)\r
+{\r
+       const unsigned channels = file_info_.channels;\r
+       const unsigned bits_per_sample = file_info_.bits_per_sample;\r
+       const unsigned target_bps = file_info_.output_bits_per_sample;\r
+       const unsigned sample_rate = file_info_.sample_rate;\r
+       const unsigned fact = channels * (target_bps/8);\r
+\r
+       while (file_info_.is_playing)\r
+       {\r
+               /* seek needed */\r
+               if (file_info_.seek_to != -1)\r
+               {\r
+                       const int pos = FLAC_plugin__seek(decoder_, &file_info_);\r
+                       if (pos != -1) mod_.outMod->Flush(pos);\r
+               }\r
+               /* stream ended */\r
+               else if (file_info_.eof)\r
+               {\r
+                       if (!mod_.outMod->IsPlaying())\r
+                       {\r
+                               PostMessage(mod_.hMainWindow, WM_WA_MPEG_EOF, 0, 0);\r
+                               return 0;\r
+                       }\r
+                       Sleep(10);\r
+               }\r
+               /* decode */\r
+               else\r
+               {\r
+                       /* decode samples */\r
+                       int bytes = FLAC_plugin__decode(decoder_, &file_info_, sample_buffer_);\r
+                       const int n = bytes / fact;\r
+                       /* visualization */\r
+                       do_vis(sample_buffer_, channels, target_bps, mod_.outMod->GetWrittenTime(), n);\r
+                       /* dsp */\r
+                       if (mod_.dsp_isactive())\r
+                               bytes = mod_.dsp_dosamples((short*)sample_buffer_, n, target_bps, channels, sample_rate) * fact;\r
+                       /* output */\r
+                       while (mod_.outMod->CanWrite()<bytes && file_info_.is_playing && file_info_.seek_to==-1)\r
+                               Sleep(20);\r
+                       if (file_info_.is_playing && file_info_.seek_to==-1)\r
+                               mod_.outMod->Write(sample_buffer_, bytes);\r
+                       /* show bitrate */\r
+                       if (flac_cfg.display.show_bps)\r
+                       {\r
+                               const int rate = FLAC_plugin__get_rate(mod_.outMod->GetWrittenTime(), mod_.outMod->GetOutputTime(), &file_info_);\r
+                               if (rate) mod_.SetInfo(rate/1000, file_info_.sample_rate/1000, file_info_.channels, 1);\r
+                       }\r
+               }\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+/*\r
+ *  title formatting\r
+ */\r
+\r
+static const T_CHAR *get_tag(const T_CHAR *tag, void *param)\r
+{\r
+       FLAC_Plugin__CanonicalTag *t = (FLAC_Plugin__CanonicalTag*)param;\r
+       const T_CHAR *val = FLAC_plugin__canonical_get(t, tag);\r
+       /* some "user friendly cheavats" */\r
+       if (!val)\r
+       {\r
+               if (!wcsicmp(tag, L"ARTIST"))\r
+               {\r
+                       val = FLAC_plugin__canonical_get(t, L"PERFORMER");\r
+                       if (!val) val = FLAC_plugin__canonical_get(t, L"COMPOSER");\r
+               }\r
+               else if (!wcsicmp(tag, L"YEAR") || !wcsicmp(tag, L"DATE"))\r
+               {\r
+                       val = FLAC_plugin__canonical_get(t, L"YEAR_RECORDED");\r
+                       if (!val) val = FLAC_plugin__canonical_get(t, L"YEAR_PERFORMED");\r
+               }\r
+       }\r
+\r
+       return val;\r
+}\r
+\r
+static void format_title(const char *filename, WCHAR *title, unsigned max_size)\r
+{\r
+       FLAC_Plugin__CanonicalTag tag;\r
+\r
+       ReadTags(filename, &tag, true);\r
+\r
+       tagz_format(flac_cfg.title.tag_format_w, get_tag, NULL, &tag, title, max_size);\r
+\r
+       FLAC_plugin__canonical_tag_clear(&tag);\r
+}\r
+\r
+static void getfileinfo(char *filename, char *title, int *length_in_msec)\r
+{\r
+       FLAC__StreamMetadata streaminfo;\r
+\r
+       if (!filename || !*filename)\r
+       {\r
+               filename = lastfn_;\r
+               if (length_in_msec)\r
+               {\r
+                       *length_in_msec = file_info_.length_in_msec;\r
+                       length_in_msec  = 0;    /* force skip in following code */\r
+               }\r
+       }\r
+\r
+       if (!FLAC__metadata_get_streaminfo(filename, &streaminfo))\r
+       {\r
+               if (length_in_msec)\r
+                       *length_in_msec = -1;\r
+               return;\r
+       }\r
+\r
+       if (title)\r
+       {\r
+               static WCHAR buffer[400];\r
+               format_title(filename, buffer, 400);\r
+               WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, buffer, -1, title, 400, NULL, NULL);\r
+       }\r
+\r
+       if (length_in_msec)\r
+               *length_in_msec = (int)(streaminfo.data.stream_info.total_samples*10 / (streaminfo.data.stream_info.sample_rate/100));\r
+}\r
+\r
+/*\r
+ *  interface\r
+ */\r
+\r
+void FLAC_plugin__show_error(const char *message,...)\r
+{\r
+       char foo[512];\r
+       va_list args;\r
+       va_start(args, message);\r
+       vsprintf(foo, message, args);\r
+       va_end(args);\r
+       MessageBox(mod_.hMainWindow, foo, "FLAC Plug-in Error", MB_ICONSTOP);\r
+}\r
+\r
+static void about(HWND hwndParent)\r
+{\r
+       MessageBox(hwndParent, "Winamp2 FLAC Plugin v"PLUGIN_VERSION"\nby Josh Coalson and X-Fixer\n\nuses libFLAC "VERSION"\nSee http://flac.sourceforge.net/\n", "About FLAC Plugin", MB_ICONINFORMATION);\r
+}\r
+\r
+static void config(HWND hwndParent)\r
+{\r
+       if (DoConfig(mod_.hDllInstance, hwndParent))\r
+               WriteConfig();\r
+}\r
+\r
+static int infobox(char *fn, HWND hwnd)\r
+{\r
+       DoInfoBox(mod_.hDllInstance, hwnd, fn);\r
+       return 0;\r
+}\r
+\r
+/*\r
+ *  exported stuff\r
+ */\r
+\r
+static In_Module mod_ =\r
+{\r
+       IN_VER,\r
+       "Reference FLAC Decoder v" PLUGIN_VERSION,\r
+       0,                                    /* hMainWindow */\r
+       0,                                    /* hDllInstance */\r
+       "FLAC\0FLAC Audio File (*.FLAC)\0",\r
+       1,                                    /* is_seekable */\r
+       1,                                    /* uses output */\r
+       config,\r
+       about,\r
+       init,\r
+       quit,\r
+       getfileinfo,\r
+       infobox,\r
+       isourfile,\r
+       play,\r
+       pause,\r
+       unpause,\r
+       ispaused,\r
+       stop,\r
+\r
+       getlength,\r
+       getoutputtime,\r
+       setoutputtime,\r
+\r
+       setvolume,\r
+       setpan,\r
+\r
+       0,0,0,0,0,0,0,0,0,                    /* vis stuff */\r
+       0,0,                                  /* dsp */\r
+       eq_set,\r
+       NULL,                                 /* setinfo */\r
+       0                                     /* out_mod */\r
+};\r
+\r
+__declspec(dllexport) In_Module *winampGetInModule2()\r
+{\r
+       return &mod_;\r
+}\r
+\r
+BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)\r
+{\r
+       return TRUE;\r
+}\r
index 8a1b5cc..f2fa67e 100644 (file)
@@ -43,7 +43,7 @@ RSC=rc.exe
 # PROP Ignore_Export_Lib 0\r
 # PROP Target_Dir ""\r
 # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "in_flac_EXPORTS" /YX /FD /c\r
-# ADD CPP /nologo /MD /W3 /GX /Ox /Og /Oi /Os /Op /Gf /Gy /I "include" /I ".." /I "..\..\include" /D "NDEBUG" /D VERSION=\"1.1.1\" /D "in_flac_EXPORTS" /D "FLAC__NO_DLL" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /WX /GX /Ox /Og /Oi /Os /Op /Gf /Gy /I "include" /I ".." /I "..\..\include" /D "NDEBUG" /D VERSION=\"1.1.1\" /D "in_flac_EXPORTS" /D "FLAC__NO_DLL" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TAGZ_UNICODE" /YX /FD /c\r
 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
 # ADD BASE RSC /l 0x409 /d "NDEBUG"\r
@@ -70,7 +70,8 @@ LINK32=link.exe
 # PROP Ignore_Export_Lib 0\r
 # PROP Target_Dir ""\r
 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "in_flac_EXPORTS" /YX /FD /GZ /c\r
-# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I ".." /I "..\..\include" /D "_DEBUG" /D "REAL_STDIO" /D VERSION=\"1.1.1\" /D "in_flac_EXPORTS" /D "FLAC__NO_DLL" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "include" /I ".." /I "..\..\include" /D "_DEBUG" /D "REAL_STDIO" /D VERSION=\"1.1.1\" /D "in_flac_EXPORTS" /D "FLAC__NO_DLL" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TAGZ_UNICODE" /YX /FD /GZ /c\r
+# SUBTRACT CPP /WX\r
 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
 # ADD BASE RSC /l 0x409 /d "_DEBUG"\r
@@ -126,6 +127,14 @@ SOURCE=.\infobox.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\playback.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\playback.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\resource.h\r
 # End Source File\r
 # Begin Source File\r
index 43a1fe5..d94054d 100644 (file)
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
 #include <windows.h>\r
 #include <stdio.h>\r
 #include "FLAC/all.h"\r
 #include "plugin_common/all.h"\r
 #include "infobox.h"\r
+#include "config.h"\r
 #include "resource.h"\r
 \r
 \r
 typedef struct\r
 {\r
-    char filename[MAX_PATH];\r
+       char filename[MAX_PATH];\r
+       FLAC_Plugin__CanonicalTag tag;\r
 } LOCALDATA;\r
 \r
-static char buffer[256];\r
+static char buffer[1024];\r
 static char *genres = NULL;\r
-static int  genresSize = 0, genresCount = 0, genresChanged = 0;\r
+static DWORD genresSize = 0, genresCount = 0;\r
+static BOOL genresChanged = FALSE, isNT;\r
 \r
 static const char infoTitle[] = "FLAC File Info";\r
 \r
-//fixme int64\r
-static __inline DWORD FileSize(const char *file)\r
-{\r
-    HANDLE hFile = CreateFile(file, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
-    DWORD res;\r
+/*\r
+ *  Genres\r
+ */\r
 \r
-    if (hFile == INVALID_HANDLE_VALUE) return 0;\r
-    res = GetFileSize(hFile, 0);\r
-    CloseHandle(hFile);\r
-    return res;\r
-}\r
+/* TODO: write genres in utf-8 ? */\r
 \r
 static __inline int GetGenresFileName(char *buffer, int size)\r
 {\r
-    char *c;\r
+       char *c;\r
 \r
-    if (!GetModuleFileName(NULL, buffer, size))\r
-        return 0;\r
-    c = strrchr(buffer, '\\');\r
-    if (!c) return 0;\r
-    strcpy(c+1, "genres.txt");\r
+       if (!GetModuleFileName(NULL, buffer, size))\r
+               return 0;\r
+       c = strrchr(buffer, '\\');\r
+       if (!c) return 0;\r
+       strcpy(c+1, "genres.txt");\r
 \r
-    return 1;\r
+       return 1;\r
 }\r
 \r
 static void LoadGenres()\r
 {\r
-    HANDLE hFile;\r
-    DWORD  spam;\r
-    char  *c;\r
-\r
-    if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
-    // load file\r
-    hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
-    if (hFile == INVALID_HANDLE_VALUE) return;\r
-    genresSize = GetFileSize(hFile, 0);\r
-    if (!genresSize) return;\r
-    genres = (char*)malloc(genresSize+2);\r
-    if (!genres) return;\r
-    if (!ReadFile(hFile, genres, genresSize, &spam, NULL))\r
-    {\r
-        free(genres);\r
-        genres = NULL;\r
-        return;\r
-    }\r
-    genres[genresSize] = 0;\r
-    genres[genresSize+1] = 0;\r
-    // replace newlines\r
-    genresChanged = 0;\r
-    genresCount = 1;\r
-\r
-    for (c=genres; *c; c++)\r
-    {\r
-        if (*c == 10)\r
-        {\r
-            *c = 0;\r
-            if (*(c+1))\r
-                genresCount++;\r
-            else genresSize--;\r
-        }\r
-    }\r
-\r
-    CloseHandle(hFile);\r
+       HANDLE hFile;\r
+       DWORD  spam;\r
+       char  *c;\r
+\r
+       FLAC__ASSERT(0 != genres);\r
+\r
+       if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
+       /* load file */\r
+       hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
+       if (hFile == INVALID_HANDLE_VALUE) return;\r
+       genresSize = GetFileSize(hFile, 0);\r
+       if (genresSize && (genres = (char*)malloc(genresSize+2)))\r
+       {\r
+               if (!ReadFile(hFile, genres, genresSize, &spam, NULL) || spam!=genresSize)\r
+               {\r
+                       free(genres);\r
+                       genres = NULL;\r
+               }\r
+               else\r
+               {\r
+                       genres[genresSize] = 0;\r
+                       genres[genresSize+1] = 0;\r
+                       /* replace newlines */\r
+                       genresChanged = FALSE;\r
+                       genresCount = 1;\r
+\r
+                       for (c=genres; *c; c++)\r
+                       {\r
+                               if (*c == 10)\r
+                               {\r
+                                       *c = 0;\r
+                                       if (*(c+1))\r
+                                               genresCount++;\r
+                                       else genresSize--;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       CloseHandle(hFile);\r
 }\r
 \r
 static void SaveGenres(HWND hlist)\r
 {\r
-    HANDLE hFile;\r
-    DWORD  spam;\r
-    int i, count, len;\r
-\r
-    if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
-    // write file\r
-    hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
-    if (hFile == INVALID_HANDLE_VALUE) return;\r
-\r
-    count = SendMessage(hlist, CB_GETCOUNT, 0, 0);\r
-    for (i=0; i<count; i++)\r
-    {\r
-        SendMessage(hlist, CB_GETLBTEXT, i, (LPARAM)buffer);\r
-        len = strlen(buffer);\r
-        if (i != count-1)\r
-        {\r
-            buffer[len] = 10;\r
-            len++;\r
-        }\r
-        WriteFile(hFile, buffer, len, &spam, NULL);\r
-    }\r
-\r
-    CloseHandle(hFile);\r
+       HANDLE hFile;\r
+       DWORD  spam;\r
+       int i, count, len;\r
+\r
+       if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
+       /* write file */\r
+       hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
+       if (hFile == INVALID_HANDLE_VALUE) return;\r
+\r
+       count = SendMessage(hlist, CB_GETCOUNT, 0, 0);\r
+       for (i=0; i<count; i++)\r
+       {\r
+               SendMessage(hlist, CB_GETLBTEXT, i, (LPARAM)buffer);\r
+               len = strlen(buffer);\r
+               if (i != count-1)\r
+               {\r
+                       buffer[len] = 10;\r
+                       len++;\r
+               }\r
+               WriteFile(hFile, buffer, len, &spam, NULL);\r
+       }\r
+\r
+       CloseHandle(hFile);\r
 }\r
 \r
+static void AddGenre(HWND hwnd, const char *genre)\r
+{\r
+       HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
+\r
+       if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)genre) == CB_ERR)\r
+       {\r
+               genresChanged = TRUE;\r
+               SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)genre);\r
+       }\r
+}\r
+\r
+static void InitGenres(HWND hwnd)\r
+{\r
+       HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
+       char *c;\r
+\r
+       /* set text length limit to 64 chars */\r
+       SendMessage(hgen, CB_LIMITTEXT, 64, 0);\r
+       /* try to load genres */\r
+       if (!genres)\r
+               LoadGenres(hgen);\r
+       /* add the to list */\r
+       if (genres)\r
+       {\r
+               SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize);\r
+\r
+               for (c = genres; *c; c += strlen(c)+1)\r
+                       SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c);\r
+       }\r
+}\r
+\r
+static void DeinitGenres(HWND hwnd, BOOL final)\r
+{\r
+       if (genresChanged && hwnd)\r
+       {\r
+               SaveGenres(GetDlgItem(hwnd, IDC_GENRE));\r
+               genresChanged = FALSE;\r
+               final = TRUE;\r
+       }\r
+       if (final)\r
+       {\r
+               free(genres);\r
+               genres = 0;\r
+       }\r
+}\r
+\r
+/*\r
+ *  Infobox helpers\r
+ */\r
+\r
+#define SetText(x,y)            WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, FLAC_plugin__canonical_get(&data->tag, y), -1, buffer, sizeof(buffer), NULL, NULL); \\r
+                                SetDlgItemText(hwnd, x, buffer)\r
+\r
+#define GetText(x,y)            GetDlgItemText(hwnd, x, buffer, sizeof(buffer));                        \\r
+                                if (*buffer) FLAC_plugin__canonical_set_ansi(&data->tag, y, buffer);    \\r
+                                else FLAC_plugin__canonical_remove_all(&data->tag, y)\r
+\r
+#define SetTextW(x,y)           SetDlgItemTextW(hwnd, x, FLAC_plugin__canonical_get(&data->tag, y))\r
+\r
+#define GetTextW(x,y)           GetDlgItemTextW(hwnd, x, (WCHAR*)buffer, sizeof(buffer)/2);                     \\r
+                                if (*(WCHAR*)buffer) FLAC_plugin__canonical_set(&data->tag, y, (WCHAR*)buffer); \\r
+                                else FLAC_plugin__canonical_remove_all(&data->tag, y)\r
 \r
-#define SetText(x,y)            SetDlgItemText(hwnd, x, y)\r
-#define GetText(x,y)            (GetDlgItemText(hwnd, x, buffer, sizeof(buffer)), y = buffer[0] ? strdup(buffer) : 0)\r
 \r
-static BOOL InitInfobox(HWND hwnd, const char *file)\r
+static BOOL InitInfoboxInfo(HWND hwnd, const char *file)\r
 {\r
-    LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
+       LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
        FLAC__StreamMetadata streaminfo;\r
-    FLAC_Plugin__CanonicalTag tag;\r
-    DWORD filesize, length, bps, ratio;\r
-\r
-    SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
-    // file name\r
-    strncpy(data->filename, file, sizeof(data->filename));\r
-    SetDlgItemText(hwnd, IDC_NAME, file);\r
-    // stream data\r
-    filesize = FileSize(file);\r
-    if (!filesize) return FALSE;\r
+       DWORD    length, bps, ratio, rg;\r
+       LONGLONG filesize;\r
+\r
+       SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
+       /* file name */\r
+       strncpy(data->filename, file, sizeof(data->filename));\r
+       SetDlgItemText(hwnd, IDC_NAME, file);\r
+       /* stream data and vorbis comment */\r
+       filesize = FileSize(file);\r
+       if (!filesize) return FALSE;\r
        if (!FLAC__metadata_get_streaminfo(file, &streaminfo))\r
-        return FALSE;\r
-\r
-    length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);\r
-    bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));\r
-    ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);\r
-\r
-    sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"\r
-                    "File size: %d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n",\r
-        streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,\r
-        streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,\r
-        length/60, length%60, bps, ratio/10, ratio%10);\r
-    //todo: replaygain\r
-\r
-    SetDlgItemText(hwnd, IDC_INFO, buffer);\r
-    // tag\r
-       FLAC_plugin__canonical_tag_init(&tag);\r
-       FLAC_plugin__canonical_tag_get_combined(file, &tag);\r
-\r
-    SetText(IDC_TITLE,   tag.title);\r
-    SetText(IDC_ARTIST,  tag.performer ? tag.performer : tag.composer);\r
-    SetText(IDC_ALBUM,   tag.album);\r
-    SetText(IDC_COMMENT, tag.comment);\r
-    SetText(IDC_YEAR,    tag.year_recorded ? tag.year_recorded : tag.year_performed);\r
-    SetText(IDC_TRACK,   tag.track_number);\r
-    SetText(IDC_GENRE,   tag.genre);\r
-\r
-       FLAC_plugin__canonical_tag_clear(&tag);\r
-\r
-    return TRUE;\r
+               return FALSE;\r
+       ReadTags(file, &data->tag, false);\r
+\r
+       length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);\r
+       bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));\r
+       ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);\r
+       rg  = FLAC_plugin__canonical_get(&data->tag, L"REPLAYGAIN_TRACK_GAIN") ? 1 : 0;\r
+       rg |= FLAC_plugin__canonical_get(&data->tag, L"REPLAYGAIN_ALBUM_GAIN") ? 2 : 0;\r
+\r
+       sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"\r
+                       "File size: %I64d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n"\r
+                       "ReplayGain: %s\n",\r
+           streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,\r
+           streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,\r
+           length/60, length%60, bps, ratio/10, ratio%10,\r
+           rg==3 ? "track gain\nReplayGain: album gain" : rg==2 ? "album gain" : rg==1 ? "track gain" : "not present");\r
+\r
+       SetDlgItemText(hwnd, IDC_INFO, buffer);\r
+       /* tag */\r
+       if (isNT)\r
+       {\r
+               SetTextW(IDC_TITLE,   L"TITLE");\r
+               SetTextW(IDC_ARTIST,  L"ARTIST");\r
+               SetTextW(IDC_ALBUM,   L"ALBUM");\r
+               SetTextW(IDC_COMMENT, L"DESCRIPTION");\r
+               SetTextW(IDC_YEAR,    L"DATE");\r
+               SetTextW(IDC_TRACK,   L"TRACKNUMBER");\r
+               SetTextW(IDC_GENRE,   L"GENRE");\r
+       }\r
+       else\r
+       {\r
+               SetText(IDC_TITLE,   L"TITLE");\r
+               SetText(IDC_ARTIST,  L"ARTIST");\r
+               SetText(IDC_ALBUM,   L"ALBUM");\r
+               SetText(IDC_COMMENT, L"DESCRIPTION");\r
+               SetText(IDC_YEAR,    L"DATE");\r
+               SetText(IDC_TRACK,   L"TRACKNUMBER");\r
+               SetText(IDC_GENRE,   L"GENRE");\r
+       }\r
+\r
+       return TRUE;\r
 }\r
 \r
 static void __inline SetTag(HWND hwnd, const char *filename, FLAC_Plugin__CanonicalTag *tag)\r
 {\r
-    strcpy(buffer, infoTitle);\r
+       strcpy(buffer, infoTitle);\r
 \r
        if (FLAC_plugin__vorbiscomment_set(filename, tag))\r
-        strcat(buffer, " [Updated]");\r
-    else strcat(buffer, " [Failed]");\r
+               strcat(buffer, " [Updated]");\r
+       else strcat(buffer, " [Failed]");\r
 \r
-    SetWindowText(hwnd, buffer);\r
+       SetWindowText(hwnd, buffer);\r
 }\r
 \r
 static void UpdateTag(HWND hwnd)\r
 {\r
-    LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
-    FLAC_Plugin__CanonicalTag tag;\r
-       FLAC_plugin__canonical_tag_init(&tag);\r
-\r
-    // get fields\r
-    GetText(IDC_TITLE,   tag.title);\r
-    GetText(IDC_ARTIST,  tag.composer);\r
-    GetText(IDC_ALBUM,   tag.album);\r
-    GetText(IDC_COMMENT, tag.comment);\r
-    GetText(IDC_YEAR,    tag.year_recorded);\r
-    GetText(IDC_TRACK,   tag.track_number);\r
-    GetText(IDC_GENRE,   tag.genre);\r
-\r
-    // update genres list\r
-    if (tag.genre)\r
-    {\r
-        HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
-\r
-        if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)tag.genre) == CB_ERR)\r
-        {\r
-            genresChanged = 1;\r
-            SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)tag.genre);\r
-        }\r
-    }\r
-\r
-    // write tag\r
-    SetTag(hwnd, data->filename, &tag);\r
-       FLAC_plugin__canonical_tag_clear(&tag);\r
+       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+\r
+       /* get fields */\r
+       if (isNT)\r
+       {\r
+               GetTextW(IDC_TITLE,   L"TITLE");\r
+               GetTextW(IDC_ARTIST,  L"ARTIST");\r
+               GetTextW(IDC_ALBUM,   L"ALBUM");\r
+               GetTextW(IDC_COMMENT, L"DESCRIPTION");\r
+               GetTextW(IDC_YEAR,    L"DATE");\r
+               GetTextW(IDC_TRACK,   L"TRACKNUMBER");\r
+               GetTextW(IDC_GENRE,   L"GENRE");\r
+\r
+               WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, FLAC_plugin__canonical_get(&data->tag, L"GENRE"), -1, buffer, sizeof(buffer), NULL, NULL);\r
+       }\r
+       else\r
+       {\r
+               GetText(IDC_TITLE,   L"TITLE");\r
+               GetText(IDC_ARTIST,  L"ARTIST");\r
+               GetText(IDC_ALBUM,   L"ALBUM");\r
+               GetText(IDC_COMMENT, L"DESCRIPTION");\r
+               GetText(IDC_YEAR,    L"DATE");\r
+               GetText(IDC_TRACK,   L"TRACKNUMBER");\r
+               GetText(IDC_GENRE,   L"GENRE");\r
+       }\r
+\r
+       /* update genres list (buffer should contain genre) */\r
+       if (buffer[0]) AddGenre(hwnd, buffer);\r
+\r
+       /* write tag */\r
+       SetTag(hwnd, data->filename, &data->tag);\r
 }\r
 \r
 static void RemoveTag(HWND hwnd)\r
 {\r
-    LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
-    FLAC_Plugin__CanonicalTag tag;\r
-       FLAC_plugin__canonical_tag_init(&tag);\r
-\r
-    SetText(IDC_TITLE,   "");\r
-    SetText(IDC_ARTIST,  "");\r
-    SetText(IDC_ALBUM,   "");\r
-    SetText(IDC_COMMENT, "");\r
-    SetText(IDC_YEAR,    "");\r
-    SetText(IDC_TRACK,   "");\r
-    SetText(IDC_GENRE,   "");\r
-\r
-    SetTag(hwnd, data->filename, &tag);\r
+       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+       FLAC_plugin__canonical_tag_clear(&data->tag);\r
+\r
+       SetDlgItemText(hwnd, IDC_TITLE,   "");\r
+       SetDlgItemText(hwnd, IDC_ARTIST,  "");\r
+       SetDlgItemText(hwnd, IDC_ALBUM,   "");\r
+       SetDlgItemText(hwnd, IDC_COMMENT, "");\r
+       SetDlgItemText(hwnd, IDC_YEAR,    "");\r
+       SetDlgItemText(hwnd, IDC_TRACK,   "");\r
+       SetDlgItemText(hwnd, IDC_GENRE,   "");\r
+\r
+       SetTag(hwnd, data->filename, &data->tag);\r
 }\r
 \r
 \r
 static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
 {\r
-    switch (msg)\r
-    {\r
-    // init\r
-    case WM_INITDIALOG:\r
-        SetWindowText(hwnd, infoTitle);\r
-        // init genres list\r
-        {\r
-            HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
-            char *c;\r
-\r
-            // set text length limit to 64 chars\r
-            SendMessage(hgen, CB_LIMITTEXT, 64, 0);\r
-            // try to load genres\r
-            if (!genres) LoadGenres(hgen);\r
-            // add the to list\r
-            if (genres)\r
-            {\r
-                SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize);\r
-\r
-                for (c = genres; *c; c += strlen(c)+1)\r
-                    SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c);\r
-            }\r
-        }\r
-        // init fields\r
-        if (!InitInfobox(hwnd, (const char*)lParam))\r
-            PostMessage(hwnd, WM_CLOSE, 0, 0);\r
-        return TRUE;\r
-    // destroy\r
-    case WM_DESTROY:\r
-        if (genresChanged)\r
-        {\r
-            SaveGenres(GetDlgItem(hwnd, IDC_GENRE));\r
-            free(genres);\r
-            genres = 0;\r
-        }\r
-        LocalFree((LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA));\r
-        break;\r
-    // commands\r
-    case WM_COMMAND:\r
-        switch (LOWORD(wParam))\r
-        {\r
-        // ok/cancel\r
-        case IDOK:\r
-        case IDCANCEL:\r
-            EndDialog(hwnd, LOWORD(wParam));\r
-            return TRUE;\r
-        // save\r
-        case IDC_UPDATE:\r
-            UpdateTag(hwnd);\r
-            break;\r
-        // remove\r
-        case IDC_REMOVE:\r
-            RemoveTag(hwnd);\r
-            break;\r
-        }\r
-        break;\r
-    }\r
-\r
-    return 0;\r
+       switch (msg)\r
+       {\r
+       /* init */\r
+       case WM_INITDIALOG:\r
+               SetWindowText(hwnd, infoTitle);\r
+               InitGenres(hwnd);\r
+               /* init fields */\r
+               if (!InitInfoboxInfo(hwnd, (const char*)lParam))\r
+                       PostMessage(hwnd, WM_CLOSE, 0, 0);\r
+               return TRUE;\r
+       /* destroy */\r
+       case WM_DESTROY:\r
+               {\r
+                       LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
+                       FLAC_plugin__canonical_tag_clear(&data->tag);\r
+                       LocalFree(data);\r
+                       DeinitGenres(hwnd, FALSE);\r
+               }\r
+               break;\r
+       /* commands */\r
+       case WM_COMMAND:\r
+               switch (LOWORD(wParam))\r
+               {\r
+               /* ok/cancel */\r
+               case IDOK:\r
+               case IDCANCEL:\r
+                       EndDialog(hwnd, LOWORD(wParam));\r
+                       return TRUE;\r
+               /* save */\r
+               case IDC_UPDATE:\r
+                       UpdateTag(hwnd);\r
+                       break;\r
+               /* remove */\r
+               case IDC_REMOVE:\r
+                       RemoveTag(hwnd);\r
+                       break;\r
+               }\r
+               break;\r
+       }\r
+\r
+       return 0;\r
 }\r
 \r
+/*\r
+ *  Helpers\r
+ */\r
+\r
+ULONGLONG FileSize(const char *fileName)\r
+{\r
+       LARGE_INTEGER res;\r
+       HANDLE hFile = CreateFile(fileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
+\r
+       if (hFile == INVALID_HANDLE_VALUE) return 0;\r
+       res.LowPart = GetFileSize(hFile, &res.HighPart);\r
+       CloseHandle(hFile);\r
+       return res.QuadPart;\r
+}\r
+\r
+static __inline char *GetFileName(const char *fullname)\r
+{\r
+       const char *c = fullname + strlen(fullname) - 1;\r
+\r
+       while (c > fullname)\r
+       {\r
+               if (*c=='\\' || *c=='/')\r
+               {\r
+                       c++;\r
+                       break;\r
+               }\r
+               c--;\r
+       }\r
+\r
+       return (char*)c;\r
+}\r
+\r
+void ReadTags(const char *fileName, FLAC_Plugin__CanonicalTag *tag, BOOL forDisplay)\r
+{\r
+       FLAC_plugin__canonical_tag_init(tag);\r
+       FLAC_plugin__vorbiscomment_get(fileName, tag, forDisplay ? flac_cfg.title.sep : NULL);\r
+\r
+       if (flac_cfg.title.read_v1)\r
+               FLAC_plugin__canonical_tag_add_id3v1(fileName, tag);\r
+\r
+       /* add file name */\r
+       if (forDisplay)\r
+       {\r
+               char *c;\r
+               FLAC_plugin__canonical_set_ansi(tag, L"filepath", fileName);\r
+\r
+               strcpy(buffer, GetFileName(fileName));\r
+               if (c = strrchr(buffer, '.')) *c = 0;\r
+               FLAC_plugin__canonical_set_ansi(tag, L"filename", buffer);\r
+       }\r
+}\r
+\r
+/*\r
+ *  Front-end\r
+ */\r
+\r
+void InitInfobox()\r
+{\r
+       isNT = !(GetVersion() & 0x80000000);\r
+}\r
+\r
+void DeinitInfobox()\r
+{\r
+       DeinitGenres(NULL, true);\r
+}\r
 \r
 void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename)\r
 {\r
-    DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);\r
+       DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);\r
 }\r
index 2d562e0..dfd7608 100644 (file)
@@ -1,5 +1,28 @@
-//\r
-//  prototypes\r
-//\r
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
 \r
+/*\r
+ *  prototypes\r
+ */\r
+\r
+ULONGLONG FileSize(const char *fileName);\r
+void ReadTags(const char *fileName, FLAC_Plugin__CanonicalTag *tag, BOOL forDisplay);\r
+\r
+void InitInfobox();\r
+void DeinitInfobox();\r
 void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename);\r
diff --git a/src/plugin_winamp2/playback.c b/src/plugin_winamp2/playback.c
new file mode 100644 (file)
index 0000000..992f84e
--- /dev/null
@@ -0,0 +1,292 @@
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2000,2001,2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include "playback.h"\r
+#include "share/grabbag.h"\r
+\r
+\r
+static FLAC__int32 reservoir_[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS][FLAC__MAX_BLOCK_SIZE * 2/*for overflow*/];\r
+static FLAC__int32 *reservoir__[FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS] = { reservoir_[0], reservoir_[1] }; /*@@@ kind of a hard-coded hack */\r
+static unsigned wide_samples_in_reservoir_;\r
+static output_config_t cfg;     /* local copy */\r
+\r
+static unsigned bh_index_last_w, bh_index_last_o, written_time_last;\r
+static FLAC__int64 decode_position, decode_position_last;\r
+\r
+/*\r
+ *  callbacks\r
+ */\r
+\r
+static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)\r
+{\r
+       file_info_struct *file_info = (file_info_struct*)client_data;\r
+       const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize;\r
+       unsigned channel;\r
+\r
+       (void)decoder;\r
+\r
+       if (file_info->abort_flag)\r
+               return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;\r
+\r
+       for (channel = 0; channel < channels; channel++)\r
+               memcpy(&reservoir_[channel][wide_samples_in_reservoir_], buffer[channel], sizeof(buffer[0][0]) * wide_samples);\r
+\r
+       wide_samples_in_reservoir_ += wide_samples;\r
+\r
+       return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;\r
+}\r
+\r
+static void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)\r
+{\r
+       file_info_struct *file_info = (file_info_struct*)client_data;\r
+       (void)decoder;\r
+\r
+       if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)\r
+       {\r
+               FLAC__ASSERT(metadata->data.stream_info.total_samples < 0x100000000); /* this plugin can only handle < 4 gigasamples */\r
+               file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xfffffffful);\r
+               file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample;\r
+               file_info->channels = metadata->data.stream_info.channels;\r
+               file_info->sample_rate = metadata->data.stream_info.sample_rate;\r
+\r
+               if (file_info->bits_per_sample!=8 && file_info->bits_per_sample!=16 && file_info->bits_per_sample!=24)\r
+               {\r
+                       FLAC_plugin__show_error("This plugin can only handle 8/16/24-bit samples.");\r
+                       file_info->abort_flag = true;\r
+                       return;\r
+               }\r
+               file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100);\r
+       }\r
+       else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)\r
+       {\r
+               double gain, peak;\r
+               if (grabbag__replaygain_load_from_vorbiscomment(metadata, cfg.replaygain.album_mode, &gain, &peak))\r
+               {\r
+                       file_info->has_replaygain = true;\r
+                       file_info->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)cfg.replaygain.preamp, !cfg.replaygain.hard_limit);\r
+               }\r
+       }\r
+}\r
+\r
+static void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)\r
+{\r
+       file_info_struct *file_info = (file_info_struct*)client_data;\r
+       (void)decoder;\r
+\r
+       if (cfg.misk.stop_err || status!=FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)\r
+               file_info->abort_flag = true;\r
+}\r
+\r
+/*\r
+ *  init/delete\r
+ */\r
+\r
+FLAC__bool FLAC_plugin__decoder_init(FLAC__FileDecoder *decoder, const char *filename, FLAC__int64 filesize, file_info_struct *file_info, output_config_t *config)\r
+{\r
+       FLAC__ASSERT(decoder);\r
+       FLAC_plugin__decoder_finish(decoder);\r
+       /* init decoder */\r
+       FLAC__file_decoder_set_md5_checking(decoder, false);\r
+       FLAC__file_decoder_set_filename(decoder, filename);\r
+       FLAC__file_decoder_set_metadata_ignore_all(decoder);\r
+       FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);\r
+       FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);\r
+       FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback);\r
+       FLAC__file_decoder_set_write_callback(decoder, write_callback);\r
+       FLAC__file_decoder_set_error_callback(decoder, error_callback);\r
+       FLAC__file_decoder_set_client_data(decoder, file_info);\r
+\r
+       if (FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK)\r
+       {\r
+               FLAC_plugin__show_error("Error while initializing decoder (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);\r
+               return false;\r
+       }\r
+       /* process */\r
+       cfg = *config;\r
+       wide_samples_in_reservoir_ = 0;\r
+       file_info->is_playing = false;\r
+       file_info->abort_flag = false;\r
+       file_info->has_replaygain = false;\r
+\r
+       if (!FLAC__file_decoder_process_until_end_of_metadata(decoder))\r
+       {\r
+               FLAC_plugin__show_error("Error while processing metadata (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);\r
+               return false;\r
+       }\r
+       /* check results */\r
+       if (file_info->abort_flag) return false;                /* metadata callback already popped up the error dialog */\r
+       /* init replaygain */\r
+       file_info->output_bits_per_sample = file_info->has_replaygain && cfg.replaygain.enable ?\r
+               cfg.resolution.replaygain.bps_out :\r
+               cfg.resolution.normal.dither_24_to_16 ? min(file_info->bits_per_sample, 16) : file_info->bits_per_sample;\r
+\r
+       if (file_info->has_replaygain && cfg.replaygain.enable && cfg.resolution.replaygain.dither)\r
+               FLAC__replaygain_synthesis__init_dither_context(&file_info->dither_context, file_info->bits_per_sample, cfg.resolution.replaygain.noise_shaping);\r
+       /* more inits */\r
+       file_info->eof = false;\r
+       file_info->seek_to = -1;\r
+       file_info->is_playing = true;\r
+       file_info->average_bps = (unsigned)(filesize / (125.*file_info->total_samples/file_info->sample_rate));\r
+       \r
+       bh_index_last_w = 0;\r
+       bh_index_last_o = BITRATE_HIST_SIZE;\r
+       decode_position = 0;\r
+       decode_position_last = 0;\r
+       written_time_last = 0;\r
+\r
+       return true;\r
+}\r
+\r
+void FLAC_plugin__decoder_finish(FLAC__FileDecoder *decoder)\r
+{\r
+       if (decoder && FLAC__file_decoder_get_state(decoder)!=FLAC__FILE_DECODER_UNINITIALIZED)\r
+               FLAC__file_decoder_finish(decoder);\r
+}\r
+\r
+void FLAC_plugin__decoder_delete(FLAC__FileDecoder *decoder)\r
+{\r
+       if (decoder)\r
+       {\r
+               FLAC_plugin__decoder_finish(decoder);\r
+               FLAC__file_decoder_delete(decoder);\r
+       }\r
+}\r
+\r
+/*\r
+ *  decode\r
+ */\r
+\r
+int FLAC_plugin__seek(FLAC__FileDecoder *decoder, file_info_struct *file_info)\r
+{\r
+       int pos;\r
+       const FLAC__uint64 target_sample =\r
+               (FLAC__uint64)file_info->total_samples*file_info->seek_to / file_info->length_in_msec;\r
+\r
+       if (!FLAC__file_decoder_seek_absolute(decoder, target_sample))\r
+               return -1;\r
+\r
+       file_info->seek_to = -1;\r
+       file_info->eof = false;\r
+       wide_samples_in_reservoir_ = 0;\r
+       pos = (int)(target_sample*1000 / file_info->sample_rate);\r
+\r
+       bh_index_last_o = bh_index_last_w = (pos/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE;\r
+       if (!FLAC__file_decoder_get_decode_position(decoder, &decode_position))\r
+               decode_position = 0;\r
+\r
+       return pos;\r
+}\r
+\r
+unsigned FLAC_plugin__decode(FLAC__FileDecoder *decoder, file_info_struct *file_info, char *sample_buffer)\r
+{\r
+       /* fill reservoir */\r
+       while (wide_samples_in_reservoir_ < SAMPLES_PER_WRITE)\r
+       {\r
+               if (FLAC__file_decoder_get_state(decoder) == FLAC__FILE_DECODER_END_OF_FILE)\r
+               {\r
+                       file_info->eof = true;\r
+                       break;\r
+               }\r
+               else if (!FLAC__file_decoder_process_single(decoder))\r
+               {\r
+                       FLAC_plugin__show_error("Error while processing frame (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);\r
+                       file_info->eof = true;\r
+                       break;\r
+               }\r
+               if (!FLAC__file_decoder_get_decode_position(decoder, &decode_position))\r
+                       decode_position = 0;\r
+       }\r
+       /* output samples */\r
+       if (wide_samples_in_reservoir_ > 0)\r
+       {\r
+               const unsigned n = min(wide_samples_in_reservoir_, SAMPLES_PER_WRITE);\r
+               const unsigned channels = file_info->channels;\r
+               unsigned i;\r
+               int bytes;\r
+\r
+               if (cfg.replaygain.enable && file_info->has_replaygain)\r
+               {\r
+                       bytes = FLAC__replaygain_synthesis__apply_gain(\r
+                               sample_buffer,\r
+                               true, /* little_endian_data_out */\r
+                               file_info->output_bits_per_sample == 8, /* unsigned_data_out */\r
+                               reservoir__,\r
+                               n,\r
+                               channels,\r
+                               file_info->bits_per_sample,\r
+                               file_info->output_bits_per_sample,\r
+                               file_info->replay_scale,\r
+                               cfg.replaygain.hard_limit,\r
+                               cfg.resolution.replaygain.dither,\r
+                               &file_info->dither_context\r
+                       );\r
+               }\r
+               else\r
+               {\r
+                       bytes = FLAC__plugin_common__pack_pcm_signed_little_endian(\r
+                               sample_buffer,\r
+                               reservoir__,\r
+                               n,\r
+                               channels,\r
+                               file_info->bits_per_sample,\r
+                               file_info->output_bits_per_sample\r
+                       );\r
+               }\r
+\r
+               wide_samples_in_reservoir_ -= n;\r
+               for (i = 0; i < channels; i++)\r
+                       memmove(&reservoir_[i][0], &reservoir_[i][n], sizeof(reservoir_[0][0]) * wide_samples_in_reservoir_);\r
+\r
+               return bytes;\r
+       }\r
+       else\r
+       {\r
+               file_info->eof = true;\r
+               return 0;\r
+       }\r
+}\r
+\r
+int FLAC_plugin__get_rate(unsigned written_time, unsigned output_time, file_info_struct *file_info)\r
+{\r
+       static int bitrate_history_[BITRATE_HIST_SIZE];\r
+       unsigned bh_index_w = (written_time/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE;\r
+       unsigned bh_index_o = (output_time/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE;\r
+\r
+       /* written bitrate */\r
+       if (bh_index_w != bh_index_last_w)\r
+       {\r
+               bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE-1)%BITRATE_HIST_SIZE] =\r
+                       decode_position>decode_position_last && written_time > written_time_last ?\r
+                       (unsigned)(8000*(decode_position - decode_position_last)/(written_time - written_time_last)) :\r
+                       file_info->average_bps;\r
+\r
+               bh_index_last_w = bh_index_w;\r
+               written_time_last = written_time;\r
+               decode_position_last = decode_position;\r
+       }\r
+\r
+       /* output bitrate */\r
+       if (bh_index_o!=bh_index_last_o && bh_index_o!=bh_index_last_w)\r
+       {\r
+               bh_index_last_o = bh_index_o;\r
+               return bitrate_history_[bh_index_o];\r
+       }\r
+\r
+       return 0;\r
+}\r
diff --git a/src/plugin_winamp2/playback.h b/src/plugin_winamp2/playback.h
new file mode 100644 (file)
index 0000000..b775876
--- /dev/null
@@ -0,0 +1,92 @@
+/* in_flac - Winamp2 FLAC input plugin\r
+ * Copyright (C) 2000,2001,2002,2003,2004  Josh Coalson\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+ */\r
+\r
+#include "FLAC/all.h"\r
+#include "share/replaygain_synthesis.h"\r
+#include "plugin_common/all.h"\r
+\r
+/*\r
+ *  constants\r
+ */\r
+\r
+#define SAMPLES_PER_WRITE           576\r
+\r
+#define BITRATE_HIST_SEGMENT_MSEC   500\r
+#define BITRATE_HIST_SIZE           64\r
+\r
+/*\r
+ *  common structures\r
+ */\r
+\r
+typedef struct {\r
+       volatile FLAC__bool is_playing;\r
+       volatile FLAC__bool abort_flag;\r
+       volatile FLAC__bool eof;\r
+       volatile int seek_to;\r
+       unsigned total_samples;\r
+       unsigned bits_per_sample;\r
+       unsigned output_bits_per_sample;\r
+       unsigned channels;\r
+       unsigned sample_rate;\r
+       unsigned length_in_msec;\r
+       unsigned average_bps;\r
+       FLAC__bool has_replaygain;\r
+       double replay_scale;\r
+       DitherContext dither_context;\r
+} file_info_struct;\r
+\r
+\r
+typedef struct {\r
+       struct {\r
+               FLAC__bool enable;\r
+               FLAC__bool album_mode;\r
+               int  preamp;\r
+               FLAC__bool hard_limit;\r
+       } replaygain;\r
+       struct {\r
+               struct {\r
+                       FLAC__bool dither_24_to_16;\r
+               } normal;\r
+               struct {\r
+                       FLAC__bool dither;\r
+                       int  noise_shaping; /* value must be one of NoiseShaping enum, see plugin_common/replaygain_synthesis.h */\r
+                       int  bps_out;\r
+               } replaygain;\r
+       } resolution;\r
+       struct {\r
+               FLAC__bool stop_err;\r
+       } misk;\r
+} output_config_t;\r
+\r
+/*\r
+ *  protopytes\r
+ */\r
+\r
+FLAC__bool FLAC_plugin__decoder_init(FLAC__FileDecoder *decoder, const char *filename, FLAC__int64 filesize, file_info_struct *file_info, output_config_t *config);\r
+void FLAC_plugin__decoder_finish(FLAC__FileDecoder *decoder);\r
+void FLAC_plugin__decoder_delete(FLAC__FileDecoder *decoder);\r
+\r
+int FLAC_plugin__seek(FLAC__FileDecoder *decoder, file_info_struct *file_info);\r
+unsigned FLAC_plugin__decode(FLAC__FileDecoder *decoder, file_info_struct *file_info, char *sample_buffer);\r
+int FLAC_plugin__get_rate(unsigned written_time, unsigned output_time, file_info_struct *file_info);\r
+\r
+/*\r
+ *  these should be defined in plug-in\r
+ */\r
+\r
+extern void FLAC_plugin__show_error(const char *message,...);\r
index f13f3bd..f09b4af 100644 (file)
 #define IDC_TITLE                       1010\r
 #define IDC_TAGZ_HELP                   1011\r
 #define IDC_ARTIST                      1011\r
+#define IDC_TAGZ_DEFAULT                1012\r
+#define IDC_SEP                         1013\r
 #define IDC_NAME                        1014\r
 #define IDC_INFO                        1015\r
 #define IDC_GENRE                       1017\r
 #define IDC_REMOVE                      1020\r
 #define IDC_UPDATE                      1021\r
+#define IDC_ID3V1                       1030\r
+#define IDC_RESERVE                     1032\r
+#define IDC_BPS                         1036\r
+#define IDC_ERRORS                      1037\r
 \r
 // Next default values for new objects\r
 // \r
@@ -35,7 +41,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS\r
 #define _APS_NEXT_RESOURCE_VALUE        106\r
 #define _APS_NEXT_COMMAND_VALUE         40001\r
-#define _APS_NEXT_CONTROL_VALUE         1030\r
+#define _APS_NEXT_CONTROL_VALUE         1037\r
 #define _APS_NEXT_SYMED_VALUE           101\r
 #endif\r
 #endif\r
index 6300877..a76457b 100644 (file)
@@ -67,7 +67,7 @@ BEGIN
     EDITTEXT        IDC_ALBUM,43,60,137,12,ES_AUTOHSCROLL\r
     RTEXT           "&Comment",IDC_STATIC,8,77,31,8\r
     EDITTEXT        IDC_COMMENT,43,75,137,12,ES_AUTOHSCROLL\r
-    RTEXT           "&Year",IDC_STATIC,8,92,31,8\r
+    RTEXT           "&Date",IDC_STATIC,8,92,31,8\r
     EDITTEXT        IDC_YEAR,43,90,40,12,ES_AUTOHSCROLL\r
     RTEXT           "Track &number",IDC_STATIC,90,92,46,8\r
     EDITTEXT        IDC_TRACK,141,90,39,12,ES_AUTOHSCROLL\r
@@ -133,10 +133,23 @@ IDD_CONFIG_GENERAL DIALOG DISCARDABLE  0, 0, 226, 171
 STYLE DS_CONTROL | WS_CHILD\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    GROUPBOX        " Title Formatting  ",IDC_STATIC,2,2,220,43\r
-    LTEXT           "&Title:",IDC_STATIC,8,17,16,8\r
+    GROUPBOX        " Title Formatting  ",IDC_STATIC,2,2,220,58\r
+    LTEXT           "&Title",IDC_STATIC,8,17,14,8\r
     EDITTEXT        IDC_TITLE,27,15,188,12,ES_AUTOHSCROLL\r
+    PUSHBUTTON      "default",IDC_TAGZ_DEFAULT,156,28,30,10\r
     PUSHBUTTON      "help",IDC_TAGZ_HELP,188,28,27,10\r
+    LTEXT           "Separate tag values &with",IDC_STATIC,8,43,79,8\r
+    EDITTEXT        IDC_SEP,91,41,27,12,ES_AUTOHSCROLL\r
+    CONTROL         "Read ID3v&1 tags",IDC_ID3V1,"Button",BS_AUTOCHECKBOX | \r
+                    WS_TABSTOP,147,43,70,10\r
+    GROUPBOX        " Tag Editor  ",IDC_STATIC,2,63,220,30\r
+    CONTROL         "Reserve space for &VorbisComment",IDC_RESERVE,"Button",\r
+                    BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,8,76,125,10\r
+    GROUPBOX        " Misk  ",IDC_STATIC,2,96,220,72\r
+    CONTROL         "&Show average bitrate while playing",IDC_BPS,"Button",\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,8,108,125,10\r
+    CONTROL         "Stop on &all errors",IDC_ERRORS,"Button",\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,8,120,69,10\r
 END\r
 \r
 IDD_CONFIG_OUTPUT DIALOG DISCARDABLE  0, 0, 224, 171\r
@@ -159,11 +172,11 @@ BEGIN
     CONTROL         "&Dither 24bps to 16bps",IDC_DITHER,"Button",\r
                     BS_AUTOCHECKBOX | WS_TABSTOP,16,84,85,10\r
     GROUPBOX        " With ReplayGain ",IDC_STATIC,7,104,209,47\r
-    CONTROL         "E&nable dithering",IDC_DITHERRG,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,134,67,10\r
     LTEXT           "&Output bit depth",IDC_STATIC,16,119,52,8\r
     COMBOBOX        IDC_TO,71,116,39,43,CBS_DROPDOWNLIST | WS_VSCROLL | \r
                     WS_TABSTOP\r
+    CONTROL         "E&nable dithering",IDC_DITHERRG,"Button",\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,16,134,67,10\r
     LTEXT           "Noise &shaping",IDC_STATIC,113,135,46,8\r
     COMBOBOX        IDC_SHAPE,164,132,46,48,CBS_DROPDOWNLIST | WS_VSCROLL | \r
                     WS_TABSTOP\r
index ebece85..89e64a3 100644 (file)
@@ -125,7 +125,7 @@ static char roman_num[]=
 };\r
 \r
 \r
-static int is_roman(T_CHAR * ptr)//could be more smart i think\r
+static int is_roman(T_CHAR * ptr)/* could be more smart i think */\r
 {\r
        if (ptr[0]==']' && ptr[1]=='[' && separator(ptr[2])) return 1;\r
        while(!separator(*ptr))\r
@@ -164,7 +164,7 @@ MAKEFUNC(If)
        if (n_src!=3) return false;\r
 \r
        out.AddString(src[found_src[0] ? 1 : 2]);\r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(If2)\r
@@ -172,7 +172,7 @@ MAKEFUNC(If2)
        if (n_src!=2) return false;\r
 \r
        out.AddString(src[found_src[0] ? 0 : 1]);\r
-    return true;\r
+       return true;\r
 }\r
 \r
 \r
@@ -181,7 +181,7 @@ MAKEFUNC(Iflonger)
        if (n_src!=4) return false;\r
 \r
        out.AddString(src[(int)t_strlen(src[0])>t_atoi(src[1]) ? 2 : 3]);\r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Ifgreater)\r
@@ -189,7 +189,7 @@ MAKEFUNC(Ifgreater)
        if (n_src!=4) return false;\r
 \r
        out.AddString(src[t_atoi(src[0])>t_atoi(src[1]) ? 2 : 3]);\r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Upper)\r
@@ -201,7 +201,7 @@ MAKEFUNC(Upper)
        while(*s)\r
                out.AddChar(toupper(*(s++)));\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Lower)\r
@@ -213,7 +213,7 @@ MAKEFUNC(Lower)
        while(*s)\r
                out.AddChar(tolower(*(s++)));\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Pad)\r
@@ -222,18 +222,18 @@ MAKEFUNC(Pad)
 \r
        T_CHAR *fill=_TX(" ");\r
        if (n_src==3 && src[2][0])\r
-        fill = src[2];\r
+               fill = src[2];\r
 \r
        int num = t_atoi(src[1]);\r
        T_CHAR *p = src[0];\r
 \r
-    while (*p) { out.AddChar(*(p++)); num--; }\r
+       while (*p) { out.AddChar(*(p++)); num--; }\r
 \r
        UINT fl = t_strlen(fill);\r
        while (num>0)\r
-        out.AddChar(fill[(--num)%fl]);\r
+               out.AddChar(fill[(--num)%fl]);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Cut)\r
@@ -245,7 +245,7 @@ MAKEFUNC(Cut)
 \r
        while (*p && num>0) {out.AddChar(*(p++));num--;}\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(PadCut)\r
@@ -254,7 +254,7 @@ MAKEFUNC(PadCut)
 \r
        T_CHAR *fill = _TX(" ");\r
        if (n_src==3 && src[2][0])\r
-        fill = src[2];\r
+               fill = src[2];\r
 \r
        int num = t_atoi(src[1]);\r
        T_CHAR *p = src[0];\r
@@ -263,13 +263,13 @@ MAKEFUNC(PadCut)
 \r
        UINT fl=t_strlen(fill);\r
        while (num>0)\r
-        out.AddChar(fill[(--num)%fl]);\r
+               out.AddChar(fill[(--num)%fl]);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
-// abbr(string)\r
-// abbr(string,len)\r
+/* abbr(string) */\r
+/* abbr(string,len) */\r
 MAKEFUNC(Abbr)\r
 {\r
        if (n_src==0 || n_src>2) return false;\r
@@ -301,7 +301,7 @@ MAKEFUNC(Abbr)
                meta++;\r
        }\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 \r
@@ -324,7 +324,7 @@ MAKEFUNC(Caps)
                out.AddChar(c);\r
        }\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Caps2)\r
@@ -344,7 +344,7 @@ MAKEFUNC(Caps2)
                out.AddChar(c);\r
        }\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Longest)\r
@@ -359,7 +359,7 @@ MAKEFUNC(Longest)
        }\r
 \r
        if (ptr) out.AddString(ptr);\r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Shortest)\r
@@ -374,7 +374,7 @@ MAKEFUNC(Shortest)
        }\r
 \r
        if (ptr) out.AddString(ptr);\r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Num)\r
@@ -387,20 +387,20 @@ MAKEFUNC(Num)
        sprintf(tmp,tmp1,t_atoi(src[0]));\r
        out.AddString(tmp);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Hex)\r
 {\r
        if (n_src!=2) return false;\r
 \r
-    T_CHAR tmp[16];\r
+       T_CHAR tmp[16];\r
        T_CHAR tmp1[16];\r
        sprintf(tmp1,_TX("%%0%ux"),t_atoi(src[1]));\r
        sprintf(tmp,tmp1,t_atoi(src[0]));\r
        out.AddString(tmp);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(StrChr)\r
@@ -412,10 +412,10 @@ MAKEFUNC(StrChr)
 \r
        while (*p && *p!=s) p++;\r
        if (*p==s)\r
-        out.AddInt(1+p-src[0]);\r
+               out.AddInt(1+p-src[0]);\r
        else out.AddChar('0');\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(StrRChr)\r
@@ -432,10 +432,10 @@ MAKEFUNC(StrRChr)
        }\r
 \r
        if (p1)\r
-        out.AddInt(1+p1-src[0]);\r
+               out.AddInt(1+p1-src[0]);\r
        else out.AddChar('0');\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(StrStr)\r
@@ -445,14 +445,14 @@ MAKEFUNC(StrStr)
        T_CHAR * p = t_strstr(src[0],src[1]);\r
 \r
        if (p)\r
-        out.AddInt(1+p-src[0]);\r
+               out.AddInt(1+p-src[0]);\r
        else out.AddChar('0');\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
-// substr(string, index)\r
-// substr(string, index, length)\r
+/* substr(string, index) */\r
+/* substr(string, index, length) */\r
 MAKEFUNC(SubStr)\r
 {\r
        if (n_src<2 || n_src>3) return false;\r
@@ -472,15 +472,15 @@ MAKEFUNC(SubStr)
                        out.AddChar(src[0][n1++]);\r
        }\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Len)\r
 {\r
        if (n_src!=1) return false;\r
 \r
-    out.AddInt(t_strlen(src[0]));\r
-    return true;\r
+       out.AddInt(t_strlen(src[0]));\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Add)\r
@@ -493,7 +493,7 @@ MAKEFUNC(Add)
 \r
        out.AddInt(s);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Sub)\r
@@ -508,7 +508,7 @@ MAKEFUNC(Sub)
 \r
        out.AddInt(s);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Mul)\r
@@ -521,7 +521,7 @@ MAKEFUNC(Mul)
 \r
        out.AddInt(s);\r
 \r
-    return true;\r
+       return true;\r
 }\r
                                \r
 MAKEFUNC(Div)\r
@@ -540,7 +540,7 @@ MAKEFUNC(Div)
 \r
        out.AddInt(s);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Mod)\r
@@ -559,7 +559,7 @@ MAKEFUNC(Mod)
 \r
        out.AddInt(s);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Max)\r
@@ -576,7 +576,7 @@ MAKEFUNC(Max)
        }\r
        out.AddInt(m);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 MAKEFUNC(Min)\r
@@ -593,10 +593,10 @@ MAKEFUNC(Min)
        }\r
        out.AddInt(m);\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
-// replace(string, what_to_replace, replacement)\r
+/* replace(string, what_to_replace, replacement) */\r
 MAKEFUNC(Replace)\r
 {\r
        if (n_src!=3) return false;\r
@@ -616,7 +616,7 @@ MAKEFUNC(Replace)
                else out.AddChar(*p++);\r
        }\r
 \r
-    return true;\r
+       return true;\r
 }\r
 \r
 struct\r
@@ -671,11 +671,11 @@ private:
        int found;\r
 \r
        void Error(T_CHAR *e=0)\r
-    {\r
-        str.Reset();\r
-        str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]"));\r
-        found++;  // force displaying\r
-    }\r
+       {\r
+               str.Reset();\r
+               str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]"));\r
+               found++;  /* force displaying */\r
+       }\r
 \r
        T_CHAR * _FMT(T_CHAR * s,UINT *f=0)\r
        {\r
@@ -739,7 +739,7 @@ private:
                                *s1=0;\r
                                const T_CHAR * tag=f(spec,fp);\r
                                *s1='%';\r
-                               //if (!tag) tag=tag_unknown;\r
+                               /*if (!tag) tag=tag_unknown; */\r
                                if (tag && tag[0])\r
                                {\r
                                        found++;\r
@@ -784,25 +784,25 @@ private:
 \r
                                for (n=0; n<TABSIZE(FUNCS); n++)\r
                                        if (!t_stricmp(spec, FUNCS[n].name))\r
-                        break;\r
+                                               break;\r
 \r
                                *s1='(';\r
 \r
                                if (n != TABSIZE(FUNCS))\r
                                {\r
                                        if (!FUNCS[n].func(nt, temp, temp_f, str))\r
-                    {\r
-                        Error(_TX("[INVALID $"));\r
-                        str.AddString(FUNCS[n].name);\r
-                        str.AddString(_TX(" SYNTAX]"));\r
-                        return;\r
-                    }\r
+                                       {\r
+                                               Error(_TX("[INVALID $"));\r
+                                               str.AddString(FUNCS[n].name);\r
+                                               str.AddString(_TX(" SYNTAX]"));\r
+                                               return;\r
+                                       }\r
                                }\r
                                else\r
-                {\r
-                    Error(_TX("[UNKNOWN FUNCTION]"));\r
-                    return;\r
-                }\r
+                               {\r
+                                       Error(_TX("[UNKNOWN FUNCTION]"));\r
+                                       return;\r
+                               }\r
 \r
                                for(n=0;n<nt;n++) free(temp[n]);\r
                                spec=s2+1;\r
@@ -897,8 +897,6 @@ T_CHAR * tagz_format_r(const T_CHAR* spec,TAGFUNC f,TAGFREEFUNC ff,void * fp)
        return FMT(spec,f,ff,fp);\r
 }\r
 \r
-//char tagz_manual[]="TODO: WTFM";\r
-\r
 const char tagz_manual[]="Syntax reference: \n"\r
        "\n"\r
        "* %tagname% - inserts field named <tagname>, eg. \"%artist%\"\n"\r
index b7a45c4..6b0d635 100644 (file)
@@ -14,7 +14,7 @@ typedef unsigned short T_CHAR;
 #define T_CHAR char\r
 #endif\r
 \r
-typedef const T_CHAR* (*TAGFUNC)(const T_CHAR *tag,void *p);    // return 0 if not found\r
+typedef const T_CHAR* (*TAGFUNC)(const T_CHAR *tag,void *p);    /* return 0 if not found */\r
 typedef void (*TAGFREEFUNC)(const T_CHAR *tag,void *p);\r
 \r
 \r
index ad11d74..3caf75a 100644 (file)
@@ -135,36 +135,14 @@ const CharsetInfo charset_trans_array[] = {
 /*
  * Commons conversion functions
  */
-char *convert_from_file_to_user(const char *string)
+char *convert_from_utf8_to_user(const char *string)
 {
-       return FLAC_plugin__charset_convert_string(string, flac_cfg.title.file_char_set, flac_cfg.title.user_char_set);
+       return FLAC_plugin__charset_convert_string(string, "UTF-8", flac_cfg.title.user_char_set);
 }
 
-char *convert_from_user_to_file(const char *string)
+char *convert_from_user_to_utf8(const char *string)
 {
-       return FLAC_plugin__charset_convert_string(string, flac_cfg.title.user_char_set, flac_cfg.title.file_char_set);
-}
-
-void convert_from_file_to_user_in_place(char **string)
-{
-       if(0 != *string) {
-               char *tmp;
-
-               tmp = convert_from_file_to_user(*string);
-               free(*string);
-               *string = tmp;
-       }
-}
-
-void convert_from_user_to_file_in_place(char **string)
-{
-       if(0 != *string) {
-               char *tmp;
-
-               tmp = convert_from_user_to_file(*string);
-               free(*string);
-               *string = tmp;
-       }
+       return FLAC_plugin__charset_convert_string(string, flac_cfg.title.user_char_set, "UTF-8");
 }
 
 GList *Charset_Create_List (void)
index 041f29f..7d5e763 100644 (file)
@@ -41,10 +41,11 @@ extern const CharsetInfo charset_trans_array[];
  * Prototypes *
  **************/
 
-char *convert_from_file_to_user(const char *string);
-char *convert_from_user_to_file(const char *string);
-void convert_from_file_to_user_in_place(char **string);
-void convert_from_user_to_file_in_place(char **string);
+/*
+ * 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);
 gchar *Charset_Get_Name_From_Title (gchar *charset_title);
index 472f402..e1ce15a 100644 (file)
@@ -48,7 +48,6 @@ flac_config_t flac_cfg = {
                FALSE, /* tag_override */
                NULL, /* tag_format */
                FALSE, /* convert_char_set */
-               NULL, /* file_char_set */
                NULL /* user_char_set */
        },
        /* output */
@@ -108,7 +107,6 @@ static void flac_configurewin_ok(GtkWidget * widget, gpointer data)
        (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.file_char_set = Charset_Get_Name_From_Title(gtk_entry_get_text_1(fileCharacterSetEntry));
        flac_cfg.title.user_char_set = Charset_Get_Name_From_Title(gtk_entry_get_text_1(userCharacterSetEntry));
 
        filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
@@ -119,7 +117,6 @@ static void flac_configurewin_ok(GtkWidget * widget, gpointer data)
        xmms_cfg_write_boolean(cfg, "flac", "title.tag_override", flac_cfg.title.tag_override);
        xmms_cfg_write_string(cfg, "flac", "title.tag_format", flac_cfg.title.tag_format);
        xmms_cfg_write_boolean(cfg, "flac", "title.convert_char_set", flac_cfg.title.convert_char_set);
-       xmms_cfg_write_string(cfg, "flac", "title.file_char_set", flac_cfg.title.file_char_set);
        xmms_cfg_write_string(cfg, "flac", "title.user_char_set", flac_cfg.title.user_char_set);
        /* output */
        xmms_cfg_write_boolean(cfg, "flac", "output.replaygain.enable", flac_cfg.output.replaygain.enable);
@@ -293,7 +290,6 @@ void FLAC_XMMS__configure(void)
        list = Charset_Create_List();
        gtk_combo_set_popdown_strings(GTK_COMBO(fileCharacterSetEntry),list);
        gtk_combo_set_popdown_strings(GTK_COMBO(userCharacterSetEntry),list);
-       gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(fileCharacterSetEntry)->entry),Charset_Get_Title_From_Name(flac_cfg.title.file_char_set));
        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, flac_cfg.title.convert_char_set);
        gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set);
index f49dea4..7bf5511 100644 (file)
@@ -28,7 +28,6 @@ typedef struct {
                gboolean tag_override;
                gchar *tag_format;
                gboolean convert_char_set;
-               gchar *file_char_set;
                gchar *user_char_set;
        } title;
 
index 547a8a1..22de4a5 100644 (file)
@@ -88,12 +88,6 @@ static const gchar *vorbis_genres[] =
        N_("Anime"), N_("JPop"), N_("Synthpop")
 };
 
-static void local__safe_free(void *object)
-{
-       if(0 != object)
-               free(object);
-}
-
 static void label_set_text(GtkWidget * label, char *str, ...)
 {
        va_list args;
@@ -107,45 +101,53 @@ static void label_set_text(GtkWidget * label, char *str, ...)
        g_free(tempstr);
 }
 
-static void set_entry_tag(GtkEntry * entry, gchar * tag)
+static void set_entry_tag(GtkEntry * entry, const wchar_t * tag)
 {
-       char *text;
 
        if(tag) {
+               char *utf8 = FLAC_plugin__convert_ucs2_to_utf8(tag);
                if(flac_cfg.title.convert_char_set) {
-                       text = convert_from_file_to_user(tag);
+                       char *text = convert_from_utf8_to_user(utf8);
                        gtk_entry_set_text(entry, text);
                        free(text);
                }
-               else
-                       gtk_entry_set_text(entry, tag);
+               else {
+                       gtk_entry_set_text(entry, utf8);
+               }
+               free(utf8);
        }
        else
                gtk_entry_set_text(entry, "");
 }
 
-static char *get_entry_tag(GtkEntry * entry)
+static void get_entry_tag(GtkEntry * entry, const char *name)
 {
        gchar *text;
+       char *utf8;
 
        text = gtk_entry_get_text(entry);
        if (!text || strlen(text) == 0)
-               return 0;
+               return;
        if(flac_cfg.title.convert_char_set)
-               return convert_from_user_to_file(text);
+               utf8 = convert_from_user_to_utf8(text);
        else
-               return strdup(text);
+               utf8 = text;
+
+       FLAC_plugin__canonical_add_utf8(canonical_tag, name, utf8, (unsigned)(-1), (unsigned)(-1), /*sep=*/0);
+
+       if(flac_cfg.title.convert_char_set)
+               free(utf8);
 }
 
 static void show_tag()
 {
-       set_entry_tag(GTK_ENTRY(title_entry), canonical_tag->title);
-       set_entry_tag(GTK_ENTRY(artist_entry), canonical_tag->composer);
-       set_entry_tag(GTK_ENTRY(album_entry), canonical_tag->album);
-       set_entry_tag(GTK_ENTRY(date_entry), canonical_tag->year_recorded);
-       set_entry_tag(GTK_ENTRY(tracknum_entry), canonical_tag->track_number);
-       set_entry_tag(GTK_ENTRY(comment_entry), canonical_tag->comment);
-       set_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), canonical_tag->genre);
+       set_entry_tag(GTK_ENTRY(title_entry)                  , FLAC_plugin__canonical_get(canonical_tag, L"TITLE"));
+       set_entry_tag(GTK_ENTRY(artist_entry)                 , FLAC_plugin__canonical_get(canonical_tag, L"ARTIST"));
+       set_entry_tag(GTK_ENTRY(album_entry)                  , FLAC_plugin__canonical_get(canonical_tag, L"ALBUM"));
+       set_entry_tag(GTK_ENTRY(date_entry)                   , FLAC_plugin__canonical_get(canonical_tag, L"DATE"));
+       set_entry_tag(GTK_ENTRY(tracknum_entry)               , FLAC_plugin__canonical_get(canonical_tag, L"TRACKNUMBER"));
+       set_entry_tag(GTK_ENTRY(comment_entry)                , FLAC_plugin__canonical_get(canonical_tag, L"DESCRIPTION"));
+       set_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), FLAC_plugin__canonical_get(canonical_tag, L"GENRE"));
 }
 
 static void save_tag(GtkWidget * w, gpointer data)
@@ -153,21 +155,22 @@ static void save_tag(GtkWidget * w, gpointer data)
        (void)w;
        (void)data;
 
-       local__safe_free(canonical_tag->title);
-       local__safe_free(canonical_tag->composer);
-       local__safe_free(canonical_tag->album);
-       local__safe_free(canonical_tag->year_recorded);
-       local__safe_free(canonical_tag->track_number);
-       local__safe_free(canonical_tag->comment);
-       local__safe_free(canonical_tag->genre);
-       canonical_tag->title = get_entry_tag(GTK_ENTRY(title_entry));
-       canonical_tag->composer = get_entry_tag(GTK_ENTRY(artist_entry));
-       canonical_tag->album = get_entry_tag(GTK_ENTRY(album_entry));
-       canonical_tag->year_recorded = get_entry_tag(GTK_ENTRY(date_entry));
-       canonical_tag->track_number = get_entry_tag(GTK_ENTRY(tracknum_entry));
-       canonical_tag->comment = get_entry_tag(GTK_ENTRY(comment_entry));
-       canonical_tag->genre = get_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry));
-       
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"TITLE")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"ARTIST")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"ALBUM")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"DATE")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"TRACKNUMBER")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"DESCRIPTION")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"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__vorbiscomment_set(current_filename, canonical_tag);
        gtk_widget_destroy(window);
 }
@@ -177,15 +180,13 @@ static void remove_tag(GtkWidget * w, gpointer data)
        (void)w;
        (void)data;
        
-       local__safe_free(canonical_tag->title);
-       local__safe_free(canonical_tag->composer);
-       local__safe_free(canonical_tag->album);
-       local__safe_free(canonical_tag->year_recorded);
-       local__safe_free(canonical_tag->track_number);
-       local__safe_free(canonical_tag->comment);
-       local__safe_free(canonical_tag->genre);
-
-       canonical_tag->title = canonical_tag->composer = canonical_tag->album = canonical_tag->year_recorded = canonical_tag->track_number = canonical_tag->comment = canonical_tag->genre = 0;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"TITLE")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"ARTIST")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"ALBUM")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"DATE")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"TRACKNUMBER")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"DESCRIPTION")) ;
+       while (FLAC_plugin__canonical_remove(canonical_tag, L"GENRE")) ;
 
        FLAC_plugin__vorbiscomment_set(current_filename, canonical_tag);
        gtk_widget_destroy(window);
@@ -419,7 +420,7 @@ void FLAC_XMMS__file_info_box(char *filename)
        else
                canonical_tag = FLAC_plugin__canonical_tag_new();
 
-       FLAC_plugin__vorbiscomment_get(current_filename, canonical_tag);
+       FLAC_plugin__vorbiscomment_get(current_filename, canonical_tag, /*sep=*/0);
 
        show_tag();
        show_file_info();
index e20b0ef..1a5652e 100644 (file)
@@ -167,9 +167,6 @@ void FLAC_XMMS__init()
 
        xmms_cfg_read_boolean(cfg, "flac", "title.convert_char_set", &flac_cfg.title.convert_char_set);
 
-       if(!xmms_cfg_read_string(cfg, "flac", "title.file_char_set", &flac_cfg.title.file_char_set))
-               flac_cfg.title.file_char_set = FLAC_plugin__charset_get_current();
-
        if(!xmms_cfg_read_string(cfg, "flac", "title.user_char_set", &flac_cfg.title.user_char_set))
                flac_cfg.title.user_char_set = FLAC_plugin__charset_get_current();
 
index b469c96..a48aaeb 100644 (file)
@@ -29,8 +29,7 @@
 #include <xmms/titlestring.h>
 
 #include "FLAC/metadata.h"
-#include "plugin_common/id3v1.h"
-#include "plugin_common/id3v2.h"
+#include "plugin_common/canonical_tag.h"
 #include "charset.h"
 #include "configure.h"
 
@@ -65,6 +64,29 @@ static int local__getnum(char* str)
        return 0;
 }
 
+static char *local__getfield(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)
+{
+       const wchar_t *ucs2 = FLAC_plugin__canonical_get(tag, name);
+       if (0 != ucs2) {
+               char *utf8 = FLAC_plugin__convert_ucs2_to_utf8(FLAC_plugin__canonical_get(tag, name));
+               if(flac_cfg.title.convert_char_set) {
+                       char *user = convert_from_utf8_to_user(utf8);
+                       free(utf8);
+                       return user;
+               }
+               else
+                       return utf8;
+       }
+       else
+               return 0;
+}
+
+static void local__safe_free(char *s)
+{
+       if (0 != s)
+               free(s);
+}
+
 /*
  * Function flac_format_song_title (tag, filename)
  *
@@ -77,35 +99,32 @@ char *flac_format_song_title(char *filename)
        char *ret = NULL;
        TitleInput *input = NULL;
        FLAC_Plugin__CanonicalTag tag;
+       char *title, *artist, *performer, *album, *date, *tracknumber, *genre, *description;
 
        FLAC_plugin__canonical_tag_init(&tag);
 
-       FLAC_plugin__canonical_tag_get_combined(filename, &tag);
-
-       if(flac_cfg.title.convert_char_set) {
-               convert_from_file_to_user_in_place(&tag.title);
-               convert_from_file_to_user_in_place(&tag.composer);
-               convert_from_file_to_user_in_place(&tag.performer);
-               convert_from_file_to_user_in_place(&tag.album);
-               convert_from_file_to_user_in_place(&tag.year_recorded);
-               convert_from_file_to_user_in_place(&tag.year_performed);
-               convert_from_file_to_user_in_place(&tag.track_number);
-               convert_from_file_to_user_in_place(&tag.tracks_in_album);
-               convert_from_file_to_user_in_place(&tag.genre);
-               convert_from_file_to_user_in_place(&tag.comment);
-       }
+       FLAC_plugin__canonical_tag_get_combined(filename, &tag, /*sep=*/0);
+
+       title       = local__getfield(&tag, L"TITLE");
+       artist      = local__getfield(&tag, L"ARTIST");
+       performer   = local__getfield(&tag, L"PERFORMER");
+       album       = local__getfield(&tag, L"ALBUM");
+       date        = local__getfield(&tag, L"DATE");
+       tracknumber = local__getfield(&tag, L"TRACKNUMBER");
+       genre       = local__getfield(&tag, L"GENRE");
+       description = local__getfield(&tag, L"DESCRIPTION");
 
        XMMS_NEW_TITLEINPUT(input);
 
-       input->performer = local__getstr(tag.performer);
+       input->performer = local__getstr(performer);
        if(!input->performer)
-               input->performer = local__getstr(tag.composer);
-       input->album_name = local__getstr(tag.album);
-       input->track_name = local__getstr(tag.title);
-       input->track_number = local__getnum(tag.track_number);
-       input->year = local__getnum(tag.year_recorded);
-       input->genre = local__getstr(tag.genre);
-       input->comment = local__getstr(tag.comment);
+               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_basename(filename);
        input->file_path = filename;
@@ -123,5 +142,13 @@ char *flac_format_song_title(char *filename)
        }
 
        FLAC_plugin__canonical_tag_clear(&tag);
+       local__safe_free(title);
+       local__safe_free(artist);
+       local__safe_free(performer);
+       local__safe_free(album);
+       local__safe_free(date);
+       local__safe_free(tracknumber);
+       local__safe_free(genre);
+       local__safe_free(description);
        return ret;
 }