From: Christophe F. <te...@us...> - 2003-11-23 18:48:58
|
CVS Root: /cvsroot/gstreamer Module: gst-sandbox Changes by: teuf Date: Sun Nov 23 2003 10:48:56 PST Log message: Initial import of the shorten gstreamer plug-in. Added files: gst-shorten : AUTHORS COPYING ChangeLog Makefile.am NEWS README autogen.sh autoregen.sh configure.ac gst-shorten/src : Makefile.am gstshndec.c gstshndec.h shnbitstream.c shnbitstream.h shorten.c shorten.h Links: http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/AUTHORS?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/COPYING?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/ChangeLog?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/Makefile.am?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/NEWS?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/README?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/autogen.sh?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/autoregen.sh?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/configure.ac?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/Makefile.am?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/gstshndec.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/gstshndec.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/shnbitstream.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/shnbitstream.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/shorten.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-sandbox/gst-shorten/src/shorten.h?rev=1.1&content-type=text/vnd.viewcvs-markup ====Begin Diffs==== --- NEW FILE: AUTHORS --- Christophe Fergeau <te...@gn...> --- NEW FILE: COPYING --- SHORTEN SOFTWARE LICENSE This software is being provided to you, the LICENSEE, by Tony Robinson and SoftSound under the following license. By obtaining, using and/or copying this software, you agree that you have read, understood, and will comply with these terms and conditions: This software may not be sold or incorporated into any product which is sold without prior permission from SoftSound. When no charge is made, this software may be copied and distributed freely. Permission is granted to use this software for decoding and non-commercial encoding (e.g. private or research use). Please email sh...@so... for commercial encoding terms. DISCLAIMER This software carries no warranty, expressed or implied. The user assumes all risks, known or unknown, direct or indirect, which involve this software in any way. --- NEW FILE: ChangeLog --- --- NEW FILE: Makefile.am --- SUBDIRS=src EXTRA_DIST=depcomp --- NEW FILE: NEWS --- Nothing much yet. --- NEW FILE: README --- This plug-in allows you to decode (and thus to play) shorten (.shn) files with GStreamer. Shorten is a lossless compression format similar to flac but inferior (non free, no tag support, no built in seeking support, ...). This plug-in works well enough for me, ie it can read all the shn files I have access to, and I checked that uncompressed files generated with it are the same as those generated by the original shorten 3.5.1 (the check was done using gstreamer to get the raw audio stream, and then running md5sum on it). It's probably possible to crash it with malformed shorten files, and the error handling could be improved. There is no support for seek tables, nor for 'dynamic seeking' (the xmms plug-in memorizes seek locations as it plays the file to be able to seek backward, and can play the file as quick as possible to seek forward). If you have files which doesn't work, please contact me (te...@gn...) so that I can try to fix the plug-in. It is untested on big endian machines, so there might be endianness issues. (Last updated 2003-11-21) --- NEW FILE: autogen.sh --- #!/bin/sh # you can either set the environment variables AUTOCONF and AUTOMAKE # to the right versions, or leave them unset and get the RedHat 7.3 defaults if test -z $AUTOMAKE; then export AUTOMAKE=automake export ACLOCAL=aclocal fi # if you would want to be running autoheader as well, you will have to do # something similar as above for it if test -z $AUTOCONF; then export AUTOCONF=autoconf; fi echo "#!/bin/sh" > autoregen.sh echo "./autogen.sh $@ \$@" >> autoregen.sh chmod +x autoregen.sh set -x # if any of these steps fails, the others will not execute, which is good # we want to treat errors as soon as possible $ACLOCAL $ACLOCAL_FLAGS && libtoolize --force && \ # autoheader $AUTOMAKE -a && \ $AUTOCONF && \ ./configure $* --- NEW FILE: autoregen.sh --- #!/bin/sh ./autogen.sh --prefix=/opt/gnome25/ $@ --- NEW FILE: configure.ac --- AC_INIT dnl Fill in your package name and version here PACKAGE=gst-shorten VERSION=0.2.0 dnl these AC_DEFINE_UNQUOTED's are necessary for make dist to work AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE") AC_DEFINE_UNQUOTED(VERSION, "$VERSION") AC_DEFINE_UNQUOTED(RELEASE, "$RELEASE") AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_SUBST(RELEASE) AM_INIT_AUTOMAKE($PACKAGE, $VERSION) AC_PROG_CC AC_PROG_LIBTOOL dnl Check for pkgconfig first AC_CHECK_PROG(HAVE_PKGCONFIG, pkg-config, yes, no) dnl Give error and exit if we don't have pkgconfig if test "x$HAVE_PKGCONFIG" = "xno"; then AC_MSG_ERROR(you need to have pkgconfig installed !) fi dnl Now we're ready to ask for gstreamer libs and cflags dnl And we can also ask for the right version of gstreamer GST_REQUIRED=0.7.0 GST_MAJORMINOR=0.7 PKG_CHECK_MODULES(GST, \ gstreamer-$GST_MAJORMINOR >= $GST_REQUIRED \ gstreamer-control-$GST_MAJORMINOR >= $GST_REQUIRED, HAVE_GST=yes,HAVE_GST=no) dnl Give error and exit if we don't have gstreamer if test "x$HAVE_GST" = "xno"; then AC_MSG_ERROR(you need gstreamer development packages installed !) fi dnl make GST_CFLAGS and GST_LIBS available AC_SUBST(GST_CFLAGS) AC_SUBST(GST_LIBS) dnl make GST_MAJORMINOR available in Makefile.am AC_SUBST(GST_MAJORMINOR) dnl If we need them, we can also use the plugin libraries PKG_CHECK_MODULES(GST_LIBS, gstreamer-libs-$GST_MAJORMINOR >= $GST_REQUIRED, HAVE_GST_LIBS=yes, HAVE_GST_LIBS=no) dnl Give a warning if we don't have gstreamer libs if test "x$HAVE_GST_LIBS" = "xno"; then AC_MSG_NOTICE(no GStreamer plugin libs found) fi GST_LIBS_CFLAGS="$GST_LIBS_CFLAGS -Wall -Werror -g -O2" dnl make GST_LIBS_CFLAGS and GST_LIBS_LIBS available AC_SUBST(GST_LIBS_CFLAGS) AC_SUBST(GST_LIBS_LIBS) AC_OUTPUT(Makefile src/Makefile) --- NEW FILE: Makefile.am --- # location where plug-in will be installed plugindir= $(libdir)/gstreamer-@GST_MAJORMINOR@ plugin_LTLIBRARIES = libgstshndec.la libgstshndec_la_SOURCES = \ gstshndec.c \ gstshndec.h \ shorten.c \ shorten.h \ shnbitstream.c\ shnbitstream.h libgstshndec_la_CFLAGS = $(GST_LIBS_CFLAGS) libgstshndec_la_LIBADD = libgstshndec_la_LDFLAGS = $(GST_LIBS_LIBS) noinst_HEADERS=gstshndec.h shorten.h shnbitstream.h--- NEW FILE: gstshndec.c --- #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <stdlib.h> #include <string.h> #include <gstshndec.h> #include "shorten.h" /* elementfactory information */ static GstElementDetails gst_shndec_details = { ".shn decoder", "Codec/Audio/Decoder", "Decode an .shn file into raw audio", "Christophe Fergeau <te...@gn...>", }; static void gst_shndec_loop (GstElement *element); GST_PAD_TEMPLATE_FACTORY (sink_factory_templ, "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_CAPS_NEW ( "shndec_sink", "audio/x-shorten", NULL ) ) GST_PAD_TEMPLATE_FACTORY (src_factory_templ, "src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_CAPS_NEW ( "shndec_src", "audio/x-raw-int", "endianness", GST_PROPS_LIST( GST_PROPS_INT (G_LITTLE_ENDIAN), GST_PROPS_INT (G_BIG_ENDIAN) ), "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) )/*, GST_CAPS_NEW ( "shndec_src_alaw", "audio/x-alaw", "rate", GST_PROPS_INT_RANGE (8000,48000), "channels", GST_PROPS_INT_RANGE (1, 2) )*/ ) /* ShnDec signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { ARG_0, /* FILL ME */ }; static void gst_shndec_class_init (GstShnDecClass *klass); static void gst_shndec_base_init (GstShnDecClass *klass); static void gst_shndec_init (GstShnDec *shndec); //static void gst_shndec_chain (GstPad *pad,GstBuffer *buf); static GstElementClass *parent_class = NULL; /*static guint gst_shndec_signals[LAST_SIGNAL] = { 0 }; */ GType gst_shndec_get_type (void) { static GType shndec_type = 0; if (!shndec_type) { static const GTypeInfo shndec_info = { sizeof(GstShnDecClass), (GBaseInitFunc) gst_shndec_base_init, NULL, (GClassInitFunc) gst_shndec_class_init, NULL, NULL, sizeof(GstShnDec), 0, (GInstanceInitFunc) gst_shndec_init, }; shndec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstShnDec", &shndec_info, 0); } return shndec_type; } static void gst_shndec_base_init (GstShnDecClass *klass) { gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), GST_PAD_TEMPLATE_GET (sink_factory_templ)); gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), GST_PAD_TEMPLATE_GET (src_factory_templ)); gst_element_class_set_details (GST_ELEMENT_CLASS (klass), &gst_shndec_details); } static void gst_shndec_class_init (GstShnDecClass *klass) { GstElementClass *gstelement_class; gstelement_class = (GstElementClass*) klass; parent_class = g_type_class_ref (GST_TYPE_ELEMENT); } static void gst_shndec_init (GstShnDec *shndec) { shndec->sinkpad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (sink_factory_templ), "sink"); gst_element_add_pad (GST_ELEMENT (shndec), shndec->sinkpad); gst_element_set_loop_function (GST_ELEMENT (shndec), gst_shndec_loop); shndec->srcpad = gst_pad_new_from_template ( GST_PAD_TEMPLATE_GET (src_factory_templ), "src"); gst_element_add_pad (GST_ELEMENT (shndec), shndec->srcpad); shndec->inited = FALSE; } static void gst_shndec_loop (GstElement *element) { GstShnDec *shndec; GstCaps *tempcaps; GstByteStream *stream; shndec = GST_SHNDEC (element); /* First, read the header of our file to get info about it */ if (!shndec->inited) { GST_DEBUG ("shndec: initializing decoder\n"); stream = gst_bytestream_new (shndec->sinkpad); if (stream == NULL) { gst_element_error (GST_ELEMENT (shndec), "Couldn't create stream\n"); return; } shndec->stream = shn_bitstream_new (stream); if (shndec->stream == NULL) { gst_element_error (GST_ELEMENT (shndec), "Couldn't create bit stream\n"); return; } shn_decode_context_init (shndec); shndec->sample_rate = 44100; shndec->inited = TRUE; /* Now we can set the caps on our src pad */ switch (shndec->type) { case TYPE_AU1: case TYPE_AU2: tempcaps = GST_CAPS_NEW ("shndec_src", "audio/x-mulaw", "rate", GST_PROPS_INT (shndec->sample_rate), "channels", GST_PROPS_INT (shndec->nchannels)); break; case TYPE_AU3: case TYPE_ALAW: tempcaps = GST_CAPS_NEW ("shndec_src", "audio/x-alaw", "rate", GST_PROPS_INT (shndec->sample_rate), "channels", GST_PROPS_INT (shndec->nchannels)); break; case TYPE_S8: case TYPE_U8: tempcaps = GST_CAPS_NEW ("shndec_src", "audio/x-raw-int", "rate", GST_PROPS_INT (shndec->sample_rate), "channels", GST_PROPS_INT (shndec->nchannels), "depth", GST_PROPS_INT (8), "width", GST_PROPS_INT (8), "signed", GST_PROPS_BOOLEAN (shndec->type == TYPE_S8)); break; case TYPE_S16HL: case TYPE_S16LH: case TYPE_U16HL: case TYPE_U16LH: { gboolean is_signed; gint endianness; is_signed = ((shndec->type == TYPE_S16HL) || (shndec->type == TYPE_S16LH)); if ((shndec->type == TYPE_S16HL) || (shndec->type == TYPE_U16HL)) { endianness = G_BIG_ENDIAN; } else { endianness = G_LITTLE_ENDIAN; } tempcaps = GST_CAPS_NEW ("shndec_src", "audio/x-raw-int", "endianness", GST_PROPS_INT (endianness), "rate", GST_PROPS_INT (shndec->sample_rate), "channels", GST_PROPS_INT (shndec->nchannels), "depth", GST_PROPS_INT (16), "width", GST_PROPS_INT (16), "signed", GST_PROPS_BOOLEAN (is_signed)); break; } default: g_warning ("Unexpected format\n"); gst_element_error (GST_ELEMENT (shndec), "unknown shn format"); return; } if (gst_pad_try_set_caps (shndec->srcpad, tempcaps) <= 0) { gst_element_error (GST_ELEMENT (shndec), "could not set audio caps"); return; } } if ((gst_element_get_state (GST_ELEMENT (shndec)) == GST_STATE_PAUSED) ) { GstBuffer *dummy; GstByteStream *stream; /* We sent the eos event in shorten.c, but there is still data * to process on the sink (seek tables), so we silently pull it (without pushing anything to the src) */ stream = shn_bitstream_get_bytestream (shndec->stream); gst_bytestream_read (stream, &dummy, 4096); gst_buffer_unref (dummy); return; } if (shn_bitstream_eos (shndec->stream)) { g_warning ("Warning, loop function called while the sink reached eos\n"); return; } /* The real decoding occurs in shorten.c */ shn_decode (shndec); } static gboolean plugin_init (GstPlugin *plugin) { if (!gst_library_load ("gstbytestream")) { return FALSE; } return gst_element_register (plugin, "shndec", GST_RANK_PRIMARY, GST_TYPE_SHNDEC); } GST_PLUGIN_DEFINE ( GST_VERSION_MAJOR, GST_VERSION_MINOR, "shndec", "shn (shorten) decoding", plugin_init, VERSION, GST_LICENSE_UNKNOWN, "(C) 2003 Christophe Fergeau <te...@gn...>", "shn plugin", "this is a test" ) --- NEW FILE: gstshndec.h --- #ifndef __GST_SHNDEC_H__ #define __GST_SHNDEC_H__ #include <gst/gst.h> #include "shnbitstream.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define GST_TYPE_SHNDEC \ (gst_shndec_get_type()) #define GST_SHNDEC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SHNDEC,GstShnDec)) #define GST_SHNDEC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SHNDEC,GstShnDec)) #define GST_IS_SHNDEC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SHNDEC)) #define GST_IS_SHNDEC_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SHNDEC)) typedef struct _GstShnDec GstShnDec; typedef struct _GstShnDecClass GstShnDecClass; struct _GstShnDec { GstElement element; GstPad *sinkpad,*srcpad; gboolean inited; guint total_samples; guint sample_rate; /* Used by shorten.c */ guint version; guint type; guint nchannels; guint block_size; guint maxnlpc; guint nmean; guint nskip; glong **buffer; glong **offset; gint *qlpc; glong lpcqoffset; glong coffset; gint nwrap; gint cur_cmd; gint cur_chan; glong resn; gint bitshift; ShnBitStream *stream; }; struct _GstShnDecClass { GstElementClass parent_class; }; GType gst_shndec_get_type (void); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __GST_SHNDEC_H__ */ --- NEW FILE: shnbitstream.c --- /****************************************************************************** * * * Copyright (C) 1992-1995 Tony Robinson * * * * See the file LICENSE for conditions on distribution and usage * * * ******************************************************************************/ /* * $Id: shnbitstream.c,v 1.1 2003/11/23 18:48:44 teuf Exp $ */ #include <math.h> #include <stdio.h> #include <stdlib.h> #include "shnbitstream.h" #include "shorten.h" //#define DEBUG_DECODE 1 struct _ShnBitStream { GstByteStream *bytestream; GstBuffer *prefetch_buf; guint cur_offset; gulong bit_buf; gint nbitget; gboolean eos; }; #define MASKTABSIZE 33 ulong masktab[MASKTABSIZE] = {0,}; /* masktab[i] contains a mask which selects the i lowest bytes of * a 32 bit int */ static void mkmasktab(void) { int i; ulong val = 0; masktab[0] = val; for(i = 1; i < MASKTABSIZE; i++) { val <<= 1; val |= 1; masktab[i] = val; } } gboolean shn_bitstream_eos (ShnBitStream *stream) { return stream->eos; } ShnBitStream *shn_bitstream_new (GstByteStream *bytestream) { ShnBitStream *bitstream; if (masktab[1] == 0) { mkmasktab(); } bitstream = g_new0 (ShnBitStream, 1); if (bitstream == NULL) { return NULL; } bitstream->bytestream = bytestream; return bitstream; } GstByteStream *shn_bitstream_get_bytestream (ShnBitStream *bitstream) { return bitstream->bytestream; } static gulong shn_bitstream_get(ShnBitStream *stream) { #define PREFETCH_COUNT 4096 ulong result; guint8 *tmp; /* Calling gst_bytestream_read lots of time to read a small amount of * data causes cpu hog, so we prefetch data to avoid calling it too often */ if ((stream->prefetch_buf == NULL) || ((stream->prefetch_buf != NULL) && (stream->cur_offset >= GST_BUFFER_SIZE (stream->prefetch_buf)))) { int bytes_read; if (stream->prefetch_buf != NULL) { gst_buffer_unref (stream->prefetch_buf); } bytes_read = gst_bytestream_read (stream->bytestream, &stream->prefetch_buf, PREFETCH_COUNT); if (bytes_read < 4) { GstEvent *event; gst_bytestream_get_status (stream->bytestream, NULL, &event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: GST_DEBUG ("eos"); stream->eos = TRUE; break; default: GST_DEBUG ("unknown event %d", GST_EVENT_TYPE (event)); break; } gst_event_unref (event); return 0; } /* We are reading 4 bytes each time, make sure we won't overflow * if we haven't read a number of bytes multiple of 4 */ bytes_read &= 0xFFFFFFFC; GST_BUFFER_SIZE (stream->prefetch_buf) = bytes_read; stream->cur_offset = 0; } tmp = GST_BUFFER_DATA (stream->prefetch_buf); result = (((glong) tmp[stream->cur_offset]) << 24) | (((glong) tmp[stream->cur_offset + 1]) << 16) | (((glong) tmp[stream->cur_offset + 2]) << 8) | (((glong) tmp[stream->cur_offset + 3])); stream->cur_offset += 4; return(result); } glong uvar_get(int nbin, ShnBitStream *stream) { glong result; if(stream->nbitget == 0) { stream->bit_buf = shn_bitstream_get(stream); if (stream->eos == TRUE) { return 0; } stream->nbitget = 32; } /* Count and skip 0s */ for(result = 0; !(stream->bit_buf & (1L << --stream->nbitget)); result++) { if(stream->nbitget == 0) { stream->bit_buf = shn_bitstream_get(stream); if (stream->eos == TRUE) { return 0; } stream->nbitget = 32; } } while(nbin != 0) { if(stream->nbitget >= nbin) { result = (result << nbin) | ((stream->bit_buf >> (stream->nbitget-nbin)) &masktab[nbin]); stream->nbitget -= nbin; nbin = 0; } else { result = (result << stream->nbitget) | (stream->bit_buf & masktab[stream->nbitget]); stream->bit_buf = shn_bitstream_get(stream); if (stream->eos == TRUE) { return 0; } nbin -= stream->nbitget; stream->nbitget = 32; } } #ifdef DEBUG_DECODE printf (" result: %x\n", result); #endif return(result); } gulong ulong_get(ShnBitStream *stream) { unsigned int nbit = uvar_get(ULONGSIZE, stream); if (stream->eos == TRUE) { return 0; } return(uvar_get(nbit, stream)); } glong var_get(gint nbin, ShnBitStream *stream) { ulong uvar = (gulong) uvar_get(nbin + 1, stream); if (stream->eos == TRUE) { return 0; } if(uvar & 1) return((glong) ~(uvar >> 1)); else return((glong) (uvar >> 1)); } --- NEW FILE: shnbitstream.h --- #ifndef SHNBITSTREAM_H #define SHNBITSTREAM_H #include <stdio.h> #include <glib.h> #include <gst/gst.h> #include <gst/bytestream/bytestream.h> typedef struct _ShnBitStream ShnBitStream; extern void var_get_init (void); extern glong uvar_get (gint, ShnBitStream *); extern glong var_get (gint, ShnBitStream *); extern gulong ulong_get (ShnBitStream *n); ShnBitStream *shn_bitstream_new (GstByteStream *bytestream); GstByteStream *shn_bitstream_get_bytestream (ShnBitStream *bitstream); gboolean shn_bitstream_eos (ShnBitStream *stream); #endif /* SHNBITSTREAM_H */ --- NEW FILE: shorten.c --- #include <glib.h> #include <math.h> #include <stdio.h> #include <string.h> #include <gst/bytestream.h> #include "shorten.h" #include "shnbitstream.h" #define CAPMAXSCHAR(x) ((x > 127) ? 127 : x) #define CAPMAXUCHAR(x) ((x > 255) ? 255 : x) #define CAPMAXSHORT(x) ((x > 32767) ? 32767 : x) #define CAPMAXUSHORT(x) ((x > 65535) ? 65535 : x) #define UINT_GET(nbit, stream, version) \ ((version == 0) ? uvar_get(nbit, stream) : ulong_get(stream)) static inline void fill_output_buffer (GstShnDec *ctxt, GstBuffer *buf) { switch (ctxt->type) { case TYPE_AU1: case TYPE_S8: case TYPE_U8: case TYPE_ULAW: case TYPE_AU2: case TYPE_AU3: case TYPE_ALAW: { gchar *buffer = GST_BUFFER_DATA (buf); int i, chan; for (i = 0; i < ctxt->block_size; i++) { for (chan = 0; chan < ctxt->nchannels; chan++) { *buffer = ctxt->buffer[chan][i]; buffer++; } } break; } case TYPE_S16HL: case TYPE_S16LH: { int i, chan; gshort *buffer = (gshort *)GST_BUFFER_DATA (buf); for (i = 0; i < ctxt->block_size; i++) { for (chan = 0; chan < ctxt->nchannels; chan++) { *buffer = CAPMAXSHORT (ctxt->buffer[chan][i]); buffer++; } } break; } case TYPE_U16HL: case TYPE_U16LH: { int i, chan; gushort *buffer = (gshort *)GST_BUFFER_DATA (buf); for (i = 0; i < ctxt->block_size; i++) { for (chan = 0; chan < ctxt->nchannels; chan++) { *buffer = CAPMAXUSHORT (ctxt->buffer[chan][i]); buffer++; } } break; } default: g_warning ("Unsupported file type\n"); break; } } static inline void fix_bitshift(GstShnDec *ctxt) { int i; glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; if (ctxt->type == TYPE_AU1) { /* for(i = 0; i < ctxt->block_size; i++) cbuffer[i] = ulaw_outward[ctxt->bitshift][cbuffer[i] + 128];*/ g_warning ("Don't know what to do with TYPE_AU1 and bitshifts\n"); } else if (ctxt->type == TYPE_AU2) { g_warning ("Don't know what to do with TYPE_AU2 and bitshifts\n"); /* for(i = 0; i < ctxt->block_size; i++) { if (cbuffer[i] >= 0) cbuffer[i] = ulaw_outward[ctxt->bitshift][cbuffer[i] + 128]; else if(cbuffer[i] == -1) cbuffer[i] = NEGATIVE_ULAW_ZERO; else cbuffer[i] = ulaw_outward[ctxt->bitshift][cbuffer[i] + 129]; }*/ } else { if(ctxt->bitshift != 0) for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] <<= ctxt->bitshift; } } } static void init_offset (GstShnDec *ctxt) { glong mean = 0; int chan, i; int nblock = MAX (1, ctxt->nmean); /* initialise offset */ switch(ctxt->type) { case TYPE_AU1: case TYPE_S8: case TYPE_S16HL: case TYPE_S16LH: case TYPE_ULAW: case TYPE_AU2: case TYPE_AU3: case TYPE_ALAW: mean = 0; break; case TYPE_U8: mean = 0x80; break; case TYPE_U16HL: case TYPE_U16LH: mean = 0x8000; break; default: g_error ("unknown file type: %d\n", ctxt->type); } for(chan = 0; chan <ctxt->nchannels; chan++) { for(i = 0; i < nblock; i++) { ctxt->offset[chan][i] = mean; } } } /* static void dump_shn_params (GstShnDec *ctxt) { gchar *file_type_str[] = { "AU1: original lossless ulaw", "S8: signed 8 bit characters", "U8: unsigned 8 bit characters", "S16HL: signed 16 bit shorts (high-low)", "U16HL: unsigned 16 bit shorts (high-low)", "S16LH: signed 16 bit shorts (low-high)", "U16LH: unsigned 16 bit shorts (low-high)", "ULAW: lossy ulaw", "AU2: new ulaw with zero mapping", "AU3: lossless alaw", "ALAW: lossy alaw", "RIFF WAVE: wav file", "AIFF: aiff file" }; g_print ("File Information:\n"); g_print ("Version: %u\n", ctxt->version); // g_print ("Type: %u\n", ctxt->type); g_print ("Type: %s (%u)\n", file_type_str[ctxt->type], ctxt->type); g_print ("%u channels\n", ctxt->nchannels); g_print ("Block size: %u\n", ctxt->block_size); } */ static int shn_get_version (GstByteStream *stream) { const char *magic = MAGIC; gchar *magic_ptr; GstBuffer *buffer; gint version; if (gst_bytestream_peek (stream, &buffer, 128) != 128) { g_warning ("Error reading stream\n"); gst_buffer_unref (buffer); return MAX_VERSION + 1; } /* This is less flexible than the original version detection code * but should do the trick in most cases */ magic_ptr = g_strstr_len (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer) - 1, magic); if (magic_ptr == NULL) { gst_buffer_unref (buffer); return MAX_VERSION + 1; } version = magic_ptr[strlen(magic)]; gst_bytestream_flush_fast (stream, ((magic_ptr - (gchar *)GST_BUFFER_DATA (buffer)) + 1) + strlen (magic)); gst_buffer_unref (buffer); return version; } static void shn_read_block_size (GstShnDec *ctxt) { ShnBitStream *stream = ctxt->stream; /* get blocksize if version > 0 */ if (ctxt->version > 0) { ctxt->block_size = UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2), stream, ctxt->version); ctxt->maxnlpc = UINT_GET(LPCQSIZE, stream, ctxt->version); ctxt->nmean = UINT_GET(0, stream, ctxt->version); ctxt->nskip = UINT_GET(NSKIPSIZE, stream, ctxt->version); } else { ctxt->block_size = DEFAULT_BLOCK_SIZE; } } static int read_header (GstShnDec *ctxt) { GstByteStream *stream = shn_bitstream_get_bytestream (ctxt->stream); ctxt->version = shn_get_version (stream); if (ctxt->version > MAX_SUPPORTED_VERSION) { g_warning ("Unsupported version\n"); return -1; } if (ctxt->version < 2) { ctxt->nmean = DEFAULT_V0NMEAN; } else { ctxt->nmean = DEFAULT_V2NMEAN; } ctxt->type = UINT_GET (TYPESIZE, ctxt->stream, ctxt->version); ctxt->nchannels = UINT_GET(CHANSIZE, ctxt->stream, ctxt->version); shn_read_block_size (ctxt); return 0; } static glong **long2d(gulong n0, gulong n1) { glong **array; glong i; array = g_malloc0 (n0*sizeof (glong*)); if (array == NULL) { return NULL; } for (i = 0; i < n0; i++) { array[i] = g_malloc0 (n1*sizeof(glong)); if (array[i] == NULL) { glong j; for (j = 0; j < i; j++) { g_free (array[j]); g_free (array); } return NULL; } } return array; } int shn_decode_context_init (GstShnDec *ctxt) { int chan; /* FIXME: if GstShnDec isn't created using g_new0, need to * manually zero out the various fields */ if (read_header (ctxt) != 0) { return -1; }; /* dump_shn_params (ctxt); */ ctxt->nwrap = MAX (NWRAP, ctxt->maxnlpc); /* grab some space for the input buffer */ ctxt->buffer = (glong **)long2d (ctxt->nchannels, ctxt->block_size+ctxt->nwrap); if (ctxt->buffer == NULL) { return -1; } ctxt->offset = (glong **) long2d (ctxt->nchannels, MAX(1, ctxt->nmean)); if (ctxt->offset == NULL) { return -1; } for (chan = 0; chan < ctxt->nchannels; chan++) { int i; for(i = 0; i < ctxt->nwrap; i++) { ctxt->buffer[chan][i] = 0; } ctxt->buffer[chan] += ctxt->nwrap; } if (ctxt->maxnlpc > 0) { ctxt->qlpc = (gint *)g_malloc((ctxt->maxnlpc*sizeof(*ctxt->qlpc))); } if(ctxt->version > 1) { ctxt->lpcqoffset = V2LPCQOFFSET; } init_offset(ctxt); return 0; } static inline void pre_decode (ShnBitStream *stream, GstShnDec *ctxt) { int i; if(ctxt->cur_cmd != FN_ZERO) { ctxt->resn = uvar_get(ENERGYSIZE, stream); /* this is a hack as version 0 differed in definition of var_get */ if(ctxt->version == 0) ctxt->resn--; } if(ctxt->nmean == 0) { ctxt->coffset = ctxt->offset[ctxt->cur_chan][0]; } else { glong sum; if (ctxt->version < 2) { sum = 0; } else { sum = ctxt->nmean / 2; } for(i = 0; i < ctxt->nmean; i++) { sum += ctxt->offset[ctxt->cur_chan][i]; } if (ctxt->version < 2) { ctxt->coffset = (glong)sum / (glong)ctxt->nmean; } else { ctxt->coffset = ROUNDEDSHIFTDOWN ((glong)sum / (glong)ctxt->nmean, (glong)ctxt->bitshift); } } } static inline void post_decode (ShnBitStream* stream, GstShnDec *ctxt) { int i; glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; /* store mean value if appropriate : N.B. Duplicated code */ if(ctxt->nmean > 0) { glong sum; if (ctxt->version < 2) { sum = 0; } else { sum = ctxt->block_size / 2; } for(i = 0; i < ctxt->block_size; i++) { sum += cbuffer[i]; } for(i = 1; i < ctxt->nmean; i++) { ctxt->offset[ctxt->cur_chan][i - 1] = ctxt->offset[ctxt->cur_chan][i]; } if(ctxt->version < 2) { ctxt->offset[ctxt->cur_chan][ctxt->nmean - 1] = sum / ctxt->block_size; } else { ctxt->offset[ctxt->cur_chan][ctxt->nmean - 1] = ((glong)sum / (glong)ctxt->block_size) << (glong)ctxt->bitshift; } } /* do the wrap */ for(i = -ctxt->nwrap; i < 0; i++) { cbuffer[i] = cbuffer[i + ctxt->block_size]; } fix_bitshift(ctxt); if(ctxt->cur_chan == ctxt->nchannels - 1) { GstBuffer *buffer; /* FIXME: need to handle sample size properly instead of * hardcoding 2 */ buffer = gst_buffer_new_and_alloc (ctxt->nchannels*ctxt->block_size*2); fill_output_buffer (ctxt, buffer); GST_BUFFER_TIMESTAMP (buffer) = ctxt->total_samples * GST_SECOND / 44100; ctxt->total_samples += GST_BUFFER_SIZE (buffer); gst_pad_push (ctxt->srcpad, GST_DATA (buffer)); } ctxt->cur_chan = (ctxt->cur_chan + 1) % ctxt->nchannels; } static inline void handle_fn_zero (ShnBitStream *stream, GstShnDec *ctxt) { glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; int i; pre_decode (stream, ctxt); for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] = 0; } post_decode (stream, ctxt); } static inline void handle_fn_diff0 (ShnBitStream *stream, GstShnDec *ctxt) { glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; int i; pre_decode (stream, ctxt); for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] = var_get(ctxt->resn, stream) + ctxt->coffset; } post_decode (stream, ctxt); } static inline void handle_fn_diff1 (ShnBitStream *stream, GstShnDec *ctxt) { glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; int i; pre_decode (stream, ctxt); for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] = var_get(ctxt->resn, stream) + cbuffer[i - 1]; } post_decode (stream, ctxt); } static inline void handle_fn_diff2 (ShnBitStream *stream, GstShnDec *ctxt) { glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; int i; pre_decode (stream, ctxt); for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] = var_get(ctxt->resn, stream) + (2 * cbuffer[i-1] - cbuffer[i-2]); } post_decode (stream, ctxt); } static inline void handle_fn_diff3 (ShnBitStream *stream, GstShnDec *ctxt) { glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; int i; pre_decode (stream, ctxt); for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] = var_get(ctxt->resn, stream) + 3 * (cbuffer[i-1] - cbuffer[i-2]) + cbuffer[i-3]; } post_decode (stream, ctxt); } static inline void handle_fn_qlpc (ShnBitStream *stream, GstShnDec *ctxt) { glong *cbuffer = ctxt->buffer[ctxt->cur_chan]; int i,j, nlpc; pre_decode (stream, ctxt); nlpc = uvar_get(LPCQSIZE, stream); for(i = 0; i < nlpc; i++) { ctxt->qlpc[i] = var_get(LPCQUANT, stream); cbuffer[i - nlpc] -= ctxt->coffset; } for(i = 0; i < ctxt->block_size; i++) { glong sum = ctxt->lpcqoffset; for(j = 0; j < nlpc; j++) { sum += ctxt->qlpc[j] * cbuffer[i - j - 1]; } cbuffer[i] = var_get(ctxt->resn, stream) + (sum >> LPCQUANT); } if (ctxt->coffset != 0) { for(i = 0; i < ctxt->block_size; i++) { cbuffer[i] += ctxt->coffset; } } post_decode (stream, ctxt); } static inline void handle_fn_block_size (ShnBitStream *stream, GstShnDec *ctxt) { ctxt->block_size = UINT_GET((int) (log((double) ctxt->block_size) / M_LN2), stream, ctxt->version); } static inline void handle_fn_bitshift (ShnBitStream *stream, GstShnDec *ctxt) { ctxt->bitshift = uvar_get(BITSHIFTSIZE, stream); } static inline void handle_fn_verbatim (ShnBitStream *stream, GstShnDec *ctxt) { int cklen = uvar_get(VERBATIM_CKSIZE_SIZE, stream); while (cklen--) { uvar_get(VERBATIM_BYTE_SIZE, stream); } } //#define DEBUG_DECODE 1 int shn_decode (GstShnDec *ctxt) { #ifdef DEBUG_DECODE char *cmd_str[] = { "FN_DIFF0", "FN_DIFF1", "FN_DIFF2", "FN_DIFF3", "FN_QUIT", "FN_BLOCKSIZE", "FN_BITSHIFT", "FN_QLPC", "FN_ZERO", "FN_VERBATIM" }; #endif ShnBitStream *stream = ctxt->stream; /* get commands from file and execute them */ ctxt->cur_cmd = uvar_get(FNSIZE, stream); #ifdef DEBUG_DECODE printf ("%s\n", cmd_str[ctxt->cur_cmd]); #endif if(FN_QUIT == ctxt->cur_cmd) { gst_element_set_eos (GST_ELEMENT (ctxt)); gst_pad_push(ctxt->srcpad, GST_DATA(gst_event_new (GST_EVENT_EOS))); return 0; } switch (ctxt->cur_cmd) { case FN_ZERO: handle_fn_zero (stream, ctxt); break; case FN_DIFF0: handle_fn_diff0 (stream, ctxt); break; case FN_DIFF1: handle_fn_diff1 (stream, ctxt); break; case FN_DIFF2: handle_fn_diff2 (stream, ctxt); break; case FN_DIFF3: handle_fn_diff3 (stream, ctxt); break; case FN_QLPC: handle_fn_qlpc (stream, ctxt); break; case FN_BLOCKSIZE: handle_fn_block_size (stream, ctxt); break; case FN_BITSHIFT: handle_fn_bitshift (stream, ctxt); break; case FN_VERBATIM: handle_fn_verbatim (stream, ctxt); break; default: g_warning("sanity check fails trying to decode function: %d\n", ctxt->cur_cmd); return -1; } return 0; } --- NEW FILE: shorten.h --- #ifndef SHORTEN_H #define SHORTEN_H #include <gst/gst.h> #include "gstshndec.h" #define MAGIC "ajkg" #define FORMAT_VERSION 2 #define MIN_SUPPORTED_VERSION 1 #define MAX_SUPPORTED_VERSION 3 #define MAX_VERSION 7 #define DEFAULT_V0NMEAN 0 #define DEFAULT_V2NMEAN 4 #define DEFAULT_BLOCK_SIZE 256 #define ULONGSIZE 2 #define CHANSIZE 0 #define ENERGYSIZE 3 #define NSKIPSIZE 1 #define LPCQSIZE 2 #define BITSHIFTSIZE 2 #define VERBATIM_CKSIZE_SIZE 5 /* a var_put code size */ #define VERBATIM_BYTE_SIZE 8 /* code size 8 on single bytes means * no compression at all */ #define TYPESIZE 4 #define TYPE_AU1 0 /* original lossless ulaw */ #define TYPE_S8 1 /* signed 8 bit characters */ #define TYPE_U8 2 /* unsigned 8 bit characters */ #define TYPE_S16HL 3 /* signed 16 bit shorts: high-low */ #define TYPE_U16HL 4 /* unsigned 16 bit shorts: high-low */ #define TYPE_S16LH 5 /* signed 16 bit shorts: low-high */ #define TYPE_U16LH 6 /* unsigned 16 bit shorts: low-high */ #define TYPE_ULAW 7 /* lossy ulaw: internal conversion to linear */ #define TYPE_AU2 8 /* new ulaw with zero mapping */ #define TYPE_AU3 9 /* lossless alaw */ #define TYPE_ALAW 10 /* lossy alaw: internal conversion to linear */ #define TYPE_RIFF_WAVE 11 /* Microsoft .WAV files */ #define TYPE_AIFF 12 /* Apple .AIFF files */ #define TYPE_EOF 13 #define TYPE_GENERIC_ULAW 128 #define TYPE_GENERIC_ALAW 129 #define FNSIZE 2 #define FN_DIFF0 0 #define FN_DIFF1 1 #define FN_DIFF2 2 #define FN_DIFF3 3 #define FN_QUIT 4 #define FN_BLOCKSIZE 5 #define FN_BITSHIFT 6 #define FN_QLPC 7 #define FN_ZERO 8 #define FN_VERBATIM 9 #define ROUNDEDSHIFTDOWN(x, n) (((n) == 0) ? (x) : ((x) >> ((n) - 1)) >> 1) #ifndef M_LN2 #define M_LN2 0.69314718055994530942 #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define NWRAP 3 #define LPCQUANT 5 #define V2LPCQOFFSET (1 << LPCQUANT); #define NEGATIVE_ULAW_ZERO 0x7f #define NBITPERLONG 32 int shn_decode_context_init (GstShnDec*); int shn_decode (GstShnDec*); #endif /* SHORTEN_H */ |