ref: 98543a898dbbfe789f13e9aad8a803bffaf08837
dir: /i_sound.c/
//**************************************************************************
//**
//** i_soundpi.c: unix sound driver using a plugin interface
//**
//** $Revision: 512 $
//** $Date: 2009-06-04 18:00:34 +0300 (Thu, 04 Jun 2009) $
//**
//**************************************************************************
#include "h2stdinc.h"
#include "h2def.h"
#include "sounds.h"
#include "i_sound.h"
#include "audio_plugin.h"
#define SAMPLE_FORMAT FMT_S16_NE
#define SAMPLE_ZERO 0
#define SAMPLE_RATE 11025 /* Hz */
#define SAMPLE_CHANNELS 2
#if 0
#define SAMPLE_TYPE char
#else
#define SAMPLE_TYPE short
#endif
/*
* SOUND HEADER & DATA
*/
int snd_Channels;
int snd_MaxVolume, /* maximum volume for sound */
snd_MusicVolume; /* maximum volume for music */
boolean snd_MusicAvail, /* whether music is available */
snd_SfxAvail; /* whether sfx are available */
/*
* SOUND FX API
*/
typedef struct
{
unsigned char *begin; /* pointers into Sample.firstSample */
unsigned char *end;
SAMPLE_TYPE *lvol_table; /* point into vol_lookup */
SAMPLE_TYPE *rvol_table;
unsigned int pitch_step;
unsigned int step_remainder; /* 0.16 bit remainder of last step. */
int pri;
unsigned int time;
} Channel;
#pragma pack on
typedef struct
{
/* Sample data is a lump from a wad: byteswap the a, freq
* and the length fields before using them */
short a; /* always 3 */
short freq; /* always 11025 */
int32_t length; /* sample length */
unsigned char firstSample;
} Sample;
#pragma pack off
static int audio_exit_thread = 1;
//static pthread_t audio_thread;
#define CHAN_COUNT 8
static Channel channel[CHAN_COUNT];
#define MAX_VOL 64 /* 64 keeps our table down to 16Kb */
static SAMPLE_TYPE vol_lookup[MAX_VOL * 256];
static int steptable[256]; /* Pitch to stepping lookup */
#define BUF_LEN (256 * 2)
static void audio_loop (void *arg)
{
Channel* chan;
Channel* cend;
char buf[BUF_LEN];
SAMPLE_TYPE *begin;
SAMPLE_TYPE *end;
unsigned int sample;
register int dl;
register int dr;
end = (SAMPLE_TYPE *) (buf + BUF_LEN);
cend = channel + CHAN_COUNT;
while (! audio_exit_thread) {
begin = (SAMPLE_TYPE *) buf;
while (begin < end)
{
// Mix all the channels together.
dl = SAMPLE_ZERO;
dr = SAMPLE_ZERO;
chan = channel;
for ( ; chan < cend; chan++)
{
// Check channel, if active.
if (chan->begin)
{
// Get the sample from the channel.
sample = *chan->begin;
// Adjust volume accordingly.
dl += chan->lvol_table[sample];
dr += chan->rvol_table[sample];
// Increment sample pointer with pitch adjustment.
chan->step_remainder += chan->pitch_step;
chan->begin += chan->step_remainder >> 16;
chan->step_remainder &= 65535;
// Check whether we are done.
if (chan->begin >= chan->end)
{
chan->begin = NULL;
// printf (" channel done %d\n", chan);
}
}
}
#if 0 /* SAMPLE_FORMAT */
if (dl > 127)
dl = 127;
else if (dl < -128)
dl = -128;
if (dr > 127)
dr = 127;
else if (dr < -128)
dr = -128;
#else
if (dl > 0x7fff)
dl = 0x7fff;
else if (dl < -0x8000)
dl = -0x8000;
if (dr > 0x7fff)
dr = 0x7fff;
else if (dr < -0x8000)
dr = -0x8000;
#endif
*begin++ = dl;
*begin++ = dr;
}
// This write is expected to block.
//audioPI->write_audio(buf, BUF_LEN);
} /* end of the while(!audio_exit_thread) loop. */
//pthread_exit(NULL);
}
void I_SetSfxVolume(int volume)
{
}
// Gets lump nums of the named sound. Returns pointer which will be
// passed to I_StartSound() when you want to start an SFX. Must be
// sure to pass this to UngetSoundEffect() so that they can be
// freed!
int I_GetSfxLumpNum(sfxinfo_t *sound)
{
return W_GetNumForName(sound->lumpname);
}
// Id is unused.
// Data is a pointer to a Sample structure.
// Volume ranges from 0 to 127.
// Separation (orientation/stereo) ranges from 0 to 255. 128 is balanced.
// Pitch ranges from 0 to 255. Normal is 128.
// Priority looks to be unused (always 0).
int I_StartSound(int id, void *data, int vol, int sep, int pitch, int priority)
{
// Relative time order to find oldest sound.
static unsigned int soundTime = 0;
int chanId;
Sample *sample;
Channel *chan;
int oldest;
int i;
// Find an empty channel, the oldest playing channel, or default to 0.
// Currently ignoring priority.
chanId = 0;
oldest = soundTime;
for (i = 0; i < CHAN_COUNT; i++)
{
if (! channel[ i ].begin)
{
chanId = i;
break;
}
if (channel[ i ].time < oldest)
{
chanId = i;
oldest = channel[ i ].time;
}
}
sample = (Sample *) data;
chan = &channel[chanId];
I_UpdateSoundParams(chanId + 1, vol, sep, pitch);
// begin must be set last because the audio thread will access the channel
// once it is non-zero. Perhaps this should be protected by a mutex.
chan->pri = priority;
chan->time = soundTime;
chan->end = &sample->firstSample + LONG(sample->length);
chan->begin = &sample->firstSample;
soundTime++;
#if 0
printf ("I_StartSound %d: v:%d s:%d p:%d pri:%d | %d %d %d %d\n",
id, vol, sep, pitch, priority,
chanId, chan->pitch_step, SHORT(sample->a), SHORT(sample->freq));
#endif
return chanId + 1;
}
void I_StopSound(int handle)
{
handle--;
handle &= 7;
channel[handle].begin = NULL;
}
int I_SoundIsPlaying(int handle)
{
handle--;
handle &= 7;
return (channel[ handle ].begin != NULL);
}
void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
{
int lvol, rvol;
Channel *chan;
// Set left/right channel volume based on seperation.
sep += 1; // range 1 - 256
lvol = vol - ((vol * sep * sep) >> 16); // (256*256);
sep = sep - 257;
rvol = vol - ((vol * sep * sep) >> 16);
// Sanity check, clamp volume.
if (rvol < 0)
{
// printf ("rvol out of bounds %d, id %d\n", rvol, handle);
rvol = 0;
}
else if (rvol > 127)
{
// printf ("rvol out of bounds %d, id %d\n", rvol, handle);
rvol = 127;
}
if (lvol < 0)
{
// printf ("lvol out of bounds %d, id %d\n", lvol, handle);
lvol = 0;
}
else if (lvol > 127)
{
// printf ("lvol out of bounds %d, id %d\n", lvol, handle);
lvol = 127;
}
// Limit to MAX_VOL (64)
lvol >>= 1;
rvol >>= 1;
handle--;
handle &= 7;
chan = &channel[handle];
chan->pitch_step = steptable[pitch];
chan->step_remainder = 0;
chan->lvol_table = &vol_lookup[lvol * 256];
chan->rvol_table = &vol_lookup[rvol * 256];
}
/*
* SOUND STARTUP STUFF
*/
// inits all sound stuff
void I_StartupSound (void)
{
/*
int ok;
snd_SfxAvail = false;
if (M_CheckParm("--nosound") || M_CheckParm("-s") || M_CheckParm("-nosound"))
{
ST_Message("I_StartupSound: Sound Disabled.\n");
return;
}
// Using get_oplugin_info() from oss.c. In the future this could
// load from a real shared library plugin.
audioPI = get_oplugin_info();
if (!audioPI)
return;
audioPI->init();
audioPI->about();
ok = audioPI->open_audio(SAMPLE_FORMAT, SAMPLE_RATE, SAMPLE_CHANNELS);
if (ok)
{
audio_exit_thread = 0;
pthread_create(&audio_thread, NULL, audio_loop, NULL);
fprintf (stdout, "I_StartupSound: success\n");
snd_SfxAvail = true;
}
else
{
fprintf (stderr, "I_StartupSound: failed\n");
}
*/
}
// shuts down all sound stuff
void I_ShutdownSound (void)
{
snd_SfxAvail = false;
}
void I_SetChannels(int channels)
{
int v, j;
int *steptablemid;
// We always have CHAN_COUNT channels.
for (j = 0; j < CHAN_COUNT; j++)
{
channel[j].begin = NULL;
channel[j].end = NULL;
channel[j].time = 0;
}
// This table provides step widths for pitch parameters.
steptablemid = steptable + 128;
for (j = -128; j < 128; j++)
{
steptablemid[j] = (int) (pow(2.0, (j/64.0)) * 65536.0);
}
// Generate the volume lookup tables.
for (v = 0; v < MAX_VOL; v++)
{
for (j = 0; j < 256; j++)
{
// vol_lookup[v*256+j] = 128 + ((v * (j-128)) / (MAX_VOL-1));
// Turn the unsigned samples into signed samples.
#if 0 /* SAMPLE_FORMAT */
vol_lookup[v*256+j] = (v * (j-128)) / (MAX_VOL-1);
#else
vol_lookup[v*256+j] = (v * (j-128) * 256) / (MAX_VOL-1);
#endif
// printf ("vol_lookup[%d*256+%d] = %d\n", v, j, vol_lookup[v*256+j]);
}
}
}
/*
* SONG API
*/
int I_RegisterSong(void *data)
{
return 0;
}
int I_RegisterExternalSong(const char *nm)
{
return 0;
}
void I_UnRegisterSong(int handle)
{
}
void I_PauseSong(int handle)
{
}
void I_ResumeSong(int handle)
{
}
void I_SetMusicVolume(int volume)
{
}
int I_QrySongPlaying(int handle)
{
return 0;
}
// Stops a song. MUST be called before I_UnregisterSong().
void I_StopSong(int handle)
{
}
void I_PlaySong(int handle, boolean looping)
{
}