mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
* use std::move() for local variables that are copied but not reused Addresses ~140 Coverity "Variable copied when it could be moved" defects across 74 files. Every std::move() targets a local variable whose value is consumed by push_back, emplace, assignment, lambda capture, or a function call and is never read again afterward. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * clang: use std::make_shared * clang: use std::make_unique * address feedback --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1951 lines
59 KiB
C++
1951 lines
59 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "cfile/cfile.h"
|
|
#include "cmdline/cmdline.h"
|
|
#include "globalincs/pstypes.h"
|
|
#include "osapi/osapi.h"
|
|
#include "sound/audiostr.h"
|
|
#include "sound/channel.h"
|
|
#include "sound/ds.h"
|
|
#include "sound/ds3d.h"
|
|
#include "sound/dscap.h"
|
|
#include "sound/openal.h"
|
|
#include "sound/sound.h" // jg18 - for enhanced sound
|
|
#include "options/Option.h"
|
|
|
|
|
|
typedef struct sound_buffer
|
|
{
|
|
ALuint buf_id; // OpenAL buffer id
|
|
int channel_id; // Channel[] index this buffer is currently bound to
|
|
|
|
int frequency;
|
|
int bits_per_sample;
|
|
int nchannels;
|
|
int nseconds;
|
|
int nbytes;
|
|
|
|
sound_buffer():
|
|
buf_id(0), channel_id(-1), frequency(0), bits_per_sample(0),
|
|
nchannels(0), nseconds(0), nbytes(0)
|
|
{
|
|
}
|
|
} sound_buffer;
|
|
|
|
|
|
static int MAX_CHANNELS; // initialized properly in ds_init_channels()
|
|
channel *Channels = NULL;
|
|
static int channel_next_sig = 1;
|
|
|
|
const int BUFFER_BUMP = 50;
|
|
SCP_vector<sound_buffer> sound_buffers;
|
|
|
|
static int Ds_use_eax = 0;
|
|
|
|
static int Ds_eax_inited = 0;
|
|
|
|
static int AL_play_position = 0;
|
|
|
|
// NOTE: these can't be static
|
|
int Ds_sound_quality = DS_SQ_MEDIUM;
|
|
int Ds_float_supported = 0;
|
|
|
|
|
|
// this is so stupid - required to get VC6 to use the following array initializer
|
|
EFXREVERBPROPERTIES::EFXREVERBPROPERTIES(const EFXREVERBPROPERTIES_list &list)
|
|
{
|
|
name = list.name;
|
|
flDensity = list.flDensity;
|
|
flDiffusion = list.flDiffusion;
|
|
flGain = list.flGain;
|
|
flGainHF = list.flGainHF;
|
|
flGainLF = list.flGainLF;
|
|
flDecayTime = list.flDecayTime;
|
|
flDecayHFRatio = list.flDecayHFRatio;
|
|
flDecayLFRatio = list.flDecayLFRatio;
|
|
flReflectionsGain = list.flReflectionsGain;
|
|
flReflectionsDelay = list.flReflectionsDelay;
|
|
flReflectionsPan[0] = list.flReflectionsPan[0];
|
|
flReflectionsPan[1] = list.flReflectionsPan[1];
|
|
flReflectionsPan[2] = list.flReflectionsPan[2];
|
|
flLateReverbGain = list.flLateReverbGain;
|
|
flLateReverbDelay = list.flLateReverbDelay;
|
|
flLateReverbPan[0] = list.flLateReverbPan[0];
|
|
flLateReverbPan[1] = list.flLateReverbPan[1];
|
|
flLateReverbPan[2] = list.flLateReverbPan[2];
|
|
flEchoTime = list.flEchoTime;
|
|
flEchoDepth = list.flEchoDepth;
|
|
flModulationTime = list.flModulationTime;
|
|
flModulationDepth = list.flModulationDepth;
|
|
flAirAbsorptionGainHF = list.flAirAbsorptionGainHF;
|
|
flHFReference = list.flHFReference;
|
|
flLFReference = list.flLFReference;
|
|
flRoomRolloffFactor = list.flRoomRolloffFactor;
|
|
iDecayHFLimit = list.iDecayHFLimit;
|
|
}
|
|
|
|
static const EFXREVERBPROPERTIES_list EFX_Reverb_Defaults[EAX_ENVIRONMENT_COUNT] =
|
|
{
|
|
{ "Generic", 1.0f, 1.0f, 0.316228f, 0.891251f, 1.0f, 1.49f, 0.83f, 1.0f, 0.050003f, 0.007f, {0.0f, 0.0f, 0.0f}, 1.258925f, 0.011f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Padded cell", 0.171500f, 1.0f, 0.316228f, 0.001f, 1.0f, 0.17f, 0.10f, 1.0f, 0.250035f, 0.001f, {0.0f, 0.0f, 0.0f}, 1.269112f, 0.002f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Room", 0.428687f, 1.0f, 0.316228f, 0.592925f, 1.0f, 0.40f, 0.83f, 1.0f, 0.150314f, 0.002f, {0.0f, 0.0f, 0.0f}, 1.062919f, 0.003f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Bathroom", 0.171500f, 1.0f, 0.316228f, 0.251189f, 1.0f, 1.49f, 0.54f, 1.0f, 0.653131f, 0.007f, {0.0f, 0.0f, 0.0f}, 3.273407f, 0.011f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Living room", 0.976563f, 1.0f, 0.316228f, 0.001f, 1.0f, 0.50f, 0.10f, 1.0f, 0.205116f, 0.003f, {0.0f, 0.0f, 0.0f}, 0.280543f, 0.004f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Stone room", 1.0f, 1.0f, 0.316228f, 0.707946f, 1.0f, 2.31f, 0.64f, 1.0f, 0.441062f, 0.012f, {0.0f, 0.0f, 0.0f}, 1.100272f, 0.017f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Auditorium", 1.0f, 1.0f, 0.316228f, 0.578096f, 1.0f, 4.32f, 0.59f, 1.0f, 0.403181f, 0.02f, {0.0f, 0.0f, 0.0f}, 0.716968f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Concert hall", 1.0f, 1.0f, 0.316228f, 0.562341f, 1.0f, 3.92f, 0.70f, 1.0f, 0.242661f, 0.02f, {0.0f, 0.0f, 0.0f}, 0.997700f, 0.029f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Cave", 1.0f, 1.0f, 0.316228f, 1.0f, 1.0f, 2.91f, 1.30f, 1.0f, 0.500035f, 0.015f, {0.0f, 0.0f, 0.0f}, 0.706318f, 0.022f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 0 },
|
|
{ "Arena", 1.0f, 1.0f, 0.316228f, 0.447713f, 1.0f, 7.24f, 0.33f, 1.0f, 0.261216f, 0.02f, {0.0f, 0.0f, 0.0f}, 1.018591f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Hangar", 1.0f, 1.0f, 0.316228f, 0.316228f, 1.0f, 10.05f, 0.23f, 1.0f, 0.500035f, 0.02f, {0.0f, 0.0f, 0.0f}, 1.256030f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Carpeted hallway", 0.428687f, 1.0f, 0.316228f, 0.01f, 1.0f, 0.30f, 0.10f, 1.0f, 0.121479f, 0.002f, {0.0f, 0.0f, 0.0f}, 0.153109f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Hallway", 0.364500f, 1.0f, 0.316228f, 0.707946f, 1.0f, 1.49f, 0.59f, 1.0f, 0.245754f, 0.007f, {0.0f, 0.0f, 0.0f}, 1.661499f, 0.011f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Stone corridor", 1.0f, 1.0f, 0.316228f, 0.761202f, 1.0f, 2.70f, 0.79f, 1.0f, 0.247172f, 0.013f, {0.0f, 0.0f, 0.0f}, 1.575796f, 0.02f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Alley", 1.0f, 0.30f, 0.316228f, 0.732825f, 1.0f, 1.49f, 0.86f, 1.0f, 0.250035f, 0.007f, {0.0f, 0.0f, 0.0f}, 0.995405f, 0.011f, {0.0f, 0.0f, 0.0f}, 0.125f, 0.95f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Forest", 1.0f, 0.30f, 0.316228f, 0.022387f, 1.0f, 1.49f, 0.54f, 1.0f, 0.052481f, 0.162f, {0.0f, 0.0f, 0.0f}, 0.768245f, 0.088f, {0.0f, 0.0f, 0.0f}, 0.125f, 1.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "City", 1.0f, 0.50f, 0.316228f, 0.398107f, 1.0f, 1.49f, 0.67f, 1.0f, 0.073030f, 0.007f, {0.0f, 0.0f, 0.0f}, 0.142725f, 0.011f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Mountains", 1.0f, 0.27f, 0.316228f, 0.056234f, 1.0f, 1.49f, 0.21f, 1.0f, 0.040738f, 0.30f, {0.0f, 0.0f, 0.0f}, 0.191867f, 0.10f, {0.0f, 0.0f, 0.0f}, 0.25f, 1.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 0 },
|
|
{ "Quarry", 1.0f, 1.0f, 0.316228f, 0.316228f, 1.0f, 1.49f, 0.83f, 1.0f, 0.0f, 0.061f, {0.0f, 0.0f, 0.0f}, 1.778279f, 0.025f, {0.0f, 0.0f, 0.0f}, 0.125f, 0.70f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Plain", 1.0f, 0.21f, 0.316228f, 0.10f, 1.0f, 1.49f, 0.50f, 1.0f, 0.058479f, 0.179f, {0.0f, 0.0f, 0.0f}, 0.108893f, 0.10f, {0.0f, 0.0f, 0.0f}, 0.25f, 1.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Parking lot", 1.0f, 1.0f, 0.316228f, 1.0f, 1.0f, 1.65f, 1.50f, 1.0f, 0.208209f, 0.008f, {0.0f, 0.0f, 0.0f}, 0.265155f, 0.012f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 0 },
|
|
{ "Sewer pipe", 0.307063f, 0.80f, 0.316228f, 0.316228f, 1.0f, 2.81f, 0.14f, 1.0f, 1.638702f, 0.014f, {0.0f, 0.0f, 0.0f}, 3.247133f, 0.021f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 0.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Underwater", 0.364500f, 1.0f, 0.316228f, 0.01f, 1.0f, 1.49f, 0.10f, 1.0f, 0.596348f, 0.007f, {0.0f, 0.0f, 0.0f}, 7.079458f, 0.011f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 1.18f, 0.348f, 0.994260f, 5000.0f, 250.0f, 0.0f, 1 },
|
|
{ "Drugged", 0.428687f, 0.50f, 0.316228f, 1.0f, 1.0f, 8.39f, 1.39f, 1.0f, 0.875992f, 0.002f, {0.0f, 0.0f, 0.0f}, 3.108136f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 0.25f, 1.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 0 },
|
|
{ "Dizzy", 0.364500f, 0.60f, 0.316228f, 0.630957f, 1.0f, 17.23f, 0.56f, 1.0f, 0.139155f, 0.02f, {0.0f, 0.0f, 0.0f}, 0.493742f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 1.0f, 0.81f, 0.31f, 0.994260f, 5000.0f, 250.0f, 0.0f, 0 },
|
|
{ "Psychotic", 0.062500f, 0.50f, 0.316228f, 0.840427f, 1.0f, 7.56f, 0.91f, 1.0f, 0.486407f, 0.02f, {0.0f, 0.0f, 0.0f}, 2.437811f, 0.03f, {0.0f, 0.0f, 0.0f}, 0.25f, 0.0f, 4.00f, 1.0f, 0.994260f, 5000.0f, 250.0f, 0.0f, 0 }
|
|
};
|
|
|
|
SCP_vector<EFXREVERBPROPERTIES> EFX_presets;
|
|
|
|
|
|
typedef ALvoid (AL_APIENTRY * ALGENFILTERS) (ALsizei, ALuint*);
|
|
typedef ALvoid (AL_APIENTRY * ALDELETEFILTERS) (ALsizei, ALuint*);
|
|
typedef ALvoid (AL_APIENTRY * ALFILTERI) (ALuint, ALenum, ALint);
|
|
typedef ALvoid (AL_APIENTRY * ALGENEFFECTS) (ALsizei, ALuint*);
|
|
typedef ALvoid (AL_APIENTRY * ALDELETEEFFECTS) (ALsizei, ALuint*);
|
|
typedef ALvoid (AL_APIENTRY * ALEFFECTI) (ALuint, ALenum, ALint);
|
|
typedef ALvoid (AL_APIENTRY * ALEFFECTF) (ALuint, ALenum, ALfloat);
|
|
typedef ALvoid (AL_APIENTRY * ALEFFECTFV) (ALuint, ALenum, ALfloat*);
|
|
typedef ALvoid (AL_APIENTRY * ALGETEFFECTF) (ALuint, ALenum, ALfloat*);
|
|
typedef ALvoid (AL_APIENTRY * ALGENAUXILIARYEFFECTSLOTS) (ALsizei, ALuint*);
|
|
typedef ALvoid (AL_APIENTRY * ALDELETEAUXILIARYEFFECTSLOTS) (ALsizei, ALuint*);
|
|
typedef ALboolean (AL_APIENTRY * ALISAUXILIARYEFFECTSLOT) (ALuint);
|
|
typedef ALvoid (AL_APIENTRY * ALAUXILIARYEFFECTSLOTI) (ALuint, ALenum, ALint);
|
|
typedef ALvoid (AL_APIENTRY * ALAUXILIARYEFFECTSLOTIV) (ALuint, ALenum, ALint*);
|
|
typedef ALvoid (AL_APIENTRY * ALAUXILIARYEFFECTSLOTF) (ALuint, ALenum, ALfloat);
|
|
typedef ALvoid (AL_APIENTRY * ALAUXILIARYEFFECTSLOTFV) (ALuint, ALenum, ALfloat*);
|
|
|
|
|
|
ALGENFILTERS v_alGenFilters = NULL;
|
|
ALDELETEFILTERS v_alDeleteFilters = NULL;
|
|
|
|
ALFILTERI v_alFilteri = NULL;
|
|
|
|
ALGENEFFECTS v_alGenEffecs = NULL;
|
|
ALDELETEEFFECTS v_alDeleteEffects = NULL;
|
|
|
|
ALEFFECTI v_alEffecti = NULL;
|
|
ALEFFECTF v_alEffectf = NULL;
|
|
ALEFFECTFV v_alEffectfv = NULL;
|
|
ALGETEFFECTF v_alGetEffectf = NULL;
|
|
|
|
ALGENAUXILIARYEFFECTSLOTS v_alGenAuxiliaryEffectSlots = NULL;
|
|
ALDELETEAUXILIARYEFFECTSLOTS v_alDeleteAuxiliaryEffectSlots = NULL;
|
|
|
|
ALISAUXILIARYEFFECTSLOT v_alIsAuxiliaryEffectSlot = NULL;
|
|
ALAUXILIARYEFFECTSLOTI v_alAuxiliaryEffectSloti = NULL;
|
|
ALAUXILIARYEFFECTSLOTIV v_alAuxiliaryEffectSlotiv = NULL;
|
|
ALAUXILIARYEFFECTSLOTF v_alAuxiliaryEffectSlotf = NULL;
|
|
ALAUXILIARYEFFECTSLOTFV v_alAuxiliaryEffectSlotfv = NULL;
|
|
|
|
ALCdevice *ds_sound_device = NULL;
|
|
ALCcontext *ds_sound_context = NULL;
|
|
|
|
ALuint AL_EFX_aux_id = 0;
|
|
|
|
static ALuint AL_EFX_effect_id = 0;
|
|
static bool Ds_active_env = false;
|
|
static size_t Ds_active_env_idx = 0;
|
|
|
|
bool Ingame_efx = false;
|
|
|
|
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
|
|
static auto EnableEFXOption = options::OptionBuilder<bool>("Audio.EnableEFX",
|
|
std::pair<const char*, int>{"Use Audio EFX", 1832},
|
|
std::pair<const char*, int>{"Toggle whether OpenAL Audio EFX are used or not", 1833})
|
|
.category(std::make_pair("Audio", 1826))
|
|
.level(options::ExpertLevel::Advanced)
|
|
.default_val(false)
|
|
.bind_to_once(&Ingame_efx)
|
|
.importance(69)
|
|
.finish();
|
|
|
|
|
|
static void *al_load_function(const char *func_name)
|
|
{
|
|
void *func = alGetProcAddress(func_name);
|
|
if ( !func ) {
|
|
throw std::runtime_error(func_name);
|
|
}
|
|
return func;
|
|
}
|
|
|
|
static void al_efx_load_preset(size_t presetid)
|
|
{
|
|
if ( !Ds_eax_inited ) {
|
|
return;
|
|
}
|
|
|
|
if (presetid >= EFX_presets.size()) {
|
|
return;
|
|
}
|
|
|
|
EFXREVERBPROPERTIES *prop = &EFX_presets[presetid];
|
|
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DENSITY, prop->flDensity) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DIFFUSION, prop->flDiffusion) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_GAIN, prop->flGain) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_GAINHF, prop->flGainHF) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_GAINLF, prop->flGainLF) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_TIME, prop->flDecayTime) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_HFRATIO, prop->flDecayHFRatio) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_LFRATIO, prop->flDecayLFRatio) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_REFLECTIONS_GAIN, prop->flReflectionsGain) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_REFLECTIONS_DELAY, prop->flReflectionsDelay) );
|
|
OpenAL_ErrorPrint( v_alEffectfv(AL_EFX_effect_id, AL_EAXREVERB_REFLECTIONS_PAN, prop->flReflectionsPan) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_LATE_REVERB_GAIN, prop->flLateReverbGain) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_LATE_REVERB_DELAY, prop->flLateReverbDelay) );
|
|
OpenAL_ErrorPrint( v_alEffectfv(AL_EFX_effect_id, AL_EAXREVERB_LATE_REVERB_PAN, prop->flLateReverbPan) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_ECHO_TIME, prop->flEchoTime) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_ECHO_DEPTH, prop->flEchoDepth) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_MODULATION_TIME, prop->flModulationTime) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_MODULATION_DEPTH, prop->flModulationDepth) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, prop->flAirAbsorptionGainHF) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_HFREFERENCE, prop->flHFReference) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_LFREFERENCE, prop->flLFReference) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, prop->flRoomRolloffFactor) );
|
|
OpenAL_ErrorPrint( v_alEffecti(AL_EFX_effect_id, AL_EAXREVERB_DECAY_HFLIMIT, prop->iDecayHFLimit) );
|
|
|
|
Ds_active_env_idx = presetid;
|
|
Ds_active_env = true;
|
|
}
|
|
|
|
|
|
int ds_initialized = FALSE;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int ds_get_sid()
|
|
{
|
|
sound_buffer new_buffer;
|
|
uint i;
|
|
|
|
for (i = 0; i < sound_buffers.size(); i++) {
|
|
if (sound_buffers[i].buf_id == 0) {
|
|
return (int)i;
|
|
}
|
|
}
|
|
|
|
sound_buffers.push_back( new_buffer );
|
|
|
|
return (int)(sound_buffers.size() - 1);
|
|
}
|
|
|
|
int ds_load_buffer(int *sid, int /*flags*/, sound::IAudioFile* file)
|
|
{
|
|
Assert(sid != NULL);
|
|
Assert(file != NULL);
|
|
|
|
// All sounds are required to have a software buffer
|
|
*sid = ds_get_sid();
|
|
if (*sid == -1) {
|
|
nprintf(("Sound", "SOUND ==> No more sound buffers available\n"));
|
|
return -1;
|
|
}
|
|
|
|
ALuint pi;
|
|
OpenAL_ErrorCheck(alGenBuffers(1, &pi), return -1);
|
|
|
|
const auto fileProps = file->getFileProperties();
|
|
|
|
ALenum format;
|
|
ALsizei size = fileProps.total_samples * fileProps.bytes_per_sample * fileProps.num_channels;
|
|
ALint n_channels = fileProps.num_channels;
|
|
ALsizei frequency;
|
|
|
|
// format is now in pcm
|
|
frequency = fileProps.sample_rate;
|
|
format = openal_get_format(fileProps.bytes_per_sample * 8, fileProps.num_channels);
|
|
|
|
if (format == AL_INVALID_VALUE) {
|
|
return -1;
|
|
}
|
|
|
|
SCP_vector<uint8_t> audio_buffer;
|
|
audio_buffer.reserve(size);
|
|
|
|
SCP_vector<uint8_t> buffer(fileProps.sample_rate * fileProps.bytes_per_sample * fileProps.num_channels);
|
|
int read;
|
|
while((read = file->Read(&buffer[0], buffer.size())) >= 0) {
|
|
if (read == 0) {
|
|
// buffer not large enough
|
|
buffer.resize(buffer.size() * 2);
|
|
} else {
|
|
audio_buffer.insert(audio_buffer.end(), buffer.begin(), std::next(buffer.begin(), read));
|
|
}
|
|
}
|
|
|
|
Snd_sram += audio_buffer.size();
|
|
|
|
OpenAL_ErrorCheck(alBufferData(pi, format, audio_buffer.data(), (ALsizei)audio_buffer.size(), frequency), return -1; );
|
|
|
|
sound_buffers[*sid].buf_id = pi;
|
|
sound_buffers[*sid].channel_id = -1;
|
|
sound_buffers[*sid].frequency = frequency;
|
|
sound_buffers[*sid].bits_per_sample = fileProps.bytes_per_sample * 8;
|
|
sound_buffers[*sid].nchannels = n_channels;
|
|
sound_buffers[*sid].nseconds = fl2i(fileProps.duration);
|
|
sound_buffers[*sid].nbytes = (int)audio_buffer.size();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Initialise the ::Channels[] array dynamically based on system resources.
|
|
*/
|
|
void ds_init_channels()
|
|
{
|
|
// Legacy assumed safe value if for some reason dynamicly setting it does not work
|
|
MAX_CHANNELS = 128;
|
|
|
|
if (Cmdline_no_enhanced_sound)
|
|
MAX_CHANNELS = 32; // Old sound code limts
|
|
else {
|
|
ALCint size;
|
|
alcGetIntegerv(ds_sound_device, ALC_ATTRIBUTES_SIZE, 1, &size);
|
|
std::vector<ALCint> attrs(size);
|
|
alcGetIntegerv(ds_sound_device, ALC_ALL_ATTRIBUTES, size, &attrs[0]);
|
|
for (size_t i = 0; i < attrs.size(); ++i) {
|
|
//We reserve a portion of the reported channels to avoid colliding with other types of sound
|
|
//Using the 32 channels of the no_enhanced_sound as a floor.
|
|
if (attrs[i] == ALC_MONO_SOURCES && attrs[i+1] - DS_RESERVED_CHANNELS > 32) {
|
|
MAX_CHANNELS = attrs[i + 1] - DS_RESERVED_CHANNELS;
|
|
mprintf((" ALC reported %i available sources, setting max to use at %i ", attrs[i + 1],MAX_CHANNELS));
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
Channels = new channel[MAX_CHANNELS];
|
|
} catch (const std::bad_alloc&) {
|
|
Error(LOCATION, "Unable to allocate " SIZE_T_ARG " bytes for %d audio channels.", sizeof(channel) * MAX_CHANNELS, MAX_CHANNELS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialise the both the software and hardware buffers
|
|
*/
|
|
void ds_init_buffers()
|
|
{
|
|
sound_buffers.clear();
|
|
|
|
// pre-allocate for at least BUFFER_BUMP buffers
|
|
sound_buffers.reserve( BUFFER_BUMP );
|
|
}
|
|
|
|
/**
|
|
* Check if the player is using OpenAL Soft,
|
|
* which is required to use enhanced sound.
|
|
* Returns true on success, false otherwise.
|
|
*/
|
|
bool ds_check_for_openal_soft()
|
|
{
|
|
const ALchar * renderer = alGetString(AL_RENDERER);
|
|
if (renderer == NULL)
|
|
{
|
|
mprintf(("ds_check_for_openal_soft: renderer is null!\n"));
|
|
return false;
|
|
}
|
|
else if (!stricmp((const char *)renderer, "OpenAL Soft"))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sound initialisation
|
|
* @return -1 if init failed, 0 if init success
|
|
*/
|
|
int ds_init()
|
|
{
|
|
ALfloat list_orien[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f };
|
|
ALCint attrList[] = { ALC_FREQUENCY, 22050, 0 };
|
|
unsigned int sample_rate = 22050;
|
|
|
|
mprintf(("Initializing OpenAL...\n"));
|
|
|
|
Ds_sound_quality = os_config_read_uint("Sound", "Quality", DS_SQ_MEDIUM);
|
|
CLAMP(Ds_sound_quality, DS_SQ_LOW, DS_SQ_HIGH);
|
|
|
|
switch (Ds_sound_quality) {
|
|
case DS_SQ_HIGH:
|
|
sample_rate = 48000;
|
|
break;
|
|
|
|
case DS_SQ_MEDIUM:
|
|
sample_rate = 44100;
|
|
break;
|
|
|
|
default:
|
|
sample_rate = 22050;
|
|
break;
|
|
}
|
|
|
|
sample_rate = os_config_read_uint("Sound", "SampleRate", sample_rate);
|
|
|
|
attrList[1] = sample_rate;
|
|
SCP_string playback_device;
|
|
SCP_string capture_device;
|
|
|
|
if ( openal_init_device(&playback_device, &capture_device) == false ) {
|
|
mprintf(("\n ERROR: Unable to find suitable playback device!\n\n"));
|
|
goto AL_InitError;
|
|
}
|
|
|
|
ds_sound_device = alcOpenDevice( (const ALCchar*) playback_device.c_str() );
|
|
|
|
if (ds_sound_device == NULL) {
|
|
mprintf((" Failed to open playback_device (%s) returning error (%s)\n", playback_device.c_str(), openal_error_string(1)));
|
|
goto AL_InitError;
|
|
}
|
|
|
|
ds_sound_context = alcCreateContext(ds_sound_device, attrList);
|
|
|
|
if (ds_sound_context == NULL) {
|
|
mprintf((" Failed to create context for playback_device (%s) with attrList = { 0x%x, %d, %d } returning error (%s)\n",
|
|
playback_device.c_str(), attrList[0], attrList[1], attrList[2], openal_error_string(1)));
|
|
goto AL_InitError;
|
|
}
|
|
|
|
alcMakeContextCurrent(ds_sound_context);
|
|
|
|
alcGetError(ds_sound_device);
|
|
|
|
mprintf((" OpenAL Vendor : %s\n", alGetString(AL_VENDOR)));
|
|
mprintf((" OpenAL Renderer : %s\n", alGetString(AL_RENDERER)));
|
|
mprintf((" OpenAL Version : %s\n", alGetString(AL_VERSION)));
|
|
mprintf(("\n"));
|
|
|
|
// we need to clear out all errors before moving on
|
|
alcGetError(NULL);
|
|
alGetError();
|
|
|
|
// set distance model to basically match what the D3D code does
|
|
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
|
|
|
// make sure we can actually use AL_BYTE_LOKI (Mac/Win OpenAL doesn't have it)
|
|
if ( alIsExtensionPresent( (const ALchar*)"AL_LOKI_play_position" ) == AL_TRUE ) {
|
|
mprintf((" Found extension \"AL_LOKI_play_position\".\n"));
|
|
AL_play_position = 1;
|
|
}
|
|
|
|
if ( alIsExtensionPresent( (const ALchar*)"AL_EXT_float32" ) == AL_TRUE ) {
|
|
mprintf((" Found extension \"AL_EXT_float32\".\n"));
|
|
Ds_float_supported = 1;
|
|
}
|
|
|
|
Ds_use_eax = 0;
|
|
|
|
if ( alcIsExtensionPresent(ds_sound_device, (const ALchar*)"ALC_EXT_EFX") == AL_TRUE ) {
|
|
mprintf((" Found extension \"ALC_EXT_EFX\".\n"));
|
|
if (Using_in_game_options) {
|
|
if (Fred_running) {
|
|
Ds_use_eax = Fred_running;
|
|
} else {
|
|
Ds_use_eax = EnableEFXOption->getValue();
|
|
}
|
|
} else {
|
|
Ds_use_eax = os_config_read_uint("Sound", "EnableEFX", Fred_running);
|
|
}
|
|
}
|
|
|
|
if (Ds_use_eax == 1) {
|
|
if (ds_eax_init() != 0) {
|
|
Ds_use_eax = 0;
|
|
}
|
|
}
|
|
|
|
// the presets always need to be available to FRED
|
|
if ( !Ds_use_eax && Fred_running ) {
|
|
EFX_presets.reserve(EAX_ENVIRONMENT_COUNT);
|
|
|
|
for (size_t i = 0; i < EAX_ENVIRONMENT_COUNT; i++) {
|
|
EFX_presets.push_back( EFX_Reverb_Defaults[i] );
|
|
}
|
|
}
|
|
|
|
if (!Cmdline_no_enhanced_sound)
|
|
{
|
|
if (!ds_check_for_openal_soft())
|
|
{
|
|
mprintf(("You are not using OpenAL Soft. Disabling enhanced sound.\n"));
|
|
Cmdline_no_enhanced_sound = 1;
|
|
}
|
|
else
|
|
{
|
|
mprintf(("Enhanced sound is enabled.\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mprintf(("Enhanced sound is manually disabled.\n"));
|
|
}
|
|
|
|
// setup default listener position/orientation
|
|
// this is needed for 2D pan
|
|
OpenAL_ErrorPrint( alListener3f(AL_POSITION, 0.0, 0.0, 0.0) );
|
|
OpenAL_ErrorPrint( alListenerfv(AL_ORIENTATION, list_orien) );
|
|
|
|
// disable doppler (FIXME)
|
|
OpenAL_ErrorPrint( alDopplerFactor(0.0f) );
|
|
|
|
ds_init_channels();
|
|
ds_init_buffers();
|
|
|
|
mprintf(("\n"));
|
|
|
|
{
|
|
ALCint freq = 0;
|
|
OpenAL_ErrorPrint( alcGetIntegerv(ds_sound_device, ALC_FREQUENCY, sizeof(ALCint), &freq) );
|
|
|
|
mprintf((" Sample rate: %d (%d)\n", freq, sample_rate));
|
|
}
|
|
|
|
if (Ds_use_eax) {
|
|
ALCint major = 0, minor = 0, max_sends = 0;
|
|
|
|
alcGetIntegerv(ds_sound_device, ALC_EFX_MAJOR_VERSION, 1, &major);
|
|
alcGetIntegerv(ds_sound_device, ALC_EFX_MINOR_VERSION, 1, &minor);
|
|
alcGetIntegerv(ds_sound_device, ALC_MAX_AUXILIARY_SENDS, 1, &max_sends);
|
|
|
|
mprintf((" EFX version: %d.%d\n", (int)major, (int)minor));
|
|
mprintf((" Max auxiliary sends: %d\n", max_sends));
|
|
} else {
|
|
mprintf((" EFX enabled: NO\n"));
|
|
}
|
|
|
|
mprintf((" Playback device: %s\n", playback_device.c_str()));
|
|
mprintf((" Capture device: %s\n", (capture_device.empty()) ? "<not available>" : capture_device.c_str()));
|
|
|
|
mprintf(("... OpenAL successfully initialized!\n"));
|
|
|
|
// we need to clear out any errors before moving on
|
|
alcGetError(NULL);
|
|
alGetError();
|
|
|
|
return 0;
|
|
|
|
AL_InitError:
|
|
alcMakeContextCurrent(NULL);
|
|
|
|
if (ds_sound_context != NULL) {
|
|
alcDestroyContext(ds_sound_context);
|
|
ds_sound_context = NULL;
|
|
}
|
|
|
|
if (ds_sound_device != NULL) {
|
|
alcCloseDevice(ds_sound_device);
|
|
ds_sound_device = NULL;
|
|
}
|
|
|
|
mprintf(("... OpenAL failed to initialize!\n"));
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Free a single channel
|
|
*/
|
|
void ds_close_channel(int i)
|
|
{
|
|
if ( (i < 0) || (i >= MAX_CHANNELS) ) {
|
|
return;
|
|
}
|
|
|
|
if ( (Channels[i].source_id != 0) && alIsSource(Channels[i].source_id) ) {
|
|
OpenAL_ErrorPrint( alSourceStop(Channels[i].source_id) );
|
|
OpenAL_ErrorPrint( alSourcei(Channels[i].source_id, AL_BUFFER, 0) );
|
|
|
|
if (Ds_eax_inited) {
|
|
OpenAL_ErrorPrint( alSource3i(Channels[i].source_id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL) );
|
|
}
|
|
|
|
OpenAL_ErrorPrint( alDeleteSources(1, &Channels[i].source_id) );
|
|
|
|
if (Channels[i].sid >= 0) {
|
|
sound_buffers[Channels[i].sid].channel_id = -1;
|
|
}
|
|
|
|
Channels[i].source_id = 0;
|
|
Channels[i].sid = -1;
|
|
Channels[i].sig = ds_sound_handle::invalid();
|
|
Channels[i].snd_id = -1;
|
|
}
|
|
}
|
|
|
|
void ds_close_channel_fast(int i)
|
|
{
|
|
if ( (i < 0) || (i >= MAX_CHANNELS) ) {
|
|
return;
|
|
}
|
|
|
|
if ( (Channels[i].source_id != 0) && alIsSource(Channels[i].source_id) ) {
|
|
OpenAL_ErrorPrint( alSourceStop(Channels[i].source_id) );
|
|
OpenAL_ErrorPrint( alSourcei(Channels[i].source_id, AL_BUFFER, 0) );
|
|
|
|
if (Ds_eax_inited) {
|
|
OpenAL_ErrorPrint( alSource3i(Channels[i].source_id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL) );
|
|
}
|
|
|
|
if (Channels[i].sid >= 0) {
|
|
sound_buffers[Channels[i].sid].channel_id = -1;
|
|
}
|
|
|
|
Channels[i].sid = -1;
|
|
Channels[i].sig = ds_sound_handle::invalid();
|
|
Channels[i].snd_id = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Free all the channel buffers
|
|
*/
|
|
void ds_close_all_channels()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_CHANNELS; i++) {
|
|
ds_close_channel(i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unload a buffer
|
|
*/
|
|
void ds_unload_buffer(int sid)
|
|
{
|
|
if ( (sid < 0) || ((size_t)sid >= sound_buffers.size()) ) {
|
|
return;
|
|
}
|
|
|
|
if (sound_buffers[sid].channel_id >= 0) {
|
|
ds_close_channel_fast(sound_buffers[sid].channel_id);
|
|
sound_buffers[sid].channel_id = -1;
|
|
}
|
|
|
|
ALuint buf_id = sound_buffers[sid].buf_id;
|
|
|
|
if ( (buf_id != 0) && alIsBuffer(buf_id) ) {
|
|
OpenAL_ErrorCheck( alDeleteBuffers(1, &buf_id), return );
|
|
}
|
|
|
|
sound_buffers[sid].buf_id = 0;
|
|
}
|
|
|
|
/**
|
|
* Unload all the channel buffers
|
|
*/
|
|
void ds_close_buffers()
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < sound_buffers.size(); i++) {
|
|
ds_unload_buffer((int)i);
|
|
}
|
|
|
|
sound_buffers.clear();
|
|
}
|
|
|
|
/**
|
|
* Close the sound system
|
|
*/
|
|
void ds_close()
|
|
{
|
|
ds_close_all_channels();
|
|
ds_close_buffers();
|
|
ds_eax_close();
|
|
|
|
// free the Channels[] array, since it was dynamically allocated
|
|
delete [] Channels;
|
|
Channels = NULL;
|
|
|
|
alcMakeContextCurrent(NULL); // hangs on me for some reason
|
|
|
|
if (ds_sound_context != NULL) {
|
|
alcDestroyContext(ds_sound_context);
|
|
ds_sound_context = NULL;
|
|
}
|
|
|
|
if (ds_sound_device != NULL) {
|
|
alcCloseDevice(ds_sound_device);
|
|
ds_sound_device = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Find a free channel to play a sound on. If no free channels exists, free up one based on volume levels.
|
|
* This is the original retail version of ds_get_free_channel().
|
|
*
|
|
* @param new_volume Volume for sound to play at
|
|
* @param snd_id Which kind of sound to play
|
|
* @param priority ::DS_MUST_PLAY, ::DS_LIMIT_ONE, ::DS_LIMIT_TWO, ::DS_LIMIT_THREE
|
|
*
|
|
* @returns Channel number to play sound on, or -1 if no channel could be found
|
|
*
|
|
* NOTE: snd_id is needed since we limit the number of concurrent samples
|
|
*/
|
|
int ds_get_free_channel_retail(float new_volume, int snd_id, int priority)
|
|
{
|
|
int i, first_free_channel, limit = 100;
|
|
int instance_count; // number of instances of sound already playing
|
|
int lowest_vol_index = -1, lowest_instance_vol_index = -1;
|
|
float lowest_vol = 1.0f, lowest_instance_vol = 1.0f;
|
|
channel *chp;
|
|
int status;
|
|
|
|
instance_count = 0;
|
|
first_free_channel = -1;
|
|
|
|
// determine the limit of concurrent instances of this sound
|
|
switch (priority) {
|
|
case DS_MUST_PLAY:
|
|
limit = 100;
|
|
break;
|
|
|
|
case DS_LIMIT_ONE:
|
|
limit = 1;
|
|
break;
|
|
|
|
case DS_LIMIT_TWO:
|
|
limit = 2;
|
|
break;
|
|
|
|
case DS_LIMIT_THREE:
|
|
limit = 3;
|
|
break;
|
|
|
|
default:
|
|
Int3(); // get Alan
|
|
limit = 100;
|
|
break;
|
|
}
|
|
|
|
// Look for a channel to use to play this sample
|
|
for ( i = 0; i < MAX_CHANNELS; i++ ) {
|
|
chp = &Channels[i];
|
|
|
|
// source not created yet
|
|
if (chp->source_id == 0) {
|
|
if (first_free_channel == -1) {
|
|
first_free_channel = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// source not bound to a buffer
|
|
if (chp->sid == -1) {
|
|
if (first_free_channel == -1) {
|
|
first_free_channel = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
OpenAL_ErrorCheck( alGetSourcei(chp->source_id, AL_SOURCE_STATE, &status), goto continue_channels );
|
|
|
|
if ( (status == AL_INITIAL) || (status == AL_STOPPED) ) {
|
|
ds_close_channel_fast(i);
|
|
|
|
if (first_free_channel == -1) {
|
|
first_free_channel = i;
|
|
}
|
|
continue;
|
|
} else {
|
|
if ( chp->snd_id == snd_id ) {
|
|
instance_count++;
|
|
if ( (chp->vol < lowest_instance_vol) && (chp->looping == FALSE) ) {
|
|
lowest_instance_vol = chp->vol;
|
|
lowest_instance_vol_index = i;
|
|
}
|
|
} else if ( chp->is_voice_msg ) {
|
|
// a playing voice message is not allowed to be preempted.
|
|
} else if ( (chp->vol < lowest_vol) && (chp->looping == FALSE) ) {
|
|
lowest_vol_index = i;
|
|
lowest_vol = chp->vol;
|
|
}
|
|
}
|
|
continue_channels: ;
|
|
}
|
|
|
|
// If we've exceeded the limit, then maybe stop the duplicate if it is lower volume
|
|
if ( (instance_count >= limit) && (lowest_instance_vol_index >= 0) ) {
|
|
// If there is a lower volume duplicate, stop it.... otherwise, don't play the sound
|
|
if (lowest_instance_vol <= new_volume) {
|
|
ds_close_channel_fast(lowest_instance_vol_index);
|
|
first_free_channel = lowest_instance_vol_index;
|
|
} else {
|
|
// NOTE: yes we are preventing the sound from playing even if
|
|
// there is an available channel because we are over the limit
|
|
// requested by the rest of the engine, which means if we do
|
|
// not honour its request to limit the count then the engine
|
|
// will trip itself up by using all channels without having
|
|
// the intention of actually doing so. This means we get
|
|
// very loud sounds, missing more important sounds, etc.
|
|
// Effectivly the problem is the rest of the engine assumes
|
|
// it is still stuck in the 90s with a sound card that only has
|
|
// <=16 channels so we need to give it a sound card that
|
|
// has 16 channels (though we are actually allowing 32 channels
|
|
// just because we can).
|
|
first_free_channel = -1;
|
|
}
|
|
} else if (first_free_channel == -1) {
|
|
// there is no limit barrier to play the sound, but we have run out of channels
|
|
// stop the lowest volume instance to play our sound if priority demands it
|
|
if ( (lowest_vol_index != -1) && (priority == DS_MUST_PLAY) ) {
|
|
// Check if the lowest volume playing is less than the volume of the requested sound.
|
|
// If so, then we are going to trash the lowest volume sound.
|
|
if ( Channels[lowest_vol_index].vol <= new_volume ) {
|
|
ds_close_channel_fast(lowest_vol_index);
|
|
first_free_channel = lowest_vol_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (first_free_channel >= 0) && (Channels[first_free_channel].source_id == 0) ) {
|
|
OpenAL_ErrorCheck( alGenSources(1, &Channels[first_free_channel].source_id), return -1 );
|
|
}
|
|
return first_free_channel;
|
|
}
|
|
|
|
/**
|
|
* Find a free channel to play a sound on. If no free channels exists, free up one based on priority and volume levels.
|
|
* Special version for enhanced mode.
|
|
*
|
|
* @param new_volume Volume for sound to play at
|
|
* @param snd_id Which kind of sound to play
|
|
* @param enhanced_priority Priority level, see EnhancedSoundPriority enum in gamesnd.h
|
|
* @param enhanced_limit Per-sound concurrency limit
|
|
*
|
|
* @returns Channel number to play sound on, or -1 if no channel could be found
|
|
*
|
|
* NOTE: snd_id is needed since we limit the number of concurrent samples
|
|
*/
|
|
int ds_get_free_channel_enhanced(float new_volume, int snd_id, int enhanced_priority, unsigned int enhanced_limit)
|
|
{
|
|
int i, first_free_channel;
|
|
int instance_count; // number of instances of sound already playing
|
|
// least important means lowest volume among lowest priority
|
|
// note that higher priority value means lower priority
|
|
int least_important_index = -1, least_important_instance_index = -1;
|
|
float least_important_vol = 1.1f, least_important_instance_vol = 1.1f;
|
|
int least_important_priority = -1, least_important_instance_priority = -1;
|
|
channel *chp;
|
|
int status;
|
|
|
|
instance_count = 0;
|
|
first_free_channel = -1;
|
|
|
|
// Look for a channel to use to play this sample
|
|
for ( i = 0; i < MAX_CHANNELS; i++ ) {
|
|
chp = &Channels[i];
|
|
|
|
// source not created yet
|
|
if (chp->source_id == 0) {
|
|
if (first_free_channel == -1) {
|
|
first_free_channel = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// source not bound to a buffer
|
|
if (chp->sid == -1) {
|
|
if (first_free_channel == -1) {
|
|
first_free_channel = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
OpenAL_ErrorCheck( alGetSourcei(chp->source_id, AL_SOURCE_STATE, &status), goto continue_channels );
|
|
|
|
if ( (status == AL_INITIAL) || (status == AL_STOPPED) ) {
|
|
ds_close_channel_fast(i);
|
|
|
|
if (first_free_channel == -1) {
|
|
first_free_channel = i;
|
|
}
|
|
continue;
|
|
} else {
|
|
if ( chp->snd_id == snd_id ) {
|
|
instance_count++;
|
|
|
|
// looping or ambient soudns can't be preempted
|
|
if ((chp->looping == FALSE) && !chp->is_ambient)
|
|
{
|
|
if (chp->priority > least_important_instance_priority) {
|
|
least_important_instance_vol = chp->vol;
|
|
least_important_instance_priority = chp->priority;
|
|
least_important_instance_index = i;
|
|
} else if ((chp->priority == least_important_instance_priority)
|
|
&& (chp->vol < least_important_instance_vol)) {
|
|
least_important_instance_vol = chp->vol;
|
|
least_important_instance_priority = chp->priority;
|
|
least_important_instance_index = i;
|
|
}
|
|
}
|
|
} else if ( chp->is_voice_msg || (chp->looping == TRUE) || chp->is_ambient ) {
|
|
// a playing voice message, looping sound, or ambient sound is not allowed to be preempted.
|
|
} else if ( (chp->priority > least_important_priority) ) {
|
|
least_important_index = i;
|
|
least_important_vol = chp->vol;
|
|
least_important_priority = chp->priority;
|
|
} else if ( (chp->priority == least_important_priority) && (chp->vol < least_important_vol) ) {
|
|
least_important_index = i;
|
|
least_important_vol = chp->vol;
|
|
least_important_priority = chp->priority;
|
|
}
|
|
}
|
|
continue_channels: ;
|
|
}
|
|
|
|
// If we've exceeded the limit, then stop the least important duplicate if it is lower or equal volume
|
|
// otherwise, don't play the sound
|
|
if ( ((unsigned int)instance_count >= enhanced_limit) && (least_important_instance_index >= 0) ) {
|
|
if (least_important_instance_vol <= new_volume) {
|
|
ds_close_channel_fast(least_important_instance_index);
|
|
first_free_channel =least_important_instance_index;
|
|
} else {
|
|
// don't exceed per-sound concurrency limits, even if there are spare channels
|
|
first_free_channel = -1;
|
|
}
|
|
} else if (first_free_channel == -1) {
|
|
// we haven't reached the limit, but we have run out of channels
|
|
// attempt to stop the least important sound to play our sound if priority demands it
|
|
if ( (least_important_index != -1)) {
|
|
// Check if the priority difference is great enough (must play or at least 2 levels away)
|
|
// and if the least important sound's volume playing is less than or equal to the volume of the requested sound.
|
|
// If so, then we are going to trash the least important sound.
|
|
if ( (enhanced_priority == SND_ENHANCED_PRIORITY_MUST_PLAY) || (Channels[least_important_index].priority - enhanced_priority >= 2)) {
|
|
if ( Channels[least_important_index].vol <= new_volume ) {
|
|
ds_close_channel_fast(least_important_index);
|
|
first_free_channel = least_important_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (first_free_channel >= 0) && (Channels[first_free_channel].source_id == 0) ) {
|
|
OpenAL_ErrorCheck( alGenSources(1, &Channels[first_free_channel].source_id), return -1 );
|
|
}
|
|
return first_free_channel;
|
|
}
|
|
|
|
/**
|
|
* Generic function for getting a free channel
|
|
* If enhanced soudn is used, set priority to the computed priority.
|
|
* Returns -1 if no free channel could be found.
|
|
*/
|
|
|
|
/**
|
|
* Find a free channel to play a sound on. If no free channels exists, free up one based on volume levels.
|
|
* This is the new generic version of ds_get_free_channel().
|
|
*
|
|
* @param new_volume Volume for sound to play at
|
|
* @param snd_id Which kind of sound to play
|
|
* @param priority From retail :DS_MUST_PLAY, ::DS_LIMIT_ONE, ::DS_LIMIT_TWO, ::DS_LIMIT_THREE
|
|
* @param enhanced_priority Output param that's updated with correct priority if enhanced sound is enabled
|
|
*
|
|
* @returns Channel number to play sound on, or -1 if no channel could be found
|
|
*
|
|
* NOTE: snd_id is needed since we limit the number of concurrent samples
|
|
*/
|
|
int ds_get_free_channel(float volume, int snd_id, int priority, int & enhanced_priority, const EnhancedSoundData & enhanced_sound_data)
|
|
{
|
|
int first_free_channel = -1;
|
|
unsigned int enhanced_limit = 0;
|
|
|
|
if (!Cmdline_no_enhanced_sound) {
|
|
enhanced_priority = enhanced_sound_data.priority;
|
|
enhanced_limit = enhanced_sound_data.limit;
|
|
|
|
// exception: if retail priority is must play, we assume it's for a good reason
|
|
// and thus follow suit with enhanced sound
|
|
if (priority == DS_MUST_PLAY) {
|
|
enhanced_priority = SND_ENHANCED_PRIORITY_MUST_PLAY;
|
|
}
|
|
|
|
first_free_channel = ds_get_free_channel_enhanced(volume, snd_id, enhanced_priority, enhanced_limit);
|
|
} else { // enhanced sound is off
|
|
first_free_channel = ds_get_free_channel_retail(volume, snd_id, priority);
|
|
}
|
|
|
|
return first_free_channel;
|
|
}
|
|
|
|
/**
|
|
* Create a sound buffer in software, without locking any data in
|
|
*/
|
|
int ds_create_buffer(int frequency, int bits_per_sample, int nchannels, int nseconds)
|
|
{
|
|
ALuint i;
|
|
int sid;
|
|
|
|
if (!ds_initialized) {
|
|
return -1;
|
|
}
|
|
|
|
sid = ds_get_sid();
|
|
if ( sid == -1 ) {
|
|
nprintf(("Sound","SOUND ==> No more OpenAL buffers available\n"));
|
|
return -1;
|
|
}
|
|
|
|
OpenAL_ErrorCheck( alGenBuffers(1, &i), return -1 );
|
|
|
|
sound_buffers[sid].buf_id = i;
|
|
sound_buffers[sid].channel_id = -1;
|
|
sound_buffers[sid].frequency = frequency;
|
|
sound_buffers[sid].bits_per_sample = bits_per_sample;
|
|
sound_buffers[sid].nchannels = nchannels;
|
|
sound_buffers[sid].nseconds = nseconds;
|
|
sound_buffers[sid].nbytes = nseconds * (bits_per_sample / 8) * nchannels * frequency;
|
|
|
|
return sid;
|
|
}
|
|
|
|
/**
|
|
* Lock data into an existing buffer
|
|
*/
|
|
int ds_lock_data(int sid, unsigned char *data, int size)
|
|
{
|
|
if ( (sid < 0) || ((size_t)sid >= sound_buffers.size()) ) {
|
|
return -1;
|
|
}
|
|
|
|
ALenum format = openal_get_format(sound_buffers[sid].bits_per_sample, sound_buffers[sid].nchannels);
|
|
|
|
if (format == AL_INVALID_VALUE) {
|
|
return -1;
|
|
}
|
|
|
|
sound_buffers[sid].nbytes = size;
|
|
|
|
OpenAL_ErrorCheck( alBufferData(sound_buffers[sid].buf_id, format, data, size, sound_buffers[sid].frequency), return -1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Stop a buffer from playing directly
|
|
*/
|
|
void ds_stop_easy(int sid)
|
|
{
|
|
Assert(sid >= 0);
|
|
|
|
int cid = sound_buffers[sid].channel_id;
|
|
|
|
if (cid != -1) {
|
|
ALuint source_id = Channels[cid].source_id;
|
|
OpenAL_ErrorPrint( alSourceStop(source_id) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Play a sound secondary buffer.
|
|
*
|
|
* @param sid Software id of sound
|
|
* @param snd_id What kind of sound this is
|
|
* @param priority ::DS_MUST_PLAY, ::DS_LIMIT_ONE, ::DS_LIMIT_TWO, ::DS_LIMIT_THREE
|
|
* @param volume Volume of sound effect in DirectSound units
|
|
* @param pan Pan of sound in sound units
|
|
* @param looping Whether the sound effect is looping or not
|
|
* @param is_voice_msg If a voice message
|
|
*
|
|
* @return 1 if sound effect could not be started, >=0 sig for sound effect successfully started
|
|
*/
|
|
ds_sound_handle ds_play(int sid, int snd_id, int priority, const EnhancedSoundData* enhanced_sound_data, float volume,
|
|
float pan, int looping, bool is_voice_msg)
|
|
{
|
|
int ch_idx;
|
|
int enhanced_priority = SND_ENHANCED_PRIORITY_INVALID;
|
|
|
|
if (!ds_initialized) {
|
|
return ds_sound_handle::invalid();
|
|
}
|
|
|
|
ch_idx = ds_get_free_channel(volume, snd_id, priority, enhanced_priority, *enhanced_sound_data);
|
|
|
|
if (ch_idx < 0) {
|
|
return ds_sound_handle::invalid();
|
|
}
|
|
|
|
if (Channels[ch_idx].source_id == 0) {
|
|
return ds_sound_handle::invalid();
|
|
}
|
|
|
|
if (pan) {
|
|
OpenAL_ErrorPrint( alSource3f(Channels[ch_idx].source_id, AL_POSITION, pan, 0.0f, -1.0f) );
|
|
} else {
|
|
OpenAL_ErrorPrint( alSource3f(Channels[ch_idx].source_id, AL_POSITION, 0.0f, 0.0f, 0.0f) );
|
|
}
|
|
|
|
OpenAL_ErrorPrint( alSource3f(Channels[ch_idx].source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f) );
|
|
|
|
OpenAL_ErrorPrint( alDopplerFactor(0.0f) );
|
|
OpenAL_ErrorPrint( alSourcef(Channels[ch_idx].source_id, AL_PITCH, 1.0f) );
|
|
OpenAL_ErrorPrint( alSourcef(Channels[ch_idx].source_id, AL_GAIN, volume) );
|
|
|
|
|
|
ALint status;
|
|
OpenAL_ErrorCheck(alGetSourcei(Channels[ch_idx].source_id, AL_SOURCE_STATE, &status),
|
|
return ds_sound_handle::invalid());
|
|
|
|
if (status == AL_PLAYING) {
|
|
OpenAL_ErrorPrint( alSourceStop(Channels[ch_idx].source_id) );
|
|
}
|
|
|
|
OpenAL_ErrorCheck(alSourcei(Channels[ch_idx].source_id, AL_BUFFER, sound_buffers[sid].buf_id),
|
|
return ds_sound_handle::invalid());
|
|
|
|
OpenAL_ErrorPrint( alSourcei(Channels[ch_idx].source_id, AL_SOURCE_RELATIVE, AL_TRUE) );
|
|
|
|
OpenAL_ErrorPrint( alSourcei(Channels[ch_idx].source_id, AL_LOOPING, (looping) ? AL_TRUE : AL_FALSE) );
|
|
|
|
if (Ds_eax_inited) {
|
|
OpenAL_ErrorPrint( alSource3i(Channels[ch_idx].source_id, AL_AUXILIARY_SEND_FILTER,
|
|
is_voice_msg ? AL_EFFECTSLOT_NULL : AL_EFX_aux_id, 0, AL_FILTER_NULL) );
|
|
}
|
|
|
|
OpenAL_ErrorPrint( alSourcePlay(Channels[ch_idx].source_id) );
|
|
|
|
sound_buffers[sid].channel_id = ch_idx;
|
|
|
|
Channels[ch_idx].sid = sid;
|
|
Channels[ch_idx].snd_id = snd_id;
|
|
Channels[ch_idx].sig = ds_sound_handle(channel_next_sig++);
|
|
Channels[ch_idx].last_position = 0;
|
|
Channels[ch_idx].is_voice_msg = is_voice_msg;
|
|
Channels[ch_idx].vol = volume;
|
|
Channels[ch_idx].looping = looping;
|
|
Channels[ch_idx].priority = enhanced_priority;
|
|
Channels[ch_idx].is_ambient = false; // no support for 2D ambient sounds
|
|
|
|
if (channel_next_sig < 0) {
|
|
channel_next_sig = 1;
|
|
}
|
|
|
|
return Channels[ch_idx].sig;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the channel number that is playing the sound identified by sig.
|
|
* @return Channel number, if not playing, return -1.
|
|
*/
|
|
int ds_get_channel(ds_sound_handle sig)
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < MAX_CHANNELS; i++ ) {
|
|
if ( Channels[i].source_id && (Channels[i].sig == sig) ) {
|
|
if ( ds_is_channel_playing(i) == TRUE ) {
|
|
return i;
|
|
}
|
|
if (ds_is_channel_paused(i) == TRUE) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Return the channel number that contains the sound identified by sig.
|
|
* This differs from ds_get_channel() in that it does not check that the sound is currently playing.
|
|
* @return Channel number, if not playing, return -1.
|
|
*/
|
|
int ds_get_channel_raw(ds_sound_handle sig)
|
|
{
|
|
for (int i = 0; i < MAX_CHANNELS; i++) {
|
|
if (Channels[i].source_id && (Channels[i].sig == sig)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
int ds_is_channel_playing(int channel_id)
|
|
{
|
|
if ( Channels[channel_id].source_id != 0 ) {
|
|
ALint status;
|
|
|
|
OpenAL_ErrorPrint( alGetSourcei(Channels[channel_id].source_id, AL_SOURCE_STATE, &status) );
|
|
|
|
return (status == AL_PLAYING);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns if a channel is paused or not.
|
|
* @return Channel number, if not playing, return -1.
|
|
* @param channel id to sound, what is returned from ds_get_channel()
|
|
*/
|
|
bool ds_is_channel_paused(int channel_id)
|
|
{
|
|
if (Channels[channel_id].source_id != 0) {
|
|
ALint status;
|
|
|
|
OpenAL_ErrorPrint(alGetSourcei(Channels[channel_id].source_id, AL_SOURCE_STATE, &status));
|
|
|
|
return (status == AL_PAUSED);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
void ds_stop_channel(int channel_id)
|
|
{
|
|
if ( Channels[channel_id].source_id != 0 ) {
|
|
OpenAL_ErrorPrint( alSourceStop(Channels[channel_id].source_id) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pauses a channel.
|
|
* @param channel id to sound, what is returned from ds_get_channel()
|
|
*/
|
|
void ds_pause_channel(int channel_id)
|
|
{
|
|
if (Channels[channel_id].source_id != 0) {
|
|
OpenAL_ErrorPrint(alSourcePause(Channels[channel_id].source_id));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resumes playing a channel.
|
|
* @param channel id to sound, what is returned from ds_get_channel()
|
|
*/
|
|
void ds_resume_channel(int channel_id)
|
|
{
|
|
if (Channels[channel_id].source_id != 0) {
|
|
OpenAL_ErrorPrint(alSourcePlay(Channels[channel_id].source_id));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
void ds_stop_channel_all()
|
|
{
|
|
int i;
|
|
|
|
for ( i=0; i<MAX_CHANNELS; i++ ) {
|
|
if ( Channels[i].source_id != 0 ) {
|
|
OpenAL_ErrorPrint( alSourceStop(Channels[i].source_id) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Set the volume for a channel. The volume is expected to be in linear scale
|
|
* @details If the sound is a 3D sound buffer, this is like re-establishing the maximum volume.
|
|
*/
|
|
void ds_set_volume( int channel_id, float vol )
|
|
{
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return;
|
|
}
|
|
|
|
ALuint source_id = Channels[channel_id].source_id;
|
|
|
|
if (source_id != 0) {
|
|
CAP(vol, 0.0f, 1.0f);
|
|
OpenAL_ErrorPrint( alSourcef(source_id, AL_GAIN, vol) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the pan for a channel. The pan is expected to be in DirectSound units
|
|
*/
|
|
void ds_set_pan( int channel_id, float pan )
|
|
{
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return;
|
|
}
|
|
|
|
ALint state;
|
|
|
|
OpenAL_ErrorCheck( alGetSourcei(Channels[channel_id].source_id, AL_SOURCE_STATE, &state), return );
|
|
|
|
if (state == AL_PLAYING) {
|
|
//OpenAL_ErrorPrint( alSourcei(Channels[channel_id].source_id, AL_SOURCE_RELATIVE, AL_TRUE) );
|
|
OpenAL_ErrorPrint( alSource3f(Channels[channel_id].source_id, AL_POSITION, pan, 0.0f, -1.0f) );
|
|
}
|
|
}
|
|
|
|
float ds_get_pitch(int channel_id)
|
|
{
|
|
ALint status;
|
|
ALfloat alpitch = 1.0;
|
|
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return -1;
|
|
}
|
|
|
|
OpenAL_ErrorCheck( alGetSourcei(Channels[channel_id].source_id, AL_SOURCE_STATE, &status), return -1 );
|
|
|
|
if (status == AL_PLAYING) {
|
|
OpenAL_ErrorPrint( alGetSourcef(Channels[channel_id].source_id, AL_PITCH, &alpitch) );
|
|
}
|
|
|
|
return alpitch;
|
|
}
|
|
|
|
void ds_set_pitch(int channel_id, float pitch)
|
|
{
|
|
Assertion(pitch > 0.0f, "Pitch may not be less than zero!");
|
|
|
|
ALint status;
|
|
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return;
|
|
}
|
|
|
|
OpenAL_ErrorCheck( alGetSourcei(Channels[channel_id].source_id, AL_SOURCE_STATE, &status), return );
|
|
|
|
if (status == AL_PLAYING) {
|
|
OpenAL_ErrorPrint( alSourcef(Channels[channel_id].source_id, AL_PITCH, pitch) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts a ds3d sound playing
|
|
*
|
|
* @param sid Software id for sound to play
|
|
* @param snd_id Identifies what type of sound is playing
|
|
* @param pos World pos of sound
|
|
* @param vel Velocity of object emitting sound
|
|
* @param min Distance at which sound doesn't get any louder
|
|
* @param max Distance at which sound becomes inaudible
|
|
* @param looping Whether to loop the sound or not
|
|
* @param max_volume Volume (0 to 1) for 3d sound at maximum
|
|
* @param estimated_vol Manual estimated volume
|
|
* @param priority ::DS_MUST_PLAY, ::DS_LIMIT_ONE, ::DS_LIMIT_TWO, ::DS_LIMIT_THREE
|
|
*
|
|
* @return 0 if sound started successfully, -1 if sound could not be played
|
|
*/
|
|
ds_sound_handle ds3d_play(int sid, int snd_id, const vec3d* pos, const vec3d* vel, float min, float max, bool looping,
|
|
float max_volume, float estimated_vol, const EnhancedSoundData* enhanced_sound_data,
|
|
int priority, bool is_ambient)
|
|
{
|
|
int channel_id;
|
|
int enhanced_priority = SND_ENHANCED_PRIORITY_INVALID;
|
|
|
|
|
|
if (!ds_initialized) {
|
|
return ds_sound_handle::invalid();
|
|
}
|
|
|
|
channel_id = ds_get_free_channel(estimated_vol, snd_id, priority, enhanced_priority, *enhanced_sound_data);
|
|
|
|
if (channel_id < 0) {
|
|
return ds_sound_handle::invalid();
|
|
}
|
|
|
|
if ( Channels[channel_id].source_id == 0 ) {
|
|
return ds_sound_handle::invalid();
|
|
}
|
|
|
|
// set up 3D sound data here
|
|
ds3d_update_buffer(channel_id, min, max, pos, vel);
|
|
|
|
|
|
OpenAL_ErrorPrint( alSourcef(Channels[channel_id].source_id, AL_PITCH, 1.0f) );
|
|
|
|
OpenAL_ErrorPrint( alSourcef(Channels[channel_id].source_id, AL_GAIN, max_volume) );
|
|
|
|
ALint status;
|
|
OpenAL_ErrorCheck(alGetSourcei(Channels[channel_id].source_id, AL_SOURCE_STATE, &status),
|
|
return ds_sound_handle::invalid());
|
|
|
|
if (status == AL_PLAYING) {
|
|
OpenAL_ErrorPrint( alSourceStop(Channels[channel_id].source_id) );
|
|
}
|
|
|
|
OpenAL_ErrorCheck(alSourcei(Channels[channel_id].source_id, AL_BUFFER, sound_buffers[sid].buf_id),
|
|
return ds_sound_handle::invalid());
|
|
|
|
if (Ds_eax_inited) {
|
|
OpenAL_ErrorPrint( alSource3i(Channels[channel_id].source_id, AL_AUXILIARY_SEND_FILTER, AL_EFX_aux_id, 0, AL_FILTER_NULL) );
|
|
}
|
|
|
|
OpenAL_ErrorPrint( alSourcei(Channels[channel_id].source_id, AL_SOURCE_RELATIVE, AL_FALSE) );
|
|
|
|
OpenAL_ErrorPrint( alSourcei(Channels[channel_id].source_id, AL_LOOPING, (looping) ? AL_TRUE : AL_FALSE) );
|
|
|
|
OpenAL_ErrorPrint( alSourcePlay(Channels[channel_id].source_id) );
|
|
|
|
|
|
sound_buffers[sid].channel_id = channel_id;
|
|
|
|
Channels[channel_id].sid = sid;
|
|
Channels[channel_id].snd_id = snd_id;
|
|
Channels[channel_id].sig = ds_sound_handle(channel_next_sig++);
|
|
Channels[channel_id].last_position = 0;
|
|
Channels[channel_id].is_voice_msg = false;
|
|
Channels[channel_id].vol = max_volume;
|
|
Channels[channel_id].looping = looping;
|
|
Channels[channel_id].priority = enhanced_priority;
|
|
Channels[channel_id].is_ambient = is_ambient;
|
|
|
|
if (channel_next_sig < 0 ) {
|
|
channel_next_sig = 1;
|
|
}
|
|
|
|
return Channels[channel_id].sig;
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
void ds_set_position(int channel_id, unsigned int offset)
|
|
{
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return;
|
|
}
|
|
|
|
OpenAL_ErrorPrint( alSourcei(Channels[channel_id].source_id, AL_BYTE_OFFSET, offset) );
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
unsigned int ds_get_play_position(int channel_id)
|
|
{
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return 0;
|
|
}
|
|
|
|
ALint pos = -1;
|
|
int sid = Channels[channel_id].sid;
|
|
|
|
if ( (sid < 0) || ((size_t)sid >= sound_buffers.size()) ) {
|
|
return 0;
|
|
}
|
|
|
|
if (AL_play_position) {
|
|
OpenAL_ErrorPrint( alGetSourcei(Channels[channel_id].source_id, AL_BYTE_LOKI, &pos) );
|
|
|
|
if ( pos < 0 ) {
|
|
pos = 0;
|
|
} else if ( pos > 0 ) {
|
|
// AL_BYTE_LOKI returns position in canon format which may differ
|
|
// from our sample, so we may have to scale it
|
|
ALuint buf_id = sound_buffers[sid].buf_id;
|
|
ALint size;
|
|
|
|
OpenAL_ErrorCheck( alGetBufferi(buf_id, AL_SIZE, &size), return 0 );
|
|
|
|
pos = (ALint)(pos * ((float)sound_buffers[sid].nbytes / size));
|
|
}
|
|
} else {
|
|
OpenAL_ErrorPrint( alGetSourcei(Channels[channel_id].source_id, AL_BYTE_OFFSET, &pos) );
|
|
|
|
if (pos < 0) {
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
return (unsigned int) pos;
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
int ds_get_channel_size(int channel_id)
|
|
{
|
|
if ( (channel_id < 0) || (channel_id >= MAX_CHANNELS) ) {
|
|
return 0;
|
|
}
|
|
|
|
int sid = Channels[channel_id].sid;
|
|
|
|
if ( (sid < 0) || ((size_t)sid >= sound_buffers.size()) ) {
|
|
return 0;
|
|
}
|
|
|
|
ALuint buf_id = sound_buffers[sid].buf_id;
|
|
ALint data_size = 0;
|
|
|
|
if ( (buf_id != 0) && alIsBuffer(buf_id)) {
|
|
OpenAL_ErrorPrint( alGetBufferi(buf_id, AL_SIZE, &data_size) );
|
|
}
|
|
|
|
return (int) data_size;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of channels that are actually playing
|
|
*/
|
|
int ds_get_number_channels()
|
|
{
|
|
int i,n;
|
|
|
|
if (!ds_initialized) {
|
|
return 0;
|
|
}
|
|
|
|
n = 0;
|
|
for ( i = 0; i < MAX_CHANNELS; i++ ) {
|
|
if ( Channels[i].source_id ) {
|
|
if ( ds_is_channel_playing(i) == TRUE ) {
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Retreive raw data from a sound buffer
|
|
*/
|
|
int ds_get_data(int /*sid*/, char * /*data*/)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Return the size of the raw sound data
|
|
*/
|
|
int ds_get_size(int sid, int *size)
|
|
{
|
|
Assert(sid >= 0);
|
|
|
|
if ( (sid < 0) || ((size_t)sid >= sound_buffers.size()) ) {
|
|
return 0;
|
|
}
|
|
|
|
ALuint buf_id = sound_buffers[sid].buf_id;
|
|
ALint data_size = 0;
|
|
|
|
if ( (buf_id != 0) && alIsBuffer(buf_id)) {
|
|
OpenAL_ErrorPrint( alGetBufferi(buf_id, AL_SIZE, &data_size) );
|
|
|
|
if (size) {
|
|
*size = (int) data_size;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// --------------------
|
|
//
|
|
// EAX Functions below
|
|
//
|
|
// --------------------
|
|
|
|
/**
|
|
* Set the master volume for the reverb added to all sound sources.
|
|
*
|
|
* @param volume Volume, range from 0 to 1.0
|
|
* @returns 0 if the volume is set successfully, otherwise return -1
|
|
*/
|
|
int ds_eax_set_volume(float volume)
|
|
{
|
|
if ( !Ds_eax_inited ) {
|
|
return -1;
|
|
}
|
|
|
|
CAP(volume, 0.0f, 1.0f);
|
|
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_GAIN, volume) );
|
|
|
|
OpenAL_ErrorCheck( v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFX_effect_id), return -1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set the decay time for the EAX environment (ie all sound sources)
|
|
*
|
|
* @param seconds Decay time in seconds
|
|
* @return 0 if decay time is successfully set, otherwise return -1
|
|
*/
|
|
int ds_eax_set_decay_time(float seconds)
|
|
{
|
|
if ( !Ds_eax_inited ) {
|
|
return -1;
|
|
}
|
|
|
|
CAP(seconds, 0.1f, 20.0f);
|
|
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_TIME, seconds) );
|
|
|
|
OpenAL_ErrorCheck( v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFX_effect_id), return -1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set the damping value for the EAX environment (ie all sound sources)
|
|
*
|
|
* @param damp Damp value from 0 to 2.0
|
|
* @return 0 if the damp value is successfully set, otherwise return -1
|
|
*/
|
|
int ds_eax_set_damping(float damp)
|
|
{
|
|
if ( !Ds_eax_inited ) {
|
|
return -1;
|
|
}
|
|
|
|
CAP(damp, 0.1f, 2.0f);
|
|
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_HFRATIO, damp) );
|
|
|
|
OpenAL_ErrorCheck( v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFX_effect_id), return -1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set up all the parameters for an environment
|
|
*
|
|
* @param id Value from the EAX_ENVIRONMENT_* enumeration
|
|
* @param vol Volume for the environment (0 to 1.0)
|
|
* @param damping Damp value for the environment (0 to 2.0)
|
|
* @param decay Decay time in seconds (0.1 to 20.0)
|
|
* @return 0 if successful, otherwise return -1
|
|
*/
|
|
int ds_eax_set_all(unsigned long id, float vol, float damping, float decay)
|
|
{
|
|
if ( !Ds_eax_inited ) {
|
|
return -1;
|
|
}
|
|
|
|
// special disabled case
|
|
if ( (id == EAX_ENVIRONMENT_GENERIC) && (vol == 0.0f) && (damping == 0.0f) && (decay == 0.0f) ) {
|
|
v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
|
|
Ds_active_env_idx = 0;
|
|
Ds_active_env = false;
|
|
return 0;
|
|
}
|
|
|
|
al_efx_load_preset(id);
|
|
|
|
CAP(vol, 0.0f, 1.0f);
|
|
CAP(decay, 0.1f, 20.0f);
|
|
CAP(damping, 0.1f, 2.0f);
|
|
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_GAIN, vol) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_TIME, decay) );
|
|
OpenAL_ErrorPrint( v_alEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_HFRATIO, damping) );
|
|
|
|
OpenAL_ErrorCheck( v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFX_effect_id), return -1 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ds_eax_get_preset_id(const char *name)
|
|
{
|
|
if ( !name || !strlen(name) ) {
|
|
return -1;
|
|
}
|
|
|
|
size_t count = EFX_presets.size();
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
if ( !stricmp(name, EFX_presets[i].name.c_str()) ) {
|
|
return (int)i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int ds_eax_get_prop(EFXREVERBPROPERTIES **props, const char *name, const char *template_name)
|
|
{
|
|
Assert( props != NULL );
|
|
Assert( name != NULL );
|
|
Assert( strlen(name) > 0 ); //-V805
|
|
|
|
int template_id = -1;
|
|
|
|
int id = ds_eax_get_preset_id(name);
|
|
|
|
if (id >= 0) {
|
|
*props = &EFX_presets[id];
|
|
} else {
|
|
id = (int)EFX_presets.size();
|
|
|
|
EFXREVERBPROPERTIES n_prop;
|
|
|
|
if ( (template_name != NULL) && (template_name[0] != '\0') ) {
|
|
template_id = ds_eax_get_preset_id(template_name);
|
|
}
|
|
|
|
if (template_id >= 0) {
|
|
n_prop = EFX_presets[template_id];
|
|
n_prop.name = name;
|
|
} else {
|
|
n_prop.name = name;
|
|
n_prop.flDensity = 1.0f;
|
|
n_prop.flDiffusion = 1.0f;
|
|
n_prop.flGain = 0.32f;
|
|
n_prop.flGainHF = 0.89f;
|
|
n_prop.flGainLF = 0.0f;
|
|
n_prop.flDecayTime = 1.49f;
|
|
n_prop.flDecayHFRatio = 0.83f;
|
|
n_prop.flDecayLFRatio = 1.0f;
|
|
n_prop.flReflectionsGain = 0.05f;
|
|
n_prop.flReflectionsDelay = 0.007f;
|
|
n_prop.flReflectionsPan[0] = 0.0f;
|
|
n_prop.flReflectionsPan[1] = 0.0f;
|
|
n_prop.flReflectionsPan[2] = 0.0f;
|
|
n_prop.flLateReverbGain = 1.26f;
|
|
n_prop.flLateReverbDelay = 0.011f;
|
|
n_prop.flLateReverbPan[0] = 0.0f;
|
|
n_prop.flLateReverbPan[1] = 0.0f;
|
|
n_prop.flLateReverbPan[2] = 0.0f;
|
|
n_prop.flEchoTime = 0.25f;
|
|
n_prop.flEchoDepth = 0.0f;
|
|
n_prop.flModulationTime = 0.25f;
|
|
n_prop.flModulationDepth = 0.0f;
|
|
n_prop.flAirAbsorptionGainHF = 0.994f;
|
|
n_prop.flHFReference = 5000.0f;
|
|
n_prop.flLFReference = 250.0f;
|
|
n_prop.flRoomRolloffFactor = 0.0f;
|
|
n_prop.iDecayHFLimit = AL_TRUE;
|
|
}
|
|
|
|
EFX_presets.push_back( std::move(n_prop) );
|
|
|
|
*props = &EFX_presets[id];
|
|
}
|
|
|
|
if ( !stricmp(name, "default") ) {
|
|
extern unsigned int SND_ENV_DEFAULT;
|
|
SND_ENV_DEFAULT = id;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get up the parameters for the current environment
|
|
*
|
|
* @param er (output) Hold environment parameters
|
|
* @param id If set will get specified preset env, otherwise current env
|
|
* @return 0 if successful, otherwise return -1
|
|
*/
|
|
int ds_eax_get_all(EAX_REVERBPROPERTIES *er, int id)
|
|
{
|
|
if ( !er ) {
|
|
return -1;
|
|
}
|
|
|
|
if (id < 0) {
|
|
if (!Ds_active_env) {
|
|
return -1;
|
|
}
|
|
|
|
er->environment = Ds_active_env_idx;
|
|
|
|
OpenAL_ErrorPrint( v_alGetEffectf(AL_EFX_effect_id, AL_EAXREVERB_GAIN, &er->fVolume) );
|
|
OpenAL_ErrorPrint( v_alGetEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_TIME, &er->fDecayTime_sec) );
|
|
OpenAL_ErrorPrint( v_alGetEffectf(AL_EFX_effect_id, AL_EAXREVERB_DECAY_HFRATIO, &er->fDamping) );
|
|
} else if (id < (int)EFX_presets.size()) {
|
|
er->environment = (size_t)id;
|
|
|
|
er->fVolume = EFX_presets[id].flGain;
|
|
er->fDecayTime_sec = EFX_presets[id].flDecayTime;
|
|
er->fDamping = EFX_presets[id].flDecayHFRatio;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Close down EAX, freeing any allocated resources
|
|
*/
|
|
void ds_eax_close()
|
|
{
|
|
if (Ds_eax_inited == 0) {
|
|
return;
|
|
}
|
|
|
|
v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
|
|
|
|
v_alDeleteEffects(1, &AL_EFX_effect_id);
|
|
AL_EFX_effect_id = 0;
|
|
|
|
v_alDeleteAuxiliaryEffectSlots(1, &AL_EFX_aux_id);
|
|
AL_EFX_aux_id = 0;
|
|
|
|
Ds_eax_inited = 0;
|
|
}
|
|
|
|
/**
|
|
* Initialize EAX
|
|
* @return 0 if initialization is successful, otherwise return -1
|
|
*/
|
|
int ds_eax_init()
|
|
{
|
|
if (Ds_eax_inited) {
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
v_alGenFilters = (ALGENFILTERS) al_load_function("alGenFilters");
|
|
v_alDeleteFilters = (ALDELETEFILTERS) al_load_function("alDeleteFilters");
|
|
v_alFilteri = (ALFILTERI) al_load_function("alFilteri");
|
|
v_alGenEffecs = (ALGENEFFECTS) al_load_function("alGenEffects");
|
|
v_alDeleteEffects = (ALDELETEEFFECTS) al_load_function("alDeleteEffects");
|
|
v_alEffecti = (ALEFFECTI) al_load_function("alEffecti");
|
|
v_alEffectf = (ALEFFECTF) al_load_function("alEffectf");
|
|
v_alEffectfv = (ALEFFECTFV) al_load_function("alEffectfv");
|
|
v_alGetEffectf = (ALGETEFFECTF) al_load_function("alGetEffectf");
|
|
|
|
v_alGenAuxiliaryEffectSlots = (ALGENAUXILIARYEFFECTSLOTS) al_load_function("alGenAuxiliaryEffectSlots");
|
|
v_alDeleteAuxiliaryEffectSlots = (ALDELETEAUXILIARYEFFECTSLOTS) al_load_function("alDeleteAuxiliaryEffectSlots");
|
|
v_alIsAuxiliaryEffectSlot = (ALISAUXILIARYEFFECTSLOT) al_load_function("alIsAuxiliaryEffectSlot");
|
|
v_alAuxiliaryEffectSloti = (ALAUXILIARYEFFECTSLOTI) al_load_function("alAuxiliaryEffectSloti");
|
|
v_alAuxiliaryEffectSlotiv = (ALAUXILIARYEFFECTSLOTIV) al_load_function("alAuxiliaryEffectSlotiv");
|
|
v_alAuxiliaryEffectSlotf = (ALAUXILIARYEFFECTSLOTF) al_load_function("alAuxiliaryEffectSlotf");
|
|
v_alAuxiliaryEffectSlotfv = (ALAUXILIARYEFFECTSLOTFV) al_load_function("alAuxiliaryEffectSlotfv");
|
|
} catch (const std::exception& err) {
|
|
mprintf(("\n EFX: Unable to load function: %s()\n", err.what()));
|
|
|
|
Ds_eax_inited = 0;
|
|
return -1;
|
|
}
|
|
|
|
v_alGenAuxiliaryEffectSlots(1, &AL_EFX_aux_id);
|
|
|
|
if (alGetError() != AL_NO_ERROR) {
|
|
mprintf(("\n EFX: Unable to create Aux effect!\n"));
|
|
return -1;
|
|
}
|
|
|
|
v_alGenEffecs(1, &AL_EFX_effect_id);
|
|
|
|
if (alGetError() != AL_NO_ERROR) {
|
|
mprintf(("\n EFX: Unable to create effect!\n"));
|
|
return -1;
|
|
}
|
|
|
|
v_alEffecti(AL_EFX_effect_id, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
|
|
|
|
if (alGetError() != AL_NO_ERROR) {
|
|
mprintf(("\n EFX: EAXReverb not supported!\n"));
|
|
return -1;
|
|
}
|
|
|
|
v_alAuxiliaryEffectSloti(AL_EFX_aux_id, AL_EFFECTSLOT_EFFECT, AL_EFX_effect_id);
|
|
|
|
if (alGetError() != AL_NO_ERROR) {
|
|
mprintf(("\n EFX: Couldn't load effect!\n"));
|
|
return -1;
|
|
}
|
|
|
|
// add default presets
|
|
EFX_presets.reserve(EAX_ENVIRONMENT_COUNT);
|
|
|
|
for (int i = 0; i < EAX_ENVIRONMENT_COUNT; i++) {
|
|
EFX_presets.push_back( EFX_Reverb_Defaults[i] );
|
|
}
|
|
|
|
Ds_eax_inited = 1;
|
|
|
|
// disabled by default
|
|
ds_eax_set_all(EAX_ENVIRONMENT_GENERIC, 0.0f, 0.0f, 0.0f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @todo Documentation
|
|
*/
|
|
int ds_eax_is_inited()
|
|
{
|
|
return Ds_eax_inited;
|
|
}
|
|
|
|
/**
|
|
* Called once per game frame to make sure voice messages aren't looping
|
|
*/
|
|
void ds_do_frame()
|
|
{
|
|
if (!ds_initialized) {
|
|
return;
|
|
}
|
|
|
|
int i;
|
|
channel *cp = NULL;
|
|
|
|
for (i = 0; i < MAX_CHANNELS; i++) {
|
|
cp = &Channels[i];
|
|
Assert( cp != NULL );
|
|
|
|
if (cp->is_voice_msg) {
|
|
if( cp->source_id == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
unsigned int current_position = ds_get_play_position(i);
|
|
if (current_position != 0) {
|
|
if (current_position < cp->last_position) {
|
|
ds_close_channel(i);
|
|
} else {
|
|
cp->last_position = current_position;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a valid channel return the sound id
|
|
*/
|
|
int ds_get_sound_id(int channel_id)
|
|
{
|
|
Assert( channel_id >= 0 );
|
|
|
|
return Channels[channel_id].snd_id;
|
|
}
|