📄 ogg.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)intogg_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.*/intogg_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 intpogg_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 intpogg_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_tpogg_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 intpogg_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 longpogg_io_tell (void * user_handle){ SF_PRIVATE * psf = (SF_PRIVATE *) user_handle ; return (size_t) psf_ftell (psf) ;} /* pogg_io_tell */static size_tpogg_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 intpogg_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 intpogg_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 intpogg_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 intpogg_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 intpogg_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 intpogg_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 ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -