shithub: qk1

Download patch

ref: 3d9a179d3de101cf405df256918a7769d02698ee
parent: a1964a416460da1d6207671930034b9e942b12ed
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Dec 27 22:13:37 EST 2023

openal: implement track playback via ffmpeg

--- a/unix/snd_openal.c
+++ b/unix/snd_openal.c
@@ -2,6 +2,8 @@
 #include <AL/al.h>
 #include <AL/alc.h>
 #include <AL/alext.h>
+#include <sys/wait.h>
+#include <unistd.h>
 
 typedef struct albuf_t albuf_t;
 typedef struct alchan_t alchan_t;
@@ -28,9 +30,16 @@
 cvar_t volume = {"volume", "0.7", true};
 cvar_t bgmvolume = {"bgmvolume", "0.5", true};
 
-static cvar_t s_al_dev = {"s_al_device", "-1", true};
-static int s_al_dev_prev = -2;
+static struct {
+	ALuint src, buf;
+	int pcm;
+	bool playing;
+	pid_t decoder, reader;
+}track;
 
+static cvar_t s_al_dev = {"s_al_device", "0", true};
+static int s_al_dev_prev = -1;
+
 static cvar_t s_al_hrtf = {"s_al_hrtf", "0", true};
 static cvar_t s_al_doppler_factor = {"s_al_doppler_factor", "2", true};
 static cvar_t s_al_resampler_default = {"s_al_resampler_default", "6", true}; // 23rd order Sinc
@@ -38,7 +47,6 @@
 
 static ALCcontext *ctx;
 static ALCdevice *dev;
-static ALuint localsrc;
 static Sfx *known_sfx;
 static int num_sfx;
 static int map;
@@ -54,6 +62,7 @@
 static ALCchar *(*qalcGetStringiSOFT)(ALCdevice *, ALenum, ALsizei);
 static ALCboolean (*qalcResetDeviceSOFT)(ALCdevice *, const ALCint *attr);
 static ALCboolean *(*qalcReopenDeviceSOFT)(ALCdevice *, const ALCchar *devname, const ALCint *attr);
+static void (*qalBufferCallbackSOFT)(ALuint buf, ALenum fmt, ALsizei freq, ALBUFFERCALLBACKTYPESOFT cb, ALvoid *aux);
 
 #define ALERR() alcheckerr(__FILE__, __LINE__)
 
@@ -142,6 +151,14 @@
 static void
 delchan(alchan_t *c)
 {
+	if(c->prev != nil)
+		c->prev->next = c->next;
+	if(c->next != nil)
+		c->next->prev = c->prev;
+	if(chans == c)
+		chans = c->next;
+	alSourceStop(c->src); ALERR();
+	alSourcei(c->src, AL_BUFFER, 0); ALERR();
 	alDeleteSources(1, &c->src); ALERR();
 	free(c);
 }
@@ -313,7 +330,9 @@
 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]};
+	alchan_t *c, *next;
 	static vec3_t ozp;
+	ALint state;
 	vec3_t vel;
 
 	if(dev == nil)
@@ -332,6 +351,13 @@
 	alListenerf(AL_GAIN, volume.value); ALERR();
 
 	ambs(zp);
+
+	for(c = chans; c != nil; c = next){
+		next = c->next;
+		alGetSourcei(c->src, AL_SOURCE_STATE, &state);
+		if(!ALERR() && state != AL_PLAYING)
+			delchan(c);
+	}
 }
 
 void
@@ -474,11 +500,6 @@
 		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); ALERR();
 
@@ -494,7 +515,17 @@
 		if(ALERR())
 			al_num_resamplers = 0;
 		qalGetStringiSOFT = alGetProcAddress("alGetStringiSOFT");
+	}else{
+		qalGetStringiSOFT = nil;
 	}
+
+	qalBufferCallbackSOFT = nil;
+	if(alIsExtensionPresent("AL_SOFT_callback_buffer") && (alGenSources(1, &track.src), !ALERR())){
+		alSourcei(track.src, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE); ALERR();
+		qalBufferCallbackSOFT = alGetProcAddress("alBufferCallbackSOFT");
+		alGenBuffers(1, &track.buf); ALERR();
+	}
+
 	return 0;
 }
 
@@ -640,12 +671,134 @@
 void
 stepcd(void)
 {
+	alSourcef(track.src, AL_GAIN, bgmvolume.value); ALERR();
 }
 
