From 2c4a42e6b6392e0f2e3a33e75d267d66a9be3004 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Sun, 30 Aug 2015 22:02:32 +0100 Subject: [PATCH] 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. --- configure.ac | 1 - src/Makefile.am | 7 +- src/id3.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/id3.h | 25 +++++++ src/libsox.sym | 1 + src/mp3-util.h | 176 ------------------------------------------------ src/mp3.c | 3 +- 7 files changed, 237 insertions(+), 179 deletions(-) create mode 100644 src/id3.c create mode 100644 src/id3.h diff --git a/configure.ac b/configure.ac index 146d0d55..39306398 100644 --- a/configure.ac +++ b/configure.ac @@ -113,7 +113,6 @@ SOX_DL_LIB([mad], [mad.h], [mad], [mad_stream_buffer]) 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]) diff --git a/src/Makefile.am b/src/Makefile.am index f30402fd..fe024416 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -111,7 +111,12 @@ libsox_la_SOURCES += raw-fmt.c s1-fmt.c s2-fmt.c s3-fmt.c \ 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 = diff --git a/src/id3.c b/src/id3.c new file mode 100644 index 00000000..acd1c89f --- /dev/null +++ b/src/id3.c @@ -0,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 + +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 diff --git a/src/id3.h b/src/id3.h new file mode 100644 index 00000000..842e4d02 --- /dev/null +++ b/src/id3.h @@ -0,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 diff --git a/src/libsox.sym b/src/libsox.sym index 8e217083..3962ea7d 100644 --- a/src/libsox.sym +++ b/src/libsox.sym @@ -15,6 +15,7 @@ lsx_find_file_extension lsx_flush lsx_getopt lsx_getopt_init +lsx_id3_read_tag lsx_lpc10_create_decoder_state lsx_lpc10_create_encoder_state lsx_lpc10_decode diff --git a/src/mp3-util.h b/src/mp3-util.h index feb53df9..d896f286 100644 --- a/src/mp3-util.h +++ b/src/mp3-util.h @@ -17,23 +17,6 @@ #include -#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) @@ -79,165 +62,6 @@ static void write_comments(sox_format_t * ft) #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 static unsigned long xing_frames(priv_t * p, struct mad_bitptr ptr, unsigned bitlen) diff --git a/src/mp3.c b/src/mp3.c index 33a371ed..9fe707c3 100644 --- a/src/mp3.c +++ b/src/mp3.c @@ -45,6 +45,7 @@ typedef enum { #ifdef USING_ID3TAG #include + #include "id3.h" #if defined(HAVE_UNISTD_H) #include #elif defined(HAVE_IO_H) @@ -383,7 +384,7 @@ static int startread(sox_format_t * ft) 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 -- 2.11.4.GIT