41b0d993f2
support RLE+luminance+alpha tga files. support half-float tga files. recognise hdr astc images. added appropriate fallbacks for astc support. load mip-less .astc files (mostly just for debugging stuff). allow packages to warn about required engine/gpu features. catch when stdin flags get changed to blocking by external libraries, to avoid fatal stalls. basic support for .mdx files (kingpin models) sort packages loaded via wildcards, by datetime then name, to avoid random ordering from certain filesystems. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5531 fc73d0e0-1445-4013-8a0c-d673dee63da5
228 lines
5.8 KiB
C
228 lines
5.8 KiB
C
#include "quakedef.h"
|
|
#if defined(HAVE_MIXER) && defined(MULTITHREAD)
|
|
|
|
#if 0
|
|
#include <pulse/simple.h>
|
|
#else
|
|
typedef struct pa_simple pa_simple;
|
|
typedef enum pa_stream_direction {PA_STREAM_PLAYBACK=1} pa_stream_direction_t;
|
|
typedef enum pa_sample_format {
|
|
PA_SAMPLE_U8,
|
|
PA_SAMPLE_ALAW,
|
|
PA_SAMPLE_ULAW,
|
|
PA_SAMPLE_S16LE,
|
|
PA_SAMPLE_S16BE,
|
|
PA_SAMPLE_FLOAT32LE,
|
|
PA_SAMPLE_FLOAT32BE,
|
|
PA_SAMPLE_S32LE,
|
|
PA_SAMPLE_S32BE,
|
|
PA_SAMPLE_S24LE,
|
|
PA_SAMPLE_S24BE,
|
|
PA_SAMPLE_S24_32LE,
|
|
PA_SAMPLE_S24_32BE,
|
|
PA_SAMPLE_MAX,
|
|
PA_SAMPLE_INVALID = -1
|
|
} pa_sample_format_t;
|
|
typedef struct pa_sample_spec {
|
|
pa_sample_format_t format;
|
|
uint32_t rate;
|
|
uint8_t channels;
|
|
} pa_sample_spec;
|
|
typedef struct pa_channel_map pa_channel_map;
|
|
typedef struct pa_buffer_attr pa_buffer_attr;
|
|
typedef uint64_t pa_usec_t;
|
|
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32BE
|
|
#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
|
|
#else
|
|
#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32LE
|
|
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
|
|
#endif
|
|
#endif
|
|
|
|
|
|
static pa_simple *(*qpa_simple_new)(const char *server,const char *name,pa_stream_direction_t dir, const char *dev, const char *stream_name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_buffer_attr *attr, int *error);
|
|
static pa_usec_t (*qpa_simple_get_latency)(pa_simple *s, int *error);
|
|
static int (*qpa_simple_write)(pa_simple *s, const void *data, size_t bytes, int *error);
|
|
static void (*qpa_simple_free)(pa_simple *s);
|
|
|
|
static qboolean Pulse_Init(void)
|
|
{
|
|
static qboolean tried;
|
|
static void *pulsemodule;
|
|
static dllfunction_t funcs[] =
|
|
{
|
|
{(void**)&qpa_simple_new, "pa_simple_new"},
|
|
{(void**)&qpa_simple_get_latency, "pa_simple_get_latency"},
|
|
{(void**)&qpa_simple_write, "pa_simple_write"},
|
|
{(void**)&qpa_simple_free, "pa_simple_free"},
|
|
{NULL, NULL}
|
|
};
|
|
if (COM_CheckParm("-nopulse"))
|
|
return false;
|
|
|
|
if (!tried)
|
|
{
|
|
tried = true;
|
|
pulsemodule = Sys_LoadLibrary("libpulse-simple.so.0", funcs);
|
|
}
|
|
|
|
return pulsemodule!=NULL;
|
|
}
|
|
|
|
static unsigned int Pulse_GetDMAPos(soundcardinfo_t *sc)
|
|
{
|
|
sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;
|
|
return sc->sn.samplepos;
|
|
}
|
|
static void Pulse_Submit(soundcardinfo_t *sc, int start, int end)
|
|
{
|
|
}
|
|
|
|
static void Pulse_Shutdown(soundcardinfo_t *sc)
|
|
{
|
|
sc->selfpainting = false;
|
|
if (sc->thread)
|
|
Sys_WaitOnThread(sc->thread);
|
|
sc->thread = NULL;
|
|
*sc->name = '\0';
|
|
}
|
|
|
|
static void *Pulse_Lock(soundcardinfo_t *sc, unsigned int *sampidx)
|
|
{
|
|
return sc->sn.buffer;
|
|
}
|
|
|
|
static void Pulse_Unlock(soundcardinfo_t *sc, void *buffer)
|
|
{
|
|
}
|
|
|
|
static int Pulse_Thread(void *arg)
|
|
{
|
|
char buffer[256];
|
|
soundcardinfo_t *sc = arg;
|
|
void *cond = sc->handle;
|
|
int err = 0;
|
|
int showlatency = 64;
|
|
|
|
pa_simple *pulse;
|
|
pa_sample_spec ss;
|
|
ss.rate = sc->sn.speed;
|
|
switch(sc->sn.sampleformat)
|
|
{
|
|
case QSF_INVALID:
|
|
case QSF_EXTERNALMIXER:
|
|
case QSF_S8: //no signed 8bit formats here
|
|
ss.format = PA_SAMPLE_INVALID;
|
|
break;
|
|
case QSF_U8:
|
|
ss.format = PA_SAMPLE_U8;
|
|
break;
|
|
case QSF_S16:
|
|
ss.format = PA_SAMPLE_S16NE;
|
|
break;
|
|
case QSF_F32:
|
|
ss.format = PA_SAMPLE_FLOAT32;
|
|
break;
|
|
}
|
|
ss.channels = sc->sn.numchannels;
|
|
|
|
pulse = qpa_simple_new( NULL, // Use the default server.
|
|
FULLENGINENAME, // Our application's name.
|
|
PA_STREAM_PLAYBACK,
|
|
NULL, // Use the default device.
|
|
"Game Audio", // Description of our stream.
|
|
&ss, // Our sample format.
|
|
NULL, // Use default channel map
|
|
NULL, // Use default buffering attributes.
|
|
NULL // Ignore error code.
|
|
);
|
|
if (pulse)
|
|
sc->selfpainting = true; //its going!
|
|
|
|
Sys_LockConditional(cond);
|
|
Sys_ConditionSignal(cond);
|
|
Sys_UnlockConditional(cond);
|
|
|
|
while(sc->selfpainting)
|
|
{
|
|
sc->sn.buffer = buffer;
|
|
sc->sn.samples = sizeof(buffer)/sc->sn.samplebytes;
|
|
sc->samplequeue = sc->sn.samples;
|
|
S_MixerThread(sc);
|
|
sc->snd_sent += sc->sn.samplebytes*sc->samplequeue;
|
|
|
|
if (qpa_simple_write(pulse, buffer, sc->sn.samplebytes*sc->samplequeue, &err) < 0)
|
|
{
|
|
Con_Printf("pa_simple_write failed\n");
|
|
sc->selfpainting = false; //some sort of error
|
|
}
|
|
|
|
if (showlatency > 0)
|
|
if (--showlatency == 0)
|
|
{ //we delay this print so that we have a chance of finding out the real value
|
|
pa_usec_t latency = qpa_simple_get_latency(pulse, &err);
|
|
Con_Printf("PulseAudio latency is about %.3f seconds\n", latency/1000000.0);
|
|
}
|
|
}
|
|
|
|
if (pulse)
|
|
qpa_simple_free(pulse);
|
|
return 0;
|
|
}
|
|
|
|
static qboolean Pulse_InitCard(soundcardinfo_t *sc, const char *snddev)
|
|
{ //FIXME: implement snd_multipledevices somehow.
|
|
|
|
if (!Pulse_Init())
|
|
return false;
|
|
|
|
sc->inactive_sound = true; //linux sound devices always play sound, even when we're not the active app...
|
|
sc->sn.samplebytes = 4;
|
|
sc->sn.sampleformat = QSF_F32;
|
|
sc->sn.buffer = NULL;
|
|
sc->sn.samplepos = 0;
|
|
sc->Submit = Pulse_Submit;
|
|
sc->GetDMAPos = Pulse_GetDMAPos;
|
|
sc->Lock = Pulse_Lock;
|
|
sc->Unlock = Pulse_Unlock;
|
|
sc->Shutdown = Pulse_Shutdown;
|
|
|
|
sc->handle = Sys_CreateConditional();
|
|
Sys_LockConditional(sc->handle);
|
|
sc->thread = Sys_CreateThread("pulse", Pulse_Thread, sc, THREADP_HIGHEST, 0);
|
|
if (sc->thread)
|
|
{
|
|
if (!Sys_ConditionWait(sc->handle))
|
|
sc->selfpainting = false;
|
|
//thread is up and running now.
|
|
}
|
|
Sys_UnlockConditional(sc->handle);
|
|
Sys_DestroyConditional(sc->handle);
|
|
|
|
if (!sc->selfpainting)
|
|
{ //err, thread signalled itself to die?
|
|
Pulse_Shutdown(sc);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#define SDRVNAME "Pulse"
|
|
|
|
static qboolean QDECL Pulse_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))
|
|
{
|
|
if (!Pulse_Init())
|
|
return true; //sucessfully enumerated no devices
|
|
return false; //not implemented (we'll get a default device only)
|
|
}
|
|
|
|
sounddriver_t Pulse_Output =
|
|
{
|
|
SDRVNAME,
|
|
Pulse_InitCard,
|
|
Pulse_Enumerate
|
|
};
|
|
|
|
#endif
|