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