Simple Ogg Vorbis support (no streaming: the file is fully decompressed in memory...
authormolivier <molivier@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 27 Dec 2003 21:15:16 +0000 (21:15 +0000)
committermolivier <molivier@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 27 Dec 2003 21:15:16 +0000 (21:15 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3756 d7cf8633-e32d-0410-b094-e92efae38249

darkplaces.dsp
makefile
ogg.c [new file with mode: 0644]
ogg.h [new file with mode: 0644]
snd_dma.c
snd_mem.c

index 36a83fe..9add5e7 100644 (file)
@@ -276,6 +276,10 @@ SOURCE=.\netconn.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\ogg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\palette.c\r
 # End Source File\r
 # Begin Source File\r
@@ -584,6 +588,10 @@ SOURCE=.\netconn.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\ogg.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\palette.h\r
 # End Source File\r
 # Begin Source File\r
index 928a278..26aa1f3 100644 (file)
--- a/makefile
+++ b/makefile
@@ -4,13 +4,13 @@
 CC=gcc
 
 #recommended for: anyone not using ALSA 0.5
-OBJ_LINUXSOUND=snd_oss.o snd_dma.o snd_mix.o snd_mem.o
+OBJ_LINUXSOUND=snd_oss.o snd_dma.o snd_mix.o snd_mem.o ogg.o
 LINUXSOUNDLIB=
 #recommended for: anyone using ALSA 0.5
-#OBJ_LINUXSOUND=snd_alsa_0_5.o snd_dma.o snd_mix.o snd_mem.o
+#OBJ_LINUXSOUND=snd_alsa_0_5.o snd_dma.o snd_mix.o snd_mem.o ogg.o
 #LINUXSOUNDLIB=-lasound
 #recommended for: no one (this driver needs to be updated, it doesn't compile anymore)
-#OBJ_LINUXSOUND=snd_alsa_0_9.o snd_dma.o snd_mix.o snd_mem.o
+#OBJ_LINUXSOUND=snd_alsa_0_9.o snd_dma.o snd_mix.o snd_mem.o ogg.o
 #LINUXSOUNDLIB=-lasound
 #recommended for: anyone who can't use the above drivers
 #OBJ_LINUXSOUND=snd_null.o
diff --git a/ogg.c b/ogg.c
new file mode 100644 (file)
index 0000000..c3bc1af
--- /dev/null
+++ b/ogg.c
@@ -0,0 +1,445 @@
+/*
+       Copyright (C) 2003  Mathieu Olivier
+
+       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
+
+*/
+
+
+#include "quakedef.h"
+#include "ogg.h"
+
+
+extern void ResampleSfx (sfxcache_t *sc, qbyte *data, char *name);
+
+
+/*
+=================================================================
+
+  Minimal set of definitions from the Ogg Vorbis lib
+  (C) COPYRIGHT 1994-2001 by the XIPHOPHORUS Company
+  http://www.xiph.org/
+
+  WARNING: for a matter of simplicity, several pointer types are
+  casted to "void*", and most enumerated values are not included
+
+=================================================================
+*/
+
+#ifdef _MSC_VER
+typedef __int64 ogg_int64_t;
+#else
+typedef long long ogg_int64_t;
+#endif
+
+typedef struct
+{
+       size_t  (*read_func)    (void *ptr, size_t size, size_t nmemb, void *datasource);
+       int             (*seek_func)    (void *datasource, ogg_int64_t offset, int whence);
+       int             (*close_func)   (void *datasource);
+       long    (*tell_func)    (void *datasource);
+} ov_callbacks;
+
+typedef struct
+{
+       unsigned char   *data;
+       int                             storage;
+       int                             fill;
+       int                             returned;
+       int                             unsynced;
+       int                             headerbytes;
+       int                             bodybytes;
+} ogg_sync_state;
+
+typedef struct
+{
+       int             version;
+       int             channels;
+       long    rate;
+       long    bitrate_upper;
+       long    bitrate_nominal;
+       long    bitrate_lower;
+       long    bitrate_window;
+       void    *codec_setup;
+} vorbis_info;
+
+typedef struct
+{
+       unsigned char   *body_data;
+       long                    body_storage;
+       long                    body_fill;
+       long                    body_returned;
+       int                             *lacing_vals;
+       ogg_int64_t             *granule_vals;
+       long                    lacing_storage;
+       long                    lacing_fill;
+       long                    lacing_packet;
+       long                    lacing_returned;
+       unsigned char   header[282];
+       int                             header_fill;
+       int                             e_o_s;
+       int                             b_o_s;
+       long                    serialno;
+       long                    pageno;
+       ogg_int64_t             packetno;
+       ogg_int64_t             granulepos;
+} ogg_stream_state;
+
+typedef struct
+{
+       int                     analysisp;
+       vorbis_info     *vi;
+       float           **pcm;
+       float           **pcmret;
+       int                     pcm_storage;
+       int                     pcm_current;
+       int                     pcm_returned;
+       int                     preextrapolate;
+       int                     eofflag;
+       long            lW;
+       long            W;
+       long            nW;
+       long            centerW;
+       ogg_int64_t     granulepos;
+       ogg_int64_t     sequence;
+       ogg_int64_t     glue_bits;
+       ogg_int64_t     time_bits;
+       ogg_int64_t     floor_bits;
+       ogg_int64_t     res_bits;
+       void            *backend_state;
+} vorbis_dsp_state;
+
+typedef struct
+{
+       long                    endbyte;
+       int                             endbit;
+       unsigned char   *buffer;
+       unsigned char   *ptr;
+       long                    storage;
+} oggpack_buffer;
+
+typedef struct
+{
+       float                           **pcm;
+       oggpack_buffer          opb;
+       long                            lW;
+       long                            W;
+       long                            nW;
+       int                                     pcmend;
+       int                                     mode;
+       int                                     eofflag;
+       ogg_int64_t                     granulepos;
+       ogg_int64_t                     sequence;
+       vorbis_dsp_state        *vd;
+       void                            *localstore;
+       long                            localtop;
+       long                            localalloc;
+       long                            totaluse;
+       void                            *reap;  // VOIDED POINTER
+       long                            glue_bits;
+       long                            time_bits;
+       long                            floor_bits;
+       long                            res_bits;
+       void                            *internal;
+} vorbis_block;
+
+typedef struct
+{
+       void                            *datasource;
+       int                                     seekable;
+       ogg_int64_t                     offset;
+       ogg_int64_t                     end;
+       ogg_sync_state          oy;
+       int                                     links;
+       ogg_int64_t                     *offsets;
+       ogg_int64_t                     *dataoffsets;
+       long                            *serialnos;
+       ogg_int64_t                     *pcmlengths;
+       vorbis_info                     *vi;
+       void                            *vc;  // VOIDED POINTER
+       ogg_int64_t                     pcm_offset;
+       int                                     ready_state;
+       long                            current_serialno;
+       int                                     current_link;
+       double                          bittrack;
+       double                          samptrack;
+       ogg_stream_state        os;
+       vorbis_dsp_state        vd;
+       vorbis_block            vb;
+       ov_callbacks            callbacks;
+} OggVorbis_File;
+
+
+/*
+=================================================================
+
+  DarkPlaces definitions
+
+=================================================================
+*/
+
+// Functions exported from the vorbisfile library
+static int (*qov_clear) (OggVorbis_File *vf);
+static vorbis_info* (*qov_info) (OggVorbis_File *vf,int link);
+static int (*qov_open_callbacks) (void *datasource, OggVorbis_File *vf,
+                                                                 char *initial, long ibytes,
+                                                                 ov_callbacks callbacks);
+static ogg_int64_t (*qov_pcm_total) (OggVorbis_File *vf,int i);
+static long (*qov_read) (OggVorbis_File *vf,char *buffer,int length,
+                                                int bigendianp,int word,int sgned,int *bitstream);
+
+static dllfunction_t oggvorbisfuncs[] =
+{
+       {"ov_clear",                    (void **) &qov_clear},
+       {"ov_info",                             (void **) &qov_info},
+       {"ov_open_callbacks",   (void **) &qov_open_callbacks},
+       {"ov_pcm_total",                (void **) &qov_pcm_total},
+       {"ov_read",                             (void **) &qov_read},
+       {NULL, NULL}
+};
+
+// Handle for the Vorbisfile DLL
+static dllhandle_t vf_dll = NULL;
+
+typedef struct
+{
+       qbyte *buffer;
+       ogg_int64_t ind, buffsize;
+} ov_decode_t;
+
+
+static size_t ovcb_read (void *ptr, size_t size, size_t nb, void *datasource)
+{
+       ov_decode_t *ov_decode = (ov_decode_t*)datasource;
+       size_t remain, len;
+
+       remain = ov_decode->buffsize - ov_decode->ind;
+       len = size * nb;
+       if (remain < len)
+               len = remain - remain % size;
+
+       memcpy (ptr, ov_decode->buffer + ov_decode->ind, len);
+       ov_decode->ind += len;
+
+       return len / size;
+}
+
+static int ovcb_seek (void *datasource, ogg_int64_t offset, int whence)
+{
+       ov_decode_t *ov_decode = (ov_decode_t*)datasource;
+
+       switch (whence)
+       {
+               case SEEK_SET:
+                       break;
+               case SEEK_CUR:
+                       offset += ov_decode->ind;
+                       break;
+               case SEEK_END:
+                       offset += ov_decode->buffsize;
+                       break;
+               default:
+                       return -1;
+       }
+       if (offset < 0 || offset > ov_decode->buffsize)
+               return -1;
+
+       ov_decode->ind = offset;
+       return 0;
+}
+
+static int ovcb_close (void *ov_decode)
+{
+       return 0;
+}
+
+static long ovcb_tell (void *ov_decode)
+{
+       return ((ov_decode_t*)ov_decode)->ind;
+}
+
+
+/*
+=================================================================
+
+  DLL load & unload
+
+=================================================================
+*/
+
+/*
+====================
+OGG_OpenLibrary
+
+Try to load the VorbisFile DLL
+====================
+*/
+qboolean OGG_OpenLibrary (void)
+{
+       const char* dllname;
+       const dllfunction_t *func;
+
+       // Already loaded?
+       if (vf_dll)
+               return true;
+
+#ifdef WIN32
+       dllname = "vorbisfile.dll";
+#else
+       dllname = "libvorbisfile.so";
+#endif
+
+       // Initializations
+       for (func = oggvorbisfuncs; func && func->name != NULL; func++)
+               *func->funcvariable = NULL;
+
+       // Load the DLL
+       if (! (vf_dll = Sys_LoadLibrary (dllname)))
+       {
+               Con_DPrintf("Can't find %s. Ogg Vorbis support disabled\n", dllname);
+               return false;
+       }
+
+       // Get the function adresses
+       for (func = oggvorbisfuncs; func && func->name != NULL; func++)
+               if (!(*func->funcvariable = (void *) Sys_GetProcAddress (vf_dll, func->name)))
+               {
+                       Con_Printf("missing function \"%s\" - broken Ogg Vorbis library!\n", func->name);
+                       OGG_CloseLibrary ();
+                       return false;
+               }
+
+       Con_DPrintf("%s loaded. Ogg Vorbis support enabled\n", dllname);
+       return true;
+}
+
+
+/*
+====================
+OGG_CloseLibrary
+
+Unload the VorbisFile DLL
+====================
+*/
+void OGG_CloseLibrary (void)
+{
+       if (!vf_dll)
+               return;
+
+       Sys_UnloadLibrary (vf_dll);
+       vf_dll = NULL;
+}
+
+
+/*
+=================================================================
+
+       Ogg Vorbis decoding
+
+=================================================================
+*/
+
+/*
+====================
+OGG_LoadVorbisFile
+
+Load an Ogg Vorbis file into a sfxcache_t
+====================
+*/
+sfxcache_t *OGG_LoadVorbisFile (const char *filename, sfx_t *s)
+{
+       qbyte *data;
+       ov_decode_t ov_decode;
+       OggVorbis_File vf;
+       ov_callbacks callbacks = {ovcb_read, ovcb_seek, ovcb_close, ovcb_tell};
+       vorbis_info *vi;
+       ogg_int64_t len;
+       char *buff;
+       ogg_int64_t done;
+       int bs;
+       long ret;
+       sfxcache_t *sc;
+
+       if (!vf_dll)
+               return NULL;
+
+       // Load the file
+       data = FS_LoadFile (filename, false);
+       if (data == NULL)
+               return NULL;
+
+       // Open it with the VorbisFile API
+       ov_decode.buffer = data;
+       ov_decode.ind = 0;
+       ov_decode.buffsize = fs_filesize;
+       if (qov_open_callbacks (&ov_decode, &vf, NULL, 0, callbacks) < 0)
+       {
+               Con_Printf ("error while opening Ogg Vorbis file \"%s\"\n", filename);
+               Mem_Free (data);
+               return NULL;
+       }
+
+       // Get the stream information
+       vi = qov_info (&vf, -1);
+       if (vi->channels < 1 || vi->channels > 2)
+       {
+               Con_Printf ("%s has an unsupported number of channels (%i)\n",
+                                       s->name, vi->channels);
+               qov_clear (&vf);
+               Mem_Free (data);
+               return NULL;
+       }
+
+       // Decode it
+       len = qov_pcm_total (&vf, -1) * vi->channels * 2;  // 16 bits => "* 2"
+       buff = Mem_Alloc (tempmempool, (int)len);
+       done = 0;
+       bs = 0;
+       while ((ret = qov_read (&vf, &buff[done], (int)(len - done), 0, 2, 1, &bs)) > 0)
+               done += ret;
+
+       // Calculate resampled length
+       len = (double)done * (double)shm->speed / (double)vi->rate;
+
+       // Resample it
+       Mem_FreePool (&s->mempool);
+       s->mempool = Mem_AllocPool (s->name);
+       sc = s->sfxcache = Mem_Alloc (s->mempool, (int)len + sizeof (sfxcache_t));
+       if (sc != NULL)
+       {
+               sc->length = (int)done / (vi->channels * 2);
+               sc->loopstart = -1;
+               sc->speed = vi->rate;
+               sc->width = 2;  // We always work with 16 bits samples
+               sc->stereo = (vi->channels == 2);
+
+               ResampleSfx (sc, buff, s->name);
+       }
+       else
+       {
+               Con_Printf ("failed to allocate memory for sound \"%s\"\n", s->name);
+               Mem_FreePool (&s->mempool);
+       }
+
+       qov_clear (&vf);
+       Mem_Free (buff);
+       Mem_Free (data);
+
+       return sc;
+}
diff --git a/ogg.h b/ogg.h
new file mode 100644 (file)
index 0000000..8a2c89e
--- /dev/null
+++ b/ogg.h
@@ -0,0 +1,33 @@
+/*
+       Copyright (C) 2003  Mathieu Olivier
+
+       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
+
+*/
+
+#ifndef OGG_H
+#define OGG_H
+
+
+qboolean OGG_OpenLibrary (void);
+void OGG_CloseLibrary (void);
+sfxcache_t *OGG_LoadVorbisFile (const char *filename, sfx_t *s);
+
+
+#endif
index de8b482..29bb664 100644 (file)
--- a/snd_dma.c
+++ b/snd_dma.c
@@ -25,6 +25,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "winquake.h"
 #endif
 
+#include "ogg.h"
+
+
 void S_Play(void);
 void S_PlayVol(void);
 void S_Play2(void);
@@ -257,6 +260,8 @@ void S_Init(void)
 
        total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
        memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
+
+       OGG_OpenLibrary ();
 }
 
 
index 7908ea8..81b0db1 100644 (file)
--- a/snd_mem.c
+++ b/snd_mem.c
@@ -21,6 +21,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
+#include "ogg.h"
+
+
 /*
 ================
 ResampleSfx
@@ -270,6 +273,7 @@ sfxcache_t *S_LoadSound (sfx_t *s, int complain)
        char namebuffer[MAX_QPATH];
        size_t len;
        sfxcache_t *sc;
+       qboolean modified_name = false;
 
        // see if still in memory
        if (!shm || !shm->speed)
@@ -277,25 +281,35 @@ sfxcache_t *S_LoadSound (sfx_t *s, int complain)
        if (s->sfxcache && (s->sfxcache->speed == shm->speed))
                return s->sfxcache;
 
+       s->silentlymissing = !complain;
+
        len = snprintf (namebuffer, sizeof (namebuffer), "sound/%s", s->name);
        if (len >= sizeof (namebuffer))
                return NULL;
 
        // Try to load it as a WAV file
        sc = S_LoadWavFile (namebuffer, s);
+       if (sc != NULL)
+               return sc;
 
-       // TODO: insert Ogg Vorbis support here
+       // Else, try to load it as an Ogg Vorbis file
+       if (!strcasecmp (namebuffer + len - 4, ".wav"))
+       {
+               strcpy (namebuffer + len - 3, "ogg");
+               modified_name = true;
+       }
+       sc = OGG_LoadVorbisFile (namebuffer, s);
+       if (sc != NULL)
+               return sc;
 
        // Can't load the sound!
-       if (sc == NULL)
+       if (complain)
        {
-               s->silentlymissing = !complain;
-               if (complain)
-                       Con_Printf ("Couldn't load %s\n", namebuffer);
-               return NULL;
+               if (modified_name)
+                       strcpy (namebuffer + len - 3, "wav");
+               Con_Printf ("Couldn't load %s\n", namebuffer);
        }
-
-       return sc;
+       return NULL;
 }
 
 void S_UnloadSound(sfx_t *s)