[easytag] Support reading tags from Ogg Opus files



commit c636cd01eca6cdc4d741050790ac00354014d7a4
Author: Abhinav <abhijangda hotmail com>
Date:   Mon Mar 24 23:45:51 2014 +0530

    Support reading tags from Ogg Opus files
    
    https://bugzilla.gnome.org/show_bug.cgi?id=692389

 Makefile.am       |    5 +
 README            |    3 +-
 configure.ac      |   21 ++++-
 po/POTFILES.in    |    1 +
 src/easytag.c     |   18 ++++
 src/et_core.c     |   48 ++++++++++-
 src/et_core.h     |    5 +
 src/ogg_tag.c     |  247 ++++++++++++++++++++++++++++-------------------------
 src/ogg_tag.h     |    5 +-
 src/opus_header.c |  250 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/opus_header.h |   65 ++++++++++++++
 src/opus_tag.c    |   96 ++++++++++++++++++++
 src/opus_tag.h    |   31 +++++++
 13 files changed, 675 insertions(+), 120 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 0260c1c..42529cf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,6 +5,7 @@ DISTCHECK_CONFIGURE_FLAGS = \
        --enable-flac \
        --enable-id3v23 \
        --enable-ogg \
+       --enable-opus \
        --enable-man \
        --enable-mp3 \
        --enable-mp4 \
@@ -59,6 +60,8 @@ easytag_SOURCES = \
        src/musepack_header.c \
        src/ogg_header.c \
        src/ogg_tag.c \
+       src/opus_header.c \
+       src/opus_tag.c \
        src/picture.c \
        src/prefs.c \
        src/scan.c \
@@ -98,6 +101,8 @@ easytag_headers = \
        src/musepack_header.h \
        src/ogg_header.h \
        src/ogg_tag.h \
+       src/opus_header.h \
+       src/opus_tag.h \
        src/picture.h \
        src/prefs.h \
        src/scan.h \
