diff options
| -rw-r--r-- | ext/wavpack/Makefile.am | 15 | ||||
| -rw-r--r-- | ext/wavpack/gstwavpack.c | 6 | ||||
| -rw-r--r-- | ext/wavpack/gstwavpackcommon.h | 2 | ||||
| -rw-r--r-- | ext/wavpack/gstwavpackenc.c | 1002 | ||||
| -rw-r--r-- | ext/wavpack/gstwavpackenc.h | 93 | ||||
| -rw-r--r-- | ext/wavpack/md5.c | 271 | ||||
| -rw-r--r-- | ext/wavpack/md5.h | 28 | 
7 files changed, 1411 insertions, 6 deletions
diff --git a/ext/wavpack/Makefile.am b/ext/wavpack/Makefile.am index 3a3fae3b..9220fd78 100644 --- a/ext/wavpack/Makefile.am +++ b/ext/wavpack/Makefile.am @@ -1,13 +1,22 @@  plugin_LTLIBRARIES = libgstwavpack.la -libgstwavpack_la_SOURCES =	gstwavpack.c \ +libgstwavpack_la_SOURCES = \ +				gstwavpack.c \  				gstwavpackcommon.c \  				gstwavpackparse.c \ -				gstwavpackdec.c +				gstwavpackdec.c \ +				gstwavpackenc.c \ +				md5.c  libgstwavpack_la_CFLAGS = $(GST_CFLAGS) $(WAVPACK_CFLAGS)  libgstwavpack_la_LIBADD = $(GST_LIBS) $(WAVPACK_LIBS)  libgstwavpack_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = gstwavpackparse.h gstwavpackdec.h gstwavpackcommon.h +noinst_HEADERS = \ +		gstwavpackparse.h \ +		gstwavpackdec.h \ +		gstwavpackenc.h \ +		gstwavpackcommon.h \ +		md5.h + diff --git a/ext/wavpack/gstwavpack.c b/ext/wavpack/gstwavpack.c index 5a734220..912b583a 100644 --- a/ext/wavpack/gstwavpack.c +++ b/ext/wavpack/gstwavpack.c @@ -25,16 +25,18 @@  #include "gstwavpackparse.h"  #include "gstwavpackdec.h" +#include "gstwavpackenc.h"  static gboolean  plugin_init (GstPlugin * plugin)  {    return (gst_wavpack_parse_plugin_init (plugin) -      && gst_wavpack_dec_plugin_init (plugin)); +      && gst_wavpack_dec_plugin_init (plugin) +      && gst_wavpack_enc_plugin_init (plugin));  }  GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,      GST_VERSION_MINOR,      "wavpack",      "Wavpack lossless/lossy audio format handling", -    plugin_init, VERSION, "LGPL", "gst-wavpack", "http://www.wavpack.com") +    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/wavpack/gstwavpackcommon.h b/ext/wavpack/gstwavpackcommon.h index fbd07cf7..e7fb693f 100644 --- a/ext/wavpack/gstwavpackcommon.h +++ b/ext/wavpack/gstwavpackcommon.h @@ -1,4 +1,4 @@  #include <gst/gst.h>  #include <wavpack/wavpack.h> -gboolean gst_wavpack_read_header (WavpackHeader *header, guint8 *buf); +gboolean gst_wavpack_read_header (WavpackHeader * header, guint8 * buf); diff --git a/ext/wavpack/gstwavpackenc.c b/ext/wavpack/gstwavpackenc.c new file mode 100644 index 00000000..a7d6f748 --- /dev/null +++ b/ext/wavpack/gstwavpackenc.c @@ -0,0 +1,1002 @@ +/* GStreamer Wavpack encoder plugin + * Copyright (c) 2006 Sebastian Dröge <mail@slomosnail.de> + * + * gstwavpackdec.c: Wavpack audio encoder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * TODO: - add multichannel handling. channel_mask is: + *                  front left + *                  front right + *                  center + *                  LFE + *                  back left + *                  back right + *                  front left center + *                  front right center + *                  back left + *                  back center + *                  side left + *                  side right + *                  ... + *        - add 32 bit float mode. CONFIG_FLOAT_DATA + */ + +#include <string.h> +#include <gst/gst.h> +#include <glib/gprintf.h> + +#include <wavpack/wavpack.h> +#include "gstwavpackenc.h" +#include "gstwavpackcommon.h" +#include "md5.h" + +static GstFlowReturn gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps); +static int gst_wavpack_enc_push_block (void *id, void *data, int32_t count); +static gboolean gst_wavpack_enc_sink_event (GstPad * pad, GstEvent * event); +static GstStateChangeReturn gst_wavpack_enc_change_state (GstElement * element, +    GstStateChange transition); +static void gst_wavpack_enc_dispose (GObject * object); +static void gst_wavpack_enc_set_property (GObject * object, guint prop_id, +    const GValue * value, GParamSpec * pspec); +static void gst_wavpack_enc_get_property (GObject * object, guint prop_id, +    GValue * value, GParamSpec * pspec); + +enum +{ +  ARG_0, +  ARG_MODE, +  ARG_BITRATE, +  ARG_CORRECTION_MODE, +  ARG_MD5, +  ARG_EXTRA_PROCESSING, +  ARG_JOINT_STEREO_MODE, +}; + +GST_DEBUG_CATEGORY_STATIC (gst_wavpack_enc_debug); +#define GST_CAT_DEFAULT gst_wavpack_enc_debug + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", +    GST_PAD_SINK, +    GST_PAD_ALWAYS, +    GST_STATIC_CAPS ("audio/x-raw-int, " +        "width = (int) 32, " +        "depth = (int) 32, " +        "endianness = (int) LITTLE_ENDIAN, " +        "channels = (int) [ 1, 2 ], " +        "rate = (int) [ 6000, 192000 ]," "signed = (boolean) TRUE;" +        "audio/x-raw-int, " +        "width = (int) 24, " +        "depth = (int) 24, " +        "endianness = (int) LITTLE_ENDIAN, " +        "channels = (int) [ 1, 2 ], " +        "rate = (int) [ 6000, 192000 ]," "signed = (boolean) TRUE;" +        "audio/x-raw-int, " +        "width = (int) 16, " +        "depth = (int) 16, " +        "endianness = (int) LITTLE_ENDIAN, " +        "channels = (int) [ 1, 2 ], " +        "rate = (int) [ 6000, 192000 ]," "signed = (boolean) TRUE;" +        "audio/x-raw-int, " +        "width = (int) 8, " +        "depth = (int) 8, " +        "endianness = (int) LITTLE_ENDIAN, " +        "channels = (int) [ 1, 2 ], " +        "rate = (int) [ 6000, 192000 ]," "signed = (boolean) TRUE") +    ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", +    GST_PAD_SRC, +    GST_PAD_ALWAYS, +    GST_STATIC_CAPS ("audio/x-wavpack, " +        "width = (int) { 8, 16, 24, 32 }, " +        "channels = (int) [ 1, 2 ], " +        "rate = (int) [ 6000, 192000 ], " "framed = (boolean) FALSE") +    ); + +static GstStaticPadTemplate wvcsrc_factory = GST_STATIC_PAD_TEMPLATE ("wvcsrc", +    GST_PAD_SRC, +    GST_PAD_SOMETIMES, +    GST_STATIC_CAPS ("audio/x-wavpack-correction, " "framed = (boolean) FALSE") +    ); + +#define DEFAULT_MODE 1 +#define GST_TYPE_WAVPACK_ENC_MODE (gst_wavpack_enc_mode_get_type ()) +static GType +gst_wavpack_enc_mode_get_type (void) +{ +  static GType qtype = 0; + +  if (qtype == 0) { +    static const GEnumValue values[] = { +      {0, "Fast Compression", "0"}, +      {1, "Default", "1"}, +      {2, "High Compression", "2"}, +      {0, NULL, NULL} +    }; + +    qtype = g_enum_register_static ("GstWavpackEncMode", values); +  } +  return qtype; +} + +#define DEFAULT_CORRECTION_MODE 0 +#define GST_TYPE_WAVPACK_ENC_CORRECTION_MODE (gst_wavpack_enc_correction_mode_get_type ()) +static GType +gst_wavpack_enc_correction_mode_get_type (void) +{ +  static GType qtype = 0; + +  if (qtype == 0) { +    static const GEnumValue values[] = { +      {0, "Create no correction file (default)", "0"}, +      {1, "Create correction file", "1"}, +      {2, "Create optimized correction file", "2"}, +      {0, NULL, NULL} +    }; + +    qtype = g_enum_register_static ("GstWavpackEncCorrectionMode", values); +  } +  return qtype; +} + +#define DEFAULT_JS_MODE 0 +#define GST_TYPE_WAVPACK_ENC_JOINT_STEREO_MODE (gst_wavpack_enc_joint_stereo_mode_get_type ()) +static GType +gst_wavpack_enc_joint_stereo_mode_get_type (void) +{ +  static GType qtype = 0; + +  if (qtype == 0) { +    static const GEnumValue values[] = { +      {0, "auto (default)", "0"}, +      {1, "left/right", "1"}, +      {2, "mid/side", "2"}, +      {0, NULL, NULL} +    }; + +    qtype = g_enum_register_static ("GstWavpackEncJSMode", values); +  } +  return qtype; +} + +GST_BOILERPLATE (GstWavpackEnc, gst_wavpack_enc, GstElement, GST_TYPE_ELEMENT); + +static void +gst_wavpack_enc_base_init (gpointer klass) +{ +  static GstElementDetails element_details = { +    "Wavpack audio encoder", +    "Codec/Encoder/Audio", +    "Encodes audio with the Wavpack lossless/lossy audio codec", +    "Sebastian Dröge <mail@slomosnail.de>" +  }; +  GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + +  /* add pad templates */ +  gst_element_class_add_pad_template (element_class, +      gst_static_pad_template_get (&sink_factory)); +  gst_element_class_add_pad_template (element_class, +      gst_static_pad_template_get (&src_factory)); +  gst_element_class_add_pad_template (element_class, +      gst_static_pad_template_get (&wvcsrc_factory)); + +  /* set element details */ +  gst_element_class_set_details (element_class, &element_details); +} + + +static void +gst_wavpack_enc_class_init (GstWavpackEncClass * klass) +{ +  GObjectClass *gobject_class = (GObjectClass *) klass; +  GstElementClass *gstelement_class = (GstElementClass *) klass; + +  parent_class = g_type_class_peek_parent (klass); + +  /* set state change handler */ +  gstelement_class->change_state = +      GST_DEBUG_FUNCPTR (gst_wavpack_enc_change_state); +  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_wavpack_enc_dispose); + +  /* set property handlers */ +  gobject_class->set_property = +      GST_DEBUG_FUNCPTR (gst_wavpack_enc_set_property); +  gobject_class->get_property = +      GST_DEBUG_FUNCPTR (gst_wavpack_enc_get_property); + +  /* install all properties */ +  g_object_class_install_property (gobject_class, ARG_MODE, +      g_param_spec_enum ("mode", "Encoding mode", +          "Speed versus compression tradeoff.", +          GST_TYPE_WAVPACK_ENC_MODE, DEFAULT_MODE, G_PARAM_READWRITE)); +  g_object_class_install_property (gobject_class, ARG_BITRATE, +      g_param_spec_double ("bitrate", "Bitrate", +          "Try to encode with this average bitrate. " +          "This enables lossy encoding! (0 .. 2.0 == disabled, 2.0 .. 23.9 == bits/sample, 24.0 .. 9600 == kbit/second)", +          0.0, 9600.0, 0.0, G_PARAM_READWRITE)); +  g_object_class_install_property (gobject_class, ARG_CORRECTION_MODE, +      g_param_spec_enum ("correction_mode", "Correction file mode", +          "Use this mode for correction file creation. Only works in lossy mode!", +          GST_TYPE_WAVPACK_ENC_CORRECTION_MODE, DEFAULT_CORRECTION_MODE, +          G_PARAM_READWRITE)); +  g_object_class_install_property (gobject_class, ARG_MD5, +      g_param_spec_boolean ("md5", "MD5", +          "Store MD5 hash of raw samples within the file.", FALSE, +          G_PARAM_READWRITE)); +  g_object_class_install_property (gobject_class, ARG_EXTRA_PROCESSING, +      g_param_spec_boolean ("extra_processing", "Extra processing", +          "Extra encode processing.", FALSE, G_PARAM_READWRITE)); +  g_object_class_install_property (gobject_class, ARG_JOINT_STEREO_MODE, +      g_param_spec_enum ("joint_stereo_mode", "Joint-Stereo mode", +          "Use this joint-stereo mode.", GST_TYPE_WAVPACK_ENC_JOINT_STEREO_MODE, +          DEFAULT_JS_MODE, G_PARAM_READWRITE)); +} + +static void +gst_wavpack_enc_init (GstWavpackEnc * wavpack_enc, GstWavpackEncClass * gclass) +{ +  GstElementClass *klass = GST_ELEMENT_GET_CLASS (wavpack_enc); + +  /* setup sink pad, add handlers */ +  wavpack_enc->sinkpad = +      gst_pad_new_from_template (gst_element_class_get_pad_template (klass, +          "sink"), "sink"); +  gst_pad_set_setcaps_function (wavpack_enc->sinkpad, +      GST_DEBUG_FUNCPTR (gst_wavpack_enc_sink_set_caps)); +  gst_pad_set_chain_function (wavpack_enc->sinkpad, +      GST_DEBUG_FUNCPTR (gst_wavpack_enc_chain)); +  gst_pad_set_event_function (wavpack_enc->sinkpad, +      GST_DEBUG_FUNCPTR (gst_wavpack_enc_sink_event)); +  gst_element_add_pad (GST_ELEMENT (wavpack_enc), +      GST_DEBUG_FUNCPTR (wavpack_enc->sinkpad)); + +  /* setup src pad */ +  wavpack_enc->srcpad = +      gst_pad_new_from_template (gst_element_class_get_pad_template (klass, +          "src"), "src"); +  gst_element_add_pad (GST_ELEMENT (wavpack_enc), +      GST_DEBUG_FUNCPTR (wavpack_enc->srcpad)); + +  /* initialize object attributes */ +  wavpack_enc->wp_config = NULL; +  wavpack_enc->wp_context = NULL; +  wavpack_enc->first_block = NULL; +  wavpack_enc->first_block_size = 0; +  wavpack_enc->md5_context = NULL; +  wavpack_enc->samplerate = 0; +  wavpack_enc->width = 0; +  wavpack_enc->channels = 0; + +  wavpack_enc->wv_id = (write_id *) g_malloc0 (sizeof (write_id)); +  wavpack_enc->wv_id->correction = FALSE; +  wavpack_enc->wv_id->wavpack_enc = wavpack_enc; +  wavpack_enc->wvc_id = (write_id *) g_malloc0 (sizeof (write_id)); +  wavpack_enc->wvc_id->correction = TRUE; +  wavpack_enc->wvc_id->wavpack_enc = wavpack_enc; + +  /* set default values of params */ +  wavpack_enc->mode = 1; +  wavpack_enc->bitrate = 0.0; +  wavpack_enc->correction_mode = 0; +  wavpack_enc->md5 = FALSE; +  wavpack_enc->extra_processing = FALSE; +  wavpack_enc->joint_stereo_mode = 0; +} + +static void +gst_wavpack_enc_dispose (GObject * object) +{ +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (object); + +  /* free the blockout helpers */ +  g_free (wavpack_enc->wv_id); +  g_free (wavpack_enc->wvc_id); + +  G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gboolean +gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps) +{ +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad)); +  GstStructure *structure = gst_caps_get_structure (caps, 0); +  int depth = 0; + +  /* check caps and put relevant parts into our object attributes */ +  if ((!gst_structure_get_int (structure, "channels", &wavpack_enc->channels)) +      || (!gst_structure_get_int (structure, "rate", &wavpack_enc->samplerate)) +      || (!gst_structure_get_int (structure, "width", &wavpack_enc->width)) +      || (!(gst_structure_get_int (structure, "depth", &depth)) +          || depth != wavpack_enc->width)) { +    GST_ELEMENT_ERROR (wavpack_enc, LIBRARY, INIT, (NULL), +        ("got invalid caps: %", GST_PTR_FORMAT, caps)); +    gst_object_unref (wavpack_enc); +    return FALSE; +  } + +  /* set fixed src pad caps now that we know what we will get */ +  caps = gst_caps_new_simple ("audio/x-wavpack", +      "channels", G_TYPE_INT, wavpack_enc->channels, +      "rate", G_TYPE_INT, wavpack_enc->samplerate, +      "width", G_TYPE_INT, wavpack_enc->width, +      "framed", G_TYPE_BOOLEAN, TRUE, NULL); + +  if (!gst_pad_set_caps (wavpack_enc->srcpad, caps)) { +    GST_ELEMENT_ERROR (wavpack_enc, LIBRARY, INIT, (NULL), +        ("setting caps failed: %", GST_PTR_FORMAT, caps)); +    gst_caps_unref (caps); +    gst_object_unref (wavpack_enc); +    return FALSE; +  } +  gst_pad_use_fixed_caps (wavpack_enc->srcpad); + +  gst_caps_unref (caps); +  gst_object_unref (wavpack_enc); +  return TRUE; +} + +static void +gst_wavpack_enc_set_wp_config (GstWavpackEnc * wavpack_enc) +{ +  wavpack_enc->wp_config = (WavpackConfig *) g_malloc0 (sizeof (WavpackConfig)); +  /* set general stream informations in the WavpackConfig */ +  wavpack_enc->wp_config->bytes_per_sample = (wavpack_enc->width + 7) >> 3; +  wavpack_enc->wp_config->bits_per_sample = wavpack_enc->width; +  wavpack_enc->wp_config->num_channels = wavpack_enc->channels; + +  /* TODO: handle more than 2 channels correctly! */ +  if (wavpack_enc->channels == 1) { +    wavpack_enc->wp_config->channel_mask = 0x4; +  } else if (wavpack_enc->channels == 2) { +    wavpack_enc->wp_config->channel_mask = 0x2 | 0x1; +  } +  wavpack_enc->wp_config->sample_rate = wavpack_enc->samplerate; + +  /* +   * Set parameters in WavpackConfig +   */ + +  /* Encoding mode */ +  switch (wavpack_enc->mode) { +    case 0: +      wavpack_enc->wp_config->flags |= CONFIG_FAST_FLAG; +      break; +    case 1:                    /* default */ +      break; +    case 2: +      wavpack_enc->wp_config->flags |= CONFIG_HIGH_FLAG; +      break; +  } + +  /* Bitrate, enables lossy mode */ +  if (wavpack_enc->bitrate > 2.0) { +    wavpack_enc->wp_config->flags |= CONFIG_HYBRID_FLAG; +    wavpack_enc->wp_config->bitrate = wavpack_enc->bitrate; +    if (wavpack_enc->bitrate >= 24.0) +      wavpack_enc->wp_config->flags |= CONFIG_BITRATE_KBPS; +  } + +  /* Correction Mode, only in lossy mode */ +  if (wavpack_enc->wp_config->flags & CONFIG_HYBRID_FLAG) { +    if (wavpack_enc->correction_mode > 0) { +      wavpack_enc->wvcsrcpad = +          gst_pad_new_from_template (gst_element_class_get_pad_template +          (GST_ELEMENT_GET_CLASS (wavpack_enc), "wvcsrc"), "wvcsrc"); + +      /* try to add correction src pad, don't set correction mode on failure */ +      if (gst_element_add_pad (GST_ELEMENT (wavpack_enc), +              GST_DEBUG_FUNCPTR (wavpack_enc->wvcsrcpad))) { +        GstCaps *caps = gst_caps_new_simple ("audio/x-wavpack-correction", +            "framed", G_TYPE_BOOLEAN, FALSE, NULL); + +        gst_element_no_more_pads (GST_ELEMENT (wavpack_enc)); + +        if (!gst_pad_set_caps (wavpack_enc->wvcsrcpad, caps)) { +          wavpack_enc->correction_mode = 0; +          GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, INIT, (NULL), +              ("setting correction caps failed: %", GST_PTR_FORMAT, caps)); +        } else { +          gst_pad_use_fixed_caps (wavpack_enc->wvcsrcpad); +          wavpack_enc->wp_config->flags |= CONFIG_CREATE_WVC; +          if (wavpack_enc->correction_mode == 2) { +            wavpack_enc->wp_config->flags |= CONFIG_OPTIMIZE_WVC; +          } +        } +        gst_caps_unref (caps); +      } else { +        wavpack_enc->correction_mode = 0; +        GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, INIT, (NULL), +            ("add correction pad failed. no correction file will be created.")); +      } +    } +  } else { +    if (wavpack_enc->correction_mode > 0) { +      wavpack_enc->correction_mode = 0; +      GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, SETTINGS, (NULL), +          ("settings correction mode only has effect if a bitrate is provided.")); +    } +  } + +  /* MD5, setup MD5 context */ +  if ((wavpack_enc->md5) && !(wavpack_enc->md5_context)) { +    wavpack_enc->wp_config->flags |= CONFIG_MD5_CHECKSUM; +    wavpack_enc->md5_context = (MD5_CTX *) g_malloc0 (sizeof (MD5_CTX)); +    MD5Init (wavpack_enc->md5_context); +  } + +  /* Extra encode processing */ +  if (wavpack_enc->extra_processing) { +    wavpack_enc->wp_config->flags |= CONFIG_EXTRA_MODE; +  } + +  /* Joint stereo mode */ +  switch (wavpack_enc->joint_stereo_mode) { +    case 0:                    /* default */ +      break; +    case 1: +      wavpack_enc->wp_config->flags |= CONFIG_JOINT_OVERRIDE; +      wavpack_enc->wp_config->flags &= ~CONFIG_JOINT_STEREO; +      break; +    case 2: +      wavpack_enc->wp_config->flags |= +          (CONFIG_JOINT_OVERRIDE | CONFIG_JOINT_STEREO); +      break; +  } +} + +static int32_t * +gst_wavpack_enc_format_samples (const uchar * src_data, uint32_t sample_count, +    guint width) +{ +  int32_t *data = (int32_t *) g_malloc0 (sizeof (int32_t) * sample_count); + +  /* put all samples into an int32_t*, no matter what +   * width we have and convert them from little endian +   * to host byte order */ + +  switch (width) { +      int i; + +    case 8: +      for (i = 0; i < sample_count; i++) +        data[i] = (int32_t) (int8_t) src_data[i]; +      break; +    case 16: +      for (i = 0; i < sample_count; i++) +        data[i] = (int32_t) src_data[2 * i] +            | ((int32_t) (int8_t) src_data[2 * i + 1] << 8); +      break; +    case 24: +      for (i = 0; i < sample_count; i++) +        data[i] = (int32_t) src_data[3 * i] +            | ((int32_t) src_data[3 * i + 1] << 8) +            | ((int32_t) (int8_t) src_data[3 * i + 2] << 16); +      break; +    case 32: +      for (i = 0; i < sample_count; i++) +        data[i] = (int32_t) src_data[4 * i] +            | ((int32_t) src_data[4 * i + 1] << 8) +            | ((int32_t) src_data[4 * i + 2] << 16) +            | ((int32_t) (int8_t) src_data[4 * i + 3] << 24); +      break; +  } + +  return data; +} + +static int +gst_wavpack_enc_push_block (void *id, void *data, int32_t count) +{ +  write_id *wid = (write_id *) id; +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (wid->wavpack_enc); +  GstFlowReturn ret; +  GstBuffer *buffer; +  guchar *block = (guchar *) data; + +  if (wid->correction == FALSE) { +    /* we got something that should be pushed to the (non-correction) src pad */ + +    /* try to allocate a buffer, compatible with the pad, fail otherwise */ +    ret = gst_pad_alloc_buffer_and_set_caps (wavpack_enc->srcpad, +        GST_BUFFER_OFFSET_NONE, count, GST_PAD_CAPS (wavpack_enc->srcpad), +        &buffer); +    if (ret != GST_FLOW_OK) { +      wavpack_enc->srcpad_last_return = ret; +      GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, ENCODE, (NULL), +          ("Dropped one block (%d bytes) of encoded data while allocating buffer! Reason: '%s'\n", +              count, gst_flow_get_name (ret))); +      return FALSE; +    } + +    g_memmove (GST_BUFFER_DATA (buffer), block, count); + +    if ((block[0] == 'w') && (block[1] == 'v') && (block[2] == 'p') +        && (block[3] == 'k')) { +      /* if it's a Wavpack block set buffer timestamp and duration, etc */ +      WavpackHeader wph; + +      GST_DEBUG ("got %d bytes of encoded wavpack data", count); +      gst_wavpack_read_header (&wph, block); + +      /* if it's the first wavpack block save it for later reference +       * i.e. sample count correction and send a NEW_SEGMENT event */ +      if (wph.block_index == 0) { +        GstEvent *event = gst_event_new_new_segment (FALSE, +            1.0, GST_FORMAT_BYTES, 0, GST_BUFFER_OFFSET_NONE, 0); + +        gst_pad_push_event (wavpack_enc->srcpad, event); +        wavpack_enc->first_block = g_malloc0 (count); +        g_memmove (wavpack_enc->first_block, block, count); +        wavpack_enc->first_block_size = count; +      } + +      /* set buffer timestamp, duration, offset, offset_end from +       * the wavpack header */ +      GST_BUFFER_TIMESTAMP (buffer) = +          gst_util_uint64_scale_int (GST_SECOND, wph.block_index, +          wavpack_enc->samplerate); +      GST_BUFFER_DURATION (buffer) = +          gst_util_uint64_scale_int (GST_SECOND, wph.block_samples, +          wavpack_enc->samplerate); +      GST_BUFFER_OFFSET (buffer) = wph.block_index; +      GST_BUFFER_OFFSET_END (buffer) = wph.block_index + wph.block_samples; +    } else { +      /* if it's something else set no timestamp and duration on the buffer */ +      GST_DEBUG ("got %d bytes of unknown data", count); + +      GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; +      GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; +    } + +    /* push the buffer and forward errors */ +    ret = gst_pad_push (wavpack_enc->srcpad, buffer); +    wavpack_enc->srcpad_last_return = ret; +    if (ret == GST_FLOW_OK) { +      return TRUE; +    } else { +      GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, ENCODE, (NULL), +          ("Dropped one block (%d bytes) of encoded data while pushing! Reason: '%s'\n", +              count, gst_flow_get_name (ret))); +      return FALSE; +    } +  } else if (wid->correction == TRUE) { +    /* we got something that should be pushed to the correction src pad */ + +    /* is the correction pad linked? */ +    if (!gst_pad_is_linked (wavpack_enc->wvcsrcpad)) { +      GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, ENCODE, (NULL), +          ("Dropped one block (%d bytes) of encoded correction data because of unlinked pad", +              count)); +      wavpack_enc->wvcsrcpad_last_return = GST_FLOW_NOT_LINKED; +      return FALSE; +    } + +    /* try to allocate a buffer, compatible with the pad, fail otherwise */ +    ret = gst_pad_alloc_buffer_and_set_caps (wavpack_enc->wvcsrcpad, +        GST_BUFFER_OFFSET_NONE, count, +        GST_PAD_CAPS (wavpack_enc->wvcsrcpad), &buffer); +    if (ret != GST_FLOW_OK) { +      wavpack_enc->wvcsrcpad_last_return = ret; +      GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, ENCODE, (NULL), +          ("Dropped one block (%d bytes) of encoded correction data while allocating buffer! Reason: '%s'\n", +              count, gst_flow_get_name (ret))); +      return FALSE; +    } + +    g_memmove (GST_BUFFER_DATA (buffer), block, count); + +    if ((block[0] == 'w') && (block[1] == 'v') && (block[2] == 'p') +        && (block[3] == 'k')) { +      /* if it's a Wavpack block set buffer timestamp and duration, etc */ +      WavpackHeader wph; + +      GST_DEBUG ("got %d bytes of encoded wavpack correction data", count); +      gst_wavpack_read_header (&wph, block); + +      /* if it's the first wavpack block send a NEW_SEGMENT +       * event */ +      if (wph.block_index == 0) { +        GstEvent *event = gst_event_new_new_segment (FALSE, +            1.0, GST_FORMAT_BYTES, 0, GST_BUFFER_OFFSET_NONE, 0); + +        gst_pad_push_event (wavpack_enc->wvcsrcpad, event); +      } + +      /* set buffer timestamp, duration, offset, offset_end from +       * the wavpack header */ +      GST_BUFFER_TIMESTAMP (buffer) = +          gst_util_uint64_scale_int (GST_SECOND, wph.block_index, +          wavpack_enc->samplerate); +      GST_BUFFER_DURATION (buffer) = +          gst_util_uint64_scale_int (GST_SECOND, wph.block_samples, +          wavpack_enc->samplerate); +      GST_BUFFER_OFFSET (buffer) = wph.block_index; +      GST_BUFFER_OFFSET_END (buffer) = wph.block_index + wph.block_samples; +    } else { +      /* if it's something else set no timestamp and duration on the buffer */ +      GST_DEBUG ("got %d bytes of unknown data", count); + +      GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; +      GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; +    } + +    /* push the buffer and forward errors */ +    ret = gst_pad_push (wavpack_enc->wvcsrcpad, buffer); +    wavpack_enc->wvcsrcpad_last_return = ret; +    if (ret == GST_FLOW_OK) +      return TRUE; +    else { +      GST_ELEMENT_WARNING (wavpack_enc, LIBRARY, ENCODE, (NULL), +          ("Dropped one block (%d bytes) of encoded correction data while pushing! Reason: '%s'\n", +              count, gst_flow_get_name (ret))); +      return FALSE; +    } +  } else { +    /* (correction != TRUE) && (correction != FALSE), wtf? ignore this */ +    g_assert_not_reached (); +    return TRUE; +  } +} + +static GstFlowReturn +gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buf) +{ +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad)); +  uint32_t sample_count = +      GST_BUFFER_SIZE (buf) / ((wavpack_enc->width + 7) >> 3); +  int32_t *data; +  GstFlowReturn ret; + +  /* reset the last returns to GST_FLOW_OK. This is only set to something else +   * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block() +   * so not valid anymore */ +  wavpack_enc->srcpad_last_return = wavpack_enc->wvcsrcpad_last_return = +      GST_FLOW_OK; + +  GST_DEBUG ("got %u raw samples", sample_count); + +  /* check if we already have a valid WavpackContext, otherwise make one */ +  if (!wavpack_enc->wp_context) { +    gint64 duration; +    GstFormat fmt = GST_FORMAT_DEFAULT; + +    /* create raw context */ +    wavpack_enc->wp_context = +        WavpackOpenFileOutput (gst_wavpack_enc_push_block, wavpack_enc->wv_id, +        (wavpack_enc->correction_mode > 0) ? wavpack_enc->wvc_id : NULL); +    if (!wavpack_enc->wp_context) { +      GST_ELEMENT_ERROR (wavpack_enc, LIBRARY, INIT, (NULL), +          ("error creating Wavpack context")); +      gst_object_unref (wavpack_enc); +      gst_buffer_unref (buf); +      return GST_FLOW_ERROR; +    } + +    /* set the WavpackConfig according to our parameters */ +    gst_wavpack_enc_set_wp_config (wavpack_enc); + +    /* try to get the duration (or an estimate) in samples from upstream */ +    if (gst_pad_query_peer_duration (pad, &fmt, &duration)) { +      switch (fmt) { +        case GST_FORMAT_DEFAULT: +          break; +        case GST_FORMAT_TIME: +          duration = +              gst_util_uint64_scale (wavpack_enc->samplerate, +              duration, GST_SECOND); +          break; +        default: +          duration = 0; +          break; +      } +    } else { +      duration = 0; +    } + +    /* Wavpack doesn't support more than 2^32 samples unfortunately */ +    if (duration > G_GINT64_CONSTANT (1) << 32) { +      GST_ELEMENT_ERROR (wavpack_enc, LIBRARY, SETTINGS, (NULL), +          ("more than 2^32 samples are not supported")); +      WavpackCloseFile (wavpack_enc->wp_context); +      gst_object_unref (wavpack_enc); +      gst_buffer_unref (buf); +      return GST_FLOW_ERROR; +    } + +    /* set the configuration to the context now that we know everything +     * and initialize the encoder */ +    if (!WavpackSetConfiguration (wavpack_enc->wp_context, +            wavpack_enc->wp_config, (uint32_t) duration) +        || !WavpackPackInit (wavpack_enc->wp_context)) { +      GST_ELEMENT_ERROR (wavpack_enc, LIBRARY, SETTINGS, (NULL), +          ("error setting up wavpack encoding context")); +      WavpackCloseFile (wavpack_enc->wp_context); +      gst_object_unref (wavpack_enc); +      gst_buffer_unref (buf); +      return GST_FLOW_ERROR; +    } +    GST_DEBUG ("setup of encoding context successfull"); +  } + +  /* if we want to append the MD5 sum to the stream update it here +   * with the current raw samples */ +  if (wavpack_enc->md5) { +    MD5Update (wavpack_enc->md5_context, GST_BUFFER_DATA (buf), +        GST_BUFFER_SIZE (buf)); +  } + +  /* put all samples into an int32_t*, no matter what +   * width we have and convert them from little endian +   * to host byte order */ +  data = +      gst_wavpack_enc_format_samples (GST_BUFFER_DATA (buf), sample_count, +      wavpack_enc->width); + +  gst_buffer_unref (buf); + +  /* encode and handle return values from encoding */ +  if (WavpackPackSamples (wavpack_enc->wp_context, data, +          sample_count / wavpack_enc->channels)) { +    GST_DEBUG ("encoding samples successfull"); +    ret = GST_FLOW_OK; +  } else { +    if ((wavpack_enc->srcpad_last_return == GST_FLOW_RESEND) || +        (wavpack_enc->wvcsrcpad_last_return == GST_FLOW_RESEND)) { +      ret = GST_FLOW_RESEND; +    } else if ((wavpack_enc->srcpad_last_return == GST_FLOW_OK) || +        (wavpack_enc->wvcsrcpad_last_return == GST_FLOW_OK)) { +      ret = GST_FLOW_OK; +    } else if ((wavpack_enc->srcpad_last_return == GST_FLOW_NOT_LINKED) && +        (wavpack_enc->wvcsrcpad_last_return == GST_FLOW_NOT_LINKED)) { +      ret = GST_FLOW_NOT_LINKED; +    } else if ((wavpack_enc->srcpad_last_return == GST_FLOW_WRONG_STATE) && +        (wavpack_enc->wvcsrcpad_last_return == GST_FLOW_WRONG_STATE)) { +      ret = GST_FLOW_WRONG_STATE; +    } else { +      GST_ELEMENT_ERROR (wavpack_enc, LIBRARY, ENCODE, (NULL), +          ("encoding samples failed")); +      ret = GST_FLOW_ERROR; +    } +  } + +  g_free (data); +  gst_object_unref (wavpack_enc); +  return ret; +} + +static void +gst_wavpack_enc_rewrite_first_block (GstWavpackEnc * wavpack_enc) +{ +  GstEvent *event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, +      0, GST_BUFFER_OFFSET_NONE, 0); +  gboolean ret; + +  g_return_if_fail (wavpack_enc); +  g_return_if_fail (wavpack_enc->first_block); + +  /* update the sample count in the first block */ +  WavpackUpdateNumSamples (wavpack_enc->wp_context, wavpack_enc->first_block); + +  /* try to seek to the beginning of the output */ +  ret = gst_pad_push_event (wavpack_enc->srcpad, event); +  if (ret) { +    /* try to rewrite the first block */ +    ret = gst_wavpack_enc_push_block (wavpack_enc->wv_id, +        wavpack_enc->first_block, wavpack_enc->first_block_size); +    if (ret) { +      GST_DEBUG ("rewriting of first block succeeded!"); +    } else { +      GST_ELEMENT_WARNING (wavpack_enc, RESOURCE, WRITE, (NULL), +          ("rewriting of first block failed while pushing!")); +    } +  } else { +    GST_ELEMENT_WARNING (wavpack_enc, RESOURCE, SEEK, (NULL), +        ("rewriting of first block failed. Seeking to first block failed!")); +  } +} + +static gboolean +gst_wavpack_enc_sink_event (GstPad * pad, GstEvent * event) +{ +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad)); +  gboolean ret = TRUE; + +  GST_DEBUG ("Received %s event on sinkpad", GST_EVENT_TYPE_NAME (event)); + +  switch (GST_EVENT_TYPE (event)) { +    case GST_EVENT_EOS: +      /* Encode all remaining samples and flush them to the src pads */ +      WavpackFlushSamples (wavpack_enc->wp_context); + +      /* write the MD5 sum if we have to write one */ +      if ((wavpack_enc->md5) && (wavpack_enc->md5_context)) { +        guchar md5_digest[16]; + +        MD5Final (md5_digest, wavpack_enc->md5_context); +        WavpackStoreMD5Sum (wavpack_enc->wp_context, md5_digest); +      } + +      /* Try to rewrite the first frame with the correct sample number if we +       * had a wrong one at the start of encoding */ +      if ((wavpack_enc->first_block) +          && (WavpackGetNumSamples (wavpack_enc->wp_context) != +              WavpackGetSampleIndex (wavpack_enc->wp_context))) +        gst_wavpack_enc_rewrite_first_block (wavpack_enc); + +      /* close the context if not already happened */ +      if (wavpack_enc->wp_context) { +        WavpackCloseFile (wavpack_enc->wp_context); +        wavpack_enc->wp_context = NULL; +      } + +      ret = gst_pad_event_default (pad, event); +      break; +    case GST_EVENT_NEWSEGMENT: +      if (wavpack_enc->wp_context) { +        GST_ELEMENT_WARNING (wavpack_enc, RESOURCE, SEEK, (NULL), +            ("got NEWSEGMENT after encoding already started")); +      } +      /* drop NEWSEGMENT events, we create our own when pushing +       * the first buffer to the pads */ +      gst_event_unref (event); +      ret = TRUE; +      break; +    default: +      ret = gst_pad_event_default (pad, event); +      break; +  } + +  gst_object_unref (wavpack_enc); +  return ret; +} + +static GstStateChangeReturn +gst_wavpack_enc_change_state (GstElement * element, GstStateChange transition) +{ +  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (element); + +  switch (transition) { +    case GST_STATE_CHANGE_NULL_TO_READY: +      /* set the last returned GstFlowReturns of the two pads to GST_FLOW_OK +       * as they're only set to something else in WavpackPackSamples() or more +       * specific gst_wavpack_enc_push_block() and nothing happened there yet */ +      wavpack_enc->srcpad_last_return = wavpack_enc->wvcsrcpad_last_return = +          GST_FLOW_OK; +    case GST_STATE_CHANGE_READY_TO_PAUSED: +    case GST_STATE_CHANGE_PAUSED_TO_PLAYING: +    default: +      break; +  } + +  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + +  switch (transition) { +    case GST_STATE_CHANGE_PLAYING_TO_PAUSED: +      break; +    case GST_STATE_CHANGE_PAUSED_TO_READY: +      /* close and free everything stream related */ +      if (wavpack_enc->wp_context) { +        WavpackCloseFile (wavpack_enc->wp_context); +        wavpack_enc->wp_context = NULL; +      } +      if (wavpack_enc->wp_config) { +        g_free (wavpack_enc->wp_config); +        wavpack_enc->wp_config = NULL; +      } +      if (wavpack_enc->first_block) { +        g_free (wavpack_enc->first_block); +        wavpack_enc->first_block = NULL; +        wavpack_enc->first_block_size = 0; +      } +      if (wavpack_enc->md5_context) { +        g_free (wavpack_enc->md5_context); +        wavpack_enc->md5_context = NULL; +      } + +      /* reset the last returns to GST_FLOW_OK. This is only set to something else +       * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block() +       * so not valid anymore */ +      wavpack_enc->srcpad_last_return = wavpack_enc->wvcsrcpad_last_return = +          GST_FLOW_OK; +      break; +    case GST_STATE_CHANGE_READY_TO_NULL: +      break; +    default: +      break; +  } + +  return ret; +} + +static void +gst_wavpack_enc_set_property (GObject * object, guint prop_id, +    const GValue * value, GParamSpec * pspec) +{ +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (object); + +  switch (prop_id) { +    case ARG_MODE: +      wavpack_enc->mode = g_value_get_enum (value); +      break; +    case ARG_BITRATE: +      wavpack_enc->bitrate = g_value_get_double (value); +      break; +    case ARG_CORRECTION_MODE: +      wavpack_enc->correction_mode = g_value_get_enum (value); +      break; +    case ARG_MD5: +      wavpack_enc->md5 = g_value_get_boolean (value); +      break; +    case ARG_EXTRA_PROCESSING: +      wavpack_enc->extra_processing = g_value_get_boolean (value); +      break; +    case ARG_JOINT_STEREO_MODE: +      wavpack_enc->joint_stereo_mode = g_value_get_enum (value); +      break; +    default: +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +      break; +  } +} + +static void +gst_wavpack_enc_get_property (GObject * object, guint prop_id, GValue * value, +    GParamSpec * pspec) +{ +  GstWavpackEnc *wavpack_enc = GST_WAVPACK_ENC (object); + +  switch (prop_id) { +    case ARG_MODE: +      g_value_set_enum (value, wavpack_enc->mode); +      break; +    case ARG_BITRATE: +      g_value_set_double (value, wavpack_enc->bitrate); +      break; +    case ARG_CORRECTION_MODE: +      g_value_set_enum (value, wavpack_enc->correction_mode); +      break; +    case ARG_MD5: +      g_value_set_boolean (value, wavpack_enc->md5); +      break; +    case ARG_EXTRA_PROCESSING: +      g_value_set_boolean (value, wavpack_enc->extra_processing); +      break; +    case ARG_JOINT_STEREO_MODE: +      g_value_set_enum (value, wavpack_enc->joint_stereo_mode); +      break; +    default: +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +      break; +  } +} + +gboolean +gst_wavpack_enc_plugin_init (GstPlugin * plugin) +{ +  if (!gst_element_register (plugin, "wavpackenc", +          GST_RANK_NONE, GST_TYPE_WAVPACK_ENC)) +    return FALSE; + +  GST_DEBUG_CATEGORY_INIT (gst_wavpack_enc_debug, "wavpackenc", 0, +      "wavpack encoder"); + +  return TRUE; +} diff --git a/ext/wavpack/gstwavpackenc.h b/ext/wavpack/gstwavpackenc.h new file mode 100644 index 00000000..fe4e9fac --- /dev/null +++ b/ext/wavpack/gstwavpackenc.h @@ -0,0 +1,93 @@ +/* GStreamer Wavpack encoder plugin + * Copyrigh (c) 2006 Sebastian Dröge <mail@slomosnail.de> + * + * gstwavpackenc.h: Wavpack audio encoder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_WAVPACK_ENC_H__ +#define __GST_WAVPACK_ENC_H__ + +#include <gst/gst.h> + +#include <wavpack/wavpack.h> +#include <wavpack/md5.h> + +G_BEGIN_DECLS +/* #define's don't like whitespacey bits */ +#define GST_TYPE_WAVPACK_ENC \ +  (gst_wavpack_enc_get_type()) +#define GST_WAVPACK_ENC(obj) \ +  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAVPACK_ENC,GstWavpackEnc)) +#define GST_WAVPACK_ENC_CLASS(klass) \ +  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAVPACK_ENC,GstWavpackEnc)) +#define GST_IS_WAVPACK_ENC(obj) \ +  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAVPACK_ENC)) +#define GST_IS_WAVPACK_ENC_CLASS(obj) \ +  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAVPACK_ENC)) +typedef struct _GstWavpackEnc GstWavpackEnc; +typedef struct _GstWavpackEncClass GstWavpackEncClass; + +typedef struct +{ +  gboolean correction; +  GstWavpackEnc *wavpack_enc; +} write_id; + + +struct _GstWavpackEnc +{ +  GstElement element; + +  GstPad *sinkpad, *srcpad; +  GstPad *wvcsrcpad; + +  GstFlowReturn srcpad_last_return; +  GstFlowReturn wvcsrcpad_last_return; + +  WavpackConfig *wp_config; +  WavpackContext *wp_context; + +  gint samplerate; +  gint channels; +  gint width; + +  write_id *wv_id, *wvc_id; + +  guint mode; +  gdouble bitrate; +  guint correction_mode; +  gboolean md5; +  MD5_CTX *md5_context; +  gboolean extra_processing; +  guint joint_stereo_mode; + +  void *first_block; +  int32_t first_block_size; +}; + +struct _GstWavpackEncClass +{ +  GstElementClass parent; +}; + +GType gst_wavpack_enc_get_type (void); + +gboolean gst_wavpack_enc_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_WAVPACK_ENC_H__ */ diff --git a/ext/wavpack/md5.c b/ext/wavpack/md5.c new file mode 100644 index 00000000..1889c377 --- /dev/null +++ b/ext/wavpack/md5.c @@ -0,0 +1,271 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest.	This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Brutally hacked by John Walker back from ANSI C to K&R (no +   prototypes) to maintain the tradition that Netfone will compile +   with Sun's original "cc". */ + +#include <string.h>             /* for memcpy() */ +#include <glib.h> +#include "md5.h" + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#define HIGHFIRST +#endif + +#ifndef HIGHFIRST +#define byteReverse(buf, len)   /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +void +byteReverse (buf, longs) +     unsigned char *buf; +     unsigned longs; +{ +  uint32 t; + +  do { +    t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | +        ((unsigned) buf[1] << 8 | buf[0]); +    *(uint32 *) buf = t; +    buf += 4; +  } while (--longs); +} +#endif + +/* + * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init (ctx) +     struct MD5Context *ctx; +{ +  ctx->buf[0] = 0x67452301; +  ctx->buf[1] = 0xefcdab89; +  ctx->buf[2] = 0x98badcfe; +  ctx->buf[3] = 0x10325476; + +  ctx->bits[0] = 0; +  ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update (ctx, buf, len) +     struct MD5Context *ctx; +     unsigned char *buf; +     unsigned len; +{ +  uint32 t; + +  /* Update bitcount */ + +  t = ctx->bits[0]; +  if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) +    ctx->bits[1]++;             /* Carry from low to high */ +  ctx->bits[1] += len >> 29; + +  t = (t >> 3) & 0x3f;          /* Bytes already in shsInfo->data */ + +  /* Handle any leading odd-sized chunks */ + +  if (t) { +    unsigned char *p = (unsigned char *) ctx->in + t; + +    t = 64 - t; +    if (len < t) { +      memcpy (p, buf, len); +      return; +    } +    memcpy (p, buf, t); +    byteReverse (ctx->in, 16); +    MD5Transform (ctx->buf, (uint32 *) ctx->in); +    buf += t; +    len -= t; +  } +  /* Process data in 64-byte chunks */ + +  while (len >= 64) { +    memcpy (ctx->in, buf, 64); +    byteReverse (ctx->in, 16); +    MD5Transform (ctx->buf, (uint32 *) ctx->in); +    buf += 64; +    len -= 64; +  } + +  /* Handle any remaining bytes of data. */ + +  memcpy (ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern  + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final (digest, ctx) +     unsigned char digest[16]; +     struct MD5Context *ctx; +{ +  unsigned count; +  unsigned char *p; + +  /* Compute number of bytes mod 64 */ +  count = (ctx->bits[0] >> 3) & 0x3F; + +  /* Set the first char of padding to 0x80.  This is safe since there is +     always at least one byte free */ +  p = ctx->in + count; +  *p++ = 0x80; + +  /* Bytes of padding needed to make 64 bytes */ +  count = 64 - 1 - count; + +  /* Pad out to 56 mod 64 */ +  if (count < 8) { +    /* Two lots of padding:  Pad the first block to 64 bytes */ +    memset (p, 0, count); +    byteReverse (ctx->in, 16); +    MD5Transform (ctx->buf, (uint32 *) ctx->in); + +    /* Now fill the next block with 56 bytes */ +    memset (ctx->in, 0, 56); +  } else { +    /* Pad block to 56 bytes */ +    memset (p, 0, count - 8); +  } +  byteReverse (ctx->in, 14); + +  /* Append length in bits and transform */ +  ((uint32 *) ctx->in)[14] = ctx->bits[0]; +  ((uint32 *) ctx->in)[15] = ctx->bits[1]; + +  MD5Transform (ctx->buf, (uint32 *) ctx->in); +  byteReverse ((unsigned char *) ctx->buf, 4); +  memcpy (digest, ctx->buf, 16); +  memset (ctx, 0, sizeof (ctx));        /* In case it's sensitive */ +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ +	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data.  MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform (buf, in) +     uint32 buf[4]; +     uint32 in[16]; +{ +  register uint32 a, b, c, d; + +  a = buf[0]; +  b = buf[1]; +  c = buf[2]; +  d = buf[3]; + +  MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7); +  MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12); +  MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17); +  MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22); +  MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7); +  MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12); +  MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17); +  MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22); +  MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7); +  MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12); +  MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17); +  MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22); +  MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7); +  MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12); +  MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17); +  MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22); + +  MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5); +  MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9); +  MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14); +  MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); +  MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5); +  MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9); +  MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14); +  MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); +  MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5); +  MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9); +  MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14); +  MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20); +  MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5); +  MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); +  MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14); +  MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + +  MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4); +  MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11); +  MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16); +  MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23); +  MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4); +  MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); +  MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); +  MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23); +  MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4); +  MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11); +  MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16); +  MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23); +  MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4); +  MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11); +  MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); +  MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + +  MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6); +  MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10); +  MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15); +  MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21); +  MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6); +  MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); +  MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15); +  MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21); +  MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); +  MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); +  MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15); +  MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21); +  MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6); +  MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10); +  MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); +  MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21); + +  buf[0] += a; +  buf[1] += b; +  buf[2] += c; +  buf[3] += d; +} diff --git a/ext/wavpack/md5.h b/ext/wavpack/md5.h new file mode 100644 index 00000000..6f810221 --- /dev/null +++ b/ext/wavpack/md5.h @@ -0,0 +1,28 @@ +#ifndef MD5_H +#define MD5_H + +#include "_stdint.h" + +#ifndef uint32 +typedef uint32_t uint32; +#endif + +struct MD5Context +{ +  uint32 buf[4]; +  uint32 bits[2]; +  unsigned char in[64]; +}; + +extern void MD5Init (struct MD5Context *ctx); +extern void MD5Update (struct MD5Context *ctx, unsigned char *buf, +    unsigned len); +extern void MD5Final (unsigned char digest[16], struct MD5Context *ctx); +extern void MD5Transform (uint32 buf[4], uint32 in[16]); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +#endif /* !MD5_H */  | 
