Merge branch 'libsndfile'
[Faustine.git] / interpretor / lib / src / libsndfile-1.0.25 / src / ogg_vorbis.c
diff --git a/interpretor/lib/src/libsndfile-1.0.25/src/ogg_vorbis.c b/interpretor/lib/src/libsndfile-1.0.25/src/ogg_vorbis.c
new file mode 100644 (file)
index 0000000..99223cd
--- /dev/null
@@ -0,0 +1,1142 @@
+/*
+** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
+** Copyright (C) 2002-2005 Michael Smith <msmith@xiph.org>
+** Copyright (C) 2007 John ffitch
+**
+** This program is free software ; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published by
+** the Free Software Foundation ; either version 2.1 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY ; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+** GNU Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program ; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+**  Much of this code is based on the examples in libvorbis from the
+** XIPHOPHORUS Company http://www.xiph.org/ which has a BSD-style Licence
+** Copyright (c) 2002, Xiph.org Foundation
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+**
+** - Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** - Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** - Neither the name of the Xiph.org Foundation nor the names of its
+** contributors may be used to endorse or promote products derived from
+** this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
+** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE,
+** DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "sfconfig.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "sndfile.h"
+#include "sfendian.h"
+#include "common.h"
+
+#if HAVE_EXTERNAL_LIBS
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisenc.h>
+
+#include "ogg.h"
+
+typedef int convert_func (int, void *, int, int, float **) ;
+
+static int     vorbis_read_header (SF_PRIVATE *psf, int log_data) ;
+static int     vorbis_write_header (SF_PRIVATE *psf, int calc_length) ;
+static int     vorbis_close (SF_PRIVATE *psf) ;
+static int     vorbis_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
+static sf_count_t      vorbis_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
+static sf_count_t      vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
+static sf_count_t      vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) ;
+static sf_count_t      vorbis_length (SF_PRIVATE *psf) ;
+
+typedef struct
+{      int id ;
+       const char *name ;
+} STR_PAIRS ;
+
+static STR_PAIRS vorbis_metatypes [] =
+{      {       SF_STR_TITLE,           "Title" },
+       {       SF_STR_COPYRIGHT,       "Copyright" },
+       {       SF_STR_SOFTWARE,        "Software" },
+       {       SF_STR_ARTIST,          "Artist" },
+       {       SF_STR_COMMENT,         "Comment" },
+       {       SF_STR_DATE,            "Date" },
+       {       SF_STR_ALBUM,           "Album" },
+       {       SF_STR_LICENSE,         "License" },
+} ;
+
+typedef struct
+{      /* Count current location */
+       sf_count_t loc ;
+       /* Struct that stores all the static vorbis bitstream settings */
+       vorbis_info     vinfo ;
+       /* Struct that stores all the bitstream user comments */
+       vorbis_comment vcomment ;
+       /* Ventral working state for the packet->PCM decoder */
+       vorbis_dsp_state vdsp ;
+       /* Local working space for packet->PCM decode */
+       vorbis_block vblock ;
+
+       /* Encoding quality in range [0.0, 1.0]. */
+       double quality ;
+} VORBIS_PRIVATE ;
+
+static int
+vorbis_read_header (SF_PRIVATE *psf, int log_data)
+{
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+       char *buffer ;
+       int      bytes ;
+       int i, nn ;
+
+       odata->eos = 0 ;
+
+       /* Weird stuff happens if these aren't called. */
+       ogg_stream_reset (&odata->ostream) ;
+       ogg_sync_reset (&odata->osync) ;
+
+       /*
+       **      Grab some data at the head of the stream.  We want the first page
+       **      (which is guaranteed to be small and only contain the Vorbis
+       **      stream initial header) We need the first page to get the stream
+       **      serialno.
+       */
+
+       /* Expose the buffer */
+       buffer = ogg_sync_buffer (&odata->osync, 4096L) ;
+
+       /* Grab the part of the header that has already been read. */
+       memcpy (buffer, psf->header, psf->headindex) ;
+       bytes = psf->headindex ;
+
+       /* Submit a 4k block to libvorbis' Ogg layer */
+       bytes += psf_fread (buffer + psf->headindex, 1, 4096 - psf->headindex, psf) ;
+       ogg_sync_wrote (&odata->osync, bytes) ;
+
+       /* Get the first page. */
+       if ((nn = ogg_sync_pageout (&odata->osync, &odata->opage)) != 1)
+       {
+               /* Have we simply run out of data?  If so, we're done. */
+               if (bytes < 4096)
+                       return 0 ;
+
+               /* Error case.  Must not be Vorbis data */
+               psf_log_printf (psf, "Input does not appear to be an Ogg bitstream.\n") ;
+               return SFE_MALFORMED_FILE ;
+               } ;
+
+       /*
+       **      Get the serial number and set up the rest of decode.
+       **      Serialno first ; use it to set up a logical stream.
+       */
+       ogg_stream_clear (&odata->ostream) ;
+       ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ;
+
+       if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0)
+       {       /* Error ; stream version mismatch perhaps. */
+               psf_log_printf (psf, "Error reading first page of Ogg bitstream data\n") ;
+               return SFE_MALFORMED_FILE ;
+               } ;
+
+       if (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1)
+       {       /* No page? must not be vorbis. */
+               psf_log_printf (psf, "Error reading initial header packet.\n") ;
+               return SFE_MALFORMED_FILE ;
+               } ;
+
+       /*
+       **      This function (vorbis_read_header) gets called multiple times, so the OGG
+       **      and vorbis structs have to be cleared every time we pass through to
+       **      prevent memory leaks.
+       */
+       vorbis_block_clear (&vdata->vblock) ;
+       vorbis_dsp_clear (&vdata->vdsp) ;
+       vorbis_comment_clear (&vdata->vcomment) ;
+       vorbis_info_clear (&vdata->vinfo) ;
+
+       /*
+       **      Extract the initial header from the first page and verify that the
+       **      Ogg bitstream is in fact Vorbis data.
+       **
+       **      I handle the initial header first instead of just having the code
+       **      read all three Vorbis headers at once because reading the initial
+       **      header is an easy way to identify a Vorbis bitstream and it's
+       **      useful to see that functionality seperated out.
+       */
+       vorbis_info_init (&vdata->vinfo) ;
+       vorbis_comment_init (&vdata->vcomment) ;
+
+       if (vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) < 0)
+       {       /* Error case ; not a vorbis header. */
+               psf_log_printf (psf, "Found Vorbis in stream header, but vorbis_synthesis_headerin failed.\n") ;
+               return SFE_MALFORMED_FILE ;
+               } ;
+
+       /*
+       **      Common Ogg metadata fields?
+       **      TITLE, VERSION, ALBUM, TRACKNUMBER, ARTIST, PERFORMER, COPYRIGHT, LICENSE,
+       **      ORGANIZATION, DESCRIPTION, GENRE, DATE, LOCATION, CONTACT, ISRC,
+       */
+
+       if (log_data)
+       {       int k ;
+
+               for (k = 0 ; k < ARRAY_LEN (vorbis_metatypes) ; k++)
+               {       char *dd ;
+
+                       dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [k].name, 0) ;
+                       if (dd == NULL)
+                               continue ;
+                       psf_store_string (psf, vorbis_metatypes [k].id, dd) ;
+                       } ;
+               } ;
+
+       /*
+       **      At this point, we're sure we're Vorbis. We've set up the logical (Ogg)
+       **      bitstream decoder. Get the comment and codebook headers and set up the
+       **      Vorbis decoder.
+       **
+       **      The next two packets in order are the comment and codebook headers.
+       **      They're likely large and may span multiple pages.  Thus we reead
+       **      and submit data until we get our two pacakets, watching that no
+       **      pages are missing.  If a page is missing, error out ; losing a
+       **      header page is the only place where missing data is fatal.
+       */
+
+       i = 0 ;                  /* Count of number of packets read */
+       while (i < 2)
+       {       int result = ogg_sync_pageout (&odata->osync, &odata->opage) ;
+               if (result == 0)
+               {       /* Need more data */
+                       buffer = ogg_sync_buffer (&odata->osync, 4096) ;
+                       bytes = psf_fread (buffer, 1, 4096, psf) ;
+
+                       if (bytes == 0 && i < 2)
+                       {       psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ;
+                               return SFE_MALFORMED_FILE ;
+                               } ;
+                       nn = ogg_sync_wrote (&odata->osync, bytes) ;
+                       }
+               else if (result == 1)
+               {       /*
+                       **      Don't complain about missing or corrupt data yet. We'll
+                       **      catch it at the packet output phase.
+                       **
+                       **      We can ignore any errors here as they'll also become apparent
+                       **      at packetout.
+                       */
+                       nn = ogg_stream_pagein (&odata->ostream, &odata->opage) ;
+                       while (i < 2)
+                       {       result = ogg_stream_packetout (&odata->ostream, &odata->opacket) ;
+                               if (result == 0)
+                                       break ;
+                               if (result < 0)
+                               {       /*      Uh oh ; data at some point was corrupted or missing!
+                                       **      We can't tolerate that in a header. Die. */
+                                       psf_log_printf (psf, "Corrupt secondary header. Exiting.\n") ;
+                                       return SFE_MALFORMED_FILE ;
+                                       } ;
+
+                               vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) ;
+                               i++ ;
+                               } ;
+                       } ;
+               } ;
+
+       if (log_data)
+       {       int printed_metadata_msg = 0 ;
+               int k ;
+
+               psf_log_printf (psf, "Bitstream is %d channel, %D Hz\n", vdata->vinfo.channels, vdata->vinfo.rate) ;
+               psf_log_printf (psf, "Encoded by : %s\n", vdata->vcomment.vendor) ;
+
+               /* Throw the comments plus a few lines about the bitstream we're decoding. */
+               for (k = 0 ; k < ARRAY_LEN (vorbis_metatypes) ; k++)
+               {       char *dd ;
+
+                       dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [k].name, 0) ;
+                       if (dd == NULL)
+                               continue ;
+
+                       if (printed_metadata_msg == 0)
+                       {       psf_log_printf (psf, "Metadata :\n") ;
+                               printed_metadata_msg = 1 ;
+                               } ;
+
+                       psf_store_string (psf, vorbis_metatypes [k].id, dd) ;
+                       psf_log_printf (psf, "  %-10s : %s\n", vorbis_metatypes [k].name, dd) ;
+                       } ;
+
+               psf_log_printf (psf, "End\n") ;
+               } ;
+
+       psf->sf.samplerate      = vdata->vinfo.rate ;
+       psf->sf.channels        = vdata->vinfo.channels ;
+       psf->sf.format          = SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
+
+       /*      OK, got and parsed all three headers. Initialize the Vorbis
+       **      packet->PCM decoder.
+       **      Central decode state. */
+       vorbis_synthesis_init (&vdata->vdsp, &vdata->vinfo) ;
+
+       /*      Local state for most of the decode so multiple block decodes can
+       **      proceed in parallel. We could init multiple vorbis_block structures
+       **      for vd here. */
+       vorbis_block_init (&vdata->vdsp, &vdata->vblock) ;
+
+       vdata->loc = 0 ;
+
+       return 0 ;
+} /* vorbis_read_header */
+
+static int
+vorbis_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
+{
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+       int k, ret ;
+
+       vorbis_info_init (&vdata->vinfo) ;
+
+       /* The style of encoding should be selectable here, VBR quality mode. */
+       ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ;
+
+#if 0
+       ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */
+       ret = ( vorbis_encode_setup_managed (&vdata->vinfo, psf->sf.channels,
+                                                psf->sf.samplerate, -1, 128000, -1) ||
+               vorbis_encode_ctl (&vdata->vinfo, OV_ECTL_RATEMANAGE_AVG, NULL) ||
+               vorbis_encode_setup_init (&vdata->vinfo)) ;
+#endif
+       if (ret)
+               return SFE_BAD_OPEN_FORMAT ;
+
+       vdata->loc = 0 ;
+
+       /* add a comment */
+       vorbis_comment_init (&vdata->vcomment) ;
+
+       vorbis_comment_add_tag (&vdata->vcomment, "ENCODER", "libsndfile") ;
+       for (k = 0 ; k < SF_MAX_STRINGS ; k++)
+       {       const char * name ;
+
+               if (psf->strings [k].type == 0)
+                       break ;
+
+               switch (psf->strings [k].type)
+               {       case SF_STR_TITLE :             name = "TITLE" ; break ;
+                       case SF_STR_COPYRIGHT : name = "COPYRIGHT" ; break ;
+                       case SF_STR_SOFTWARE :  name = "SOFTWARE" ; break ;
+                       case SF_STR_ARTIST :    name = "ARTIST" ; break ;
+                       case SF_STR_COMMENT :   name = "COMMENT" ; break ;
+                       case SF_STR_DATE :              name = "DATE" ; break ;
+                       case SF_STR_ALBUM :             name = "ALBUM" ; break ;
+                       case SF_STR_LICENSE :   name = "LICENSE" ; break ;
+                       default : continue ;
+                       } ;
+
+               vorbis_comment_add_tag (&vdata->vcomment, name, psf->strings [k].str) ;
+               } ;
+
+       /* set up the analysis state and auxiliary encoding storage */
+       vorbis_analysis_init (&vdata->vdsp, &vdata->vinfo) ;
+       vorbis_block_init (&vdata->vdsp, &vdata->vblock) ;
+
+       /*
+       **      Set up our packet->stream encoder.
+       **      Pick a random serial number ; that way we can more likely build
+       **      chained streams just by concatenation.
+       */
+
+       ogg_stream_init (&odata->ostream, psf_rand_int32 ()) ;
+
+       /* Vorbis streams begin with three headers ; the initial header (with
+          most of the codec setup parameters) which is mandated by the Ogg
+          bitstream spec.  The second header holds any comment fields.  The
+          third header holds the bitstream codebook.  We merely need to
+          make the headers, then pass them to libvorbis one at a time ;
+          libvorbis handles the additional Ogg bitstream constraints */
+
+       {       ogg_packet header ;
+               ogg_packet header_comm ;
+               ogg_packet header_code ;
+               int result ;
+
+               vorbis_analysis_headerout (&vdata->vdsp, &vdata->vcomment, &header, &header_comm, &header_code) ;
+               ogg_stream_packetin (&odata->ostream, &header) ; /* automatically placed in its own page */
+               ogg_stream_packetin (&odata->ostream, &header_comm) ;
+               ogg_stream_packetin (&odata->ostream, &header_code) ;
+
+               /* This ensures the actual
+                * audio data will start on a new page, as per spec
+                */
+               while ((result = ogg_stream_flush (&odata->ostream, &odata->opage)) != 0)
+               {       psf_fwrite (odata->opage.header, 1, odata->opage.header_len, psf) ;
+                       psf_fwrite (odata->opage.body, 1, odata->opage.body_len, psf) ;
+                       } ;
+       }
+
+       return 0 ;
+} /* vorbis_write_header */
+
+static int
+vorbis_close (SF_PRIVATE *psf)
+{      OGG_PRIVATE* odata = psf->container_data ;
+       VORBIS_PRIVATE *vdata = psf->codec_data ;
+
+       if (odata == NULL || vdata == NULL)
+               return 0 ;
+
+       /*      Clean up this logical bitstream ; before exit we shuld see if we're
+       **      followed by another [chained]. */
+
+       if (psf->file.mode == SFM_WRITE)
+       {
+               if (psf->write_current <= 0)
+                       vorbis_write_header (psf, 0) ;
+
+               vorbis_analysis_wrote (&vdata->vdsp, 0) ;
+               while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1)
+               {
+
+               /* analysis, assume we want to use bitrate management */
+                       vorbis_analysis (&vdata->vblock, NULL) ;
+                       vorbis_bitrate_addblock (&vdata->vblock) ;
+
+                       while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket))
+                       {       /* weld the packet into the bitstream */
+                               ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
+
+                               /* write out pages (if any) */
+                               while (!odata->eos)
+                               {       int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ;
+                                       if (result == 0) break ;
+                                       psf_fwrite (odata->opage.header, 1, odata->opage.header_len, psf) ;
+                                       psf_fwrite (odata->opage.body, 1, odata->opage.body_len, psf) ;
+
+               /* this could be set above, but for illustrative purposes, I do
+                  it here (to show that vorbis does know where the stream ends) */
+
+                                       if (ogg_page_eos (&odata->opage)) odata->eos = 1 ;
+                               }
+                       }
+               }
+       }
+
+       /* ogg_page and ogg_packet structs always point to storage in
+          libvorbis.  They are never freed or manipulated directly */
+
+       vorbis_block_clear (&vdata->vblock) ;
+       vorbis_dsp_clear (&vdata->vdsp) ;
+       vorbis_comment_clear (&vdata->vcomment) ;
+       vorbis_info_clear (&vdata->vinfo) ;
+
+       return 0 ;
+} /* vorbis_close */
+
+int
+ogg_vorbis_open (SF_PRIVATE *psf)
+{      OGG_PRIVATE* odata = psf->container_data ;
+       VORBIS_PRIVATE* vdata = calloc (1, sizeof (VORBIS_PRIVATE)) ;
+       int     error = 0 ;
+
+       if (odata == NULL)
+       {       psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ;
+               return SFE_INTERNAL ;
+               } ;
+
+       psf->codec_data = vdata ;
+
+       if (psf->file.mode == SFM_RDWR)
+               return SFE_BAD_MODE_RW ;
+
+       psf_log_printf (psf, "Vorbis library version : %s\n", vorbis_version_string ()) ;
+
+       if (psf->file.mode == SFM_READ)
+       {       /* Call this here so it only gets called once, so no memory is leaked. */
+               ogg_sync_init (&odata->osync) ;
+
+               if ((error = vorbis_read_header (psf, 1)))
+                       return error ;
+
+               psf->read_short         = vorbis_read_s ;
+               psf->read_int           = vorbis_read_i ;
+               psf->read_float         = vorbis_read_f ;
+               psf->read_double        = vorbis_read_d ;
+               psf->sf.frames          = vorbis_length (psf) ;
+               } ;
+
+       psf->codec_close = vorbis_close ;
+       if (psf->file.mode == SFM_WRITE)
+       {
+               /* Set the default vorbis quality here. */
+               vdata->quality = 0.4 ;
+
+               psf->write_header       = vorbis_write_header ;
+               psf->write_short        = vorbis_write_s ;
+               psf->write_int          = vorbis_write_i ;
+               psf->write_float        = vorbis_write_f ;
+               psf->write_double       = vorbis_write_d ;
+
+               psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */
+               psf->str_flags = SF_STR_ALLOW_START ;
+               } ;
+
+       psf->bytewidth = 1 ;
+       psf->blockwidth = psf->bytewidth * psf->sf.channels ;
+
+       psf->seek = vorbis_seek ;
+       psf->command = vorbis_command ;
+
+       /* FIXME, FIXME, FIXME : Hack these here for now and correct later. */
+       psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
+       psf->sf.sections = 1 ;
+
+       psf->datalength = 1 ;
+       psf->dataoffset = 0 ;
+       /* End FIXME. */
+
+       return error ;
+} /* ogg_vorbis_open */
+
+static int
+vorbis_command (SF_PRIVATE *psf, int command, void * data, int datasize)
+{      VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+
+       switch (command)
+       {       case SFC_SET_VBR_ENCODING_QUALITY :
+                       if (data == NULL || datasize != sizeof (double))
+                               return SF_FALSE ;
+
+                       if (psf->have_written)
+                               return SF_FALSE ;
+
+                       vdata->quality = *((double *) data) ;
+
+                       /* Clip range. */
+                       vdata->quality = SF_MAX (0.0, SF_MIN (1.0, vdata->quality)) ;
+
+                       psf_log_printf (psf, "%s : Setting SFC_SET_VBR_ENCODING_QUALITY to %f.\n", __func__, vdata->quality) ;
+                       return SF_TRUE ;
+
+               default :
+                       return SF_FALSE ;
+               } ;
+
+       return SF_FALSE ;
+} /* vorbis_command */
+
+static int
+vorbis_rnull (int samples, void *UNUSED (vptr), int UNUSED (off) , int channels, float **UNUSED (pcm))
+{
+       return samples * channels ;
+} /* vorbis_rnull */
+
+static int
+vorbis_rshort (int samples, void *vptr, int off, int channels, float **pcm)
+{
+       short *ptr = (short*) vptr + off ;
+       int i = 0, j, n ;
+       for (j = 0 ; j < samples ; j++)
+               for (n = 0 ; n < channels ; n++)
+                       ptr [i++] = lrintf (pcm [n][j] * 32767.0f) ;
+       return i ;
+} /* vorbis_rshort */
+
+static int
+vorbis_rint (int samples, void *vptr, int off, int channels, float **pcm)
+{
+       int *ptr = (int*) vptr + off ;
+       int i = 0, j, n ;
+
+       for (j = 0 ; j < samples ; j++)
+               for (n = 0 ; n < channels ; n++)
+                       ptr [i++] = lrintf (pcm [n][j] * 2147483647.0f) ;
+       return i ;
+} /* vorbis_rint */
+
+static int
+vorbis_rfloat (int samples, void *vptr, int off, int channels, float **pcm)
+{
+       float *ptr = (float*) vptr + off ;
+       int i = 0, j, n ;
+       for (j = 0 ; j < samples ; j++)
+               for (n = 0 ; n < channels ; n++)
+                       ptr [i++] = pcm [n][j] ;
+       return i ;
+} /* vorbis_rfloat */
+
+static int
+vorbis_rdouble (int samples, void *vptr, int off, int channels, float **pcm)
+{
+       double *ptr = (double*) vptr + off ;
+       int i = 0, j, n ;
+       for (j = 0 ; j < samples ; j++)
+               for (n = 0 ; n < channels ; n++)
+                       ptr [i++] = pcm [n][j] ;
+       return i ;
+} /* vorbis_rdouble */
+
+
+static sf_count_t
+vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn)
+{
+       VORBIS_PRIVATE *vdata = psf->codec_data ;
+       OGG_PRIVATE *odata = psf->container_data ;
+       int len, samples, i = 0 ;
+       float **pcm ;
+
+       len = lens / psf->sf.channels ;
+
+       while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0)
+       {       if (samples > len) samples = len ;
+               i += transfn (samples, ptr, i, psf->sf.channels, pcm) ;
+               len -= samples ;
+               /* tell libvorbis how many samples we actually consumed */
+               vorbis_synthesis_read (&vdata->vdsp, samples) ;
+               vdata->loc += samples ;
+               if (len == 0)
+                       return i ; /* Is this necessary */
+       }
+       goto start0 ;            /* Jump into the nasty nest */
+       while (len > 0 && !odata->eos)
+       {
+               while (len > 0 && !odata->eos)
+               {       int result = ogg_sync_pageout (&odata->osync, &odata->opage) ;
+                       if (result == 0) break ; /* need more data */
+                       if (result < 0)
+                       {       /* missing or corrupt data at this page position */
+                               psf_log_printf (psf, "Corrupt or missing data in bitstream ; continuing...\n") ;
+                               }
+                       else
+                       {       /* can safely ignore errors at this point */
+                               ogg_stream_pagein (&odata->ostream, &odata->opage) ;
+                       start0:
+                               while (1)
+                               {       result = ogg_stream_packetout (&odata->ostream, &odata->opacket) ;
+                                       if (result == 0)
+                                               break ; /* need more data */
+                                       if (result < 0)
+                                       {       /* missing or corrupt data at this page position */
+                                               /* no reason to complain ; already complained above */
+                                               }
+                                       else
+                                       {       /* we have a packet.    Decode it */
+                                               if (vorbis_synthesis (&vdata->vblock, &odata->opacket) == 0) /* test for success! */
+                                                       vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ;
+                 /*
+                 **pcm is a multichannel float vector.  In stereo, for
+                 example, pcm [0] is left, and pcm [1] is right.        samples is
+                 the size of each channel.      Convert the float values
+                 (-1.<=range<=1.) to whatever PCM format and write it out */
+
+                                               while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0)
+                                               {       if (samples>len) samples = len ;
+                                                       i += transfn (samples, ptr, i, psf->sf.channels, pcm) ;
+                                                       len -= samples ;
+                                                       /* tell libvorbis how many samples we actually consumed */
+                                                       vorbis_synthesis_read (&vdata->vdsp, samples) ;
+                                                       vdata->loc += samples ;
+                                                       if (len == 0)
+                                                               return i ; /* Is this necessary */
+                                                       } ;
+                                       }
+                               }
+                               if (ogg_page_eos (&odata->opage)) odata->eos = 1 ;
+                       }
+               }
+               if (!odata->eos)
+               {       char *buffer ;
+                       int bytes ;
+                       buffer = ogg_sync_buffer (&odata->osync, 4096) ;
+                       bytes = psf_fread (buffer, 1, 4096, psf) ;
+                       ogg_sync_wrote (&odata->osync, bytes) ;
+                       if (bytes == 0) odata->eos = 1 ;
+               }
+       }
+       return i ;
+} /* vorbis_read_sample */
+
+static sf_count_t
+vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t lens)
+{      return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rshort) ;
+} /* vorbis_read_s */
+
+static sf_count_t
+vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t lens)
+{      return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rint) ;
+} /* vorbis_read_i */
+
+static sf_count_t
+vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t lens)
+{      return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rfloat) ;
+} /* vorbis_read_f */
+
+static sf_count_t
+vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t lens)
+{      return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rdouble) ;
+} /* vorbis_read_d */
+
+/*==============================================================================
+*/
+
+static void
+vorbis_write_samples (SF_PRIVATE *psf, OGG_PRIVATE *odata, VORBIS_PRIVATE *vdata, int in_frames)
+{
+       vorbis_analysis_wrote (&vdata->vdsp, in_frames) ;
+
+       /*
+       **      Vorbis does some data preanalysis, then divvies up blocks for
+       **      more involved (potentially parallel) processing. Get a single
+       **      block for encoding now.
+       */
+       while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1)
+       {
+               /* analysis, assume we want to use bitrate management */
+               vorbis_analysis (&vdata->vblock, NULL) ;
+               vorbis_bitrate_addblock (&vdata->vblock) ;
+
+               while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket))
+               {
+                       /* weld the packet into the bitstream */
+                       ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
+
+                       /* write out pages (if any) */
+                       while (!odata->eos)
+                       {       int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ;
+                               if (result == 0)
+                                       break ;
+                               psf_fwrite (odata->opage.header, 1, odata->opage.header_len, psf) ;
+                               psf_fwrite (odata->opage.body, 1, odata->opage.body_len, psf) ;
+
+                               /*      This could be set above, but for illustrative purposes, I do
+                               **      it here (to show that vorbis does know where the stream ends) */
+                               if (ogg_page_eos (&odata->opage))
+                                       odata->eos = 1 ;
+                               } ;
+                       } ;
+               } ;
+
+       vdata->loc += in_frames ;
+} /* vorbis_write_data */
+
+
+static sf_count_t
+vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t lens)
+{
+       int i, m, j = 0 ;
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+       int in_frames = lens / psf->sf.channels ;
+       float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+       for (i = 0 ; i < in_frames ; i++)
+               for (m = 0 ; m < psf->sf.channels ; m++)
+                       buffer [m][i] = (float) (ptr [j++]) / 32767.0f ;
+
+       vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+       return lens ;
+} /* vorbis_write_s */
+
+static sf_count_t
+vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t lens)
+{      int i, m, j = 0 ;
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+       int in_frames = lens / psf->sf.channels ;
+       float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+       for (i = 0 ; i < in_frames ; i++)
+               for (m = 0 ; m < psf->sf.channels ; m++)
+                       buffer [m][i] = (float) (ptr [j++]) / 2147483647.0f ;
+
+       vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+       return lens ;
+} /* vorbis_write_i */
+
+static sf_count_t
+vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t lens)
+{      int i, m, j = 0 ;
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+       int in_frames = lens / psf->sf.channels ;
+       float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+       for (i = 0 ; i < in_frames ; i++)
+               for (m = 0 ; m < psf->sf.channels ; m++)
+                       buffer [m][i] = ptr [j++] ;
+
+       vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+       return lens ;
+} /* vorbis_write_f */
+
+static sf_count_t
+vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t lens)
+{      int i, m, j = 0 ;
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+       int in_frames = lens / psf->sf.channels ;
+       float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
+       for (i = 0 ; i < in_frames ; i++)
+               for (m = 0 ; m < psf->sf.channels ; m++)
+                       buffer [m][i] = (float) ptr [j++] ;
+
+       vorbis_write_samples (psf, odata, vdata, in_frames) ;
+
+       return lens ;
+} /* vorbis_write_d */
+
+static sf_count_t
+vorbis_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset)
+{
+       OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
+       VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
+
+       if (odata == NULL || vdata == NULL)
+               return 0 ;
+
+       if (offset < 0)
+       {       psf->error = SFE_BAD_SEEK ;
+               return ((sf_count_t) -1) ;
+               } ;
+
+       if (psf->file.mode == SFM_READ)
+       {       sf_count_t target = offset - vdata->loc ;
+
+               if (target < 0)
+               {       /* 12 to allow for OggS bit */
+                       psf_fseek (psf, 12, SEEK_SET) ;
+                       vorbis_read_header (psf, 0) ; /* Reset state */
+                       target = offset ;
+                       } ;
+
+               while (target > 0)
+               {       sf_count_t m = target > 4096 ? 4096 : target ;
+
+                       /*
+                       **      Need to multiply by channels here because the seek is done in
+                       **      terms of frames and the read function is done in terms of
+                       **      samples.
+                       */
+                       vorbis_read_sample (psf, (void *) NULL, m * psf->sf.channels, vorbis_rnull) ;
+
+                       target -= m ;
+                       } ;
+
+               return vdata->loc ;
+               } ;
+
+       return 0 ;
+} /* vorbis_seek */
+
+/*==============================================================================
+**     Most of the following code was snipped from Mike Smith's ogginfo utility
+**     which is part of vorbis-tools.
+**     Vorbis tools is released under the GPL but Mike has kindly allowed the
+**     following to be relicensed as LGPL for libsndfile.
+*/
+
+typedef struct
+{
+       int isillegal ;
+       int shownillegal ;
+       int isnew ;
+       int end ;
+
+       uint32_t serial ; /* must be 32 bit unsigned */
+       ogg_stream_state ostream ;
+
+       vorbis_info vinfo ;
+       vorbis_comment vcomment ;
+       sf_count_t lastgranulepos ;
+       int doneheaders ;
+} stream_processor ;
+
+typedef struct
+{
+       stream_processor *streams ;
+       int allocated ;
+       int used ;
+       int in_headers ;
+} stream_set ;
+
+static stream_set *
+create_stream_set (void)
+{      stream_set *set = calloc (1, sizeof (stream_set)) ;
+
+       set->streams = calloc (5, sizeof (stream_processor)) ;
+       set->allocated = 5 ;
+       set->used = 0 ;
+
+       return set ;
+} /* create_stream_set */
+
+static void
+vorbis_end (stream_processor *stream, sf_count_t * len)
+{      *len += stream->lastgranulepos ;
+       vorbis_comment_clear (&stream->vcomment) ;
+       vorbis_info_clear (&stream->vinfo) ;
+} /* vorbis_end */
+
+static void
+free_stream_set (stream_set *set, sf_count_t * len)
+{      int i ;
+
+       for (i = 0 ; i < set->used ; i++)
+       {       if (!set->streams [i].end)
+                       vorbis_end (&set->streams [i], len) ;
+               ogg_stream_clear (&set->streams [i].ostream) ;
+               } ;
+
+       free (set->streams) ;
+       free (set) ;
+} /* free_stream_set */
+
+static int
+streams_open (stream_set *set)
+{      int i, res = 0 ;
+
+       for (i = 0 ; i < set->used ; i++)
+               if (!set->streams [i].end)
+                       res ++ ;
+       return res ;
+} /* streams_open */
+
+static stream_processor *
+find_stream_processor (stream_set *set, ogg_page *page)
+{      uint32_t serial = ogg_page_serialno (page) ;
+       int i, invalid = 0 ;
+
+       stream_processor *stream ;
+
+       for (i = 0 ; i < set->used ; i++)
+       {
+               if (serial == set->streams [i].serial)
+               {       /* We have a match! */
+                       stream = & (set->streams [i]) ;
+
+                       set->in_headers = 0 ;
+                       /* if we have detected EOS, then this can't occur here. */
+                       if (stream->end)
+                       {       stream->isillegal = 1 ;
+                               return stream ;
+                               }
+
+                       stream->isnew = 0 ;
+                       stream->end = ogg_page_eos (page) ;
+                       stream->serial = serial ;
+                       return stream ;
+                       } ;
+               } ;
+
+       /* If there are streams open, and we've reached the end of the
+       ** headers, then we can't be starting a new stream.
+       ** XXX: might this sometimes catch ok streams if EOS flag is missing,
+       ** but the stream is otherwise ok?
+       */
+       if (streams_open (set) && !set->in_headers)
+               invalid = 1 ;
+
+       set->in_headers = 1 ;
+
+       if (set->allocated < set->used)
+               stream = &set->streams [set->used] ;
+       else
+       {       set->allocated += 5 ;
+               set->streams = realloc (set->streams, sizeof (stream_processor) * set->allocated) ;
+               stream = &set->streams [set->used] ;
+               } ;
+
+       set->used++ ;
+
+       stream->isnew = 1 ;
+       stream->isillegal = invalid ;
+
+       {
+               int res ;
+               ogg_packet packet ;
+
+               /* We end up processing the header page twice, but that's ok. */
+               ogg_stream_init (&stream->ostream, serial) ;
+               ogg_stream_pagein (&stream->ostream, page) ;
+               res = ogg_stream_packetout (&stream->ostream, &packet) ;
+               if (res <= 0)
+                       return NULL ;
+               else if (packet.bytes >= 7 && memcmp (packet.packet, "\x01vorbis", 7) == 0)
+               {
+                       stream->lastgranulepos = 0 ;
+                       vorbis_comment_init (&stream->vcomment) ;
+                       vorbis_info_init (&stream->vinfo) ;
+                       } ;
+
+               res = ogg_stream_packetout (&stream->ostream, &packet) ;
+
+               /* re-init, ready for processing */
+               ogg_stream_clear (&stream->ostream) ;
+               ogg_stream_init (&stream->ostream, serial) ;
+       }
+
+       stream->end = ogg_page_eos (page) ;
+       stream->serial = serial ;
+
+       return stream ;
+} /* find_stream_processor */
+
+static int
+vorbis_length_get_next_page (SF_PRIVATE *psf, ogg_sync_state * osync, ogg_page *page)
+{      static const int CHUNK_SIZE = 4500 ;
+
+       while (ogg_sync_pageout (osync, page) <= 0)
+       {       char * buffer = ogg_sync_buffer (osync, CHUNK_SIZE) ;
+               int bytes = psf_fread (buffer, 1, 4096, psf) ;
+
+               if (bytes <= 0)
+               {       ogg_sync_wrote (osync, 0) ;
+                       return 0 ;
+                       } ;
+
+               ogg_sync_wrote (osync, bytes) ;
+               } ;
+
+       return 1 ;
+} /* vorbis_length_get_next_page */
+
+static sf_count_t
+vorbis_length_aux (SF_PRIVATE * psf)
+{
+       ogg_sync_state osync ;
+       ogg_page page ;
+       sf_count_t len = 0 ;
+       stream_set *processors ;
+
+       processors = create_stream_set () ;
+       if (processors == NULL)
+               return 0 ;      // out of memory?
+
+       ogg_sync_init (&osync) ;
+
+       while (vorbis_length_get_next_page (psf, &osync, &page))
+       {
+               stream_processor *p = find_stream_processor (processors, &page) ;
+
+               if (!p)
+               {       len = 0 ;
+                       break ;
+                       } ;
+
+               if (p->isillegal && !p->shownillegal)
+               {
+                       p->shownillegal = 1 ;
+                       /* If it's a new stream, we want to continue processing this page
+                       ** anyway to suppress additional spurious errors
+                       */
+                       if (!p->isnew) continue ;
+                       } ;
+
+               if (!p->isillegal)
+               {       ogg_packet packet ;
+                       int header = 0 ;
+
+                       ogg_stream_pagein (&p->ostream, &page) ;
+                       if (p->doneheaders < 3)
+                               header = 1 ;
+
+                       while (ogg_stream_packetout (&p->ostream, &packet) > 0)
+                       {
+                               if (p->doneheaders < 3)
+                               {       if (vorbis_synthesis_headerin (&p->vinfo, &p->vcomment, &packet) < 0)
+                                               continue ;
+                                       p->doneheaders ++ ;
+                                       } ;
+                               } ;
+                       if (!header)
+                       {       sf_count_t gp = ogg_page_granulepos (&page) ;
+                               if (gp > 0) p->lastgranulepos = gp ;
+                               } ;
+                       if (p->end)
+                       {       vorbis_end (p, &len) ;
+                               p->isillegal = 1 ;
+                               } ;
+                       } ;
+               } ;
+
+       ogg_sync_clear (&osync) ;
+       free_stream_set (processors, &len) ;
+
+       return len ;
+} /* vorbis_length_aux */
+
+static sf_count_t
+vorbis_length (SF_PRIVATE *psf)
+{      sf_count_t length ;
+       int error ;
+
+       if (psf->sf.seekable == 0)
+               return SF_COUNT_MAX ;
+
+       psf_fseek (psf, 0, SEEK_SET) ;
+       length = vorbis_length_aux (psf) ;
+
+       psf_fseek (psf, 12, SEEK_SET) ;
+       if ((error = vorbis_read_header (psf, 0)) != 0)
+               psf->error = error ;
+
+       return length ;
+} /* vorbis_length */
+
+#else /* HAVE_EXTERNAL_LIBS */
+
+int
+ogg_vorbis_open        (SF_PRIVATE *psf)
+{
+       psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ;
+       return SFE_UNIMPLEMENTED ;
+} /* ogg_vorbis_open */
+
+#endif