X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=snd_main.c;h=84e2469e32767baea0c4e2a17bd85901560b0c2a;hb=d019c87dba3e0ca70ee481b8ffcf15ab8d3a5d99;hp=4078e7371014434583e43d9fd78a8c64a0c8dbe9;hpb=a3e918a0b3e3ffed0104af14a504f6c207faf56f;p=xonotic%2Fdarkplaces.git diff --git a/snd_main.c b/snd_main.c index 4078e737..84e2469e 100644 --- a/snd_main.c +++ b/snd_main.c @@ -23,10 +23,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "snd_main.h" #include "snd_ogg.h" +#include "snd_modplug.h" #define SND_MIN_SPEED 8000 -#define SND_MAX_SPEED 48000 +#define SND_MAX_SPEED 96000 #define SND_MIN_WIDTH 1 #define SND_MAX_WIDTH 2 #define SND_MIN_CHANNELS 1 @@ -139,10 +140,12 @@ channel_t channels[MAX_CHANNELS]; unsigned int total_channels; snd_ringbuffer_t *snd_renderbuffer = NULL; -unsigned int soundtime = 0; +static unsigned int soundtime = 0; static unsigned int oldpaintedtime = 0; static unsigned int extrasoundtime = 0; static double snd_starttime = 0.0; +qboolean snd_threaded = false; +qboolean snd_usethreadedmixing = false; vec3_t listener_origin; matrix4x4_t listener_matrix[SND_LISTENERS]; @@ -175,6 +178,7 @@ cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1", "enables keeping compr cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0", "swaps left/right speakers for old ISA soundblaster cards"}; extern cvar_t v_flipped; cvar_t snd_channellayout = {0, "snd_channellayout", "0", "channel layout. Can be 0 (auto - snd_restart needed), 1 (standard layout), or 2 (ALSA layout)"}; +cvar_t snd_mutewhenidle = {CVAR_SAVE, "snd_mutewhenidle", "1", "whether to disable sound output when game window is inactive"}; // Local cvars static cvar_t nosound = {0, "nosound", "0", "disables sound"}; @@ -318,6 +322,7 @@ static qboolean S_ChooseCheaperFormat (snd_format_t* format, qboolean fixed_spee { 22050, 2, 2 }, { 44100, 2, 2 }, { 48000, 2, 6 }, + { 96000, 2, 6 }, { SND_MAX_SPEED, SND_MAX_WIDTH, SND_MAX_CHANNELS }, }; const unsigned int nb_thresholds = sizeof(thresholds) / sizeof(thresholds[0]); @@ -590,7 +595,7 @@ void S_Startup (void) accepted = false; do { - Con_DPrintf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n", + Con_Printf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n", chosen_fmt.speed, chosen_fmt.width * 8, chosen_fmt.channels); @@ -599,7 +604,7 @@ void S_Startup (void) if (!accepted) { - Con_DPrintf("S_Startup: sound output initialization FAILED\n"); + Con_Printf("S_Startup: sound output initialization FAILED\n"); // If the module is suggesting another one if (suggest_fmt.speed != 0) @@ -655,7 +660,7 @@ void S_Startup (void) // some modules write directly to a shared (DMA) buffer extrasoundtime = oldpaintedtime + snd_renderbuffer->maxframes - 1; extrasoundtime -= extrasoundtime % snd_renderbuffer->maxframes; - Con_DPrintf("S_Startup: extra sound time = %u\n", extrasoundtime); + Con_Printf("S_Startup: extra sound time = %u\n", extrasoundtime); soundtime = extrasoundtime; } @@ -687,6 +692,14 @@ void S_Shutdown(void) void S_Restart_f(void) { + // NOTE: we can't free all sounds if we are running a map (this frees sfx_t that are still referenced by precaches) + // So, refuse to do this if we are connected. + if(cls.state == ca_connected) + { + Con_Printf("snd_restart would wreak havoc if you do that while connected!\n"); + return; + } + S_Shutdown(); S_Startup(); } @@ -698,8 +711,6 @@ S_Init */ void S_Init(void) { - Con_DPrint("\nSound Initialization\n"); - Cvar_RegisterVariable(&volume); Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&snd_staticvolume); @@ -707,10 +718,16 @@ void S_Init(void) Cvar_RegisterVariable(&snd_speed); Cvar_RegisterVariable(&snd_width); Cvar_RegisterVariable(&snd_channels); + Cvar_RegisterVariable(&snd_mutewhenidle); // COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio) if (COM_CheckParm("-nosound")) + { + // dummy out Play and Play2 because mods stuffcmd that + Cmd_AddCommand("play", Host_NoOperation_f, "does nothing because -nosound was specified"); + Cmd_AddCommand("play2", Host_NoOperation_f, "does nothing because -nosound was specified"); return; + } snd_mempool = Mem_AllocPool("sound", 0, NULL); @@ -748,6 +765,7 @@ void S_Init(void) memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); OGG_OpenLibrary (); + ModPlug_OpenLibrary (); } @@ -761,6 +779,7 @@ Shutdown and free all resources void S_Terminate (void) { S_Shutdown (); + ModPlug_CloseLibrary (); OGG_CloseLibrary (); // Free all SFXs @@ -781,6 +800,14 @@ void S_UnloadAllSounds_f (void) { int i; + // NOTE: we can't free all sounds if we are running a map (this frees sfx_t that are still referenced by precaches) + // So, refuse to do this if we are connected. + if(cls.state == ca_connected) + { + Con_Printf("snd_unloadallsounds would wreak havoc if you do that while connected!\n"); + return; + } + // stop any active sounds S_StopAllSounds(); @@ -843,7 +870,8 @@ void S_FreeSfx (sfx_t *sfx, qboolean force) if (!force && (sfx->locks > 0 || (sfx->flags & SFXFLAG_PERMANENTLOCK))) return; - Con_DPrintf ("S_FreeSfx: freeing %s\n", sfx->name); + if (developer_loading.integer) + Con_Printf ("unloading sound %s\n", sfx->name); // Remove it from the list of known sfx if (sfx == known_sfx) @@ -872,7 +900,7 @@ void S_FreeSfx (sfx_t *sfx, qboolean force) // Free it if (sfx->fetcher != NULL && sfx->fetcher->free != NULL) - sfx->fetcher->free (sfx); + sfx->fetcher->free (sfx->fetcher_data); Mem_Free (sfx); } @@ -1100,6 +1128,7 @@ SND_Spatialize Spatializes a channel ================= */ +extern cvar_t cl_gameplayfix_soundsmovewithentities; void SND_Spatialize(channel_t *ch, qboolean isstatic) { int i; @@ -1107,7 +1136,7 @@ void SND_Spatialize(channel_t *ch, qboolean isstatic) vec3_t source_vec; // update sound origin if we know about the entity - if (ch->entnum > 0 && cls.state == ca_connected) + if (ch->entnum > 0 && cls.state == ca_connected && cl_gameplayfix_soundsmovewithentities.integer) { if (ch->entnum >= 32768) { @@ -1169,7 +1198,6 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, // Initialize the channel memset (target_chan, 0, sizeof (*target_chan)); VectorCopy (origin, target_chan->origin); - target_chan->master_vol = (int)(fvol * 255); target_chan->sfx = sfx; target_chan->flags = flags; target_chan->pos = 0; // start of the sound @@ -1186,6 +1214,9 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, // Lock the SFX during play S_LockSfx (sfx); + + // and finally, apply the volume + S_SetChannelVolume(target_chan - channels, fvol); } @@ -1245,12 +1276,13 @@ void S_StopChannel (unsigned int channel_ind) { snd_fetcher_endsb_t fetcher_endsb = sfx->fetcher->endsb; if (fetcher_endsb != NULL) - fetcher_endsb (ch); + fetcher_endsb (ch->fetcher_data); } // Remove the lock it holds S_UnlockSfx (sfx); + ch->fetcher_data = NULL; ch->sfx = NULL; } } @@ -1335,6 +1367,16 @@ void S_PauseGameSounds (qboolean toggle) void S_SetChannelVolume (unsigned int ch_ind, float fvol) { + sfx_t *sfx = channels[ch_ind].sfx; + if(sfx->volume_peak > 0) + { + // Replaygain support + // Con_DPrintf("Setting volume on ReplayGain-enabled track... %f -> ", fvol); + fvol *= sfx->volume_mult; + if(fvol * sfx->volume_peak > 1) + fvol = 1 / sfx->volume_peak; + // Con_DPrintf("%f\n", fvol); + } channels[ch_ind].master_vol = (int)(fvol * 255.0f); } @@ -1352,7 +1394,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation) return; if (!sfx->fetcher) { - Con_DPrintf ("S_StaticSound: \"%s\" hasn't been precached\n", sfx->name); + Con_Printf ("S_StaticSound: \"%s\" hasn't been precached\n", sfx->name); return; } @@ -1423,24 +1465,36 @@ void S_UpdateAmbientSounds (void) static void S_PaintAndSubmit (void) { unsigned int newsoundtime, paintedtime, endtime, maxtime, usedframes; - qboolean usesoundtimehack; - static qboolean soundtimehack = true; + int usesoundtimehack; + static int soundtimehack = -1; + static int oldsoundtime = 0; if (snd_renderbuffer == NULL || nosound.integer) return; // Update sound time + snd_usethreadedmixing = false; usesoundtimehack = true; if (cls.timedemo) // SUPER NASTY HACK to mix non-realtime sound for more reliable benchmarking + { + usesoundtimehack = 1; newsoundtime = (unsigned int)((double)cl.mtime[0] * (double)snd_renderbuffer->format.speed); + } else if (cls.capturevideo.soundrate && !cls.capturevideo.realtime) // SUPER NASTY HACK to record non-realtime sound + { + usesoundtimehack = 2; newsoundtime = (unsigned int)((double)cls.capturevideo.frame * (double)snd_renderbuffer->format.speed / (double)cls.capturevideo.framerate); + } else if (simsound) + { + usesoundtimehack = 3; newsoundtime = (unsigned int)((realtime - snd_starttime) * (double)snd_renderbuffer->format.speed); + } else { + snd_usethreadedmixing = snd_threaded && !cls.capturevideo.soundrate; + usesoundtimehack = 0; newsoundtime = SndSys_GetSoundTime(); - usesoundtimehack = false; } // if the soundtimehack state changes we need to reset the soundtime if (soundtimehack != usesoundtimehack) @@ -1466,6 +1520,9 @@ static void S_PaintAndSubmit (void) if (!soundtimehack && snd_blocked > 0) return; + if (snd_usethreadedmixing) + return; // the audio thread will mix its own data + newsoundtime += extrasoundtime; if (newsoundtime < soundtime) { @@ -1493,23 +1550,58 @@ static void S_PaintAndSubmit (void) soundtime = newsoundtime; recording_sound = (cls.capturevideo.soundrate != 0); + // Lock submitbuffer + if (!simsound && !SndSys_LockRenderBuffer()) + { + // If the lock failed, stop here + Con_DPrint(">> S_PaintAndSubmit: SndSys_LockRenderBuffer() failed\n"); + return; + } + // Check to make sure that we haven't overshot paintedtime = snd_renderbuffer->endframe; if (paintedtime < soundtime) paintedtime = soundtime; // mix ahead of current position - endtime = soundtime + (unsigned int)(_snd_mixahead.value * (float)snd_renderbuffer->format.speed); + if (soundtimehack) + endtime = soundtime + (unsigned int)(_snd_mixahead.value * (float)snd_renderbuffer->format.speed); + else + endtime = soundtime + (unsigned int)(max(_snd_mixahead.value * (float)snd_renderbuffer->format.speed, min(3 * (soundtime - oldsoundtime), 0.3 * (float)snd_renderbuffer->format.speed))); usedframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe; maxtime = paintedtime + snd_renderbuffer->maxframes - usedframes; endtime = min(endtime, maxtime); - S_PaintChannels(snd_renderbuffer, paintedtime, endtime); + while (paintedtime < endtime) + { + unsigned int startoffset; + unsigned int nbframes; + + // see how much we can fit in the paint buffer + nbframes = endtime - paintedtime; + // limit to the end of the ring buffer (in case of wrapping) + startoffset = paintedtime % snd_renderbuffer->maxframes; + nbframes = min(nbframes, snd_renderbuffer->maxframes - startoffset); + + // mix into the buffer + S_MixToBuffer(&snd_renderbuffer->ring[startoffset * snd_renderbuffer->format.width * snd_renderbuffer->format.channels], nbframes); + + paintedtime += nbframes; + snd_renderbuffer->endframe = paintedtime; + } + if (!simsound) + SndSys_UnlockRenderBuffer(); + + // Remove outdated samples from the ring buffer, if any + if (snd_renderbuffer->startframe < soundtime) + snd_renderbuffer->startframe = soundtime; if (simsound) snd_renderbuffer->startframe = snd_renderbuffer->endframe; else SndSys_Submit(); + + oldsoundtime = soundtime; } /*