From 336f624277933a34a049d1d5e88f1357bf8048d8 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 2 Mar 2009 18:00:46 +0100 Subject: [PATCH] tag_id3: parse ID3 tags in RIFF/WAV files Added a small RIFF parser library. Look for an "id3" chunk, and let libid3tag parse it. --- Makefile.am | 8 ++-- NEWS | 1 + src/riff.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/riff.h | 40 +++++++++++++++++++ src/tag_id3.c | 32 ++++++++++++++++ 5 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 src/riff.c create mode 100644 src/riff.h diff --git a/Makefile.am b/Makefile.am index 6d72b3051..2b75b9955 100644 --- a/Makefile.am +++ b/Makefile.am @@ -103,6 +103,7 @@ mpd_headers = \ src/playlist_print.h \ src/playlist_save.h \ src/playlist_state.h \ + src/riff.h \ src/queue.h \ src/queue_print.h \ src/queue_save.h \ @@ -230,7 +231,8 @@ src_mpd_SOURCES += src/pcm_resample_fallback.c endif if HAVE_ID3TAG -src_mpd_SOURCES += src/tag_id3.c +src_mpd_SOURCES += src/tag_id3.c \ + src/riff.c endif # archive plugins @@ -558,8 +560,8 @@ test_software_volume_SOURCES = test/software_volume.c \ src/pcm_volume.c if HAVE_ID3TAG -test_run_decoder_SOURCES += src/tag_id3.c -test_read_tags_SOURCES += src/tag_id3.c +test_run_decoder_SOURCES += src/tag_id3.c src/riff.c +test_read_tags_SOURCES += src/tag_id3.c src/riff.c endif endif diff --git a/NEWS b/NEWS index aa687a22f..c90e3e82a 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ ver 0.15 - (200?/??/??) - support the "album artist" tag - support MusicBrainz tags - parse RVA2 tags in mp3 files + - parse ID3 tags in RIFF/WAV files * decoders: - audiofile: streaming support added - audiofile: added 24 bit support diff --git a/src/riff.c b/src/riff.c new file mode 100644 index 000000000..9af0a2127 --- /dev/null +++ b/src/riff.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + */ + +#include "riff.h" + +#include + +#include +#include +#include +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "riff" + +struct riff_header { + char id[4]; + uint32_t size; + char format[4]; +}; + +struct riff_chunk_header { + char id[4]; + uint32_t size; +}; + +size_t +riff_seek_id3(FILE *file) +{ + int ret; + struct stat st; + struct riff_header header; + struct riff_chunk_header chunk; + size_t size; + + /* determine the file size */ + + ret = fstat(fileno(file), &st); + if (ret < 0) { + g_warning("Failed to stat file descriptor: %s", + strerror(errno)); + return 0; + } + + /* seek to the beginning and read the RIFF header */ + + ret = fseek(file, 0, SEEK_SET); + if (ret != 0) { + g_warning("Failed to seek: %s", g_strerror(errno)); + return 0; + } + + size = fread(&header, sizeof(header), 1, file); + if (size != 1 || + memcmp(header.id, "RIFF", 4) != 0 || + GUINT32_FROM_LE(header.size) > st.st_size) + /* not a RIFF file */ + return 0; + + while (true) { + /* read the chunk header */ + + size = fread(&chunk, sizeof(chunk), 1, file); + if (size != 1) + return 0; + + size = GUINT32_FROM_LE(chunk.size); + if (size % 2 != 0) + /* pad byte */ + ++size; + + g_debug("chunk='%.4s' size=%zu\n", chunk.id, size); + + if (memcmp(chunk.id, "id3 ", 4) == 0) + /* found it! */ + return size; + + if ((off_t)size < 0) + /* integer underflow after cast to signed + type */ + return 0; + + ret = fseek(file, size, SEEK_CUR); + if (ret != 0) + return 0; + } +} diff --git a/src/riff.h b/src/riff.h new file mode 100644 index 000000000..5f45e2a03 --- /dev/null +++ b/src/riff.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + */ + +/** \file + * + * A parser for the RIFF file format (e.g. WAV). + */ + +#ifndef MPD_RIFF_H +#define MPD_RIFF_H + +#include +#include +#include + +/** + * Seeks the RIFF file to the ID3 chunk. + * + * @return the size of the ID3 chunk on success, or 0 if this is not a + * RIFF file or no ID3 chunk was found + */ +size_t +riff_seek_id3(FILE *file); + +#endif diff --git a/src/tag_id3.c b/src/tag_id3.c index f919111d5..7cf82c109 100644 --- a/src/tag_id3.c +++ b/src/tag_id3.c @@ -18,6 +18,7 @@ #include "tag_id3.h" #include "tag.h" +#include "riff.h" #include "conf.h" #include @@ -424,6 +425,35 @@ static struct id3_tag *findId3TagFromEnd(FILE * stream) return tag; } +static struct id3_tag * +tag_id3_riff_load(FILE *file) +{ + size_t size; + void *buffer; + size_t ret; + struct id3_tag *tag; + + size = riff_seek_id3(file); + if (size == 0) + return NULL; + + if (size > 65536) + /* too large, don't allocate so much memory */ + return NULL; + + buffer = g_malloc(size); + ret = fread(buffer, size, 1, file); + if (ret != 1) { + g_warning("Failed to read RIFF chunk"); + g_free(buffer); + return NULL; + } + + tag = id3_tag_parse(buffer, size); + g_free(buffer); + return tag; +} + struct tag *tag_id3_load(const char *file) { struct tag *ret; @@ -438,6 +468,8 @@ struct tag *tag_id3_load(const char *file) } tag = findId3TagFromBeginning(stream); + if (tag == NULL) + tag = tag_id3_riff_load(stream); if (!tag) tag = findId3TagFromEnd(stream);