/*
** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
** 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 <vorbis/codec.h>
#include <vorbis/vorbisenc.h>

typedef int convert_func (int, void *, int, int, float **) ;

static int	ogg_read_header (SF_PRIVATE *psf, int log_data) ;
static int	ogg_write_header (SF_PRIVATE *psf, int calc_length) ;
static int	ogg_close (SF_PRIVATE *psf) ;
static int	ogg_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
static sf_count_t	ogg_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
static sf_count_t	ogg_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
static sf_count_t	ogg_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
static sf_count_t	ogg_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
static sf_count_t	ogg_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
static sf_count_t	ogg_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
static sf_count_t	ogg_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
static sf_count_t	ogg_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
static sf_count_t	ogg_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
static sf_count_t	ogg_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) ;
static sf_count_t	ogg_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" },
	{	SF_STR_TRACKNUMBER,	"Tracknumber" },
	{	SF_STR_GENRE,		"Genre" },
} ;

typedef struct
{	/* Sync and verify incoming physical bitstream */
	ogg_sync_state oy ;
	/* Take physical pages, weld into a logical stream of packets */
	ogg_stream_state os ;
	/* One Ogg bitstream page.  Vorbis packets are inside */
	ogg_page og ;
	/* One raw packet of data for decode */
	ogg_packet op ;
	int eos ;
} OGG_PRIVATE ;

typedef struct
{	/* Count current location */
	sf_count_t loc ;
	/* Struct that stores all the static vorbis bitstream settings */
	vorbis_info	vi ;
	/* Struct that stores all the bitstream user comments */
	vorbis_comment vc ;
	/* Ventral working state for the packet->PCM decoder */
	vorbis_dsp_state vd ;
	/* Local working space for packet->PCM decode */
	vorbis_block vb ;

	/* Encoding quality in range [0.0, 1.0]. */
	double quality ;
} VORBIS_PRIVATE ;

static int
ogg_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->os) ;
	ogg_sync_reset (&odata->oy) ;

	/*
	**	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->oy, 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->oy, bytes) ;

	/* Get the first page. */
	if ((nn = ogg_sync_pageout (&odata->oy, &odata->og)) != 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->os) ;
	ogg_stream_init (&odata->os, ogg_page_serialno (&odata->og)) ;

	/*
	**	This function (ogg_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->vb) ;
	vorbis_dsp_clear (&vdata->vd) ;
	vorbis_comment_clear (&vdata->vc) ;
	vorbis_info_clear (&vdata->vi) ;

	/*
	**	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->vi) ;
	vorbis_comment_init (&vdata->vc) ;

	if (ogg_stream_pagein (&odata->os, &odata->og) < 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->os, &odata->op) != 1)
	{	/* No page? must not be vorbis. */
		psf_log_printf (psf, "Error reading initial header packet.\n") ;
		return SFE_MALFORMED_FILE ;
		} ;

	if (vorbis_synthesis_headerin (&vdata->vi, &vdata->vc, &odata->op) < 0)
	{	/* Error case ; not a vorbis header. */
		psf_log_printf (psf, "This Ogg bitstream does not contain Vorbis audio data.\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->vc, 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->oy, &odata->og) ;
		if (result == 0)
		{	/* Need more data */
			buffer = ogg_sync_buffer (&odata->oy, 4096) ;
			bytes = psf_fread (buffer, 1, 4096, psf) ;

			if (bytes == 0)
			{	psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ;
				return SFE_MALFORMED_FILE ;
				} ;
			nn = ogg_sync_wrote (&odata->oy, 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->os, &odata->og) ;
			while (i < 2)
			{	result = ogg_stream_packetout (&odata->os, &odata->op) ;
				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->vi, &vdata->vc, &odata->op) ;
				i++ ;
				} ;
			} ;
		} ;

	if (log_data)
	{	int printed_metadata_msg = 0 ;
		int k ;

		psf_log_printf (psf, "\nBitstream is %d channel, %D Hz\n", vdata->vi.channels, vdata->vi.rate) ;
		psf_log_printf (psf, "Encoded by: %s\n", vdata->vc.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->vc, 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->vi.rate ;
	psf->sf.channels	= vdata->vi.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->vd, &vdata->vi) ;

	/*	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->vd, &vdata->vb) ;

	vdata->loc = 0 ;

	return 0 ;
} /* ogg_read_header */

static int
ogg_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->vi) ;

	/* The style of encoding should be selectable here, VBR quality mode. */
	ret = vorbis_encode_init_vbr (&vdata->vi, psf->sf.channels, psf->sf.samplerate, vdata->quality) ;

#if 0
	ret = vorbis_encode_init (&vdata->vi, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */
	ret = ( vorbis_encode_setup_managed (&vdata->vi, psf->sf.channels,
						 psf->sf.samplerate, -1, 128000, -1) ||
		vorbis_encode_ctl (&vdata->vi, OV_ECTL_RATEMANAGE_AVG, NULL) ||
		vorbis_encode_setup_init (&vdata->vi)) ;
#endif
	if (ret)
		return SFE_BAD_OPEN_FORMAT ;

	vdata->loc = 0 ;

	/* add a comment */
	vorbis_comment_init (&vdata->vc) ;

	vorbis_comment_add_tag (&vdata->vc, "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 ;
			case SF_STR_TRACKNUMBER :	name = "TRACKNUMBER" ; break ;
			case SF_STR_GENRE :			name = "GENRE" ; break ;
			default : continue ;
			} ;

		vorbis_comment_add_tag (&vdata->vc, name, psf->strings [k].str) ;
		} ;

	/* set up the analysis state and auxiliary encoding storage */
	vorbis_analysis_init (&vdata->vd, &vdata->vi) ;
	vorbis_block_init (&vdata->vd, &vdata->vb) ;

	/*
	**	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->os, 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->vd, &vdata->vc, &header, &header_comm, &header_code) ;
		ogg_stream_packetin (&odata->os, &header) ; /* automatically placed in its own page */
		ogg_stream_packetin (&odata->os, &header_comm) ;
		ogg_stream_packetin (&odata->os, &header_code) ;

		/* This ensures the actual
		 * audio data will start on a new page, as per spec
		 */
		while ((result = ogg_stream_flush (&odata->os, &odata->og)) != 0)
		{	psf_fwrite (odata->og.header, 1, odata->og.header_len, psf) ;
			psf_fwrite (odata->og.body, 1, odata->og.body_len, psf) ;
			} ;
	}

	return 0 ;
} /* ogg_write_header */

static int
ogg_close (SF_PRIVATE *psf)
{
	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) 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)
			ogg_write_header (psf, 0) ;

		vorbis_analysis_wrote (&vdata->vd, 0) ;
		while (vorbis_analysis_blockout (&vdata->vd, &vdata->vb) == 1)
		{

		/* analysis, assume we want to use bitrate management */
			vorbis_analysis (&vdata->vb, NULL) ;
			vorbis_bitrate_addblock (&vdata->vb) ;

			while (vorbis_bitrate_flushpacket (&vdata->vd, &odata->op))
			{	/* weld the packet into the bitstream */
				ogg_stream_packetin (&odata->os, &odata->op) ;

				/* write out pages (if any) */
				while (!odata->eos)
				{	int result = ogg_stream_pageout (&odata->os, &odata->og) ;
					if (result == 0) break ;
					psf_fwrite (odata->og.header, 1, odata->og.header_len, psf) ;
					psf_fwrite (odata->og.body, 1, odata->og.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->og)) 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->vb) ;
	vorbis_dsp_clear (&vdata->vd) ;
	vorbis_comment_clear (&vdata->vc) ;
	vorbis_info_clear (&vdata->vi) ;	 /* must be called last */
	/* should look here to reopen if chained */

	/* OK, clean up the framer */
	ogg_sync_clear (&odata->oy) ;
	ogg_stream_clear (&odata->os) ;

	return 0 ;
} /* ogg_close */

int
ogg_open (SF_PRIVATE *psf)
{	OGG_PRIVATE* odata = calloc (1, sizeof (OGG_PRIVATE)) ;
	VORBIS_PRIVATE* vdata = calloc (1, sizeof (VORBIS_PRIVATE)) ;
	int	error = 0 ;

	if (vdata == NULL || odata == NULL)
		return SFE_MALLOC_FAILED ;

	psf->container_data = odata ;
	psf->codec_data = vdata ;

	if (psf->file.mode == SFM_RDWR)
		return SFE_BAD_MODE_RW ;

#if HAVE_VORBIS_VERSION_STRING
	psf_log_printf (psf, "Vorbis library version : %s\n", vorbis_version_string ()) ;
#endif

	if (psf->file.mode == SFM_READ)
	{	/* Call this here so it only gets called once, so no memory is leaked. */
		ogg_sync_init (&odata->oy) ;

		if ((error = ogg_read_header (psf, 1)))
			return error ;

		psf->read_short		= ogg_read_s ;
		psf->read_int		= ogg_read_i ;
		psf->read_float		= ogg_read_f ;
		psf->read_double	= ogg_read_d ;
		psf->sf.frames		= ogg_length (psf) ;
		} ;

	psf->container_close = ogg_close ;
	if (psf->file.mode == SFM_WRITE)
	{
		/* Set the default vorbis quality here. */
		vdata->quality = 0.4 ;

		psf->write_header	= ogg_write_header ;
		psf->write_short	= ogg_write_s ;
		psf->write_int		= ogg_write_i ;
		psf->write_float	= ogg_write_f ;
		psf->write_double	= ogg_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 = ogg_seek ;
	psf->command = ogg_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_open */

static int
ogg_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 1 ;

			if (psf->have_written)
				return 1 ;

			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) ;
			break ;

		default :
			return 0 ;
		} ;

	return 0 ;
} /* ogg_command */

