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