summaryrefslogtreecommitdiff
path: root/src/libs/sound/sfx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/sound/sfx.c')
-rw-r--r--src/libs/sound/sfx.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/libs/sound/sfx.c b/src/libs/sound/sfx.c
new file mode 100644
index 0000000..3060434
--- /dev/null
+++ b/src/libs/sound/sfx.c
@@ -0,0 +1,306 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "options.h"
+#include "sound.h"
+#include "sndintrn.h"
+#include "libs/reslib.h"
+#include "libs/log.h"
+#include "libs/strlib.h"
+ // for GetStringAddress()
+#include "libs/strings/strintrn.h"
+ // for AllocStringTable(), FreeStringTable()
+#include "libs/memlib.h"
+#include <math.h>
+
+
+static void CheckFinishedChannels (void);
+
+static const SoundPosition notPositional = {FALSE, 0, 0};
+
+void
+PlayChannel (COUNT channel, SOUND snd, SoundPosition pos,
+ void *positional_object, unsigned char priority)
+{
+ SOUNDPTR snd_ptr = GetSoundAddress (snd);
+ TFB_SoundSample *sample;
+
+ StopSource (channel);
+ // all finished (stopped) channels can be cleaned up at this point
+ // since this is the only func that can initiate an sfx sound
+ CheckFinishedChannels ();
+
+ if (!snd_ptr)
+ return; // nothing to play
+
+ sample = *(TFB_SoundSample**) snd_ptr;
+
+ soundSource[channel].sample = sample;
+ soundSource[channel].positional_object = positional_object;
+
+ UpdateSoundPosition (channel, optStereoSFX ? pos : notPositional);
+
+ audio_Sourcei (soundSource[channel].handle, audio_BUFFER,
+ sample->buffer[0]);
+ audio_SourcePlay (soundSource[channel].handle);
+ (void) priority;
+}
+
+void
+StopChannel (COUNT channel, unsigned char Priority)
+{
+ StopSource (channel);
+ (void)Priority; // ignored
+}
+
+static void
+CheckFinishedChannels (void)
+{
+ int i;
+
+ for (i = FIRST_SFX_SOURCE; i <= LAST_SFX_SOURCE; ++i)
+ {
+ audio_IntVal state;
+
+ audio_GetSourcei (soundSource[i].handle, audio_SOURCE_STATE,
+ &state);
+ if (state == audio_STOPPED)
+ {
+ CleanSource (i);
+ // and if it failed... we still dont care
+ audio_GetError();
+ }
+ }
+}
+
+BOOLEAN
+ChannelPlaying (COUNT WhichChannel)
+{
+ audio_IntVal state;
+
+ audio_GetSourcei (soundSource[WhichChannel].handle,
+ audio_SOURCE_STATE, &state);
+ if (state == audio_PLAYING)
+ return TRUE;
+ return FALSE;
+}
+
+void *
+GetPositionalObject (COUNT channel)
+{
+ return soundSource[channel].positional_object;
+}
+
+void
+SetPositionalObject (COUNT channel, void *positional_object)
+{
+ soundSource[channel].positional_object = positional_object;
+}
+
+void
+UpdateSoundPosition (COUNT channel, SoundPosition pos)
+{
+ const float ATTENUATION = 160.0f;
+ const float MIN_DISTANCE = 0.5f;
+ float fpos[3];
+
+ if (pos.positional)
+ {
+ float dist;
+
+ fpos[0] = pos.x / ATTENUATION;
+ fpos[1] = 0.0f;
+ fpos[2] = pos.y / ATTENUATION;
+ dist = (float) sqrt (fpos[0] * fpos[0] + fpos[2] * fpos[2]);
+ if (dist < MIN_DISTANCE)
+ { // object is too close to listener
+ // move it away along the same vector
+ float scale = MIN_DISTANCE / dist;
+ fpos[0] *= scale;
+ fpos[2] *= scale;
+ }
+
+ audio_Sourcefv (soundSource[channel].handle, audio_POSITION, fpos);
+ //log_add (log_Debug, "UpdateSoundPosition(): channel %d, pos %d %d, posobj %x",
+ // channel, pos.x, pos.y, (unsigned int)soundSource[channel].positional_object);
+ }
+ else
+ {
+ fpos[0] = fpos[1] = 0.0f;
+ fpos[2] = -1.0f;
+ audio_Sourcefv (soundSource[channel].handle, audio_POSITION, fpos);
+ }
+}
+
+void
+SetChannelVolume (COUNT channel, COUNT volume, BYTE priority)
+ // I wonder what this whole priority business is...
+ // I can probably ignore it.
+{
+ audio_Sourcef (soundSource[channel].handle, audio_GAIN,
+ (volume / (float)MAX_VOLUME) * sfxVolumeScale);
+ (void)priority; // ignored
+}
+
+void *
+_GetSoundBankData (uio_Stream *fp, DWORD length)
+{
+ int snd_ct, n;
+ DWORD opos;
+ char CurrentLine[1024], filename[1024];
+#define MAX_FX 256
+ TFB_SoundSample *sndfx[MAX_FX];
+ STRING_TABLE Snd;
+ STRING str;
+ int i;
+
+ (void) length; // ignored
+ opos = uio_ftell (fp);
+
+ {
+ char *s1, *s2;
+
+ if (_cur_resfile_name == 0
+ || (((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
+ && (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
+ n = 0;
+ else
+ {
+ if (s2 > s1)
+ s1 = s2;
+ n = s1 - _cur_resfile_name + 1;
+ strncpy (filename, _cur_resfile_name, n);
+ }
+ }
+
+ snd_ct = 0;
+ while (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) &&
+ snd_ct < MAX_FX)
+ {
+ TFB_SoundSample* sample;
+ TFB_SoundDecoder* decoder;
+ uint32 decoded_bytes;
+
+ if (sscanf (CurrentLine, "%s", &filename[n]) != 1)
+ {
+ log_add (log_Warning, "_GetSoundBankData: bad line: '%s'",
+ CurrentLine);
+ continue;
+ }
+
+ log_add (log_Info, "_GetSoundBankData(): loading %s", filename);
+
+ decoder = SoundDecoder_Load (contentDir, filename, 4096, 0, 0);
+ if (!decoder)
+ {
+ log_add (log_Warning, "_GetSoundBankData(): couldn't load %s",
+ filename);
+ continue;
+ }
+
+ // SFX samples don't have decoders, everything is pre-decoded below
+ sample = TFB_CreateSoundSample (NULL, 1, NULL);
+
+ // Decode everything and stash it in 1 buffer
+ decoded_bytes = SoundDecoder_DecodeAll (decoder);
+ log_add (log_Info, "_GetSoundBankData(): decoded bytes %d",
+ decoded_bytes);
+
+ audio_BufferData (sample->buffer[0], decoder->format,
+ decoder->buffer, decoded_bytes, decoder->frequency);
+ // just for informational purposes
+ sample->length = decoder->length;
+
+ SoundDecoder_Free (decoder);
+
+ sndfx[snd_ct] = sample;
+ ++snd_ct;
+ }
+
+ if (!snd_ct)
+ return NULL; // no sounds decoded
+
+ Snd = AllocStringTable (snd_ct, 0);
+ if (!Snd)
+ { // Oops, have to delete everything now
+ while (snd_ct--)
+ TFB_DestroySoundSample (sndfx[snd_ct]);
+
+ return NULL;
+ }
+
+ // Populate the STRING_TABLE with ptrs to sample
+ for (i = 0, str = Snd->strings; i < snd_ct; ++i, ++str)
+ {
+ TFB_SoundSample **target = HMalloc (sizeof (sndfx[0]));
+ *target = sndfx[i];
+ str->data = (STRINGPTR)target;
+ str->length = sizeof (sndfx[0]);
+ }
+
+ return Snd;
+}
+
+BOOLEAN
+_ReleaseSoundBankData (void *Snd)
+{
+ STRING_TABLE fxTab = Snd;
+ int index;
+
+ if (!fxTab)
+ return FALSE;
+
+ for (index = 0; index < fxTab->size; ++index)
+ {
+ int i;
+ void **sptr = (void**)fxTab->strings[index].data;
+ TFB_SoundSample *sample = (TFB_SoundSample*)*sptr;
+
+ // Check all sources and see if we are currently playing this sample
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample == sample)
+ { // Playing this sample. Have to stop it.
+ StopSource (i);
+ soundSource[i].sample = NULL;
+ }
+ }
+
+ if (sample->decoder)
+ SoundDecoder_Free (sample->decoder);
+ sample->decoder = NULL;
+ TFB_DestroySoundSample (sample);
+ // sptr will be deleted by FreeStringTable() below
+ }
+
+ FreeStringTable (fxTab);
+
+ return TRUE;
+}
+
+BOOLEAN
+DestroySound(SOUND_REF target)
+{
+ return _ReleaseSoundBankData (target);
+}
+
+// The type conversions are implicit and will generate errors
+// or warnings if types change imcompatibly
+SOUNDPTR
+GetSoundAddress (SOUND sound)
+{
+ return GetStringAddress (sound);
+}