From 9745f25e785ebcbc7a185282abf90bf4f64ae6a6 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Fri, 24 Sep 2004 13:57:40 +0000 Subject: [PATCH] major plugin revamp based on x-fixer's code --- build/config.mk | 2 +- configure.in | 6 + src/plugin_common/canonical_tag.c | 888 +++++++++++++---- src/plugin_common/canonical_tag.h | 173 +++- src/plugin_common/id3v1.c | 443 ++++---- src/plugin_common/id3v1.h | 18 +- src/plugin_common/id3v2.c | 69 +- src/plugin_common/id3v2.h | 35 +- src/plugin_common/plugin_common_static.dsp | 2 +- src/plugin_common/vorbiscomment.c | 290 ++---- src/plugin_common/vorbiscomment.h | 2 +- src/plugin_winamp2/config.c | 550 +++++----- src/plugin_winamp2/config.h | 61 +- src/plugin_winamp2/in_flac.c | 1053 ++++++++------------ src/plugin_winamp2/in_flac.dsp | 13 +- src/plugin_winamp2/infobox.c | 562 +++++++---- src/plugin_winamp2/infobox.h | 29 +- src/plugin_winamp2/playback.c | 292 ++++++ src/plugin_winamp2/playback.h | 92 ++ src/plugin_winamp2/resource.h | 8 +- src/plugin_winamp2/resource.rc | 23 +- src/plugin_winamp2/tagz.cpp | 126 ++- src/plugin_winamp2/tagz.h | 2 +- src/plugin_xmms/charset.c | 30 +- src/plugin_xmms/charset.h | 9 +- src/plugin_xmms/configure.c | 4 - src/plugin_xmms/configure.h | 1 - src/plugin_xmms/fileinfo.c | 95 +- src/plugin_xmms/plugin.c | 3 - src/plugin_xmms/wrap_id3.c | 73 +- 30 files changed, 2991 insertions(+), 1963 deletions(-) create mode 100644 src/plugin_winamp2/playback.c create mode 100644 src/plugin_winamp2/playback.h diff --git a/build/config.mk b/build/config.mk index 2cae9346..eaba1829 100644 --- a/build/config.mk +++ b/build/config.mk @@ -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 diff --git a/configure.in b/configure.in index 5cb7e770..2ecf9a2d 100644 --- a/configure.in +++ b/configure.in @@ -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) ;; diff --git a/src/plugin_common/canonical_tag.c b/src/plugin_common/canonical_tag.c index fe072144..7195119c 100644 --- a/src/plugin_common/canonical_tag.c +++ b/src/plugin_common/canonical_tag.c @@ -1,169 +1,719 @@ -/* 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 -#include - -#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 + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "canonical_tag.h" +#include "id3v2.h" +#include "vorbiscomment.h" +#include "FLAC/assert.h" +#include "FLAC/metadata.h" + +#if 0 +#define __USE_GNU /*@@@@@@ needed on glibc systems to get wcsdup() and wcscasecmp() */ +#endif +#include + +/* + * Here lies hackage to get any missing wide character string functions we + * need. The fallback implementations here are from glibc. + */ + +#if !defined(_MSC_VER) && !defined(HAVE_WCSDUP) +/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1995. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include + + +/* Duplicate S, returning an identical malloc'd string. */ +wchar_t * +wcsdup (s) + const wchar_t *s; +{ + size_t len = (__wcslen (s) + 1) * sizeof (wchar_t); + void *new = malloc (len); + + if (new == NULL) + return NULL; + + return (wchar_t *) memcpy (new, (void *) s, len); +} +#endif + +#if !defined(_MSC_VER) && !defined(HAVE_WCSCASECMP) +/* Copyright (C) 1991, 1992, 1995, 1996, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include + +#ifndef weak_alias +# define __wcscasecmp wcscasecmp +# define TOLOWER(Ch) towlower (Ch) +#else +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define __wcscasecmp __wcscasecmp_l +# define TOLOWER(Ch) __towlower_l ((Ch), loc) +# else +# define TOLOWER(Ch) towlower (Ch) +# endif +#endif + +#ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define LOCALE_PARAM , loc +# define LOCALE_PARAM_DECL __locale_t loc; +#else +# define LOCALE_PARAM +# define LOCALE_PARAM_DECL +#endif + +/* Compare S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexicographically less than, + equal to or greater than S2. */ +int +__wcscasecmp (s1, s2 LOCALE_PARAM) + const wchar_t *s1; + const wchar_t *s2; + LOCALE_PARAM_DECL +{ + wint_t c1, c2; + + if (s1 == s2) + return 0; + + do + { + c1 = TOLOWER (*s1++); + c2 = TOLOWER (*s2++); + if (c1 == L'\0') + break; + } + while (c1 == c2); + + return c1 - c2; +} +#ifndef __wcscasecmp +weak_alias (__wcscasecmp, wcscasecmp) +#endif +#endif + +#ifndef _MSC_VER +/* @@@ cheesy and does base 10 only */ +wchar_t *local__itow(int value, wchar_t *string) +{ + if (value == 0) { + string[0] = (wchar_t)'0'; + string[1] = (wchar_t)0; + } + else { + /* convert backwards, then reverse string */ + wchar_t *start = string, *s; + if (value < 0) { + *start++ = (wchar_t)'-'; + value = -value; /* @@@ overflow at INT_MIN */ + } + s = start; + while (value > 0) { + *s++ = (wchar_t)((value % 10) + '0'); + value /= 10; + } + *s-- = (wchar_t)0; + while (s > start) { + wchar_t tmp = *s; + *s-- = *start; + *start++ = tmp; + } + } + + return string; +} +#endif + +/* + * helpers + */ + +/* TODO: should be moved out somewhere? @@@ */ + +wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src) +{ + int len; + wchar_t *dest; + + FLAC__ASSERT(0 != src); + + len = strlen(src) + 1; + /* copy */ + dest = malloc(len*sizeof(wchar_t)); + if (dest) mbstowcs(dest, src, len); + return dest; +} + +/* TODO: more validation? @@@ */ +static __inline int utf8len(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((*utf8 & 0x80) == 0) + return 1; + else if ((*utf8 & 0xE0) == 0xC0) + return 2; + else if ((*utf8 & 0xF0) == 0xE0) + return 3; + else return 0; +} + +/* TODO: validation? @@@ */ +static __inline int utf8_to_ucs2(const FLAC__byte *utf8, wchar_t *ucs2) +{ + int len; + FLAC__ASSERT(utf8!=0 && *utf8!=0 && ucs2!=0); + + if (!(len = utf8len(utf8))) return 0; + + if (len == 1) + *ucs2 = *utf8; + else if (len == 2) + *ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F); + else if (len == 3) + *ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F); + else { + FLAC__ASSERT(len == 0); + } + + return len; +} + +wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length) +{ + wchar_t *out, *p; + const char *s; + int len = 0; + /* calculate length */ + for (s=src; length && *s; len++) + { + int l = utf8len(s); + if (!l) break; + s += l; + length -= l; + } + /* allocate */ + len++; + p = out = (wchar_t*)malloc(len * sizeof(wchar_t)); + if (!out) return NULL; + /* convert */ + for (s=src; --len; p++) + { + int l = utf8_to_ucs2(s, p); + /* l==0 is possible, because real conversion */ + /* might do more careful validation */ + if (!l) break; + s += l; + } + *p = 0; + + return out; +} + +static __inline int ucs2len(wchar_t ucs2) +{ + if (ucs2 < 0x0080) + return 1; + else if (ucs2 < 0x0800) + return 2; + else return 3; +} + +static __inline int ucs2_to_utf8(wchar_t ucs2, FLAC__byte *utf8) +{ + if (ucs2 < 0x080) + { + utf8[0] = (FLAC__byte)ucs2; + return 1; + } + else if (ucs2 < 0x800) + { + utf8[0] = 0xc0 | (ucs2 >> 6); + utf8[1] = 0x80 | (ucs2 & 0x3f); + return 2; + } + else + { + utf8[0] = 0xe0 | (ucs2 >> 12); + utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f); + utf8[2] = 0x80 | (ucs2 & 0x3f); + return 3; + } +} + +char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src) +{ + const wchar_t *s; + char *out, *p; + int len = 0; + FLAC__ASSERT(0 != src); + /* calculate length */ + for (s=src; *s; s++) + len += ucs2len(*s); + /* allocate */ + len++; + p = out = malloc(len); + if (!out) return NULL; + /* convert */ + for (s=src; *s; s++) + { + int l = ucs2_to_utf8(*s, p); + p += l; + } + *p = 0; + + return out; +} + +/* + * init/clear/delete + */ + +FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new() +{ + FLAC_Plugin__CanonicalTag *object = (FLAC_Plugin__CanonicalTag*)malloc(sizeof(FLAC_Plugin__CanonicalTag)); + if (object != 0) + FLAC_plugin__canonical_tag_init(object); + return object; +} + +void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *object) +{ + FLAC_plugin__canonical_tag_clear(object); + free(object); +} + +void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *object) +{ + object->head = object->tail = 0; + object->count = 0; +} + +static void FLAC_plugin__canonical_tag_clear_entry(FLAC__tag_entry *entry) +{ + free(entry->name); + free(entry->value); + free(entry); +} + +void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *object) +{ + FLAC__tag_entry *entry = object->head; + + while (entry) + { + FLAC__tag_entry *next = entry->next; + FLAC_plugin__canonical_tag_clear_entry(entry); + entry = next; + } + + FLAC_plugin__canonical_tag_init(object); +} + +/* + * internal + */ + +static FLAC__tag_entry *FLAC_plugin__canonical_find(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) +{ + FLAC__tag_entry *entry = tag->head; + + while (entry) + { +#if defined _MSC_VER || defined __MINGW32__ +#define FLAC__WCSCASECMP wcsicmp +#else +#define FLAC__WCSCASECMP wcscasecmp +#endif + if (!FLAC__WCSCASECMP(name, entry->name)) +#undef FLAC__WCSCASECMP + break; + entry = entry->next; + } + + return entry; +} + +/* NOTE: does NOT copy strings. takes ownership over passed strings. */ +static void FLAC_plugin__canonical_add_tail(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value) +{ + FLAC__tag_entry *entry = (FLAC__tag_entry*)malloc(sizeof(FLAC__tag_entry)); + if (!entry) + { + free(name); + free(value); + return; + } + /* init */ + entry->name = name; + entry->value = value; + /* add */ + entry->prev = tag->tail; + if (tag->tail) + tag->tail->next = entry; + tag->tail = entry; + if (!tag->head) + tag->head = entry; + entry->next = 0; + tag->count++; +} + +static void FLAC_plugin__canonical_add_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value) +{ + FLAC_plugin__canonical_add_tail(tag, wcsdup(name), wcsdup(value)); +} + +/* NOTE: does NOT copy value, but copies name */ +static void FLAC_plugin__canonical_set_nc(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, wchar_t *value) +{ + FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); + + if (entry) + { + free(entry->value); + entry->value = value; + } + else FLAC_plugin__canonical_add_tail(tag, wcsdup(name), value); +} + +/* NOTE: does NOT copy strings. takes ownership over passed strings. (except sep!) */ +static void FLAC_plugin__canonical_add_nc(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value, const wchar_t *sep) +{ + FLAC__tag_entry *entry; + + if (sep && (entry = FLAC_plugin__canonical_find(tag, name))) + { + unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1; + wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t)); + + if (newvalue) + { + entry->value = newvalue; + wcscat(entry->value, sep); + wcscat(entry->value, value); + } + + free(name); + free(value); + } + else FLAC_plugin__canonical_add_tail(tag, name, value); +} + +/* + * manipulation + */ + +void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value) +{ + FLAC_plugin__canonical_set_nc(tag, name, wcsdup(value)); +} + +void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value) +{ + FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); + if (!entry) FLAC_plugin__canonical_add_new(tag, name, value); +} + +void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value) +{ + wchar_t *val = FLAC_plugin__convert_ansi_to_wide(value); + if (val) FLAC_plugin__canonical_set_nc(tag, name, val); +} + +void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep) +{ + FLAC__tag_entry *entry; + + if (sep && (entry = FLAC_plugin__canonical_find(tag, name))) + { + unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1; + wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t)); + + if (newvalue) + { + entry->value = newvalue; + wcscat(entry->value, sep); + wcscat(entry->value, value); + } + } + else FLAC_plugin__canonical_add_new(tag, name, value); +} + +void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep) +{ + wchar_t *n = FLAC_plugin__convert_utf8_to_ucs2(name, namelen); + wchar_t *v = FLAC_plugin__convert_utf8_to_ucs2(value, vallen); + wchar_t *s = sep ? FLAC_plugin__convert_utf8_to_ucs2(sep, -1) : 0; + + if (n && v) + { + FLAC_plugin__canonical_add_nc(tag, n, v, s); + } + else + { + if (n) free(n); + if (v) free(v); + } + if (s) free(s); +} + +const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) +{ + FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); + return entry ? entry->value : 0; +} + +FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) +{ + FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); + + if (entry) + { + if (entry->prev) + entry->prev->next = entry->next; + else tag->head = entry->next; + + if (entry->next) + entry->next->prev = entry->prev; + else tag->tail = entry->prev; + + FLAC_plugin__canonical_tag_clear_entry(entry); + tag->count--; + return true; + } + + return false; +} + +void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) +{ + while (FLAC_plugin__canonical_remove(tag, name)); +} + +char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it) +{ + int len1 = wcslen(it->name); + int len2 = wcslen(it->value); + int len = len1 + len2 + 1; + wchar_t *val = malloc((len+1) * sizeof(wchar_t)); + + if (val) + { + char *res; + + memcpy(val, it->name, len1 * sizeof(wchar_t)); + val[len1] = '='; + memcpy(val+len1+1, it->value, len2 * sizeof(wchar_t)); + val[len] = 0; + + res = FLAC_plugin__convert_ucs2_to_utf8(val); + free(val); + return res; + } + + return NULL; +} + +/* + * merging + */ + +void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src) +{ + FLAC__tag_entry *entry = src->head; + + while (entry) + { + FLAC_plugin__canonical_set_new(dest, entry->name, entry->value); + entry = entry->next; + } +} + +static wchar_t *local__copy_field(const char *src, unsigned n) +{ + const char *p = src + n; + wchar_t *dest; + FLAC__ASSERT(n > 0); + + while (p>src && *(--p)==' '); + + n = p - src + 1; + if (!n) return NULL; + + if ((dest = malloc((n+1)*sizeof(wchar_t))) != 0) + { + mbstowcs(dest, src, n); + dest[n] = 0; + } + return dest; +} + +static void local__add_id3_field(FLAC_Plugin__CanonicalTag *object, const char *value, size_t length, const wchar_t *new_name) +{ + wchar_t *tmp; + if (0 != value && length > 0) { + tmp = local__copy_field(value, length); + if (tmp) + FLAC_plugin__canonical_add_tail(object, wcsdup(new_name), tmp); + } +} + +void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v1_Tag *id3v1_tag) +{ + wchar_t *tmp; + FLAC_plugin__canonical_tag_clear(object); + + local__add_id3_field(object, id3v1_tag->title, 30, L"TITLE"); + local__add_id3_field(object, id3v1_tag->artist, 30, L"ARTIST"); + local__add_id3_field(object, id3v1_tag->album, 30, L"ALBUM"); + local__add_id3_field(object, id3v1_tag->year, 4, L"YEAR"); + + /* check for v1.1 tags */ + if (id3v1_tag->zero == 0) + { + if (id3v1_tag->track && (tmp=(wchar_t*)malloc(sizeof(id3v1_tag->track)*4*sizeof(wchar_t)))!=0) + { +#ifdef _MSC_VER + _itow(id3v1_tag->track, tmp, 10); +#else + local__itow(id3v1_tag->track, tmp); +#endif + FLAC_plugin__canonical_add_tail(object, wcsdup(L"TRACKNUMBER"), tmp); + } + local__add_id3_field(object, id3v1_tag->comment, 28, L"DESCRIPTION"); + } + else + { + local__add_id3_field(object, id3v1_tag->comment, 30, L"DESCRIPTION"); + } + + tmp = FLAC_plugin__convert_ansi_to_wide(FLAC_plugin__id3v1_tag_get_genre_as_string(id3v1_tag->genre)); + if (tmp) FLAC_plugin__canonical_add_tail(object, wcsdup(L"GENRE"), tmp); +} + +void FLAC_plugin__canonical_tag_convert_from_id3v2(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v2_Tag *id3v2_tag) +{ + FLAC_plugin__canonical_tag_clear(object); + + local__add_id3_field(object, id3v2_tag->title , strlen(id3v2_tag->title) , L"TITLE"); + local__add_id3_field(object, id3v2_tag->composer , strlen(id3v2_tag->composer) , L"ARTIST"); + local__add_id3_field(object, id3v2_tag->performer , strlen(id3v2_tag->performer) , L"PERFORMER"); + local__add_id3_field(object, id3v2_tag->album , strlen(id3v2_tag->album) , L"ALBUM"); + local__add_id3_field(object, id3v2_tag->year_recorded , strlen(id3v2_tag->year_recorded) , L"YEAR_RECORDED"); + local__add_id3_field(object, id3v2_tag->year_performed , strlen(id3v2_tag->year_performed) , L"YEAR_PERFORMED"); + local__add_id3_field(object, id3v2_tag->track_number , strlen(id3v2_tag->track_number) , L"TRACKNUMBER"); + local__add_id3_field(object, id3v2_tag->tracks_in_album, strlen(id3v2_tag->tracks_in_album), L"TRACKS_IN_ALBUM"); + local__add_id3_field(object, id3v2_tag->genre , strlen(id3v2_tag->genre) , L"GENRE"); + local__add_id3_field(object, id3v2_tag->comment , strlen(id3v2_tag->comment) , L"DESCRIPTION"); +} + +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; +} + +static FLAC__bool local__get_id3v2_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag) +{ + FLAC_Plugin__Id3v2_Tag id3v2_tag; + + if (FLAC_plugin__id3v2_tag_get(filename, &id3v2_tag)) + { + FLAC_plugin__canonical_tag_convert_from_id3v2(tag, &id3v2_tag); + return true; + } + return false; +} + +void FLAC_plugin__canonical_tag_add_id3v1(const char *filename, FLAC_Plugin__CanonicalTag *tag) +{ + FLAC_Plugin__CanonicalTag id3v1_tag; + + FLAC_plugin__canonical_tag_init(&id3v1_tag); + (void)local__get_id3v1_tag_as_canonical(filename, &id3v1_tag); + FLAC_plugin__canonical_tag_merge(tag, &id3v1_tag); + + FLAC_plugin__canonical_tag_clear(&id3v1_tag); +} + +void FLAC_plugin__canonical_tag_add_id3v2(const char *filename, FLAC_Plugin__CanonicalTag *tag) +{ + FLAC_Plugin__CanonicalTag id3v2_tag; + + FLAC_plugin__canonical_tag_init(&id3v2_tag); + (void)local__get_id3v2_tag_as_canonical(filename, &id3v2_tag); + FLAC_plugin__canonical_tag_merge(tag, &id3v2_tag); + + FLAC_plugin__canonical_tag_clear(&id3v2_tag); +} + +void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep) +{ + FLAC_plugin__vorbiscomment_get(filename, tag, sep); + FLAC_plugin__canonical_tag_add_id3v2(filename, tag); + FLAC_plugin__canonical_tag_add_id3v1(filename, tag); +} diff --git a/src/plugin_common/canonical_tag.h b/src/plugin_common/canonical_tag.h index 6ba2be02..3133c5a7 100644 --- a/src/plugin_common/canonical_tag.h +++ b/src/plugin_common/canonical_tag.h @@ -1,55 +1,118 @@ -/* 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 + * 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" +#include "id3v2.h" + +/* TODO: splay tree? */ +typedef struct tagFLAC__tag_entry FLAC__tag_entry; +struct tagFLAC__tag_entry +{ + FLAC__tag_entry *next, *prev; + /* TODO: name in ascii? */ + wchar_t *name; + wchar_t *value; +}; + +typedef struct { + FLAC__tag_entry *head, *tail; + unsigned count; +} FLAC_Plugin__CanonicalTag; + + +typedef FLAC__tag_entry *FLAC__tag_iterator; + +FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new(); +void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *); +void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *); +void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *); + +/* note that multiple fields with the same name are allowed. + * set - adds field if it's not present yet, or replaces + * existing field if it's present. + */ +void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value); +void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value); +/* set_new - only adds field if it's not present yet. */ +void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value); +/* add - adds field if it's not present yet, or merges value with existing + * field, if it's present. (sep - separator string to use when merging; + * if sep==NULL no merging occurs - always adds new field) + */ +void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep); +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 */ + +/* gets value of the first field with the given name (NULL if field not found) */ +const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name); +/* removes first field with the given name. + * (returns 'true' if deleted, 'false' if not found) + */ +FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name); +/* removes all fields with the given name. */ +void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name); + +/* enumeration */ +static __inline unsigned FLAC_plugin__canonical_get_count(FLAC_Plugin__CanonicalTag *tag) +{ + return tag->count; +} +static __inline FLAC__tag_iterator FLAC_plugin__canonical_first(FLAC_Plugin__CanonicalTag *tag) +{ + return tag->head; +} +static __inline FLAC__tag_iterator FLAC_plugin__canonical_next(FLAC__tag_iterator it) +{ + return it->next; +} +static __inline wchar_t *FLAC_plugin__canonical_get_name(FLAC__tag_iterator it) +{ + return it->name; +} +static __inline wchar_t *FLAC_plugin__canonical_get_value(FLAC__tag_iterator it) +{ + return it->value; +} + +/* returns a new string containing the current entry in UTF-8 in "NAME=VALUE" form */ +char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it); + +void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src); +void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v1_Tag *); +void FLAC_plugin__canonical_tag_convert_from_id3v2(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v2_Tag *); + +void FLAC_plugin__canonical_tag_add_id3v1(const char *filename, FLAC_Plugin__CanonicalTag *tag); +void FLAC_plugin__canonical_tag_add_id3v2(const char *filename, FLAC_Plugin__CanonicalTag *tag); + +/* Returns a merged tag based on any Vorbis comments, id3v2 tag, and id3v1. + * In case of overlaps the preceding precedence applies. + * + * sep - separator to use when merging fields with same name (in VorbisComment). + * should be in UTF-8. if sep==NULL, no merging occurs, so multiple fields + * with the same name can exist. + */ +void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep); + +/* helpers */ +wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src); +wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length); /* 'length' may be (unsigned)(-1) if 'src' is NUL-terminated */ +char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src); + +#endif diff --git a/src/plugin_common/id3v1.c b/src/plugin_common/id3v1.c index aa5fc68b..274cc9b3 100644 --- a/src/plugin_common/id3v1.c +++ b/src/plugin_common/id3v1.c @@ -1,229 +1,214 @@ -/* 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 - -#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 + * 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 + +#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) +{ + FILE *f; + int res; + + 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; + } + res = fread(tag, 128, 1, f); + fclose(f); + return res==1 && !strncmp(tag->tag, "TAG", 3); +} + +const char *FLAC_plugin__id3v1_tag_get_genre_as_string(unsigned char genre_code) +{ + if (genre_code < (sizeof(FLAC_plugin__id3v1_tag_genre_table)/sizeof(FLAC_plugin__id3v1_tag_genre_table[0]))) + return gettext(FLAC_plugin__id3v1_tag_genre_table[genre_code]); + return "Unknown"; +} + +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; +} diff --git a/src/plugin_common/id3v1.h b/src/plugin_common/id3v1.h index 46e070db..28d361f5 100644 --- a/src/plugin_common/id3v1.h +++ b/src/plugin_common/id3v1.h @@ -23,25 +23,23 @@ #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); diff --git a/src/plugin_common/id3v2.c b/src/plugin_common/id3v2.c index b18a1da3..219a8085 100644 --- a/src/plugin_common/id3v2.c +++ b/src/plugin_common/id3v2.c @@ -24,6 +24,11 @@ #include #endif +#include "FLAC/assert.h" + +#include /* for free() */ +#include /* for memset() */ + #ifdef FLAC__HAS_ID3LIB #include #include @@ -32,8 +37,6 @@ #include #include -#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)); +} diff --git a/src/plugin_common/id3v2.h b/src/plugin_common/id3v2.h index 4d03fef9..dbb766c1 100644 --- a/src/plugin_common/id3v2.h +++ b/src/plugin_common/id3v2.h @@ -19,8 +19,39 @@ #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 diff --git a/src/plugin_common/plugin_common_static.dsp b/src/plugin_common/plugin_common_static.dsp index 9562a876..cafa6ff2 100644 --- a/src/plugin_common/plugin_common_static.dsp +++ b/src/plugin_common/plugin_common_static.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release_static" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /Ox /Og /Oi /Os /Op /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /WX /GX /Ox /Og /Oi /Os /Op /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe diff --git a/src/plugin_common/vorbiscomment.c b/src/plugin_common/vorbiscomment.c index 278c2a77..934b4f89 100644 --- a/src/plugin_common/vorbiscomment.c +++ b/src/plugin_common/vorbiscomment.c @@ -1,180 +1,110 @@ -/* 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 -#include -#include - -#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 + * 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 +#include +#include + +#include "vorbiscomment.h" +#include "FLAC/metadata.h" + + +static void local__add_vcentry(FLAC_Plugin__CanonicalTag *tag, FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *sep) +{ + FLAC__byte *value = memchr(entry->entry, '=', entry->length); + int len; + if (!value) return; + len = value - entry->entry; + value++; + FLAC_plugin__canonical_add_utf8(tag, entry->entry, value, len, entry->length-len-1, sep); +} + +void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep) +{ + 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++) + local__add_vcentry(tag, vc->comments+i, sep); + + 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, result; + FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new(); + FLAC__StreamMetadata *block; + FLAC__tag_iterator it; + unsigned position = 0; + + 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); + + FLAC__metadata_object_vorbiscomment_resize_comments(block, FLAC_plugin__canonical_get_count(tag)); + + for (it=FLAC_plugin__canonical_first(tag); it; it=FLAC_plugin__canonical_next(it)) + { + FLAC__StreamMetadata_VorbisComment_Entry entry; + /* replace entry */ + entry.entry = FLAC_plugin__canonical_get_formatted(it); + entry.length = strlen(entry.entry); + FLAC__metadata_object_vorbiscomment_set_comment(block, position++, entry, /*copy=*/false); + } + + if (!got_vorbis_comments) + result = FLAC__metadata_simple_iterator_insert_block_after(iterator, block, /*use_padding=*/true); + else + result = FLAC__metadata_simple_iterator_set_block(iterator, block, /*use_padding=*/true); + + FLAC__metadata_object_delete(block); + FLAC__metadata_simple_iterator_delete(iterator); + return result; +} diff --git a/src/plugin_common/vorbiscomment.h b/src/plugin_common/vorbiscomment.h index 84c64473..f02b5cac 100644 --- a/src/plugin_common/vorbiscomment.h +++ b/src/plugin_common/vorbiscomment.h @@ -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 diff --git a/src/plugin_winamp2/config.c b/src/plugin_winamp2/config.c index ff894e04..c9b03629 100644 --- a/src/plugin_winamp2/config.c +++ b/src/plugin_winamp2/config.c @@ -1,3 +1,21 @@ +/* in_flac - Winamp2 FLAC input plugin + * 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 #include #include @@ -7,10 +25,11 @@ static char buffer[256]; +static char ini_name[MAX_PATH]; -// -// Read/write config -// +/* + * Read/write config + */ #define RI(x, def) (x = GetPrivateProfileInt("FLAC", #x, def, ini_name)) #define WI(x) WritePrivateProfileString("FLAC", #x, itoa(x, buffer, 10), ini_name) @@ -18,38 +37,65 @@ static char buffer[256]; #define WS(x) WritePrivateProfileString("FLAC", #x, x, ini_name) static const char default_format[] = "[%artist% - ]$if2(%title%,%filename%)"; +static const char default_sep[] = ", "; + +void InitConfig() +{ + char *p; + + GetModuleFileName(NULL, ini_name, sizeof(ini_name)); + p = strrchr(ini_name, '.'); + if (!p) p = ini_name + strlen(ini_name); + strcpy(p, ".ini"); + + flac_cfg.title.tag_format_w = NULL; +} void ReadConfig() { - RS(flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format), default_format); + RS(flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format), default_format); + if (flac_cfg.title.tag_format_w) + free(flac_cfg.title.tag_format_w); + flac_cfg.title.tag_format_w = FLAC_plugin__convert_ansi_to_wide(flac_cfg.title.tag_format); + /* @@@ FIXME: trailing spaces */ + RS(flac_cfg.title.sep, sizeof(flac_cfg.title.sep), default_sep); + RI(flac_cfg.title.read_v1, 1); + RI(flac_cfg.tag.reserve_space, 1); - RI(flac_cfg.output.replaygain.enable, 1); - RI(flac_cfg.output.replaygain.album_mode, 0); - RI(flac_cfg.output.replaygain.hard_limit, 0); - RI(flac_cfg.output.replaygain.preamp, 0); - RI(flac_cfg.output.resolution.normal.dither_24_to_16, 0); - RI(flac_cfg.output.resolution.replaygain.dither, 0); - RI(flac_cfg.output.resolution.replaygain.noise_shaping, 1); - RI(flac_cfg.output.resolution.replaygain.bps_out, 16); + RI(flac_cfg.display.show_bps, 1); + RI(flac_cfg.output.misk.stop_err, 0); + RI(flac_cfg.output.replaygain.enable, 1); + RI(flac_cfg.output.replaygain.album_mode, 0); + RI(flac_cfg.output.replaygain.hard_limit, 0); + RI(flac_cfg.output.replaygain.preamp, 0); + RI(flac_cfg.output.resolution.normal.dither_24_to_16, 0); + RI(flac_cfg.output.resolution.replaygain.dither, 0); + RI(flac_cfg.output.resolution.replaygain.noise_shaping, 1); + RI(flac_cfg.output.resolution.replaygain.bps_out, 16); } void WriteConfig() { - WS(flac_cfg.title.tag_format); + WS(flac_cfg.title.tag_format); + WI(flac_cfg.title.read_v1); + WI(flac_cfg.tag.reserve_space); + WS(flac_cfg.title.sep); - WI(flac_cfg.output.replaygain.enable); - WI(flac_cfg.output.replaygain.album_mode); - WI(flac_cfg.output.replaygain.hard_limit); - WI(flac_cfg.output.replaygain.preamp); - WI(flac_cfg.output.resolution.normal.dither_24_to_16); - WI(flac_cfg.output.resolution.replaygain.dither); - WI(flac_cfg.output.resolution.replaygain.noise_shaping); - WI(flac_cfg.output.resolution.replaygain.bps_out); + WI(flac_cfg.display.show_bps); + WI(flac_cfg.output.misk.stop_err); + WI(flac_cfg.output.replaygain.enable); + WI(flac_cfg.output.replaygain.album_mode); + WI(flac_cfg.output.replaygain.hard_limit); + WI(flac_cfg.output.replaygain.preamp); + WI(flac_cfg.output.resolution.normal.dither_24_to_16); + WI(flac_cfg.output.resolution.replaygain.dither); + WI(flac_cfg.output.resolution.replaygain.noise_shaping); + WI(flac_cfg.output.resolution.replaygain.bps_out); } -// -// Dialog -// +/* + * Dialog + */ #define PREAMP_RANGE 24 @@ -59,284 +105,308 @@ void WriteConfig() #define GetPos(x) SendDlgItemMessage(hwnd, x, TBM_GETPOS, 0, 0) #define Enable(x,y) EnableWindow(GetDlgItem(hwnd, x), y) - static INT_PTR CALLBACK GeneralProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) - { - // init - case WM_INITDIALOG: - SetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format); - return TRUE; - // commands - case WM_COMMAND: - switch (LOWORD(wParam)) - { - // ok - case IDOK: - GetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format)); - break; - // reset - case IDC_RESET: - SetDlgItemText(hwnd, IDC_TITLE, default_format); - break; - // help - case IDC_TAGZ_HELP: - MessageBox(hwnd, tagz_manual, "Help", 0); - break; - } - break; - } + switch (msg) + { + /* init */ + case WM_INITDIALOG: + SendDlgItemMessage(hwnd, IDC_TITLE, EM_LIMITTEXT, 255, 0); + SendDlgItemMessage(hwnd, IDC_SEP, EM_LIMITTEXT, 15, 0); - return 0; + SetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format); + SetDlgItemText(hwnd, IDC_SEP, flac_cfg.title.sep); + Check(IDC_ID3V1, flac_cfg.title.read_v1); +/*! Check(IDC_RESERVE, flac_cfg.tag.reserve_space); */ + Check(IDC_BPS, flac_cfg.display.show_bps); + Check(IDC_ERRORS, flac_cfg.output.misk.stop_err); + return TRUE; + /* commands */ + case WM_COMMAND: + switch (LOWORD(wParam)) + { + /* ok */ + case IDOK: + GetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format)); + if (flac_cfg.title.tag_format_w) + free(flac_cfg.title.tag_format_w); + GetDlgItemText(hwnd, IDC_SEP, flac_cfg.title.sep, sizeof(flac_cfg.title.sep)); + flac_cfg.title.tag_format_w = FLAC_plugin__convert_ansi_to_wide(flac_cfg.title.tag_format); + + flac_cfg.title.read_v1 = GetCheck(IDC_ID3V1); +/*! flac_cfg.tag.reserve_space = GetCheck(IDC_RESERVE); */ + flac_cfg.display.show_bps = GetCheck(IDC_BPS); + flac_cfg.output.misk.stop_err = GetCheck(IDC_ERRORS); + break; + /* reset */ + case IDC_RESET: + Check(IDC_ID3V1, 1); + Check(IDC_RESERVE, 1); + Check(IDC_BPS, 1); + Check(IDC_ERRORS, 0); + /* fall throught */ + /* default */ + case IDC_TAGZ_DEFAULT: + SetDlgItemText(hwnd, IDC_TITLE, default_format); + break; + /* help */ + case IDC_TAGZ_HELP: + MessageBox(hwnd, tagz_manual, "Help", 0); + break; + } + break; + } + + return 0; } static void UpdatePreamp(HWND hwnd, HWND hamp) { - int pos = SendMessage(hamp, TBM_GETPOS, 0, 0) - PREAMP_RANGE; - sprintf(buffer, "%d dB", pos); - SetDlgItemText(hwnd, IDC_PA, buffer); + int pos = SendMessage(hamp, TBM_GETPOS, 0, 0) - PREAMP_RANGE; + sprintf(buffer, "%d dB", pos); + SetDlgItemText(hwnd, IDC_PA, buffer); } static void UpdateRG(HWND hwnd) { - int on = GetCheck(IDC_ENABLE); - Enable(IDC_ALBUM, on); - Enable(IDC_LIMITER, on); - Enable(IDC_PREAMP, on); - Enable(IDC_PA, on); + int on = GetCheck(IDC_ENABLE); + Enable(IDC_ALBUM, on); + Enable(IDC_LIMITER, on); + Enable(IDC_PREAMP, on); + Enable(IDC_PA, on); } static void UpdateDither(HWND hwnd) { - int on = GetCheck(IDC_DITHERRG); - Enable(IDC_SHAPE, on); + int on = GetCheck(IDC_DITHERRG); + Enable(IDC_SHAPE, on); } static INT_PTR CALLBACK OutputProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) - { - // init - case WM_INITDIALOG: - Check(IDC_ENABLE, flac_cfg.output.replaygain.enable); - Check(IDC_ALBUM, flac_cfg.output.replaygain.album_mode); - Check(IDC_LIMITER, flac_cfg.output.replaygain.hard_limit); - Check(IDC_DITHER, flac_cfg.output.resolution.normal.dither_24_to_16); - Check(IDC_DITHERRG, flac_cfg.output.resolution.replaygain.dither); - // prepare preamp slider - { - HWND hamp = GetDlgItem(hwnd, IDC_PREAMP); - SendMessage(hamp, TBM_SETRANGE, 1, MAKELONG(0, PREAMP_RANGE*2)); - SendMessage(hamp, TBM_SETPOS, 1, flac_cfg.output.replaygain.preamp+PREAMP_RANGE); - UpdatePreamp(hwnd, hamp); - } - // fill comboboxes - { - HWND hlist = GetDlgItem(hwnd, IDC_TO); - SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"16 bps"); - SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"24 bps"); - SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.bps_out/8 - 2, 0); - - hlist = GetDlgItem(hwnd, IDC_SHAPE); - SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"None"); - SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Low"); - SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Medium"); - SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"High"); - SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.noise_shaping, 0); - } - UpdateRG(hwnd); - UpdateDither(hwnd); - return TRUE; - // commands - case WM_COMMAND: - switch (LOWORD(wParam)) - { - // ok - case IDOK: - flac_cfg.output.replaygain.enable = GetCheck(IDC_ENABLE); - flac_cfg.output.replaygain.album_mode = GetCheck(IDC_ALBUM); - flac_cfg.output.replaygain.hard_limit = GetCheck(IDC_LIMITER); - flac_cfg.output.replaygain.preamp = GetPos(IDC_PREAMP) - PREAMP_RANGE; - flac_cfg.output.resolution.normal.dither_24_to_16 = GetCheck(IDC_DITHER); - flac_cfg.output.resolution.replaygain.dither = GetCheck(IDC_DITHERRG); - flac_cfg.output.resolution.replaygain.noise_shaping = GetSel(IDC_SHAPE); - flac_cfg.output.resolution.replaygain.bps_out = (GetSel(IDC_TO)+2)*8; - break; - // reset - case IDC_RESET: - Check(IDC_ENABLE, 1); - Check(IDC_ALBUM, 0); - Check(IDC_LIMITER, 0); - Check(IDC_DITHER, 0); - Check(IDC_DITHERRG, 0); + switch (msg) + { + /* init */ + case WM_INITDIALOG: + Check(IDC_ENABLE, flac_cfg.output.replaygain.enable); + Check(IDC_ALBUM, flac_cfg.output.replaygain.album_mode); + Check(IDC_LIMITER, flac_cfg.output.replaygain.hard_limit); + Check(IDC_DITHER, flac_cfg.output.resolution.normal.dither_24_to_16); + Check(IDC_DITHERRG, flac_cfg.output.resolution.replaygain.dither); + /* prepare preamp slider */ + { + HWND hamp = GetDlgItem(hwnd, IDC_PREAMP); + SendMessage(hamp, TBM_SETRANGE, 1, MAKELONG(0, PREAMP_RANGE*2)); + SendMessage(hamp, TBM_SETPOS, 1, flac_cfg.output.replaygain.preamp+PREAMP_RANGE); + UpdatePreamp(hwnd, hamp); + } + /* fill comboboxes */ + { + HWND hlist = GetDlgItem(hwnd, IDC_TO); + SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"16 bps"); + SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"24 bps"); + SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.bps_out/8 - 2, 0); - SendDlgItemMessage(hwnd, IDC_PREAMP, TBM_SETPOS, 1, PREAMP_RANGE); - SendDlgItemMessage(hwnd, IDC_TO, CB_SETCURSEL, 0, 0); - SendDlgItemMessage(hwnd, IDC_SHAPE, CB_SETCURSEL, 1, 0); + hlist = GetDlgItem(hwnd, IDC_SHAPE); + SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"None"); + SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Low"); + SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"Medium"); + SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"High"); + SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.noise_shaping, 0); + } + UpdateRG(hwnd); + UpdateDither(hwnd); + return TRUE; + /* commands */ + case WM_COMMAND: + switch (LOWORD(wParam)) + { + /* ok */ + case IDOK: + flac_cfg.output.replaygain.enable = GetCheck(IDC_ENABLE); + flac_cfg.output.replaygain.album_mode = GetCheck(IDC_ALBUM); + flac_cfg.output.replaygain.hard_limit = GetCheck(IDC_LIMITER); + flac_cfg.output.replaygain.preamp = GetPos(IDC_PREAMP) - PREAMP_RANGE; + flac_cfg.output.resolution.normal.dither_24_to_16 = GetCheck(IDC_DITHER); + flac_cfg.output.resolution.replaygain.dither = GetCheck(IDC_DITHERRG); + flac_cfg.output.resolution.replaygain.noise_shaping = GetSel(IDC_SHAPE); + flac_cfg.output.resolution.replaygain.bps_out = (GetSel(IDC_TO)+2)*8; + break; + /* reset */ + case IDC_RESET: + Check(IDC_ENABLE, 1); + Check(IDC_ALBUM, 0); + Check(IDC_LIMITER, 0); + Check(IDC_DITHER, 0); + Check(IDC_DITHERRG, 0); - UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP)); - UpdateRG(hwnd); - UpdateDither(hwnd); - break; - // active check-boxes - case IDC_ENABLE: - UpdateRG(hwnd); - break; - case IDC_DITHERRG: - UpdateDither(hwnd); - break; - } - break; - // scroller - case WM_HSCROLL: - if (GetDlgCtrlID((HWND)lParam)==IDC_PREAMP) - UpdatePreamp(hwnd, (HWND)lParam); - return 0; - } + SendDlgItemMessage(hwnd, IDC_PREAMP, TBM_SETPOS, 1, PREAMP_RANGE); + SendDlgItemMessage(hwnd, IDC_TO, CB_SETCURSEL, 0, 0); + SendDlgItemMessage(hwnd, IDC_SHAPE, CB_SETCURSEL, 1, 0); - return 0; + UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP)); + UpdateRG(hwnd); + UpdateDither(hwnd); + break; + /* active check-boxes */ + case IDC_ENABLE: + UpdateRG(hwnd); + break; + case IDC_DITHERRG: + UpdateDither(hwnd); + break; + } + break; + /* scroller */ + case WM_HSCROLL: + if (GetDlgCtrlID((HWND)lParam)==IDC_PREAMP) + UpdatePreamp(hwnd, (HWND)lParam); + return 0; + } + + return 0; } #define NUM_PAGES 2 typedef struct { - HWND htab; - HWND hdlg; - RECT r; - HWND all[NUM_PAGES]; + HWND htab; + HWND hdlg; + RECT r; + HWND all[NUM_PAGES]; } LOCALDATA; static void ScreenToClientRect(HWND hwnd, RECT *rect) { - POINT pt = { rect->left, rect->top }; - ScreenToClient(hwnd, &pt); - rect->left = pt.x; - rect->top = pt.y; + POINT pt = { rect->left, rect->top }; + ScreenToClient(hwnd, &pt); + rect->left = pt.x; + rect->top = pt.y; - pt.x = rect->right; - pt.y = rect->bottom; - ScreenToClient(hwnd, &pt); - rect->right = pt.x; - rect->bottom = pt.y; + pt.x = rect->right; + pt.y = rect->bottom; + ScreenToClient(hwnd, &pt); + rect->right = pt.x; + rect->bottom = pt.y; } static void SendCommand(HWND hwnd, int command) { - LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - SendMessage(data->hdlg, WM_COMMAND, command, 0); + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); + SendMessage(data->hdlg, WM_COMMAND, command, 0); } static void BroadcastCommand(HWND hwnd, int command) { - LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - int i; + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); + int i; - for (i=0; iall[i], WM_COMMAND, command, 0); + for (i=0; iall[i], WM_COMMAND, command, 0); } static void OnSelChange(HWND hwnd) { - LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - int index = TabCtrl_GetCurSel(data->htab); - if (index < 0) return; - // hide previous - if (data->hdlg) - ShowWindow(data->hdlg, SW_HIDE); - // display - data->hdlg = data->all[index]; - 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); + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); + int index = TabCtrl_GetCurSel(data->htab); + if (index < 0) return; + /* hide previous */ + if (data->hdlg) + ShowWindow(data->hdlg, SW_HIDE); + /* display */ + data->hdlg = data->all[index]; + 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); + SetFocus(hwnd); } static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static activePage = 0; + static activePage = 0; - switch (msg) - { - // init - case WM_INITDIALOG: - { - LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA)); - HINSTANCE inst = (HINSTANCE)lParam; - TCITEM item; + switch (msg) + { + /* init */ + case WM_INITDIALOG: + { + LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA)); + HINSTANCE inst = (HINSTANCE)lParam; + TCITEM item; - // init - SetWindowLong(hwnd, GWL_USERDATA, (LONG)data); - data->htab = GetDlgItem(hwnd, IDC_TABS); - data->hdlg = NULL; - // add pages - item.mask = TCIF_TEXT; - data->all[0] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_GENERAL), hwnd, GeneralProc); - item.pszText = "General"; - TabCtrl_InsertItem(data->htab, 0, &item); + /* init */ + SetWindowLong(hwnd, GWL_USERDATA, (LONG)data); + data->htab = GetDlgItem(hwnd, IDC_TABS); + data->hdlg = NULL; + /* add pages */ + item.mask = TCIF_TEXT; + data->all[0] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_GENERAL), hwnd, GeneralProc); + item.pszText = "General"; + TabCtrl_InsertItem(data->htab, 0, &item); - data->all[1] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_OUTPUT), hwnd, OutputProc); - item.pszText = "Output"; - TabCtrl_InsertItem(data->htab, 1, &item); - // get rect (after adding pages) - GetWindowRect(data->htab, &data->r); - ScreenToClientRect(hwnd, &data->r); - TabCtrl_AdjustRect(data->htab, 0, &data->r); - // simulate item change - TabCtrl_SetCurSel(data->htab, activePage); - OnSelChange(hwnd); - } - return TRUE; - // destory - case WM_DESTROY: - { - LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - int i; + data->all[1] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_OUTPUT), hwnd, OutputProc); + item.pszText = "Output"; + TabCtrl_InsertItem(data->htab, 1, &item); + /* get rect (after adding pages) */ + GetWindowRect(data->htab, &data->r); + ScreenToClientRect(hwnd, &data->r); + TabCtrl_AdjustRect(data->htab, 0, &data->r); + /* simulate item change */ + TabCtrl_SetCurSel(data->htab, activePage); + OnSelChange(hwnd); + } + return TRUE; + /* destory */ + case WM_DESTROY: + { + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); + int i; - activePage = TabCtrl_GetCurSel(data->htab); + activePage = TabCtrl_GetCurSel(data->htab); - for (i=0; iall[i]); + for (i=0; iall[i]); - LocalFree(data); - } - break; - // commands - case WM_COMMAND: - switch (LOWORD(wParam)) - { - // ok/cancel - case IDOK: - BroadcastCommand(hwnd, IDOK); - /* fall through */ - case IDCANCEL: - EndDialog(hwnd, LOWORD(wParam)); - return TRUE; - case IDC_RESET: - SendCommand(hwnd, IDC_RESET); - break; - } - break; - // notification - case WM_NOTIFY: - if (LOWORD(wParam) == IDC_TABS) - { - NMHDR *hdr = (NMHDR*)lParam; + LocalFree(data); + } + break; + /* commands */ + case WM_COMMAND: + switch (LOWORD(wParam)) + { + /* ok/cancel */ + case IDOK: + BroadcastCommand(hwnd, IDOK); + /* fall through */ + case IDCANCEL: + EndDialog(hwnd, LOWORD(wParam)); + return TRUE; + case IDC_RESET: + SendCommand(hwnd, IDC_RESET); + break; + } + break; + /* notification */ + case WM_NOTIFY: + if (LOWORD(wParam) == IDC_TABS) + { + NMHDR *hdr = (NMHDR*)lParam; - switch (hdr->code) - { - case TCN_SELCHANGE: - OnSelChange(hwnd); - break; - } - } - break; - } + switch (hdr->code) + { + case TCN_SELCHANGE: + OnSelChange(hwnd); + break; + } + } + break; + } - return 0; + return 0; } int DoConfig(HINSTANCE inst, HWND parent) { - return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc, (LONG)inst) == IDOK; + return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc, (LONG)inst) == IDOK; } diff --git a/src/plugin_winamp2/config.h b/src/plugin_winamp2/config.h index 60e5efa3..11ec20bf 100644 --- a/src/plugin_winamp2/config.h +++ b/src/plugin_winamp2/config.h @@ -1,41 +1,50 @@ +/* in_flac - Winamp2 FLAC input plugin + * 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. + */ -// -// common stuff -// +#include "playback.h" -typedef struct { - struct { - BOOL enable; - BOOL album_mode; - INT preamp; - BOOL hard_limit; - } replaygain; - struct { - struct { - BOOL dither_24_to_16; - } normal; - struct { - BOOL dither; - INT noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */ - INT bps_out; - } replaygain; - } resolution; -} output_config_t; +/* + * common stuff + */ typedef struct { struct { char tag_format[256]; + char sep[16]; + WCHAR *tag_format_w; + BOOL read_v1; } title; + struct { + BOOL reserve_space; + } tag; + struct { + FLAC__bool show_bps; + } display; output_config_t output; } flac_config_t; extern flac_config_t flac_cfg; -extern char ini_name[MAX_PATH]; -// -// prototypes -// +/* + * prototypes + */ +void InitConfig(); void ReadConfig(); void WriteConfig(); -int DoConfig(HINSTANCE inst, HWND parent); +int DoConfig(HINSTANCE inst, HWND parent); diff --git a/src/plugin_winamp2/in_flac.c b/src/plugin_winamp2/in_flac.c index 4458ce52..3ba942a1 100644 --- a/src/plugin_winamp2/in_flac.c +++ b/src/plugin_winamp2/in_flac.c @@ -1,641 +1,412 @@ -/* 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 -#include -#include -#include -#include - -#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(¶m.t); - FLAC_plugin__canonical_tag_get_combined(filename, ¶m.t); - param.filename = filename; - - tagz_format(flac_cfg.title.tag_format, get_tag, NULL, ¶m, description, max_size); - - FLAC_plugin__canonical_tag_clear(¶m.t); -} +/* 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 +#include + +#include "winamp2/in2.h" +#include "config.h" +#include "infobox.h" +#include "tagz.h" + +#define PLUGIN_VERSION "1.1.1" + +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) */ +flac_config_t flac_cfg; + +static file_info_struct file_info_; +static int paused; +static FLAC__FileDecoder *decoder_; +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 DSPs */ + +static HANDLE thread_handle = NULL; /* the handle to the decode thread */ +static DWORD WINAPI DecodeThread(void *b); /* the decode thread procedure */ + +/* + * init/quit + */ + +static void init() +{ + decoder_ = FLAC__file_decoder_new(); + strcpy(lastfn_, ""); + + InitConfig(); + ReadConfig(); + InitInfobox(); +} + +static void quit() +{ + WriteConfig(); + DeinitInfobox(); + FLAC_plugin__decoder_delete(decoder_); + decoder_ = 0; +} + +/* + * open/close + */ + +static int isourfile(char *fn) { return 0; } + +static int play(char *fn) +{ + LONGLONG filesize; + DWORD thread_id; + int maxlatency; + /* checks */ + if (decoder_ == 0) return 1; + if (!(filesize = FileSize(fn))) return -1; + /* init decoder */ + if (!FLAC_plugin__decoder_init(decoder_, fn, filesize, &file_info_, &flac_cfg.output)) + return 1; + strcpy(lastfn_, fn); + /* open output */ + maxlatency = mod_.outMod->Open(file_info_.sample_rate, file_info_.channels, file_info_.output_bits_per_sample, -1, -1); + if (maxlatency < 0) + { + FLAC_plugin__decoder_finish(decoder_); + return 1; + } + /* set defaults */ + mod_.outMod->SetVolume(-666); + mod_.outMod->SetPan(0); + /* initialize vis stuff */ + mod_.SAVSAInit(maxlatency, file_info_.sample_rate); + mod_.VSASetInfo(file_info_.sample_rate, file_info_.channels); + /* set info */ + mod_.SetInfo(file_info_.average_bps, file_info_.sample_rate/1000, file_info_.channels, 1); + /* start playing thread */ + paused = 0; + thread_handle = CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id); + if (!thread_handle) return 1; + + return 0; +} + +static void stop() +{ + if (thread_handle) + { + file_info_.is_playing = false; + if (WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT) + { + FLAC_plugin__show_error("Error while stopping decoding thread."); + TerminateThread(thread_handle, 0); + } + CloseHandle(thread_handle); + thread_handle = NULL; + } + + FLAC_plugin__decoder_finish(decoder_); + mod_.outMod->Close(); + mod_.SAVSADeInit(); +} + +/* + * play control + */ + +static void pause() +{ + paused = 1; + mod_.outMod->Pause(1); +} + +static void unpause() +{ + paused = 0; + mod_.outMod->Pause(0); +} + +static int ispaused() +{ + return paused; +} + +static int getlength() +{ + return file_info_.length_in_msec; +} + +static int getoutputtime() +{ + return mod_.outMod->GetOutputTime(); +} + +static void setoutputtime(int time_in_ms) +{ + file_info_.seek_to = time_in_ms; +} + +static void setvolume(int volume) +{ + mod_.outMod->SetVolume(volume); +} + +static void setpan(int pan) +{ + mod_.outMod->SetPan(pan); +} + +static void eq_set(int on, char data[10], int preamp) {} + +/* + * playing loop + */ + +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: + mod_.SAAddPCMData(data, nch, resolution, position); + mod_.VSAAddPCMData(data, nch, resolution, position); + } +} + +static DWORD WINAPI DecodeThread(void *unused) +{ + 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; + const unsigned fact = channels * (target_bps/8); + + while (file_info_.is_playing) + { + /* seek needed */ + if (file_info_.seek_to != -1) + { + const int pos = FLAC_plugin__seek(decoder_, &file_info_); + if (pos != -1) mod_.outMod->Flush(pos); + } + /* stream ended */ + else if (file_info_.eof) + { + if (!mod_.outMod->IsPlaying()) + { + PostMessage(mod_.hMainWindow, WM_WA_MPEG_EOF, 0, 0); + return 0; + } + Sleep(10); + } + /* decode */ + else + { + /* decode samples */ + int bytes = FLAC_plugin__decode(decoder_, &file_info_, sample_buffer_); + const int n = bytes / fact; + /* visualization */ + do_vis(sample_buffer_, channels, target_bps, mod_.outMod->GetWrittenTime(), n); + /* dsp */ + if (mod_.dsp_isactive()) + bytes = mod_.dsp_dosamples((short*)sample_buffer_, n, target_bps, channels, sample_rate) * fact; + /* output */ + while (mod_.outMod->CanWrite()Write(sample_buffer_, bytes); + /* show bitrate */ + if (flac_cfg.display.show_bps) + { + const int rate = FLAC_plugin__get_rate(mod_.outMod->GetWrittenTime(), mod_.outMod->GetOutputTime(), &file_info_); + if (rate) mod_.SetInfo(rate/1000, file_info_.sample_rate/1000, file_info_.channels, 1); + } + } + } + + return 0; +} + +/* + * title formatting + */ + +static const T_CHAR *get_tag(const T_CHAR *tag, void *param) +{ + FLAC_Plugin__CanonicalTag *t = (FLAC_Plugin__CanonicalTag*)param; + const T_CHAR *val = FLAC_plugin__canonical_get(t, tag); + /* some "user friendly cheavats" */ + if (!val) + { + if (!wcsicmp(tag, L"ARTIST")) + { + val = FLAC_plugin__canonical_get(t, L"PERFORMER"); + if (!val) val = FLAC_plugin__canonical_get(t, L"COMPOSER"); + } + else if (!wcsicmp(tag, L"YEAR") || !wcsicmp(tag, L"DATE")) + { + val = FLAC_plugin__canonical_get(t, L"YEAR_RECORDED"); + if (!val) val = FLAC_plugin__canonical_get(t, L"YEAR_PERFORMED"); + } + } + + return val; +} + +static void format_title(const char *filename, WCHAR *title, unsigned max_size) +{ + FLAC_Plugin__CanonicalTag tag; + + ReadTags(filename, &tag, true); + + tagz_format(flac_cfg.title.tag_format_w, get_tag, NULL, &tag, title, max_size); + + FLAC_plugin__canonical_tag_clear(&tag); +} + +static 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 = file_info_.length_in_msec; + length_in_msec = 0; /* force skip in following code */ + } + } + + if (!FLAC__metadata_get_streaminfo(filename, &streaminfo)) + { + if (length_in_msec) + *length_in_msec = -1; + return; + } + + if (title) + { + static WCHAR buffer[400]; + format_title(filename, buffer, 400); + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, buffer, -1, title, 400, NULL, NULL); + } + + if (length_in_msec) + *length_in_msec = (int)(streaminfo.data.stream_info.total_samples*10 / (streaminfo.data.stream_info.sample_rate/100)); +} + +/* + * interface + */ + +void FLAC_plugin__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); +} + +static void about(HWND hwndParent) +{ + 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); +} + +static void config(HWND hwndParent) +{ + if (DoConfig(mod_.hDllInstance, hwndParent)) + WriteConfig(); +} + +static int infobox(char *fn, HWND hwnd) +{ + DoInfoBox(mod_.hDllInstance, hwnd, fn); + return 0; +} + +/* + * exported stuff + */ + +static In_Module mod_ = +{ + IN_VER, + "Reference FLAC Decoder v" PLUGIN_VERSION, + 0, /* hMainWindow */ + 0, /* hDllInstance */ + "FLAC\0FLAC Audio File (*.FLAC)\0", + 1, /* is_seekable */ + 1, /* uses output */ + config, + about, + init, + quit, + getfileinfo, + infobox, + 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; +} diff --git a/src/plugin_winamp2/in_flac.dsp b/src/plugin_winamp2/in_flac.dsp index 8a1b5cc8..f2fa67e5 100644 --- a/src/plugin_winamp2/in_flac.dsp +++ b/src/plugin_winamp2/in_flac.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # 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 -# 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 +# 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 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" @@ -70,7 +70,8 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # 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 -# 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 +# 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 +# SUBTRACT CPP /WX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" @@ -126,6 +127,14 @@ SOURCE=.\infobox.h # End Source File # Begin Source File +SOURCE=.\playback.c +# End Source File +# Begin Source File + +SOURCE=.\playback.h +# End Source File +# Begin Source File + SOURCE=.\resource.h # End Source File # Begin Source File diff --git a/src/plugin_winamp2/infobox.c b/src/plugin_winamp2/infobox.c index 43a1fe51..d94054d0 100644 --- a/src/plugin_winamp2/infobox.c +++ b/src/plugin_winamp2/infobox.c @@ -1,290 +1,430 @@ +/* in_flac - Winamp2 FLAC input plugin + * 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 #include #include "FLAC/all.h" #include "plugin_common/all.h" #include "infobox.h" +#include "config.h" #include "resource.h" typedef struct { - char filename[MAX_PATH]; + char filename[MAX_PATH]; + FLAC_Plugin__CanonicalTag tag; } LOCALDATA; -static char buffer[256]; +static char buffer[1024]; static char *genres = NULL; -static int genresSize = 0, genresCount = 0, genresChanged = 0; +static DWORD genresSize = 0, genresCount = 0; +static BOOL genresChanged = FALSE, isNT; static const char infoTitle[] = "FLAC File Info"; -//fixme int64 -static __inline DWORD FileSize(const char *file) -{ - HANDLE hFile = CreateFile(file, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - DWORD res; +/* + * Genres + */ - if (hFile == INVALID_HANDLE_VALUE) return 0; - res = GetFileSize(hFile, 0); - CloseHandle(hFile); - return res; -} +/* TODO: write genres in utf-8 ? */ static __inline int GetGenresFileName(char *buffer, int size) { - char *c; + char *c; - if (!GetModuleFileName(NULL, buffer, size)) - return 0; - c = strrchr(buffer, '\\'); - if (!c) return 0; - strcpy(c+1, "genres.txt"); + if (!GetModuleFileName(NULL, buffer, size)) + return 0; + c = strrchr(buffer, '\\'); + if (!c) return 0; + strcpy(c+1, "genres.txt"); - return 1; + return 1; } static void LoadGenres() { - HANDLE hFile; - DWORD spam; - char *c; + HANDLE hFile; + DWORD spam; + char *c; - if (!GetGenresFileName(buffer, sizeof(buffer))) return; - // load file - hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) return; - genresSize = GetFileSize(hFile, 0); - if (!genresSize) return; - genres = (char*)malloc(genresSize+2); - if (!genres) return; - if (!ReadFile(hFile, genres, genresSize, &spam, NULL)) - { - free(genres); - genres = NULL; - return; - } - genres[genresSize] = 0; - genres[genresSize+1] = 0; - // replace newlines - genresChanged = 0; - genresCount = 1; + FLAC__ASSERT(0 != genres); - for (c=genres; *c; c++) - { - if (*c == 10) - { - *c = 0; - if (*(c+1)) - genresCount++; - else genresSize--; - } - } + if (!GetGenresFileName(buffer, sizeof(buffer))) return; + /* load file */ + hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return; + genresSize = GetFileSize(hFile, 0); + if (genresSize && (genres = (char*)malloc(genresSize+2))) + { + if (!ReadFile(hFile, genres, genresSize, &spam, NULL) || spam!=genresSize) + { + free(genres); + genres = NULL; + } + else + { + genres[genresSize] = 0; + genres[genresSize+1] = 0; + /* replace newlines */ + genresChanged = FALSE; + genresCount = 1; - CloseHandle(hFile); + for (c=genres; *c; c++) + { + if (*c == 10) + { + *c = 0; + if (*(c+1)) + genresCount++; + else genresSize--; + } + } + } + } + + CloseHandle(hFile); } static void SaveGenres(HWND hlist) { - HANDLE hFile; - DWORD spam; - int i, count, len; + HANDLE hFile; + DWORD spam; + int i, count, len; - if (!GetGenresFileName(buffer, sizeof(buffer))) return; - // write file - hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) return; + if (!GetGenresFileName(buffer, sizeof(buffer))) return; + /* write file */ + hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return; - count = SendMessage(hlist, CB_GETCOUNT, 0, 0); - for (i=0; itag, y), -1, buffer, sizeof(buffer), NULL, NULL); \ + SetDlgItemText(hwnd, x, buffer) + +#define GetText(x,y) GetDlgItemText(hwnd, x, buffer, sizeof(buffer)); \ + if (*buffer) FLAC_plugin__canonical_set_ansi(&data->tag, y, buffer); \ + else FLAC_plugin__canonical_remove_all(&data->tag, y) + +#define SetTextW(x,y) SetDlgItemTextW(hwnd, x, FLAC_plugin__canonical_get(&data->tag, y)) + +#define GetTextW(x,y) GetDlgItemTextW(hwnd, x, (WCHAR*)buffer, sizeof(buffer)/2); \ + if (*(WCHAR*)buffer) FLAC_plugin__canonical_set(&data->tag, y, (WCHAR*)buffer); \ + else FLAC_plugin__canonical_remove_all(&data->tag, y) + + +static BOOL InitInfoboxInfo(HWND hwnd, const char *file) +{ + LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA)); FLAC__StreamMetadata streaminfo; - FLAC_Plugin__CanonicalTag tag; - DWORD filesize, length, bps, ratio; + DWORD length, bps, ratio, rg; + LONGLONG filesize; - SetWindowLong(hwnd, GWL_USERDATA, (LONG)data); - // file name - strncpy(data->filename, file, sizeof(data->filename)); - SetDlgItemText(hwnd, IDC_NAME, file); - // stream data - filesize = FileSize(file); - if (!filesize) return FALSE; + SetWindowLong(hwnd, GWL_USERDATA, (LONG)data); + /* file name */ + strncpy(data->filename, file, sizeof(data->filename)); + SetDlgItemText(hwnd, IDC_NAME, file); + /* stream data and vorbis comment */ + filesize = FileSize(file); + if (!filesize) return FALSE; if (!FLAC__metadata_get_streaminfo(file, &streaminfo)) - return FALSE; + return FALSE; + ReadTags(file, &data->tag, false); - length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate); - bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate)); - ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample); + length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate); + bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate)); + ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample); + rg = FLAC_plugin__canonical_get(&data->tag, L"REPLAYGAIN_TRACK_GAIN") ? 1 : 0; + rg |= FLAC_plugin__canonical_get(&data->tag, L"REPLAYGAIN_ALBUM_GAIN") ? 2 : 0; - sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n" - "File size: %d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n", - streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample, - streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples, - length/60, length%60, bps, ratio/10, ratio%10); - //todo: replaygain + sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n" + "File size: %I64d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n" + "ReplayGain: %s\n", + streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample, + streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples, + length/60, length%60, bps, ratio/10, ratio%10, + rg==3 ? "track gain\nReplayGain: album gain" : rg==2 ? "album gain" : rg==1 ? "track gain" : "not present"); - SetDlgItemText(hwnd, IDC_INFO, buffer); - // tag - FLAC_plugin__canonical_tag_init(&tag); - FLAC_plugin__canonical_tag_get_combined(file, &tag); + SetDlgItemText(hwnd, IDC_INFO, buffer); + /* tag */ + if (isNT) + { + SetTextW(IDC_TITLE, L"TITLE"); + SetTextW(IDC_ARTIST, L"ARTIST"); + SetTextW(IDC_ALBUM, L"ALBUM"); + SetTextW(IDC_COMMENT, L"DESCRIPTION"); + SetTextW(IDC_YEAR, L"DATE"); + SetTextW(IDC_TRACK, L"TRACKNUMBER"); + SetTextW(IDC_GENRE, L"GENRE"); + } + else + { + SetText(IDC_TITLE, L"TITLE"); + SetText(IDC_ARTIST, L"ARTIST"); + SetText(IDC_ALBUM, L"ALBUM"); + SetText(IDC_COMMENT, L"DESCRIPTION"); + SetText(IDC_YEAR, L"DATE"); + SetText(IDC_TRACK, L"TRACKNUMBER"); + SetText(IDC_GENRE, L"GENRE"); + } - SetText(IDC_TITLE, tag.title); - SetText(IDC_ARTIST, tag.performer ? tag.performer : tag.composer); - SetText(IDC_ALBUM, tag.album); - SetText(IDC_COMMENT, tag.comment); - SetText(IDC_YEAR, tag.year_recorded ? tag.year_recorded : tag.year_performed); - SetText(IDC_TRACK, tag.track_number); - SetText(IDC_GENRE, tag.genre); - - FLAC_plugin__canonical_tag_clear(&tag); - - return TRUE; + return TRUE; } static void __inline SetTag(HWND hwnd, const char *filename, FLAC_Plugin__CanonicalTag *tag) { - strcpy(buffer, infoTitle); + strcpy(buffer, infoTitle); if (FLAC_plugin__vorbiscomment_set(filename, tag)) - strcat(buffer, " [Updated]"); - else strcat(buffer, " [Failed]"); + strcat(buffer, " [Updated]"); + else strcat(buffer, " [Failed]"); - SetWindowText(hwnd, buffer); + SetWindowText(hwnd, buffer); } static void UpdateTag(HWND hwnd) { - LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - FLAC_Plugin__CanonicalTag tag; - FLAC_plugin__canonical_tag_init(&tag); + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - // get fields - GetText(IDC_TITLE, tag.title); - GetText(IDC_ARTIST, tag.composer); - GetText(IDC_ALBUM, tag.album); - GetText(IDC_COMMENT, tag.comment); - GetText(IDC_YEAR, tag.year_recorded); - GetText(IDC_TRACK, tag.track_number); - GetText(IDC_GENRE, tag.genre); + /* get fields */ + if (isNT) + { + GetTextW(IDC_TITLE, L"TITLE"); + GetTextW(IDC_ARTIST, L"ARTIST"); + GetTextW(IDC_ALBUM, L"ALBUM"); + GetTextW(IDC_COMMENT, L"DESCRIPTION"); + GetTextW(IDC_YEAR, L"DATE"); + GetTextW(IDC_TRACK, L"TRACKNUMBER"); + GetTextW(IDC_GENRE, L"GENRE"); - // update genres list - if (tag.genre) - { - HWND hgen = GetDlgItem(hwnd, IDC_GENRE); + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, FLAC_plugin__canonical_get(&data->tag, L"GENRE"), -1, buffer, sizeof(buffer), NULL, NULL); + } + else + { + GetText(IDC_TITLE, L"TITLE"); + GetText(IDC_ARTIST, L"ARTIST"); + GetText(IDC_ALBUM, L"ALBUM"); + GetText(IDC_COMMENT, L"DESCRIPTION"); + GetText(IDC_YEAR, L"DATE"); + GetText(IDC_TRACK, L"TRACKNUMBER"); + GetText(IDC_GENRE, L"GENRE"); + } - if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)tag.genre) == CB_ERR) - { - genresChanged = 1; - SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)tag.genre); - } - } + /* update genres list (buffer should contain genre) */ + if (buffer[0]) AddGenre(hwnd, buffer); - // write tag - SetTag(hwnd, data->filename, &tag); - FLAC_plugin__canonical_tag_clear(&tag); + /* write tag */ + SetTag(hwnd, data->filename, &data->tag); } static void RemoveTag(HWND hwnd) { - LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); - FLAC_Plugin__CanonicalTag tag; - FLAC_plugin__canonical_tag_init(&tag); + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); + FLAC_plugin__canonical_tag_clear(&data->tag); - SetText(IDC_TITLE, ""); - SetText(IDC_ARTIST, ""); - SetText(IDC_ALBUM, ""); - SetText(IDC_COMMENT, ""); - SetText(IDC_YEAR, ""); - SetText(IDC_TRACK, ""); - SetText(IDC_GENRE, ""); + SetDlgItemText(hwnd, IDC_TITLE, ""); + SetDlgItemText(hwnd, IDC_ARTIST, ""); + SetDlgItemText(hwnd, IDC_ALBUM, ""); + SetDlgItemText(hwnd, IDC_COMMENT, ""); + SetDlgItemText(hwnd, IDC_YEAR, ""); + SetDlgItemText(hwnd, IDC_TRACK, ""); + SetDlgItemText(hwnd, IDC_GENRE, ""); - SetTag(hwnd, data->filename, &tag); + SetTag(hwnd, data->filename, &data->tag); } static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) - { - // init - case WM_INITDIALOG: - SetWindowText(hwnd, infoTitle); - // init genres list - { - HWND hgen = GetDlgItem(hwnd, IDC_GENRE); - char *c; + switch (msg) + { + /* init */ + case WM_INITDIALOG: + SetWindowText(hwnd, infoTitle); + InitGenres(hwnd); + /* init fields */ + if (!InitInfoboxInfo(hwnd, (const char*)lParam)) + PostMessage(hwnd, WM_CLOSE, 0, 0); + return TRUE; + /* destroy */ + case WM_DESTROY: + { + LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA); + FLAC_plugin__canonical_tag_clear(&data->tag); + LocalFree(data); + DeinitGenres(hwnd, FALSE); + } + break; + /* commands */ + case WM_COMMAND: + switch (LOWORD(wParam)) + { + /* ok/cancel */ + case IDOK: + case IDCANCEL: + EndDialog(hwnd, LOWORD(wParam)); + return TRUE; + /* save */ + case IDC_UPDATE: + UpdateTag(hwnd); + break; + /* remove */ + case IDC_REMOVE: + RemoveTag(hwnd); + break; + } + break; + } - // set text length limit to 64 chars - SendMessage(hgen, CB_LIMITTEXT, 64, 0); - // try to load genres - if (!genres) LoadGenres(hgen); - // add the to list - if (genres) - { - SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize); - - for (c = genres; *c; c += strlen(c)+1) - SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c); - } - } - // init fields - if (!InitInfobox(hwnd, (const char*)lParam)) - PostMessage(hwnd, WM_CLOSE, 0, 0); - return TRUE; - // destroy - case WM_DESTROY: - if (genresChanged) - { - SaveGenres(GetDlgItem(hwnd, IDC_GENRE)); - free(genres); - genres = 0; - } - LocalFree((LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA)); - break; - // commands - case WM_COMMAND: - switch (LOWORD(wParam)) - { - // ok/cancel - case IDOK: - case IDCANCEL: - EndDialog(hwnd, LOWORD(wParam)); - return TRUE; - // save - case IDC_UPDATE: - UpdateTag(hwnd); - break; - // remove - case IDC_REMOVE: - RemoveTag(hwnd); - break; - } - break; - } - - return 0; + return 0; } +/* + * Helpers + */ + +ULONGLONG FileSize(const char *fileName) +{ + LARGE_INTEGER res; + HANDLE hFile = CreateFile(fileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile == INVALID_HANDLE_VALUE) return 0; + res.LowPart = GetFileSize(hFile, &res.HighPart); + CloseHandle(hFile); + return res.QuadPart; +} + +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; +} + +void ReadTags(const char *fileName, FLAC_Plugin__CanonicalTag *tag, BOOL forDisplay) +{ + FLAC_plugin__canonical_tag_init(tag); + FLAC_plugin__vorbiscomment_get(fileName, tag, forDisplay ? flac_cfg.title.sep : NULL); + + if (flac_cfg.title.read_v1) + FLAC_plugin__canonical_tag_add_id3v1(fileName, tag); + + /* add file name */ + if (forDisplay) + { + char *c; + FLAC_plugin__canonical_set_ansi(tag, L"filepath", fileName); + + strcpy(buffer, GetFileName(fileName)); + if (c = strrchr(buffer, '.')) *c = 0; + FLAC_plugin__canonical_set_ansi(tag, L"filename", buffer); + } +} + +/* + * Front-end + */ + +void InitInfobox() +{ + isNT = !(GetVersion() & 0x80000000); +} + +void DeinitInfobox() +{ + DeinitGenres(NULL, true); +} void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename) { - DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename); + DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename); } diff --git a/src/plugin_winamp2/infobox.h b/src/plugin_winamp2/infobox.h index 2d562e0c..dfd7608f 100644 --- a/src/plugin_winamp2/infobox.h +++ b/src/plugin_winamp2/infobox.h @@ -1,5 +1,28 @@ -// -// prototypes -// +/* in_flac - Winamp2 FLAC input plugin + * 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. + */ +/* + * prototypes + */ + +ULONGLONG FileSize(const char *fileName); +void ReadTags(const char *fileName, FLAC_Plugin__CanonicalTag *tag, BOOL forDisplay); + +void InitInfobox(); +void DeinitInfobox(); void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename); diff --git a/src/plugin_winamp2/playback.c b/src/plugin_winamp2/playback.c new file mode 100644 index 00000000..992f84ec --- /dev/null +++ b/src/plugin_winamp2/playback.c @@ -0,0 +1,292 @@ +/* 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 +#include "playback.h" +#include "share/grabbag.h" + + +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 unsigned wide_samples_in_reservoir_; +static output_config_t cfg; /* local copy */ + +static unsigned bh_index_last_w, bh_index_last_o, written_time_last; +static FLAC__int64 decode_position, decode_position_last; + +/* + * callbacks + */ + +static 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; +} + +static 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) + { + FLAC_plugin__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); + } + } +} + +static 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 (cfg.misk.stop_err || status!=FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + file_info->abort_flag = true; +} + +/* + * init/delete + */ + +FLAC__bool FLAC_plugin__decoder_init(FLAC__FileDecoder *decoder, const char *filename, FLAC__int64 filesize, file_info_struct *file_info, output_config_t *config) +{ + FLAC__ASSERT(decoder); + FLAC_plugin__decoder_finish(decoder); + /* init 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) + { + FLAC_plugin__show_error("Error while initializing decoder (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]); + return false; + } + /* process */ + cfg = *config; + wide_samples_in_reservoir_ = 0; + file_info->is_playing = false; + file_info->abort_flag = false; + file_info->has_replaygain = false; + + if (!FLAC__file_decoder_process_until_end_of_metadata(decoder)) + { + FLAC_plugin__show_error("Error while processing metadata (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]); + return false; + } + /* check results */ + if (file_info->abort_flag) return false; /* metadata callback already popped up the error dialog */ + /* init replaygain */ + 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); + /* more inits */ + file_info->eof = false; + file_info->seek_to = -1; + file_info->is_playing = true; + file_info->average_bps = (unsigned)(filesize / (125.*file_info->total_samples/file_info->sample_rate)); + + bh_index_last_w = 0; + bh_index_last_o = BITRATE_HIST_SIZE; + decode_position = 0; + decode_position_last = 0; + written_time_last = 0; + + return true; +} + +void FLAC_plugin__decoder_finish(FLAC__FileDecoder *decoder) +{ + if (decoder && FLAC__file_decoder_get_state(decoder)!=FLAC__FILE_DECODER_UNINITIALIZED) + FLAC__file_decoder_finish(decoder); +} + +void FLAC_plugin__decoder_delete(FLAC__FileDecoder *decoder) +{ + if (decoder) + { + FLAC_plugin__decoder_finish(decoder); + FLAC__file_decoder_delete(decoder); + } +} + +/* + * decode + */ + +int FLAC_plugin__seek(FLAC__FileDecoder *decoder, file_info_struct *file_info) +{ + int pos; + const FLAC__uint64 target_sample = + (FLAC__uint64)file_info->total_samples*file_info->seek_to / file_info->length_in_msec; + + if (!FLAC__file_decoder_seek_absolute(decoder, target_sample)) + return -1; + + file_info->seek_to = -1; + file_info->eof = false; + wide_samples_in_reservoir_ = 0; + pos = (int)(target_sample*1000 / file_info->sample_rate); + + bh_index_last_o = bh_index_last_w = (pos/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE; + if (!FLAC__file_decoder_get_decode_position(decoder, &decode_position)) + decode_position = 0; + + return pos; +} + +unsigned FLAC_plugin__decode(FLAC__FileDecoder *decoder, file_info_struct *file_info, char *sample_buffer) +{ + /* fill reservoir */ + while (wide_samples_in_reservoir_ < SAMPLES_PER_WRITE) + { + if (FLAC__file_decoder_get_state(decoder) == FLAC__FILE_DECODER_END_OF_FILE) + { + file_info->eof = true; + break; + } + else if (!FLAC__file_decoder_process_single(decoder)) + { + FLAC_plugin__show_error("Error while processing frame (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]); + file_info->eof = true; + break; + } + if (!FLAC__file_decoder_get_decode_position(decoder, &decode_position)) + decode_position = 0; + } + /* output samples */ + if (wide_samples_in_reservoir_ > 0) + { + const unsigned n = min(wide_samples_in_reservoir_, SAMPLES_PER_WRITE); + const unsigned channels = file_info->channels; + unsigned i; + int bytes; + + if (cfg.replaygain.enable && file_info->has_replaygain) + { + bytes = FLAC__replaygain_synthesis__apply_gain( + sample_buffer, + true, /* little_endian_data_out */ + file_info->output_bits_per_sample == 8, /* unsigned_data_out */ + reservoir__, + n, + channels, + file_info->bits_per_sample, + file_info->output_bits_per_sample, + file_info->replay_scale, + cfg.replaygain.hard_limit, + cfg.resolution.replaygain.dither, + &file_info->dither_context + ); + } + else + { + bytes = FLAC__plugin_common__pack_pcm_signed_little_endian( + sample_buffer, + reservoir__, + n, + channels, + file_info->bits_per_sample, + file_info->output_bits_per_sample + ); + } + + wide_samples_in_reservoir_ -= n; + for (i = 0; i < channels; i++) + memmove(&reservoir_[i][0], &reservoir_[i][n], sizeof(reservoir_[0][0]) * wide_samples_in_reservoir_); + + return bytes; + } + else + { + file_info->eof = true; + return 0; + } +} + +int FLAC_plugin__get_rate(unsigned written_time, unsigned output_time, file_info_struct *file_info) +{ + static int bitrate_history_[BITRATE_HIST_SIZE]; + unsigned bh_index_w = (written_time/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE; + unsigned bh_index_o = (output_time/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE; + + /* written bitrate */ + if (bh_index_w != bh_index_last_w) + { + bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE-1)%BITRATE_HIST_SIZE] = + decode_position>decode_position_last && written_time > written_time_last ? + (unsigned)(8000*(decode_position - decode_position_last)/(written_time - written_time_last)) : + file_info->average_bps; + + bh_index_last_w = bh_index_w; + written_time_last = written_time; + decode_position_last = decode_position; + } + + /* output bitrate */ + if (bh_index_o!=bh_index_last_o && bh_index_o!=bh_index_last_w) + { + bh_index_last_o = bh_index_o; + return bitrate_history_[bh_index_o]; + } + + return 0; +} diff --git a/src/plugin_winamp2/playback.h b/src/plugin_winamp2/playback.h new file mode 100644 index 00000000..b7758767 --- /dev/null +++ b/src/plugin_winamp2/playback.h @@ -0,0 +1,92 @@ +/* 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 "FLAC/all.h" +#include "share/replaygain_synthesis.h" +#include "plugin_common/all.h" + +/* + * constants + */ + +#define SAMPLES_PER_WRITE 576 + +#define BITRATE_HIST_SEGMENT_MSEC 500 +#define BITRATE_HIST_SIZE 64 + +/* + * common structures + */ + +typedef struct { + volatile FLAC__bool is_playing; + volatile FLAC__bool abort_flag; + volatile FLAC__bool eof; + volatile int seek_to; + unsigned total_samples; + unsigned bits_per_sample; + unsigned output_bits_per_sample; + unsigned channels; + unsigned sample_rate; + unsigned length_in_msec; + unsigned average_bps; + FLAC__bool has_replaygain; + double replay_scale; + DitherContext dither_context; +} file_info_struct; + + +typedef struct { + struct { + FLAC__bool enable; + FLAC__bool album_mode; + int preamp; + FLAC__bool hard_limit; + } replaygain; + struct { + struct { + FLAC__bool dither_24_to_16; + } normal; + struct { + FLAC__bool dither; + int noise_shaping; /* value must be one of NoiseShaping enum, see plugin_common/replaygain_synthesis.h */ + int bps_out; + } replaygain; + } resolution; + struct { + FLAC__bool stop_err; + } misk; +} output_config_t; + +/* + * protopytes + */ + +FLAC__bool FLAC_plugin__decoder_init(FLAC__FileDecoder *decoder, const char *filename, FLAC__int64 filesize, file_info_struct *file_info, output_config_t *config); +void FLAC_plugin__decoder_finish(FLAC__FileDecoder *decoder); +void FLAC_plugin__decoder_delete(FLAC__FileDecoder *decoder); + +int FLAC_plugin__seek(FLAC__FileDecoder *decoder, file_info_struct *file_info); +unsigned FLAC_plugin__decode(FLAC__FileDecoder *decoder, file_info_struct *file_info, char *sample_buffer); +int FLAC_plugin__get_rate(unsigned written_time, unsigned output_time, file_info_struct *file_info); + +/* + * these should be defined in plug-in + */ + +extern void FLAC_plugin__show_error(const char *message,...); diff --git a/src/plugin_winamp2/resource.h b/src/plugin_winamp2/resource.h index f13f3bdc..f09b4afc 100644 --- a/src/plugin_winamp2/resource.h +++ b/src/plugin_winamp2/resource.h @@ -23,11 +23,17 @@ #define IDC_TITLE 1010 #define IDC_TAGZ_HELP 1011 #define IDC_ARTIST 1011 +#define IDC_TAGZ_DEFAULT 1012 +#define IDC_SEP 1013 #define IDC_NAME 1014 #define IDC_INFO 1015 #define IDC_GENRE 1017 #define IDC_REMOVE 1020 #define IDC_UPDATE 1021 +#define IDC_ID3V1 1030 +#define IDC_RESERVE 1032 +#define IDC_BPS 1036 +#define IDC_ERRORS 1037 // Next default values for new objects // @@ -35,7 +41,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1030 +#define _APS_NEXT_CONTROL_VALUE 1037 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/plugin_winamp2/resource.rc b/src/plugin_winamp2/resource.rc index 6300877b..a76457bd 100644 --- a/src/plugin_winamp2/resource.rc +++ b/src/plugin_winamp2/resource.rc @@ -67,7 +67,7 @@ BEGIN EDITTEXT IDC_ALBUM,43,60,137,12,ES_AUTOHSCROLL RTEXT "&Comment",IDC_STATIC,8,77,31,8 EDITTEXT IDC_COMMENT,43,75,137,12,ES_AUTOHSCROLL - RTEXT "&Year",IDC_STATIC,8,92,31,8 + RTEXT "&Date",IDC_STATIC,8,92,31,8 EDITTEXT IDC_YEAR,43,90,40,12,ES_AUTOHSCROLL RTEXT "Track &number",IDC_STATIC,90,92,46,8 EDITTEXT IDC_TRACK,141,90,39,12,ES_AUTOHSCROLL @@ -133,10 +133,23 @@ IDD_CONFIG_GENERAL DIALOG DISCARDABLE 0, 0, 226, 171 STYLE DS_CONTROL | WS_CHILD FONT 8, "MS Sans Serif" BEGIN - GROUPBOX " Title Formatting ",IDC_STATIC,2,2,220,43 - LTEXT "&Title:",IDC_STATIC,8,17,16,8 + GROUPBOX " Title Formatting ",IDC_STATIC,2,2,220,58 + LTEXT "&Title",IDC_STATIC,8,17,14,8 EDITTEXT IDC_TITLE,27,15,188,12,ES_AUTOHSCROLL + PUSHBUTTON "default",IDC_TAGZ_DEFAULT,156,28,30,10 PUSHBUTTON "help",IDC_TAGZ_HELP,188,28,27,10 + LTEXT "Separate tag values &with",IDC_STATIC,8,43,79,8 + EDITTEXT IDC_SEP,91,41,27,12,ES_AUTOHSCROLL + CONTROL "Read ID3v&1 tags",IDC_ID3V1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,147,43,70,10 + GROUPBOX " Tag Editor ",IDC_STATIC,2,63,220,30 + CONTROL "Reserve space for &VorbisComment",IDC_RESERVE,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,8,76,125,10 + GROUPBOX " Misk ",IDC_STATIC,2,96,220,72 + CONTROL "&Show average bitrate while playing",IDC_BPS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,108,125,10 + CONTROL "Stop on &all errors",IDC_ERRORS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,120,69,10 END IDD_CONFIG_OUTPUT DIALOG DISCARDABLE 0, 0, 224, 171 @@ -159,11 +172,11 @@ BEGIN CONTROL "&Dither 24bps to 16bps",IDC_DITHER,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,16,84,85,10 GROUPBOX " With ReplayGain ",IDC_STATIC,7,104,209,47 - CONTROL "E&nable dithering",IDC_DITHERRG,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,134,67,10 LTEXT "&Output bit depth",IDC_STATIC,16,119,52,8 COMBOBOX IDC_TO,71,116,39,43,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "E&nable dithering",IDC_DITHERRG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,16,134,67,10 LTEXT "Noise &shaping",IDC_STATIC,113,135,46,8 COMBOBOX IDC_SHAPE,164,132,46,48,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP diff --git a/src/plugin_winamp2/tagz.cpp b/src/plugin_winamp2/tagz.cpp index ebece859..89e64a37 100644 --- a/src/plugin_winamp2/tagz.cpp +++ b/src/plugin_winamp2/tagz.cpp @@ -125,7 +125,7 @@ static char roman_num[]= }; -static int is_roman(T_CHAR * ptr)//could be more smart i think +static int is_roman(T_CHAR * ptr)/* could be more smart i think */ { if (ptr[0]==']' && ptr[1]=='[' && separator(ptr[2])) return 1; while(!separator(*ptr)) @@ -164,7 +164,7 @@ MAKEFUNC(If) if (n_src!=3) return false; out.AddString(src[found_src[0] ? 1 : 2]); - return true; + return true; } MAKEFUNC(If2) @@ -172,7 +172,7 @@ MAKEFUNC(If2) if (n_src!=2) return false; out.AddString(src[found_src[0] ? 0 : 1]); - return true; + return true; } @@ -181,7 +181,7 @@ MAKEFUNC(Iflonger) if (n_src!=4) return false; out.AddString(src[(int)t_strlen(src[0])>t_atoi(src[1]) ? 2 : 3]); - return true; + return true; } MAKEFUNC(Ifgreater) @@ -189,7 +189,7 @@ MAKEFUNC(Ifgreater) if (n_src!=4) return false; out.AddString(src[t_atoi(src[0])>t_atoi(src[1]) ? 2 : 3]); - return true; + return true; } MAKEFUNC(Upper) @@ -201,7 +201,7 @@ MAKEFUNC(Upper) while(*s) out.AddChar(toupper(*(s++))); - return true; + return true; } MAKEFUNC(Lower) @@ -213,7 +213,7 @@ MAKEFUNC(Lower) while(*s) out.AddChar(tolower(*(s++))); - return true; + return true; } MAKEFUNC(Pad) @@ -222,18 +222,18 @@ MAKEFUNC(Pad) T_CHAR *fill=_TX(" "); if (n_src==3 && src[2][0]) - fill = src[2]; + fill = src[2]; int num = t_atoi(src[1]); T_CHAR *p = src[0]; - while (*p) { out.AddChar(*(p++)); num--; } + while (*p) { out.AddChar(*(p++)); num--; } UINT fl = t_strlen(fill); while (num>0) - out.AddChar(fill[(--num)%fl]); + out.AddChar(fill[(--num)%fl]); - return true; + return true; } MAKEFUNC(Cut) @@ -245,7 +245,7 @@ MAKEFUNC(Cut) while (*p && num>0) {out.AddChar(*(p++));num--;} - return true; + return true; } MAKEFUNC(PadCut) @@ -254,7 +254,7 @@ MAKEFUNC(PadCut) T_CHAR *fill = _TX(" "); if (n_src==3 && src[2][0]) - fill = src[2]; + fill = src[2]; int num = t_atoi(src[1]); T_CHAR *p = src[0]; @@ -263,13 +263,13 @@ MAKEFUNC(PadCut) UINT fl=t_strlen(fill); while (num>0) - out.AddChar(fill[(--num)%fl]); + out.AddChar(fill[(--num)%fl]); - return true; + return true; } -// abbr(string) -// abbr(string,len) +/* abbr(string) */ +/* abbr(string,len) */ MAKEFUNC(Abbr) { if (n_src==0 || n_src>2) return false; @@ -301,7 +301,7 @@ MAKEFUNC(Abbr) meta++; } - return true; + return true; } @@ -324,7 +324,7 @@ MAKEFUNC(Caps) out.AddChar(c); } - return true; + return true; } MAKEFUNC(Caps2) @@ -344,7 +344,7 @@ MAKEFUNC(Caps2) out.AddChar(c); } - return true; + return true; } MAKEFUNC(Longest) @@ -359,7 +359,7 @@ MAKEFUNC(Longest) } if (ptr) out.AddString(ptr); - return true; + return true; } MAKEFUNC(Shortest) @@ -374,7 +374,7 @@ MAKEFUNC(Shortest) } if (ptr) out.AddString(ptr); - return true; + return true; } MAKEFUNC(Num) @@ -387,20 +387,20 @@ MAKEFUNC(Num) sprintf(tmp,tmp1,t_atoi(src[0])); out.AddString(tmp); - return true; + return true; } MAKEFUNC(Hex) { if (n_src!=2) return false; - T_CHAR tmp[16]; + T_CHAR tmp[16]; T_CHAR tmp1[16]; sprintf(tmp1,_TX("%%0%ux"),t_atoi(src[1])); sprintf(tmp,tmp1,t_atoi(src[0])); out.AddString(tmp); - return true; + return true; } MAKEFUNC(StrChr) @@ -412,10 +412,10 @@ MAKEFUNC(StrChr) while (*p && *p!=s) p++; if (*p==s) - out.AddInt(1+p-src[0]); + out.AddInt(1+p-src[0]); else out.AddChar('0'); - return true; + return true; } MAKEFUNC(StrRChr) @@ -432,10 +432,10 @@ MAKEFUNC(StrRChr) } if (p1) - out.AddInt(1+p1-src[0]); + out.AddInt(1+p1-src[0]); else out.AddChar('0'); - return true; + return true; } MAKEFUNC(StrStr) @@ -445,14 +445,14 @@ MAKEFUNC(StrStr) T_CHAR * p = t_strstr(src[0],src[1]); if (p) - out.AddInt(1+p-src[0]); + out.AddInt(1+p-src[0]); else out.AddChar('0'); - return true; + return true; } -// substr(string, index) -// substr(string, index, length) +/* substr(string, index) */ +/* substr(string, index, length) */ MAKEFUNC(SubStr) { if (n_src<2 || n_src>3) return false; @@ -472,15 +472,15 @@ MAKEFUNC(SubStr) out.AddChar(src[0][n1++]); } - return true; + return true; } MAKEFUNC(Len) { if (n_src!=1) return false; - out.AddInt(t_strlen(src[0])); - return true; + out.AddInt(t_strlen(src[0])); + return true; } MAKEFUNC(Add) @@ -493,7 +493,7 @@ MAKEFUNC(Add) out.AddInt(s); - return true; + return true; } MAKEFUNC(Sub) @@ -508,7 +508,7 @@ MAKEFUNC(Sub) out.AddInt(s); - return true; + return true; } MAKEFUNC(Mul) @@ -521,7 +521,7 @@ MAKEFUNC(Mul) out.AddInt(s); - return true; + return true; } MAKEFUNC(Div) @@ -540,7 +540,7 @@ MAKEFUNC(Div) out.AddInt(s); - return true; + return true; } MAKEFUNC(Mod) @@ -559,7 +559,7 @@ MAKEFUNC(Mod) out.AddInt(s); - return true; + return true; } MAKEFUNC(Max) @@ -576,7 +576,7 @@ MAKEFUNC(Max) } out.AddInt(m); - return true; + return true; } MAKEFUNC(Min) @@ -593,10 +593,10 @@ MAKEFUNC(Min) } out.AddInt(m); - return true; + return true; } -// replace(string, what_to_replace, replacement) +/* replace(string, what_to_replace, replacement) */ MAKEFUNC(Replace) { if (n_src!=3) return false; @@ -616,7 +616,7 @@ MAKEFUNC(Replace) else out.AddChar(*p++); } - return true; + return true; } struct @@ -671,11 +671,11 @@ private: int found; void Error(T_CHAR *e=0) - { - str.Reset(); - str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]")); - found++; // force displaying - } + { + str.Reset(); + str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]")); + found++; /* force displaying */ + } T_CHAR * _FMT(T_CHAR * s,UINT *f=0) { @@ -739,7 +739,7 @@ private: *s1=0; const T_CHAR * tag=f(spec,fp); *s1='%'; - //if (!tag) tag=tag_unknown; + /*if (!tag) tag=tag_unknown; */ if (tag && tag[0]) { found++; @@ -784,25 +784,25 @@ private: for (n=0; n, eg. \"%artist%\"\n" diff --git a/src/plugin_winamp2/tagz.h b/src/plugin_winamp2/tagz.h index b7a45c40..6b0d635f 100644 --- a/src/plugin_winamp2/tagz.h +++ b/src/plugin_winamp2/tagz.h @@ -14,7 +14,7 @@ typedef unsigned short T_CHAR; #define T_CHAR char #endif -typedef const T_CHAR* (*TAGFUNC)(const T_CHAR *tag,void *p); // return 0 if not found +typedef const T_CHAR* (*TAGFUNC)(const T_CHAR *tag,void *p); /* return 0 if not found */ typedef void (*TAGFREEFUNC)(const T_CHAR *tag,void *p); diff --git a/src/plugin_xmms/charset.c b/src/plugin_xmms/charset.c index ad11d740..3caf75a2 100644 --- a/src/plugin_xmms/charset.c +++ b/src/plugin_xmms/charset.c @@ -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) diff --git a/src/plugin_xmms/charset.h b/src/plugin_xmms/charset.h index 041f29fd..7d5e763c 100644 --- a/src/plugin_xmms/charset.h +++ b/src/plugin_xmms/charset.h @@ -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); diff --git a/src/plugin_xmms/configure.c b/src/plugin_xmms/configure.c index 472f402d..e1ce15a6 100644 --- a/src/plugin_xmms/configure.c +++ b/src/plugin_xmms/configure.c @@ -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); diff --git a/src/plugin_xmms/configure.h b/src/plugin_xmms/configure.h index f49dea41..7bf55116 100644 --- a/src/plugin_xmms/configure.h +++ b/src/plugin_xmms/configure.h @@ -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; diff --git a/src/plugin_xmms/fileinfo.c b/src/plugin_xmms/fileinfo.c index 547a8a18..22de4a57 100644 --- a/src/plugin_xmms/fileinfo.c +++ b/src/plugin_xmms/fileinfo.c @@ -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(); diff --git a/src/plugin_xmms/plugin.c b/src/plugin_xmms/plugin.c index e20b0ef3..1a5652eb 100644 --- a/src/plugin_xmms/plugin.c +++ b/src/plugin_xmms/plugin.c @@ -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(); diff --git a/src/plugin_xmms/wrap_id3.c b/src/plugin_xmms/wrap_id3.c index b469c961..a48aaeb9 100644 --- a/src/plugin_xmms/wrap_id3.c +++ b/src/plugin_xmms/wrap_id3.c @@ -29,8 +29,7 @@ #include #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); + FLAC_plugin__canonical_tag_get_combined(filename, &tag, /*sep=*/0); - 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); - } + 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; }