aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/dsdiff_decoder_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/dsdiff_decoder_plugin.c')
-rw-r--r--src/decoder/dsdiff_decoder_plugin.c151
1 files changed, 145 insertions, 6 deletions
diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/dsdiff_decoder_plugin.c
index 84471fb3..44d12d89 100644
--- a/src/decoder/dsdiff_decoder_plugin.c
+++ b/src/decoder/dsdiff_decoder_plugin.c
@@ -52,10 +52,23 @@ struct dsdiff_chunk_header {
uint32_t size_high, size_low;
};
+/** struct for DSDIFF native Artist and Title tags */
+struct dsdiff_native_tag {
+ uint32_t size;
+};
+
struct dsdiff_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
+ /** offset for artist tag */
+ goffset diar_offset;
+ /** offset for title tag */
+ goffset diti_offset;
};
static bool lsbitfirst;
@@ -115,12 +128,12 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
goffset end_offset)
{
struct dsdiff_chunk_header header;
- while ((goffset)(is->offset + sizeof(header)) <= end_offset) {
+ while ((goffset)(input_stream_get_offset(is) + sizeof(header)) <= end_offset) {
if (!dsdiff_read_chunk_header(decoder, is, &header))
return false;
- goffset chunk_end_offset =
- is->offset + dsdiff_chunk_size(&header);
+ goffset chunk_end_offset = input_stream_get_offset(is)
+ + dsdiff_chunk_size(&header);
if (chunk_end_offset > end_offset)
return false;
@@ -161,7 +174,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
}
}
- return is->offset == end_offset;
+ return input_stream_get_offset(is) == end_offset;
}
/**
@@ -173,7 +186,7 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
const struct dsdiff_chunk_header *prop_header)
{
uint64_t prop_size = dsdiff_chunk_size(prop_header);
- goffset end_offset = is->offset + prop_size;
+ goffset end_offset = input_stream_get_offset(is) + prop_size;
struct dsdlib_id prop_id;
if (prop_size < sizeof(prop_id) ||
@@ -187,6 +200,127 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
return dsdlib_skip_to(decoder, is, end_offset);
}
+static void
+dsdiff_handle_native_tag(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset,
+ enum tag_type type)
+{
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct dsdiff_native_tag metatag;
+
+ if (!dsdlib_read(NULL, is, &metatag, sizeof(metatag)))
+ return;
+
+ uint32_t length = GUINT32_FROM_BE(metatag.size);
+
+ /* Check and limit size of the tag to prevent a stack overflow */
+ if (length == 0 || length > 60)
+ return;
+
+ char string[length];
+ char *label;
+ label = string;
+
+ if (!dsdlib_read(NULL, is, label, (size_t)length))
+ return;
+
+ string[length] = '\0';
+ tag_handler_invoke_tag(handler, handler_ctx, type, label);
+ return;
+}
+
+/**
+ * Read and parse additional metadata chunks for tagging purposes. By default
+ * dsdiff files only support equivalents for artist and title but some of the
+ * extract tools add an id3 tag to provide more tags. If such id3 is found
+ * this will be used for tagging otherwise the native tags (if any) will be
+ * used
+ */
+
+static bool
+dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
+ struct dsdiff_metadata *metadata,
+ struct dsdiff_chunk_header *chunk_header,
+ const struct tag_handler *handler,
+ void *handler_ctx)
+{
+
+ /* skip from DSD data to next chunk header */
+ if (!dsdlib_skip(decoder, is, metadata->chunk_size))
+ return false;
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+
+#ifdef HAVE_ID3TAG
+ metadata->id3_size = 0;
+#endif
+
+ /* Now process all the remaining chunk headers in the stream
+ and record their position and size */
+
+ const goffset size = input_stream_get_size(is);
+ while (input_stream_get_offset(is) < size) {
+ uint64_t chunk_size = dsdiff_chunk_size(chunk_header);
+
+ /* DIIN chunk, is directly followed by other chunks */
+ if (dsdlib_id_equals(&chunk_header->id, "DIIN"))
+ chunk_size = 0;
+
+ /* DIAR chunk - DSDIFF native tag for Artist */
+ if (dsdlib_id_equals(&chunk_header->id, "DIAR")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diar_offset = input_stream_get_offset(is);
+ }
+
+ /* DITI chunk - DSDIFF native tag for Title */
+ if (dsdlib_id_equals(&chunk_header->id, "DITI")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diti_offset = input_stream_get_offset(is);
+ }
+#ifdef HAVE_ID3TAG
+ /* 'ID3 ' chunk, offspec. Used by sacdextract */
+ if (dsdlib_id_equals(&chunk_header->id, "ID3 ")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->id3_offset = input_stream_get_offset(is);
+ metadata->id3_size = chunk_size;
+ }
+#endif
+ if (chunk_size != 0) {
+ if (!dsdlib_skip(decoder, is, chunk_size))
+ break;
+ }
+
+ if (input_stream_get_offset(is) < size) {
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+ }
+ chunk_size = 0;
+ }
+ /* done processing chunk headers, process tags if any */
+
+#ifdef HAVE_ID3TAG
+ if (metadata->id3_offset != 0)
+ {
+ /* a ID3 tag has preference over the other tags, do not process
+ other tags if we have one */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata->id3_offset);
+ return true;
+ }
+#endif
+
+ if (metadata->diar_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diar_offset, TAG_ARTIST);
+
+ if (metadata->diti_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diti_offset, TAG_TITLE);
+ return true;
+}
+
/**
* Read and parse all metadata chunks at the beginning. Stop when the
* first "DSD" chunk is seen, and return its header in the
@@ -221,7 +355,8 @@ dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
/* ignore unknown chunk */
uint64_t chunk_size;
chunk_size = dsdiff_chunk_size(chunk_header);
- goffset chunk_end_offset = is->offset + chunk_size;
+ goffset chunk_end_offset = input_stream_get_offset(is)
+ + chunk_size;
if (!dsdlib_skip_to(decoder, is, chunk_end_offset))
return false;
@@ -374,6 +509,10 @@ dsdiff_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+ /* Read additional metadata and created tags if available */
+ dsdiff_read_metadata_extra(NULL, is, &metadata, &chunk_header,
+ handler, handler_ctx);
+
return true;
}