// 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 >= 0)
+ 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;
if (first_to_die == -1)
return NULL;
- S_StopChannel (first_to_die);
-
return &channels[first_to_die];
}
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)
{
channel_t *target_chan, *check;
int ch_idx;
- int skip;
if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer)
return -1;
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;
}
}
ch->sfx = NULL;
}
- ch->end = 0;
}
unsigned int flags; // cf SFXFLAG_* defines
int loopstart; // in sample frames. -1 if not looped
- unsigned int total_length; // in sample frames
+ int total_length; // in sample frames
const snd_fetcher_t *fetcher;
void *fetcher_data; // Per-sfx data for the sound fetching functions
};
int master_vol; // 0-255 master volume
sfx_t *sfx; // sfx number
unsigned int flags; // cf CHANNELFLAG_* defines
- unsigned int end; // end time in global paintsamples
- unsigned int lastptime; // last time this channel was painted
- unsigned int pos; // sample position in sfx
+ int pos; // sample position in sfx, negative values delay the start of the sound playback
int entnum; // to allow overriding a specific sound
int entchannel;
vec3_t origin; // origin of sound effect
else
snd_vol = (int)(volume.value * 256);
+ // calculate mixing volumes based on channel volumes and volume cvar
+ // also limit the volumes to values that won't clip
for (i = 0;i < SND_LISTENERS;i++)
+ {
vol[i] = ch->listener_volume[i] * snd_vol;
+ vol[i] = bound(0, vol[i], 65536);
+ }
+
+ // if volumes are all zero, just return
+ for (i = 0;i < SND_LISTENERS;i++)
+ if (vol[i])
+ break;
+ if (i == SND_LISTENERS)
+ return false;
sb_offset = ch->pos;
sb = ch->sfx->fetcher->getsb (ch, &sb_offset, count);
{
Con_DPrintf("SND_PaintChannel: ERROR: can't get sound buffer from sfx \"%s\"\n",
ch->sfx->name); // , count); // or add this? FIXME
+ return false;
}
else
{
return false; // unsupported number of channels in sound
}
}
-
- ch->pos += count;
return true;
}
memset (paintbuffer, 0, (partialend - paintedtime) * sizeof (paintbuffer[0]));
// paint in the channels.
+ // channels with zero volumes still advance in time but don't paint.
ch = channels;
for (i = 0; i < total_channels ; i++, ch++)
{
sfx_t *sfx;
- unsigned int ltime, j;
+ unsigned int ltime;
+ int count;
sfx = ch->sfx;
if (sfx == NULL)
continue;
- for (j = 0;j < SND_LISTENERS;j++)
- if (ch->listener_volume[j])
- break;
- if (j == SND_LISTENERS)
- continue;
if (!S_LoadSound (sfx, true))
continue;
-
- // if the channel is paused
if (ch->flags & CHANNELFLAG_PAUSED)
- {
- int pausedtime = partialend - paintedtime;
- ch->lastptime += pausedtime;
- ch->end += pausedtime;
continue;
- }
- // if the sound hasn't been painted last time, update his position
- if (ch->lastptime < paintedtime)
+ ltime = paintedtime;
+ if (ch->pos < 0)
{
- ch->pos += paintedtime - ch->lastptime;
-
- // If the sound should have ended by then
- if ((unsigned int)ch->pos > sfx->total_length)
- {
- int loopstart;
-
- if (sfx->loopstart >= 0)
- loopstart = bound(0, sfx->loopstart, (int)sfx->total_length - 1);
- else
- {
- if (ch->flags & CHANNELFLAG_FORCELOOP)
- loopstart = 0;
- else
- loopstart = -1;
- }
-
- // If the sound is looped
- if (loopstart >= 0)
- ch->pos = (ch->pos - sfx->total_length) % (sfx->total_length - loopstart) + loopstart;
- else
- ch->pos = sfx->total_length;
- ch->end = paintedtime + sfx->total_length - ch->pos;
- }
+ count = -ch->pos;
+ count = min(count, (int)(partialend - ltime));
+ ch->pos += count;
+ ltime += count;
}
- ltime = paintedtime;
while (ltime < partialend)
{
- int count;
- qboolean stop_paint;
-
- // paint up to end
- if (ch->end < partialend)
- count = ch->end - ltime;
- else
- count = partialend - ltime;
-
- if (count > 0)
+ // paint up to end of buffer or of input, whichever is lower
+ count = sfx->total_length - ch->pos;
+ count = bound(0, count, (int)(partialend - ltime));
+ if (count)
{
- for (j = 0; j < SND_LISTENERS; j++)
- ch->listener_volume[j] = bound(0, ch->listener_volume[j], 255);
-
- stop_paint = !SND_PaintChannel (ch, (unsigned int)count);
- if (!stop_paint)
- {
- ltime += count;
- ch->lastptime = ltime;
- }
+ SND_PaintChannel (ch, (unsigned int)count);
+ ch->pos += count;
+ ltime += count;
}
- else
- stop_paint = false;
- if (ltime >= ch->end)
+ // if at end of sfx, loop or stop the channel
+ if (ch->pos >= sfx->total_length)
{
- // if at end of loop, restart
- if ((sfx->loopstart >= 0 || (ch->flags & CHANNELFLAG_FORCELOOP)) && !stop_paint)
- {
+ if (sfx->loopstart >= 0 || (ch->flags & CHANNELFLAG_FORCELOOP))
ch->pos = bound(0, sfx->loopstart, (int)sfx->total_length - 1);
- ch->end = ltime + sfx->total_length - ch->pos;
- }
- // channel just stopped
else
- stop_paint = true;
- }
-
- if (stop_paint)
- {
- S_StopChannel (ch - channels);
- break;
+ {
+ S_StopChannel (ch - channels);
+ break;
+ }
}
}
}
ch->fetcher_data = per_ch;
}
-
+
real_start = *start;
sb = &per_ch->sb;
unsigned int time_start;
ogg_int64_t ogg_start;
int err;
-
- if (real_start > sfx->total_length)
+
+ if (real_start > (unsigned int)sfx->total_length)
{
Con_Printf ("OGG_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n",
real_start, sfx->total_length);