shithub: sox

Download patch

ref: 2c4a42e6b6392e0f2e3a33e75d267d66a9be3004
parent: ce770096cd06b1e581b37171f01bee2f25894e8a
author: Mans Rullgard <mans@mansr.com>
date: Sun Aug 30 18:02:32 EDT 2015

id3: make parsing available to all format handlers

Several formats beside mp3 can use id3 tags.  This makes it possible to
parse them from their respective handlers.

--- a/configure.ac
+++ b/configure.ac
@@ -113,7 +113,6 @@
 SOX_DL_LIB([lame], [lame/lame.h lame.h], [mp3lame], [lame_init])
 SOX_DL_LIB([twolame], [twolame.h], [twolame], [twolame_init])
 SOX_FMT_REQ([mp3], [MAD LAME TWOLAME])
-MP3_LIBS="$MP3_LIBS $ID3TAG_LIBS"
 
 SOX_FMT_PKG([oggvorbis], [ogg vorbis vorbisenc vorbisfile])
 SOX_FMT_PKG([opus], [opusfile])
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,7 +111,12 @@
   lu-fmt.c 8svx.c aiff-fmt.c aifc-fmt.c au.c avr.c cdr.c cvsd-fmt.c \
   dvms-fmt.c dat.c hcom.c htk.c maud.c prc.c sf.c smp.c \
   sounder.c soundtool.c sphere.c tx16w.c voc.c vox-fmt.c ima-fmt.c adpcm.c adpcm.h \
-  ima_rw.c ima_rw.h wav.c wve.c xa.c nulfile.c f4-fmt.c f8-fmt.c gsrt.c
+  ima_rw.c ima_rw.h wav.c wve.c xa.c nulfile.c f4-fmt.c f8-fmt.c gsrt.c \
+  id3.c id3.h
+
+if HAVE_ID3TAG
+  libsox_la_LIBADD += @ID3TAG_LIBS@
+endif
 
 pkglib_LTLIBRARIES =
 
