[easytag/wip/dsf-support: 1/7] Read DSF header information



commit 1bdfb8efdbc3700f7e07a1e191aa478658f88aa4
Author: David King <amigadave amigadave com>
Date:   Sun Nov 9 00:01:55 2014 +0000

    Read DSF header information
    
    Split functions for reading integers from bytes out to a new private
    file for the exclusive use of the tagging code.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=708368

 Makefile.am            |    4 +
 po/POTFILES.in         |    1 +
 src/et_core.c          |   10 ++
 src/et_core.h          |    1 +
 src/tags/dsf_header.c  |  234 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/tags/dsf_header.h  |   32 +++++++
 src/tags/ogg_tag.c     |   31 +------
 src/tags/tag_private.c |   65 +++++++++++++
 src/tags/tag_private.h |   31 +++++++
 9 files changed, 383 insertions(+), 26 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d208a8f..086cb84 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,6 +70,7 @@ easytag_SOURCES = \
        src/tags/libapetag/info_mac.c \
        src/tags/libapetag/info_mpc.c \
        src/tags/ape_tag.c \
+       src/tags/dsf_header.c \
        src/tags/flac_header.c \
        src/tags/flac_tag.c \
        src/tags/gio_wrapper.cc \
@@ -83,6 +84,7 @@ easytag_SOURCES = \
        src/tags/ogg_tag.c \
        src/tags/opus_header.c \
        src/tags/opus_tag.c \
+       src/tags/tag_private.c \
        src/tags/vcedit.c \
        src/tags/wavpack_header.c \
        src/tags/wavpack_tag.c \
@@ -122,6 +124,7 @@ easytag_headers = \
        src/tags/libapetag/info_mac.h \
        src/tags/libapetag/info_mpc.h \
        src/tags/ape_tag.h \
+       src/tags/dsf_header.h \
        src/tags/flac_header.h \
        src/tags/flac_tag.h \
        src/tags/gio_wrapper.h \
@@ -135,6 +138,7 @@ easytag_headers = \
        src/tags/ogg_tag.h \
        src/tags/opus_header.h \
        src/tags/opus_tag.h \
+       src/tags/tag_private.h \
        src/tags/vcedit.h \
        src/tags/wavpack_header.h \
        src/tags/wavpack_tag.h \
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 195bbf3..4c67fb6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,7 @@ src/setting.c
 src/status_bar.c
 src/tag_area.c
 src/tags/ape_tag.c
+src/tags/dsf_header.c
 src/tags/flac_header.c
 src/tags/flac_tag.c
 src/tags/id3_tag.c
diff --git a/src/et_core.c b/src/et_core.c
index a07b0f9..fabf16f 100644
--- a/src/et_core.c
+++ b/src/et_core.c
@@ -32,6 +32,7 @@
 #include <errno.h>
 
 #include "application_window.h"
+#include "dsf_header.h"
 #include "easytag.h"
 #include "et_core.h"
 #include "mpeg_header.h"
@@ -110,6 +111,7 @@ const ET_File_Description ETFileDescription[] =
     { MP4_FILE, ".m4p", MP4_TAG}, /* Implemented by Michael Ihde. */
     { MP4_FILE, ".m4v", MP4_TAG},
 #endif
+    { DSF_FILE, ".dsf", UNKNOWN_TAG },
 #ifdef ENABLE_WAVPACK
     { WAVPACK_FILE, ".wv", WAVPACK_TAG}, /* Implemented by Maarten Maathuis. */
 #endif
@@ -659,6 +661,9 @@ GList *ET_Add_File_To_File_List (gchar *filename)
             success = et_mp4_header_read_file_info (file, ETFileInfo, &error);
             break;
 #endif
+        case DSF_FILE:
+            success = et_dsf_header_read_file_info (file, ETFileInfo, &error);
+            break;
 #ifdef ENABLE_OPUS
         case OPUS_FILE:
             success = et_opus_read_file_info (file, ETFileInfo, &error);
@@ -2957,6 +2962,11 @@ ET_Display_File_Data_To_UI (ET_File *ETFile)
             et_mp4_file_header_fields_free (fields);
             break;
 #endif
+        case DSF_FILE:
+            fields = et_dsf_header_display_file_info_to_ui (ETFile);
+            et_application_window_file_area_set_header_fields (window, fields);
+            et_dsf_file_header_fields_free (fields);
+            break;
 #ifdef ENABLE_WAVPACK
         case WAVPACK_FILE:
             fields = et_wavpack_header_display_file_info_to_ui (ETFile);