static int
ogg_rnull (int samples, void *UNUSED (vptr), int UNUSED (off) , int channels, float **UNUSED (pcm))
{
	return samples * channels ;
} /* ogg_rnull */

static int
ogg_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++)
		{	float value = pcm [n][j] ;

			if (CPU_CLIPS_POSITIVE == 0 && value >= 1.0)
			{	ptr [i++] = 0x7fff ;
				continue ;
				} ;
			if (CPU_CLIPS_NEGATIVE == 0 && value <= -1.0)
			{	ptr [i++] = 0x8000 ;
				continue ;
				} ;

			ptr [i++] = lrintf (value * 32767.0f) ;
			} ;

	return i ;
} /* ogg_rshort */

static int
ogg_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++)
		{	float value = pcm [n][j] ;

			if (CPU_CLIPS_POSITIVE == 0 && value >= 1.0)
			{	ptr [i++] = 0x7fffffff ;
				continue ;
				} ;
			if (CPU_CLIPS_NEGATIVE == 0 && value <= -1.0)
			{	ptr [i++] = 0x80000000 ;
				continue ;
				} ;

			ptr [i++] = lrintf (value * 2147483647.0f) ;
			} ;

	return i ;
} /* ogg_rint */

static int
ogg_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 ;
} /* ogg_rfloat */

static int
ogg_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 ;
} /* ogg_rdouble */


static sf_count_t
ogg_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 result, len, samples, i = 0 ;
	float **pcm ;

	len = lens / psf->sf.channels ;

	while ((samples = vorbis_synthesis_pcmout (&vdata->vd, &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->vd, 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)
		{	result = ogg_sync_pageout (&odata->oy, &odata->og) ;
			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->os, &odata->og) ;
			start0:
				while (1)
				{	result = ogg_stream_packetout (&odata->os, &odata->op) ;
					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->vb, &odata->op) == 0) /* test for success! */
							vorbis_synthesis_blockin (&vdata->vd, &vdata->vb) ;
						/*
						** The **pcm variable 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->vd, &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->vd, samples) ;
							vdata->loc += samples ;
							if (len == 0)
								return i ; /* Is this necessary */
							} ;
					}
				}
				if (ogg_page_eos (&odata->og)) odata->eos = 1 ;
			}
		}
		if (!odata->eos)
		{	char *buffer ;
			int bytes ;
			buffer = ogg_sync_buffer (&odata->oy, 4096) ;
			bytes = psf_fread (buffer, 1, 4096, psf) ;
			ogg_sync_wrote (&odata->oy, bytes) ;
			if (bytes == 0) odata->eos = 1 ;
		}
	}
	return i ;
} /* ogg_read_sample */

static sf_count_t
ogg_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t lens)
{	return ogg_read_sample (psf, (void*) ptr, lens, ogg_rshort) ;
} /* ogg_read_s */

static sf_count_t
ogg_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t lens)
{	return ogg_read_sample (psf, (void*) ptr, lens, ogg_rint) ;
} /* ogg_read_i */

static sf_count_t
ogg_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t lens)
{	return ogg_read_sample (psf, (void*) ptr, lens, ogg_rfloat) ;
} /* ogg_read_f */

static sf_count_t
ogg_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t lens)
{	return ogg_read_sample (psf, (void*) ptr, lens, ogg_rdouble) ;
} /* ogg_read_d */

/*==============================================================================
*/

static void
ogg_write_samples (SF_PRIVATE *psf, OGG_PRIVATE *odata, VORBIS_PRIVATE *vdata, int in_frames)
{
	vorbis_analysis_wrote (&vdata->vd, 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->vd, &vdata->vb) == 1)
	{
		/* analysis, assume we want to use bitrate management */
		vorbis_analysis (&vdata->vb, NULL) ;
		vorbis_bitrate_addblock (&vdata->vb) ;

		while (vorbis_bitrate_flushpacket (&vdata->vd, &odata->op))
		{
			/* weld the packet into the bitstream */
			ogg_stream_packetin (&odata->os, &odata->op) ;

			/* write out pages (if any) */
			while (!odata->eos)
			{	int result = ogg_stream_pageout (&odata->os, &odata->og) ;
				if (result == 0)
					break ;
				psf_fwrite (odata->og.header, 1, odata->og.header_len, psf) ;
				psf_fwrite (odata->og.body, 1, odata->og.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->og))
					odata->eos = 1 ;
				} ;
			} ;
		} ;

	vdata->loc += in_frames ;
} /* ogg_write_data */


static sf_count_t
ogg_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->vd, 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 ;

	ogg_write_samples (psf, odata, vdata, in_frames) ;

	return lens ;
} /* ogg_write_s */

static sf_count_t
ogg_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->vd, 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 ;

	ogg_write_samples (psf, odata, vdata, in_frames) ;

	return lens ;
} /* ogg_write_i */

static sf_count_t
ogg_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->vd, in_frames) ;
	for (i = 0 ; i < in_frames ; i++)
		for (m = 0 ; m < psf->sf.channels ; m++)
			buffer [m][i] = ptr [j++] ;

	ogg_write_samples (psf, odata, vdata, in_frames) ;

	return lens ;
} /* ogg_write_f */

static sf_count_t
ogg_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->vd, in_frames) ;
	for (i = 0 ; i < in_frames ; i++)
		for (m = 0 ; m < psf->sf.channels ; m++)
			buffer [m][i] = (float) ptr [j++] ;

	ogg_write_samples (psf, odata, vdata, in_frames) ;

	return lens ;
} /* ogg_write_d */

static sf_count_t
ogg_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) ;
			ogg_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.
			*/
			ogg_read_sample (psf, (void *) NULL, m * psf->sf.channels, ogg_rnull) ;

			target -= m ;
			} ;

		return vdata->loc ;
		} ;

	return 0 ;
} /* ogg_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 os ;

	vorbis_info vi ;
	vorbis_comment vc ;
	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)) ;

	if (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->vc) ;
	vorbis_info_clear (&stream->vi) ;
} /* 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].os) ;
		} ;

	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, found = 0 ;
	int invalid = 0 ;

	stream_processor *stream ;

	for (i = 0 ; i < set->used ; i++)
	{
		if (serial == set->streams [i].serial)
		{	/* We have a match! */
			found = 1 ;
			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->os, serial) ;
		ogg_stream_pagein (&stream->os, page) ;
		res = ogg_stream_packetout (&stream->os, &packet) ;
		if (res <= 0)
			return NULL ;
		else if (packet.bytes >= 7 && memcmp (packet.packet, "\x01vorbis", 7) == 0)
		{
			stream->lastgranulepos = 0 ;
			vorbis_comment_init (&stream->vc) ;
			vorbis_info_init (&stream->vi) ;
			} ;

		res = ogg_stream_packetout (&stream->os, &packet) ;

		/* re-init, ready for processing */
		ogg_stream_clear (&stream->os) ;
		ogg_stream_init (&stream->os, serial) ;
	}

	stream->end = ogg_page_eos (page) ;
	stream->serial = serial ;

	return stream ;
} /* find_stream_processor */

static int
ogg_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 ;
} /* ogg_length_get_next_page */

static sf_count_t
ogg_length_aux (SF_PRIVATE * psf)
{
	ogg_sync_state osync ;
	ogg_page page ;
	int gotpage = 0 ;
	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 (ogg_length_get_next_page (psf, &osync, &page))
	{
		stream_processor *p = find_stream_processor (processors, &page) ;
		gotpage = 1 ;

		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->os, &page) ;
			if (p->doneheaders < 3)
				header = 1 ;

			while (ogg_stream_packetout (&p->os, &packet) > 0)
			{
				if (p->doneheaders < 3)
				{	if (vorbis_synthesis_headerin (&p->vi, &p->vc, &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 ;
} /* ogg_length_aux */

static sf_count_t
ogg_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 = ogg_length_aux (psf) ;

	psf_fseek (psf, 12, SEEK_SET) ;
	if ((error = ogg_read_header (psf, 0)) != 0)
		psf->error = error ;

	return length ;
} /* ogg_length */

#else /* HAVE_EXTERNAL_LIBS */

int
ogg_open	(SF_PRIVATE *psf)
{
	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ;
	return SFE_UNIMPLEMENTED ;
} /* ogg_open */

#endif
