diff --git a/doc/html/changelog.html b/doc/html/changelog.html
index a3d20e6b..030a3207 100644
--- a/doc/html/changelog.html
+++ b/doc/html/changelog.html
@@ -168,6 +168,7 @@
libFLAC:
+ - Added FLAC__metadata_get_tags()
- Added callback-based versions of metadata editing functions:
- FLAC__metadata_chain_read_with_callbacks()
@@ -198,6 +199,7 @@
-
libFLAC++:
+ - Added FLAC::Metadata::get_tags()
- Added decoder functions for skipping single frames, also useful for quickly finding frame boundaries:
- FLAC::Decoder::Stream::skip_single_frame()
diff --git a/include/FLAC++/metadata.h b/include/FLAC++/metadata.h
index 2839d3b4..d7e91a6a 100644
--- a/include/FLAC++/metadata.h
+++ b/include/FLAC++/metadata.h
@@ -787,7 +787,7 @@ namespace FLAC {
* \ingroup flacpp_metadata
*
* \brief
- * Level 0 metadata iterator.
+ * Level 0 metadata iterators.
*
* See the \link flac_metadata_level0 C layer equivalent \endlink
* for more.
@@ -797,6 +797,9 @@ namespace FLAC {
//! See FLAC__metadata_get_streaminfo().
FLACPP_API bool get_streaminfo(const char *filename, StreamInfo &streaminfo);
+ //
+ //! See FLAC__metadata_get_tags().
+ FLACPP_API bool get_tags(const char *filename, VorbisComment *&tags);
/* \} */
diff --git a/include/FLAC/metadata.h b/include/FLAC/metadata.h
index d17d1658..328ca1d4 100644
--- a/include/FLAC/metadata.h
+++ b/include/FLAC/metadata.h
@@ -64,7 +64,7 @@
* There are three metadata interfaces of increasing complexity:
*
* Level 0:
- * Read-only access to the STREAMINFO block.
+ * Read-only access to the STREAMINFO and VORBIS_COMMENT blocks.
*
* Level 1:
* Read-write access to all metadata blocks. This level is write-
@@ -123,8 +123,8 @@ extern "C" {
* \ingroup flac_metadata
*
* \brief
- * The level 0 interface consists of a single routine to read the
- * STREAMINFO block.
+ * The level 0 interface consists of individual routines to read the
+ * STREAMINFO and VORBIS_COMMENT blocks, requiring only a filename.
*
* It skips any ID3v2 tag at the head of the file.
*
@@ -135,17 +135,40 @@ extern "C" {
* will skip any ID3v2 tag at the head of the file.
*
* \param filename The path to the FLAC file to read.
- * \param streaminfo A pointer to space for the STREAMINFO block.
+ * \param streaminfo A pointer to space for the STREAMINFO block. Since
+ * FLAC__StreamMetadata is a simple structure with no
+ * memory allocation involved, you pass the address of
+ * an existing structure. It need not be initialized.
* \assert
* \code filename != NULL \endcode
* \code streaminfo != NULL \endcode
* \retval FLAC__bool
* \c true if a valid STREAMINFO block was read from \a filename. Returns
* \c false if there was a memory allocation error, a file decoder error,
- * or the file contained no STREAMINFO block.
+ * or the file contained no STREAMINFO block. (A memory allocation error
+ * is possible because this function must set up a file decoder.)
*/
FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo);
+/** Read the VORBIS_COMMENT metadata block of the given FLAC file. This
+ * function will skip any ID3v2 tag at the head of the file.
+ *
+ * \param filename The path to the FLAC file to read.
+ * \param tags The address where the returned pointer will be
+ * stored. The \a tags object must be deleted by
+ * the caller using FLAC__metadata_object_delete().
+ * \assert
+ * \code filename != NULL \endcode
+ * \code streaminfo != NULL \endcode
+ * \retval FLAC__bool
+ * \c true if a valid VORBIS_COMMENT block was read from \a filename,
+ * and \a *tags will be set to the address of the tag structure.
+ * Returns \c false if there was a memory allocation error, a file
+ * decoder error, or the file contained no VORBIS_COMMENT block, and
+ * \a *tags will be set to \c NULL.
+ */
+FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags);
+
/* \} */
diff --git a/src/libFLAC++/metadata.cpp b/src/libFLAC++/metadata.cpp
index ba0e0d30..0a9cd6e1 100644
--- a/src/libFLAC++/metadata.cpp
+++ b/src/libFLAC++/metadata.cpp
@@ -985,10 +985,26 @@ namespace FLAC {
{
FLAC__ASSERT(0 != filename);
- ::FLAC__StreamMetadata s;
+ ::FLAC__StreamMetadata object;
- if(::FLAC__metadata_get_streaminfo(filename, &s)) {
- streaminfo = s;
+ if(::FLAC__metadata_get_streaminfo(filename, &object)) {
+ streaminfo = object;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ FLACPP_API bool get_tags(const char *filename, VorbisComment *&tags)
+ {
+ FLAC__ASSERT(0 != filename);
+
+ ::FLAC__StreamMetadata *object;
+
+ tags = 0;
+
+ if(::FLAC__metadata_get_tags(filename, &object)) {
+ tags = new VorbisComment(object, /*copy=*/false);
return true;
}
else
diff --git a/src/libFLAC/metadata_iterators.c b/src/libFLAC/metadata_iterators.c
index 7709c663..9c56879f 100644
--- a/src/libFLAC/metadata_iterators.c
+++ b/src/libFLAC/metadata_iterators.c
@@ -153,8 +153,8 @@ static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecode
typedef struct {
FLAC__bool got_error;
- FLAC__bool got_streaminfo;
- FLAC__StreamMetadata *streaminfo;
+ FLAC__bool got_object;
+ FLAC__StreamMetadata *object;
} level0_client_data;
FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo)
@@ -171,8 +171,8 @@ FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__St
return false;
cd.got_error = false;
- cd.got_streaminfo = false;
- cd.streaminfo = streaminfo;
+ cd.got_object = false;
+ cd.object = 0;
FLAC__file_decoder_set_md5_checking(decoder, false);
FLAC__file_decoder_set_filename(decoder, filename);
@@ -189,17 +189,77 @@ FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__St
return false;
}
- /* the first thing decoded must be the STREAMINFO block: */
if(!FLAC__file_decoder_process_until_end_of_metadata(decoder) || cd.got_error) {
FLAC__file_decoder_finish(decoder);
FLAC__file_decoder_delete(decoder);
+ if(0 != cd.object)
+ FLAC__metadata_object_delete(cd.object);
return false;
}
FLAC__file_decoder_finish(decoder);
FLAC__file_decoder_delete(decoder);
- return !cd.got_error && cd.got_streaminfo;
+ if(cd.got_object) {
+ /* can just copy the contents since STREAMINFO has no internal structure */
+ *streaminfo = *(cd.object);
+ }
+
+ if(0 != cd.object)
+ FLAC__metadata_object_delete(cd.object);
+
+ return cd.got_object;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags)
+{
+ level0_client_data cd;
+ FLAC__FileDecoder *decoder;
+
+ FLAC__ASSERT(0 != filename);
+ FLAC__ASSERT(0 != tags);
+
+ decoder = FLAC__file_decoder_new();
+
+ if(0 == decoder)
+ return false;
+
+ *tags = 0;
+
+ cd.got_error = false;
+ cd.got_object = false;
+ cd.object = 0;
+
+ FLAC__file_decoder_set_md5_checking(decoder, false);
+ FLAC__file_decoder_set_filename(decoder, filename);
+ FLAC__file_decoder_set_metadata_ignore_all(decoder);
+ FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ FLAC__file_decoder_set_write_callback(decoder, write_callback_);
+ FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
+ FLAC__file_decoder_set_error_callback(decoder, error_callback_);
+ FLAC__file_decoder_set_client_data(decoder, &cd);
+
+ if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK || cd.got_error) {
+ FLAC__file_decoder_finish(decoder);
+ FLAC__file_decoder_delete(decoder);
+ return false;
+ }
+
+ if(!FLAC__file_decoder_process_until_end_of_metadata(decoder) || cd.got_error) {
+ FLAC__file_decoder_finish(decoder);
+ FLAC__file_decoder_delete(decoder);
+ if(0 != cd.object)
+ FLAC__metadata_object_delete(cd.object);
+ return false;
+ }
+
+ FLAC__file_decoder_finish(decoder);
+ FLAC__file_decoder_delete(decoder);
+
+ if(cd.got_object)
+ *tags = cd.object;
+
+ return cd.got_object;
}
FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
@@ -214,9 +274,15 @@ void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMeta
level0_client_data *cd = (level0_client_data *)client_data;
(void)decoder;
- if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO && 0 != cd->streaminfo) {
- *(cd->streaminfo) = *metadata;
- cd->got_streaminfo = true;
+ /*
+ * we assume we only get here when the one metadata block we were
+ * looking for was passed to us
+ */
+ if(!cd->got_object) {
+ if(0 == (cd->object = FLAC__metadata_object_clone(metadata)))
+ cd->got_error = true;
+ else
+ cd->got_object = true;
}
}
diff --git a/src/test_libFLAC++/metadata_manip.cpp b/src/test_libFLAC++/metadata_manip.cpp
index fccf0d21..1ede4206 100644
--- a/src/test_libFLAC++/metadata_manip.cpp
+++ b/src/test_libFLAC++/metadata_manip.cpp
@@ -591,6 +591,7 @@ static bool remove_file_(const char *filename)
static bool test_level_0_()
{
FLAC::Metadata::StreamInfo streaminfo;
+ FLAC::Metadata::VorbisComment *tags = 0;
printf("\n\n++++++ testing level 0 interface\n");
@@ -600,6 +601,8 @@ static bool test_level_0_()
if(!test_file_(flacfile_, /*ignore_metadata=*/true))
return false;
+ printf("testing FLAC::Metadata::get_streaminfo()... ");
+
if(!FLAC::Metadata::get_streaminfo(flacfile_, streaminfo))
return die_("during FLAC::Metadata::get_streaminfo()");
@@ -615,6 +618,21 @@ static bool test_level_0_()
if(streaminfo.get_max_blocksize() != 576)
return die_("mismatch in streaminfo.get_max_blocksize()");
+ printf("OK\n");
+
+ printf("testing FLAC::Metadata::get_tags()... ");
+
+ if(!FLAC::Metadata::get_tags(flacfile_, tags))
+ return die_("during FLAC::Metadata::get_tags()");
+
+ /* check to see if some basic data matches (c.f. generate_file_()) */
+ if(tags->get_num_comments() != 0)
+ return die_("mismatch in tags->get_num_comments()");
+
+ printf("OK\n");
+
+ delete tags;
+
if(!remove_file_(flacfile_))
return false;
diff --git a/src/test_libFLAC/metadata_manip.c b/src/test_libFLAC/metadata_manip.c
index d8e73d93..b0c2a273 100644
--- a/src/test_libFLAC/metadata_manip.c
+++ b/src/test_libFLAC/metadata_manip.c
@@ -604,6 +604,7 @@ static FLAC__bool remove_file_(const char *filename)
static FLAC__bool test_level_0_()
{
FLAC__StreamMetadata streaminfo;
+ FLAC__StreamMetadata *tags = 0;
printf("\n\n++++++ testing level 0 interface\n");
@@ -613,6 +614,8 @@ static FLAC__bool test_level_0_()
if(!test_file_(flacfile_, decoder_metadata_callback_null_))
return false;
+ printf("testing FLAC__metadata_get_streaminfo()... ");
+
if(!FLAC__metadata_get_streaminfo(flacfile_, &streaminfo))
return die_("during FLAC__metadata_get_streaminfo()");
@@ -628,6 +631,21 @@ static FLAC__bool test_level_0_()
if(streaminfo.data.stream_info.max_blocksize != 576)
return die_("mismatch in streaminfo.data.stream_info.max_blocksize");
+ printf("OK\n");
+
+ printf("testing FLAC__metadata_get_tags()... ");
+
+ if(!FLAC__metadata_get_tags(flacfile_, &tags))
+ return die_("during FLAC__metadata_get_tags()");
+
+ /* check to see if some basic data matches (c.f. generate_file_()) */
+ if(tags->data.vorbis_comment.num_comments != 0)
+ return die_("mismatch in tags->data.vorbis_comment.num_comments");
+
+ printf("OK\n");
+
+ FLAC__metadata_object_delete(tags);
+
if(!remove_file_(flacfile_))
return false;