X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/1059e1cc0c2ecfa237406949aa26155b6a5b9154..66f23d4fabf89ad09adbd4dfc15ac6b5b2b7da83:/interpreter/lib/src/libsndfile-1.0.25/src/ogg_vorbis.c diff --git a/interpreter/lib/src/libsndfile-1.0.25/src/ogg_vorbis.c b/interpreter/lib/src/libsndfile-1.0.25/src/ogg_vorbis.c new file mode 100644 index 0000000..99223cd --- /dev/null +++ b/interpreter/lib/src/libsndfile-1.0.25/src/ogg_vorbis.c @@ -0,0 +1,1142 @@ +/* +** Copyright (C) 2002-2011 Erik de Castro Lopo +** Copyright (C) 2002-2005 Michael Smith +** 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 +#include +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#if HAVE_EXTERNAL_LIBS + +#include +#include +#include + +#include "ogg.h" + +typedef int convert_func (int, void *, int, int, float **) ; + +static int vorbis_read_header (SF_PRIVATE *psf, int log_data) ; +static int vorbis_write_header (SF_PRIVATE *psf, int calc_length) ; +static int vorbis_close (SF_PRIVATE *psf) ; +static int vorbis_command (SF_PRIVATE *psf, int command, void *data, int datasize) ; +static sf_count_t vorbis_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; +static sf_count_t vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) ; +static sf_count_t vorbis_length (SF_PRIVATE *psf) ; + +typedef struct +{ int id ; + const char *name ; +} STR_PAIRS ; + +static STR_PAIRS vorbis_metatypes [] = +{ { SF_STR_TITLE, "Title" }, + { SF_STR_COPYRIGHT, "Copyright" }, + { SF_STR_SOFTWARE, "Software" }, + { SF_STR_ARTIST, "Artist" }, + { SF_STR_COMMENT, "Comment" }, + { SF_STR_DATE, "Date" }, + { SF_STR_ALBUM, "Album" }, + { SF_STR_LICENSE, "License" }, +} ; + +typedef struct +{ /* Count current location */ + sf_count_t loc ; + /* Struct that stores all the static vorbis bitstream settings */ + vorbis_info vinfo ; + /* Struct that stores all the bitstream user comments */ + vorbis_comment vcomment ; + /* Ventral working state for the packet->PCM decoder */ + vorbis_dsp_state vdsp ; + /* Local working space for packet->PCM decode */ + vorbis_block vblock ; + + /* Encoding quality in range [0.0, 1.0]. */ + double quality ; +} VORBIS_PRIVATE ; + +static int +vorbis_read_header (SF_PRIVATE *psf, int log_data) +{ + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + char *buffer ; + int bytes ; + int i, nn ; + + odata->eos = 0 ; + + /* Weird stuff happens if these aren't called. */ + ogg_stream_reset (&odata->ostream) ; + ogg_sync_reset (&odata->osync) ; + + /* + ** Grab some data at the head of the stream. We want the first page + ** (which is guaranteed to be small and only contain the Vorbis + ** stream initial header) We need the first page to get the stream + ** serialno. + */ + + /* Expose the buffer */ + buffer = ogg_sync_buffer (&odata->osync, 4096L) ; + + /* Grab the part of the header that has already been read. */ + memcpy (buffer, psf->header, psf->headindex) ; + bytes = psf->headindex ; + + /* Submit a 4k block to libvorbis' Ogg layer */ + bytes += psf_fread (buffer + psf->headindex, 1, 4096 - psf->headindex, psf) ; + ogg_sync_wrote (&odata->osync, bytes) ; + + /* Get the first page. */ + if ((nn = ogg_sync_pageout (&odata->osync, &odata->opage)) != 1) + { + /* Have we simply run out of data? If so, we're done. */ + if (bytes < 4096) + return 0 ; + + /* Error case. Must not be Vorbis data */ + psf_log_printf (psf, "Input does not appear to be an Ogg bitstream.\n") ; + return SFE_MALFORMED_FILE ; + } ; + + /* + ** Get the serial number and set up the rest of decode. + ** Serialno first ; use it to set up a logical stream. + */ + ogg_stream_clear (&odata->ostream) ; + ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ; + + if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0) + { /* Error ; stream version mismatch perhaps. */ + psf_log_printf (psf, "Error reading first page of Ogg bitstream data\n") ; + return SFE_MALFORMED_FILE ; + } ; + + if (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1) + { /* No page? must not be vorbis. */ + psf_log_printf (psf, "Error reading initial header packet.\n") ; + return SFE_MALFORMED_FILE ; + } ; + + /* + ** This function (vorbis_read_header) gets called multiple times, so the OGG + ** and vorbis structs have to be cleared every time we pass through to + ** prevent memory leaks. + */ + vorbis_block_clear (&vdata->vblock) ; + vorbis_dsp_clear (&vdata->vdsp) ; + vorbis_comment_clear (&vdata->vcomment) ; + vorbis_info_clear (&vdata->vinfo) ; + + /* + ** Extract the initial header from the first page and verify that the + ** Ogg bitstream is in fact Vorbis data. + ** + ** I handle the initial header first instead of just having the code + ** read all three Vorbis headers at once because reading the initial + ** header is an easy way to identify a Vorbis bitstream and it's + ** useful to see that functionality seperated out. + */ + vorbis_info_init (&vdata->vinfo) ; + vorbis_comment_init (&vdata->vcomment) ; + + if (vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) < 0) + { /* Error case ; not a vorbis header. */ + psf_log_printf (psf, "Found Vorbis in stream header, but vorbis_synthesis_headerin failed.\n") ; + return SFE_MALFORMED_FILE ; + } ; + + /* + ** Common Ogg metadata fields? + ** TITLE, VERSION, ALBUM, TRACKNUMBER, ARTIST, PERFORMER, COPYRIGHT, LICENSE, + ** ORGANIZATION, DESCRIPTION, GENRE, DATE, LOCATION, CONTACT, ISRC, + */ + + if (log_data) + { int k ; + + for (k = 0 ; k < ARRAY_LEN (vorbis_metatypes) ; k++) + { char *dd ; + + dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [k].name, 0) ; + if (dd == NULL) + continue ; + psf_store_string (psf, vorbis_metatypes [k].id, dd) ; + } ; + } ; + + /* + ** At this point, we're sure we're Vorbis. We've set up the logical (Ogg) + ** bitstream decoder. Get the comment and codebook headers and set up the + ** Vorbis decoder. + ** + ** The next two packets in order are the comment and codebook headers. + ** They're likely large and may span multiple pages. Thus we reead + ** and submit data until we get our two pacakets, watching that no + ** pages are missing. If a page is missing, error out ; losing a + ** header page is the only place where missing data is fatal. + */ + + i = 0 ; /* Count of number of packets read */ + while (i < 2) + { int result = ogg_sync_pageout (&odata->osync, &odata->opage) ; + if (result == 0) + { /* Need more data */ + buffer = ogg_sync_buffer (&odata->osync, 4096) ; + bytes = psf_fread (buffer, 1, 4096, psf) ; + + if (bytes == 0 && i < 2) + { psf_log_printf (psf, "End of file before finding all Vorbis headers!\n") ; + return SFE_MALFORMED_FILE ; + } ; + nn = ogg_sync_wrote (&odata->osync, bytes) ; + } + else if (result == 1) + { /* + ** Don't complain about missing or corrupt data yet. We'll + ** catch it at the packet output phase. + ** + ** We can ignore any errors here as they'll also become apparent + ** at packetout. + */ + nn = ogg_stream_pagein (&odata->ostream, &odata->opage) ; + while (i < 2) + { result = ogg_stream_packetout (&odata->ostream, &odata->opacket) ; + if (result == 0) + break ; + if (result < 0) + { /* Uh oh ; data at some point was corrupted or missing! + ** We can't tolerate that in a header. Die. */ + psf_log_printf (psf, "Corrupt secondary header. Exiting.\n") ; + return SFE_MALFORMED_FILE ; + } ; + + vorbis_synthesis_headerin (&vdata->vinfo, &vdata->vcomment, &odata->opacket) ; + i++ ; + } ; + } ; + } ; + + if (log_data) + { int printed_metadata_msg = 0 ; + int k ; + + psf_log_printf (psf, "Bitstream is %d channel, %D Hz\n", vdata->vinfo.channels, vdata->vinfo.rate) ; + psf_log_printf (psf, "Encoded by : %s\n", vdata->vcomment.vendor) ; + + /* Throw the comments plus a few lines about the bitstream we're decoding. */ + for (k = 0 ; k < ARRAY_LEN (vorbis_metatypes) ; k++) + { char *dd ; + + dd = vorbis_comment_query (&vdata->vcomment, vorbis_metatypes [k].name, 0) ; + if (dd == NULL) + continue ; + + if (printed_metadata_msg == 0) + { psf_log_printf (psf, "Metadata :\n") ; + printed_metadata_msg = 1 ; + } ; + + psf_store_string (psf, vorbis_metatypes [k].id, dd) ; + psf_log_printf (psf, " %-10s : %s\n", vorbis_metatypes [k].name, dd) ; + } ; + + psf_log_printf (psf, "End\n") ; + } ; + + psf->sf.samplerate = vdata->vinfo.rate ; + psf->sf.channels = vdata->vinfo.channels ; + psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ; + + /* OK, got and parsed all three headers. Initialize the Vorbis + ** packet->PCM decoder. + ** Central decode state. */ + vorbis_synthesis_init (&vdata->vdsp, &vdata->vinfo) ; + + /* Local state for most of the decode so multiple block decodes can + ** proceed in parallel. We could init multiple vorbis_block structures + ** for vd here. */ + vorbis_block_init (&vdata->vdsp, &vdata->vblock) ; + + vdata->loc = 0 ; + + return 0 ; +} /* vorbis_read_header */ + +static int +vorbis_write_header (SF_PRIVATE *psf, int UNUSED (calc_length)) +{ + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + int k, ret ; + + vorbis_info_init (&vdata->vinfo) ; + + /* The style of encoding should be selectable here, VBR quality mode. */ + ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ; + +#if 0 + ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */ + ret = ( vorbis_encode_setup_managed (&vdata->vinfo, psf->sf.channels, + psf->sf.samplerate, -1, 128000, -1) || + vorbis_encode_ctl (&vdata->vinfo, OV_ECTL_RATEMANAGE_AVG, NULL) || + vorbis_encode_setup_init (&vdata->vinfo)) ; +#endif + if (ret) + return SFE_BAD_OPEN_FORMAT ; + + vdata->loc = 0 ; + + /* add a comment */ + vorbis_comment_init (&vdata->vcomment) ; + + vorbis_comment_add_tag (&vdata->vcomment, "ENCODER", "libsndfile") ; + for (k = 0 ; k < SF_MAX_STRINGS ; k++) + { const char * name ; + + if (psf->strings [k].type == 0) + break ; + + switch (psf->strings [k].type) + { case SF_STR_TITLE : name = "TITLE" ; break ; + case SF_STR_COPYRIGHT : name = "COPYRIGHT" ; break ; + case SF_STR_SOFTWARE : name = "SOFTWARE" ; break ; + case SF_STR_ARTIST : name = "ARTIST" ; break ; + case SF_STR_COMMENT : name = "COMMENT" ; break ; + case SF_STR_DATE : name = "DATE" ; break ; + case SF_STR_ALBUM : name = "ALBUM" ; break ; + case SF_STR_LICENSE : name = "LICENSE" ; break ; + default : continue ; + } ; + + vorbis_comment_add_tag (&vdata->vcomment, name, psf->strings [k].str) ; + } ; + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init (&vdata->vdsp, &vdata->vinfo) ; + vorbis_block_init (&vdata->vdsp, &vdata->vblock) ; + + /* + ** Set up our packet->stream encoder. + ** Pick a random serial number ; that way we can more likely build + ** chained streams just by concatenation. + */ + + ogg_stream_init (&odata->ostream, psf_rand_int32 ()) ; + + /* Vorbis streams begin with three headers ; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time ; + libvorbis handles the additional Ogg bitstream constraints */ + + { ogg_packet header ; + ogg_packet header_comm ; + ogg_packet header_code ; + int result ; + + vorbis_analysis_headerout (&vdata->vdsp, &vdata->vcomment, &header, &header_comm, &header_code) ; + ogg_stream_packetin (&odata->ostream, &header) ; /* automatically placed in its own page */ + ogg_stream_packetin (&odata->ostream, &header_comm) ; + ogg_stream_packetin (&odata->ostream, &header_code) ; + + /* This ensures the actual + * audio data will start on a new page, as per spec + */ + while ((result = ogg_stream_flush (&odata->ostream, &odata->opage)) != 0) + { psf_fwrite (odata->opage.header, 1, odata->opage.header_len, psf) ; + psf_fwrite (odata->opage.body, 1, odata->opage.body_len, psf) ; + } ; + } + + return 0 ; +} /* vorbis_write_header */ + +static int +vorbis_close (SF_PRIVATE *psf) +{ OGG_PRIVATE* odata = psf->container_data ; + VORBIS_PRIVATE *vdata = psf->codec_data ; + + if (odata == NULL || vdata == NULL) + return 0 ; + + /* Clean up this logical bitstream ; before exit we shuld see if we're + ** followed by another [chained]. */ + + if (psf->file.mode == SFM_WRITE) + { + if (psf->write_current <= 0) + vorbis_write_header (psf, 0) ; + + vorbis_analysis_wrote (&vdata->vdsp, 0) ; + while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1) + { + + /* analysis, assume we want to use bitrate management */ + vorbis_analysis (&vdata->vblock, NULL) ; + vorbis_bitrate_addblock (&vdata->vblock) ; + + while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket)) + { /* weld the packet into the bitstream */ + ogg_stream_packetin (&odata->ostream, &odata->opacket) ; + + /* write out pages (if any) */ + while (!odata->eos) + { int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ; + if (result == 0) break ; + psf_fwrite (odata->opage.header, 1, odata->opage.header_len, psf) ; + psf_fwrite (odata->opage.body, 1, odata->opage.body_len, psf) ; + + /* this could be set above, but for illustrative purposes, I do + it here (to show that vorbis does know where the stream ends) */ + + if (ogg_page_eos (&odata->opage)) odata->eos = 1 ; + } + } + } + } + + /* ogg_page and ogg_packet structs always point to storage in + libvorbis. They are never freed or manipulated directly */ + + vorbis_block_clear (&vdata->vblock) ; + vorbis_dsp_clear (&vdata->vdsp) ; + vorbis_comment_clear (&vdata->vcomment) ; + vorbis_info_clear (&vdata->vinfo) ; + + return 0 ; +} /* vorbis_close */ + +int +ogg_vorbis_open (SF_PRIVATE *psf) +{ OGG_PRIVATE* odata = psf->container_data ; + VORBIS_PRIVATE* vdata = calloc (1, sizeof (VORBIS_PRIVATE)) ; + int error = 0 ; + + if (odata == NULL) + { psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ; + return SFE_INTERNAL ; + } ; + + psf->codec_data = vdata ; + + if (psf->file.mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + psf_log_printf (psf, "Vorbis library version : %s\n", vorbis_version_string ()) ; + + if (psf->file.mode == SFM_READ) + { /* Call this here so it only gets called once, so no memory is leaked. */ + ogg_sync_init (&odata->osync) ; + + if ((error = vorbis_read_header (psf, 1))) + return error ; + + psf->read_short = vorbis_read_s ; + psf->read_int = vorbis_read_i ; + psf->read_float = vorbis_read_f ; + psf->read_double = vorbis_read_d ; + psf->sf.frames = vorbis_length (psf) ; + } ; + + psf->codec_close = vorbis_close ; + if (psf->file.mode == SFM_WRITE) + { + /* Set the default vorbis quality here. */ + vdata->quality = 0.4 ; + + psf->write_header = vorbis_write_header ; + psf->write_short = vorbis_write_s ; + psf->write_int = vorbis_write_i ; + psf->write_float = vorbis_write_f ; + psf->write_double = vorbis_write_d ; + + psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */ + psf->str_flags = SF_STR_ALLOW_START ; + } ; + + psf->bytewidth = 1 ; + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + psf->seek = vorbis_seek ; + psf->command = vorbis_command ; + + /* FIXME, FIXME, FIXME : Hack these here for now and correct later. */ + psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ; + psf->sf.sections = 1 ; + + psf->datalength = 1 ; + psf->dataoffset = 0 ; + /* End FIXME. */ + + return error ; +} /* ogg_vorbis_open */ + +static int +vorbis_command (SF_PRIVATE *psf, int command, void * data, int datasize) +{ VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + + switch (command) + { case SFC_SET_VBR_ENCODING_QUALITY : + if (data == NULL || datasize != sizeof (double)) + return SF_FALSE ; + + if (psf->have_written) + return SF_FALSE ; + + vdata->quality = *((double *) data) ; + + /* Clip range. */ + vdata->quality = SF_MAX (0.0, SF_MIN (1.0, vdata->quality)) ; + + psf_log_printf (psf, "%s : Setting SFC_SET_VBR_ENCODING_QUALITY to %f.\n", __func__, vdata->quality) ; + return SF_TRUE ; + + default : + return SF_FALSE ; + } ; + + return SF_FALSE ; +} /* vorbis_command */ + +static int +vorbis_rnull (int samples, void *UNUSED (vptr), int UNUSED (off) , int channels, float **UNUSED (pcm)) +{ + return samples * channels ; +} /* vorbis_rnull */ + +static int +vorbis_rshort (int samples, void *vptr, int off, int channels, float **pcm) +{ + short *ptr = (short*) vptr + off ; + int i = 0, j, n ; + for (j = 0 ; j < samples ; j++) + for (n = 0 ; n < channels ; n++) + ptr [i++] = lrintf (pcm [n][j] * 32767.0f) ; + return i ; +} /* vorbis_rshort */ + +static int +vorbis_rint (int samples, void *vptr, int off, int channels, float **pcm) +{ + int *ptr = (int*) vptr + off ; + int i = 0, j, n ; + + for (j = 0 ; j < samples ; j++) + for (n = 0 ; n < channels ; n++) + ptr [i++] = lrintf (pcm [n][j] * 2147483647.0f) ; + return i ; +} /* vorbis_rint */ + +static int +vorbis_rfloat (int samples, void *vptr, int off, int channels, float **pcm) +{ + float *ptr = (float*) vptr + off ; + int i = 0, j, n ; + for (j = 0 ; j < samples ; j++) + for (n = 0 ; n < channels ; n++) + ptr [i++] = pcm [n][j] ; + return i ; +} /* vorbis_rfloat */ + +static int +vorbis_rdouble (int samples, void *vptr, int off, int channels, float **pcm) +{ + double *ptr = (double*) vptr + off ; + int i = 0, j, n ; + for (j = 0 ; j < samples ; j++) + for (n = 0 ; n < channels ; n++) + ptr [i++] = pcm [n][j] ; + return i ; +} /* vorbis_rdouble */ + + +static sf_count_t +vorbis_read_sample (SF_PRIVATE *psf, void *ptr, sf_count_t lens, convert_func *transfn) +{ + VORBIS_PRIVATE *vdata = psf->codec_data ; + OGG_PRIVATE *odata = psf->container_data ; + int len, samples, i = 0 ; + float **pcm ; + + len = lens / psf->sf.channels ; + + while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0) + { if (samples > len) samples = len ; + i += transfn (samples, ptr, i, psf->sf.channels, pcm) ; + len -= samples ; + /* tell libvorbis how many samples we actually consumed */ + vorbis_synthesis_read (&vdata->vdsp, samples) ; + vdata->loc += samples ; + if (len == 0) + return i ; /* Is this necessary */ + } + goto start0 ; /* Jump into the nasty nest */ + while (len > 0 && !odata->eos) + { + while (len > 0 && !odata->eos) + { int result = ogg_sync_pageout (&odata->osync, &odata->opage) ; + if (result == 0) break ; /* need more data */ + if (result < 0) + { /* missing or corrupt data at this page position */ + psf_log_printf (psf, "Corrupt or missing data in bitstream ; continuing...\n") ; + } + else + { /* can safely ignore errors at this point */ + ogg_stream_pagein (&odata->ostream, &odata->opage) ; + start0: + while (1) + { result = ogg_stream_packetout (&odata->ostream, &odata->opacket) ; + if (result == 0) + break ; /* need more data */ + if (result < 0) + { /* missing or corrupt data at this page position */ + /* no reason to complain ; already complained above */ + } + else + { /* we have a packet. Decode it */ + if (vorbis_synthesis (&vdata->vblock, &odata->opacket) == 0) /* test for success! */ + vorbis_synthesis_blockin (&vdata->vdsp, &vdata->vblock) ; + /* + **pcm is a multichannel float vector. In stereo, for + example, pcm [0] is left, and pcm [1] is right. samples is + the size of each channel. Convert the float values + (-1.<=range<=1.) to whatever PCM format and write it out */ + + while ((samples = vorbis_synthesis_pcmout (&vdata->vdsp, &pcm)) > 0) + { if (samples>len) samples = len ; + i += transfn (samples, ptr, i, psf->sf.channels, pcm) ; + len -= samples ; + /* tell libvorbis how many samples we actually consumed */ + vorbis_synthesis_read (&vdata->vdsp, samples) ; + vdata->loc += samples ; + if (len == 0) + return i ; /* Is this necessary */ + } ; + } + } + if (ogg_page_eos (&odata->opage)) odata->eos = 1 ; + } + } + if (!odata->eos) + { char *buffer ; + int bytes ; + buffer = ogg_sync_buffer (&odata->osync, 4096) ; + bytes = psf_fread (buffer, 1, 4096, psf) ; + ogg_sync_wrote (&odata->osync, bytes) ; + if (bytes == 0) odata->eos = 1 ; + } + } + return i ; +} /* vorbis_read_sample */ + +static sf_count_t +vorbis_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t lens) +{ return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rshort) ; +} /* vorbis_read_s */ + +static sf_count_t +vorbis_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t lens) +{ return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rint) ; +} /* vorbis_read_i */ + +static sf_count_t +vorbis_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t lens) +{ return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rfloat) ; +} /* vorbis_read_f */ + +static sf_count_t +vorbis_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t lens) +{ return vorbis_read_sample (psf, (void*) ptr, lens, vorbis_rdouble) ; +} /* vorbis_read_d */ + +/*============================================================================== +*/ + +static void +vorbis_write_samples (SF_PRIVATE *psf, OGG_PRIVATE *odata, VORBIS_PRIVATE *vdata, int in_frames) +{ + vorbis_analysis_wrote (&vdata->vdsp, in_frames) ; + + /* + ** Vorbis does some data preanalysis, then divvies up blocks for + ** more involved (potentially parallel) processing. Get a single + ** block for encoding now. + */ + while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1) + { + /* analysis, assume we want to use bitrate management */ + vorbis_analysis (&vdata->vblock, NULL) ; + vorbis_bitrate_addblock (&vdata->vblock) ; + + while (vorbis_bitrate_flushpacket (&vdata->vdsp, &odata->opacket)) + { + /* weld the packet into the bitstream */ + ogg_stream_packetin (&odata->ostream, &odata->opacket) ; + + /* write out pages (if any) */ + while (!odata->eos) + { int result = ogg_stream_pageout (&odata->ostream, &odata->opage) ; + if (result == 0) + break ; + psf_fwrite (odata->opage.header, 1, odata->opage.header_len, psf) ; + psf_fwrite (odata->opage.body, 1, odata->opage.body_len, psf) ; + + /* This could be set above, but for illustrative purposes, I do + ** it here (to show that vorbis does know where the stream ends) */ + if (ogg_page_eos (&odata->opage)) + odata->eos = 1 ; + } ; + } ; + } ; + + vdata->loc += in_frames ; +} /* vorbis_write_data */ + + +static sf_count_t +vorbis_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t lens) +{ + int i, m, j = 0 ; + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + int in_frames = lens / psf->sf.channels ; + float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; + for (i = 0 ; i < in_frames ; i++) + for (m = 0 ; m < psf->sf.channels ; m++) + buffer [m][i] = (float) (ptr [j++]) / 32767.0f ; + + vorbis_write_samples (psf, odata, vdata, in_frames) ; + + return lens ; +} /* vorbis_write_s */ + +static sf_count_t +vorbis_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t lens) +{ int i, m, j = 0 ; + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + int in_frames = lens / psf->sf.channels ; + float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; + for (i = 0 ; i < in_frames ; i++) + for (m = 0 ; m < psf->sf.channels ; m++) + buffer [m][i] = (float) (ptr [j++]) / 2147483647.0f ; + + vorbis_write_samples (psf, odata, vdata, in_frames) ; + + return lens ; +} /* vorbis_write_i */ + +static sf_count_t +vorbis_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t lens) +{ int i, m, j = 0 ; + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + int in_frames = lens / psf->sf.channels ; + float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; + for (i = 0 ; i < in_frames ; i++) + for (m = 0 ; m < psf->sf.channels ; m++) + buffer [m][i] = ptr [j++] ; + + vorbis_write_samples (psf, odata, vdata, in_frames) ; + + return lens ; +} /* vorbis_write_f */ + +static sf_count_t +vorbis_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t lens) +{ int i, m, j = 0 ; + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + int in_frames = lens / psf->sf.channels ; + float **buffer = vorbis_analysis_buffer (&vdata->vdsp, in_frames) ; + for (i = 0 ; i < in_frames ; i++) + for (m = 0 ; m < psf->sf.channels ; m++) + buffer [m][i] = (float) ptr [j++] ; + + vorbis_write_samples (psf, odata, vdata, in_frames) ; + + return lens ; +} /* vorbis_write_d */ + +static sf_count_t +vorbis_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset) +{ + OGG_PRIVATE *odata = (OGG_PRIVATE *) psf->container_data ; + VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; + + if (odata == NULL || vdata == NULL) + return 0 ; + + if (offset < 0) + { psf->error = SFE_BAD_SEEK ; + return ((sf_count_t) -1) ; + } ; + + if (psf->file.mode == SFM_READ) + { sf_count_t target = offset - vdata->loc ; + + if (target < 0) + { /* 12 to allow for OggS bit */ + psf_fseek (psf, 12, SEEK_SET) ; + vorbis_read_header (psf, 0) ; /* Reset state */ + target = offset ; + } ; + + while (target > 0) + { sf_count_t m = target > 4096 ? 4096 : target ; + + /* + ** Need to multiply by channels here because the seek is done in + ** terms of frames and the read function is done in terms of + ** samples. + */ + vorbis_read_sample (psf, (void *) NULL, m * psf->sf.channels, vorbis_rnull) ; + + target -= m ; + } ; + + return vdata->loc ; + } ; + + return 0 ; +} /* vorbis_seek */ + +/*============================================================================== +** Most of the following code was snipped from Mike Smith's ogginfo utility +** which is part of vorbis-tools. +** Vorbis tools is released under the GPL but Mike has kindly allowed the +** following to be relicensed as LGPL for libsndfile. +*/ + +typedef struct +{ + int isillegal ; + int shownillegal ; + int isnew ; + int end ; + + uint32_t serial ; /* must be 32 bit unsigned */ + ogg_stream_state ostream ; + + vorbis_info vinfo ; + vorbis_comment vcomment ; + sf_count_t lastgranulepos ; + int doneheaders ; +} stream_processor ; + +typedef struct +{ + stream_processor *streams ; + int allocated ; + int used ; + int in_headers ; +} stream_set ; + +static stream_set * +create_stream_set (void) +{ stream_set *set = calloc (1, sizeof (stream_set)) ; + + set->streams = calloc (5, sizeof (stream_processor)) ; + set->allocated = 5 ; + set->used = 0 ; + + return set ; +} /* create_stream_set */ + +static void +vorbis_end (stream_processor *stream, sf_count_t * len) +{ *len += stream->lastgranulepos ; + vorbis_comment_clear (&stream->vcomment) ; + vorbis_info_clear (&stream->vinfo) ; +} /* vorbis_end */ + +static void +free_stream_set (stream_set *set, sf_count_t * len) +{ int i ; + + for (i = 0 ; i < set->used ; i++) + { if (!set->streams [i].end) + vorbis_end (&set->streams [i], len) ; + ogg_stream_clear (&set->streams [i].ostream) ; + } ; + + free (set->streams) ; + free (set) ; +} /* free_stream_set */ + +static int +streams_open (stream_set *set) +{ int i, res = 0 ; + + for (i = 0 ; i < set->used ; i++) + if (!set->streams [i].end) + res ++ ; + return res ; +} /* streams_open */ + +static stream_processor * +find_stream_processor (stream_set *set, ogg_page *page) +{ uint32_t serial = ogg_page_serialno (page) ; + int i, invalid = 0 ; + + stream_processor *stream ; + + for (i = 0 ; i < set->used ; i++) + { + if (serial == set->streams [i].serial) + { /* We have a match! */ + stream = & (set->streams [i]) ; + + set->in_headers = 0 ; + /* if we have detected EOS, then this can't occur here. */ + if (stream->end) + { stream->isillegal = 1 ; + return stream ; + } + + stream->isnew = 0 ; + stream->end = ogg_page_eos (page) ; + stream->serial = serial ; + return stream ; + } ; + } ; + + /* If there are streams open, and we've reached the end of the + ** headers, then we can't be starting a new stream. + ** XXX: might this sometimes catch ok streams if EOS flag is missing, + ** but the stream is otherwise ok? + */ + if (streams_open (set) && !set->in_headers) + invalid = 1 ; + + set->in_headers = 1 ; + + if (set->allocated < set->used) + stream = &set->streams [set->used] ; + else + { set->allocated += 5 ; + set->streams = realloc (set->streams, sizeof (stream_processor) * set->allocated) ; + stream = &set->streams [set->used] ; + } ; + + set->used++ ; + + stream->isnew = 1 ; + stream->isillegal = invalid ; + + { + int res ; + ogg_packet packet ; + + /* We end up processing the header page twice, but that's ok. */ + ogg_stream_init (&stream->ostream, serial) ; + ogg_stream_pagein (&stream->ostream, page) ; + res = ogg_stream_packetout (&stream->ostream, &packet) ; + if (res <= 0) + return NULL ; + else if (packet.bytes >= 7 && memcmp (packet.packet, "\x01vorbis", 7) == 0) + { + stream->lastgranulepos = 0 ; + vorbis_comment_init (&stream->vcomment) ; + vorbis_info_init (&stream->vinfo) ; + } ; + + res = ogg_stream_packetout (&stream->ostream, &packet) ; + + /* re-init, ready for processing */ + ogg_stream_clear (&stream->ostream) ; + ogg_stream_init (&stream->ostream, serial) ; + } + + stream->end = ogg_page_eos (page) ; + stream->serial = serial ; + + return stream ; +} /* find_stream_processor */ + +static int +vorbis_length_get_next_page (SF_PRIVATE *psf, ogg_sync_state * osync, ogg_page *page) +{ static const int CHUNK_SIZE = 4500 ; + + while (ogg_sync_pageout (osync, page) <= 0) + { char * buffer = ogg_sync_buffer (osync, CHUNK_SIZE) ; + int bytes = psf_fread (buffer, 1, 4096, psf) ; + + if (bytes <= 0) + { ogg_sync_wrote (osync, 0) ; + return 0 ; + } ; + + ogg_sync_wrote (osync, bytes) ; + } ; + + return 1 ; +} /* vorbis_length_get_next_page */ + +static sf_count_t +vorbis_length_aux (SF_PRIVATE * psf) +{ + ogg_sync_state osync ; + ogg_page page ; + sf_count_t len = 0 ; + stream_set *processors ; + + processors = create_stream_set () ; + if (processors == NULL) + return 0 ; // out of memory? + + ogg_sync_init (&osync) ; + + while (vorbis_length_get_next_page (psf, &osync, &page)) + { + stream_processor *p = find_stream_processor (processors, &page) ; + + if (!p) + { len = 0 ; + break ; + } ; + + if (p->isillegal && !p->shownillegal) + { + p->shownillegal = 1 ; + /* If it's a new stream, we want to continue processing this page + ** anyway to suppress additional spurious errors + */ + if (!p->isnew) continue ; + } ; + + if (!p->isillegal) + { ogg_packet packet ; + int header = 0 ; + + ogg_stream_pagein (&p->ostream, &page) ; + if (p->doneheaders < 3) + header = 1 ; + + while (ogg_stream_packetout (&p->ostream, &packet) > 0) + { + if (p->doneheaders < 3) + { if (vorbis_synthesis_headerin (&p->vinfo, &p->vcomment, &packet) < 0) + continue ; + p->doneheaders ++ ; + } ; + } ; + if (!header) + { sf_count_t gp = ogg_page_granulepos (&page) ; + if (gp > 0) p->lastgranulepos = gp ; + } ; + if (p->end) + { vorbis_end (p, &len) ; + p->isillegal = 1 ; + } ; + } ; + } ; + + ogg_sync_clear (&osync) ; + free_stream_set (processors, &len) ; + + return len ; +} /* vorbis_length_aux */ + +static sf_count_t +vorbis_length (SF_PRIVATE *psf) +{ sf_count_t length ; + int error ; + + if (psf->sf.seekable == 0) + return SF_COUNT_MAX ; + + psf_fseek (psf, 0, SEEK_SET) ; + length = vorbis_length_aux (psf) ; + + psf_fseek (psf, 12, SEEK_SET) ; + if ((error = vorbis_read_header (psf, 0)) != 0) + psf->error = error ; + + return length ; +} /* vorbis_length */ + +#else /* HAVE_EXTERNAL_LIBS */ + +int +ogg_vorbis_open (SF_PRIVATE *psf) +{ + psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ; + return SFE_UNIMPLEMENTED ; +} /* ogg_vorbis_open */ + +#endif