diff --git a/src/et_core.h b/src/et_core.h
index 3feaecb..785a9aa 100644
--- a/src/et_core.h
+++ b/src/et_core.h
@@ -50,6 +50,7 @@ typedef enum
     OFR_FILE,        // OptimFROG (lossless)      : .ofr .ofs
     WAVPACK_FILE,    // Wavpack (lossless)        : .wv
     OPUS_FILE, /* Ogg Opus File: .opus */
+    DSF_FILE, /* DSD codec in DSF container: .dsf */
     UNKNOWN_FILE
 } ET_File_Type;
 
diff --git a/src/tags/dsf_header.c b/src/tags/dsf_header.c
new file mode 100644
index 0000000..24d5a45
--- /dev/null
+++ b/src/tags/dsf_header.c
@@ -0,0 +1,234 @@
+/* EasyTAG - tag editor for audio files
+ * Copyright (C) 2014  David King <amigadave amigadave com>
+ *
+ * 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "dsf_header.h"
+
+#include "easytag.h"
+#include "et_core.h"
+#include "misc.h"
+#include "tag_private.h"
+
+#define DSF_HEADER_LENGTH 80
+#define DSF_HEADER_MAGIC "DSD "
+#define DSF_HEADER_FORMAT_MAGIC "fmt "
+
+gboolean
+et_dsf_header_read_file_info (GFile *file,
+                              ET_File_Info *ETFileInfo,
+                              GError **error)
+{
+    GFileInputStream *file_istream;
+    GInputStream *istream;
+    guchar header[DSF_HEADER_LENGTH];
+    gsize bytes_read;
+    GFileInfo *info;
+    guint64 file_size_header;
+    goffset file_size;
+    guint32 bps;
+    guint64 n_samples;
+
+    g_return_val_if_fail (file != NULL && ETFileInfo != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    file_istream = g_file_read (file, NULL, error);
+
+    if (file_istream == NULL)
+    {
+        return FALSE;
+    }
+
+    istream = G_INPUT_STREAM (file_istream);
+
+    /* Read the complete header from the file. */
+    if (!g_input_stream_read_all (istream, &header, DSF_HEADER_LENGTH,
+                                  &bytes_read, NULL, error))
+    {
+        g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %d "
+                 "bytes were read", bytes_read, DSF_HEADER_LENGTH);
+        goto err;
+    }
+
+    info = g_file_input_stream_query_info (file_istream,
+                                           G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                           NULL, error);
+    g_object_unref (file_istream);
+
+    if (info == NULL)
+    {
+        goto err;
+    }
+
+    file_size = g_file_info_get_size (info);
+    g_object_unref (info);
+
+    if (memcmp (&header, DSF_HEADER_MAGIC, 4) != 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Not a DSF file"));
+        goto err;
+    }
+
+    if (memcmp (&header[28], DSF_HEADER_FORMAT_MAGIC, 4) != 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Not a DSF file"));
+        goto err;
+    }
+
+    /* 12 (8 bytes) total file size. */
+    file_size_header = guint64_from_le_bytes (&header[12]);
+
+    if (file_size_header != file_size)
+    {
+        g_debug ("DSF file is %" G_GUINT64_FORMAT
+                 " bytes, but the file size stored in the header is %"
+                 G_GOFFSET_FORMAT " bytes", file_size, file_size_header);
+    }
+
+    ETFileInfo->size = file_size;
+    
+    /* 40 (4 bytes) format version, normally 1. */
+
+    /* 44 (4 bytes) format ID, normally 0. */
+    ETFileInfo->version = guint32_from_le_bytes (&header[44]);
+    
+    /* 48 (4 bytes) channel type, normally 1-7. */
+    ETFileInfo->mode = guint32_from_le_bytes (&header[52]);
+
+    /* 52 (4 bytes) number of channels. */
+
+    /* 56 (4 bytes) sampling frequency, 2822400 or 5644800 Hz. */
+    ETFileInfo->samplerate = guint32_from_le_bytes (&header[56]);
+
+    /* 60 (4 bytes) bits per sample, 1 or 8. */
+    bps = guint32_from_le_bytes (&header[60]);
+
+    /* 64 (8 bytes) sample count (per channel). */
+    n_samples = guint64_from_le_bytes (&header[64]);
+
+    /* 72 (4 bytes) block size per channel, 4096. */
+
+    /* 76 (4 bytes) zero padded. */
+
+    ETFileInfo->duration = n_samples / ETFileInfo->samplerate;
+    ETFileInfo->bitrate = bps * (n_samples / ETFileInfo->duration / 1000);
+
+    return TRUE;
+
+err:
+    g_object_unref (file_istream);
+    return FALSE;
+}
+
+EtFileHeaderFields *
+et_dsf_header_display_file_info_to_ui (const ET_File *ETFile)
+{
+    EtFileHeaderFields *fields;
+    ET_File_Info *info;
+    gchar *time = NULL;
+    gchar *time1 = NULL;
+    gchar *size = NULL;
+    gchar *size1 = NULL;
+
+    info = ETFile->ETFileInfo;
+    fields = g_slice_new (EtFileHeaderFields);
+
+    fields->description = _("DSF File");
+
+    /* Encoder version */
+    fields->version_label = _("Encoder:");
+
+    switch (info->version)
+    {
+        case 0:
+            fields->version = g_strdup (_("DSD raw"));
+            break;
+        default:
+            fields->version = g_strdup_printf ("%d", info->version);
+            break;
+    }
+
+    /* Bitrate */
+    fields->bitrate = g_strdup_printf (_("%d kb/s"), info->bitrate);
+
+    /* Samplerate */
+    fields->samplerate = g_strdup_printf (_("%d Hz"), info->samplerate);
+
+    /* Mode */
+    fields->mode_label = _("Channels:");
+
+    switch (info->mode)
+    {
+        case 1:
+            fields->mode = g_strdup (_("Mono"));
+            break;
+        case 2:
+            fields->mode = g_strdup (_("Stereo"));
+            break;
+        case 3:
+            fields->mode = g_strdup_printf ("%d", info->mode);
+            break;
+        case 4:
+            fields->mode = g_strdup (_("Quadrophonic"));
+            break;
+        case 5:
+        case 6:
+            fields->mode = g_strdup_printf ("%d", info->mode + 1);
+            break;
+        case 7:
+            fields->mode = g_strdup (_("5.1"));
+            break;
+        default:
+            fields->mode = g_strdup (_("Unknown"));
+            break;
+    }
+
+    /* Size */
+    size = g_format_size (info->size);
+    size1 = g_format_size (ETCore->ETFileDisplayedList_TotalSize);
+    fields->size = g_strdup_printf ("%s (%s)", size, size1);
+    g_free (size);
+    g_free (size1);
+
+    /* Duration */
+    time = Convert_Duration (info->duration);
+    time1 = Convert_Duration (ETCore->ETFileDisplayedList_TotalDuration);
+    fields->duration = g_strdup_printf ("%s (%s)", time, time1);
+    g_free (time);
+    g_free (time1);
+
+    return fields;
+}
+
+void
+et_dsf_file_header_fields_free (EtFileHeaderFields *fields)
+{
+    g_return_if_fail (fields != NULL);
+
+    g_free (fields->version);
+    g_free (fields->bitrate);
+    g_free (fields->samplerate);
+    g_free (fields->mode);
+    g_free (fields->size);
+    g_free (fields->duration);
+    g_slice_free (EtFileHeaderFields, fields);
+}
diff --git a/src/tags/dsf_header.h b/src/tags/dsf_header.h
new file mode 100644
index 0000000..2d2b891
--- /dev/null
+++ b/src/tags/dsf_header.h
@@ -0,0 +1,32 @@
+/* EasyTAG - Tag editor for audio files
+ * Copyright (C) 2014  David King <amigadave amigadave com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ET_DSF_HEADER_H_
+#define ET_DSF_HEADER_H_
+
+#include "et_core.h"
+
+G_BEGIN_DECLS
+
+gboolean et_dsf_header_read_file_info (GFile *file, ET_File_Info *ETFileInfo, GError **error);
+EtFileHeaderFields * et_dsf_header_display_file_info_to_ui (const ET_File *ETFile);
+void et_dsf_file_header_fields_free (EtFileHeaderFields *fields);
+
+G_END_DECLS
+
+#endif /* ET_DSF_HEADER_H_ */
diff --git a/src/tags/ogg_tag.c b/src/tags/ogg_tag.c
index 29e7fbe..4aa7bd1 100644
--- a/src/tags/ogg_tag.c
+++ b/src/tags/ogg_tag.c
@@ -38,6 +38,7 @@
 #include "misc.h"
 #include "picture.h"
 #include "setting.h"
+#include "tag_private.h"
 #include "charset.h"
 
 /* for mkstemp. */
@@ -138,28 +139,6 @@ add_to_guchar_str (guchar *ustr, gsize *ustr_len, guchar *str, gsize str_len)
 }
 
 /*
- * read_guint_from_byte:
- * @str: the byte string
- * @start: position to start with
- *
- * Reads and returns an integer from given byte string starting from start.
- * Returns: Integer which is read
- */
-static guint32
-read_guint32_from_byte (guchar *str, gsize start)
-{
-    gsize i;
-    guint32 read = 0;
-
-    for (i = start; i < start + 4; i++)
-    {
-        read = (read << 8) + str[i];
-    }
-
-    return read;
-}
-
-/*
  * et_add_file_tags_from_vorbis_comments:
  * @vc: Vorbis comment from which to fill @FileTag
  * @FileTag: tag to populate from @vc
@@ -547,15 +526,15 @@ et_add_file_tags_from_vorbis_comments (vorbis_comment *vc,
         decoded_ustr = g_base64_decode (string, &decoded_len);
 
         /* Reading picture type. */
-        pic->type = read_guint32_from_byte (decoded_ustr, 0);
+        pic->type = guint32_from_le_bytes (&decoded_ustr[0]);
         bytes_pos = 4;
 
         /* Reading MIME data. */
