Partly integrated Q3 sound codec and background

music code
This commit is contained in:
Eric Wasylishen 2011-01-16 18:35:22 -07:00
parent c657e08b20
commit 474dde2a87
8 changed files with 1446 additions and 21 deletions

View file

@ -103,6 +103,12 @@
48FE585B0D3A82C8006BB491 /* QuakeArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FE585A0D3A82C8006BB491 /* QuakeArguments.m */; };
66A5467212E3AF2300FFA7D5 /* cd_shared.c in Sources */ = {isa = PBXBuildFile; fileRef = 66A5467012E3AF2300FFA7D5 /* cd_shared.c */; };
66A5467312E3AF2300FFA7D5 /* cd_ogg.c in Sources */ = {isa = PBXBuildFile; fileRef = 66A5467112E3AF2300FFA7D5 /* cd_ogg.c */; };
66A5470D12E3CF8100FFA7D5 /* snd_codec.c in Sources */ = {isa = PBXBuildFile; fileRef = 66A5470A12E3CF8100FFA7D5 /* snd_codec.c */; };
66A5470E12E3CF8100FFA7D5 /* snd_codec_wav.c in Sources */ = {isa = PBXBuildFile; fileRef = 66A5470B12E3CF8100FFA7D5 /* snd_codec_wav.c */; };
66A5470F12E3CF8100FFA7D5 /* snd_codec_ogg.c in Sources */ = {isa = PBXBuildFile; fileRef = 66A5470C12E3CF8100FFA7D5 /* snd_codec_ogg.c */; };
66A5473512E3D14B00FFA7D5 /* libogg.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 66A5473412E3D14B00FFA7D5 /* libogg.dylib */; };
66A5473712E3D15F00FFA7D5 /* libvorbis.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 66A5473612E3D15F00FFA7D5 /* libvorbis.dylib */; };
66A5473912E3D17100FFA7D5 /* libvorbisfile.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 66A5473812E3D17100FFA7D5 /* libvorbisfile.dylib */; };
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
/* End PBXBuildFile section */
@ -277,6 +283,13 @@
48FE585A0D3A82C8006BB491 /* QuakeArguments.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QuakeArguments.m; sourceTree = "<group>"; };
66A5467012E3AF2300FFA7D5 /* cd_shared.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cd_shared.c; path = ../Quake/cd_shared.c; sourceTree = SOURCE_ROOT; };
66A5467112E3AF2300FFA7D5 /* cd_ogg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cd_ogg.c; path = ../Quake/cd_ogg.c; sourceTree = SOURCE_ROOT; };
66A5470912E3CF6B00FFA7D5 /* snd_codec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = snd_codec.h; path = ../Quake/snd_codec.h; sourceTree = SOURCE_ROOT; };
66A5470A12E3CF8100FFA7D5 /* snd_codec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = snd_codec.c; path = ../Quake/snd_codec.c; sourceTree = SOURCE_ROOT; };
66A5470B12E3CF8100FFA7D5 /* snd_codec_wav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = snd_codec_wav.c; path = ../Quake/snd_codec_wav.c; sourceTree = SOURCE_ROOT; };
66A5470C12E3CF8100FFA7D5 /* snd_codec_ogg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = snd_codec_ogg.c; path = ../Quake/snd_codec_ogg.c; sourceTree = SOURCE_ROOT; };
66A5473412E3D14B00FFA7D5 /* libogg.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libogg.dylib; path = /usr/local/lib/libogg.dylib; sourceTree = "<absolute>"; };
66A5473612E3D15F00FFA7D5 /* libvorbis.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libvorbis.dylib; path = /usr/local/lib/libvorbis.dylib; sourceTree = "<absolute>"; };
66A5473812E3D17100FFA7D5 /* libvorbisfile.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libvorbisfile.dylib; path = /usr/local/lib/libvorbisfile.dylib; sourceTree = "<absolute>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8D1107320486CEB800E47090 /* QuakeSpasm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuakeSpasm.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@ -291,6 +304,9 @@
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
002F3C0109D093BD00EBEB88 /* OpenGL.framework in Frameworks */,
48348943120595B1004184BC /* Sparkle.framework in Frameworks */,
66A5473512E3D14B00FFA7D5 /* libogg.dylib in Frameworks */,
66A5473712E3D15F00FFA7D5 /* libvorbis.dylib in Frameworks */,
66A5473912E3D17100FFA7D5 /* libvorbisfile.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -337,6 +353,9 @@
19C28FACFE9D520D11CA2CBB /* Products */,
4830B79D0D464CAE00EF4498 /* Changelog.txt */,
4830B79E0D464CAE00EF4498 /* Todo.txt */,
66A5473412E3D14B00FFA7D5 /* libogg.dylib */,
66A5473612E3D15F00FFA7D5 /* libvorbis.dylib */,
66A5473812E3D17100FFA7D5 /* libvorbisfile.dylib */,
);
name = QuakeSpasm;
sourceTree = "<group>";
@ -477,6 +496,9 @@
486577C90D31A22A00E7920A /* snd_mem.c */,
486577CA0D31A22A00E7920A /* snd_mix.c */,
483A78540D2EEAC300CB2E4C /* snd_sdl.c */,
66A5470A12E3CF8100FFA7D5 /* snd_codec.c */,
66A5470B12E3CF8100FFA7D5 /* snd_codec_wav.c */,
66A5470C12E3CF8100FFA7D5 /* snd_codec_ogg.c */,
);
name = Sound;
sourceTree = "<group>";
@ -556,6 +578,7 @@
483A77E10D2EE91000CB2E4C /* Headers */ = {
isa = PBXGroup;
children = (
66A5470912E3CF6B00FFA7D5 /* snd_codec.h */,
483A77FD0D2EE9BD00CB2E4C /* cdaudio.h */,
483A77FE0D2EE9BD00CB2E4C /* sound.h */,
);
@ -748,6 +771,9 @@
48134A1912102F400015BF15 /* net_udp.c in Sources */,
66A5467212E3AF2300FFA7D5 /* cd_shared.c in Sources */,
66A5467312E3AF2300FFA7D5 /* cd_ogg.c in Sources */,
66A5470D12E3CF8100FFA7D5 /* snd_codec.c in Sources */,
66A5470E12E3CF8100FFA7D5 /* snd_codec_wav.c in Sources */,
66A5470F12E3CF8100FFA7D5 /* snd_codec_ogg.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -818,6 +844,7 @@
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
HEADER_SEARCH_PATHS = (
/usr/local/include,
/Library/Frameworks/SDL.framework/Headers,
/Library/Frameworks/SDL_net.framework/Headers,
"$(HEADER_SEARCH_PATHS)",

237
Quake/snd_codec.c Normal file
View file

@ -0,0 +1,237 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "quakedef.h"
#include "snd_codec.h"
static snd_codec_t *codecs;
/*
=================
S_FileExtension
=================
*/
static char *S_FileExtension(const char *fni)
{
// we should search from the ending to the last '/'
char *fn = (char *) fni + strlen(fni) - 1;
char *eptr = NULL;
while(*fn != '/' && fn != fni)
{
if(*fn == '.')
{
eptr = fn;
break;
}
fn--;
}
return eptr;
}
/*
=================
S_FindCodecForFile
Select an appropriate codec for a file based on its extension
=================
*/
static snd_codec_t *S_FindCodecForFile(const char *filename)
{
char *ext = S_FileExtension(filename);
snd_codec_t *codec = codecs;
if(!ext)
{
// No extension - auto-detect
while(codec)
{
char fn[MAX_QPATH];
// there is no extension so we do not need to subtract 4 chars
Q_strncpyz(fn, filename, MAX_QPATH);
COM_DefaultExtension(fn, MAX_QPATH, codec->ext);
// Check it exists
if(FS_ReadFile(fn, NULL) != -1)
return codec;
// Nope. Next!
codec = codec->next;
}
// Nothin'
return NULL;
}
while(codec)
{
if(!Q_stricmp(ext, codec->ext))
return codec;
codec = codec->next;
}
return NULL;
}
/*
=================
S_CodecInit
=================
*/
void S_CodecInit()
{
codecs = NULL;
S_CodecRegister(&wav_codec);
#ifdef USE_CODEC_VORBIS
S_CodecRegister(&ogg_codec);
#endif
}
/*
=================
S_CodecShutdown
=================
*/
void S_CodecShutdown()
{
codecs = NULL;
}
/*
=================
S_CodecRegister
=================
*/
void S_CodecRegister(snd_codec_t *codec)
{
codec->next = codecs;
codecs = codec;
}
/*
=================
S_CodecLoad
=================
*/
void *S_CodecLoad(const char *filename, snd_info_t *info)
{
snd_codec_t *codec;
char fn[MAX_QPATH];
codec = S_FindCodecForFile(filename);
if(!codec)
{
Com_Printf("Unknown extension for %s\n", filename);
return NULL;
}
strncpy(fn, filename, sizeof(fn));
COM_DefaultExtension(fn, sizeof(fn), codec->ext);
return codec->load(fn, info);
}
/*
=================
S_CodecOpenStream
=================
*/
snd_stream_t *S_CodecOpenStream(const char *filename)
{
snd_codec_t *codec;
char fn[MAX_QPATH];
codec = S_FindCodecForFile(filename);
if(!codec)
{
Com_Printf("Unknown extension for %s\n", filename);
return NULL;
}
strncpy(fn, filename, sizeof(fn));
COM_DefaultExtension(fn, sizeof(fn), codec->ext);
return codec->open(fn);
}
void S_CodecCloseStream(snd_stream_t *stream)
{
stream->codec->close(stream);
}
int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
return stream->codec->read(stream, bytes, buffer);
}
//=======================================================================
// Util functions (used by codecs)
/*
=================
S_CodecUtilOpen
=================
*/
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
{
snd_stream_t *stream;
fileHandle_t hnd;
int length;
// Try to open the file
length = FS_FOpenFileRead(filename, &hnd, qtrue);
if(!hnd)
{
Com_Printf("Can't read sound file %s\n", filename);
return NULL;
}
// Allocate a stream
stream = Z_Malloc(sizeof(snd_stream_t));
if(!stream)
{
FS_FCloseFile(hnd);
return NULL;
}
// Copy over, return
stream->codec = codec;
stream->file = hnd;
stream->length = length;
return stream;
}
/*
=================
S_CodecUtilClose
=================
*/
void S_CodecUtilClose(snd_stream_t **stream)
{
FS_FCloseFile((*stream)->file);
Z_Free(*stream);
*stream = NULL;
}

95
Quake/snd_codec.h Normal file
View file

@ -0,0 +1,95 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifndef _SND_CODEC_H_
#define _SND_CODEC_H_
typedef struct snd_info_s
{
int rate;
int width;
int channels;
int samples;
int size;
int dataofs;
} snd_info_t;
typedef struct snd_codec_s snd_codec_t;
typedef struct snd_stream_s
{
snd_codec_t *codec;
FILE *file;
snd_info_t info;
int length;
int pos;
void *ptr;
} snd_stream_t;
// Codec functions
typedef void *(*CODEC_LOAD)(const char *filename, snd_info_t *info);
typedef snd_stream_t *(*CODEC_OPEN)(const char *filename);
typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer);
typedef void (*CODEC_CLOSE)(snd_stream_t *stream);
// Codec data structure
struct snd_codec_s
{
char *ext;
CODEC_LOAD load;
CODEC_OPEN open;
CODEC_READ read;
CODEC_CLOSE close;
snd_codec_t *next;
};
// Codec management
void S_CodecInit( void );
void S_CodecShutdown( void );
void S_CodecRegister(snd_codec_t *codec);
void *S_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_CodecOpenStream(const char *filename);
void S_CodecCloseStream(snd_stream_t *stream);
int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
// Util functions (used by codecs)
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec);
void S_CodecUtilClose(snd_stream_t **stream);
// WAV Codec
extern snd_codec_t wav_codec;
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_WAV_CodecOpenStream(const char *filename);
void S_WAV_CodecCloseStream(snd_stream_t *stream);
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
// Ogg Vorbis codec
#ifdef USE_CODEC_VORBIS
extern snd_codec_t ogg_codec;
void *S_OGG_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_OGG_CodecOpenStream(const char *filename);
void S_OGG_CodecCloseStream(snd_stream_t *stream);
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
#endif // USE_CODEC_VORBIS
#endif // !_SND_CODEC_H_

