X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=snd_main.c;h=d0b9139036faee4db8ddf1b13565efcbd3d2b041;hb=eb529936e63194bad80c9a4896a85a7a2e3610c6;hp=38ff9d1a2ff545c80c52ea154fc1a96223d6d0a9;hpb=9290dadb868e9097b9c9071157025b61c0a7573d;p=xonotic%2Fdarkplaces.git diff --git a/snd_main.c b/snd_main.c index 38ff9d1a..d0b91390 100644 --- a/snd_main.c +++ b/snd_main.c @@ -53,6 +53,9 @@ speakerlayout_t; static speakerlayout_t snd_speakerlayout; +// Our speaker layouts are based on ALSA. They differ from those +// Win32 and Mac OS X APIs use when there's more than 4 channels. +// (rear left + rear right, and front center + LFE are swapped). #define SND_SPEAKERLAYOUTS (sizeof(snd_speakerlayouts) / sizeof(snd_speakerlayouts[0])) static const speakerlayout_t snd_speakerlayouts[] = { @@ -138,12 +141,11 @@ unsigned int total_channels; snd_ringbuffer_t *snd_renderbuffer = NULL; unsigned int soundtime = 0; static unsigned int oldpaintedtime = 0; -unsigned int extrasoundtime = 0; +static unsigned int extrasoundtime = 0; static double snd_starttime = 0.0; vec3_t listener_origin; matrix4x4_t listener_matrix[SND_LISTENERS]; -vec_t sound_nominal_clip_dist=1000.0; mempool_t *snd_mempool; // Linked list of known sfx @@ -153,18 +155,27 @@ static qboolean sound_spatialized = false; qboolean simsound = false; +static qboolean recording_sound = false; + int snd_blocked = 0; +static int current_swapstereo = false; +static int current_channellayout = SND_CHANNELLAYOUT_AUTO; +static int current_channellayout_used = SND_CHANNELLAYOUT_AUTO; // Cvars declared in sound.h (part of the sound API) 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_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"}; 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_soundradius = {0, "snd_soundradius", "2000", "radius of weapon sounds and other standard sound effects (monster idle noises are half this radius and flickering light noises are one third of this radius)"}; // Cvars declared in snd_main.h (shared with other snd_*.c files) cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1", "how much sound to mix ahead of time"}; 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)"}; 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"}; @@ -255,11 +266,11 @@ static void S_SoundList_f (void) { unsigned int size; const snd_format_t* format; - + size = sfx->memsize; format = sfx->fetcher->getfmt(sfx); Con_Printf ("%c%c%c%c(%2db, %6s) %8i : %s\n", - (sfx->loopstart >= 0) ? 'L' : ' ', + (sfx->loopstart < sfx->total_length) ? 'L' : ' ', (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ', (sfx->locks > 0) ? 'K' : ' ', (sfx->flags & SFXFLAG_PERMANENTLOCK) ? 'P' : ' ', @@ -292,54 +303,163 @@ void S_SoundInfo_f(void) } -// TODO: make this function smarter... +int S_GetSoundRate(void) +{ + return snd_renderbuffer ? snd_renderbuffer->format.speed : 0; +} + + static qboolean S_ChooseCheaperFormat (snd_format_t* format, qboolean fixed_speed, qboolean fixed_width, qboolean fixed_channels) { - // Can we decrease the number of channels? - if (!fixed_channels && format->channels > 1) + static const snd_format_t thresholds [] = { - unsigned short channels = format->channels; + // speed width channels + { SND_MIN_SPEED, SND_MIN_WIDTH, SND_MIN_CHANNELS }, + { 11025, 1, 2 }, + { 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]); + unsigned int speed_level, width_level, channels_level; + + // If we have reached the minimum values, there's nothing more we can do + if ((format->speed == thresholds[0].speed || fixed_speed) && + (format->width == thresholds[0].width || fixed_width) && + (format->channels == thresholds[0].channels || fixed_channels)) + return false; - // If it has an odd number of channels(?!), make it even - if (channels & 1) - channels--; - else - { - // Remove 2 speakers, unless it's a stereo format - if (channels != 2) - channels -= 2; - else - channels = 1; - } + // Check the min and max values + #define CHECK_BOUNDARIES(param) \ + if (format->param < thresholds[0].param) \ + { \ + format->param = thresholds[0].param; \ + return true; \ + } \ + if (format->param > thresholds[nb_thresholds - 1].param) \ + { \ + format->param = thresholds[nb_thresholds - 1].param; \ + return true; \ + } + CHECK_BOUNDARIES(speed); + CHECK_BOUNDARIES(width); + CHECK_BOUNDARIES(channels); + #undef CHECK_BOUNDARIES + + // Find the level of each parameter + #define FIND_LEVEL(param) \ + param##_level = 0; \ + while (param##_level < nb_thresholds - 1) \ + { \ + if (format->param <= thresholds[param##_level].param) \ + break; \ + \ + param##_level++; \ + } + FIND_LEVEL(speed); + FIND_LEVEL(width); + FIND_LEVEL(channels); + #undef FIND_LEVEL - format->channels = channels; + // Decrease the parameter with the highest level to the previous level + if (channels_level >= speed_level && channels_level >= width_level && !fixed_channels) + { + format->channels = thresholds[channels_level - 1].channels; return true; } + if (speed_level >= width_level && !fixed_speed) + { + format->speed = thresholds[speed_level - 1].speed; + return true; + } + + format->width = thresholds[width_level - 1].width; + return true; +} + + +#define SWAP_LISTENERS(l1, l2, tmpl) { tmpl = (l1); (l1) = (l2); (l2) = tmpl; } + +static void S_SetChannelLayout (void) +{ + unsigned int i; + listener_t swaplistener; + listener_t *listeners; + int layout; + + for (i = 0; i < SND_SPEAKERLAYOUTS; i++) + if (snd_speakerlayouts[i].channels == snd_renderbuffer->format.channels) + break; + if (i >= SND_SPEAKERLAYOUTS) + { + Con_Printf("S_SetChannelLayout: can't find the speaker layout for %hu channels. Defaulting to mono output\n", + snd_renderbuffer->format.channels); + i = SND_SPEAKERLAYOUTS - 1; + } - // Can we decrease the speed? - if (!fixed_speed) + snd_speakerlayout = snd_speakerlayouts[i]; + listeners = snd_speakerlayout.listeners; + + // Swap the left and right channels if snd_swapstereo is set + if (boolxor(snd_swapstereo.integer, v_flipped.integer)) { - unsigned int suggest_speeds [] = { 44100, 22050, 11025 }; - unsigned int i; - - for (i = 0; i < sizeof(suggest_speeds) / sizeof(suggest_speeds[0]); i++) - if (format->speed > suggest_speeds[i]) - { - format->speed = suggest_speeds[i]; - return true; - } + switch (snd_speakerlayout.channels) + { + case 8: + SWAP_LISTENERS(listeners[6], listeners[7], swaplistener); + // no break + case 4: + case 6: + SWAP_LISTENERS(listeners[2], listeners[3], swaplistener); + // no break + case 2: + SWAP_LISTENERS(listeners[0], listeners[1], swaplistener); + break; + + default: + case 1: + // Nothing to do + break; + } + } - // the speed is already low + // Sanity check + if (snd_channellayout.integer < SND_CHANNELLAYOUT_AUTO || + snd_channellayout.integer > SND_CHANNELLAYOUT_ALSA) + Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_STANDARD); + + if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO) + { + // If we're in the sound engine initialization + if (current_channellayout_used == SND_CHANNELLAYOUT_AUTO) + { + layout = SND_CHANNELLAYOUT_STANDARD; + Cvar_SetValueQuick (&snd_channellayout, layout); + } + else + layout = current_channellayout_used; } + else + layout = snd_channellayout.integer; - // Can we decrease the number of bits per sample? - if (!fixed_width && format->width > 1) + // Convert our layout (= ALSA) to the standard layout if necessary + if (snd_speakerlayout.channels == 6 || snd_speakerlayout.channels == 8) { - format->width = 1; - return true; + if (layout == SND_CHANNELLAYOUT_STANDARD) + { + SWAP_LISTENERS(listeners[2], listeners[4], swaplistener); + SWAP_LISTENERS(listeners[3], listeners[5], swaplistener); + } + + Con_Printf("S_SetChannelLayout: using %s speaker layout for 3D sound\n", + (layout == SND_CHANNELLAYOUT_ALSA) ? "ALSA" : "standard"); } - return false; + current_swapstereo = boolxor(snd_swapstereo.integer, v_flipped.integer); + current_channellayout = snd_channellayout.integer; + current_channellayout_used = layout; } @@ -350,7 +470,6 @@ void S_Startup (void) static snd_format_t prev_render_format = {0, 0, 0}; const char* env; int i; - unsigned int layout_id; if (!snd_initialized.integer) return; @@ -429,7 +548,7 @@ void S_Startup (void) chosen_fmt.speed = prev_render_format.speed; } } - + // Sanity checks if (chosen_fmt.speed < SND_MIN_SPEED) { @@ -463,7 +582,7 @@ void S_Startup (void) chosen_fmt.channels = SND_MAX_CHANNELS; fixed_channels = false; } - + // create the sound buffer used for sumitting the samples to the plaform-dependent module if (!simsound) { @@ -517,9 +636,15 @@ void S_Startup (void) chosen_fmt.speed, chosen_fmt.channels, chosen_fmt.width * 8); // Update the cvars - snd_speed.integer = chosen_fmt.speed; - snd_width.integer = chosen_fmt.width; - snd_channels.integer = chosen_fmt.channels; + if (snd_speed.integer != (int)chosen_fmt.speed) + Cvar_SetValueQuick(&snd_speed, chosen_fmt.speed); + if (snd_width.integer != chosen_fmt.width) + Cvar_SetValueQuick(&snd_width, chosen_fmt.width); + if (snd_channels.integer != chosen_fmt.channels) + Cvar_SetValueQuick(&snd_channels, chosen_fmt.channels); + + current_channellayout_used = SND_CHANNELLAYOUT_AUTO; + S_SetChannelLayout(); snd_starttime = realtime; @@ -540,19 +665,7 @@ void S_Startup (void) extrasoundtime = 0; snd_renderbuffer->startframe = soundtime; snd_renderbuffer->endframe = soundtime; - - // select speaker layout - for (layout_id = 0; layout_id < SND_SPEAKERLAYOUTS; layout_id++) - if (snd_speakerlayouts[layout_id].channels == snd_renderbuffer->format.channels) - break; - if (layout_id >= SND_SPEAKERLAYOUTS) - { - Con_Printf("S_Startup: invalid number of channels (%hu). Can't find the corresponding speaker layout.\n" - "Defaulting to mono output\n", - snd_renderbuffer->format.channels); - layout_id = SND_SPEAKERLAYOUTS - 1; - } - snd_speakerlayout = snd_speakerlayouts[layout_id]; + recording_sound = false; } void S_Shutdown(void) @@ -576,6 +689,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(); } @@ -596,9 +717,10 @@ 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") || COM_CheckParm("-safe")) + if (COM_CheckParm("-nosound")) return; snd_mempool = Mem_AllocPool("sound", 0, NULL); @@ -614,6 +736,7 @@ void S_Init(void) Cmd_AddCommand("soundlist", S_SoundList_f, "list loaded sounds"); Cmd_AddCommand("soundinfo", S_SoundInfo_f, "print sound system information (such as channels and speed)"); Cmd_AddCommand("snd_restart", S_Restart_f, "restart sound system"); + Cmd_AddCommand("snd_unloadallsounds", S_UnloadAllSounds_f, "unload all sound files"); Cvar_RegisterVariable(&nosound); Cvar_RegisterVariable(&snd_precache); @@ -625,6 +748,8 @@ void S_Init(void) Cvar_RegisterVariable(&snd_show); Cvar_RegisterVariable(&_snd_mixahead); Cvar_RegisterVariable(&snd_swapstereo); // for people with backwards sound wiring + Cvar_RegisterVariable(&snd_channellayout); + Cvar_RegisterVariable(&snd_soundradius); Cvar_SetValueQuick(&snd_initialized, true); @@ -658,6 +783,36 @@ void S_Terminate (void) } +/* +================== +S_UnloadAllSounds_f +================== +*/ +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(); + + // because the ambient sounds will be freed, clear the pointers + for (i = 0;i < (int)sizeof (ambient_sfxs) / (int)sizeof (ambient_sfxs[0]);i++) + ambient_sfxs[i] = NULL; + + // now free all sounds + while (known_sfx != NULL) + S_FreeSfx (known_sfx, true); +} + + /* ================== S_FindName @@ -677,6 +832,7 @@ sfx_t *S_FindName (const char *name) } // Look for this sound in the list of known sfx + // TODO: hash table search? for (sfx = known_sfx; sfx != NULL; sfx = sfx->next) if(!strcmp (sfx->name, name)) return sfx; @@ -735,7 +891,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); } @@ -783,6 +939,9 @@ void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds) sfx = S_FindName (serversound[i]); if (sfx != NULL) { + // clear the FILEMISSING flag so that S_LoadSound will try again on a + // previously missing file + sfx->flags &= ~ SFXFLAG_FILEMISSING; S_LockSfx (sfx); sfx->flags |= SFXFLAG_SERVERSOUND; } @@ -813,9 +972,14 @@ sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean lock) return NULL; sfx = S_FindName (name); + if (sfx == NULL) return NULL; + // clear the FILEMISSING flag so that S_LoadSound will try again on a + // previously missing file + sfx->flags &= ~ SFXFLAG_FILEMISSING; + if (lock) S_LockSfx (sfx); @@ -899,41 +1063,42 @@ channel_t *SND_PickChannel(int entnum, int entchannel) // Check for replacement sound, or find the best one to replace first_to_die = -1; first_life_left = 0x7fffffff; - for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) + + // entity channels try to replace the existing sound on the channel + if (entchannel != 0) { - ch = &channels[ch_idx]; - if (entchannel != 0) + for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) { - // try to override an existing channel + ch = &channels[ch_idx]; if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1) ) { // always override sound from same entity - first_to_die = ch_idx; - break; + S_StopChannel (ch_idx); + return &channels[ch_idx]; } } - else + } + + // there was no channel to override, so look for the first empty one + for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) + { + ch = &channels[ch_idx]; + if (!ch->sfx) { - if (!ch->sfx) - { - // no sound on this channel - first_to_die = ch_idx; - break; - } + // no sound on this channel + first_to_die = ch_idx; + break; } - if (ch->sfx) - { - // don't let monster sounds override player sounds - if (ch->entnum == cl.viewentity && entnum != cl.viewentity) - continue; + // don't let monster sounds override player sounds + if (ch->entnum == cl.viewentity && entnum != cl.viewentity) + continue; - // don't override looped sounds - if ((ch->flags & CHANNELFLAG_FORCELOOP) || ch->sfx->loopstart >= 0) - continue; - } + // don't override looped sounds + if ((ch->flags & CHANNELFLAG_FORCELOOP) || ch->sfx->loopstart < ch->sfx->total_length) + continue; + life_left = ch->sfx->total_length - ch->pos; - life_left = (int)(ch->end - snd_renderbuffer->endframe); if (life_left < first_life_left) { first_life_left = life_left; @@ -944,8 +1109,6 @@ channel_t *SND_PickChannel(int entnum, int entchannel) if (first_to_die == -1) return NULL; - S_StopChannel (first_to_die); - return &channels[first_to_die]; } @@ -956,6 +1119,7 @@ SND_Spatialize Spatializes a channel ================= */ +extern cvar_t cl_gameplayfix_soundsmovewithentities; void SND_Spatialize(channel_t *ch, qboolean isstatic) { int i; @@ -963,12 +1127,19 @@ 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 && cl.entities[ch->entnum].state_current.active) + if (ch->entnum > 0 && cls.state == ca_connected && cl_gameplayfix_soundsmovewithentities.integer) { - //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl.entities[ch->entnum].state_current.origin[0], cl.entities[ch->entnum].state_current.origin[1], cl.entities[ch->entnum].state_current.origin[2]); - VectorCopy(cl.entities[ch->entnum].state_current.origin, ch->origin); - if (cl.entities[ch->entnum].state_current.modelindex && cl.model_precache[cl.entities[ch->entnum].state_current.modelindex] && cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->soundfromcenter) - VectorMAMAM(1.0f, ch->origin, 0.5f, cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->normalmins, 0.5f, cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->normalmaxs, ch->origin); + if (ch->entnum >= 32768) + { + // TODO: sounds that follow CSQC entities? + } + else if (cl.entities[ch->entnum].state_current.active) + { + //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl.entities[ch->entnum].state_current.origin[0], cl.entities[ch->entnum].state_current.origin[1], cl.entities[ch->entnum].state_current.origin[2]); + VectorCopy(cl.entities[ch->entnum].state_current.origin, ch->origin); + if (cl.entities[ch->entnum].state_current.modelindex && cl.model_precache[cl.entities[ch->entnum].state_current.modelindex] && cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->soundfromcenter) + VectorMAMAM(1.0f, ch->origin, 0.5f, cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->normalmins, 0.5f, cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->normalmaxs, ch->origin); + } } mastervol = ch->master_vol; @@ -1018,24 +1189,25 @@ 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->end = snd_renderbuffer->endframe + sfx->total_length; - target_chan->lastptime = snd_renderbuffer->endframe; target_chan->flags = flags; + target_chan->pos = 0; // start of the sound // If it's a static sound if (isstatic) { - if (sfx->loopstart == -1) + if (sfx->loopstart >= sfx->total_length) Con_DPrintf("Quake compatibility warning: Static sound \"%s\" is not looped\n", sfx->name); - target_chan->dist_mult = attenuation / (64.0f * sound_nominal_clip_dist); + target_chan->dist_mult = attenuation / (64.0f * snd_soundradius.value); } else - target_chan->dist_mult = attenuation / sound_nominal_clip_dist; + target_chan->dist_mult = attenuation / snd_soundradius.value; // Lock the SFX during play S_LockSfx (sfx); + + // and finally, apply the volume + S_SetChannelVolume(target_chan - channels, fvol); } @@ -1043,18 +1215,12 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f { channel_t *target_chan, *check; int ch_idx; - int skip; if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer) return -1; - if (!sfx->fetcher) - { - Con_DPrintf ("S_StartSound: \"%s\" hasn't been precached\n", sfx->name); - return -1; - } - if (entnum && entnum >= cl.max_entities) - CL_ExpandEntities(entnum); + if (sfx->fetcher == NULL) + return -1; // Pick a channel to play on target_chan = SND_PickChannel(entnum, entchannel); @@ -1076,13 +1242,8 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f continue; if (check->sfx == sfx && !check->pos) { - skip = (int)(0.1 * snd_renderbuffer->format.speed); - if (skip > (int)sfx->total_length) - skip = (int)sfx->total_length; - if (skip > 0) - skip = rand() % skip; - target_chan->pos += skip; - target_chan->end -= skip; + // use negative pos offset to delay this sound effect + target_chan->pos += (int)lhrandom(0, -0.1 * snd_renderbuffer->format.speed); break; } } @@ -1106,15 +1267,15 @@ 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; } - ch->end = 0; } @@ -1148,6 +1309,7 @@ void S_StopSound(int entnum, int entchannel) } } +extern void CDAudio_Stop(void); void S_StopAllSounds (void) { unsigned int i; @@ -1156,6 +1318,9 @@ void S_StopAllSounds (void) if (snd_renderbuffer == NULL) return; + // stop CD audio because it may be using a faketrack + CDAudio_Stop(); + for (i = 0; i < total_channels; i++) S_StopChannel (i); @@ -1167,7 +1332,7 @@ void S_StopAllSounds (void) { int clear; size_t memsize; - + clear = (snd_renderbuffer->format.width == 1) ? 0x80 : 0; memsize = snd_renderbuffer->maxframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels; memset(snd_renderbuffer->ring, clear, memsize); @@ -1193,6 +1358,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); } @@ -1257,17 +1432,20 @@ void S_UpdateAmbientSounds (void) // Don't adjust volume too fast // FIXME: this rounds off to an int each frame, meaning there is little to no fade at extremely high framerates! - if (chan->master_vol < vol) - { - chan->master_vol += (int)(cl.realframetime * ambient_fade.value); - if (chan->master_vol > vol) - chan->master_vol = vol; - } - else if (chan->master_vol > vol) + if (cl.time > cl.oldtime) { - chan->master_vol -= (int)(cl.realframetime * ambient_fade.value); if (chan->master_vol < vol) - chan->master_vol = vol; + { + chan->master_vol += (int)((cl.time - cl.oldtime) * ambient_fade.value); + if (chan->master_vol > vol) + chan->master_vol = vol; + } + else if (chan->master_vol > vol) + { + chan->master_vol -= (int)((cl.time - cl.oldtime) * ambient_fade.value); + if (chan->master_vol < vol) + chan->master_vol = vol; + } } for (i = 0;i < SND_LISTENERS;i++) @@ -1278,23 +1456,75 @@ void S_UpdateAmbientSounds (void) static void S_PaintAndSubmit (void) { unsigned int newsoundtime, paintedtime, endtime, maxtime, usedframes; + qboolean usesoundtimehack; + static qboolean soundtimehack = true; - if (snd_renderbuffer == NULL || snd_blocked > 0 || nosound.integer) + if (snd_renderbuffer == NULL || nosound.integer) return; // Update sound time - if (cls.capturevideo_soundfile) // SUPER NASTY HACK to record non-realtime sound - newsoundtime = (unsigned int)((double)cls.capturevideo_frame * (double)snd_renderbuffer->format.speed / (double)cls.capturevideo_framerate); + usesoundtimehack = true; + if (cls.timedemo) // SUPER NASTY HACK to mix non-realtime sound for more reliable benchmarking + 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 + newsoundtime = (unsigned int)((double)cls.capturevideo.frame * (double)snd_renderbuffer->format.speed / (double)cls.capturevideo.framerate); else if (simsound) newsoundtime = (unsigned int)((realtime - snd_starttime) * (double)snd_renderbuffer->format.speed); else + { newsoundtime = SndSys_GetSoundTime(); - + usesoundtimehack = false; + } + // if the soundtimehack state changes we need to reset the soundtime + if (soundtimehack != usesoundtimehack) + { + snd_renderbuffer->startframe = snd_renderbuffer->endframe = soundtime = newsoundtime; + + // Mute the contents of the submission buffer + if (simsound || SndSys_LockRenderBuffer ()) + { + int clear; + size_t memsize; + + clear = (snd_renderbuffer->format.width == 1) ? 0x80 : 0; + memsize = snd_renderbuffer->maxframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels; + memset(snd_renderbuffer->ring, clear, memsize); + + if (!simsound) + SndSys_UnlockRenderBuffer (); + } + } + soundtimehack = usesoundtimehack; + + if (!soundtimehack && snd_blocked > 0) + return; + newsoundtime += extrasoundtime; if (newsoundtime < soundtime) - Con_Printf("S_PaintAndSubmit: WARNING: newsoundtime < soundtime (%u < %u)\n", - newsoundtime, soundtime); + { + if ((cls.capturevideo.soundrate != 0) != recording_sound) + { + unsigned int additionaltime; + + // add some time to extrasoundtime make newsoundtime higher + + // The extra time must be a multiple of the render buffer size + // to avoid modifying the current position in the buffer, + // some modules write directly to a shared (DMA) buffer + additionaltime = (soundtime - newsoundtime) + snd_renderbuffer->maxframes - 1; + additionaltime -= additionaltime % snd_renderbuffer->maxframes; + + extrasoundtime += additionaltime; + newsoundtime += additionaltime; + Con_DPrintf("S_PaintAndSubmit: new extra sound time = %u\n", + extrasoundtime); + } + else if (!soundtimehack) + Con_Printf("S_PaintAndSubmit: WARNING: newsoundtime < soundtime (%u < %u)\n", + newsoundtime, soundtime); + } soundtime = newsoundtime; + recording_sound = (cls.capturevideo.soundrate != 0); // Check to make sure that we haven't overshot paintedtime = snd_renderbuffer->endframe; @@ -1328,9 +1558,14 @@ void S_Update(const matrix4x4_t *listenermatrix) channel_t *ch, *combine; matrix4x4_t basematrix, rotatematrix; - if (snd_renderbuffer == NULL || snd_blocked > 0 || nosound.integer) + if (snd_renderbuffer == NULL || nosound.integer) return; + // If snd_swapstereo or snd_channellayout has changed, recompute the channel layout + if (current_swapstereo != boolxor(snd_swapstereo.integer, v_flipped.integer) || + current_channellayout != snd_channellayout.integer) + S_SetChannelLayout(); + Matrix4x4_Invert_Simple(&basematrix, listenermatrix); Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin);