From: havoc Date: Mon, 24 Mar 2008 20:47:24 +0000 (+0000) Subject: Patch from Qantourisc adding support for his 3D sound library X-Git-Tag: xonotic-v0.1.0preview~2296 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=f14470cac73d0fc2101dfb375e24fbcec871908c;p=xonotic%2Fdarkplaces.git Patch from Qantourisc adding support for his 3D sound library git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8232 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/makefile b/makefile index 45b2d97f..5c95d055 100644 --- a/makefile +++ b/makefile @@ -225,6 +225,10 @@ ifeq ($(DP_SOUND_API), WIN) LIB_SOUND=$(LIB_SND_WIN) endif +ifeq ($(DP_SOUND_API),3DRAS) + OBJ_SOUND=$(OBJ_SND_3DRAS) + LIB_SOUND=$(LIB_SND_3DRAS) +endif ##### Extra CFLAGS ##### diff --git a/makefile.inc b/makefile.inc index 93fccf49..279fa2e9 100644 --- a/makefile.inc +++ b/makefile.inc @@ -58,6 +58,9 @@ LIB_SND_BSD=$(LIB_SND_MODPLUG) OBJ_SND_WIN=$(OBJ_SND_COMMON) snd_win.o LIB_SND_WIN=$(LIB_SND_MODPLUG) +# Qantourisc's 3D Realtime Acoustic Lib (3D RAS) +OBJ_SND_3DRAS=snd_3dras.o +LIB_SND_3DRAS= # CD objects OBJ_NOCD=cd_null.o @@ -416,6 +419,9 @@ sys_sdl.o: sys_sdl.c snd_sdl.o: snd_sdl.c $(DO_CC) $(CFLAGS_SDL) +snd_3dras.o: snd_3dras.c + $(DO_CC) + cd_sdl.o: cd_sdl.c $(DO_CC) $(CFLAGS_SDL) diff --git a/snd_3dras.c b/snd_3dras.c new file mode 100644 index 00000000..5ef28bc5 --- /dev/null +++ b/snd_3dras.c @@ -0,0 +1,1024 @@ +// BSD + +#include "quakedef.h" +#include "snd_3dras_typedefs.h" +#include "snd_3dras.h" + +cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1", "volume of background music (such as CD music or replacement files such as sound/cdtracks/track002.ogg)"}; +cvar_t volume = {CVAR_SAVE, "volume", "0.7", "volume of sound effects"}; +cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1", "volume of ambient sound effects (such as swampy sounds at the start of e1m2)"}; +cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"}; +cvar_t snd_mutewhenidle = {CVAR_SAVE, "snd_mutewhenidle", "1", "whether to disable sound output when game window is inactive"}; +cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory)"}; +static cvar_t snd_precache = {0, "snd_precache", "1", "loads sounds before they are used"}; + +static dllhandle_t ras_dll = NULL; +// This values is used as a multiple version support check AND to check if the lib has been loaded with success (its 0 if it failed) +int ras_version; + +static mempool_t *snd_mempool; +static sfx_t sfx_list ={//This is a header, never contains only useful data, only the first next (makes the code less complex, later) + NULL, //next + "", //name[MAX_QPATH]; + NULL, //sounddata + 0, //locks + 0 //flags + //0, //loopstart, + //0 //total_length +}; +static unsigned int channel_id_count=0; +static channel_t channel_list={ + NULL, //next + NULL, //soundevent + 0, //entnum + 0, //entchannel + 0 //id +}; +static entnum_t entnum_list={ + NULL,// *next; + 0, // entnum; + {0.0,0.0,0.0}, //lastloc + NULL,// *soundsource;// This is also used to indicate a unused slot (when it's pointing to 0) +}; + +int updatecount=0; +int soundblocked=0; +int openframe; +void* soundworld; +void* listener; +float listener_location [3]; + +//1 qu = 0.0381 meter aka 38.1 mm +//2^17 qu's is the max map size in DP +//3DRAS uses atleast 32 bit to store it's locations. +//So the smallest possible step is 0.0381*2^17/2^(32) +// =~ 1.16 nm so let's pick 2 to be safe +static float DP_Ras_UnitSize=(float)2/1000/1000; //2 nm +static float DP_Ras_VolumeScale=0.075; +//static float QU_Size = 0.0381; //meter +//static float DP_QU_Ras_Scale=QU_Size/DP_Ras_UnitSize; +static float DP_QU_Ras_Scale=19050; +static void* (*ras_delete )(void*); +static int (*ras_getversion )(); +static void* (*ras_soundworld_new )(SampleRate, WaveLength); +static void (*ras_soundworld_destroy )(void*); +static void (*ras_soundworld_endframe )(void*); +static int (*ras_soundworld_beginframe )(void*); +static void (*ras_soundworld_setmainlistener )(void*,void*); +static void (*ras_soundworld_setscale )(void*,const Scale); +static void* (*ras_fileinputwhole_new )(unsigned char*, Index); +static void* (*ras_audiodecoderwav_new )(void*, int); +static void* (*ras_audiodecoderogg_new )(void*); +static void* (*ras_sounddataoneshot_new )(void*,WaveLength,Amount); +static void* (*ras_sounddataloop_new )(void*,WaveLength,Amount); +static void* (*ras_listener_new )(void*,Location*,Scalar*); +static void* (*ras_listener_setlocation )(void*,Location*); +static void* (*ras_listener_setrotation )(void*,Scalar *,Scalar*,Scalar*); +static void* (*ras_soundsource_new )(void*,SoundVolume,Location*); +static int (*ras_soundsource_ended )(void*); +static void (*ras_soundsource_setlocation )(void*,Location*); +static void* (*ras_soundevent_new )(void*,void*,void*,SoundPower,Ratio); +static void (*ras_soundevent_setsoundpower )(void*,SoundPower); +static int (*ras_soundevent_ended )(void*); +static int (*ras_setcoordinatesystem )(Location*,Location*,Location*); +static int (*ras_testrotation )(Scalar *,Scalar *,Scalar *); + +// #define RAS_PRINT //Comment out for to not print extra crap. + +static dllfunction_t ras_funcs[] = +{ + {"Delete" ,(void**) &ras_delete }, + {"SetCoordinateSystem" ,(void**) &ras_setcoordinatesystem }, + {"TestRotation" ,(void**) &ras_testrotation }, + {"GetVersion" ,(void**) &ras_getversion }, + {"SoundWorld_New" ,(void**) &ras_soundworld_new }, + {"SoundWorld_Destroy" ,(void**) &ras_soundworld_destroy }, + {"SoundWorld_EndFrame" ,(void**) &ras_soundworld_endframe }, + {"SoundWorld_BeginFrame" ,(void**) &ras_soundworld_beginframe }, + {"FileInputWhile_New" ,(void**) &ras_fileinputwhole_new }, + {"AudioDecoderFileWav_New" ,(void**) &ras_audiodecoderwav_new }, + {"AudioDecoderFileOgg_New" ,(void**) &ras_audiodecoderogg_new }, + {"SoundDataAudioDecoderOneShot_New" ,(void**) &ras_sounddataoneshot_new }, + //{"SoundDataAudioDecoderFileLoop_New" ,(void**) &ras_sounddataloop_new }, + {"SoundWorld_SetMainListener" ,(void**) &ras_soundworld_setmainlistener }, + {"SoundWorld_SetScale" ,(void**) &ras_soundworld_setscale }, + {"ListenerPlayer_New" ,(void**) &ras_listener_new }, + {"ListenerPlayer_SetLocation" ,(void**) &ras_listener_setlocation }, + {"ListenerPlayer_SetRotation_InVectors" ,(void**) &ras_listener_setrotation }, + {"SoundSource_Ended" ,(void**) &ras_soundsource_ended }, + {"SoundSourcePoint_New" ,(void**) &ras_soundsource_new }, + {"SoundSourcePoint_SetLocation" ,(void**) &ras_soundsource_setlocation }, + {"SoundEvent_New" ,(void**) &ras_soundevent_new }, + {"SoundEvent_Ended" ,(void**) &ras_soundevent_ended }, + {"SoundEvent_SetSoundPower" ,(void**) &ras_soundevent_setsoundpower }, + { NULL , NULL } +}; +static const char* ras_dllname [] = +{ + #if defined(WIN64) + "3dras64.dll", + #elif defined(WIN32) + "3dras32.dll", + #elif defined(MACOSX) + "3dras.dylib", + #else + "3dras.so", + #endif + NULL +}; + +// --- entnum_t List functions ---- +void entnum_new(entnum_t** prev, entnum_t** new){ //Adds a new item to the start of the list and sets the pointers. + (*new)=Mem_Alloc(snd_mempool,sizeof(entnum_t)); + if(&new){ + (*new)->next=entnum_list.next; + entnum_list.next=(*new); + (*prev)=&entnum_list; + }else{ + Con_Printf("Could not allocate memory for a new entnum_t"); + } +} +void entnum_begin(entnum_t** prev, entnum_t** now){ //Goes to the beginning of the list and sets the pointers. + (*prev)=&entnum_list; + (*now )=entnum_list.next; +} +void entnum_next(entnum_t** prev, entnum_t** now){ //Goes to the next element + (*prev)=(*now); + (*now )=(*now)->next; +} +void entnum_delete_and_next(entnum_t** prev, entnum_t** now){ //Deletes the element and goes to the next element + entnum_t* next; + next=(*now)->next; + if((*now)->rasptr) ras_delete((*now)->rasptr); + Mem_Free(*now); + (*now)=next; + (*prev)->next=(*now); +} +// --- End Of entnum_t List functions ---- + +// --- channel_t List functions ---- +void channel_new(channel_t** prev, channel_t** new){ //Adds a new item to the start of the list and sets the pointers. + (*new)=Mem_Alloc(snd_mempool,sizeof(channel_t)); + if(&new){ + (*new)->next=channel_list.next; + channel_list.next=(*new); + (*prev)=&channel_list; + }else{ + Con_Printf("Could not allocate memory for a new channel_t"); + } +} +void channel_begin(channel_t** prev, channel_t** now){ //Goes to the beginning of the list and sets the pointers. + (*prev)=&channel_list; + (*now )=channel_list.next; +} +void channel_next(channel_t** prev, channel_t** now){ //Goes to the next element + (*prev)=(*now ); + (*now )=(*now)->next; +} +void channel_delete_and_next(channel_t** prev, channel_t** now){ //Deletes the element and goes to the next element + channel_t* next; + next=(*now)->next; + if((*now)->rasptr) ras_delete((*now)->rasptr); + Mem_Free(*now); + (*now)=next; + (*prev)->next=(*now); +} +// --- End Of channel_t List functions ---- + +// --- sfx_t List functions ---- +void sfx_new(sfx_t** prev, sfx_t** new){ //Adds a new item to the start of the list and sets the pointers. + (*new)=Mem_Alloc(snd_mempool,sizeof(sfx_t)); + if(&new){ + (*new)->next=sfx_list.next; + sfx_list.next=(*new); + (*prev)=&sfx_list; + }else{ + Con_Printf("Could not allocate memory for a new sfx_t"); + } +} +void sfx_begin(sfx_t** prev, sfx_t** now){ //Goes to the beginning of the list and sets the pointers. + (*prev)=&sfx_list; + (*now )=sfx_list.next; +} +void sfx_next(sfx_t** prev, sfx_t** now){ //Goes to the next element + (*prev)=(*now ); + (*now )=(*now)->next; +} +void sfx_delete_and_next(sfx_t** prev, sfx_t** now){ //Deletes the element and goes to the next element + sfx_t* next; + next=(*now)->next; + if((*now)->rasptr) ras_delete((*now)->rasptr); + Mem_Free(*now); + (*now)=next; + (*prev)->next=(*now); +} +// --- End Of sfx_t List functions ---- + +void channel_new_smart(channel_t** prev, channel_t** now){ + channel_new(prev,now); + ++channel_id_count; + (*now)->id=channel_id_count; +} +void Free_Unlocked_Sfx(void){ + sfx_t *prev, *now; + sfx_begin(&prev,&now); + while(now){ + if( + !(now->flags & SFXFLAG_SERVERSOUND) && + now->locks<=0 + ){ + sfx_delete_and_next(&prev,&now); + }else{ + sfx_next(&prev,&now); + } + } +} +void DP_To_Ras_Location(const float in[3],Location out[3]){ + out[0]=(Location)(in[0]*DP_QU_Ras_Scale ); + out[1]=(Location)(in[1]*DP_QU_Ras_Scale ); + out[2]=(Location)(in[2]*DP_QU_Ras_Scale ); +} +void S_Restart_f(void){ + S_Shutdown(); + S_Startup(); +} +static void S_Play_Common (float fvol, float attenuation){ + int i; + char name [MAX_QPATH]; + sfx_t *sfx; + if(ras_version>0 && ras_dll){ + #ifdef RAS_PRINT + Con_Printf("Called S_Play_Common\n"); + Con_Printf("Does this need to be location in depend channel ?\n"); + #endif + + i = 1; + while (i < Cmd_Argc ()) + { + // Get the name, and appends ".wav" as an extension if there's none + strlcpy (name, Cmd_Argv (i), sizeof (name)); + if (!strrchr (name, '.')) + strlcat (name, ".wav", sizeof (name)); + i++; + + // If we need to get the volume from the command line + if (fvol == -1.0f) + { + fvol = atof (Cmd_Argv (i)); + i++; + } + + sfx = S_PrecacheSound (name, true, false); + if (sfx) + S_StartSound (-1, 0, sfx, listener_location, fvol, attenuation); + } + } +} +static void S_Play_f(void){ + S_Play_Common (1.0f, 1.0f); +} +static void S_Play2_f(void){ + S_Play_Common (1.0f, 0.0f); +} +static void S_PlayVol_f(void){ + S_Play_Common (-1.0f, 0.0f); +} +static void S_SoundList_f(void){ + channel_t *prev_c, *now_c; + sfx_t *prev_s, *now_s; + entnum_t *prev_e, *now_e; + int count_c,count_s,count_e; + + if(ras_version>0 && ras_dll){ + + Con_Printf("Sfx (SoundDatas) :\n" + "------------------\n" + "Locks\tflags\tpointer\tName\n"); + count_s=0; + sfx_begin(&prev_s,&now_s); + while(now_s){ + ++count_s; + Con_Printf("%i\t%i\t%i\t%s\n", + now_s->locks, now_s->flags, now_s->rasptr!=NULL, now_s->name + ); + sfx_next(&prev_s,&now_s); + } + + Con_Printf("Entnum (SoundSources) :\n" + "-----------------------\n" + "Ent\tpointer\n"); + count_e=0; + entnum_begin(&prev_e,&now_e); + while(now_e){ + ++count_e; + Con_Printf("%i\t%i\n", + now_e->entnum, now_e->rasptr!=NULL + ); + entnum_next(&prev_e,&now_e); + } + + Con_Printf("Channels (SoundEvents) :\n" + "------------------------\n" + "Ent\tChannel\tID\tpointer\n"); + count_c=0; + channel_begin(&prev_c,&now_c); + while(now_c){ + ++count_c; + Con_Printf("%i\t%i\t%i\t%i\n", + now_c->entnum, now_c->entchannel, now_c->id, now_c->rasptr!=NULL + ); + channel_next(&prev_c,&now_c); + } + + Con_Printf( + "Count:\n" + "------\n" + "Channels: %i\n" + "Sfx's: %i\n" + "Entities: %i\n", + count_c,count_s,count_e + ); + } +} +void Free_All_sfx(){ + sfx_t *prev, *now; + sfx_begin(&prev,&now); + while(now) sfx_delete_and_next(&prev,&now); +} +void Free_All_channel(){ + channel_t *prev, *now; + channel_begin(&prev,&now); + while(now) channel_delete_and_next(&prev,&now); +} +void Free_All_entnum(){ + entnum_t *prev, *now; + entnum_begin(&prev,&now); + while(now) entnum_delete_and_next(&prev,&now); +} +void S_Init (void){ + Location up[3],right[3],front[3]; + ras_version=0; + snd_mempool = Mem_AllocPool("sound", 0, NULL); + if(ras_dll) Con_Printf( "3D RAS already loaded ... (this indicates a bug)\n"); + if (Sys_LoadLibrary (ras_dllname, &ras_dll, ras_funcs)) + { + Con_Printf ("Loading 3D RAS succeeded\n"); + Con_Printf ("Checking the lib version\n"); + ras_version=ras_getversion(); + if (ras_version>0){ + + Con_Printf ("Version %i found\n",ras_version); + Cvar_RegisterVariable(&volume); + Cvar_RegisterVariable(&bgmvolume); + Cvar_RegisterVariable(&snd_staticvolume); + Cvar_RegisterVariable(&snd_precache); + + Cmd_AddCommand("play", S_Play_f, "play a sound at your current location (not heard by anyone else)"); + Cmd_AddCommand("snd_play", S_Play_f, "play a sound at your current location (not heard by anyone else)"); + Cmd_AddCommand("play2", S_Play2_f, "play a sound globally throughout the level (not heard by anyone else)"); + Cmd_AddCommand("snd_play2", S_Play2_f, "play a sound globally throughout the level (not heard by anyone else)"); + Cmd_AddCommand("playvol", S_PlayVol_f, "play a sound at the specified volume level at your current location (not heard by anyone else)"); + Cmd_AddCommand("snd_playvol", S_PlayVol_f, "play a sound at the specified volume level at your current location (not heard by anyone else)"); + Cmd_AddCommand("stopsound", S_StopAllSounds, "silence"); + Cmd_AddCommand("soundlist", S_SoundList_f, "list loaded sounds"); + Cmd_AddCommand("snd_stopsound", S_StopAllSounds, "silence"); + Cmd_AddCommand("snd_soundlist", S_SoundList_f, "list loaded sounds"); + Cmd_AddCommand("snd_restart", S_Restart_f, "restart sound system"); + Cmd_AddCommand("snd_shutdown", S_Shutdown, "shutdown the sound system keeping the dll loaded"); + Cmd_AddCommand("snd_startup", S_Startup, "start the sound system"); + Cmd_AddCommand("snd_unloadallsounds", S_UnloadAllSounds_f, "unload all sound files"); + + //Set the coordinate system inside the lib equal to the one inside dp: + up[0]= 0 , up[1]= 0 , up[2]=1; + right[0]= 0 ,right[1]=-1 , right[2]=0; + front[0]= 1 ,front[1]= 0 , front[2]=0; + if(ras_setcoordinatesystem(right,up,front)==0){ + Con_Printf("Failed to set the Coordinate System\n"); + ras_version=0; + } + }else{ + Con_Printf ("Failed to get the lib version\n"); + Sys_UnloadLibrary (&ras_dll); + ras_dll=0; + } + }else{ + ras_dll=0; + Con_Printf ("Loading 3D RAS failed\n"); + Sys_UnloadLibrary (&ras_dll); + } +} +void S_Terminate (void){ + if(ras_dll){ + S_Shutdown(); + Free_All_sfx(); // <= The only valid place to free the sfx. + Sys_UnloadLibrary(&ras_dll); + ras_dll=0; + ras_version=0; + } +} + +void S_Startup (void){ + Location loc[3]={0, 0, 0}; + Scalar rot[4]={1.0, 0, 0, 0}; + if(ras_version>0 && ras_dll){ + channel_id_count=1; + soundworld= ras_soundworld_new(48000,0.1); + if(soundworld==0){ + Con_Printf("Failed to start a SoundWorld\n"); + }else{ + Con_Printf("Succeeded in starting a new SoundWorld\n"); + listener=ras_listener_new(soundworld,loc,rot); + ras_soundworld_setmainlistener(soundworld,listener); + openframe = ras_soundworld_beginframe(soundworld); + ras_soundworld_setscale(soundworld,DP_Ras_UnitSize); + } + } +} +void S_Shutdown (void){ + if(ras_version>0 && ras_dll && soundworld){ + if(openframe) ras_soundworld_endframe(soundworld); + + //Order doesn't really matter because the lib takes care of the references + //Free_All_sfx(); <= DO NOT FREE SFX ... they just keep sending in the old sfx causing havoc. + Free_All_channel(); + Free_All_entnum(); + + ras_soundworld_destroy(soundworld); + soundworld=ras_delete(soundworld); + if(soundworld){ + Con_Printf("Failed to stop the SoundWorld\n"); + }else{ + Con_Printf("Succeeded in stopping the SoundWorld\n"); + } + } +} +void S_UnloadAllSounds_f(void){ + if(ras_version>0 && ras_dll){ + Free_All_sfx(); + } +} + +void S_Update(const matrix4x4_t *listener_matrix){ + float forward [3]; + float left [3]; + float up [3]; + float float3 [3]; + Location location3 [3]; + entnum_t *prev_e, *now_e; + channel_t *prev_c, *now_c; + if(ras_version>0 && ras_dll && soundworld){ + Matrix4x4_ToVectors(listener_matrix,forward,left,up,listener_location); //Add the new player location. + if(openframe){ + VectorNegate(left,left); + DP_To_Ras_Location(listener_location,location3); + ras_listener_setlocation(listener,location3); + ras_listener_setrotation(listener,left,up,forward); + /* + Con_Printf( + "DP: Left={%f|%f|%f} Up={%f|%f|%f} Front={%f|%f|%f}\n", + left[0], left[1], left[2], + up[0], up[1], up[2], + forward[0],forward[1],forward[2] + ); + ras_testrotation(left,up,forward); + Con_Printf( + "RAS: Left={%f|%f|%f} Up={%f|%f|%f} Front={%f|%f|%f}\n", + left[0], left[1], left[2], + up[0], up[1], up[2], + forward[0],forward[1],forward[2] + ); + */ + if(updatecount>100){ + updatecount=0; + #ifdef RAS_PRINT + Con_Printf("S_Update: Add a callback to SCR_CaptureVideo_SoundFrame.\n"); + Con_Printf("S_Update: Add Slomo.\n"); + Con_Printf("S_Update: Add BlockedSoundCheck.\n"); + Con_Printf("S_Update: Add Slomo(as a cvar) and pauze.\n"); + #endif + }else{ + ++updatecount; + } + //(15:20:31) div0: (at the moment, you can extend it to multichannel) + //(15:20:40) div0: see S_CaptureAVISound() + if(cl.entities){ //if there is a list of ents + //Update all entities there position into the sound sources. + entnum_begin(&prev_e,&now_e); + while(now_e){ + if(!now_e->rasptr){ + Con_Printf("S_Update: Found an entnum_t without a valid RAS-ptr... This indicates a bug.\n"); + entnum_delete_and_next(&prev_e,&now_e); + }else{ //Look for unused ent and drop them. + if(now_e->entnum!=-1){ //Talking about an ent ? Or a static sound source ? + if(ras_soundsource_ended(now_e->rasptr)){ + VectorCopy(cl.entities[now_e->entnum].state_current.origin,float3); + VectorCopy(now_e->lastloc,float3); + DP_To_Ras_Location(float3,location3); + ras_soundsource_setlocation(now_e->rasptr,location3); + entnum_next(&prev_e,&now_e); + }else{ + entnum_delete_and_next(&prev_e,&now_e); + } + }else{ + if(ras_soundsource_ended(now_e->rasptr)){ + entnum_delete_and_next(&prev_e,&now_e); + }else{ + entnum_next(&prev_e,&now_e); + } + } + } + } + }else{ + Free_All_entnum(); + } + channel_begin(&prev_c,&now_c); + while(now_c){ + if(!now_c->rasptr){ + Con_Printf("S_Update: Found an channel_t without a valid RAS-ptr... This indicates a bug.\n"); + channel_delete_and_next(&prev_c,&now_c); + }else{ //Look for stopped sound channels and free them + if(ras_soundevent_ended(now_c->rasptr)){ + channel_delete_and_next(&prev_c,&now_c); + }else{ + channel_next(&prev_c,&now_c); + } + } + } + ras_soundworld_endframe (soundworld); + } + openframe =ras_soundworld_beginframe(soundworld); + } +} +void S_ExtraUpdate (void){ + // This lib is unable to use any extra updates. + //if(ras_version>0 && ras_dll){ + //} +} +sfx_t* S_FindName (const char *name){ + sfx_t *prev,*now; + if(ras_version>0 && ras_dll){ + #ifdef RAS_PRINT + Con_Printf("Called S_FindName %s\n",name); + #endif + + if (strlen (name) >= sizeof (now->name)) + { + Con_Printf ("S_FindName: sound name too long (%s)\n", name); + return NULL; + } + + sfx_begin(&prev,&now); + // Seek in list + while (now){ + if(strcmp (now->name, name)==0) return now; + sfx_next(&prev,&now); + } + + // None found in the list, + // Add a sfx_t struct for this sound + sfx_new(&prev,&now); + now->locks=0; + now->flags=0; + now->rasptr=0; + //sfx->looptstart=0; + //sfx->total_length=0; + strlcpy (now->name, name, sizeof (now->name)); + return now; + } + return NULL; +} +int S_LoadSound(sfx_t *sfx, int complain){ + fs_offset_t filesize; + char namebuffer[MAX_QPATH +16 ]; + char filename [MAX_QPATH +16+4]; + char fileext [4]; + size_t len; + unsigned char *data=NULL; + void* file_ptr=NULL; + void* decoder_ptr=NULL; + if(ras_version>0 && ras_dll){ + + fileext[4]=0; //Terminator + // See if already loaded + if (sfx->rasptr) return true; + + // LordHavoc: if the sound filename does not begin with sound/, try adding it + if (!data && strncasecmp(sfx->name, "sound/", 6)) + { + len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name); + if (len < 0){ // name too long + Con_DPrintf("S_LoadSound: name \"%s\" is too long\n", sfx->name); + return false; + } + if(!data){ + data = FS_LoadFile(namebuffer, snd_mempool, false, &filesize); + if(data) memcpy(fileext,namebuffer+len-3,3); //Copy the extention + } + if(!data){ //Stick .wav to the end and try again + memcpy(filename,namebuffer,len); + memcpy(filename+len-4,".wav",5); + data = FS_LoadFile(filename, snd_mempool, false, &filesize); + if(data) memcpy(fileext,"wav",3); + } + if(!data){ //Stick .ogg to the end and try again + memcpy(filename,namebuffer,len); + memcpy(filename+len-4,".ogg",5); + data = FS_LoadFile(filename, snd_mempool, false, &filesize); + if(data) memcpy(fileext,"ogg",3); + } + } + if(!data){ + // LordHavoc: then try without the added sound/ as wav and ogg + len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", sfx->name); + if (len < 0){ // name too long + Con_DPrintf("S_LoadSound: name \"%s\" is too long\n", sfx->name); + return false; + } + if(!data){ + data = FS_LoadFile(namebuffer, snd_mempool, false, &filesize); + if(data) memcpy(fileext,namebuffer+len-3,3); //Copy the file extention + } + if(!data){ //Stick .wav to the end + memcpy(filename,namebuffer,len); + memcpy(filename+len-4,".wav",5); + data = FS_LoadFile(filename, snd_mempool, false, &filesize); + if(data) memcpy(fileext,"wav",3); + } + if(!data){ //Stick .ogg to the end + memcpy(filename,namebuffer,len); + memcpy(filename+len-4,".ogg",5); + data = FS_LoadFile(filename, snd_mempool, false, &filesize); + if(data) memcpy(fileext,"ogg",3); + } + } + if (!data){ + if(complain) Con_Printf("Failed attempt load file '%s'\n",namebuffer); + }else{ //if the file loaded: pass to RAS 3D + file_ptr=ras_fileinputwhole_new(data,filesize); + // There we transfered to file to RAS 3D + // So lets free up data shall we ? + FS_Close(data); + + if(!file_ptr){ + Con_Printf("Failed to upload file to audio lib\n"); + }else{ + if(0==strncasecmp(fileext,"wav",3)){ + decoder_ptr=ras_audiodecoderwav_new(file_ptr,true); //(true)use seek mode: some quake files are broken. + } + if(0==strncasecmp(fileext,"ogg",3)){ + decoder_ptr=ras_audiodecoderogg_new(file_ptr); + } + if(!decoder_ptr){ + Con_Printf("File succeeded to load, but no decoder available for '%s'\n",fileext); + }else{ + #ifdef RAS_PRINT + Con_Printf("ToDo: Add a cvar to configure the cache size and number of cache blocks.\n"); + Con_Printf("ToDo: Add support for looping sounds.\n"); + #endif + sfx->rasptr=ras_sounddataoneshot_new(decoder_ptr,0.05,8); + } + file_ptr=ras_delete(file_ptr); + } + return !(sfx->rasptr); + } + return false; + } + return false; +} +sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean lock){ + sfx_t *sfx; + if(ras_version>0 && ras_dll){ + #ifdef RAS_PRINT + Con_Printf("Called S_PrecacheSound %s, %i, %i\n",name,complain,lock); + #endif + if (name == NULL || name[0] == 0) + return NULL; + sfx = S_FindName (name); + if (sfx == NULL) return NULL; + if (lock) ++(sfx->locks); + if (snd_precache.integer) S_LoadSound(sfx, complain); + return sfx; + } + return NULL; +} +void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds){ + sfx_t *prev_s, *now_s; + unsigned int i; + + if(ras_version>0 && ras_dll){ + Con_Printf("Called S_ServerSounds\n"); + for(i=0;iflags & SFXFLAG_SERVERSOUND) now_s->flags &= ~SFXFLAG_SERVERSOUND; + sfx_next(&prev_s,&now_s); + } + + // Add 1 lock and the SFXFLAG_SERVERSOUND flag to each sfx in "serversound" + for (i = 1; i < numsounds; i++){ + now_s = S_FindName (serversound[i]); + if (now_s != NULL) + now_s->flags |= SFXFLAG_SERVERSOUND; + } + + Free_Unlocked_Sfx(); + } + } +} +qboolean S_IsSoundPrecached (const sfx_t *sfx){ + if(ras_version>0 && ras_dll){ + return !sfx->rasptr; + } + return 0; +} + +void S_KillChannel (channel_t *now){ //Silences a SoundEvent + if(now->rasptr){ + ras_soundevent_setsoundpower(now->rasptr,0); + ras_delete(now->rasptr); + now->rasptr=0; + }else{ + Con_Printf("S_KillChannel: Warning pointer was 0 ... this indicates a bug.\n"); + } +} + +int S_StartSound_OnEnt (int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation){ + entnum_t *prev_e, *now_e; + channel_t *prev_c, *now_c; + Location tmp_location[3]; + + //If there is a game world + if(!cl.entities){ + Con_Printf("S_StartSound_OnEnt: no entity list exists\n"); + return -1; + } + + // Look for the correct ent_t + entnum_begin(&prev_e,&now_e); + while(now_e){ + if(now_e->entnum==entnum) break; + entnum_next(&prev_e,&now_e); + } + //We found no ent ... lets make one... + if(!now_e){ + entnum_new(&prev_e,&now_e); + if(!now_e){ + Con_Printf("S_StartSound_OnEnt: could not make new entnum_t\n"); + return -1; + } + VectorCopy(cl.entities[entnum].state_current.origin, now_e->lastloc); + DP_To_Ras_Location(now_e->lastloc,tmp_location); + now_e->rasptr=ras_soundsource_new(soundworld,1.0,tmp_location); + if(!now_e->rasptr){ + Con_Printf("S_StartSound_OnEnt: could not create a new sound source\n"); + return -1; + } + } + + //Ok now lets look for the channel. + channel_begin(&prev_c,&now_c); + while(now_c){ + if( + now_c->entnum==entnum && + now_c->entchannel==entchannel + ) break; + channel_next(&prev_c,&now_c); + } + + if(now_c){ //O dear the channel excists .... + S_KillChannel(now_c); + }else{ //We found no channel .... So we need to make a new one ... + channel_new_smart(&prev_c,&now_c); + now_c->entnum =entnum; + now_c->entchannel=entchannel; + if(!now_c){ + Con_Printf("S_StartSound_OnEnt: could not make new channel_t\n"); + channel_delete_and_next(&prev_c,&now_c); + return -1; + } + } + + //Lets start the sound on the acquired sound source and channel + now_c->rasptr=ras_soundevent_new( + soundworld,now_e->rasptr,sfx->rasptr,fvol*DP_Ras_VolumeScale,1.0 + ); + if(!now_c->rasptr){ //Whoops, failed, lets delete this channel then. + channel_delete_and_next(&prev_c,&now_c); + Con_Printf("S_StartSound_OnEnt: could not make a new soundevent.\n"); + return -1; + } + return now_c->id; +} +int S_StartSound_OnLocation (sfx_t *sfx, vec3_t origin, float fvol, float attenuation){ + entnum_t *prev_e, *now_e; + channel_t *prev_c, *now_c; + Location tmp_location[3]; + DP_To_Ras_Location(origin,tmp_location); + + entnum_new (&prev_e,&now_e); + VectorCopy(now_e->lastloc,origin); + now_e->entnum=-1; + now_e->rasptr=ras_soundsource_new(soundworld,1.0,tmp_location); + if(!now_e->rasptr){ + Con_Printf("S_StartSound_OnLocation: Could not make a new soundsource.\n"); + entnum_delete_and_next(&prev_e,&now_e); + return -1; + } + channel_new_smart(&prev_c,&now_c); + now_c->entnum=-1; + now_c->entchannel=-1; + now_c->rasptr =ras_soundevent_new(soundworld,now_e->rasptr,sfx->rasptr,fvol*DP_Ras_VolumeScale,1.0); + if(!now_c->rasptr){ + entnum_delete_and_next(&prev_e,&now_e); + channel_delete_and_next(&prev_c,&now_c); + Con_Printf("S_StartSound_OnLocation: Could not make a new soundevent.\n"); + return -1; + } + return now_c->id; +} + + +// Qantourisc on the wicked-quake-sound-system: +// -------------------------------------------- +// entnum can be Zero or lower => This means "use the origin" so it's not tracked. +// entnum -1 is a "world" containing more then 1 soundsource. +// If channel != 0 try to overwrite the requested channel. Otherwise play it on some random channel. +// If channel == -1 overwrite the first track of the ent. +// If a channel replacement is requested, only allow overwriting if it's owned by the same channel. +// If no channel can be replaced, pick a new one. +// Also when you overwrite a channel, that one has to stop dead. +// This function returns the channel it was played on (so it can be stopped later) +// This starts CD-music: S_StartSound (-1, 0, sfx, vec3_origin, cdvolume, 0); +// The channel you return then, can later be requested to be stopped. + +int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation){ + sfx_t *prev_s,*now_s; + int sfx_ok; + if(ras_version>0 && ras_dll && soundworld){ + #ifdef RAS_PRINT + Con_Printf("Called S_StartSound %i, %i, %f, %f\n",entnum,entchannel,fvol,attenuation); + #endif + if(sfx==NULL){ // They pass this to me ... but WHY ? it makes no sense ! + #ifdef RAS_PRINT + Con_Printf("S_StartSound: forgot to mention a sfx!\n"); + #endif + return -1; + } + + sfx_ok=0; + sfx_begin(&prev_s,&now_s); + while(now_s){ + if(now_s==sfx){ + sfx_ok=1; + break; + } + sfx_next(&prev_s,&now_s); + } + if(!sfx_ok){ + Con_Printf("S_StartSound: passed illegal sfx_t!\n"); + return -1; + } + if (!S_LoadSound(sfx,true)) return -1; + + + if(entnum!=-1){ //If we are talking about an ent + return S_StartSound_OnEnt(entnum,entchannel,sfx,fvol,attenuation); + }else{ + return S_StartSound_OnLocation( sfx,origin,fvol,attenuation); + } + } + Con_Printf("S_StartSound: engine not stated\n"); + return -1; +} +qboolean S_LocalSound (const char *s){ + sfx_t *sfx; + int ch_ind; + if(ras_version>0 && ras_dll){ + #ifdef RAS_PRINT + Con_Printf("Called S_LocalSound %s\n",s); + #endif + + sfx = S_PrecacheSound (s, true, false); + if (!sfx) + { + Con_Printf("S_LocalSound: can't precache %s\n", s); + return false; + } + + // Local sounds must not be freed + sfx->flags |= SFXFLAG_PERMANENTLOCK; + #ifdef RAS_PRINT + Con_Printf("S_LocalSound: this is still a small hack\n"); + #endif + ch_ind = S_StartSound (cl.viewentity, 0, sfx, listener_location, 1, 0); + if (ch_ind < 0) + return false; + + //channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND; + return true; + } + return 0; +} +void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation){ + //Static sounds should not be looped + if(ras_version>0 && ras_dll){ + #ifdef RAS_PRINT + Con_Printf("Called S_StaticSound\n"); + Con_Printf("Waiting on Qantourisc to add Static sounds in his lib.\n"); + #endif + //Static sounds are sounds that are not pauzed, and or played locally. + } +} +void S_StopSound (int entnum, int entchannel){ + channel_t *prev, *now; + if(ras_version>0 && ras_dll){ + //Con_Printf("Called S_StopSound %i, %i\n",entnum,entchannel); + channel_begin(&prev,&now); + while(now){ + if(now->entnum==entnum && now->entchannel==entchannel) break; + channel_next(&prev,&now); + } + if(now){ //If we found our to delete sound. + S_KillChannel(now); + channel_delete_and_next(&prev,&now); + }else{ + Con_Printf("S_StopSound: Could not find the requested entnum-entchannel sound\n"); + } + } +} +void S_StopAllSounds (void){ + channel_t *prev, *now; + if(ras_version>0 && ras_dll){ + //Con_Printf("Called S_StopAllSounds\n"); + channel_begin(&prev,&now); + while(now){ + S_KillChannel(now); + channel_next(&prev,&now); + } + } +} +void S_PauseGameSounds (qboolean toggle){ + if(ras_version>0 && ras_dll){ + Con_Printf("Called S_PauseGameSounds %i\n",toggle); + //Localsounds should not be pauzed + } +} +void S_StopChannel (unsigned int channel_ind){ + channel_t *prev,*now; + if(ras_version>0 && ras_dll){ + channel_begin(&prev,&now); + while(now){ + if(now->id==channel_ind){ + S_KillChannel(now); + channel_delete_and_next(&prev,&now); + }else{ + channel_next(&prev,&now); + } + } + } +} +qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value){ + if(ras_version>0 && ras_dll){ + Con_Printf("Called S_SetChannelFlag %u, %u, %i\n",ch_ind, flag, value); + } + return 0; +} +void S_SetChannelVolume (unsigned int ch_ind, float fvol){ + channel_t *prev,*now; + if(ras_version>0 && ras_dll){ + Con_Printf("Called S_SetChannelVolume %u, %f\n",ch_ind, fvol); + channel_begin(&prev,&now); + while(now){ + if(now->id==ch_ind){ + if(now->rasptr){ + ras_soundevent_setsoundpower(now->rasptr,fvol*DP_Ras_VolumeScale); + }else{ + Con_Printf("S_StopChannel: Warning pointer was 0 ... this indicates a bug.\n"); + } + } + channel_next(&prev,&now); + } + } +} + +void S_BlockSound (void){ + soundblocked++; +} +void S_UnblockSound (void){ + soundblocked--; + if(soundblocked<0){ + Con_Printf("S_UnblockSound: Requested more S_UnblockSound then S_BlockSound.\n"); + } +} + +int S_GetSoundRate (void){ + Con_Printf("Inside 3DRAS, the soundrate of the end-user is NONE of the dev's concern.\n"); + Con_Printf("So let's assume 44100.\n"); + return 44100; +} diff --git a/snd_3dras.h b/snd_3dras.h new file mode 100644 index 00000000..b735a6dd --- /dev/null +++ b/snd_3dras.h @@ -0,0 +1,49 @@ +//BSD + +#ifndef SND_3DRAS_H +#define SND_3DRAS_H + +#include "sound.h" + +#define DEFAULT_SOUND_PACKET_VOLUME 255 +#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 + +#define CHANNELFLAG_NONE 0 +#define CHANNELFLAG_FORCELOOP (1 << 0) // force looping even if the sound is not looped +#define CHANNELFLAG_LOCALSOUND (1 << 1) // INTERNAL USE. Not settable by S_SetChannelFlag +#define CHANNELFLAG_PAUSED (1 << 2) +#define CHANNELFLAG_FULLVOLUME (1 << 3) // isn't affected by the general volume + +#define SFXFLAG_NONE 0 +//#define SFXFLAG_FILEMISSING (1 << 0) // wasn't able to load the associated sound file +#define SFXFLAG_SERVERSOUND (1 << 1) // the sfx is part of the server precache list +//#define SFXFLAG_STREAMED (1 << 2) // informative only. You shouldn't need to know that +#define SFXFLAG_PERMANENTLOCK (1 << 3) // can never be freed (ex: used by the client code) + +typedef struct channel_s{ + struct channel_s* next; + void* rasptr;//Sound Event // This is also used to indicate a unused slot (when it's pointing to 0) + int entnum;// to allow overriding a specific sound + int entchannel; + unsigned int id; +} channel_t; + +typedef struct entnum_s{ + struct entnum_s *next; + int entnum; + vec3_t lastloc; //Since DP has no way of tracking the deletion, we will use this instead (great jumps indicate teleport or new ent + void *rasptr;//Sound Source // This is also used to indicate a unused slot (when it's pointing to 0) +} entnum_t; + +struct sfx_s{ + struct sfx_s *next; + char name[MAX_QPATH]; + void* rasptr; //Sound Data// The sound data allocated in the lib + + int locks; + unsigned int flags; // cf SFXFLAG_* defines + //unsigned int loopstart; // in sample frames. equals total_length if not looped + //unsigned int total_length; // in sample frames +}; + +#endif diff --git a/snd_3dras_typedefs.h b/snd_3dras_typedefs.h new file mode 100644 index 00000000..a56ef9ec --- /dev/null +++ b/snd_3dras_typedefs.h @@ -0,0 +1,57 @@ +// This file defines a few "basis measurement" types that extern program will need. +#ifndef Typedefs_h +#define Typedefs_h +#include + ///To address a location in a file. +typedef unsigned int FilePosition; + ///This will express an Amount of something. +typedef uint64_t Amount; + /// Index expresses an address in a certain array of data. +typedef uint64_t Index; + /// A signed index, to access things that can be access before 0 +typedef int64_t SignedIndex; + ///The depth at witch we are tracing. +typedef unsigned int TraceDepth; + ///The type of a Location (as in a messurement ... from 0) +typedef int64_t Location; + ///The type of a Location on a texture +typedef float TextureLocation; + ///The type of a Distance +typedef float Distance; + ///The type of a Scalar type for use in: Normal3D, Dot product, Direction3D, ... +typedef float Scalar; + ///Howmuch of something ? +typedef float Ratio; + ///The type of a an EulerAngle for use in: EulerAngle2D, EulerAngle3D, ... +typedef float EulerAngle; + ///The type that detemens the size of 1 Location. Expressed in meters/Location. +typedef float Scale; + ///The frequency of something. +typedef float Frequency; + ///The wavelength of a frequency +typedef Distance WaveLength; + /// Howmany samples we take per secod +typedef float SampleRate; + /// The type in witch we will express a SoundSample. +typedef float Sample; + /// The type that express the speed of sound (meter/second). +typedef float SoundSpeed; + /// The type that that express 1 Time. As in a small step. Note in the feature this will be a class. To make it ring. +typedef unsigned int Time; +typedef float Duration; +//typedef StrongType Time; // Example of a strong typecheck + /// The amplitude of the SoundPower. This for export to an AudioOutputDevice. +typedef float SoundVolume; + /// How mutch power per square meter is received per meter (Watt/Meter^2) +typedef float SoundIntensity; + /// An expression of the power of sound source (Watt) +typedef float SoundPower; // W, The power of the sound source + +typedef float LightIntensity; +typedef float LightPower; +typedef float Brightness; +typedef float Gamma; +typedef float Color; +typedef float RefractionIndex; +typedef unsigned int Resolution; +#endif