--- /dev/null
+++ b/src/id3.c
@@ -1,0 +1,203 @@
+/* libSoX MP3 utilities  Copyright (c) 2007-9 SoX contributors
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "sox_i.h"
+#include "id3.h"
+
+#ifdef HAVE_ID3TAG
+
+#include <id3tag.h>
+
+static char const * id3tagmap[][2] =
+{
+  {"TIT2", "Title"},
+  {"TPE1", "Artist"},
+  {"TALB", "Album"},
+  {"TRCK", "Tracknumber"},
+  {"TDRC", "Year"},
+  {"TCON", "Genre"},
+  {"COMM", "Comment"},
+  {"TPOS", "Discnumber"},
+  {NULL, NULL}
+};
+
+static id3_utf8_t * utf8_id3tag_findframe(
+    struct id3_tag * tag, const char * const frameid, unsigned index)
+{
+  struct id3_frame const * frame = id3_tag_findframe(tag, frameid, index);
+  if (frame) {
+    union id3_field  const * field = id3_frame_field(frame, 1);
+    unsigned nstrings = id3_field_getnstrings(field);
+    while (nstrings--){
+      id3_ucs4_t const * ucs4 = id3_field_getstrings(field, nstrings);
+      if (ucs4)
+        return id3_ucs4_utf8duplicate(ucs4); /* Must call free() on this */
+    }
+  }
+  return NULL;
+}
+
+struct tag_info_node
+{
+    struct tag_info_node * next;
+    off_t start;
+    off_t end;
+};
+
+struct tag_info {
+  sox_format_t * ft;
+  struct tag_info_node * head;
+  struct id3_tag * tag;
+};
+
+static int add_tag(struct tag_info * info)
+{
+  struct tag_info_node * current;
+  off_t start, end;
+  id3_byte_t query[ID3_TAG_QUERYSIZE];
+  id3_byte_t * buffer;
+  long size;
+  int result = 0;
+
+  /* Ensure we're at the start of a valid tag and get its size. */
+  if (ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
+      !(size = id3_tag_query(query, ID3_TAG_QUERYSIZE))) {
+    return 0;
+  }
+  if (size < 0) {
+    if (0 != lsx_seeki(info->ft, size, SEEK_CUR) ||
+        ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
+        (size = id3_tag_query(query, ID3_TAG_QUERYSIZE)) <= 0) {
+      return 0;
+    }
+  }
+
+  /* Don't read a tag more than once. */
+  start = lsx_tell(info->ft);
+  end = start + size;
+  for (current = info->head; current; current = current->next) {
+    if (start == current->start && end == current->end) {
+      return 1;
+    } else if (start < current->end && current->start < end) {
+      return 0;
+    }
+  }
+
+  buffer = lsx_malloc((size_t)size);
+  if (!buffer) {
+    return 0;
+  }
+  memcpy(buffer, query, ID3_TAG_QUERYSIZE);
+  if ((unsigned long)size - ID3_TAG_QUERYSIZE ==
+      lsx_readbuf(info->ft, buffer + ID3_TAG_QUERYSIZE, (size_t)size - ID3_TAG_QUERYSIZE)) {
+    struct id3_tag * tag = id3_tag_parse(buffer, (size_t)size);
+    if (tag) {
+      current = lsx_malloc(sizeof(struct tag_info_node));
+      if (current) {
+        current->next = info->head;
+        current->start = start;
+        current->end = end;
+        info->head = current;
+        if (info->tag && (info->tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)) {
+          struct id3_frame * frame;
+          unsigned i;
+          for (i = 0; (frame = id3_tag_findframe(tag, NULL, i)); i++) {
+            id3_tag_attachframe(info->tag, frame);
+          }
+          id3_tag_delete(tag);
+        } else {
+          if (info->tag) {
+            id3_tag_delete(info->tag);
+          }
+          info->tag = tag;
+        }
+      }
+    }
+  }
+  free(buffer);
+  return result;
+}
+
+void lsx_id3_read_tag(sox_format_t * ft, sox_bool search)
+{
+  struct tag_info   info;
+  id3_utf8_t        * utf8;
+  int               i;
+  int               has_id3v1 = 0;
+
+  info.ft = ft;
+  info.head = NULL;
+  info.tag = NULL;
+
+  /*
+  We look for:
+  ID3v1 at end (EOF - 128).
+  ID3v2 at start.
+  ID3v2 at end (but before ID3v1 from end if there was one).
+  */
+
+  if (search) {
+    if (0 == lsx_seeki(ft, -128, SEEK_END)) {
+      has_id3v1 =
+        add_tag(&info) &&
+        1 == ID3_TAG_VERSION_MAJOR(id3_tag_version(info.tag));
+    }
+    if (0 == lsx_seeki(ft, 0, SEEK_SET)) {
+      add_tag(&info);
+    }
+    if (0 == lsx_seeki(ft, has_id3v1 ? -138 : -10, SEEK_END)) {
+      add_tag(&info);
+    }
+  } else {
+    add_tag(&info);
+  }
+
+  if (info.tag && info.tag->frames) {
+    for (i = 0; id3tagmap[i][0]; ++i) {
+      if ((utf8 = utf8_id3tag_findframe(info.tag, id3tagmap[i][0], 0))) {
+        char * comment = lsx_malloc(strlen(id3tagmap[i][1]) + 1 + strlen((char *)utf8) + 1);
+        sprintf(comment, "%s=%s", id3tagmap[i][1], utf8);
+        sox_append_comment(&ft->oob.comments, comment);
+        free(comment);
+        free(utf8);
+      }
+    }
+    if ((utf8 = utf8_id3tag_findframe(info.tag, "TLEN", 0))) {
+      unsigned long tlen = strtoul((char *)utf8, NULL, 10);
+      if (tlen > 0 && tlen < ULONG_MAX) {
+        ft->signal.length= tlen; /* In ms; convert to samples later */
+        lsx_debug("got exact duration from ID3 TLEN");
+      }
+      free(utf8);
+    }
+  }
+  while (info.head) {
+    struct tag_info_node * head = info.head;
+    info.head = head->next;
+    free(head);
+  }
+  if (info.tag) {
+    id3_tag_delete(info.tag);
+  }
+}
+
+#else
+
+/* Stub for format modules */
+void lsx_id3_read_tag(sox_format_t *ft, sox_bool search) { }
+
+#endif
--- /dev/null
+++ b/src/id3.h
@@ -1,0 +1,25 @@
+/* libSoX MP3 utilities  Copyright (c) 2007-9 SoX contributors
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef SOX_ID3_H
+#define SOX_ID3_H
+
+#include "sox_i.h"
+
+void lsx_id3_read_tag(sox_format_t *ft, sox_bool search);
+
+#endif
--- a/src/libsox.sym
+++ b/src/libsox.sym
@@ -15,6 +15,7 @@
 lsx_flush
 lsx_getopt
 lsx_getopt_init
+lsx_id3_read_tag
 lsx_lpc10_create_decoder_state
 lsx_lpc10_create_encoder_state
 lsx_lpc10_decode
--- a/src/mp3-util.h
+++ b/src/mp3-util.h
@@ -17,23 +17,6 @@
 
 #include <sys/stat.h>
 
-#ifdef USING_ID3TAG
-
-static char const * id3tagmap[][2] =
-{
-  {"TIT2", "Title"},
-  {"TPE1", "Artist"},
-  {"TALB", "Album"},
-  {"TRCK", "Tracknumber"},
-  {"TDRC", "Year"},
-  {"TCON", "Genre"},
-  {"COMM", "Comment"},
-  {"TPOS", "Discnumber"},
-  {NULL, NULL}
-};
-
-#endif /* USING_ID3TAG */
-
 #if defined(HAVE_LAME)
 
 static void write_comments(sox_format_t * ft)
@@ -78,165 +61,6 @@
 }
 
 #endif /* HAVE_LAME */
-
-#ifdef USING_ID3TAG
-
-static id3_utf8_t * utf8_id3tag_findframe(
-    struct id3_tag * tag, const char * const frameid, unsigned index)
-{
-  struct id3_frame const * frame = id3_tag_findframe(tag, frameid, index);
-  if (frame) {
-    union id3_field  const * field = id3_frame_field(frame, 1);
-    unsigned nstrings = id3_field_getnstrings(field);
-    while (nstrings--){
-      id3_ucs4_t const * ucs4 = id3_field_getstrings(field, nstrings);
-      if (ucs4)
-        return id3_ucs4_utf8duplicate(ucs4); /* Must call free() on this */
-    }
-  }
-  return NULL;
-}
-
-struct tag_info_node
-{
-    struct tag_info_node * next;
-    off_t start;
-    off_t end;
-};
-
-struct tag_info {
-  sox_format_t * ft;
-  struct tag_info_node * head;
-  struct id3_tag * tag;
-};
-
-static int add_tag(struct tag_info * info)
-{
-  struct tag_info_node * current;
-  off_t start, end;
-  id3_byte_t query[ID3_TAG_QUERYSIZE];
-  id3_byte_t * buffer;
-  long size;
-  int result = 0;
-
-  /* Ensure we're at the start of a valid tag and get its size. */
-  if (ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
-      !(size = id3_tag_query(query, ID3_TAG_QUERYSIZE))) {
-    return 0;
-  }
-  if (size < 0) {
-    if (0 != lsx_seeki(info->ft, size, SEEK_CUR) ||
-        ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
-        (size = id3_tag_query(query, ID3_TAG_QUERYSIZE)) <= 0) {
-      return 0;
-    }
-  }
-
-  /* Don't read a tag more than once. */
-  start = lsx_tell(info->ft);
-  end = start + size;
-  for (current = info->head; current; current = current->next) {
-    if (start == current->start && end == current->end) {
-      return 1;
-    } else if (start < current->end && current->start < end) {
-      return 0;
-    }
-  }
-
-  buffer = lsx_malloc((size_t)size);
-  if (!buffer) {
-    return 0;
-  }
-  memcpy(buffer, query, ID3_TAG_QUERYSIZE);
-  if ((unsigned long)size - ID3_TAG_QUERYSIZE ==
-      lsx_readbuf(info->ft, buffer + ID3_TAG_QUERYSIZE, (size_t)size - ID3_TAG_QUERYSIZE)) {
-    struct id3_tag * tag = id3_tag_parse(buffer, (size_t)size);
-    if (tag) {
-      current = lsx_malloc(sizeof(struct tag_info_node));
-      if (current) {
-        current->next = info->head;
-        current->start = start;
-        current->end = end;
-        info->head = current;
-        if (info->tag && (info->tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)) {
-          struct id3_frame * frame;
-          unsigned i;
-          for (i = 0; (frame = id3_tag_findframe(tag, NULL, i)); i++) {
-            id3_tag_attachframe(info->tag, frame);
-          }
-          id3_tag_delete(tag);
-        } else {
-          if (info->tag) {
-            id3_tag_delete(info->tag);
-          }
-          info->tag = tag;
-        }
-      }
-    }
-  }
-  free(buffer);
-  return result;
-}
-
-static void read_comments(sox_format_t * ft)
-{
-  struct tag_info   info;
-  id3_utf8_t        * utf8;
-  int               i;
-  int               has_id3v1 = 0;
-
-  info.ft = ft;
-  info.head = NULL;
-  info.tag = NULL;
-
-  /*
-  We look for:
-  ID3v1 at end (EOF - 128).
-  ID3v2 at start.
-  ID3v2 at end (but before ID3v1 from end if there was one).
-  */
-
-  if (0 == lsx_seeki(ft, -128, SEEK_END)) {
-    has_id3v1 =
-      add_tag(&info) &&
-      1 == ID3_TAG_VERSION_MAJOR(id3_tag_version(info.tag));
-  }
-  if (0 == lsx_seeki(ft, 0, SEEK_SET)) {
-    add_tag(&info);
-  }
-  if (0 == lsx_seeki(ft, has_id3v1 ? -138 : -10, SEEK_END)) {
-    add_tag(&info);
-  }
-  if (info.tag && info.tag->frames) {
-    for (i = 0; id3tagmap[i][0]; ++i) {
-      if ((utf8 = utf8_id3tag_findframe(info.tag, id3tagmap[i][0], 0))) {
-        char * comment = lsx_malloc(strlen(id3tagmap[i][1]) + 1 + strlen((char *)utf8) + 1);
-        sprintf(comment, "%s=%s", id3tagmap[i][1], utf8);
-        sox_append_comment(&ft->oob.comments, comment);
-        free(comment);
-        free(utf8);
-      }
-    }
-    if ((utf8 = utf8_id3tag_findframe(info.tag, "TLEN", 0))) {
-      unsigned long tlen = strtoul((char *)utf8, NULL, 10);
-      if (tlen > 0 && tlen < ULONG_MAX) {
-        ft->signal.length= tlen; /* In ms; convert to samples later */
-        lsx_debug("got exact duration from ID3 TLEN");
-      }
-      free(utf8);
-    }
-  }
-  while (info.head) {
-    struct tag_info_node * head = info.head;
-    info.head = head->next;
-    free(head);
-  }
-  if (info.tag) {
-    id3_tag_delete(info.tag);
-  }
-}
-
-#endif /* USING_ID3TAG */
 
 #ifdef HAVE_MAD_H
 
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -45,6 +45,7 @@
 
 #ifdef USING_ID3TAG
   #include <id3tag.h>
+  #include "id3.h"
 #if defined(HAVE_UNISTD_H)
   #include <unistd.h>
 #elif defined(HAVE_IO_H)
@@ -383,7 +384,7 @@
   ft->signal.length = SOX_UNSPEC;
   if (ft->seekable) {
 #ifdef USING_ID3TAG
-    read_comments(ft);
+    lsx_id3_read_tag(ft, sox_true);
     lsx_rewind(ft);
     if (!ft->signal.length)
 #endif