[Pkg-gtkpod-devel] r245 - in gtkpod/tags: . 0.99.10-3/debian 0.99.10-3/src
nion at alioth.debian.org
nion at alioth.debian.org
Sun Sep 9 19:04:45 UTC 2007
Author: nion
Date: 2007-09-09 19:04:45 +0000 (Sun, 09 Sep 2007)
New Revision: 245
Added:
gtkpod/tags/0.99.10-3/
gtkpod/tags/0.99.10-3/debian/changelog
gtkpod/tags/0.99.10-3/debian/gtkpod.desktop
gtkpod/tags/0.99.10-3/src/mp3file.c
Removed:
gtkpod/tags/0.99.10-3/debian/changelog
gtkpod/tags/0.99.10-3/debian/gtkpod.desktop
gtkpod/tags/0.99.10-3/src/mp3file.c
Log:
tagging 0.99.10-3
Copied: gtkpod/tags/0.99.10-3 (from rev 237, gtkpod/trunk)
Deleted: gtkpod/tags/0.99.10-3/debian/changelog
===================================================================
--- gtkpod/trunk/debian/changelog 2007-08-06 00:08:16 UTC (rev 237)
+++ gtkpod/tags/0.99.10-3/debian/changelog 2007-09-09 19:04:45 UTC (rev 245)
@@ -1,205 +0,0 @@
-gtkpod (0.99.10-2) unstable; urgency=low
-
- * Upload to unstable
- * Update menu entry according to new menu policy
- * Lintian: Don't ignore all errors from "make distclean"
-
- -- Frank Lichtenheld <djpig at debian.org> Tue, 10 Jul 2007 00:25:04 +0200
-
-gtkpod (0.99.10-1) experimental; urgency=low
-
- * New upstream release
- - Allows resizing the preferences dialog (Closes: #428011)
- * Update man page debian/gtkpod.pod
-
- -- Frank Lichtenheld <djpig at debian.org> Thu, 28 Jun 2007 02:12:56 +0200
-
-gtkpod (0.99.8-3) unstable; urgency=low
-
- [ Frank Lichtenheld ]
- * Upload to unstable (Closes: #391812)
- * Change Maintainer to pkg-gtkpod-devel list. Add myself
- and Nico as Uploaders
- * Add Vcs-{Svn,Browser} fields
-
- [ Nico Golde ]
- * Added Homepage tag to control.
- * Bumped compat level since debhelper >= 5 is in etch.
-
- -- Nico Golde <nion at debian.org> Mon, 07 May 2007 17:40:39 +0200
-
-gtkpod (0.99.8-2) experimental; urgency=low
-
- * Adapt for libgpod >= 0.4.2 (Patch by upstream).
-
- -- Frank Lichtenheld <djpig at debian.org> Sat, 10 Feb 2007 14:22:53 +0100
-
-gtkpod (0.99.8-1) experimental; urgency=low
-
- * New upstream release
- + Update libgpod-dev build-dependency to (>= 0.4.0)
- and remove conflict against libgpod0
- (upload to experimental because this version isn't
- available in unstable yet)
- + Change /mnt/ipod to /media/ipod as usual in new scripts
- * Bump Standards-Version to 3.7.2 (no changes).
- * Remove glade file symlinks on clean. Found by Martin Zobel-Helas.
- (Closes: #395120)
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 22 Dec 2006 18:38:05 +0100
-
-gtkpod (0.99.4-2) unstable; urgency=low
-
- * Add conflict against libgpod0 >= 0.4.0
- to quiten up the BTS a bit.
- * Bump Standards-Version to 3.7.2 (no changes).
- * Remove glade file symlinks on clean. Found by Martin Zobel-Helas.
- (Closes: #395120)
-
- -- Frank Lichtenheld <djpig at debian.org> Thu, 30 Nov 2006 23:12:32 +0100
-
-gtkpod (0.99.4-1) unstable; urgency=low
-
- * New upstream release
-
- -- Frank Lichtenheld <djpig at debian.org> Sat, 8 Apr 2006 21:45:22 +0200
-
-gtkpod (0.99.2-1) unstable; urgency=low
-
- * New upstream release (Closes: #343856)
- + Fixes sorting bug (Closes: #330082)
- * Add build-depends on libgpod-dev
- * Add build-depends on flex
- * Add suggests on python since one of the scripts uses it
- * Update FSF address in debian/copyright
-
- -- Frank Lichtenheld <djpig at debian.org> Thu, 29 Dec 2005 17:38:38 +0100
-
-gtkpod (0.94.0-1) unstable; urgency=low
-
- * New upstream release (Closes: #319408)
- - support for new iTunes and new firmware version
- (Closes: #317701)
- * Add watch file, by Filippo Giunchedi (Closes: #318760)
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 22 Jul 2005 13:23:13 +0200
-
-gtkpod (0.93.1-1) unstable; urgency=low
-
- * New upstream release (Closes: #316399)
- - New build-dependency libglade2-dev
- * Bump Standards-Version to 3.6.2, no changes needed
- * Don't create glade files as absolute links as that fails miserably
- in a packaging situation
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 1 Jul 2005 02:24:13 +0200
-
-gtkpod (0.88.2-1) unstable; urgency=low
-
- * New upstream release
- - Fixes typo in preferences dialog reported by Michael Shields
- (Closes: #289087)
- - Included additional sync script previously offered for
- download separatly (Closes: #302361)
- * Include typo fixes for the German translation by Jens Seidel
- (Closes: #314053)
- * Add build dependency on gettext even though many other build
- dependecies already get it for us. We shouldn't rely on that
- * Fix syntax error in sync-notes.sh and change paths from
- /mnt/ipod to /media/ipod as usual
- * Add Suggests on recode as it is used in the scripts
- * Replace the outdated, buggy, and GFDL licensed man page written in
- docbook by the former Debian maintainer with an updated, more
- extensive, GPL licensed version written in POD by me. Replace
- build-depends on docbook-to-man by build-depends on perl.
- * Rework copyright file to be more precise about which file is
- copyrighted by whom
- * Fix path to scripts in preferences window
- * Replace ScsiEject code in src/misc.c with my new version from the
- eject package which uses the newer SG ioctl interface. Add new
- header check for scsi/sg.h to configure.in and regenerate configure
- and config.h.in.
-
- -- Frank Lichtenheld <djpig at debian.org> Thu, 16 Jun 2005 18:28:41 +0200
-
-gtkpod (0.88.1-1) unstable; urgency=low
-
- * New upstream release
- - fixes the iPod Shuffle support (Closes: #299768)
- Thanks to Jörg Kurlbaum for pointing this out
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 18 Mar 2005 13:15:17 +0100
-
-gtkpod (0.88-1) unstable; urgency=low
-
- * New upstream version (Closes: #299053)
- * Fix FTBFS with gcc-4.0; patch by Andreas Jochens
- (Closes: #298273)
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 11 Mar 2005 18:50:52 +0100
-
-gtkpod (0.87.3-1) unstable; urgency=low
-
- * New upstream version
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 4 Mar 2005 14:35:13 -0800
-
-gtkpod (0.85.0-1) unstable; urgency=low
-
- * New maintainer (Closes: #287988)
- Acknowledge my own NMU (Closes: #264682, #268340, #230898, #275363)
- * Apply patch by Andreas Jochens to let gtkpod compile
- with gcc-4.0 (Closes: #286926)
-
- -- Frank Lichtenheld <djpig at debian.org> Fri, 7 Jan 2005 01:01:58 +0100
-
-gtkpod (0.85.0-0.1) unstable; urgency=low
-
- * NMU since maintainer is unresponsive to any contact
- attempts for a log time now.
- * New upstream release (Closes: #264682)
- * debian/control:
- + Shorten short description (Closes: #268340)
- + Suggest mp3gain (Closes: #230898)
- + Adjust debhelper build-dependency so that we can use dh_desktop
- * debian/rules: Tidy up.
- * debian/docs: Don't install empty NEWS file
- * debian/menu:
- + Quote values
- + Use xpm icon created from the 32x32 icon
- * debian/gtkpod.sgml: fix some <arg> vs. <option> mistakes
- * debian/gtkpod.desktop: add .desktop file
- * Change all occourences of /mnt/ipod to /media/ipod (Closes: #275363)
- add a NEWS.Debian file to document this.
- * Fix UTF-8 characters in several files
- * Don't issue warning about not beeing able to "unsort" a table.
- It's quite annyoing and the user has no possibility to fix it.
-
- -- Frank Lichtenheld <djpig at debian.org> Thu, 9 Dec 2004 18:17:17 +0100
-
-gtkpod (0.72-2-2) unstable; urgency=low
-
- * Fix build dependencies (libid3tag0-dev).
- * Change priority to extra.
-
- -- Quôc Peyrot <chojin at debian.org> Sat, 7 Feb 2004 05:14:03 +0000
-
-gtkpod (0.72-2-1) unstable; urgency=low
-
- * New upstream release. Closes: #216155.
- * Update policy revision to 3.6.1 (use UTF-8).
- * Change the debian maintainer email to chojin at debian.org.
-
- -- Quôc Peyrot <chojin at debian.org> Tue, 3 Feb 2004 07:15:04 +0000
-
-gtkpod (0.51-1) unstable; urgency=low
-
- * New upstream release
-
- -- Quôc Peyrot <chojin at debian.org> Sat, 17 May 2003 04:15:02 +0000
-
-gtkpod (0.50-1) unstable; urgency=low
-
- * Initial Release. Closes: #182289.
-
- -- Quôc Peyrot <chojin at debian.org> Tue, 22 Apr 2003 05:16:37 +0000
Copied: gtkpod/tags/0.99.10-3/debian/changelog (from rev 244, gtkpod/trunk/debian/changelog)
===================================================================
--- gtkpod/tags/0.99.10-3/debian/changelog (rev 0)
+++ gtkpod/tags/0.99.10-3/debian/changelog 2007-09-09 19:04:45 UTC (rev 245)
@@ -0,0 +1,213 @@
+gtkpod (0.99.10-3) unstable; urgency=low
+
+ * Added missing fclose() call in mp3file.c to prevent too many open
+ file descriptors (Closes: #441308).
+ * Adapted .desktop file to current freedesktop standards.
+
+ -- Nico Golde <nion at debian.org> Sun, 09 Sep 2007 20:13:31 +0200
+
+gtkpod (0.99.10-2) unstable; urgency=low
+
+ * Upload to unstable
+ * Update menu entry according to new menu policy
+ * Lintian: Don't ignore all errors from "make distclean"
+
+ -- Frank Lichtenheld <djpig at debian.org> Tue, 10 Jul 2007 00:25:04 +0200
+
+gtkpod (0.99.10-1) experimental; urgency=low
+
+ * New upstream release
+ - Allows resizing the preferences dialog (Closes: #428011)
+ * Update man page debian/gtkpod.pod
+
+ -- Frank Lichtenheld <djpig at debian.org> Thu, 28 Jun 2007 02:12:56 +0200
+
+gtkpod (0.99.8-3) unstable; urgency=low
+
+ [ Frank Lichtenheld ]
+ * Upload to unstable (Closes: #391812)
+ * Change Maintainer to pkg-gtkpod-devel list. Add myself
+ and Nico as Uploaders
+ * Add Vcs-{Svn,Browser} fields
+
+ [ Nico Golde ]
+ * Added Homepage tag to control.
+ * Bumped compat level since debhelper >= 5 is in etch.
+
+ -- Nico Golde <nion at debian.org> Mon, 07 May 2007 17:40:39 +0200
+
+gtkpod (0.99.8-2) experimental; urgency=low
+
+ * Adapt for libgpod >= 0.4.2 (Patch by upstream).
+
+ -- Frank Lichtenheld <djpig at debian.org> Sat, 10 Feb 2007 14:22:53 +0100
+
+gtkpod (0.99.8-1) experimental; urgency=low
+
+ * New upstream release
+ + Update libgpod-dev build-dependency to (>= 0.4.0)
+ and remove conflict against libgpod0
+ (upload to experimental because this version isn't
+ available in unstable yet)
+ + Change /mnt/ipod to /media/ipod as usual in new scripts
+ * Bump Standards-Version to 3.7.2 (no changes).
+ * Remove glade file symlinks on clean. Found by Martin Zobel-Helas.
+ (Closes: #395120)
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 22 Dec 2006 18:38:05 +0100
+
+gtkpod (0.99.4-2) unstable; urgency=low
+
+ * Add conflict against libgpod0 >= 0.4.0
+ to quiten up the BTS a bit.
+ * Bump Standards-Version to 3.7.2 (no changes).
+ * Remove glade file symlinks on clean. Found by Martin Zobel-Helas.
+ (Closes: #395120)
+
+ -- Frank Lichtenheld <djpig at debian.org> Thu, 30 Nov 2006 23:12:32 +0100
+
+gtkpod (0.99.4-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Frank Lichtenheld <djpig at debian.org> Sat, 8 Apr 2006 21:45:22 +0200
+
+gtkpod (0.99.2-1) unstable; urgency=low
+
+ * New upstream release (Closes: #343856)
+ + Fixes sorting bug (Closes: #330082)
+ * Add build-depends on libgpod-dev
+ * Add build-depends on flex
+ * Add suggests on python since one of the scripts uses it
+ * Update FSF address in debian/copyright
+
+ -- Frank Lichtenheld <djpig at debian.org> Thu, 29 Dec 2005 17:38:38 +0100
+
+gtkpod (0.94.0-1) unstable; urgency=low
+
+ * New upstream release (Closes: #319408)
+ - support for new iTunes and new firmware version
+ (Closes: #317701)
+ * Add watch file, by Filippo Giunchedi (Closes: #318760)
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 22 Jul 2005 13:23:13 +0200
+
+gtkpod (0.93.1-1) unstable; urgency=low
+
+ * New upstream release (Closes: #316399)
+ - New build-dependency libglade2-dev
+ * Bump Standards-Version to 3.6.2, no changes needed
+ * Don't create glade files as absolute links as that fails miserably
+ in a packaging situation
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 1 Jul 2005 02:24:13 +0200
+
+gtkpod (0.88.2-1) unstable; urgency=low
+
+ * New upstream release
+ - Fixes typo in preferences dialog reported by Michael Shields
+ (Closes: #289087)
+ - Included additional sync script previously offered for
+ download separatly (Closes: #302361)
+ * Include typo fixes for the German translation by Jens Seidel
+ (Closes: #314053)
+ * Add build dependency on gettext even though many other build
+ dependecies already get it for us. We shouldn't rely on that
+ * Fix syntax error in sync-notes.sh and change paths from
+ /mnt/ipod to /media/ipod as usual
+ * Add Suggests on recode as it is used in the scripts
+ * Replace the outdated, buggy, and GFDL licensed man page written in
+ docbook by the former Debian maintainer with an updated, more
+ extensive, GPL licensed version written in POD by me. Replace
+ build-depends on docbook-to-man by build-depends on perl.
+ * Rework copyright file to be more precise about which file is
+ copyrighted by whom
+ * Fix path to scripts in preferences window
+ * Replace ScsiEject code in src/misc.c with my new version from the
+ eject package which uses the newer SG ioctl interface. Add new
+ header check for scsi/sg.h to configure.in and regenerate configure
+ and config.h.in.
+
+ -- Frank Lichtenheld <djpig at debian.org> Thu, 16 Jun 2005 18:28:41 +0200
+
+gtkpod (0.88.1-1) unstable; urgency=low
+
+ * New upstream release
+ - fixes the iPod Shuffle support (Closes: #299768)
+ Thanks to Jörg Kurlbaum for pointing this out
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 18 Mar 2005 13:15:17 +0100
+
+gtkpod (0.88-1) unstable; urgency=low
+
+ * New upstream version (Closes: #299053)
+ * Fix FTBFS with gcc-4.0; patch by Andreas Jochens
+ (Closes: #298273)
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 11 Mar 2005 18:50:52 +0100
+
+gtkpod (0.87.3-1) unstable; urgency=low
+
+ * New upstream version
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 4 Mar 2005 14:35:13 -0800
+
+gtkpod (0.85.0-1) unstable; urgency=low
+
+ * New maintainer (Closes: #287988)
+ Acknowledge my own NMU (Closes: #264682, #268340, #230898, #275363)
+ * Apply patch by Andreas Jochens to let gtkpod compile
+ with gcc-4.0 (Closes: #286926)
+
+ -- Frank Lichtenheld <djpig at debian.org> Fri, 7 Jan 2005 01:01:58 +0100
+
+gtkpod (0.85.0-0.1) unstable; urgency=low
+
+ * NMU since maintainer is unresponsive to any contact
+ attempts for a log time now.
+ * New upstream release (Closes: #264682)
+ * debian/control:
+ + Shorten short description (Closes: #268340)
+ + Suggest mp3gain (Closes: #230898)
+ + Adjust debhelper build-dependency so that we can use dh_desktop
+ * debian/rules: Tidy up.
+ * debian/docs: Don't install empty NEWS file
+ * debian/menu:
+ + Quote values
+ + Use xpm icon created from the 32x32 icon
+ * debian/gtkpod.sgml: fix some <arg> vs. <option> mistakes
+ * debian/gtkpod.desktop: add .desktop file
+ * Change all occourences of /mnt/ipod to /media/ipod (Closes: #275363)
+ add a NEWS.Debian file to document this.
+ * Fix UTF-8 characters in several files
+ * Don't issue warning about not beeing able to "unsort" a table.
+ It's quite annyoing and the user has no possibility to fix it.
+
+ -- Frank Lichtenheld <djpig at debian.org> Thu, 9 Dec 2004 18:17:17 +0100
+
+gtkpod (0.72-2-2) unstable; urgency=low
+
+ * Fix build dependencies (libid3tag0-dev).
+ * Change priority to extra.
+
+ -- Quôc Peyrot <chojin at debian.org> Sat, 7 Feb 2004 05:14:03 +0000
+
+gtkpod (0.72-2-1) unstable; urgency=low
+
+ * New upstream release. Closes: #216155.
+ * Update policy revision to 3.6.1 (use UTF-8).
+ * Change the debian maintainer email to chojin at debian.org.
+
+ -- Quôc Peyrot <chojin at debian.org> Tue, 3 Feb 2004 07:15:04 +0000
+
+gtkpod (0.51-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Quôc Peyrot <chojin at debian.org> Sat, 17 May 2003 04:15:02 +0000
+
+gtkpod (0.50-1) unstable; urgency=low
+
+ * Initial Release. Closes: #182289.
+
+ -- Quôc Peyrot <chojin at debian.org> Tue, 22 Apr 2003 05:16:37 +0000
Deleted: gtkpod/tags/0.99.10-3/debian/gtkpod.desktop
===================================================================
--- gtkpod/trunk/debian/gtkpod.desktop 2007-08-06 00:08:16 UTC (rev 237)
+++ gtkpod/tags/0.99.10-3/debian/gtkpod.desktop 2007-09-09 19:04:45 UTC (rev 245)
@@ -1,9 +0,0 @@
-[Desktop Entry]
-Encoding=UTF-8
-Name=gtkpod
-Icon=gtkpod-icon-32x32.png
-Comment=Manage songs and playlists on your Apple iPod
-Exec=gtkpod
-Terminal=false
-Type=Application
-Categories=Application;AudioVideo;
Copied: gtkpod/tags/0.99.10-3/debian/gtkpod.desktop (from rev 244, gtkpod/trunk/debian/gtkpod.desktop)
===================================================================
--- gtkpod/tags/0.99.10-3/debian/gtkpod.desktop (rev 0)
+++ gtkpod/tags/0.99.10-3/debian/gtkpod.desktop 2007-09-09 19:04:45 UTC (rev 245)
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=gtkpod
+Icon=gtkpod-icon-32x32.png
+Comment=Manage songs and playlists on your Apple iPod
+Exec=gtkpod
+Terminal=false
+Type=Application
+Categories=GTK;AudioVideo;
Deleted: gtkpod/tags/0.99.10-3/src/mp3file.c
===================================================================
--- gtkpod/trunk/src/mp3file.c 2007-08-06 00:08:16 UTC (rev 237)
+++ gtkpod/tags/0.99.10-3/src/mp3file.c 2007-09-09 19:04:45 UTC (rev 245)
@@ -1,2677 +0,0 @@
-/* Time-stamp: <2007-06-26 00:39:11 jcs>
-|
-| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
-| Part of the gtkpod project.
-|
-| URL: http://www.gtkpod.org/
-| URL: http://gtkpod.sourceforge.net/
-|
-| 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
-|
-| iTunes and iPod are trademarks of Apple
-|
-| This product is not supported/written/published by Apple!
-|
-| $Id: mp3file.c 1591 2007-06-26 03:33:25Z tmzullinger $
-*/
-
-
-#define LOCALDEBUG 0
-
-
-/* The code in the first section of this file is taken from the
- * mp3info (http://www.ibiblio.org/mp3info/) project. Only the code
- * needed for the playlength calculation has been extracted. */
-
-/* The code in the second section of this file is taken from the
- * mpg123 code used in xmms-1.2.7 (Input/mpg123). Only the code needed
- * for the playlength calculation has been extracted. */
-
-/* The code in the last section of this file is original gtkpod
- * code. */
-
-/****************
- * Declarations *
- ****************/
-
-#include <glib.h>
-#include <math.h>
-/*
- * Description of each item of the TagList list
- */
-typedef struct _File_Tag File_Tag;
-typedef struct _GainData GainData;
-typedef struct _GaplessData GaplessData;
-
-struct _File_Tag
-{
- gchar *title; /* Title of track */
- gchar *artist; /* Artist name */
- gchar *album; /* Album name */
- gchar *year; /* Year of track */
- gchar *trackstring; /* Position of track in the album */
- gchar *track_total; /* The number of tracks for the album (ex: 12/20) */
- gchar *genre; /* Genre of song */
- gchar *comment; /* Comment */
- gchar *composer; /* Composer */
- guint32 songlen; /* Length of file in ms */
- gchar *cdnostring; /* Position of disc in the album */
- gchar *cdno_total; /* The number of discs in the album (ex: 1/2) */
- gchar *compilation; /* The track is a member of a compilation */
- gchar *podcasturl; /* The following are mainly used for podcasts */
- gchar *sort_artist;
- gchar *sort_title;
- gchar *sort_album;
- gchar *sort_albumartist;
- gchar *sort_composer;
- gchar *description;
- gchar *podcastrss;
- gchar *time_released;
- gchar *subtitle;
- gchar *BPM; /* beats per minute */
- gchar *lyrics; /* does not appear to be the full lyrics --
- only used to set the flag 'lyrics_flag'
- of the Track structure */
-};
-
-
-struct _GainData
-{
- guint32 peak_signal; /* LAME Peak Signal * 0x800000 */
- gdouble radio_gain; /* RadioGain in dB
- (as defined by www.replaygain.org) */
- gdouble audiophile_gain;/* AudiophileGain in dB
- (as defined by www.replaygain.org) */
- gboolean peak_signal_set; /* has the peak signal been set? */
- gboolean radio_gain_set; /* has the radio gain been set? */
- gboolean audiophile_gain_set;/* has the audiophile gain been set? */
-};
-
-struct _GaplessData
-{
- guint32 pregap; /* number of pregap samples */
- guint64 samplecount; /* number of actual music samples */
- guint32 postgap; /* number of postgap samples */
- guint32 gapless_data; /* number of bytes from the first sync frame to the 8th to last frame */
-};
-
-/* This code is taken from the mp3info code. Only the code needed for
- * the playlength calculation has been extracted */
-
-/*
- mp3tech.c - Functions for handling MP3 files and most MP3 data
- structure manipulation.
-
- Copyright (C) 2000-2001 Cedric Tefft <cedric at earthling.net>
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- ***************************************************************************
-
- This file is based in part on:
-
- * MP3Info 0.5 by Ricardo Cerqueira <rmc at rccn.net>
- * MP3Stat 0.9 by Ed Sweetman <safemode at voicenet.com> and
- Johannes Overmann <overmann at iname.com>
-
-*/
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "mp3file.h"
-#include "charset.h"
-#include "itdb.h"
-#include "file.h"
-#include "misc.h"
-
-
-/* MIN_CONSEC_GOOD_FRAMES defines how many consecutive valid MP3 frames
- we need to see before we decide we are looking at a real MP3 file */
-#define MIN_CONSEC_GOOD_FRAMES 4
-#define FRAME_HEADER_SIZE 4
-#define MIN_FRAME_SIZE 21
-
-enum VBR_REPORT { VBR_VARIABLE, VBR_AVERAGE, VBR_MEDIAN };
-
-typedef struct {
- gulong sync;
- guint version;
- guint layer;
- guint crc;
- guint bitrate;
- guint freq;
- guint padding;
- guint extension;
- guint mode;
- guint mode_extension;
- guint copyright;
- guint original;
- guint emphasis;
-} MP3Header;
-
-typedef struct {
- gchar *filename;
- FILE *file;
- off_t datasize;
- gint header_isvalid;
- MP3Header header;
- gint id3_isvalid;
- gint vbr;
- float vbr_average;
- gint milliseconds;
- gint frames;
- gint badframes;
-} MP3Info;
-
-/* This is for xmms code */
-static guint get_track_time(gchar *path);
-
-
-
-/* ------------------------------------------------------------
-
- start of first section
-
- ------------------------------------------------------------ */
-void get_mp3_info(MP3Info *mp3);
-
-gint frequencies[3][4] = {
- {22050,24000,16000,50000}, /* MPEG 2.0 */
- {44100,48000,32000,50000}, /* MPEG 1.0 */
- {11025,12000,8000,50000} /* MPEG 2.5 */
-};
-
-/* "0" added by JCS */
-gint bitrate[2][3][16] = {
- { /* MPEG 2.0 */
- {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0},/* layer 1 */
- {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0}, /* layer 2 */
- {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0} /* layer 3 */
- },
-
- { /* MPEG 1.0 */
- {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},/* layer 1 */
- {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, /* layer 2 */
- {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0} /* layer 3 */
- }
-};
-
-gint frame_size_index[] = {24000, 72000, 72000};
-
-
-gchar *mode_text[] = {
- "stereo", "joint stereo", "dual channel", "mono"
-};
-
-gchar *emphasis_text[] = {
- "none", "50/15 microsecs", "reserved", "CCITT J 17"
-};
-
-
-static gint mp3file_header_bitrate(MP3Header *h) {
- return bitrate[h->version & 1][3-h->layer][h->bitrate];
-}
-
-
-static gint mp3file_header_frequency(MP3Header *h) {
- return frequencies[h->version][h->freq];
-}
-
-
-gint frame_length(MP3Header *header) {
- return header->sync == 0xFFE ?
- (frame_size_index[3-header->layer]*((header->version&1)+1)*
- mp3file_header_bitrate(header)/(float)mp3file_header_frequency(header))+
- header->padding : 1;
-}
-
-/* Get next MP3 frame header.
- Return codes:
- positive value = Frame Length of this header
- 0 = No, we did not retrieve a valid frame header
-*/
-gint get_header(FILE *file,MP3Header *header)
-{
- guchar buffer[FRAME_HEADER_SIZE];
- gint fl;
-
- if(fread(&buffer,FRAME_HEADER_SIZE,1,file)<1) {
- header->sync=0;
- return 0;
- }
- header->sync=(((gint)buffer[0]<<4) | ((gint)(buffer[1]&0xE0)>>4));
- if(buffer[1] & 0x10) header->version=(buffer[1] >> 3) & 1;
- else header->version=2;
- header->layer=(buffer[1] >> 1) & 3;
- if (header->layer == 0)
- {
- header->layer = 1; /* sanity added by JCS */
- }
- if((header->sync != 0xFFE) || (header->layer != 1)) {
- header->sync=0;
- return 0;
- }
- header->crc=buffer[1] & 1;
- header->bitrate=(buffer[2] >> 4) & 0x0F;
- header->freq=(buffer[2] >> 2) & 0x3;
- header->padding=(buffer[2] >>1) & 0x1;
- header->extension=(buffer[2]) & 0x1;
- header->mode=(buffer[3] >> 6) & 0x3;
- header->mode_extension=(buffer[3] >> 4) & 0x3;
- header->copyright=(buffer[3] >> 3) & 0x1;
- header->original=(buffer[3] >> 2) & 0x1;
- header->emphasis=(buffer[3]) & 0x3;
-
- return ((fl=frame_length(header)) >= MIN_FRAME_SIZE ? fl : 0);
-}
-
-gint sameConstant(MP3Header *h1, MP3Header *h2) {
- if((*(guint*)h1) == (*(guint*)h2)) return 1;
-
- if((h1->version == h2->version ) &&
- (h1->layer == h2->layer ) &&
- (h1->crc == h2->crc ) &&
- (h1->freq == h2->freq ) &&
- (h1->mode == h2->mode ) &&
- (h1->copyright == h2->copyright ) &&
- (h1->original == h2->original ) &&
- (h1->emphasis == h2->emphasis ))
- return 1;
- else return 0;
-}
-
-
-gint get_first_header(MP3Info *mp3, long startpos)
-{
- gint k, l=0,c;
- MP3Header h, h2;
- long valid_start=0;
-
- fseek(mp3->file,startpos,SEEK_SET);
- while (1) {
- while((c=fgetc(mp3->file)) != 255 && (c != EOF));
- if(c == 255) {
- ungetc(c,mp3->file);
- valid_start=ftell(mp3->file);
- if((l=get_header(mp3->file,&h))) {
- fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
- for(k=1; (k < MIN_CONSEC_GOOD_FRAMES) && (mp3->datasize-ftell(mp3->file) >= FRAME_HEADER_SIZE); k++) {
- if(!(l=get_header(mp3->file,&h2))) break;
- if(!sameConstant(&h,&h2)) break;
- fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
- }
- if(k == MIN_CONSEC_GOOD_FRAMES) {
- fseek(mp3->file,valid_start,SEEK_SET);
- memcpy(&(mp3->header),&h2,sizeof(MP3Header));
- mp3->header_isvalid=1;
- return 1;
- }
- }
- } else {
- return 0;
- }
- }
-
- return 0;
-}
-
-
-/* get_next_header() - read header at current position or look for
- the next valid header if there isn't one at the current position
-*/
-gint get_next_header(MP3Info *mp3)
-{
- gint l=0,c,skip_bytes=0;
- MP3Header h;
-
- while(1) {
- while((c=fgetc(mp3->file)) != 255 && (ftell(mp3->file) < mp3->datasize)) skip_bytes++;
- if(c == 255) {
- ungetc(c,mp3->file);
- if((l=get_header(mp3->file,&h))) {
- if(skip_bytes) mp3->badframes++;
- fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
- return 15-h.bitrate;
- } else {
- skip_bytes += FRAME_HEADER_SIZE;
- }
- } else {
- if(skip_bytes) mp3->badframes++;
- return 0;
- }
- }
-}
-
-
-void get_mp3_info(MP3Info *mp3)
-{
- gint frame_type[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- float milliseconds=0,total_rate=0;
- gint frames=0,frame_types=0,frames_so_far=0;
- gint vbr_median=-1;
- guint bitrate;
- gint counter=0;
- MP3Header header;
- struct stat filestat;
- off_t data_start=0;
-
-
- stat(mp3->filename,&filestat);
- mp3->datasize=filestat.st_size;
-
- if(get_first_header(mp3,0L)) {
- data_start=ftell(mp3->file);
- while((bitrate=get_next_header(mp3))) {
- if (bitrate < 15) /* sanity added by JCS */
- frame_type[15-bitrate]++;
- frames++;
- }
- memcpy(&header,&(mp3->header),sizeof(MP3Header));
- for(counter=0;counter<15;counter++) {
- if(frame_type[counter]) {
- float header_bitrate; /* introduced by JCS to speed up */
- frame_types++;
- header.bitrate=counter;
- frames_so_far += frame_type[counter];
- header_bitrate = mp3file_header_bitrate(&header);
- if (header_bitrate != 0)
- milliseconds += (float)(8*frame_length(&header)*frame_type[counter])/header_bitrate;
- total_rate += header_bitrate*frame_type[counter];
- if((vbr_median == -1) && (frames_so_far >= frames/2))
- vbr_median=counter;
- }
- }
- mp3->milliseconds=(gint)(milliseconds+0.5);
- mp3->header.bitrate=vbr_median;
- mp3->vbr_average=total_rate/(float)frames;
- mp3->frames=frames;
- if(frame_types > 1) {
- mp3->vbr=1;
- }
- }
-}
-
-
-
-
-/* ------------------------------------------------------------
-
- xmms code
-
-
- ------------------------------------------------------------ */
-
-/*
-| Changed by Jorg Schuler <jcsjcs at users.sourceforge.net> to
-| compile with the gtkpod project. 2003/04/01
-*/
-
-/* This code is taken from the mpg123 code used in xmms-1.2.7
- * (Input/mpg123). Only the code needed for the playlength calculation
- * has been extracted */
-
-#include "mp3file.h"
-#include <stdio.h>
-#include <string.h>
-
-#define FRAMES_FLAG 0x0001
-#define BYTES_FLAG 0x0002
-#define TOC_FLAG 0x0004
-#define VBR_SCALE_FLAG 0x0008
-
-#define SBLIMIT 32
-#define SCALE_BLOCK 12
-#define SSLIMIT 18
-
-#define MPG_MD_STEREO 0
-#define MPG_MD_JOINT_STEREO 1
-#define MPG_MD_DUAL_CHANNEL 2
-#define MPG_MD_MONO 3
-#define MAXFRAMESIZE 1792
-#define real float
-
-struct bitstream_info
-{
- int bitindex;
- unsigned char *wordpointer;
-};
-
-struct bitstream_info bsi;
-
-real mpg123_muls[27][64]; /* also used by layer 1 */
-
-int tabsel_123[2][3][16] =
-{
- {
- {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,},
- {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,},
- {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}},
-
- {
- {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,},
- {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,},
- {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}}
-};
-
-long mpg123_freqs[9] =
-{44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000};
-
-/*
- * structure to receive extracted header
- */
-typedef struct
-{
- int frames; /* total bit stream frames from Xing header data */
- int bytes; /* total bit stream bytes from Xing header data */
- unsigned char toc[100]; /* "table of contents" */
-} xing_header_t;
-
-struct al_table
-{
- short bits;
- short d;
-};
-
-struct frame
-{
- struct al_table *alloc;
- int (*synth) (real *, int, unsigned char *, int *);
- int (*synth_mono) (real *, unsigned char *, int *);
-#ifdef USE_3DNOW
- void (*dct36)(real *,real *,real *,real *,real *);
-#endif
- int stereo;
- int jsbound;
- int single;
- int II_sblimit;
- int down_sample_sblimit;
- int lsf;
- int mpeg25;
- int down_sample;
- int header_change;
- int lay;
- int (*do_layer) (struct frame * fr);
- int error_protection;
- int bitrate_index;
- int sampling_frequency;
- int padding;
- int extension;
- int mode;
- int mode_ext;
- int copyright;
- int original;
- int emphasis;
- int framesize; /* computed framesize */
-};
-
-static guint32 convert_to_header(guint8 * buf)
-{
-
- return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
-}
-
-static int mpg123_head_check(unsigned long head)
-{
- if ((head & 0xffe00000) != 0xffe00000)
- return FALSE;
- if (!((head >> 17) & 3))
- return FALSE;
- if (((head >> 12) & 0xf) == 0xf)
- return FALSE;
- if (!((head >> 12) & 0xf))
- return FALSE;
- if (((head >> 10) & 0x3) == 0x3)
- return FALSE;
- if (((head >> 19) & 1) == 1 && ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1)
- return FALSE;
- if ((head & 0xffff0000) == 0xfffe0000)
- return FALSE;
-
- return TRUE;
-}
-
-
-/*
- * the code a header and write the information
- * into the frame structure
- */
-static int mpg123_decode_header(struct frame *fr, unsigned long newhead)
-{
- int ssize;
-
- if (newhead & (1 << 20))
- {
- fr->lsf = (newhead & (1 << 19)) ? 0x0 : 0x1;
- fr->mpeg25 = 0;
- }
- else
- {
- fr->lsf = 1;
- fr->mpeg25 = 1;
- }
- fr->lay = 4 - ((newhead >> 17) & 3);
- if (fr->mpeg25)
- {
- fr->sampling_frequency = 6 + ((newhead >> 10) & 0x3);
- }
- else
- fr->sampling_frequency = ((newhead >> 10) & 0x3) + (fr->lsf * 3);
- fr->error_protection = ((newhead >> 16) & 0x1) ^ 0x1;
-
- fr->bitrate_index = ((newhead >> 12) & 0xf);
- fr->padding = ((newhead >> 9) & 0x1);
- fr->extension = ((newhead >> 8) & 0x1);
- fr->mode = ((newhead >> 6) & 0x3);
- fr->mode_ext = ((newhead >> 4) & 0x3);
- fr->copyright = ((newhead >> 3) & 0x1);
- fr->original = ((newhead >> 2) & 0x1);
- fr->emphasis = newhead & 0x3;
-
- fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
-
- ssize = 0;
-
- if (!fr->bitrate_index)
- return (0);
-
- switch (fr->lay)
- {
- case 1:
-/* fr->do_layer = mpg123_do_layer1; */
-/* mpg123_init_layer2(); /\* inits also shared tables with layer1 *\/ */
- fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
- fr->framesize /= mpg123_freqs[fr->sampling_frequency];
- fr->framesize = ((fr->framesize + fr->padding) << 2) - 4;
- break;
- case 2:
-/* fr->do_layer = mpg123_do_layer2; */
-/* mpg123_init_layer2(); /\* inits also shared tables with layer1 *\/ */
- fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
- fr->framesize /= mpg123_freqs[fr->sampling_frequency];
- fr->framesize += fr->padding - 4;
- break;
- case 3:
-/* fr->do_layer = mpg123_do_layer3; */
- if (fr->lsf)
- ssize = (fr->stereo == 1) ? 9 : 17;
- else
- ssize = (fr->stereo == 1) ? 17 : 32;
- if (fr->error_protection)
- ssize += 2;
- fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
- fr->framesize /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
- fr->framesize = fr->framesize + fr->padding - 4;
- break;
- default:
- return (0);
- }
- if(fr->framesize > MAXFRAMESIZE)
- return 0;
- return 1;
-}
-
-#define GET_INT32BE(b) \
-(i = (b[0] << 24) | (b[1] << 16) | b[2] << 8 | b[3], b += 4, i)
-
-static int mpg123_get_xing_header(xing_header_t * xing, unsigned char *buf)
-{
- int i, head_flags;
- int id, mode;
-
- memset(xing, 0, sizeof(xing_header_t));
-
- /* get selected MPEG header data */
- id = (buf[1] >> 3) & 1;
- mode = (buf[3] >> 6) & 3;
- buf += 4;
-
- /* Skip the sub band data */
- if (id)
- {
- /* mpeg1 */
- if (mode != 3)
- buf += 32;
- else
- buf += 17;
- }
- else
- {
- /* mpeg2 */
- if (mode != 3)
- buf += 17;
- else
- buf += 9;
- }
-
- if (strncmp(buf, "Xing", 4))
- return 0;
- buf += 4;
-
- head_flags = GET_INT32BE(buf);
-
- if (head_flags & FRAMES_FLAG)
- xing->frames = GET_INT32BE(buf);
- if (xing->frames < 1)
- xing->frames = 1;
- if (head_flags & BYTES_FLAG)
- xing->bytes = GET_INT32BE(buf);
-
- if (head_flags & TOC_FLAG)
- {
- for (i = 0; i < 100; i++)
- xing->toc[i] = buf[i];
- buf += 100;
- }
-
-#ifdef XING_DEBUG
- for (i = 0; i < 100; i++)
- {
- if ((i % 10) == 0)
- fprintf(stderr, "\n");
- fprintf(stderr, " %3d", xing->toc[i]);
- }
-#endif
-
- return 1;
-}
-
-static double mpg123_compute_tpf(struct frame *fr)
-{
- const int bs[4] = {0, 384, 1152, 1152};
- double tpf;
-
- tpf = bs[fr->lay];
- tpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
- return tpf;
-}
-
-static double mpg123_compute_bpf(struct frame *fr)
-{
- double bpf;
-
- switch (fr->lay)
- {
- case 1:
- bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
- bpf *= 12000.0 * 4.0;
- bpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
- break;
- case 2:
- case 3:
- bpf = tabsel_123[fr->lsf][fr->lay - 1][fr->bitrate_index];
- bpf *= 144000;
- bpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
- break;
- default:
- bpf = 1.0;
- }
-
- return bpf;
-}
-
-
-unsigned int mpg123_getbits(int number_of_bits)
-{
- unsigned long rval;
-
-#ifdef DEBUG_GETBITS
- fprintf(stderr, "g%d", number_of_bits);
-#endif
-
- if(!number_of_bits)
- return 0;
-
-#if 0
- check_buffer_range(number_of_bits + bsi.bitindex);
-#endif
-
- {
- rval = bsi.wordpointer[0];
- rval <<= 8;
- rval |= bsi.wordpointer[1];
- rval <<= 8;
- rval |= bsi.wordpointer[2];
-
- rval <<= bsi.bitindex;
- rval &= 0xffffff;
-
- bsi.bitindex += number_of_bits;
-
- rval >>= (24-number_of_bits);
-
- bsi.wordpointer += (bsi.bitindex >> 3);
- bsi.bitindex &= 7;
- }
-
-#ifdef DEBUG_GETBITS
- fprintf(stderr,":%x ",rval);
-#endif
-
- return rval;
-}
-
-
-void I_step_one(unsigned int balloc[], unsigned int scale_index[2][SBLIMIT], struct frame *fr)
-{
- unsigned int *ba = balloc;
- unsigned int *sca = (unsigned int *) scale_index;
-
- if (fr->stereo)
- {
- int i;
- int jsbound = fr->jsbound;
-
- for (i = 0; i < jsbound; i++)
- {
- *ba++ = mpg123_getbits(4);
- *ba++ = mpg123_getbits(4);
- }
- for (i = jsbound; i < SBLIMIT; i++)
- *ba++ = mpg123_getbits(4);
-
- ba = balloc;
-
- for (i = 0; i < jsbound; i++)
- {
- if ((*ba++))
- *sca++ = mpg123_getbits(6);
- if ((*ba++))
- *sca++ = mpg123_getbits(6);
- }
- for (i = jsbound; i < SBLIMIT; i++)
- if ((*ba++))
- {
- *sca++ = mpg123_getbits(6);
- *sca++ = mpg123_getbits(6);
- }
- }
- else
- {
- int i;
-
- for (i = 0; i < SBLIMIT; i++)
- *ba++ = mpg123_getbits(4);
- ba = balloc;
- for (i = 0; i < SBLIMIT; i++)
- if ((*ba++))
- *sca++ = mpg123_getbits(6);
- }
-}
-
-void I_step_two(real fraction[2][SBLIMIT], unsigned int balloc[2 * SBLIMIT],
- unsigned int scale_index[2][SBLIMIT], struct frame *fr)
-{
- int i, n;
- int smpb[2 * SBLIMIT]; /* values: 0-65535 */
- int *sample;
- register unsigned int *ba;
- register unsigned int *sca = (unsigned int *) scale_index;
-
- if (fr->stereo)
- {
- int jsbound = fr->jsbound;
- register real *f0 = fraction[0];
- register real *f1 = fraction[1];
-
- ba = balloc;
- for (sample = smpb, i = 0; i < jsbound; i++)
- {
- if ((n = *ba++))
- *sample++ = mpg123_getbits(n + 1);
- if ((n = *ba++))
- *sample++ = mpg123_getbits(n + 1);
- }
- for (i = jsbound; i < SBLIMIT; i++)
- if ((n = *ba++))
- *sample++ = mpg123_getbits(n + 1);
-
- ba = balloc;
- for (sample = smpb, i = 0; i < jsbound; i++)
- {
- if ((n = *ba++))
- *f0++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
- else
- *f0++ = 0.0;
- if ((n = *ba++))
- *f1++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
- else
- *f1++ = 0.0;
- }
- for (i = jsbound; i < SBLIMIT; i++)
- {
- if ((n = *ba++))
- {
- real samp = (((-1) << n) + (*sample++) + 1);
-
- *f0++ = samp * mpg123_muls[n + 1][*sca++];
- *f1++ = samp * mpg123_muls[n + 1][*sca++];
- }
- else
- *f0++ = *f1++ = 0.0;
- }
- for (i = fr->down_sample_sblimit; i < 32; i++)
- fraction[0][i] = fraction[1][i] = 0.0;
- }
- else
- {
- register real *f0 = fraction[0];
-
- ba = balloc;
- for (sample = smpb, i = 0; i < SBLIMIT; i++)
- if ((n = *ba++))
- *sample++ = mpg123_getbits(n + 1);
- ba = balloc;
- for (sample = smpb, i = 0; i < SBLIMIT; i++)
- {
- if ((n = *ba++))
- *f0++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
- else
- *f0++ = 0.0;
- }
- for (i = fr->down_sample_sblimit; i < 32; i++)
- fraction[0][i] = 0.0;
- }
-}
-
-static guint get_track_time_file(FILE * file)
-{
- guint32 head;
- guchar tmp[4], *buf;
- struct frame frm;
- xing_header_t xing_header;
- double tpf, bpf;
- guint32 len;
-
- if (!file)
- return -1;
-
- fseek(file, 0, SEEK_SET);
- if (fread(tmp, 1, 4, file) != 4)
- return 0;
- head = convert_to_header(tmp);
- while (!mpg123_head_check(head))
- {
- head <<= 8;
- if (fread(tmp, 1, 1, file) != 1)
- return 0;
- head |= tmp[0];
- }
- if (mpg123_decode_header(&frm, head))
- {
- buf = g_malloc(frm.framesize + 4);
- fseek(file, -4, SEEK_CUR);
- fread(buf, 1, frm.framesize + 4, file);
- tpf = mpg123_compute_tpf(&frm);
- if (mpg123_get_xing_header(&xing_header, buf))
- {
- g_free(buf);
- return ((guint) (tpf * xing_header.frames * 1000));
- }
- g_free(buf);
- bpf = mpg123_compute_bpf(&frm);
- fseek(file, 0, SEEK_END);
- len = ftell(file);
- fseek(file, -128, SEEK_END);
- fread(tmp, 1, 3, file);
- if (!strncmp(tmp, "TAG", 3))
- len -= 128;
- return ((guint) ((guint)(len / bpf) * tpf * 1000));
- }
- return 0;
-}
-
-static guint get_track_time (gchar *path)
-{
- guint result = 0;
-
- if (path)
- {
- FILE *file = fopen (path, "r");
- result = get_track_time_file (file);
- if (file) fclose (file);
- }
- return result;
-}
-
-
-/* libid3tag stuff */
-
-#include <id3tag.h>
-#include "prefs.h"
-
-#ifndef ID3_FRAME_GROUP
-#define ID3_FRAME_GROUP "TPE2"
-#endif
-
-
-
-static const gchar* id3_get_binary (struct id3_tag *tag,
- char *frame_name,
- id3_length_t *len,
- int index)
-{
- const id3_byte_t *binary = NULL;
- struct id3_frame *frame;
- union id3_field *field;
-
- g_return_val_if_fail (len, NULL);
-
- *len = 0;
-
- frame = id3_tag_findframe (tag, frame_name, index);
-#if LOCALDEBUG
- printf ("frame: %p\n", frame);
-#endif
-
- if (!frame) return NULL;
-
-#if LOCALDEBUG
- printf (" nfields: %d\n", frame->nfields);
- if (strncmp (frame_name, "APIC", 4) == 0)
- {
- field = id3_frame_field (frame, 2);
- printf (" picture type: %ld\n", field->number.value);
- }
-#endif
-
-
-#if 0
-/*-----------------*/
-/* just to show that this field (before last) contains the d8 ff e0 ff
- part of the start of a jpeg file when the coverart war embedded by iTunes */
-
- const id3_ucs4_t *string = NULL;
- gchar *raw = NULL;
-
- /* The last field contains the data */
- field = id3_frame_field (frame, frame->nfields-2);
-
-#if LOCALDEBUG
- printf (" field: %p\n", field);
-#endif
-
- if (!field) return NULL;
-
-#if LOCALDEBUG
- printf (" type: %d\n", field->type);
-#endif
-
- switch (field->type)
- {
- case ID3_FIELD_TYPE_STRING:
- string = id3_field_getstring (field);
- break;
- default:
- break;
- }
-
- /* ISO_8859_1 is just a "marker" -- most people just drop
- whatever coding system they are using into it, so we use
- charset_to_utf8() to convert to utf8 */
-
- if (string)
- {
- raw = id3_ucs4_latin1duplicate (string);
- }
-
-
-#if LOCALDEBUG
- {
- FILE *file;
- printf (" string len: %d\n", raw?strlen(raw):0);
- file = fopen ("/tmp/folder1.jpg", "w");
- fwrite (raw, 1, raw?strlen(raw):0, file);
- fclose (file);
- }
-#endif
- g_free (raw);
-
-/*-----------------*/
-#endif
-
- /* The last field contains the data */
- field = id3_frame_field (frame, frame->nfields-1);
-
-#if LOCALDEBUG
- printf (" field: %p\n", field);
-#endif
-
- if (!field) return NULL;
-
-#if LOCALDEBUG
- printf (" type: %d\n", field->type);
-#endif
-
- switch (field->type)
- {
- case ID3_FIELD_TYPE_BINARYDATA:
- binary = id3_field_getbinarydata(field, len);
- break;
- default:
- break;
- }
-
-#if LOCALDEBUG
- {
- FILE *file;
- printf (" binary len: %ld\n", *len);
- file = fopen ("/tmp/folder2.jpg", "w");
- fwrite (binary, 1, *len, file);
- fclose (file);
- }
-#endif
-
-
-
- return binary;
-}
-
-
-
-static gchar* id3_get_string (struct id3_tag *tag, char *frame_name)
-{
- const id3_ucs4_t *string = NULL;
- const id3_byte_t *binary = NULL;
- id3_length_t len = 0;
- struct id3_frame *frame;
- union id3_field *field;
- gchar *utf8 = NULL;
- enum id3_field_textencoding encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1;
-
- frame = id3_tag_findframe (tag, frame_name, 0);
-#if LOCALDEGUB
- printf ("frame: %p\n", frame);
-#endif
-
- if (!frame) return NULL;
-
- /* Find the encoding used for the field */
- field = id3_frame_field (frame, 0);
-#if LOCALDEBUG
- printf ("field: %p\n", field);
- printf ("type: %d\n", id3_field_type (field));
-#endif
-
- if (field && (id3_field_type (field) == ID3_FIELD_TYPE_TEXTENCODING))
- {
- encoding = field->number.value;
-#if LOCALDEBUG
- printf ("encoding: %d\n", encoding);
-#endif
- }
-
- /* The last field contains the data */
- field = id3_frame_field (frame, frame->nfields-1);
-
-#if LOCALDEBUG
- printf ("field: %p\n", field);
-#endif
-
- if (!field) return NULL;
-
-#if LOCALDEBUG
- printf ("type: %d\n", field->type);
-#endif
-
-
- switch (field->type)
- {
- case ID3_FIELD_TYPE_STRINGLIST:
- string = id3_field_getstrings (field, 0);
- break;
- case ID3_FIELD_TYPE_STRINGFULL:
- string = id3_field_getfullstring (field);
- break;
- case ID3_FIELD_TYPE_BINARYDATA:
- binary = id3_field_getbinarydata(field, &len);
-#if LOCALDEBUG
- printf ("len: %ld\nbinary: %s\n", len, binary+1);
-#endif
- if (len > 0)
- return charset_to_utf8 (binary+1);
- break;
- default:
- break;
- }
-
-/* printf ("string: %p\n", string); */
-
- if (!string) return NULL;
-
- if (strcmp (frame_name, ID3_FRAME_GENRE) == 0)
- string = id3_genre_name (string);
-
- if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1)
- {
- /* ISO_8859_1 is just a "marker" -- most people just drop
- whatever coding system they are using into it, so we use
- charset_to_utf8() to convert to utf8 */
- id3_latin1_t *raw = id3_ucs4_latin1duplicate (string);
- utf8 = charset_to_utf8 (raw);
- g_free (raw);
- }
- else
- {
- /* Standard unicode is being used -- we won't have to worry
- about charsets then. */
- utf8 = id3_ucs4_utf8duplicate (string);
- }
- return utf8;
-}
-
-static void id3_set_string (struct id3_tag *tag,
- const char *frame_name,
- const char *data,
- enum id3_field_textencoding encoding)
-{
- int res;
- struct id3_frame *frame;
- union id3_field *field;
- id3_ucs4_t *ucs4;
-
- /* clear the frame, because of bug in libid3tag see
- http://www.mars.org/mailman/public/mad-dev/2004-October/001113.html
- */
- while ((frame = id3_tag_findframe (tag, frame_name, 0)))
- {
- id3_tag_detachframe (tag, frame);
- id3_frame_delete (frame);
- }
-
- if ((data == NULL) || (strlen(data) == 0))
- return;
-
- frame = id3_frame_new (frame_name);
- id3_tag_attachframe (tag, frame);
-
- /* Use the specified text encoding */
- field = id3_frame_field (frame, 0);
- id3_field_settextencoding(field, encoding);
-
- if (strcmp (frame_name, ID3_FRAME_COMMENT) == 0)
- {
- field = id3_frame_field (frame, 3);
- field->type = ID3_FIELD_TYPE_STRINGFULL;
- }
- else
- {
- field = id3_frame_field (frame, 1);
- field->type = ID3_FIELD_TYPE_STRINGLIST;
- }
-
-
- /* maybe could be optimized see
- http://www.mars.org/mailman/public/mad-dev/2002-October/000739.html
- */
- if (strcmp (frame_name, ID3_FRAME_GENRE) == 0)
- {
- id3_ucs4_t *tmp_ucs4 = id3_utf8_ucs4duplicate ((id3_utf8_t *)data);
- int index = id3_genre_number (tmp_ucs4);
- if (index != -1)
- {
- /* valid genre -- simply store the genre number */
- gchar *tmp = g_strdup_printf("%d", index);
- ucs4 = id3_latin1_ucs4duplicate (tmp);
- g_free (tmp);
- }
- else
- {
- /* oups -- not a valid genre -- save the entire genre string */
- if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1)
- {
- /* we read 'ISO_8859_1' to stand for 'any locale
- charset' -- most programs seem to work that way */
- id3_latin1_t *raw = charset_from_utf8 (data);
- ucs4 = id3_latin1_ucs4duplicate (raw);
- g_free (raw);
- }
- else
- {
- /* Yeah! We use unicode encoding and won't have to
- worry about charsets */
- ucs4 = tmp_ucs4;
- tmp_ucs4 = NULL;
- }
- }
- g_free (tmp_ucs4);
- }
- else
- {
- if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1)
- {
- /* we read 'ISO_8859_1' to stand for 'any locale charset'
- -- most programs seem to work that way */
- id3_latin1_t *raw = charset_from_utf8 (data);
- ucs4 = id3_latin1_ucs4duplicate (raw);
- g_free (raw);
- }
- else
- {
- /* Yeah! We use unicode encoding and won't have to
- worry about charsets */
- ucs4 = id3_utf8_ucs4duplicate ((id3_utf8_t *)data);
- }
- }
-
- if (strcmp (frame_name, ID3_FRAME_COMMENT) == 0)
- res = id3_field_setfullstring (field, ucs4);
- else
- res = id3_field_setstrings (field, 1, &ucs4);
-
- g_free (ucs4);
-
- if (res != 0)
- g_print(_("Error setting ID3 field: %s\n"), frame_name);
-}
-
-
-/***
- * Reads id3v1.x / id3v2 apic data
- * @returns: TRUE on success, else FALSE.
- */
-static gboolean id3_apic_read (gchar *filename,
- guchar **image_data, gsize *image_data_len)
-{
- struct id3_file *id3file;
- struct id3_tag *id3tag;
-
- g_return_val_if_fail (filename, FALSE);
- g_return_val_if_fail (image_data, FALSE);
- g_return_val_if_fail (image_data_len, FALSE);
-
- *image_data = NULL;
- *image_data_len = 0;
-
- if (!(id3file = id3_file_open (filename, ID3_FILE_MODE_READONLY)))
- {
- gchar *fbuf = charset_to_utf8 (filename);
- g_print(_("ERROR while opening file: '%s' (%s).\n"),
- fbuf, g_strerror(errno));
- g_free (fbuf);
- return FALSE;
- }
-
- if ((id3tag = id3_file_tag(id3file)))
- {
- id3_length_t len;
- const guchar *coverart = NULL;
- int i;
- struct id3_frame *frame;
-
- /* Loop through APIC tags and set coverart. The picture type should be
- * 3 -- Cover (front), but iTunes has been known to use 0 -- Other. */
- for (i = 0; (frame = id3_tag_findframe(id3tag, "APIC", i)) != NULL; i++)
- {
- union id3_field *field = id3_frame_field (frame, 2);
- int pictype = field->number.value;
-/* printf ("%s: found apic type %d\n", filename, pictype);*/
-
- /* We'll prefer type 3 (cover) over type 0 (other) */
- if (pictype == 3)
- {
- coverart = id3_get_binary (id3tag, "APIC", &len, i);
- break;
- }
- if ((pictype == 0) && !coverart)
- {
- coverart = id3_get_binary (id3tag, "APIC", &len, i);
- }
- }
-
- if (coverart)
- { /* I guess iTunes is doing something wrong -- the
- * beginning of the coverart data ends up in a different
- field... We'll just add the missing data manually. */
- const guchar itunes_broken_jfif_marker[] =
- { 0x10, 'J', 'F', 'I', 'F'};
- if (len >= 5)
- {
- if (strncmp (itunes_broken_jfif_marker, coverart, 5) == 0)
- {
- const guchar itunes_missing_header[] =
- { 0xff, 0xd8, 0xff, 0xe0, 0x00 };
- *image_data = g_malloc (len+5);
- memcpy (*image_data, itunes_missing_header, 5);
- memcpy ((*image_data)+5, coverart, len);
- *image_data_len = len+5;
- }
- }
- if (!*image_data)
- {
- *image_data = g_malloc (len);
- memcpy (*image_data, coverart, len);
- *image_data_len = len;
- }
-#if LOCALDEBUG
- if (*image_data)
- {
- FILE *file;
- file = fopen ("/tmp/folder.jpg", "w");
- fwrite (*image_data, 1, *image_data_len, file);
- fclose (file);
- }
-#endif
- }
- }
- id3_file_close (id3file);
- return TRUE;
-}
-
-/***
- * Reads id3v1.x / id3v2 tag and load data into the Id3tag structure.
- * If a tag entry exists (ex: title), we allocate memory, else value
- * stays to NULL
- * @returns: TRUE on success, else FALSE.
- */
-gboolean id3_tag_read (gchar *filename, File_Tag *tag)
-{
- struct id3_file *id3file;
- struct id3_tag *id3tag;
- gchar* string;
- gchar* string2;
-
- g_return_val_if_fail (filename, FALSE);
- g_return_val_if_fail (tag, FALSE);
-
- memset (tag, 0, sizeof (File_Tag));
-
- if (!(id3file = id3_file_open (filename, ID3_FILE_MODE_READONLY)))
- {
- gchar *fbuf = charset_to_utf8 (filename);
- g_print(_("ERROR while opening file: '%s' (%s).\n"),
- fbuf, g_strerror(errno));
- g_free (fbuf);
- return FALSE;
- }
-
- if ((id3tag = id3_file_tag(id3file)))
- {
- tag->title = id3_get_string (id3tag, ID3_FRAME_TITLE);
- tag->artist = id3_get_string (id3tag, ID3_FRAME_GROUP);
- if (!tag->artist || !*tag->artist)
- {
- g_free (tag->artist);
- tag->artist = id3_get_string (id3tag, ID3_FRAME_ARTIST);
- }
- tag->album = id3_get_string (id3tag, ID3_FRAME_ALBUM);
- tag->year = id3_get_string (id3tag, ID3_FRAME_YEAR);
- tag->composer = id3_get_string (id3tag, "TCOM");
- tag->comment = id3_get_string (id3tag, ID3_FRAME_COMMENT);
- tag->genre = id3_get_string (id3tag, ID3_FRAME_GENRE);
- tag->compilation = id3_get_string (id3tag, "TCMP");
- tag->subtitle = id3_get_string (id3tag, "TIT3");
- tag->lyrics = id3_get_string (id3tag, "USLT");
- tag->podcasturl = id3_get_string (id3tag, "YTID");
- tag->podcastrss = id3_get_string (id3tag, "YWFD");
- tag->description = id3_get_string (id3tag, "YTDS");
- tag->time_released = id3_get_string (id3tag, "YTDR");
- tag->BPM = id3_get_string (id3tag, "TBPM");
- tag->sort_artist = id3_get_string (id3tag, "TSOP");
- tag->sort_album = id3_get_string (id3tag, "TSOA");
- tag->sort_title = id3_get_string (id3tag, "TSOT");
- tag->sort_albumartist = id3_get_string (id3tag, "TSO2");
- tag->sort_composer = id3_get_string (id3tag, "TSOC");
-
- string = id3_get_string (id3tag, "TLEN");
- if (string)
- {
- tag->songlen = (guint32) strtoul (string, 0, 10);
- g_free (string);
- }
-
- string = id3_get_string (id3tag, ID3_FRAME_TRACK);
- if (string)
- {
- string2 = strchr(string,'/');
- if (string2)
- {
- tag->track_total = g_strdup_printf ("%.2d", atoi (string2+1));
- *string2 = '\0';
- }
- tag->trackstring = g_strdup_printf ("%.2d", atoi (string));
- g_free(string);
- }
-
- /* CD/disc number tag handling */
- string = id3_get_string (id3tag, "TPOS");
- if (string)
- {
- string2 = strchr(string,'/');
- if (string2)
- {
- tag->cdno_total = g_strdup_printf ("%.2d", atoi (string2+1));
- *string2 = '\0';
- }
- tag->cdnostring = g_strdup_printf ("%.2d", atoi (string));
- g_free(string);
- }
- }
-
- id3_file_close (id3file);
- return TRUE;
-}
-
-
-
-static enum id3_field_textencoding get_encoding_of (struct id3_tag *tag, const char *frame_name)
-{
- struct id3_frame *frame;
- enum id3_field_textencoding encoding = -1;
-
- frame = id3_tag_findframe (tag, frame_name, 0);
- if (frame)
- {
- union id3_field *field = id3_frame_field (frame, 0);
- if (field && (id3_field_type (field) == ID3_FIELD_TYPE_TEXTENCODING))
- encoding = field->number.value;
- }
- return encoding;
-}
-
-/* Find out which encoding is being used. If in doubt, return
- * latin1. This code assumes that the same encoding is used in all
- * fields. */
-static enum id3_field_textencoding get_encoding (struct id3_tag *tag)
-{
- enum id3_field_textencoding enc;
-
- enc = get_encoding_of (tag, ID3_FRAME_TITLE);
- if (enc != -1) return enc;
- enc = get_encoding_of (tag, ID3_FRAME_ARTIST);
- if (enc != -1) return enc;
- enc = get_encoding_of (tag, ID3_FRAME_ALBUM);
- if (enc != -1) return enc;
- enc = get_encoding_of (tag, "TCOM");
- if (enc != -1) return enc;
- enc = get_encoding_of (tag, ID3_FRAME_COMMENT);
- if (enc != -1) return enc;
- enc = get_encoding_of (tag, ID3_FRAME_YEAR);
- if (enc != -1) return enc;
- return ID3_FIELD_TEXTENCODING_ISO_8859_1;
-}
-
-
-/* I'm not really sure about this: The original TAG identifier was
- "TID", but no matter what I do I end up writing "YTID" */
-void set_uncommon_tag (struct id3_tag *id3tag,
- const gchar *id,
- const gchar *text,
- enum id3_field_textencoding encoding)
-{
-#if 0
- struct id3_frame *frame;
-
- frame = id3_tag_findframe (id3tag, id, 0);
- union id3_field *field;
- frame->flags = 0;
- field = id3_frame_field (frame, 0);
- if (field)
- {
- string1 = g_strdup_printf ("%c%s", '\0',
- track->podcasturl);
- id3_field_setbinarydata (field, string1,
- strlen(track->podcasturl)+1);
- g_free (string1);
- }
-
-#endif
-}
-
-
-
-/**
- * Write the ID3 tags to the file.
- * @returns: TRUE on success, else FALSE.
- */
-gboolean mp3_write_file_info (gchar *filename, Track *track)
-{
- struct id3_tag* id3tag;
- struct id3_file* id3file;
- gint error = 0;
-
- id3file = id3_file_open (filename, ID3_FILE_MODE_READWRITE);
- if (!id3file)
- {
- gchar *fbuf = charset_to_utf8 (filename);
- g_print(_("ERROR while opening file: '%s' (%s).\n"),
- fbuf, g_strerror(errno));
- g_free (fbuf);
- return FALSE;
- }
-
- if ((id3tag = id3_file_tag(id3file)))
- {
- char *string1;
-
- enum id3_field_textencoding encoding;
-
- /* use the same coding as before... */
- encoding = get_encoding (id3tag);
- /* ...unless it's ISO_8859_1 and prefs say we should use
- unicode (i.e. ID3v2.4) */
- if (prefs_get_int("id3_write_id3v24") &&
- (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1))
- encoding = ID3_FIELD_TEXTENCODING_UTF_8;
-
- /* always render id3v1 to prevent dj studio from crashing */
- id3_tag_options(id3tag, ID3_TAG_OPTION_ID3V1, ~0);
-
- /* turn off frame compression and crc information to let
- itunes read tags see
- http://www.mars.org/mailman/public/mad-dev/2002-October/000742.html
- */
- id3_tag_options(id3tag, ID3_TAG_OPTION_COMPRESSION, 0);
- id3_tag_options(id3tag, ID3_TAG_OPTION_CRC, 0);
-
- id3_set_string (id3tag, ID3_FRAME_TITLE, track->title, encoding);
- id3_set_string (id3tag, ID3_FRAME_ARTIST, track->artist, encoding);
- id3_set_string (id3tag, ID3_FRAME_ALBUM, track->album, encoding);
- id3_set_string (id3tag, ID3_FRAME_GENRE, track->genre, encoding);
- id3_set_string (id3tag, ID3_FRAME_COMMENT, track->comment, encoding);
- id3_set_string (id3tag, "TIT3", track->subtitle, encoding);
- id3_set_string (id3tag, "TSOP", track->sort_artist, encoding);
- id3_set_string (id3tag, "TSOA", track->sort_album, encoding);
- id3_set_string (id3tag, "TSOT", track->sort_title, encoding);
- id3_set_string (id3tag, "TSO2", track->sort_albumartist, encoding);
- id3_set_string (id3tag, "TSOC", track->sort_composer, encoding);
-
- set_uncommon_tag (id3tag, "YTID", track->podcasturl, encoding);
- set_uncommon_tag (id3tag, "YTDS", track->description, encoding);
- set_uncommon_tag (id3tag, "YWFD", track->podcastrss, encoding);
-
- id3_set_string (id3tag, "TCOM", track->composer, encoding);
-
- string1 = g_strdup_printf("%d", track->year);
- id3_set_string(id3tag, ID3_FRAME_YEAR, string1, encoding);
- g_free(string1);
-
- string1 = g_strdup_printf("%d", track->BPM);
- id3_set_string(id3tag, "TBPM", string1, encoding);
- g_free(string1);
-
- if (track->tracks)
- string1 = g_strdup_printf ("%d/%d",
- track->track_nr, track->tracks);
- else
- string1 = g_strdup_printf ("%d", track->track_nr);
- id3_set_string (id3tag, ID3_FRAME_TRACK, string1, encoding);
- g_free(string1);
-
- if (track->cds)
- string1 = g_strdup_printf ("%d/%d",
- track->cd_nr, track->cds);
- else
- string1 = g_strdup_printf ("%d", track->cd_nr);
- id3_set_string (id3tag, "TPOS", string1, encoding);
- g_free(string1);
-
- string1 = g_strdup_printf ("%d", track->compilation);
- id3_set_string (id3tag, "TCMP", string1, encoding);
- g_free(string1);
- }
-
- if (id3_file_update(id3file) != 0)
- {
- gchar *fbuf = charset_to_utf8 (filename);
- g_print(_("ERROR while writing tag to file: '%s' (%s).\n"),
- fbuf, g_strerror(errno));
- g_free (fbuf);
- return FALSE;
- }
-
- id3_file_close (id3file);
-
- if (error) return FALSE;
- else return TRUE;
-}
-
-
-/*
- * Code to read the ReplayGain Values stored by LAME in its own tag.
- *
- * Most of the relevant information has been extracted from them LAME sources
- * (http://lame.sourceforge.net/).
- * The "Mp3 info Tag rev 1 specifications - draft 0"
- * (http://gabriel.mp3-tech.org/mp3infotag.html) by Gabriel Bouvigne describes
- * the actual Tag (except for small changes).
- * Details on the actual ReplayGain fields have been obtained from
- * http://www.replaygain.org .
- *
- * Apart from that, some information has been derived from phwip's LameTag
- * (http://www.silisoftware.com/applets/?scriptname=LameTag)
- *
- *
- * Code to read the ReplayGain Values stored in an Ape tag.
- *
- * Info on Lyrics3 V2.00 can be found at:
- * http://www.id3.org/lyrics3200.html
- * On the actual Ape Tag V2.0:
- * http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
- *
- * Copyright (C) 2004 Jens Taprogge <jens.taprogge at post.rwth-aachen.de>
- *
- * Provided under GPL according to Jens Taprogge. (JCS -- 12 March 2004)
- */
-
-#define TAG_FOOTER 0x10
-#define LAME_OFFSET 0x74
-#define SIDEINFO_MPEG1_MONO 17
-#define SIDEINFO_MPEG1_MULTI 32
-#define SIDEINFO_MPEG2_MONO 9
-#define SIDEINFO_MPEG2_MULTI 17
-#define ID3V1_SIZE 0x80
-#define APE_FOOTER_SIZE 0x20
-#define LYRICS_FOOTER_SIZE 0x0f
-
-
-static gint lame_vcmp(gchar a[5], gchar b[5]) {
- int r;
-
- r = strncmp(a, b, 4);
- if (r) return r;
-
- if (a[4] == b[4]) return 0;
-
- /* check for '.': indicates subminor version */
- if (a[4] == '.') return 1;
- if (b[4] == '.') return -1;
-
- /* check for alpha or beta versions */
- if (a[4] == ' ') return 1;
- if (b[4] == ' ') return -1;
-
- /* check for alpha, beta etc. indicated by a, b... */
- return strncmp(&a[4], &b[4], 1);
-}
-
-
-/* buf[] must be declared unsigned -- otherwise the casts, shifts and
- additions below produce funny results */
-static void read_lame_replaygain(unsigned char buf[],
- GainData *gd, int gain_adjust) {
- char oc, nc;
- gint gain;
-
- g_return_if_fail (gd);
-
- /* buf[0] and buf[1] are a bit field:
- 3 bits: name (mask: 0xe0 = 11100000)
- 3 bits: originator (mask: 0x1c = 00011100)
- 1 bit: negative if set (mask: 0x02 = 00000010)
- 9 bits: value
- */
-
- /* check originator */
- oc = (buf[0] & 0x1c) >> 2;
- if ((oc <= 0) || (oc > 3)) return;
-
- /* check name code */
- nc = buf[0] & 0xe0;
- if (!((nc == 0x20) || (nc == 0x40))) return;
-
- gain = ((((guint)buf[0]) & 0x1) << 8) + buf[1];
-
- /* This would be a value of -0.
- * That value however is illegal by current standards and reserved for
- * future use. */
- if ((!gain) && (buf[0] & 0x02)) return;
-
- if (buf[0] & 2) gain = -gain;
-
- gain += gain_adjust;
-
- switch (nc) {
- case 0x20:
- if (gd->radio_gain_set) return;
- gd->radio_gain = (gdouble)gain / 10;
- gd->radio_gain_set = TRUE;
-/* printf("radio_gain (lame): %i\n", gd->radio_gain); */
- break;
- case 0x40:
- if (gd->audiophile_gain_set) return;
- gd->audiophile_gain = (gdouble)gain / 10;
- gd->audiophile_gain_set = TRUE;
-/* printf("audiophile_gain (lame): %i\n",
- gd->audiophile_gain);*/
- break;
- }
-}
-
-
-static inline guint32 parse_ape_uint32(char *buf) {
- return (buf[0] & 0xff) | (buf[1] & 0xff) << 8
- | (buf[2] & 0xff) << 16 | (buf[3] & 0xff) << 24;
-}
-
-static inline guint32 parse_lame_uint32(char *buf) {
- return (buf[0] & 0xff) << 24 | (buf[1] & 0xff) << 16
- | (buf[2] & 0xff) << 8 | (buf[3] & 0xff);
-}
-
-
-/*
- * mp3_get_track_lame_replaygain - read the specified file and scan for LAME Tag
- * ReplayGain information.
- *
- * @path: localtion of the file
- * @track: structure holding track information
- *
- * FIXME: Are there other encoders writing a LAME Tag using a different magic
- * string?
- * TODO: Check CRC.
- */
-
-gboolean mp3_get_track_lame_replaygain (gchar *path, GainData *gd)
-{
- struct {
- /* All members are defined in terms of chars so padding does not
- * occur. Is there a cleaner way to keep the compiler from
- * padding? */
-
- char id[3];
- char version[2];
- char flags;
- char size[4];
- } id3head;
-
- FILE *file = NULL;
- char buf[4], version[5];
- int gain_adjust = 0;
- int sideinfo;
- guint32 ps;
-
- g_return_val_if_fail (gd, FALSE);
-
- gd->radio_gain = 0;
- gd->audiophile_gain = 0;
- gd->peak_signal = 0;
- gd->radio_gain_set = FALSE;
- gd->audiophile_gain_set = FALSE;
- gd->peak_signal_set = FALSE;
-
- if (!path)
- goto rg_fail;
-
- file = fopen (path, "r");
-
- if (!file)
- goto rg_fail;
-
- /* Skip ID3 header if appropriate */
- if (fread(&id3head, 1, sizeof(id3head), file) !=
- sizeof(id3head))
- goto rg_fail;
-
- if (!strncmp(id3head.id, "ID3", 3)) {
- int realsize = 0;
-
- realsize = (id3head.size[0] & 0x7f) << 21
- | (id3head.size[1] & 0x7f) << 14
- | (id3head.size[2] & 0x7f) << 7
- | (id3head.size[3] & 0x7f);
-
- if (id3head.flags & TAG_FOOTER) {
- /* footer is copy of header */
- realsize += sizeof(id3head);
- }
-
- if (fseek(file, realsize-1, SEEK_CUR) ||
- (!fread(&buf[0], 1, 1, file)))
- goto rg_fail;
- } else {
- /* no ID3 Tag - go back */
- fseek(file, -sizeof(id3head), SEEK_CUR);
- }
-
- /* Search Xing header. The location is dependant on the MPEG Layer and
- * whether the stream is mono or not. */
- if (fread(buf, 1, 4, file) != 4) goto rg_fail;
-
- /* should start with 0xff 0xf? (synch) */
- if (((buf[0] & 0xff) != 0xff)
- || ((buf[1] & 0xf0) != 0xf0)) goto rg_fail;
-
- /* determine the length of the sideinfo */
- if (buf[1] & 0x08) {
- sideinfo = ((buf[3] & 0xc0) == 0xc0) ?
- SIDEINFO_MPEG1_MONO : SIDEINFO_MPEG1_MULTI;
- } else {
- sideinfo = ((buf[3] & 0xc0) == 0xc0) ?
- SIDEINFO_MPEG2_MONO : SIDEINFO_MPEG2_MULTI;
- }
-
- if (fseek(file, sideinfo, SEEK_CUR) ||
- (fread(&buf[0], 1, 4, file) != 4))
- goto rg_fail;
-
- /* Is this really a Xing or Info Header?
- * FIXME: Apparently (according to madplay sources) there is a different
- * possible location for the Xing header ("due to an unfortunate
- * historical event"). I do not thing we need to care though since
- * RaplayGain information is only conatined in recent files. */
- if (strncmp(buf, "Xing", 4) && strncmp(buf, "Info", 4))
- goto rg_fail;
-
- /* Check for LAME Tag */
- if (fseek(file, LAME_OFFSET, SEEK_CUR) ||
- (fread(&buf[0], 1, 4, file) != 4))
- goto rg_fail;
- if (strncmp(buf, "LAME", 4))
- goto rg_fail;
-
- /* Check LAME Version */
- if (fread(version, 1, 5, file) != 5)
- goto rg_fail;
-
- /* Skip really old versions altogether. I am not sure when radio_gain
- * information was introduced. 3.89 does not seem to supprt it though.
- * */
- if (lame_vcmp(version, "3.90") < 0) {
-/* fprintf(stderr, "Old lame version (%c%c%c%c%c). Not used.\n",
- version[0], version[1], version[2], version[3], version[4]); */
- goto rg_fail;
- }
-
- if (fseek(file, 0x2, SEEK_CUR) || (fread(buf, 1, 4, file) != 4))
- goto rg_fail;
-
- /* get the peak signal. */
- ps = parse_lame_uint32(buf);
-
- /* Don't know when fixed-point PeakSingleAmplitude
- * was introduced exactly. 3.94b will be used for now.) */
- if ((lame_vcmp(version, "3.94b") >= 0)) {
- if ((!gd->peak_signal_set) && ps) {
- gd->peak_signal = ps;
- gd->peak_signal_set = TRUE;
-/* printf("peak_signal (lame): %f\n", (double)
- gd->peak_signal / 0x800000);*/
- }
- } else {
- float f = *((float *) (void *) (&ps)) * 0x800000;
- gd->peak_signal = (guint32) f;
- /* I would like to see an example of that. */
-/* printf("peak_signal (lame floating point): %f. PLEASE report.\n",
- (double) gd->peak_signal / 0x800000);*/
- }
-
- /*
- * Versions prior to 3.95.1 used a reference volume of 83dB.
- * (As compared to the currently used 89dB.)
- */
- if ((lame_vcmp(version, "3.95.") < 0)) {
- gain_adjust = 60;
-/* fprintf(stderr, "Old lame version (%c%c%c%c%c). Adjusting gain.\n",
- version[0], version[1], version[2], version[3], version[4]); */
- }
-
- if (fread(&buf[0], 1, 2, file) != 2)
- goto rg_fail;
-
- /* radio gain */
- read_lame_replaygain (buf, gd, gain_adjust);
-
- if (fread(&buf[0], 1, 2, file) != 2)
- goto rg_fail;
-
- /* audiophile gain */
- read_lame_replaygain (buf, gd, gain_adjust);
-
- fclose(file);
- return TRUE;
-
-rg_fail:
- if (file)
- fclose(file);
- return FALSE;
-}
-
-
-/*
- * mp3_get_track_ape_replaygain - read the specified file and scan for Ape Tag
- * ReplayGain information.
- *
- * @path: localtion of the file
- * @track: structure holding track information
- *
- * The function only modifies the gains if they have not previously been set.
- */
-
-gboolean mp3_get_track_ape_replaygain(gchar *path, GainData *gd)
-{
- /* The Ape Tag is located a t the end of the file. Or at least that
- * seems where it can most likely be found. Either it is at the very end
- * or before a trailing ID3v1 Tag. Sometimes a Lyrics3 Tag is placed
- * between the ID3v1 and the Ape Tag.
- * If you find files that have the Tags located in different
- * positions please let me know. */
-
- FILE *file = NULL;
- char buf[16];
- char *dbuf = NULL, *ep;
-
- int offset = 0;
- int i;
- int pos = 0, pos2 = 0;
- guint32 version;
- guint32 data_length;
- guint32 entry_length = 0;
- guint32 entries;
- double d;
-
- g_return_val_if_fail (gd, FALSE);
- g_return_val_if_fail (path, FALSE);
-
- file = fopen (path, "r");
-
- if (!file)
- goto rg_fail;
-
- /* check for ID3v1 Tag */
- if (fseek(file, -ID3V1_SIZE, SEEK_END) ||
- fread(&buf, 1, 3, file) != 3)
- goto rg_fail;
- if (!strncmp(buf, "TAG", 3)) offset -= ID3V1_SIZE;
-
- /* check for Lyrics3 Tag */
- if (fseek(file, -9 + offset, SEEK_END) ||
- fread(&buf, 1, 9, file) != 9)
- goto rg_fail;
- if (!strncmp(buf, "LYRICS200", 9)) {
- if (fseek(file, -LYRICS_FOOTER_SIZE + offset, SEEK_END) ||
- fread(&buf, 1, 9, file) != 9)
- goto rg_fail;
- data_length = buf[0] - '0';
- for (i = 1; i < 6; i++) {
- data_length *= 10;
- data_length += buf[i] - '0';
- }
- if (fseek(file, -LYRICS_FOOTER_SIZE - data_length + offset,
- SEEK_END) ||
- fread(&buf, 1, 11, file) != 11)
- goto rg_fail;
- if (!strncmp(buf, "LYRICSBEGIN", 11))
- offset -= LYRICS_FOOTER_SIZE + data_length;
- }
-
- /* check for APE Tag */
- if (fseek(file, -APE_FOOTER_SIZE + offset, SEEK_END) ||
- fread(&buf, 1, 8, file) != 8)
- goto rg_fail;
- if (strncmp(buf, "APETAGEX", 8)) goto rg_fail;
-
- /* Check the version of the tag. 1000 and 2000 (v1.0 and 2.0) are the
- * only ones I know about. Make suer things do not break in the future.
- * */
- if (fread(&buf, 1, 4, file) != 4)
- goto rg_fail;
- version = parse_ape_uint32(buf);
- if (version != 1000 && version != 2000)
- goto rg_fail;
-
- /* determine data length */
- if (fread(&buf, 1, 4, file) != 4)
- goto rg_fail;
- data_length = parse_ape_uint32(buf) - APE_FOOTER_SIZE;
-
- /* determine number of entries */
- if (fread(&buf, 1, 4, file) != 4)
- goto rg_fail;
- entries = parse_ape_uint32(buf);
-
- /* seek to first entry and read the whole buffer*/
- if (fseek(file, -APE_FOOTER_SIZE + offset - data_length, SEEK_END))
- goto rg_fail;
- if (!(dbuf = malloc(data_length)))
- goto rg_fail;
- if (fread(dbuf, 1, data_length, file) != data_length)
- goto rg_fail;
-
- for (i = 0; i < entries; i++) {
- if (gd->radio_gain_set && gd->peak_signal_set) break;
- pos = pos2 + entry_length;
- if (pos > data_length - 10) break;
-
- entry_length = parse_ape_uint32(&dbuf[pos]); pos += 4;
- pos += 4;
-
- pos2 = pos;
- while (dbuf[pos2] && pos2 < data_length) pos2++;
- if (pos2 == data_length) break;
- pos2++;
-
- if (entry_length + 1 > sizeof(buf))
- continue;
-/* printf ("%s:%d:%d\n",&dbuf[pos], pos2, pos); */
- if (!gd->radio_gain_set && !strcasecmp(&dbuf[pos],
- "REPLAYGAIN_TRACK_GAIN")) {
- memcpy(buf, &dbuf[pos2], entry_length);
- buf[entry_length] = '\0';
-
- d = g_ascii_strtod(buf, &ep);
-/* printf("%f\n", d); */
- if ((ep == buf + entry_length - 3)
- && (!strncasecmp(ep, " dB", 3))) {
- gd->radio_gain = d;
- gd->radio_gain_set = TRUE;
-/* printf("radio_gain (ape): %i\n", gd->radio_gain);*/
- }
-
- continue;
- }
- if (!gd->peak_signal_set && !strcasecmp(&dbuf[pos],
- "REPLAYGAIN_TRACK_PEAK")) {
- memcpy(buf, &dbuf[pos2], entry_length);
- buf[entry_length] = '\0';
-
- d = g_ascii_strtod(buf, &ep);
- if (ep == buf + entry_length) {
- d *= 0x800000;
- gd->peak_signal = (guint32) floor(d + 0.5);
- gd->peak_signal_set = TRUE;
-/* printf("peak_signal (ape): %f\n", (double) gd->peak_signal / 0x800000);*/
- }
-
- continue;
- }
- }
-
- free(dbuf);
- fclose(file);
- return TRUE;
-
-rg_fail:
- if (dbuf)
- free(dbuf);
- if (file)
- fclose(file);
- return FALSE;
-}
-
-
-/* ----------------------------------------------------------------------
-
- mp3gain code
-
----------------------------------------------------------------------- */
-
-#include <sys/wait.h>
-#include <fcntl.h>
-
-
-
-
-
-
-/**
- * mp3_read_soundcheck:
- *
- * try to read the ReplayGain values from the LAME or Ape Tags and set
- * the track's soundcheck field accordingly.
- *
- * @path: localtion of the file
- * @track: structure holding track information
- *
- * The function always rereads the gain from the file.
- *
- * Returns TRUE if the soundcheck field could be set.
- */
-gboolean mp3_read_soundcheck (gchar *path, Track *track)
-{
- GainData gd;
-
- g_return_val_if_fail (track, FALSE);
-
- memset (&gd, 0, sizeof (GainData));
-
- gd.radio_gain_set = FALSE;
- gd.audiophile_gain_set = FALSE;
- gd.peak_signal_set = FALSE;
-
- mp3_get_track_lame_replaygain (path, &gd);
- if (gd.radio_gain_set)
- {
- track->soundcheck = replaygain_to_soundcheck (gd.radio_gain);
- return TRUE;
- }
-
- mp3_get_track_ape_replaygain (path, &gd);
- if (gd.radio_gain_set)
- {
- track->soundcheck = replaygain_to_soundcheck (gd.radio_gain);
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-
-
-
-
-/* mp3 slot size in bytes */
-int slotsize[3] = {4,1,1}; /* layer 1, layer 2, layer 3 */
-
-int samplesperframe[2][3] = {
- { /* MPEG 2.0 */
- 384,1152,576 /* layer 1, layer 2, layer 3 */
- },
-
- { /* MPEG 1.0 */
- 384,1152,1152 /* layer 1, layer 2, layer 3 */
- }
-};
-
-
-/*
- * mp3_get_track_lame_gapless - read the specified file and scan for LAME Tag
- * gapless information.
- *
- * @path: localtion of the file
- * @track: structure holding track information
- *
- * TODO: Split off non-LAME stuff (samplecount, gapless_data) to a separate function since it's generic
- */
-gboolean mp3_get_track_lame_gapless (gchar *path, GaplessData *gd)
-{
- FILE *file = NULL;
- char buf[4], version[5];
- unsigned char ubuf[4];
- int sideinfo;
- int i;
-
- g_return_val_if_fail (gd, FALSE);
-
- if (!path)
- goto gp_fail;
-
- file = fopen (path, "rb");
-
- if (!file)
- goto gp_fail;
-
- /* use get_first_header() to seek to the first mp3 header */
- MP3Info *mp3i = NULL;
- mp3i = g_malloc0 (sizeof (MP3Info));
- mp3i->filename = path;
- mp3i->file = file;
- get_mp3_info (mp3i);
- get_first_header (mp3i, 0);
-
- int xing_header_offset = ftell (file);
-
- MP3Header h;
- if (!get_header (file, &h))
- goto gp_fail;
-
- int mysamplesperframe = samplesperframe[h.version & 1][3 - h.layer];
-
- /* Determine offset of Xing header based on sideinfo size */
- if (h.version & 0x1)
- {
- sideinfo = (h.mode & 0x2) ?
- SIDEINFO_MPEG1_MONO : SIDEINFO_MPEG1_MULTI;
- }
- else
- {
- sideinfo = (h.mode & 0x2) ?
- SIDEINFO_MPEG2_MONO : SIDEINFO_MPEG2_MULTI;
- }
-
- if (fseek (file, sideinfo, SEEK_CUR) ||
- (fread (&buf[0], 1, 4, file) != 4))
- goto gp_fail;
-
- /* Is this really a Xing or Info Header?
- * FIXME: Apparently (according to madplay sources) there is a different
- * possible location for the Xing header ("due to an unfortunate
- * historical event"). I do not thing we need to care though since
- * ReplayGain information is only contained in recent files. */
- if (strncmp (buf, "Xing", 4) && strncmp (buf, "Info", 4))
- goto gp_fail;
-
- /* Determine the offset of the LAME tag based on contents of the Xing header */
- int flags;
- fread (&flags, 4, 1, file);
- int toskip = 0;
- if (flags | 0x1)
- { /* frames field is set */
- toskip += 4;
- }
- if (flags | 0x2)
- { /* bytes field is set */
- toskip += 4;
- }
- if (flags | 0x4)
- { /* TOC field is set */
- toskip += 100;
- }
- if (flags | 0x8)
- { /* quality field is set */
- toskip += 4;
- }
-
- /* Check for LAME Tag */
- if (fseek (file, toskip, SEEK_CUR) || (fread (&buf[0], 1, 4, file) != 4))
- goto gp_fail;
- if (strncmp (buf, "LAME", 4))
- goto gp_fail;
-
- /* Check LAME Version */
- if (fread (version, 1, 5, file) != 5)
- goto gp_fail;
-
- /* XXX skip old LAME versions, or just assume that pre/postgap
- * turn out zeros anyway, or check the CRC to vaidate the tag? */
-
- gboolean cbr = FALSE;
- if (fread (ubuf, 1, 1, file) != 1)
- goto gp_fail;
-
- if ((ubuf[0] & 0xf) == 0x1)
- cbr = TRUE;
-
- if (fseek (file, 0xB, SEEK_CUR) || (fread (ubuf, 1, 4, file) != 4))
- goto gp_fail;
-
- /* set pregap and postgap directly from LAME header */
- gd->pregap = (ubuf[0] << 4) + (ubuf[1] >> 4);
- gd->postgap = ((ubuf[1] & 0xf) << 8) + ubuf[2];
-
- /* jump the end of the frame with the xing header */
- if (fseek (file, xing_header_offset + frame_length (&h), SEEK_SET))
- goto gp_fail;
-
- /* counts bytes from the start of the 1st sync frame */
- int totaldatasize = frame_length (&h);
-
- /* keeps track of the last 8 frame sizes */
- int lastframes[8];
-
- /* counts number of music frames */
- int totalframes = 0;
-
- /* quickly parse the file, reading only frame headers */
- int l = 0;
- while ((l = get_header (file, &h)) != 0)
- {
- for (i = 7; i > 0; i--)
- {
- lastframes[i] = lastframes[i - 1];
- }
- lastframes[0] = l;
- totaldatasize += l;
- totalframes++;
-
- if (fseek (file, l - FRAME_HEADER_SIZE, SEEK_CUR))
- goto gp_fail;
-
- }
-
- int finaleight = 0;
- for (i = 0; i < 8; i++)
- {
- finaleight += lastframes[i];
- }
-
- if (cbr)
- totalframes++;
-
- /* all but last eight frames */
- gd->gapless_data = totaldatasize - finaleight;
- /* total samples minus pre/postgap */
- gd->samplecount = totalframes * mysamplesperframe - gd->pregap - gd->postgap;
-
- return TRUE;
-
-
- gp_fail:
- if (file)
- fclose (file);
- return FALSE;
-
-}
-
-
-
-/**
- * mp3_read_gapless:
- *
- * try to read the gapless values from the LAME Tag and set
- * the track's pregap, postgap, samplecount, and gapless_data fields
- * accordingly.
- *
- * @path: location of the file
- * @track: structure holding track information
- *
- * The function always rereads the data from the file.
- *
- * Returns TRUE if all four gapless fields could be
- * set. etrack->tchanged is set to TRUE if data has been changed,
- * FALSE otherwise.
- */
-gboolean mp3_read_gapless (char *path, Track *track)
-{
- GaplessData gd;
- ExtraTrackData *etr;
-
- g_return_val_if_fail (track, FALSE);
-
- etr = track->userdata;
-
- memset (&gd, 0, sizeof (GaplessData));
-
- gd.pregap = 0;
- gd.samplecount = 0;
- gd.postgap = 0;
- gd.gapless_data = 0;
-
- mp3_get_track_lame_gapless (path, &gd);
-
- etr->tchanged = FALSE;
-
- if ((gd.pregap) && (gd.samplecount) && (gd.postgap) && (gd.gapless_data))
- {
- if ((track->pregap != gd.pregap) ||
- (track->samplecount != gd.samplecount) ||
- (track->postgap != gd.postgap) ||
- (track->gapless_data != gd.gapless_data) ||
- (track->gapless_track_flag == FALSE))
- {
- etr->tchanged = TRUE;
- track->pregap = gd.pregap;
- track->samplecount = gd.samplecount;
- track->postgap = gd.postgap;
- track->gapless_data = gd.gapless_data;
- track->gapless_track_flag = TRUE;
- }
- }
- return FALSE;
-}
-
-
-
-/* ----------------------------------------------------------------------
-
- From here starts original gtkpod code
-
----------------------------------------------------------------------- */
-
-/* Return a Track structure with all information read from the mp3
- file filled in */
-Track *mp3_get_file_info (gchar *name)
-{
- Track *track = NULL;
- File_Tag filetag;
- MP3Info *mp3i=NULL;
- FILE *file;
- guchar *image_data = NULL;
- gsize image_data_len = 0;
-
- g_return_val_if_fail (name, NULL);
-
- /* Attempt to open the file */
- file = fopen (name, "r");
- if (file)
- {
- mp3i = g_malloc0 (sizeof (MP3Info));
- mp3i->filename = name;
- mp3i->file = file;
- get_mp3_info (mp3i);
- mp3i->file = NULL;
- fclose (file);
- }
- else
- {
- gchar *fbuf = charset_to_utf8 (name);
- gtkpod_warning(_("ERROR while opening file: '%s' (%s).\n"),
- fbuf, g_strerror(errno));
- g_free (fbuf);
- return NULL;
- }
-
- track = gp_track_new ();
- track->filetype = g_strdup ("MPEG audio file");
-
- if (prefs_get_int("readtags") && (id3_tag_read (name, &filetag) == TRUE))
- {
-
- if (filetag.album)
- {
- track->album = filetag.album;
- }
-
- if (filetag.artist)
- {
- track->artist = filetag.artist;
- }
-
- if (filetag.title)
- {
- track->title = filetag.title;
- }
-
- if (filetag.genre)
- {
- track->genre = filetag.genre;
- }
-
- if (filetag.composer)
- {
- track->composer = filetag.composer;
- }
-
- if (filetag.comment)
- {
- track->comment = filetag.comment;
- }
-
- if (filetag.podcasturl)
- {
- track->podcasturl = filetag.podcasturl;
- }
-
- if (filetag.podcastrss)
- {
- track->podcastrss = filetag.podcastrss;
- }
-
- if (filetag.subtitle)
- {
- track->subtitle = filetag.subtitle;
- }
-
- if (filetag.description)
- {
- track->description = filetag.description;
- }
-
- if (filetag.sort_artist)
- {
- track->sort_artist = filetag.sort_artist;
- }
-
- if (filetag.sort_title)
- {
- track->sort_title = filetag.sort_title;
- }
-
- if (filetag.sort_album)
- {
- track->sort_album = filetag.sort_album;
- }
-
- if (filetag.sort_albumartist)
- {
- track->sort_albumartist = filetag.sort_albumartist;
- }
-
- if (filetag.sort_composer)
- {
- track->sort_composer = filetag.sort_composer;
- }
-
- if (filetag.year == NULL)
- {
- track->year = 0;
- }
- else
- {
- track->year = atoi(filetag.year);
- g_free (filetag.year);
- }
-
- if (filetag.trackstring == NULL)
- {
- track->track_nr = 0;
- }
- else
- {
- track->track_nr = atoi(filetag.trackstring);
- g_free (filetag.trackstring);
- }
-
- if (filetag.track_total == NULL)
- {
- track->tracks = 0;
- }
- else
- {
- track->tracks = atoi(filetag.track_total);
- g_free (filetag.track_total);
- }
- /* CD/disc number handling */
- if (filetag.cdnostring == NULL)
- {
- track->cd_nr = 0;
- }
- else
- {
- track->cd_nr = atoi(filetag.cdnostring);
- g_free (filetag.cdnostring);
- }
-
- if (filetag.cdno_total == NULL)
- {
- track->cds = 0;
- }
- else
- {
- track->cds = atoi(filetag.cdno_total);
- g_free (filetag.cdno_total);
- }
-
- if (filetag.compilation == NULL)
- {
- track->compilation = 0;
- }
- else
- {
- track->compilation = atoi(filetag.compilation);
- g_free (filetag.compilation);
- }
-
- if (filetag.BPM == NULL)
- {
- track->BPM = 0;
- }
- else
- {
- track->BPM = atoi(filetag.BPM);
- g_free (filetag.BPM);
- }
-
- if (filetag.lyrics)
- {
- track->lyrics_flag = 0x01;
- g_free (filetag.lyrics);
- }
- else
- {
- track->lyrics_flag = 0x00;
- }
- }
-
- if (prefs_get_int("coverart_apic") &&
- (id3_apic_read (name, &image_data, &image_data_len) == TRUE))
- {
- if (image_data)
- {
- gp_track_set_thumbnails_from_data (track,
- image_data,
- image_data_len);
- g_free (image_data);
- }
- }
-
- mp3_read_soundcheck (name, track);
-
- mp3_read_gapless (name, track);
-
-#if LOCALDEBUG
- printf("%s\n", name);
- printf("\tpregap: %i\n", track->pregap);
- printf("\tpostgap: %i\n", track->postgap);
- printf("\tsamplecount: %li\n", track->samplecount);
- printf("\tgaplessdata: %i\n", track->gapless_data);
-#endif
-
-
- /* Get additional info (play time and bitrate */
- if (mp3i)
- {
- track->tracklen = mp3i->milliseconds;
- track->bitrate = (gint)(mp3i->vbr_average);
- track->samplerate = mp3file_header_frequency (&mp3i->header);
- g_free (mp3i);
- }
- /* Fall back to xmms code if tracklen is 0 */
- if (track->tracklen == 0)
- {
- track->tracklen = get_track_time (name);
- if (track->tracklen)
- track->bitrate = (float)track->size*8/track->tracklen;
- }
-
- if (track->tracklen == 0)
- {
- /* Tracks with zero play length are ignored by iPod... */
- gtkpod_warning (_("File \"%s\" has zero play length. Ignoring.\n"),
- name);
- gp_track_free (track);
- track = NULL;
- }
- return track;
-}
Copied: gtkpod/tags/0.99.10-3/src/mp3file.c (from rev 243, gtkpod/trunk/src/mp3file.c)
===================================================================
--- gtkpod/tags/0.99.10-3/src/mp3file.c (rev 0)
+++ gtkpod/tags/0.99.10-3/src/mp3file.c 2007-09-09 19:04:45 UTC (rev 245)
@@ -0,0 +1,2678 @@
+/* Time-stamp: <2007-06-26 00:39:11 jcs>
+|
+| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
+| Part of the gtkpod project.
+|
+| URL: http://www.gtkpod.org/
+| URL: http://gtkpod.sourceforge.net/
+|
+| 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
+|
+| iTunes and iPod are trademarks of Apple
+|
+| This product is not supported/written/published by Apple!
+|
+| $Id: mp3file.c 1591 2007-06-26 03:33:25Z tmzullinger $
+*/
+
+
+#define LOCALDEBUG 0
+
+
+/* The code in the first section of this file is taken from the
+ * mp3info (http://www.ibiblio.org/mp3info/) project. Only the code
+ * needed for the playlength calculation has been extracted. */
+
+/* The code in the second section of this file is taken from the
+ * mpg123 code used in xmms-1.2.7 (Input/mpg123). Only the code needed
+ * for the playlength calculation has been extracted. */
+
+/* The code in the last section of this file is original gtkpod
+ * code. */
+
+/****************
+ * Declarations *
+ ****************/
+
+#include <glib.h>
+#include <math.h>
+/*
+ * Description of each item of the TagList list
+ */
+typedef struct _File_Tag File_Tag;
+typedef struct _GainData GainData;
+typedef struct _GaplessData GaplessData;
+
+struct _File_Tag
+{
+ gchar *title; /* Title of track */
+ gchar *artist; /* Artist name */
+ gchar *album; /* Album name */
+ gchar *year; /* Year of track */
+ gchar *trackstring; /* Position of track in the album */
+ gchar *track_total; /* The number of tracks for the album (ex: 12/20) */
+ gchar *genre; /* Genre of song */
+ gchar *comment; /* Comment */
+ gchar *composer; /* Composer */
+ guint32 songlen; /* Length of file in ms */
+ gchar *cdnostring; /* Position of disc in the album */
+ gchar *cdno_total; /* The number of discs in the album (ex: 1/2) */
+ gchar *compilation; /* The track is a member of a compilation */
+ gchar *podcasturl; /* The following are mainly used for podcasts */
+ gchar *sort_artist;
+ gchar *sort_title;
+ gchar *sort_album;
+ gchar *sort_albumartist;
+ gchar *sort_composer;
+ gchar *description;
+ gchar *podcastrss;
+ gchar *time_released;
+ gchar *subtitle;
+ gchar *BPM; /* beats per minute */
+ gchar *lyrics; /* does not appear to be the full lyrics --
+ only used to set the flag 'lyrics_flag'
+ of the Track structure */
+};
+
+
+struct _GainData
+{
+ guint32 peak_signal; /* LAME Peak Signal * 0x800000 */
+ gdouble radio_gain; /* RadioGain in dB
+ (as defined by www.replaygain.org) */
+ gdouble audiophile_gain;/* AudiophileGain in dB
+ (as defined by www.replaygain.org) */
+ gboolean peak_signal_set; /* has the peak signal been set? */
+ gboolean radio_gain_set; /* has the radio gain been set? */
+ gboolean audiophile_gain_set;/* has the audiophile gain been set? */
+};
+
+struct _GaplessData
+{
+ guint32 pregap; /* number of pregap samples */
+ guint64 samplecount; /* number of actual music samples */
+ guint32 postgap; /* number of postgap samples */
+ guint32 gapless_data; /* number of bytes from the first sync frame to the 8th to last frame */
+};
+
+/* This code is taken from the mp3info code. Only the code needed for
+ * the playlength calculation has been extracted */
+
+/*
+ mp3tech.c - Functions for handling MP3 files and most MP3 data
+ structure manipulation.
+
+ Copyright (C) 2000-2001 Cedric Tefft <cedric at earthling.net>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ ***************************************************************************
+
+ This file is based in part on:
+
+ * MP3Info 0.5 by Ricardo Cerqueira <rmc at rccn.net>
+ * MP3Stat 0.9 by Ed Sweetman <safemode at voicenet.com> and
+ Johannes Overmann <overmann at iname.com>
+
+*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "mp3file.h"
+#include "charset.h"
+#include "itdb.h"
+#include "file.h"
+#include "misc.h"
+
+
+/* MIN_CONSEC_GOOD_FRAMES defines how many consecutive valid MP3 frames
+ we need to see before we decide we are looking at a real MP3 file */
+#define MIN_CONSEC_GOOD_FRAMES 4
+#define FRAME_HEADER_SIZE 4
+#define MIN_FRAME_SIZE 21
+
+enum VBR_REPORT { VBR_VARIABLE, VBR_AVERAGE, VBR_MEDIAN };
+
+typedef struct {
+ gulong sync;
+ guint version;
+ guint layer;
+ guint crc;
+ guint bitrate;
+ guint freq;
+ guint padding;
+ guint extension;
+ guint mode;
+ guint mode_extension;
+ guint copyright;
+ guint original;
+ guint emphasis;
+} MP3Header;
+
+typedef struct {
+ gchar *filename;
+ FILE *file;
+ off_t datasize;
+ gint header_isvalid;
+ MP3Header header;
+ gint id3_isvalid;
+ gint vbr;
+ float vbr_average;
+ gint milliseconds;
+ gint frames;
+ gint badframes;
+} MP3Info;
+
+/* This is for xmms code */
+static guint get_track_time(gchar *path);
+
+
+
+/* ------------------------------------------------------------
+
+ start of first section
+
+ ------------------------------------------------------------ */
+void get_mp3_info(MP3Info *mp3);
+
+gint frequencies[3][4] = {
+ {22050,24000,16000,50000}, /* MPEG 2.0 */
+ {44100,48000,32000,50000}, /* MPEG 1.0 */
+ {11025,12000,8000,50000} /* MPEG 2.5 */
+};
+
+/* "0" added by JCS */
+gint bitrate[2][3][16] = {
+ { /* MPEG 2.0 */
+ {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0},/* layer 1 */
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0}, /* layer 2 */
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0} /* layer 3 */
+ },
+
+ { /* MPEG 1.0 */
+ {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},/* layer 1 */
+ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, /* layer 2 */
+ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0} /* layer 3 */
+ }
+};
+
+gint frame_size_index[] = {24000, 72000, 72000};
+
+
+gchar *mode_text[] = {
+ "stereo", "joint stereo", "dual channel", "mono"
+};
+
+gchar *emphasis_text[] = {
+ "none", "50/15 microsecs", "reserved", "CCITT J 17"
+};
+
+
+static gint mp3file_header_bitrate(MP3Header *h) {
+ return bitrate[h->version & 1][3-h->layer][h->bitrate];
+}
+
+
+static gint mp3file_header_frequency(MP3Header *h) {
+ return frequencies[h->version][h->freq];
+}
+
+
+gint frame_length(MP3Header *header) {
+ return header->sync == 0xFFE ?
+ (frame_size_index[3-header->layer]*((header->version&1)+1)*
+ mp3file_header_bitrate(header)/(float)mp3file_header_frequency(header))+
+ header->padding : 1;
+}
+
+/* Get next MP3 frame header.
+ Return codes:
+ positive value = Frame Length of this header
+ 0 = No, we did not retrieve a valid frame header
+*/
+gint get_header(FILE *file,MP3Header *header)
+{
+ guchar buffer[FRAME_HEADER_SIZE];
+ gint fl;
+
+ if(fread(&buffer,FRAME_HEADER_SIZE,1,file)<1) {
+ header->sync=0;
+ return 0;
+ }
+ header->sync=(((gint)buffer[0]<<4) | ((gint)(buffer[1]&0xE0)>>4));
+ if(buffer[1] & 0x10) header->version=(buffer[1] >> 3) & 1;
+ else header->version=2;
+ header->layer=(buffer[1] >> 1) & 3;
+ if (header->layer == 0)
+ {
+ header->layer = 1; /* sanity added by JCS */
+ }
+ if((header->sync != 0xFFE) || (header->layer != 1)) {
+ header->sync=0;
+ return 0;
+ }
+ header->crc=buffer[1] & 1;
+ header->bitrate=(buffer[2] >> 4) & 0x0F;
+ header->freq=(buffer[2] >> 2) & 0x3;
+ header->padding=(buffer[2] >>1) & 0x1;
+ header->extension=(buffer[2]) & 0x1;
+ header->mode=(buffer[3] >> 6) & 0x3;
+ header->mode_extension=(buffer[3] >> 4) & 0x3;
+ header->copyright=(buffer[3] >> 3) & 0x1;
+ header->original=(buffer[3] >> 2) & 0x1;
+ header->emphasis=(buffer[3]) & 0x3;
+
+ return ((fl=frame_length(header)) >= MIN_FRAME_SIZE ? fl : 0);
+}
+
+gint sameConstant(MP3Header *h1, MP3Header *h2) {
+ if((*(guint*)h1) == (*(guint*)h2)) return 1;
+
+ if((h1->version == h2->version ) &&
+ (h1->layer == h2->layer ) &&
+ (h1->crc == h2->crc ) &&
+ (h1->freq == h2->freq ) &&
+ (h1->mode == h2->mode ) &&
+ (h1->copyright == h2->copyright ) &&
+ (h1->original == h2->original ) &&
+ (h1->emphasis == h2->emphasis ))
+ return 1;
+ else return 0;
+}
+
+
+gint get_first_header(MP3Info *mp3, long startpos)
+{
+ gint k, l=0,c;
+ MP3Header h, h2;
+ long valid_start=0;
+
+ fseek(mp3->file,startpos,SEEK_SET);
+ while (1) {
+ while((c=fgetc(mp3->file)) != 255 && (c != EOF));
+ if(c == 255) {
+ ungetc(c,mp3->file);
+ valid_start=ftell(mp3->file);
+ if((l=get_header(mp3->file,&h))) {
+ fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
+ for(k=1; (k < MIN_CONSEC_GOOD_FRAMES) && (mp3->datasize-ftell(mp3->file) >= FRAME_HEADER_SIZE); k++) {
+ if(!(l=get_header(mp3->file,&h2))) break;
+ if(!sameConstant(&h,&h2)) break;
+ fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
+ }
+ if(k == MIN_CONSEC_GOOD_FRAMES) {
+ fseek(mp3->file,valid_start,SEEK_SET);
+ memcpy(&(mp3->header),&h2,sizeof(MP3Header));
+ mp3->header_isvalid=1;
+ return 1;
+ }
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* get_next_header() - read header at current position or look for
+ the next valid header if there isn't one at the current position
+*/
+gint get_next_header(MP3Info *mp3)
+{
+ gint l=0,c,skip_bytes=0;
+ MP3Header h;
+
+ while(1) {
+ while((c=fgetc(mp3->file)) != 255 && (ftell(mp3->file) < mp3->datasize)) skip_bytes++;
+ if(c == 255) {
+ ungetc(c,mp3->file);
+ if((l=get_header(mp3->file,&h))) {
+ if(skip_bytes) mp3->badframes++;
+ fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
+ return 15-h.bitrate;
+ } else {
+ skip_bytes += FRAME_HEADER_SIZE;
+ }
+ } else {
+ if(skip_bytes) mp3->badframes++;
+ return 0;
+ }
+ }
+}
+
+
+void get_mp3_info(MP3Info *mp3)
+{
+ gint frame_type[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ float milliseconds=0,total_rate=0;
+ gint frames=0,frame_types=0,frames_so_far=0;
+ gint vbr_median=-1;
+ guint bitrate;
+ gint counter=0;
+ MP3Header header;
+ struct stat filestat;
+ off_t data_start=0;
+
+
+ stat(mp3->filename,&filestat);
+ mp3->datasize=filestat.st_size;
+
+ if(get_first_header(mp3,0L)) {
+ data_start=ftell(mp3->file);
+ while((bitrate=get_next_header(mp3))) {
+ if (bitrate < 15) /* sanity added by JCS */
+ frame_type[15-bitrate]++;
+ frames++;
+ }
+ memcpy(&header,&(mp3->header),sizeof(MP3Header));
+ for(counter=0;counter<15;counter++) {
+ if(frame_type[counter]) {
+ float header_bitrate; /* introduced by JCS to speed up */
+ frame_types++;
+ header.bitrate=counter;
+ frames_so_far += frame_type[counter];
+ header_bitrate = mp3file_header_bitrate(&header);
+ if (header_bitrate != 0)
+ milliseconds += (float)(8*frame_length(&header)*frame_type[counter])/header_bitrate;
+ total_rate += header_bitrate*frame_type[counter];
+ if((vbr_median == -1) && (frames_so_far >= frames/2))
+ vbr_median=counter;
+ }
+ }
+ mp3->milliseconds=(gint)(milliseconds+0.5);
+ mp3->header.bitrate=vbr_median;
+ mp3->vbr_average=total_rate/(float)frames;
+ mp3->frames=frames;
+ if(frame_types > 1) {
+ mp3->vbr=1;
+ }
+ }
+}
+
+
+
+
+/* ------------------------------------------------------------
+
+ xmms code
+
+
+ ------------------------------------------------------------ */
+
+/*
+| Changed by Jorg Schuler <jcsjcs at users.sourceforge.net> to
+| compile with the gtkpod project. 2003/04/01
+*/
+
+/* This code is taken from the mpg123 code used in xmms-1.2.7
+ * (Input/mpg123). Only the code needed for the playlength calculation
+ * has been extracted */
+
+#include "mp3file.h"
+#include <stdio.h>
+#include <string.h>
+
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+#define SBLIMIT 32
+#define SCALE_BLOCK 12
+#define SSLIMIT 18
+
+#define MPG_MD_STEREO 0
+#define MPG_MD_JOINT_STEREO 1
+#define MPG_MD_DUAL_CHANNEL 2
+#define MPG_MD_MONO 3
+#define MAXFRAMESIZE 1792
+#define real float
+
+struct bitstream_info
+{
+ int bitindex;
+ unsigned char *wordpointer;
+};
+
+struct bitstream_info bsi;
+
+real mpg123_muls[27][64]; /* also used by layer 1 */
+
+int tabsel_123[2][3][16] =
+{
+ {
+ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}},
+
+ {
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}}
+};
+
+long mpg123_freqs[9] =
+{44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000};
+
+/*
+ * structure to receive extracted header
+ */
+typedef struct
+{
+ int frames; /* total bit stream frames from Xing header data */
+ int bytes; /* total bit stream bytes from Xing header data */
+ unsigned char toc[100]; /* "table of contents" */
+} xing_header_t;
+
+struct al_table
+{
+ short bits;
+ short d;
+};
+
+struct frame
+{
+ struct al_table *alloc;
+ int (*synth) (real *, int, unsigned char *, int *);
+ int (*synth_mono) (real *, unsigned char *, int *);
+#ifdef USE_3DNOW
+ void (*dct36)(real *,real *,real *,real *,real *);
+#endif
+ int stereo;
+ int jsbound;
+ int single;
+ int II_sblimit;
+ int down_sample_sblimit;
+ int lsf;
+ int mpeg25;
+ int down_sample;
+ int header_change;
+ int lay;
+ int (*do_layer) (struct frame * fr);
+ int error_protection;
+ int bitrate_index;
+ int sampling_frequency;
+ int padding;
+ int extension;
+ int mode;
+ int mode_ext;
+ int copyright;
+ int original;
+ int emphasis;
+ int framesize; /* computed framesize */
+};
+
+static guint32 convert_to_header(guint8 * buf)
+{
+
+ return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+}
+
+static int mpg123_head_check(unsigned long head)
+{
+ if ((head & 0xffe00000) != 0xffe00000)
+ return FALSE;
+ if (!((head >> 17) & 3))
+ return FALSE;
+ if (((head >> 12) & 0xf) == 0xf)
+ return FALSE;
+ if (!((head >> 12) & 0xf))
+ return FALSE;
+ if (((head >> 10) & 0x3) == 0x3)
+ return FALSE;
+ if (((head >> 19) & 1) == 1 && ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1)
+ return FALSE;
+ if ((head & 0xffff0000) == 0xfffe0000)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/*
+ * the code a header and write the information
+ * into the frame structure
+ */
+static int mpg123_decode_header(struct frame *fr, unsigned long newhead)
+{
+ int ssize;
+
+ if (newhead & (1 << 20))
+ {
+ fr->lsf = (newhead & (1 << 19)) ? 0x0 : 0x1;
+ fr->mpeg25 = 0;
+ }
+ else
+ {
+ fr->lsf = 1;
+ fr->mpeg25 = 1;
+ }
+ fr->lay = 4 - ((newhead >> 17) & 3);
+ if (fr->mpeg25)
+ {
+ fr->sampling_frequency = 6 + ((newhead >> 10) & 0x3);
+ }
+ else
+ fr->sampling_frequency = ((newhead >> 10) & 0x3) + (fr->lsf * 3);
+ fr->error_protection = ((newhead >> 16) & 0x1) ^ 0x1;
+
+ fr->bitrate_index = ((newhead >> 12) & 0xf);
+ fr->padding = ((newhead >> 9) & 0x1);
+ fr->extension = ((newhead >> 8) & 0x1);
+ fr->mode = ((newhead >> 6) & 0x3);
+ fr->mode_ext = ((newhead >> 4) & 0x3);
+ fr->copyright = ((newhead >> 3) & 0x1);
+ fr->original = ((newhead >> 2) & 0x1);
+ fr->emphasis = newhead & 0x3;
+
+ fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
+
+ ssize = 0;
+
+ if (!fr->bitrate_index)
+ return (0);
+
+ switch (fr->lay)
+ {
+ case 1:
+/* fr->do_layer = mpg123_do_layer1; */
+/* mpg123_init_layer2(); /\* inits also shared tables with layer1 *\/ */
+ fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
+ fr->framesize /= mpg123_freqs[fr->sampling_frequency];
+ fr->framesize = ((fr->framesize + fr->padding) << 2) - 4;
+ break;
+ case 2:
+/* fr->do_layer = mpg123_do_layer2; */
+/* mpg123_init_layer2(); /\* inits also shared tables with layer1 *\/ */
+ fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
+ fr->framesize /= mpg123_freqs[fr->sampling_frequency];
+ fr->framesize += fr->padding - 4;
+ break;
+ case 3:
+/* fr->do_layer = mpg123_do_layer3; */
+ if (fr->lsf)
+ ssize = (fr->stereo == 1) ? 9 : 17;
+ else
+ ssize = (fr->stereo == 1) ? 17 : 32;
+ if (fr->error_protection)
+ ssize += 2;
+ fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
+ fr->framesize /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ fr->framesize = fr->framesize + fr->padding - 4;
+ break;
+ default:
+ return (0);
+ }
+ if(fr->framesize > MAXFRAMESIZE)
+ return 0;
+ return 1;
+}
+
+#define GET_INT32BE(b) \
+(i = (b[0] << 24) | (b[1] << 16) | b[2] << 8 | b[3], b += 4, i)
+
+static int mpg123_get_xing_header(xing_header_t * xing, unsigned char *buf)
+{
+ int i, head_flags;
+ int id, mode;
+
+ memset(xing, 0, sizeof(xing_header_t));
+
+ /* get selected MPEG header data */
+ id = (buf[1] >> 3) & 1;
+ mode = (buf[3] >> 6) & 3;
+ buf += 4;
+
+ /* Skip the sub band data */
+ if (id)
+ {
+ /* mpeg1 */
+ if (mode != 3)
+ buf += 32;
+ else
+ buf += 17;
+ }
+ else
+ {
+ /* mpeg2 */
+ if (mode != 3)
+ buf += 17;
+ else
+ buf += 9;
+ }
+
+ if (strncmp(buf, "Xing", 4))
+ return 0;
+ buf += 4;
+
+ head_flags = GET_INT32BE(buf);
+
+ if (head_flags & FRAMES_FLAG)
+ xing->frames = GET_INT32BE(buf);
+ if (xing->frames < 1)
+ xing->frames = 1;
+ if (head_flags & BYTES_FLAG)
+ xing->bytes = GET_INT32BE(buf);
+
+ if (head_flags & TOC_FLAG)
+ {
+ for (i = 0; i < 100; i++)
+ xing->toc[i] = buf[i];
+ buf += 100;
+ }
+
+#ifdef XING_DEBUG
+ for (i = 0; i < 100; i++)
+ {
+ if ((i % 10) == 0)
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %3d", xing->toc[i]);
+ }
+#endif
+
+ return 1;
+}
+
+static double mpg123_compute_tpf(struct frame *fr)
+{
+ const int bs[4] = {0, 384, 1152, 1152};
+ double tpf;
+
+ tpf = bs[fr->lay];
+ tpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ return tpf;
+}
+
+static double mpg123_compute_bpf(struct frame *fr)
+{
+ double bpf;
+
+ switch (fr->lay)
+ {
+ case 1:
+ bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
+ bpf *= 12000.0 * 4.0;
+ bpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ break;
+ case 2:
+ case 3:
+ bpf = tabsel_123[fr->lsf][fr->lay - 1][fr->bitrate_index];
+ bpf *= 144000;
+ bpf /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf);
+ break;
+ default:
+ bpf = 1.0;
+ }
+
+ return bpf;
+}
+
+
+unsigned int mpg123_getbits(int number_of_bits)
+{
+ unsigned long rval;
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr, "g%d", number_of_bits);
+#endif
+
+ if(!number_of_bits)
+ return 0;
+
+#if 0
+ check_buffer_range(number_of_bits + bsi.bitindex);
+#endif
+
+ {
+ rval = bsi.wordpointer[0];
+ rval <<= 8;
+ rval |= bsi.wordpointer[1];
+ rval <<= 8;
+ rval |= bsi.wordpointer[2];
+
+ rval <<= bsi.bitindex;
+ rval &= 0xffffff;
+
+ bsi.bitindex += number_of_bits;
+
+ rval >>= (24-number_of_bits);
+
+ bsi.wordpointer += (bsi.bitindex >> 3);
+ bsi.bitindex &= 7;
+ }
+
+#ifdef DEBUG_GETBITS
+ fprintf(stderr,":%x ",rval);
+#endif
+
+ return rval;
+}
+
+
+void I_step_one(unsigned int balloc[], unsigned int scale_index[2][SBLIMIT], struct frame *fr)
+{
+ unsigned int *ba = balloc;
+ unsigned int *sca = (unsigned int *) scale_index;
+
+ if (fr->stereo)
+ {
+ int i;
+ int jsbound = fr->jsbound;
+
+ for (i = 0; i < jsbound; i++)
+ {
+ *ba++ = mpg123_getbits(4);
+ *ba++ = mpg123_getbits(4);
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ *ba++ = mpg123_getbits(4);
+
+ ba = balloc;
+
+ for (i = 0; i < jsbound; i++)
+ {
+ if ((*ba++))
+ *sca++ = mpg123_getbits(6);
+ if ((*ba++))
+ *sca++ = mpg123_getbits(6);
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ if ((*ba++))
+ {
+ *sca++ = mpg123_getbits(6);
+ *sca++ = mpg123_getbits(6);
+ }
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < SBLIMIT; i++)
+ *ba++ = mpg123_getbits(4);
+ ba = balloc;
+ for (i = 0; i < SBLIMIT; i++)
+ if ((*ba++))
+ *sca++ = mpg123_getbits(6);
+ }
+}
+
+void I_step_two(real fraction[2][SBLIMIT], unsigned int balloc[2 * SBLIMIT],
+ unsigned int scale_index[2][SBLIMIT], struct frame *fr)
+{
+ int i, n;
+ int smpb[2 * SBLIMIT]; /* values: 0-65535 */
+ int *sample;
+ register unsigned int *ba;
+ register unsigned int *sca = (unsigned int *) scale_index;
+
+ if (fr->stereo)
+ {
+ int jsbound = fr->jsbound;
+ register real *f0 = fraction[0];
+ register real *f1 = fraction[1];
+
+ ba = balloc;
+ for (sample = smpb, i = 0; i < jsbound; i++)
+ {
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+
+ ba = balloc;
+ for (sample = smpb, i = 0; i < jsbound; i++)
+ {
+ if ((n = *ba++))
+ *f0++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
+ else
+ *f0++ = 0.0;
+ if ((n = *ba++))
+ *f1++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
+ else
+ *f1++ = 0.0;
+ }
+ for (i = jsbound; i < SBLIMIT; i++)
+ {
+ if ((n = *ba++))
+ {
+ real samp = (((-1) << n) + (*sample++) + 1);
+
+ *f0++ = samp * mpg123_muls[n + 1][*sca++];
+ *f1++ = samp * mpg123_muls[n + 1][*sca++];
+ }
+ else
+ *f0++ = *f1++ = 0.0;
+ }
+ for (i = fr->down_sample_sblimit; i < 32; i++)
+ fraction[0][i] = fraction[1][i] = 0.0;
+ }
+ else
+ {
+ register real *f0 = fraction[0];
+
+ ba = balloc;
+ for (sample = smpb, i = 0; i < SBLIMIT; i++)
+ if ((n = *ba++))
+ *sample++ = mpg123_getbits(n + 1);
+ ba = balloc;
+ for (sample = smpb, i = 0; i < SBLIMIT; i++)
+ {
+ if ((n = *ba++))
+ *f0++ = (real) (((-1) << n) + (*sample++) + 1) * mpg123_muls[n + 1][*sca++];
+ else
+ *f0++ = 0.0;
+ }
+ for (i = fr->down_sample_sblimit; i < 32; i++)
+ fraction[0][i] = 0.0;
+ }
+}
+
+static guint get_track_time_file(FILE * file)
+{
+ guint32 head;
+ guchar tmp[4], *buf;
+ struct frame frm;
+ xing_header_t xing_header;
+ double tpf, bpf;
+ guint32 len;
+
+ if (!file)
+ return -1;
+
+ fseek(file, 0, SEEK_SET);
+ if (fread(tmp, 1, 4, file) != 4)
+ return 0;
+ head = convert_to_header(tmp);
+ while (!mpg123_head_check(head))
+ {
+ head <<= 8;
+ if (fread(tmp, 1, 1, file) != 1)
+ return 0;
+ head |= tmp[0];
+ }
+ if (mpg123_decode_header(&frm, head))
+ {
+ buf = g_malloc(frm.framesize + 4);
+ fseek(file, -4, SEEK_CUR);
+ fread(buf, 1, frm.framesize + 4, file);
+ tpf = mpg123_compute_tpf(&frm);
+ if (mpg123_get_xing_header(&xing_header, buf))
+ {
+ g_free(buf);
+ return ((guint) (tpf * xing_header.frames * 1000));
+ }
+ g_free(buf);
+ bpf = mpg123_compute_bpf(&frm);
+ fseek(file, 0, SEEK_END);
+ len = ftell(file);
+ fseek(file, -128, SEEK_END);
+ fread(tmp, 1, 3, file);
+ if (!strncmp(tmp, "TAG", 3))
+ len -= 128;
+ return ((guint) ((guint)(len / bpf) * tpf * 1000));
+ }
+ return 0;
+}
+
+static guint get_track_time (gchar *path)
+{
+ guint result = 0;
+
+ if (path)
+ {
+ FILE *file = fopen (path, "r");
+ result = get_track_time_file (file);
+ if (file) fclose (file);
+ }
+ return result;
+}
+
+
+/* libid3tag stuff */
+
+#include <id3tag.h>
+#include "prefs.h"
+
+#ifndef ID3_FRAME_GROUP
+#define ID3_FRAME_GROUP "TPE2"
+#endif
+
+
+
+static const gchar* id3_get_binary (struct id3_tag *tag,
+ char *frame_name,
+ id3_length_t *len,
+ int index)
+{
+ const id3_byte_t *binary = NULL;
+ struct id3_frame *frame;
+ union id3_field *field;
+
+ g_return_val_if_fail (len, NULL);
+
+ *len = 0;
+
+ frame = id3_tag_findframe (tag, frame_name, index);
+#if LOCALDEBUG
+ printf ("frame: %p\n", frame);
+#endif
+
+ if (!frame) return NULL;
+
+#if LOCALDEBUG
+ printf (" nfields: %d\n", frame->nfields);
+ if (strncmp (frame_name, "APIC", 4) == 0)
+ {
+ field = id3_frame_field (frame, 2);
+ printf (" picture type: %ld\n", field->number.value);
+ }
+#endif
+
+
+#if 0
+/*-----------------*/
+/* just to show that this field (before last) contains the d8 ff e0 ff
+ part of the start of a jpeg file when the coverart war embedded by iTunes */
+
+ const id3_ucs4_t *string = NULL;
+ gchar *raw = NULL;
+
+ /* The last field contains the data */
+ field = id3_frame_field (frame, frame->nfields-2);
+
+#if LOCALDEBUG
+ printf (" field: %p\n", field);
+#endif
+
+ if (!field) return NULL;
+
+#if LOCALDEBUG
+ printf (" type: %d\n", field->type);
+#endif
+
+ switch (field->type)
+ {
+ case ID3_FIELD_TYPE_STRING:
+ string = id3_field_getstring (field);
+ break;
+ default:
+ break;
+ }
+
+ /* ISO_8859_1 is just a "marker" -- most people just drop
+ whatever coding system they are using into it, so we use
+ charset_to_utf8() to convert to utf8 */
+
+ if (string)
+ {
+ raw = id3_ucs4_latin1duplicate (string);
+ }
+
+
+#if LOCALDEBUG
+ {
+ FILE *file;
+ printf (" string len: %d\n", raw?strlen(raw):0);
+ file = fopen ("/tmp/folder1.jpg", "w");
+ fwrite (raw, 1, raw?strlen(raw):0, file);
+ fclose (file);
+ }
+#endif
+ g_free (raw);
+
+/*-----------------*/
+#endif
+
+ /* The last field contains the data */
+ field = id3_frame_field (frame, frame->nfields-1);
+
+#if LOCALDEBUG
+ printf (" field: %p\n", field);
+#endif
+
+ if (!field) return NULL;
+
+#if LOCALDEBUG
+ printf (" type: %d\n", field->type);
+#endif
+
+ switch (field->type)
+ {
+ case ID3_FIELD_TYPE_BINARYDATA:
+ binary = id3_field_getbinarydata(field, len);
+ break;
+ default:
+ break;
+ }
+
+#if LOCALDEBUG
+ {
+ FILE *file;
+ printf (" binary len: %ld\n", *len);
+ file = fopen ("/tmp/folder2.jpg", "w");
+ fwrite (binary, 1, *len, file);
+ fclose (file);
+ }
+#endif
+
+
+
+ return binary;
+}
+
+
+
+static gchar* id3_get_string (struct id3_tag *tag, char *frame_name)
+{
+ const id3_ucs4_t *string = NULL;
+ const id3_byte_t *binary = NULL;
+ id3_length_t len = 0;
+ struct id3_frame *frame;
+ union id3_field *field;
+ gchar *utf8 = NULL;
+ enum id3_field_textencoding encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1;
+
+ frame = id3_tag_findframe (tag, frame_name, 0);
+#if LOCALDEGUB
+ printf ("frame: %p\n", frame);
+#endif
+
+ if (!frame) return NULL;
+
+ /* Find the encoding used for the field */
+ field = id3_frame_field (frame, 0);
+#if LOCALDEBUG
+ printf ("field: %p\n", field);
+ printf ("type: %d\n", id3_field_type (field));
+#endif
+
+ if (field && (id3_field_type (field) == ID3_FIELD_TYPE_TEXTENCODING))
+ {
+ encoding = field->number.value;
+#if LOCALDEBUG
+ printf ("encoding: %d\n", encoding);
+#endif
+ }
+
+ /* The last field contains the data */
+ field = id3_frame_field (frame, frame->nfields-1);
+
+#if LOCALDEBUG
+ printf ("field: %p\n", field);
+#endif
+
+ if (!field) return NULL;
+
+#if LOCALDEBUG
+ printf ("type: %d\n", field->type);
+#endif
+
+
+ switch (field->type)
+ {
+ case ID3_FIELD_TYPE_STRINGLIST:
+ string = id3_field_getstrings (field, 0);
+ break;
+ case ID3_FIELD_TYPE_STRINGFULL:
+ string = id3_field_getfullstring (field);
+ break;
+ case ID3_FIELD_TYPE_BINARYDATA:
+ binary = id3_field_getbinarydata(field, &len);
+#if LOCALDEBUG
+ printf ("len: %ld\nbinary: %s\n", len, binary+1);
+#endif
+ if (len > 0)
+ return charset_to_utf8 (binary+1);
+ break;
+ default:
+ break;
+ }
+
+/* printf ("string: %p\n", string); */
+
+ if (!string) return NULL;
+
+ if (strcmp (frame_name, ID3_FRAME_GENRE) == 0)
+ string = id3_genre_name (string);
+
+ if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1)
+ {
+ /* ISO_8859_1 is just a "marker" -- most people just drop
+ whatever coding system they are using into it, so we use
+ charset_to_utf8() to convert to utf8 */
+ id3_latin1_t *raw = id3_ucs4_latin1duplicate (string);
+ utf8 = charset_to_utf8 (raw);
+ g_free (raw);
+ }
+ else
+ {
+ /* Standard unicode is being used -- we won't have to worry
+ about charsets then. */
+ utf8 = id3_ucs4_utf8duplicate (string);
+ }
+ return utf8;
+}
+
+static void id3_set_string (struct id3_tag *tag,
+ const char *frame_name,
+ const char *data,
+ enum id3_field_textencoding encoding)
+{
+ int res;
+ struct id3_frame *frame;
+ union id3_field *field;
+ id3_ucs4_t *ucs4;
+
+ /* clear the frame, because of bug in libid3tag see
+ http://www.mars.org/mailman/public/mad-dev/2004-October/001113.html
+ */
+ while ((frame = id3_tag_findframe (tag, frame_name, 0)))
+ {
+ id3_tag_detachframe (tag, frame);
+ id3_frame_delete (frame);
+ }
+
+ if ((data == NULL) || (strlen(data) == 0))
+ return;
+
+ frame = id3_frame_new (frame_name);
+ id3_tag_attachframe (tag, frame);
+
+ /* Use the specified text encoding */
+ field = id3_frame_field (frame, 0);
+ id3_field_settextencoding(field, encoding);
+
+ if (strcmp (frame_name, ID3_FRAME_COMMENT) == 0)
+ {
+ field = id3_frame_field (frame, 3);
+ field->type = ID3_FIELD_TYPE_STRINGFULL;
+ }
+ else
+ {
+ field = id3_frame_field (frame, 1);
+ field->type = ID3_FIELD_TYPE_STRINGLIST;
+ }
+
+
+ /* maybe could be optimized see
+ http://www.mars.org/mailman/public/mad-dev/2002-October/000739.html
+ */
+ if (strcmp (frame_name, ID3_FRAME_GENRE) == 0)
+ {
+ id3_ucs4_t *tmp_ucs4 = id3_utf8_ucs4duplicate ((id3_utf8_t *)data);
+ int index = id3_genre_number (tmp_ucs4);
+ if (index != -1)
+ {
+ /* valid genre -- simply store the genre number */
+ gchar *tmp = g_strdup_printf("%d", index);
+ ucs4 = id3_latin1_ucs4duplicate (tmp);
+ g_free (tmp);
+ }
+ else
+ {
+ /* oups -- not a valid genre -- save the entire genre string */
+ if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1)
+ {
+ /* we read 'ISO_8859_1' to stand for 'any locale
+ charset' -- most programs seem to work that way */
+ id3_latin1_t *raw = charset_from_utf8 (data);
+ ucs4 = id3_latin1_ucs4duplicate (raw);
+ g_free (raw);
+ }
+ else
+ {
+ /* Yeah! We use unicode encoding and won't have to
+ worry about charsets */
+ ucs4 = tmp_ucs4;
+ tmp_ucs4 = NULL;
+ }
+ }
+ g_free (tmp_ucs4);
+ }
+ else
+ {
+ if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1)
+ {
+ /* we read 'ISO_8859_1' to stand for 'any locale charset'
+ -- most programs seem to work that way */
+ id3_latin1_t *raw = charset_from_utf8 (data);
+ ucs4 = id3_latin1_ucs4duplicate (raw);
+ g_free (raw);
+ }
+ else
+ {
+ /* Yeah! We use unicode encoding and won't have to
+ worry about charsets */
+ ucs4 = id3_utf8_ucs4duplicate ((id3_utf8_t *)data);
+ }
+ }
+
+ if (strcmp (frame_name, ID3_FRAME_COMMENT) == 0)
+ res = id3_field_setfullstring (field, ucs4);
+ else
+ res = id3_field_setstrings (field, 1, &ucs4);
+
+ g_free (ucs4);
+
+ if (res != 0)
+ g_print(_("Error setting ID3 field: %s\n"), frame_name);
+}
+
+
+/***
+ * Reads id3v1.x / id3v2 apic data
+ * @returns: TRUE on success, else FALSE.
+ */
+static gboolean id3_apic_read (gchar *filename,
+ guchar **image_data, gsize *image_data_len)
+{
+ struct id3_file *id3file;
+ struct id3_tag *id3tag;
+
+ g_return_val_if_fail (filename, FALSE);
+ g_return_val_if_fail (image_data, FALSE);
+ g_return_val_if_fail (image_data_len, FALSE);
+
+ *image_data = NULL;
+ *image_data_len = 0;
+
+ if (!(id3file = id3_file_open (filename, ID3_FILE_MODE_READONLY)))
+ {
+ gchar *fbuf = charset_to_utf8 (filename);
+ g_print(_("ERROR while opening file: '%s' (%s).\n"),
+ fbuf, g_strerror(errno));
+ g_free (fbuf);
+ return FALSE;
+ }
+
+ if ((id3tag = id3_file_tag(id3file)))
+ {
+ id3_length_t len;
+ const guchar *coverart = NULL;
+ int i;
+ struct id3_frame *frame;
+
+ /* Loop through APIC tags and set coverart. The picture type should be
+ * 3 -- Cover (front), but iTunes has been known to use 0 -- Other. */
+ for (i = 0; (frame = id3_tag_findframe(id3tag, "APIC", i)) != NULL; i++)
+ {
+ union id3_field *field = id3_frame_field (frame, 2);
+ int pictype = field->number.value;
+/* printf ("%s: found apic type %d\n", filename, pictype);*/
+
+ /* We'll prefer type 3 (cover) over type 0 (other) */
+ if (pictype == 3)
+ {
+ coverart = id3_get_binary (id3tag, "APIC", &len, i);
+ break;
+ }
+ if ((pictype == 0) && !coverart)
+ {
+ coverart = id3_get_binary (id3tag, "APIC", &len, i);
+ }
+ }
+
+ if (coverart)
+ { /* I guess iTunes is doing something wrong -- the
+ * beginning of the coverart data ends up in a different
+ field... We'll just add the missing data manually. */
+ const guchar itunes_broken_jfif_marker[] =
+ { 0x10, 'J', 'F', 'I', 'F'};
+ if (len >= 5)
+ {
+ if (strncmp (itunes_broken_jfif_marker, coverart, 5) == 0)
+ {
+ const guchar itunes_missing_header[] =
+ { 0xff, 0xd8, 0xff, 0xe0, 0x00 };
+ *image_data = g_malloc (len+5);
+ memcpy (*image_data, itunes_missing_header, 5);
+ memcpy ((*image_data)+5, coverart, len);
+ *image_data_len = len+5;
+ }
+ }
+ if (!*image_data)
+ {
+ *image_data = g_malloc (len);
+ memcpy (*image_data, coverart, len);
+ *image_data_len = len;
+ }
+#if LOCALDEBUG
+ if (*image_data)
+ {
+ FILE *file;
+ file = fopen ("/tmp/folder.jpg", "w");
+ fwrite (*image_data, 1, *image_data_len, file);
+ fclose (file);
+ }
+#endif
+ }
+ }
+ id3_file_close (id3file);
+ return TRUE;
+}
+
+/***
+ * Reads id3v1.x / id3v2 tag and load data into the Id3tag structure.
+ * If a tag entry exists (ex: title), we allocate memory, else value
+ * stays to NULL
+ * @returns: TRUE on success, else FALSE.
+ */
+gboolean id3_tag_read (gchar *filename, File_Tag *tag)
+{
+ struct id3_file *id3file;
+ struct id3_tag *id3tag;
+ gchar* string;
+ gchar* string2;
+
+ g_return_val_if_fail (filename, FALSE);
+ g_return_val_if_fail (tag, FALSE);
+
+ memset (tag, 0, sizeof (File_Tag));
+
+ if (!(id3file = id3_file_open (filename, ID3_FILE_MODE_READONLY)))
+ {
+ gchar *fbuf = charset_to_utf8 (filename);
+ g_print(_("ERROR while opening file: '%s' (%s).\n"),
+ fbuf, g_strerror(errno));
+ g_free (fbuf);
+ return FALSE;
+ }
+
+ if ((id3tag = id3_file_tag(id3file)))
+ {
+ tag->title = id3_get_string (id3tag, ID3_FRAME_TITLE);
+ tag->artist = id3_get_string (id3tag, ID3_FRAME_GROUP);
+ if (!tag->artist || !*tag->artist)
+ {
+ g_free (tag->artist);
+ tag->artist = id3_get_string (id3tag, ID3_FRAME_ARTIST);
+ }
+ tag->album = id3_get_string (id3tag, ID3_FRAME_ALBUM);
+ tag->year = id3_get_string (id3tag, ID3_FRAME_YEAR);
+ tag->composer = id3_get_string (id3tag, "TCOM");
+ tag->comment = id3_get_string (id3tag, ID3_FRAME_COMMENT);
+ tag->genre = id3_get_string (id3tag, ID3_FRAME_GENRE);
+ tag->compilation = id3_get_string (id3tag, "TCMP");
+ tag->subtitle = id3_get_string (id3tag, "TIT3");
+ tag->lyrics = id3_get_string (id3tag, "USLT");
+ tag->podcasturl = id3_get_string (id3tag, "YTID");
+ tag->podcastrss = id3_get_string (id3tag, "YWFD");
+ tag->description = id3_get_string (id3tag, "YTDS");
+ tag->time_released = id3_get_string (id3tag, "YTDR");
+ tag->BPM = id3_get_string (id3tag, "TBPM");
+ tag->sort_artist = id3_get_string (id3tag, "TSOP");
+ tag->sort_album = id3_get_string (id3tag, "TSOA");
+ tag->sort_title = id3_get_string (id3tag, "TSOT");
+ tag->sort_albumartist = id3_get_string (id3tag, "TSO2");
+ tag->sort_composer = id3_get_string (id3tag, "TSOC");
+
+ string = id3_get_string (id3tag, "TLEN");
+ if (string)
+ {
+ tag->songlen = (guint32) strtoul (string, 0, 10);
+ g_free (string);
+ }
+
+ string = id3_get_string (id3tag, ID3_FRAME_TRACK);
+ if (string)
+ {
+ string2 = strchr(string,'/');
+ if (string2)
+ {
+ tag->track_total = g_strdup_printf ("%.2d", atoi (string2+1));
+ *string2 = '\0';
+ }
+ tag->trackstring = g_strdup_printf ("%.2d", atoi (string));
+ g_free(string);
+ }
+
+ /* CD/disc number tag handling */
+ string = id3_get_string (id3tag, "TPOS");
+ if (string)
+ {
+ string2 = strchr(string,'/');
+ if (string2)
+ {
+ tag->cdno_total = g_strdup_printf ("%.2d", atoi (string2+1));
+ *string2 = '\0';
+ }
+ tag->cdnostring = g_strdup_printf ("%.2d", atoi (string));
+ g_free(string);
+ }
+ }
+
+ id3_file_close (id3file);
+ return TRUE;
+}
+
+
+
+static enum id3_field_textencoding get_encoding_of (struct id3_tag *tag, const char *frame_name)
+{
+ struct id3_frame *frame;
+ enum id3_field_textencoding encoding = -1;
+
+ frame = id3_tag_findframe (tag, frame_name, 0);
+ if (frame)
+ {
+ union id3_field *field = id3_frame_field (frame, 0);
+ if (field && (id3_field_type (field) == ID3_FIELD_TYPE_TEXTENCODING))
+ encoding = field->number.value;
+ }
+ return encoding;
+}
+
+/* Find out which encoding is being used. If in doubt, return
+ * latin1. This code assumes that the same encoding is used in all
+ * fields. */
+static enum id3_field_textencoding get_encoding (struct id3_tag *tag)
+{
+ enum id3_field_textencoding enc;
+
+ enc = get_encoding_of (tag, ID3_FRAME_TITLE);
+ if (enc != -1) return enc;
+ enc = get_encoding_of (tag, ID3_FRAME_ARTIST);
+ if (enc != -1) return enc;
+ enc = get_encoding_of (tag, ID3_FRAME_ALBUM);
+ if (enc != -1) return enc;
+ enc = get_encoding_of (tag, "TCOM");
+ if (enc != -1) return enc;
+ enc = get_encoding_of (tag, ID3_FRAME_COMMENT);
+ if (enc != -1) return enc;
+ enc = get_encoding_of (tag, ID3_FRAME_YEAR);
+ if (enc != -1) return enc;
+ return ID3_FIELD_TEXTENCODING_ISO_8859_1;
+}
+
+
+/* I'm not really sure about this: The original TAG identifier was
+ "TID", but no matter what I do I end up writing "YTID" */
+void set_uncommon_tag (struct id3_tag *id3tag,
+ const gchar *id,
+ const gchar *text,
+ enum id3_field_textencoding encoding)
+{
+#if 0
+ struct id3_frame *frame;
+
+ frame = id3_tag_findframe (id3tag, id, 0);
+ union id3_field *field;
+ frame->flags = 0;
+ field = id3_frame_field (frame, 0);
+ if (field)
+ {
+ string1 = g_strdup_printf ("%c%s", '\0',
+ track->podcasturl);
+ id3_field_setbinarydata (field, string1,
+ strlen(track->podcasturl)+1);
+ g_free (string1);
+ }
+
+#endif
+}
+
+
+
+/**
+ * Write the ID3 tags to the file.
+ * @returns: TRUE on success, else FALSE.
+ */
+gboolean mp3_write_file_info (gchar *filename, Track *track)
+{
+ struct id3_tag* id3tag;
+ struct id3_file* id3file;
+ gint error = 0;
+
+ id3file = id3_file_open (filename, ID3_FILE_MODE_READWRITE);
+ if (!id3file)
+ {
+ gchar *fbuf = charset_to_utf8 (filename);
+ g_print(_("ERROR while opening file: '%s' (%s).\n"),
+ fbuf, g_strerror(errno));
+ g_free (fbuf);
+ return FALSE;
+ }
+
+ if ((id3tag = id3_file_tag(id3file)))
+ {
+ char *string1;
+
+ enum id3_field_textencoding encoding;
+
+ /* use the same coding as before... */
+ encoding = get_encoding (id3tag);
+ /* ...unless it's ISO_8859_1 and prefs say we should use
+ unicode (i.e. ID3v2.4) */
+ if (prefs_get_int("id3_write_id3v24") &&
+ (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1))
+ encoding = ID3_FIELD_TEXTENCODING_UTF_8;
+
+ /* always render id3v1 to prevent dj studio from crashing */
+ id3_tag_options(id3tag, ID3_TAG_OPTION_ID3V1, ~0);
+
+ /* turn off frame compression and crc information to let
+ itunes read tags see
+ http://www.mars.org/mailman/public/mad-dev/2002-October/000742.html
+ */
+ id3_tag_options(id3tag, ID3_TAG_OPTION_COMPRESSION, 0);
+ id3_tag_options(id3tag, ID3_TAG_OPTION_CRC, 0);
+
+ id3_set_string (id3tag, ID3_FRAME_TITLE, track->title, encoding);
+ id3_set_string (id3tag, ID3_FRAME_ARTIST, track->artist, encoding);
+ id3_set_string (id3tag, ID3_FRAME_ALBUM, track->album, encoding);
+ id3_set_string (id3tag, ID3_FRAME_GENRE, track->genre, encoding);
+ id3_set_string (id3tag, ID3_FRAME_COMMENT, track->comment, encoding);
+ id3_set_string (id3tag, "TIT3", track->subtitle, encoding);
+ id3_set_string (id3tag, "TSOP", track->sort_artist, encoding);
+ id3_set_string (id3tag, "TSOA", track->sort_album, encoding);
+ id3_set_string (id3tag, "TSOT", track->sort_title, encoding);
+ id3_set_string (id3tag, "TSO2", track->sort_albumartist, encoding);
+ id3_set_string (id3tag, "TSOC", track->sort_composer, encoding);
+
+ set_uncommon_tag (id3tag, "YTID", track->podcasturl, encoding);
+ set_uncommon_tag (id3tag, "YTDS", track->description, encoding);
+ set_uncommon_tag (id3tag, "YWFD", track->podcastrss, encoding);
+
+ id3_set_string (id3tag, "TCOM", track->composer, encoding);
+
+ string1 = g_strdup_printf("%d", track->year);
+ id3_set_string(id3tag, ID3_FRAME_YEAR, string1, encoding);
+ g_free(string1);
+
+ string1 = g_strdup_printf("%d", track->BPM);
+ id3_set_string(id3tag, "TBPM", string1, encoding);
+ g_free(string1);
+
+ if (track->tracks)
+ string1 = g_strdup_printf ("%d/%d",
+ track->track_nr, track->tracks);
+ else
+ string1 = g_strdup_printf ("%d", track->track_nr);
+ id3_set_string (id3tag, ID3_FRAME_TRACK, string1, encoding);
+ g_free(string1);
+
+ if (track->cds)
+ string1 = g_strdup_printf ("%d/%d",
+ track->cd_nr, track->cds);
+ else
+ string1 = g_strdup_printf ("%d", track->cd_nr);
+ id3_set_string (id3tag, "TPOS", string1, encoding);
+ g_free(string1);
+
+ string1 = g_strdup_printf ("%d", track->compilation);
+ id3_set_string (id3tag, "TCMP", string1, encoding);
+ g_free(string1);
+ }
+
+ if (id3_file_update(id3file) != 0)
+ {
+ gchar *fbuf = charset_to_utf8 (filename);
+ g_print(_("ERROR while writing tag to file: '%s' (%s).\n"),
+ fbuf, g_strerror(errno));
+ g_free (fbuf);
+ return FALSE;
+ }
+
+ id3_file_close (id3file);
+
+ if (error) return FALSE;
+ else return TRUE;
+}
+
+
+/*
+ * Code to read the ReplayGain Values stored by LAME in its own tag.
+ *
+ * Most of the relevant information has been extracted from them LAME sources
+ * (http://lame.sourceforge.net/).
+ * The "Mp3 info Tag rev 1 specifications - draft 0"
+ * (http://gabriel.mp3-tech.org/mp3infotag.html) by Gabriel Bouvigne describes
+ * the actual Tag (except for small changes).
+ * Details on the actual ReplayGain fields have been obtained from
+ * http://www.replaygain.org .
+ *
+ * Apart from that, some information has been derived from phwip's LameTag
+ * (http://www.silisoftware.com/applets/?scriptname=LameTag)
+ *
+ *
+ * Code to read the ReplayGain Values stored in an Ape tag.
+ *
+ * Info on Lyrics3 V2.00 can be found at:
+ * http://www.id3.org/lyrics3200.html
+ * On the actual Ape Tag V2.0:
+ * http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
+ *
+ * Copyright (C) 2004 Jens Taprogge <jens.taprogge at post.rwth-aachen.de>
+ *
+ * Provided under GPL according to Jens Taprogge. (JCS -- 12 March 2004)
+ */
+
+#define TAG_FOOTER 0x10
+#define LAME_OFFSET 0x74
+#define SIDEINFO_MPEG1_MONO 17
+#define SIDEINFO_MPEG1_MULTI 32
+#define SIDEINFO_MPEG2_MONO 9
+#define SIDEINFO_MPEG2_MULTI 17
+#define ID3V1_SIZE 0x80
+#define APE_FOOTER_SIZE 0x20
+#define LYRICS_FOOTER_SIZE 0x0f
+
+
+static gint lame_vcmp(gchar a[5], gchar b[5]) {
+ int r;
+
+ r = strncmp(a, b, 4);
+ if (r) return r;
+
+ if (a[4] == b[4]) return 0;
+
+ /* check for '.': indicates subminor version */
+ if (a[4] == '.') return 1;
+ if (b[4] == '.') return -1;
+
+ /* check for alpha or beta versions */
+ if (a[4] == ' ') return 1;
+ if (b[4] == ' ') return -1;
+
+ /* check for alpha, beta etc. indicated by a, b... */
+ return strncmp(&a[4], &b[4], 1);
+}
+
+
+/* buf[] must be declared unsigned -- otherwise the casts, shifts and
+ additions below produce funny results */
+static void read_lame_replaygain(unsigned char buf[],
+ GainData *gd, int gain_adjust) {
+ char oc, nc;
+ gint gain;
+
+ g_return_if_fail (gd);
+
+ /* buf[0] and buf[1] are a bit field:
+ 3 bits: name (mask: 0xe0 = 11100000)
+ 3 bits: originator (mask: 0x1c = 00011100)
+ 1 bit: negative if set (mask: 0x02 = 00000010)
+ 9 bits: value
+ */
+
+ /* check originator */
+ oc = (buf[0] & 0x1c) >> 2;
+ if ((oc <= 0) || (oc > 3)) return;
+
+ /* check name code */
+ nc = buf[0] & 0xe0;
+ if (!((nc == 0x20) || (nc == 0x40))) return;
+
+ gain = ((((guint)buf[0]) & 0x1) << 8) + buf[1];
+
+ /* This would be a value of -0.
+ * That value however is illegal by current standards and reserved for
+ * future use. */
+ if ((!gain) && (buf[0] & 0x02)) return;
+
+ if (buf[0] & 2) gain = -gain;
+
+ gain += gain_adjust;
+
+ switch (nc) {
+ case 0x20:
+ if (gd->radio_gain_set) return;
+ gd->radio_gain = (gdouble)gain / 10;
+ gd->radio_gain_set = TRUE;
+/* printf("radio_gain (lame): %i\n", gd->radio_gain); */
+ break;
+ case 0x40:
+ if (gd->audiophile_gain_set) return;
+ gd->audiophile_gain = (gdouble)gain / 10;
+ gd->audiophile_gain_set = TRUE;
+/* printf("audiophile_gain (lame): %i\n",
+ gd->audiophile_gain);*/
+ break;
+ }
+}
+
+
+static inline guint32 parse_ape_uint32(char *buf) {
+ return (buf[0] & 0xff) | (buf[1] & 0xff) << 8
+ | (buf[2] & 0xff) << 16 | (buf[3] & 0xff) << 24;
+}
+
+static inline guint32 parse_lame_uint32(char *buf) {
+ return (buf[0] & 0xff) << 24 | (buf[1] & 0xff) << 16
+ | (buf[2] & 0xff) << 8 | (buf[3] & 0xff);
+}
+
+
+/*
+ * mp3_get_track_lame_replaygain - read the specified file and scan for LAME Tag
+ * ReplayGain information.
+ *
+ * @path: localtion of the file
+ * @track: structure holding track information
+ *
+ * FIXME: Are there other encoders writing a LAME Tag using a different magic
+ * string?
+ * TODO: Check CRC.
+ */
+
+gboolean mp3_get_track_lame_replaygain (gchar *path, GainData *gd)
+{
+ struct {
+ /* All members are defined in terms of chars so padding does not
+ * occur. Is there a cleaner way to keep the compiler from
+ * padding? */
+
+ char id[3];
+ char version[2];
+ char flags;
+ char size[4];
+ } id3head;
+
+ FILE *file = NULL;
+ char buf[4], version[5];
+ int gain_adjust = 0;
+ int sideinfo;
+ guint32 ps;
+
+ g_return_val_if_fail (gd, FALSE);
+
+ gd->radio_gain = 0;
+ gd->audiophile_gain = 0;
+ gd->peak_signal = 0;
+ gd->radio_gain_set = FALSE;
+ gd->audiophile_gain_set = FALSE;
+ gd->peak_signal_set = FALSE;
+
+ if (!path)
+ goto rg_fail;
+
+ file = fopen (path, "r");
+
+ if (!file)
+ goto rg_fail;
+
+ /* Skip ID3 header if appropriate */
+ if (fread(&id3head, 1, sizeof(id3head), file) !=
+ sizeof(id3head))
+ goto rg_fail;
+
+ if (!strncmp(id3head.id, "ID3", 3)) {
+ int realsize = 0;
+
+ realsize = (id3head.size[0] & 0x7f) << 21
+ | (id3head.size[1] & 0x7f) << 14
+ | (id3head.size[2] & 0x7f) << 7
+ | (id3head.size[3] & 0x7f);
+
+ if (id3head.flags & TAG_FOOTER) {
+ /* footer is copy of header */
+ realsize += sizeof(id3head);
+ }
+
+ if (fseek(file, realsize-1, SEEK_CUR) ||
+ (!fread(&buf[0], 1, 1, file)))
+ goto rg_fail;
+ } else {
+ /* no ID3 Tag - go back */
+ fseek(file, -sizeof(id3head), SEEK_CUR);
+ }
+
+ /* Search Xing header. The location is dependant on the MPEG Layer and
+ * whether the stream is mono or not. */
+ if (fread(buf, 1, 4, file) != 4) goto rg_fail;
+
+ /* should start with 0xff 0xf? (synch) */
+ if (((buf[0] & 0xff) != 0xff)
+ || ((buf[1] & 0xf0) != 0xf0)) goto rg_fail;
+
+ /* determine the length of the sideinfo */
+ if (buf[1] & 0x08) {
+ sideinfo = ((buf[3] & 0xc0) == 0xc0) ?
+ SIDEINFO_MPEG1_MONO : SIDEINFO_MPEG1_MULTI;
+ } else {
+ sideinfo = ((buf[3] & 0xc0) == 0xc0) ?
+ SIDEINFO_MPEG2_MONO : SIDEINFO_MPEG2_MULTI;
+ }
+
+ if (fseek(file, sideinfo, SEEK_CUR) ||
+ (fread(&buf[0], 1, 4, file) != 4))
+ goto rg_fail;
+
+ /* Is this really a Xing or Info Header?
+ * FIXME: Apparently (according to madplay sources) there is a different
+ * possible location for the Xing header ("due to an unfortunate
+ * historical event"). I do not thing we need to care though since
+ * RaplayGain information is only conatined in recent files. */
+ if (strncmp(buf, "Xing", 4) && strncmp(buf, "Info", 4))
+ goto rg_fail;
+
+ /* Check for LAME Tag */
+ if (fseek(file, LAME_OFFSET, SEEK_CUR) ||
+ (fread(&buf[0], 1, 4, file) != 4))
+ goto rg_fail;
+ if (strncmp(buf, "LAME", 4))
+ goto rg_fail;
+
+ /* Check LAME Version */
+ if (fread(version, 1, 5, file) != 5)
+ goto rg_fail;
+
+ /* Skip really old versions altogether. I am not sure when radio_gain
+ * information was introduced. 3.89 does not seem to supprt it though.
+ * */
+ if (lame_vcmp(version, "3.90") < 0) {
+/* fprintf(stderr, "Old lame version (%c%c%c%c%c). Not used.\n",
+ version[0], version[1], version[2], version[3], version[4]); */
+ goto rg_fail;
+ }
+
+ if (fseek(file, 0x2, SEEK_CUR) || (fread(buf, 1, 4, file) != 4))
+ goto rg_fail;
+
+ /* get the peak signal. */
+ ps = parse_lame_uint32(buf);
+
+ /* Don't know when fixed-point PeakSingleAmplitude
+ * was introduced exactly. 3.94b will be used for now.) */
+ if ((lame_vcmp(version, "3.94b") >= 0)) {
+ if ((!gd->peak_signal_set) && ps) {
+ gd->peak_signal = ps;
+ gd->peak_signal_set = TRUE;
+/* printf("peak_signal (lame): %f\n", (double)
+ gd->peak_signal / 0x800000);*/
+ }
+ } else {
+ float f = *((float *) (void *) (&ps)) * 0x800000;
+ gd->peak_signal = (guint32) f;
+ /* I would like to see an example of that. */
+/* printf("peak_signal (lame floating point): %f. PLEASE report.\n",
+ (double) gd->peak_signal / 0x800000);*/
+ }
+
+ /*
+ * Versions prior to 3.95.1 used a reference volume of 83dB.
+ * (As compared to the currently used 89dB.)
+ */
+ if ((lame_vcmp(version, "3.95.") < 0)) {
+ gain_adjust = 60;
+/* fprintf(stderr, "Old lame version (%c%c%c%c%c). Adjusting gain.\n",
+ version[0], version[1], version[2], version[3], version[4]); */
+ }
+
+ if (fread(&buf[0], 1, 2, file) != 2)
+ goto rg_fail;
+
+ /* radio gain */
+ read_lame_replaygain (buf, gd, gain_adjust);
+
+ if (fread(&buf[0], 1, 2, file) != 2)
+ goto rg_fail;
+
+ /* audiophile gain */
+ read_lame_replaygain (buf, gd, gain_adjust);
+
+ fclose(file);
+ return TRUE;
+
+rg_fail:
+ if (file)
+ fclose(file);
+ return FALSE;
+}
+
+
+/*
+ * mp3_get_track_ape_replaygain - read the specified file and scan for Ape Tag
+ * ReplayGain information.
+ *
+ * @path: localtion of the file
+ * @track: structure holding track information
+ *
+ * The function only modifies the gains if they have not previously been set.
+ */
+
+gboolean mp3_get_track_ape_replaygain(gchar *path, GainData *gd)
+{
+ /* The Ape Tag is located a t the end of the file. Or at least that
+ * seems where it can most likely be found. Either it is at the very end
+ * or before a trailing ID3v1 Tag. Sometimes a Lyrics3 Tag is placed
+ * between the ID3v1 and the Ape Tag.
+ * If you find files that have the Tags located in different
+ * positions please let me know. */
+
+ FILE *file = NULL;
+ char buf[16];
+ char *dbuf = NULL, *ep;
+
+ int offset = 0;
+ int i;
+ int pos = 0, pos2 = 0;
+ guint32 version;
+ guint32 data_length;
+ guint32 entry_length = 0;
+ guint32 entries;
+ double d;
+
+ g_return_val_if_fail (gd, FALSE);
+ g_return_val_if_fail (path, FALSE);
+
+ file = fopen (path, "r");
+
+ if (!file)
+ goto rg_fail;
+
+ /* check for ID3v1 Tag */
+ if (fseek(file, -ID3V1_SIZE, SEEK_END) ||
+ fread(&buf, 1, 3, file) != 3)
+ goto rg_fail;
+ if (!strncmp(buf, "TAG", 3)) offset -= ID3V1_SIZE;
+
+ /* check for Lyrics3 Tag */
+ if (fseek(file, -9 + offset, SEEK_END) ||
+ fread(&buf, 1, 9, file) != 9)
+ goto rg_fail;
+ if (!strncmp(buf, "LYRICS200", 9)) {
+ if (fseek(file, -LYRICS_FOOTER_SIZE + offset, SEEK_END) ||
+ fread(&buf, 1, 9, file) != 9)
+ goto rg_fail;
+ data_length = buf[0] - '0';
+ for (i = 1; i < 6; i++) {
+ data_length *= 10;
+ data_length += buf[i] - '0';
+ }
+ if (fseek(file, -LYRICS_FOOTER_SIZE - data_length + offset,
+ SEEK_END) ||
+ fread(&buf, 1, 11, file) != 11)
+ goto rg_fail;
+ if (!strncmp(buf, "LYRICSBEGIN", 11))
+ offset -= LYRICS_FOOTER_SIZE + data_length;
+ }
+
+ /* check for APE Tag */
+ if (fseek(file, -APE_FOOTER_SIZE + offset, SEEK_END) ||
+ fread(&buf, 1, 8, file) != 8)
+ goto rg_fail;
+ if (strncmp(buf, "APETAGEX", 8)) goto rg_fail;
+
+ /* Check the version of the tag. 1000 and 2000 (v1.0 and 2.0) are the
+ * only ones I know about. Make suer things do not break in the future.
+ * */
+ if (fread(&buf, 1, 4, file) != 4)
+ goto rg_fail;
+ version = parse_ape_uint32(buf);
+ if (version != 1000 && version != 2000)
+ goto rg_fail;
+
+ /* determine data length */
+ if (fread(&buf, 1, 4, file) != 4)
+ goto rg_fail;
+ data_length = parse_ape_uint32(buf) - APE_FOOTER_SIZE;
+
+ /* determine number of entries */
+ if (fread(&buf, 1, 4, file) != 4)
+ goto rg_fail;
+ entries = parse_ape_uint32(buf);
+
+ /* seek to first entry and read the whole buffer*/
+ if (fseek(file, -APE_FOOTER_SIZE + offset - data_length, SEEK_END))
+ goto rg_fail;
+ if (!(dbuf = malloc(data_length)))
+ goto rg_fail;
+ if (fread(dbuf, 1, data_length, file) != data_length)
+ goto rg_fail;
+
+ for (i = 0; i < entries; i++) {
+ if (gd->radio_gain_set && gd->peak_signal_set) break;
+ pos = pos2 + entry_length;
+ if (pos > data_length - 10) break;
+
+ entry_length = parse_ape_uint32(&dbuf[pos]); pos += 4;
+ pos += 4;
+
+ pos2 = pos;
+ while (dbuf[pos2] && pos2 < data_length) pos2++;
+ if (pos2 == data_length) break;
+ pos2++;
+
+ if (entry_length + 1 > sizeof(buf))
+ continue;
+/* printf ("%s:%d:%d\n",&dbuf[pos], pos2, pos); */
+ if (!gd->radio_gain_set && !strcasecmp(&dbuf[pos],
+ "REPLAYGAIN_TRACK_GAIN")) {
+ memcpy(buf, &dbuf[pos2], entry_length);
+ buf[entry_length] = '\0';
+
+ d = g_ascii_strtod(buf, &ep);
+/* printf("%f\n", d); */
+ if ((ep == buf + entry_length - 3)
+ && (!strncasecmp(ep, " dB", 3))) {
+ gd->radio_gain = d;
+ gd->radio_gain_set = TRUE;
+/* printf("radio_gain (ape): %i\n", gd->radio_gain);*/
+ }
+
+ continue;
+ }
+ if (!gd->peak_signal_set && !strcasecmp(&dbuf[pos],
+ "REPLAYGAIN_TRACK_PEAK")) {
+ memcpy(buf, &dbuf[pos2], entry_length);
+ buf[entry_length] = '\0';
+
+ d = g_ascii_strtod(buf, &ep);
+ if (ep == buf + entry_length) {
+ d *= 0x800000;
+ gd->peak_signal = (guint32) floor(d + 0.5);
+ gd->peak_signal_set = TRUE;
+/* printf("peak_signal (ape): %f\n", (double) gd->peak_signal / 0x800000);*/
+ }
+
+ continue;
+ }
+ }
+
+ free(dbuf);
+ fclose(file);
+ return TRUE;
+
+rg_fail:
+ if (dbuf)
+ free(dbuf);
+ if (file)
+ fclose(file);
+ return FALSE;
+}
+
+
+/* ----------------------------------------------------------------------
+
+ mp3gain code
+
+---------------------------------------------------------------------- */
+
+#include <sys/wait.h>
+#include <fcntl.h>
+
+
+
+
+
+
+/**
+ * mp3_read_soundcheck:
+ *
+ * try to read the ReplayGain values from the LAME or Ape Tags and set
+ * the track's soundcheck field accordingly.
+ *
+ * @path: localtion of the file
+ * @track: structure holding track information
+ *
+ * The function always rereads the gain from the file.
+ *
+ * Returns TRUE if the soundcheck field could be set.
+ */
+gboolean mp3_read_soundcheck (gchar *path, Track *track)
+{
+ GainData gd;
+
+ g_return_val_if_fail (track, FALSE);
+
+ memset (&gd, 0, sizeof (GainData));
+
+ gd.radio_gain_set = FALSE;
+ gd.audiophile_gain_set = FALSE;
+ gd.peak_signal_set = FALSE;
+
+ mp3_get_track_lame_replaygain (path, &gd);
+ if (gd.radio_gain_set)
+ {
+ track->soundcheck = replaygain_to_soundcheck (gd.radio_gain);
+ return TRUE;
+ }
+
+ mp3_get_track_ape_replaygain (path, &gd);
+ if (gd.radio_gain_set)
+ {
+ track->soundcheck = replaygain_to_soundcheck (gd.radio_gain);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+
+/* mp3 slot size in bytes */
+int slotsize[3] = {4,1,1}; /* layer 1, layer 2, layer 3 */
+
+int samplesperframe[2][3] = {
+ { /* MPEG 2.0 */
+ 384,1152,576 /* layer 1, layer 2, layer 3 */
+ },
+
+ { /* MPEG 1.0 */
+ 384,1152,1152 /* layer 1, layer 2, layer 3 */
+ }
+};
+
+
+/*
+ * mp3_get_track_lame_gapless - read the specified file and scan for LAME Tag
+ * gapless information.
+ *
+ * @path: localtion of the file
+ * @track: structure holding track information
+ *
+ * TODO: Split off non-LAME stuff (samplecount, gapless_data) to a separate function since it's generic
+ */
+gboolean mp3_get_track_lame_gapless (gchar *path, GaplessData *gd)
+{
+ FILE *file = NULL;
+ char buf[4], version[5];
+ unsigned char ubuf[4];
+ int sideinfo;
+ int i;
+
+ g_return_val_if_fail (gd, FALSE);
+
+ if (!path)
+ goto gp_fail;
+
+ file = fopen (path, "rb");
+
+ if (!file)
+ goto gp_fail;
+
+ /* use get_first_header() to seek to the first mp3 header */
+ MP3Info *mp3i = NULL;
+ mp3i = g_malloc0 (sizeof (MP3Info));
+ mp3i->filename = path;
+ mp3i->file = file;
+ get_mp3_info (mp3i);
+ get_first_header (mp3i, 0);
+
+ int xing_header_offset = ftell (file);
+
+ MP3Header h;
+ if (!get_header (file, &h))
+ goto gp_fail;
+
+ int mysamplesperframe = samplesperframe[h.version & 1][3 - h.layer];
+
+ /* Determine offset of Xing header based on sideinfo size */
+ if (h.version & 0x1)
+ {
+ sideinfo = (h.mode & 0x2) ?
+ SIDEINFO_MPEG1_MONO : SIDEINFO_MPEG1_MULTI;
+ }
+ else
+ {
+ sideinfo = (h.mode & 0x2) ?
+ SIDEINFO_MPEG2_MONO : SIDEINFO_MPEG2_MULTI;
+ }
+
+ if (fseek (file, sideinfo, SEEK_CUR) ||
+ (fread (&buf[0], 1, 4, file) != 4))
+ goto gp_fail;
+
+ /* Is this really a Xing or Info Header?
+ * FIXME: Apparently (according to madplay sources) there is a different
+ * possible location for the Xing header ("due to an unfortunate
+ * historical event"). I do not thing we need to care though since
+ * ReplayGain information is only contained in recent files. */
+ if (strncmp (buf, "Xing", 4) && strncmp (buf, "Info", 4))
+ goto gp_fail;
+
+ /* Determine the offset of the LAME tag based on contents of the Xing header */
+ int flags;
+ fread (&flags, 4, 1, file);
+ int toskip = 0;
+ if (flags | 0x1)
+ { /* frames field is set */
+ toskip += 4;
+ }
+ if (flags | 0x2)
+ { /* bytes field is set */
+ toskip += 4;
+ }
+ if (flags | 0x4)
+ { /* TOC field is set */
+ toskip += 100;
+ }
+ if (flags | 0x8)
+ { /* quality field is set */
+ toskip += 4;
+ }
+
+ /* Check for LAME Tag */
+ if (fseek (file, toskip, SEEK_CUR) || (fread (&buf[0], 1, 4, file) != 4))
+ goto gp_fail;
+ if (strncmp (buf, "LAME", 4))
+ goto gp_fail;
+
+ /* Check LAME Version */
+ if (fread (version, 1, 5, file) != 5)
+ goto gp_fail;
+
+ /* XXX skip old LAME versions, or just assume that pre/postgap
+ * turn out zeros anyway, or check the CRC to vaidate the tag? */
+
+ gboolean cbr = FALSE;
+ if (fread (ubuf, 1, 1, file) != 1)
+ goto gp_fail;
+
+ if ((ubuf[0] & 0xf) == 0x1)
+ cbr = TRUE;
+
+ if (fseek (file, 0xB, SEEK_CUR) || (fread (ubuf, 1, 4, file) != 4))
+ goto gp_fail;
+
+ /* set pregap and postgap directly from LAME header */
+ gd->pregap = (ubuf[0] << 4) + (ubuf[1] >> 4);
+ gd->postgap = ((ubuf[1] & 0xf) << 8) + ubuf[2];
+
+ /* jump the end of the frame with the xing header */
+ if (fseek (file, xing_header_offset + frame_length (&h), SEEK_SET))
+ goto gp_fail;
+
+ /* counts bytes from the start of the 1st sync frame */
+ int totaldatasize = frame_length (&h);
+
+ /* keeps track of the last 8 frame sizes */
+ int lastframes[8];
+
+ /* counts number of music frames */
+ int totalframes = 0;
+
+ /* quickly parse the file, reading only frame headers */
+ int l = 0;
+ while ((l = get_header (file, &h)) != 0)
+ {
+ for (i = 7; i > 0; i--)
+ {
+ lastframes[i] = lastframes[i - 1];
+ }
+ lastframes[0] = l;
+ totaldatasize += l;
+ totalframes++;
+
+ if (fseek (file, l - FRAME_HEADER_SIZE, SEEK_CUR))
+ goto gp_fail;
+
+ }
+
+ int finaleight = 0;
+ for (i = 0; i < 8; i++)
+ {
+ finaleight += lastframes[i];
+ }
+
+ if (cbr)
+ totalframes++;
+
+ /* all but last eight frames */
+ gd->gapless_data = totaldatasize - finaleight;
+ /* total samples minus pre/postgap */
+ gd->samplecount = totalframes * mysamplesperframe - gd->pregap - gd->postgap;
+ fclose(file);
+
+ return TRUE;
+
+
+ gp_fail:
+ if (file)
+ fclose (file);
+ return FALSE;
+
+}
+
+
+
+/**
+ * mp3_read_gapless:
+ *
+ * try to read the gapless values from the LAME Tag and set
+ * the track's pregap, postgap, samplecount, and gapless_data fields
+ * accordingly.
+ *
+ * @path: location of the file
+ * @track: structure holding track information
+ *
+ * The function always rereads the data from the file.
+ *
+ * Returns TRUE if all four gapless fields could be
+ * set. etrack->tchanged is set to TRUE if data has been changed,
+ * FALSE otherwise.
+ */
+gboolean mp3_read_gapless (char *path, Track *track)
+{
+ GaplessData gd;
+ ExtraTrackData *etr;
+
+ g_return_val_if_fail (track, FALSE);
+
+ etr = track->userdata;
+
+ memset (&gd, 0, sizeof (GaplessData));
+
+ gd.pregap = 0;
+ gd.samplecount = 0;
+ gd.postgap = 0;
+ gd.gapless_data = 0;
+
+ mp3_get_track_lame_gapless (path, &gd);
+
+ etr->tchanged = FALSE;
+
+ if ((gd.pregap) && (gd.samplecount) && (gd.postgap) && (gd.gapless_data))
+ {
+ if ((track->pregap != gd.pregap) ||
+ (track->samplecount != gd.samplecount) ||
+ (track->postgap != gd.postgap) ||
+ (track->gapless_data != gd.gapless_data) ||
+ (track->gapless_track_flag == FALSE))
+ {
+ etr->tchanged = TRUE;
+ track->pregap = gd.pregap;
+ track->samplecount = gd.samplecount;
+ track->postgap = gd.postgap;
+ track->gapless_data = gd.gapless_data;
+ track->gapless_track_flag = TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+/* ----------------------------------------------------------------------
+
+ From here starts original gtkpod code
+
+---------------------------------------------------------------------- */
+
+/* Return a Track structure with all information read from the mp3
+ file filled in */
+Track *mp3_get_file_info (gchar *name)
+{
+ Track *track = NULL;
+ File_Tag filetag;
+ MP3Info *mp3i=NULL;
+ FILE *file;
+ guchar *image_data = NULL;
+ gsize image_data_len = 0;
+
+ g_return_val_if_fail (name, NULL);
+
+ /* Attempt to open the file */
+ file = fopen (name, "r");
+ if (file)
+ {
+ mp3i = g_malloc0 (sizeof (MP3Info));
+ mp3i->filename = name;
+ mp3i->file = file;
+ get_mp3_info (mp3i);
+ mp3i->file = NULL;
+ fclose (file);
+ }
+ else
+ {
+ gchar *fbuf = charset_to_utf8 (name);
+ gtkpod_warning(_("ERROR while opening file: '%s' (%s).\n"),
+ fbuf, g_strerror(errno));
+ g_free (fbuf);
+ return NULL;
+ }
+
+ track = gp_track_new ();
+ track->filetype = g_strdup ("MPEG audio file");
+
+ if (prefs_get_int("readtags") && (id3_tag_read (name, &filetag) == TRUE))
+ {
+
+ if (filetag.album)
+ {
+ track->album = filetag.album;
+ }
+
+ if (filetag.artist)
+ {
+ track->artist = filetag.artist;
+ }
+
+ if (filetag.title)
+ {
+ track->title = filetag.title;
+ }
+
+ if (filetag.genre)
+ {
+ track->genre = filetag.genre;
+ }
+
+ if (filetag.composer)
+ {
+ track->composer = filetag.composer;
+ }
+
+ if (filetag.comment)
+ {
+ track->comment = filetag.comment;
+ }
+
+ if (filetag.podcasturl)
+ {
+ track->podcasturl = filetag.podcasturl;
+ }
+
+ if (filetag.podcastrss)
+ {
+ track->podcastrss = filetag.podcastrss;
+ }
+
+ if (filetag.subtitle)
+ {
+ track->subtitle = filetag.subtitle;
+ }
+
+ if (filetag.description)
+ {
+ track->description = filetag.description;
+ }
+
+ if (filetag.sort_artist)
+ {
+ track->sort_artist = filetag.sort_artist;
+ }
+
+ if (filetag.sort_title)
+ {
+ track->sort_title = filetag.sort_title;
+ }
+
+ if (filetag.sort_album)
+ {
+ track->sort_album = filetag.sort_album;
+ }
+
+ if (filetag.sort_albumartist)
+ {
+ track->sort_albumartist = filetag.sort_albumartist;
+ }
+
+ if (filetag.sort_composer)
+ {
+ track->sort_composer = filetag.sort_composer;
+ }
+
+ if (filetag.year == NULL)
+ {
+ track->year = 0;
+ }
+ else
+ {
+ track->year = atoi(filetag.year);
+ g_free (filetag.year);
+ }
+
+ if (filetag.trackstring == NULL)
+ {
+ track->track_nr = 0;
+ }
+ else
+ {
+ track->track_nr = atoi(filetag.trackstring);
+ g_free (filetag.trackstring);
+ }
+
+ if (filetag.track_total == NULL)
+ {
+ track->tracks = 0;
+ }
+ else
+ {
+ track->tracks = atoi(filetag.track_total);
+ g_free (filetag.track_total);
+ }
+ /* CD/disc number handling */
+ if (filetag.cdnostring == NULL)
+ {
+ track->cd_nr = 0;
+ }
+ else
+ {
+ track->cd_nr = atoi(filetag.cdnostring);
+ g_free (filetag.cdnostring);
+ }
+
+ if (filetag.cdno_total == NULL)
+ {
+ track->cds = 0;
+ }
+ else
+ {
+ track->cds = atoi(filetag.cdno_total);
+ g_free (filetag.cdno_total);
+ }
+
+ if (filetag.compilation == NULL)
+ {
+ track->compilation = 0;
+ }
+ else
+ {
+ track->compilation = atoi(filetag.compilation);
+ g_free (filetag.compilation);
+ }
+
+ if (filetag.BPM == NULL)
+ {
+ track->BPM = 0;
+ }
+ else
+ {
+ track->BPM = atoi(filetag.BPM);
+ g_free (filetag.BPM);
+ }
+
+ if (filetag.lyrics)
+ {
+ track->lyrics_flag = 0x01;
+ g_free (filetag.lyrics);
+ }
+ else
+ {
+ track->lyrics_flag = 0x00;
+ }
+ }
+
+ if (prefs_get_int("coverart_apic") &&
+ (id3_apic_read (name, &image_data, &image_data_len) == TRUE))
+ {
+ if (image_data)
+ {
+ gp_track_set_thumbnails_from_data (track,
+ image_data,
+ image_data_len);
+ g_free (image_data);
+ }
+ }
+
+ mp3_read_soundcheck (name, track);
+
+ mp3_read_gapless (name, track);
+
+#if LOCALDEBUG
+ printf("%s\n", name);
+ printf("\tpregap: %i\n", track->pregap);
+ printf("\tpostgap: %i\n", track->postgap);
+ printf("\tsamplecount: %li\n", track->samplecount);
+ printf("\tgaplessdata: %i\n", track->gapless_data);
+#endif
+
+
+ /* Get additional info (play time and bitrate */
+ if (mp3i)
+ {
+ track->tracklen = mp3i->milliseconds;
+ track->bitrate = (gint)(mp3i->vbr_average);
+ track->samplerate = mp3file_header_frequency (&mp3i->header);
+ g_free (mp3i);
+ }
+ /* Fall back to xmms code if tracklen is 0 */
+ if (track->tracklen == 0)
+ {
+ track->tracklen = get_track_time (name);
+ if (track->tracklen)
+ track->bitrate = (float)track->size*8/track->tracklen;
+ }
+
+ if (track->tracklen == 0)
+ {
+ /* Tracks with zero play length are ignored by iPod... */
+ gtkpod_warning (_("File \"%s\" has zero play length. Ignoring.\n"),
+ name);
+ gp_track_free (track);
+ track = NULL;
+ }
+ return track;
+}
More information about the Pkg-gtkpod-devel
mailing list