898 lines
23 KiB
C
898 lines
23 KiB
C
/*
|
|
** Copyright (C) 2002-2004 Erik de Castro Lopo <erikd@mega-nerd.com>
|
|
**
|
|
** 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.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "sndfile.h"
|
|
#include "config.h"
|
|
#include "sfendian.h"
|
|
#include "common.h"
|
|
|
|
#if (ENABLE_EXPERIMENTAL_CODE == 0)
|
|
|
|
int
|
|
ogg_open (SF_PRIVATE *psf)
|
|
{ if (psf)
|
|
return SFE_UNIMPLEMENTED ;
|
|
return (psf && 0) ;
|
|
} /* ogg_open */
|
|
|
|
#else
|
|
|
|
#define SFE_OGG_NOT_OGG 666
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Macros to handle big/little endian issues.
|
|
*/
|
|
|
|
#define ALAW_MARKER MAKE_MARKER ('A', 'L', 'a', 'w')
|
|
#define SOUN_MARKER MAKE_MARKER ('S', 'o', 'u', 'n')
|
|
#define DFIL_MARKER MAKE_MARKER ('d', 'F', 'i', 'l')
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Private static functions.
|
|
*/
|
|
|
|
static int ogg_read_header (SF_PRIVATE *psf) ;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Public function.
|
|
*/
|
|
|
|
int
|
|
ogg_open (SF_PRIVATE *psf)
|
|
{ OGG_PRIVATE *pogg ;
|
|
int subformat, error = 0 ;
|
|
|
|
if (psf->mode == SFM_RDWR)
|
|
return SFE_UNIMPLEMENTED ;
|
|
|
|
psf->sf.sections = 1 ;
|
|
|
|
psf->datalength = psf->filelength ;
|
|
psf->dataoffset = 0 ;
|
|
psf->blockwidth = 0 ;
|
|
psf->bytewidth = 1 ;
|
|
|
|
if (! (pogg = calloc (1, sizeof (OGG_PRIVATE))))
|
|
return SFE_MALLOC_FAILED ;
|
|
psf->fdata = pogg ;
|
|
|
|
if (psf->mode == SFM_READ)
|
|
{ if ((error = pogg_read_header (psf)))
|
|
return error ;
|
|
} ;
|
|
|
|
if (psf->mode == SFM_WRITE)
|
|
{ psf->str_flags = SF_STR_ALLOW_START ;
|
|
|
|
if ((error = pogg_write_header (psf)))
|
|
return error ;
|
|
} ;
|
|
|
|
if ((psf->sf.format & SF_FORMAT_TYPEMASK) == 0)
|
|
return SFE_BAD_OPEN_FORMAT ;
|
|
|
|
subformat = psf->sf.format & SF_FORMAT_SUBMASK ;
|
|
if (subformat == 0)
|
|
return SFE_BAD_OPEN_FORMAT ;
|
|
|
|
return pogg_init (psf) ;
|
|
} /* ogg_open */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Private functions
|
|
*/
|
|
|
|
static int
|
|
pogg_init (SF_PRIVATE * psf)
|
|
{
|
|
psf->close = pogg_close ;
|
|
|
|
if (psf->mode == SFM_READ)
|
|
{ /* set the virtual functions for reading */
|
|
psf->read_short = pogg_read_s ;
|
|
psf->read_int = pogg_read_i ;
|
|
psf->read_float = pogg_read_f ;
|
|
psf->read_double = pogg_read_d ;
|
|
|
|
/* set the virtual function for seeking */
|
|
psf->seek = pogg_seek ;
|
|
} ;
|
|
|
|
if (psf->mode == SFM_WRITE)
|
|
{ /* set the virtual functions for writing */
|
|
psf->write_short = pogg_write_s ;
|
|
psf->write_int = pogg_write_i ;
|
|
psf->write_float = pogg_write_f ;
|
|
psf->write_double = pogg_write_d ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
} /* pogg_init */
|
|
|
|
static int
|
|
pogg_close (SF_PRIVATE * psf)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
long n ;
|
|
|
|
if (psf->mode == SFM_READ)
|
|
{ if (pogg->cache_pcm != NULL)
|
|
free (pogg->cache_pcm) ;
|
|
/* MUST NOT free pogg->ptr, it is a pointer into the user's buffers */
|
|
} ;
|
|
|
|
if (psf->mode == SFM_WRITE)
|
|
{ fish_sound_flush (pogg->fsound) ;
|
|
while ((n = oggz_write (pogg->oggz, 1024)) > 0) ;
|
|
} ;
|
|
|
|
if (pogg->oggz)
|
|
oggz_close (pogg->oggz) ;
|
|
if (pogg->fsound)
|
|
fish_sound_delete (pogg->fsound) ;
|
|
|
|
return 0 ;
|
|
} /* pogg_close */
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** OggzIO methods
|
|
*/
|
|
|
|
static size_t
|
|
pogg_io_read (void * user_handle, void * buf, size_t n)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_handle ;
|
|
|
|
return (size_t) psf_fread (buf, 1, n, psf) ;
|
|
} /* pogg_io_read */
|
|
|
|
static int
|
|
pogg_io_seek (void * user_handle, long offset, int whence)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_handle ;
|
|
|
|
return (size_t) psf_fseek (psf, offset, whence) ;
|
|
} /* pogg_io_seek */
|
|
|
|
static long
|
|
pogg_io_tell (void * user_handle)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_handle ;
|
|
|
|
return (size_t) psf_ftell (psf) ;
|
|
} /* pogg_io_tell */
|
|
|
|
static size_t
|
|
pogg_io_write (void * user_handle, void * buf, size_t n)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_handle ;
|
|
|
|
return (size_t) psf_fwrite (buf, 1, n, psf) ;
|
|
} /* pogg_io_write */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Read last packet -- set the number of frames to be the last recorded
|
|
** granulepos.
|
|
*/
|
|
|
|
static int
|
|
pogg_read_last_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * data)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) data ;
|
|
|
|
/* Avoid compiler warning. */
|
|
oggz = NULL ;
|
|
serialno = 0 ;
|
|
|
|
if (op->granulepos == -1)
|
|
return OGGZ_CONTINUE ;
|
|
|
|
psf->sf.frames = op->granulepos ;
|
|
|
|
return OGGZ_STOP_OK ;
|
|
} /* pogg_read_least_packet */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Decode header -- by the time FishSound calls this, all header codebooks etc.
|
|
** have been parsed and the Oggz is ready for seeking.
|
|
*/
|
|
|
|
static int
|
|
pogg_decode_header (FishSound * fsound, float ** pcm, long frames, void * user_data)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_data ;
|
|
FishSoundInfo fsinfo ;
|
|
const FishSoundComment * comment ;
|
|
|
|
/* Avoid compiler warnings. */
|
|
pcm = NULL ;
|
|
frames = 0 ;
|
|
|
|
fish_sound_command (fsound, FISH_SOUND_GET_INFO, &fsinfo, sizeof (FishSoundInfo)) ;
|
|
|
|
switch (fsinfo.format)
|
|
{ case FISH_SOUND_VORBIS :
|
|
psf_log_printf (psf, "Vorbis\n") ;
|
|
psf->sf.format |= SF_FORMAT_VORBIS ;
|
|
break ;
|
|
case FISH_SOUND_SPEEX :
|
|
psf_log_printf (psf, "Speex\n") ;
|
|
psf->sf.format |= SF_FORMAT_SPEEX ;
|
|
break ;
|
|
default :
|
|
psf_log_printf (psf, "Unknown Ogg codec\n") ;
|
|
break ;
|
|
} ;
|
|
|
|
psf->sf.samplerate = fsinfo.samplerate ;
|
|
psf->sf.channels = fsinfo.channels ;
|
|
|
|
/* Get comments */
|
|
for (comment = fish_sound_comment_first (fsound) ; comment ;
|
|
comment = fish_sound_comment_next (fsound, comment))
|
|
{ psf_log_printf (psf, "%s : %s\n", comment->name, comment->value) ;
|
|
|
|
if (strcasecmp (comment->name, "TITLE") == 0)
|
|
psf_store_string (psf, SF_STR_TITLE, comment->value) ;
|
|
else if (strcasecmp (comment->name, "COPYRIGHT") == 0)
|
|
psf_store_string (psf, SF_STR_COPYRIGHT, comment->value) ;
|
|
else if (strcasecmp (comment->name, "ENCODER") == 0)
|
|
psf_store_string (psf, SF_STR_SOFTWARE, comment->value) ;
|
|
else if (strcasecmp (comment->name, "ARTIST") == 0)
|
|
psf_store_string (psf, SF_STR_ARTIST, comment->value) ;
|
|
else if (strcasecmp (comment->name, "DATE") == 0)
|
|
psf_store_string (psf, SF_STR_DATE, comment->value) ;
|
|
else if (strcasecmp (comment->name, "author") == 0)
|
|
{ /* speexenc provides this */
|
|
psf_store_string (psf, SF_STR_ARTIST, comment->value) ;
|
|
} ;
|
|
} ;
|
|
|
|
puts (psf->logbuffer) ;
|
|
|
|
return 1 ;
|
|
} /* pogg_decode_header */
|
|
|
|
static int
|
|
pogg_read_header_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * data)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) data ;
|
|
OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
int format ;
|
|
|
|
/* Avoid compiler warning. */
|
|
oggz = NULL ;
|
|
|
|
if (pogg->serialno == -1)
|
|
psf_log_printf (psf, "Read Ogg packet header : [%s]\n", op->packet) ;
|
|
|
|
if (pogg->serialno == -1 && op->bytes >= 8)
|
|
{ format = fish_sound_identify (op->packet, 8) ;
|
|
if (format == FISH_SOUND_VORBIS || format == FISH_SOUND_SPEEX)
|
|
{ /*
|
|
** Detect this is (probably) the audio stream. Don't set the subformat
|
|
** yet, do that in the decoded callback, once FishSound has had a proper
|
|
** look at all the headers and codebooks etc. and the file is ready for
|
|
** decoding and seeking. We use the value of (psf->sf.format & _SUBMASK)
|
|
** below to determine whether the headers have all been read or not.
|
|
*/
|
|
pogg->serialno = serialno ;
|
|
}
|
|
else if (strncmp (op->packet, "Annodex", 8) == 0)
|
|
{ /* The overall stream encpasulation is Annodex */
|
|
psf->sf.format = SF_FORMAT_ANX ;
|
|
} ;
|
|
} ;
|
|
|
|
if (serialno == pogg->serialno)
|
|
fish_sound_decode (pogg->fsound, op->packet, op->bytes) ;
|
|
|
|
if ((psf->sf.format & SF_FORMAT_SUBMASK) == 0)
|
|
return OGGZ_CONTINUE ;
|
|
|
|
return OGGZ_STOP_OK ;
|
|
} /* pogg_read_header_packet */
|
|
|
|
static int
|
|
pogg_read_header (SF_PRIVATE *psf)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
unsigned char buf [1024] ;
|
|
|
|
OGGZ * oggz ;
|
|
FishSound * fsound ;
|
|
FishSoundInfo fsinfo ;
|
|
int nread = 1024 ;
|
|
|
|
psf->sf.format = SF_FORMAT_OGG ;
|
|
psf->sf.frames = 0 ;
|
|
|
|
oggz = oggz_new (OGGZ_READ|OGGZ_AUTO) ;
|
|
|
|
oggz_io_set_read (oggz, pogg_io_read, psf) ;
|
|
oggz_io_set_seek (oggz, pogg_io_seek, psf) ;
|
|
oggz_io_set_tell (oggz, pogg_io_tell, psf) ;
|
|
|
|
fsound = fish_sound_new (FISH_SOUND_DECODE, &fsinfo) ;
|
|
fish_sound_set_interleave (fsound, 1) ;
|
|
fish_sound_set_decoded_callback (fsound, pogg_decode_header, psf) ;
|
|
|
|
pogg->oggz = oggz ;
|
|
pogg->fsound = fsound ;
|
|
pogg->serialno = -1 ;
|
|
pogg->cache_pcm = NULL ;
|
|
pogg->cache_size = 0 ;
|
|
pogg->cache_granulepos = 0 ; /* We set this to a known value of zero to begin */
|
|
pogg->cache_frames = 0 ;
|
|
pogg->cache_remaining = 0 ;
|
|
pogg->ptr = NULL ;
|
|
pogg->pcmtype = POGG_PCM_SHORT ;
|
|
pogg->remaining = 0 ;
|
|
pogg->seek_from_start = 0 ;
|
|
|
|
/* Set position to start of file to begin reading header. */
|
|
psf_binheader_readf (psf, "p", 0) ;
|
|
|
|
/* Get the header info */
|
|
oggz_set_read_callback (oggz, -1, pogg_read_header_packet, psf) ;
|
|
while (nread > 0 && ((psf->sf.format & SF_FORMAT_SUBMASK) == 0))
|
|
{ nread = psf_binheader_readf (psf, "b", buf, sizeof (buf)) ;
|
|
oggz_read_input (oggz, buf, nread) ;
|
|
} ;
|
|
|
|
/* Get the duration */
|
|
oggz_set_read_callback (oggz, -1, NULL, NULL) ;
|
|
oggz_set_read_callback (oggz, pogg->serialno, pogg_read_last_packet, psf) ;
|
|
oggz_seek_units (oggz, 0, SEEK_END) ;
|
|
nread = 1024 ;
|
|
while (nread > 0)
|
|
nread = oggz_read (oggz, 1024) ;
|
|
|
|
/* reset to the beginning of the audio data */
|
|
oggz_seek_units (oggz, 0, SEEK_SET) ;
|
|
|
|
psf->dataoffset = oggz_tell (oggz) ;
|
|
psf->datalength = psf->filelength - psf->dataoffset ;
|
|
|
|
/* set the Oggz and FishSound up for decoding */
|
|
oggz_set_read_callback (oggz, -1, NULL, NULL) ;
|
|
oggz_set_read_callback (oggz, pogg->serialno, pogg_read_packet, psf) ;
|
|
fish_sound_set_decoded_callback (fsound, pogg_decode, psf) ;
|
|
|
|
return 0 ;
|
|
} /* pogg_read_header */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Decode functions
|
|
*/
|
|
|
|
static int
|
|
pogg_copyout (SF_PRIVATE * psf)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
size_t frame_size, bytes, cache_offset ;
|
|
long cache_usable, i ;
|
|
unsigned char * src ;
|
|
|
|
if (pogg->seek_from_start > 0)
|
|
{ /* If we've seeked and don't know where we are, don't do anything yet */
|
|
if (pogg->cache_granulepos == -1)
|
|
return -1 ;
|
|
|
|
/* If we've seeked and are before the seek point, don't do anything yet */
|
|
else if (pogg->cache_granulepos < pogg->seek_from_start)
|
|
return -1 ;
|
|
|
|
/* If this block contains the seek point, adjust the cache offset accordingly */
|
|
else if (pogg->cache_granulepos - pogg->cache_frames <= pogg->seek_from_start)
|
|
{ pogg->cache_remaining = pogg->cache_granulepos - pogg->seek_from_start ;
|
|
pogg->seek_from_start = 0 ; /* bingo */
|
|
} ;
|
|
} ;
|
|
|
|
frame_size = psf->sf.channels * sizeof (float) ;
|
|
cache_usable = SF_MIN (pogg->remaining, pogg->cache_remaining) ;
|
|
|
|
if (cache_usable <= 0)
|
|
return 0 ;
|
|
|
|
bytes = cache_usable * frame_size ;
|
|
cache_offset = (pogg->cache_frames - pogg->cache_remaining) * frame_size ;
|
|
src = (unsigned char *) pogg->cache_pcm + cache_offset ;
|
|
|
|
switch (pogg->pcmtype)
|
|
{ case POGG_PCM_SHORT :
|
|
for (i = 0 ; i < cache_usable ; i++)
|
|
((short *) pogg->ptr) [i] = (short) (((float *) src) [i] * SHRT_MAX) ;
|
|
break ;
|
|
|
|
case POGG_PCM_INT :
|
|
for (i = 0 ; i < cache_usable ; i++)
|
|
((double *) pogg->ptr) [i] = (double) (((float *) src) [i] * INT_MAX) ;
|
|
break ;
|
|
|
|
case POGG_PCM_FLOAT :
|
|
memcpy (pogg->ptr, src, bytes) ;
|
|
break ;
|
|
|
|
case POGG_PCM_DOUBLE :
|
|
for (i = 0 ; i < cache_usable ; i++)
|
|
((double *) pogg->ptr) [i] = (double) ((float *) src) [i] ;
|
|
break ;
|
|
} ;
|
|
|
|
pogg->ptr += bytes ;
|
|
pogg->cache_remaining -= cache_usable ;
|
|
pogg->remaining -= cache_usable ;
|
|
|
|
return 0 ;
|
|
} /* pogg_copyout*/
|
|
|
|
static int
|
|
pogg_decode (FishSound * fsound, float ** pcm, long frames, void * user_data)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_data ;
|
|
OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
size_t bytes ;
|
|
float ** new_block ;
|
|
|
|
/* Avoid compiler warning. */
|
|
fsound = NULL ;
|
|
|
|
bytes = sizeof (float) * psf->sf.channels * frames ;
|
|
|
|
if (bytes > pogg->cache_size)
|
|
{ new_block = realloc (pogg->cache_pcm, bytes) ;
|
|
if (new_block == NULL)
|
|
/* XXX : SFE_MALLOC_FAILED */
|
|
return -1 ;
|
|
|
|
pogg->cache_pcm = new_block ;
|
|
pogg->cache_size = bytes ;
|
|
} ;
|
|
|
|
memcpy (pogg->cache_pcm, pcm, bytes) ;
|
|
pogg->cache_frames = frames ;
|
|
pogg->cache_remaining = frames ;
|
|
|
|
if (pogg->cache_granulepos != -1)
|
|
pogg->cache_granulepos += frames ;
|
|
|
|
pogg_copyout (psf) ;
|
|
|
|
return 0 ;
|
|
} /* pogg_decode */
|
|
|
|
static int
|
|
pogg_read_packet (OGGZ * oggz, ogg_packet * op, long serialno, void * data)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) data ;
|
|
OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
FishSound * fsound = pogg->fsound ;
|
|
|
|
/* Avoid warning message. */
|
|
oggz = NULL ;
|
|
serialno = 0 ;
|
|
|
|
fish_sound_decode (fsound, op->packet, op->bytes) ;
|
|
|
|
if (op->granulepos != -1)
|
|
pogg->cache_granulepos = op->granulepos ;
|
|
|
|
if (pogg->remaining == 0)
|
|
return OGGZ_STOP_OK ;
|
|
|
|
return OGGZ_CONTINUE ;
|
|
} /* pogg_read_packet */
|
|
|
|
static sf_count_t
|
|
pogg_read_loop (SF_PRIVATE *psf, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
long nread = 1024 ;
|
|
sf_count_t ret = len ;
|
|
|
|
/** Calculate nr. frames remaining */
|
|
pogg->remaining = len / psf->sf.channels ;
|
|
|
|
/** Serve out any remaining cached data first */
|
|
pogg_copyout (psf) ;
|
|
|
|
while (nread > 0 && pogg->remaining > 0)
|
|
nread = oggz_read (pogg->oggz, 1024) ;
|
|
|
|
if (nread == 0)
|
|
ret -= pogg->remaining * psf->sf.channels ;
|
|
|
|
return ret ;
|
|
} /* pogg_read_loop */
|
|
|
|
static sf_count_t
|
|
pogg_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
|
|
pogg->ptr = ptr ;
|
|
pogg->pcmtype = POGG_PCM_SHORT ;
|
|
|
|
return pogg_read_loop (psf, len) ;
|
|
} /* pogg_read_s */
|
|
|
|
static sf_count_t
|
|
pogg_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
|
|
pogg->ptr = ptr ;
|
|
pogg->pcmtype = POGG_PCM_INT ;
|
|
|
|
return pogg_read_loop (psf, len) ;
|
|
} /* pogg_read_i */
|
|
|
|
static sf_count_t
|
|
pogg_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
|
|
pogg->ptr = ptr ;
|
|
pogg->pcmtype = POGG_PCM_FLOAT ;
|
|
|
|
return pogg_read_loop (psf, len) ;
|
|
} /* pogg_read_f */
|
|
|
|
static sf_count_t
|
|
pogg_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
|
|
pogg->ptr = ptr ;
|
|
pogg->pcmtype = POGG_PCM_DOUBLE ;
|
|
|
|
return pogg_read_loop (psf, len) ;
|
|
} /* pogg_read_d */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Seek functions
|
|
*/
|
|
|
|
static sf_count_t
|
|
pogg_seek (SF_PRIVATE *psf, int mode, sf_count_t seek_from_start)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
ogg_int64_t units = seek_from_start * 1000 / psf->sf.samplerate ;
|
|
|
|
if (mode != SFM_READ)
|
|
{ psf->error = SFE_BAD_SEEK ;
|
|
return SF_SEEK_ERROR ;
|
|
} ;
|
|
|
|
oggz_seek_units (pogg->oggz, units, SEEK_SET) ;
|
|
|
|
/* Invalidate cache and set the desired seek position */
|
|
pogg->cache_remaining = 0 ;
|
|
pogg->cache_granulepos = -1 ;
|
|
pogg->seek_from_start = seek_from_start ;
|
|
|
|
return seek_from_start ;
|
|
} /* pogg_seek */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Write functions
|
|
*/
|
|
|
|
static int
|
|
pogg_encoded (FishSound * fsound, unsigned char * buf, long bytes, void * user_data)
|
|
{ SF_PRIVATE * psf = (SF_PRIVATE *) user_data ;
|
|
OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
ogg_packet op ;
|
|
int err ;
|
|
|
|
op.packet = buf ;
|
|
op.bytes = bytes ;
|
|
op.b_o_s = pogg->b_o_s ;
|
|
op.e_o_s = 0 ;
|
|
op.granulepos = fish_sound_get_frameno (fsound) ;
|
|
op.packetno = -1 ;
|
|
|
|
err = oggz_write_feed (pogg->oggz, &op, pogg->serialno, 0, NULL) ;
|
|
|
|
pogg->b_o_s = 0 ;
|
|
|
|
return OGGZ_CONTINUE ;
|
|
} /* pogg_encoded */
|
|
|
|
static int
|
|
pogg_write_anx_headers (SF_PRIVATE *psf, int format)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
unsigned char buf [128] ;
|
|
long anx_serialno ;
|
|
int headers_len = 0 ;
|
|
const char * content_type ;
|
|
ogg_packet op ;
|
|
int err ;
|
|
|
|
anx_serialno = oggz_serialno_new (pogg->oggz) ;
|
|
|
|
/* Write Annodex header */
|
|
memset (buf, 0, 48) ;
|
|
snprintf (buf, 8, "Annodex") ;
|
|
|
|
/* Version */
|
|
*(ogg_int16_t *) &buf [8] = (ogg_int16_t) POGG_ANX_VERSION_MAJOR ;
|
|
*(ogg_int16_t *) &buf [10] = (ogg_int16_t) POGG_ANX_VERSION_MINOR ;
|
|
if (CPU_IS_BIG_ENDIAN)
|
|
endswap_short_array ((short *) &buf [8], 2) ;
|
|
|
|
/* Timebase numerator */
|
|
*(ogg_int64_t *) &buf [12] = (ogg_int64_t) 0 ;
|
|
|
|
/* Timebase denominator */
|
|
*(ogg_int64_t *) &buf [20] = (ogg_int64_t) 1 ;
|
|
|
|
if (CPU_IS_BIG_ENDIAN)
|
|
endswap_long_array ((long *) &buf [12], 2) ;
|
|
|
|
op.packet = buf ;
|
|
op.bytes = 48 ;
|
|
op.b_o_s = 1 ;
|
|
op.e_o_s = 0 ;
|
|
op.granulepos = 0 ;
|
|
op.packetno = -1 ;
|
|
|
|
err = oggz_write_feed (pogg->oggz, &op, anx_serialno, 0, NULL) ;
|
|
|
|
pogg->b_o_s = 0 ;
|
|
|
|
/* Write AnxData header */
|
|
memset (buf, 0, 48) ;
|
|
snprintf (buf, 8, "AnxData") ;
|
|
|
|
/* Granule rate numerator */
|
|
*(ogg_int64_t *) &buf [8] = (ogg_int64_t) psf->sf.samplerate ;
|
|
|
|
/* Granule rate denominator */
|
|
*(ogg_int64_t *) &buf [16] = (ogg_int64_t) 1 ;
|
|
|
|
if (CPU_IS_BIG_ENDIAN)
|
|
endswap_long_array ((long *) &buf [8], 2) ;
|
|
|
|
/* Number of secondary header pages */
|
|
*(ogg_int32_t *) &buf [24] = (ogg_int32_t) 3 ;
|
|
if (CPU_IS_BIG_ENDIAN)
|
|
endswap_int_array ((int *) &buf [24], 1) ;
|
|
|
|
/* Headers */
|
|
if (format == FISH_SOUND_VORBIS)
|
|
content_type = POGG_VORBIS_CONTENT_TYPE ;
|
|
else
|
|
content_type = POGG_SPEEX_CONTENT_TYPE ;
|
|
|
|
headers_len = snprintf ((char *) &buf [28], 100, "Content-Type : %s\r\n", content_type) ;
|
|
|
|
op.packet = buf ;
|
|
op.bytes = 28 + headers_len + 1 ;
|
|
op.b_o_s = 1 ;
|
|
op.e_o_s = 0 ;
|
|
op.granulepos = 0 ;
|
|
op.packetno = -1 ;
|
|
|
|
err = oggz_write_feed (pogg->oggz, &op, pogg->serialno, 0, NULL) ;
|
|
|
|
/* Write Annodex eos packet */
|
|
op.packet = NULL ;
|
|
op.bytes = 0 ;
|
|
op.b_o_s = 0 ;
|
|
op.e_o_s = 1 ;
|
|
op.granulepos = 0 ;
|
|
op.packetno = -1 ;
|
|
|
|
err = oggz_write_feed (pogg->oggz, &op, anx_serialno, 0, NULL) ;
|
|
|
|
return 0 ;
|
|
} /* pogg_write_anx_headers */
|
|
|
|
static int
|
|
pogg_write_header (SF_PRIVATE *psf)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
|
|
OGGZ * oggz ;
|
|
FishSound * fsound ;
|
|
FishSoundInfo fsinfo ;
|
|
|
|
oggz = oggz_new (OGGZ_WRITE) ;
|
|
|
|
oggz_io_set_write (oggz, pogg_io_write, psf) ;
|
|
|
|
fsinfo.samplerate = psf->sf.samplerate ;
|
|
fsinfo.channels = psf->sf.channels ;
|
|
|
|
switch (psf->sf.format & SF_FORMAT_SUBMASK)
|
|
{ case SF_FORMAT_VORBIS :
|
|
fsinfo.format = FISH_SOUND_VORBIS ;
|
|
break ;
|
|
|
|
case SF_FORMAT_SPEEX :
|
|
fsinfo.format = FISH_SOUND_SPEEX ;
|
|
break ;
|
|
} ;
|
|
|
|
fsound = fish_sound_new (FISH_SOUND_ENCODE, &fsinfo) ;
|
|
fish_sound_set_interleave (fsound, 1) ;
|
|
fish_sound_set_encoded_callback (fsound, pogg_encoded, psf) ;
|
|
|
|
pogg->oggz = oggz ;
|
|
pogg->fsound = fsound ;
|
|
pogg->serialno = oggz_serialno_new (oggz) ;
|
|
pogg->b_o_s = 1 ;
|
|
pogg->comments_written = 0 ;
|
|
pogg->granulepos = 0 ;
|
|
pogg->fptr = (float *) malloc (sizeof (float) * 1024) ;
|
|
|
|
if ((psf->sf.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_ANX)
|
|
pogg_write_anx_headers (psf, fsinfo.format) ;
|
|
|
|
return 0 ;
|
|
} /* pogg_write_header */
|
|
|
|
static void
|
|
pogg_write_comments (SF_PRIVATE *psf)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
int k ;
|
|
int err ;
|
|
|
|
for (k = 0 ; k < SF_MAX_STRINGS ; k++)
|
|
{ printf ("hrumf comment : %d => %s\n", psf->strings [k].type, psf->strings [k].str) ;
|
|
/*
|
|
if (psf->strings [k].type == 0)
|
|
break ;
|
|
*/
|
|
|
|
if (psf->strings [k].type != 0)
|
|
{ printf ("adding comment : %d => %s\n", psf->strings [k].type, psf->strings [k].str) ;
|
|
|
|
switch (psf->strings [k].type)
|
|
{ case SF_STR_SOFTWARE :
|
|
err = fish_sound_comment_add_byname (pogg->fsound, "ENCODER", psf->strings [k].str) ;
|
|
break ;
|
|
|
|
case SF_STR_TITLE :
|
|
err = fish_sound_comment_add_byname (pogg->fsound, "TITLE", psf->strings [k].str) ;
|
|
break ;
|
|
|
|
case SF_STR_COPYRIGHT :
|
|
err = fish_sound_comment_add_byname (pogg->fsound, "COPYRIGHT", psf->strings [k].str) ;
|
|
break ;
|
|
|
|
case SF_STR_ARTIST :
|
|
err = fish_sound_comment_add_byname (pogg->fsound, "ARTIST", psf->strings [k].str) ;
|
|
break ;
|
|
|
|
/*
|
|
case SF_STR_COMMENT :
|
|
fish_sound_comment_add_byname (pogg->fsound, "COMMENT", psf->strings [k].str) ;
|
|
break ;
|
|
*/
|
|
|
|
case SF_STR_DATE :
|
|
err = fish_sound_comment_add_byname (pogg->fsound, "DATE", psf->strings [k].str) ;
|
|
break ;
|
|
} ;
|
|
|
|
} ; /* if type !0 */
|
|
} ;
|
|
|
|
pogg->comments_written = 1 ;
|
|
|
|
return ;
|
|
} /* pogg_write_comments*/
|
|
|
|
static sf_count_t
|
|
pogg_write_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
long n, i, remaining = len / psf->sf.channels ;
|
|
|
|
if (pogg->comments_written == 0)
|
|
pogg_write_comments (psf) ;
|
|
|
|
while (remaining > 0)
|
|
{ n = SF_MIN (remaining, 1024) ;
|
|
|
|
for (i = 0 ; i < n * psf->sf.channels ; i++)
|
|
pogg->fptr [i] = (float) ptr [i] ;
|
|
|
|
fish_sound_encode (pogg->fsound, (float **) ptr, n) ;
|
|
|
|
while (oggz_write (pogg->oggz, 1024) > 0) ;
|
|
|
|
ptr += n * psf->sf.channels ;
|
|
remaining -= n ;
|
|
} ;
|
|
|
|
return len ;
|
|
} /* pogg_write_s */
|
|
|
|
static sf_count_t
|
|
pogg_write_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
|
|
{
|
|
OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
long n, i, remaining = len / psf->sf.channels ;
|
|
|
|
if (pogg->comments_written == 0)
|
|
pogg_write_comments (psf) ;
|
|
|
|
while (remaining > 0)
|
|
{ n = SF_MIN (remaining, 1024) ;
|
|
|
|
for (i = 0 ; i < n * psf->sf.channels ; i++)
|
|
pogg->fptr [i] = (float) ptr [i] ;
|
|
|
|
fish_sound_encode (pogg->fsound, (float **) ptr, n) ;
|
|
|
|
while (oggz_write (pogg->oggz, 1024) > 0) ;
|
|
|
|
ptr += n * psf->sf.channels ;
|
|
remaining -= n ;
|
|
} ;
|
|
|
|
return len ;
|
|
} /* pogg_write_i */
|
|
|
|
static sf_count_t
|
|
pogg_write_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
long n, remaining = len / psf->sf.channels ;
|
|
|
|
if (pogg->comments_written == 0)
|
|
pogg_write_comments (psf) ;
|
|
|
|
while (remaining > 0)
|
|
{ n = SF_MIN (remaining, 1024) ;
|
|
|
|
fish_sound_encode (pogg->fsound, (float **) ptr, n) ;
|
|
|
|
while (oggz_write (pogg->oggz, 1024) > 0) ;
|
|
|
|
ptr += n * psf->sf.channels ;
|
|
remaining -= n ;
|
|
} ;
|
|
|
|
return len ;
|
|
} /* pog_write_f */
|
|
|
|
static sf_count_t
|
|
pogg_write_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
|
|
{ OGG_PRIVATE * pogg = (OGG_PRIVATE *) psf->fdata ;
|
|
long n, i, remaining = len / psf->sf.channels ;
|
|
|
|
if (pogg->comments_written == 0)
|
|
pogg_write_comments (psf) ;
|
|
|
|
while (remaining > 0)
|
|
{ n = SF_MIN (remaining, 1024) ;
|
|
|
|
for (i = 0 ; i < n * psf->sf.channels ; i++)
|
|
pogg->fptr [i] = (float) ptr [i] ;
|
|
|
|
fish_sound_encode (pogg->fsound, (float **) ptr, n) ;
|
|
|
|
while (oggz_write (pogg->oggz, 1024) > 0) ;
|
|
|
|
ptr += n * psf->sf.channels ;
|
|
remaining -= n ;
|
|
} ;
|
|
|
|
return len ;
|
|
} /* pogg_write_d */
|
|
|
|
#endif
|
|
|
|
/*
|
|
** Do not edit or modify anything in this comment block.
|
|
** The arch-tag line is a file identity tag for the GNU Arch
|
|
** revision control system.
|
|
**
|
|
** arch-tag: 9ff1fe9c-629e-4e9c-9ef5-3d0eb1e427a0
|
|
*/
|