Update for alloc_buffer changes.
[platform/upstream/gst-plugins-good.git] / gst / auparse / gstauparse.c
index d9eeb8e..d39c239 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+/* Element-Checklist-Version: 5 */
 
 /* 2001/04/03 - Updated parseau to use caps nego
- *              Zaheer Merali <zaheer@grid9.net
+ *              Zaheer Abbas Merali <zaheerabbas at merali dot org>
  */
 
 #ifdef HAVE_CONFIG_H
 #include <stdlib.h>
 #include <string.h>
 
-#include <gstauparse.h>
-
+#include "gstauparse.h"
+#include <gst/audio/audio.h>
 
 /* elementfactory information */
-static GstElementDetails gst_auparse_details = {
-  ".au parser",
-  "Codec/Parser",
-  "LGPL",
-  "Parse an .au file into raw audio",
-  VERSION,
-  "Erik Walthinsen <omega@cse.ogi.edu>",
-  "(C) 1999",
-};
-
-static GstCaps*
-au_type_find (GstBuffer *buf, gpointer private)
+static GstElementDetails gst_auparse_details =
+GST_ELEMENT_DETAILS (".au parser",
+    "Codec/Demuxer/Audio",
+    "Parse an .au file into raw audio",
+    "Erik Walthinsen <omega@cse.ogi.edu>");
+
+static GstStaticPadTemplate gst_auparse_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-au")
+    );
+
+static GstStaticPadTemplate gst_auparse_src_template =
+    GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,          /* FIXME: spider */
+    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
+        /* we don't use GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS
+           because of min buffer-frames which is 1, not 0 */
+        "audio/x-raw-float, "
+        "rate = (int) [ 1, MAX ], "
+        "channels = (int) [ 1, MAX ], "
+        "endianness = (int) { LITTLE_ENDIAN , BIG_ENDIAN }, "
+        "width = (int) { 32, 64 }, "
+        "buffer-frames = (int) [ 0, MAX]" "; "
+        "audio/x-alaw, "
+        "rate = (int) [ 8000, 192000 ], "
+        "channels = (int) [ 1, 2 ]" "; "
+        "audio/x-mulaw, "
+        "rate = (int) [ 8000, 192000 ], " "channels = (int) [ 1, 2 ]" "; "
+        /* Nothing to decode those ADPCM streams for now */
+        "audio/x-adpcm, " "layout = (string) { g721, g722, g723_3, g723_5 }")
+    );
+
+enum
 {
-  GstCaps *new = NULL;
-  gulong *head = (gulong *) GST_BUFFER_DATA (buf);
-
-  if (*head == 0x2e736e64 || *head == 0x646e732e)
-    new = gst_caps_new ("au_type_find", "audio/au", NULL);
-
-  return new;
-}
-
-/* typefactory for 'au' */
-static GstTypeDefinition audefinition = {
-  "auparse_audio/au",
-  "audio/au",
-  ".au",
-  au_type_find,
+  ARG_0
+      /* FILL ME */
 };
 
-GST_PAD_TEMPLATE_FACTORY (sink_factory_templ,
-  "sink",
-  GST_PAD_SINK,
-  GST_PAD_ALWAYS,
-  GST_CAPS_NEW (
-    "auparse_sink",
-    "audio/au",
-    NULL
-  )
-)
-
-
-GST_PAD_TEMPLATE_FACTORY (src_factory_templ,
-  "src",
-  GST_PAD_SRC,
-  GST_PAD_ALWAYS,
-  GST_CAPS_NEW (
-    "auparse_src",
-    "audio/raw",
-      "format",     GST_PROPS_STRING ("int"),
-      "law",        GST_PROPS_INT_RANGE (0, 1),
-      "endianness", GST_PROPS_INT (G_BYTE_ORDER),
-      "signed",     GST_PROPS_LIST(
-                     GST_PROPS_BOOLEAN (FALSE),
-                     GST_PROPS_BOOLEAN (TRUE)
-                   ),
-      "width",      GST_PROPS_LIST(
-                     GST_PROPS_INT (8),
-                     GST_PROPS_INT (16)
-                   ),
-      "depth",      GST_PROPS_LIST(
-                     GST_PROPS_INT (8),
-                     GST_PROPS_INT (16)
-                   ),
-      "rate",       GST_PROPS_INT_RANGE (8000,48000),
-      "channels",   GST_PROPS_INT_RANGE (1, 2)
-  )
-)
-
-/* AuParse signals and args */
-enum {
-  /* FILL ME */
-  LAST_SIGNAL
-};
+static void gst_auparse_base_init (gpointer g_class);
+static void gst_auparse_class_init (GstAuParseClass * klass);
+static void gst_auparse_init (GstAuParse * auparse);
+static void gst_auparse_dispose (GObject * object);
 
