mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2025-03-16 07:52:24 +00:00
Partly integrated Q3 sound codec and background
music code
This commit is contained in:
parent
c657e08b20
commit
474dde2a87
8 changed files with 1446 additions and 21 deletions
|
@ -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
237
Quake/snd_codec.c
Normal 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
95
Quake/snd_codec.h
Normal 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
480
Quake/snd_codec_ogg.c
Normal 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
294
Quake/snd_codec_wav.c
Normal 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;
|
||||
}
|
294
Quake/snd_dma.c
294
Quake/snd_dma.c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue