mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-22 20:11:44 +00:00
lame attempt at simple pulseaudio driver
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5379 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
2361c7d14f
commit
aaa78e5101
1 changed files with 228 additions and 0 deletions
228
engine/client/snd_pulse.c
Normal file
228
engine/client/snd_pulse.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
#include "quakedef.h"
|
||||
#ifdef HAVE_MIXER
|
||||
|
||||
#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
|
Loading…
Reference in a new issue