-enum {
-  ARG_0,
-  /* FILL ME */
-};
-
-static void    gst_auparse_class_init          (GstAuParseClass *klass);
-static void    gst_auparse_init                (GstAuParse *auparse);
+static GstFlowReturn gst_auparse_chain (GstPad * pad, GstBuffer * buf);
 
-static void    gst_auparse_chain               (GstPad *pad,GstBuffer *buf);
+static GstStateChangeReturn gst_auparse_change_state (GstElement * element,
+    GstStateChange transition);
 
 static GstElementClass *parent_class = NULL;
+
+
 /*static guint gst_auparse_signals[LAST_SIGNAL] = { 0 }; */
 
 GType
-gst_auparse_get_type (void) 
+gst_auparse_get_type (void)
 {
   static GType auparse_type = 0;
 
   if (!auparse_type) {
     static const GTypeInfo auparse_info = {
-      sizeof(GstAuParseClass),      NULL,
+      sizeof (GstAuParseClass),
+      gst_auparse_base_init,
       NULL,
       (GClassInitFunc) gst_auparse_class_init,
       NULL,
       NULL,
-      sizeof(GstAuParse),
+      sizeof (GstAuParse),
       0,
       (GInstanceInitFunc) gst_auparse_init,
     };
-    auparse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAuParse", &auparse_info, 0);
+
+    auparse_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstAuParse", &auparse_info,
+        0);
   }
   return auparse_type;
 }
 
 static void
-gst_auparse_class_init (GstAuParseClass *klass) 
+gst_auparse_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_auparse_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_auparse_src_template));
+  gst_element_class_set_details (element_class, &gst_auparse_details);
+
+}
+
+static void
+gst_auparse_class_init (GstAuParseClass * klass)
 {
+  GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
 
-  gstelement_class = (GstElementClass*) klass;
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
 
   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  gobject_class->dispose = gst_auparse_dispose;
+
+  gstelement_class->change_state = gst_auparse_change_state;
 }
 
-static void 
-gst_auparse_init (GstAuParse *auparse) 
+static void
+gst_auparse_init (GstAuParse * auparse)
 {
-  auparse->sinkpad = gst_pad_new_from_template (
-                 GST_PAD_TEMPLATE_GET (sink_factory_templ), "sink");
+  auparse->sinkpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&gst_auparse_sink_template), "sink");
   gst_element_add_pad (GST_ELEMENT (auparse), auparse->sinkpad);
   gst_pad_set_chain_function (auparse->sinkpad, gst_auparse_chain);
 
-  auparse->srcpad = gst_pad_new_from_template (
-                 GST_PAD_TEMPLATE_GET (src_factory_templ), "src");
+  auparse->srcpad = gst_pad_new_from_template (gst_static_pad_template_get
+      (&gst_auparse_src_template), "src");
+  gst_pad_use_fixed_caps (auparse->srcpad);
   gst_element_add_pad (GST_ELEMENT (auparse), auparse->srcpad);
 
   auparse->offset = 0;
+  auparse->buffer_offset = 0;
+  auparse->adapter = gst_adapter_new ();
   auparse->size = 0;
   auparse->encoding = 0;
   auparse->frequency = 0;
   auparse->channels = 0;
 }
 
-static void 
-gst_auparse_chain (GstPad *pad, GstBuffer *buf) 
+static void
+gst_auparse_dispose (GObject * object)
 {
+  GstAuParse *au = GST_AUPARSE (object);
+
+  if (au->adapter != NULL) {
+    gst_object_unref (au->adapter);
+    au->adapter = NULL;
+  }
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static GstFlowReturn
+gst_auparse_chain (GstPad * pad, GstBuffer * buf)
+{
+  GstFlowReturn ret;
   GstAuParse *auparse;
-  gchar *data;
+  guchar *data;
   glong size;
-  GstCaps* tempcaps;
-  gint law, depth;
-  gboolean sign;
+  GstCaps *tempcaps;
+  gint law = 0, depth = 0, ieee = 0;
+  gchar layout[7];
+  GstBuffer *subbuf;
+  GstEvent *event;
 
-  g_return_if_fail (pad != NULL);
-  g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (buf != NULL);
+  layout[0] = 0;
 
   auparse = GST_AUPARSE (gst_pad_get_parent (pad));
-  
+
   GST_DEBUG ("gst_auparse_chain: got buffer in '%s'",
-          gst_element_get_name (GST_ELEMENT (auparse)));
+      gst_element_get_name (GST_ELEMENT (auparse)));
 
   data = GST_BUFFER_DATA (buf);
   size = GST_BUFFER_SIZE (buf);
 
   /* if we haven't seen any data yet... */
   if (auparse->size == 0) {
-    GstBuffer *newbuf;
-    guint32 *head = (guint32 *)data;
+    guint32 *head = (guint32 *) data;
 
     /* normal format is big endian (au is a Sparc format) */
-    if (GUINT32_FROM_BE (*head) == 0x2e736e64) {
+    if (GST_READ_UINT32_BE (head) == 0x2e736e64) {      /* ".snd" */
       head++;
       auparse->le = 0;
-      auparse->offset          = GUINT32_FROM_BE (*head);
+      auparse->offset = GST_READ_UINT32_BE (head);
       head++;
-      auparse->size            = GUINT32_FROM_BE (*head);
+      /* Do not trust size, could be set to -1 : unknown */
+      auparse->size = GST_READ_UINT32_BE (head);
       head++;
-      auparse->encoding        = GUINT32_FROM_BE (*head);
+      auparse->encoding = GST_READ_UINT32_BE (head);
       head++;
-      auparse->frequency       = GUINT32_FROM_BE (*head);
+      auparse->frequency = GST_READ_UINT32_BE (head);
       head++;
-      auparse->channels        = GUINT32_FROM_BE (*head);
+      auparse->channels = GST_READ_UINT32_BE (head);
       head++;
 
-    /* and of course, someone had to invent a little endian
-     * version.  Used by DEC systems. */
-    } else if (GUINT32_FROM_LE (*head) == 0x0064732E) {
-      auparse->le = 1;
+      /* and of course, someone had to invent a little endian
+       * version.  Used by DEC systems. */
+    } else if (GST_READ_UINT32_LE (head) == 0x0064732E) {       /* other source say it is "dns." */
       head++;
-      auparse->le = 0;
-      auparse->offset          = GUINT32_FROM_LE (*head);
+      auparse->le = 1;
+      auparse->offset = GST_READ_UINT32_LE (head);
       head++;
-      auparse->size            = GUINT32_FROM_LE (*head);
+      /* Do not trust size, could be set to -1 : unknown */
+      auparse->size = GST_READ_UINT32_LE (head);
       head++;
-      auparse->encoding        = GUINT32_FROM_LE (*head);
+      auparse->encoding = GST_READ_UINT32_LE (head);
       head++;
-      auparse->frequency       = GUINT32_FROM_LE (*head);
+      auparse->frequency = GST_READ_UINT32_LE (head);
       head++;
-      auparse->channels        = GUINT32_FROM_LE (*head);
+      auparse->channels = GST_READ_UINT32_LE (head);
       head++;
 
     } else {
-      g_warning ("help, dunno what I'm looking at!\n");
-      gst_buffer_unref(buf);
-      return;
+      GST_ELEMENT_ERROR (auparse, STREAM, WRONG_TYPE, (NULL), (NULL));
+      gst_buffer_unref (buf);
+      g_object_unref (auparse);
+      return GST_FLOW_ERROR;
     }
 
-    g_print ("offset %ld, size %ld, encoding %ld, frequency %ld, channels %ld\n",
-             auparse->offset,auparse->size,auparse->encoding,
-             auparse->frequency,auparse->channels);
-    GST_DEBUG ("offset %ld, size %ld, encoding %ld, frequency %ld, channels %ld",
-             auparse->offset,auparse->size,auparse->encoding,
-             auparse->frequency,auparse->channels);
-    
+    GST_DEBUG
+        ("offset %ld, size %ld, encoding %ld, frequency %ld, channels %ld\n",
+        auparse->offset, auparse->size, auparse->encoding, auparse->frequency,
+        auparse->channels);
+
+/*
+Docs :
+       http://www.opengroup.org/public/pubs/external/auformat.html
+       http://astronomy.swin.edu.au/~pbourke/dataformats/au/
+       Solaris headers : /usr/include/audio/au.h
+       libsndfile : src/au.c
+Samples :
+       http://www.tsp.ece.mcgill.ca/MMSP/Documents/AudioFormats/AU/Samples.html
+*/
+
     switch (auparse->encoding) {
-      case 1:
-       law = 1;
-       depth = 8;
-       sign = FALSE;
-       break;
-      case 2:
-       law = 0;
-       depth = 8;
-       sign = TRUE;
-       break;
-      case 3:
-       law = 0;
-       depth = 16;
-       sign = TRUE;
-       break;
+
+      case 1:                  /* 8-bit ISDN mu-law G.711 */
+        law = 1;
+        depth = 8;
+        break;
+      case 27:                 /* 8-bit ISDN  A-law G.711 */
+        law = 2;
+        depth = 8;
+        break;
+
+      case 2:                  /*  8-bit linear PCM */
+        depth = 8;
+        break;
+      case 3:                  /* 16-bit linear PCM */
+        depth = 16;
+        break;
+      case 4:                  /* 24-bit linear PCM */
+        depth = 24;
+        break;
+      case 5:                  /* 32-bit linear PCM */
+        depth = 32;
+        break;
+
+      case 6:                  /* 32-bit IEEE floating point */
+        ieee = 1;
+        depth = 32;
+        break;
+      case 7:                  /* 64-bit IEEE floating point */
+        ieee = 1;
+        depth = 64;
+        break;
+
+      case 23:                 /* 4-bit CCITT G.721   ADPCM 32kbps -> modplug/libsndfile (compressed 8-bit mu-law) */
+        strcpy (layout, "g721");
+        break;
+      case 24:                 /* 8-bit CCITT G.722   ADPCM        -> rtp */
+        strcpy (layout, "g722");
+        break;
+      case 25:                 /* 3-bit CCITT G.723.3 ADPCM 24kbps -> rtp/xine/modplug/libsndfile */
+        strcpy (layout, "g723_3");
+        break;
+      case 26:                 /* 5-bit CCITT G.723.5 ADPCM 40kbps -> rtp/xine/modplug/libsndfile */
+        strcpy (layout, "g723_5");
+        break;
+
+      case 8:                  /* Fragmented sample data */
+      case 9:                  /* AU_ENCODING_NESTED */
+
+      case 10:                 /* DSP program */
+      case 11:                 /* DSP  8-bit fixed point */
+      case 12:                 /* DSP 16-bit fixed point */
+      case 13:                 /* DSP 24-bit fixed point */
+      case 14:                 /* DSP 32-bit fixed point */
+
+      case 16:                 /* AU_ENCODING_DISPLAY : non-audio display data */
+      case 17:                 /* AU_ENCODING_MULAW_SQUELCH */
+
+      case 18:                 /* 16-bit linear with emphasis */
+      case 19:                 /* 16-bit linear compressed (NeXT) */
+      case 20:                 /* 16-bit linear with emphasis and compression */
+
+      case 21:                 /* Music kit DSP commands */
+      case 22:                 /* Music kit DSP commands samples */
+
       default:
-       g_warning ("help!, dont know how to deal with this format yet\n");
-       return;
+        GST_ELEMENT_ERROR (auparse, STREAM, FORMAT, (NULL), (NULL));
+        gst_buffer_unref (buf);
+        g_object_unref (auparse);
+        return GST_FLOW_ERROR;
     }
 
-    tempcaps = GST_CAPS_NEW ("auparse_src",
-                            "audio/raw",
-                              "format",        GST_PROPS_STRING ("int"),
-                              "endianness",    GST_PROPS_INT (G_BYTE_ORDER),
-                              "rate",          GST_PROPS_INT (auparse->frequency),
-                              "channels",      GST_PROPS_INT (auparse->channels),
-                              "law",           GST_PROPS_INT (law),
-                              "depth",         GST_PROPS_INT (depth),
-                              "width",         GST_PROPS_INT (depth),
-                              "signed",        GST_PROPS_BOOLEAN (sign));
-
-    if (gst_pad_try_set_caps (auparse->srcpad, tempcaps) <= 0) {
-      gst_buffer_unref (buf);
-      gst_element_error (GST_ELEMENT (auparse), "could not set audio caps");
-      return;
+    if (law) {
+      tempcaps =
+          gst_caps_new_simple ((law == 1) ? "audio/x-mulaw" : "audio/x-alaw",
+          "rate", G_TYPE_INT, auparse->frequency,
+          "channels", G_TYPE_INT, auparse->channels, NULL);
+      auparse->sample_size = auparse->channels;
+    } else if (ieee) {
+      tempcaps = gst_caps_new_simple ("audio/x-raw-float",
+          "rate", G_TYPE_INT, auparse->frequency,
+          "channels", G_TYPE_INT, auparse->channels,
+          "endianness", G_TYPE_INT,
+          auparse->le ? G_LITTLE_ENDIAN : G_BIG_ENDIAN,
+          "width", G_TYPE_INT, depth, NULL);
+      auparse->sample_size = auparse->channels * depth / 8;
+    } else if (layout[0]) {
+      tempcaps = gst_caps_new_simple ("audio/x-adpcm",
+          "layout", G_TYPE_STRING, layout, NULL);
+      auparse->sample_size = 0;
+    } else {
+      tempcaps = gst_caps_new_simple ("audio/x-raw-int",
+          "rate", G_TYPE_INT, auparse->frequency,
+          "channels", G_TYPE_INT, auparse->channels,
+          "endianness", G_TYPE_INT,
+          auparse->le ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, "depth", G_TYPE_INT,
+          depth, "width", G_TYPE_INT, depth, "signed", G_TYPE_BOOLEAN, TRUE,
+          NULL);
+      auparse->sample_size = auparse->channels * depth / 8;
     }
 
-    newbuf = gst_buffer_new ();
-    GST_BUFFER_DATA (newbuf) = (gpointer) malloc (size-(auparse->offset));
-    memcpy (GST_BUFFER_DATA (newbuf), data+24, size-(auparse->offset));
-    GST_BUFFER_SIZE (newbuf) = size-(auparse->offset);
+    gst_pad_set_active (auparse->srcpad, TRUE);
+    gst_pad_set_caps (auparse->srcpad, tempcaps);
+
+    event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_DEFAULT,
+        0, GST_CLOCK_TIME_NONE, 0);
+
+    gst_pad_push_event (auparse->srcpad, event);
+
+    subbuf = gst_buffer_create_sub (buf, auparse->offset,
+        size - auparse->offset);
 
     gst_buffer_unref (buf);
 
-    gst_pad_push (auparse->srcpad, newbuf);
-    return;
+    gst_adapter_push (auparse->adapter, subbuf);
+  } else {
+    gst_adapter_push (auparse->adapter, buf);
   }
 
-  gst_pad_push (auparse->srcpad, buf);
-}
+  if (auparse->sample_size) {
+    /* Ensure we push a buffer that's a multiple of the frame size downstream */
+    int avail = gst_adapter_available (auparse->adapter);
 
+    avail -= avail % auparse->sample_size;
 
-static gboolean
-plugin_init (GModule *module, GstPlugin *plugin)
-{
-  GstElementFactory *factory;
-  GstTypeFactory *type;
+    if (avail > 0) {
+      const guint8 *data = gst_adapter_peek (auparse->adapter, avail);
+      GstBuffer *newbuf;
 
-  /* create the plugin structure */
-  /* create an elementfactory for the auparse element and list it */
-  factory = gst_element_factory_new ("auparse", GST_TYPE_AUPARSE,
-                                    &gst_auparse_details);
-  g_return_val_if_fail (factory != NULL, FALSE);
-  gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_SECONDARY);
+      if ((ret =
+              gst_pad_alloc_buffer_and_set_caps (auparse->srcpad,
+                  auparse->buffer_offset, avail, GST_PAD_CAPS (auparse->srcpad),
+                  &newbuf)) == GST_FLOW_OK) {
 
-  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_factory_templ));
-  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_factory_templ));
+        memcpy (GST_BUFFER_DATA (newbuf), data, avail);
+        gst_adapter_flush (auparse->adapter, avail);
 
-  type = gst_type_factory_new (&audefinition);
+        auparse->buffer_offset += avail;
 
-  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
+        ret = gst_pad_push (auparse->srcpad, newbuf);
+      }
+    } else
+      ret = GST_FLOW_OK;
+  } else {
+    /* It's something non-trivial (such as ADPCM), we don't understand it, so
+     * just push downstream and assume this will know what to do with it */
+    ret = gst_pad_push (auparse->srcpad, buf);
+  }
 
-  return TRUE;
+  g_object_unref (auparse);
+
+  return ret;
 }
 
-GstPluginDesc plugin_desc = {
-  GST_VERSION_MAJOR,
-  GST_VERSION_MINOR,
-  "auparse",
-  plugin_init
-};
+static GstStateChangeReturn
+gst_auparse_change_state (GstElement * element, GstStateChange transition)
+{
+  GstAuParse *auparse = GST_AUPARSE (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  if (parent_class->change_state)
+    ret = parent_class->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_adapter_clear (auparse->adapter);
+      auparse->buffer_offset = 0;
+      auparse->offset = 0;
+      auparse->size = 0;
+      auparse->encoding = 0;
+      auparse->frequency = 0;
+      auparse->channels = 0;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_element_register (plugin, "auparse", GST_RANK_SECONDARY,
+          GST_TYPE_AUPARSE)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
 
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "auparse",
+    "parses au streams", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
+    GST_PACKAGE_ORIGIN)