/*
* src/syssnd.c
*
* Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
*
* The use and distribution terms for this software are contained in the file
* named README, which can be found in the root of this distribution. By
* using this software in any fashion, you are agreeing to be bound by the
* terms of this license.
*
* You must not remove this notice, or any other, from this software.
*/
#include <SDL.h>
#include <stdlib.h>
#include <memory.h>
#include "system.h"
#include "game.h"
#include "syssnd.h"
#define ADJVOL(S) (((S) *sndVol) / SDL_MIX_MAXVOLUME)
static U8 isAudioActive = FALSE;
static channel_t channels[SYSSND_MIXCHANNELS];
static U8 sndVol = SDL_MIX_MAXVOLUME; /* internal volume */
static U8 sndUVol = SYSSND_MAXVOL; /* user-selected volume */
static U8 sndMute = FALSE; /* mute flag */
/*
* prototypes
*/
static void end_channel (channel_t *);
/*
* Callback -- this is also where all sound mixing is done
*
* Note: it may not be that much a good idea to do all the mixing here ; it
* may be more efficient to mix samples every frame, or maybe everytime a
* new sound is sent to be played. I don't know.
*/
void syssnd_callback (void *userdata, U8 *stream, int len)
{
channel_t *channel;
U8 channel_index;
S16 final_sample;
U32 i;
//
for (i = 0; i < (U32)len; i += 2)
{
final_sample = 0;
for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++)
{
channel = &channels[channel_index]; // quick access to channel
if (channel->loop == 0)
continue;
// channel is active
if (channel->len > 0)
{
// not ending
final_sample += ADJVOL (*((U16 *) channel->buf));
channel->buf += 2;
channel->len -= 2;
}
else
{
// ending
if (channel->loop > 0)
channel->loop--;
if (channel->loop)
{
// just loop
channel->buf = channel->snd->buf;
channel->len = channel->snd->len;
final_sample += ADJVOL (*((U16 *) channel->buf));
channel->buf += 2;
channel->len -= 2;
}
else
end_channel (channel); // end for real
}
}
*((S16 *) &stream[i]) = (sndMute ? 0 : final_sample);
}
}
static void end_channel (channel_t *channel)
{
channel->loop = 0;
if (channel->snd->dispose)
syssnd_free (channel->snd);
channel->snd = NULL;
}
void syssnd_init (void)
{
SDL_AudioSpec desired;
U16 c;
if (SDL_InitSubSystem (SDL_INIT_AUDIO) < 0)
return;
desired.freq = SYSSND_FREQ;
desired.format = AUDIO_S16;
desired.channels = SYSSND_CHANNELS;
desired.samples = SYSSND_MIXSAMPLES;
desired.callback = syssnd_callback;
desired.userdata = NULL;
if (SDL_OpenAudio (&desired, NULL) < 0)
return;
if (sysarg_args_vol != 0)
{
sndUVol = sysarg_args_vol;
sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
}
for (c = 0; c < SYSSND_MIXCHANNELS; c++)
channels[c].loop = 0; /* deactivate */
isAudioActive = TRUE;
SDL_PauseAudio (0);
}
/*
* Shutdown
*/
void syssnd_shutdown (void)
{
if (!isAudioActive)
return;
SDL_CloseAudio ();
isAudioActive = FALSE;
}
/*
* Toggle mute
*
* When muted, sounds are still managed but not sent to the dsp, hence
* it is possible to un-mute at any time.
*/
void syssnd_toggleMute (void)
{
sndMute = !sndMute;
}
void syssnd_vol (S8 d)
{
if ((d < 0 && sndUVol > 0) || (d > 0 && sndUVol < SYSSND_MAXVOL))
{
sndUVol += d;
sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
}
}
/*
* Play a sound
*
* loop: number of times the sound should be played, -1 to loop forever
* returns: channel number, or -1 if none was available
*
* NOTE if sound is already playing, simply reset it (i.e. can not have
* twice the same sound playing -- tends to become noisy when too many
* bad guys die at the same time).
*/
S8 syssnd_play (sound_t *sound, S8 loop)
{
S8 channel_index;
if ((sound == NULL) || !isAudioActive)
return (-1);
for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++)
if ((channels[channel_index].snd == sound) || (channels[channel_index].loop == 0))
break; // look for a free channel slot
if (channel_index == SYSSND_MIXCHANNELS)
return (-1);
channels[channel_index].loop = loop;
channels[channel_index].snd = sound;
channels[channel_index].buf = sound->buf;
channels[channel_index].len = sound->len;
return (channel_index);
}
/*
* Pause
*
* pause: TRUE or FALSE
* clear: TRUE to cleanup all sounds and make sure we start from scratch
*/
void syssnd_pause (U8 want_pause, U8 want_clear)
{
U8 channel_index;
if (!isAudioActive)
return;
if (want_clear)
for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++)
channels[channel_index].loop = 0;
SDL_PauseAudio (want_pause ? 1 : 0);
}
/*
* Stop a channel
*/
void syssnd_stopchan (S8 chan)
{
if (chan < 0 || chan > SYSSND_MIXCHANNELS)
return;
if (channels[chan].snd)
end_channel (&channels[chan]);
}
/*
* Stop a sound
*/
void syssnd_stopsound (sound_t *sound)
{
U8 i;
if (!sound)
return;
for (i = 0; i < SYSSND_MIXCHANNELS; i++)
if (channels[i].snd == sound)
end_channel (&channels[i]);
}
/*
* See if a sound is playing
*/
int syssnd_isplaying (sound_t *sound)
{
U8 i;
for (i = 0; i < SYSSND_MIXCHANNELS; i++)
if (channels[i].snd == sound)
return (1);
return (0);
}
/*
* Stops all channels.
*/
void syssnd_stopall (void)
{
U8 i;
for (i = 0; i < SYSSND_MIXCHANNELS; i++)
if (channels[i].snd)
end_channel (&channels[i]);
}
/*
* Load a sound.
*/
sound_t *syssnd_load (char *name)
{
char *soundfile_fullpathname;
sound_t *s;
SDL_AudioSpec audiospec;
/* alloc space for sound file's full pathname and build it */
soundfile_fullpathname
= (char *) malloc (1024);
sprintf_s (soundfile_fullpathname, 1024, "%s/%s", sys_getbasepath (), name);
/* alloc sound */
s
= malloc (sizeof (sound_t
));
/* read */
/* second param == 1 -> close source once read */
if (!SDL_LoadWAV_RW (SDL_RWFromFile (soundfile_fullpathname, "rb"), 1, &audiospec, &(s->buf), &(s->len)))
{
return NULL;
}
free (soundfile_fullpathname
);
s->dispose = FALSE;
return s;
}
/*
*
*/
void syssnd_free (sound_t *s)
{
if (!s)
return;
if (s->buf)
SDL_FreeWAV (s->buf);
s->buf = NULL;
s->len = 0;
}