480
Quake/snd_codec_ogg.c Normal file
View file

@ -0,0 +1,480 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// FIXME:
#define USE_CODEC_VORBIS
// OGG support is enabled by this define
#ifdef USE_CODEC_VORBIS
// includes for the Q3 sound system
#include "quakedef.h"
#include "snd_codec.h"
// includes for the OGG codec
#include <errno.h>
#include <vorbis/vorbisfile.h>
// The OGG codec can return the samples in a number of different formats,
// we use the standard signed short format.
#define OGG_SAMPLEWIDTH 2
// Q3 OGG codec
snd_codec_t ogg_codec =
{
".ogg",
S_OGG_CodecLoad,
S_OGG_CodecOpenStream,
S_OGG_CodecReadStream,
S_OGG_CodecCloseStream,
NULL
};
// callbacks for vobisfile
// fread() replacement
size_t S_OGG_Callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
{
snd_stream_t *stream;
int byteSize = 0;
int bytesRead = 0;
size_t nMembRead = 0;
// check if input is valid
if(!ptr)
{
errno = EFAULT;
return 0;
}
if(!(size && nmemb))
{
// It's not an error, caller just wants zero bytes!
errno = 0;
return 0;
}
if(!datasource)
{
errno = EBADF;
return 0;
}
// we use a snd_stream_t in the generic pointer to pass around
stream = (snd_stream_t *) datasource;
// FS_Read does not support multi-byte elements
byteSize = nmemb * size;
// read it with the Q3 function FS_Read()
bytesRead = FS_Read(ptr, byteSize, stream->file);
// update the file position
stream->pos += bytesRead;
// this function returns the number of elements read not the number of bytes
nMembRead = bytesRead / size;
// even if the last member is only read partially
// it is counted as a whole in the return value
if(bytesRead % size)
{
nMembRead++;
}
return nMembRead;
}
// fseek() replacement
int S_OGG_Callback_seek(void *datasource, ogg_int64_t offset, int whence)
{
snd_stream_t *stream;
int retVal = 0;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
// we must map the whence to its Q3 counterpart
switch(whence)
{
case SEEK_SET :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos = (int) offset;
break;
}
case SEEK_CUR :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos += (int) offset;
break;
}
case SEEK_END :
{
// Quake 3 seems to have trouble with FS_SEEK_END
// so we use the file length and FS_SEEK_SET
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) stream->length + (long) offset, FS_SEEK_SET);
// something has gone wrong, so we return here
if(retVal < 0)
{
return retVal;
}
// keep track of file position
stream->pos = stream->length + (int) offset;
break;
}
default :
{
// unknown whence, so we return an error
errno = EINVAL;
return -1;
}
}
// stream->pos shouldn't be smaller than zero or bigger than the filesize
stream->pos = (stream->pos < 0) ? 0 : stream->pos;
stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
return 0;
}
// fclose() replacement
int S_OGG_Callback_close(void *datasource)
{
// we do nothing here and close all things manually in S_OGG_CodecCloseStream()
return 0;
}
// ftell() replacement
long S_OGG_Callback_tell(void *datasource)
{
snd_stream_t *stream;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
return (long) FS_FTell(stream->file);
}
// the callback structure
const ov_callbacks S_OGG_Callbacks =
{
&S_OGG_Callback_read,
&S_OGG_Callback_seek,
&S_OGG_Callback_close,
&S_OGG_Callback_tell
};
/*
=================
S_OGG_CodecOpenStream
=================
*/
snd_stream_t *S_OGG_CodecOpenStream(const char *filename)
{
snd_stream_t *stream;
// OGG codec control structure
OggVorbis_File *vf;
// some variables used to get informations about the OGG
vorbis_info *OGGInfo;
ogg_int64_t numSamples;
// check if input is valid
if(!filename)
{
return NULL;
}
// Open the stream
stream = S_CodecUtilOpen(filename, &ogg_codec);
if(!stream)
{
return NULL;
}
// alloctate the OggVorbis_File
vf = Z_Malloc(sizeof(OggVorbis_File));
if(!vf)
{
S_CodecUtilClose(&stream);
return NULL;
}
// open the codec with our callbacks and stream as the generic pointer
if(ov_open_callbacks(stream, vf, NULL, 0, S_OGG_Callbacks) != 0)
{
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// the stream must be seekable
if(!ov_seekable(vf))
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// we only support OGGs with one substream
if(ov_streams(vf) != 1)
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// get the info about channels and rate
OGGInfo = ov_info(vf, 0);
if(!OGGInfo)
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(&stream);
return NULL;
}
// get the number of sample-frames in the OGG
numSamples = ov_pcm_total(vf, 0);
// fill in the info-structure in the stream
stream->info.rate = OGGInfo->rate;
stream->info.width = OGG_SAMPLEWIDTH;
stream->info.channels = OGGInfo->channels;
stream->info.samples = numSamples;
stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
stream->info.dataofs = 0;
// We use stream->pos for the file pointer in the compressed ogg file
stream->pos = 0;
// We use the generic pointer in stream for the OGG codec control structure
stream->ptr = vf;
return stream;
}
/*
=================
S_OGG_CodecCloseStream
=================
*/
void S_OGG_CodecCloseStream(snd_stream_t *stream)
{
// check if input is valid
if(!stream)
{
return;
}
// let the OGG codec cleanup its stuff
ov_clear((OggVorbis_File *) stream->ptr);
// free the OGG codec control struct
Z_Free(stream->ptr);
// close the stream
S_CodecUtilClose(&stream);
}
/*
=================
S_OGG_CodecReadStream
=================
*/
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
// buffer handling
int bytesRead, bytesLeft, c;
char *bufPtr;
// Bitstream for the decoder
int BS = 0;
// big endian machines want their samples in big endian order
int IsBigEndian = 0;
# ifdef Q3_BIG_ENDIAN
IsBigEndian = 1;
# endif // Q3_BIG_ENDIAN
// check if input is valid
if(!(stream && buffer))
{
return 0;
}
if(bytes <= 0)
{
return 0;
}
bytesRead = 0;
bytesLeft = bytes;
bufPtr = buffer;
// cycle until we have the requested or all available bytes read
while(-1)
{
// read some bytes from the OGG codec
c = ov_read((OggVorbis_File *) stream->ptr, bufPtr, bytesLeft, IsBigEndian, OGG_SAMPLEWIDTH, 1, &BS);
// no more bytes are left
if(c <= 0)
{
break;
}
bytesRead += c;
bytesLeft -= c;
bufPtr += c;
// we have enough bytes
if(bytesLeft <= 0)
{
break;
}
}
return bytesRead;
}
/*
=====================================================================
S_OGG_CodecLoad
We handle S_OGG_CodecLoad as a special case of the streaming functions
where we read the whole stream at once.
======================================================================
*/
void *S_OGG_CodecLoad(const char *filename, snd_info_t *info)
{
snd_stream_t *stream;
byte *buffer;
int bytesRead;
// check if input is valid
if(!(filename && info))
{
return NULL;
}
// open the file as a stream
stream = S_OGG_CodecOpenStream(filename);
if(!stream)
{
return NULL;
}
// copy over the info
info->rate = stream->info.rate;
info->width = stream->info.width;
info->channels = stream->info.channels;
info->samples = stream->info.samples;
info->size = stream->info.size;
info->dataofs = stream->info.dataofs;
// allocate a buffer
// this buffer must be free-ed by the caller of this function
buffer = Z_Malloc(info->size);
if(!buffer)
{
S_OGG_CodecCloseStream(stream);
return NULL;
}
// fill the buffer
bytesRead = S_OGG_CodecReadStream(stream, info->size, buffer);
// we don't even have read a single byte
if(bytesRead <= 0)
{
Z_Free(buffer);
S_OGG_CodecCloseStream(stream);
return NULL;
}
S_OGG_CodecCloseStream(stream);
return buffer;
}
#endif // USE_CODEC_VORBIS

