| /* | |
| ** Copyright (C) 2018-2021 Arthur Taylor <art@ified.ca> | |
| ** Copyright (C) 2002-2016 Erik de Castro Lopo <erikd@mega-nerd.com> | |
| ** Copyright (C) 2002-2005 Michael Smith <msmith@xiph.org> | |
| ** Copyright (C) 2007 John ffitch | |
| ** | |
| ** This program is free software ; you can redistribute it and/or modify | |
| ** it under the terms of the GNU Lesser General Public License as published by | |
| ** the Free Software Foundation ; either version 2.1 of the License, or | |
| ** (at your option) any later version. | |
| ** | |
| ** This program is distributed in the hope that it will be useful, | |
| ** but WITHOUT ANY WARRANTY ; without even the implied warranty of | |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| ** GNU Lesser General Public License for more details. | |
| ** | |
| ** You should have received a copy of the GNU Lesser General Public License | |
| ** along with this program ; if not, write to the Free Software | |
| ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| */ | |
| /* | |
| ** Much of this code is based on the examples in libvorbis from the | |
| ** XIPHOPHORUS Company http://www.xiph.org/ which has a BSD-style Licence | |
| ** Copyright (c) 2002, Xiph.org Foundation | |
| ** | |
| ** Redistribution and use in source and binary forms, with or without | |
| ** modification, are permitted provided that the following conditions | |
| ** are met: | |
| ** | |
| ** - Redistributions of source code must retain the above copyright | |
| ** notice, this list of conditions and the following disclaimer. | |
| ** | |
| ** - Redistributions in binary form must reproduce the above copyright | |
| ** notice, this list of conditions and the following disclaimer in the | |
| ** documentation and/or other materials provided with the distribution. | |
| ** | |
| ** - Neither the name of the Xiph.org Foundation nor the names of its | |
| ** contributors may be used to endorse or promote products derived from | |
| ** this software without specific prior written permission. | |
| ** | |
| ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| ** ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION | |
| ** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE, | |
| ** DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| #include "sfconfig.h" | |
| #include <stdio.h> | |
| #include <fcntl.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include <time.h> | |
| #include <math.h> | |
| #if HAVE_UNISTD_H | |
| #include <unistd.h> | |
| #else | |
| #include "sf_unistd.h" | |
| #endif | |
| #include "sndfile.h" | |
| #include "sfendian.h" | |
| #include "common.h" | |
| #if HAVE_EXTERNAL_XIPH_LIBS | |
| #include <ogg/ogg.h> | |
| #include <vorbis/codec.h> | |
| #include <vorbis/vorbisenc.h> | |
| #include "ogg.h" | |
| /* How many seconds in the future to not bother bisection searching for. */ | |
| #define VORBIS_SEEK_THRESHOLD 2 | |
| typedef int convert_func (SF_PRIVATE *psf, int, void *, int, int, float **) ; | |
| static int vorbis_read_header (SF_PRIVATE *psf) ; | |
| static int vorbis_write_header (SF_PRIVATE *psf, int calc_length) ; | |
| static int vorbis_close (SF_PRIVATE *psf) ; | |
| static int vorbis_command (SF_PRIVATE *psf, int command, void *data, int datasize) ; | |
| static int vorbis_byterate (SF_PRIVATE *psf) ; | |
| static int vorbis_calculate_granulepos (SF_PRIVATE *psf, uint64_t *gp_out) ; | |
| static int vorbis_skip (SF_PRIVATE *psf, uint64_t target_gp) ; | |
| static int vorbis_seek_trysearch (SF_PRIVATE *psf, uint64_t target_gp) ; | |
| static sf_count_t vorbis_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; | |
| static sf_count_t vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; | |
| static sf_count_t vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) ; | |
| static int vorbis_rnull (SF_PRIVATE *psf, int samples, void *vptr, int off , int channels, float **pcm) ; | |
| typedef struct | |
| { int id ; | |
| const char *name ; | |
| } STR_PAIRS ; | |
| /* See https://xiph.org/vorbis/doc/v-comment.html */ | |
| static STR_PAIRS vorbis_metatypes [] = | |
| { { SF_STR_TITLE, "Title" }, | |
| { SF_STR_COPYRIGHT, "Copyright" }, | |
| { SF_STR_SOFTWARE, "Software" }, | |
| { SF_STR_ARTIST, "Artist" }, | |
| { SF_STR_COMMENT, "Comment" }, | |
| { SF_STR_DATE, "Date" }, | |
| { SF_STR_ALBUM, "Album" }, | |
| { SF_STR_LICENSE, "License" }, | |
| { SF_STR_TRACKNUMBER, "Tracknumber" }, | |
| { SF_STR_GENRE, "Genre" }, | |
| } ; | |
| typedef struct | |
| { /* Current granule position. */ | |
| uint64_t gp ; | |
| /* Struct that stores all the static vorbis bitstream settings */ | |
| vorbis_info vinfo ; | |
| /* Struct that stores all the bitstream user comments */ | |
| vorbis_comment vcomment ; | |
| /* Central working state for the packet->PCM decoder */ | |
| vorbis_dsp_state vdsp ; | |
| /* Local working space for packet->PCM decode */ | |
| vorbis_block vblock ; | |
| /* Encoding quality in range [0.0, 1.0]. */ | |
| double quality ; | |
| /* Offset of the first samples' granule position. */ | |
| uint64_t pcm_start ; | |
| /* Last valid samples' granule position. */ | |
| uint64_t pcm_end ; | |
| /* File offset of the start of the last page. */ | |
| sf_count_t last_page ; | |
| } VORBIS_PRIVATE ; | |
| static int | |
| vorbis_read_header (SF_PRIVATE *psf) | |
| { OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| int printed_metadata_msg = 0 ; | |
| int i, nn ; | |
| sf_count_t last_page ; | |
| sf_count_t saved_offset ; | |
| /* | |
| ** The first page of the Ogg stream we are told to try and open as Vorbis | |
| ** has already been loaded into odata->ostream by ogg_open(). | |
| ** | |
| ** Extract the initial header from the first page and verify that the | |
| ** Ogg bitstream is in fact Vorbis data. | |
| */ | |
| vorbis_info_init (&vdata->vinfo) ; | |
| vorbis_comment_init (&vdata->vcomment) ; | |
| if (!odata->opacket.b_o_s) | |
| { psf_log_printf (psf, "Vorbis: First packet does not have a beginning-of-stream bit.\n") ; | |
| return SFE_MALFORMED_FILE ; | |
| } | |
| if (ogg_stream_packetpeek (&odata->ostream, NULL)) | |
| { psf_log_printf (psf, "Vorbis: First page contains extraneous packets!\n") ; | |
| return SFE_MALFORMED_FILE ; | |
| } | |
| if (vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) < 0) | |
| { /* Error case ; not a vorbis header. */ | |
| psf_log_printf (psf, "Found Vorbis in stream header, but vorbis_synthesis_headerin failed.\n") ; | |
| return SFE_MALFORMED_FILE ; | |
| } ; | |
| /* | |
| ** At this point, we're sure we're Vorbis. We've set up the logical (Ogg) | |
| ** bitstream decoder. Get the comment and codebook headers and set up the | |
| ** Vorbis decoder. | |
| ** | |
| ** The next two packets in order are the comment and codebook headers. | |
| ** They're likely large and may span multiple pages. Thus we read | |
| ** and submit data until we get our two packets, watching that no | |
| ** pages are missing. If a page is missing, error out ; losing a | |
| ** header page is the only place where missing data is fatal. | |
| */ | |
| i = 0 ; /* Count of number of packets read */ | |
| while (i < 2) | |
| { nn = ogg_stream_packetout (&odata->ostream, &odata->opacket) ; | |
| if (nn == 0) | |
| { nn = ogg_stream_next_page (psf, odata) ; | |
| if (nn == 0) | |
| { psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ; | |
| return SFE_MALFORMED_FILE ; | |
| } ; | |
| if (nn == -1) | |
| { psf_log_printf (psf, "Error reading file while finding Vorbis headers!\n") ; | |
| return psf->error ; | |
| } ; | |
| continue ; | |
| } | |
| if (nn < 0) | |
| { /* A hole while reading headers. This could be bad. */ | |
| psf_log_printf (psf, "Corrupt secondary header. Exiting.\n") ; | |
| return SFE_MALFORMED_FILE ; | |
| } ; | |
| vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) ; | |
| i++ ; | |
| } ; | |
| /* Check for extraneous packets in the last headers page. */ | |
| while (ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1) | |
| { i++ ; | |
| } | |
| if (i > 2) | |
| psf_log_printf (psf, "Vorbis: stream has extraneous header packets.\n") ; | |
| psf_log_printf (psf, "Bitstream is %d channel, %D Hz\n", vdata->vinfo.channels, vdata->vinfo.rate) ; | |
| psf_log_printf (psf, "Encoded by : %s\n", vdata->vcomment.vendor) ; | |
| /* Save the offset of the first payload page */ | |
| psf->dataoffset = ogg_sync_ftell (psf) ; | |
| /* | |
| ** Calculate the granule position offset. The first page with a payload | |
| ** packet shouldn't end in a continued packet. The difference between the | |
| ** page's granule position and the sum of frames on the page tells us the | |
| ** granule position offset. | |
| ** See https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-132000A.2 | |
| */ | |
| ogg_stream_unpack_page (psf, odata) ; | |
| vorbis_calculate_granulepos (psf, &vdata->pcm_start) ; | |
| vdata->gp = vdata->pcm_start ; | |
| /* | |
| ** Find the end of the stream, save it. Only works if the file is seekable. | |
| */ | |
| vdata->pcm_end = (uint64_t) -1 ; | |
| psf->datalength = psf->filelength ; | |
| if (!psf->is_pipe) | |
| { saved_offset = ogg_sync_ftell (psf) ; | |
| last_page = ogg_sync_last_page_before (psf, odata, &vdata->pcm_end, psf->filelength, odata->ostream.serialno) ; | |
| if (last_page > 0) | |
| { if (!ogg_page_eos (&odata->opage)) | |
| psf_log_printf (psf, "Ogg: Last page lacks an end-of-stream bit.\n") ; | |
| psf->datalength = last_page + odata->opage.header_len + odata->opage.body_len - psf->dataoffset ; | |
| if (psf->datalength + psf->dataoffset < psf->filelength) | |
| psf_log_printf (psf, "Ogg: Junk after the last page.\n") ; | |
| vdata->last_page = last_page ; | |
| } ; | |
| ogg_sync_fseek (psf, saved_offset, SEEK_SET) ; | |
| } | |
| psf_log_printf (psf, "PCM offset : %D\n", vdata->pcm_start) ; | |
| if (vdata->pcm_end != (uint64_t) -1) | |
| psf_log_printf (psf, "PCM end : %D\n", vdata->pcm_end) ; | |
| else | |
| psf_log_printf (psf, "PCM end : unknown\n") ; | |
| /* Throw the comments plus a few lines about the bitstream we're decoding. */ | |
| for (i = 0 ; i < ARRAY_LEN (vorbis_metatypes) ; i++) | |
| { char *dd ; | |
| dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [i].name, 0) ; | |
| if (dd == NULL) | |
| continue ; | |
| if (printed_metadata_msg == 0) | |
| { psf_log_printf (psf, "Metadata :\n") ; | |
| printed_metadata_msg = 1 ; | |
| } ; | |
| psf_store_string (psf, vorbis_metatypes [i].id, dd) ; | |
| psf_log_printf (psf, " %-10s : %s\n", vorbis_metatypes [i].name, dd) ; | |
| } ; | |
| psf_log_printf (psf, "End\n") ; | |
| psf->sf.samplerate = vdata->vinfo.rate ; | |
| psf->sf.channels = vdata->vinfo.channels ; | |
| psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ; | |
| psf->sf.frames = (vdata->pcm_end != (uint64_t) -1) ? vdata->pcm_end - vdata->pcm_start : SF_COUNT_MAX ; | |
| /* OK, got and parsed all three headers. Initialize the Vorbis | |
| ** packet->PCM decoder. | |
| ** Central decode state. */ | |
| vorbis_synthesis_init (&vdata->vdsp, &vdata->vinfo) ; | |
| /* Local state for most of the decode so multiple block decodes can | |
| ** proceed in parallel. We could init multiple vorbis_block structures | |
| ** for vdsp here. */ | |
| vorbis_block_init (&vdata->vdsp, &vdata->vblock) ; | |
| return 0 ; | |
| } /* vorbis_read_header */ | |
| static int | |
| vorbis_write_header (SF_PRIVATE *psf, int UNUSED (calc_length)) | |
| { | |
| OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| int k, ret ; | |
| vorbis_info_init (&vdata->vinfo) ; | |
| /* The style of encoding should be selectable here, VBR quality mode. */ | |
| ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ; | |
| #if 0 | |
| ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */ | |
| ret = ( vorbis_encode_setup_managed (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) | |
| || vorbis_encode_ctl (&vdata->vinfo, OV_ECTL_RATEMANAGE_AVG, NULL) | |
| || vorbis_encode_setup_init (&vdata->vinfo) | |
| ) ; | |
| #endif | |
| if (ret) | |
| return SFE_BAD_OPEN_FORMAT ; | |
| vdata->gp = 0 ; | |
| /* add a comment */ | |
| vorbis_comment_init (&vdata->vcomment) ; | |
| vorbis_comment_add_tag (&vdata->vcomment, "ENCODER", "libsndfile") ; | |
| for (k = 0 ; k < SF_MAX_STRINGS ; k++) | |
| { const char * name ; | |
| if (psf->strings.data [k].type == 0) | |
| break ; | |
| switch (psf->strings.data [k].type) | |
| { case SF_STR_TITLE : name = "TITLE" ; break ; | |
| case SF_STR_COPYRIGHT : name = "COPYRIGHT" ; break ; | |
| case SF_STR_SOFTWARE : name = "SOFTWARE" ; break ; | |
| case SF_STR_ARTIST : name = "ARTIST" ; break ; | |
| case SF_STR_COMMENT : name = "COMMENT" ; break ; | |
| case SF_STR_DATE : name = "DATE" ; break ; | |
| case SF_STR_ALBUM : name = "ALBUM" ; break ; | |
| case SF_STR_LICENSE : name = "LICENSE" ; break ; | |
| case SF_STR_TRACKNUMBER : name = "Tracknumber" ; break ; | |
| case SF_STR_GENRE : name = "Genre" ; break ; | |
| default : continue ; | |
| } ; | |
| vorbis_comment_add_tag (&vdata->vcomment, name, psf->strings.storage + psf->strings.data [k].offset) ; | |
| } ; | |
| /* set up the analysis state and auxiliary encoding storage */ | |
| vorbis_analysis_init (&vdata->vdsp, &vdata->vinfo) ; | |
| vorbis_block_init (&vdata->vdsp, &vdata->vblock) ; | |
| /* | |
| ** Set up our packet->stream encoder. | |
| ** Pick a random serial number ; that way we can more likely build | |
| ** chained streams just by concatenation. | |
| */ | |
| ogg_stream_init (&odata->ostream, psf_rand_int32 ()) ; | |
| /* Vorbis streams begin with three headers ; the initial header (with | |
| most of the codec setup parameters) which is mandated by the Ogg | |
| bitstream spec. The second header holds any comment fields. The | |
| third header holds the bitstream codebook. We merely need to | |
| make the headers, then pass them to libvorbis one at a time ; | |
| libvorbis handles the additional Ogg bitstream constraints */ | |
| { ogg_packet header ; | |
| ogg_packet header_comm ; | |
| ogg_packet header_code ; | |
| int result ; | |
| vorbis_analysis_headerout (&vdata->vdsp, &vdata->vcomment, &header, &header_comm, &header_code) ; | |
| ogg_stream_packetin (&odata->ostream, &header) ; /* automatically placed in its own page */ | |
| ogg_stream_packetin (&odata->ostream, &header_comm) ; | |
| ogg_stream_packetin (&odata->ostream, &header_code) ; | |
| /* This ensures the actual | |
| * audio data will start on a new page, as per spec | |
| */ | |
| while ((result = ogg_stream_flush (&odata->ostream, &odata->opage)) != 0) | |
| { ogg_write_page (psf, &odata->opage) ; | |
| } ; | |
| } | |
| return 0 ; | |
| } /* vorbis_write_header */ | |
| static int | |
| vorbis_close (SF_PRIVATE *psf) | |
| { OGG_PRIVATE* odata = psf->container_data ; | |
| VORBIS_PRIVATE *vdata = psf->codec_data ; | |
| if (odata == NULL || vdata == NULL) | |
| return 0 ; | |
| /* Clean up this logical bitstream ; before exit we shuld see if we're | |
| ** followed by another [chained]. */ | |
| if (psf->file.mode == SFM_WRITE) | |
| { | |
| if (psf->write_current <= 0) | |
| vorbis_write_header (psf, 0) ; | |
| vorbis_analysis_wrote (&vdata->vdsp, 0) ; | |
| while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1) | |
| { | |
| /* analysis, assume we want to use bitrate management */ | |
| vorbis_analysis (&vdata->vblock, NULL) ; | |
| vorbis_bitrate_addblock (&vdata->vblock) ; | |
| while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket)) | |
| { /* weld the packet into the bitstream */ | |
| ogg_stream_packetin (&odata->ostream, &odata->opacket) ; | |
| /* write out pages (if any) */ | |
| while (!odata->eos) | |
| { int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ; | |
| if (result == 0) break ; | |
| ogg_write_page (psf, &odata->opage) ; | |
| /* this could be set above, but for illustrative purposes, I do | |
| it here (to show that vorbis does know where the stream ends) */ | |
| if (ogg_page_eos (&odata->opage)) odata->eos = 1 ; | |
| } | |
| } | |
| } | |
| } | |
| /* ogg_page and ogg_packet structs always point to storage in | |
| libvorbis. They are never freed or manipulated directly */ | |
| vorbis_block_clear (&vdata->vblock) ; | |
| vorbis_dsp_clear (&vdata->vdsp) ; | |
| vorbis_comment_clear (&vdata->vcomment) ; | |
| vorbis_info_clear (&vdata->vinfo) ; | |
| return 0 ; | |
| } /* vorbis_close */ | |
| int | |
| ogg_vorbis_open (SF_PRIVATE *psf) | |
| { OGG_PRIVATE* odata = psf->container_data ; | |
| VORBIS_PRIVATE* vdata ; | |
| int error = 0 ; | |
| if (odata == NULL) | |
| { psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ; | |
| return SFE_INTERNAL ; | |
| } ; | |
| vdata = calloc (1, sizeof (VORBIS_PRIVATE)) ; | |
| psf->codec_data = vdata ; | |
| if (psf->file.mode == SFM_RDWR) | |
| return SFE_BAD_MODE_RW ; | |
| psf_log_printf (psf, "Vorbis library version : %s\n", vorbis_version_string ()) ; | |
| if (psf->file.mode == SFM_READ) | |
| { if ((error = vorbis_read_header (psf))) | |
| return error ; | |
| psf->read_short = vorbis_read_s ; | |
| psf->read_int = vorbis_read_i ; | |
| psf->read_float = vorbis_read_f ; | |
| psf->read_double = vorbis_read_d ; | |
| } ; | |
| psf->codec_close = vorbis_close ; | |
| if (psf->file.mode == SFM_WRITE) | |
| { | |
| /* Set the default vorbis quality here. */ | |
| vdata->quality = 0.4 ; | |
| psf->write_header = vorbis_write_header ; | |
| psf->write_short = vorbis_write_s ; | |
| psf->write_int = vorbis_write_i ; | |
| psf->write_float = vorbis_write_f ; | |
| psf->write_double = vorbis_write_d ; | |
| psf->sf.frames = 0 ; | |
| psf->datalength = 0 ; | |
| psf->filelength = 0 ; | |
| psf->dataoffset = 0 ; | |
| psf->strings.flags = SF_STR_ALLOW_START ; | |
| } ; | |
| psf->seek = vorbis_seek ; | |
| psf->command = vorbis_command ; | |
| psf->byterate = vorbis_byterate ; | |
| psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ; | |
| psf->sf.sections = 1 ; | |
| return error ; | |
| } /* ogg_vorbis_open */ | |
| static int | |
| vorbis_command (SF_PRIVATE *psf, int command, void * data, int datasize) | |
| { OGG_PRIVATE* odata = psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| switch (command) | |
| { case SFC_SET_COMPRESSION_LEVEL : | |
| if (data == NULL || datasize != sizeof (double)) | |
| return SF_FALSE ; | |
| if (psf->have_written) | |
| return SF_FALSE ; | |
| vdata->quality = 1.0 - *((double *) data) ; | |
| /* Clip range. */ | |
| vdata->quality = SF_MAX (0.0, SF_MIN (1.0, vdata->quality)) ; | |
| psf_log_printf (psf, "%s : Setting SFC_SET_VBR_ENCODING_QUALITY to %f.\n", __func__, vdata->quality) ; | |
| return SF_TRUE ; | |
| case SFC_GET_OGG_STREAM_SERIALNO : | |
| if (data == NULL || datasize != sizeof (int32_t)) | |
| return SF_FALSE ; | |
| *((int32_t *) data) = odata->ostream.serialno ; | |
| return SF_TRUE ; | |
| default : | |
| return SF_FALSE ; | |
| } ; | |
| return SF_FALSE ; | |
| } /* vorbis_command */ | |
| static int | |
| vorbis_rnull (SF_PRIVATE *UNUSED (psf), int samples, void *UNUSED (vptr), int UNUSED (off) , int channels, float **UNUSED (pcm)) | |
| { | |
| return samples * channels ; | |
| } /* vorbis_rnull */ | |
| static int | |
| vorbis_rshort (SF_PRIVATE *psf, int samples, void *vptr, int off, int channels, float **pcm) | |
| { | |
| short *ptr = (short*) vptr + off ; | |
| int i = 0, j, n ; | |
| if (psf->float_int_mult) | |
| { | |
| float inverse = 1.0 / psf->float_max ; | |
| for (j = 0 ; j < samples ; j++) | |
| for (n = 0 ; n < channels ; n++) | |
| ptr [i++] = psf_lrintf ((pcm [n][j] * inverse) * 32767.0f) ; | |
| } | |
| else | |
| { | |
| for (j = 0 ; j < samples ; j++) | |
| for (n = 0 ; n < channels ; n++) | |
| ptr [i++] = psf_lrintf (pcm [n][j] * 32767.0f) ; | |
| } | |
| return i ; | |
| } /* vorbis_rshort */ | |
| static int | |
| vorbis_rint (SF_PRIVATE *psf, int samples, void *vptr, int off, int channels, float **pcm) | |
| { | |
| int *ptr = (int*) vptr + off ; | |
| int i = 0, j, n ; | |
| if (psf->float_int_mult) | |
| { | |
| float inverse = 1.0 / psf->float_max ; | |
| for (j = 0 ; j < samples ; j++) | |
| for (n = 0 ; n < channels ; n++) | |
| ptr [i++] = psf_lrintf ((pcm [n][j] * inverse) * 2147483647.0f) ; | |
| } | |
| else | |
| { | |
| for (j = 0 ; j < samples ; j++) | |
| for (n = 0 ; n < channels ; n++) | |
| ptr [i++] = psf_lrintf (pcm [n][j] * 2147483647.0f) ; | |
| } | |
| return i ; | |
| } /* vorbis_rint */ | |
| static int | |
| vorbis_rfloat (SF_PRIVATE *UNUSED (psf), int samples, void *vptr, int off, int channels, float **pcm) | |
| { | |
| float *ptr = (float*) vptr + off ; | |
| int i = 0, j, n ; | |
| for (j = 0 ; j < samples ; j++) | |
| for (n = 0 ; n < channels ; n++) | |
| ptr [i++] = pcm [n][j] ; | |
| return i ; | |
| } /* vorbis_rfloat */ | |
| static int | |
| vorbis_rdouble (SF_PRIVATE *UNUSED (psf), int samples, void *vptr, int off, int channels, float **pcm) | |
| { | |
| double *ptr = (double*) vptr + off ; | |
| int i = 0, j, n ; | |
| for (j = 0 ; j < samples ; j++) | |
| for (n = 0 ; n < channels ; n++) | |
| ptr [i++] = pcm [n][j] ; | |
| return i ; | |
| } /* vorbis_rdouble */ | |
| static sf_count_t | |
| vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) | |
| { VORBIS_PRIVATE *vdata = psf->codec_data ; | |
| OGG_PRIVATE *odata = psf->container_data ; | |
| int len, samples, i = 0 , nn ; | |
| float **pcm ; | |
| len = lens / psf->sf.channels ; | |
| while (len > 0) | |
| { /* | |
| ** pcm is a multichannel float vector. In stereo, for | |
| ** example, pcm [0] is left, and pcm [1] is right. samples is | |
| ** the size of each channel. Convert the float values | |
| ** (-1.<=range<=1.) to whatever PCM format and write it out. | |
| */ | |
| while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0) | |
| { if (samples > len) samples = len ; | |
| i += transfn (psf, samples, ptr, i, psf->sf.channels, pcm) ; | |
| len -= samples ; | |
| /* tell libvorbis how many samples we actually consumed */ | |
| vorbis_synthesis_read (&vdata->vdsp, samples) ; | |
| vdata->gp += samples ; | |
| if (len == 0) | |
| return i ; | |
| } ; | |
| /* Out of samples, load the next packet. */ | |
| if (odata->pkt_indx == odata->pkt_len) | |
| { /* Page out of packets, load and unpack the next page. */ | |
| nn = ogg_stream_unpack_page (psf, odata) ; | |
| if (nn <= 0) | |
| return i ; | |
| if (nn == 2) | |
| { /* Ran over a hole. gp is now out of date, need to recalculate. */ | |
| vorbis_synthesis_restart (&vdata->vdsp) ; | |
| vorbis_calculate_granulepos (psf, &vdata->gp) ; | |
| } | |
| } ; | |
| /* Decode the packet */ | |
| if (vorbis_synthesis (&vdata->vblock, &(odata->pkt [odata->pkt_indx])) == 0) /* test for success! */ | |
| vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ; | |
| odata->pkt_indx++ ; | |
| } ; | |
| return i ; | |
| } /* vorbis_read_sample */ | |
| static sf_count_t | |
| vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t lens) | |
| { return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rshort) ; | |
| } /* vorbis_read_s */ | |
| static sf_count_t | |
| vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t lens) | |
| { return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rint) ; | |
| } /* vorbis_read_i */ | |
| static sf_count_t | |
| vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t lens) | |
| { return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rfloat) ; | |
| } /* vorbis_read_f */ | |
| static sf_count_t | |
| vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t lens) | |
| { return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rdouble) ; | |
| } /* vorbis_read_d */ | |
| /*============================================================================== | |
| */ | |
| static void | |
| vorbis_write_samples (SF_PRIVATE *psf, OGG_PRIVATE *odata, VORBIS_PRIVATE *vdata, int in_frames) | |
| { | |
| vorbis_analysis_wrote (&vdata->vdsp, in_frames) ; | |
| /* | |
| ** Vorbis does some data preanalysis, then divvies up blocks for | |
| ** more involved (potentially parallel) processing. Get a single | |
| ** block for encoding now. | |
| */ | |
| while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1) | |
| { | |
| /* analysis, assume we want to use bitrate management */ | |
| vorbis_analysis (&vdata->vblock, NULL) ; | |
| vorbis_bitrate_addblock (&vdata->vblock) ; | |
| while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket)) | |
| { | |
| /* weld the packet into the bitstream */ | |
| ogg_stream_packetin (&odata->ostream, &odata->opacket) ; | |
| /* write out pages (if any) */ | |
| while (!odata->eos) | |
| { int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ; | |
| if (result == 0) | |
| break ; | |
| ogg_write_page (psf, &odata->opage) ; | |
| /* This could be set above, but for illustrative purposes, I do | |
| ** it here (to show that vorbis does know where the stream ends) */ | |
| if (ogg_page_eos (&odata->opage)) | |
| odata->eos = 1 ; | |
| } ; | |
| } ; | |
| } ; | |
| vdata->gp += in_frames ; | |
| } /* vorbis_write_data */ | |
| static sf_count_t | |
| vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t lens) | |
| { | |
| int i, m, j = 0 ; | |
| OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| int in_frames = lens / psf->sf.channels ; | |
| float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; | |
| for (i = 0 ; i < in_frames ; i++) | |
| for (m = 0 ; m < psf->sf.channels ; m++) | |
| buffer [m][i] = (float) (ptr [j++]) / 32767.0f ; | |
| vorbis_write_samples (psf, odata, vdata, in_frames) ; | |
| return lens ; | |
| } /* vorbis_write_s */ | |
| static sf_count_t | |
| vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t lens) | |
| { int i, m, j = 0 ; | |
| OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| int in_frames = lens / psf->sf.channels ; | |
| float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; | |
| for (i = 0 ; i < in_frames ; i++) | |
| for (m = 0 ; m < psf->sf.channels ; m++) | |
| buffer [m][i] = (float) (ptr [j++]) / 2147483647.0f ; | |
| vorbis_write_samples (psf, odata, vdata, in_frames) ; | |
| return lens ; | |
| } /* vorbis_write_i */ | |
| static sf_count_t | |
| vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t lens) | |
| { int i, m, j = 0 ; | |
| OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| int in_frames = lens / psf->sf.channels ; | |
| float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; | |
| for (i = 0 ; i < in_frames ; i++) | |
| for (m = 0 ; m < psf->sf.channels ; m++) | |
| buffer [m][i] = ptr [j++] ; | |
| vorbis_write_samples (psf, odata, vdata, in_frames) ; | |
| return lens ; | |
| } /* vorbis_write_f */ | |
| static sf_count_t | |
| vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t lens) | |
| { int i, m, j = 0 ; | |
| OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| int in_frames = lens / psf->sf.channels ; | |
| float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; | |
| for (i = 0 ; i < in_frames ; i++) | |
| for (m = 0 ; m < psf->sf.channels ; m++) | |
| buffer [m][i] = (float) ptr [j++] ; | |
| vorbis_write_samples (psf, odata, vdata, in_frames) ; | |
| return lens ; | |
| } /* vorbis_write_d */ | |
| static int | |
| vorbis_skip (SF_PRIVATE *psf, uint64_t target) | |
| { OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| ogg_packet *pkt ; | |
| int thisblock, lastblock, nn ; | |
| const int blocksize = vorbis_info_blocksize (&vdata->vinfo, 1) ; | |
| /* Read out any samples that may be in the decoder from a seek without a | |
| ** search. */ | |
| thisblock = vorbis_synthesis_pcmout (&vdata->vdsp, NULL) ; | |
| if (thisblock > 0) | |
| { if ((uint64_t) thisblock + vdata->gp >= target) | |
| thisblock = SF_MIN (thisblock, (int) (target - vdata->gp)) ; | |
| vorbis_synthesis_read (&vdata->vdsp, thisblock) ; | |
| vdata->gp += thisblock ; | |
| if (vdata->gp == target) | |
| return 0 ; | |
| } ; | |
| /* Read through packets that are before our target */ | |
| lastblock = 0 ; | |
| for ( ; vdata->gp < target ; ) | |
| { /* Ensure there are unpacked packets. */ | |
| if (odata->pkt_indx == odata->pkt_len) | |
| { /* Page out of packets, load and unpack the next page. */ | |
| nn = ogg_stream_unpack_page (psf, odata) ; | |
| if (nn < 0) | |
| return nn ; | |
| if (nn == 0) | |
| break ; | |
| if (nn == 2) | |
| { /* Ran over a hole. gp is now out of date, need to recalculate. */ | |
| vorbis_synthesis_restart (&vdata->vdsp) ; | |
| vorbis_calculate_granulepos (psf, &vdata->gp) ; | |
| if (target < vdata->gp) | |
| { /* Our target is inside the hole :-( */ | |
| return 0 ; | |
| } ; | |
| } ; | |
| } ; | |
| pkt = &odata->pkt [odata->pkt_indx] ; | |
| thisblock = vorbis_packet_blocksize (&vdata->vinfo, pkt) ; | |
| if (thisblock < 0) | |
| { /* Not an audio packet */ | |
| odata->pkt_indx++ ; | |
| continue ; | |
| } ; | |
| if (lastblock) | |
| { vdata->gp += ((lastblock + thisblock) / 4) ; | |
| } ; | |
| /* Check to see if the block contains our target */ | |
| if (vdata->gp + ((thisblock + blocksize) / 4) >= target) | |
| break ; | |
| /* Block is before the target. Track for state, but don't decode. */ | |
| odata->pkt_indx++ ; | |
| vorbis_synthesis_trackonly (&vdata->vblock, pkt) ; | |
| vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ; | |
| lastblock = thisblock ; | |
| } ; | |
| /* We are at the correct block, but still need to consume samples to reach | |
| ** our target. */ | |
| vorbis_read_sample (psf, (void *) NULL, (target - vdata->gp) * psf->sf.channels, vorbis_rnull) ; | |
| return 0 ; | |
| } /* vorbis_skip */ | |
| static int | |
| vorbis_seek_trysearch (SF_PRIVATE *psf, uint64_t target_gp) | |
| { OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| uint64_t best_gp, search_target_gp ; | |
| int ret ; | |
| /* Can't bisect a file we don't know the end of (cannot seek). */ | |
| if (vdata->pcm_end == (uint64_t) -1) | |
| return 0 ; | |
| /* If the target is for the near future, don't bother bisecting, just skip | |
| ** to it. */ | |
| if (target_gp >= vdata->gp && | |
| target_gp - vdata->gp < ((unsigned) (VORBIS_SEEK_THRESHOLD) * psf->sf.samplerate)) | |
| return 0 ; | |
| /* Search for a position a half large-block before our target. As Vorbis is | |
| ** lapped, every sample position come from two blocks, the "left" half of | |
| ** one block and the "right" half of the previous block. The granule | |
| ** position of an Ogg page of a Vorbis stream is the sample offset of the | |
| ** last finished sample in the stream that can be decoded from a page. A | |
| ** page also contains another half-block of samples waiting to be lapped | |
| ** with the first half-block of samples from the next page. | |
| ** | |
| ** Searching for a sample one half of a large block before our target | |
| ** guarantees we always load a page containing the previous half block | |
| ** required to decode the target. Downside is we can't use best_gp | |
| ** parameter of the page seek function. */ | |
| search_target_gp = vorbis_info_blocksize (&vdata->vinfo, 1) / 2 ; | |
| search_target_gp = search_target_gp < target_gp ? target_gp - search_target_gp : 0 ; | |
| ret = ogg_stream_seek_page_search (psf, odata, search_target_gp, vdata->pcm_start, | |
| vdata->pcm_end, &best_gp, psf->dataoffset, vdata->last_page, vdata->vinfo.rate) ; | |
| if (ret < 0) | |
| return ret ; | |
| ret = ogg_stream_unpack_page (psf, odata) ; | |
| if (ret > 0) | |
| { /* Reset the decoder, recalculate position */ | |
| vorbis_synthesis_restart (&vdata->vdsp) ; | |
| ret = vorbis_calculate_granulepos (psf, &vdata->gp) ; | |
| } ; | |
| return ret ; | |
| } /* vorbis_seek_trysearch */ | |
| static sf_count_t | |
| vorbis_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset) | |
| { OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| uint64_t target_gp ; | |
| int ret ; | |
| if (odata == NULL || vdata == NULL) | |
| return 0 ; | |
| if (offset < 0) | |
| { psf->error = SFE_BAD_SEEK ; | |
| return ((sf_count_t) -1) ; | |
| } ; | |
| if (psf->file.mode == SFM_READ) | |
| { target_gp = (uint64_t) offset + vdata->pcm_start ; | |
| ret = vorbis_seek_trysearch (psf, target_gp) ; | |
| if (ret < 0 || vdata->gp > target_gp) | |
| { /* Search failed (bad data?), reset to the beginning of the stream. */ | |
| psf_log_printf (psf, "Vorbis: Seek search failed. Reading through stream from start.\n") ; | |
| ogg_stream_reset_serialno (&odata->ostream, odata->ostream.serialno) ; | |
| odata->pkt_len = 0 ; | |
| odata->pkt_indx = 0 ; | |
| ogg_sync_fseek (psf, psf->dataoffset, SEEK_SET) ; | |
| vdata->gp = vdata->pcm_start ; | |
| vorbis_synthesis_restart (&vdata->vdsp) ; | |
| } ; | |
| vorbis_skip (psf, target_gp) ; | |
| return vdata->gp - vdata->pcm_start ; | |
| } ; | |
| psf->error = SFE_BAD_SEEK ; | |
| return ((sf_count_t) -1) ; | |
| } /* vorbis_seek */ | |
| static int | |
| vorbis_byterate (SF_PRIVATE *psf) | |
| { | |
| if (psf->file.mode == SFM_READ) | |
| return (psf->datalength * psf->sf.samplerate) / psf->sf.frames ; | |
| return -1 ; | |
| } /* vorbis_byterate */ | |
| static int | |
| vorbis_calculate_granulepos (SF_PRIVATE *psf, uint64_t *gp_out) | |
| { OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; | |
| VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; | |
| ogg_packet *pkt ; | |
| uint64_t last_gp ; | |
| int thisblock, lastblock, i ; | |
| unsigned duration ; | |
| /* Calculate the granule position when dropped into the middle of a stream | |
| ** with an un-primed decoder. | |
| ** | |
| ** Normally the last unpacked packet contains the granule position of the | |
| ** last completed sample from decoding all the blocks in the page's | |
| ** packets. By calculating how many samples we can decode from the blocks | |
| ** in the page's packets and subtracting it from the final packet's granule | |
| ** position we get the position of the first sample to be output from the | |
| ** decoder after it primes. That is, the current granule position. | |
| ** | |
| ** However, there is an ambiguity if this is the last page of a stream. The | |
| ** last page of a stream may have a granule position of fewer samples than | |
| ** the page actually contains. The excess samples are padding leftovers | |
| ** for and exact sample length file. */ | |
| if (odata->pkt_len > 0) | |
| { /* Calculate how many samples can be decoded from blocks in this page, | |
| ** accounting for the fact that blocks are 1/2 lapped. */ | |
| lastblock = -1 ; | |
| duration = 0 ; | |
| pkt = odata->pkt ; | |
| for (i = 0 ; i < odata->pkt_len ; i++) | |
| { thisblock = vorbis_packet_blocksize (&vdata->vinfo, &pkt [i]) ; | |
| if (thisblock >= 0) | |
| { if (lastblock != -1) | |
| duration += (lastblock + thisblock) >> 2 ; | |
| lastblock = thisblock ; | |
| } ; | |
| } ; | |
| pkt = &odata->pkt [odata->pkt_len - 1] ; | |
| last_gp = pkt->granulepos ; | |
| if (last_gp == (uint64_t) -1) | |
| { psf_log_printf (psf, "Vorbis: Ogg page has no granule position, cannot calculate sample position!\n") ; | |
| psf->error = SFE_MALFORMED_FILE ; | |
| return -1 ; | |
| } ; | |
| if (pkt->e_o_s) | |
| { if (last_gp <= duration) | |
| { /* Corner case: One page stream. Ogg/Vorbis spec dictates the | |
| ** granule position offset MUST be zero, hence this first (and | |
| ** only) page must start at 0. */ | |
| *gp_out = 0 ; | |
| return 1 ; | |
| } ; | |
| /* Otherwise, we cannot know where we are without looking at the | |
| ** blocks of the previous page. (The granule position of the | |
| ** previous page is not enough, we need the block sizes.) | |
| ** | |
| ** We avoid this case by never allowing a bisection search to seek | |
| ** beyond the second-to-last page, so the last page is always | |
| ** approached with a known location and never dropped into. | |
| ** | |
| ** The only way we should be able to end up here is if there was a | |
| ** hole in stream just before the last page, in which case all bets | |
| ** are off anyways. */ | |
| psf_log_printf (psf, "Vorbis: Cannot calculate ambiguous last page duration. Sample count may be wrong.\n") ; | |
| } ; | |
| if (last_gp < duration) | |
| { psf_log_printf (psf, "Vorbis: Granule position is nonsensical! (Missing end-of-stream marker?)\n") ; | |
| psf->error = SFE_MALFORMED_FILE ; | |
| return -1 ; | |
| } ; | |
| *gp_out = last_gp - duration ; | |
| return 1 ; | |
| } ; | |
| return 0 ; | |
| } /* vorbis_calculate_granulepos */ | |
| #else /* HAVE_EXTERNAL_XIPH_LIBS */ | |
| int | |
| ogg_vorbis_open (SF_PRIVATE *psf) | |
| { | |
| psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ; | |
| return SFE_UNIMPLEMENTED ; | |
| } /* ogg_vorbis_open */ | |
| #endif |