Revision: 1655
          http://gtkpod.svn.sourceforge.net/gtkpod/?rev=1655&view=rev
Author:   tiffman
Date:     2007-07-26 19:22:31 -0700 (Thu, 26 Jul 2007)

Log Message:
-----------
Merge gapless_playback_branch back to HEAD

Modified Paths:
--------------
    gtkpod/trunk/ChangeLog_detailed
    gtkpod/trunk/data/gtkpod.glade
    gtkpod/trunk/src/details.c
    gtkpod/trunk/src/display.h
    gtkpod/trunk/src/file.c
    gtkpod/trunk/src/misc_conversion.c
    gtkpod/trunk/src/misc_track.c
    gtkpod/trunk/src/mp3file.c

Modified: gtkpod/trunk/ChangeLog_detailed
===================================================================
--- gtkpod/trunk/ChangeLog_detailed     2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/ChangeLog_detailed     2007-07-27 02:22:31 UTC (rev 1655)
@@ -1,3 +1,7 @@
+2007-07-26 Michael Tiffany <tiffman at users.sourceforge.net>
+
+       * merge gapless_playback_branch back to HEAD.
+
 2007-07-23 P.G. Richardson <phantom_sf at users.sourceforge.net>
 
        * src/details.c: set tartwork_changed flag to TRUE rather than relying 
on
@@ -15,8 +19,8 @@
 
 2007-07-15 Michael Tiffany <tiffman at users.sourceforge.net>
 
-       * src/mp3file.c (mp3_get_track_lame_gapless): wasn't properly closing
-         files (bugfix).
+       * src/mp3file.c: fixed memory leak: wasn't closing files in
+         mp3_read_lame_tag.
 
 2007-07-14 Jorg Schuler <jcsjcs at users.sourceforge.net>
 
@@ -56,6 +60,24 @@
          try harder to find evolution-addressbook-export (thanks to
          Götz Waschk)
 
+2007-07-08  Michael Tiffany <tiffman at users.sourceforge.net>
+
+       * src/mp3file.c: modified mp3_read_lame_tag() to check the info tag
+         crc and return the result of the check.  Changed the replaygain and
+         gapless functions to check the return value and only use tag data if
+         the crc matched.
+
+2007-07-06  Michael Tiffany <tiffman at users.sourceforge.net>
+
+       * src/display.h
+         src/misc_conversion.c
+         src/misc_track.c
+         src/file.c
+         src/details.c
+         data/gtkpod.glade:
+         added a read-only checkbox to the Misc tab in the Details window to
+         show the state of the gapless track flag.
+
 2007-07-05  Todd Zullinger <tmzullinger at users.sourceforge.net>
 
        * src/autodection.c:
@@ -87,6 +109,13 @@
        * po/fr.po:
          updated French translation (thanks to Eric Lassauge)
 
+2007-07-02  Michael Tiffany <tiffman at users.sourceforge.net>
+
+       * src/mp3file.c: created struct to store the lame tag, and a function to
+         parse it.  Changed the replaygain and gapless functions to use this to
+         get any necessary lame data.  Added an info tag CRC function from the
+         libmad code (GPL) to check the validity of the lame tag.
+
 2007-06-29 Jorg Schuler <jcsjcs at users.sourceforge.net>
 
        * src/details.c (details_setup_widget): fixed memory leak

Modified: gtkpod/trunk/data/gtkpod.glade
===================================================================
--- gtkpod/trunk/data/gtkpod.glade      2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/data/gtkpod.glade      2007-07-27 02:22:31 UTC (rev 1655)
@@ -17187,6 +17187,56 @@
                                      <property name="y_options"></property>
                                    </packing>
                                  </child>
