1b815c7f3Sopenharmony_ci/*
2b815c7f3Sopenharmony_ci** Copyright (C) 2018-2021 Arthur Taylor <art@ified.ca>
3b815c7f3Sopenharmony_ci** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
4b815c7f3Sopenharmony_ci** Copyright (C) 2002-2005 Michael Smith <msmith@xiph.org>
5b815c7f3Sopenharmony_ci** Copyright (C) 2007 John ffitch
6b815c7f3Sopenharmony_ci**
7b815c7f3Sopenharmony_ci** This program is free software ; you can redistribute it and/or modify
8b815c7f3Sopenharmony_ci** it under the terms of the GNU Lesser General Public License as published by
9b815c7f3Sopenharmony_ci** the Free Software Foundation ; either version 2.1 of the License, or
10b815c7f3Sopenharmony_ci** (at your option) any later version.
11b815c7f3Sopenharmony_ci**
12b815c7f3Sopenharmony_ci** This program is distributed in the hope that it will be useful,
13b815c7f3Sopenharmony_ci** but WITHOUT ANY WARRANTY ; without even the implied warranty of
14b815c7f3Sopenharmony_ci** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
15b815c7f3Sopenharmony_ci** GNU Lesser General Public License for more details.
16b815c7f3Sopenharmony_ci**
17b815c7f3Sopenharmony_ci** You should have received a copy of the GNU Lesser General Public License
18b815c7f3Sopenharmony_ci** along with this program ; if not, write to the Free Software
19b815c7f3Sopenharmony_ci** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20b815c7f3Sopenharmony_ci*/
21b815c7f3Sopenharmony_ci
22b815c7f3Sopenharmony_ci/*
23b815c7f3Sopenharmony_ci** Much of this code is based on the examples in libvorbis from the
24b815c7f3Sopenharmony_ci** XIPHOPHORUS Company http://www.xiph.org/ which has a BSD-style Licence
25b815c7f3Sopenharmony_ci** Copyright (c) 2002, Xiph.org Foundation
26b815c7f3Sopenharmony_ci**
27b815c7f3Sopenharmony_ci** Redistribution and use in source and binary forms, with or without
28b815c7f3Sopenharmony_ci** modification, are permitted provided that the following conditions
29b815c7f3Sopenharmony_ci** are met:
30b815c7f3Sopenharmony_ci**
31b815c7f3Sopenharmony_ci** - Redistributions of source code must retain the above copyright
32b815c7f3Sopenharmony_ci** notice, this list of conditions and the following disclaimer.
33b815c7f3Sopenharmony_ci**
34b815c7f3Sopenharmony_ci** - Redistributions in binary form must reproduce the above copyright
35b815c7f3Sopenharmony_ci** notice, this list of conditions and the following disclaimer in the
36b815c7f3Sopenharmony_ci** documentation and/or other materials provided with the distribution.
37b815c7f3Sopenharmony_ci**
38b815c7f3Sopenharmony_ci** - Neither the name of the Xiph.org Foundation nor the names of its
39b815c7f3Sopenharmony_ci** contributors may be used to endorse or promote products derived from
40b815c7f3Sopenharmony_ci** this software without specific prior written permission.
41b815c7f3Sopenharmony_ci**
42b815c7f3Sopenharmony_ci** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43b815c7f3Sopenharmony_ci** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44b815c7f3Sopenharmony_ci** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45b815c7f3Sopenharmony_ci** A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
46b815c7f3Sopenharmony_ci** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47b815c7f3Sopenharmony_ci** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48b815c7f3Sopenharmony_ci** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE,
49b815c7f3Sopenharmony_ci** DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50b815c7f3Sopenharmony_ci** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51b815c7f3Sopenharmony_ci** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52b815c7f3Sopenharmony_ci** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53b815c7f3Sopenharmony_ci*/
54b815c7f3Sopenharmony_ci
55b815c7f3Sopenharmony_ci#include "sfconfig.h"
56b815c7f3Sopenharmony_ci
57b815c7f3Sopenharmony_ci#include <stdio.h>
58b815c7f3Sopenharmony_ci#include <fcntl.h>
59b815c7f3Sopenharmony_ci#include <string.h>
60b815c7f3Sopenharmony_ci#include <ctype.h>
61b815c7f3Sopenharmony_ci#include <time.h>
62b815c7f3Sopenharmony_ci#include <math.h>
63b815c7f3Sopenharmony_ci
64b815c7f3Sopenharmony_ci#if HAVE_UNISTD_H
65b815c7f3Sopenharmony_ci#include <unistd.h>
66b815c7f3Sopenharmony_ci#else
67b815c7f3Sopenharmony_ci#include "sf_unistd.h"
68b815c7f3Sopenharmony_ci#endif
69b815c7f3Sopenharmony_ci
70b815c7f3Sopenharmony_ci#include "sndfile.h"
71b815c7f3Sopenharmony_ci#include "sfendian.h"
72b815c7f3Sopenharmony_ci#include "common.h"
73b815c7f3Sopenharmony_ci
74b815c7f3Sopenharmony_ci#if HAVE_EXTERNAL_XIPH_LIBS
75b815c7f3Sopenharmony_ci
76b815c7f3Sopenharmony_ci#include <ogg/ogg.h>
77b815c7f3Sopenharmony_ci#include <vorbis/codec.h>
78b815c7f3Sopenharmony_ci#include <vorbis/vorbisenc.h>
79b815c7f3Sopenharmony_ci
80b815c7f3Sopenharmony_ci#include "ogg.h"
81b815c7f3Sopenharmony_ci
82b815c7f3Sopenharmony_ci/* How many seconds in the future to not bother bisection searching for. */
83b815c7f3Sopenharmony_ci#define VORBIS_SEEK_THRESHOLD 2
84b815c7f3Sopenharmony_ci
85b815c7f3Sopenharmony_citypedef int convert_func (SF_PRIVATE *psf, int, void *, int, int, float **) ;
86b815c7f3Sopenharmony_ci
87b815c7f3Sopenharmony_cistatic int	vorbis_read_header (SF_PRIVATE *psf) ;
88b815c7f3Sopenharmony_cistatic int	vorbis_write_header (SF_PRIVATE *psf, int calc_length) ;
89b815c7f3Sopenharmony_cistatic int	vorbis_close (SF_PRIVATE *psf) ;
90b815c7f3Sopenharmony_cistatic int	vorbis_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
91b815c7f3Sopenharmony_cistatic int	vorbis_byterate (SF_PRIVATE *psf) ;
92b815c7f3Sopenharmony_cistatic int	vorbis_calculate_granulepos (SF_PRIVATE *psf, uint64_t *gp_out) ;
93b815c7f3Sopenharmony_cistatic int	vorbis_skip (SF_PRIVATE *psf, uint64_t target_gp) ;
94b815c7f3Sopenharmony_cistatic int	vorbis_seek_trysearch (SF_PRIVATE *psf, uint64_t target_gp) ;
95b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ;
96b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
97b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
98b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
99b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
100b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
101b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
102b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
103b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
104b815c7f3Sopenharmony_cistatic sf_count_t	vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) ;
105b815c7f3Sopenharmony_cistatic int	vorbis_rnull (SF_PRIVATE *psf, int samples, void *vptr, int off , int channels, float **pcm) ;
106b815c7f3Sopenharmony_ci
107b815c7f3Sopenharmony_citypedef struct
108b815c7f3Sopenharmony_ci{	int id ;
109b815c7f3Sopenharmony_ci	const char *name ;
110b815c7f3Sopenharmony_ci} STR_PAIRS ;
111b815c7f3Sopenharmony_ci
112b815c7f3Sopenharmony_ci
113b815c7f3Sopenharmony_ci/* See https://xiph.org/vorbis/doc/v-comment.html */
114b815c7f3Sopenharmony_cistatic STR_PAIRS vorbis_metatypes [] =
115b815c7f3Sopenharmony_ci{	{	SF_STR_TITLE,		"Title" },
116b815c7f3Sopenharmony_ci	{	SF_STR_COPYRIGHT,	"Copyright" },
117b815c7f3Sopenharmony_ci	{	SF_STR_SOFTWARE,	"Software" },
118b815c7f3Sopenharmony_ci	{	SF_STR_ARTIST,		"Artist" },
119b815c7f3Sopenharmony_ci	{	SF_STR_COMMENT,		"Comment" },
120b815c7f3Sopenharmony_ci	{	SF_STR_DATE,		"Date" },
121b815c7f3Sopenharmony_ci	{	SF_STR_ALBUM,		"Album" },
122b815c7f3Sopenharmony_ci	{	SF_STR_LICENSE,		"License" },
123b815c7f3Sopenharmony_ci	{	SF_STR_TRACKNUMBER,	"Tracknumber" },
124b815c7f3Sopenharmony_ci	{	SF_STR_GENRE,		"Genre" },
125b815c7f3Sopenharmony_ci} ;
126b815c7f3Sopenharmony_ci
127b815c7f3Sopenharmony_citypedef struct
128b815c7f3Sopenharmony_ci{	/* Current granule position. */
129b815c7f3Sopenharmony_ci	uint64_t gp ;
130b815c7f3Sopenharmony_ci	/* Struct that stores all the static vorbis bitstream settings */
131b815c7f3Sopenharmony_ci	vorbis_info	vinfo ;
132b815c7f3Sopenharmony_ci	/* Struct that stores all the bitstream user comments */
133b815c7f3Sopenharmony_ci	vorbis_comment vcomment ;
134b815c7f3Sopenharmony_ci	/* Central working state for the packet->PCM decoder */
135b815c7f3Sopenharmony_ci	vorbis_dsp_state vdsp ;
136b815c7f3Sopenharmony_ci	/* Local working space for packet->PCM decode */
137b815c7f3Sopenharmony_ci	vorbis_block vblock ;
138b815c7f3Sopenharmony_ci	/* Encoding quality in range [0.0, 1.0]. */
139b815c7f3Sopenharmony_ci	double quality ;
140b815c7f3Sopenharmony_ci	/* Offset of the first samples' granule position. */
141b815c7f3Sopenharmony_ci	uint64_t pcm_start ;
142b815c7f3Sopenharmony_ci	/* Last valid samples' granule position. */
143b815c7f3Sopenharmony_ci	uint64_t pcm_end ;
144b815c7f3Sopenharmony_ci	/* File offset of the start of the last page. */
145b815c7f3Sopenharmony_ci	sf_count_t last_page ;
146b815c7f3Sopenharmony_ci} VORBIS_PRIVATE ;
147b815c7f3Sopenharmony_ci
148b815c7f3Sopenharmony_cistatic int
149b815c7f3Sopenharmony_civorbis_read_header (SF_PRIVATE *psf)
150b815c7f3Sopenharmony_ci{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
151b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
152b815c7f3Sopenharmony_ci	int printed_metadata_msg = 0 ;
153b815c7f3Sopenharmony_ci	int i, nn ;
154b815c7f3Sopenharmony_ci	sf_count_t last_page ;
155b815c7f3Sopenharmony_ci	sf_count_t saved_offset ;
156b815c7f3Sopenharmony_ci
157b815c7f3Sopenharmony_ci	/*
158b815c7f3Sopenharmony_ci	**  The first page of the Ogg stream we are told to try and open as Vorbis
159b815c7f3Sopenharmony_ci	**  has already been loaded into odata->ostream by ogg_open().
160b815c7f3Sopenharmony_ci	**
161b815c7f3Sopenharmony_ci	**	Extract the initial header from the first page and verify that the
162b815c7f3Sopenharmony_ci	**	Ogg bitstream is in fact Vorbis data.
163b815c7f3Sopenharmony_ci	*/
164b815c7f3Sopenharmony_ci
165b815c7f3Sopenharmony_ci	vorbis_info_init (&vdata->vinfo) ;
166b815c7f3Sopenharmony_ci	vorbis_comment_init (&vdata->vcomment) ;
167b815c7f3Sopenharmony_ci
168b815c7f3Sopenharmony_ci	if (!odata->opacket.b_o_s)
169b815c7f3Sopenharmony_ci	{	psf_log_printf (psf, "Vorbis: First packet does not have a beginning-of-stream bit.\n") ;
170b815c7f3Sopenharmony_ci		return SFE_MALFORMED_FILE ;
171b815c7f3Sopenharmony_ci		}
172b815c7f3Sopenharmony_ci
173b815c7f3Sopenharmony_ci	if (ogg_stream_packetpeek (&odata->ostream, NULL))
174b815c7f3Sopenharmony_ci	{	psf_log_printf (psf, "Vorbis: First page contains extraneous packets!\n") ;
175b815c7f3Sopenharmony_ci		return SFE_MALFORMED_FILE ;
176b815c7f3Sopenharmony_ci		}
177b815c7f3Sopenharmony_ci
178b815c7f3Sopenharmony_ci	if (vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) < 0)
179b815c7f3Sopenharmony_ci	{	/* Error case ; not a vorbis header. */
180b815c7f3Sopenharmony_ci		psf_log_printf (psf, "Found Vorbis in stream header, but vorbis_synthesis_headerin failed.\n") ;
181b815c7f3Sopenharmony_ci		return SFE_MALFORMED_FILE ;
182b815c7f3Sopenharmony_ci		} ;
183b815c7f3Sopenharmony_ci
184b815c7f3Sopenharmony_ci	/*
185b815c7f3Sopenharmony_ci	**	At this point, we're sure we're Vorbis.	We've set up the logical (Ogg)
186b815c7f3Sopenharmony_ci	**	bitstream decoder. Get the comment and codebook headers and set up the
187b815c7f3Sopenharmony_ci	**	Vorbis decoder.
188b815c7f3Sopenharmony_ci	**
189b815c7f3Sopenharmony_ci	**	The next two packets in order are the comment and codebook headers.
190b815c7f3Sopenharmony_ci	**	They're likely large and may span multiple pages.  Thus we read
191b815c7f3Sopenharmony_ci	**	and submit data until we get our two packets, watching that no
192b815c7f3Sopenharmony_ci	**	pages are missing.  If a page is missing, error out ; losing a
193b815c7f3Sopenharmony_ci	**	header page is the only place where missing data is fatal.
194b815c7f3Sopenharmony_ci	*/
195b815c7f3Sopenharmony_ci
196b815c7f3Sopenharmony_ci	i = 0 ;			/* Count of number of packets read */
197b815c7f3Sopenharmony_ci	while (i < 2)
198b815c7f3Sopenharmony_ci	{	nn = ogg_stream_packetout (&odata->ostream, &odata->opacket) ;
199b815c7f3Sopenharmony_ci
200b815c7f3Sopenharmony_ci		if (nn == 0)
201b815c7f3Sopenharmony_ci		{	nn = ogg_stream_next_page (psf, odata) ;
202b815c7f3Sopenharmony_ci			if (nn == 0)
203b815c7f3Sopenharmony_ci			{	psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ;
204b815c7f3Sopenharmony_ci				return SFE_MALFORMED_FILE ;
205b815c7f3Sopenharmony_ci				} ;
206b815c7f3Sopenharmony_ci			if (nn == -1)
207b815c7f3Sopenharmony_ci			{	psf_log_printf (psf, "Error reading file while finding Vorbis headers!\n") ;
208b815c7f3Sopenharmony_ci				return psf->error ;
209b815c7f3Sopenharmony_ci				} ;
210b815c7f3Sopenharmony_ci			continue ;
211b815c7f3Sopenharmony_ci			}
212b815c7f3Sopenharmony_ci
213b815c7f3Sopenharmony_ci		if (nn < 0)
214b815c7f3Sopenharmony_ci		{	/* A hole while reading headers. This could be bad. */
215b815c7f3Sopenharmony_ci			psf_log_printf (psf, "Corrupt secondary header.	Exiting.\n") ;
216b815c7f3Sopenharmony_ci			return SFE_MALFORMED_FILE ;
217b815c7f3Sopenharmony_ci			} ;
218b815c7f3Sopenharmony_ci
219b815c7f3Sopenharmony_ci		vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) ;
220b815c7f3Sopenharmony_ci		i++ ;
221b815c7f3Sopenharmony_ci		} ;
222b815c7f3Sopenharmony_ci
223b815c7f3Sopenharmony_ci	/* Check for extraneous packets in the last headers page. */
224b815c7f3Sopenharmony_ci	while (ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1)
225b815c7f3Sopenharmony_ci	{	i++ ;
226b815c7f3Sopenharmony_ci		}
227b815c7f3Sopenharmony_ci	if (i > 2)
228b815c7f3Sopenharmony_ci		psf_log_printf (psf, "Vorbis: stream has extraneous header packets.\n") ;
229b815c7f3Sopenharmony_ci
230b815c7f3Sopenharmony_ci	psf_log_printf (psf, "Bitstream is %d channel, %D Hz\n", vdata->vinfo.channels, vdata->vinfo.rate) ;
231b815c7f3Sopenharmony_ci	psf_log_printf (psf, "Encoded by : %s\n", vdata->vcomment.vendor) ;
232b815c7f3Sopenharmony_ci
233b815c7f3Sopenharmony_ci	/* Save the offset of the first payload page */
234b815c7f3Sopenharmony_ci	psf->dataoffset	= ogg_sync_ftell (psf) ;
235b815c7f3Sopenharmony_ci
236b815c7f3Sopenharmony_ci	/*
237b815c7f3Sopenharmony_ci	**	Calculate the granule position offset. The first page with a payload
238b815c7f3Sopenharmony_ci	**	packet shouldn't end in a continued packet. The difference between the
239b815c7f3Sopenharmony_ci	**	page's granule position and the sum of frames on the page tells us the
240b815c7f3Sopenharmony_ci	**	granule position offset.
241b815c7f3Sopenharmony_ci	**	See https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-132000A.2
242b815c7f3Sopenharmony_ci	*/
243b815c7f3Sopenharmony_ci	ogg_stream_unpack_page (psf, odata) ;
244b815c7f3Sopenharmony_ci	vorbis_calculate_granulepos (psf, &vdata->pcm_start) ;
245b815c7f3Sopenharmony_ci	vdata->gp = vdata->pcm_start ;
246b815c7f3Sopenharmony_ci
247b815c7f3Sopenharmony_ci	/*
248b815c7f3Sopenharmony_ci	**	Find the end of the stream, save it. Only works if the file is seekable.
249b815c7f3Sopenharmony_ci	*/
250b815c7f3Sopenharmony_ci	vdata->pcm_end = (uint64_t) -1 ;
251b815c7f3Sopenharmony_ci	psf->datalength = psf->filelength ;
252b815c7f3Sopenharmony_ci	if (!psf->is_pipe)
253b815c7f3Sopenharmony_ci	{	saved_offset = ogg_sync_ftell (psf) ;
254b815c7f3Sopenharmony_ci		last_page = ogg_sync_last_page_before (psf, odata, &vdata->pcm_end, psf->filelength, odata->ostream.serialno) ;
255b815c7f3Sopenharmony_ci		if (last_page > 0)
256b815c7f3Sopenharmony_ci		{	if (!ogg_page_eos (&odata->opage))
257b815c7f3Sopenharmony_ci				psf_log_printf (psf, "Ogg: Last page lacks an end-of-stream bit.\n") ;
258b815c7f3Sopenharmony_ci			psf->datalength = last_page + odata->opage.header_len + odata->opage.body_len - psf->dataoffset ;
259b815c7f3Sopenharmony_ci			if (psf->datalength + psf->dataoffset < psf->filelength)
260b815c7f3Sopenharmony_ci				psf_log_printf (psf, "Ogg: Junk after the last page.\n") ;
261b815c7f3Sopenharmony_ci			vdata->last_page = last_page ;
262b815c7f3Sopenharmony_ci			} ;
263b815c7f3Sopenharmony_ci
264b815c7f3Sopenharmony_ci		ogg_sync_fseek (psf, saved_offset, SEEK_SET) ;
265b815c7f3Sopenharmony_ci		}
266b815c7f3Sopenharmony_ci
267b815c7f3Sopenharmony_ci	psf_log_printf (psf, "PCM offset  : %D\n", vdata->pcm_start) ;
268b815c7f3Sopenharmony_ci	if (vdata->pcm_end != (uint64_t) -1)
269b815c7f3Sopenharmony_ci		psf_log_printf (psf, "PCM end     : %D\n", vdata->pcm_end) ;
270b815c7f3Sopenharmony_ci	else
271b815c7f3Sopenharmony_ci		psf_log_printf (psf, "PCM end     : unknown\n") ;
272b815c7f3Sopenharmony_ci
273b815c7f3Sopenharmony_ci	/* Throw the comments plus a few lines about the bitstream we're decoding. */
274b815c7f3Sopenharmony_ci	for (i = 0 ; i < ARRAY_LEN (vorbis_metatypes) ; i++)
275b815c7f3Sopenharmony_ci	{	char *dd ;
276b815c7f3Sopenharmony_ci
277b815c7f3Sopenharmony_ci		dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [i].name, 0) ;
278b815c7f3Sopenharmony_ci		if (dd == NULL)
279b815c7f3Sopenharmony_ci			continue ;
280b815c7f3Sopenharmony_ci
281b815c7f3Sopenharmony_ci		if (printed_metadata_msg == 0)
282b815c7f3Sopenharmony_ci		{	psf_log_printf (psf, "Metadata :\n") ;
283b815c7f3Sopenharmony_ci			printed_metadata_msg = 1 ;
284b815c7f3Sopenharmony_ci			} ;
285b815c7f3Sopenharmony_ci
286b815c7f3Sopenharmony_ci		psf_store_string (psf, vorbis_metatypes [i].id, dd) ;
287b815c7f3Sopenharmony_ci		psf_log_printf (psf, "  %-10s : %s\n", vorbis_metatypes [i].name, dd) ;
288b815c7f3Sopenharmony_ci		} ;
289b815c7f3Sopenharmony_ci	psf_log_printf (psf, "End\n") ;
290b815c7f3Sopenharmony_ci
291b815c7f3Sopenharmony_ci	psf->sf.samplerate	= vdata->vinfo.rate ;
292b815c7f3Sopenharmony_ci	psf->sf.channels	= vdata->vinfo.channels ;
293b815c7f3Sopenharmony_ci	psf->sf.format		= SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
294b815c7f3Sopenharmony_ci	psf->sf.frames		= (vdata->pcm_end != (uint64_t) -1) ? vdata->pcm_end - vdata->pcm_start : SF_COUNT_MAX ;
295b815c7f3Sopenharmony_ci
296b815c7f3Sopenharmony_ci	/*	OK, got and parsed all three headers. Initialize the Vorbis
297b815c7f3Sopenharmony_ci	**	packet->PCM decoder.
298b815c7f3Sopenharmony_ci	**	Central decode state. */
299b815c7f3Sopenharmony_ci	vorbis_synthesis_init (&vdata->vdsp, &vdata->vinfo) ;
300b815c7f3Sopenharmony_ci
301b815c7f3Sopenharmony_ci	/*	Local state for most of the decode so multiple block decodes can
302b815c7f3Sopenharmony_ci	**	proceed in parallel. We could init multiple vorbis_block structures
303b815c7f3Sopenharmony_ci	**	for vdsp here. */
304b815c7f3Sopenharmony_ci	vorbis_block_init (&vdata->vdsp, &vdata->vblock) ;
305b815c7f3Sopenharmony_ci
306b815c7f3Sopenharmony_ci	return 0 ;
307b815c7f3Sopenharmony_ci} /* vorbis_read_header */
308b815c7f3Sopenharmony_ci
309b815c7f3Sopenharmony_cistatic int
310b815c7f3Sopenharmony_civorbis_write_header (SF_PRIVATE *psf, int UNUSED (calc_length))
311b815c7f3Sopenharmony_ci{
312b815c7f3Sopenharmony_ci	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
313b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
314b815c7f3Sopenharmony_ci	int k, ret ;
315b815c7f3Sopenharmony_ci
316b815c7f3Sopenharmony_ci	vorbis_info_init (&vdata->vinfo) ;
317b815c7f3Sopenharmony_ci
318b815c7f3Sopenharmony_ci	/* The style of encoding should be selectable here, VBR quality mode. */
319b815c7f3Sopenharmony_ci	ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ;
320b815c7f3Sopenharmony_ci
321b815c7f3Sopenharmony_ci#if 0
322b815c7f3Sopenharmony_ci	ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */
323b815c7f3Sopenharmony_ci	ret = (	vorbis_encode_setup_managed (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1)
324b815c7f3Sopenharmony_ci			|| vorbis_encode_ctl (&vdata->vinfo, OV_ECTL_RATEMANAGE_AVG, NULL)
325b815c7f3Sopenharmony_ci			|| vorbis_encode_setup_init (&vdata->vinfo)
326b815c7f3Sopenharmony_ci			) ;
327b815c7f3Sopenharmony_ci#endif
328b815c7f3Sopenharmony_ci	if (ret)
329b815c7f3Sopenharmony_ci		return SFE_BAD_OPEN_FORMAT ;
330b815c7f3Sopenharmony_ci
331b815c7f3Sopenharmony_ci	vdata->gp = 0 ;
332b815c7f3Sopenharmony_ci
333b815c7f3Sopenharmony_ci	/* add a comment */
334b815c7f3Sopenharmony_ci	vorbis_comment_init (&vdata->vcomment) ;
335b815c7f3Sopenharmony_ci
336b815c7f3Sopenharmony_ci	vorbis_comment_add_tag (&vdata->vcomment, "ENCODER", "libsndfile") ;
337b815c7f3Sopenharmony_ci	for (k = 0 ; k < SF_MAX_STRINGS ; k++)
338b815c7f3Sopenharmony_ci	{	const char * name ;
339b815c7f3Sopenharmony_ci
340b815c7f3Sopenharmony_ci		if (psf->strings.data [k].type == 0)
341b815c7f3Sopenharmony_ci			break ;
342b815c7f3Sopenharmony_ci
343b815c7f3Sopenharmony_ci		switch (psf->strings.data [k].type)
344b815c7f3Sopenharmony_ci		{	case SF_STR_TITLE :			name = "TITLE" ; break ;
345b815c7f3Sopenharmony_ci			case SF_STR_COPYRIGHT :		name = "COPYRIGHT" ; break ;
346b815c7f3Sopenharmony_ci			case SF_STR_SOFTWARE :		name = "SOFTWARE" ; break ;
347b815c7f3Sopenharmony_ci			case SF_STR_ARTIST :		name = "ARTIST" ; break ;
348b815c7f3Sopenharmony_ci			case SF_STR_COMMENT :		name = "COMMENT" ; break ;
349b815c7f3Sopenharmony_ci			case SF_STR_DATE :			name = "DATE" ; break ;
350b815c7f3Sopenharmony_ci			case SF_STR_ALBUM :			name = "ALBUM" ; break ;
351b815c7f3Sopenharmony_ci			case SF_STR_LICENSE :		name = "LICENSE" ; break ;
352b815c7f3Sopenharmony_ci			case SF_STR_TRACKNUMBER :	name = "Tracknumber" ; break ;
353b815c7f3Sopenharmony_ci			case SF_STR_GENRE :			name = "Genre" ; break ;
354b815c7f3Sopenharmony_ci
355b815c7f3Sopenharmony_ci			default : continue ;
356b815c7f3Sopenharmony_ci			} ;
357b815c7f3Sopenharmony_ci
358b815c7f3Sopenharmony_ci		vorbis_comment_add_tag (&vdata->vcomment, name, psf->strings.storage + psf->strings.data [k].offset) ;
359b815c7f3Sopenharmony_ci		} ;
360b815c7f3Sopenharmony_ci
361b815c7f3Sopenharmony_ci	/* set up the analysis state and auxiliary encoding storage */
362b815c7f3Sopenharmony_ci	vorbis_analysis_init (&vdata->vdsp, &vdata->vinfo) ;
363b815c7f3Sopenharmony_ci	vorbis_block_init (&vdata->vdsp, &vdata->vblock) ;
364b815c7f3Sopenharmony_ci
365b815c7f3Sopenharmony_ci	/*
366b815c7f3Sopenharmony_ci	**	Set up our packet->stream encoder.
367b815c7f3Sopenharmony_ci	**	Pick a random serial number ; that way we can more likely build
368b815c7f3Sopenharmony_ci	**	chained streams just by concatenation.
369b815c7f3Sopenharmony_ci	*/
370b815c7f3Sopenharmony_ci
371b815c7f3Sopenharmony_ci	ogg_stream_init (&odata->ostream, psf_rand_int32 ()) ;
372b815c7f3Sopenharmony_ci
373b815c7f3Sopenharmony_ci	/* Vorbis streams begin with three headers ; the initial header (with
374b815c7f3Sopenharmony_ci	   most of the codec setup parameters) which is mandated by the Ogg
375b815c7f3Sopenharmony_ci	   bitstream spec.  The second header holds any comment fields.	 The
376b815c7f3Sopenharmony_ci	   third header holds the bitstream codebook.  We merely need to
377b815c7f3Sopenharmony_ci	   make the headers, then pass them to libvorbis one at a time ;
378b815c7f3Sopenharmony_ci	   libvorbis handles the additional Ogg bitstream constraints */
379b815c7f3Sopenharmony_ci
380b815c7f3Sopenharmony_ci	{	ogg_packet header ;
381b815c7f3Sopenharmony_ci		ogg_packet header_comm ;
382b815c7f3Sopenharmony_ci		ogg_packet header_code ;
383b815c7f3Sopenharmony_ci		int result ;
384b815c7f3Sopenharmony_ci
385b815c7f3Sopenharmony_ci		vorbis_analysis_headerout (&vdata->vdsp, &vdata->vcomment, &header, &header_comm, &header_code) ;
386b815c7f3Sopenharmony_ci		ogg_stream_packetin (&odata->ostream, &header) ; /* automatically placed in its own page */
387b815c7f3Sopenharmony_ci		ogg_stream_packetin (&odata->ostream, &header_comm) ;
388b815c7f3Sopenharmony_ci		ogg_stream_packetin (&odata->ostream, &header_code) ;
389b815c7f3Sopenharmony_ci
390b815c7f3Sopenharmony_ci		/* This ensures the actual
391b815c7f3Sopenharmony_ci		 * audio data will start on a new page, as per spec
392b815c7f3Sopenharmony_ci		 */
393b815c7f3Sopenharmony_ci		while ((result = ogg_stream_flush (&odata->ostream, &odata->opage)) != 0)
394b815c7f3Sopenharmony_ci		{	ogg_write_page (psf, &odata->opage) ;
395b815c7f3Sopenharmony_ci			} ;
396b815c7f3Sopenharmony_ci	}
397b815c7f3Sopenharmony_ci
398b815c7f3Sopenharmony_ci	return 0 ;
399b815c7f3Sopenharmony_ci} /* vorbis_write_header */
400b815c7f3Sopenharmony_ci
401b815c7f3Sopenharmony_cistatic int
402b815c7f3Sopenharmony_civorbis_close (SF_PRIVATE *psf)
403b815c7f3Sopenharmony_ci{	OGG_PRIVATE* odata = psf->container_data ;
404b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = psf->codec_data ;
405b815c7f3Sopenharmony_ci
406b815c7f3Sopenharmony_ci	if (odata == NULL || vdata == NULL)
407b815c7f3Sopenharmony_ci		return 0 ;
408b815c7f3Sopenharmony_ci
409b815c7f3Sopenharmony_ci	/*	Clean up this logical bitstream ; before exit we shuld see if we're
410b815c7f3Sopenharmony_ci	**	followed by another [chained]. */
411b815c7f3Sopenharmony_ci
412b815c7f3Sopenharmony_ci	if (psf->file.mode == SFM_WRITE)
413b815c7f3Sopenharmony_ci	{
414b815c7f3Sopenharmony_ci		if (psf->write_current <= 0)
415b815c7f3Sopenharmony_ci			vorbis_write_header (psf, 0) ;
416b815c7f3Sopenharmony_ci
417b815c7f3Sopenharmony_ci		vorbis_analysis_wrote (&vdata->vdsp, 0) ;
418b815c7f3Sopenharmony_ci		while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1)
419b815c7f3Sopenharmony_ci		{
420b815c7f3Sopenharmony_ci
421b815c7f3Sopenharmony_ci		/* analysis, assume we want to use bitrate management */
422b815c7f3Sopenharmony_ci			vorbis_analysis (&vdata->vblock, NULL) ;
423b815c7f3Sopenharmony_ci			vorbis_bitrate_addblock (&vdata->vblock) ;
424b815c7f3Sopenharmony_ci
425b815c7f3Sopenharmony_ci			while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket))
426b815c7f3Sopenharmony_ci			{	/* weld the packet into the bitstream */
427b815c7f3Sopenharmony_ci				ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
428b815c7f3Sopenharmony_ci
429b815c7f3Sopenharmony_ci				/* write out pages (if any) */
430b815c7f3Sopenharmony_ci				while (!odata->eos)
431b815c7f3Sopenharmony_ci				{	int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ;
432b815c7f3Sopenharmony_ci					if (result == 0) break ;
433b815c7f3Sopenharmony_ci					ogg_write_page (psf, &odata->opage) ;
434b815c7f3Sopenharmony_ci
435b815c7f3Sopenharmony_ci		/* this could be set above, but for illustrative purposes, I do
436b815c7f3Sopenharmony_ci		   it here (to show that vorbis does know where the stream ends) */
437b815c7f3Sopenharmony_ci
438b815c7f3Sopenharmony_ci					if (ogg_page_eos (&odata->opage)) odata->eos = 1 ;
439b815c7f3Sopenharmony_ci				}
440b815c7f3Sopenharmony_ci			}
441b815c7f3Sopenharmony_ci		}
442b815c7f3Sopenharmony_ci	}
443b815c7f3Sopenharmony_ci
444b815c7f3Sopenharmony_ci	/* ogg_page and ogg_packet structs always point to storage in
445b815c7f3Sopenharmony_ci	   libvorbis.  They are never freed or manipulated directly */
446b815c7f3Sopenharmony_ci
447b815c7f3Sopenharmony_ci	vorbis_block_clear (&vdata->vblock) ;
448b815c7f3Sopenharmony_ci	vorbis_dsp_clear (&vdata->vdsp) ;
449b815c7f3Sopenharmony_ci	vorbis_comment_clear (&vdata->vcomment) ;
450b815c7f3Sopenharmony_ci	vorbis_info_clear (&vdata->vinfo) ;
451b815c7f3Sopenharmony_ci
452b815c7f3Sopenharmony_ci	return 0 ;
453b815c7f3Sopenharmony_ci} /* vorbis_close */
454b815c7f3Sopenharmony_ci
455b815c7f3Sopenharmony_ciint
456b815c7f3Sopenharmony_ciogg_vorbis_open (SF_PRIVATE *psf)
457b815c7f3Sopenharmony_ci{	OGG_PRIVATE* odata = psf->container_data ;
458b815c7f3Sopenharmony_ci	VORBIS_PRIVATE* vdata ;
459b815c7f3Sopenharmony_ci	int	error = 0 ;
460b815c7f3Sopenharmony_ci
461b815c7f3Sopenharmony_ci	if (odata == NULL)
462b815c7f3Sopenharmony_ci	{	psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ;
463b815c7f3Sopenharmony_ci		return SFE_INTERNAL ;
464b815c7f3Sopenharmony_ci		} ;
465b815c7f3Sopenharmony_ci
466b815c7f3Sopenharmony_ci	vdata = calloc (1, sizeof (VORBIS_PRIVATE)) ;
467b815c7f3Sopenharmony_ci	psf->codec_data = vdata ;
468b815c7f3Sopenharmony_ci
469b815c7f3Sopenharmony_ci	if (psf->file.mode == SFM_RDWR)
470b815c7f3Sopenharmony_ci		return SFE_BAD_MODE_RW ;
471b815c7f3Sopenharmony_ci
472b815c7f3Sopenharmony_ci	psf_log_printf (psf, "Vorbis library version : %s\n", vorbis_version_string ()) ;
473b815c7f3Sopenharmony_ci
474b815c7f3Sopenharmony_ci	if (psf->file.mode == SFM_READ)
475b815c7f3Sopenharmony_ci	{	if ((error = vorbis_read_header (psf)))
476b815c7f3Sopenharmony_ci			return error ;
477b815c7f3Sopenharmony_ci
478b815c7f3Sopenharmony_ci		psf->read_short		= vorbis_read_s ;
479b815c7f3Sopenharmony_ci		psf->read_int		= vorbis_read_i ;
480b815c7f3Sopenharmony_ci		psf->read_float		= vorbis_read_f ;
481b815c7f3Sopenharmony_ci		psf->read_double	= vorbis_read_d ;
482b815c7f3Sopenharmony_ci		} ;
483b815c7f3Sopenharmony_ci
484b815c7f3Sopenharmony_ci	psf->codec_close = vorbis_close ;
485b815c7f3Sopenharmony_ci	if (psf->file.mode == SFM_WRITE)
486b815c7f3Sopenharmony_ci	{
487b815c7f3Sopenharmony_ci		/* Set the default vorbis quality here. */
488b815c7f3Sopenharmony_ci		vdata->quality = 0.4 ;
489b815c7f3Sopenharmony_ci
490b815c7f3Sopenharmony_ci		psf->write_header	= vorbis_write_header ;
491b815c7f3Sopenharmony_ci		psf->write_short	= vorbis_write_s ;
492b815c7f3Sopenharmony_ci		psf->write_int		= vorbis_write_i ;
493b815c7f3Sopenharmony_ci		psf->write_float	= vorbis_write_f ;
494b815c7f3Sopenharmony_ci		psf->write_double	= vorbis_write_d ;
495b815c7f3Sopenharmony_ci
496b815c7f3Sopenharmony_ci		psf->sf.frames = 0 ;
497b815c7f3Sopenharmony_ci		psf->datalength = 0 ;
498b815c7f3Sopenharmony_ci		psf->filelength = 0 ;
499b815c7f3Sopenharmony_ci		psf->dataoffset = 0 ;
500b815c7f3Sopenharmony_ci		psf->strings.flags = SF_STR_ALLOW_START ;
501b815c7f3Sopenharmony_ci		} ;
502b815c7f3Sopenharmony_ci
503b815c7f3Sopenharmony_ci	psf->seek = vorbis_seek ;
504b815c7f3Sopenharmony_ci	psf->command = vorbis_command ;
505b815c7f3Sopenharmony_ci	psf->byterate = vorbis_byterate ;
506b815c7f3Sopenharmony_ci	psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
507b815c7f3Sopenharmony_ci	psf->sf.sections = 1 ;
508b815c7f3Sopenharmony_ci
509b815c7f3Sopenharmony_ci	return error ;
510b815c7f3Sopenharmony_ci} /* ogg_vorbis_open */
511b815c7f3Sopenharmony_ci
512b815c7f3Sopenharmony_cistatic int
513b815c7f3Sopenharmony_civorbis_command (SF_PRIVATE *psf, int command, void * data, int datasize)
514b815c7f3Sopenharmony_ci{	OGG_PRIVATE* odata = psf->container_data ;
515b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
516b815c7f3Sopenharmony_ci
517b815c7f3Sopenharmony_ci	switch (command)
518b815c7f3Sopenharmony_ci	{	case SFC_SET_COMPRESSION_LEVEL :
519b815c7f3Sopenharmony_ci			if (data == NULL || datasize != sizeof (double))
520b815c7f3Sopenharmony_ci				return SF_FALSE ;
521b815c7f3Sopenharmony_ci
522b815c7f3Sopenharmony_ci			if (psf->have_written)
523b815c7f3Sopenharmony_ci				return SF_FALSE ;
524b815c7f3Sopenharmony_ci
525b815c7f3Sopenharmony_ci			vdata->quality = 1.0 - *((double *) data) ;
526b815c7f3Sopenharmony_ci
527b815c7f3Sopenharmony_ci			/* Clip range. */
528b815c7f3Sopenharmony_ci			vdata->quality = SF_MAX (0.0, SF_MIN (1.0, vdata->quality)) ;
529b815c7f3Sopenharmony_ci
530b815c7f3Sopenharmony_ci			psf_log_printf (psf, "%s : Setting SFC_SET_VBR_ENCODING_QUALITY to %f.\n", __func__, vdata->quality) ;
531b815c7f3Sopenharmony_ci			return SF_TRUE ;
532b815c7f3Sopenharmony_ci
533b815c7f3Sopenharmony_ci		case SFC_GET_OGG_STREAM_SERIALNO :
534b815c7f3Sopenharmony_ci			if (data == NULL || datasize != sizeof (int32_t))
535b815c7f3Sopenharmony_ci				return SF_FALSE ;
536b815c7f3Sopenharmony_ci
537b815c7f3Sopenharmony_ci			*((int32_t *) data) = odata->ostream.serialno ;
538b815c7f3Sopenharmony_ci			return SF_TRUE ;
539b815c7f3Sopenharmony_ci
540b815c7f3Sopenharmony_ci		default :
541b815c7f3Sopenharmony_ci			return SF_FALSE ;
542b815c7f3Sopenharmony_ci		} ;
543b815c7f3Sopenharmony_ci
544b815c7f3Sopenharmony_ci	return SF_FALSE ;
545b815c7f3Sopenharmony_ci} /* vorbis_command */
546b815c7f3Sopenharmony_ci
547b815c7f3Sopenharmony_cistatic int
548b815c7f3Sopenharmony_civorbis_rnull (SF_PRIVATE *UNUSED (psf), int samples, void *UNUSED (vptr), int UNUSED (off) , int channels, float **UNUSED (pcm))
549b815c7f3Sopenharmony_ci{
550b815c7f3Sopenharmony_ci	return samples * channels ;
551b815c7f3Sopenharmony_ci} /* vorbis_rnull */
552b815c7f3Sopenharmony_ci
553b815c7f3Sopenharmony_cistatic int
554b815c7f3Sopenharmony_civorbis_rshort (SF_PRIVATE *psf, int samples, void *vptr, int off, int channels, float **pcm)
555b815c7f3Sopenharmony_ci{
556b815c7f3Sopenharmony_ci	short *ptr = (short*) vptr + off ;
557b815c7f3Sopenharmony_ci	int i = 0, j, n ;
558b815c7f3Sopenharmony_ci	if (psf->float_int_mult)
559b815c7f3Sopenharmony_ci	{
560b815c7f3Sopenharmony_ci		float inverse = 1.0 / psf->float_max ;
561b815c7f3Sopenharmony_ci		for (j = 0 ; j < samples ; j++)
562b815c7f3Sopenharmony_ci			for (n = 0 ; n < channels ; n++)
563b815c7f3Sopenharmony_ci				ptr [i++] = psf_lrintf ((pcm [n][j] * inverse) * 32767.0f) ;
564b815c7f3Sopenharmony_ci	}
565b815c7f3Sopenharmony_ci	else
566b815c7f3Sopenharmony_ci	{
567b815c7f3Sopenharmony_ci		for (j = 0 ; j < samples ; j++)
568b815c7f3Sopenharmony_ci			for (n = 0 ; n < channels ; n++)
569b815c7f3Sopenharmony_ci				ptr [i++] = psf_lrintf (pcm [n][j] * 32767.0f) ;
570b815c7f3Sopenharmony_ci	}
571b815c7f3Sopenharmony_ci	return i ;
572b815c7f3Sopenharmony_ci} /* vorbis_rshort */
573b815c7f3Sopenharmony_ci
574b815c7f3Sopenharmony_cistatic int
575b815c7f3Sopenharmony_civorbis_rint (SF_PRIVATE *psf, int samples, void *vptr, int off, int channels, float **pcm)
576b815c7f3Sopenharmony_ci{
577b815c7f3Sopenharmony_ci	int *ptr = (int*) vptr + off ;
578b815c7f3Sopenharmony_ci	int i = 0, j, n ;
579b815c7f3Sopenharmony_ci
580b815c7f3Sopenharmony_ci	if (psf->float_int_mult)
581b815c7f3Sopenharmony_ci	{
582b815c7f3Sopenharmony_ci		float inverse = 1.0 / psf->float_max ;
583b815c7f3Sopenharmony_ci		for (j = 0 ; j < samples ; j++)
584b815c7f3Sopenharmony_ci			for (n = 0 ; n < channels ; n++)
585b815c7f3Sopenharmony_ci				ptr [i++] = psf_lrintf ((pcm [n][j] * inverse) * 2147483647.0f) ;
586b815c7f3Sopenharmony_ci	}
587b815c7f3Sopenharmony_ci	else
588b815c7f3Sopenharmony_ci	{
589b815c7f3Sopenharmony_ci		for (j = 0 ; j < samples ; j++)
590b815c7f3Sopenharmony_ci			for (n = 0 ; n < channels ; n++)
591b815c7f3Sopenharmony_ci				ptr [i++] = psf_lrintf (pcm [n][j] * 2147483647.0f) ;
592b815c7f3Sopenharmony_ci	}
593b815c7f3Sopenharmony_ci	return i ;
594b815c7f3Sopenharmony_ci} /* vorbis_rint */
595b815c7f3Sopenharmony_ci
596b815c7f3Sopenharmony_cistatic int
597b815c7f3Sopenharmony_civorbis_rfloat (SF_PRIVATE *UNUSED (psf), int samples, void *vptr, int off, int channels, float **pcm)
598b815c7f3Sopenharmony_ci{
599b815c7f3Sopenharmony_ci	float *ptr = (float*) vptr + off ;
600b815c7f3Sopenharmony_ci	int i = 0, j, n ;
601b815c7f3Sopenharmony_ci	for (j = 0 ; j < samples ; j++)
602b815c7f3Sopenharmony_ci		for (n = 0 ; n < channels ; n++)
603b815c7f3Sopenharmony_ci			ptr [i++] = pcm [n][j] ;
604b815c7f3Sopenharmony_ci	return i ;
605b815c7f3Sopenharmony_ci} /* vorbis_rfloat */
606b815c7f3Sopenharmony_ci
607b815c7f3Sopenharmony_cistatic int
608b815c7f3Sopenharmony_civorbis_rdouble (SF_PRIVATE *UNUSED (psf), int samples, void *vptr, int off, int channels, float **pcm)
609b815c7f3Sopenharmony_ci{
610b815c7f3Sopenharmony_ci	double *ptr = (double*) vptr + off ;
611b815c7f3Sopenharmony_ci	int i = 0, j, n ;
612b815c7f3Sopenharmony_ci	for (j = 0 ; j < samples ; j++)
613b815c7f3Sopenharmony_ci		for (n = 0 ; n < channels ; n++)
614b815c7f3Sopenharmony_ci			ptr [i++] = pcm [n][j] ;
615b815c7f3Sopenharmony_ci	return i ;
616b815c7f3Sopenharmony_ci} /* vorbis_rdouble */
617b815c7f3Sopenharmony_ci
618b815c7f3Sopenharmony_ci
619b815c7f3Sopenharmony_cistatic sf_count_t
620b815c7f3Sopenharmony_civorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn)
621b815c7f3Sopenharmony_ci{	VORBIS_PRIVATE *vdata = psf->codec_data ;
622b815c7f3Sopenharmony_ci	OGG_PRIVATE *odata = psf->container_data ;
623b815c7f3Sopenharmony_ci	int len, samples, i = 0 , nn ;
624b815c7f3Sopenharmony_ci	float **pcm ;
625b815c7f3Sopenharmony_ci
626b815c7f3Sopenharmony_ci	len = lens / psf->sf.channels ;
627b815c7f3Sopenharmony_ci
628b815c7f3Sopenharmony_ci	while (len > 0)
629b815c7f3Sopenharmony_ci	{	/*
630b815c7f3Sopenharmony_ci		** pcm is a multichannel float vector.	 In stereo, for
631b815c7f3Sopenharmony_ci		** example, pcm [0] is left, and pcm [1] is right.	 samples is
632b815c7f3Sopenharmony_ci		** the size of each channel.	 Convert the float values
633b815c7f3Sopenharmony_ci		** (-1.<=range<=1.) to whatever PCM format and write it out.
634b815c7f3Sopenharmony_ci		*/
635b815c7f3Sopenharmony_ci		while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0)
636b815c7f3Sopenharmony_ci		{	if (samples > len) samples = len ;
637b815c7f3Sopenharmony_ci			i += transfn (psf, samples, ptr, i, psf->sf.channels, pcm) ;
638b815c7f3Sopenharmony_ci			len -= samples ;
639b815c7f3Sopenharmony_ci			/* tell libvorbis how many samples we actually consumed */
640b815c7f3Sopenharmony_ci			vorbis_synthesis_read (&vdata->vdsp, samples) ;
641b815c7f3Sopenharmony_ci			vdata->gp += samples ;
642b815c7f3Sopenharmony_ci			if (len == 0)
643b815c7f3Sopenharmony_ci				return i ;
644b815c7f3Sopenharmony_ci			} ;
645b815c7f3Sopenharmony_ci
646b815c7f3Sopenharmony_ci		/* Out of samples, load the next packet. */
647b815c7f3Sopenharmony_ci		if (odata->pkt_indx == odata->pkt_len)
648b815c7f3Sopenharmony_ci		{	/* Page out of packets, load and unpack the next page. */
649b815c7f3Sopenharmony_ci			nn = ogg_stream_unpack_page (psf, odata) ;
650b815c7f3Sopenharmony_ci			if (nn <= 0)
651b815c7f3Sopenharmony_ci				return i ;
652b815c7f3Sopenharmony_ci			if (nn == 2)
653b815c7f3Sopenharmony_ci			{	/* Ran over a hole. gp is now out of date, need to recalculate. */
654b815c7f3Sopenharmony_ci				vorbis_synthesis_restart (&vdata->vdsp) ;
655b815c7f3Sopenharmony_ci				vorbis_calculate_granulepos (psf, &vdata->gp) ;
656b815c7f3Sopenharmony_ci				}
657b815c7f3Sopenharmony_ci			} ;
658b815c7f3Sopenharmony_ci
659b815c7f3Sopenharmony_ci		/* Decode the packet */
660b815c7f3Sopenharmony_ci		if (vorbis_synthesis (&vdata->vblock, &(odata->pkt [odata->pkt_indx])) == 0) /* test for success! */
661b815c7f3Sopenharmony_ci			vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ;
662b815c7f3Sopenharmony_ci		odata->pkt_indx++ ;
663b815c7f3Sopenharmony_ci		} ;
664b815c7f3Sopenharmony_ci
665b815c7f3Sopenharmony_ci	return i ;
666b815c7f3Sopenharmony_ci} /* vorbis_read_sample */
667b815c7f3Sopenharmony_ci
668b815c7f3Sopenharmony_cistatic sf_count_t
669b815c7f3Sopenharmony_civorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t lens)
670b815c7f3Sopenharmony_ci{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rshort) ;
671b815c7f3Sopenharmony_ci} /* vorbis_read_s */
672b815c7f3Sopenharmony_ci
673b815c7f3Sopenharmony_cistatic sf_count_t
674b815c7f3Sopenharmony_civorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t lens)
675b815c7f3Sopenharmony_ci{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rint) ;
676b815c7f3Sopenharmony_ci} /* vorbis_read_i */
677b815c7f3Sopenharmony_ci
678b815c7f3Sopenharmony_cistatic sf_count_t
679b815c7f3Sopenharmony_civorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t lens)
680b815c7f3Sopenharmony_ci{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rfloat) ;
681b815c7f3Sopenharmony_ci} /* vorbis_read_f */
682b815c7f3Sopenharmony_ci
683b815c7f3Sopenharmony_cistatic sf_count_t
684b815c7f3Sopenharmony_civorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t lens)
685b815c7f3Sopenharmony_ci{	return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rdouble) ;
686b815c7f3Sopenharmony_ci} /* vorbis_read_d */
687b815c7f3Sopenharmony_ci
688b815c7f3Sopenharmony_ci/*==============================================================================
689b815c7f3Sopenharmony_ci*/
690b815c7f3Sopenharmony_ci
691b815c7f3Sopenharmony_cistatic void
692b815c7f3Sopenharmony_civorbis_write_samples (SF_PRIVATE *psf, OGG_PRIVATE *odata, VORBIS_PRIVATE *vdata, int in_frames)
693b815c7f3Sopenharmony_ci{
694b815c7f3Sopenharmony_ci	vorbis_analysis_wrote (&vdata->vdsp, in_frames) ;
695b815c7f3Sopenharmony_ci
696b815c7f3Sopenharmony_ci	/*
697b815c7f3Sopenharmony_ci	**	Vorbis does some data preanalysis, then divvies up blocks for
698b815c7f3Sopenharmony_ci	**	more involved (potentially parallel) processing. Get a single
699b815c7f3Sopenharmony_ci	**	block for encoding now.
700b815c7f3Sopenharmony_ci	*/
701b815c7f3Sopenharmony_ci	while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1)
702b815c7f3Sopenharmony_ci	{
703b815c7f3Sopenharmony_ci		/* analysis, assume we want to use bitrate management */
704b815c7f3Sopenharmony_ci		vorbis_analysis (&vdata->vblock, NULL) ;
705b815c7f3Sopenharmony_ci		vorbis_bitrate_addblock (&vdata->vblock) ;
706b815c7f3Sopenharmony_ci
707b815c7f3Sopenharmony_ci		while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket))
708b815c7f3Sopenharmony_ci		{
709b815c7f3Sopenharmony_ci			/* weld the packet into the bitstream */
710b815c7f3Sopenharmony_ci			ogg_stream_packetin (&odata->ostream, &odata->opacket) ;
711b815c7f3Sopenharmony_ci
712b815c7f3Sopenharmony_ci			/* write out pages (if any) */
713b815c7f3Sopenharmony_ci			while (!odata->eos)
714b815c7f3Sopenharmony_ci			{	int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ;
715b815c7f3Sopenharmony_ci				if (result == 0)
716b815c7f3Sopenharmony_ci					break ;
717b815c7f3Sopenharmony_ci				ogg_write_page (psf, &odata->opage) ;
718b815c7f3Sopenharmony_ci
719b815c7f3Sopenharmony_ci				/*	This could be set above, but for illustrative purposes, I do
720b815c7f3Sopenharmony_ci				**	it here (to show that vorbis does know where the stream ends) */
721b815c7f3Sopenharmony_ci				if (ogg_page_eos (&odata->opage))
722b815c7f3Sopenharmony_ci					odata->eos = 1 ;
723b815c7f3Sopenharmony_ci				} ;
724b815c7f3Sopenharmony_ci			} ;
725b815c7f3Sopenharmony_ci		} ;
726b815c7f3Sopenharmony_ci
727b815c7f3Sopenharmony_ci	vdata->gp += in_frames ;
728b815c7f3Sopenharmony_ci} /* vorbis_write_data */
729b815c7f3Sopenharmony_ci
730b815c7f3Sopenharmony_ci
731b815c7f3Sopenharmony_cistatic sf_count_t
732b815c7f3Sopenharmony_civorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t lens)
733b815c7f3Sopenharmony_ci{
734b815c7f3Sopenharmony_ci	int i, m, j = 0 ;
735b815c7f3Sopenharmony_ci	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
736b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
737b815c7f3Sopenharmony_ci	int in_frames = lens / psf->sf.channels ;
738b815c7f3Sopenharmony_ci	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
739b815c7f3Sopenharmony_ci	for (i = 0 ; i < in_frames ; i++)
740b815c7f3Sopenharmony_ci		for (m = 0 ; m < psf->sf.channels ; m++)
741b815c7f3Sopenharmony_ci			buffer [m][i] = (float) (ptr [j++]) / 32767.0f ;
742b815c7f3Sopenharmony_ci
743b815c7f3Sopenharmony_ci	vorbis_write_samples (psf, odata, vdata, in_frames) ;
744b815c7f3Sopenharmony_ci
745b815c7f3Sopenharmony_ci	return lens ;
746b815c7f3Sopenharmony_ci} /* vorbis_write_s */
747b815c7f3Sopenharmony_ci
748b815c7f3Sopenharmony_cistatic sf_count_t
749b815c7f3Sopenharmony_civorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t lens)
750b815c7f3Sopenharmony_ci{	int i, m, j = 0 ;
751b815c7f3Sopenharmony_ci	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
752b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
753b815c7f3Sopenharmony_ci	int in_frames = lens / psf->sf.channels ;
754b815c7f3Sopenharmony_ci	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
755b815c7f3Sopenharmony_ci	for (i = 0 ; i < in_frames ; i++)
756b815c7f3Sopenharmony_ci		for (m = 0 ; m < psf->sf.channels ; m++)
757b815c7f3Sopenharmony_ci			buffer [m][i] = (float) (ptr [j++]) / 2147483647.0f ;
758b815c7f3Sopenharmony_ci
759b815c7f3Sopenharmony_ci	vorbis_write_samples (psf, odata, vdata, in_frames) ;
760b815c7f3Sopenharmony_ci
761b815c7f3Sopenharmony_ci	return lens ;
762b815c7f3Sopenharmony_ci} /* vorbis_write_i */
763b815c7f3Sopenharmony_ci
764b815c7f3Sopenharmony_cistatic sf_count_t
765b815c7f3Sopenharmony_civorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t lens)
766b815c7f3Sopenharmony_ci{	int i, m, j = 0 ;
767b815c7f3Sopenharmony_ci	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
768b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
769b815c7f3Sopenharmony_ci	int in_frames = lens / psf->sf.channels ;
770b815c7f3Sopenharmony_ci	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
771b815c7f3Sopenharmony_ci	for (i = 0 ; i < in_frames ; i++)
772b815c7f3Sopenharmony_ci		for (m = 0 ; m < psf->sf.channels ; m++)
773b815c7f3Sopenharmony_ci			buffer [m][i] = ptr [j++] ;
774b815c7f3Sopenharmony_ci
775b815c7f3Sopenharmony_ci	vorbis_write_samples (psf, odata, vdata, in_frames) ;
776b815c7f3Sopenharmony_ci
777b815c7f3Sopenharmony_ci	return lens ;
778b815c7f3Sopenharmony_ci} /* vorbis_write_f */
779b815c7f3Sopenharmony_ci
780b815c7f3Sopenharmony_cistatic sf_count_t
781b815c7f3Sopenharmony_civorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t lens)
782b815c7f3Sopenharmony_ci{	int i, m, j = 0 ;
783b815c7f3Sopenharmony_ci	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
784b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
785b815c7f3Sopenharmony_ci	int in_frames = lens / psf->sf.channels ;
786b815c7f3Sopenharmony_ci	float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ;
787b815c7f3Sopenharmony_ci	for (i = 0 ; i < in_frames ; i++)
788b815c7f3Sopenharmony_ci		for (m = 0 ; m < psf->sf.channels ; m++)
789b815c7f3Sopenharmony_ci			buffer [m][i] = (float) ptr [j++] ;
790b815c7f3Sopenharmony_ci
791b815c7f3Sopenharmony_ci	vorbis_write_samples (psf, odata, vdata, in_frames) ;
792b815c7f3Sopenharmony_ci
793b815c7f3Sopenharmony_ci	return lens ;
794b815c7f3Sopenharmony_ci} /* vorbis_write_d */
795b815c7f3Sopenharmony_ci
796b815c7f3Sopenharmony_cistatic int
797b815c7f3Sopenharmony_civorbis_skip (SF_PRIVATE *psf, uint64_t target)
798b815c7f3Sopenharmony_ci{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
799b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
800b815c7f3Sopenharmony_ci	ogg_packet *pkt ;
801b815c7f3Sopenharmony_ci	int thisblock, lastblock, nn ;
802b815c7f3Sopenharmony_ci	const int blocksize = vorbis_info_blocksize (&vdata->vinfo, 1) ;
803b815c7f3Sopenharmony_ci
804b815c7f3Sopenharmony_ci	/*	Read out any samples that may be in the decoder from a seek without a
805b815c7f3Sopenharmony_ci	**	search. */
806b815c7f3Sopenharmony_ci	thisblock = vorbis_synthesis_pcmout (&vdata->vdsp, NULL) ;
807b815c7f3Sopenharmony_ci	if (thisblock > 0)
808b815c7f3Sopenharmony_ci	{	if ((uint64_t) thisblock + vdata->gp >= target)
809b815c7f3Sopenharmony_ci			thisblock = SF_MIN (thisblock, (int) (target - vdata->gp)) ;
810b815c7f3Sopenharmony_ci
811b815c7f3Sopenharmony_ci		vorbis_synthesis_read (&vdata->vdsp, thisblock) ;
812b815c7f3Sopenharmony_ci		vdata->gp += thisblock ;
813b815c7f3Sopenharmony_ci		if (vdata->gp == target)
814b815c7f3Sopenharmony_ci			return 0 ;
815b815c7f3Sopenharmony_ci		} ;
816b815c7f3Sopenharmony_ci
817b815c7f3Sopenharmony_ci	/* Read through packets that are before our target */
818b815c7f3Sopenharmony_ci	lastblock = 0 ;
819b815c7f3Sopenharmony_ci	for ( ; vdata->gp < target ; )
820b815c7f3Sopenharmony_ci	{	/* Ensure there are unpacked packets. */
821b815c7f3Sopenharmony_ci		if (odata->pkt_indx == odata->pkt_len)
822b815c7f3Sopenharmony_ci		{	/* Page out of packets, load and unpack the next page. */
823b815c7f3Sopenharmony_ci			nn = ogg_stream_unpack_page (psf, odata) ;
824b815c7f3Sopenharmony_ci			if (nn < 0)
825b815c7f3Sopenharmony_ci				return nn ;
826b815c7f3Sopenharmony_ci			if (nn == 0)
827b815c7f3Sopenharmony_ci				break ;
828b815c7f3Sopenharmony_ci			if (nn == 2)
829b815c7f3Sopenharmony_ci			{	/* Ran over a hole. gp is now out of date, need to recalculate. */
830b815c7f3Sopenharmony_ci				vorbis_synthesis_restart (&vdata->vdsp) ;
831b815c7f3Sopenharmony_ci				vorbis_calculate_granulepos (psf, &vdata->gp) ;
832b815c7f3Sopenharmony_ci				if (target < vdata->gp)
833b815c7f3Sopenharmony_ci				{	/* Our target is inside the hole :-( */
834b815c7f3Sopenharmony_ci					return 0 ;
835b815c7f3Sopenharmony_ci					} ;
836b815c7f3Sopenharmony_ci				} ;
837b815c7f3Sopenharmony_ci			} ;
838b815c7f3Sopenharmony_ci
839b815c7f3Sopenharmony_ci		pkt = &odata->pkt [odata->pkt_indx] ;
840b815c7f3Sopenharmony_ci		thisblock = vorbis_packet_blocksize (&vdata->vinfo, pkt) ;
841b815c7f3Sopenharmony_ci		if (thisblock < 0)
842b815c7f3Sopenharmony_ci		{	/* Not an audio packet */
843b815c7f3Sopenharmony_ci			odata->pkt_indx++ ;
844b815c7f3Sopenharmony_ci			continue ;
845b815c7f3Sopenharmony_ci			} ;
846b815c7f3Sopenharmony_ci
847b815c7f3Sopenharmony_ci		if (lastblock)
848b815c7f3Sopenharmony_ci		{	vdata->gp += ((lastblock + thisblock) / 4) ;
849b815c7f3Sopenharmony_ci			} ;
850b815c7f3Sopenharmony_ci
851b815c7f3Sopenharmony_ci		/* Check to see if the block contains our target */
852b815c7f3Sopenharmony_ci		if (vdata->gp + ((thisblock + blocksize) / 4) >= target)
853b815c7f3Sopenharmony_ci			break ;
854b815c7f3Sopenharmony_ci
855b815c7f3Sopenharmony_ci		/* Block is before the target. Track for state, but don't decode. */
856b815c7f3Sopenharmony_ci		odata->pkt_indx++ ;
857b815c7f3Sopenharmony_ci		vorbis_synthesis_trackonly (&vdata->vblock, pkt) ;
858b815c7f3Sopenharmony_ci		vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ;
859b815c7f3Sopenharmony_ci		lastblock = thisblock ;
860b815c7f3Sopenharmony_ci		} ;
861b815c7f3Sopenharmony_ci
862b815c7f3Sopenharmony_ci	/*	We are at the correct block, but still need to consume samples to reach
863b815c7f3Sopenharmony_ci	**	our target. */
864b815c7f3Sopenharmony_ci	vorbis_read_sample (psf, (void *) NULL, (target - vdata->gp) * psf->sf.channels, vorbis_rnull) ;
865b815c7f3Sopenharmony_ci
866b815c7f3Sopenharmony_ci	return 0 ;
867b815c7f3Sopenharmony_ci} /* vorbis_skip */
868b815c7f3Sopenharmony_ci
869b815c7f3Sopenharmony_cistatic int
870b815c7f3Sopenharmony_civorbis_seek_trysearch (SF_PRIVATE *psf, uint64_t target_gp)
871b815c7f3Sopenharmony_ci{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
872b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
873b815c7f3Sopenharmony_ci	uint64_t best_gp, search_target_gp ;
874b815c7f3Sopenharmony_ci	int ret ;
875b815c7f3Sopenharmony_ci
876b815c7f3Sopenharmony_ci	/* Can't bisect a file we don't know the end of (cannot seek). */
877b815c7f3Sopenharmony_ci	if (vdata->pcm_end == (uint64_t) -1)
878b815c7f3Sopenharmony_ci		return 0 ;
879b815c7f3Sopenharmony_ci
880b815c7f3Sopenharmony_ci	/* If the target is for the near future, don't bother bisecting, just skip
881b815c7f3Sopenharmony_ci	** to it. */
882b815c7f3Sopenharmony_ci	if (target_gp >= vdata->gp &&
883b815c7f3Sopenharmony_ci		target_gp - vdata->gp < ((unsigned) (VORBIS_SEEK_THRESHOLD) * psf->sf.samplerate))
884b815c7f3Sopenharmony_ci		return 0 ;
885b815c7f3Sopenharmony_ci
886b815c7f3Sopenharmony_ci	/*	Search for a position a half large-block before our target. As Vorbis is
887b815c7f3Sopenharmony_ci	**	lapped, every sample position come from two blocks, the "left" half of
888b815c7f3Sopenharmony_ci	**	one block and the "right" half of the previous block.  The granule
889b815c7f3Sopenharmony_ci	**	position of an Ogg page of a Vorbis stream is the sample offset of the
890b815c7f3Sopenharmony_ci	**	last finished sample in the stream that can be decoded from a page.  A
891b815c7f3Sopenharmony_ci	**	page also contains another half-block of samples waiting to be lapped
892b815c7f3Sopenharmony_ci	**	with the first half-block of samples from the next page.
893b815c7f3Sopenharmony_ci	**
894b815c7f3Sopenharmony_ci	**	Searching for a sample one half of a large block before our target
895b815c7f3Sopenharmony_ci	**	guarantees we always load a page containing the previous half block
896b815c7f3Sopenharmony_ci	**	required to decode the target.  Downside is we can't use best_gp
897b815c7f3Sopenharmony_ci	**	parameter of the page seek function. */
898b815c7f3Sopenharmony_ci	search_target_gp = vorbis_info_blocksize (&vdata->vinfo, 1) / 2 ;
899b815c7f3Sopenharmony_ci	search_target_gp = search_target_gp < target_gp ? target_gp - search_target_gp : 0 ;
900b815c7f3Sopenharmony_ci
901b815c7f3Sopenharmony_ci	ret = ogg_stream_seek_page_search (psf, odata, search_target_gp, vdata->pcm_start,
902b815c7f3Sopenharmony_ci			vdata->pcm_end, &best_gp, psf->dataoffset, vdata->last_page, vdata->vinfo.rate) ;
903b815c7f3Sopenharmony_ci	if (ret < 0)
904b815c7f3Sopenharmony_ci		return ret ;
905b815c7f3Sopenharmony_ci
906b815c7f3Sopenharmony_ci	ret = ogg_stream_unpack_page (psf, odata) ;
907b815c7f3Sopenharmony_ci	if (ret > 0)
908b815c7f3Sopenharmony_ci	{	/* Reset the decoder, recalculate position */
909b815c7f3Sopenharmony_ci		vorbis_synthesis_restart (&vdata->vdsp) ;
910b815c7f3Sopenharmony_ci		ret = vorbis_calculate_granulepos (psf, &vdata->gp) ;
911b815c7f3Sopenharmony_ci		} ;
912b815c7f3Sopenharmony_ci
913b815c7f3Sopenharmony_ci	return ret ;
914b815c7f3Sopenharmony_ci} /* vorbis_seek_trysearch */
915b815c7f3Sopenharmony_ci
916b815c7f3Sopenharmony_cistatic sf_count_t
917b815c7f3Sopenharmony_civorbis_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset)
918b815c7f3Sopenharmony_ci{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
919b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
920b815c7f3Sopenharmony_ci	uint64_t target_gp ;
921b815c7f3Sopenharmony_ci	int ret ;
922b815c7f3Sopenharmony_ci
923b815c7f3Sopenharmony_ci	if (odata == NULL || vdata == NULL)
924b815c7f3Sopenharmony_ci		return 0 ;
925b815c7f3Sopenharmony_ci
926b815c7f3Sopenharmony_ci	if (offset < 0)
927b815c7f3Sopenharmony_ci	{	psf->error = SFE_BAD_SEEK ;
928b815c7f3Sopenharmony_ci		return ((sf_count_t) -1) ;
929b815c7f3Sopenharmony_ci		} ;
930b815c7f3Sopenharmony_ci
931b815c7f3Sopenharmony_ci	if (psf->file.mode == SFM_READ)
932b815c7f3Sopenharmony_ci	{	target_gp = (uint64_t) offset + vdata->pcm_start ;
933b815c7f3Sopenharmony_ci
934b815c7f3Sopenharmony_ci		ret = vorbis_seek_trysearch (psf, target_gp) ;
935b815c7f3Sopenharmony_ci
936b815c7f3Sopenharmony_ci		if (ret < 0 || vdata->gp > target_gp)
937b815c7f3Sopenharmony_ci		{	/* Search failed (bad data?), reset to the beginning of the stream. */
938b815c7f3Sopenharmony_ci			psf_log_printf (psf, "Vorbis: Seek search failed. Reading through stream from start.\n") ;
939b815c7f3Sopenharmony_ci			ogg_stream_reset_serialno (&odata->ostream, odata->ostream.serialno) ;
940b815c7f3Sopenharmony_ci			odata->pkt_len = 0 ;
941b815c7f3Sopenharmony_ci			odata->pkt_indx = 0 ;
942b815c7f3Sopenharmony_ci			ogg_sync_fseek (psf, psf->dataoffset, SEEK_SET) ;
943b815c7f3Sopenharmony_ci			vdata->gp = vdata->pcm_start ;
944b815c7f3Sopenharmony_ci			vorbis_synthesis_restart (&vdata->vdsp) ;
945b815c7f3Sopenharmony_ci			} ;
946b815c7f3Sopenharmony_ci
947b815c7f3Sopenharmony_ci		vorbis_skip (psf, target_gp) ;
948b815c7f3Sopenharmony_ci
949b815c7f3Sopenharmony_ci		return vdata->gp - vdata->pcm_start ;
950b815c7f3Sopenharmony_ci		} ;
951b815c7f3Sopenharmony_ci
952b815c7f3Sopenharmony_ci	psf->error = SFE_BAD_SEEK ;
953b815c7f3Sopenharmony_ci	return ((sf_count_t) -1) ;
954b815c7f3Sopenharmony_ci} /* vorbis_seek */
955b815c7f3Sopenharmony_ci
956b815c7f3Sopenharmony_cistatic int
957b815c7f3Sopenharmony_civorbis_byterate (SF_PRIVATE *psf)
958b815c7f3Sopenharmony_ci{
959b815c7f3Sopenharmony_ci	if (psf->file.mode == SFM_READ)
960b815c7f3Sopenharmony_ci		return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ;
961b815c7f3Sopenharmony_ci
962b815c7f3Sopenharmony_ci	return -1 ;
963b815c7f3Sopenharmony_ci} /* vorbis_byterate */
964b815c7f3Sopenharmony_ci
965b815c7f3Sopenharmony_cistatic int
966b815c7f3Sopenharmony_civorbis_calculate_granulepos (SF_PRIVATE *psf, uint64_t *gp_out)
967b815c7f3Sopenharmony_ci{	OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ;
968b815c7f3Sopenharmony_ci	VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ;
969b815c7f3Sopenharmony_ci	ogg_packet *pkt ;
970b815c7f3Sopenharmony_ci	uint64_t last_gp ;
971b815c7f3Sopenharmony_ci	int thisblock, lastblock, i ;
972b815c7f3Sopenharmony_ci	unsigned duration ;
973b815c7f3Sopenharmony_ci
974b815c7f3Sopenharmony_ci	/*	Calculate the granule position when dropped into the middle of a stream
975b815c7f3Sopenharmony_ci	**	with an un-primed decoder.
976b815c7f3Sopenharmony_ci	**
977b815c7f3Sopenharmony_ci	**	Normally the last unpacked packet contains the granule position of the
978b815c7f3Sopenharmony_ci	**	last completed sample from decoding all the blocks in the page's
979b815c7f3Sopenharmony_ci	**	packets.  By calculating how many samples we can decode from the blocks
980b815c7f3Sopenharmony_ci	**	in the page's packets and subtracting it from the final packet's granule
981b815c7f3Sopenharmony_ci	**	position we get the position of the first sample to be output from the
982b815c7f3Sopenharmony_ci	**	decoder after it primes.  That is, the current granule position.
983b815c7f3Sopenharmony_ci	**
984b815c7f3Sopenharmony_ci	**	However, there is an ambiguity if this is the last page of a stream. The
985b815c7f3Sopenharmony_ci	**	last page of a stream may have a granule position of fewer samples than
986b815c7f3Sopenharmony_ci	**	the page actually contains.  The excess samples are padding leftovers
987b815c7f3Sopenharmony_ci	**	for and exact sample length file. */
988b815c7f3Sopenharmony_ci
989b815c7f3Sopenharmony_ci	if (odata->pkt_len > 0)
990b815c7f3Sopenharmony_ci	{	/* Calculate how many samples can be decoded from blocks in this page,
991b815c7f3Sopenharmony_ci		** accounting for the fact that blocks are 1/2 lapped. */
992b815c7f3Sopenharmony_ci		lastblock = -1 ;
993b815c7f3Sopenharmony_ci		duration = 0 ;
994b815c7f3Sopenharmony_ci		pkt = odata->pkt ;
995b815c7f3Sopenharmony_ci		for (i = 0 ; i < odata->pkt_len ; i++)
996b815c7f3Sopenharmony_ci		{	thisblock = vorbis_packet_blocksize (&vdata->vinfo, &pkt [i]) ;
997b815c7f3Sopenharmony_ci			if (thisblock >= 0)
998b815c7f3Sopenharmony_ci			{	if (lastblock != -1)
999b815c7f3Sopenharmony_ci					duration += (lastblock + thisblock) >> 2 ;
1000b815c7f3Sopenharmony_ci				lastblock = thisblock ;
1001b815c7f3Sopenharmony_ci				} ;
1002b815c7f3Sopenharmony_ci			} ;
1003b815c7f3Sopenharmony_ci
1004b815c7f3Sopenharmony_ci		pkt = &odata->pkt [odata->pkt_len - 1] ;
1005b815c7f3Sopenharmony_ci		last_gp = pkt->granulepos ;
1006b815c7f3Sopenharmony_ci		if (last_gp == (uint64_t) -1)
1007b815c7f3Sopenharmony_ci		{	psf_log_printf (psf, "Vorbis: Ogg page has no granule position, cannot calculate sample position!\n") ;
1008b815c7f3Sopenharmony_ci			psf->error = SFE_MALFORMED_FILE ;
1009b815c7f3Sopenharmony_ci			return -1 ;
1010b815c7f3Sopenharmony_ci			} ;
1011b815c7f3Sopenharmony_ci
1012b815c7f3Sopenharmony_ci		if (pkt->e_o_s)
1013b815c7f3Sopenharmony_ci		{	if (last_gp <= duration)
1014b815c7f3Sopenharmony_ci			{	/*	Corner case: One page stream. Ogg/Vorbis spec dictates the
1015b815c7f3Sopenharmony_ci				**	granule position offset MUST be zero, hence this first (and
1016b815c7f3Sopenharmony_ci				**	only) page must start at 0. */
1017b815c7f3Sopenharmony_ci				*gp_out = 0 ;
1018b815c7f3Sopenharmony_ci				return 1 ;
1019b815c7f3Sopenharmony_ci				} ;
1020b815c7f3Sopenharmony_ci
1021b815c7f3Sopenharmony_ci			/*	Otherwise, we cannot know where we are without looking at the
1022b815c7f3Sopenharmony_ci			**	blocks of the previous page.  (The granule position of the
1023b815c7f3Sopenharmony_ci			**	previous page is not enough, we need the block sizes.)
1024b815c7f3Sopenharmony_ci			**
1025b815c7f3Sopenharmony_ci			**	We avoid this case by never allowing a bisection search to seek
1026b815c7f3Sopenharmony_ci			**	beyond the second-to-last page, so the last page is always
1027b815c7f3Sopenharmony_ci			**	approached with a known location and never dropped into.
1028b815c7f3Sopenharmony_ci			**
1029b815c7f3Sopenharmony_ci			**	The only way we should be able to end up here is if there was a
1030b815c7f3Sopenharmony_ci			**	hole in stream just before the last page, in which case all bets
1031b815c7f3Sopenharmony_ci			**	are off anyways. */
1032b815c7f3Sopenharmony_ci			psf_log_printf (psf, "Vorbis: Cannot calculate ambiguous last page duration. Sample count may be wrong.\n") ;
1033b815c7f3Sopenharmony_ci			} ;
1034b815c7f3Sopenharmony_ci
1035b815c7f3Sopenharmony_ci		if (last_gp < duration)
1036b815c7f3Sopenharmony_ci		{	psf_log_printf (psf, "Vorbis: Granule position is nonsensical! (Missing end-of-stream marker?)\n") ;
1037b815c7f3Sopenharmony_ci			psf->error = SFE_MALFORMED_FILE ;
1038b815c7f3Sopenharmony_ci			return -1 ;
1039b815c7f3Sopenharmony_ci			} ;
1040b815c7f3Sopenharmony_ci
1041b815c7f3Sopenharmony_ci		*gp_out = last_gp - duration ;
1042b815c7f3Sopenharmony_ci		return 1 ;
1043b815c7f3Sopenharmony_ci		} ;
1044b815c7f3Sopenharmony_ci
1045b815c7f3Sopenharmony_ci	return 0 ;
1046b815c7f3Sopenharmony_ci} /* vorbis_calculate_granulepos */
1047b815c7f3Sopenharmony_ci
1048b815c7f3Sopenharmony_ci#else /* HAVE_EXTERNAL_XIPH_LIBS */
1049b815c7f3Sopenharmony_ci
1050b815c7f3Sopenharmony_ciint
1051b815c7f3Sopenharmony_ciogg_vorbis_open	(SF_PRIVATE *psf)
1052b815c7f3Sopenharmony_ci{
1053b815c7f3Sopenharmony_ci	psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ;
1054b815c7f3Sopenharmony_ci	return SFE_UNIMPLEMENTED ;
1055b815c7f3Sopenharmony_ci} /* ogg_vorbis_open */
1056b815c7f3Sopenharmony_ci
1057b815c7f3Sopenharmony_ci#endif
1058