-        mimelen = read_guint32_from_byte (decoded_ustr, bytes_pos);
+        mimelen = guint32_from_le_bytes (&decoded_ustr[bytes_pos]);
         bytes_pos = 8 + mimelen;
 
         /* Reading description */
-        desclen = read_guint32_from_byte (decoded_ustr, bytes_pos);
+        desclen = guint32_from_le_bytes (&decoded_ustr[bytes_pos]);
         bytes_pos += 4;
 
         pic->description = g_malloc (desclen + 1);
@@ -569,7 +548,7 @@ et_add_file_tags_from_vorbis_comments (vorbis_comment *vc,
         bytes_pos += desclen + 16;
 
         /* Reading picture size */
-        pic->size = read_guint32_from_byte (decoded_ustr, bytes_pos);
+        pic->size = guint32_from_le_bytes (&decoded_ustr[bytes_pos]);
         bytes_pos += 4;
 
         /* Reading decoded picture */
diff --git a/src/tags/tag_private.c b/src/tags/tag_private.c
new file mode 100644
index 0000000..f43eece
--- /dev/null
+++ b/src/tags/tag_private.c
@@ -0,0 +1,65 @@
+/* EasyTAG - Tag editor for audio files
+ * Copyright (C) 2014  David King <amigadave amigadave com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "tag_private.h"
+
+/*
+ * guint32_from_le_bytes:
+ * @str: a pointer to the first of 4 bytes from which to read an integer
+ *
+ * Reads from the given @str, in little-endian byte order, and gives a 32-bit
+ * integer.
+ *
+ * Returns: a 32-bit integer corresponding to @str
+ */
+guint32
+guint32_from_le_bytes (guchar *str)
+{
+    gsize i;
+    guint32 result = 0;
+
+    for (i = 4; i > 0; i--)
+    {
+        result = (result << 8) + str[i - 1];
+    }
+
+    return result;
+}
+
+/*
+ * guint64_from_le_bytes:
+ * @str: a pointer to the first of 8 bytes from which to read an integer
+ *
+ * Reads from the given @str, in little-endian byte order, and gives a 64-bit
+ * integer.
+ *
+ * Returns: a 64-bit integer corresponding to @str
+ */
+guint64
+guint64_from_le_bytes (guchar *str)
+{
+    gsize i;
+    guint64 result = 0;
+
+    for (i = 8; i > 0; i--)
+    {
+        result = (result << 8) + str[i - 1];
+    }
+
+    return result;
+}
diff --git a/src/tags/tag_private.h b/src/tags/tag_private.h
new file mode 100644
index 0000000..594ec06
--- /dev/null
+++ b/src/tags/tag_private.h
@@ -0,0 +1,31 @@
+/* EasyTAG - Tag editor for audio files
+ * Copyright (C) 2014  David King <amigadave amigadave com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ET_TAG_PRIVATE_H_
+#define ET_TAG_PRIVATE_H_
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+guint32 guint32_from_le_bytes (guchar *str);
+guint64 guint64_from_le_bytes (guchar *str);
+
+G_END_DECLS
+
+#endif /* ET_DSF_HEADER_H_ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]