2002-06-14 20:36:28 +00:00
|
|
|
/*
|
|
|
|
vorbis.c
|
|
|
|
|
2003-04-14 06:11:53 +00:00
|
|
|
Ogg Vorbis support
|
2002-06-14 20:36:28 +00:00
|
|
|
|
|
|
|
Copyright (C) 2001 Bill Currie <bill@taniwha.org>
|
|
|
|
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
|
|
Date: 2002/6/14
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
2003-01-15 15:31:36 +00:00
|
|
|
|
2005-08-04 15:27:09 +00:00
|
|
|
static __attribute__ ((used)) const char rcsid[] =
|
2003-01-15 15:31:36 +00:00
|
|
|
"$Id$";
|
|
|
|
|
2002-06-15 05:43:56 +00:00
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
# include "string.h"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
# include "strings.h"
|
|
|
|
#endif
|
2002-06-14 20:36:28 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <vorbis/vorbisfile.h>
|
|
|
|
|
|
|
|
#include "QF/cvar.h"
|
2003-04-11 01:17:48 +00:00
|
|
|
#include "QF/quakefs.h"
|
2002-06-14 20:36:28 +00:00
|
|
|
#include "QF/sound.h"
|
|
|
|
#include "QF/sys.h"
|
|
|
|
|
2003-01-31 20:51:04 +00:00
|
|
|
#include "snd_render.h"
|
2002-06-14 20:36:28 +00:00
|
|
|
|
2010-08-11 23:47:03 +00:00
|
|
|
#define FRAMES 1024
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
float *data;
|
|
|
|
OggVorbis_File *vf;
|
|
|
|
} vorbis_file_t;
|
|
|
|
|
2002-06-14 20:36:28 +00:00
|
|
|
static size_t
|
2010-08-11 23:41:04 +00:00
|
|
|
vorbis_read_func (void *ptr, size_t size, size_t nmemb, void *datasource)
|
2002-06-14 20:36:28 +00:00
|
|
|
{
|
|
|
|
return Qread (datasource, ptr, size * nmemb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-08-11 23:41:04 +00:00
|
|
|
vorbis_seek_func (void *datasource, ogg_int64_t offset, int whence)
|
2002-06-14 20:36:28 +00:00
|
|
|
{
|
|
|
|
return Qseek (datasource, offset, whence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-08-11 23:41:04 +00:00
|
|
|
vorbis_close_func (void *datasource)
|
2002-06-14 20:36:28 +00:00
|
|
|
{
|
|
|
|
Qclose (datasource);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
2010-08-11 23:41:04 +00:00
|
|
|
vorbis_tell_func (void *datasource)
|
2002-06-14 20:36:28 +00:00
|
|
|
{
|
|
|
|
return Qtell (datasource);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ov_callbacks callbacks = {
|
2010-08-11 23:41:04 +00:00
|
|
|
vorbis_read_func,
|
|
|
|
vorbis_seek_func,
|
|
|
|
vorbis_close_func,
|
|
|
|
vorbis_tell_func,
|
2002-06-14 20:36:28 +00:00
|
|
|
};
|
|
|
|
|
2003-04-11 21:14:38 +00:00
|
|
|
static wavinfo_t
|
2010-08-11 23:41:04 +00:00
|
|
|
vorbis_get_info (OggVorbis_File *vf)
|
2002-06-14 20:36:28 +00:00
|
|
|
{
|
|
|
|
vorbis_info *vi;
|
2002-07-29 22:25:32 +00:00
|
|
|
int sample_start = -1, sample_count = 0;
|
2003-04-11 21:14:38 +00:00
|
|
|
int samples;
|
2002-07-29 22:25:32 +00:00
|
|
|
char **ptr;
|
2003-04-13 06:46:24 +00:00
|
|
|
wavinfo_t info;
|
2002-06-14 20:36:28 +00:00
|
|
|
|
2003-04-11 01:17:48 +00:00
|
|
|
vi = ov_info (vf, -1);
|
2003-04-11 21:14:38 +00:00
|
|
|
samples = ov_pcm_total (vf, -1);
|
2002-07-29 22:25:32 +00:00
|
|
|
|
2003-04-11 01:17:48 +00:00
|
|
|
for (ptr = ov_comment (vf, -1)->user_comments; *ptr; ptr++) {
|
2002-07-29 22:25:32 +00:00
|
|
|
Sys_DPrintf ("%s\n", *ptr);
|
|
|
|
if (strncmp ("CUEPOINT=", *ptr, 9) == 0) {
|
|
|
|
sscanf (*ptr + 9, "%d %d", &sample_start, &sample_count);
|
|
|
|
}
|
|
|
|
}
|
2003-04-11 01:17:48 +00:00
|
|
|
|
2003-04-11 21:14:38 +00:00
|
|
|
if (sample_start != -1)
|
|
|
|
samples = sample_start + sample_count;
|
|
|
|
|
|
|
|
info.rate = vi->rate;
|
2010-08-11 23:44:34 +00:00
|
|
|
info.width = sizeof (float);
|
2003-04-11 21:14:38 +00:00
|
|
|
info.channels = vi->channels;
|
|
|
|
info.loopstart = sample_start;
|
2010-08-11 23:44:34 +00:00
|
|
|
info.frames = samples;
|
2003-04-11 21:14:38 +00:00
|
|
|
info.dataofs = 0;
|
2010-08-11 23:44:34 +00:00
|
|
|
info.datalen = samples * info.channels * info.width;
|
2003-04-11 21:14:38 +00:00
|
|
|
|
2002-06-14 20:36:28 +00:00
|
|
|
if (developer->int_val) {
|
2003-04-11 01:17:48 +00:00
|
|
|
Sys_Printf ("\nBitstream is %d channel, %dHz\n",
|
2003-04-11 21:14:38 +00:00
|
|
|
info.channels, info.rate);
|
2003-04-11 01:17:48 +00:00
|
|
|
Sys_Printf ("\nDecoded length: %d samples (%d bytes)\n",
|
2010-08-11 23:44:34 +00:00
|
|
|
info.frames, info.width);
|
2003-04-11 01:17:48 +00:00
|
|
|
Sys_Printf ("Encoded by: %s\n\n", ov_comment (vf, -1)->vendor);
|
2002-06-14 20:36:28 +00:00
|
|
|
}
|
2003-04-11 01:17:48 +00:00
|
|
|
|
2003-04-11 21:14:38 +00:00
|
|
|
return info;
|
2003-04-11 01:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-08-11 23:44:34 +00:00
|
|
|
vorbis_read (OggVorbis_File *vf, float *buf, int len, wavinfo_t *info)
|
2003-04-11 01:17:48 +00:00
|
|
|
{
|
2010-08-11 23:44:34 +00:00
|
|
|
unsigned i;
|
|
|
|
int j;
|
2003-04-11 01:17:48 +00:00
|
|
|
int count = 0;
|
|
|
|
int current_section;
|
2010-08-11 23:44:34 +00:00
|
|
|
float **vbuf;
|
2003-04-11 01:17:48 +00:00
|
|
|
|
|
|
|
while (len) {
|
2010-08-11 23:44:34 +00:00
|
|
|
int res = ov_read_float (vf, &vbuf, len, ¤t_section);
|
|
|
|
|
2002-06-15 05:43:56 +00:00
|
|
|
if (res > 0) {
|
2010-08-11 23:44:34 +00:00
|
|
|
for (i = 0; i < info->channels; i++)
|
|
|
|
for (j = 0; j < res; j++) {
|
|
|
|
buf[j * info->channels + i] = vbuf[i][j];
|
|
|
|
}
|
2003-04-11 01:17:48 +00:00
|
|
|
count += res;
|
|
|
|
len -= res;
|
2010-08-11 23:44:34 +00:00
|
|
|
buf += res * info->channels;
|
2002-06-15 05:43:56 +00:00
|
|
|
} else if (res < 0) {
|
|
|
|
Sys_Printf ("vorbis error %d\n", res);
|
2003-04-11 01:17:48 +00:00
|
|
|
return -1;
|
2002-06-15 05:43:56 +00:00
|
|
|
} else {
|
|
|
|
Sys_Printf ("unexpected eof\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-04-11 01:17:48 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sfxbuffer_t *
|
2003-04-14 06:11:53 +00:00
|
|
|
vorbis_load (OggVorbis_File *vf, sfxblock_t *block, cache_allocator_t allocator)
|
2003-04-11 01:17:48 +00:00
|
|
|
{
|
2010-08-11 23:44:34 +00:00
|
|
|
float *data;
|
2003-04-11 01:17:48 +00:00
|
|
|
sfxbuffer_t *sc = 0;
|
|
|
|
sfx_t *sfx = block->sfx;
|
2003-04-13 06:46:24 +00:00
|
|
|
wavinfo_t *info = &block->wavinfo;
|
2003-04-11 01:17:48 +00:00
|
|
|
|
2003-04-13 06:46:24 +00:00
|
|
|
data = malloc (info->datalen);
|
2003-04-11 01:17:48 +00:00
|
|
|
if (!data)
|
|
|
|
goto bail;
|
2010-08-11 23:44:34 +00:00
|
|
|
sc = SND_GetCache (info->frames, info->rate, info->channels,
|
2003-04-11 21:14:38 +00:00
|
|
|
block, allocator);
|
2003-04-11 01:17:48 +00:00
|
|
|
if (!sc)
|
|
|
|
goto bail;
|
2003-04-11 21:14:38 +00:00
|
|
|
sc->sfx = sfx;
|
2010-08-11 23:44:34 +00:00
|
|
|
if (vorbis_read (vf, data, info->frames, info) < 0)
|
2003-04-11 01:17:48 +00:00
|
|
|
goto bail;
|
2007-03-26 11:44:52 +00:00
|
|
|
SND_SetPaint (sc);
|
2010-08-11 23:45:09 +00:00
|
|
|
SND_SetupResampler (sc, 0);
|
2010-08-11 23:44:34 +00:00
|
|
|
SND_Resample (sc, data, info->frames);
|
2003-04-14 01:40:40 +00:00
|
|
|
sc->head = sc->length;
|
2002-06-14 20:36:28 +00:00
|
|
|
bail:
|
|
|
|
if (data)
|
|
|
|
free (data);
|
2003-04-11 01:17:48 +00:00
|
|
|
ov_clear (vf);
|
2002-06-14 20:36:28 +00:00
|
|
|
return sc;
|
|
|
|
}
|
|
|
|
|
2003-04-11 01:17:48 +00:00
|
|
|
static void
|
2003-04-14 06:11:53 +00:00
|
|
|
vorbis_callback_load (void *object, cache_allocator_t allocator)
|
2003-04-11 01:17:48 +00:00
|
|
|
{
|
|
|
|
QFile *file;
|
|
|
|
OggVorbis_File vf;
|
|
|
|
|
|
|
|
sfxblock_t *block = (sfxblock_t *) object;
|
|
|
|
|
|
|
|
QFS_FOpenFile (block->file, &file);
|
|
|
|
if (!file)
|
|
|
|
return; //FIXME Sys_Error?
|
|
|
|
|
|
|
|
if (ov_open_callbacks (file, &vf, 0, 0, callbacks) < 0) {
|
|
|
|
Sys_Printf ("Input does not appear to be an Ogg bitstream.\n");
|
|
|
|
Qclose (file);
|
|
|
|
return; //FIXME Sys_Error?
|
|
|
|
}
|
2003-04-14 06:11:53 +00:00
|
|
|
vorbis_load (&vf, block, allocator);
|
2003-04-11 01:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2003-04-14 06:11:53 +00:00
|
|
|
vorbis_cache (sfx_t *sfx, char *realname, OggVorbis_File *vf, wavinfo_t info)
|
2003-04-13 06:46:24 +00:00
|
|
|
{
|
|
|
|
ov_clear (vf);
|
2007-03-18 01:44:46 +00:00
|
|
|
SND_SFX_Cache (sfx, realname, info, vorbis_callback_load);
|
2003-04-13 06:46:24 +00:00
|
|
|
}
|
|
|
|
|
2010-08-11 23:47:03 +00:00
|
|
|
static long
|
|
|
|
vorbis_stream_read (void *file, float **buf)
|
2003-04-13 06:46:24 +00:00
|
|
|
{
|
2010-08-11 23:47:03 +00:00
|
|
|
sfxstream_t *stream = (sfxstream_t *) file;
|
|
|
|
vorbis_file_t *vf = (vorbis_file_t *) stream->file;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!vf->data)
|
|
|
|
vf->data = malloc (FRAMES * stream->wavinfo.channels * sizeof (float));
|
|
|
|
res = vorbis_read (vf->vf, vf->data, FRAMES, &stream->wavinfo);
|
2010-08-13 01:48:20 +00:00
|
|
|
if (res <= 0) {
|
|
|
|
stream->error = 1;
|
2010-08-13 01:48:48 +00:00
|
|
|
return 0;
|
2010-08-13 01:48:20 +00:00
|
|
|
}
|
2010-08-11 23:47:03 +00:00
|
|
|
*buf = vf->data;
|
|
|
|
return res;
|
2003-04-13 06:46:24 +00:00
|
|
|
}
|
|
|
|
|
2003-04-14 06:11:53 +00:00
|
|
|
static int
|
2010-08-11 23:47:03 +00:00
|
|
|
vorbis_stream_seek (sfxstream_t *stream, int pos)
|
2003-04-13 06:46:24 +00:00
|
|
|
{
|
2010-08-11 23:47:03 +00:00
|
|
|
vorbis_file_t *vf = (vorbis_file_t *) stream->file;
|
|
|
|
return ov_pcm_seek (vf->vf, pos);
|
2003-04-13 06:46:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2003-04-20 07:19:51 +00:00
|
|
|
vorbis_stream_close (sfx_t *sfx)
|
2003-04-11 01:17:48 +00:00
|
|
|
{
|
2007-05-07 05:20:24 +00:00
|
|
|
sfxstream_t *stream = sfx->data.stream;
|
2010-08-11 23:47:03 +00:00
|
|
|
vorbis_file_t *vf = (vorbis_file_t *) stream->file;
|
2003-04-20 07:19:51 +00:00
|
|
|
|
2010-08-11 23:47:03 +00:00
|
|
|
if (vf->data)
|
|
|
|
free (vf->data);
|
|
|
|
ov_clear (vf->vf);
|
|
|
|
free (vf);
|
|
|
|
SND_SFX_StreamClose (sfx);
|
2003-04-20 07:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static sfx_t *
|
2007-03-18 01:44:46 +00:00
|
|
|
vorbis_stream_open (sfx_t *sfx)
|
2003-04-20 07:19:51 +00:00
|
|
|
{
|
2007-05-07 05:20:24 +00:00
|
|
|
sfxstream_t *stream = sfx->data.stream;
|
2003-04-20 07:19:51 +00:00
|
|
|
QFile *file;
|
2010-08-11 23:47:03 +00:00
|
|
|
vorbis_file_t *f;
|
2003-04-20 07:19:51 +00:00
|
|
|
|
|
|
|
QFS_FOpenFile (stream->file, &file);
|
|
|
|
if (!file)
|
|
|
|
return 0;
|
2003-04-21 01:44:55 +00:00
|
|
|
|
2010-08-11 23:47:03 +00:00
|
|
|
f = calloc (sizeof (vorbis_file_t), 1);
|
|
|
|
f->vf = malloc (sizeof (OggVorbis_File));
|
|
|
|
if (ov_open_callbacks (file, f->vf, 0, 0, callbacks) < 0) {
|
2003-04-20 07:19:51 +00:00
|
|
|
Sys_Printf ("Input does not appear to be an Ogg bitstream.\n");
|
|
|
|
Qclose (file);
|
2007-03-18 01:44:46 +00:00
|
|
|
free (f);
|
2003-04-20 07:19:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-18 01:44:46 +00:00
|
|
|
return SND_SFX_StreamOpen (sfx, f, vorbis_stream_read, vorbis_stream_seek,
|
|
|
|
vorbis_stream_close);
|
2003-04-20 07:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vorbis_stream (sfx_t *sfx, char *realname, OggVorbis_File *vf, wavinfo_t info)
|
|
|
|
{
|
|
|
|
ov_clear (vf);
|
2007-03-18 01:44:46 +00:00
|
|
|
SND_SFX_Stream (sfx, realname, info, vorbis_stream_open);
|
2003-04-11 01:17:48 +00:00
|
|
|
}
|
|
|
|
|
2010-08-08 03:02:55 +00:00
|
|
|
int
|
2003-04-11 01:17:48 +00:00
|
|
|
SND_LoadOgg (QFile *file, sfx_t *sfx, char *realname)
|
|
|
|
{
|
|
|
|
OggVorbis_File vf;
|
2003-04-13 06:46:24 +00:00
|
|
|
wavinfo_t info;
|
2003-04-11 01:17:48 +00:00
|
|
|
|
|
|
|
if (ov_open_callbacks (file, &vf, 0, 0, callbacks) < 0) {
|
|
|
|
Sys_Printf ("Input does not appear to be an Ogg bitstream.\n");
|
|
|
|
free (realname);
|
2010-08-08 03:02:55 +00:00
|
|
|
return -1;
|
2003-04-11 01:17:48 +00:00
|
|
|
}
|
2010-08-11 23:41:04 +00:00
|
|
|
info = vorbis_get_info (&vf);
|
2010-08-12 02:28:27 +00:00
|
|
|
if (info.channels < 1 || info.channels > 8) {
|
2003-04-13 06:46:24 +00:00
|
|
|
Sys_Printf ("unsupported number of channels");
|
2010-08-08 03:02:55 +00:00
|
|
|
return -1;
|
2003-04-13 06:46:24 +00:00
|
|
|
}
|
2010-08-11 23:44:34 +00:00
|
|
|
if (info.frames / info.rate < 3) {
|
2003-04-15 02:34:17 +00:00
|
|
|
Sys_DPrintf ("cache %s\n", realname);
|
2003-04-14 06:11:53 +00:00
|
|
|
vorbis_cache (sfx, realname, &vf, info);
|
2003-04-11 01:17:48 +00:00
|
|
|
} else {
|
2003-04-15 02:34:17 +00:00
|
|
|
Sys_DPrintf ("stream %s\n", realname);
|
2003-04-14 06:11:53 +00:00
|
|
|
vorbis_stream (sfx, realname, &vf, info);
|
2003-04-11 01:17:48 +00:00
|
|
|
}
|
2010-08-08 03:02:55 +00:00
|
|
|
return 0;
|
2003-04-11 01:17:48 +00:00
|
|
|
}
|