294
Quake/snd_codec_wav.c Normal file
View file

@ -0,0 +1,294 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "quakedef.h"
#include "snd_codec.h"
/*
=================
FGetLittleLong
=================
*/
static int FGetLittleLong( fileHandle_t f ) {
int v;
FS_Read( &v, sizeof(v), f );
return LittleLong( v);
}
/*
=================
FGetLittleShort
=================
*/
static short FGetLittleShort( fileHandle_t f ) {
short v;
FS_Read( &v, sizeof(v), f );
return LittleShort( v);
}
/*
=================
S_ReadChunkInfo
=================
*/
static int S_ReadChunkInfo(fileHandle_t f, char *name)
{
int len, r;
name[4] = 0;
r = FS_Read(name, 4, f);
if(r != 4)
return -1;
len = FGetLittleLong(f);
if( len < 0 ) {
Com_Printf( S_COLOR_YELLOW "WARNING: Negative chunk length\n" );
return -1;
}
return len;
}
/*
=================
S_FindRIFFChunk
Returns the length of the data in the chunk, or -1 if not found
=================
*/
static int S_FindRIFFChunk( fileHandle_t f, char *chunk ) {
char name[5];
int len;
while( ( len = S_ReadChunkInfo(f, name) ) >= 0 )
{
// If this is the right chunk, return
if( !Q_strncmp( name, chunk, 4 ) )
return len;
len = PAD( len, 2 );
// Not the right chunk - skip it
FS_Seek( f, len, FS_SEEK_CUR );
}
return -1;
}
/*
=================
S_ByteSwapRawSamples
=================
*/
static void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
int i;
if ( width != 2 ) {
return;
}
if ( LittleShort( 256 ) == 256 ) {
return;
}
if ( s_channels == 2 ) {
samples <<= 1;
}
for ( i = 0 ; i < samples ; i++ ) {
((short *)data)[i] = LittleShort( ((short *)data)[i] );
}
}
/*
=================
S_ReadRIFFHeader
=================
*/
static qboolean S_ReadRIFFHeader(fileHandle_t file, snd_info_t *info)
{
char dump[16];
int wav_format;
int bits;
int fmtlen = 0;
// skip the riff wav header
FS_Read(dump, 12, file);
// Scan for the format chunk
if((fmtlen = S_FindRIFFChunk(file, "fmt ")) < 0)
{
Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"fmt\" chunk\n");
return qfalse;
}
// Save the parameters
wav_format = FGetLittleShort(file);
info->channels = FGetLittleShort(file);
info->rate = FGetLittleLong(file);
FGetLittleLong(file);
FGetLittleShort(file);
bits = FGetLittleShort(file);
if( bits < 8 )
{
Com_Printf( S_COLOR_RED "ERROR: Less than 8 bit sound is not supported\n");
return qfalse;
}
info->width = bits / 8;
info->dataofs = 0;
// Skip the rest of the format chunk if required
if(fmtlen > 16)
{
fmtlen -= 16;
FS_Seek( file, fmtlen, FS_SEEK_CUR );
}
// Scan for the data chunk
if( (info->size = S_FindRIFFChunk(file, "data")) < 0)
{
Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"data\" chunk\n");
return qfalse;
}
info->samples = (info->size / info->width) / info->channels;
return qtrue;
}
// WAV codec
snd_codec_t wav_codec =
{
".wav",
S_WAV_CodecLoad,
S_WAV_CodecOpenStream,
S_WAV_CodecReadStream,
S_WAV_CodecCloseStream,
NULL
};
/*
=================
S_WAV_CodecLoad
=================
*/
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info)
{
fileHandle_t file;
void *buffer;
// Try to open the file
FS_FOpenFileRead(filename, &file, qtrue);
if(!file)
{
Com_Printf( S_COLOR_RED "ERROR: Could not open \"%s\"\n",
filename);
return NULL;
}
// Read the RIFF header
if(!S_ReadRIFFHeader(file, info))
{
FS_FCloseFile(file);
Com_Printf( S_COLOR_RED "ERROR: Incorrect/unsupported format in \"%s\"\n",
filename);
return NULL;
}
// Allocate some memory
buffer = Z_Malloc(info->size);
if(!buffer)
{
FS_FCloseFile(file);
Com_Printf( S_COLOR_RED "ERROR: Out of memory reading \"%s\"\n",
filename);
return NULL;
}
// Read, byteswap
FS_Read(buffer, info->size, file);
S_ByteSwapRawSamples(info->samples, info->width, info->channels, (byte *)buffer);
// Close and return
FS_FCloseFile(file);
return buffer;
}
/*
=================
S_WAV_CodecOpenStream
=================
*/
snd_stream_t *S_WAV_CodecOpenStream(const char *filename)
{
snd_stream_t *rv;
// Open
rv = S_CodecUtilOpen(filename, &wav_codec);
if(!rv)
return NULL;
// Read the RIFF header
if(!S_ReadRIFFHeader(rv->file, &rv->info))
{
S_CodecUtilClose(&rv);
return NULL;
}
return rv;
}
/*
=================
S_WAV_CodecCloseStream
=================
*/
void S_WAV_CodecCloseStream(snd_stream_t *stream)
{
S_CodecUtilClose(&stream);
}
/*
=================
S_WAV_CodecReadStream
=================
*/
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
int remaining = stream->info.size - stream->pos;
int samples;
if(remaining <= 0)
return 0;
if(bytes > remaining)
bytes = remaining;
stream->pos += bytes;
samples = (bytes / stream->info.width) / stream->info.channels;
FS_Read(buffer, bytes, stream->file);
S_ByteSwapRawSamples(samples, stream->info.width, stream->info.channels, buffer);
return bytes;
}

