mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-23 12:08:56 +00:00
dc138e4912
This definition was used to control the accessibility of some class members, changing protected qualifiers to public. This introduced unnecessary coupling between components and headers. All conditional access specifiers have been set to public, which should not be a problem given the low number of classes that actually used affected members. Another albeit more complex solution could have been to use friend classes.
842 lines
24 KiB
C++
842 lines
24 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include "cfile.h"
|
|
#include "pserror.h"
|
|
#include "mono.h"
|
|
#include "soundload.h"
|
|
#include "ssl_lib.h"
|
|
#include "mem.h"
|
|
#include "application.h"
|
|
#include "linux/lnxsound.h"
|
|
#include "ddlnxsound.h"
|
|
#include "mixer.h"
|
|
#include "ddio.h"
|
|
#include "SDL.h"
|
|
#include "SDL_thread.h"
|
|
#include "args.h"
|
|
|
|
#define SOUNDLIB_SAMPLE_RATE 22050
|
|
#define SOUNDLIB_SAMPLE_SIZE 16
|
|
#define SOUNDLIB_CHANNELS 2
|
|
#define SOUNDLIB_DEFAULT_SAMPLES 1024
|
|
|
|
// ===============================
|
|
#define MAX_SOUNDS_PLAYING_AT_ONCE 256
|
|
static sound_buffer_info sound_cache[MAX_SOUNDS_PLAYING_AT_ONCE];
|
|
static int sound_buffer_size = MAX_SOUNDS_PLAYING_AT_ONCE;
|
|
|
|
LNXSTREAM m_sb_info;
|
|
lnxsound *ll_sound_ptr;
|
|
|
|
// A peroidic mixer that uses the primary buffer as a stream buffer
|
|
static void StreamAudio(void *user_ptr, Uint8 *stream, int len);
|
|
|
|
lnxsound::lnxsound() : llsSystem() {
|
|
ll_sound_ptr = this;
|
|
sound_device = -1;
|
|
in_at_exit = false;
|
|
}
|
|
|
|
lnxsound::~lnxsound() {
|
|
in_at_exit = true;
|
|
DestroySoundLib();
|
|
SetSoundCard(NULL);
|
|
}
|
|
|
|
// Starts the sound library, maybe have it send back some information -- 3d support?
|
|
int lnxsound::InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played) {
|
|
SDL_AudioSpec spec;
|
|
|
|
// setup mixer
|
|
tMixerInit mi;
|
|
mi.primary_buffer = NULL;
|
|
mi.primary_frequency = SOUNDLIB_SAMPLE_RATE;
|
|
mi.max_sounds_available = &sound_buffer_size;
|
|
mi.sound_cache = sound_cache;
|
|
mi.primary_alignment = SOUNDLIB_CHANNELS * (SOUNDLIB_SAMPLE_SIZE >> 3);
|
|
mi.fp_SetError = lnxsound_SetError;
|
|
mi.fp_ErrorText = lnxsound_ErrorText;
|
|
mi.p_error_code = &m_lib_error_code;
|
|
mi.ll_sound_ptr = ll_sound_ptr;
|
|
|
|
if (!m_mixer.Initialize(&mi)) {
|
|
return false;
|
|
} // if
|
|
|
|
int sampleCount = SOUNDLIB_DEFAULT_SAMPLES;
|
|
int sampleArgIndex = FindArg("-sdlSndSize");
|
|
if (sampleArgIndex != 0) {
|
|
const char *sampleCountStr = GetArg(sampleArgIndex + 1);
|
|
if (sampleCountStr) {
|
|
sampleCount = atoi(sampleCountStr);
|
|
if (sampleCount <= 0) {
|
|
sampleCount = SOUNDLIB_DEFAULT_SAMPLES;
|
|
}
|
|
}
|
|
}
|
|
|
|
spec.freq = SOUNDLIB_SAMPLE_RATE;
|
|
spec.format = SOUNDLIB_SAMPLE_SIZE == 8 ? AUDIO_U8 : AUDIO_S16SYS;
|
|
spec.channels = SOUNDLIB_CHANNELS;
|
|
spec.samples = sampleCount;
|
|
spec.callback = StreamAudio;
|
|
spec.userdata = &m_mixer;
|
|
|
|
if (SDL_OpenAudio(&spec, NULL) < 0) {
|
|
return false;
|
|
}
|
|
sound_device = 1;
|
|
|
|
mprintf((0, "Sound: Hardware configured. Kicking off stream thread..."));
|
|
SDL_PauseAudio(0);
|
|
|
|
m_total_sounds_played = 0;
|
|
m_cur_sounds_played = 0;
|
|
m_in_sound_frame = false;
|
|
m_pending_actions = false;
|
|
m_cache_stress_timer = 0.0f;
|
|
m_timer_last_frametime = -1;
|
|
m_sound_quality = SQT_HIGH;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool lnxsound::GetDeviceSettings(int *device, unsigned int *freq, unsigned int *bit_depth, unsigned int *channels) {
|
|
if (sound_device == -1)
|
|
return false;
|
|
|
|
*device = sound_device;
|
|
*freq = SOUNDLIB_SAMPLE_RATE;
|
|
*bit_depth = SOUNDLIB_SAMPLE_SIZE;
|
|
*channels = SOUNDLIB_CHANNELS;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Cleans up after the Sound Library
|
|
void lnxsound::DestroySoundLib(void) {
|
|
SDL_CloseAudio();
|
|
sound_device = -1;
|
|
}
|
|
|
|
// Locks and unlocks sounds (used when changing play_info data)
|
|
bool lnxsound::LockSound(int sound_uid) { return false; }
|
|
|
|
bool lnxsound::UnlockSound(int sound_uid) { return false; }
|
|
|
|
bool lnxsound::SetSoundQuality(char quality) {
|
|
int i;
|
|
|
|
if (quality == m_sound_quality)
|
|
return true;
|
|
|
|
// pause any sounds that may be playing
|
|
PauseSounds();
|
|
|
|
if (quality == SQT_NORMAL) {
|
|
m_sound_quality = SQT_NORMAL;
|
|
} else {
|
|
m_sound_quality = SQT_HIGH;
|
|
}
|
|
|
|
for (i = 0; i < MAX_SOUNDS; i++) {
|
|
if (Sounds[i].used != 0) {
|
|
int j = Sounds[i].sample_index;
|
|
|
|
if (SoundFiles[j].sample_8bit && m_sound_quality == SQT_HIGH) {
|
|
GlobalFree(SoundFiles[j].sample_8bit);
|
|
SoundFiles[j].sample_8bit = NULL;
|
|
|
|
CheckAndForceSoundDataAlloc(i);
|
|
}
|
|
if (SoundFiles[j].sample_16bit && m_sound_quality == SQT_NORMAL) {
|
|
int count;
|
|
|
|
ASSERT(SoundFiles[j].sample_8bit == NULL);
|
|
SoundFiles[j].sample_8bit = (unsigned char *)GlobalAlloc(0, SoundFiles[j].sample_length);
|
|
|
|
// NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned
|
|
// (+128 biase).
|
|
for (count = 0; count < (int)SoundFiles[j].sample_length; count++) {
|
|
SoundFiles[j].sample_8bit[count] = (unsigned char)((((int)SoundFiles[j].sample_16bit[count]) + 32767) >> 8);
|
|
}
|
|
|
|
GlobalFree(SoundFiles[j].sample_16bit);
|
|
SoundFiles[j].sample_16bit = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
ResumeSounds();
|
|
|
|
return true;
|
|
}
|
|
|
|
char lnxsound::GetSoundQuality(void) { return m_sound_quality; }
|
|
|
|
bool lnxsound::SetSoundMixer(char mixer_type) { return true; }
|
|
|
|
char lnxsound::GetSoundMixer(void) { return SOUND_MIXER_SOFTWARE_16; }
|
|
|
|
// Determines if a sound will play. Takes into account maximum allowable
|
|
// sounds.
|
|
// Also put prioritization code in here
|
|
// ignore reserved slots
|
|
#ifdef _DEBUG
|
|
short lnxsound::FindFreeSoundSlot(int sound_index, float volume, int priority)
|
|
#else
|
|
short lnxsound::FindFreeSoundSlot(float volume, int priority)
|
|
#endif
|
|
{
|
|
int current_slot;
|
|
sound_buffer_info *sb;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
sb = &sound_cache[current_slot];
|
|
if (sb->m_status == SSF_UNUSED) {
|
|
return current_slot;
|
|
}
|
|
}
|
|
|
|
// no more slots? take priority into account.
|
|
// throw out lowest priority sound slot (must be lower than or equal to new sound priority)
|
|
float weighted_priority = (priority * 2.0f) * volume;
|
|
if (current_slot == MAX_SOUNDS_PLAYING_AT_ONCE) {
|
|
int throw_out_slot = -1, equiv_priority_slot = -1;
|
|
float weighted_priorityA, weighted_priorityB;
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
sb = &sound_cache[current_slot];
|
|
if (!(sb->m_status & (SSF_PLAY_LOOPING + SSF_PLAY_STREAMING))) {
|
|
weighted_priorityA = sb->play_info->priority * 2.0f * sb->m_volume;
|
|
if (weighted_priorityA < weighted_priority) {
|
|
if (throw_out_slot == -1) {
|
|
throw_out_slot = current_slot;
|
|
} else {
|
|
play_information *play_info2 = sound_cache[throw_out_slot].play_info;
|
|
weighted_priorityB = play_info2->priority * 2.0f * sb->m_volume;
|
|
if (weighted_priorityB > weighted_priorityA) {
|
|
throw_out_slot = current_slot;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (equiv_priority_slot == -1 && weighted_priorityA == weighted_priority) {
|
|
equiv_priority_slot = current_slot;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if no slot found to stop, look for a slot with priority == new priority
|
|
if (throw_out_slot == -1) {
|
|
throw_out_slot = equiv_priority_slot;
|
|
}
|
|
if (throw_out_slot > -1) {
|
|
sb = &sound_cache[throw_out_slot];
|
|
StopSound(sb->m_unique_id, SKT_HOLD_UNTIL_STOP);
|
|
mprintf((0, "DDSNDLIB: Replace sound (p:%d) with sound (p:%d) in slot %d\n", sb->play_info->priority, priority,
|
|
throw_out_slot));
|
|
return throw_out_slot;
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if (sound_index > -1) {
|
|
mprintf((0, "DDSNDLIB: Sound %s with priority (%d) too low.\n", Sounds[sound_index].name, priority));
|
|
} else {
|
|
mprintf((0, "DDSNDLIB: Sound unknown with priority (%d) too low.\n", priority));
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
// Plays a 2d sound
|
|
int lnxsound::PlaySound2d(play_information *play_info, int sound_index, float f_volume, float f_pan, bool f_looped) {
|
|
sound_buffer_info *sb = NULL;
|
|
short sound_slot;
|
|
|
|
if (sound_device == -1) {
|
|
return -1;
|
|
}
|
|
|
|
// calculate volume and pan
|
|
f_volume = (f_volume < 0.0f) ? 0.0f : (f_volume > 1.0f) ? 1.0f : f_volume;
|
|
play_info->left_volume = play_info->right_volume = f_volume;
|
|
|
|
f_pan = (f_pan < -1.0f) ? -1.0f : (f_pan > 1.0f) ? 1.0f : f_pan;
|
|
if (f_pan < 0.0) {
|
|
play_info->right_volume += f_volume * f_pan;
|
|
} else {
|
|
play_info->left_volume -= f_volume * f_pan;
|
|
}
|
|
|
|
// do common processing.
|
|
if (SoundFiles[Sounds[sound_index].sample_index].used == 0) {
|
|
mprintf((0, "Tryed to play %d sound, it DNE.\n", sound_index));
|
|
return -1;
|
|
}
|
|
#ifdef _DEBUG
|
|
sound_slot = FindFreeSoundSlot(sound_index, f_volume, play_info->priority);
|
|
#else
|
|
sound_slot = FindFreeSoundSlot(f_volume, play_info->priority);
|
|
#endif
|
|
if (sound_slot < 0) {
|
|
// do prioritization code here.
|
|
return -1;
|
|
}
|
|
sb = &sound_cache[sound_slot];
|
|
m_total_sounds_played++;
|
|
sb->play_info = play_info;
|
|
sb->m_unique_id = MakeUniqueId(sound_slot);
|
|
sb->m_buffer_type = SBT_2D;
|
|
sb->m_sound_index = sound_index;
|
|
sb->m_status = SSF_UNUSED;
|
|
|
|
ASSERT(sb->m_unique_id != -1);
|
|
|
|
// play 2d sound
|
|
sb->m_status = (f_looped) ? SSF_PLAY_LOOPING : SSF_PLAY_NORMAL;
|
|
return sb->m_unique_id;
|
|
}
|
|
|
|
// This function limits the number of sounds cached to 255(8bits) and 256 bit is for invalid channel
|
|
// The purpose is to create unique signatures for each sound played (and allow for
|
|
// the slot_number to be quickly determined)
|
|
inline int lnxsound::MakeUniqueId(int sound_slot) { return ((((int)m_total_sounds_played) << 8) + sound_slot); }
|
|
|
|
inline int lnxsound::ValidateUniqueId(int sound_uid) {
|
|
if (sound_uid == sound_cache[sound_uid & 0x00FF].m_unique_id) {
|
|
return sound_uid & 0x00FF;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int lnxsound::PlayStream(play_information *play_info) {
|
|
short sound_slot;
|
|
int ds_flags = 0;
|
|
|
|
ASSERT(play_info != NULL);
|
|
|
|
float volume = (play_info->left_volume > play_info->right_volume) ? play_info->left_volume : play_info->right_volume;
|
|
|
|
if (sound_device == -1)
|
|
return -1;
|
|
|
|
#ifdef _DEBUG
|
|
sound_slot = FindFreeSoundSlot(-1, volume, play_info->priority);
|
|
#else
|
|
sound_slot = FindFreeSoundSlot(volume, play_info->priority);
|
|
#endif
|
|
// Out of sound slots
|
|
if (sound_slot < 0) {
|
|
return -1;
|
|
}
|
|
|
|
m_total_sounds_played++;
|
|
sound_cache[sound_slot].play_info = play_info;
|
|
|
|
sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot);
|
|
ASSERT(sound_cache[sound_slot].m_unique_id != -1);
|
|
|
|
sound_cache[sound_slot].m_buffer_type = SBT_2D;
|
|
sound_cache[sound_slot].m_status = SSF_PLAY_STREAMING;
|
|
|
|
m_cur_sounds_played++;
|
|
|
|
return (sound_cache[sound_slot].m_unique_id);
|
|
}
|
|
|
|
void lnxsound::SetListener(pos_state *cur_pos) {
|
|
if (sound_device == -1)
|
|
return;
|
|
|
|
m_emulated_listener.orient = *cur_pos->orient;
|
|
m_emulated_listener.position = *cur_pos->position;
|
|
m_emulated_listener.velocity = *cur_pos->velocity;
|
|
}
|
|
|
|
int lnxsound::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float adjusted_volume,
|
|
bool f_looped, float reverb) //, unsigned short frequency
|
|
{
|
|
short sound_slot;
|
|
int ds_flags = 0;
|
|
float volume;
|
|
|
|
volume = adjusted_volume; // Adjust base volume by sent volume, let 3d stuff do the rest
|
|
|
|
if (sound_device == -1)
|
|
return -1;
|
|
|
|
ASSERT(Sounds[sound_index].used != 0);
|
|
if (Sounds[sound_index].used == 0)
|
|
return -1;
|
|
|
|
float dist;
|
|
vector dir_to_sound = *cur_pos->position - m_emulated_listener.position;
|
|
float pan;
|
|
|
|
dist = vm_NormalizeVector(&dir_to_sound);
|
|
if (dist < .1f) {
|
|
dir_to_sound = m_emulated_listener.orient.fvec;
|
|
}
|
|
|
|
if (dist >= Sounds[sound_index].max_distance) {
|
|
return -1;
|
|
} else if (dist > Sounds[sound_index].min_distance) {
|
|
volume *= (1.0 - ((dist - Sounds[sound_index].min_distance) /
|
|
(Sounds[sound_index].max_distance - Sounds[sound_index].min_distance)));
|
|
}
|
|
|
|
pan = (dir_to_sound * m_emulated_listener.orient.rvec);
|
|
|
|
if (volume < 0.0f)
|
|
volume = 0.0f;
|
|
else if (volume > 1.0f)
|
|
volume = 1.0f;
|
|
|
|
if (pan < -1.0f)
|
|
pan = -1.0f;
|
|
else if (pan > 1.0f)
|
|
pan = 1.0f;
|
|
|
|
return PlaySound2d(play_info, sound_index, volume, pan, f_looped);
|
|
}
|
|
|
|
void lnxsound::AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) {
|
|
int current_slot;
|
|
|
|
if (sound_device == -1)
|
|
return;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
if (sound_cache[current_slot].m_status == SSF_UNUSED)
|
|
return;
|
|
|
|
sound_buffer_info *sb = &sound_cache[current_slot];
|
|
play_information *play_info = sb->play_info;
|
|
|
|
play_info->left_volume = play_info->right_volume = f_volume;
|
|
if (f_pan < 0.0)
|
|
play_info->right_volume += f_volume * f_pan;
|
|
else
|
|
play_info->left_volume -= f_volume * f_pan;
|
|
}
|
|
|
|
void lnxsound::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) {
|
|
if (sound_device == -1)
|
|
return;
|
|
|
|
int current_slot;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
if (sound_cache[current_slot].m_status == SSF_UNUSED)
|
|
return;
|
|
|
|
// We need to determine the pan and volume
|
|
float volume;
|
|
|
|
volume = adjusted_volume;
|
|
|
|
float dist;
|
|
vector dir_to_sound = *cur_pos->position - m_emulated_listener.position;
|
|
float pan;
|
|
|
|
dist = vm_NormalizeVector(&dir_to_sound);
|
|
if (dist < .1f) {
|
|
dir_to_sound = m_emulated_listener.orient.fvec;
|
|
}
|
|
|
|
if (dist >= Sounds[sound_cache[current_slot].m_sound_index].max_distance) {
|
|
volume = 0.0f;
|
|
} else if (dist > Sounds[sound_cache[current_slot].m_sound_index].min_distance) {
|
|
volume *= (1.0 - ((dist - Sounds[sound_cache[current_slot].m_sound_index].min_distance) /
|
|
(Sounds[sound_cache[current_slot].m_sound_index].max_distance -
|
|
Sounds[sound_cache[current_slot].m_sound_index].min_distance)));
|
|
}
|
|
|
|
pan = (dir_to_sound * m_emulated_listener.orient.rvec);
|
|
|
|
if (volume < 0.0f)
|
|
volume = 0.0f;
|
|
else if (volume > 1.0f)
|
|
volume = 1.0f;
|
|
|
|
if (pan < -1.0f)
|
|
pan = -1.0f;
|
|
else if (pan > 1.0f)
|
|
pan = 1.0f;
|
|
|
|
AdjustSound(sound_cache[current_slot].m_unique_id, volume, pan, 22050);
|
|
}
|
|
|
|
void lnxsound::StopAllSounds(void) {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
if (sound_cache[current_slot].m_status != SSF_UNUSED) {
|
|
StopSound(sound_cache[current_slot].m_unique_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checks if a sound is playing (removes finished sound);
|
|
bool lnxsound::IsSoundInstancePlaying(int sound_uid) {
|
|
int current_slot;
|
|
|
|
if (sound_device == -1)
|
|
return false;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return false;
|
|
|
|
if (sound_cache[current_slot].m_status != SSF_UNUSED) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int lnxsound::IsSoundPlaying(int sound_index) {
|
|
int current_slot;
|
|
|
|
if (sound_device == -1)
|
|
return -1;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
if ((sound_cache[current_slot].m_status != SSF_UNUSED) &&
|
|
(sound_cache[current_slot].m_sound_index == sound_index)) {
|
|
return sound_cache[current_slot].m_unique_id;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Stops 2d and 3d sounds
|
|
void lnxsound::StopSound(int sound_uid, unsigned char f_immediately) {
|
|
int current_slot;
|
|
sound_buffer_info *sb;
|
|
|
|
if (sound_device == -1)
|
|
return;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
|
|
sb = &sound_cache[current_slot];
|
|
|
|
if (sb->m_status == SSF_UNUSED)
|
|
return;
|
|
|
|
// update sound count.
|
|
m_cur_sounds_played--;
|
|
|
|
if (f_immediately == SKT_STOP_AFTER_LOOP) {
|
|
sb->m_status &= ~SSF_PLAY_LOOPING;
|
|
sb->m_status |= SSF_PLAY_NORMAL;
|
|
return;
|
|
}
|
|
|
|
sound_cache[current_slot].m_status = SSF_UNUSED;
|
|
}
|
|
|
|
// Pause all sounds/resume all sounds
|
|
void lnxsound::PauseSounds(void) {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
sound_buffer_info *sb = &sound_cache[current_slot];
|
|
if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) {
|
|
sb->m_status |= SSF_PAUSED;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lnxsound::ResumeSounds(void) {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
sound_buffer_info *sb = &sound_cache[current_slot];
|
|
|
|
if (sb->m_status != SSF_UNUSED && (sb->m_status & SSF_PAUSED)) {
|
|
sound_cache[current_slot].m_status &= (~SSF_PAUSED);
|
|
}
|
|
}
|
|
}
|
|
|
|
void lnxsound::PauseSound(int sound_uid) {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
sound_buffer_info *sb = &sound_cache[current_slot];
|
|
if (sb->m_unique_id == sound_uid) {
|
|
if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) {
|
|
sb->m_status |= SSF_PAUSED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lnxsound::ResumeSound(int sound_uid) {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
if (sound_uid == sound_cache[current_slot].m_unique_id) {
|
|
if (sound_cache[current_slot].m_status != SSF_UNUSED && (sound_cache[current_slot].m_status & SSF_PAUSED)) {
|
|
sound_cache[current_slot].m_status &= (~SSF_PAUSED);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool lnxsound::CheckAndForceSoundDataAlloc(int sound_index) {
|
|
int result;
|
|
int sound_file_index = Sounds[sound_index].sample_index;
|
|
|
|
// ASSERT(sound_file_index >= 0 && sound_file_index < MAX_SOUND_FILES);
|
|
|
|
if (sound_file_index < 0 || sound_file_index >= MAX_SOUND_FILES) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the sample data is already loaded
|
|
if (SoundFiles[sound_file_index].sample_16bit != NULL || SoundFiles[sound_file_index].sample_8bit != NULL)
|
|
return true;
|
|
|
|
// If not, get the sound data
|
|
result = SoundLoadWaveFile(SoundFiles[sound_file_index].name, Sounds[sound_index].import_volume, sound_file_index,
|
|
(m_sound_quality == SQT_HIGH), true);
|
|
|
|
// Why would it load once (table load time) and not now?
|
|
if (!result)
|
|
return false;
|
|
|
|
mprintf((0, "Sound %s loaded.\n", SoundFiles[sound_file_index].name));
|
|
|
|
return true;
|
|
}
|
|
|
|
// Begin sound frame
|
|
void lnxsound::SoundStartFrame(void) {
|
|
float frame_time;
|
|
int current_slot;
|
|
int i;
|
|
|
|
if (m_timer_last_frametime == -1) {
|
|
frame_time = 0.0f;
|
|
} else {
|
|
frame_time = (timer_GetMSTime() - m_timer_last_frametime) / 1000.0f;
|
|
}
|
|
m_timer_last_frametime = timer_GetMSTime();
|
|
|
|
// perform necessary functions if sound events are pending for frame, this doesn't have to do anything
|
|
// if the mixer doesn't require such actions. Aureal does though.
|
|
if (m_pending_actions) {
|
|
mprintf((0, "pending actions\n"));
|
|
}
|
|
|
|
m_in_sound_frame = true;
|
|
m_pending_actions = false;
|
|
|
|
int counter = 0, loop_counter = 0, stream_counter = 0, buf_loop_counter = 0;
|
|
|
|
#ifdef _DEBUG
|
|
int n_p5 = 0, n_p4 = 0, n_p3 = 0, n_p2 = 0, n_p1 = 0, n_p0 = 0;
|
|
#endif
|
|
|
|
for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) {
|
|
sound_buffer_info *sb = &sound_cache[current_slot];
|
|
if (sb->m_status != SSF_UNUSED) {
|
|
counter++;
|
|
if (sb->m_status & SSF_PLAY_LOOPING) {
|
|
if (sb->m_status & SSF_BUFFERED_LOOP)
|
|
buf_loop_counter++;
|
|
loop_counter++;
|
|
}
|
|
if (sb->m_status & SSF_PLAY_STREAMING)
|
|
stream_counter++;
|
|
|
|
#ifdef _DEBUG
|
|
if (sb->play_info->priority == SND_PRIORITY_CRITICAL)
|
|
n_p5++;
|
|
else if (sb->play_info->priority == SND_PRIORITY_HIGHEST)
|
|
n_p4++;
|
|
else if (sb->play_info->priority == SND_PRIORITY_HIGH)
|
|
n_p3++;
|
|
else if (sb->play_info->priority == SND_PRIORITY_NORMAL)
|
|
n_p2++;
|
|
else if (sb->play_info->priority == SND_PRIORITY_LOW)
|
|
n_p1++;
|
|
else if (sb->play_info->priority == SND_PRIORITY_LOWEST)
|
|
n_p0++;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// update cache stress timer.
|
|
if (counter < (MAX_SOUNDS_PLAYING_AT_ONCE * 3 / 4)) {
|
|
m_cache_stress_timer += frame_time;
|
|
} else {
|
|
m_cache_stress_timer = 0.0f;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
mprintf_at((3, 2, 0, "LNS: %02d/%02d", counter, MAX_SOUNDS_PLAYING_AT_ONCE));
|
|
mprintf_at((3, 3, 1, "Lp: %02d", loop_counter));
|
|
mprintf_at((3, 4, 1, "St: %02d", stream_counter));
|
|
mprintf_at((3, 5, 0, " Ot: %02d", counter - loop_counter - stream_counter));
|
|
|
|
mprintf_at((3, 2, 20, "P5:%02d P4:%02d P3:%02d", n_p5, n_p4, n_p3));
|
|
mprintf_at((3, 3, 20, "P2:%02d P1:%02d P0:%02d", n_p2, n_p1, n_p0));
|
|
#endif
|
|
}
|
|
|
|
// End sound frame
|
|
void lnxsound::SoundEndFrame(void) {
|
|
CheckForErrors(); // handles errors.
|
|
m_in_sound_frame = false;
|
|
}
|
|
|
|
// Sound System Error Handler.
|
|
void lnxsound::CheckForErrors() {
|
|
// if a fatal error occurred, quit and display an error
|
|
// non fatal errors should be put inside a logfile, or just mprinted out.
|
|
switch (m_lib_error_code) {
|
|
case SSL_ERROR_SAMPLE_NODATA:
|
|
Error("%s\nSample had no data.", m_error_text);
|
|
break;
|
|
|
|
case SSL_ERROR_STREAMMIXER:
|
|
Error("%s\nMixer alignment check failed.", m_error_text);
|
|
break;
|
|
|
|
case SSL_ERROR_GENERIC:
|
|
Error("%s\nGeneric error.", m_error_text);
|
|
break;
|
|
}
|
|
|
|
// must call!
|
|
llsSystem::CheckForErrors();
|
|
}
|
|
|
|
// returns the error string.
|
|
const char *lnxsound::GetErrorStr() const {
|
|
static char buffer[] = "No Error Given";
|
|
return buffer;
|
|
}
|
|
|
|
bool lnxsound::SetGlobalReverbProperties(float volume, float damping, float decay) { return false; }
|
|
|
|
void lnxsound::StartStreaming(void) {}
|
|
|
|
void lnxsound::EndStreaming(void) {}
|
|
|
|
// may be called before init (must be to be valid, the card passed here will be initialized in InitSoundLib)
|
|
void lnxsound::SetSoundCard(const char *name) {}
|
|
|
|
// set special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void lnxsound::SetEnvironmentValues(const t3dEnvironmentValues *env) {}
|
|
|
|
// get special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void lnxsound::GetEnvironmentValues(t3dEnvironmentValues *env) {}
|
|
|
|
// enable special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void lnxsound::SetEnvironmentToggles(const t3dEnvironmentToggles *env) {}
|
|
|
|
// get states of special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void lnxsound::GetEnvironmentToggles(t3dEnvironmentToggles *env) {}
|
|
|
|
void lnxsound_SetError(int code) { ll_sound_ptr->SetError(code); }
|
|
|
|
void lnxsound_ErrorText(const char *fmt, ...) {
|
|
char buffer[384];
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
|
va_end(args);
|
|
|
|
ll_sound_ptr->ErrorText(buffer);
|
|
}
|
|
|
|
// A peroidic mixer that uses the primary buffer as a stream buffer
|
|
static void StreamAudio(void *user_ptr, Uint8 *stream, int len) {
|
|
((software_mixer *)user_ptr)->StreamMixer((char *)stream, len);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// llsGeometry
|
|
#include "ddsndgeometry.h"
|
|
|
|
// specify a sound library to associate geometry with
|
|
bool llsGeometry::Init(llsSystem *snd_sys) { return false; }
|
|
|
|
// closes low level geometry system.
|
|
void llsGeometry::Shutdown() {}
|
|
|
|
void llsGeometry::StartFrame() {}
|
|
void llsGeometry::EndFrame() {}
|
|
|
|
// clears out geometry info
|
|
void llsGeometry::Clear() {}
|
|
|
|
// polygon lists
|
|
// is a group cached?, check before rendering it.
|
|
void llsGeometry::IsGroupValid(int group) {}
|
|
|
|
// marks beginning of a list of polygons to render, (-1 group for non cache)
|
|
void llsGeometry::StartPolygonGroup(int group) {}
|
|
|
|
// ends a list of polygons to render.
|
|
void llsGeometry::EndPolygonGroup(int group) {}
|
|
|
|
// renders a group.
|
|
void llsGeometry::RenderGroup(int group) {}
|
|
|
|
// primatives, nv = number of verts, and verts is an array of pointers to vertices.
|
|
// you can pass a sound material value if you want special reflective properties on this polygon.
|
|
void llsGeometry::AddPoly(int nv, vector **verts, unsigned tag, tSoundMaterial material) {}
|
|
|
|
// 4 verts here.
|
|
void llsGeometry::AddQuad(unsigned tag, vector **verts) {}
|
|
|
|
// 3 verts here.
|
|
void llsGeometry::AddTriangle(unsigned tag, vector **verts) {}
|
|
|
|
void llsGeometry::CreateMaterial(tSoundMaterial material, float transmit_gain, float transmit_highfreq,
|
|
float reflect_gain, float reflect_highfreq) {}
|
|
|
|
void llsGeometry::DestroyMaterial(tSoundMaterial material) {}
|