+static ALsizei
+trackcb(ALvoid *aux, ALvoid *sampledata, ALsizei numbytes)
+{
+	ssize_t n;
+	byte *b;
+
+	USED(aux);
+	for(b = sampledata; numbytes > 0; b += n, numbytes -= n){
+		if((n = read(track.pcm, b, numbytes)) <= 0){
+			close(track.pcm);
+			track.pcm = -1;
+			break;
+		}
+	}
+
+	return b - (byte*)sampledata;
+}
+
 void
 playcd(int nt, int loop)
 {
-	USED(nt); USED(loop);
+	pid_t pid;
+	FILE *f;
+	fpos_t off;
+	int len, left, s[2], in[2];
+
+	shutcd();
+	if(qalBufferCallbackSOFT == nil)
+		return;
+
+	if((f = openlmp(va("music/track%02d.ogg", nt), &len)) == nil){
+		if((f = openlmp(va("music/track%02d.mp3", nt), &len)) == nil)
+			f = openlmp(va("music/track%02d.wav", nt), &len);
+	}
+	if(f == nil)
+		return;
+	if(fgetpos(f, &off) != 0){
+err:
+		close(track.pcm);
+		fclose(f);
+		if(track.decoder > 0)
+			waitpid(track.decoder, nil, 0);
+		if(track.reader > 0)
+			waitpid(track.reader, nil, 0);
+		return;
+	}
+
+	if(pipe(s) != 0)
+		goto err;
+	if(pipe(in) != 0){
+		close(s[0]);
+		close(s[1]);
+		goto err;
+	}
+
+	switch((pid = fork())){
+	case 0:
+		close(s[1]); dup2(s[0], 0);
+		close(in[0]); dup2(in[1], 1);
+		close(s[0]);
+		execl(
+			"/usr/bin/env", "/usr/bin/env",
+			"ffmpeg",
+				"-loglevel", "fatal",
+				"-i", "-",
+				"-acodec", "pcm_s16le",
+				"-f", "s16le",
+				"-ac", "2",
+				"-ar", "44100",
+				"-",
+			nil
+		);
+		perror("execl ffmpeg"); // FIXME(sigrid): add and use Con_Errorf?
+		exit(1);
+	case -1:
+		goto err;
+	}
+	track.decoder = pid;
+
+	close(s[0]);
+	close(in[1]);
+	track.pcm = in[0];
+
+	switch((pid = fork())){
+	case 0:
+		close(in[0]);
+		close(0);
+		close(1);
+		left = len;
+		for(;;){
+			byte tmp[32768];
+			ssize_t n;
+			if((n = fread(tmp, 1, min(left, (int)sizeof(tmp)), f)) < 1){
+				if(ferror(f)){
+					perror("fread");
+					break;
+				}
+				left -= n;
+				if(left < 1){
+					if(!loop)
+						break;
+					if(fsetpos(f, &off) != 0){
+						perror("fsetpos");
+						break;
+					}
+					left = len;
+				}
+			}
+			if(write(s[1], tmp, n) != n)
+				break;
+		}
+		close(s[1]);
+		fclose(f);
+		exit(1);
+	case -1:
+		goto err;
+	}
+	track.reader = pid;
+
+	qalBufferCallbackSOFT(track.buf, AL_FORMAT_STEREO16, 44100, trackcb, nil);
+	if(ALERR())
+		goto err;
+	alSourcei(track.src, AL_BUFFER, track.buf); ALERR();
+	alSourcePlay(track.src); ALERR();
+	track.playing = true;
 }
 
 static void
@@ -656,11 +809,13 @@
 void
 resumecd(void)
 {
+	alSourcePlay(track.src); ALERR();
 }
 
 void
 pausecd(void)
 {
+	alSourcePause(track.src); ALERR();
 }
 
 int
@@ -672,6 +827,17 @@
 void
 shutcd(void)
 {
+	if(track.playing){
+		alSourceStop(track.src); ALERR();
+		alSourcei(track.src, AL_BUFFER, 0); ALERR();
+		if(track.pcm >= 0)
+			close(track.pcm);
+		waitpid(track.decoder, nil, 0);
+		waitpid(track.reader, nil, 0);
+	}
+	track.playing = false;
+	track.pcm = -1;
+	track.decoder = track.reader = -1;
 }
 
 int