}
if (FS_FileExists(filename) && (sfx = S_PrecacheSound (filename, false, false)))
{
- faketrack = S_StartSound_StartPosition_Flags (-1, 0, sfx, vec3_origin, cdvolume, 0, startposition, (looping ? CHANNELFLAG_FORCELOOP : 0) | CHANNELFLAG_FULLVOLUME | CHANNELFLAG_LOCALSOUND);
+ faketrack = S_StartSound_StartPosition_Flags (-1, 0, sfx, vec3_origin, cdvolume, 0, startposition, (looping ? CHANNELFLAG_FORCELOOP : 0) | CHANNELFLAG_FULLVOLUME | CHANNELFLAG_LOCALSOUND, 1.0f);
if (faketrack != -1)
{
if(track >= 1)
bytesperblock = 8;
ddssize -= 128;
ddssize /= 2;
- for (i = 0;i < ddssize;i += bytesperblock)
+ for (i = 0;i < (int)ddssize;i += bytesperblock)
memcpy(&ddspixels[i], &ddspixels[(i<<1)+8], 8);
ddssize += 128;
}
#define SND_MIN_SPEED 8000
-#define SND_MAX_SPEED 96000
+#define SND_MAX_SPEED 192000
#define SND_MIN_WIDTH 1
#define SND_MAX_WIDTH 2
#define SND_MIN_CHANNELS 1
// Cvars declared in snd_main.h (shared with other snd_*.c files)
cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.15", "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); when set to 2, streaming is performed even if this would waste memory"};
-cvar_t snd_streaming_length = { CVAR_SAVE, "snd_streaming_length", "0", "When set, sound files are only streamed if longer than the given length in seconds"};
+cvar_t snd_streaming_length = { CVAR_SAVE, "snd_streaming_length", "1", "decompress sounds completely if they are less than this play time when snd_streaming is 1"};
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)"};
if (sfx->fetcher != NULL)
{
unsigned int size;
- const snd_format_t* format;
size = sfx->memsize;
- format = sfx->fetcher->getfmt(sfx);
- Con_Printf ("%c%c%c(%2db, %6s) %8i : %s\n",
+ Con_Printf ("%c%c%c(%5iHz %2db %6s) %8i : %s\n",
(sfx->loopstart < sfx->total_length) ? 'L' : ' ',
(sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ',
(sfx->flags & SFXFLAG_MENUSOUND) ? 'P' : ' ',
- format->width * 8,
- (format->channels == 1) ? "mono" : "stereo",
+ sfx->format.speed,
+ sfx->format.width * 8,
+ (sfx->format.channels == 1) ? "mono" : "stereo",
size,
sfx->name);
total += size;
fixed_width = true;
}
+#if 0
+ // LordHavoc: now you can with the resampler...
// You can't change sound speed after start time (not yet supported)
if (prev_render_format.speed != 0)
{
chosen_fmt.speed = prev_render_format.speed;
}
}
+#endif
// Sanity checks
if (chosen_fmt.speed < SND_MIN_SPEED)
}
// Free it
- if (sfx->fetcher != NULL && sfx->fetcher->free != NULL)
- sfx->fetcher->free (sfx->fetcher_data);
+ if (sfx->fetcher != NULL && sfx->fetcher->freesfx != NULL)
+ sfx->fetcher->freesfx(sfx);
Mem_Free (sfx);
}
channels[i].sfx = ambient_sfxs[i];
channels[i].sfx->flags |= SFXFLAG_MENUSOUND;
channels[i].flags |= CHANNELFLAG_FORCELOOP;
- channels[i].master_vol = 0;
+ channels[i].basevolume = 0.0f;
+ channels[i].basespeed = channels[i].mixspeed = 1.0f;
}
}
// don't override looped sounds
if ((ch->flags & CHANNELFLAG_FORCELOOP) || sfx->loopstart < sfx->total_length)
continue;
- life_left = sfx->total_length - ch->pos;
+ life_left = (int)((double)sfx->total_length - ch->position);
if (life_left < first_life_left)
{
{
int i;
double f;
- float angle_side, angle_front, angle_factor;
- vec_t dist, mastervol, intensity, vol;
+ float angle_side, angle_front, angle_factor, mixspeed;
+ vec_t dist, mastervol, intensity;
vec3_t source_vec;
// update sound origin if we know about the entity
}
}
- mastervol = ch->master_vol;
+ mastervol = ch->basevolume;
+ mixspeed = ch->basespeed;
+
+ // TODO: implement doppler based on origin change relative to viewer and time of recent origin changes
// Adjust volume of static sounds
if (isstatic)
mastervol *= volume.value;
// clamp HERE to allow to go at most 10dB past mastervolume (before clamping), when mastervolume < -10dB (so relative volumes don't get too messy)
- mastervol = bound(0, mastervol, 655360);
+ mastervol = bound(0.0f, mastervol, 10.0f);
// always apply "master"
mastervol *= mastervolume.value;
// Replaygain support
// Con_DPrintf("Setting volume on ReplayGain-enabled track... %f -> ", fvol);
mastervol *= sfx->volume_mult;
- if(mastervol * sfx->volume_peak > 65536)
- mastervol = 65536 / sfx->volume_peak;
+ if(mastervol * sfx->volume_peak > 1.0f)
+ mastervol = 1.0f / sfx->volume_peak;
// Con_DPrintf("%f\n", fvol);
}
// clamp HERE to keep relative volumes of the channels correct
- mastervol = bound(0, mastervol, 65536);
+ mastervol = bound(0.0f, mastervol, 1.0f);
+
+ ch->mixspeed = mixspeed;
// anything coming from the view entity will always be full volume
// LordHavoc: make sounds with ATTN_NONE have no spatialization
- if (ch->entnum == cl.viewentity || ch->dist_mult == 0)
+ if (ch->entnum == cl.viewentity || ch->distfade == 0)
{
ch->prologic_invert = 1;
if (snd_spatialization_prologic.integer != 0)
{
- vol = mastervol * snd_speakerlayout.listeners[0].ambientvolume * sqrt(0.5);
- ch->listener_volume[0] = (int)bound(0, vol, 65536);
- vol = mastervol * snd_speakerlayout.listeners[1].ambientvolume * sqrt(0.5);
- ch->listener_volume[1] = (int)bound(0, vol, 65536);
+ ch->volume[0] = mastervol * snd_speakerlayout.listeners[0].ambientvolume * sqrt(0.5);
+ ch->volume[1] = mastervol * snd_speakerlayout.listeners[1].ambientvolume * sqrt(0.5);
for (i = 2;i < SND_LISTENERS;i++)
- ch->listener_volume[i] = 0;
+ ch->volume[i] = 0;
}
else
{
for (i = 0;i < SND_LISTENERS;i++)
- {
- vol = mastervol * snd_speakerlayout.listeners[i].ambientvolume;
- ch->listener_volume[i] = (int)bound(0, vol, 65536);
- }
+ ch->volume[i] = mastervol * snd_speakerlayout.listeners[i].ambientvolume;
}
}
else
// calculate stereo seperation and distance attenuation
VectorSubtract(listener_origin, ch->origin, source_vec);
dist = VectorLength(source_vec);
- intensity = mastervol * (1.0 - dist * ch->dist_mult);
+ intensity = mastervol * (1.0f - dist * ch->distfade);
if (intensity > 0)
{
qboolean occluded = false;
occluded = true;
}
if(occluded)
- intensity *= 0.5;
+ intensity *= 0.5f;
ch->prologic_invert = 1;
if (snd_spatialization_prologic.integer != 0)
{
if (dist == 0)
- angle_factor = 0.5;
+ angle_factor = 0.5f;
else
{
Matrix4x4_Transform(&listener_basematrix, ch->origin, source_vec);
//angle_factor is between 0 and 1 and represents the angle range from the front left to the center to the front right speaker
}
- vol = intensity * sqrt(angle_factor);
- ch->listener_volume[0] = (int)bound(0, vol, 65536);
- vol = intensity * sqrt(1 - angle_factor);
- ch->listener_volume[1] = (int)bound(0, vol, 65536);
+ ch->volume[0] = intensity * sqrt(angle_factor);
+ ch->volume[1] = intensity * sqrt(1 - angle_factor);
for (i = 2;i < SND_LISTENERS;i++)
- ch->listener_volume[i] = 0;
+ ch->volume[i] = 0;
}
else
{
break;
}
- vol = intensity * max(0, source_vec[0] * snd_speakerlayout.listeners[i].dotscale + snd_speakerlayout.listeners[i].dotbias);
-
- ch->listener_volume[i] = (int)bound(0, vol, 65536);
+ ch->volume[i] = intensity * max(0, source_vec[0] * snd_speakerlayout.listeners[i].dotscale + snd_speakerlayout.listeners[i].dotbias);
}
}
}
else
for (i = 0;i < SND_LISTENERS;i++)
- ch->listener_volume[i] = 0;
+ ch->volume[i] = 0;
}
}
void SND_Spatialize(channel_t *ch, qboolean isstatic)
// Start a sound effect
// =======================================================================
-void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic, int entnum, int entchannel, int startpos)
+void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic, int entnum, int entchannel, int startpos, float fspeed)
{
if (!sfx)
{
memset (target_chan, 0, sizeof (*target_chan));
VectorCopy (origin, target_chan->origin);
target_chan->flags = flags;
- target_chan->pos = startpos; // start of the sound
+ target_chan->position = startpos; // start of the sound
target_chan->entnum = entnum;
target_chan->entchannel = entchannel;
{
if (sfx->loopstart >= sfx->total_length && (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEWORLD))
Con_DPrintf("Quake compatibility warning: Static sound \"%s\" is not looped\n", sfx->name);
- target_chan->dist_mult = attenuation / (64.0f * snd_soundradius.value);
+ target_chan->distfade = attenuation / (64.0f * snd_soundradius.value);
}
else
- target_chan->dist_mult = attenuation / snd_soundradius.value;
+ target_chan->distfade = attenuation / snd_soundradius.value;
// set the listener volumes
S_SetChannelVolume(target_chan - channels, fvol);
+ S_SetChannelSpeed(target_chan - channels, fspeed);
SND_Spatialize_WithSfx (target_chan, isstatic, sfx);
// finally, set the sfx pointer, so the channel becomes valid for playback
}
-int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags)
+int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags, float fspeed)
{
channel_t *target_chan, *check, *ch;
int ch_idx, startpos;
if (ch->entnum == entnum && ch->entchannel == entchannel)
{
S_SetChannelVolume(ch_idx, fvol);
- ch->dist_mult = attenuation / snd_soundradius.value;
+ S_SetChannelSpeed(ch_idx, fspeed);
+ ch->distfade = attenuation / snd_soundradius.value;
SND_Spatialize(ch, false);
return ch_idx;
}
// if an identical sound has also been started this frame, offset the pos
// a bit to keep it from just making the first one louder
check = &channels[NUM_AMBIENTS];
- startpos = (int)(startposition * S_GetSoundRate());
+ startpos = (int)(startposition * sfx->format.speed);
if (startpos == 0)
{
for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++)
{
if (check == target_chan)
continue;
- if (check->sfx == sfx && check->pos == 0)
+ if (check->sfx == sfx && check->position == 0)
{
// use negative pos offset to delay this sound effect
startpos = (int)lhrandom(0, -0.1 * snd_renderbuffer->format.speed);
}
}
- S_PlaySfxOnChannel (sfx, target_chan, flags, origin, fvol, attenuation, false, entnum, entchannel, startpos);
+ S_PlaySfxOnChannel (sfx, target_chan, flags, origin, fvol, attenuation, false, entnum, entchannel, startpos, fspeed);
return (target_chan - channels);
}
int S_StartSound_StartPosition (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition)
{
- return S_StartSound_StartPosition_Flags(entnum, entchannel, sfx, origin, fvol, attenuation, startposition, CHANNELFLAG_NONE);
+ return S_StartSound_StartPosition_Flags(entnum, entchannel, sfx, origin, fvol, attenuation, startposition, CHANNELFLAG_NONE, 1.0f);
}
int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
sfx = ch->sfx;
if (ch->sfx != NULL)
{
- if (sfx->fetcher != NULL)
- {
- snd_fetcher_endsb_t fetcher_endsb = sfx->fetcher->endsb;
- if (fetcher_endsb != NULL)
- fetcher_endsb (ch->fetcher_data);
- }
-
+ if (sfx->fetcher != NULL && sfx->fetcher->stopchannel != NULL)
+ sfx->fetcher->stopchannel(ch);
ch->fetcher_data = NULL;
ch->sfx = NULL;
}
void S_SetChannelVolume(unsigned int ch_ind, float fvol)
{
- channels[ch_ind].master_vol = (int)(fvol * 65536.0f);
+ channels[ch_ind].basevolume = fvol;
+}
+
+void S_SetChannelSpeed(unsigned int ch_ind, float fspeed)
+{
+ channels[ch_ind].basespeed = fspeed;
}
float S_GetChannelPosition (unsigned int ch_ind)
{
// note: this is NOT accurate yet
- int s;
+ double s;
channel_t *ch = &channels[ch_ind];
sfx_t *sfx = ch->sfx;
if (!sfx)
return -1;
- s = ch->pos;
+ s = ch->position / sfx->format.speed;
/*
if(!snd_usethreadedmixing)
- s += _snd_mixahead.value * S_GetSoundRate();
+ s += _snd_mixahead.value;
*/
- return (s % sfx->total_length) / (float) S_GetSoundRate();
+ return (float)s;
}
float S_GetEntChannelPosition(int entnum, int entchannel)
}
target_chan = &channels[total_channels++];
- S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true, 0, 0, 0);
+ S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true, 0, 0, 0, 1.0f);
}
void S_UpdateAmbientSounds (void)
{
int i;
- int vol;
+ float vol;
+ float fade = (float)max(0.0, cl.time - cl.oldtime) * ambient_fade.value / 256.0f;
int ambient_channel;
channel_t *chan;
unsigned char ambientlevels[NUM_AMBIENTS];
if (sfx == NULL || sfx->fetcher == NULL)
continue;
- vol = (int)ambientlevels[ambient_channel];
- if (vol < 8)
- vol = 0;
- vol *= 256;
+ i = ambientlevels[ambient_channel];
+ if (i < 8)
+ i = 0;
+ vol = i * (1.0f / 256.0f);
// 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 (cl.time > cl.oldtime)
+ if (chan->basevolume < vol)
{
- if (chan->master_vol < vol)
- {
- chan->master_vol += (int)((cl.time - cl.oldtime) * 256.0 * 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) * 256.0 * ambient_fade.value);
- if (chan->master_vol < vol)
- chan->master_vol = vol;
- }
+ chan->basevolume += fade;
+ if (chan->basevolume > vol)
+ chan->basevolume = vol;
+ }
+ else if (chan->basevolume > vol)
+ {
+ chan->basevolume -= fade;
+ if (chan->basevolume < vol)
+ chan->basevolume = vol;
}
if (snd_spatialization_prologic.integer != 0)
{
- chan->listener_volume[0] = (int)bound(0, chan->master_vol * ambient_level.value * volume.value * mastervolume.value * snd_speakerlayout.listeners[0].ambientvolume * sqrt(0.5), 65536);
- chan->listener_volume[1] = (int)bound(0, chan->master_vol * ambient_level.value * volume.value * mastervolume.value * snd_speakerlayout.listeners[1].ambientvolume * sqrt(0.5), 65536);
+ chan->volume[0] = chan->basevolume * ambient_level.value * volume.value * mastervolume.value * snd_speakerlayout.listeners[0].ambientvolume * sqrt(0.5);
+ chan->volume[1] = chan->basevolume * ambient_level.value * volume.value * mastervolume.value * snd_speakerlayout.listeners[1].ambientvolume * sqrt(0.5);
for (i = 2;i < SND_LISTENERS;i++)
- chan->listener_volume[i] = 0;
+ chan->volume[i] = 0.0f;
}
else
{
for (i = 0;i < SND_LISTENERS;i++)
- chan->listener_volume[i] = (int)bound(0, chan->master_vol * ambient_level.value * volume.value * mastervolume.value * snd_speakerlayout.listeners[i].ambientvolume, 65536);
+ chan->volume[i] = chan->basevolume * ambient_level.value * volume.value * mastervolume.value * snd_speakerlayout.listeners[i].ambientvolume;
}
}
}
{
// no need to merge silent channels
for (j = 0;j < SND_LISTENERS;j++)
- if (ch->listener_volume[j])
+ if (ch->volume[j])
break;
if (j == SND_LISTENERS)
continue;
{
for (j = 0;j < SND_LISTENERS;j++)
{
- combine->listener_volume[j] = bound(0, combine->listener_volume[j] + ch->listener_volume[j], 65536);
- ch->listener_volume[j] = 0;
+ combine->volume[j] += ch->volume[j];
+ ch->volume[j] = 0;
}
}
}
for (k = 0;k < SND_LISTENERS;k++)
- if (ch->listener_volume[k])
+ if (ch->volume[k])
break;
if (k < SND_LISTENERS)
cls.soundstats.mixedsounds++;
sfx_t *next;
size_t memsize; // total memory used (including sfx_t and fetcher data)
+ snd_format_t format; // format describing the audio data that fetcher->getsamplesfloat will return
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
typedef struct channel_s
{
- int listener_volume [SND_LISTENERS]; // 0-65536 volume per speaker
- int master_vol; // 0-65536 master volume
+ // provided sound information
sfx_t *sfx; // pointer to sound sample being used
+ float basevolume; // 0-1 master volume
unsigned int flags; // cf CHANNELFLAG_* defines
- 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;
+ int entnum; // makes sound follow entity origin (allows replacing interrupting existing sound on same id)
+ int entchannel; // which channel id on the entity
vec3_t origin; // origin of sound effect
- vec_t dist_mult; // distance multiplier (attenuation/clipK)
+ vec_t distfade; // distance multiplier (attenuation/clipK)
void *fetcher_data; // Per-channel data for the sound fetching function
int prologic_invert;// whether a sound is played on the surround channels in prologic
+ float basespeed; // playback rate multiplier for pitch variation
+
+ // these are often updated while mixer is running, glitching should be minimized (mismatched channel volumes from spatialization is okay)
+ // spatialized playback speed (speed * doppler ratio)
+ float mixspeed;
+ // spatialized volume per speaker (mastervol * distanceattenuation * channelvolume cvars)
+ float volume[SND_LISTENERS];
+
+ // updated ONLY by mixer
+ // position in sfx, starts at 0, loops or stops at sfx->total_length
+ double position;
} channel_t;
// Sound fetching functions
// "start" is both an input and output parameter: it returns the actual start time of the sound buffer
-typedef const snd_buffer_t* (*snd_fetcher_getsb_t) (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes);
-typedef void (*snd_fetcher_endsb_t) (void *chfetcherdata);
-typedef void (*snd_fetcher_free_t) (void *sfxfetcherdata);
-typedef const snd_format_t* (*snd_fetcher_getfmt_t) (sfx_t* sfx);
+typedef void (*snd_fetcher_getsamplesfloat_t) (channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat);
+typedef void (*snd_fetcher_stopchannel_t) (channel_t *ch);
+typedef void (*snd_fetcher_freesfx_t) (sfx_t *sfx);
struct snd_fetcher_s
{
- snd_fetcher_getsb_t getsb;
- snd_fetcher_endsb_t endsb;
- snd_fetcher_free_t free;
- snd_fetcher_getfmt_t getfmt;
+ snd_fetcher_getsamplesfloat_t getsamplesfloat;
+ snd_fetcher_stopchannel_t stopchannel;
+ snd_fetcher_freesfx_t freesfx;
};
extern unsigned int total_channels;
extern qboolean simsound;
-#define STREAM_BUFFER_DURATION 0.3f // in seconds
-#define STREAM_BUFFER_FILL 0.2f // in seconds
-#define STREAM_BUFFER_SIZE(format_ptr) ((int)ceil (STREAM_BUFFER_DURATION * (format_ptr)->speed) * (format_ptr)->width * (format_ptr)->channels)
-
-// We work with 1 sec sequences, so this buffer must be able to contain
-// 1 sec of sound of the highest quality (48 KHz, 16 bit samples, stereo)
-extern unsigned char resampling_buffer [48000 * 2 * 2];
+#define STREAM_BUFFERSIZE 16384 // in sampleframes
// ====================================================================
// exported for capturevideo so ogg can see all channels
typedef struct portable_samplepair_s
{
- int sample[SND_LISTENERS];
+ float sample[SND_LISTENERS];
} portable_sampleframe_t;
typedef struct listener_s
#include "snd_wav.h"
#include "snd_modplug.h"
-unsigned char resampling_buffer [48000 * 2 * 2];
-
/*
====================
extern speakerlayout_t snd_speakerlayout; // for querying the listeners
extern void SCR_CaptureVideo_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length);
-static void S_CaptureAVISound(size_t length)
+static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
{
size_t i;
unsigned int j;
static void S_ConvertPaintBuffer(const portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int channels)
{
int i, val;
+ // FIXME: add 24bit and 32bit float formats
+ // FIXME: optimize with SSE intrinsics?
if (width == 2) // 16bit
{
short *snd_out = (short*)rb_ptr;
{
for (i = 0;i < nbframes;i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[6], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[7], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[4] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[6] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[7] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 6) // 5.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[4] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 4) // 4.0 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 2) // 2.0 stereo
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
- *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+ val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
+ val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
else if (channels == 1) // 1.0 mono
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] + painted_ptr->sample[1]) >> 1;
- *snd_out++ = bound(-32768, val, 32767);
+ val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 16384.0f);*snd_out++ = bound(-32768, val, 32767);
}
}
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[6] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[7] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[4] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[6] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[7] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 6) // 5.1 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[4] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 4) // 4.0 surround
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 2) // 2.0 stereo
{
for (i = 0; i < nbframes; i++, painted_ptr++)
{
- val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
- val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
+ val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
else if (channels == 1) // 1.0 mono
{
for (i = 0;i < nbframes;i++, painted_ptr++)
{
- val = ((painted_ptr->sample[0] + painted_ptr->sample[1]) >> 9) + 128;
- *snd_out++ = bound(0, val, 255);
+ val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 64.0f) + 128; *snd_out++ = bound(0, val, 255);
}
}
===============================================================================
*/
-static qboolean SND_PaintChannel (channel_t *ch, portable_sampleframe_t *paint, unsigned int count)
-{
- int vol[SND_LISTENERS];
- const snd_buffer_t *sb;
- unsigned int i, sb_offset;
- sfx_t *sfx;
-
- sfx = ch->sfx; // fetch the volatile variable
- if (!sfx) // given that this is called by the mixer thread, this never happens, but...
- return false;
-
- // move to the stack (do we need to?)
- for (i = 0;i < SND_LISTENERS;i++)
- vol[i] = ch->listener_volume[i];
-
- // 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 = sfx->fetcher->getsb (sfx->fetcher_data, &ch->fetcher_data, &sb_offset, count);
- if (sb == NULL)
- {
- Con_DPrintf("SND_PaintChannel: ERROR: can't get sound buffer from sfx \"%s\"\n",
- sfx->name); // , count); // or add this? FIXME
- return false;
- }
- else
- {
-#if SND_LISTENERS != 8
-# error the following code only supports up to 8 channels, update it
-#endif
- if (sb->format.width == 1)
- {
- const signed char *samples = (signed char*)sb->samples + (ch->pos - sb_offset) * sb->format.channels;
-
- // Stereo sound support
- if (sb->format.channels == 2)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 8;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 8;
- paint[i].sample[7] += (samples[1] * vol[7]) >> 8;
- samples += 2;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 8;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9;
- samples += 2;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 8;
- samples += 2;
- }
- }
- else if (vol[0] + vol[1] > 0 && ch->prologic_invert == -1)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] -= (samples[1] * vol[1]) >> 8;
- samples += 2;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 8;
- samples += 2;
- }
- }
- }
- else if (sb->format.channels == 1)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 8;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 8;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 8;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 8;
- paint[i].sample[7] += (samples[0] * vol[7]) >> 8;
- samples += 1;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 8;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 8;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 8;
- samples += 1;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 8;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 8;
- samples += 1;
- }
- }
- else if (vol[0] + vol[1] > 0 && ch->prologic_invert == -1)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] -= (samples[0] * vol[1]) >> 8;
- samples += 1;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 8;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 8;
- samples += 1;
- }
- }
- }
- else
- return false; // unsupported number of channels in sound
- }
- else if (sb->format.width == 2)
- {
- const signed short *samples = (signed short*)sb->samples + (ch->pos - sb_offset) * sb->format.channels;
-
- // Stereo sound support
- if (sb->format.channels == 2)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 16;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 16;
- paint[i].sample[7] += (samples[1] * vol[7]) >> 16;
- samples += 2;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 16;
- paint[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17;
- paint[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17;
- samples += 2;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[1] * vol[3]) >> 16;
- samples += 2;
- }
- }
- else if (vol[0] + vol[1] > 0 && ch->prologic_invert == -1)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] -= (samples[1] * vol[1]) >> 16;
- samples += 2;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[1] * vol[1]) >> 16;
- samples += 2;
- }
- }
- }
- else if (sb->format.channels == 1)
- {
- if (vol[6] + vol[7] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 16;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 16;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 16;
- paint[i].sample[6] += (samples[0] * vol[6]) >> 16;
- paint[i].sample[7] += (samples[0] * vol[7]) >> 16;
- samples += 1;
- }
- }
- else if (vol[4] + vol[5] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 16;
- paint[i].sample[4] += (samples[0] * vol[4]) >> 16;
- paint[i].sample[5] += (samples[0] * vol[5]) >> 16;
- samples += 1;
- }
- }
- else if (vol[2] + vol[3] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- paint[i].sample[2] += (samples[0] * vol[2]) >> 16;
- paint[i].sample[3] += (samples[0] * vol[3]) >> 16;
- samples += 1;
- }
- }
- else if (vol[0] + vol[1] > 0 && ch->prologic_invert == -1)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] -= (samples[0] * vol[1]) >> 16;
- samples += 1;
- }
- }
- else if (vol[0] + vol[1] > 0)
- {
- for (i = 0;i < count;i++)
- {
- paint[i].sample[0] += (samples[0] * vol[0]) >> 16;
- paint[i].sample[1] += (samples[0] * vol[1]) >> 16;
- samples += 1;
- }
- }
- }
- else
- return false; // unsupported number of channels in sound
- }
- }
- return true;
-}
-
void S_MixToBuffer(void *stream, unsigned int bufferframes)
{
- unsigned int i;
+ int channelindex;
channel_t *ch;
- unsigned int frames;
+ int totalmixframes;
unsigned char *outbytes = (unsigned char *) stream;
+ sfx_t *sfx;
+ portable_sampleframe_t *paint;
+ int wantframes;
+ int i;
+ int count;
+ int fetched;
+ int fetch;
+ int istartframe;
+ int iendframe;
+ int ilengthframes;
+ int totallength;
+ int loopstart;
+ int indexfrac;
+ int indexfracstep;
+ const int fetchsampleframesmax = 1024;
+ float fetchsampleframes[1024*2];
+ const float *fetchsampleframe;
+ float vol[SND_LISTENERS];
+ float lerp[2];
+ float sample[3];
+ double posd;
+ double speedd;
+ float sum;
+ qboolean looping;
+ qboolean silent;
// mix as many times as needed to fill the requested buffer
while (bufferframes)
{
// limit to the size of the paint buffer
- frames = min(bufferframes, PAINTBUFFER_SIZE);
+ totalmixframes = min(bufferframes, PAINTBUFFER_SIZE);
// clear the paint buffer
- memset (paintbuffer, 0, frames * sizeof (paintbuffer[0]));
+ memset(paintbuffer, 0, totalmixframes * 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++)
+ for (channelindex = 0;channelindex < (int)total_channels;channelindex++, ch++)
{
- sfx_t *sfx;
- int ltime;
- int count;
-
sfx = ch->sfx;
if (sfx == NULL)
continue;
continue;
if (!sfx->total_length)
continue;
- if (sfx->total_length > 1<<30)
- Sys_Error("S_MixToBuffer: sfx corrupt\n");
- ltime = 0;
- if (ch->pos < 0)
+ // copy the channel information to the stack for reference, otherwise the
+ // values might change during a mix if the spatializer is updating them
+ // (note: this still may get some old and some new values!)
+ posd = ch->position;
+ speedd = ch->mixspeed * sfx->format.speed / snd_renderbuffer->format.speed;
+ for (i = 0;i < SND_LISTENERS;i++)
+ vol[i] = ch->volume[i];
+
+ // check total volume level, because we can skip some code on silent sounds but other code must still run (position updates mainly)
+ for (i = 0;i < SND_LISTENERS;i++)
+ sum += vol[i]*vol[i];
+ silent = sum < 0.001f;
+
+ // when doing prologic mixing, some channels invert one side
+ if (ch->prologic_invert == -1)
+ vol[1] *= -1.0f;
+
+ // get some sfx info in a consistent form
+ totallength = sfx->total_length;
+ loopstart = (int)sfx->loopstart < totallength ? (int)sfx->loopstart : ((ch->flags & CHANNELFLAG_FORCELOOP) ? 0 : totallength);
+ looping = loopstart < totallength;
+
+ // do the actual paint now (may skip work if silent)
+ paint = paintbuffer;
+ wantframes = totalmixframes;
+ while (wantframes > 0)
{
- count = -ch->pos;
- count = min(count, (int)frames - ltime);
- ch->pos += count;
- ltime += count;
- }
+ // mix full output length (if possible)
+ count = wantframes;
+ if (posd < 0)
+ {
+ // for a delayed sound we have to eat into the delay first
+ count = (int)-posd;
+ if (count > wantframes)
+ count = wantframes;
+ posd += count;
+ wantframes -= count;
+ continue;
+ }
- while (ltime < (int)frames)
- {
- // paint up to end of buffer or of input, whichever is lower
- count = sfx->total_length - ch->pos;
- count = bound(0, count, (int)frames - ltime);
- // mix the remaining samples
- if (count)
+ // get fetch size
+ istartframe = (int)floor(posd);
+ iendframe = (int)floor(posd + count * speedd);
+ ilengthframes = iendframe + 2 - istartframe;
+ // don't overflow fetch buffer
+ while (ilengthframes > fetchsampleframesmax)
{
- SND_PaintChannel (ch, paintbuffer + ltime, count);
- ch->pos += count;
- ltime += count;
+ count /= 2;
+ iendframe = (int)floor(posd + count * speedd);
+ ilengthframes = iendframe + 2 - istartframe;
+ if (count < 2)
+ ilengthframes = 2;
}
- // if at end of sfx, loop or stop the channel
- else
+
+ // zero whole fetch buffer for safety
+ // (floating point noise from uninitialized memory = HORRIBLE)
+ // otherwise we would only need to clear the excess
+ if (!silent)
+ memset(fetchsampleframes, 0, ilengthframes*sfx->format.channels*sizeof(fetchsampleframes[0]));
+
+ // if looping, do multiple fetches
+ fetched = 0;
+ for (;;)
{
- if (sfx->loopstart < sfx->total_length)
- ch->pos = sfx->loopstart;
- else if (ch->flags & CHANNELFLAG_FORCELOOP)
- ch->pos = 0;
- else
+ fetch = min(ilengthframes, totallength - istartframe);
+ if (fetch > 0)
+ {
+ if (!silent)
+ sfx->fetcher->getsamplesfloat(ch, sfx, istartframe, fetch, fetchsampleframes + fetched*sfx->format.channels);
+ istartframe += fetch;
+ fetched += fetch;
+ }
+ if (istartframe == totallength && looping && fetched < ilengthframes)
{
- S_StopChannel (ch - channels, false, false);
+ // loop and fetch some more
+ posd += loopstart - totallength;
+ istartframe = loopstart;
+ }
+ else
break;
+ }
+
+ // set up our fixedpoint resampling variables (float to int conversions are expensive so do not do one per sampleframe)
+ fetchsampleframe = fetchsampleframes;
+ indexfrac = (int)floor((posd - floor(posd)) * 65536.0);
+ indexfracstep = (int)floor(speedd * 65536.0);
+ if (!silent)
+ {
+ if (sfx->format.channels == 2)
+ {
+ // music is stereo
+#if SND_LISTENERS != 8
+#error the following code only supports up to 8 channels, update it
+#endif
+ if (snd_speakerlayout.channels > 2)
+ {
+ // surround mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[2] * lerp[1];
+ sample[1] = fetchsampleframe[1] * lerp[0] + fetchsampleframe[3] * lerp[1];
+ sample[2] = (sample[0] + sample[1]) * 0.5f;
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[1] * vol[1];
+ paint->sample[2] += sample[0] * vol[2];
+ paint->sample[3] += sample[1] * vol[3];
+ paint->sample[4] += sample[2] * vol[4];
+ paint->sample[5] += sample[2] * vol[5];
+ paint->sample[6] += sample[0] * vol[6];
+ paint->sample[7] += sample[1] * vol[7];
+ indexfrac += indexfracstep;
+ fetchsampleframe += 2 * (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ else
+ {
+ // stereo mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[2] * lerp[1];
+ sample[1] = fetchsampleframe[1] * lerp[0] + fetchsampleframe[3] * lerp[1];
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[1] * vol[1];
+ indexfrac += indexfracstep;
+ fetchsampleframe += 2 * (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ }
+ else if (sfx->format.channels == 1)
+ {
+ // most sounds are mono
+#if SND_LISTENERS != 8
+#error the following code only supports up to 8 channels, update it
+#endif
+ if (snd_speakerlayout.channels > 2)
+ {
+ // surround mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[1] * lerp[1];
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[0] * vol[1];
+ paint->sample[2] += sample[0] * vol[2];
+ paint->sample[3] += sample[0] * vol[3];
+ paint->sample[4] += sample[0] * vol[4];
+ paint->sample[5] += sample[0] * vol[5];
+ paint->sample[6] += sample[0] * vol[6];
+ paint->sample[7] += sample[0] * vol[7];
+ indexfrac += indexfracstep;
+ fetchsampleframe += (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
+ else
+ {
+ // stereo mixing
+ for (i = 0;i < count;i++, paint++)
+ {
+ lerp[1] = indexfrac * (1.0f / 65536.0f);
+ lerp[0] = 1.0f - lerp[1];
+ sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[1] * lerp[1];
+ paint->sample[0] += sample[0] * vol[0];
+ paint->sample[1] += sample[0] * vol[1];
+ indexfrac += indexfracstep;
+ fetchsampleframe += (indexfrac >> 16);
+ indexfrac &= 0xFFFF;
+ }
+ }
}
}
+ posd += count * speedd;
+ wantframes -= count;
}
+ ch->position = posd;
+ if (!looping && istartframe == totallength)
+ S_StopChannel(ch - channels, false, false);
}
if (!snd_usethreadedmixing)
- S_CaptureAVISound(frames);
+ S_CaptureAVISound(paintbuffer, totalmixframes);
- S_ConvertPaintBuffer(paintbuffer, outbytes, frames, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
+ S_ConvertPaintBuffer(paintbuffer, outbytes, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
// advance the output pointer
- outbytes += frames * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
- bufferframes -= frames;
+ outbytes += totalmixframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+ bufferframes -= totalmixframes;
}
}
{
unsigned char *file;
size_t filesize;
- snd_format_t format;
- unsigned int total_length;
- char name[128];
- sfx_t *sfx;
} modplug_stream_persfx_t;
// Per-channel data structure
typedef struct
{
ModPlugFile *mf;
- unsigned int sb_offset;
int bs;
- snd_buffer_t sb; // must be at the end due to its dynamically allocated size
+ int buffer_firstframe;
+ int buffer_numframes;
+ unsigned char buffer[STREAM_BUFFERSIZE*4];
} modplug_stream_perchannel_t;
/*
====================
-ModPlug_FetchSound
+ModPlug_GetSamplesFloat
====================
*/
-static const snd_buffer_t* ModPlug_FetchSound (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes)
+static void ModPlug_GetSamplesFloat(channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat)
{
- modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)*chfetcherpointer;
- modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcher;
- snd_buffer_t* sb;
+ modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)ch->fetcher_data;
+ modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfx->fetcher_data;
int newlength, done, ret;
- unsigned int real_start;
- unsigned int factor;
+ int f = sfx->format.width * sfx->format.channels; // bytes per frame
+ short *buf;
+ int i, len;
// If there's no fetcher structure attached to the channel yet
if (per_ch == NULL)
{
- size_t buff_len, memsize;
- snd_format_t sb_format;
-
- sb_format.speed = snd_renderbuffer->format.speed;
- sb_format.width = per_sfx->format.width;
- sb_format.channels = per_sfx->format.channels;
-
- buff_len = STREAM_BUFFER_SIZE(&sb_format);
- memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len;
- per_ch = (modplug_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize);
+ per_ch = (modplug_stream_perchannel_t *)Mem_Alloc(snd_mempool, sizeof(*per_ch));
// Open it with the modplugFile API
per_ch->mf = qModPlug_Load(per_sfx->file, per_sfx->filesize);
if (!per_ch->mf)
{
- Con_Printf("error while reading ModPlug stream \"%s\"\n", per_sfx->name);
- Mem_Free (per_ch);
- return NULL;
+ // we can't call Con_Printf here, not thread safe
+// Con_Printf("error while reading ModPlug stream \"%s\"\n", per_sfx->name);
+ Mem_Free(per_ch);
+ return;
}
#ifndef SND_MODPLUG_STATIC
per_ch->bs = 0;
- per_ch->sb_offset = 0;
- per_ch->sb.format = sb_format;
- per_ch->sb.nbframes = 0;
- per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width);
-
- *chfetcherpointer = per_ch;
+ per_ch->buffer_firstframe = 0;
+ per_ch->buffer_numframes = 0;
+ ch->fetcher_data = per_ch;
}
- real_start = *start;
-
- sb = &per_ch->sb;
- factor = per_sfx->format.width * per_sfx->format.channels;
-
- // If the stream buffer can't contain that much samples anyway
- if (nbsampleframes > sb->maxframes)
+ // if the request is too large for our buffer, loop...
+ while (numsampleframes * f > (int)sizeof(per_ch->buffer))
{
- Con_Printf ("ModPlug_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes);
- return NULL;
+ done = sizeof(per_ch->buffer) / f;
+ ModPlug_GetSamplesFloat(ch, sfx, firstsampleframe, done, outsamplesfloat);
+ firstsampleframe += done;
+ numsampleframes -= done;
+ outsamplesfloat += done * sfx->format.channels;
}
- // If the data we need has already been decompressed in the sfxbuffer, just return it
- if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes)
+ // seek if the request is before the current buffer (loop back)
+ // seek if the request starts beyond the current buffer by at least one frame (channel was zero volume for a while)
+ // do not seek if the request overlaps the buffer end at all (expected behavior)
+ if (per_ch->buffer_firstframe > firstsampleframe || per_ch->buffer_firstframe + per_ch->buffer_numframes < firstsampleframe)
{
- *start = per_ch->sb_offset;
- return sb;
+ // we expect to decode forward from here so this will be our new buffer start
+ per_ch->buffer_firstframe = firstsampleframe;
+ per_ch->buffer_numframes = 0;
+ // we don't actually seek - we don't care much about timing on silent mod music streams and looping never happens
+ //qModPlug_Seek(per_ch->mf, firstsampleframe * 1000.0 / sfx->format.speed);
}
- newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start;
-
- // If we need to skip some data before decompressing the rest, or if the stream has looped
- if (newlength < 0 || per_ch->sb_offset > real_start)
+ // decompress the file as needed
+ if (firstsampleframe + numsampleframes > per_ch->buffer_firstframe + per_ch->buffer_numframes)
{
- unsigned int time_start;
- unsigned int modplug_start;
-
- /*
- MODs loop on their own, so any position is valid!
- if (real_start > (unsigned int)per_sfx->total_length)
- {
- Con_Printf ("ModPlug_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n",
- real_start, per_sfx->total_length);
- return NULL;
- }
- */
-
- // We work with 200ms (1/5 sec) steps to avoid rounding errors
- time_start = real_start * 5 / snd_renderbuffer->format.speed;
- modplug_start = time_start * (1000 / 5);
-
- Con_DPrintf("warning: mod file needed to seek (to %d)\n", modplug_start);
-
- qModPlug_Seek(per_ch->mf, modplug_start);
- sb->nbframes = 0;
-
- real_start = (unsigned int) ((float)modplug_start / 1000 * snd_renderbuffer->format.speed);
- if (*start - real_start + nbsampleframes > sb->maxframes)
+ // first slide the buffer back, discarding any data preceding the range we care about
+ int offset = firstsampleframe - per_ch->buffer_firstframe;
+ int keeplength = per_ch->buffer_numframes - offset;
+ if (keeplength > 0)
+ memmove(per_ch->buffer, per_ch->buffer + offset * sfx->format.width * sfx->format.channels, keeplength * sfx->format.width * sfx->format.channels);
+ per_ch->buffer_firstframe = firstsampleframe;
+ per_ch->buffer_numframes -= offset;
+ // decompress as much as we can fit in the buffer
+ newlength = sizeof(per_ch->buffer) - per_ch->buffer_numframes * f;
+ done = 0;
+ while (newlength > done && (ret = qModPlug_Read(per_ch->mf, (void *)((unsigned char *)per_ch->buffer + done), (int)(newlength - done))) > 0)
+ done += ret;
+ // clear the missing space if any
+ if (done < newlength)
{
- Con_Printf ("ModPlug_FetchSound: stream buffer too small after seek (%u sample frames required)\n",
- *start - real_start + nbsampleframes);
- per_ch->sb_offset = real_start;
- return NULL;
+ memset(per_ch->buffer + done, 0, newlength - done);
+ // Argh. We didn't get as many samples as we wanted. Probably
+ // libmodplug forgot what mLoopCount==-1 means... basically, this means
+ // we can't loop like this. Try to let DP fix it later...
+ sfx->total_length = firstsampleframe + done / f;
+ sfx->loopstart = 0;
+ // can't Con_Printf from this thread
+ //if (newlength != done)
+ // Con_DPrintf("ModPlug_Fetch: wanted: %d, got: %d\n", newlength, done);
}
+ // we now have more data in the buffer
+ per_ch->buffer_numframes += done / f;
}
- // Else, move forward the samples we need to keep in the sound buffer
- else
- {
- memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor);
- sb->nbframes = newlength;
- }
-
- per_ch->sb_offset = real_start;
- // We add more than one frame of sound to the buffer:
- // 1- to ensure we won't lose many samples during the resampling process
- // 2- to reduce calls to ModPlug_FetchSound to regulate workload
- newlength = (int)(per_sfx->format.speed*STREAM_BUFFER_FILL);
- if ((size_t) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed) + sb->nbframes > sb->maxframes)
- {
- Con_Printf ("ModPlug_FetchSound: stream buffer overflow (%u + %u = %u sample frames / %u)\n",
- (unsigned int) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed), sb->nbframes, (unsigned int) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed) + sb->nbframes, sb->maxframes);
- return NULL;
- }
- newlength *= factor; // convert from sample frames to bytes
- if(newlength > (int)sizeof(resampling_buffer))
- newlength = sizeof(resampling_buffer);
-
- // Decompress in the resampling_buffer
- done = 0;
- while ((ret = qModPlug_Read (per_ch->mf, (char *)&resampling_buffer[done], (int)(newlength - done))) > 0)
- done += ret;
- if(done < newlength)
- {
- // Argh. We didn't get as many samples as we wanted. Probably
- // libmodplug forgot what mLoopCount==-1 means... basically, this means
- // we can't loop like this. Try to let DP fix it later...
- per_sfx->sfx->total_length = (real_start + ((size_t)done / (size_t)factor));
- per_sfx->sfx->loopstart = 0;
-
- if(newlength != done)
- Con_DPrintf("ModPlug_Fetch: wanted: %d, got: %d\n", newlength, done);
- }
-
- Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format);
-
- *start = per_ch->sb_offset;
- return sb;
+ // convert the sample format for the caller
+ buf = (short *)(per_ch->buffer + (firstsampleframe - per_ch->buffer_firstframe) * f);
+ len = numsampleframes * sfx->format.channels;
+ for (i = 0;i < len;i++)
+ outsamplesfloat[i] = buf[i] * (1.0f / 32768.0f);
}
/*
====================
-ModPlug_FetchEnd
+ModPlug_StopChannel
====================
*/
-static void ModPlug_FetchEnd (void *chfetcherdata)
+static void ModPlug_StopChannel(channel_t *ch)
{
- modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)chfetcherdata;
+ modplug_stream_perchannel_t *per_ch = (modplug_stream_perchannel_t *)ch->fetcher_data;
if (per_ch != NULL)
{
// Free the modplug decoder
- qModPlug_Unload (per_ch->mf);
+ qModPlug_Unload(per_ch->mf);
- Mem_Free (per_ch);
+ Mem_Free(per_ch);
}
}
ModPlug_FreeSfx
====================
*/
-static void ModPlug_FreeSfx (void *sfxfetcherdata)
+static void ModPlug_FreeSfx (sfx_t *sfx)
{
- modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcherdata;
+ modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfx->fetcher_data;
// Free the modplug file
Mem_Free(per_sfx->file);
}
-/*
-====================
-ModPlug_GetFormat
-====================
-*/
-static const snd_format_t* qModPlug_GetFormat (sfx_t* sfx)
-{
- modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfx->fetcher_data;
- return &per_sfx->format;
-}
-
-static const snd_fetcher_t modplug_fetcher = { ModPlug_FetchSound, ModPlug_FetchEnd, ModPlug_FreeSfx, qModPlug_GetFormat };
+static const snd_fetcher_t modplug_fetcher = { ModPlug_GetSamplesFloat, ModPlug_StopChannel, ModPlug_FreeSfx };
/*
if (developer_loading.integer >= 2)
Con_Printf ("\"%s\" will be streamed\n", filename);
per_sfx = (modplug_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx));
- strlcpy(per_sfx->name, sfx->name, sizeof(per_sfx->name));
- sfx->memsize += sizeof (*per_sfx);
per_sfx->file = data;
per_sfx->filesize = filesize;
+ sfx->memsize += sizeof(*per_sfx);
sfx->memsize += filesize;
-
- per_sfx->format.speed = 44100; // modplug always works at that rate
- per_sfx->format.width = 2; // We always work with 16 bits samples
- per_sfx->format.channels = 2; // stereo rulez ;) (MAYBE default to mono because Amiga MODs sound better then?)
- per_sfx->sfx = sfx;
-
+ sfx->format.speed = 44100; // modplug always works at that rate
+ sfx->format.width = 2; // We always work with 16 bits samples
+ sfx->format.channels = 2; // stereo rulez ;) (MAYBE default to mono because Amiga MODs sound better then?)
sfx->fetcher_data = per_sfx;
sfx->fetcher = &modplug_fetcher;
sfx->flags |= SFXFLAG_STREAMED;
return -1;
}
-int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags)
+int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags, float fspeed)
{
return -1;
}
{
unsigned char *file;
size_t filesize;
- snd_format_t format;
- unsigned int total_length;
- char name[128];
} ogg_stream_persfx_t;
// Per-channel data structure
{
OggVorbis_File vf;
ov_decode_t ov_decode;
- unsigned int sb_offset;
int bs;
- snd_buffer_t sb; // must be at the end due to its dynamically allocated size
+ int buffer_firstframe;
+ int buffer_numframes;
+ unsigned char buffer[STREAM_BUFFERSIZE*4];
} ogg_stream_perchannel_t;
/*
====================
-OGG_FetchSound
+OGG_GetSamplesFloat
====================
*/
-static const snd_buffer_t* OGG_FetchSound (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes)
+static void OGG_GetSamplesFloat (channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat)
{
- ogg_stream_perchannel_t* per_ch = (ogg_stream_perchannel_t *)*chfetcherpointer;
- ogg_stream_persfx_t* per_sfx = (ogg_stream_persfx_t *)sfxfetcher;
- snd_buffer_t* sb;
+ ogg_stream_perchannel_t *per_ch = (ogg_stream_perchannel_t *)ch->fetcher_data;
+ ogg_stream_persfx_t *per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
+ int f = sfx->format.width * sfx->format.channels; // bytes per frame in the buffer
+ short *buf;
+ int i, len;
int newlength, done, ret;
- unsigned int real_start;
- unsigned int factor;
- // If there's no fetcher structure attached to the channel yet
+ // if this channel does not yet have a channel fetcher, make one
if (per_ch == NULL)
{
- size_t buff_len, memsize;
- snd_format_t sb_format;
-
- sb_format.speed = snd_renderbuffer->format.speed;
- sb_format.width = per_sfx->format.width;
- sb_format.channels = per_sfx->format.channels;
-
- buff_len = STREAM_BUFFER_SIZE(&sb_format);
- memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len;
- per_ch = (ogg_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize);
-
- // Open it with the VorbisFile API
+ // allocate a struct to keep track of our file position and buffer
+ per_ch = (ogg_stream_perchannel_t *)Mem_Alloc(snd_mempool, sizeof(*per_ch));
+ // begin decoding the file
per_ch->ov_decode.buffer = per_sfx->file;
per_ch->ov_decode.ind = 0;
per_ch->ov_decode.buffsize = per_sfx->filesize;
- if (qov_open_callbacks (&per_ch->ov_decode, &per_ch->vf, NULL, 0, callbacks) < 0)
+ if (qov_open_callbacks(&per_ch->ov_decode, &per_ch->vf, NULL, 0, callbacks) < 0)
{
- Con_Printf("error while reading Ogg Vorbis stream \"%s\"\n", per_sfx->name);
- Mem_Free (per_ch);
- return NULL;
+ // this never happens - this function succeeded earlier on the same data
+ Mem_Free(per_ch);
+ return;
}
per_ch->bs = 0;
-
- per_ch->sb_offset = 0;
- per_ch->sb.format = sb_format;
- per_ch->sb.nbframes = 0;
- per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width);
-
- *chfetcherpointer = per_ch;
- }
-
- real_start = *start;
-
- sb = &per_ch->sb;
- factor = per_sfx->format.width * per_sfx->format.channels;
-
- // If the stream buffer can't contain that much samples anyway
- if (nbsampleframes > sb->maxframes)
- {
- Con_Printf ("OGG_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes);
- return NULL;
+ per_ch->buffer_firstframe = 0;
+ per_ch->buffer_numframes = 0;
+ // attach the struct to our channel
+ ch->fetcher_data = (void *)per_ch;
}
- // If the data we need has already been decompressed in the sfxbuffer, just return it
- if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes)
+ // if the request is too large for our buffer, loop...
+ while (numsampleframes * f > (int)sizeof(per_ch->buffer))
{
- *start = per_ch->sb_offset;
- return sb;
+ done = sizeof(per_ch->buffer) / f;
+ OGG_GetSamplesFloat(ch, sfx, firstsampleframe, done, outsamplesfloat);
+ firstsampleframe += done;
+ numsampleframes -= done;
+ outsamplesfloat += done * sfx->format.channels;
}
- newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start;
-
- // If we need to skip some data before decompressing the rest, or if the stream has looped
- if (newlength < 0 || per_ch->sb_offset > real_start)
+ // seek if the request is before the current buffer (loop back)
+ // seek if the request starts beyond the current buffer by at least one frame (channel was zero volume for a while)
+ // do not seek if the request overlaps the buffer end at all (expected behavior)
+ if (per_ch->buffer_firstframe > firstsampleframe || per_ch->buffer_firstframe + per_ch->buffer_numframes < firstsampleframe)
{
- unsigned int time_start;
- ogg_int64_t ogg_start;
- int err;
-
- if (real_start > (unsigned int)per_sfx->total_length)
+ // we expect to decode forward from here so this will be our new buffer start
+ per_ch->buffer_firstframe = firstsampleframe;
+ per_ch->buffer_numframes = 0;
+ ret = qov_pcm_seek(&per_ch->vf, (ogg_int64_t)firstsampleframe);
+ if (ret != 0)
{
- Con_Printf ("OGG_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n",
- real_start, per_sfx->total_length);
- return NULL;
+ // LordHavoc: we can't Con_Printf here, not thread safe...
+ //Con_Printf("OGG_FetchSound: qov_pcm_seek(..., %d) returned %d\n", firstsampleframe, ret);
+ return;
}
-
- // We work with 200ms (1/5 sec) steps to avoid rounding errors
- time_start = real_start * 5 / snd_renderbuffer->format.speed;
- ogg_start = time_start * (per_sfx->format.speed / 5);
- err = qov_pcm_seek (&per_ch->vf, ogg_start);
- if (err != 0)
- {
- Con_Printf ("OGG_FetchSound: qov_pcm_seek(..., %d) returned %d\n",
- real_start, err);
- return NULL;
- }
- sb->nbframes = 0;
-
- real_start = (unsigned int) ((float)ogg_start / per_sfx->format.speed * snd_renderbuffer->format.speed);
- if (*start - real_start + nbsampleframes > sb->maxframes)
- {
- Con_Printf ("OGG_FetchSound: stream buffer too small after seek (%u sample frames required)\n",
- *start - real_start + nbsampleframes);
- per_ch->sb_offset = real_start;
- return NULL;
- }
- }
- // Else, move forward the samples we need to keep in the sound buffer
- else
- {
- memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor);
- sb->nbframes = newlength;
}
- per_ch->sb_offset = real_start;
-
- // We add more than one frame of sound to the buffer:
- // 1- to ensure we won't lose many samples during the resampling process
- // 2- to reduce calls to OGG_FetchSound to regulate workload
- newlength = (int)(per_sfx->format.speed*STREAM_BUFFER_FILL);
- // this is how much we FETCH...
- if ((size_t) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed) + sb->nbframes > sb->maxframes)
+ // decompress the file as needed
+ if (firstsampleframe + numsampleframes > per_ch->buffer_firstframe + per_ch->buffer_numframes)
{
- Con_Printf ("OGG_FetchSound: stream buffer overflow (%u + %u = %u sample frames / %u)\n",
- (unsigned int) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed), sb->nbframes, (unsigned int) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed) + sb->nbframes, sb->maxframes);
- return NULL;
+ // first slide the buffer back, discarding any data preceding the range we care about
+ int offset = firstsampleframe - per_ch->buffer_firstframe;
+ int keeplength = per_ch->buffer_numframes - offset;
+ if (keeplength > 0)
+ memmove(per_ch->buffer, per_ch->buffer + offset * sfx->format.width * sfx->format.channels, keeplength * sfx->format.width * sfx->format.channels);
+ per_ch->buffer_firstframe = firstsampleframe;
+ per_ch->buffer_numframes -= offset;
+ // decompress as much as we can fit in the buffer
+ newlength = sizeof(per_ch->buffer) - per_ch->buffer_numframes * f;
+ done = 0;
+ while (newlength > done && (ret = qov_read(&per_ch->vf, (char *)per_ch->buffer + per_ch->buffer_numframes * f + done, (int)(newlength - done), mem_bigendian, 2, 1, &per_ch->bs)) > 0)
+ done += ret;
+ // clear the missing space if any
+ if (done < newlength)
+ memset(per_ch->buffer + done, 0, newlength - done);
+ // we now have more data in the buffer
+ per_ch->buffer_numframes += done / f;
}
- newlength *= factor; // convert from sample frames to bytes
- if(newlength > (int)sizeof(resampling_buffer))
- newlength = sizeof(resampling_buffer);
- // Decompress in the resampling_buffer
- done = 0;
- while ((ret = qov_read (&per_ch->vf, (char *)&resampling_buffer[done], (int)(newlength - done), mem_bigendian, 2, 1, &per_ch->bs)) > 0)
- done += ret;
-
- Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format);
-
- *start = per_ch->sb_offset;
- return sb;
+ // convert the sample format for the caller
+ buf = (short *)((char *)per_ch->buffer + (firstsampleframe - per_ch->buffer_firstframe) * f);
+ len = numsampleframes * sfx->format.channels;
+ for (i = 0;i < len;i++)
+ outsamplesfloat[i] = buf[i] * (1.0f / 32768.0f);
}
/*
====================
-OGG_FetchEnd
+OGG_StopChannel
====================
*/
-static void OGG_FetchEnd (void *chfetcherdata)
+static void OGG_StopChannel(channel_t *ch)
{
- ogg_stream_perchannel_t* per_ch = (ogg_stream_perchannel_t *)chfetcherdata;
-
+ ogg_stream_perchannel_t *per_ch = (ogg_stream_perchannel_t *)ch->fetcher_data;
if (per_ch != NULL)
{
- // Free the ogg vorbis decoder
- qov_clear (&per_ch->vf);
-
- Mem_Free (per_ch);
+ // release the vorbis decompressor
+ qov_clear(&per_ch->vf);
+ Mem_Free(per_ch);
}
}
OGG_FreeSfx
====================
*/
-static void OGG_FreeSfx (void *sfxfetcherdata)
+static void OGG_FreeSfx(sfx_t *sfx)
{
- ogg_stream_persfx_t* per_sfx = (ogg_stream_persfx_t *)sfxfetcherdata;
-
- // Free the Ogg Vorbis file
+ ogg_stream_persfx_t *per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
+ // free the complete file we were keeping around
Mem_Free(per_sfx->file);
-
- // Free the stream structure
+ // free the file information structure
Mem_Free(per_sfx);
}
-/*
-====================
-OGG_GetFormat
-====================
-*/
-static const snd_format_t* OGG_GetFormat (sfx_t* sfx)
-{
- ogg_stream_persfx_t* per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
- return &per_sfx->format;
-}
-
-static const snd_fetcher_t ogg_fetcher = { OGG_FetchSound, OGG_FetchEnd, OGG_FreeSfx, OGG_GetFormat };
+static const snd_fetcher_t ogg_fetcher = {OGG_GetSamplesFloat, OGG_StopChannel, OGG_FreeSfx};
-static void OGG_DecodeTags(vorbis_comment *vc, unsigned int *start, unsigned int *length, double samplesfactor, unsigned int numsamples, double *peak, double *gaindb)
+static void OGG_DecodeTags(vorbis_comment *vc, unsigned int *start, unsigned int *length, unsigned int numsamples, double *peak, double *gaindb)
{
const char *startcomment = NULL, *lengthcomment = NULL, *endcomment = NULL, *thiscomment = NULL;
if(startcomment)
{
- *start = (unsigned int) bound(0, atof(startcomment) * samplesfactor, numsamples);
+ *start = (unsigned int) bound(0, atof(startcomment), numsamples);
if(endcomment)
- *length = (unsigned int) bound(0, atof(endcomment) * samplesfactor, numsamples);
+ *length = (unsigned int) bound(0, atof(endcomment), numsamples);
else if(lengthcomment)
- *length = (unsigned int) bound(0, *start + atof(lengthcomment) * samplesfactor, numsamples);
+ *length = (unsigned int) bound(0, *start + atof(lengthcomment), numsamples);
}
}
Load an Ogg Vorbis file into memory
====================
*/
-qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx)
+qboolean OGG_LoadVorbisFile(const char *filename, sfx_t *sfx)
{
unsigned char *data;
fs_offset_t filesize;
OggVorbis_File vf;
vorbis_info *vi;
vorbis_comment *vc;
- ogg_int64_t len, buff_len;
double peak, gaindb;
- qboolean want_stream;
#ifndef LINK_TO_LIBVORBIS
if (!vf_dll)
return false;
#endif
- // Already loaded?
+ // Return if already loaded
if (sfx->fetcher != NULL)
return true;
- // Load the file
- data = FS_LoadFile (filename, snd_mempool, false, &filesize);
+ // Load the file completely
+ data = FS_LoadFile(filename, snd_mempool, false, &filesize);
if (data == NULL)
return false;
if (developer_loading.integer >= 2)
- Con_Printf ("Loading Ogg Vorbis file \"%s\"\n", filename);
+ Con_Printf("Loading Ogg Vorbis file \"%s\"\n", filename);
// Open it with the VorbisFile API
ov_decode.buffer = data;
ov_decode.ind = 0;
ov_decode.buffsize = filesize;
- if (qov_open_callbacks (&ov_decode, &vf, NULL, 0, callbacks) < 0)
+ if (qov_open_callbacks(&ov_decode, &vf, NULL, 0, callbacks) < 0)
{
- Con_Printf ("error while opening Ogg Vorbis file \"%s\"\n", filename);
+ Con_Printf("error while opening Ogg Vorbis file \"%s\"\n", filename);
Mem_Free(data);
return false;
}
// Get the stream information
- vi = qov_info (&vf, -1);
+ vi = qov_info(&vf, -1);
if (vi->channels < 1 || vi->channels > 2)
{
Con_Printf("%s has an unsupported number of channels (%i)\n",
return false;
}
- len = qov_pcm_total (&vf, -1) * vi->channels * 2; // 16 bits => "* 2"
-
- // Decide if we go for a stream or a simple PCM cache
- buff_len = (int)ceil (STREAM_BUFFER_DURATION * snd_renderbuffer->format.speed) * 2 * vi->channels;
+ sfx->format.speed = vi->rate;
+ sfx->format.channels = vi->channels;
+ sfx->format.width = 2; // We always work with 16 bits samples
- if(snd_streaming.integer)
- {
- want_stream = true;
-
- // don't stream if we would need more RAM when streaming
- if(snd_streaming.integer < 2)
- if(len <= (ogg_int64_t)filesize + 3 * buff_len)
- want_stream = false;
+ sfx->total_length = qov_pcm_total(&vf, -1);
- // if streaming length is set, do NOT stream if below the length
- if(snd_streaming_length.value > 0)
- if(len <= snd_streaming_length.value * vi->channels * 2)
- want_stream = false;
- }
- else
- want_stream = false;
-
- if (want_stream)
+ if (snd_streaming.integer && (snd_streaming.integer >= 2 || sfx->total_length > max(sizeof(ogg_stream_perchannel_t), snd_streaming_length.value * sfx->format.speed)))
{
+ // large sounds use the OGG fetcher to decode the file on demand (but the entire file is held in memory)
ogg_stream_persfx_t* per_sfx;
-
if (developer_loading.integer >= 2)
- Con_Printf ("Ogg sound file \"%s\" will be streamed\n", filename);
- per_sfx = (ogg_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx));
- strlcpy(per_sfx->name, sfx->name, sizeof(per_sfx->name));
+ Con_Printf("Ogg sound file \"%s\" will be streamed\n", filename);
+ per_sfx = (ogg_stream_persfx_t *)Mem_Alloc(snd_mempool, sizeof(*per_sfx));
sfx->memsize += sizeof (*per_sfx);
per_sfx->file = data;
per_sfx->filesize = filesize;
sfx->memsize += filesize;
-
- per_sfx->format.speed = vi->rate;
- per_sfx->format.width = 2; // We always work with 16 bits samples
- per_sfx->format.channels = vi->channels;
-
sfx->fetcher_data = per_sfx;
sfx->fetcher = &ogg_fetcher;
sfx->flags |= SFXFLAG_STREAMED;
- sfx->total_length = (int)((size_t)len / (per_sfx->format.channels * 2) * ((double)snd_renderbuffer->format.speed / per_sfx->format.speed));
vc = qov_comment(&vf, -1);
- OGG_DecodeTags(vc, &sfx->loopstart, &sfx->total_length, (double)snd_renderbuffer->format.speed / (double)per_sfx->format.speed, sfx->total_length, &peak, &gaindb);
- per_sfx->total_length = sfx->total_length;
- qov_clear (&vf);
+ OGG_DecodeTags(vc, &sfx->loopstart, &sfx->total_length, sfx->total_length, &peak, &gaindb);
+ qov_clear(&vf);
}
else
{
+ // small sounds are entirely loaded and use the PCM fetcher
char *buff;
+ ogg_int64_t len;
ogg_int64_t done;
int bs;
long ret;
- snd_buffer_t *sb;
- snd_format_t ogg_format;
-
if (developer_loading.integer >= 2)
Con_Printf ("Ogg sound file \"%s\" will be cached\n", filename);
-
- // Decode it
- buff = (char *)Mem_Alloc (snd_mempool, (int)len);
+ len = sfx->total_length * sfx->format.channels * sfx->format.width;
+ sfx->flags &= ~SFXFLAG_STREAMED;
+ sfx->memsize += len;
+ sfx->fetcher = &wav_fetcher;
+ sfx->fetcher_data = Mem_Alloc(snd_mempool, (size_t)len);
+ buff = (char *)sfx->fetcher_data;
done = 0;
bs = 0;
- while ((ret = qov_read (&vf, &buff[done], (int)(len - done), mem_bigendian, 2, 1, &bs)) > 0)
+ while ((ret = qov_read(&vf, &buff[done], (int)(len - done), mem_bigendian, 2, 1, &bs)) > 0)
done += ret;
-
- // Build the sound buffer
- ogg_format.speed = vi->rate;
- ogg_format.channels = vi->channels;
- ogg_format.width = 2; // We always work with 16 bits samples
- sb = Snd_CreateSndBuffer ((unsigned char *)buff, (size_t)done / (vi->channels * 2), &ogg_format, snd_renderbuffer->format.speed);
- if (sb == NULL)
- {
- qov_clear (&vf);
- Mem_Free (data);
- Mem_Free (buff);
- return false;
- }
-
- sfx->fetcher = &wav_fetcher;
- sfx->fetcher_data = sb;
-
- sfx->total_length = sb->nbframes;
- sfx->memsize += sb->maxframes * sb->format.channels * sb->format.width + sizeof (*sb) - sizeof (sb->samples);
-
- sfx->flags &= ~SFXFLAG_STREAMED;
vc = qov_comment(&vf, -1);
- OGG_DecodeTags(vc, &sfx->loopstart, &sfx->total_length, (double)snd_renderbuffer->format.speed / (double)sb->format.speed, sfx->total_length, &peak, &gaindb);
- sb->nbframes = sfx->total_length;
- qov_clear (&vf);
- Mem_Free (data);
- Mem_Free (buff);
+ OGG_DecodeTags(vc, &sfx->loopstart, &sfx->total_length, sfx->total_length, &peak, &gaindb);
+ qov_clear(&vf);
+ Mem_Free(data);
}
if(peak)
/*
====================
-WAV_FetchSound
+WAV_GetSamplesFloat
====================
*/
-static const snd_buffer_t* WAV_FetchSound (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes)
+static void WAV_GetSamplesFloat(channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat)
{
- *start = 0;
- return (snd_buffer_t *)sfxfetcher;
+ int i, len = numsampleframes * sfx->format.channels;
+ if (sfx->format.width == 2)
+ {
+ const short *bufs = (const short *)sfx->fetcher_data + firstsampleframe * sfx->format.channels;
+ for (i = 0;i < len;i++)
+ outsamplesfloat[i] = bufs[i] * (1.0f / 32768.0f);
+ }
+ else
+ {
+ const signed char *bufb = (const signed char *)sfx->fetcher_data + firstsampleframe * sfx->format.channels;
+ for (i = 0;i < len;i++)
+ outsamplesfloat[i] = bufb[i] * (1.0f / 128.0f);
+ }
}
/*
WAV_FreeSfx
====================
*/
-static void WAV_FreeSfx (void *sfxfetcherdata)
+static void WAV_FreeSfx(sfx_t *sfx)
{
- snd_buffer_t* sb = (snd_buffer_t *)sfxfetcherdata;
- // Free the sound buffer
- Mem_Free(sb);
+ // free the loaded sound data
+ Mem_Free(sfx->fetcher_data);
}
-/*
-====================
-WAV_GetFormat
-====================
-*/
-static const snd_format_t* WAV_GetFormat (sfx_t* sfx)
-{
- snd_buffer_t* sb = (snd_buffer_t *)sfx->fetcher_data;
- return &sb->format;
-}
-
-const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx, WAV_GetFormat };
+const snd_fetcher_t wav_fetcher = { WAV_GetSamplesFloat, NULL, WAV_FreeSfx };
/*
fs_offset_t filesize;
unsigned char *data;
wavinfo_t info;
- snd_format_t wav_format;
- snd_buffer_t* sb;
+ int i, len;
+ const unsigned char *inb;
+ unsigned char *outb;
// Already loaded?
if (sfx->fetcher != NULL)
ptr[i] = LittleShort (ptr[i]);
}
- wav_format.speed = info.rate;
- wav_format.width = info.width;
- wav_format.channels = info.channels;
- sb = Snd_CreateSndBuffer (data + info.dataofs, info.samples, &wav_format, snd_renderbuffer->format.speed);
- if (sb == NULL)
+ sfx->format.speed = info.rate;
+ sfx->format.width = info.width;
+ sfx->format.channels = info.channels;
+ sfx->fetcher = &wav_fetcher;
+ sfx->fetcher_data = Mem_Alloc(snd_mempool, info.samples * sfx->format.width * sfx->format.channels);
+ sfx->total_length = info.samples;
+ sfx->memsize += filesize;
+ len = info.samples * sfx->format.channels * sfx->format.width;
+ inb = data + info.dataofs;
+ outb = (unsigned char *)sfx->fetcher_data;
+ if (info.width == 2)
{
- Mem_Free(data);
- return false;
+ if (mem_bigendian)
+ {
+ // we have to byteswap the data at load (better than doing it while mixing)
+ for (i = 0;i < len;i += 2)
+ {
+ outb[i] = inb[i+1];
+ outb[i+1] = inb[i];
+ }
+ }
+ else
+ {
+ // we can just copy it straight
+ memcpy(outb, inb, len);
+ }
+ }
+ else
+ {
+ // convert unsigned byte sound data to signed bytes for quicker mixing
+ for (i = 0;i < len;i++)
+ outb[i] = inb[i] - 0x80;
}
- sfx->fetcher = &wav_fetcher;
- sfx->fetcher_data = sb;
-
- sfx->total_length = sb->nbframes;
- sfx->memsize += sb->maxframes * sb->format.channels * sb->format.width + sizeof (*sb) - sizeof (sb->samples);
if (info.loopstart < 0)
sfx->loopstart = sfx->total_length;
else
- sfx->loopstart = (unsigned int) ((double)info.loopstart * (double)sb->format.speed / (double)info.rate);
+ sfx->loopstart = info.loopstart;
sfx->loopstart = min(sfx->loopstart, sfx->total_length);
sfx->flags &= ~SFXFLAG_STREAMED;
- Mem_Free (data);
return true;
}
// S_StartSound returns the channel index, or -1 if an error occurred
int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation);
int S_StartSound_StartPosition (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition);
-int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags);
+int S_StartSound_StartPosition_Flags (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation, float startposition, int flags, float fspeed);
qboolean S_LocalSound (const char *s);
void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation);
void S_StopChannel (unsigned int channel_ind, qboolean lockmutex, qboolean freesfx);
qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value);
void S_SetChannelVolume (unsigned int ch_ind, float fvol);
+void S_SetChannelSpeed (unsigned int ch_ind, float fspeed);
float S_GetChannelPosition (unsigned int ch_ind);
float S_GetEntChannelPosition(int entnum, int entchannel);