/* midasfx.c
*
* MIDAS sound effect library
*
* $Id: midasfx.c,v 1.4 1997/01/16 18:41:59 pekangas Exp $
*
* Copyright 1996,1997 Housemarque Inc.
*
* This file is part of the MIDAS Sound System, and may only be
* used, modified and distributed under the terms of the MIDAS
* Sound System license, LICENSE.TXT. By continuing to use,
* modify or distribute this file you indicate that you have
* read the license and understand and accept it fully.
*/
#include "lang.h"
#include "mtypes.h"
#include "errors.h"
#include "mmem.h"
#include "sdevice.h"
#include "midasfx.h"
#include "file.h"
/* Magic constant that is used to identify playing handles that refer to
sounds on automatic effect channels: */
#define AUTOMAGIC 0x100
/* Maximum playing handle: */
#define MAXHANDLE 0xFFFE
/* Magic channel number - not found: */
#define NOCHANNEL 0xFFFF
/****************************************************************************\
* enum fxFunctIDs
* ---------------
* Description: Function IDs for sound effect library functions
\****************************************************************************/
enum fxFunctIDs
{
ID_fxInit = ID_fx,
ID_fxClose,
ID_fxLoadRawSample,
ID_fxFreeSample,
ID_fxSetAutoChannels,
ID_fxPlaySample,
ID_fxStopSample,
ID_fxSetSampleRate,
ID_fxSetSampleVolume,
ID_fxSetSamplePanning,
ID_fxSetSamplePriority
};
static unsigned numAutoChannels; /* Number of auto FX channels */
static fxChannel *autoChannels; /* Automatic effect channels */
static unsigned nextHandle; /* Next sample playing handle */
static SoundDevice *SD; /* Sound Device for the effects */
static unsigned nextChannel; /* Next automatic channel */
/****************************************************************************\
*
* Function: int fxInit(void)
*
* Description: Initializes the sound effect library
*
* Input: SoundDevice *SD Pointer to the Sound Device that will
* be used for playing the effects
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxInit(SoundDevice *_SD)
{
/* Remember the Sound Device: */
SD = _SD;
/* We don't have any automatic channels: */
numAutoChannels = 0;
autoChannels = NULL;
nextHandle = AUTOMAGIC;
nextChannel = 0;
return OK;
}
/****************************************************************************\
*
* Function: int fxClose(void)
*
* Description: Uninitializes the sound effect library
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxClose(void)
{
int error;
unsigned i;
unsigned chan;
/* Check if we have automatic effect channels. If yes, clear the sounds
on them and free the channel structures: */
if ( numAutoChannels != 0 )
{
for ( i = 0; i < numAutoChannels; i++ )
{
chan = autoChannels[i].sdChannel;
if ( (error = SD->StopSound(chan)) != OK )
PASSERROR(ID_fxClose);
}
if ( (error = memFree(autoChannels)) != OK )
PASSERROR(ID_fxClose);
}
return OK;
}
/****************************************************************************\
*
* Function: int fxLoadRawSample(char *fileName, unsigned sampleType,
* int loopSample, unsigned *sampleHandle)
*
* Description: Loads a raw sample file into memory and adds the sample to
* the sound device.
*
* Input: char *fileName sample file name
* unsigned sampleType sample type, see enum sdSampleType
* int loopSample 1 if sample is looped, 0 if not
* unsigned *sampleHandle pointer to sample handle variable
*
* Returns: MIDAS error code. The sample handle for the sample will be
* written to *sampleHandle.
*
\****************************************************************************/
int CALLING fxLoadRawSample(char *fileName, unsigned sampleType,
int loopSample, unsigned *sampleHandle)
{
int error;
static char *buffer;
static fileHandle f;
static long len;
static sdSample smp;
/* Open the sound effect file: */
if ( (error = fileOpen(fileName, fileOpenRead, &f)) != OK )
PASSERROR(ID_fxLoadRawSample);
/* Get file size: */
if ( (error = fileGetSize(f, &len)) != OK )
{
fileClose(f);
PASSERROR(ID_fxLoadRawSample);
}
/* Allocate sample loading buffer: */
if ( (error = memAlloc(len, (void**) &buffer)) != OK )
{
fileClose(f);
PASSERROR(ID_fxLoadRawSample);
}
/* Read the sample: */
if ( (error = fileRead(f, buffer, len)) != OK )
{
fileClose(f);
memFree(buffer);
PASSERROR(ID_fxLoadRawSample);
}
/* Close the sample file: */
if ( (error = fileClose(f)) != OK )
{
memFree(buffer);
PASSERROR(ID_fxLoadRawSample);
}
/* Build Sound Device sample structure for the sample: */
smp.sample = buffer;
smp.samplePos = sdSmpConv;
smp.sampleType = sampleType;
smp.sampleLength = len;
if ( loopSample )
{
/* Loop the whole sample: */
smp.loopMode = sdLoop1;
smp.loop1Start = 0;
smp.loop1End = len;
smp.loop1Type = loopUnidir;
}
else
{
/* No loop: */
smp.loopMode = sdLoopNone;
smp.loop1Start = smp.loop1End = 0;
smp.loop1Type = loopNone;
}
/* No loop 2: */
smp.loop2Start = smp.loop2End = 0;
smp.loop2Type = loopNone;
/* Add the sample to the Sound Device: */
if ( (error = SD->AddSample(&smp, 1, sampleHandle)) != OK )
{
memFree(buffer);
PASSERROR(ID_fxLoadRawSample);
}
/* Deallocate the buffer: */
if ( (error = memFree(buffer)) != OK )
PASSERROR(ID_fxLoadRawSample);
return OK;
}
/****************************************************************************\
*
* Function: int fxFreeSample(unsigned sample)
*
* Description: Deallocates a sample and frees it from the Sound Device.
*
* Input: unsigned sample sample handle for the sample to be
* deallocated.
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxFreeSample(unsigned sample)
{
int error;
/* Just free the sample from the Sound Device: */
if ( (error = SD->RemoveSample(sample)) != OK )
PASSERROR(ID_fxFreeSample);
return OK;
}
/****************************************************************************\
*
* Function: int fxSetAutoChannels(int numChannels,
* unsigned *channelNumbers)
*
* Description: Sets the channel numbers that can be used as automatic effect
* channels.
*
* Input: int numChannels number of channels that can be
* used
* unsigned *channelNumbers pointer to a table that contains
* the channels that can be used
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxSetAutoChannels(int numChannels, unsigned *channelNumbers)
{
int error;
unsigned i;
unsigned chan;
fxChannel *fxchan;
/* Check if there are previous automatic effect channels. If yes, clear
them and free the channel structures: */
if ( numAutoChannels != 0 )
{
for ( i = 0; i < numAutoChannels; i++ )
{
chan = autoChannels[i].sdChannel;
if ( (error = SD->StopSound(chan)) != OK )
PASSERROR(ID_fxClose);
}
if ( (error = memFree(autoChannels)) != OK )
PASSERROR(ID_fxClose);
}
if ( numChannels )
{
/* Allocate memory for channel structures: */
numAutoChannels = numChannels;
if ( (error = memAlloc(numChannels * sizeof(fxChannel),
(void**) &autoChannels)) != OK )
PASSERROR(ID_fxSetAutoChannels);
/* Initialize channels: */
for ( i = 0; i < (unsigned) numChannels; i++ )
{
fxchan = &autoChannels[i];
fxchan->sdChannel = channelNumbers[i];
fxchan->sampleHandle = 0;
fxchan->playHandle = 0;
fxchan->priority = 0;
}
}
else
{
/* There are no automatic effect channels: */
numAutoChannels = 0;
autoChannels = NULL;
}
nextHandle = AUTOMAGIC;
nextChannel = 0;
return OK;
}
/****************************************************************************\
*
* Function: int fxPlaySample(unsigned channel, unsigned sample,
* int priority, unsigned rate, unsigned volume, int panning,
* unsigned *playHandle)
*
* Description: Starts playing a sound effect sample on a channel
*
* Input: unsigned channel channel number, or fxAutoChannel for
* automatic selection
* unsigned sample sample handle
* int priority effect priority, the higher the value
* the higher the priority
* unsigned rate effect initial sample rate
* unsigned volume effect initial volume (0-64)
* int panning effect initial panning, see enum
* sdPanning
* unsigned *playHandle effect playing handle variable
*
* Returns: MIDAS error code. The playing handle for the effect will be
* written to *playHandle.
*
\****************************************************************************/
int CALLING fxPlaySample(unsigned channel, unsigned sample, int priority,
unsigned rate, unsigned volume, int panning, unsigned *playHandle)
{
int error;
unsigned chan;
unsigned handle;
if ( channel == fxAutoChannel )
{
/* We should select the channel automatically */
/* Check that we do have automatic channels: */
if ( numAutoChannels == 0 )
{
ERROR(errNoChannels, ID_fxPlaySample);
return errNoChannels;
}
handle = nextHandle;
/* We'll just use the next automatic channel: */
autoChannels[nextChannel].sampleHandle = sample;
autoChannels[nextChannel].priority = priority;
autoChannels[nextChannel].playHandle = handle;
chan = autoChannels[nextChannel].sdChannel;
/* Go to the next channel: */
nextChannel++;
if ( nextChannel >= numAutoChannels )
nextChannel = 0;
/* Prepare next possible play handle: */
nextHandle++;
if ( (nextHandle < AUTOMAGIC) || (nextHandle > MAXHANDLE) )
nextHandle = AUTOMAGIC;
}
else
{
/* Just use the channel given: */
chan = channel;
handle = chan;
}
/* The handle returned will be >= AUTOMAGIC if it refers to a sound on an
automagic channel, otherwise it is simply the SD channel number */
/* Stop any previous sound on the channel: */
if ( (error = SD->StopSound(chan)) != OK )
PASSERROR(ID_fxPlaySample);
/* Set the sample to the channel: */
if ( (error = SD->SetSample(chan, sample)) != OK )
PASSERROR(ID_fxPlaySample);
/* Set the new volume to the channel: */
if ( (error = SD->SetVolume(chan, volume)) != OK )
PASSERROR(ID_fxPlaySample);
/* Set the new panning position: */
if ( (error = SD->SetPanning(chan, panning)) != OK )
PASSERROR(ID_fxPlaySample);
/* Play the sound: */
if ( (error = SD->PlaySound(chan, rate)) != OK )
PASSERROR(ID_fxPlaySample);
*playHandle = handle;
return OK;
}
/****************************************************************************\
*
* Function: unsigned FindChannel(unsigned playHandle)
*
* Description: Finds the channel where a sound is being played
*
* Input: unsigned playHandle playing handle for the sound
*
* Returns: Sound Device channel number for the sound, or NOCHANNEL if it
* is not being played.
*
\****************************************************************************/
static unsigned FindChannel(unsigned playHandle)
{
unsigned i;
if ( playHandle < AUTOMAGIC )
{
/* Just a simple channel number: */
return playHandle;
}
/* Search through the channels and try to find the sound: */
for ( i = 0; i < numAutoChannels; i++ )
{
if ( autoChannels[i].playHandle == playHandle )
return autoChannels[i].sdChannel;
}
/* We couldn't find it: */
return NOCHANNEL;
}
/****************************************************************************\
*
* Function: int fxStopSample(unsigned playHandle)
*
* Description: Stops playing a sample
*
* Input: unsigned playHandle sample playing handle (from
* fxPlaySample)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxStopSample(unsigned playHandle)
{
unsigned chan;
int error;
/* Find the channel number for the sound, exit if it's not being played:*/
if ( (chan = FindChannel(playHandle)) == NOCHANNEL )
return OK;
/* Stop the sound: */
if ( (error = SD->StopSound(chan)) != OK )
PASSERROR(ID_fxStopSample);
return OK;
}
/****************************************************************************\
*
* Function: int fxSetSampleRate(unsigned playHandle, ulong rate)
*
* Description: Changes the sample rate for a sample that is being played
*
* Input: unsigned playHandle sample playing handle (from
* fxPlaySample)
* ulong rate new rate
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxSetSampleRate(unsigned playHandle, ulong rate)
{
unsigned chan;
int error;
/* Find the channel number for the sound, exit if it's not being played:*/
if ( (chan = FindChannel(playHandle)) == NOCHANNEL )
return OK;
/* Set the new sample rate: */
if ( (error = SD->SetRate(chan, rate)) != OK )
PASSERROR(ID_fxSetSampleRate);
return OK;
}
/****************************************************************************\
*
* Function: int fxSetSampleVolume(unsigned playHandle, unsigned volume)
*
* Description: Changes the volume for a sample that is being played
*
* Input: unsigned playHandle sample playing handle (from
* fxPlaySample)
* unsigned volume new volume
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxSetSampleVolume(unsigned playHandle, unsigned volume)
{
unsigned chan;
int error;
/* Find the channel number for the sound, exit if it's not being played:*/
if ( (chan = FindChannel(playHandle)) == NOCHANNEL )
return OK;
/* Set the new sample rate: */
if ( (error = SD->SetVolume(chan, volume)) != OK )
PASSERROR(ID_fxSetSampleVolume);
return OK;
}
/****************************************************************************\
*
* Function: int fxSetSamplePanning(unsigned playHandle, int panning)
*
* Description: Changes the panning position for a sample that is being played
*
* Input: unsigned playHandle sample playing handle (from
* fxPlaySample)
* int panning new panning position
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxSetSamplePanning(unsigned playHandle, int panning)
{
unsigned chan;
int error;
/* Find the channel number for the sound, exit if it's not being played:*/
if ( (chan = FindChannel(playHandle)) == NOCHANNEL )
return OK;
/* Set the new sample rate: */
if ( (error = SD->SetPanning(chan, panning)) != OK )
PASSERROR(ID_fxSetSamplePanning);
return OK;
}
/****************************************************************************\
*
* Function: int fxSetSamplePriority(unsigned playHandle, int priority)
*
* Description: Changes the priority for a sample that is being played
*
* Input: unsigned playHandle sample playing handle (from
* fxPlaySample)
* int priority new playing priority
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING fxSetSamplePriority(unsigned playHandle, int priority)
{
unsigned i;
/* Check that the handle is for an automatic channel - priorities don't
make sense otherwise: */
if ( playHandle < AUTOMAGIC )
return OK;
/* Try to find the channel the sound is being played on, and set its
priority if found: */
for ( i = 0; i < numAutoChannels; i++ )
{
if ( autoChannels[i].playHandle == playHandle )
autoChannels[i].priority = priority;
}
return OK;
}
/*
* $Log: midasfx.c,v $
* Revision 1.4 1997/01/16 18:41:59 pekangas
* Changed copyright messages to Housemarque
*
* Revision 1.3 1996/09/28 08:12:40 jpaana
* Fixed for Linux
*
* Revision 1.2 1996/09/25 18:36:41 pekangas
* Fixed to compile in DOS without warnings
*
* Revision 1.1 1996/09/22 23:17:48 pekangas
* Initial revision
*
*/