shithub: qk1

Download patch

ref: 9a09ea8aae69d9a62c3c6fde8ddac80025214ab0
parent: bb5d36fa55021e81d20e8cd1309c6a163b4fe498
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sat Dec 23 20:28:57 EST 2023

replace SDL audio with OpenAL (wip)

--- a/Makefile
+++ b/Makefile
@@ -2,14 +2,14 @@
 DESTDIR?=
 PREFIX?=/usr/local
 BIN=${DESTDIR}${PREFIX}/bin
-SDL2_CFLAGS=$$(pkg-config --cflags sdl2)
-SDL2_LDFLAGS=$$(pkg-config --libs sdl2)
+EXTRA_CFLAGS=$$(pkg-config --cflags sdl2 openal)
+EXTRA_LDFLAGS=$$(pkg-config --libs sdl2 openal)
 CFLAGS?=-O2 -g
 CFLAGS+=-Wall -Wextra -Wno-unknown-pragmas -Wno-missing-field-initializers -Wno-implicit-fallthrough -Wno-microsoft-anon-tag
 CFLAGS+=-fms-extensions
-CFLAGS+=-I3rd/parg -Iunix -I. ${SDL2_CFLAGS}
+CFLAGS+=-I3rd/parg -Iunix -I. ${EXTRA_CFLAGS}
 LDFLAGS?=
-LDFLAGS+=-lm ${SDL2_LDFLAGS}
+LDFLAGS+=-lm ${EXTRA_LDFLAGS}
 
 OBJS=\
 	3rd/parg/parg.o\
@@ -76,7 +76,6 @@
 	r_surf.o\
 	sbar.o\
 	screen.o\
-	snd.o\
 	softfloat.o\
 	span.o\
 	span_alpha.o\
@@ -89,7 +88,7 @@
 	unix/net_udp.o\
 	unix/qk1.o\
 	unix/seprint.o\
-	unix/snd_sdl.o\
+	unix/snd_openal.o\
 	unix/vid.o\
 	view.o\
 	wav.o\
--- a/cl_parse.c
+++ b/cl_parse.c
@@ -86,9 +86,8 @@
 	vec3_t  pos;
 	int 	channel, ent;
 	int 	sound_num;
-	int 	volume;
 	int 	field_mask;
-	float 	attenuation;
+	float 	volume, attenuation;
 
 	field_mask = MSG_ReadByte();
 	volume = (field_mask & SND_VOLUME) ? MSG_ReadByte() : Spktvol;
@@ -597,12 +596,13 @@
 static void CL_ParseStaticSound (int large_sound)
 {
 	vec3_t		org;
-	int			sound_num, vol, atten;
+	int			sound_num;
+	float vol, atten;
 
 	MSG_ReadVec(cl.protocol, org);
 	sound_num = large_sound ? MSG_ReadShort() : MSG_ReadByte();
-	vol = MSG_ReadByte();
-	atten = MSG_ReadByte();
+	vol = MSG_ReadByte() / 255.0;
+	atten = MSG_ReadByte() / 64.0;
 
 	staticsfx(cl.sound_precache[sound_num], org, vol, atten);
 }
--- a/dat.h
+++ b/dat.h
@@ -54,9 +54,7 @@
 	Namb
 };
 
-enum{
-	Spktvol = 255
-};
+#define Spktvol 255.0
 #define Spktatt 1.0
 
 extern int debug;
--- a/shell.nix
+++ b/shell.nix
@@ -3,6 +3,7 @@
  name = "qk1";
  buildInputs = with pkgs; [
   SDL2
+  openal
   pkg-config
   gcc13
  ];
--- a/snd.c
+++ b/snd.c
@@ -505,8 +505,8 @@
 	}
 	c->sfx = sfx;
 	VectorCopy(zp, c->zp);
-	c->chvol = vol;
-	c->attf = (att / 64) / Clipdist;
+	c->chvol = vol * 255;
+	c->attf = att / Clipdist;
 	c->n = sc->length;
 	spatialize(c);
 }
--- /dev/null
+++ b/unix/snd_openal.c
@@ -1,0 +1,437 @@
+#include "quakedef.h"
+#include <AL/al.h>
+#include <AL/alc.h>
+#include <AL/alext.h>
+
+typedef struct alsfx_t alsfx_t;
+typedef struct alchan_t alchan_t;
+
+struct alsfx_t {
+	ALuint buf;
+	bool loop;
+	bool upsample;
+};
+
+struct alchan_t {
+	int ent;
+	int ch;
+	ALuint src;
+
+	alchan_t *prev, *next;
+};
+
+enum {
+	Srcstatic = -666,
+};
+
+cvar_t volume = {"volume", "0.7", 1};
+static cvar_t s_al_dev = {"s_al_device", "", true};
+static cvar_t s_al_resampler_default = {"s_al_resampler_default", "6", true}; // 23rd order Sinc
+static cvar_t s_al_resampler_up = {"s_al_resampler_up", "1", true}; // Linear
+static cvar_t s_al_hrtf = {"s_al_hrtf", "1", true};
+static cvar_t ambient_level = {"ambient_level", "0.3"};
+static cvar_t ambient_fade = {"ambient_fade", "100"};
+
+static ALCcontext *ctx;
+static ALCdevice *dev;
+static ALuint localsrc;
+static Sfx *known_sfx;
+static int num_sfx;
+static int map;
+static alchan_t *chans;
+
+static int al_default_resampler, al_num_resamplers;
+static char *(*alGetStringiSOFT)(ALenum, ALsizei);
+
+#define ALERR() alcheckerr(__FILE__, __LINE__)
+
+static int
+alcheckerr(const char *file, int line)
+{
+	int e, ret;
+	char *s, tmp[32];
+
+	ret = 0;
+	if(ctx != nil && (e = alGetError()) != AL_NO_ERROR){
+		switch(e){
+		case AL_INVALID_NAME: s = "invalid name"; break;
+		case AL_INVALID_ENUM: s = "invalid enum"; break;
+		case AL_INVALID_VALUE: s = "invalid value"; break;
+		case AL_INVALID_OPERATION: s = "invalid operation"; break;
+		case AL_OUT_OF_MEMORY: s = "out of memory"; break;
+		default:
+			snprint(tmp, sizeof(tmp), "unknown (0x%x)", e);
+			s = tmp;
+			break;
+		}
+		ret |= e;
+		fprintf(stderr, "%s:%d: AL: %s\n", file, line, s);
+	}
+	if(dev != nil && (e = alcGetError(dev)) != ALC_NO_ERROR){
+		switch(e){
+		case ALC_INVALID_DEVICE: s = "invalid device"; break;
+		case ALC_INVALID_ENUM: s = "invalid enum"; break;
+		case ALC_INVALID_VALUE: s = "invalid value"; break;
+		case ALC_INVALID_CONTEXT: s = "invalid context"; break;
+		case ALC_OUT_OF_MEMORY: s = "out of memory"; break;
+		default:
+			snprint(tmp, sizeof(tmp), "unknown error (0x%x)", e);
+			s = tmp;
+			break;
+		}
+		ret |= e;
+		fprintf(stderr, "%s:%d: ALC: %s\n", file, line, s);
+	}
+
+	return ret;
+}
+
+static alchan_t *
+getchan(int ent, int ch)
+{
+	alchan_t *c, *stopped;
+	ALint state;
+	ALuint src;
+
+	stopped = nil;
+	for(c = chans; c != nil; c = c->next){
+		if(c->ent == ent && c->ch == ch){
+			alSourceStop(c->src); ALERR();
+			return c;
+		}
+		if(stopped == nil){
+			alGetSourcei(c->src, AL_SOURCE_STATE, &state);
+			if(!ALERR() && state == AL_STOPPED)
+				stopped = c;
+		}
+	}
+
+	if(stopped != nil){
+		c = stopped;
+		c->ent = ent;
+		c->ch = ch;
+		return c;
+	}
+
+	alGenSources(1, &src);
+	if(ALERR())
+		return nil;
+
+	c = calloc(1, sizeof(*c));
+	c->ent = ent;
+	c->ch = ch;
+	c->src = src;
+	c->next = chans;
+	if(chans != nil)
+		chans->prev = c;
+	chans = c;
+
+	return c;
+}
+
+static void
+delchan(alchan_t *c)
+{
+	alSourceStop(c->src); ALERR();
+	alDeleteSources(1, &c->src); ALERR();
+	free(c);
+}
+
+static Sfx *
+findsfx(char *s)
+{
+	Sfx *sfx, *e;
+
+	if(strlen(s) >= Npath)
+		Host_Error("findsfx: path too long %s", s);
+	sfx = known_sfx;
+	e = known_sfx + num_sfx;
+	while(sfx < e){
+		if(strcmp(sfx->s, s) == 0)
+			return sfx;
+		sfx++;
+	}
+	if(num_sfx == MAX_SOUNDS){
+		sfx = known_sfx;
+		while(sfx < e){
+			if(sfx->map && sfx->map < map)
+				break;
+			sfx++;
+		}
+		if(sfx == e)
+			Host_Error("findsfx: sfx list overflow: %s", s);
+		if(Cache_Check(&sfx->cu))
+			Cache_Free(&sfx->cu);
+	}else
+		num_sfx++;
+	strcpy(sfx->s, s);
+	return sfx;
+}
+
+static alsfx_t *
+loadsfx(Sfx *sfx)
+{
+	alsfx_t *s;
+	wavinfo_t info;
+	byte *in;
+	ALuint buf;
+	ALenum fmt;
+	ALint loop[2];
+	int len;
+
+	if((s = Cache_Check(&sfx->cu)) != nil)
+		return s;
+	in = loadstklmp(va("sound/%s", sfx->s), nil, 0, &len);
+	if(in == nil){
+		Con_DPrintf("loadsfx: %s\n", lerr());
+		return nil;
+	}
+	if(wavinfo(in, len, &info) != 0){
+		Con_Printf("loadsfx: %s: %s\n", sfx->s, lerr());
+		return nil;
+	}
+	if(info.channels < 2)
+		fmt = info.width == 1 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
+	else
+		fmt = info.width == 1 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
+	alGenBuffers(1, &buf);
+	if(ALERR())
+		return nil;
+	alBufferData(buf, fmt, in+info.dataofs, info.samples*info.width, info.rate);
+	if(ALERR()){
+		alDeleteBuffers(1, &buf); ALERR();
+		return nil;
+	}
+	s = Cache_Alloc(&sfx->cu, sizeof(*s));
+	s->buf = buf;
+	if(info.loopofs >= 0){
+		loop[0] = info.loopofs;
+		loop[1] = info.samples;
+		alBufferiv(s->buf, AL_LOOP_POINTS_SOFT, loop); ALERR();
+		s->loop = true;
+	}
+	s->upsample = info.rate < 22050;
+
+	return s;
+}
+
+void
+stepsnd(vec3_t zp, vec3_t fw, vec3_t rt, vec3_t up)
+{
+	vec_t fwup[6] = {fw[0], fw[1], fw[2], up[0], up[1], up[2]};
+
+	if(dev == nil)
+		return;
+	USED(rt);
+	alListenerfv(AL_POSITION, zp); ALERR();
+	alListenerfv(AL_ORIENTATION, fwup); ALERR();
+	alListenerf(AL_GAIN, volume.value); ALERR();
+}
+
+void
+stopallsfx(void)
+{
+	alchan_t *c, *next;
+
+	if(dev == nil)
+		return;
+	alListenerf(AL_GAIN, 0); ALERR();
+	for(c = chans; c != nil; c = next){
+		next = c->next;
+		delchan(c);
+	}
+	chans = nil;
+}
+
+void
+stopsfx(int ent, int ch)
+{
+	alchan_t *c;
+
+	if(dev == nil)
+		return;
+	for(c = chans; c != nil; c = c->next){
+		if(c->ent == ent && c->ch == ch)
+			break;
+	}
+	if(c == nil)
+		return;
+	if(c->prev != nil)
+		c->prev->next = c->next;
+	if(c->next != nil)
+		c->next->prev = c->prev;
+	if(chans == c)
+		chans = c->next;
+	delchan(c);
+}
+
+void
+startsfx(int ent, int ch, Sfx *sfx, vec3_t zp, float vol, float att)
+{
+	alchan_t *c;
+	alsfx_t *s;
+	float x;
+	int n;
+
+	if(dev == nil || (s = loadsfx(sfx)) == nil)
+		return;
+	if((c = getchan(ent, ch)) == nil)
+		return;
+	x = att * 0.001f;
+	if(ent == cl.viewentity){
+		alSourcefv(c->src, AL_POSITION, vec3_origin); ALERR();
+		alSourcei(c->src, AL_SOURCE_RELATIVE, AL_TRUE); ALERR();
+		alSourcef(c->src, AL_ROLLOFF_FACTOR, 0.0f); ALERR();
+		alSourcef(c->src, AL_REFERENCE_DISTANCE, 0.0f); ALERR();
+	}else{
+		alSourcefv(c->src, AL_POSITION, zp); ALERR();
+		alSourcei(c->src, AL_SOURCE_RELATIVE, AL_FALSE); ALERR();
+		alSourcef(c->src, AL_ROLLOFF_FACTOR, x * (8192.0f - 1.0f)); ALERR();
+		alSourcef(c->src, AL_REFERENCE_DISTANCE, 1.0f); ALERR();
+		alSourcef(c->src, AL_MAX_DISTANCE, 8192.0f); ALERR();
+	}
+	alSourcef(c->src, AL_GAIN, vol); ALERR();
+	if(al_num_resamplers > 0){
+		n = s->upsample ? s_al_resampler_up.value : s_al_resampler_default.value;
+		if(n >= 0){
+			alSourcei(c->src, AL_SOURCE_RESAMPLER_SOFT, n);
+			ALERR();
+		}
+	}
+	alSourcei(c->src, AL_BUFFER, s->buf); ALERR();
+	alSourcei(c->src, AL_LOOPING, (s->loop || ent == Srcstatic) ? AL_TRUE : AL_FALSE); ALERR();
+	alSourcePlay(c->src); ALERR();
+}
+
+void
+localsfx(char *name)
+{
+	if(dev == nil)
+		return;
+
+	startsfx(cl.viewentity, -1, findsfx(name), vec3_origin, 1.0f, 1.0f);
+}
+
+void
+staticsfx(Sfx *sfx, vec3_t zp, float vol, float att)
+{
+	static int numst = 0;
+	alsfx_t *s;
+
+	if(dev == nil || (s = loadsfx(sfx)) == nil)
+		return;
+
+	startsfx(Srcstatic, numst++, sfx, zp, vol, att/1.5f);
+}
+
+void
+touchsfx(char *s)
+{
+	Sfx *sfx;
+
+	if(dev == nil)
+		return;
+	sfx = findsfx(s);
+	Cache_Check(&sfx->cu);
+}
+
+Sfx *
+precachesfx(char *s)
+{
+	Sfx *sfx;
+
+	if(dev == nil)
+		return nil;
+	sfx = findsfx(s);
+	assert(sfx != nil);
+	sfx->map = map;
+	loadsfx(sfx);
+	return sfx;
+}
+
+static int
+alinit(void)
+{
+	const char *devname;
+	ALCcontext *c;
+	int e;
+	ALCint attr[] = {
+		0, 0, 0
+	};
+
+	if(*(devname = s_al_dev.string) == 0)
+		devname = alcGetString(nil, ALC_DEFAULT_DEVICE_SPECIFIER);
+	if(devname == nil)
+		return -1;
+	dev = alcOpenDevice(devname); ALERR();
+	if(dev == nil)
+		return -1;
+
+	attr[0] = s_al_hrtf.value != 0 ? ALC_HRTF_SOFT : 0;
+	attr[1] = s_al_hrtf.value > 0 ? ALC_TRUE : ALC_FALSE;
+	c = alcCreateContext(dev, attr); ALERR();
+	if(c == nil){
+closedev:
+		alcCloseDevice(dev); ALERR();
+		dev = nil;
+		return -1;
+	}
+	ctx = c;
+	e = alcMakeContextCurrent(c); ALERR();
+	if(!e){
+		ctx = nil;
+		alcDestroyContext(c); ALERR();
+		goto closedev;
+	}
+	alGenSources(1, &localsrc);
+	if(!ALERR()){
+		alSourcei(localsrc, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE);
+		ALERR();
+	}
+	alListenerf(AL_GAIN, volume.value); ALERR();
+	alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
+
+	if(alIsExtensionPresent("AL_SOFT_source_resampler")){
+		al_default_resampler = alGetInteger(AL_DEFAULT_RESAMPLER_SOFT);
+		if(ALERR())
+			al_default_resampler = 0;
+		al_num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT);
+		if(ALERR())
+			al_num_resamplers = 0;
+		alGetStringiSOFT = alGetProcAddress("alGetStringiSOFT");
+	}
+
+	return 0;
+}
+
+void
+sfxbegin(void)
+{
+}
+
+int
+initsnd(void)
+{
+	Cvar_RegisterVariable(&volume);
+	Cvar_RegisterVariable(&ambient_level);
+	Cvar_RegisterVariable(&ambient_fade);
+	Cvar_RegisterVariable(&s_al_dev);
+	Cvar_RegisterVariable(&s_al_resampler_default);
+	Cvar_RegisterVariable(&s_al_resampler_up);
+	Cvar_RegisterVariable(&s_al_hrtf);
+	Cmd_AddCommand("stopsound", stopallsfx);
+
+	alinit();
+	known_sfx = Hunk_Alloc(MAX_SOUNDS * sizeof *known_sfx);
+	num_sfx = 0;
+
+	//ambsfx[Ambwater] = precachesfx("ambience/water1.wav");
+	//ambsfx[Ambsky] = precachesfx("ambience/wind2.wav");
+
+	return 0;
+}
+
+void
+sndclose(void)
+{
+}
--- a/unix/snd_sdl.c
+++ /dev/null
@@ -1,65 +1,0 @@
-#include "quakedef.h"
-#include <SDL.h>
-
-static SDL_AudioDeviceID adev;
-
-long
-sndqueued(void)
-{
-	return SDL_GetQueuedAudioSize(adev);
-}
-
-void
-sndstop(void)
-{
-	if(adev == 0)
-		return;
-	SDL_ClearQueuedAudio(adev);
-	SDL_PauseAudioDevice(adev, 1);
-}
-
-void
-sndwrite(uchar *buf, long sz)
-{
-	if(adev == 0)
-		return;
-	if(SDL_QueueAudio(adev, buf, sz) == 0)
-		SDL_PauseAudioDevice(adev, 0);
-	else{
-		Con_Printf("sndwrite: %s\n", SDL_GetError());
-		sndclose();
-	}
-}
-
-void
-sndclose(void)
-{
-	if(adev == 0)
-		return;
-	SDL_CloseAudioDevice(adev);
-	SDL_QuitSubSystem(SDL_INIT_AUDIO);
-	adev = 0;
-}
-
-int
-sndopen(void)
-{
-	SDL_AudioSpec got, want = {
-		.freq = 44100,
-		.format = AUDIO_S16LSB,
-		.channels = 2,
-		.samples = 441,
-		0,
-	};
-
-	if(SDL_InitSubSystem(SDL_INIT_AUDIO) != 0){
-err:
-		Con_Printf("sndopen: %s\n", SDL_GetError());
-		return -1;
-	}
-	if((adev = SDL_OpenAudioDevice(nil, 0, &want, &got, SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0){
-		SDL_QuitSubSystem(SDL_INIT_AUDIO);
-		goto err;
-	}
-	return 0;
-}