+
+                                 <child>
+                                   <widget class="GtkLabel" 
id="details_label_54">
+                                     <property name="visible">True</property>
+                                     <property name="label" 
translatable="yes">label</property>
+                                     <property 
name="use_underline">False</property>
+                                     <property 
name="use_markup">False</property>
+                                     <property 
name="justify">GTK_JUSTIFY_LEFT</property>
+                                     <property name="wrap">False</property>
+                                     <property 
name="selectable">False</property>
+                                     <property name="xalign">0</property>
+                                     <property name="yalign">0.5</property>
+                                     <property name="xpad">0</property>
+                                     <property name="ypad">0</property>
+                                     <property 
name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+                                     <property name="width_chars">-1</property>
+                                     <property 
name="single_line_mode">False</property>
+                                     <property name="angle">0</property>
+                                   </widget>
+                                   <packing>
+                                     <property name="left_attach">2</property>
+                                     <property name="right_attach">3</property>
+                                     <property name="top_attach">12</property>
+                                     <property 
name="bottom_attach">13</property>
+                                     <property name="x_options">fill</property>
+                                     <property name="y_options"></property>
+                                   </packing>
+                                 </child>
+
+                                 <child>
+                                   <widget class="GtkCheckButton" 
id="details_checkbutton_54">
+                                     <property name="visible">True</property>
+                                     <property 
name="sensitive">False</property>
+                                     <property name="can_focus">True</property>
+                                     <property name="label" 
translatable="yes"></property>
+                                     <property 
name="use_underline">True</property>
+                                     <property 
name="relief">GTK_RELIEF_NORMAL</property>
+                                     <property 
name="focus_on_click">True</property>
+                                     <property name="active">False</property>
+                                     <property 
name="inconsistent">False</property>
+                                     <property 
name="draw_indicator">True</property>
+                                   </widget>
+                                   <packing>
+                                     <property name="left_attach">3</property>
+                                     <property name="right_attach">4</property>
+                                     <property name="top_attach">12</property>
+                                     <property 
name="bottom_attach">13</property>
+                                     <property name="y_options"></property>
+                                   </packing>
+                                 </child>
                                </widget>
                                <packing>
                                  <property name="padding">0</property>

Modified: gtkpod/trunk/src/details.c
===================================================================
--- gtkpod/trunk/src/details.c  2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/src/details.c  2007-07-27 02:22:31 UTC (rev 1655)
@@ -760,6 +760,7 @@
     case T_CHECKED:
     case T_REMEMBER_PLAYBACK_POSITION:
     case T_SKIP_WHEN_SHUFFLING:
+    case T_GAPLESS_TRACK_FLAG:
        buf = g_strdup_printf ("details_checkbutton_%d", item);
        w = gtkpod_xml_get_widget (detail->xml, buf);
        g_signal_connect (w, "toggled",
@@ -966,6 +967,17 @@
            gtk_combo_box_set_active (GTK_COMBO_BOX (w), index);
        }
        break;
+    case T_GAPLESS_TRACK_FLAG:
+       if ((w = gtkpod_xml_get_widget (detail->xml, checkbutton)))
+       {
+           if (track)
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+                                             track->gapless_track_flag);
+           else
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+                                             FALSE);
+       }
+       break;
     case T_ALL:
     case T_ITEM_NUM:
        /* cannot happen because of assertion above */
@@ -1186,6 +1198,7 @@
     case T_IPOD_PATH:
     case T_IPOD_ID:
     case T_THUMB_PATH:
+    case T_GAPLESS_TRACK_FLAG:
        /* These are read-only only */
        break;
        break;

Modified: gtkpod/trunk/src/display.h
===================================================================
--- gtkpod/trunk/src/display.h  2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/src/display.h  2007-07-27 02:22:31 UTC (rev 1655)
@@ -256,6 +256,7 @@
     T_SORT_ALBUMARTIST,
     T_SORT_COMPOSER,
     T_SORT_TVSHOW,
+    T_GAPLESS_TRACK_FLAG,
     T_ITEM_NUM,
 } T_item;
 

Modified: gtkpod/trunk/src/file.c
===================================================================
--- gtkpod/trunk/src/file.c     2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/src/file.c     2007-07-27 02:22:31 UTC (rev 1655)
@@ -856,6 +856,7 @@
        case T_TRANSFERRED:
        case T_PLAYCOUNT:
        case T_VOLUME:
