From 8b57bcb1deffad227731484e52d631277fe3d521 Mon Sep 17 00:00:00 2001 From: havoc Date: Wed, 23 Nov 2005 03:00:34 +0000 Subject: [PATCH] implemented 7.1 audio, only works with SDL (attempted ALSA support but ALSA doesn't seem to like mmap access to 4/6/8 channel buffers) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@5818 d7cf8633-e32d-0410-b094-e92efae38249 --- snd_alsa.c | 341 +++++++++++++----------------- snd_bsd.c | 3 +- snd_main.c | 310 +++++++++++++++++++-------- snd_main.h | 9 +- snd_mix.c | 600 ++++++++++++++++++++++++++++++++++++++--------------- snd_oss.c | 3 +- snd_sdl.c | 88 ++++---- snd_win.c | 2 + 8 files changed, 864 insertions(+), 492 deletions(-) diff --git a/snd_alsa.c b/snd_alsa.c index 95310d84..4328a603 100644 --- a/snd_alsa.c +++ b/snd_alsa.c @@ -39,232 +39,177 @@ static snd_pcm_t *pcm; qboolean SNDDMA_Init (void) { - int err, i; - int bps = -1, stereo = -1; - unsigned int rate = 0; + int err, i, j; + int width; + int channels; + unsigned int rate; snd_pcm_hw_params_t *hw; snd_pcm_sw_params_t *sw; - snd_pcm_uframes_t frag_size; + snd_pcm_uframes_t frag_size; snd_pcm_hw_params_alloca (&hw); snd_pcm_sw_params_alloca (&sw); -// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm selects which pcm device to us, default is "default" - if ((i=COM_CheckParm("-sndpcm"))!=0) - pcmname=com_argv[i+1]; - if (!pcmname) - pcmname = "default"; - // COMMANDLINEOPTION: Linux ALSA Sound: -sndbits sets sound precision to 8 or 16 bit (email me if you want others added) + width = 2; if ((i=COM_CheckParm("-sndbits")) != 0) { - bps = atoi(com_argv[i+1]); - if (bps != 16 && bps != 8) - { - Con_Printf("Error: invalid sample bits: %d\n", bps); - return false; - } + j = atoi(com_argv[i+1]); + if (j == 16 || j == 8) + width = j / 8; + else + Con_Printf("Error: invalid sample bits: %d\n", j); } // COMMANDLINEOPTION: Linux ALSA Sound: -sndspeed chooses 44100 hz, 22100 hz, or 11025 hz sound output rate + rate = 44100; if ((i=COM_CheckParm("-sndspeed")) != 0) { - rate = atoi(com_argv[i+1]); - if (rate!=44100 && rate!=22050 && rate!=11025) - { + j = atoi(com_argv[i+1]); + if (j >= 1) + rate = j; + else Con_Printf("Error: invalid sample rate: %d\n", rate); - return false; - } } + for (channels = 8;channels >= 1;channels--) + { + if ((channels & 1) && channels != 1) + continue; // COMMANDLINEOPTION: Linux ALSA Sound: -sndmono sets sound output to mono - if ((i=COM_CheckParm("-sndmono")) != 0) - stereo=0; + if ((i=COM_CheckParm("-sndmono")) != 0) + if (channels != 1) + continue; // COMMANDLINEOPTION: Linux ALSA Sound: -sndstereo sets sound output to stereo - if ((i=COM_CheckParm("-sndstereo")) != 0) - stereo=1; + if ((i=COM_CheckParm("-sndstereo")) != 0) + if (channels != 2) + continue; - err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK); - if (0 > err) { - Con_Printf ("Error: audio open error: %s\n", snd_strerror (err)); - return 0; - } - Con_Printf ("ALSA: Using PCM %s.\n", pcmname); +// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm selects which pcm device to us, default is "default" + if (channels == 8) + pcmname = "surround71"; + else if (channels == 6) + pcmname = "surround51"; + else if (channels == 4) + pcmname = "surround40"; + else + pcmname = "default"; + if ((i=COM_CheckParm("-sndpcm"))!=0) + pcmname = com_argv[i+1]; + + Con_Printf ("ALSA: Trying PCM %s.\n", pcmname); + + err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if (0 > err) + { + Con_Printf ("Error: audio open error: %s\n", snd_strerror (err)); + continue; + } - err = snd_pcm_hw_params_any (pcm, hw); - if (0 > err) { - Con_Printf ("ALSA: error setting hw_params_any. %s\n", - snd_strerror (err)); - goto error; - } + err = snd_pcm_hw_params_any (pcm, hw); + if (0 > err) + { + Con_Printf ("ALSA: error setting hw_params_any. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - err = snd_pcm_hw_params_set_access (pcm, hw, - SND_PCM_ACCESS_MMAP_INTERLEAVED); - if (0 > err) { - Con_Printf ("ALSA: Failure to set noninterleaved PCM access. %s\n" - "Note: Interleaved is not supported\n", - snd_strerror (err)); - goto error; - } + err = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED); + if (0 > err) + { + Con_Printf ("ALSA: Failure to set interleaved mmap PCM access. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - switch (bps) { - case -1: - err = snd_pcm_hw_params_set_format (pcm, hw, - SND_PCM_FORMAT_S16); - if (0 <= err) { - bps = 16; - } else if (0 <= (err = snd_pcm_hw_params_set_format (pcm, hw, - SND_PCM_FORMAT_U8))) { - bps = 8; - } else { - Con_Printf ("ALSA: no useable formats. %s\n", - snd_strerror (err)); - goto error; - } - break; - case 8: - case 16: - err = snd_pcm_hw_params_set_format (pcm, hw, bps == 8 ? - SND_PCM_FORMAT_U8 : - SND_PCM_FORMAT_S16); - if (0 > err) { - Con_Printf ("ALSA: no usable formats. %s\n", - snd_strerror (err)); - goto error; - } - break; - default: - Con_Printf ("ALSA: desired format not supported\n"); - goto error; - } + err = snd_pcm_hw_params_set_format (pcm, hw, width == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16); + if (0 > err) + { + Con_Printf ("ALSA: desired bits %i not supported by driver. %s\n", width * 8, snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - switch (stereo) { - case -1: - err = snd_pcm_hw_params_set_channels (pcm, hw, 2); - if (0 <= err) { - stereo = 1; - } else if (0 <= (err = snd_pcm_hw_params_set_channels (pcm, hw, - 1))) { - stereo = 0; - } else { - Con_Printf ("ALSA: no usable channels. %s\n", - snd_strerror (err)); - goto error; - } - break; - case 0: - case 1: - err = snd_pcm_hw_params_set_channels (pcm, hw, stereo ? 2 : 1); - if (0 > err) { - Con_Printf ("ALSA: no usable channels. %s\n", - snd_strerror (err)); - goto error; - } - break; - default: - Con_Printf ("ALSA: desired channels not supported\n"); - goto error; - } + err = snd_pcm_hw_params_set_channels (pcm, hw, channels); + if (0 > err) + { + Con_Printf ("ALSA: no usable channels. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - switch (rate) { - case 0: - rate = 44100; - err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); - if (0 <= err) { - frag_size = 32 * bps; - } else { - rate = 22050; - err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); - if (0 <= err) { - frag_size = 16 * bps; - } else { - rate = 11025; - err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, - 0); - if (0 <= err) { - frag_size = 8 * bps; - } else { - Con_Printf ("ALSA: no usable rates. %s\n", - snd_strerror (err)); - goto error; - } - } - } - break; - case 11025: - case 22050: - case 44100: - err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); - if (0 > err) { - Con_Printf ("ALSA: desired rate %i not supported. %s\n", rate, - snd_strerror (err)); - goto error; - } - frag_size = 8 * bps * rate / 11025; - break; - default: - Con_Printf ("ALSA: desired rate %i not supported.\n", rate); - goto error; - } + err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); + if (0 > err) + { + Con_Printf ("ALSA: desired rate %i not supported by driver. %s\n", rate, snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0); - if (0 > err) { - Con_Printf ("ALSA: unable to set period size near %i. %s\n", - (int) frag_size, snd_strerror (err)); - goto error; - } - err = snd_pcm_hw_params (pcm, hw); - if (0 > err) { - Con_Printf ("ALSA: unable to install hw params: %s\n", - snd_strerror (err)); - goto error; - } - err = snd_pcm_sw_params_current (pcm, sw); - if (0 > err) { - Con_Printf ("ALSA: unable to determine current sw params. %s\n", - snd_strerror (err)); - goto error; - } - err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U); - if (0 > err) { - Con_Printf ("ALSA: unable to set playback threshold. %s\n", - snd_strerror (err)); - goto error; - } - err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U); - if (0 > err) { - Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", - snd_strerror (err)); - goto error; - } - err = snd_pcm_sw_params (pcm, sw); - if (0 > err) { - Con_Printf ("ALSA: unable to install sw params. %s\n", - snd_strerror (err)); - goto error; - } + frag_size = 64 * width * rate / 11025; + err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0); + if (0 > err) + { + Con_Printf ("ALSA: unable to set period size near %i. %s\n", (int) frag_size, snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } + err = snd_pcm_hw_params (pcm, hw); + if (0 > err) + { + Con_Printf ("ALSA: unable to install hw params: %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } + err = snd_pcm_sw_params_current (pcm, sw); + if (0 > err) + { + Con_Printf ("ALSA: unable to determine current sw params. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } + err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U); + if (0 > err) + { + Con_Printf ("ALSA: unable to set playback threshold. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } + err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U); + if (0 > err) + { + Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } + err = snd_pcm_sw_params (pcm, sw); + if (0 > err) + { + Con_Printf ("ALSA: unable to install sw params. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - shm->format.channels = stereo + 1; - shm->samplepos = 0; - shm->format.width = bps / 8; + err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size); + if (0 > err) + { + Con_Printf ("ALSA: unable to get buffer size. %s\n", snd_strerror (err)); + snd_pcm_close (pcm); + continue; + } - err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size); - if (0 > err) { - Con_Printf ("ALSA: unable to get buffer size. %s\n", - snd_strerror (err)); - goto error; + memset( (void*) shm, 0, sizeof(*shm) ); + shm->format.channels = channels; + shm->format.width = width; + shm->format.speed = rate; + shm->samplepos = 0; + shm->sampleframes = buffer_size; + shm->samples = shm->sampleframes * shm->format.channels; + SNDDMA_GetDMAPos (); // sets shm->buffer + + snd_inited = 1; + return true; } - - shm->samples = buffer_size * shm->format.channels; // mono samples in buffer - shm->format.speed = rate; - SNDDMA_GetDMAPos (); // sets shm->buffer - - snd_inited = 1; - return true; - -error: - snd_pcm_close (pcm); return false; } @@ -272,7 +217,7 @@ int SNDDMA_GetDMAPos (void) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset; - snd_pcm_uframes_t nframes = shm->samples/shm->format.channels; + snd_pcm_uframes_t nframes = shm->sampleframes; if (!snd_inited) return 0; diff --git a/snd_bsd.c b/snd_bsd.c index caa7ee0c..ef22f5bc 100644 --- a/snd_bsd.c +++ b/snd_bsd.c @@ -111,7 +111,8 @@ qboolean SNDDMA_Init (void) (info.play.channels == 2) ? "stereo" : "mono", info.play.sample_rate); - shm->samples = sizeof (dma_buffer) / shm->format.width; + shm->sampleframes = sizeof (dma_buffer) / shm->format.width / shm->format.channels; + shm->samples = shm->sampleframes * shm->format.channels; shm->samplepos = 0; shm->buffer = dma_buffer; diff --git a/snd_main.c b/snd_main.c index 7a39a48a..0708a15b 100644 --- a/snd_main.c +++ b/snd_main.c @@ -24,6 +24,27 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "snd_main.h" #include "snd_ogg.h" +#if SND_LISTENERS != 8 +#error this data only supports up to 8 channel, update it! +#endif +typedef struct listener_s +{ + float yawangle; + float dotscale; + float dotbias; + float ambientvolume; +} +listener_t; +typedef struct speakerlayout_s +{ + const char *name; + unsigned int channels; + qboolean headphones; + listener_t listeners[SND_LISTENERS]; +} +speakerlayout_t; + +static speakerlayout_t snd_speakerlayout; void S_Play(void); void S_PlayVol(void); @@ -42,12 +63,13 @@ unsigned int total_channels; int snd_blocked = 0; cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0"}; cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1"}; +cvar_t snd_headphones = { CVAR_SAVE, "snd_headphones", "0"}; volatile dma_t *shm = 0; volatile dma_t sn; vec3_t listener_origin; -matrix4x4_t listener_matrix; +matrix4x4_t listener_matrix[SND_LISTENERS]; vec_t sound_nominal_clip_dist=1000.0; mempool_t *snd_mempool; @@ -98,7 +120,8 @@ void S_SoundInfo_f(void) return; } - Con_Printf("%5d stereo\n", shm->format.channels - 1); + Con_Printf("%5d speakers\n", shm->format.channels); + Con_Printf("%5d frames\n", shm->sampleframes); Con_Printf("%5d samples\n", shm->samples); Con_Printf("%5d samplepos\n", shm->samplepos); Con_Printf("%5d samplebits\n", shm->format.width * 8); @@ -122,9 +145,10 @@ void S_Startup(void) shm->format.width = 2; shm->format.speed = 22050; shm->format.channels = 2; - shm->samples = 32768; + shm->sampleframes = 16384; + shm->samples = shm->sampleframes * shm->format.channels; shm->samplepos = 0; - shm->buffer = (unsigned char *)Mem_Alloc(snd_mempool, shm->format.channels * shm->samples * shm->format.width); + shm->buffer = (unsigned char *)Mem_Alloc(snd_mempool, shm->samples * shm->format.width); } else { @@ -177,6 +201,7 @@ void S_Init(void) Cvar_RegisterVariable(&volume); Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&snd_staticvolume); + Cvar_RegisterVariable(&snd_headphones); // COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio) if (COM_CheckParm("-nosound") || COM_CheckParm("-safe")) @@ -459,22 +484,36 @@ channel_t *SND_PickChannel(int entnum, int entchannel) for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) { ch = &channels[ch_idx]; - if (entchannel != 0 // channel 0 never overrides - && ch->entnum == entnum - && (ch->entchannel == entchannel || entchannel == -1) ) - { // always override sound from same entity - first_to_die = ch_idx; - break; + if (entchannel != 0) + { + // try to override an existing channel + if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1) ) + { + // always override sound from same entity + first_to_die = ch_idx; + break; + } + } + else + { + if (!ch->sfx) + { + // no sound on this channel + first_to_die = ch_idx; + break; + } } - // don't let monster sounds override player sounds - if (ch->entnum == cl.viewentity && entnum != cl.viewentity && ch->sfx) - continue; + if (ch->sfx) + { + // 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) != 0 || - (ch->sfx != NULL && ch->sfx->loopstart >= 0)) - continue; + // don't override looped sounds + if ((ch->flags & CHANNELFLAG_FORCELOOP) || ch->sfx->loopstart >= 0) + continue; + } if (ch->end - paintedtime < life_left) { @@ -500,48 +539,54 @@ Spatializes a channel */ void SND_Spatialize(channel_t *ch, qboolean isstatic) { - vec_t dist, scale, pan; + int i; + vec_t dist, mastervol, intensity, vol; 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) + { + //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; + // Adjust volume of static sounds + if (isstatic) + mastervol *= snd_staticvolume.value; + // 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) { - ch->leftvol = ch->master_vol; - ch->rightvol = ch->master_vol; + for (i = 0;i < SND_LISTENERS;i++) + { + vol = mastervol * snd_speakerlayout.listeners[i].ambientvolume; + ch->listener_volume[i] = bound(0, vol, 255); + } } else { - // update sound origin if we know about the entity - if (ch->entnum > 0 && cls.state == ca_connected && cl_entities[ch->entnum].render.model) + // 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); + if (intensity > 0) { - //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].persistent.trail_origin[0], cl_entities[ch->entnum].persistent.trail_origin[1], cl_entities[ch->entnum].persistent.trail_origin[2]); - VectorCopy(cl_entities[ch->entnum].persistent.trail_origin, ch->origin); + for (i = 0;i < SND_LISTENERS;i++) + { + Matrix4x4_Transform(&listener_matrix[i], ch->origin, source_vec); + VectorNormalize(source_vec); + vol = intensity * max(0, source_vec[0] * snd_speakerlayout.listeners[i].dotscale + snd_speakerlayout.listeners[i].dotbias); + ch->listener_volume[i] = bound(0, vol, 255); + } } - - // calculate stereo seperation and distance attenuation - Matrix4x4_Transform(&listener_matrix, ch->origin, source_vec); - dist = VectorNormalizeLength(source_vec); - // distance - scale = ch->master_vol * (1.0 - (dist * ch->dist_mult)); - // panning - pan = scale * source_vec[1]; - // calculate the volumes - ch->leftvol = (int) (scale + pan); - ch->rightvol = (int) (scale - pan); - //Con_Printf("%f %f %f:%f %f %f:%f %f:%d %d\n", ch->origin[0], ch->origin[1], ch->origin[2], source_vec[0], source_vec[1], source_vec[2], scale, pan, ch->leftvol, ch->rightvol); - } - - // Adjust volume of static sounds - if (isstatic) - { - ch->leftvol *= snd_staticvolume.value; - ch->rightvol *= snd_staticvolume.value; + else + for (i = 0;i < SND_LISTENERS;i++) + ch->listener_volume[i] = 0; } - - // clamp volumes - ch->leftvol = bound(0, ch->leftvol, 255); - ch->rightvol = bound(0, ch->rightvol, 255); } @@ -777,15 +822,15 @@ S_UpdateAmbientSounds */ void S_UpdateAmbientSounds (void) { + int i; float vol; int ambient_channel; channel_t *chan; unsigned char ambientlevels[NUM_AMBIENTS]; - if (ambient_level.value <= 0 || !cl.worldmodel || !cl.worldmodel->brush.AmbientSoundLevelsForPoint) - return; - - cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels)); + memset(ambientlevels, 0, sizeof(ambientlevels)); + if (cl.worldmodel && cl.worldmodel->brush.AmbientSoundLevelsForPoint) + cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels)); // Calc ambient sound levels for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) @@ -794,7 +839,7 @@ void S_UpdateAmbientSounds (void) if (chan->sfx == NULL || chan->sfx->fetcher == NULL) continue; - vol = ambient_level.value * ambientlevels[ambient_channel]; + vol = ambientlevels[ambient_channel]; if (vol < 8) vol = 0; @@ -812,10 +857,84 @@ void S_UpdateAmbientSounds (void) chan->master_vol = vol; } - chan->leftvol = chan->rightvol = chan->master_vol; + for (i = 0;i < SND_LISTENERS;i++) + chan->listener_volume[i] = (int)(chan->master_vol * ambient_level.value * snd_speakerlayout.listeners[i].ambientvolume); } } +#define SND_SPEAKERLAYOUTS 5 +static speakerlayout_t snd_speakerlayouts[SND_SPEAKERLAYOUTS] = +{ + { + "surround71", 8, false, + { + {45, 0.2, 0.2, 0.5}, // front left + {315, 0.2, 0.2, 0.5}, // front right + {135, 0.2, 0.2, 0.5}, // rear left + {225, 0.2, 0.2, 0.5}, // rear right + {0, 0.2, 0.2, 0.5}, // front center + {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so... no lfe) + {90, 0.2, 0.2, 0.5}, // side left + {180, 0.2, 0.2, 0.5}, // side right + } + }, + { + "surround51", 6, false, + { + {45, 0.2, 0.2, 0.5}, // front left + {315, 0.2, 0.2, 0.5}, // front right + {135, 0.2, 0.2, 0.5}, // rear left + {225, 0.2, 0.2, 0.5}, // rear right + {0, 0.2, 0.2, 0.5}, // front center + {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so... no lfe) + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + }, + { + // these systems sometimes have a subwoofer as well, but it has no + // channel of its own + "surround40", 4, false, + { + {45, 0.3, 0.3, 0.8}, // front left + {315, 0.3, 0.3, 0.8}, // front right + {135, 0.3, 0.3, 0.8}, // rear left + {225, 0.3, 0.3, 0.8}, // rear right + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + }, + { + "headphones", 2, true, + { + {90, 0.5, 0.5, 1}, // side left + {270, 0.5, 0.5, 1}, // side right + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + }, + { + // these systems sometimes have a subwoofer as well, but it has no + // channel of its own + "stereo", 2, false, + { + {45, 0.5, 0.5, 1}, // front left + {315, 0.5, 0.5, 1}, // front right + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + } +}; /* ============ @@ -828,13 +947,27 @@ void S_Update(const matrix4x4_t *listenermatrix) { unsigned int i, j, total; channel_t *ch, *combine; + matrix4x4_t basematrix, rotatematrix; if (!snd_initialized.integer || (snd_blocked > 0)) return; - Matrix4x4_Invert_Simple(&listener_matrix, listenermatrix); + Matrix4x4_Invert_Simple(&basematrix, listenermatrix); Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin); + // select speaker layout + for (i = 0;i < SND_SPEAKERLAYOUTS - 1;i++) + if (snd_speakerlayouts[i].channels == shm->format.channels && (!snd_speakerlayouts[i].headphones || snd_headphones.integer)) + break; + snd_speakerlayout = snd_speakerlayouts[i]; + + // calculate the current matrices + for (j = 0;j < SND_LISTENERS;j++) + { + Matrix4x4_CreateFromQuakeEntity(&rotatematrix, 0, 0, 0, 0, snd_speakerlayout.listeners[j].yawangle, 0, 1); + Matrix4x4_Concat(&listener_matrix[j], &basematrix, &rotatematrix); + } + // update general area ambient sound sources S_UpdateAmbientSounds (); @@ -846,39 +979,41 @@ void S_Update(const matrix4x4_t *listenermatrix) { if (!ch->sfx) continue; - SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS); // respatialize channel - if (!ch->leftvol && !ch->rightvol) - continue; + + // respatialize channel + SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS); // try to combine static sounds with a previous channel of the same // sound effect so we don't mix five torches every frame if (i > MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) { - // see if it can just use the last one - if (combine && combine->sfx == ch->sfx) - { - combine->leftvol += ch->leftvol; - combine->rightvol += ch->rightvol; - ch->leftvol = ch->rightvol = 0; - continue; - } - // search for one - combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; - for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == ch->sfx) + // no need to merge silent channels + for (j = 0;j < SND_LISTENERS;j++) + if (ch->listener_volume[j]) break; - - if (j == total_channels) + if (j == SND_LISTENERS) + continue; + // if the last combine chosen isn't suitable, find a new one + if (!(combine && combine != ch && combine->sfx == ch->sfx)) + { + // search for one combine = NULL; - else + for (j = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;j < i;j++) + { + if (channels[j].sfx == ch->sfx) + { + combine = channels + j; + break; + } + } + } + if (combine && combine != ch && combine->sfx == ch->sfx) { - if (combine != ch) + for (j = 0;j < SND_LISTENERS;j++) { - combine->leftvol += ch->leftvol; - combine->rightvol += ch->rightvol; - ch->leftvol = ch->rightvol = 0; + combine->listener_volume[j] += ch->listener_volume[j]; + ch->listener_volume[j] = 0; } - continue; } } } @@ -891,8 +1026,16 @@ void S_Update(const matrix4x4_t *listenermatrix) total = 0; ch = channels; for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) - total++; + { + if (ch->sfx) + { + for (j = 0;j < SND_LISTENERS;j++) + if (ch->listener_volume[j]) + break; + if (j < SND_LISTENERS) + total++; + } + } Con_Printf("----(%u)----\n", total); } @@ -907,7 +1050,7 @@ void GetSoundtime(void) static int oldsamplepos; int fullsamples; - fullsamples = shm->samples / shm->format.channels; + fullsamples = shm->sampleframes; // it is possible to miscount buffers if it has wrapped twice between // calls to S_Update. Oh well. @@ -940,7 +1083,6 @@ void S_ExtraUpdate (void) void S_Update_(void) { unsigned endtime; - int samps; if (!sound_started || (snd_blocked > 0)) return; @@ -954,9 +1096,7 @@ void S_Update_(void) // mix ahead of current position endtime = soundtime + _snd_mixahead.value * shm->format.speed; - samps = shm->samples >> (shm->format.channels - 1); - if (endtime > (unsigned int)(soundtime + samps)) - endtime = soundtime + samps; + endtime = min(endtime, (unsigned int)(soundtime + shm->sampleframes)); S_PaintChannels (endtime); @@ -974,7 +1114,7 @@ console functions static void S_Play_Common(float fvol, float attenuation) { int i, ch_ind; - char name[256]; + char name[MAX_QPATH]; sfx_t *sfx; i = 1; diff --git a/snd_main.h b/snd_main.h index fbf2f3e4..ec097d22 100644 --- a/snd_main.h +++ b/snd_main.h @@ -67,19 +67,24 @@ struct sfx_s typedef struct dma_s { snd_format_t format; + int sampleframes; // frames in buffer (frame = samples for all speakers) int samples; // mono samples in buffer int samplepos; // in mono samples unsigned char *buffer; int bufferlength; // used only by certain drivers } dma_t; +// maximum supported speakers constant +#define SND_LISTENERS 8 + typedef struct channel_s { + int pad[8]; sfx_t *sfx; // sfx number + int pad2[8]; unsigned int flags; // cf CHANNELFLAG_* defines int master_vol; // 0-255 master volume - int leftvol; // 0-255 volume - int rightvol; // 0-255 volume + short listener_volume[SND_LISTENERS]; // 0-255 volume per speaker int end; // end time in global paintsamples int lastptime; // last time this channel was painted int pos; // sample position in sfx diff --git a/snd_mix.c b/snd_mix.c index d2329f40..f4a857d3 100644 --- a/snd_mix.c +++ b/snd_mix.c @@ -24,18 +24,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef struct portable_samplepair_s { - int left; - int right; -} portable_samplepair_t; + int sample[SND_LISTENERS]; +} portable_sampleframe_t; // LordHavoc: was 512, expanded to 2048 #define PAINTBUFFER_SIZE 2048 -portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; +portable_sampleframe_t paintbuffer[PAINTBUFFER_SIZE]; // FIXME: this desyncs with the video too easily extern qboolean cl_capturevideo_active; extern void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate); -void S_CaptureAVISound(portable_samplepair_t *buf, size_t length) +void S_CaptureAVISound(portable_sampleframe_t *buf, size_t length) { int n; size_t i; @@ -45,11 +44,11 @@ void S_CaptureAVISound(portable_samplepair_t *buf, size_t length) // write the sound buffer as little endian 16bit interleaved stereo for(i = 0;i < length;i++) { - n = buf[i].left; + n = buf[i].sample[0]; n = bound(-32768, n, 32767); out[i*4+0] = n & 0xFF; out[i*4+1] = (n >> 8) & 0xFF; - n = buf[i].right; + n = buf[i].sample[1]; n = bound(-32768, n, 32767); out[i*4+2] = n & 0xFF; out[i*4+3] = (n >> 8) & 0xFF; @@ -61,134 +60,253 @@ void S_CaptureAVISound(portable_samplepair_t *buf, size_t length) void S_TransferPaintBuffer(int endtime) { void *pbuf; + int i; + portable_sampleframe_t *snd_p; + int lpaintedtime; + int snd_frames; + int val; if ((pbuf = S_LockBuffer())) { - int i; - int *snd_p; - int lpaintedtime; - int snd_linear_count; - int val; - snd_p = (int *) paintbuffer; + snd_p = paintbuffer; lpaintedtime = paintedtime; - if (shm->format.width == 2) + for (lpaintedtime = paintedtime;lpaintedtime < endtime;lpaintedtime += snd_frames) { - // 16bit - short *snd_out; - if (shm->format.channels == 2) + // handle recirculating buffer issues + i = lpaintedtime & (shm->sampleframes - 1); + snd_frames = shm->sampleframes - i; + if (snd_frames > endtime - lpaintedtime) + snd_frames = endtime - lpaintedtime; + if (shm->format.width == 2) { - // 16bit 2 channels (stereo) - while (lpaintedtime < endtime) + // 16bit + short *snd_out = (short *) pbuf + i * shm->format.channels; + if (shm->format.channels == 8) { - // handle recirculating buffer issues - i = lpaintedtime & ((shm->samples >> 1) - 1); - snd_out = (short *) pbuf + (i << 1); - snd_linear_count = (shm->samples >> 1) - i; - if (snd_linear_count > endtime - lpaintedtime) - snd_linear_count = endtime - lpaintedtime; - snd_linear_count <<= 1; + // 7.1 surround if (snd_swapstereo.value) { - for (i = 0;i < snd_linear_count;i += 2) + for (i = 0;i < snd_frames;i++, snd_p++) { - snd_out[i ] = bound(-32768, snd_p[i + 1], 32767); - snd_out[i + 1] = bound(-32768, snd_p[i ], 32767); + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[3], 32767); + *snd_out++ = bound(-32768, snd_p->sample[2], 32767); + *snd_out++ = bound(-32768, snd_p->sample[4], 32767); + *snd_out++ = bound(-32768, snd_p->sample[5], 32767); + *snd_out++ = bound(-32768, snd_p->sample[6], 32767); + *snd_out++ = bound(-32768, snd_p->sample[7], 32767); } } else { - for (i = 0;i < snd_linear_count;i += 2) + for (i = 0;i < snd_frames;i++, snd_p++) { - snd_out[i ] = bound(-32768, snd_p[i ], 32767); - snd_out[i + 1] = bound(-32768, snd_p[i + 1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[2], 32767); + *snd_out++ = bound(-32768, snd_p->sample[3], 32767); + *snd_out++ = bound(-32768, snd_p->sample[4], 32767); + *snd_out++ = bound(-32768, snd_p->sample[5], 32767); + *snd_out++ = bound(-32768, snd_p->sample[6], 32767); + *snd_out++ = bound(-32768, snd_p->sample[7], 32767); } } - snd_p += snd_linear_count; - lpaintedtime += (snd_linear_count >> 1); } - } - else - { - // 16bit 1 channel (mono) - while (lpaintedtime < endtime) + else if (shm->format.channels == 6) { - // handle recirculating buffer issues - i = lpaintedtime & (shm->samples - 1); - snd_out = (short *) pbuf + i; - snd_linear_count = shm->samples - i; - if (snd_linear_count > endtime - lpaintedtime) - snd_linear_count = endtime - lpaintedtime; - for (i = 0;i < snd_linear_count;i++) + // 5.1 surround + if (snd_swapstereo.value) { - val = (snd_p[i * 2 + 0] + snd_p[i * 2 + 1]) >> 1; - snd_out[i] = bound(-32768, val, 32767); + for (i = 0;i < snd_frames;i++, snd_p++) + { + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[3], 32767); + *snd_out++ = bound(-32768, snd_p->sample[2], 32767); + *snd_out++ = bound(-32768, snd_p->sample[4], 32767); + *snd_out++ = bound(-32768, snd_p->sample[5], 32767); + } + } + else + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[2], 32767); + *snd_out++ = bound(-32768, snd_p->sample[3], 32767); + *snd_out++ = bound(-32768, snd_p->sample[4], 32767); + *snd_out++ = bound(-32768, snd_p->sample[5], 32767); + } } - snd_p += snd_linear_count << 1; - lpaintedtime += snd_linear_count; } - } - } - else - { - // 8bit - unsigned char *snd_out; - if (shm->format.channels == 2) - { - // 8bit 2 channels (stereo) - while (lpaintedtime < endtime) + else if (shm->format.channels == 4) + { + // 4.0 surround + if (snd_swapstereo.value) + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[3], 32767); + *snd_out++ = bound(-32768, snd_p->sample[2], 32767); + } + } + else + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[2], 32767); + *snd_out++ = bound(-32768, snd_p->sample[3], 32767); + } + } + } + else if (shm->format.channels == 2) { - // handle recirculating buffer issues - i = lpaintedtime & ((shm->samples >> 1) - 1); - snd_out = (unsigned char *) pbuf + (i << 1); - snd_linear_count = (shm->samples >> 1) - i; - if (snd_linear_count > endtime - lpaintedtime) - snd_linear_count = endtime - lpaintedtime; - snd_linear_count <<= 1; + // 2.0 stereo if (snd_swapstereo.value) { - for (i = 0;i < snd_linear_count;i += 2) + for (i = 0;i < snd_frames;i++, snd_p++) { - val = (snd_p[i + 1] >> 8) + 128; - snd_out[i ] = bound(0, val, 255); - val = (snd_p[i ] >> 8) + 128; - snd_out[i + 1] = bound(0, val, 255); + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); } } else { - for (i = 0;i < snd_linear_count;i += 2) + for (i = 0;i < snd_frames;i++, snd_p++) { - val = (snd_p[i ] >> 8) + 128; - snd_out[i ] = bound(0, val, 255); - val = (snd_p[i + 1] >> 8) + 128; - snd_out[i + 1] = bound(0, val, 255); + *snd_out++ = bound(-32768, snd_p->sample[0], 32767); + *snd_out++ = bound(-32768, snd_p->sample[1], 32767); } } - snd_p += snd_linear_count; - lpaintedtime += (snd_linear_count >> 1); + } + else if (shm->format.channels == 1) + { + // 1.0 mono + for (i = 0;i < snd_frames;i++, snd_p++) + *snd_out++ = bound(-32768, (snd_p->sample[0] + snd_p->sample[1]) >> 1, 32767); } } else { - // 8bit 1 channel (mono) - while (lpaintedtime < endtime) + // 8bit + unsigned char *snd_out = (unsigned char *) pbuf + i * shm->format.channels; + if (shm->format.channels == 8) + { + // 7.1 surround + if (snd_swapstereo.value) + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + else + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + } + else if (shm->format.channels == 6) + { + // 5.1 surround + if (snd_swapstereo.value) + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + else + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + } + else if (shm->format.channels == 4) + { + // 4.0 surround + if (snd_swapstereo.value) + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + else + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + } + else if (shm->format.channels == 2) { - // handle recirculating buffer issues - i = lpaintedtime & (shm->samples - 1); - snd_out = (unsigned char *) pbuf + i; - snd_linear_count = shm->samples - i; - if (snd_linear_count > endtime - lpaintedtime) - snd_linear_count = endtime - lpaintedtime; - for (i = 0;i < snd_linear_count;i++) + // 2.0 stereo + if (snd_swapstereo.value) { - val = ((snd_p[i * 2] + snd_p[i * 2 + 1]) >> 9) + 128; - snd_out[i ] = bound(0, val, 255); + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } } - snd_p += snd_linear_count << 1; - lpaintedtime += snd_linear_count; + else + { + for (i = 0;i < snd_frames;i++, snd_p++) + { + val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); + val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); + } + } + } + else if (shm->format.channels == 1) + { + // 1.0 mono + for (i = 0;i < snd_frames;i++, snd_p++) + val = ((snd_p->sample[0]+snd_p->sample[1]) >> 9) + 128;*snd_out++ = bound(0, val, 255); } } } - S_UnlockBuffer(); } } @@ -202,12 +320,11 @@ CHANNEL MIXING =============================================================================== */ -qboolean SND_PaintChannelFrom8 (channel_t *ch, int endtime); -qboolean SND_PaintChannelFrom16 (channel_t *ch, int endtime); +qboolean SND_PaintChannel (channel_t *ch, int endtime); void S_PaintChannels(int endtime) { - unsigned int i; + unsigned int i, j; int end; channel_t *ch; sfx_t *sfx; @@ -230,7 +347,12 @@ void S_PaintChannels(int endtime) sfx = ch->sfx; if (!sfx) continue; - if (!ch->leftvol && !ch->rightvol) + if (sfx->fetcher) + j++; + for (j = 0;j < SND_LISTENERS;j++) + if (ch->listener_volume[j]) + break; + if (j == SND_LISTENERS) continue; if (!S_LoadSound (sfx, true)) continue; @@ -283,15 +405,10 @@ void S_PaintChannels(int endtime) if (count > 0) { - if (ch->leftvol > 255) - ch->leftvol = 255; - if (ch->rightvol > 255) - ch->rightvol = 255; + for (j = 0;j < SND_LISTENERS;j++) + ch->listener_volume[j] = bound(0, ch->listener_volume[j], 255); - if (sfx->format.width == 1) - stop_paint = !SND_PaintChannelFrom8 (ch, count); - else - stop_paint = !SND_PaintChannelFrom16 (ch, count); + stop_paint = !SND_PaintChannel (ch, count); if (!stop_paint) { @@ -331,11 +448,9 @@ void S_PaintChannels(int endtime) } -// TODO: Try to merge SND_PaintChannelFrom8 and SND_PaintChannelFrom16 -qboolean SND_PaintChannelFrom8 (channel_t *ch, int count) +qboolean SND_PaintChannel (channel_t *ch, int count) { - int snd_vol, leftvol, rightvol; - const signed char *sfx; + int snd_vol, vol[SND_LISTENERS]; const sfxbuffer_t *sb; int i; @@ -345,78 +460,235 @@ qboolean SND_PaintChannelFrom8 (channel_t *ch, int count) else snd_vol = volume.value * 256; - leftvol = ch->leftvol * snd_vol; - rightvol = ch->rightvol * snd_vol; + for (i = 0;i < SND_LISTENERS;i++) + vol[i] = ch->listener_volume[i] * snd_vol; sb = ch->sfx->fetcher->getsb (ch, ch->pos, count); if (sb == NULL) return false; - // Stereo sound support - if (ch->sfx->format.channels == 2) +#if SND_LISTENERS != 8 +#error this code only supports up to 8 channels, update it +#endif + + if (ch->sfx->format.width == 1) { - sfx = (signed char *)sb->data + (ch->pos - sb->offset) * 2; - for (i = 0;i < count;i++) + const signed char *sfx = (signed char *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels; + // Stereo sound support + if (ch->sfx->format.channels == 2) { - paintbuffer[i].left += (*sfx++ * leftvol) >> 8; - paintbuffer[i].right += (*sfx++ * rightvol) >> 8; + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8; + paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9; + paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9; + paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8; + paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 8; + sfx += 2; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8; + paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9; + paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9; + sfx += 2; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8; + sfx += 2; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; + sfx += 2; + } + } } - } - else - { - sfx = (signed char *)sb->data + ch->pos - sb->offset; - for (i = 0;i < count;i++) + else if (ch->sfx->format.channels == 1) { - paintbuffer[i].left += (*sfx * leftvol) >> 8; - paintbuffer[i].right += (*sfx++ * rightvol) >> 8; + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8; + paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8; + paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8; + paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8; + paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 8; + sfx += 1; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8; + paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8; + paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8; + sfx += 1; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8; + sfx += 1; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; + sfx += 1; + } + } } + else + return true; // unsupported number of channels in sound } - ch->pos += count; - return true; -} - -qboolean SND_PaintChannelFrom16 (channel_t *ch, int count) -{ - int snd_vol, leftvol, rightvol; - signed short *sfx; - const sfxbuffer_t *sb; - int i; - - // If this channel manages its own volume - if (ch->flags & CHANNELFLAG_FULLVOLUME) - snd_vol = 256; - else - snd_vol = volume.value * 256; - - leftvol = ch->leftvol * snd_vol; - rightvol = ch->rightvol * snd_vol; - - sb = ch->sfx->fetcher->getsb (ch, ch->pos, count); - if (sb == NULL) - return false; - - // Stereo sound support - if (ch->sfx->format.channels == 2) + else if (ch->sfx->format.width == 2) { - sfx = (signed short *)sb->data + (ch->pos - sb->offset) * 2; - - for (i=0 ; idata + (ch->pos - sb->offset) * ch->sfx->format.channels; + // Stereo sound support + if (ch->sfx->format.channels == 2) { - paintbuffer[i].left += (*sfx++ * leftvol) >> 16; - paintbuffer[i].right += (*sfx++ * rightvol) >> 16; + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16; + paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17; + paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17; + paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16; + paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 16; + sfx += 2; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16; + paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17; + paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17; + sfx += 2; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16; + sfx += 2; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; + sfx += 2; + } + } } - } - else - { - sfx = (signed short *)sb->data + ch->pos - sb->offset; - - for (i=0 ; isfx->format.channels == 1) { - paintbuffer[i].left += (*sfx * leftvol) >> 16; - paintbuffer[i].right += (*sfx++ * rightvol) >> 16; + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16; + paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16; + paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16; + paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16; + paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 16; + sfx += 1; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16; + paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16; + paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16; + sfx += 1; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16; + sfx += 1; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; + sfx += 1; + } + } } + else + return true; // unsupported number of channels in sound } - ch->pos += count; return true; } diff --git a/snd_oss.c b/snd_oss.c index eef8e62c..b1503200 100644 --- a/snd_oss.c +++ b/snd_oss.c @@ -189,7 +189,8 @@ qboolean SNDDMA_Init(void) return 0; } - shm->samples = info.fragstotal * info.fragsize / shm->format.width; + shm->sampleframes = info.fragstotal * info.fragsize / shm->format.width / shm->format.channels; + shm->samples = shm->sampleframes * shm->format.channels; // memory map the dma buffer shm->bufferlength = info.fragstotal * info.fragsize; diff --git a/snd_sdl.c b/snd_sdl.c index dbeb3ced..1b9ef7d6 100644 --- a/snd_sdl.c +++ b/snd_sdl.c @@ -22,12 +22,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /* Info: -One SDL sample consists of x channel samples -The mixer supposes that the driver has one channel entry/sample though it has x channels/sample -like the SDL +SDL samples are really frames (full set of samples for all speakers) */ -#define AUDIO_SDL_SAMPLES 4096 +#define AUDIO_SDL_SAMPLEFRAMES 4096 #define AUDIO_LOCALFACTOR 4 typedef struct AudioState_s @@ -81,8 +79,9 @@ Returns false if nothing is found. qboolean SNDDMA_Init(void) { - SDL_AudioSpec spec; + SDL_AudioSpec wantspec; int i; + int channels; // Init the SDL Audio subsystem if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) { @@ -90,48 +89,55 @@ qboolean SNDDMA_Init(void) return false; } - // Init the shm structure - memset( (void*) shm, 0, sizeof(*shm) ); - - shm->format.channels = 2; //stereo - shm->format.width = 2; - -// COMMANDLINEOPTION: SDL Sound: -sndspeed chooses 44100 hz, 22100 hz, or 11025 hz sound output rate - i = COM_CheckParm( "-sndspeed" ); - if( i && i != ( com_argc - 1 ) ) - shm->format.speed = atoi( com_argv[ i+1 ] ); - else - shm->format.speed = 44100; - - shm->samplepos = 0; - shm->samples = AUDIO_SDL_SAMPLES * AUDIO_LOCALFACTOR; - shm->bufferlength = shm->samples * shm->format.width; - shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength ); - - // Init the as structure - as.buffer = shm->buffer; - as.width = shm->format.width; - as.pos = 0; - as.size = shm->bufferlength; - - // Init the SDL Audio subsystem - spec.callback = Buffer_Callback; - spec.channels = shm->format.channels; - spec.format = AUDIO_S16SYS; - spec.freq = shm->format.speed; - spec.userdata = NULL; - spec.samples = AUDIO_SDL_SAMPLES; - - if( SDL_OpenAudio( &spec, NULL ) ) { + for (channels = 8;channels >= 2;channels -= 2) + { + // Init the SDL Audio subsystem + wantspec.callback = Buffer_Callback; + wantspec.userdata = NULL; + wantspec.freq = 44100; + // COMMANDLINEOPTION: SDL Sound: -sndspeed chooses sound output rate (try values such as 44100, 48000, 22050, 11025 (quake), 24000, 32000, 96000, 192000, etc) + i = COM_CheckParm( "-sndspeed" ); + if( i && i != ( com_argc - 1 ) ) + wantspec.freq = atoi( com_argv[ i+1 ] ); + wantspec.format = AUDIO_S16SYS; + wantspec.channels = channels; + wantspec.samples = AUDIO_SDL_SAMPLEFRAMES; + + if( SDL_OpenAudio( &wantspec, NULL ) ) + { + Con_Printf("%s\n", SDL_GetError()); + continue; + } + + // Init the shm structure + memset( (void*) shm, 0, sizeof(*shm) ); + shm->format.channels = wantspec.channels; + shm->format.width = 2; + shm->format.speed = wantspec.freq; + + shm->samplepos = 0; + shm->sampleframes = wantspec.samples * AUDIO_LOCALFACTOR; + shm->samples = shm->sampleframes * shm->format.channels; + shm->bufferlength = shm->samples * shm->format.width; + shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength ); + + // Init the as structure + as.buffer = shm->buffer; + as.width = shm->format.width; + as.pos = 0; + as.size = shm->bufferlength; + break; + } + if (channels < 2) + { Con_Print( "Failed to open the audio device!\n" ); Con_DPrintf( "Audio Specification:\n" "\tChannels : %i\n" "\tFormat : %x\n" "\tFrequency : %i\n" - "\tBuffersize: %i Bytes(%i Samples)\n", - spec.channels, spec.format, spec.freq, shm->bufferlength , spec.samples ); - Mem_Free( shm->buffer ); + "\tSamples : %i\n", + wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples ); return false; } diff --git a/snd_win.c b/snd_win.c index 2752446f..0f62112c 100644 --- a/snd_win.c +++ b/snd_win.c @@ -397,6 +397,7 @@ sndinitstat SNDDMA_InitDirect (void) pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); shm->samples = gSndBufSize / shm->format.width; + shm->sampleframes = shm->samples / shm->format.channels; shm->samplepos = 0; shm->buffer = (unsigned char *) lpData; @@ -527,6 +528,7 @@ qboolean SNDDMA_InitWav (void) } shm->samples = gSndBufSize / shm->format.width; + shm->sampleframes = shm->samples / shm->format.channels; shm->samplepos = 0; shm->buffer = (unsigned char *) lpData; -- 2.39.2