View file

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// snd_dma.c -- main control for any streaming sound output device
#include "quakedef.h"
#include "snd_codec.h"
void S_Play(void);
void S_PlayVol(void);
@ -30,6 +31,10 @@ void S_Update_();
void S_StopAllSounds(qboolean clear);
void S_StopAllSoundsC(void);
snd_stream_t *s_backgroundStream = NULL;
static char s_backgroundLoop[MAX_QPATH];
// =======================================================================
// Internal sound data & structures
// =======================================================================
@ -62,6 +67,10 @@ sfx_t *ambient_sfx[NUM_AMBIENTS];
qboolean sound_started = false;
int s_rawend[MAX_RAW_STREAMS];
portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
cvar_t bgmvolume = {"bgmvolume", "1", true};
cvar_t sfxvolume = {"volume", "0.7", true};
@ -564,6 +573,142 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
}
//=============================================================================
/*
=================
S_ByteSwapRawSamples
If raw data has been loaded in little endien binary form, this must be done.
If raw data was calculated, as with ADPCM, this should not be called.
=================
*/
void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
int i;
if ( width != 2 ) {
return;
}
if ( LittleShort( 256 ) == 256 ) {
return;
}
if ( s_channels == 2 ) {
samples <<= 1;
}
for ( i = 0 ; i < samples ; i++ ) {
((short *)data)[i] = LittleShort( ((short *)data)[i] );
}
}
/*
============
S_Base_RawSamples
Music streaming
============
*/
void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
int i;
int src, dst;
float scale;
int intVolume;
portable_samplepair_t *rawsamples;
if ( !sound_started ) {
return;
}
if ( (stream < 0) || (stream >= MAX_RAW_STREAMS) ) {
return;
}
rawsamples = s_rawsamples[stream];
intVolume = 256 * volume;
if ( s_rawend[stream] < soundtime ) {
Com_DPrintf( "S_Base_RawSamples: resetting minimum: %i < %i\n", s_rawend[stream], soundtime );
s_rawend[stream] = soundtime;
}
scale = (float)rate / shm->speed;
//Com_Printf ("%i < %i < %i\n", soundtime, s_paintedtime, s_rawend[stream]);
if (s_channels == 2 && width == 2)
{
if (scale == 1.0)
{ // optimized case
for (i=0 ; i<samples ; i++)
{
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
}
}
else
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
}
}
}
else if (s_channels == 1 && width == 2)
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((short *)data)[src] * intVolume;
rawsamples[dst].right = ((short *)data)[src] * intVolume;
}
}
else if (s_channels == 2 && width == 1)
{
intVolume *= 256;
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
}
}
else if (s_channels == 1 && width == 1)
{
intVolume *= 256;
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
}
}
if ( s_rawend[stream] > soundtime + MAX_RAW_SAMPLES ) {
Com_DPrintf( "S_Base_RawSamples: overflowed %i > %i\n", s_rawend[stream], soundtime );
}
}
//=============================================================================
/*
@ -716,6 +861,9 @@ void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
Con_Printf ("----(%i)----\n", total);
}
// add raw data from streamed samples
S_UpdateBackgroundTrack();
// mix some sound
S_Update_();
}
@ -796,6 +944,152 @@ void S_Update_(void)
#endif
}
/*
===============================================================================
background music functions
===============================================================================
*/
/*
======================
S_StopBackgroundTrack
======================
*/
void S_Base_StopBackgroundTrack( void ) {
if(!s_backgroundStream)
return;
S_CodecCloseStream(s_backgroundStream);
s_backgroundStream = NULL;
s_rawend[0] = 0;
}
/*
======================
S_StartBackgroundTrack
======================
*/
void S_Base_StartBackgroundTrack( const char *intro, const char *loop ){
if ( !intro ) {
intro = "";
}
if ( !loop || !loop[0] ) {
loop = intro;
}
Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop );
if(!*intro)
{
S_Base_StopBackgroundTrack();
return;
}
if( !loop ) {
s_backgroundLoop[0] = 0;
} else {
Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
}
// close the background track, but DON'T reset s_rawend
// if restarting the same back ground track
if(s_backgroundStream)
{
S_CodecCloseStream(s_backgroundStream);
s_backgroundStream = NULL;
}
// Open stream
s_backgroundStream = S_CodecOpenStream(intro);
if(!s_backgroundStream) {
Com_Printf( "WARNING: couldn't open music file %s\n", intro );
return;
}
if(s_backgroundStream->info.channels != 2 || s_backgroundStream->info.rate != 22050) {
Com_Printf( "WARNING: music file %s is not 22k stereo\n", intro );
}
}
/*
======================
S_UpdateBackgroundTrack
======================
*/
void S_UpdateBackgroundTrack( void ) {
int bufferSamples;
int fileSamples;
byte raw[30000]; // just enough to fit in a mac stack frame
int fileBytes;
int r;
if(!s_backgroundStream) {
return;
}
// don't bother playing anything if musicvolume is 0
if ( bgmvolume.value <= 0 ) {
return;
}
// see how many samples should be copied into the raw buffer
if ( s_rawend[0] < soundtime ) {
s_rawend[0] = soundtime;
}
while ( s_rawend[0] < soundtime + MAX_RAW_SAMPLES ) {
bufferSamples = MAX_RAW_SAMPLES - (s_rawend[0] - soundtime);
// decide how much data needs to be read from the file
fileSamples = bufferSamples * s_backgroundStream->info.rate / shm->speed;
if (!fileSamples)
return;
// our max buffer size
fileBytes = fileSamples * (s_backgroundStream->info.width * s_backgroundStream->info.channels);
if ( fileBytes > sizeof(raw) ) {
fileBytes = sizeof(raw);
fileSamples = fileBytes / (s_backgroundStream->info.width * s_backgroundStream->info.channels);
}
// Read
r = S_CodecReadStream(s_backgroundStream, fileBytes, raw);
if(r < fileBytes)
{
fileBytes = r;
fileSamples = r / (s_backgroundStream->info.width * s_backgroundStream->info.channels);
}
if(r > 0)
{
// add to raw buffer
S_Base_RawSamples( 0, fileSamples, s_backgroundStream->info.rate,
s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, bgmvolume.value );
}
else
{
// loop
if(s_backgroundLoop[0])
{
S_CodecCloseStream(s_backgroundStream);
s_backgroundStream = NULL;
S_Base_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop );
if(!s_backgroundStream)
return;
}
else
{
S_Base_StopBackgroundTrack();
return;
}
}
}
}
void S_BlockSound (void)
{
/* FIXME: do we really need the blocking at the

View file

@ -155,6 +155,7 @@ void S_PaintChannels (int endtime)
{
int i;
int end, ltime, count;
int stream;
channel_t *ch;
sfxcache_t *sc;
@ -167,28 +168,20 @@ void S_PaintChannels (int endtime)
if (endtime - paintedtime > PAINTBUFFER_SIZE)
end = paintedtime + PAINTBUFFER_SIZE;
// clear the paint buffer
// clear the paint buffer and mix any raw samples... (e.g. OGG music)
memset(paintbuffer, 0, sizeof(paintbuffer));
// paint the OGG music
{
ltime = paintedtime;
int volume = 255 * bgmvolume.value;
count = end - ltime;
int i;
for (i = 0; i < count; i++)
{
int data = 32767 - (rand() % 65535);
paintbuffer[i].left += data * volume;
paintbuffer[i].right += data * volume;
}
}
for (stream = 0; stream < MAX_RAW_STREAMS; stream++) {
if ( s_rawend[stream] >= paintedtime ) {
// copy from the streaming sound source
const portable_samplepair_t *rawsamples = s_rawsamples[stream];
const int stop = (end < s_rawend[stream]) ? end : s_rawend[stream];
for ( i = paintedtime ; i < stop ; i++ ) {
const int s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-paintedtime].left += rawsamples[s].left;
paintbuffer[i-paintedtime].right += rawsamples[s].right;
}
}
}
// paint in the sfx channels.
ch = snd_channels;

View file

@ -171,6 +171,11 @@ extern vec3_t listener_up;
extern volatile dma_t *shm;
extern volatile dma_t sn;
#define MAX_RAW_SAMPLES 16384
#define MAX_RAW_STREAMS 128
extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
extern int s_rawend[MAX_RAW_STREAMS];
extern cvar_t loadas8bit;
extern cvar_t bgmvolume;
extern cvar_t sfxvolume;