+       case T_GAPLESS_TRACK_FLAG:
            /* not applicable */
            break;
        case T_ITEM_NUM:

Modified: gtkpod/trunk/src/misc_conversion.c
===================================================================
--- gtkpod/trunk/src/misc_conversion.c  2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/src/misc_conversion.c  2007-07-27 02:22:31 UTC (rev 1655)
@@ -110,6 +110,7 @@
     N_("Sort Album Artist"),
     N_("Sort Composer"),
     N_("Sort TV Show"),
+    N_("Gapless Track Flag"),
     NULL };
 
 /* Tooltips for prefs window */
@@ -169,7 +170,8 @@
     N_("Used for sorting on the iPod"),  /* 50 */
     N_("Used for sorting on the iPod"),
     N_("Used for sorting on the iPod"),
-    N_("Used for sorting on the iPod")
+    N_("Used for sorting on the iPod"),
+    NULL
  };
 
 

Modified: gtkpod/trunk/src/misc_track.c
===================================================================
--- gtkpod/trunk/src/misc_track.c       2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/src/misc_track.c       2007-07-27 02:22:31 UTC (rev 1655)
@@ -909,6 +909,7 @@
     case T_MEDIA_TYPE:
     case T_SEASON_NR:
     case T_EPISODE_NR:
+    case T_GAPLESS_TRACK_FLAG:
     case T_ITEM_NUM:
        g_return_val_if_reached (NULL);
     }
@@ -1203,6 +1204,13 @@
            changed = TRUE;
        }
        break;
+    case T_GAPLESS_TRACK_FLAG:
+       if (frtrack->gapless_track_flag != totrack->gapless_track_flag)
+       {
+           totrack->gapless_track_flag = frtrack->gapless_track_flag;
+           changed = TRUE;
+       }
+       break;
     case T_ITEM_NUM:
     case T_ALL:
        g_return_val_if_reached (FALSE);
@@ -1408,6 +1416,7 @@
     case T_ALL:
     case T_CHECKED:
     case T_ITEM_NUM:
+    case T_GAPLESS_TRACK_FLAG:
        break;
     }
     return text;
@@ -1674,6 +1683,7 @@
     case T_SKIP_WHEN_SHUFFLING:
     case T_CHECKED:
     case T_ALL:
+    case T_GAPLESS_TRACK_FLAG:
     case T_ITEM_NUM:
     case T_THUMB_PATH: // TODO: this should in fact be settable
        gtkpod_warning ("Programming error: track_set_text() called with 
illegal argument (item: %d)\n", item);

Modified: gtkpod/trunk/src/mp3file.c
===================================================================
--- gtkpod/trunk/src/mp3file.c  2007-07-23 22:04:58 UTC (rev 1654)
+++ gtkpod/trunk/src/mp3file.c  2007-07-27 02:22:31 UTC (rev 1655)
@@ -39,6 +39,9 @@
  * 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 third section of this file is taken from the
+ * crc code used in libmad (crc.h). */
+
 /* The code in the last section of this file is original gtkpod
  * code. */
 
@@ -54,6 +57,7 @@
 typedef struct _File_Tag File_Tag;
 typedef struct _GainData GainData;
 typedef struct _GaplessData GaplessData;
+typedef struct _LameTag LameTag;
 
 struct _File_Tag
 {
@@ -107,6 +111,41 @@
     guint32 gapless_data; /* number of bytes from the first sync frame to the 
8th to last frame */
 };
 
+#define LAME_TAG_SIZE 0x24
+#define INFO_TAG_CRC_SIZE 0xBE /* number of bytes to pass to crc_compute */
+
+/* A structure to hold the various data found in a LAME info tag.
+ * Please see http://gabriel.mp3-tech.org/mp3infotag.html for full
+ * documentation.
+ */
+struct _LameTag
+{
+    gchar encoder[4];
+    gchar version_string[5];
+    guint8 info_tag_revision;
+    guint8 vbr_method;
+    guint8 lowpass;
+    float peak_signal_amplitude;
+    guint16 radio_replay_gain;
+    guint16 audiophile_replay_gain;
+    guint8 encoding_flags;
+    guint8 ath_type;
+    guint8 bitrate;
+    guint16 delay;
+    guint16 padding;
+    guint8 noise_shaping;
+    guint8 stereo_mode;
+    gboolean unwise_settings;
+    guint8 source_sample_frequency;
+    guint8 mp3_gain;
+    guint8 surround_info;
+    guint16 preset;
+    guint32 music_length;
+    guint16 music_crc;
+    guint16 info_tag_crc;
+    guint16 calculated_info_tag_crc;
+};
+
 /* This code is taken from the mp3info code. Only the code needed for
  * the playlength calculation has been extracted */
 
@@ -198,6 +237,8 @@
 static guint get_track_time(gchar *path);
 
 
+/* This is for soundcheck code */
+gboolean mp3_read_lame_tag (gchar *path, LameTag *lt);
 
 /* ------------------------------------------------------------
 
@@ -1658,6 +1699,82 @@
 
 
 /*
+ * Code used to calculate the CRC-16 of the info tag.  Used to check the
+ * validity of the data read from the lame tag.
+ * Code taken from the libmad project, licensed under GPL
+ */
+
+static
+unsigned short const crc_table[256] = {
+  0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+  0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+  0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+  0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+  0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+  0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+  0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+  0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+
+  0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+  0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+  0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+  0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+  0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+  0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+  0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+  0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+
+  0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+  0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+  0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+  0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+  0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+  0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+  0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+  0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+
+  0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+  0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+  0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+  0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+  0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+  0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+  0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+  0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+};
+
+unsigned short crc_compute(char const *data, unsigned int length,
+                          unsigned short init)
+{
+  register unsigned int crc;
+
+  for (crc = init; length >= 8; length -= 8) {
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  }
+
+  switch (length) {
+  case 7: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 6: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 5: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 4: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 3: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 2: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 1: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+  case 0: break;
+  }
+
+  return crc;
+}
+
+
+/*
  * 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
@@ -1779,6 +1896,9 @@
                | (buf[2] & 0xff) << 8 | (buf[3] & 0xff);
 }
 
+static inline guint16 parse_lame_uint16(char *buf) {
+       return (buf[0] & 0xff) << 8 | (buf[1] & 0xff);
+}
 
 /* 
  * mp3_get_track_lame_replaygain - read the specified file and scan for LAME 
Tag
@@ -1794,22 +1914,12 @@
 
 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;
+       g_return_val_if_fail (path, FALSE);
 
-       FILE *file = NULL;
-       char buf[4], version[5];
+       LameTag lt;
+       if (!mp3_read_lame_tag (path, &lt))
+           goto rg_fail;
        int gain_adjust = 0;
-       int sideinfo;
-       guint32 ps;
 
        g_return_val_if_fail (gd, FALSE);
 
@@ -1820,106 +1930,23 @@
        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) {
+       if (lame_vcmp(lt.version_string, "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;
+       if ((lame_vcmp(lt.version_string, "3.94b") >= 0)) {
+               if ((!gd->peak_signal_set) && lt.peak_signal_amplitude) {
+                       gd->peak_signal = lt.peak_signal_amplitude;
                        gd->peak_signal_set = TRUE;
 /*                     printf("peak_signal (lame): %f\n", (double)
                                        gd->peak_signal / 0x800000);*/
                }
        } else {
-               float f = *((float *) (void *) (&ps)) * 0x800000;
+               float f = *((float *) (void *) (&lt.peak_signal_amplitude)) * 
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", 
@@ -1930,34 +1957,27 @@
         * 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)) {
+       if ((lame_vcmp(lt.version_string, "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;
-
+       unsigned char ubuf[2];
        /* radio gain */
-       read_lame_replaygain (buf, gd, gain_adjust);
+       memcpy(ubuf,&lt.radio_replay_gain,2);
+       read_lame_replaygain (ubuf, gd, gain_adjust);
 
-       if (fread(&buf[0], 1, 2, file) != 2)
-               goto rg_fail;
-
        /* audiophile gain */
-       read_lame_replaygain (buf, gd, gain_adjust);
+       memcpy(ubuf,&lt.audiophile_replay_gain,2);
+       read_lame_replaygain (ubuf, 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.
@@ -2180,8 +2200,13 @@
 
 
 
+/* ----------------------------------------------------------------------
 
-/* mp3 slot size in bytes */
+             From here starts original gtkpod code
+
+---------------------------------------------------------------------- */
+
+/* mpeg audio slot size in bytes */
 int slotsize[3] = {4,1,1}; /* layer 1, layer 2, layer 3 */
 
 int samplesperframe[2][3] = {
@@ -2196,48 +2221,42 @@
 
 
 /* 
- * mp3_get_track_lame_gapless - read the specified file and scan for LAME Tag
- * gapless information.
+ * mp3_read_lame_tag - read the data from the lame tag (if it exists)
  *
- * @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
+ * @path: location of the file
+ * @lt: pointer to structure to be filled
  */
-gboolean mp3_get_track_lame_gapless (gchar *path, GaplessData *gd)
+gboolean mp3_read_lame_tag (gchar *path, LameTag *lt)
 {
-    FILE *file = NULL;
-    char buf[4], version[5];
-    unsigned char ubuf[4];
+    unsigned char ubuf[LAME_TAG_SIZE];
     int sideinfo;
-    int i;
 
-    g_return_val_if_fail (gd, FALSE);
+    unsigned char full_info_tag[INFO_TAG_CRC_SIZE];
 
-    if (!path)
-       goto gp_fail;
+    g_return_val_if_fail (path, FALSE);
 
-    file = fopen (path, "rb");
-
+    /* Attempt to open the file */
+    FILE *file = fopen (path, "r");
     if (!file)
-       goto gp_fail;
+       goto lt_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);
+
+    /* use get_first_header() to seek to the first mp3 header */
     get_first_header (mp3i, 0);
 
-    int xing_header_offset = ftell (file);
+    if (fread (full_info_tag, 1, INFO_TAG_CRC_SIZE, mp3i->file) != 
INFO_TAG_CRC_SIZE)
+       goto lt_fail;
+    fseek(mp3i->file, -INFO_TAG_CRC_SIZE, SEEK_CUR);
 
     MP3Header h;
-    if (!get_header (file, &h))
-       goto gp_fail;
+    if (!get_header (mp3i->file, &h))
+       goto lt_fail;
 
-    int mysamplesperframe = samplesperframe[h.version & 1][3 - h.layer];
-
     /* Determine offset of Xing header based on sideinfo size */
     if (h.version & 0x1)
     {
@@ -2250,21 +2269,21 @@
            SIDEINFO_MPEG2_MONO : SIDEINFO_MPEG2_MULTI;
     }
 
-    if (fseek (file, sideinfo, SEEK_CUR) ||
-       (fread (&buf[0], 1, 4, file) != 4))
-       goto gp_fail;
+    if (fseek (mp3i->file, sideinfo, SEEK_CUR) ||
+       (fread (&ubuf[0], 1, 4, mp3i->file) != 4))
+       goto lt_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;
+    if (strncmp (ubuf, "Xing", 4) && strncmp (ubuf, "Info", 4))
+       goto lt_fail;
 
     /* Determine the offset of the LAME tag based on contents of the Xing 
header */
     int flags;
-    fread (&flags, 4, 1, file);
+    fread (&flags, 4, 1, mp3i->file);
     int toskip = 0;
     if (flags | 0x1)
     {                          /* frames field is set */
@@ -2284,38 +2303,90 @@
     }
 
     /* 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;
+    if (fseek (mp3i->file, toskip, SEEK_CUR) || (fread (ubuf, 1, 
LAME_TAG_SIZE, mp3i->file) != LAME_TAG_SIZE))
+       goto lt_fail;
+    if (strncmp (ubuf, "LAME", 4))
+       goto lt_fail;
 
-    /* Check LAME Version */
-    if (fread (version, 1, 5, file) != 5)
-       goto gp_fail;
+    strncpy(lt->encoder, &ubuf[0x0], 4);
 
-    /* XXX skip old LAME versions, or just assume that pre/postgap
-     * turn out zeros anyway, or check the CRC to vaidate the tag? */
+    strncpy(lt->version_string, &ubuf[0x4], 5);
 
-    gboolean cbr = FALSE;
-    if (fread (ubuf, 1, 1, file) != 1)
-       goto gp_fail;
+    lt->info_tag_revision = (ubuf[0x9] >> 4);
+    lt->vbr_method = (ubuf[0x9] & 0xf);
+    lt->lowpass = ubuf[0xa];
 
-    if ((ubuf[0] & 0xf) == 0x1)
-       cbr = TRUE;
+    memcpy(&lt->peak_signal_amplitude,&ubuf[0xb],4);
+    memcpy(&lt->radio_replay_gain,&ubuf[0xf],2);
+    memcpy(&lt->audiophile_replay_gain,&ubuf[0x11],2);
 
-    if (fseek (file, 0xB, SEEK_CUR) || (fread (ubuf, 1, 4, file) != 4))
-       goto gp_fail;
+    lt->encoding_flags = ubuf[0x13] >> 4;
+    lt->ath_type = ubuf[0x13] & 0xf;
 
-    /* set pregap and postgap directly from LAME header */
-    gd->pregap = (ubuf[0] << 4) + (ubuf[1] >> 4);
-    gd->postgap = ((ubuf[1] & 0xf) << 8) + ubuf[2];
+    lt->bitrate = ubuf[0x14];
 
-    /* jump the end of the frame with the xing header */
-    if (fseek (file, xing_header_offset + frame_length (&h), SEEK_SET))
+    lt->delay = (ubuf[0x15] << 4) + (ubuf[0x16] >> 4);
+    lt->padding = ((ubuf[0x16] & 0xf) << 8) + ubuf[0x17];
+
+    lt->noise_shaping = ubuf[0x18] & 0x3;
+    lt->stereo_mode = (ubuf[0x18] >> 2) & 0x7;
+    lt->unwise_settings = (ubuf[0x18] >> 5) & 0x1;
+    lt->source_sample_frequency = (ubuf[0x18] >> 6) & 0x3;
+
+    lt->mp3_gain = ubuf[0x19];
+
+    lt->surround_info = (ubuf[0x1a] >> 3) & 0x7;
+    lt->preset = ((ubuf[0x1a] & 0x7) << 8) + ubuf[0x1b];
+
+    lt->music_length = parse_lame_uint32(&ubuf[0x1c]);
+
+    lt->music_crc = parse_lame_uint16(&ubuf[0x20]);
+    lt->info_tag_crc = parse_lame_uint16(&ubuf[0x22]);
+
+    lt->calculated_info_tag_crc = crc_compute(full_info_tag, 
INFO_TAG_CRC_SIZE, 0x0000);
+
+    fclose(file);
+    return (lt->calculated_info_tag_crc == lt->info_tag_crc);
+
+  lt_fail:
+    if (file)
+       fclose(file);
+    return FALSE;
+
+}
+
+
+/* 
+ * mp3_get_track_gapless - read the specified file and calculate gapless
+ * information: totalsamples and gapless_data
+ *
+ * @mp3i: MP3Info of the file; should already have run get_mp3_info() on it
+ * @gd: structure holding gapless information; should have pregap and
+ *     postgap already filled
+ */
+
+gboolean mp3_get_track_gapless (MP3Info *mp3i, GaplessData *gd)
+{
+    int i;
+
+    g_return_val_if_fail (mp3i, FALSE);
+    g_return_val_if_fail (gd, FALSE);
+
+    /* use get_first_header() to seek to the first mp3 header */
+    get_first_header (mp3i, 0);
+
+    int xing_header_offset = ftell (mp3i->file);
+
+    get_header(mp3i->file, &(mp3i->header));
+
+    int mysamplesperframe = samplesperframe[mp3i->header.version & 1][3 - 
mp3i->header.layer];
+
+    /* jump to the end of the frame with the xing header */
+    if (fseek (mp3i->file, xing_header_offset + frame_length 
(&(mp3i->header)), SEEK_SET))
        goto gp_fail;
 
     /* counts bytes from the start of the 1st sync frame */
-    int totaldatasize = frame_length (&h);
+    int totaldatasize = frame_length (&(mp3i->header));
 
     /* keeps track of the last 8 frame sizes */
     int lastframes[8];
@@ -2325,19 +2396,14 @@
 
     /* quickly parse the file, reading only frame headers */
     int l = 0;
-    while ((l = get_header (file, &h)) != 0)
+    while ((l = get_header (mp3i->file, &(mp3i->header))) != 0)
     {
-       for (i = 7; i > 0; i--)
-       {
-           lastframes[i] = lastframes[i - 1];
-       }
-       lastframes[0] = l;
+       lastframes[totalframes%8] = l;
        totaldatasize += l;
        totalframes++;
 
-       if (fseek (file, l - FRAME_HEADER_SIZE, SEEK_CUR))
+       if (fseek (mp3i->file, l - FRAME_HEADER_SIZE, SEEK_CUR))
            goto gp_fail;
-
     }
 
     int finaleight = 0;
@@ -2346,7 +2412,12 @@
        finaleight += lastframes[i];
     }
 
-    if (cbr)
+    /* For some reason, iTunes appears to add an extra frames worth of
+     * samples to the samplecount for CBR files.  CBR files don't currently
+     * (2 Jul 07) play gaplessly whether uploaded from iTunes or gtkpod,
+     * even with apparently correct values, but we will attempt to emulate
+     * iTunes' behavior */
+    if (mp3i->vbr == 0) // CBR
        totalframes++;
 
     /* all but last eight frames */
@@ -2354,13 +2425,10 @@
     /* 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;
 
 }
@@ -2382,8 +2450,11 @@
  * set. etrack->tchanged is set to TRUE if data has been changed,
  * FALSE otherwise.
  */
-gboolean mp3_read_gapless (char *path, Track *track)
-{
+
+gboolean mp3_read_gapless (gchar *path, Track *track) {
+    MP3Info *mp3i=NULL;
+    FILE *file;
+
     GaplessData gd;
     ExtraTrackData *etr;
 
@@ -2393,30 +2464,51 @@
 
     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);
+    g_return_val_if_fail (path, FALSE);
 
-    etr->tchanged = FALSE;
+    /* Attempt to open the file */
+    file = fopen (path, "r");
+    if (file)
+    {
+       mp3i = g_malloc0 (sizeof (MP3Info));
+       mp3i->filename = path;
+       mp3i->file = file;
+       get_mp3_info (mp3i);
 
-    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))
+       /* Try the LAME tag for pregap and postgap */
+       LameTag lt;
+       if (mp3_read_lame_tag (path, &lt)) {
+           gd.pregap = lt.delay;
+           gd.postgap = lt.padding;
+       } else {
+           /* insert non-LAME methods of finding pregap and postgap */
+           fclose(file);
+           return FALSE;
+       }
+           
+       mp3_get_track_gapless (mp3i, &gd);
+           
+       etr->tchanged = FALSE;
+
+       if ((gd.pregap) && (gd.samplecount) && (gd.postgap) && 
(gd.gapless_data))
        {
-           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;
+           if ((track->pregap != lt.delay) ||
+               (track->samplecount != gd.samplecount) ||
+               (track->postgap != lt.padding) ||
+               (track->gapless_data != gd.gapless_data) ||
+               (track->gapless_track_flag == FALSE))
+           {
+               etr->tchanged = TRUE;
+               track->pregap = lt.delay;
+               track->samplecount = gd.samplecount;
+               track->postgap = lt.padding;
+               track->gapless_data = gd.gapless_data;
+               track->gapless_track_flag = TRUE;
+           }
        }
+       fclose(file);
+       return TRUE;
     }
     return FALSE;
 }
@@ -2656,15 +2748,6 @@
 
     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)
     {


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
gtkpod-cvs2 mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gtkpod-cvs2

Reply via email to