/* 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 * */