mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-18 00:00:24 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02df95fc21 | ||
|
|
03100b47c1 | ||
|
|
67444b0f15 | ||
|
|
f6ae9a2b43 | ||
|
|
98cd85bc9a | ||
|
|
83a83b08f6 | ||
|
|
448e9010dc | ||
|
|
ce69ed75f7 | ||
|
|
6ac70d6b7c | ||
|
|
4f588d939d | ||
|
|
f6ebdd1998 | ||
|
|
7438dd709c | ||
|
|
1207b46937 | ||
|
|
497f853171 | ||
|
|
4984f8b925 | ||
|
|
a1d581df39 |
No files matched your search
+6
-2
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(omohaaded)
|
||||
project(openmohaa)
|
||||
|
||||
include(TargetArch.cmake)
|
||||
|
||||
@@ -205,7 +205,8 @@ if(WIN32)
|
||||
endif()
|
||||
target_link_libraries(omohaaded PRIVATE wsock32 ws2_32)
|
||||
target_link_libraries(omohaaded PRIVATE winmm)
|
||||
|
||||
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:fgame> DESTINATION "${TARGET_BASE_GAME}" OPTIONAL)
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:omohaaded> DESTINATION "./" OPTIONAL)
|
||||
elseif(UNIX)
|
||||
find_package(Threads)
|
||||
@@ -280,6 +281,9 @@ if (WITH_CLIENT)
|
||||
|
||||
target_link_libraries(openmohaa PRIVATE wsock32 ws2_32)
|
||||
target_link_libraries(openmohaa PRIVATE winmm)
|
||||
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:cgame> DESTINATION "${TARGET_BASE_GAME}" OPTIONAL)
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:openmohaa> DESTINATION "./" OPTIONAL)
|
||||
elseif(UNIX)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(GLUT REQUIRED)
|
||||
|
||||
@@ -14,28 +14,36 @@
|
||||
|
||||
## Current State
|
||||
|
||||
OpenMoHAA is still in its early days.
|
||||
|
||||
### Server
|
||||
|
||||
The server version can successfully be built. There are few unimplemented stuff from the game library :
|
||||
The server version can successfully be built.
|
||||
Some stuff are not working as intended, such as Actor or Vehicle. These systems are both almost fully implemented, but not really in stable state.
|
||||
|
||||
- Actor
|
||||
- Vehicle
|
||||
Overall the server part and the game module are almost fully implemented, but the stability may vary, there may be some minor bugs and crashes.
|
||||
|
||||
### Client
|
||||
|
||||
There is currently no client build because the UI code is currently unimplemented. Here is what's missing or unimplemented :
|
||||
The client version of OpenMoHAA is partially implemented and not stable currently. The cgame module is partially implemented as well, but is compatible with the original MOH:AA game. Credits to **Heavy Metal: F.A.K.K. 2**'s SDK for the cgame module.
|
||||
|
||||
- rendering (mostly advanced)
|
||||
- sound (must be reworked)
|
||||
- UI
|
||||
Here is the current working state for each part of the engine:
|
||||
|
||||
Using CMake, the client build and successfully be built by adding `-DWITH_CLIENT=1`
|
||||
|
||||
The client game module (cgame) is partially implemented, and is compatible with the original MOH:AA module.
|
||||
| Part name | Full | Almost | Half | Early | Bad | Not working | Comment |
|
||||
|-------------------------|------|--------|------|-------|-----|-------------|-------------------------------------------------------------------|
|
||||
| Audio | | | | | x | | Very basic implementation from Quake III |
|
||||
| CG Module | | x | | | | | Missing FX, Marks and decals |
|
||||
| Client | | | x | | | | Missing GameSpy, server list, binds, map picker, ... |
|
||||
| Collision | x | | | | | | |
|
||||
| Model/TIKI/Skeletor | x | | | | | | |
|
||||
| Renderer | | | | x | | | Missing ghost, marks, sphere lights, sky portal, sun flare, swipe |
|
||||
| Server | | x | | | | | Probably a few bugs remaining |
|
||||
| Server module (fgame) | | x | | | | | Actor, vehicle, and few gameplay bugs |
|
||||
| UI | | | | x | | | Has core features only (menu, widgets, console, hud, urc loading) |
|
||||
|
||||
## Running
|
||||
|
||||
Backup the existing **gamex86.dll** (rename it to **gamex86.bak**).
|
||||
Backup existing mohaa files, **cgamex86.dll** and **gamex86.dll** (set their extension to **.bak**)
|
||||
|
||||
a) extract archive to your MOHAA installation directory.
|
||||
|
||||
@@ -53,6 +61,8 @@ You can now start a local OpenMOHAA server or play on a server.
|
||||
|
||||
## Compiling
|
||||
|
||||
OpenMoHAA supports both x86 and x64 architectures, and although the target platform is Windows, it still can be compiled for other platforms, and even other architectures.
|
||||
|
||||
These are the tools required for all platforms :
|
||||
- CMake >= 3.5
|
||||
- Flex and Bison (currently not used)
|
||||
@@ -60,6 +70,8 @@ These are the tools required for all platforms :
|
||||
|
||||
The installation directory can be set to MOHAA directory.
|
||||
|
||||
The client build and successfully be built by adding `-DWITH_CLIENT=1` to the CMake command line.
|
||||
|
||||
### Linux
|
||||
|
||||
These are the tools required on Unix :
|
||||
|
||||
@@ -4732,6 +4732,15 @@ void ClientGameCommandManager::SpawnTempModel(int mcount, int timeAlive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to not spawn the world model as a tempmodel
|
||||
// Of course it would become extremely confusing...
|
||||
if (!(p->cgd.flags & T_DLIGHT) && p->ent.reType == ET_MODELANIM_SKEL && !p->ent.hModel)
|
||||
{
|
||||
Com_Printf("^~^~^ not spawning tempmodel because it is using the world as a brush model\n");
|
||||
FreeTempModel(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1142,17 +1142,17 @@ redump:
|
||||
case ZA_SOUND_MONO:
|
||||
if (!cinTable[currentHandle].silent) {
|
||||
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
|
||||
S_RawSamples(0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, -1);
|
||||
S_RawSamples(0, 22050, 2, 1, (byte *)sbuf, 1.0f);
|
||||
}
|
||||
break;
|
||||
case ZA_SOUND_STEREO:
|
||||
if (!cinTable[currentHandle].silent) {
|
||||
if (cinTable[currentHandle].numQuads == -1) {
|
||||
S_Update();
|
||||
s_rawend[0] = s_soundtime;
|
||||
s_rawend = s_soundtime;
|
||||
}
|
||||
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
|
||||
S_RawSamples(0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, -1);
|
||||
S_RawSamples(0, 22050, 2, 2, (byte *)sbuf, 1.0f);
|
||||
}
|
||||
break;
|
||||
case ROQ_QUAD_INFO:
|
||||
@@ -1469,7 +1469,7 @@ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBi
|
||||
}
|
||||
|
||||
if (!cinTable[currentHandle].silent) {
|
||||
s_rawend[0] = s_soundtime;
|
||||
s_rawend = s_soundtime;
|
||||
}
|
||||
|
||||
return currentHandle;
|
||||
|
||||
@@ -25,7 +25,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
/*
|
||||
** Intel/DVI ADPCM coder/decoder.
|
||||
**
|
||||
** The algorithm for this coder was taken from the IMA Compatibility Project
|
||||
** The algorithm for this coder was taken from the IMA Compatability Project
|
||||
** proceedings, Vol 2, Number 2; May 1992.
|
||||
**
|
||||
** Version 1.2, 18-Dec-92.
|
||||
@@ -278,7 +278,7 @@ void S_AdpcmGetSamples(sndBuffer *chunk, short *to) {
|
||||
|
||||
out = (byte *)chunk->sndChunk;
|
||||
// get samples
|
||||
S_AdpcmDecode((char *) out, to, SND_CHUNK_SIZE_BYTE*2, &state );
|
||||
S_AdpcmDecode( out, to, SND_CHUNK_SIZE_BYTE*2, &state );
|
||||
}
|
||||
|
||||
|
||||
@@ -310,7 +310,7 @@ void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
|
||||
newchunk = SND_malloc();
|
||||
if (sfx->soundData == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else if (chunk != NULL) {
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
@@ -322,7 +322,7 @@ void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
|
||||
out = (byte *)chunk->sndChunk;
|
||||
|
||||
// encode the samples
|
||||
S_AdpcmEncode( samples + inOffset, (char *) out, n, &state );
|
||||
S_AdpcmEncode( samples + inOffset, out, n, &state );
|
||||
|
||||
inOffset += n;
|
||||
count -= n;
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
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
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
/* This file is only compiled for PowerPC builds with Altivec support.
|
||||
Altivec intrinsics need to be in a separate file, so GCC's -maltivec
|
||||
command line can enable them, but give us the option to _not_ use that
|
||||
on other files, where the compiler might then generate Altivec
|
||||
instructions for normal floating point, crashing on G3 (etc) processors. */
|
||||
|
||||
#include "client.h"
|
||||
#include "snd_local.h"
|
||||
|
||||
#if idppc_altivec
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
void S_PaintChannelFrom16_altivec( portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE], int snd_vol, channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data, aoff, boff;
|
||||
int leftvol, rightvol;
|
||||
int i, j;
|
||||
portable_samplepair_t *samp;
|
||||
sndBuffer *chunk;
|
||||
short *samples;
|
||||
float ooff, fdata[2], fdiv, fleftvol, frightvol;
|
||||
|
||||
if (sc->soundChannels <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
samp = &paintbuffer[ bufferOffset ];
|
||||
|
||||
if (ch->doppler) {
|
||||
sampleOffset = sampleOffset*ch->oldDopplerScale;
|
||||
}
|
||||
|
||||
if ( sc->soundChannels == 2 ) {
|
||||
sampleOffset *= sc->soundChannels;
|
||||
|
||||
if ( sampleOffset & 1 ) {
|
||||
sampleOffset &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
chunk = sc->soundData;
|
||||
while (sampleOffset>=SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
sampleOffset -= SND_CHUNK_SIZE;
|
||||
if (!chunk) {
|
||||
chunk = sc->soundData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ch->doppler || ch->dopplerScale==1.0f) {
|
||||
vector signed short volume_vec;
|
||||
vector unsigned int volume_shift;
|
||||
int vectorCount, samplesLeft, chunkSamplesLeft;
|
||||
leftvol = ch->leftvol*snd_vol;
|
||||
rightvol = ch->rightvol*snd_vol;
|
||||
samples = chunk->sndChunk;
|
||||
((short *)&volume_vec)[0] = leftvol;
|
||||
((short *)&volume_vec)[1] = leftvol;
|
||||
((short *)&volume_vec)[4] = leftvol;
|
||||
((short *)&volume_vec)[5] = leftvol;
|
||||
((short *)&volume_vec)[2] = rightvol;
|
||||
((short *)&volume_vec)[3] = rightvol;
|
||||
((short *)&volume_vec)[6] = rightvol;
|
||||
((short *)&volume_vec)[7] = rightvol;
|
||||
volume_shift = vec_splat_u32(8);
|
||||
i = 0;
|
||||
|
||||
while(i < count) {
|
||||
/* Try to align destination to 16-byte boundary */
|
||||
while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
|
||||
if ( sc->soundChannels == 2 ) {
|
||||
data = samples[sampleOffset++];
|
||||
}
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
/* Destination is now aligned. Process as many 8-sample
|
||||
chunks as we can before we run out of room from the current
|
||||
sound chunk. We do 8 per loop to avoid extra source data reads. */
|
||||
samplesLeft = count - i;
|
||||
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
|
||||
if(samplesLeft > chunkSamplesLeft)
|
||||
samplesLeft = chunkSamplesLeft;
|
||||
|
||||
vectorCount = samplesLeft / 8;
|
||||
|
||||
if(vectorCount)
|
||||
{
|
||||
vector unsigned char tmp;
|
||||
vector short s0, s1, sampleData0, sampleData1;
|
||||
vector signed int merge0, merge1;
|
||||
vector signed int d0, d1, d2, d3;
|
||||
vector unsigned char samplePermute0 =
|
||||
VECCONST_UINT8(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
|
||||
vector unsigned char samplePermute1 =
|
||||
VECCONST_UINT8(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
|
||||
vector unsigned char loadPermute0, loadPermute1;
|
||||
|
||||
// Rather than permute the vectors after we load them to do the sample
|
||||
// replication and rearrangement, we permute the alignment vector so
|
||||
// we do everything in one step below and avoid data shuffling.
|
||||
tmp = vec_lvsl(0,&samples[sampleOffset]);
|
||||
loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
|
||||
loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
|
||||
|
||||
s0 = *(vector short *)&samples[sampleOffset];
|
||||
while(vectorCount)
|
||||
{
|
||||
/* Load up source (16-bit) sample data */
|
||||
s1 = *(vector short *)&samples[sampleOffset+7];
|
||||
|
||||
/* Load up destination sample data */
|
||||
d0 = *(vector signed int *)&samp[i];
|
||||
d1 = *(vector signed int *)&samp[i+2];
|
||||
d2 = *(vector signed int *)&samp[i+4];
|
||||
d3 = *(vector signed int *)&samp[i+6];
|
||||
|
||||
sampleData0 = vec_perm(s0,s1,loadPermute0);
|
||||
sampleData1 = vec_perm(s0,s1,loadPermute1);
|
||||
|
||||
merge0 = vec_mule(sampleData0,volume_vec);
|
||||
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
|
||||
|
||||
merge1 = vec_mulo(sampleData0,volume_vec);
|
||||
merge1 = vec_sra(merge1,volume_shift);
|
||||
|
||||
d0 = vec_add(merge0,d0);
|
||||
d1 = vec_add(merge1,d1);
|
||||
|
||||
merge0 = vec_mule(sampleData1,volume_vec);
|
||||
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
|
||||
|
||||
merge1 = vec_mulo(sampleData1,volume_vec);
|
||||
merge1 = vec_sra(merge1,volume_shift);
|
||||
|
||||
d2 = vec_add(merge0,d2);
|
||||
d3 = vec_add(merge1,d3);
|
||||
|
||||
/* Store destination sample data */
|
||||
*(vector signed int *)&samp[i] = d0;
|
||||
*(vector signed int *)&samp[i+2] = d1;
|
||||
*(vector signed int *)&samp[i+4] = d2;
|
||||
*(vector signed int *)&samp[i+6] = d3;
|
||||
|
||||
i += 8;
|
||||
vectorCount--;
|
||||
s0 = s1;
|
||||
sampleOffset += 8;
|
||||
}
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fleftvol = ch->leftvol*snd_vol;
|
||||
frightvol = ch->rightvol*snd_vol;
|
||||
|
||||
ooff = sampleOffset;
|
||||
samples = chunk->sndChunk;
|
||||
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
|
||||
aoff = ooff;
|
||||
ooff = ooff + ch->dopplerScale * sc->soundChannels;
|
||||
boff = ooff;
|
||||
fdata[0] = fdata[1] = 0;
|
||||
for (j=aoff; j<boff; j += sc->soundChannels) {
|
||||
if (j == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
if (!chunk) {
|
||||
chunk = sc->soundData;
|
||||
}
|
||||
samples = chunk->sndChunk;
|
||||
ooff -= SND_CHUNK_SIZE;
|
||||
}
|
||||
if ( sc->soundChannels == 2 ) {
|
||||
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
fdata[1] += samples[(j+1)&(SND_CHUNK_SIZE-1)];
|
||||
} else {
|
||||
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
fdata[1] += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
}
|
||||
}
|
||||
fdiv = 256 * (boff-aoff) / sc->soundChannels;
|
||||
samp[i].left += (fdata[0] * fleftvol)/fdiv;
|
||||
samp[i].right += (fdata[1] * frightvol)/fdiv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 "client.h"
|
||||
#include "snd_codec.h"
|
||||
|
||||
static snd_codec_t *codecs;
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecGetSound
|
||||
|
||||
Opens/loads a sound, tries codec based on the sound's file extension
|
||||
then tries all supported codecs.
|
||||
=================
|
||||
*/
|
||||
static void *S_CodecGetSound(const char *filename, snd_info_t *info)
|
||||
{
|
||||
snd_codec_t *codec;
|
||||
snd_codec_t *orgCodec = NULL;
|
||||
qboolean orgNameFailed = qfalse;
|
||||
char localName[ MAX_QPATH ];
|
||||
const char *ext;
|
||||
char altName[ MAX_QPATH ];
|
||||
void *rtn = NULL;
|
||||
|
||||
Q_strncpyz(localName, filename, MAX_QPATH);
|
||||
|
||||
ext = COM_GetExtension(localName);
|
||||
|
||||
if( *ext )
|
||||
{
|
||||
// Look for the correct loader and use it
|
||||
for( codec = codecs; codec; codec = codec->next )
|
||||
{
|
||||
if( !Q_stricmp( ext, codec->ext ) )
|
||||
{
|
||||
// Load
|
||||
if( info )
|
||||
rtn = codec->load(localName, info);
|
||||
else
|
||||
rtn = codec->open(localName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// A loader was found
|
||||
if( codec )
|
||||
{
|
||||
if( !rtn )
|
||||
{
|
||||
// Loader failed, most likely because the file isn't there;
|
||||
// try again without the extension
|
||||
orgNameFailed = qtrue;
|
||||
orgCodec = codec;
|
||||
COM_StripExtension( filename, localName, MAX_QPATH );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Something loaded
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try and find a suitable match using all
|
||||
// the sound codecs supported
|
||||
for( codec = codecs; codec; codec = codec->next )
|
||||
{
|
||||
if( codec == orgCodec )
|
||||
continue;
|
||||
|
||||
Com_sprintf( altName, sizeof (altName), "%s.%s", localName, codec->ext );
|
||||
|
||||
// Load
|
||||
if( info )
|
||||
rtn = codec->load(altName, info);
|
||||
else
|
||||
rtn = codec->open(altName);
|
||||
|
||||
if( rtn )
|
||||
{
|
||||
if( orgNameFailed )
|
||||
{
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s not present, using %s instead\n",
|
||||
filename, altName );
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
||||
Com_Printf(S_COLOR_YELLOW "WARNING: Failed to %s sound %s!\n", info ? "load" : "open", filename);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecInit
|
||||
=================
|
||||
*/
|
||||
void S_CodecInit()
|
||||
{
|
||||
codecs = NULL;
|
||||
|
||||
#ifdef USE_CODEC_OPUS
|
||||
S_CodecRegister(&opus_codec);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CODEC_VORBIS
|
||||
S_CodecRegister(&ogg_codec);
|
||||
#endif
|
||||
|
||||
// Register wav codec last so that it is always tried first when a file extension was not found
|
||||
S_CodecRegister(&wav_codec);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
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)
|
||||
{
|
||||
return S_CodecGetSound(filename, info);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecOpenStream
|
||||
=================
|
||||
*/
|
||||
snd_stream_t *S_CodecOpenStream(const char *filename)
|
||||
{
|
||||
return S_CodecGetSound(filename, NULL);
|
||||
}
|
||||
|
||||
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, qtrue);
|
||||
if(!hnd)
|
||||
{
|
||||
Com_DPrintf("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;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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_
|
||||
|
||||
#include "../qcommon/q_shared.h"
|
||||
#include "../qcommon/qcommon.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;
|
||||
fileHandle_t 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
|
||||
|
||||
// Ogg Opus codec
|
||||
#ifdef USE_CODEC_OPUS
|
||||
extern snd_codec_t opus_codec;
|
||||
void *S_OggOpus_CodecLoad(const char *filename, snd_info_t *info);
|
||||
snd_stream_t *S_OggOpus_CodecOpenStream(const char *filename);
|
||||
void S_OggOpus_CodecCloseStream(snd_stream_t *stream);
|
||||
int S_OggOpus_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
|
||||
#endif // USE_CODEC_OPUS
|
||||
|
||||
#endif // !_SND_CODEC_H_
|
||||
@@ -1,717 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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>
|
||||
Copyright (C) 2006 Thilo Schulz <arny@ats.s.bawue.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
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#if USE_CODEC_MP3
|
||||
|
||||
// includes for the Q3 sound system
|
||||
#include "client.h"
|
||||
#include "snd_codec.h"
|
||||
|
||||
// includes for the MP3 codec
|
||||
#include <mad.h>
|
||||
|
||||
#define MP3_SAMPLE_WIDTH 2
|
||||
#define MP3_PCMSAMPLES_PERSLICE 32
|
||||
|
||||
// buffer size used when reading through the mp3
|
||||
#define MP3_DATA_BUFSIZ 128*1024
|
||||
|
||||
// undefine this if you don't want any dithering.
|
||||
#define MP3_DITHERING
|
||||
|
||||
// Q3 MP3 codec
|
||||
snd_codec_t mp3_codec =
|
||||
{
|
||||
".mp3",
|
||||
S_MP3_CodecLoad,
|
||||
S_MP3_CodecOpenStream,
|
||||
S_MP3_CodecReadStream,
|
||||
S_MP3_CodecCloseStream,
|
||||
NULL
|
||||
};
|
||||
|
||||
// structure used for info purposes
|
||||
struct snd_codec_mp3_info
|
||||
{
|
||||
byte encbuf[MP3_DATA_BUFSIZ]; // left over bytes not consumed
|
||||
// by the decoder.
|
||||
struct mad_stream madstream; // uses encbuf as buffer.
|
||||
struct mad_frame madframe; // control structures for libmad.
|
||||
struct mad_synth madsynth;
|
||||
|
||||
byte *pcmbuf; // buffer for not-used samples.
|
||||
int buflen; // length of buffer data.
|
||||
int pcmbufsize; // amount of allocated memory for
|
||||
// pcmbuf. This should have at least
|
||||
// the size of a decoded mp3 frame.
|
||||
|
||||
byte *dest; // copy decoded data here.
|
||||
int destlen; // amount of already copied data.
|
||||
int destsize; // amount of bytes we must decode.
|
||||
};
|
||||
|
||||
/*************** MP3 utility functions ***************/
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_ReadData
|
||||
=================
|
||||
*/
|
||||
|
||||
// feed libmad with data
|
||||
int S_MP3_ReadData(snd_stream_t *stream, struct mad_stream *madstream, byte *encbuf, int encbufsize)
|
||||
{
|
||||
int retval;
|
||||
int leftover;
|
||||
|
||||
if(!stream)
|
||||
return -1;
|
||||
|
||||
leftover = madstream->bufend - madstream->next_frame;
|
||||
if(leftover > 0)
|
||||
memmove(encbuf, madstream->this_frame, leftover);
|
||||
|
||||
|
||||
// Fill the buffer right to the end
|
||||
|
||||
retval = FS_Read(&encbuf[leftover], encbufsize - leftover, stream->file);
|
||||
|
||||
if(retval <= 0)
|
||||
{
|
||||
// EOF reached, that's ok.
|
||||
return 0;
|
||||
}
|
||||
|
||||
mad_stream_buffer(madstream, encbuf, retval + leftover);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_Scanfile
|
||||
|
||||
to determine the samplecount, we apparently must get *all* headers :(
|
||||
I basically used the xmms-mad plugin source to see how this stuff works.
|
||||
|
||||
returns a value < 0 on error.
|
||||
=================
|
||||
*/
|
||||
|
||||
int S_MP3_Scanfile(snd_stream_t *stream)
|
||||
{
|
||||
struct mad_stream madstream;
|
||||
struct mad_header madheader;
|
||||
int retval;
|
||||
int samplecount;
|
||||
byte encbuf[MP3_DATA_BUFSIZ];
|
||||
|
||||
// error out on invalid input.
|
||||
if(!stream)
|
||||
return -1;
|
||||
|
||||
mad_stream_init(&madstream);
|
||||
mad_header_init(&madheader);
|
||||
|
||||
while(1)
|
||||
{
|
||||
retval = S_MP3_ReadData(stream, &madstream, encbuf, sizeof(encbuf));
|
||||
if(retval < 0)
|
||||
return -1;
|
||||
else if(retval == 0)
|
||||
break;
|
||||
|
||||
// Start decoding the headers.
|
||||
while(1)
|
||||
{
|
||||
if((retval = mad_header_decode(&madheader, &madstream)) < 0)
|
||||
{
|
||||
if(madstream.error == MAD_ERROR_BUFLEN)
|
||||
{
|
||||
// We need to read more data
|
||||
break;
|
||||
}
|
||||
|
||||
if(!MAD_RECOVERABLE (madstream.error))
|
||||
{
|
||||
// unrecoverable error... we must bail out.
|
||||
return retval;
|
||||
}
|
||||
|
||||
mad_stream_skip(&madstream, madstream.skiplen);
|
||||
continue;
|
||||
}
|
||||
|
||||
// we got a valid header.
|
||||
|
||||
if(madheader.layer != MAD_LAYER_III)
|
||||
{
|
||||
// we don't support non-mp3s
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!stream->info.samples)
|
||||
{
|
||||
// This here is the very first frame. Set initial values now,
|
||||
// that we expect to stay constant throughout the whole mp3.
|
||||
|
||||
stream->info.rate = madheader.samplerate;
|
||||
stream->info.width = MP3_SAMPLE_WIDTH;
|
||||
stream->info.channels = MAD_NCHANNELS(&madheader);
|
||||
stream->info.samples = 0;
|
||||
stream->info.size = 0; // same here.
|
||||
stream->info.dataofs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check whether something changed that shouldn't.
|
||||
|
||||
if(stream->info.rate != madheader.samplerate ||
|
||||
stream->info.channels != MAD_NCHANNELS(&madheader))
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Update the counters
|
||||
samplecount = MAD_NSBSAMPLES(&madheader) * MP3_PCMSAMPLES_PERSLICE;
|
||||
stream->info.samples += samplecount;
|
||||
stream->info.size += samplecount * stream->info.channels * stream->info.width;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the file pointer so we can do the real decoding.
|
||||
FS_Seek(stream->file, 0, FS_SEEK_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************ dithering functions ***************************/
|
||||
|
||||
#ifdef MP3_DITHERING
|
||||
|
||||
// All dithering done here is taken from the GPL'ed xmms-mad plugin.
|
||||
|
||||
/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */
|
||||
/* Any feedback is very welcome. For any question, comments, */
|
||||
/* see http://www.math.keio.ac.jp/matumoto/emt.html or email */
|
||||
/* matumoto@math.keio.ac.jp */
|
||||
|
||||
/* Period parameters */
|
||||
#define MP3_DITH_N 624
|
||||
#define MP3_DITH_M 397
|
||||
#define MATRIX_A 0x9908b0df /* constant vector a */
|
||||
#define UPPER_MASK 0x80000000 /* most significant w-r bits */
|
||||
#define LOWER_MASK 0x7fffffff /* least significant r bits */
|
||||
|
||||
/* Tempering parameters */
|
||||
#define TEMPERING_MASK_B 0x9d2c5680
|
||||
#define TEMPERING_MASK_C 0xefc60000
|
||||
#define TEMPERING_SHIFT_U(y) (y >> 11)
|
||||
#define TEMPERING_SHIFT_S(y) (y << 7)
|
||||
#define TEMPERING_SHIFT_T(y) (y << 15)
|
||||
#define TEMPERING_SHIFT_L(y) (y >> 18)
|
||||
|
||||
static unsigned long mt[MP3_DITH_N]; /* the array for the state vector */
|
||||
static int mti=MP3_DITH_N+1; /* mti==MP3_DITH_N+1 means mt[MP3_DITH_N] is not initialized */
|
||||
|
||||
/* initializing the array with a NONZERO seed */
|
||||
void sgenrand(unsigned long seed)
|
||||
{
|
||||
/* setting initial seeds to mt[MP3_DITH_N] using */
|
||||
/* the generator Line 25 of Table 1 in */
|
||||
/* [KNUTH 1981, The Art of Computer Programming */
|
||||
/* Vol. 2 (2nd Ed.), pp102] */
|
||||
mt[0]= seed & 0xffffffff;
|
||||
for (mti=1; mti<MP3_DITH_N; mti++)
|
||||
mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
|
||||
}
|
||||
|
||||
unsigned long genrand(void)
|
||||
{
|
||||
unsigned long y;
|
||||
static unsigned long mag01[2]={0x0, MATRIX_A};
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
if (mti >= MP3_DITH_N) { /* generate MP3_DITH_N words at one time */
|
||||
int kk;
|
||||
|
||||
if (mti == MP3_DITH_N+1) /* if sgenrand() has not been called, */
|
||||
sgenrand(4357); /* a default initial seed is used */
|
||||
|
||||
for (kk=0;kk<MP3_DITH_N-MP3_DITH_M;kk++) {
|
||||
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
|
||||
mt[kk] = mt[kk+MP3_DITH_M] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
for (;kk<MP3_DITH_N-1;kk++) {
|
||||
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
|
||||
mt[kk] = mt[kk+(MP3_DITH_M-MP3_DITH_N)] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
y = (mt[MP3_DITH_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
|
||||
mt[MP3_DITH_N-1] = mt[MP3_DITH_M-1] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
|
||||
mti = 0;
|
||||
}
|
||||
|
||||
y = mt[mti++];
|
||||
y ^= TEMPERING_SHIFT_U(y);
|
||||
y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
|
||||
y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
|
||||
y ^= TEMPERING_SHIFT_L(y);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
long triangular_dither_noise(int nbits) {
|
||||
// parameter nbits : the peak-to-peak amplitude desired (in bits)
|
||||
// use with nbits set to 2 + nber of bits to be trimmed.
|
||||
// (because triangular is made from two uniformly distributed processes,
|
||||
// it starts at 2 bits peak-to-peak amplitude)
|
||||
// see The Theory of Dithered Quantization by Robert Alexander Wannamaker
|
||||
// for complete proof of why that's optimal
|
||||
|
||||
long v = (genrand()/2 - genrand()/2); // in ]-2^31, 2^31[
|
||||
//int signe = (v>0) ? 1 : -1;
|
||||
long P = 1 << (32 - nbits); // the power of 2
|
||||
v /= P;
|
||||
// now v in ]-2^(nbits-1), 2^(nbits-1) [
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif // MP3_DITHERING
|
||||
|
||||
/************************ decoder functions ***************************/
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_Scale
|
||||
|
||||
Converts the signal to 16 bit LE-PCM data and does dithering.
|
||||
|
||||
- borrowed from xmms-mad plugin source.
|
||||
=================
|
||||
*/
|
||||
|
||||
/*
|
||||
* xmms-mad - mp3 plugin for xmms
|
||||
* Copyright (C) 2001-2002 Sam Clegg
|
||||
*/
|
||||
|
||||
signed int S_MP3_Scale(mad_fixed_t sample)
|
||||
{
|
||||
int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16;
|
||||
#ifdef MP3_DITHERING
|
||||
int dither;
|
||||
#endif
|
||||
|
||||
// round
|
||||
sample += (1L << (n_bits_to_loose - 1));
|
||||
|
||||
#ifdef MP3_DITHERING
|
||||
dither = triangular_dither_noise(n_bits_to_loose + 1);
|
||||
sample += dither;
|
||||
#endif
|
||||
|
||||
/* clip */
|
||||
if (sample >= MAD_F_ONE)
|
||||
sample = MAD_F_ONE - 1;
|
||||
else if (sample < -MAD_F_ONE)
|
||||
sample = -MAD_F_ONE;
|
||||
|
||||
/* quantize */
|
||||
return sample >> n_bits_to_loose;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_PCMCopy
|
||||
|
||||
Copy and convert pcm data until bytecount bytes have been written.
|
||||
return the position in pcm->samples.
|
||||
indicate the amount of actually written bytes in wrotecnt.
|
||||
=================
|
||||
*/
|
||||
|
||||
int S_MP3_PCMCopy(byte *buf, struct mad_pcm *pcm, int bufofs,
|
||||
int sampleofs, int bytecount, int *wrotecnt)
|
||||
{
|
||||
int written = 0;
|
||||
signed int sample;
|
||||
int framesize = pcm->channels * MP3_SAMPLE_WIDTH;
|
||||
|
||||
// add new pcm data.
|
||||
while(written < bytecount && sampleofs < pcm->length)
|
||||
{
|
||||
sample = S_MP3_Scale(pcm->samples[0][sampleofs]);
|
||||
|
||||
#ifdef Q3_BIG_ENDIAN
|
||||
// output to 16 bit big endian PCM
|
||||
buf[bufofs++] = (sample >> 8) & 0xff;
|
||||
buf[bufofs++] = sample & 0xff;
|
||||
#else
|
||||
// output to 16 bit little endian PCM
|
||||
buf[bufofs++] = sample & 0xff;
|
||||
buf[bufofs++] = (sample >> 8) & 0xff;
|
||||
#endif
|
||||
|
||||
if(pcm->channels == 2)
|
||||
{
|
||||
sample = S_MP3_Scale(pcm->samples[1][sampleofs]);
|
||||
|
||||
#ifdef Q3_BIG_ENDIAN
|
||||
buf[bufofs++] = (sample >> 8) & 0xff;
|
||||
buf[bufofs++] = sample & 0xff;
|
||||
#else
|
||||
buf[bufofs++] = sample & 0xff;
|
||||
buf[bufofs++] = (sample >> 8) & 0xff;
|
||||
#endif
|
||||
}
|
||||
|
||||
sampleofs++;
|
||||
written += framesize;
|
||||
}
|
||||
|
||||
if(wrotecnt)
|
||||
*wrotecnt = written;
|
||||
|
||||
return sampleofs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_Decode
|
||||
=================
|
||||
*/
|
||||
|
||||
// gets executed for every decoded frame.
|
||||
int S_MP3_Decode(snd_stream_t *stream)
|
||||
{
|
||||
struct snd_codec_mp3_info *mp3info;
|
||||
struct mad_stream *madstream;
|
||||
struct mad_frame *madframe;
|
||||
struct mad_synth *madsynth;
|
||||
struct mad_pcm *pcm;
|
||||
int cursize;
|
||||
int samplecount;
|
||||
int needcount;
|
||||
int wrote;
|
||||
int retval;
|
||||
|
||||
if(!stream)
|
||||
return -1;
|
||||
|
||||
mp3info = stream->ptr;
|
||||
madstream = &mp3info->madstream;
|
||||
madframe = &mp3info->madframe;
|
||||
|
||||
if(mad_frame_decode(madframe, madstream))
|
||||
{
|
||||
if(madstream->error == MAD_ERROR_BUFLEN)
|
||||
{
|
||||
// we need more data. Read another chunk.
|
||||
retval = S_MP3_ReadData(stream, madstream, mp3info->encbuf, sizeof(mp3info->encbuf));
|
||||
|
||||
// call myself again now that buffer is full.
|
||||
if(retval > 0)
|
||||
retval = S_MP3_Decode(stream);
|
||||
}
|
||||
else if(MAD_RECOVERABLE(madstream->error))
|
||||
{
|
||||
mad_stream_skip(madstream, madstream->skiplen);
|
||||
return S_MP3_Decode(stream);
|
||||
}
|
||||
else
|
||||
retval = -1;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// check whether this really is an mp3
|
||||
if(madframe->header.layer != MAD_LAYER_III)
|
||||
return -1;
|
||||
|
||||
// generate pcm data
|
||||
madsynth = &mp3info->madsynth;
|
||||
mad_synth_frame(madsynth, madframe);
|
||||
|
||||
pcm = &madsynth->pcm;
|
||||
|
||||
// perform a few checks to see whether something changed that shouldn't.
|
||||
|
||||
if(stream->info.rate != pcm->samplerate ||
|
||||
stream->info.channels != pcm->channels)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// see whether we have got enough data now.
|
||||
cursize = pcm->length * pcm->channels * stream->info.width;
|
||||
needcount = mp3info->destsize - mp3info->destlen;
|
||||
|
||||
// Copy exactly as many samples as required.
|
||||
samplecount = S_MP3_PCMCopy(mp3info->dest, pcm,
|
||||
mp3info->destlen, 0, needcount, &wrote);
|
||||
mp3info->destlen += wrote;
|
||||
|
||||
if(samplecount < pcm->length)
|
||||
{
|
||||
// Not all samples got copied. Copy the rest into the pcm buffer.
|
||||
samplecount = S_MP3_PCMCopy(mp3info->pcmbuf, pcm,
|
||||
mp3info->buflen,
|
||||
samplecount,
|
||||
mp3info->pcmbufsize - mp3info->buflen,
|
||||
&wrote);
|
||||
mp3info->buflen += wrote;
|
||||
|
||||
|
||||
if(samplecount < pcm->length)
|
||||
{
|
||||
// The pcm buffer was not large enough. Make it bigger.
|
||||
byte *newbuf = Z_Malloc(cursize);
|
||||
|
||||
if(mp3info->pcmbuf)
|
||||
{
|
||||
memcpy(newbuf, mp3info->pcmbuf, mp3info->buflen);
|
||||
Z_Free(mp3info->pcmbuf);
|
||||
}
|
||||
|
||||
mp3info->pcmbuf = newbuf;
|
||||
mp3info->pcmbufsize = cursize;
|
||||
|
||||
samplecount = S_MP3_PCMCopy(mp3info->pcmbuf, pcm,
|
||||
mp3info->buflen,
|
||||
samplecount,
|
||||
mp3info->pcmbufsize - mp3info->buflen,
|
||||
&wrote);
|
||||
mp3info->buflen += wrote;
|
||||
}
|
||||
|
||||
// we're definitely done.
|
||||
retval = 0;
|
||||
}
|
||||
else if(mp3info->destlen >= mp3info->destsize)
|
||||
retval = 0;
|
||||
else
|
||||
retval = 1;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*************** Callback functions for quake3 ***************/
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_CodecOpenStream
|
||||
=================
|
||||
*/
|
||||
|
||||
snd_stream_t *S_MP3_CodecOpenStream(const char *filename)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
struct snd_codec_mp3_info *mp3info;
|
||||
|
||||
// Open the stream
|
||||
stream = S_CodecUtilOpen(filename, &mp3_codec);
|
||||
if(!stream || stream->length <= 0)
|
||||
return NULL;
|
||||
|
||||
// We have to scan through the MP3 to determine the important mp3 info.
|
||||
if(S_MP3_Scanfile(stream) < 0)
|
||||
{
|
||||
// scanning didn't work out...
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Initialize the mp3 info structure we need for streaming
|
||||
mp3info = Z_Malloc(sizeof(*mp3info));
|
||||
if(!mp3info)
|
||||
{
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->ptr = mp3info;
|
||||
|
||||
// initialize the libmad control structures.
|
||||
mad_stream_init(&mp3info->madstream);
|
||||
mad_frame_init(&mp3info->madframe);
|
||||
mad_synth_init(&mp3info->madsynth);
|
||||
|
||||
if(S_MP3_ReadData(stream, &mp3info->madstream, mp3info->encbuf, sizeof(mp3info->encbuf)) <= 0)
|
||||
{
|
||||
// we didnt read anything, that's bad.
|
||||
S_MP3_CodecCloseStream(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_CodecCloseStream
|
||||
=================
|
||||
*/
|
||||
|
||||
// free all memory we allocated.
|
||||
void S_MP3_CodecCloseStream(snd_stream_t *stream)
|
||||
{
|
||||
struct snd_codec_mp3_info *mp3info;
|
||||
|
||||
if(!stream)
|
||||
return;
|
||||
|
||||
// free all data in our mp3info tree
|
||||
|
||||
if(stream->ptr)
|
||||
{
|
||||
mp3info = stream->ptr;
|
||||
|
||||
if(mp3info->pcmbuf)
|
||||
Z_Free(mp3info->pcmbuf);
|
||||
|
||||
mad_synth_finish(&mp3info->madsynth);
|
||||
mad_frame_finish(&mp3info->madframe);
|
||||
mad_stream_finish(&mp3info->madstream);
|
||||
|
||||
Z_Free(stream->ptr);
|
||||
}
|
||||
|
||||
S_CodecUtilClose(&stream);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_MP3_CodecReadStream
|
||||
=================
|
||||
*/
|
||||
int S_MP3_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
|
||||
{
|
||||
struct snd_codec_mp3_info *mp3info;
|
||||
int retval;
|
||||
|
||||
if(!stream)
|
||||
return -1;
|
||||
|
||||
mp3info = stream->ptr;
|
||||
|
||||
// Make sure we get complete frames all the way through.
|
||||
bytes -= bytes % (stream->info.channels * stream->info.width);
|
||||
|
||||
if(mp3info->buflen)
|
||||
{
|
||||
if(bytes < mp3info->buflen)
|
||||
{
|
||||
// we still have enough bytes in our decoded pcm buffer
|
||||
memcpy(buffer, mp3info->pcmbuf, bytes);
|
||||
|
||||
// remove the portion from our buffer.
|
||||
mp3info->buflen -= bytes;
|
||||
memmove(mp3info->pcmbuf, &mp3info->pcmbuf[bytes], mp3info->buflen);
|
||||
return bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy over the samples we already have.
|
||||
memcpy(buffer, mp3info->pcmbuf, mp3info->buflen);
|
||||
mp3info->destlen = mp3info->buflen;
|
||||
mp3info->buflen = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
mp3info->destlen = 0;
|
||||
|
||||
mp3info->dest = buffer;
|
||||
mp3info->destsize = bytes;
|
||||
|
||||
do
|
||||
{
|
||||
retval = S_MP3_Decode(stream);
|
||||
} while(retval > 0);
|
||||
|
||||
// if there was an error return nothing.
|
||||
if(retval < 0)
|
||||
return 0;
|
||||
|
||||
return mp3info->destlen;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================================================
|
||||
S_MP3_CodecLoad
|
||||
|
||||
We handle S_MP3_CodecLoad as a special case of the streaming functions
|
||||
where we read the whole stream at once.
|
||||
======================================================================
|
||||
*/
|
||||
void *S_MP3_CodecLoad(const char *filename, snd_info_t *info)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
byte *pcmbuffer;
|
||||
|
||||
// check if input is valid
|
||||
if(!filename)
|
||||
return NULL;
|
||||
|
||||
stream = S_MP3_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->dataofs = stream->info.dataofs;
|
||||
|
||||
// allocate enough buffer for all pcm data
|
||||
// pcmbuffer = Z_Malloc(stream->info.size);
|
||||
pcmbuffer = Hunk_AllocateTempMemory(stream->info.size);
|
||||
|
||||
if(!pcmbuffer)
|
||||
{
|
||||
S_MP3_CodecCloseStream(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info->size = S_MP3_CodecReadStream(stream, stream->info.size, pcmbuffer);
|
||||
|
||||
if(info->size <= 0)
|
||||
{
|
||||
// we didn't read anything at all. darn.
|
||||
// Z_Free(pcmbuffer);
|
||||
Hunk_FreeTempMemory(pcmbuffer);
|
||||
pcmbuffer = NULL;
|
||||
}
|
||||
|
||||
S_MP3_CodecCloseStream(stream);
|
||||
|
||||
return pcmbuffer;
|
||||
}
|
||||
#endif
|
||||
@@ -1,475 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// OGG support is enabled by this define
|
||||
#ifdef USE_CODEC_VORBIS
|
||||
|
||||
// includes for the Q3 sound system
|
||||
#include "client.h"
|
||||
#include "snd_codec.h"
|
||||
|
||||
// includes for the OGG codec
|
||||
#include <errno.h>
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#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 :
|
||||
{
|
||||
// set the file position in the actual file with the Q3 function
|
||||
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_END);
|
||||
|
||||
// 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 = Hunk_AllocateTempMemory(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)
|
||||
{
|
||||
Hunk_FreeTempMemory(buffer);
|
||||
S_OGG_CodecCloseStream(stream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S_OGG_CodecCloseStream(stream);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endif // USE_CODEC_VORBIS
|
||||
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// Ogg Opus support is enabled by this define
|
||||
#ifdef USE_CODEC_OPUS
|
||||
|
||||
// includes for the Q3 sound system
|
||||
#include "client.h"
|
||||
#include "snd_codec.h"
|
||||
|
||||
// includes for the Ogg Opus codec
|
||||
#include <errno.h>
|
||||
#include <opusfile.h>
|
||||
|
||||
// samples are 16 bit
|
||||
#define OPUS_SAMPLEWIDTH 2
|
||||
|
||||
// Q3 Ogg Opus codec
|
||||
snd_codec_t opus_codec =
|
||||
{
|
||||
"opus",
|
||||
S_OggOpus_CodecLoad,
|
||||
S_OggOpus_CodecOpenStream,
|
||||
S_OggOpus_CodecReadStream,
|
||||
S_OggOpus_CodecCloseStream,
|
||||
NULL
|
||||
};
|
||||
|
||||
// callbacks for opusfile
|
||||
|
||||
// fread() replacement
|
||||
int S_OggOpus_Callback_read(void *datasource, unsigned char *ptr, int size )
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
int bytesRead = 0;
|
||||
|
||||
// check if input is valid
|
||||
if(!ptr)
|
||||
{
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!size)
|
||||
{
|
||||
// It's not an error, caller just wants zero bytes!
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!datasource)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// we use a snd_stream_t in the generic pointer to pass around
|
||||
stream = (snd_stream_t *) datasource;
|
||||
|
||||
// read it with the Q3 function FS_Read()
|
||||
bytesRead = FS_Read(ptr, size, stream->file);
|
||||
|
||||
// update the file position
|
||||
stream->pos += bytesRead;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
// fseek() replacement
|
||||
int S_OggOpus_Callback_seek(void *datasource, opus_int64 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 :
|
||||
{
|
||||
// set the file position in the actual file with the Q3 function
|
||||
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_END);
|
||||
|
||||
// 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_OggOpus_Callback_close(void *datasource)
|
||||
{
|
||||
// we do nothing here and close all things manually in S_OggOpus_CodecCloseStream()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ftell() replacement
|
||||
opus_int64 S_OggOpus_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 (opus_int64) FS_FTell(stream->file);
|
||||
}
|
||||
|
||||
// the callback structure
|
||||
const OpusFileCallbacks S_OggOpus_Callbacks =
|
||||
{
|
||||
&S_OggOpus_Callback_read,
|
||||
&S_OggOpus_Callback_seek,
|
||||
&S_OggOpus_Callback_tell,
|
||||
&S_OggOpus_Callback_close
|
||||
};
|
||||
|
||||
/*
|
||||
=================
|
||||
S_OggOpus_CodecOpenStream
|
||||
=================
|
||||
*/
|
||||
snd_stream_t *S_OggOpus_CodecOpenStream(const char *filename)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
|
||||
// Opus codec control structure
|
||||
OggOpusFile *of;
|
||||
|
||||
// some variables used to get informations about the file
|
||||
const OpusHead *opusInfo;
|
||||
ogg_int64_t numSamples;
|
||||
|
||||
// check if input is valid
|
||||
if(!filename)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open the stream
|
||||
stream = S_CodecUtilOpen(filename, &opus_codec);
|
||||
if(!stream)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// open the codec with our callbacks and stream as the generic pointer
|
||||
of = op_open_callbacks(stream, &S_OggOpus_Callbacks, NULL, 0, NULL );
|
||||
if (!of)
|
||||
{
|
||||
S_CodecUtilClose(&stream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// the stream must be seekable
|
||||
if(!op_seekable(of))
|
||||
{
|
||||
op_free(of);
|
||||
|
||||
S_CodecUtilClose(&stream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the info about channels and rate
|
||||
opusInfo = op_head(of, -1);
|
||||
if(!opusInfo)
|
||||
{
|
||||
op_free(of);
|
||||
|
||||
S_CodecUtilClose(&stream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(opusInfo->stream_count != 1)
|
||||
{
|
||||
op_free(of);
|
||||
|
||||
S_CodecUtilClose(&stream);
|
||||
|
||||
Com_Printf("Only Ogg Opus files with one stream are support\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(opusInfo->channel_count != 1 && opusInfo->channel_count != 2)
|
||||
{
|
||||
op_free(of);
|
||||
|
||||
S_CodecUtilClose(&stream);
|
||||
|
||||
Com_Printf("Only mono and stereo Ogg Opus files are supported\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the number of sample-frames in the file
|
||||
numSamples = op_pcm_total(of, -1);
|
||||
|
||||
// fill in the info-structure in the stream
|
||||
stream->info.rate = 48000;
|
||||
stream->info.width = OPUS_SAMPLEWIDTH;
|
||||
stream->info.channels = opusInfo->channel_count;
|
||||
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 opus codec control structure
|
||||
stream->ptr = of;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_OggOpus_CodecCloseStream
|
||||
=================
|
||||
*/
|
||||
void S_OggOpus_CodecCloseStream(snd_stream_t *stream)
|
||||
{
|
||||
// check if input is valid
|
||||
if(!stream)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// let the opus codec cleanup its stuff
|
||||
op_free((OggOpusFile *) stream->ptr);
|
||||
|
||||
// close the stream
|
||||
S_CodecUtilClose(&stream);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_OggOpus_CodecReadStream
|
||||
=================
|
||||
*/
|
||||
int S_OggOpus_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
|
||||
{
|
||||
// buffer handling
|
||||
int samplesRead, samplesLeft, c;
|
||||
opus_int16 *bufPtr;
|
||||
|
||||
// check if input is valid
|
||||
if(!(stream && buffer))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(bytes <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
samplesRead = 0;
|
||||
samplesLeft = bytes / stream->info.channels / stream->info.width;
|
||||
bufPtr = buffer;
|
||||
|
||||
if(samplesLeft <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cycle until we have the requested or all available bytes read
|
||||
while(-1)
|
||||
{
|
||||
// read some samples from the opus codec
|
||||
c = op_read((OggOpusFile *) stream->ptr, bufPtr + samplesRead * stream->info.channels, samplesLeft * stream->info.channels, NULL);
|
||||
|
||||
// no more samples are left
|
||||
if(c <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
samplesRead += c;
|
||||
samplesLeft -= c;
|
||||
|
||||
// we have enough samples
|
||||
if(samplesLeft <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return samplesRead * stream->info.channels * stream->info.width;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================================================
|
||||
S_OggOpus_CodecLoad
|
||||
|
||||
We handle S_OggOpus_CodecLoad as a special case of the streaming functions
|
||||
where we read the whole stream at once.
|
||||
======================================================================
|
||||
*/
|
||||
void *S_OggOpus_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_OggOpus_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 = Hunk_AllocateTempMemory(info->size);
|
||||
if(!buffer)
|
||||
{
|
||||
S_OggOpus_CodecCloseStream(stream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// fill the buffer
|
||||
bytesRead = S_OggOpus_CodecReadStream(stream, info->size, buffer);
|
||||
|
||||
// we don't even have read a single byte
|
||||
if(bytesRead <= 0)
|
||||
{
|
||||
Hunk_FreeTempMemory(buffer);
|
||||
S_OggOpus_CodecCloseStream(stream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S_OggOpus_CodecCloseStream(stream);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endif // USE_CODEC_OPUS
|
||||
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 "client.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 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
|
||||
FGetLittleShort(file); // wav_format
|
||||
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, qtrue);
|
||||
if(!file)
|
||||
{
|
||||
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 = Hunk_AllocateTempMemory(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;
|
||||
}
|
||||
+789
-438
File diff suppressed because it is too large.
Load diff
+24
-87
@@ -15,14 +15,14 @@ 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
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// snd_local.h -- private sound definations
|
||||
|
||||
|
||||
#include "../qcommon/q_shared.h"
|
||||
#include "q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "snd_public.h"
|
||||
|
||||
@@ -30,9 +30,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
#define PAINTBUFFER_SIZE 4096 // this is in samples
|
||||
|
||||
#define SND_CHUNK_SIZE 1024 // samples
|
||||
@@ -63,7 +60,6 @@ typedef struct sfx_s {
|
||||
qboolean soundCompressed; // not in Memory
|
||||
int soundCompressionMethod;
|
||||
int soundLength;
|
||||
int soundChannels;
|
||||
char soundName[MAX_QPATH];
|
||||
int lastTimeUsed;
|
||||
struct sfx_s *next;
|
||||
@@ -72,20 +68,14 @@ typedef struct sfx_s {
|
||||
typedef struct {
|
||||
int channels;
|
||||
int samples; // mono samples in buffer
|
||||
int fullsamples; // samples with all channels in buffer (samples divided by channels)
|
||||
int submission_chunk; // don't mix less than this #
|
||||
int samplebits;
|
||||
int isfloat;
|
||||
int speed;
|
||||
byte *buffer;
|
||||
} dma_t;
|
||||
|
||||
#define START_SAMPLE_IMMEDIATE 0x7fffffff
|
||||
|
||||
#define MAX_DOPPLER_SCALE 50.0f //arbitrary
|
||||
|
||||
#define THIRD_PERSON_THRESHOLD_SQ (48.0f*48.0f)
|
||||
|
||||
typedef struct loopSound_s {
|
||||
vec3_t origin;
|
||||
vec3_t velocity;
|
||||
@@ -114,7 +104,6 @@ typedef struct
|
||||
qboolean fixed_origin; // use origin instead of fetching entnum's origin
|
||||
sfx_t *thesfx; // sfx structure
|
||||
qboolean doppler;
|
||||
qboolean fullVolume;
|
||||
} channel_t;
|
||||
|
||||
|
||||
@@ -130,38 +119,6 @@ typedef struct {
|
||||
int dataofs; // chunk starts this many bytes from file start
|
||||
} wavinfo_t;
|
||||
|
||||
// Interface between Q3 sound "api" and the sound backend
|
||||
typedef struct
|
||||
{
|
||||
void (*Shutdown)(void);
|
||||
void (*StartSound)(const vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
|
||||
void (*StartLocalSound)( sfxHandle_t sfx, int channelNum );
|
||||
void (*StartBackgroundTrack)( const char *intro, const char *loop );
|
||||
void (*StopBackgroundTrack)( void );
|
||||
void (*RawSamples)(int stream, int samples, int rate, int width, int channels, const byte *data, float volume, int entityNum);
|
||||
void (*StopAllSounds)( void );
|
||||
void (*ClearLoopingSounds)( qboolean killall );
|
||||
void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
|
||||
void (*AddRealLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
|
||||
void (*StopLoopingSound)(int entityNum );
|
||||
void (*Respatialize)( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
|
||||
void (*UpdateEntityPosition)( int entityNum, const vec3_t origin );
|
||||
void (*Update)( void );
|
||||
void (*DisableSounds)( void );
|
||||
void (*BeginRegistration)( void );
|
||||
sfxHandle_t (*RegisterSound)( const char *sample, qboolean compressed );
|
||||
void (*ClearSoundBuffer)( void );
|
||||
void (*SoundInfo)( void );
|
||||
void (*SoundList)( void );
|
||||
#ifdef USE_VOIP
|
||||
void (*StartCapture)( void );
|
||||
int (*AvailableCaptureSamples)( void );
|
||||
void (*Capture)( int samples, byte *data );
|
||||
void (*StopCapture)( void );
|
||||
void (*MasterGain)( float gain );
|
||||
#endif
|
||||
} soundInterface_t;
|
||||
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
@@ -184,51 +141,43 @@ void SNDDMA_BeginPainting (void);
|
||||
|
||||
void SNDDMA_Submit(void);
|
||||
|
||||
#ifdef USE_VOIP
|
||||
void SNDDMA_StartCapture(void);
|
||||
int SNDDMA_AvailableCaptureSamples(void);
|
||||
void SNDDMA_Capture(int samples, byte *data);
|
||||
void SNDDMA_StopCapture(void);
|
||||
void SNDDMA_MasterGain(float val);
|
||||
#endif
|
||||
|
||||
|
||||
//====================================================================
|
||||
|
||||
#define MAX_SOUNDCHANNELS 96
|
||||
#define MAX_CHANNELS 96
|
||||
|
||||
extern channel_t s_channels[MAX_SOUNDCHANNELS];
|
||||
extern channel_t loop_channels[MAX_SOUNDCHANNELS];
|
||||
extern channel_t s_channels[MAX_CHANNELS];
|
||||
extern channel_t loop_channels[MAX_CHANNELS];
|
||||
extern int numLoopChannels;
|
||||
|
||||
extern int s_paintedtime;
|
||||
extern int s_rawend;
|
||||
extern vec3_t listener_forward;
|
||||
extern vec3_t listener_right;
|
||||
extern vec3_t listener_up;
|
||||
extern dma_t dma;
|
||||
|
||||
#define MAX_RAW_SAMPLES 16384
|
||||
#define MAX_RAW_STREAMS (MAX_CLIENTS * 2 + 1)
|
||||
extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
|
||||
extern int s_rawend[MAX_RAW_STREAMS];
|
||||
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
|
||||
|
||||
extern cvar_t *s_volume;
|
||||
extern cvar_t *s_musicVolume;
|
||||
extern cvar_t *s_muted;
|
||||
extern cvar_t *s_doppler;
|
||||
extern cvar_t *s_volume;
|
||||
extern cvar_t *s_nosound;
|
||||
extern cvar_t *s_khz;
|
||||
extern cvar_t *s_show;
|
||||
extern cvar_t *s_mixahead;
|
||||
|
||||
extern cvar_t *s_testsound;
|
||||
extern cvar_t *s_testsound;
|
||||
extern cvar_t *s_separation;
|
||||
|
||||
qboolean S_LoadSound( sfx_t *sfx );
|
||||
|
||||
void SND_free(sndBuffer *v);
|
||||
sndBuffer* SND_malloc( void );
|
||||
void SND_setup( void );
|
||||
void SND_shutdown(void);
|
||||
sndBuffer* SND_malloc();
|
||||
void SND_setup();
|
||||
|
||||
void S_PaintChannels(int endtime);
|
||||
|
||||
void S_memoryLoad(sfx_t *sfx);
|
||||
portable_samplepair_t *S_GetRawSamplePointer();
|
||||
|
||||
// spatializes a channel
|
||||
void S_Spatialize(channel_t *ch);
|
||||
@@ -243,7 +192,7 @@ void S_AdpcmGetSamples(sndBuffer *chunk, short *to);
|
||||
#define SENTINEL_MULAW_ZERO_RUN 127
|
||||
#define SENTINEL_MULAW_FOUR_BIT_RUN 126
|
||||
|
||||
void S_FreeOldestSound( void );
|
||||
void S_FreeOldestSound();
|
||||
|
||||
#define NXStream byte
|
||||
|
||||
@@ -257,24 +206,12 @@ extern short *sfxScratchBuffer;
|
||||
extern sfx_t *sfxScratchPointer;
|
||||
extern int sfxScratchIndex;
|
||||
|
||||
qboolean S_Base_Init( soundInterface_t *si );
|
||||
|
||||
// OpenAL stuff
|
||||
typedef enum
|
||||
{
|
||||
SRCPRI_AMBIENT = 0, // Ambient sound effects
|
||||
SRCPRI_ENTITY, // Entity sound effects
|
||||
SRCPRI_ONESHOT, // One-shot sounds
|
||||
SRCPRI_LOCAL, // Local sounds
|
||||
SRCPRI_STREAM // Streams (music, cutscenes)
|
||||
} alSrcPriority_t;
|
||||
|
||||
typedef int srcHandle_t;
|
||||
|
||||
qboolean S_AL_Init( soundInterface_t *si );
|
||||
|
||||
#ifdef idppc_altivec
|
||||
void S_PaintChannelFrom16_altivec( portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE], int snd_vol, channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset );
|
||||
#ifdef __linux__
|
||||
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
|
||||
// custom Snd_Memset implementation for glibc memset bug workaround
|
||||
void Snd_Memset(void* dest, const int val, const size_t count);
|
||||
#else
|
||||
#define Snd_Memset Com_Memset
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -22,18 +22,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "snd_codec.h"
|
||||
#include "snd_local.h"
|
||||
#include "snd_public.h"
|
||||
|
||||
cvar_t *s_volume;
|
||||
cvar_t *s_muted;
|
||||
cvar_t *s_musicVolume;
|
||||
cvar_t *s_doppler;
|
||||
cvar_t *s_backend;
|
||||
cvar_t *s_muteWhenMinimized;
|
||||
cvar_t *s_muteWhenUnfocused;
|
||||
|
||||
#if 0
|
||||
cvar_t* s_volume;
|
||||
cvar_t* s_musicVolume;
|
||||
cvar_t* s_doppler;
|
||||
|
||||
static soundInterface_t si;
|
||||
|
||||
/*
|
||||
@@ -332,13 +333,11 @@ S_RegisterSound
|
||||
*/
|
||||
sfxHandle_t S_RegisterSound(const char *sample, qboolean compressed, qboolean streamed)
|
||||
{
|
||||
/*
|
||||
if( si.RegisterSound ) {
|
||||
return si.RegisterSound( sample, compressed );
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
// FIXME: infinite loop
|
||||
}
|
||||
|
||||
@@ -889,3 +888,4 @@ void S_FadeSound(float fTime)
|
||||
{
|
||||
// FIXME: stub
|
||||
}
|
||||
#endif
|
||||
+196
-66
@@ -15,7 +15,7 @@ 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
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
@@ -30,7 +30,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*****************************************************************************/
|
||||
|
||||
#include "snd_local.h"
|
||||
#include "snd_codec.h"
|
||||
|
||||
#define DEF_COMSOUNDMEGS "8"
|
||||
|
||||
@@ -57,7 +56,7 @@ void SND_free(sndBuffer *v) {
|
||||
inUse += sizeof(sndBuffer);
|
||||
}
|
||||
|
||||
sndBuffer* SND_malloc(void) {
|
||||
sndBuffer* SND_malloc() {
|
||||
sndBuffer *v;
|
||||
redo:
|
||||
if (freelist == NULL) {
|
||||
@@ -74,7 +73,7 @@ redo:
|
||||
return v;
|
||||
}
|
||||
|
||||
void SND_setup(void) {
|
||||
void SND_setup() {
|
||||
sndBuffer *p, *q;
|
||||
cvar_t *cv;
|
||||
int scs;
|
||||
@@ -100,12 +99,137 @@ void SND_setup(void) {
|
||||
Com_Printf("Sound memory manager started\n");
|
||||
}
|
||||
|
||||
void SND_shutdown(void)
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
WAV loading
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static byte *data_p;
|
||||
static byte *iff_end;
|
||||
static byte *last_chunk;
|
||||
static byte *iff_data;
|
||||
static int iff_chunk_len;
|
||||
|
||||
static short GetLittleShort(void)
|
||||
{
|
||||
free(sfxScratchBuffer);
|
||||
free(buffer);
|
||||
short val = 0;
|
||||
val = *data_p;
|
||||
val = val + (*(data_p+1)<<8);
|
||||
data_p += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
static int GetLittleLong(void)
|
||||
{
|
||||
int val = 0;
|
||||
val = *data_p;
|
||||
val = val + (*(data_p+1)<<8);
|
||||
val = val + (*(data_p+2)<<16);
|
||||
val = val + (*(data_p+3)<<24);
|
||||
data_p += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void FindNextChunk(char *name)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
data_p=last_chunk;
|
||||
|
||||
if (data_p >= iff_end)
|
||||
{ // didn't find the chunk
|
||||
data_p = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
data_p += 4;
|
||||
iff_chunk_len = GetLittleLong();
|
||||
if (iff_chunk_len < 0)
|
||||
{
|
||||
data_p = NULL;
|
||||
return;
|
||||
}
|
||||
data_p -= 8;
|
||||
last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
|
||||
if (!strncmp((char *)data_p, name, 4))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void FindChunk(char *name)
|
||||
{
|
||||
last_chunk = iff_data;
|
||||
FindNextChunk (name);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
GetWavinfo
|
||||
============
|
||||
*/
|
||||
static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
|
||||
{
|
||||
wavinfo_t info;
|
||||
|
||||
Com_Memset (&info, 0, sizeof(info));
|
||||
|
||||
if (!wav)
|
||||
return info;
|
||||
|
||||
iff_data = wav;
|
||||
iff_end = wav + wavlength;
|
||||
|
||||
// find "RIFF" chunk
|
||||
FindChunk("RIFF");
|
||||
if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
|
||||
{
|
||||
Com_Printf("Missing RIFF/WAVE chunks\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
// get "fmt " chunk
|
||||
iff_data = data_p + 12;
|
||||
// DumpChunks ();
|
||||
|
||||
FindChunk("fmt ");
|
||||
if (!data_p)
|
||||
{
|
||||
Com_Printf("Missing fmt chunk\n");
|
||||
return info;
|
||||
}
|
||||
data_p += 8;
|
||||
info.format = GetLittleShort();
|
||||
info.channels = GetLittleShort();
|
||||
info.rate = GetLittleLong();
|
||||
data_p += 4+2;
|
||||
info.width = GetLittleShort() / 8;
|
||||
|
||||
if (info.format != 1)
|
||||
{
|
||||
Com_Printf("Microsoft PCM format only\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
// find data chunk
|
||||
FindChunk("data");
|
||||
if (!data_p)
|
||||
{
|
||||
Com_Printf("Missing data chunk\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
data_p += 4;
|
||||
info.samples = GetLittleLong () / info.width;
|
||||
info.dataofs = data_p - wav;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
ResampleSfx
|
||||
@@ -113,53 +237,47 @@ ResampleSfx
|
||||
resample / decimate to the current source rate
|
||||
================
|
||||
*/
|
||||
static int ResampleSfx( sfx_t *sfx, int channels, int inrate, int inwidth, int samples, byte *data, qboolean compressed ) {
|
||||
static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
|
||||
int outcount;
|
||||
int srcsample;
|
||||
float stepscale;
|
||||
int i, j;
|
||||
int i;
|
||||
int sample, samplefrac, fracstep;
|
||||
int part;
|
||||
sndBuffer *chunk;
|
||||
|
||||
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
|
||||
|
||||
outcount = samples / stepscale;
|
||||
outcount = sfx->soundLength / stepscale;
|
||||
sfx->soundLength = outcount;
|
||||
|
||||
srcsample = 0;
|
||||
samplefrac = 0;
|
||||
fracstep = stepscale * 256 * channels;
|
||||
fracstep = stepscale * 256;
|
||||
chunk = sfx->soundData;
|
||||
|
||||
for (i=0 ; i<outcount ; i++)
|
||||
{
|
||||
srcsample += samplefrac >> 8;
|
||||
samplefrac &= 255;
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
for (j=0 ; j<channels ; j++)
|
||||
{
|
||||
if( inwidth == 2 ) {
|
||||
sample = ( ((short *)data)[srcsample+j] );
|
||||
if( inwidth == 2 ) {
|
||||
sample = LittleShort ( ((short *)data)[srcsample] );
|
||||
} else {
|
||||
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
|
||||
}
|
||||
part = (i&(SND_CHUNK_SIZE-1));
|
||||
if (part == 0) {
|
||||
sndBuffer *newchunk;
|
||||
newchunk = SND_malloc();
|
||||
if (chunk == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else {
|
||||
sample = (unsigned int)( (unsigned char)(data[srcsample+j]) - 128) << 8;
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
part = (i*channels+j)&(SND_CHUNK_SIZE-1);
|
||||
if (part == 0) {
|
||||
sndBuffer *newchunk;
|
||||
newchunk = SND_malloc();
|
||||
if (chunk == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
}
|
||||
|
||||
chunk->sndChunk[part] = sample;
|
||||
chunk = newchunk;
|
||||
}
|
||||
}
|
||||
|
||||
return outcount;
|
||||
chunk->sndChunk[part] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -169,39 +287,35 @@ ResampleSfx
|
||||
resample / decimate to the current source rate
|
||||
================
|
||||
*/
|
||||
static int ResampleSfxRaw( short *sfx, int channels, int inrate, int inwidth, int samples, byte *data ) {
|
||||
static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
|
||||
int outcount;
|
||||
int srcsample;
|
||||
float stepscale;
|
||||
int i, j;
|
||||
int i;
|
||||
int sample, samplefrac, fracstep;
|
||||
|
||||
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
|
||||
|
||||
outcount = samples / stepscale;
|
||||
|
||||
srcsample = 0;
|
||||
samplefrac = 0;
|
||||
fracstep = stepscale * 256 * channels;
|
||||
fracstep = stepscale * 256;
|
||||
|
||||
for (i=0 ; i<outcount ; i++)
|
||||
{
|
||||
srcsample += samplefrac >> 8;
|
||||
samplefrac &= 255;
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
for (j=0 ; j<channels ; j++)
|
||||
{
|
||||
if( inwidth == 2 ) {
|
||||
sample = LittleShort ( ((short *)data)[srcsample+j] );
|
||||
} else {
|
||||
sample = (int)( (unsigned char)(data[srcsample+j]) - 128) << 8;
|
||||
}
|
||||
sfx[i*channels+j] = sample;
|
||||
if( inwidth == 2 ) {
|
||||
sample = LittleShort ( ((short *)data)[srcsample] );
|
||||
} else {
|
||||
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
|
||||
}
|
||||
sfx[i] = sample;
|
||||
}
|
||||
return outcount;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
@@ -216,23 +330,40 @@ qboolean S_LoadSound( sfx_t *sfx )
|
||||
{
|
||||
byte *data;
|
||||
short *samples;
|
||||
snd_info_t info;
|
||||
// int size;
|
||||
wavinfo_t info;
|
||||
int size;
|
||||
|
||||
// player specific sounds are never directly loaded
|
||||
if ( sfx->soundName[0] == '*') {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// load it in
|
||||
data = S_CodecLoad(sfx->soundName, &info);
|
||||
if(!data)
|
||||
size = FS_ReadFile( sfx->soundName, (void **)&data );
|
||||
if ( !data ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
info = GetWavinfo( sfx->soundName, data, size );
|
||||
if ( info.channels != 1 ) {
|
||||
Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
|
||||
FS_FreeFile (data);
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( info.width == 1 ) {
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit audio file\n", sfx->soundName);
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
|
||||
}
|
||||
|
||||
if ( info.rate != 22050 ) {
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz audio file\n", sfx->soundName);
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
|
||||
}
|
||||
|
||||
samples = Hunk_AllocateTempMemory(info.channels * info.samples * sizeof(short) * 2);
|
||||
if (!info.samples) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
|
||||
|
||||
sfx->lastTimeUsed = Com_Milliseconds()+1;
|
||||
|
||||
@@ -242,37 +373,36 @@ qboolean S_LoadSound( sfx_t *sfx )
|
||||
// manager to do the right thing for us and page
|
||||
// sound in as needed
|
||||
|
||||
if( info.channels == 1 && sfx->soundCompressed == qtrue) {
|
||||
if( sfx->soundCompressed == qtrue) {
|
||||
sfx->soundCompressionMethod = 1;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.channels, info.rate, info.width, info.samples, data + info.dataofs );
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
S_AdpcmEncodeSound(sfx, samples);
|
||||
#if 0
|
||||
} else if (info.channels == 1 && info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
|
||||
} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
|
||||
sfx->soundCompressionMethod = 3;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.channels, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
encodeMuLaw( sfx, samples);
|
||||
} else if (info.channels == 1 && info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
|
||||
} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
|
||||
sfx->soundCompressionMethod = 2;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.channels, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
encodeWavelet( sfx, samples);
|
||||
#endif
|
||||
} else {
|
||||
sfx->soundCompressionMethod = 0;
|
||||
sfx->soundLength = info.samples;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfx( sfx, info.channels, info.rate, info.width, info.samples, data + info.dataofs, qfalse );
|
||||
ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
|
||||
}
|
||||
|
||||
sfx->soundChannels = info.channels;
|
||||
|
||||
Hunk_FreeTempMemory(samples);
|
||||
Hunk_FreeTempMemory(data);
|
||||
FS_FreeFile( data );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
void S_DisplayFreeMemory(void) {
|
||||
void S_DisplayFreeMemory() {
|
||||
Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
|
||||
}
|
||||
+182
-128
@@ -15,23 +15,24 @@ 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
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// snd_mix.c -- portable code to mix sounds for snd_dma.c
|
||||
|
||||
#include "client.h"
|
||||
#include "snd_local.h"
|
||||
|
||||
static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
|
||||
static int snd_vol;
|
||||
|
||||
// bk001119 - these not static, required by unix/snd_mixa.s
|
||||
int* snd_p;
|
||||
int snd_linear_count;
|
||||
short* snd_out;
|
||||
|
||||
#if !id386 // if configured not to use asm
|
||||
#if !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__) ) // rb010123
|
||||
#if !id386
|
||||
|
||||
void S_WriteLinearBlastStereo16 (void)
|
||||
{
|
||||
@@ -57,9 +58,6 @@ void S_WriteLinearBlastStereo16 (void)
|
||||
snd_out[i+1] = val;
|
||||
}
|
||||
}
|
||||
#elif defined(__GNUC__)
|
||||
// uses snd_mixa.s
|
||||
void S_WriteLinearBlastStereo16 (void);
|
||||
#else
|
||||
|
||||
__declspec( naked ) void S_WriteLinearBlastStereo16 (void)
|
||||
@@ -106,6 +104,10 @@ LClampDone2:
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
// forward declare, implementation somewhere else
|
||||
void S_WriteLinearBlastStereo16 (void);
|
||||
#endif
|
||||
|
||||
void S_TransferStereo16 (unsigned long *pbuf, int endtime)
|
||||
@@ -119,24 +121,21 @@ void S_TransferStereo16 (unsigned long *pbuf, int endtime)
|
||||
while (ls_paintedtime < endtime)
|
||||
{
|
||||
// handle recirculating buffer issues
|
||||
lpos = ls_paintedtime % dma.fullsamples;
|
||||
lpos = ls_paintedtime & ((dma.samples>>1)-1);
|
||||
|
||||
snd_out = (short *) pbuf + (lpos<<1); // lpos * dma.channels
|
||||
snd_out = (short *) pbuf + (lpos<<1);
|
||||
|
||||
snd_linear_count = dma.fullsamples - lpos;
|
||||
snd_linear_count = (dma.samples>>1) - lpos;
|
||||
if (ls_paintedtime + snd_linear_count > endtime)
|
||||
snd_linear_count = endtime - ls_paintedtime;
|
||||
|
||||
snd_linear_count <<= 1; // snd_linear_count *= dma.channels
|
||||
snd_linear_count <<= 1;
|
||||
|
||||
// write a linear blast of samples
|
||||
S_WriteLinearBlastStereo16 ();
|
||||
|
||||
snd_p += snd_linear_count;
|
||||
ls_paintedtime += (snd_linear_count>>1); // snd_linear_count / dma.channels
|
||||
|
||||
if( CL_VideoRecording( ) )
|
||||
CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 ); // snd_linear_count * (dma.samplebits/8)
|
||||
ls_paintedtime += (snd_linear_count>>1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,16 +149,19 @@ void S_TransferPaintBuffer(int endtime)
|
||||
{
|
||||
int out_idx;
|
||||
int count;
|
||||
int out_mask;
|
||||
int *p;
|
||||
int step;
|
||||
int val;
|
||||
int i;
|
||||
unsigned long *pbuf;
|
||||
|
||||
pbuf = (unsigned long *)dma.buffer;
|
||||
|
||||
|
||||
if ( s_testsound->integer ) {
|
||||
int i;
|
||||
int count;
|
||||
|
||||
// write a fixed sine wave
|
||||
count = (endtime - s_paintedtime);
|
||||
for (i=0 ; i<count ; i++)
|
||||
@@ -175,73 +177,38 @@ void S_TransferPaintBuffer(int endtime)
|
||||
{ // general case
|
||||
p = (int *) paintbuffer;
|
||||
count = (endtime - s_paintedtime) * dma.channels;
|
||||
out_idx = ((unsigned int)s_paintedtime * dma.channels) % dma.samples;
|
||||
step = 3 - MIN(dma.channels, 2);
|
||||
out_mask = dma.samples - 1;
|
||||
out_idx = s_paintedtime * dma.channels & out_mask;
|
||||
step = 3 - dma.channels;
|
||||
|
||||
if ((dma.isfloat) && (dma.samplebits == 32))
|
||||
{
|
||||
float *out = (float *) pbuf;
|
||||
for (i=0 ; i<count ; i++)
|
||||
{
|
||||
if ((i % dma.channels) >= 2)
|
||||
{
|
||||
val = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
}
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < -32767) /* clamp to one less than max to make division max out at -1.0f. */
|
||||
val = -32767;
|
||||
out[out_idx] = ((float) val) / 32767.0f;
|
||||
out_idx = (out_idx + 1) % dma.samples;
|
||||
}
|
||||
}
|
||||
else if (dma.samplebits == 16)
|
||||
if (dma.samplebits == 16)
|
||||
{
|
||||
short *out = (short *) pbuf;
|
||||
for (i=0 ; i<count ; i++)
|
||||
while (count--)
|
||||
{
|
||||
if ((i % dma.channels) >= 2)
|
||||
{
|
||||
val = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
}
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < -32768)
|
||||
val = -32768;
|
||||
out[out_idx] = val;
|
||||
out_idx = (out_idx + 1) % dma.samples;
|
||||
out_idx = (out_idx + 1) & out_mask;
|
||||
}
|
||||
}
|
||||
else if (dma.samplebits == 8)
|
||||
{
|
||||
unsigned char *out = (unsigned char *) pbuf;
|
||||
for (i=0 ; i<count ; i++)
|
||||
while (count--)
|
||||
{
|
||||
if ((i % dma.channels) >= 2)
|
||||
{
|
||||
val = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
}
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < -32768)
|
||||
val = -32768;
|
||||
out[out_idx] = (val>>8) + 128;
|
||||
out_idx = (out_idx + 1) % dma.samples;
|
||||
out_idx = (out_idx + 1) & out_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,18 +223,14 @@ CHANNEL MIXING
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data, aoff, boff;
|
||||
int leftvol, rightvol;
|
||||
int i, j;
|
||||
portable_samplepair_t *samp;
|
||||
sndBuffer *chunk;
|
||||
short *samples;
|
||||
float ooff, fdata[2], fdiv, fleftvol, frightvol;
|
||||
|
||||
if (sc->soundChannels <= 0) {
|
||||
return;
|
||||
}
|
||||
float ooff, fdata, fdiv, fleftvol, frightvol;
|
||||
|
||||
samp = &paintbuffer[ bufferOffset ];
|
||||
|
||||
@@ -275,14 +238,6 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
|
||||
sampleOffset = sampleOffset*ch->oldDopplerScale;
|
||||
}
|
||||
|
||||
if ( sc->soundChannels == 2 ) {
|
||||
sampleOffset *= sc->soundChannels;
|
||||
|
||||
if ( sampleOffset & 1 ) {
|
||||
sampleOffset &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
chunk = sc->soundData;
|
||||
while (sampleOffset>=SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
@@ -293,16 +248,126 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
|
||||
}
|
||||
|
||||
if (!ch->doppler || ch->dopplerScale==1.0f) {
|
||||
#if idppc_altivec
|
||||
vector signed short volume_vec;
|
||||
vector unsigned int volume_shift;
|
||||
int vectorCount, samplesLeft, chunkSamplesLeft;
|
||||
#endif
|
||||
leftvol = ch->leftvol*snd_vol;
|
||||
rightvol = ch->rightvol*snd_vol;
|
||||
samples = chunk->sndChunk;
|
||||
#if idppc_altivec
|
||||
((short *)&volume_vec)[0] = leftvol;
|
||||
((short *)&volume_vec)[1] = leftvol;
|
||||
((short *)&volume_vec)[4] = leftvol;
|
||||
((short *)&volume_vec)[5] = leftvol;
|
||||
((short *)&volume_vec)[2] = rightvol;
|
||||
((short *)&volume_vec)[3] = rightvol;
|
||||
((short *)&volume_vec)[6] = rightvol;
|
||||
((short *)&volume_vec)[7] = rightvol;
|
||||
volume_shift = vec_splat_u32(8);
|
||||
i = 0;
|
||||
|
||||
while(i < count) {
|
||||
/* Try to align destination to 16-byte boundary */
|
||||
while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
/* Destination is now aligned. Process as many 8-sample
|
||||
chunks as we can before we run out of room from the current
|
||||
sound chunk. We do 8 per loop to avoid extra source data reads. */
|
||||
samplesLeft = count - i;
|
||||
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
|
||||
if(samplesLeft > chunkSamplesLeft)
|
||||
samplesLeft = chunkSamplesLeft;
|
||||
|
||||
vectorCount = samplesLeft / 8;
|
||||
|
||||
if(vectorCount)
|
||||
{
|
||||
vector unsigned char tmp;
|
||||
vector short s0, s1, sampleData0, sampleData1;
|
||||
vector short samples0, samples1;
|
||||
vector signed int left0, right0;
|
||||
vector signed int merge0, merge1;
|
||||
vector signed int d0, d1, d2, d3;
|
||||
vector unsigned char samplePermute0 =
|
||||
(vector unsigned char)(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
|
||||
vector unsigned char samplePermute1 =
|
||||
(vector unsigned char)(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
|
||||
vector unsigned char loadPermute0, loadPermute1;
|
||||
|
||||
// Rather than permute the vectors after we load them to do the sample
|
||||
// replication and rearrangement, we permute the alignment vector so
|
||||
// we do everything in one step below and avoid data shuffling.
|
||||
tmp = vec_lvsl(0,&samples[sampleOffset]);
|
||||
loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
|
||||
loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
|
||||
|
||||
s0 = *(vector short *)&samples[sampleOffset];
|
||||
while(vectorCount)
|
||||
{
|
||||
/* Load up source (16-bit) sample data */
|
||||
s1 = *(vector short *)&samples[sampleOffset+7];
|
||||
|
||||
/* Load up destination sample data */
|
||||
d0 = *(vector signed int *)&samp[i];
|
||||
d1 = *(vector signed int *)&samp[i+2];
|
||||
d2 = *(vector signed int *)&samp[i+4];
|
||||
d3 = *(vector signed int *)&samp[i+6];
|
||||
|
||||
sampleData0 = vec_perm(s0,s1,loadPermute0);
|
||||
sampleData1 = vec_perm(s0,s1,loadPermute1);
|
||||
|
||||
merge0 = vec_mule(sampleData0,volume_vec);
|
||||
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
|
||||
|
||||
merge1 = vec_mulo(sampleData0,volume_vec);
|
||||
merge1 = vec_sra(merge1,volume_shift);
|
||||
|
||||
d0 = vec_add(merge0,d0);
|
||||
d1 = vec_add(merge1,d1);
|
||||
|
||||
merge0 = vec_mule(sampleData1,volume_vec);
|
||||
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
|
||||
|
||||
merge1 = vec_mulo(sampleData1,volume_vec);
|
||||
merge1 = vec_sra(merge1,volume_shift);
|
||||
|
||||
d2 = vec_add(merge0,d2);
|
||||
d3 = vec_add(merge1,d3);
|
||||
|
||||
/* Store destination sample data */
|
||||
*(vector signed int *)&samp[i] = d0;
|
||||
*(vector signed int *)&samp[i+2] = d1;
|
||||
*(vector signed int *)&samp[i+4] = d2;
|
||||
*(vector signed int *)&samp[i+6] = d3;
|
||||
|
||||
i += 8;
|
||||
vectorCount--;
|
||||
s0 = s1;
|
||||
sampleOffset += 8;
|
||||
}
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
|
||||
if ( sc->soundChannels == 2 ) {
|
||||
data = samples[sampleOffset++];
|
||||
}
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
@@ -311,6 +376,7 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
|
||||
sampleOffset = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
fleftvol = ch->leftvol*snd_vol;
|
||||
frightvol = ch->rightvol*snd_vol;
|
||||
@@ -324,10 +390,10 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
|
||||
aoff = ooff;
|
||||
ooff = ooff + ch->dopplerScale * sc->soundChannels;
|
||||
ooff = ooff + ch->dopplerScale;
|
||||
boff = ooff;
|
||||
fdata[0] = fdata[1] = 0;
|
||||
for (j=aoff; j<boff; j += sc->soundChannels) {
|
||||
fdata = 0;
|
||||
for (j=aoff; j<boff; j++) {
|
||||
if (j == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
if (!chunk) {
|
||||
@@ -336,32 +402,15 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou
|
||||
samples = chunk->sndChunk;
|
||||
ooff -= SND_CHUNK_SIZE;
|
||||
}
|
||||
if ( sc->soundChannels == 2 ) {
|
||||
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
fdata[1] += samples[(j+1)&(SND_CHUNK_SIZE-1)];
|
||||
} else {
|
||||
fdata[0] += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
fdata[1] += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
}
|
||||
fdata += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
}
|
||||
fdiv = 256 * (boff-aoff) / sc->soundChannels;
|
||||
samp[i].left += (fdata[0] * fleftvol)/fdiv;
|
||||
samp[i].right += (fdata[1] * frightvol)/fdiv;
|
||||
fdiv = 256 * (boff-aoff);
|
||||
samp[i].left += (fdata * fleftvol)/fdiv;
|
||||
samp[i].right += (fdata * frightvol)/fdiv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
#if idppc_altivec
|
||||
if (com_altivec->integer) {
|
||||
// must be in a separate translation unit or G3 systems will crash.
|
||||
S_PaintChannelFrom16_altivec( paintbuffer, snd_vol, ch, sc, count, sampleOffset, bufferOffset );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
S_PaintChannelFrom16_scalar( ch, sc, count, sampleOffset, bufferOffset );
|
||||
}
|
||||
|
||||
void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data;
|
||||
int leftvol, rightvol;
|
||||
@@ -383,7 +432,7 @@ void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleO
|
||||
}
|
||||
|
||||
if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
|
||||
decodeWavelet(chunk, sfxScratchBuffer);
|
||||
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
|
||||
sfxScratchIndex = i;
|
||||
sfxScratchPointer = sc;
|
||||
}
|
||||
@@ -480,7 +529,7 @@ void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOff
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
samples++;
|
||||
if (chunk != NULL && samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
|
||||
if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
|
||||
chunk = chunk->next;
|
||||
samples = (byte *)chunk->sndChunk;
|
||||
}
|
||||
@@ -513,16 +562,13 @@ S_PaintChannels
|
||||
void S_PaintChannels( int endtime ) {
|
||||
int i;
|
||||
int end;
|
||||
int stream;
|
||||
channel_t *ch;
|
||||
sfx_t *sc;
|
||||
int ltime, count;
|
||||
int sampleOffset;
|
||||
|
||||
if(s_muted->integer)
|
||||
snd_vol = 0;
|
||||
else
|
||||
snd_vol = s_volume->value*255;
|
||||
|
||||
snd_vol = s_volume->value*255;
|
||||
|
||||
//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
|
||||
while ( s_paintedtime < endtime ) {
|
||||
@@ -533,24 +579,36 @@ void S_PaintChannels( int endtime ) {
|
||||
end = s_paintedtime + PAINTBUFFER_SIZE;
|
||||
}
|
||||
|
||||
// clear the paint buffer and mix any raw samples...
|
||||
Com_Memset(paintbuffer, 0, sizeof (paintbuffer));
|
||||
for (stream = 0; stream < MAX_RAW_STREAMS; stream++) {
|
||||
if ( s_rawend[stream] >= s_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 = s_paintedtime ; i < stop ; i++ ) {
|
||||
const int s = i&(MAX_RAW_SAMPLES-1);
|
||||
paintbuffer[i-s_paintedtime].left += rawsamples[s].left;
|
||||
paintbuffer[i-s_paintedtime].right += rawsamples[s].right;
|
||||
}
|
||||
// clear the paint buffer to either music or zeros
|
||||
if ( s_rawend < s_paintedtime ) {
|
||||
if ( s_rawend ) {
|
||||
//Com_DPrintf ("background sound underrun\n");
|
||||
}
|
||||
Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t));
|
||||
} else {
|
||||
// copy from the streaming sound source
|
||||
int s;
|
||||
int stop;
|
||||
|
||||
stop = (end < s_rawend) ? end : s_rawend;
|
||||
|
||||
for ( i = s_paintedtime ; i < stop ; i++ ) {
|
||||
s = i&(MAX_RAW_SAMPLES-1);
|
||||
paintbuffer[i-s_paintedtime] = s_rawsamples[s];
|
||||
}
|
||||
// if (i != end)
|
||||
// Com_Printf ("partial stream\n");
|
||||
// else
|
||||
// Com_Printf ("full stream\n");
|
||||
for ( ; i < end ; i++ ) {
|
||||
paintbuffer[i-s_paintedtime].left =
|
||||
paintbuffer[i-s_paintedtime].right = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// paint in the channels.
|
||||
ch = s_channels;
|
||||
for ( i = 0; i < MAX_SOUNDCHANNELS ; i++, ch++ ) {
|
||||
for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
|
||||
if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
|
||||
continue;
|
||||
}
|
||||
@@ -558,10 +616,6 @@ void S_PaintChannels( int endtime ) {
|
||||
ltime = s_paintedtime;
|
||||
sc = ch->thesfx;
|
||||
|
||||
if (sc->soundData==NULL || sc->soundLength==0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sampleOffset = ltime - ch->startSample;
|
||||
count = end - ltime;
|
||||
if ( sampleOffset + count > sc->soundLength ) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -15,7 +15,7 @@ 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
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
@@ -38,8 +38,8 @@ void S_StopBackgroundTrack( void );
|
||||
|
||||
// cinematics and voice-over-network will send raw samples
|
||||
// 1.0 volume will be direct output of source samples
|
||||
void S_RawSamples(int stream, int samples, int rate, int width, int channels,
|
||||
const byte *data, float volume, int entityNum);
|
||||
void S_RawSamples (int samples, int rate, int width, int channels,
|
||||
const byte *data, float volume);
|
||||
|
||||
// stop all sounds and the background track
|
||||
void S_StopAllSounds(qboolean stop_music);
|
||||
@@ -51,21 +51,20 @@ void S_AddLoopingSound(const vec3_t origin, const vec3_t velocity, sfxHandle_t s
|
||||
void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
|
||||
void S_StopLoopingSound(int entityNum );
|
||||
|
||||
// recompute the relative volumes for all running sounds
|
||||
// relative to the given entityNum / orientation
|
||||
void S_Respatialize(int entityNum, const vec3_t origin, vec3_t axis[3], int inwater);
|
||||
// recompute the reletive volumes for all running sounds
|
||||
// reletive to the given entityNum / orientation
|
||||
void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
|
||||
|
||||
// let the sound system know where an entity currently is
|
||||
void S_UpdateEntityPosition(int entityNum, const vec3_t origin);
|
||||
void S_UpdateEntityPosition( int entityNum, const vec3_t origin );
|
||||
|
||||
void S_Update( void );
|
||||
|
||||
void S_DisableSounds( void );
|
||||
|
||||
void S_BeginRegistration(void);
|
||||
void S_BeginRegistration( void );
|
||||
void S_EndRegistration(void);
|
||||
qboolean S_IsSoundRegistered(const char* name);
|
||||
|
||||
// RegisterSound will allways return a valid sample, even if it
|
||||
// has to create a placeholder. This prevents continuous filesystem
|
||||
// checks for missing files
|
||||
@@ -110,15 +109,6 @@ void S_RespatializeOld(int entityNum, const vec3_t origin, vec3_t axis[3]);
|
||||
void S_UpdateEntity(int entityNum, const vec3_t origin, const vec3_t velocity, qboolean use_listener);
|
||||
void S_FadeSound(float fTime);
|
||||
|
||||
#ifdef USE_VOIP
|
||||
void S_StartCapture( void );
|
||||
int S_AvailableCaptureSamples( void );
|
||||
void S_Capture( int samples, byte *data );
|
||||
void S_StopCapture( void );
|
||||
void S_MasterGain( float gain );
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+18
-17
@@ -15,13 +15,15 @@ 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
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "snd_local.h"
|
||||
|
||||
long myftol( float f );
|
||||
|
||||
#define C0 0.4829629131445341
|
||||
#define C1 0.8365163037378079
|
||||
#define C2 0.2241438680420134
|
||||
@@ -29,8 +31,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
void daub4(float b[], unsigned long n, int isign)
|
||||
{
|
||||
float wksp[4097] = { 0.0f };
|
||||
#define a(x) b[(x)-1] // numerical recipies so a[1] = b[0]
|
||||
float wksp[4097];
|
||||
float *a=b-1; // numerical recipies so a[1] = b[0]
|
||||
|
||||
unsigned long nh,nh1,i,j;
|
||||
|
||||
@@ -39,23 +41,22 @@ void daub4(float b[], unsigned long n, int isign)
|
||||
nh1=(nh=n >> 1)+1;
|
||||
if (isign >= 0) {
|
||||
for (i=1,j=1;j<=n-3;j+=2,i++) {
|
||||
wksp[i] = C0*a(j)+C1*a(j+1)+C2*a(j+2)+C3*a(j+3);
|
||||
wksp[i+nh] = C3*a(j)-C2*a(j+1)+C1*a(j+2)-C0*a(j+3);
|
||||
wksp[i] = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3];
|
||||
wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3];
|
||||
}
|
||||
wksp[i ] = C0*a(n-1)+C1*a(n)+C2*a(1)+C3*a(2);
|
||||
wksp[i+nh] = C3*a(n-1)-C2*a(n)+C1*a(1)-C0*a(2);
|
||||
wksp[i ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2];
|
||||
wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2];
|
||||
} else {
|
||||
wksp[1] = C2*a(nh)+C1*a(n)+C0*a(1)+C3*a(nh1);
|
||||
wksp[2] = C3*a(nh)-C0*a(n)+C1*a(1)-C2*a(nh1);
|
||||
wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1];
|
||||
wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1];
|
||||
for (i=1,j=3;i<nh;i++) {
|
||||
wksp[j++] = C2*a(i)+C1*a(i+nh)+C0*a(i+1)+C3*a(i+nh1);
|
||||
wksp[j++] = C3*a(i)-C0*a(i+nh)+C1*a(i+1)-C2*a(i+nh1);
|
||||
wksp[j++] = C2*a[i]+C1*a[i+nh]+C0*a[i+1]+C3*a[i+nh1];
|
||||
wksp[j++] = C3*a[i]-C0*a[i+nh]+C1*a[i+1]-C2*a[i+nh1];
|
||||
}
|
||||
}
|
||||
for (i=1;i<=n;i++) {
|
||||
a(i)=wksp[i];
|
||||
a[i]=wksp[i];
|
||||
}
|
||||
#undef a
|
||||
}
|
||||
|
||||
void wt1(float a[], unsigned long n, int isign)
|
||||
@@ -120,7 +121,7 @@ void NXPutc(NXStream *stream, char out) {
|
||||
|
||||
|
||||
void encodeWavelet( sfx_t *sfx, short *packets) {
|
||||
float wksp[4097] = {0}, temp;
|
||||
float wksp[4097], temp;
|
||||
int i, samples, size;
|
||||
sndBuffer *newchunk, *chunk;
|
||||
byte *out;
|
||||
@@ -147,7 +148,7 @@ void encodeWavelet( sfx_t *sfx, short *packets) {
|
||||
newchunk = SND_malloc();
|
||||
if (sfx->soundData == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else if (chunk != NULL) {
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
@@ -170,7 +171,7 @@ void encodeWavelet( sfx_t *sfx, short *packets) {
|
||||
}
|
||||
|
||||
void decodeWavelet(sndBuffer *chunk, short *to) {
|
||||
float wksp[4097] = {0};
|
||||
float wksp[4097];
|
||||
int i;
|
||||
byte *out;
|
||||
|
||||
@@ -216,7 +217,7 @@ void encodeMuLaw( sfx_t *sfx, short *packets) {
|
||||
newchunk = SND_malloc();
|
||||
if (sfx->soundData == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else if (chunk != NULL) {
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(gcd)
|
||||
|
||||
set(CURRENT_DIR "code/gamespy")
|
||||
|
||||
include_directories("${CURRENT_DIR}/common")
|
||||
|
||||
@@ -728,7 +728,7 @@ void Alias_ListFindRandomRange(AliasList_t* list, const char* alias, int* min_in
|
||||
}
|
||||
} while (r >= l);
|
||||
|
||||
if (l >= r)
|
||||
if (r >= l)
|
||||
{
|
||||
numfound = 0;
|
||||
*min_index = index;
|
||||
@@ -736,7 +736,7 @@ void Alias_ListFindRandomRange(AliasList_t* list, const char* alias, int* min_in
|
||||
totalfoundweight = 0.f;
|
||||
|
||||
ptr = &list->sorted_list[index];
|
||||
for (i = 0; i < index; i++, ptr--)
|
||||
for (i = index + 1; i > 0; i--, ptr--)
|
||||
{
|
||||
if (strncmp(convalias, (*ptr)->alias_name, length)) {
|
||||
break;
|
||||
@@ -746,7 +746,7 @@ void Alias_ListFindRandomRange(AliasList_t* list, const char* alias, int* min_in
|
||||
{
|
||||
foundlist[numfound++] = *ptr;
|
||||
totalfoundweight += (*ptr)->weight;
|
||||
*min_index = i;
|
||||
*min_index = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define PRODUCT_NAME "OpenMoHAA"
|
||||
#define PRODUCT_VERSION "0.49-alpha"
|
||||
#define PRODUCT_VERSION "0.50-alpha"
|
||||
#define PRODUCT_DATE __DATE__
|
||||
|
||||
#if TARGET_GAME_TYPE == 1
|
||||
|
||||
@@ -1237,6 +1237,13 @@ char **Sys_ListFiles( const char *directory, const char *extension, const char *
|
||||
void Sys_FreeFileList( char **list );
|
||||
void Sys_Sleep(int msec);
|
||||
|
||||
void Sys_InitStreamThread(void);
|
||||
void Sys_ShutdownStreamThread(void);
|
||||
void Sys_BeginStreamedFile(fileHandle_t f, int readAhead);
|
||||
void Sys_EndStreamedFile(fileHandle_t f);
|
||||
int Sys_StreamedRead(void* buffer, int size, int count, fileHandle_t f);
|
||||
void Sys_StreamSeek(fileHandle_t f, int offset, int origin);
|
||||
|
||||
qboolean Sys_LowPhysicalMemory( void );
|
||||
|
||||
void SetNormalThreadPriority( void );
|
||||
|
||||
@@ -535,7 +535,7 @@ typedef struct cTerraPatch_s {
|
||||
byte s;
|
||||
byte t;
|
||||
|
||||
float texCoord[ 2 ][ 2 ][ 2 ];
|
||||
vec2_t texCoord[ 2 ][ 2 ];
|
||||
|
||||
char x;
|
||||
char y;
|
||||
|
||||
@@ -90,6 +90,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#define GL_TEXTURE2_ARB 0x84C2
|
||||
#define GL_TEXTURE3_ARB 0x84C3
|
||||
|
||||
#define GL_COMBINE 0x8570
|
||||
#define GL_COMBINE4_NV 0x8503
|
||||
|
||||
// NOTE: some Linux platforms would need those prototypes
|
||||
#if defined(MACOS_X)
|
||||
typedef void (APIENTRY * PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s);
|
||||
|
||||
@@ -188,6 +188,10 @@ void GL_TexEnv( int env )
|
||||
case GL_ADD:
|
||||
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
|
||||
break;
|
||||
case GL_COMBINE4_NV:
|
||||
case GL_COMBINE:
|
||||
qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env);
|
||||
break;
|
||||
default:
|
||||
ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed\n", env );
|
||||
break;
|
||||
|
||||
+45
-28
@@ -457,7 +457,11 @@ R_UnpackTerraPatch
|
||||
================
|
||||
*/
|
||||
void R_UnpackTerraPatch(cTerraPatch_t* pPacked, cTerraPatchUnpacked_t* pUnpacked) {
|
||||
int i;
|
||||
int i, j;
|
||||
union {
|
||||
int16_t v;
|
||||
uint8_t b[2];
|
||||
} flags;
|
||||
|
||||
pUnpacked->byDirty = qfalse;
|
||||
pUnpacked->visCountCheck = 0;
|
||||
@@ -468,41 +472,54 @@ void R_UnpackTerraPatch(cTerraPatch_t* pPacked, cTerraPatchUnpacked_t* pUnpacked
|
||||
Com_Error(ERR_DROP, "invalid map: terrain has lmapScale <= 0");
|
||||
}
|
||||
|
||||
pUnpacked->drawinfo.lmapStep = (pPacked->lmapScale / 64);
|
||||
pUnpacked->drawinfo.lmapSize = (pPacked->lmapScale * 8) | 1;
|
||||
pUnpacked->drawinfo.lmapStep = (float)(pPacked->lmapScale / 64);
|
||||
pUnpacked->drawinfo.lmapSize = (float)((pPacked->lmapScale * 8) + 1);
|
||||
pUnpacked->s = ((float)pPacked->s + 0.5) * 0.0078125;
|
||||
pUnpacked->t = ((float)pPacked->t + 0.5) * 0.0078125;
|
||||
|
||||
//if( s_worldData.lighting ) {
|
||||
// pUnpacked->drawinfo.lmData = &s_worldData.lighting[ 3 * ( ( pPacked->t << 7 ) + pPacked->s + ( pPacked->iLightMap << 14 ) ) ];
|
||||
//} else {
|
||||
// pUnpacked->drawinfo.lmData = NULL;
|
||||
//}
|
||||
|
||||
// fixme: is it important to have a lightmap data?
|
||||
pUnpacked->drawinfo.lmData = NULL;
|
||||
|
||||
memcpy(pUnpacked->texCoord, pPacked->texCoord, sizeof(pUnpacked->texCoord));
|
||||
pUnpacked->x0 = ((int)pPacked->x << 6);
|
||||
pUnpacked->y0 = ((int)pPacked->y << 6);
|
||||
pUnpacked->z0 = pPacked->iBaseHeight;
|
||||
pUnpacked->shader = ShaderForShaderNum(pPacked->iShader, pPacked->iLightMap);
|
||||
pUnpacked->iNorth = pPacked->iNorth;
|
||||
pUnpacked->iEast = pPacked->iEast;
|
||||
pUnpacked->iSouth = pPacked->iSouth;
|
||||
pUnpacked->iWest = pPacked->iWest;
|
||||
|
||||
for (i = 0; i < 63; i++)
|
||||
{
|
||||
pUnpacked->varTree[0][i / 4].fVariance = pPacked->varTree[0][i].flags >> 12;
|
||||
pUnpacked->varTree[1][i / 4].fVariance = pPacked->varTree[1][i].flags >> 12;
|
||||
if( s_worldData.lighting ) {
|
||||
pUnpacked->drawinfo.lmData = &s_worldData.lighting[ 3 * ( ( pPacked->t << 7 ) + pPacked->s + ( pPacked->iLightMap << 14 ) ) ];
|
||||
} else {
|
||||
pUnpacked->drawinfo.lmData = NULL;
|
||||
}
|
||||
|
||||
memcpy(pUnpacked->heightmap, pPacked->heightmap, sizeof(pUnpacked->heightmap));
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
pUnpacked->texCoord[i][j][0] = LittleFloat(pPacked->texCoord[i][j][0]);
|
||||
pUnpacked->texCoord[i][j][1] = LittleFloat(pPacked->texCoord[i][j][1]);
|
||||
}
|
||||
}
|
||||
|
||||
pUnpacked->x0 = ((int)pPacked->x << 6);
|
||||
pUnpacked->y0 = ((int)pPacked->y << 6);
|
||||
pUnpacked->z0 = LittleShort(pPacked->iBaseHeight);
|
||||
pUnpacked->shader = ShaderForShaderNum(LittleShort(pPacked->iShader), LittleShort(pPacked->iLightMap));
|
||||
pUnpacked->iNorth = LittleShort(pPacked->iNorth);
|
||||
pUnpacked->iEast = LittleShort(pPacked->iEast);
|
||||
pUnpacked->iSouth = LittleShort(pPacked->iSouth);
|
||||
pUnpacked->iWest = LittleShort(pPacked->iWest);
|
||||
|
||||
for (i = 0; i < 63; i++)
|
||||
{
|
||||
flags.v = pPacked->varTree[0][i].flags;
|
||||
flags.b[1] &= 7;
|
||||
pUnpacked->varTree[0][i].fVariance = LittleShort(flags.v);
|
||||
pUnpacked->varTree[0][i].s.flags = LittleShort(pPacked->varTree[0][i].flags) >> 12;
|
||||
|
||||
flags.v = pPacked->varTree[1][i].flags;
|
||||
flags.b[1] &= 7;
|
||||
pUnpacked->varTree[1][i].fVariance = LittleShort(flags.v);
|
||||
pUnpacked->varTree[1][i].s.flags = LittleShort(pPacked->varTree[1][i].flags) >> 12;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(pUnpacked->heightmap) / sizeof(pUnpacked->heightmap[0]); i++) {
|
||||
pUnpacked->heightmap[i] = pPacked->heightmap[i];
|
||||
}
|
||||
|
||||
pUnpacked->zmax = 0;
|
||||
pUnpacked->flags = pPacked->flags;
|
||||
|
||||
for (i = 0; i < sizeof(pUnpacked->heightmap); i++)
|
||||
for (i = 0; i < sizeof(pUnpacked->heightmap) / sizeof(pUnpacked->heightmap[0]); i++)
|
||||
{
|
||||
if (pUnpacked->zmax < pUnpacked->heightmap[i]) {
|
||||
pUnpacked->zmax = pUnpacked->heightmap[i];
|
||||
|
||||
@@ -1200,6 +1200,10 @@ void R_Register( void )
|
||||
ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f );
|
||||
}
|
||||
|
||||
void R_InitExtensions() {
|
||||
// FIXME: unimplemented
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_Init
|
||||
@@ -1292,6 +1296,8 @@ void R_Init( void ) {
|
||||
|
||||
InitOpenGL();
|
||||
|
||||
R_InitExtensions();
|
||||
|
||||
R_InitImages();
|
||||
|
||||
R_StartupShaders();
|
||||
|
||||
@@ -441,7 +441,7 @@ typedef struct {
|
||||
qboolean active;
|
||||
|
||||
textureBundle_t bundle[NUM_TEXTURE_BUNDLES];
|
||||
int multitextureEnv;
|
||||
int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage)
|
||||
|
||||
waveForm_t rgbWave;
|
||||
colorGen_t rgbGen;
|
||||
@@ -540,8 +540,6 @@ typedef struct shader_s {
|
||||
|
||||
float portalRange; // distance to fog out at
|
||||
|
||||
int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage)
|
||||
|
||||
cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED
|
||||
qboolean polygonOffset; // set for decals and other items that must be offset
|
||||
qboolean noMipMaps; // for console fonts, 2D elements, etc.
|
||||
@@ -829,7 +827,10 @@ typedef struct {
|
||||
|
||||
typedef union varnodeUnpacked_u {
|
||||
float fVariance;
|
||||
byte flags;
|
||||
struct {
|
||||
byte flags;
|
||||
unsigned char unused[3];
|
||||
} s;
|
||||
} varnodeUnpacked_t;
|
||||
|
||||
typedef unsigned short terraInt;
|
||||
@@ -893,7 +894,7 @@ typedef struct cTerraPatchUnpacked_s {
|
||||
unsigned int uiDistRecalc;
|
||||
float s;
|
||||
float t;
|
||||
float texCoord[2][2][2];
|
||||
vec2_t texCoord[2][2];
|
||||
float x0;
|
||||
float y0;
|
||||
float z0;
|
||||
@@ -1069,9 +1070,7 @@ typedef struct {
|
||||
const byte *vis; // may be passed in by CM_LoadMap to save space
|
||||
|
||||
byte *novis; // clusterBytes of 0xff
|
||||
|
||||
char *entityString;
|
||||
char *entityParsePoint;
|
||||
byte *lighting;
|
||||
} world_t;
|
||||
|
||||
//======================================================================
|
||||
@@ -1172,7 +1171,11 @@ typedef struct {
|
||||
qboolean finishCalled;
|
||||
int texEnv[2];
|
||||
int faceCulling;
|
||||
unsigned long glStateBits;
|
||||
int cntTexEnvExt;
|
||||
int cntnvblendmode;
|
||||
long unsigned int glStateBits;
|
||||
long unsigned int externalSetState;
|
||||
vec4_t fFogColor;
|
||||
} glstate_t;
|
||||
|
||||
|
||||
@@ -1619,6 +1622,7 @@ void GL_Cull( int cullType );
|
||||
#define GLS_DEPTHTEST_DISABLE 0x00000400
|
||||
|
||||
#define GLS_POLYMODE_LINE 0x00001000
|
||||
#define GLS_MULTITEXTURE_ENV 0x00004000
|
||||
|
||||
#define GLS_DEPTHFUNC_EQUAL 0x00020000
|
||||
|
||||
@@ -1806,8 +1810,13 @@ WORLD MAP
|
||||
============================================================
|
||||
*/
|
||||
|
||||
extern terraTri_t* g_pTris;
|
||||
extern terrainVert_t* g_pVert;
|
||||
|
||||
void R_AddBrushModelSurfaces( trRefEntity_t *e );
|
||||
void R_GetInlineModelBounds(int iIndex, vec3_t vMins, vec3_t vMaxs);
|
||||
int R_DlightTerrain(cTerraPatchUnpacked_t* surf, int dlightBits);
|
||||
int R_CheckDlightTerrain(cTerraPatchUnpacked_t* surf, int dlightBits);
|
||||
void R_AddWorldSurfaces( void );
|
||||
qboolean R_inPVS( const vec3_t p1, const vec3_t p2 );
|
||||
|
||||
@@ -2013,6 +2022,7 @@ TERRAIN
|
||||
|
||||
=============================================================
|
||||
*/
|
||||
void R_MarkTerrainPatch(cTerraPatchUnpacked_t* pPatch);
|
||||
void R_AddTerrainSurfaces();
|
||||
void R_InitTerrain();
|
||||
void R_TerrainPrepareFrame();
|
||||
|
||||
+58
-12
@@ -354,6 +354,37 @@ static void DrawMultitextured( shaderCommands_t *input, int stage ) {
|
||||
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] );
|
||||
R_BindAnimatedImage( &pStage->bundle[0] );
|
||||
|
||||
if (pStage->stateBits & GLS_MULTITEXTURE_ENV)
|
||||
{
|
||||
glState.cntnvblendmode = pStage->multitextureEnv;
|
||||
glState.cntTexEnvExt = GLS_MULTITEXTURE_ENV;
|
||||
|
||||
if (pStage->multitextureEnv == GL_ADD)
|
||||
{
|
||||
qglTexEnvf(8960, 34161, 260.0);
|
||||
qglTexEnvf(8960, 34176, 33984.0);
|
||||
qglTexEnvf(8960, 34192, 1145044992);
|
||||
qglTexEnvf(8960, 34177, 0);
|
||||
qglTexEnvf(8960, 34193, 1145061376);
|
||||
qglTexEnvf(8960, 34178, 33985.0);
|
||||
qglTexEnvf(8960, 34194, 1145044992);
|
||||
qglTexEnvf(8960, 34179, 0);
|
||||
qglTexEnvf(8960, 34195, 1145061376);
|
||||
}
|
||||
else if (pStage->multitextureEnv == GL_MODULATE)
|
||||
{
|
||||
qglTexEnvf(8960, 34161, 8448.0);
|
||||
qglTexEnvf(8960, 34176, 33984.0);
|
||||
qglTexEnvf(8960, 34192, 1145044992);
|
||||
qglTexEnvf(8960, 34177, 33985.0);
|
||||
qglTexEnvf(8960, 34193, 1145044992);
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf(3, "Unknown MT mode for: %s\n", input->shader);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// lightmap/secondary pass
|
||||
//
|
||||
@@ -361,23 +392,38 @@ static void DrawMultitextured( shaderCommands_t *input, int stage ) {
|
||||
qglEnable( GL_TEXTURE_2D );
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
|
||||
if ( r_lightmap->integer ) {
|
||||
if ( r_lightmap->integer && pStage->bundle[1].isLightmap ) {
|
||||
GL_TexEnv( GL_REPLACE );
|
||||
} else if (pStage->stateBits & GLS_MULTITEXTURE_ENV) {
|
||||
glState.cntTexEnvExt = GLS_MULTITEXTURE_ENV;
|
||||
|
||||
GL_TexEnv(GL_COMBINE);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34161, 34165.0);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34176, 0x47057700);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34192, 0x44400000);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34177, 34168.0);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34193, 0x44400000);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34178, 0x47057700);
|
||||
qglTexEnvf(GL_TEXTURE_ENV, 34194, 770.0);
|
||||
} else {
|
||||
GL_TexEnv( tess.shader->multitextureEnv );
|
||||
GL_TexEnv( pStage->multitextureEnv );
|
||||
}
|
||||
|
||||
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] );
|
||||
|
||||
R_BindAnimatedImage( &pStage->bundle[1] );
|
||||
if (!input->dlightMap || !pStage->bundle[1].isLightmap) {
|
||||
R_BindAnimatedImage(&pStage->bundle[1]);
|
||||
} else {
|
||||
GL_Bind(&tr.identityLightImage[input->dlightMap]);
|
||||
}
|
||||
|
||||
R_DrawElements( input->numIndexes, input->indexes );
|
||||
|
||||
qglDisable(GL_TEXTURE_2D);
|
||||
//
|
||||
// disable texturing on TEXTURE1, then select TEXTURE0
|
||||
//
|
||||
//qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
qglDisable( GL_TEXTURE_2D );
|
||||
qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
GL_SelectTexture( 0 );
|
||||
}
|
||||
@@ -1158,21 +1204,21 @@ void RB_StageIteratorGeneric( void )
|
||||
// to avoid compiling those arrays since they will change
|
||||
// during multipass rendering
|
||||
//
|
||||
if ( tess.numPasses > 1 || input->shader->multitextureEnv )
|
||||
if (tess.numPasses > 1 || (tess.xstages[0] && tess.xstages[0]->multitextureEnv))
|
||||
{
|
||||
setArraysOnce = qfalse;
|
||||
qglDisableClientState (GL_COLOR_ARRAY);
|
||||
qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
|
||||
qglDisableClientState(GL_COLOR_ARRAY);
|
||||
qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
setArraysOnce = qtrue;
|
||||
|
||||
qglEnableClientState( GL_COLOR_ARRAY);
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors );
|
||||
qglEnableClientState(GL_COLOR_ARRAY);
|
||||
qglColorPointer(4, GL_UNSIGNED_BYTE, 0, tess.svars.colors);
|
||||
|
||||
qglEnableClientState( GL_TEXTURE_COORD_ARRAY);
|
||||
qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] );
|
||||
qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
qglTexCoordPointer(2, GL_FLOAT, 0, tess.svars.texcoords[0]);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+179
-145
@@ -405,19 +405,19 @@ static void ParseWaveForm( char **text, waveForm_t *wave )
|
||||
ParseTexMod
|
||||
===================
|
||||
*/
|
||||
static void ParseTexMod( char *_text, shaderStage_t *stage )
|
||||
static void ParseTexMod( char *_text, shaderStage_t *stage, int cntBundle )
|
||||
{
|
||||
const char *token;
|
||||
char **text = &_text;
|
||||
texModInfo_t *tmi;
|
||||
|
||||
if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) {
|
||||
if ( stage->bundle[cntBundle].numTexMods == TR_MAX_TEXMODS ) {
|
||||
ri.Error( ERR_DROP, "ERROR: too many tcMod unfoggedStages in shader '%s'\n", shader.name );
|
||||
return;
|
||||
}
|
||||
|
||||
tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods];
|
||||
stage->bundle[0].numTexMods++;
|
||||
tmi = &stage->bundle[cntBundle].texMods[stage->bundle[cntBundle].numTexMods];
|
||||
stage->bundle[cntBundle].numTexMods++;
|
||||
|
||||
token = COM_ParseExt( text, qfalse );
|
||||
|
||||
@@ -640,6 +640,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
char *token;
|
||||
int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0;
|
||||
qboolean depthMaskExplicit = qfalse;
|
||||
int cntBundle = 0;
|
||||
|
||||
stage->active = qtrue;
|
||||
stage->noMipMaps = shader_noMipMaps;
|
||||
@@ -673,23 +674,23 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
|
||||
if ( !Q_stricmp( token, "$whiteimage" ) )
|
||||
{
|
||||
stage->bundle[0].image[0] = tr.whiteImage;
|
||||
stage->bundle[cntBundle].image[0] = tr.whiteImage;
|
||||
continue;
|
||||
}
|
||||
else if ( !Q_stricmp( token, "$lightmap" ) )
|
||||
{
|
||||
stage->bundle[0].isLightmap = qtrue;
|
||||
stage->bundle[cntBundle].isLightmap = qtrue;
|
||||
if ( shader.lightmapIndex < 0 ) {
|
||||
stage->bundle[0].image[0] = tr.whiteImage;
|
||||
stage->bundle[cntBundle].image[0] = tr.whiteImage;
|
||||
} else {
|
||||
stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex];
|
||||
stage->bundle[cntBundle].image[0] = tr.lightmaps[shader.lightmapIndex];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
stage->bundle[0].image[0] = R_FindImageFile( token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
|
||||
if ( !stage->bundle[0].image[0] )
|
||||
stage->bundle[cntBundle].image[0] = R_FindImageFile( token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
|
||||
if ( !stage->bundle[cntBundle].image[0] )
|
||||
{
|
||||
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
|
||||
return qfalse;
|
||||
@@ -708,8 +709,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
stage->bundle[0].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP);
|
||||
if ( !stage->bundle[0].image[0] )
|
||||
stage->bundle[cntBundle].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP, r_forceClampToEdge->value ? GL_CLAMP_TO_EDGE : GL_CLAMP);
|
||||
if ( !stage->bundle[cntBundle].image[0] )
|
||||
{
|
||||
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
|
||||
return qfalse;
|
||||
@@ -726,7 +727,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name );
|
||||
return qfalse;
|
||||
}
|
||||
stage->bundle[0].imageAnimationSpeed = atof( token );
|
||||
stage->bundle[cntBundle].imageAnimationSpeed = atof( token );
|
||||
|
||||
// parse up to MAX_IMAGE_ANIMATIONS animations
|
||||
while ( 1 ) {
|
||||
@@ -736,15 +737,15 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
if ( !token[0] ) {
|
||||
break;
|
||||
}
|
||||
num = stage->bundle[0].numImageAnimations;
|
||||
num = stage->bundle[cntBundle].numImageAnimations;
|
||||
if ( num < MAX_IMAGE_ANIMATIONS ) {
|
||||
stage->bundle[0].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
|
||||
if ( !stage->bundle[0].image[num] )
|
||||
stage->bundle[cntBundle].image[0] = R_FindImageFile(token, !stage->noMipMaps, !stage->noPicMip, qfalse, GL_REPEAT, GL_REPEAT );
|
||||
if ( !stage->bundle[cntBundle].image[num] )
|
||||
{
|
||||
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
|
||||
return qfalse;
|
||||
}
|
||||
stage->bundle[0].numImageAnimations++;
|
||||
stage->bundle[cntBundle].numImageAnimations++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1146,32 +1147,32 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
if ( !Q_stricmp( token, "environment" ) )
|
||||
{
|
||||
shader.needsNormal = qtrue;
|
||||
stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED;
|
||||
stage->bundle[cntBundle].tcGen = TCGEN_ENVIRONMENT_MAPPED;
|
||||
}
|
||||
else if (!Q_stricmp(token, "environmentmodel"))
|
||||
{
|
||||
shader.needsNormal = qtrue;
|
||||
stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED2;
|
||||
stage->bundle[cntBundle].tcGen = TCGEN_ENVIRONMENT_MAPPED2;
|
||||
}
|
||||
else if (!Q_stricmp(token, "sunreflection"))
|
||||
{
|
||||
shader.needsNormal = qtrue;
|
||||
stage->bundle[0].tcGen = TCGEN_SUN_REFLECTION;
|
||||
stage->bundle[cntBundle].tcGen = TCGEN_SUN_REFLECTION;
|
||||
}
|
||||
else if ( !Q_stricmp( token, "lightmap" ) )
|
||||
{
|
||||
stage->bundle[0].tcGen = TCGEN_LIGHTMAP;
|
||||
stage->bundle[cntBundle].tcGen = TCGEN_LIGHTMAP;
|
||||
}
|
||||
else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) )
|
||||
{
|
||||
stage->bundle[0].tcGen = TCGEN_TEXTURE;
|
||||
stage->bundle[cntBundle].tcGen = TCGEN_TEXTURE;
|
||||
}
|
||||
else if ( !Q_stricmp( token, "vector" ) )
|
||||
{
|
||||
ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] );
|
||||
ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] );
|
||||
ParseVector( text, 3, stage->bundle[cntBundle].tcGenVectors[0] );
|
||||
ParseVector( text, 3, stage->bundle[cntBundle].tcGenVectors[1] );
|
||||
|
||||
stage->bundle[0].tcGen = TCGEN_VECTOR;
|
||||
stage->bundle[cntBundle].tcGen = TCGEN_VECTOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1194,7 +1195,7 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
strcat( buffer, " " );
|
||||
}
|
||||
|
||||
ParseTexMod( buffer, stage );
|
||||
ParseTexMod( buffer, stage, cntBundle );
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -1216,14 +1217,22 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
else if (!Q_stricmp(token, "nextBundle"))
|
||||
{
|
||||
if (!qglActiveTextureARB) {
|
||||
continue;
|
||||
ri.Printf(PRINT_ALL, "WARNING: " PRODUCT_NAME " requires a video card with multitexturing capability\n");
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
token = COM_ParseExt(text, qfalse);
|
||||
if (token[0] == 0) {
|
||||
if (token[0] && !Q_stricmp(token, "add")) {
|
||||
stage->multitextureEnv = GL_ADD;
|
||||
} else {
|
||||
stage->multitextureEnv = GL_MODULATE;
|
||||
}
|
||||
|
||||
// FIXME: unimplemented
|
||||
cntBundle++;
|
||||
if (cntBundle > NUM_TEXTURE_BUNDLES) {
|
||||
ri.Printf(PRINT_WARNING, "WARNING: too many nextBundle commands in shader '%s'\n", shader.name);
|
||||
return qfalse;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -1237,7 +1246,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text )
|
||||
// if cgen isn't explicitly specified, use either identity or identitylighting
|
||||
//
|
||||
if ( stage->rgbGen == CGEN_BAD ) {
|
||||
if ( blendSrcBits == 0 ||
|
||||
if ( (!cntBundle && !stage->bundle[0].isLightmap) ||
|
||||
blendSrcBits == 0 ||
|
||||
blendSrcBits == GLS_SRCBLEND_ONE ||
|
||||
blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) {
|
||||
stage->rgbGen = CGEN_IDENTITY_LIGHTING;
|
||||
@@ -1962,7 +1972,7 @@ static void ComputeStageIteratorFunc( void )
|
||||
{
|
||||
if ( !shader.polygonOffset )
|
||||
{
|
||||
if ( !shader.multitextureEnv )
|
||||
if ( !unfoggedStages[0].multitextureEnv )
|
||||
{
|
||||
if ( !shader.numDeforms )
|
||||
{
|
||||
@@ -1990,7 +2000,7 @@ static void ComputeStageIteratorFunc( void )
|
||||
{
|
||||
if ( !shader.numDeforms )
|
||||
{
|
||||
if ( shader.multitextureEnv )
|
||||
if (unfoggedStages[0].multitextureEnv )
|
||||
{
|
||||
shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture;
|
||||
goto done;
|
||||
@@ -2052,113 +2062,121 @@ Attempt to combine two unfoggedStages into a single multitexture stage
|
||||
FIXME: I think modulated add + modulated add collapses incorrectly
|
||||
=================
|
||||
*/
|
||||
static qboolean CollapseMultitexture( void ) {
|
||||
static void CollapseMultitexture(int *stagecounter) {
|
||||
int iUseCollapse;
|
||||
int stagenum;
|
||||
int abits, bbits;
|
||||
int i;
|
||||
shaderStage_t* stage;
|
||||
textureBundle_t tmpBundle;
|
||||
|
||||
if ( !qglActiveTextureARB ) {
|
||||
return qfalse;
|
||||
}
|
||||
for (stagenum = 0; stagenum < *stagecounter - 1; stagenum++) {
|
||||
stage = &unfoggedStages[stagenum];
|
||||
|
||||
// make sure both unfoggedStages are active
|
||||
if ( !unfoggedStages[0].active || !unfoggedStages[1].active ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// on voodoo2, don't combine different tmus
|
||||
if ( glConfig.driverType == GLDRV_VOODOO ) {
|
||||
if ( unfoggedStages[0].bundle[0].image[0]->TMU ==
|
||||
unfoggedStages[1].bundle[0].image[0]->TMU ) {
|
||||
return qfalse;
|
||||
// make sure both unfoggedStages are active
|
||||
if (!stage->active || !unfoggedStages[stagenum + 1].active) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
abits = unfoggedStages[0].stateBits;
|
||||
bbits = unfoggedStages[1].stateBits;
|
||||
|
||||
// make sure that both unfoggedStages have identical state other than blend modes
|
||||
if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) !=
|
||||
( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
|
||||
bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
|
||||
|
||||
// search for a valid multitexture blend function
|
||||
for ( i = 0; collapse[i].blendA != -1 ; i++ ) {
|
||||
if ( abits == collapse[i].blendA
|
||||
&& bbits == collapse[i].blendB ) {
|
||||
break;
|
||||
if (stage->multitextureEnv) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// nothing found
|
||||
if ( collapse[i].blendA == -1 ) {
|
||||
return qfalse;
|
||||
}
|
||||
// on voodoo2, don't combine different tmus
|
||||
if (glConfig.driverType == GLDRV_VOODOO) {
|
||||
if (stage->bundle[0].image[0]->TMU ==
|
||||
unfoggedStages[stagenum + 1].bundle[0].image[0]->TMU) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// GL_ADD is a separate extension
|
||||
if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) {
|
||||
return qfalse;
|
||||
}
|
||||
abits = stage->stateBits;
|
||||
bbits = unfoggedStages[stagenum + 1].stateBits;
|
||||
|
||||
// make sure waveforms have identical parameters
|
||||
if ( ( unfoggedStages[0].rgbGen != unfoggedStages[1].rgbGen ) ||
|
||||
( unfoggedStages[0].alphaGen != unfoggedStages[1].alphaGen ) ) {
|
||||
return qfalse;
|
||||
}
|
||||
// make sure that both unfoggedStages have identical state other than blend modes
|
||||
if ((abits & ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE)) !=
|
||||
(bbits & ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// an add collapse can only have identity colors
|
||||
if ( collapse[i].multitextureEnv == GL_ADD && unfoggedStages[0].rgbGen != CGEN_IDENTITY ) {
|
||||
return qfalse;
|
||||
}
|
||||
abits &= (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
|
||||
bbits &= (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
|
||||
|
||||
if ( unfoggedStages[0].rgbGen == CGEN_WAVEFORM )
|
||||
{
|
||||
if ( memcmp( &unfoggedStages[0].rgbWave,
|
||||
&unfoggedStages[1].rgbWave,
|
||||
sizeof( unfoggedStages[0].rgbWave ) ) )
|
||||
// search for a valid multitexture blend function
|
||||
for (iUseCollapse = 0; collapse[iUseCollapse].blendA != -1; iUseCollapse++) {
|
||||
if (abits == collapse[iUseCollapse].blendA
|
||||
&& bbits == collapse[iUseCollapse].blendB) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// nothing found
|
||||
if (collapse[iUseCollapse].blendA == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// GL_ADD is a separate extension
|
||||
if (collapse[iUseCollapse].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure waveforms have identical parameters
|
||||
if ((stage->rgbGen != unfoggedStages[stagenum + 1].rgbGen) ||
|
||||
(stage->alphaGen != unfoggedStages[stagenum + 1].alphaGen)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// an add collapse can only have identity colors
|
||||
if (collapse[iUseCollapse].multitextureEnv == GL_ADD && stage->rgbGen != CGEN_IDENTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stage->rgbGen == CGEN_WAVEFORM)
|
||||
{
|
||||
return qfalse;
|
||||
if (memcmp(&stage->rgbWave,
|
||||
&unfoggedStages[stagenum + 1].rgbWave,
|
||||
sizeof(stage->rgbWave)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( unfoggedStages[0].alphaGen == CGEN_WAVEFORM )
|
||||
{
|
||||
if ( memcmp( &unfoggedStages[0].alphaWave,
|
||||
&unfoggedStages[1].alphaWave,
|
||||
sizeof( unfoggedStages[0].alphaWave ) ) )
|
||||
if (stage->alphaGen == CGEN_WAVEFORM)
|
||||
{
|
||||
return qfalse;
|
||||
if (memcmp(&stage->alphaWave,
|
||||
&unfoggedStages[stagenum + 1].alphaWave,
|
||||
sizeof(stage->alphaWave)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// make sure that lightmaps are in bundle 1 for 3dfx
|
||||
if (stage->bundle[0].isLightmap)
|
||||
{
|
||||
tmpBundle = stage->bundle[0];
|
||||
stage->bundle[0] = unfoggedStages[stagenum + 1].bundle[0];
|
||||
stage->bundle[1] = tmpBundle;
|
||||
}
|
||||
else
|
||||
{
|
||||
stage->bundle[1] = unfoggedStages[stagenum + 1].bundle[0];
|
||||
}
|
||||
|
||||
// set the new blend state bits
|
||||
stage->multitextureEnv = collapse[iUseCollapse].multitextureEnv;
|
||||
stage->stateBits &= ~(GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS);
|
||||
stage->stateBits |= collapse[iUseCollapse].multitextureBlend;
|
||||
|
||||
//
|
||||
// move down subsequent shaders
|
||||
//
|
||||
if (stagenum + 2 < MAX_SHADER_STAGES) {
|
||||
memmove(&unfoggedStages[stagenum + 1], &unfoggedStages[stagenum + 2], sizeof(unfoggedStages[0]) * (MAX_SHADER_STAGES - 2 - stagenum));
|
||||
}
|
||||
|
||||
Com_Memset(&unfoggedStages[MAX_SHADER_STAGES - 1], 0, sizeof(unfoggedStages[0]));
|
||||
(*stagecounter)--;
|
||||
}
|
||||
|
||||
|
||||
// make sure that lightmaps are in bundle 1 for 3dfx
|
||||
if ( unfoggedStages[0].bundle[0].isLightmap )
|
||||
{
|
||||
tmpBundle = unfoggedStages[0].bundle[0];
|
||||
unfoggedStages[0].bundle[0] = unfoggedStages[1].bundle[0];
|
||||
unfoggedStages[0].bundle[1] = tmpBundle;
|
||||
}
|
||||
else
|
||||
{
|
||||
unfoggedStages[0].bundle[1] = unfoggedStages[1].bundle[0];
|
||||
}
|
||||
|
||||
// set the new blend state bits
|
||||
shader.multitextureEnv = collapse[i].multitextureEnv;
|
||||
unfoggedStages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS );
|
||||
unfoggedStages[0].stateBits |= collapse[i].multitextureBlend;
|
||||
|
||||
//
|
||||
// move down subsequent shaders
|
||||
//
|
||||
memmove( &unfoggedStages[1], &unfoggedStages[2], sizeof( unfoggedStages[0] ) * ( MAX_SHADER_STAGES - 2 ) );
|
||||
Com_Memset( &unfoggedStages[MAX_SHADER_STAGES-1], 0, sizeof( unfoggedStages[0] ) );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2425,6 +2443,7 @@ from the current global working shader
|
||||
*/
|
||||
static shader_t *FinishShader( void ) {
|
||||
int stage;
|
||||
int bundle;
|
||||
qboolean hasLightmapStage;
|
||||
qboolean vertexLightmap;
|
||||
|
||||
@@ -2478,25 +2497,34 @@ static shader_t *FinishShader( void ) {
|
||||
//
|
||||
// default texture coordinate generation
|
||||
//
|
||||
if ( pStage->bundle[0].isLightmap ) {
|
||||
if ( pStage->bundle[0].tcGen == TCGEN_BAD ) {
|
||||
pStage->bundle[0].tcGen = TCGEN_LIGHTMAP;
|
||||
for (bundle = 2; bundle > 0; bundle--) {
|
||||
if (pStage->bundle[bundle - 1].isLightmap) {
|
||||
if (pStage->bundle[bundle - 1].tcGen == TCGEN_BAD) {
|
||||
pStage->bundle[bundle - 1].tcGen = TCGEN_LIGHTMAP;
|
||||
}
|
||||
hasLightmapStage = qtrue;
|
||||
}
|
||||
hasLightmapStage = qtrue;
|
||||
} else {
|
||||
if ( pStage->bundle[0].tcGen == TCGEN_BAD ) {
|
||||
pStage->bundle[0].tcGen = TCGEN_TEXTURE;
|
||||
else {
|
||||
if (pStage->bundle[bundle - 1].tcGen == TCGEN_BAD) {
|
||||
pStage->bundle[bundle - 1].tcGen = TCGEN_TEXTURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not a true lightmap but we want to leave existing
|
||||
// behaviour in place and not print out a warning
|
||||
//if (pStage->rgbGen == CGEN_VERTEX) {
|
||||
// vertexLightmap = qtrue;
|
||||
//}
|
||||
|
||||
// not a true lightmap but we want to leave existing
|
||||
// behaviour in place and not print out a warning
|
||||
//if (pStage->rgbGen == CGEN_VERTEX) {
|
||||
// vertexLightmap = qtrue;
|
||||
//}
|
||||
|
||||
|
||||
if (pStage->multitextureEnv && pStage->bundle[0].isLightmap) {
|
||||
//
|
||||
// exchange bundle
|
||||
//
|
||||
textureBundle_t tmp = pStage->bundle[0];
|
||||
pStage->bundle[0] = pStage->bundle[1];
|
||||
pStage->bundle[1] = tmp;
|
||||
}
|
||||
|
||||
//
|
||||
// determine sort order and fog color adjustment
|
||||
@@ -2537,7 +2565,7 @@ static shader_t *FinishShader( void ) {
|
||||
// look for multitexture potential
|
||||
//
|
||||
if (stage > 1 && qglActiveTextureARB) {
|
||||
CollapseMultitexture();
|
||||
CollapseMultitexture(&stage);
|
||||
};
|
||||
|
||||
// FIXME: fog shader
|
||||
@@ -2874,6 +2902,8 @@ void R_ShaderList_f (void) {
|
||||
|
||||
count = 0;
|
||||
for ( i = 0 ; i < tr.numShaders ; i++ ) {
|
||||
int stage;
|
||||
|
||||
if ( ri.Cmd_Argc() > 1 ) {
|
||||
shader = tr.sortedShaders[i];
|
||||
} else {
|
||||
@@ -2887,15 +2917,19 @@ void R_ShaderList_f (void) {
|
||||
} else {
|
||||
ri.Printf (PRINT_ALL, " ");
|
||||
}
|
||||
if ( shader->multitextureEnv == GL_ADD ) {
|
||||
ri.Printf( PRINT_ALL, "MT(a) " );
|
||||
} else if ( shader->multitextureEnv == GL_MODULATE ) {
|
||||
ri.Printf( PRINT_ALL, "MT(m) " );
|
||||
} else if ( shader->multitextureEnv == GL_DECAL ) {
|
||||
ri.Printf( PRINT_ALL, "MT(d) " );
|
||||
} else {
|
||||
ri.Printf( PRINT_ALL, " " );
|
||||
|
||||
for (stage = 0; shader->unfoggedStages[stage] && shader->unfoggedStages[stage]->active; stage++) {
|
||||
if (shader->unfoggedStages[stage]->multitextureEnv == GL_ADD ) {
|
||||
ri.Printf( PRINT_ALL, "MT(a) " );
|
||||
} else if (shader->unfoggedStages[stage]->multitextureEnv == GL_MODULATE ) {
|
||||
ri.Printf( PRINT_ALL, "MT(m) " );
|
||||
} else if (shader->unfoggedStages[stage]->multitextureEnv == GL_DECAL ) {
|
||||
ri.Printf( PRINT_ALL, "MT(d) " );
|
||||
} else {
|
||||
ri.Printf( PRINT_ALL, " " );
|
||||
}
|
||||
}
|
||||
|
||||
if ( shader->explicitlyDefined ) {
|
||||
ri.Printf( PRINT_ALL, "E " );
|
||||
} else {
|
||||
|
||||
@@ -718,7 +718,79 @@ void RB_SurfaceSkip( void *surf ) {
|
||||
}
|
||||
|
||||
void RB_DrawTerrainTris(srfTerrain_t* p) {
|
||||
// FIXME: unimplemented
|
||||
int i;
|
||||
terraInt numv;
|
||||
int dlightBits;
|
||||
|
||||
if (tess.numVertexes + p->nVerts >= SHADER_MAX_VERTEXES || tess.numIndexes + p->nTris * 3 >= SHADER_MAX_INDEXES) {
|
||||
RB_CheckOverflow(p->nVerts, 3 * p->nTris);
|
||||
}
|
||||
|
||||
dlightBits = p->dlightBits[backEnd.smpFrame];
|
||||
tess.dlightBits |= dlightBits;
|
||||
if (p->dlightMap[backEnd.smpFrame])
|
||||
{
|
||||
float lmScale = (1 / 128.0) / p->lmapStep;
|
||||
|
||||
for (i = p->iVertHead; i; i = g_pVert[i].iNext) {
|
||||
assert(tess.numVertexes < SHADER_MAX_VERTEXES);
|
||||
|
||||
VectorCopy(g_pVert[i].xyz, tess.xyz[tess.numVertexes]);
|
||||
tess.texCoords[tess.numVertexes][0][0] = g_pVert[i].texCoords[0][0];
|
||||
tess.texCoords[tess.numVertexes][0][1] = g_pVert[i].texCoords[0][1];
|
||||
tess.texCoords[tess.numVertexes][1][0] = g_pVert[i].texCoords[1][0] * lmScale + p->lmapX;
|
||||
tess.texCoords[tess.numVertexes][1][1] = g_pVert[i].texCoords[1][1] * lmScale + p->lmapY;
|
||||
tess.normal[tess.numVertexes][0] = 0;
|
||||
tess.normal[tess.numVertexes][1] = 0;
|
||||
tess.normal[tess.numVertexes][2] = 1.0;
|
||||
tess.vertexColors[tess.numVertexes][0] = -1;
|
||||
tess.vertexColors[tess.numVertexes][1] = -1;
|
||||
tess.vertexColors[tess.numVertexes][2] = -1;
|
||||
tess.vertexColors[tess.numVertexes][3] = -1;
|
||||
|
||||
g_pVert[i].iVertArray = tess.numVertexes;
|
||||
tess.numVertexes++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = p->iVertHead; i; i = g_pVert[i].iNext) {
|
||||
assert(tess.numVertexes < SHADER_MAX_VERTEXES);
|
||||
|
||||
VectorCopy(g_pVert[i].xyz, tess.xyz[tess.numVertexes]);
|
||||
tess.texCoords[tess.numVertexes][0][0] = g_pVert[i].texCoords[0][0];
|
||||
tess.texCoords[tess.numVertexes][0][1] = g_pVert[i].texCoords[0][1];
|
||||
tess.texCoords[tess.numVertexes][1][0] = g_pVert[i].texCoords[1][0];
|
||||
tess.texCoords[tess.numVertexes][1][1] = g_pVert[i].texCoords[1][1];
|
||||
tess.vertexDlightBits[tess.numVertexes] = dlightBits;
|
||||
tess.normal[tess.numVertexes][0] = 0;
|
||||
tess.normal[tess.numVertexes][1] = 0;
|
||||
tess.normal[tess.numVertexes][2] = 1.0;
|
||||
tess.vertexColors[tess.numVertexes][0] = -1;
|
||||
tess.vertexColors[tess.numVertexes][1] = -1;
|
||||
tess.vertexColors[tess.numVertexes][2] = -1;
|
||||
tess.vertexColors[tess.numVertexes][3] = -1;
|
||||
|
||||
g_pVert[i].iVertArray = tess.numVertexes;
|
||||
tess.numVertexes++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = p->iTriHead; i; i = g_pTris[i].iNext)
|
||||
{
|
||||
assert(tess.numVertexes < SHADER_MAX_INDEXES);
|
||||
|
||||
//
|
||||
// Make sure these can be drawn
|
||||
//
|
||||
if (g_pTris[i].byConstChecks & 4)
|
||||
{
|
||||
tess.indexes[tess.numIndexes] = g_pVert[g_pTris[i].iPt[0]].iVertArray;
|
||||
tess.indexes[tess.numIndexes + 1] = g_pVert[g_pTris[i].iPt[1]].iVertArray;
|
||||
tess.indexes[tess.numIndexes + 2] = g_pVert[g_pTris[i].iPt[2]].iVertArray;
|
||||
tess.numIndexes += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = {
|
||||
|
||||
+1366
-21
File diff suppressed because it is too large.
Load diff
@@ -501,7 +501,14 @@ static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits )
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: terrain
|
||||
if (r_drawterrain->integer)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < node->numTerraPatches; i++) {
|
||||
R_MarkTerrainPatch(tr.world->visTerraPatches[node->firstTerraPatch + i]);
|
||||
}
|
||||
}
|
||||
// FIXME: static decals
|
||||
// FIXME: static models
|
||||
}
|
||||
|
||||
@@ -237,16 +237,16 @@ static char *XLateKey(XKeyEvent *ev, int *key)
|
||||
case XK_Pause: *key = K_PAUSE; break;
|
||||
|
||||
case XK_Shift_L:
|
||||
case XK_Shift_R: *key = K_SHIFT; break;
|
||||
case XK_Shift_R: *key = K_LSHIFT; break;
|
||||
|
||||
case XK_Execute:
|
||||
case XK_Control_L:
|
||||
case XK_Control_R: *key = K_CTRL; break;
|
||||
case XK_Control_R: *key = K_LCTRL; break;
|
||||
|
||||
case XK_Alt_L:
|
||||
case XK_Meta_L:
|
||||
case XK_Alt_R:
|
||||
case XK_Meta_R: *key = K_ALT; break;
|
||||
case XK_Meta_R: *key = K_LALT; break;
|
||||
|
||||
case XK_KP_Begin: *key = K_KP_5; break;
|
||||
|
||||
|
||||
Reference in new issue
Block a user