diff --git a/README b/README
index df9af68..0e16fe7 100644
--- a/README
+++ b/README
@@ -57,8 +57,9 @@ Installation
 * GLib version greater than 2.30.0 (http://www.gtk.org)
 * GTK+ version greater than 2.24.0 (http://www.gtk.org)
 * id3lib version greater than 3.7.12 (http://id3lib.sourceforge.net) (Recommended: id3lib-3.8.3)
-* libogg and libvorbis (http://www.vorbis.com) (if not deactivated by './configure --disable-ogg')
 * flac (http://flac.sourceforge.net) (if not deactivated by './configure --disable-flac')
+* libogg and libvorbis (http://www.vorbis.com) (if not deactivated by './configure --disable-ogg')
+* opus and opusfile (http://www.opus-codec.org/) (if not deactivated by './configure --disable-opus')
 * taglib (http://taglib.github.com/) (if not deactivated by './configure --disable-mp4')
 * wavpack (http://www.wavpack.com/) (if not deactivated by './configure --disable-wavpack')
 * yelp-tools (https://git.gnome.org/browse/yelp-tools)
diff --git a/configure.ac b/configure.ac
index daf0cf9..4d1a635 100644
--- a/configure.ac
+++ b/configure.ac
@@ -115,6 +115,9 @@ AC_ARG_ENABLE([id3v23],
 AC_ARG_ENABLE([ogg],
               [AS_HELP_STRING([--disable-ogg], [Disable support for Ogg Vorbis files (default=auto)])])
 
+AC_ARG_ENABLE([opus],
+              [AS_HELP_STRING([--disable-opus], [Disable support for Opus Vorbis files (default=auto)])])
+
 AC_ARG_ENABLE([speex],
               [AS_HELP_STRING([--disable-speex], [Disable support for Ogg Speex files (default=auto)])])
 
@@ -176,6 +179,21 @@ AS_IF([test "x$have_ogg" != "xno"],
              [AC_MSG_ERROR([OGG Vorbis support requested but required dependencies ($OGG_DEPS) not 
found])])])
 
 dnl ################################################
+dnl # Opus libraries
+dnl ################################################
+
+OPUS_DEPS="opus >= 1.0 opusfile"
+AS_IF([test "x$enable_opus" != "xno"],
+      [PKG_CHECK_EXISTS([$OPUS_DEPS], [have_opus=yes], [have_opus=no])],
+      [have_opus=no])
+
+AS_IF([test "x$have_opus" != "xno"],
+      [AC_DEFINE([ENABLE_OPUS], [], [Define for Ogg Opus support])],
+      [OPUS_DEPS=""
+       AS_IF([test "x$enable_opus" = "xyes"],
+             [AC_MSG_ERROR([Opus support requested but required dependencies ($OPUS_DEPS) not found])])])
+
+dnl ################################################
 dnl # libSpeex library
 dnl ################################################
 dnl check for system libspeex
@@ -284,7 +302,7 @@ AS_IF([test "x$have_wavpack" != "xno"],
 
 dnl Check the pkg-config dependencies
 GIO_DEPS="gio-2.0 >= 2.32.0" dnl For g_file_new_tmp()
-PKG_CHECK_MODULES([EASYTAG], [$GIO_DEPS $GTK_DEPS $OGG_DEPS $SPEEX_DEPS $FLAC_DEPS $ID3TAG_DEPS $TAGLIB_DEPS 
$WAVPACK_DEPS])
+PKG_CHECK_MODULES([EASYTAG], [$GIO_DEPS $GTK_DEPS $OPUS_DEPS $OGG_DEPS $SPEEX_DEPS $FLAC_DEPS $ID3TAG_DEPS 
$TAGLIB_DEPS $WAVPACK_DEPS])
 
 dnl Check for winsock
 AC_SEARCH_LIBS([gethostbyname], [nsl socket], [],
@@ -363,6 +381,7 @@ echo MP3 file support ........: $have_mp3
 echo ID3v2.3 tags support ....: $have_id3lib $ID3LIB_VERSION
 echo Ogg Vorbis file support .: $have_ogg
 echo Ogg Speex file support ..: $have_speex
+echo Ogg Opus file support ...: $have_opus
 echo FLAC file support .......: $have_flac
 echo MP4 file support ........: $have_taglib
 echo WavPack support .........: $have_wavpack
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2270e4d..9af196d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -22,6 +22,7 @@ src/mpeg_header.c
 src/musepack_header.c
 src/ogg_header.c
 src/ogg_tag.c
+src/opus_header.c
 src/picture.c
 src/prefs.c
 src/scan.c
diff --git a/src/easytag.c b/src/easytag.c
index 7612a55..9d8aebd 100644
--- a/src/easytag.c
+++ b/src/easytag.c
@@ -4070,6 +4070,24 @@ void Tag_Area_Display_Controls (ET_File *ETFile)
             break;
 #endif
 
+#ifdef ENABLE_OPUS
+        case OPUS_TAG:
+            gtk_widget_show (GTK_WIDGET (DiscNumberLabel));
+            gtk_widget_show (GTK_WIDGET (DiscNumberEntry));
+            gtk_widget_show (GTK_WIDGET (ComposerLabel));
+            gtk_widget_show (GTK_WIDGET (ComposerEntry));
+            gtk_widget_show (GTK_WIDGET (OrigArtistLabel));
+            gtk_widget_show (GTK_WIDGET (OrigArtistEntry));
+            gtk_widget_show (GTK_WIDGET (CopyrightLabel));
+            gtk_widget_show (GTK_WIDGET (CopyrightEntry));
+            gtk_widget_show (GTK_WIDGET (URLLabel));
+            gtk_widget_show (GTK_WIDGET (URLEntry));
+            gtk_widget_show (GTK_WIDGET (EncodedByLabel));
+            gtk_widget_show (GTK_WIDGET (EncodedByEntry));
+            et_tag_notebook_show_images_tab (TagNoteBook);
+            break;
+#endif
+
 #ifdef ENABLE_FLAC
         case FLAC_TAG:
             gtk_widget_show(GTK_WIDGET(DiscNumberLabel));
diff --git a/src/et_core.c b/src/et_core.c
index 4dbb1db..bc834d5 100644
--- a/src/et_core.c
+++ b/src/et_core.c
@@ -58,6 +58,10 @@
 #   include "wavpack_header.h"
 #   include "wavpack_tag.h"
 #endif
+#ifdef ENABLE_OPUS
+#include "opus_tag.h"
+#include "opus_header.h"
+#endif
 #include "bar.h"
 #include "browser.h"
 #include "log.h"
@@ -463,6 +467,8 @@ GList *ET_Add_File_To_File_List (gchar *filename)
     if (!filename)
         return ETCore->ETFileList;
 
+    file = g_file_new_for_path (filename);
+
     /* Primary Key for this file */
     ETFileKey = ET_File_Key_New();
 
@@ -525,6 +531,17 @@ GList *ET_Add_File_To_File_List (gchar *filename)
             Wavpack_Tag_Read_File_Tag(filename, FileTag);
         break;
 #endif
+#ifdef ENABLE_OPUS
+        case OPUS_TAG:
+            if (!et_opus_tag_read_file_tag (file, FileTag, &error))
+            {
+                Log_Print (LOG_ERROR,
+                           _("Error reading tag from Opus file (%s)"),
+                           error->message);
+                g_clear_error (&error);
+            }
+            break;
+#endif
         case UNKNOWN_TAG:
         default:
             Log_Print(LOG_ERROR,"FileTag: Undefined tag type (%d) for file 
%s",ETFileDescription->TagType,filename_utf8);
@@ -573,6 +590,16 @@ GList *ET_Add_File_To_File_List (gchar *filename)
             Mp4_Header_Read_File_Info(filename,ETFileInfo);
             break;
 #endif
+#ifdef ENABLE_OPUS
+        case OPUS_FILE:
+            if (!et_opus_read_file_info (file, ETFileInfo, &error))
+            {
+                Log_Print (LOG_ERROR, _("Error while querying information for file: '%s' (%s)"),
+                           filename_utf8, error->message);
+                g_error_free (error);
+            }
+            break;
+#endif
         case UNKNOWN_FILE:
         default:
             Log_Print(LOG_ERROR,"ETFileInfo: Undefined file type (%d) for file 
%s",ETFileDescription->FileType,filename_utf8);
@@ -592,7 +619,6 @@ GList *ET_Add_File_To_File_List (gchar *filename)
 
     /* Store the modification time of the file to check if the file was changed
      * before saving */
-    file = g_file_new_for_path (filename);
     fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
                                   G_FILE_QUERY_INFO_NONE, NULL, NULL);
     g_object_unref (file);
@@ -2652,6 +2678,9 @@ void ET_Display_File_Data_To_UI (ET_File *ETFile)
     gchar *cur_filename;
     gchar *cur_filename_utf8;
     gchar *msg;
+#ifdef ENABLE_OPUS
+    GFile *file;
+#endif
 
     g_return_if_fail (ETFile != NULL &&
                       ((GList *)ETFile->FileNameCur)->data != NULL);
@@ -2707,6 +2736,12 @@ void ET_Display_File_Data_To_UI (ET_File *ETFile)
             ET_Display_File_Tag_To_UI(ETFile);
             break;
 #endif
+#ifdef ENABLE_OPUS
+        case OPUS_TAG:
+            gtk_frame_set_label (GTK_FRAME (TagFrame), _("Opus Tag"));
+            ET_Display_File_Tag_To_UI (ETFile);
+            break;
+#endif
         case UNKNOWN_TAG:
         default:
             gtk_frame_set_label(GTK_FRAME(TagFrame),_("Tag"));
@@ -2769,6 +2804,14 @@ void ET_Display_File_Data_To_UI (ET_File *ETFile)
             Wavpack_Header_Display_File_Info_To_UI(cur_filename,ETFile->ETFileInfo);
             break;
 #endif
+#ifdef ENABLE_OPUS
+        case OPUS_FILE:
+            gtk_frame_set_label (GTK_FRAME (FileFrame), _("Opus File"));
+            file = g_file_new_for_path (cur_filename);
+            et_opus_header_display_file_info_to_ui (file, ETFile->ETFileInfo);
+            g_object_unref (file);
+            break;
+#endif
         case UNKNOWN_FILE:
         default:
             gtk_frame_set_label(GTK_FRAME(FileFrame),_("File"));
@@ -3268,6 +3311,9 @@ void ET_Save_File_Data_From_UI (ET_File *ETFile)
 #ifdef ENABLE_WAVPACK
         case WAVPACK_TAG:
 #endif
+#ifdef ENABLE_OPUS
+        case OPUS_TAG:
+#endif
         case APE_TAG:
             ET_Save_File_Tag_From_UI(FileTag);
             ET_Copy_File_Tag_Item_Other_Field(ETFile,FileTag);
diff --git a/src/et_core.h b/src/et_core.h
index 9d3fa11..d5e67fe 100644
--- a/src/et_core.h
+++ b/src/et_core.h
@@ -107,6 +107,7 @@ typedef enum
     SPEEX_FILE,      // Speech audio files        : .spx
     OFR_FILE,        // OptimFROG (lossless)      : .ofr .ofs
     WAVPACK_FILE,    // Wavpack (lossless)        : .wv
+    OPUS_FILE, /* Ogg Opus File: .opus */
     UNKNOWN_FILE
 } ET_File_Type;
 
@@ -122,6 +123,7 @@ typedef enum
     FLAC_TAG,
     MP4_TAG,
     WAVPACK_TAG,
+    OPUS_TAG,
     UNKNOWN_TAG
 } ET_Tag_Type;
 
@@ -228,6 +230,9 @@ static const ET_File_Description ETFileDescription[] =
     {MP3_FILE,     ".mp3",  ID3_TAG},
     {MP2_FILE,     ".mp2",  ID3_TAG},
 #endif
+#ifdef ENABLE_OPUS
+    { OPUS_FILE, ".opus", OPUS_TAG},
+#endif
 #ifdef ENABLE_OGG
     {OGG_FILE,     ".ogg",  OGG_TAG},
     {OGG_FILE,     ".oga",  OGG_TAG},
diff --git a/src/ogg_tag.c b/src/ogg_tag.c
index f544fda..df2ee7f 100644
--- a/src/ogg_tag.c
+++ b/src/ogg_tag.c
@@ -176,124 +176,23 @@ read_guint32_from_byte (guchar *str, gsize start)
 }
 
 /*
- * Read tag data into an Ogg Vorbis file.
- * Note:
- *  - if field is found but contains no info (strlen(str)==0), we don't read it
+ * et_add_file_tags_from_vorbis_comments:
+ * @vc: the byte string
+ * @FileTag: position to start with
+ * @filename_utf8: display filename
+ *
+ * Reads vorbis comments and copies them to file tag.
  */
-gboolean
-ogg_tag_read_file_tag (gchar *filename, File_Tag *FileTag, GError **error)
-{
-    GFile *file;
-    GFileInputStream *istream;
-    vcedit_state   *state;
-    vorbis_comment *vc;
-    gchar          *string = NULL;
-    gchar          *string1 = NULL;
-    gchar          *string2 = NULL;
-    gchar *filename_utf8;
-    guint           field_num, i;
-    Picture        *prev_pic = NULL;
-
-    g_return_val_if_fail (filename != NULL && FileTag != NULL, FALSE);
-    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-    file = g_file_new_for_path (filename);
-    istream = g_file_read (file, NULL, error);
-
-    if (!istream)
-    {
-        g_object_unref (file);
-        g_assert (error == NULL || *error != NULL);
-        return FALSE;
-    }
-
-    filename_utf8 = filename_to_display (filename);
-
-    {
-    // Skip the id3v2 tag
-    guchar tmp_id3[4];
-    gulong id3v2size;
-
-    // Check if there is an ID3v2 tag...
-    if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET, NULL, error))
-    {
-        goto err;
-    }
-
-    if (g_input_stream_read (G_INPUT_STREAM (istream), tmp_id3, 4, NULL,
-                             error) == 4)
-    {
-        // Calculate ID3v2 length
-        if (tmp_id3[0] == 'I' && tmp_id3[1] == 'D' && tmp_id3[2] == '3' && tmp_id3[3] < 0xFF)
-        {
-            // id3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
-            /* Size is 6-9 position */
-            if (!g_seekable_seek (G_SEEKABLE (istream), 2, G_SEEK_CUR,
-                                  NULL, error))
-            {
-                goto err;
-            }
-
-            if (g_input_stream_read (G_INPUT_STREAM (istream), tmp_id3, 4,
-                                     NULL, error) == 4)
-            {
-                id3v2size = 10 + ( (long)(tmp_id3[3])        | ((long)(tmp_id3[2]) << 7)
-                                | ((long)(tmp_id3[1]) << 14) | ((long)(tmp_id3[0]) << 21) );
-
-                if (!g_seekable_seek (G_SEEKABLE (istream), id3v2size,
-                                      G_SEEK_SET, NULL, error))
-                {
-                    goto err;
-                }
-
-                Log_Print(LOG_ERROR,_("Warning: The Ogg Vorbis file '%s' contains an ID3v2 
tag."),filename_utf8);
-            }
-            else if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET,
-                                       NULL, error))
-            {
-                goto err;
-            }
-
-        }
-        else if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET,
-                                   NULL, error))
-        {
-            goto err;
-        }
-
-    }
-    else if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET,
-                               NULL, error))
-    {
-        goto err;
-    }
 
-    }
-
-    g_assert (error == NULL || *error == NULL);
-
-    g_object_unref (istream);
-
-    state = vcedit_new_state();    // Allocate memory for 'state'
-
-    if (!vcedit_open (state, file, error))
-    {
-        g_assert (error == NULL || *error != NULL);
-        g_object_unref (file);
-        vcedit_clear(state);
-        g_free (filename_utf8);
-        return FALSE;
-    }
-
-    g_assert (error == NULL || *error == NULL);
-
-    /* Get data from tag */
-    vc = vcedit_comments(state);
-    /*{
-        gint i; 
-        for (i=0;i<vc->comments;i++) 
-            g_print("%s -> Ogg vc:'%s'\n",g_path_get_basename(filename),vc->user_comments[i]);
-    }*/
+void
+et_add_file_tags_from_vorbis_comments (vorbis_comment *vc, File_Tag *FileTag,
+                                       const gchar *filename_utf8)
+{
+    gchar *string = NULL;
+    gchar *string1 = NULL;
+    gchar *string2 = NULL;
+    guint field_num, i;
+    Picture *prev_pic = NULL;
 
     /*********
      * Title *
@@ -743,7 +642,123 @@ ogg_tag_read_file_tag (gchar *filename, File_Tag *FileTag, GError **error)
                                            Try_To_Validate_Utf8_String(vc->user_comments[i]));
         }
     }
+}
+
+/*
+ * Read tag data into an Ogg Vorbis file.
+ * Note:
+ *  - if field is found but contains no info (strlen(str)==0), we don't read it
+ */
+gboolean
+ogg_tag_read_file_tag (gchar *filename, File_Tag *FileTag, GError **error)
+{
+    GFile *file;
+    GFileInputStream *istream;
+    vcedit_state   *state;
+    gchar *filename_utf8;
+
+    g_return_val_if_fail (filename != NULL && FileTag != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    file = g_file_new_for_path (filename);
+    istream = g_file_read (file, NULL, error);
+
+    if (!istream)
+    {
+        g_object_unref (file);
+        g_assert (error == NULL || *error != NULL);
+        return FALSE;
+    }
+
+    filename_utf8 = filename_to_display (filename);
+
+    {
+    // Skip the id3v2 tag
+    guchar tmp_id3[4];
+    gulong id3v2size;
+
+    // Check if there is an ID3v2 tag...
+    if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET, NULL, error))
+    {
+        goto err;
+    }
+
+    if (g_input_stream_read (G_INPUT_STREAM (istream), tmp_id3, 4, NULL,
+                             error) == 4)
+    {
+        // Calculate ID3v2 length
+        if (tmp_id3[0] == 'I' && tmp_id3[1] == 'D' && tmp_id3[2] == '3' && tmp_id3[3] < 0xFF)
+        {
+            // id3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size]
+            /* Size is 6-9 position */
+            if (!g_seekable_seek (G_SEEKABLE (istream), 2, G_SEEK_CUR,
+                                  NULL, error))
+            {
+                goto err;
+            }
+
+            if (g_input_stream_read (G_INPUT_STREAM (istream), tmp_id3, 4,
+                                     NULL, error) == 4)
+            {
+                id3v2size = 10 + ( (long)(tmp_id3[3])        | ((long)(tmp_id3[2]) << 7)
+                                | ((long)(tmp_id3[1]) << 14) | ((long)(tmp_id3[0]) << 21) );
+
+                if (!g_seekable_seek (G_SEEKABLE (istream), id3v2size,
+                                      G_SEEK_SET, NULL, error))
+                {
+                    goto err;
+                }
+
+                Log_Print(LOG_ERROR,_("Warning: The Ogg Vorbis file '%s' contains an ID3v2 
tag."),filename_utf8);
+            }
+            else if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET,
+                                       NULL, error))
+            {
+                goto err;
+            }
+
+        }
+        else if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET,
+                                   NULL, error))
+        {
+            goto err;
+        }
+
+    }
+    else if (!g_seekable_seek (G_SEEKABLE (istream), 0L, G_SEEK_SET,
+                               NULL, error))
+    {
+        goto err;
+    }
+
+    }
+
+    g_assert (error == NULL || *error == NULL);
+
+    g_object_unref (istream);
+
+    state = vcedit_new_state();    // Allocate memory for 'state'
+
+    if (!vcedit_open (state, file, error))
+    {
+        g_assert (error == NULL || *error != NULL);
+        g_object_unref (file);
+        vcedit_clear(state);
+        g_free (filename_utf8);
+        return FALSE;
+    }
+
+    g_assert (error == NULL || *error == NULL);
+
+    /* Get data from tag */
+    /*{
+        gint i; 
+        for (i=0;i<vc->comments;i++) 
+            g_print("%s -> Ogg vc:'%s'\n",g_path_get_basename(filename),vc->user_comments[i]);
+    }*/
 
+    et_add_file_tags_from_vorbis_comments (vcedit_comments(state), FileTag,
+                                           filename_utf8);
     vcedit_clear(state);
     g_object_unref (file);
     g_free(filename_utf8);
diff --git a/src/ogg_tag.h b/src/ogg_tag.h
index 620731d..e37eb77 100644
--- a/src/ogg_tag.h
+++ b/src/ogg_tag.h
@@ -24,6 +24,7 @@
 
 
 #include <glib.h>
+#include "vcedit.h"
 #include "et_core.h"
 
 /**************
@@ -32,6 +33,8 @@
 gboolean ogg_tag_read_file_tag (gchar *filename, File_Tag *FileTag,
                                 GError **error);
 gboolean ogg_tag_write_file_tag (ET_File *ETFile, GError **error);
-
+void
+et_add_file_tags_from_vorbis_comments (vorbis_comment *vc, File_Tag *FileTag,
+                                       const gchar *filename_utf8);
 
 #endif /* __OGG_TAG_H__ */
diff --git a/src/opus_header.c b/src/opus_header.c
new file mode 100644
index 0000000..61bbfc8
--- /dev/null
+++ b/src/opus_header.c
@@ -0,0 +1,250 @@
+/* EasyTAG - tag editor for audio files
+ * Copyright (C) 2014 Abhinav Jangda (abhijangda hotmail 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" /* For definition of ENABLE_OPUS */
+
+#ifdef ENABLE_OPUS
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <errno.h>
+
+#include "easytag.h"
+#include "opus_header.h"
+#include "et_core.h"
+#include "charset.h"
+#include "log.h"
+#include "misc.h"
+
+/*
+ * et_opus_error_quark:
+ *
+ * To get EtOpusError domain.
+ *
+ * Returns: GQuark for EtOpusError domain
+ */
+GQuark
+et_opus_error_quark (void)
+{
+    return g_quark_from_static_string ("et-opus-error-quark");
+}
+
+/*
+ * et_opus_open_file:
+ * @filename: Filepath to open
+ * @error: GError or %NULL
+ *
+ * Opens an Opus file.
+ *
+ * Returns: a OggOpusFile on success or %NULL on error.
+ */
+OggOpusFile *
+et_opus_open_file (GFile *gfile, GError **error)
+{
+    OggOpusFile *file;
+    gchar *path;
+    int error_val;
+
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+    g_return_val_if_fail (gfile != NULL, NULL);
+
+    path = g_file_get_path (gfile);
+    file = op_open_file (path, &error_val);
+    g_free (path);
+
+    if (!file)
+    {
+        /* Got error while opening opus file */
+        switch (error_val)
+        {
+            case OP_EREAD:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_READ,
+                             "Error reading file");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EFAULT:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_FAULT,
+                             "Memory allocation failure or internal library error");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EIMPL:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_IMPL,
+                             "Stream used an unimplemented feature");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EINVAL:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_INVAL,
+                             "seek () succeeded on this source but tell () did not");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_ENOTFORMAT:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_NOTFORMAT,
+                             "No logical stream found in a link");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EBADHEADER:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_BADHEADER,
+                             "Corrupted header packet");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EVERSION:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_VERSION,
+                             "ID header contained an unrecognized version number");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EBADLINK:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_BADLINK,
+                             "Corrupted link found");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+
+            case OP_EBADTIMESTAMP:
+                g_set_error (error, ET_OPUS_ERROR, ET_OPUS_ERROR_BADTIMESTAMP,
+                             "First/last timestamp in a link failed checks");
+                g_assert (error == NULL || *error != NULL);
+                return NULL;
+        }
+    }
+
+    return file;
+}
+
+/*
+ * et_opus_read_file_info:
+ * @file: file to read info from
+ * @ETFileInfo: ET_File_Info to put information into
+ * @error: a GError or %NULL
+ *
+ * Read header information of an Opus file.
+ *
+ * Returns: %TRUE if successful otherwise %FALSE
+ */
+gboolean
+et_opus_read_file_info (GFile *gfile, ET_File_Info *ETFileInfo,
+                        GError **error)
+{
+    OggOpusFile *file;
+    const OpusHead* head;
+    GFileInfo *info;
+
+    g_return_val_if_fail (gfile != NULL && ETFileInfo != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    file = et_opus_open_file (gfile, error);
+    if (!file)
+    {
+        g_assert (error == NULL || *error != NULL);
+        return FALSE;
+    }
+
+    info = g_file_query_info (gfile, G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                              G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+    head = op_head (file, -1);
+    ETFileInfo->version = head->version;
+    ETFileInfo->bitrate = op_bitrate (file, -1);
+    ETFileInfo->samplerate = head->input_sample_rate;
+    ETFileInfo->mode = head->channel_count;
+
+    if (info)
+    {
+        ETFileInfo->size = g_file_info_get_size (info);
+        g_object_unref (info);
+    }
+    else
+    {
+        ETFileInfo->size = 0;
+    }
+    
+    ETFileInfo->duration = op_pcm_total (file, -1);
+    op_free (file);
+
+    g_assert (error == NULL || *error == NULL);
+    return TRUE;
+}
+
+/*
+ * et_opus_header_display_file_info_to_ui:
+ * @filename: file to display info of
+ * @ETFileInfo: ET_File_Info to display information
+ *
+ * Display header info from ET_File_Info.
+ *
+ * Returns: %TRUE if successful, otherwise %FALSE
+ */
+gboolean
+et_opus_header_display_file_info_to_ui (GFile *file,
+                                        ET_File_Info *ETFileInfo)
+{
+    gchar *text;
+    gchar *time = NULL;
+    gchar *time1 = NULL;
+    gchar *size = NULL;
+    gchar *size1 = NULL;
+
+    /* Encoder version */
+    gtk_label_set_text (GTK_LABEL (VersionLabel), _("Encoder:"));
+    text = g_strdup_printf ("%d", ETFileInfo->version);
+    gtk_label_set_text (GTK_LABEL (VersionValueLabel), text);
+    g_free (text);
+
+    /* Bitrate */
+    text = g_strdup_printf (_("%d kb/s"), ETFileInfo->bitrate);
+    gtk_label_set_text (GTK_LABEL (BitrateValueLabel), text);
+    g_free (text);
+
+    /* Samplerate */
+    text = g_strdup_printf (_("%d Hz"), ETFileInfo->samplerate);
+    gtk_label_set_text (GTK_LABEL (SampleRateValueLabel), text);
+    g_free (text);
+
+    /* Mode */
+    gtk_label_set_text (GTK_LABEL (ModeLabel), _("Channels:"));
+    text = g_strdup_printf ("%d", ETFileInfo->mode);
+    gtk_label_set_text (GTK_LABEL (ModeValueLabel), text);
+    g_free (text);
+
+    /* Size */
+    size  = g_format_size (ETFileInfo->size);
+    size1 = g_format_size (ETCore->ETFileDisplayedList_TotalSize);
+    text  = g_strdup_printf ("%s (%s)", size, size1);
+    gtk_label_set_text (GTK_LABEL (SizeValueLabel), text);
+    g_free (size);
+    g_free (size1);
+    g_free (text);
+
+    /* Duration */
+    time  = Convert_Duration (ETFileInfo->duration);
+    time1 = Convert_Duration (ETCore->ETFileDisplayedList_TotalDuration);
+    text  = g_strdup_printf ("%s (%s)", time, time1);
+    gtk_label_set_text (GTK_LABEL (DurationValueLabel), text);
+    g_free (time);
+    g_free (time1);
+    g_free (text);
+
+    return TRUE;
+}
+
+#endif /* ENABLE_OPUS */
diff --git a/src/opus_header.h b/src/opus_header.h
new file mode 100644
index 0000000..8feb0f6
--- /dev/null
+++ b/src/opus_header.h
@@ -0,0 +1,65 @@
+/* EasyTAG - tag editor for audio files
+ * Copyright (C) 2014 Abhinav Jangda <abhijangda hotmail 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_OPUS_HEADER_H_
+#define ET_OPUS_HEADER_H_
+
+#include <glib.h>
+#include <opus/opusfile.h>
+
+#include "et_core.h"
+
+/*
+ * Error domain and codes for errors while reading/writing Opus files
+ */
+GQuark et_opus_error_quark (void);
+
+#define ET_OPUS_ERROR et_opus_error_quark ()
+
+/*
+ * EtOpusError:
+ * @ET_OPUS_ERROR_READ: Error reading file
+ * @ET_OPUS_ERROR_FAULT: Memory allocation failure or internal library error
+ * @ET_OPUS_ERROR_IMPL: Stream used an unimplemented feature
+ * @ET_OPUS_ERROR_INVAL: seek () succeeded on this source but tell () didn't
+ * @ET_OPUS_ERROR_NOTFORMAT: No logical stream found in a link
+ * @ET_OPUS_ERROR_BADHEADER: Corrputed header packet
+ * @ET_OPUS_ERROR_VERSION: ID header contained an unrecognized version number
+ * @ET_OPUS_ERROR_BADLINK: Corrupted link found
+ * @ET_OPUS_ERROR_BADTIMESTAMP: First/last timestamp in a link failed checks
+ *
+ * Errors that can occur when reading Opus files.
+ */
+typedef enum
+{
+    ET_OPUS_ERROR_READ,
+    ET_OPUS_ERROR_FAULT,
+    ET_OPUS_ERROR_IMPL,
+    ET_OPUS_ERROR_INVAL,
+    ET_OPUS_ERROR_NOTFORMAT,
+    ET_OPUS_ERROR_BADHEADER,
+    ET_OPUS_ERROR_VERSION,
+    ET_OPUS_ERROR_BADLINK,
+    ET_OPUS_ERROR_BADTIMESTAMP,
+} EtOpusError;
+
+gboolean et_opus_read_file_info (GFile *gfile, ET_File_Info *ETFileInfo, GError **error);
+OggOpusFile * et_opus_open_file (GFile *gfile, GError **error);
+gboolean et_opus_header_display_file_info_to_ui (GFile *gfile, ET_File_Info *ETFileInfo);
+
+#endif /* ET_OPUS_HEADER_H_ */
diff --git a/src/opus_tag.c b/src/opus_tag.c
new file mode 100644
index 0000000..c28addf
--- /dev/null
+++ b/src/opus_tag.c
@@ -0,0 +1,96 @@
+/* EasyTAG - tag editor for audio files
+ * Copyright (C) 2014 Abhinav Jangda <abhijangda hotmail 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" /* For definition of ENABLE_OPUS */
+
+#ifdef ENABLE_OPUS
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <opus/opus.h>
+#include <vorbis/codec.h>
+
+#include "easytag.h"
+#include "opus_tag.h"
+#include "opus_header.h"
+#include "ogg_tag.h"
+#include "et_core.h"
+#include "log.h"
+#include "misc.h"
+#include "picture.h"
+#include "setting.h"
+#include "charset.h"
+
+#define MULTIFIELD_SEPARATOR " - "
+
+/*
+ * et_opus_tag_read_file_tag:
+ * @filename: file from which to read tags
+ * @FileTag: File_Tag to read tag into
+ * @error: a GError or %NULL
+ *
+ * Read file tags and store into File_Tag.
+ *
+ * Returns: %TRUE if successful otherwise %FALSE
+ */
+gboolean
+et_opus_tag_read_file_tag (GFile *gfile, File_Tag *FileTag,
+                           GError **error)
+{
+    OggOpusFile *file;
+    const OpusTags *tags;
+    GFileInfo *info;
+
+    g_return_val_if_fail (gfile != NULL && FileTag != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    file = et_opus_open_file (gfile, error);
+
+    if (!file)
+    {
+        g_assert (error == NULL || *error != NULL);
+        return FALSE;
+    }
+
+    tags = op_tags (file, 0);
+    info = g_file_query_info (gfile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                              G_FILE_QUERY_INFO_NONE, NULL, error);
+
+    if (!info)
+    {
+        op_free (file);
+        g_assert (error == NULL || *error != NULL);
+        return FALSE;
+    }
+
+    /* The cast is safe according to the opusfile documentation. */
+    et_add_file_tags_from_vorbis_comments ((vorbis_comment *)tags, FileTag,
+                                           g_file_info_get_display_name (info));
+    g_object_unref (info);
+    op_free (file);
+
+    g_assert (error == NULL || *error == NULL);
+    return TRUE;
+}
+
+#endif
diff --git a/src/opus_tag.h b/src/opus_tag.h
new file mode 100644
index 0000000..e012727
--- /dev/null
+++ b/src/opus_tag.h
@@ -0,0 +1,31 @@
+/* EasyTAG - tag editor for audio files
+ * Copyright (C) 2014 Abhinav Jangda <abhijangda hotmail 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_OPUS_TAG_H_
+#define ET_OPUS_TAG_H_
+
+#include <glib.h>
+#include "et_core.h"
+
+G_BEGIN_DECLS
+
+gboolean et_opus_tag_read_file_tag (GFile *file, File_Tag *FileTag, GError **error);
+
+G_END_DECLS
+
+#endif /* ET_OPUS_TAG_H_ */


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