mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
3569 lines
106 KiB
C++
3569 lines
106 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/>.
|
|
*/
|
|
|
|
/*
|
|
* $Logfile: /DescentIII/Main/dd_sndlib/Ds3dlib.cpp $
|
|
* $Revision: 149 $
|
|
* $Date: 9/27/99 5:38p $
|
|
* $Author: Samir $
|
|
*
|
|
* DirectSound(2d and 3d) implementation of the Descent III low-level sound interface
|
|
*
|
|
* $Log: /DescentIII/Main/dd_sndlib/Ds3dlib.cpp $
|
|
*
|
|
* 149 9/27/99 5:38p Samir
|
|
* EAX 2.0->1.0 compatibility checkin.
|
|
*
|
|
* 148 8/23/99 5:29p Samir
|
|
* incremental EAX 2.0 checkin
|
|
*
|
|
* 147 8/13/99 2:00p Samir
|
|
* more aureal and geometry fixes.
|
|
*
|
|
* 146 5/23/99 12:48a Samir
|
|
* decreased rolloff factor of 3d sounds under 3d sound mixers from 0.75
|
|
* to 0.5
|
|
*
|
|
* 145 5/20/99 6:23p Kevin
|
|
* minor speed up of software mixer and fix for memory debugging
|
|
*
|
|
* 144 5/20/99 1:00a Samir
|
|
* changes in ordering of EAX and NONE mixers.
|
|
*
|
|
* 143 5/09/99 7:09p Samir
|
|
* fixed sound card selection (enumeration) problems.
|
|
*
|
|
* 142 5/07/99 5:39p Samir
|
|
* better error checking for CheckAndForceDataAlloc for PageInSound.
|
|
*
|
|
* 141 5/03/99 3:12a Samir
|
|
* fixed up aureal so it works (a little slow though...)
|
|
*
|
|
* 140 5/01/99 10:25p Jeff
|
|
* (samir) fixed crash bug when alt-tabbing,inserting cds,etc
|
|
*
|
|
* 139 4/29/99 4:36p Kevin
|
|
* fixed a problem with low quality sounds & the optimizd software mixer
|
|
*
|
|
* 138 4/29/99 3:01p Samir
|
|
* added code for direct sound mixers only (and function for Aureal
|
|
* really) that will use direct sound looping for simple loops.
|
|
*
|
|
* 137 4/27/99 7:08p Kevin
|
|
* optimized software mixer!
|
|
*
|
|
* 136 4/27/99 6:21p Samir
|
|
* if callback gets a NULL guid, just return, assume that the sound device
|
|
* is the default?
|
|
*
|
|
* 135 4/27/99 5:19p Samir
|
|
* mprintf change.
|
|
*
|
|
* 134 4/27/99 2:10p Samir
|
|
* added code to set the desired sound card given the descriptive name of
|
|
* a sound card.
|
|
*
|
|
* 133 4/25/99 9:52p Samir
|
|
* fixed looping sequencing bug with thread for direct sound mixers.
|
|
* SSF_PLAY_LOOPING must be set before any buffer filling occurs,
|
|
* otherwise bad things happen with very small loops....
|
|
*
|
|
* 132 4/23/99 7:51p Samir
|
|
* looping fixes for directsound
|
|
*
|
|
* 131 4/22/99 10:33p Samir
|
|
* modifications so that DirectSound mixers use one thread for all looping
|
|
* and streaming sounds. It worked without crashing for about twenty
|
|
* minutes of playing from level 1 to level 2 of D3. We'll see.
|
|
*
|
|
* 130 4/18/99 5:37p Kevin
|
|
* Very simple optimization for the software mixer
|
|
*
|
|
* 129 4/13/99 4:15p Jason
|
|
* took out register keyword because it was hurting performance
|
|
*
|
|
* 128 4/13/99 4:09p Samir
|
|
* more priority stuff.
|
|
*
|
|
*
|
|
* 126 4/12/99 7:14p Samir
|
|
* prioritization code added.
|
|
*
|
|
* 125 4/10/99 5:08p Samir
|
|
* took out obsolete data from play_information structure that should save
|
|
* around 70 bytes per instance.
|
|
*
|
|
* 124 4/09/99 5:00p Kevin
|
|
* put the memset in -- memset should be doing at least 32 bit copies,
|
|
* which is FAR better than a for loop.
|
|
*
|
|
* 123 4/09/99 12:03p Samir
|
|
* took out windows.h again, this time made HWND a void pointer, and
|
|
* checked under both editor and main projects.
|
|
*
|
|
* 122 4/06/99 8:29p Samir
|
|
* added error check system.
|
|
*
|
|
* 121 3/29/99 11:00a Samir
|
|
* added system to support different 3d sound options.
|
|
*
|
|
* 120 3/17/99 4:20p Samir
|
|
* added functions to pause and resume individual sounds.
|
|
*
|
|
* 119 3/04/99 1:23p Kevin
|
|
* Fixed Poping sound!!!
|
|
*
|
|
* 118 3/03/99 6:53p Matt
|
|
* Fixed compile warning
|
|
*
|
|
* 117 3/03/99 3:12p Chris
|
|
* Fixed volume problems
|
|
*
|
|
* 116 3/01/99 8:12p Samir
|
|
* pause sounds when switching sound quality!
|
|
*
|
|
* 115 2/25/99 4:50p Kevin
|
|
* Semi-hack to fix the problem with music looping and stuttering in DS
|
|
*
|
|
* 114 2/24/99 3:15p Kevin
|
|
* OEM menu changes, and bug fixes for the save/load system
|
|
*
|
|
* 113 2/22/99 6:28p Kevin
|
|
* Fixed a bug that was introduced (?) when the thread problem was fixed.
|
|
*
|
|
* 112 2/20/99 1:14a Kevin
|
|
* Fixed another bug
|
|
*
|
|
* 111 2/19/99 10:45p Kevin
|
|
* Fixed bug I just introduced...
|
|
*
|
|
* 110 2/19/99 5:21p Kevin
|
|
* Fixed some connection DLLs and a Direct Sound bug with threads.
|
|
*
|
|
* 109 2/11/99 3:30p Doug
|
|
* error checking in PlaySoundBuffer.
|
|
*
|
|
* 108 2/04/99 9:46a Kevin
|
|
* OEM version changes
|
|
*
|
|
* 107 1/14/99 6:10p Samir
|
|
* added DirectSound buffer duplication code.
|
|
*
|
|
* 106 1/11/99 5:51p Samir
|
|
* reverb on the buffer level.
|
|
*
|
|
* 105 1/08/99 6:31p Samir
|
|
* added reverb
|
|
*
|
|
* 104 1/08/99 11:36a Samir
|
|
* implemented basic Aureal 2.0 support.
|
|
*
|
|
* 103 1/08/99 10:32a Chris
|
|
*
|
|
* 102 12/23/98 11:49a Samir
|
|
* reorganized code so it works with different APIs.
|
|
*
|
|
* 101 12/11/98 5:21p Samir
|
|
* (chris) fixed problem with software streaming audio.
|
|
*
|
|
* 100 12/10/98 3:16p Chris
|
|
* added mprintfs for later use
|
|
*
|
|
* 99 12/10/98 2:18p Chris
|
|
* Added more asserts to the streaming code
|
|
*
|
|
* 98 11/13/98 4:55p Nate
|
|
* don't do dedicated server check in lowlevel.
|
|
*
|
|
* 97 11/13/98 12:17p Chris
|
|
*
|
|
* 96 11/12/98 12:15p Chris
|
|
* Fixed a bug with streaming mixers
|
|
*
|
|
* 95 10/30/98 1:20p Chris
|
|
* Fixed a m_unique_id bug where it was being used before it was being
|
|
* assigned
|
|
*
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
// NEED THIS SINCE DDSNDLIB is a DD library.
|
|
#include "DDAccess.h"
|
|
|
|
#include "ds3dlib_internal.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <process.h>
|
|
#include "cfile.h"
|
|
#include "pserror.h"
|
|
#include "mono.h"
|
|
#include "soundload.h"
|
|
#include "mem.h"
|
|
#include "application.h"
|
|
#include "auddev.h"
|
|
#include "Macros.h"
|
|
#include "ddio.h"
|
|
|
|
// Hacked window handle -- chrishack
|
|
static oeWin32Application *SoundApp = NULL;
|
|
static void *GameWindowHandle;
|
|
#define MIN_SOUND_MIX_VOLUME 0.0
|
|
#define MAX_WRITE_AHEAD .04 // Seconds to write ahead of the play position (in seconds)
|
|
|
|
// write position
|
|
|
|
#define IS_3D_MIXER(_type) ((_type) >= SOUND_MIXER_DS3D_16 && (_type) != SOUND_MIXER_NONE)
|
|
|
|
win_llsSystem *ll_sound_ptr;
|
|
emulated_listener *g_emulated_listener = NULL; // silly hack (Samir)
|
|
short Global_DS_alloced_sounds = 0;
|
|
|
|
int *Fast_mixer = NULL;
|
|
int Fast_mixer_len = 0;
|
|
|
|
#define VOLUME_FIX_BITS 1024
|
|
|
|
// Streaming primary buffer information
|
|
DSSTREAM m_sb_info;
|
|
char m_sound_device_name[256] = ""; // set by set sound card
|
|
|
|
// Loads a sound buffer with data
|
|
long LoadSoundData(LPDIRECTSOUNDBUFFER lp_dsb, char *sound_data_ptr, ulong total_bytes);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static t3dEnvironmentValues Env3dValues;
|
|
static t3dEnvironmentToggles Env3dToggles;
|
|
|
|
#define ENV3DTOG_DOPPLER true
|
|
#define ENV3DTOG_GEOMETRY true
|
|
|
|
#define ENV3DVAL_DOPPLER_DEFAULT 0.5f
|
|
#define ENV3DVAL_ROLLOFF_DEFAULT 0.5f
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
win_llsSystem::win_llsSystem(void) : llsSystem() {
|
|
m_lp_ds = NULL;
|
|
m_f_sound_lib_init = 0;
|
|
m_hwnd_main = NULL;
|
|
m_mixer_type = -1;
|
|
m_sound_quality = SQT_NORMAL;
|
|
m_cache_stress_timer = 0.0f;
|
|
ll_sound_ptr = NULL;
|
|
m_timer_last_frametime = -1;
|
|
memset(&Env3dValues, 0, sizeof(Env3dValues));
|
|
memset(&Env3dToggles, 0, sizeof(Env3dToggles));
|
|
SetError(SSL_OK);
|
|
}
|
|
|
|
win_llsSystem::~win_llsSystem(void) {
|
|
DestroySoundLib();
|
|
SetSoundCard(NULL);
|
|
}
|
|
|
|
inline void opti_8m_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, int *mixer_buffer16,
|
|
const float l_volume, const float r_volume) {
|
|
int i;
|
|
int *mb = mixer_buffer16;
|
|
|
|
const int fix_rvol = r_volume * VOLUME_FIX_BITS;
|
|
const int fix_lvol = l_volume * VOLUME_FIX_BITS;
|
|
|
|
for (i = 0; i < (num_write << 1); i += 2) {
|
|
int sample;
|
|
int l_sample;
|
|
int r_sample;
|
|
|
|
sample = (((*cur_sample_8bit)) - 128) << 8;
|
|
cur_sample_8bit++;
|
|
|
|
l_sample = *mb + (sample * fix_lvol);
|
|
r_sample = *(mb + 1) + (sample * fix_rvol);
|
|
|
|
*mb = l_sample;
|
|
mb++;
|
|
*mb = r_sample;
|
|
mb++;
|
|
}
|
|
samples_played += (i / 2);
|
|
}
|
|
|
|
inline void opti_8s_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, int *mixer_buffer16,
|
|
const float l_volume, const float r_volume) {
|
|
int i;
|
|
int *mb = mixer_buffer16;
|
|
|
|
const int fix_rvol = r_volume * VOLUME_FIX_BITS;
|
|
const int fix_lvol = l_volume * VOLUME_FIX_BITS;
|
|
|
|
for (i = 0; i < (num_write << 1); i += 2) {
|
|
int lsample;
|
|
int rsample;
|
|
int l_sample;
|
|
int r_sample;
|
|
|
|
lsample = (((*cur_sample_8bit)) - 128) << 8;
|
|
cur_sample_8bit++;
|
|
rsample = (((*cur_sample_8bit)) - 128) << 8;
|
|
cur_sample_8bit++;
|
|
|
|
l_sample = *mb + (lsample * fix_lvol);
|
|
r_sample = *(mb + 1) + (rsample * fix_rvol);
|
|
|
|
*mb = l_sample;
|
|
mb++;
|
|
*mb = r_sample;
|
|
mb++;
|
|
}
|
|
samples_played += (i / 2);
|
|
}
|
|
|
|
inline void opti_16m_mix(short *cur_sample_16bit, const int num_write, int &samples_played, int *mixer_buffer16,
|
|
const float l_volume, const float r_volume) {
|
|
int i;
|
|
int *mb = mixer_buffer16;
|
|
|
|
const int fix_rvol = r_volume * VOLUME_FIX_BITS;
|
|
const int fix_lvol = l_volume * VOLUME_FIX_BITS;
|
|
|
|
for (i = 0; i < (num_write << 1); i += 2) {
|
|
int sample;
|
|
int l_sample;
|
|
int r_sample;
|
|
|
|
sample = *cur_sample_16bit;
|
|
cur_sample_16bit++;
|
|
|
|
l_sample = *mb + (sample * fix_lvol);
|
|
r_sample = *(mb + 1) + (sample * fix_rvol);
|
|
|
|
*mb = l_sample;
|
|
mb++;
|
|
*mb = r_sample;
|
|
mb++;
|
|
}
|
|
samples_played += (i / 2);
|
|
}
|
|
|
|
inline void opti_16s_mix(short *cur_sample_16bit, const int num_write, int &samples_played, int *mixer_buffer16,
|
|
const float l_volume, const float r_volume) {
|
|
int i;
|
|
int *mb = mixer_buffer16;
|
|
|
|
const int fix_rvol = r_volume * VOLUME_FIX_BITS;
|
|
const int fix_lvol = l_volume * VOLUME_FIX_BITS;
|
|
|
|
for (i = 0; i < (num_write << 1); i += 2) {
|
|
int lsample;
|
|
int rsample;
|
|
int l_sample;
|
|
int r_sample;
|
|
|
|
lsample = *cur_sample_16bit;
|
|
cur_sample_16bit++;
|
|
rsample = *cur_sample_16bit;
|
|
cur_sample_16bit++;
|
|
|
|
l_sample = *mb + (lsample * fix_lvol);
|
|
r_sample = *(mb + 1) + (rsample * fix_rvol);
|
|
|
|
*mb = l_sample;
|
|
mb++;
|
|
*mb = r_sample;
|
|
*mb++;
|
|
}
|
|
samples_played += (i / 2);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MAX_DS_PAN 1000.0f
|
|
|
|
int GamePanToDsPan(float pan) {
|
|
int ds_pan;
|
|
ds_pan = (int)(pan * MAX_DS_PAN);
|
|
|
|
if (ds_pan > MAX_DS_PAN)
|
|
ds_pan = MAX_DS_PAN;
|
|
if (ds_pan < -MAX_DS_PAN)
|
|
ds_pan = -MAX_DS_PAN;
|
|
|
|
// mprintf((0, "P %f %d\n", pan, ds_pan));
|
|
return ds_pan;
|
|
}
|
|
|
|
int GameVolumeToDsAttenuation(float volume) {
|
|
float fvol;
|
|
|
|
if (volume <= 0.0f) {
|
|
fvol = 2000.0f;
|
|
} else {
|
|
fvol = logf(volume) / logf(10.0f);
|
|
fvol *= 2000.0;
|
|
}
|
|
|
|
if (volume <= 0.01f) {
|
|
fvol = DSBVOLUME_MIN;
|
|
}
|
|
|
|
// mprintf((0, "V %f %f\n", volume, fvol));
|
|
return (int)fvol;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline char *get_sound_info(sound_buffer_info *sb, int *length, bool *f16bit) {
|
|
if (sb->m_sound_index < 0) {
|
|
if (sb->s) {
|
|
*f16bit = sb->s->f_sample_16bit;
|
|
}
|
|
return NULL;
|
|
}
|
|
if (SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_8bit) {
|
|
*length = SoundFiles[Sounds[sb->m_sound_index].sample_index].np_sample_length;
|
|
*f16bit = false;
|
|
return (char *)SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_8bit;
|
|
} else {
|
|
ASSERT(SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_16bit);
|
|
*f16bit = true;
|
|
*length = SoundFiles[Sounds[sb->m_sound_index].sample_index].np_sample_length * 2;
|
|
return (char *)SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_16bit;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
#define SB_STATUS_PLAYING 0x1
|
|
#define SB_STATUS_INVALID 0x2
|
|
|
|
inline int sb_get_status(sound_buffer_info *sb) {
|
|
int retflags = 0;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
int status;
|
|
|
|
if (sb->m_snd_obj) {
|
|
status = A3D_GetSourceStatus(sb->m_snd_obj);
|
|
if (status & A3D_STATUS_PLAYING)
|
|
retflags |= SB_STATUS_PLAYING;
|
|
} else {
|
|
retflags |= SB_STATUS_INVALID;
|
|
}
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
LPDIRECTSOUNDBUFFER sound_ptr = sb->m_sound_buffer;
|
|
unsigned long status;
|
|
|
|
if (sound_ptr) {
|
|
sound_ptr->GetStatus(&status);
|
|
if (status & DSBSTATUS_PLAYING)
|
|
retflags |= SB_STATUS_PLAYING;
|
|
} else {
|
|
retflags |= SB_STATUS_INVALID;
|
|
}
|
|
}
|
|
|
|
return retflags;
|
|
}
|
|
|
|
inline void sb_adjust_properties_3d(sound_buffer_info *sb, float f_volume, pos_state *pos, float reverb) {
|
|
if (!ll_sound_ptr->m_in_sound_frame)
|
|
ll_sound_ptr->m_pending_actions = true;
|
|
|
|
sb->m_volume = f_volume;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
// f_volume = f_volume * 1.2;
|
|
if (f_volume > 1.0f)
|
|
f_volume = 1.0f;
|
|
A3D_SetSourceVolume(sb->m_snd_obj, f_volume);
|
|
// A3D_SetMinMaxDistance(sb->m_snd_obj, Sounds[sb->m_sound_index].min_distance,
|
|
// Sounds[sb->m_sound_index].max_distance);
|
|
A3D_SetMinMaxDistance(sb->m_snd_obj, 30.0f, Sounds[sb->m_sound_index].max_distance);
|
|
// A3D_SetSourceCone(sb->m_snd_obj, x,y,z);
|
|
A3D_SetSourceOrientation(sb->m_snd_obj, &pos->orient->fvec, &pos->orient->uvec);
|
|
A3D_SetSourcePosition(sb->m_snd_obj, pos->position->x, pos->position->y, pos->position->z);
|
|
A3D_SetSourceVelocity(sb->m_snd_obj, pos->velocity->x, pos->velocity->y, pos->velocity->z);
|
|
} else
|
|
#endif
|
|
if (IS_3D_MIXER(sb->m_mixer_type)) {
|
|
LPDIRECTSOUNDBUFFER lp_dsb = sb->m_sound_buffer;
|
|
LPDIRECTSOUND3DBUFFER lpDSB3D = sb->m_sound_buffer_3d;
|
|
|
|
lp_dsb->SetVolume(GameVolumeToDsAttenuation(f_volume));
|
|
|
|
ASSERT(lpDSB3D != NULL);
|
|
|
|
if (sb->m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
lpDSB3D->SetMinDistance(30.0f, DS3D_IMMEDIATE);
|
|
} else {
|
|
lpDSB3D->SetMinDistance(Sounds[sb->m_sound_index].min_distance, DS3D_IMMEDIATE);
|
|
}
|
|
lpDSB3D->SetMaxDistance(Sounds[sb->m_sound_index].max_distance, DS3D_IMMEDIATE);
|
|
lpDSB3D->SetConeOrientation(pos->orient->fvec.x, pos->orient->fvec.y, pos->orient->fvec.z, DS3D_IMMEDIATE);
|
|
lpDSB3D->SetPosition(pos->position->x, pos->position->y, pos->position->z, DS3D_IMMEDIATE);
|
|
lpDSB3D->SetVelocity(pos->velocity->x, pos->velocity->y, pos->velocity->z, DS3D_IMMEDIATE);
|
|
|
|
// if (sb->m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
// EAX_SetBufferReverbMix(lpDSB3D, reverb);
|
|
// }
|
|
} else {
|
|
mprintf((0, "m_mixer_type = %d\n", sb->m_mixer_type));
|
|
Int3();
|
|
}
|
|
}
|
|
|
|
inline void sb_adjust_properties_2d(sound_buffer_info *sb, float f_volume, float f_pan, ushort frequency) {
|
|
if (!ll_sound_ptr->m_in_sound_frame)
|
|
ll_sound_ptr->m_pending_actions = true;
|
|
|
|
sb->m_volume = f_volume;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
if (sb->m_snd_obj) {
|
|
float l_pan = 1.0f, r_pan = 1.0f;
|
|
if (f_pan < 0.0f)
|
|
r_pan = r_pan + f_pan;
|
|
if (f_pan > 0.0f)
|
|
l_pan = l_pan - f_pan;
|
|
A3D_SetSourceVolume(sb->m_snd_obj, f_volume);
|
|
A3D_SetSourcePan(sb->m_snd_obj, l_pan, r_pan);
|
|
// mprintf((0, "2d vol:%.1f pan:%.1f,%.1f\n", f_volume, l_pan, r_pan));
|
|
}
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
LPDIRECTSOUNDBUFFER lpdsb;
|
|
|
|
ASSERT(f_pan >= -1.0f && f_pan <= 1.0f);
|
|
ASSERT(f_volume >= 0.0f && f_volume <= 1.0f);
|
|
|
|
lpdsb = sb->m_sound_buffer;
|
|
if (lpdsb == NULL)
|
|
return;
|
|
|
|
// mprintf((0, "Sound UID %d is now at %d volume,%d pan\n", sound_uid, volume, pan));
|
|
|
|
lpdsb->SetVolume(GameVolumeToDsAttenuation(f_volume));
|
|
lpdsb->SetPan(GamePanToDsPan(f_pan)); // chrishack pan is off
|
|
lpdsb->SetFrequency(frequency);
|
|
}
|
|
}
|
|
|
|
// functions for different APIs
|
|
int sb_get_current_position(sound_buffer_info *sb, uint *writep) {
|
|
DWORD playp, wp;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
if (!sb->m_snd_obj) {
|
|
*writep = (uint)(-1);
|
|
playp = (DWORD)(-1);
|
|
} else {
|
|
uint pp;
|
|
A3D_GetCurrentPosition(sb->m_snd_obj, &pp);
|
|
*writep = pp;
|
|
playp = (DWORD)pp;
|
|
}
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
if (sb->m_sound_buffer && sb->m_sound_buffer->GetCurrentPosition(&playp, &wp) == DS_OK) {
|
|
*writep = (uint)wp;
|
|
} else {
|
|
playp = (DWORD)(-1);
|
|
*writep = (uint)(-1);
|
|
}
|
|
}
|
|
|
|
return (uint)playp;
|
|
}
|
|
|
|
inline void sb_set_current_position(sound_buffer_info *sb, uint pos) {
|
|
if (!ll_sound_ptr->m_in_sound_frame)
|
|
ll_sound_ptr->m_pending_actions = true;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_SetCurrentPosition(sb->m_snd_obj, pos);
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
sb->m_sound_buffer->SetCurrentPosition((DWORD)pos);
|
|
}
|
|
}
|
|
|
|
void sb_free_buffer(sound_buffer_info *sb) {
|
|
if (!ll_sound_ptr->m_in_sound_frame)
|
|
ll_sound_ptr->m_pending_actions = true;
|
|
if (!sb->m_sound_buffer)
|
|
return;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_FreeSSource(sb->m_snd_obj);
|
|
sb->m_snd_obj = NULL;
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
if (sb->m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
EAX_FreeSource(sb->m_lpksps);
|
|
}
|
|
|
|
if (sb->m_sound_buffer) {
|
|
sb->m_sound_buffer->Release();
|
|
sb->m_sound_buffer = NULL;
|
|
}
|
|
if (sb->m_sound_buffer_3d) {
|
|
sb->m_sound_buffer_3d->Release();
|
|
sb->m_sound_buffer_3d = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void sb_stop_buffer(sound_buffer_info *sb) {
|
|
if (!ll_sound_ptr->m_in_sound_frame)
|
|
ll_sound_ptr->m_pending_actions = true;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_Stop(sb->m_snd_obj);
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
sb->m_sound_buffer->Stop();
|
|
}
|
|
}
|
|
|
|
bool sb_lock_buffer(sound_buffer_info *sb, uint dwWriteCursor, uint dwWriteBytes, void **lplpvAudioPtr1,
|
|
uint *lpdwAudioBytes1, void **lplpvAudioPtr2, uint *lpdwAudioBytes2) {
|
|
DWORD len1, len2;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
if (A3D_Lock(sb->m_snd_obj, dwWriteCursor, dwWriteBytes, lplpvAudioPtr1, &len1, lplpvAudioPtr2, &len2)) {
|
|
*lpdwAudioBytes1 = len1;
|
|
*lpdwAudioBytes2 = len2;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
TryLockAgainLabel:
|
|
switch (sb->m_sound_buffer->Lock(dwWriteCursor, dwWriteBytes, lplpvAudioPtr1, &len1, lplpvAudioPtr2, &len2, 0)) {
|
|
case DS_OK:
|
|
*lpdwAudioBytes1 = len1;
|
|
*lpdwAudioBytes2 = len2;
|
|
return true;
|
|
|
|
case DSERR_BUFFERLOST:
|
|
if (sb->m_sound_buffer->Restore() == DS_OK)
|
|
goto TryLockAgainLabel;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool sb_unlock_buffer(sound_buffer_info *sb, void *ptr1, uint len1, void *ptr2, uint len2) {
|
|
#ifdef SUPPORT_AUREAL
|
|
if (sb->m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_Unlock(sb->m_snd_obj, ptr1, len1, ptr2, len2);
|
|
} else
|
|
#endif
|
|
if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
sb->m_sound_buffer->Unlock(ptr1, len1, ptr2, len2);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool sb_load_buffer(sound_buffer_info *sb, void *sample_data, int length) {
|
|
if (!ll_sound_ptr->m_in_sound_frame)
|
|
ll_sound_ptr->m_pending_actions = true;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
switch (sb->m_mixer_type) {
|
|
case SOUND_MIXER_AUREAL:
|
|
if (!A3D_LoadSample(sb->m_snd_obj, sample_data, length)) {
|
|
Int3();
|
|
sb->m_status = SSF_UNUSED;
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
#endif
|
|
if (LoadSoundData(sb->m_sound_buffer, (char *)sample_data, length) != DS_OK) {
|
|
Int3();
|
|
sb->m_status = SSF_UNUSED;
|
|
return false;
|
|
}
|
|
#ifdef SUPPORT_AUREAL
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// The actual mixer code that sum's the sounds on each channel and does all the actual
|
|
// mixing and effects (writes data to the locked primary buffer)
|
|
void StreamMixer(char *ptr, int len) {
|
|
int i;
|
|
short *mixer_buffer16 = (short *)ptr;
|
|
int current_slot = 0;
|
|
bool f_loop;
|
|
bool f_mono;
|
|
|
|
const int buff_len = len / ll_sound_ptr->m_primary_alignment;
|
|
|
|
// this code will assure that this function will not be called when sound system is in error mode.
|
|
if (ll_sound_ptr->m_lib_error_code != SSL_OK) {
|
|
return;
|
|
}
|
|
|
|
ASSERT(len <= m_sb_info.BufferSize);
|
|
ASSERT(ptr && len >= 0);
|
|
|
|
ASSERT((len % ll_sound_ptr->m_primary_alignment) == 0);
|
|
|
|
// memset((char *)ptr, 0, len);
|
|
|
|
// This memset is hopefully temporary
|
|
memset(Fast_mixer, 0, Fast_mixer_len * sizeof(int));
|
|
int *fast_mix_ptr = Fast_mixer;
|
|
|
|
// Mix the sound slots
|
|
while (current_slot < ll_sound_ptr->m_sound_mixer.m_max_sounds_played) {
|
|
sound_buffer_info *cur_buf = &ll_sound_ptr->m_sound_mixer.m_sound_cache[current_slot];
|
|
int num_samples = buff_len;
|
|
// mixer_buffer16 = (short *) ptr;
|
|
fast_mix_ptr = Fast_mixer;
|
|
f_mono = true;
|
|
|
|
// Find slots with sounds in them
|
|
if ((cur_buf->m_status != SSF_UNUSED) && !(cur_buf->m_status & SSF_PAUSED)) {
|
|
float l_volume = cur_buf->play_info->left_volume;
|
|
float r_volume = cur_buf->play_info->right_volume;
|
|
int skip_interval = cur_buf->play_info->sample_skip_interval;
|
|
int samples_played = cur_buf->play_info->m_samples_played;
|
|
short *sample_16bit;
|
|
unsigned char *sample_8bit;
|
|
int np_sample_length;
|
|
int sample_length;
|
|
int loop_start;
|
|
int loop_end;
|
|
|
|
if (cur_buf->m_status & SSF_PLAY_STREAMING) {
|
|
switch (cur_buf->play_info->m_stream_format) {
|
|
case SIF_STREAMING_16_M:
|
|
sample_16bit = (short *)cur_buf->play_info->m_stream_data;
|
|
sample_8bit = NULL;
|
|
np_sample_length = sample_length = cur_buf->play_info->m_stream_size / 2;
|
|
break;
|
|
case SIF_STREAMING_8_M:
|
|
sample_16bit = NULL;
|
|
sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data;
|
|
np_sample_length = sample_length = cur_buf->play_info->m_stream_size;
|
|
break;
|
|
case SIF_STREAMING_16_S:
|
|
sample_16bit = (short *)cur_buf->play_info->m_stream_data;
|
|
sample_8bit = NULL;
|
|
np_sample_length = sample_length = cur_buf->play_info->m_stream_size / 4;
|
|
f_mono = false;
|
|
break;
|
|
case SIF_STREAMING_8_S:
|
|
sample_16bit = NULL;
|
|
sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data;
|
|
np_sample_length = sample_length = cur_buf->play_info->m_stream_size / 2;
|
|
f_mono = false;
|
|
break;
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
loop_start = 0;
|
|
loop_end = np_sample_length - 1;
|
|
} else {
|
|
int sound_index = cur_buf->m_sound_index;
|
|
int sample_index = Sounds[sound_index].sample_index;
|
|
sound_file_info *snd_file = &SoundFiles[sample_index];
|
|
sample_16bit = snd_file->sample_16bit;
|
|
sample_8bit = snd_file->sample_8bit;
|
|
np_sample_length = snd_file->np_sample_length;
|
|
sample_length = snd_file->sample_length;
|
|
loop_start = Sounds[sound_index].loop_start;
|
|
loop_end = Sounds[sound_index].loop_end;
|
|
if (!sample_16bit && !sample_8bit) {
|
|
mprintf((0, "sound file %s didn't have data for samples.\n", snd_file->name));
|
|
}
|
|
}
|
|
|
|
// cleanly continue if this happens, and inform a logfile, if it does. error handling
|
|
// ASSERT(sample_16bit || sample_8bit);
|
|
if (!sample_16bit && !sample_8bit) {
|
|
sound_file_info *snd_file = &SoundFiles[Sounds[cur_buf->m_sound_index].sample_index];
|
|
ll_sound_ptr->SetError(SSL_ERROR_SAMPLE_NODATA);
|
|
ll_sound_ptr->ErrorText("ASSERT(sample_16bit || sample_8bit)\nNo data found for sound file: %s",
|
|
snd_file->name);
|
|
cur_buf->m_status = SSF_UNUSED;
|
|
goto error_bail;
|
|
}
|
|
|
|
ASSERT(np_sample_length <= sample_length);
|
|
ASSERT(samples_played >= 0 || samples_played <= sample_length);
|
|
|
|
int num_write;
|
|
|
|
error_trap:
|
|
|
|
// We have not looped -- yet
|
|
f_loop = false;
|
|
|
|
// Verify the volume levels are o.k.
|
|
ASSERT(l_volume >= 0.0 && l_volume <= 1.0);
|
|
ASSERT(r_volume >= 0.0 && r_volume <= 1.0);
|
|
|
|
looping: // Will go to this label to do the next iteration of a looping sample
|
|
|
|
if (cur_buf->m_status &
|
|
(SSF_PLAY_LOOPING | SSF_PLAY_STREAMING)) // Looping sample's process is broken up into linear pieces
|
|
{
|
|
if (f_loop) {
|
|
ASSERT(num_write >= 0);
|
|
|
|
num_samples -= num_write;
|
|
ASSERT(num_samples > 0);
|
|
|
|
fast_mix_ptr += (num_write * 2);
|
|
// mixer_buffer16 += num_write << 1; // update to the new start position
|
|
// (2x because of left and right channels)
|
|
|
|
samples_played = loop_start;
|
|
}
|
|
|
|
if (cur_buf->m_status & SSF_PLAY_LOOPING) // Looping sample's process is broken up into linear pieces
|
|
{
|
|
ASSERT(loop_end < sample_length);
|
|
if (loop_end < samples_played) {
|
|
// CHRISHACK -- Fuck milestone. Fix later. Code below is a major hack (but it works). :)
|
|
|
|
if (loop_end != loop_start) {
|
|
while (loop_end < samples_played) {
|
|
// Int3();
|
|
samples_played -= loop_end - loop_start;
|
|
}
|
|
|
|
ASSERT(samples_played >= 0);
|
|
} else {
|
|
cur_buf->m_status &= ~SSF_PLAY_LOOPING;
|
|
cur_buf->m_status |= SSF_PLAY_NORMAL;
|
|
}
|
|
|
|
goto error_trap;
|
|
}
|
|
}
|
|
// The number of samples to write to the primary buffer
|
|
num_write = ((num_samples) < (loop_end - samples_played + 1)) ? (num_samples) : (loop_end - samples_played + 1);
|
|
ASSERT(num_write >= 0 && num_write <= num_samples);
|
|
|
|
if (num_write < num_samples)
|
|
f_loop = true;
|
|
else
|
|
f_loop = false;
|
|
|
|
if (num_write <= 0) {
|
|
num_write = 0;
|
|
mprintf((0, "d"));
|
|
goto stream_done;
|
|
}
|
|
} else {
|
|
// The number of samples to write to the primary buffer
|
|
num_write =
|
|
((num_samples) < (np_sample_length - samples_played)) ? (num_samples) : (np_sample_length - samples_played);
|
|
if (!(num_write > 0 && num_write <= num_samples)) {
|
|
num_write = 0;
|
|
goto done;
|
|
}
|
|
|
|
// Optimization for silent sounds
|
|
if (l_volume <= MIN_SOUND_MIX_VOLUME && r_volume <= MIN_SOUND_MIX_VOLUME) {
|
|
cur_buf->play_info->m_samples_played += num_write;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (!(num_write > 0 && num_write <= num_samples)) // this was an assert
|
|
{
|
|
num_write = 0;
|
|
mprintf((0, "D"));
|
|
goto done;
|
|
}
|
|
|
|
// Mix at 16 bits per sample
|
|
if (skip_interval == 0) {
|
|
short *cur_sample_16bit = sample_16bit;
|
|
unsigned char *cur_sample_8bit = sample_8bit;
|
|
|
|
if (f_mono) {
|
|
if (sample_8bit) {
|
|
cur_sample_8bit += samples_played;
|
|
opti_8m_mix(cur_sample_8bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume);
|
|
} else {
|
|
cur_sample_16bit += samples_played;
|
|
opti_16m_mix(cur_sample_16bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume);
|
|
}
|
|
} else {
|
|
if (sample_8bit) {
|
|
cur_sample_8bit += (samples_played << 1);
|
|
opti_8s_mix(cur_sample_8bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume);
|
|
} else {
|
|
cur_sample_16bit += (samples_played << 1);
|
|
opti_16s_mix(cur_sample_16bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume);
|
|
}
|
|
}
|
|
} else
|
|
// Account for lower-sampling rate
|
|
{
|
|
if (skip_interval == 1) {
|
|
const int fix_rvol = r_volume * VOLUME_FIX_BITS;
|
|
const int fix_lvol = l_volume * VOLUME_FIX_BITS;
|
|
|
|
for (i = 0; i < (num_write << 1); i += 2) {
|
|
int sample;
|
|
|
|
if (sample_16bit) {
|
|
if (samples_played & 0x0001) {
|
|
sample = ((int)sample_16bit[samples_played ^ 0x0001] + (int)sample_16bit[samples_played + 1]) >> 1;
|
|
} else
|
|
sample = sample_16bit[samples_played];
|
|
} else {
|
|
if (samples_played & 0x0001) {
|
|
// Notes: (<<7) is from a (<<8) - (>>1)
|
|
// Notes: (-256) is from (-128) + (-128)
|
|
sample = ((int)sample_8bit[samples_played ^ 0x0001] + (int)sample_8bit[samples_played + 1] - 256) << 7;
|
|
} else
|
|
sample = (((int)sample_8bit[samples_played]) - (int)128) << 8;
|
|
}
|
|
|
|
samples_played++;
|
|
|
|
ASSERT(i >= 0 && (i + 1 < num_samples * 2));
|
|
|
|
int l_sample = fast_mix_ptr[i] + (sample * fix_lvol);
|
|
int r_sample = fast_mix_ptr[i + 1] + (sample * fix_rvol);
|
|
|
|
fast_mix_ptr[i] = l_sample;
|
|
fast_mix_ptr[i + 1] = r_sample;
|
|
}
|
|
} else {
|
|
const int fix_rvol = r_volume * VOLUME_FIX_BITS;
|
|
const int fix_lvol = l_volume * VOLUME_FIX_BITS;
|
|
for (i = 0; i < (num_write << 1); i += 2) {
|
|
int sample;
|
|
const int mod_pos = samples_played % 4;
|
|
|
|
if (sample_16bit) {
|
|
switch (mod_pos) {
|
|
case 0:
|
|
sample = sample_16bit[samples_played];
|
|
break;
|
|
case 1:
|
|
sample = (sample_16bit[samples_played - 1] * 3 + sample_16bit[samples_played + 3]) >> 2;
|
|
break;
|
|
case 2:
|
|
sample = (sample_16bit[samples_played - 2] + sample_16bit[samples_played + 2]) >> 1;
|
|
break;
|
|
case 3:
|
|
sample = (sample_16bit[samples_played - 3] + sample_16bit[samples_played + 1] * 3) >> 2;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (mod_pos) {
|
|
case 0:
|
|
sample = ((((int)sample_8bit[samples_played]) - 128) << 8);
|
|
break;
|
|
case 1:
|
|
sample = (((((int)sample_8bit[samples_played - 1]) - 128) << 8) * 3 +
|
|
((((int)sample_8bit[samples_played + 3]) - 128) << 8)) >>
|
|
2;
|
|
break;
|
|
case 2:
|
|
sample = (((((int)sample_8bit[samples_played - 2]) - 128) << 8) +
|
|
((((int)sample_8bit[samples_played + 2]) - 128) << 8)) >>
|
|
1;
|
|
break;
|
|
case 3:
|
|
sample = (((((int)sample_8bit[samples_played - 3]) - 128) << 8) +
|
|
((((int)sample_8bit[samples_played + 1]) - 128) << 8) * 3) >>
|
|
2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
samples_played++;
|
|
|
|
ASSERT(i >= 0 && (i + 1 < num_samples * 2));
|
|
|
|
int l_sample = fast_mix_ptr[i] + (sample * fix_lvol);
|
|
int r_sample = fast_mix_ptr[i + 1] + (sample * fix_rvol);
|
|
|
|
fast_mix_ptr[i] = l_sample;
|
|
fast_mix_ptr[i + 1] = r_sample;
|
|
}
|
|
}
|
|
}
|
|
|
|
stream_done:
|
|
|
|
cur_buf->play_info->m_samples_played = samples_played;
|
|
|
|
if (cur_buf->m_status & SSF_PLAY_STREAMING) {
|
|
if (f_loop) {
|
|
if (cur_buf->play_info->m_stream_cback && cur_buf->play_info->m_stream_data) {
|
|
cur_buf->play_info->m_stream_data = (*cur_buf->play_info->m_stream_cback)(
|
|
cur_buf->play_info->user_data, cur_buf->play_info->m_stream_handle, &cur_buf->play_info->m_stream_size);
|
|
// cur_buf->s->current_position = (char
|
|
//*)cur_buf->play_info->m_stream_data; mprintf((0, "%x %d\n",
|
|
// cur_buf->play_info->m_stream_data, cur_buf->play_info->m_stream_size));
|
|
ASSERT(!(cur_buf->play_info->m_stream_data && cur_buf->play_info->m_stream_size <= 0));
|
|
// mprintf((0, "Data %X, length %d\n",
|
|
// cur_buf->play_info->m_stream_data, cur_buf->play_info->m_stream_size));
|
|
|
|
if (cur_buf->play_info->m_stream_data) {
|
|
switch (cur_buf->play_info->m_stream_format) {
|
|
case SIF_STREAMING_16_M:
|
|
sample_16bit = (short *)cur_buf->play_info->m_stream_data;
|
|
loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size / 2;
|
|
break;
|
|
case SIF_STREAMING_8_M:
|
|
sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data;
|
|
loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size;
|
|
break;
|
|
case SIF_STREAMING_16_S:
|
|
sample_16bit = (short *)cur_buf->play_info->m_stream_data;
|
|
loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size / 4;
|
|
break;
|
|
case SIF_STREAMING_8_S:
|
|
sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data;
|
|
loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size / 2;
|
|
break;
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
loop_end -= 1;
|
|
} else {
|
|
mprintf((0, "SE: Data is NULL\n"));
|
|
cur_buf->m_status &= ~SSF_PLAY_STREAMING;
|
|
f_loop = false;
|
|
}
|
|
} else {
|
|
mprintf((0, "SE: Callback/data is NULL\n"));
|
|
cur_buf->m_status &= ~SSF_PLAY_STREAMING;
|
|
f_loop = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (f_loop)
|
|
goto looping;
|
|
|
|
done:
|
|
|
|
if (cur_buf->play_info->m_samples_played >= (np_sample_length) &&
|
|
!(cur_buf->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING))) {
|
|
ll_sound_ptr->StopSound(cur_buf->m_unique_id);
|
|
}
|
|
}
|
|
|
|
error_bail:
|
|
current_slot++;
|
|
}
|
|
|
|
for (int a = 0; a < (buff_len * 2); a++) {
|
|
Fast_mixer[a] = Fast_mixer[a] / VOLUME_FIX_BITS;
|
|
|
|
if (Fast_mixer[a] > 32767)
|
|
Fast_mixer[a] = 32767;
|
|
if (Fast_mixer[a] < -32767)
|
|
Fast_mixer[a] = -32767;
|
|
|
|
mixer_buffer16[a] = Fast_mixer[a];
|
|
|
|
Fast_mixer[a + 1] = Fast_mixer[a + 1] / VOLUME_FIX_BITS;
|
|
|
|
if (Fast_mixer[a + 1] > 32767)
|
|
Fast_mixer[a + 1] = 32767;
|
|
if (Fast_mixer[a + 1] < -32767)
|
|
Fast_mixer[a + 1] = -32767;
|
|
|
|
mixer_buffer16[a + 1] = Fast_mixer[a + 1];
|
|
|
|
a++;
|
|
}
|
|
// mprintf((0," -%d- ",a));
|
|
}
|
|
|
|
// Locks the primary buffer and fills in the new data
|
|
void StreamFill(int start_byte, int num_bytes) {
|
|
char *ptr1;
|
|
char *ptr2;
|
|
int len1, len2;
|
|
|
|
if ((num_bytes % ll_sound_ptr->m_primary_alignment) != 0) {
|
|
ll_sound_ptr->SetError(SSL_ERROR_STREAMMIXER);
|
|
ll_sound_ptr->ErrorText("ASSERT((len % ll_sound_ptr->m_primary_alignment) == 0)\nLen:%d PrA:%d", num_bytes,
|
|
ll_sound_ptr->m_primary_alignment);
|
|
return;
|
|
}
|
|
ASSERT((num_bytes % ll_sound_ptr->m_primary_alignment) == 0);
|
|
ASSERT((start_byte % ll_sound_ptr->m_primary_alignment) == 0);
|
|
|
|
start_byte = (start_byte % m_sb_info.BufferSize);
|
|
|
|
TryLockAgainLabel:
|
|
|
|
switch (m_sb_info.m_lp_looping_buffer->Lock(start_byte, num_bytes, (void **)&ptr1, (DWORD *)&len1, (void **)&ptr2,
|
|
(DWORD *)&len2, 0)) {
|
|
ASSERT((len1 % ll_sound_ptr->m_primary_alignment) == 0);
|
|
ASSERT((len2 % ll_sound_ptr->m_primary_alignment) == 0);
|
|
|
|
// The pointers are to the lock areas of the primary buffer. There are two because locked region
|
|
// might be a discontinuous chunk (wrapped around the end of the buffer i.e. 'LLLLBBBBBBLLLLL'
|
|
// L is for Locked and B is unlocked buffer.
|
|
case DS_OK:
|
|
StreamMixer(ptr1, len1);
|
|
if (ptr2)
|
|
StreamMixer(ptr2, len2);
|
|
|
|
m_sb_info.m_lp_looping_buffer->Unlock(ptr1, len1, ptr2, len2);
|
|
|
|
m_sb_info.NextWritePos = (start_byte + num_bytes) % m_sb_info.BufferSize;
|
|
break;
|
|
|
|
case DSERR_BUFFERLOST:
|
|
if (m_sb_info.m_lp_primary_buffer->Restore() == DS_OK && m_sb_info.m_lp_looping_buffer->Restore() == DS_OK)
|
|
goto TryLockAgainLabel;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// A peroidic mixer that uses the primary buffer as a stream buffer
|
|
void __cdecl StreamTimer(void *user_ptr) {
|
|
int playp, writep;
|
|
int try_count = 0;
|
|
DSSTREAM *sb_info = (DSSTREAM *)user_ptr;
|
|
DWORD result;
|
|
|
|
m_sb_info.m_lp_primary_buffer->Restore();
|
|
m_sb_info.m_lp_primary_buffer->Play(0, 0, DSBPLAY_LOOPING);
|
|
|
|
while (!sb_info->thread_request_kill) {
|
|
try_count = 0;
|
|
|
|
TryAgain:
|
|
try_count++;
|
|
|
|
// hresult = m_sb_info.m_lp_primary_buffer->GetStatus(&stat_result);
|
|
//
|
|
// if(hresult != DS_OK || !(stat_result & DSBSTATUS_LOOPING))
|
|
// {
|
|
// m_sb_info.m_lp_primary_buffer->Restore();
|
|
m_sb_info.m_lp_primary_buffer->Restore();
|
|
m_sb_info.m_lp_primary_buffer->Play(0, 0, DSBPLAY_LOOPING);
|
|
// }
|
|
|
|
result = sb_info->m_lp_looping_buffer->GetCurrentPosition((DWORD *)&playp, (DWORD *)&writep);
|
|
|
|
// mprintf((0, "(%d,%d)\n", playp, writep));
|
|
|
|
// If primary buffer was stopped from playing
|
|
if (writep == playp) {
|
|
sb_info->NextWritePos = -1;
|
|
|
|
if ((try_count == 1) &&
|
|
(m_sb_info.m_lp_primary_buffer->Restore() == DS_OK && m_sb_info.m_lp_looping_buffer->Restore() == DS_OK)) {
|
|
if ((try_count == 1) && (m_sb_info.m_lp_looping_buffer->Play(0, 0, DSBPLAY_LOOPING) == DS_OK)) {
|
|
goto TryAgain;
|
|
}
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
// Insert mixer code
|
|
{
|
|
int num_write_bytes;
|
|
|
|
if (sb_info->NextWritePos < 0)
|
|
sb_info->NextWritePos = writep;
|
|
|
|
// Debugging code (not sure what would happen if we hit these without an Int3()) -- Skipping noise
|
|
if (sb_info->LastPlayPos < sb_info->NextWritePos) {
|
|
|
|
if (playp >= sb_info->NextWritePos || playp < sb_info->LastPlayPos) {
|
|
if (!sb_info->m_f_secondary_looping)
|
|
goto done;
|
|
playp = sb_info->NextWritePos - 4;
|
|
num_write_bytes = 4 * (int)(22050 * DSPB_TICK_INTERVAL);
|
|
} else {
|
|
// Determine how much we can write out.
|
|
num_write_bytes = (sb_info->MaxWriteBytes + playp) - sb_info->NextWritePos;
|
|
}
|
|
} else {
|
|
if (playp >= sb_info->NextWritePos && playp < sb_info->LastPlayPos) {
|
|
if (!sb_info->m_f_secondary_looping)
|
|
goto done;
|
|
|
|
playp = sb_info->NextWritePos - 4;
|
|
num_write_bytes = 4 * (int)(22050 * DSPB_TICK_INTERVAL);
|
|
} else {
|
|
// Determine how much we can write out.
|
|
if (playp < sb_info->NextWritePos)
|
|
num_write_bytes = (sb_info->MaxWriteBytes + playp) - sb_info->NextWritePos;
|
|
else
|
|
num_write_bytes = sb_info->MaxWriteBytes - (sb_info->NextWritePos + (sb_info->BufferSize - playp));
|
|
}
|
|
}
|
|
|
|
num_write_bytes &= (0xFFFFFFFF & (~(ll_sound_ptr->m_primary_alignment)));
|
|
|
|
if (num_write_bytes > 0) {
|
|
// ASSERT(num_write_bytes < sb_info->BufferSize);
|
|
if (num_write_bytes >= sb_info->BufferSize) {
|
|
num_write_bytes = sb_info->BufferSize / 2;
|
|
}
|
|
|
|
StreamFill(sb_info->NextWritePos, num_write_bytes);
|
|
}
|
|
}
|
|
|
|
sb_info->LastPlayPos = playp;
|
|
|
|
done:
|
|
Sleep(DSPB_TICK_MILLISECONDS);
|
|
}
|
|
|
|
sb_info->thread_alive = false;
|
|
}
|
|
|
|
// Begins the whole streaming process
|
|
bool win_llsSystem::StartStreaming(void) {
|
|
DSBCAPS dsbcaps;
|
|
|
|
dsbcaps.dwSize = sizeof(DSBCAPS);
|
|
m_sb_info.m_lp_looping_buffer->GetCaps(&dsbcaps);
|
|
|
|
m_sb_info.m_f_secondary_looping = (m_sb_info.m_lp_looping_buffer != m_sb_info.m_lp_primary_buffer);
|
|
|
|
m_sb_info.BufferSize = dsbcaps.dwBufferBytes;
|
|
|
|
m_sb_info.MaxWriteSamples = m_primary_frequency * MAX_WRITE_AHEAD;
|
|
m_sb_info.MaxWriteBytes = m_sb_info.MaxWriteSamples * m_primary_alignment;
|
|
|
|
m_sb_info.NextWritePos = -1;
|
|
m_sb_info.LastPlayPos = 0;
|
|
|
|
StreamFill(0, dsbcaps.dwBufferBytes);
|
|
|
|
m_sb_info.thread_request_kill = false;
|
|
m_sb_info.thread_alive = true;
|
|
|
|
// Start mixing thread.
|
|
m_sb_info.thread_handle = _beginthread(StreamTimer, 16384, (void *)&m_sb_info);
|
|
if (m_sb_info.thread_handle == -1) {
|
|
m_sb_info.thread_alive = false;
|
|
mprintf((0, "Thread failed\n"));
|
|
Int3();
|
|
return false;
|
|
}
|
|
|
|
if (m_sb_info.m_f_secondary_looping) {
|
|
if (!SetThreadPriority((HANDLE)m_sb_info.thread_handle, THREAD_PRIORITY_HIGHEST))
|
|
Int3();
|
|
} else {
|
|
if (!SetThreadPriority((HANDLE)m_sb_info.thread_handle, THREAD_PRIORITY_TIME_CRITICAL))
|
|
Int3();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Creates a 2d, 3d, or Primary direct sound buffer
|
|
long win_llsSystem::CreateDSBuffer(int buffer_type, LPDIRECTSOUNDBUFFER *lp_lp_dsb, LPDIRECTSOUND3DBUFFER *lp_lp_dsb_3d,
|
|
ulong sound_bytes, ulong frequency, bool f_is_stereo, bool f_is_16_bit) {
|
|
DSBUFFERDESC dsbd;
|
|
tWAVEFORMATEX fmt;
|
|
HRESULT result = DS_OK;
|
|
|
|
ASSERT(m_lp_ds != NULL && sound_bytes >= 0);
|
|
ASSERT(frequency == 44100 || frequency == 22050 || frequency == 11025);
|
|
if (!m_f_sound_lib_init)
|
|
return 0;
|
|
|
|
if (lp_lp_dsb) {
|
|
*lp_lp_dsb = NULL;
|
|
}
|
|
if (lp_lp_dsb_3d) {
|
|
*lp_lp_dsb_3d = NULL;
|
|
}
|
|
|
|
// Setup the wave format
|
|
fmt.nChannels = (f_is_stereo) ? 2 : 1;
|
|
fmt.wBitsPerSample = (f_is_16_bit) ? 16 : 8;
|
|
fmt.nSamplesPerSec = ((DWORD)frequency);
|
|
fmt.nBlockAlign = fmt.nChannels * (fmt.wBitsPerSample >> 3);
|
|
fmt.nAvgBytesPerSec = ((DWORD)fmt.nSamplesPerSec) * ((DWORD)fmt.nBlockAlign);
|
|
fmt.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
// Setup the secondary direct sound buffer
|
|
memset(&dsbd, 0, sizeof(dsbd));
|
|
dsbd.lpwfxFormat = (LPWAVEFORMATEX)&fmt;
|
|
dsbd.dwSize = sizeof(DSBUFFERDESC);
|
|
dsbd.dwBufferBytes = sound_bytes;
|
|
|
|
if (m_mixer_type == SOUND_MIXER_SOFTWARE_16) {
|
|
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
|
|
|
ASSERT(buffer_type == SBT_PRIMARY);
|
|
|
|
m_primary_frequency = m_current_frequency = frequency;
|
|
m_primary_bit_depth = fmt.wBitsPerSample;
|
|
m_primary_alignment = fmt.nBlockAlign;
|
|
|
|
dsbd.lpwfxFormat = NULL;
|
|
dsbd.dwBufferBytes = 0;
|
|
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2;
|
|
} else if (m_mixer_type == SOUND_MIXER_DS_16 || m_mixer_type == SOUND_MIXER_DS_8) {
|
|
// There are three buffer types that we should consider.
|
|
switch (buffer_type) {
|
|
case SBT_2D:
|
|
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN;
|
|
break;
|
|
case SBT_PRIMARY:
|
|
dsbd.lpwfxFormat = NULL;
|
|
dsbd.dwBufferBytes = 0;
|
|
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
|
break;
|
|
default:
|
|
ASSERT(0); // Invalid type of buffer
|
|
}
|
|
} else if (IS_3D_MIXER(m_mixer_type)) {
|
|
// There are three buffer types that we should consider.
|
|
switch (buffer_type) {
|
|
case SBT_2D:
|
|
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN;
|
|
break;
|
|
case SBT_3D:
|
|
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
|
|
break;
|
|
case SBT_PRIMARY:
|
|
dsbd.lpwfxFormat = NULL;
|
|
dsbd.dwBufferBytes = 0;
|
|
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
|
|
break;
|
|
default:
|
|
ASSERT(0); // Invalid type of buffer
|
|
}
|
|
} else {
|
|
mprintf(
|
|
(0, "DS3DLIB: Unsupported function for mixer specfied in CreateDSBuffer (%d,%d)\n", m_mixer_type, buffer_type));
|
|
Int3(); // Get Samir!!!
|
|
return DSERR_UNSUPPORTED;
|
|
}
|
|
|
|
// Create the buffer
|
|
result = m_lp_ds->CreateSoundBuffer(&dsbd, lp_lp_dsb, 0);
|
|
/// ASSERT(result == DS_OK);
|
|
|
|
if (result == DS_OK && buffer_type == SBT_PRIMARY) {
|
|
// Creative EAX Init
|
|
if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
if (!EAX_SetPrimaryBuffer()) {
|
|
mprintf((0, "CreateDSBuffer: EAX Init failed.\n"));
|
|
result = DSERR_UNSUPPORTED;
|
|
goto ds_error;
|
|
}
|
|
}
|
|
|
|
// Succeeded. Set primary buffer to desired format.
|
|
result = (*lp_lp_dsb)->SetFormat(&fmt);
|
|
}
|
|
|
|
if (result != DS_OK) {
|
|
mprintf((0, "DS3DLIB: Failed to init sound buffer of type %d for mixer %d.\n", buffer_type, m_mixer_type));
|
|
goto ds_error;
|
|
}
|
|
|
|
// call get format to make sure we're okay.
|
|
if (buffer_type == SBT_PRIMARY) {
|
|
}
|
|
|
|
// do 3d sound support stuff.
|
|
if (IS_3D_MIXER(m_mixer_type)) {
|
|
// a 3d buffer needs a 3d interface pointer
|
|
if (buffer_type == SBT_3D) {
|
|
ASSERT(lp_lp_dsb_3d != NULL);
|
|
result = (*lp_lp_dsb)->QueryInterface(IID_IDirectSound3DBuffer, (void **)lp_lp_dsb_3d);
|
|
if (result != DS_OK) {
|
|
goto ds_error;
|
|
}
|
|
|
|
// ASSERT(result == DS_OK);
|
|
} else if (buffer_type == SBT_PRIMARY) {
|
|
if ((result = (*lp_lp_dsb)->QueryInterface(IID_IDirectSound3DListener, (void **)&m_lp_listener)) == S_OK) {
|
|
m_lp_listener->SetRolloffFactor(ENV3DVAL_ROLLOFF_DEFAULT, DS3D_IMMEDIATE);
|
|
}
|
|
}
|
|
}
|
|
|
|
ds_error:
|
|
if (result != DS_OK) {
|
|
mprintf((0, "DS3DLIB:result=%x\n", result));
|
|
if (lp_lp_dsb_3d && (*lp_lp_dsb_3d)) {
|
|
(*lp_lp_dsb_3d)->Release();
|
|
*lp_lp_dsb_3d = NULL;
|
|
}
|
|
|
|
if (lp_lp_dsb && (*lp_lp_dsb)) {
|
|
(*lp_lp_dsb)->Release();
|
|
*lp_lp_dsb = NULL;
|
|
}
|
|
}
|
|
|
|
return (long)result;
|
|
}
|
|
|
|
// Internal function to enumerate sound devices
|
|
BOOL CALLBACK LLEnumCallback(LPGUID lp_guid, LPCSTR lpstr_description, LPCSTR lpstr_module, LPVOID lp_Context) {
|
|
GUID FAR *lp_ret_guid = (GUID FAR *)lp_Context;
|
|
|
|
if (m_sound_device_name[0]) {
|
|
if (strcmp(lpstr_description, m_sound_device_name) == 0) {
|
|
mprintf((0, "Using sound card:%s-%s\n", lpstr_description, lpstr_module));
|
|
if (lp_guid) {
|
|
memmove(lp_ret_guid, lp_guid, sizeof(GUID));
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void win_llsSystem::SetSoundCard(const char *name) {
|
|
if (name) {
|
|
strcpy(m_sound_device_name, name);
|
|
} else {
|
|
m_sound_device_name[0] = 0;
|
|
}
|
|
}
|
|
|
|
// Initializes the sound library
|
|
int win_llsSystem::InitSoundLib(char mixer_type, oeApplication *sos, unsigned char MaxSoundsPlayed) // add playlist info
|
|
{
|
|
GUID card_guid, zero_card_guid;
|
|
GUID *pguid = NULL;
|
|
DSCAPS dscaps;
|
|
HRESULT hresult;
|
|
bool f16bit;
|
|
bool retval = true;
|
|
|
|
// reset error system.
|
|
SetError(SSL_OK);
|
|
|
|
#ifdef OEM_AUREAL
|
|
mixer_type = SOUND_MIXER_AUREAL;
|
|
#endif
|
|
|
|
SoundApp = (oeWin32Application *)sos;
|
|
|
|
if (sos) {
|
|
oeWin32Application *obj = (oeWin32Application *)sos;
|
|
|
|
// If the the library if already init'ed, then return o.k.
|
|
if (m_f_sound_lib_init)
|
|
return 1;
|
|
|
|
GameWindowHandle = (void *)obj->m_hWnd;
|
|
} else {
|
|
ASSERT(GameWindowHandle);
|
|
}
|
|
|
|
ll_sound_ptr = this;
|
|
m_timer_last_frametime = -1;
|
|
m_cache_stress_timer = 0.0f;
|
|
m_in_sound_frame = false;
|
|
m_pending_actions = false;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// chrishack -- Need to enumerate sound devices for now
|
|
// Attempt to enumerate the sound device
|
|
// if(DirectSoundEnumerate(&LLEnumCallback, NULL) != DS_OK) return 0;
|
|
|
|
// Setup the game handle
|
|
m_hwnd_main = GameWindowHandle;
|
|
|
|
// Check for invalid values and NULL pointers
|
|
ASSERT(m_hwnd_main != NULL);
|
|
ASSERT(Sounds != NULL);
|
|
ASSERT(SoundFiles != NULL);
|
|
ASSERT(MaxSoundsPlayed > 0);
|
|
|
|
memset(&m_sb_info, 0, sizeof(DSSTREAM));
|
|
|
|
// Currently restart total sounds played count (for unique ids and stats)
|
|
m_total_sounds_played = 0;
|
|
|
|
// Setup the sound mixer object
|
|
m_sound_mixer.m_cur_sounds_played = 0;
|
|
m_sound_mixer.m_max_sounds_played = MaxSoundsPlayed;
|
|
m_sound_mixer.m_sound_cache = new sound_buffer_info[MaxSoundsPlayed];
|
|
m_mixer_type = SOUND_MIXER_NONE;
|
|
|
|
if (m_sound_mixer.m_sound_cache == NULL) {
|
|
Int3();
|
|
goto error_sub;
|
|
}
|
|
|
|
// Create the Direct Sound Interface
|
|
// determine GUID for sound card which was chosen in the launcher...
|
|
ZeroMemory(&card_guid, sizeof(GUID));
|
|
ZeroMemory(&zero_card_guid, sizeof(GUID));
|
|
hresult = DirectSoundEnumerate(LLEnumCallback, &card_guid);
|
|
if (hresult == DS_OK) {
|
|
if (memcmp(&card_guid, &zero_card_guid, sizeof(GUID)) != 0) {
|
|
pguid = &card_guid;
|
|
}
|
|
}
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (mixer_type == SOUND_MIXER_AUREAL) {
|
|
if (!A3D_Init((HWND)GameWindowHandle)) {
|
|
retval = false;
|
|
goto error_sub;
|
|
}
|
|
m_lp_ds = NULL;
|
|
m_sound_mixer.m_loop_method = DSLOOP_BUFFER_METHOD;
|
|
} else
|
|
#endif
|
|
if (mixer_type != SOUND_MIXER_NONE) {
|
|
if (mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
if (!EAX_Create(pguid, &m_lp_ds)) {
|
|
mprintf((0, "Sound NT: Error EAX\n"));
|
|
retval = false;
|
|
goto error_sub;
|
|
}
|
|
} else {
|
|
hresult = DirectSoundCreate(pguid, &m_lp_ds, NULL);
|
|
if (hresult != DS_OK) {
|
|
retval = false;
|
|
goto error_sub;
|
|
}
|
|
}
|
|
|
|
ASSERT(m_lp_ds != NULL);
|
|
|
|
// determine if we're using a crappy card
|
|
dscaps.dwSize = sizeof(DSCAPS);
|
|
m_lp_ds->GetCaps(&dscaps);
|
|
if (dscaps.dwFlags & DSCAPS_EMULDRIVER) {
|
|
mixer_type = SOUND_MIXER_DS_8;
|
|
mprintf((0, "SOUND INIT(1): We are in NT or have a crappy sound card\n"));
|
|
}
|
|
|
|
m_sound_mixer.m_loop_method = DSLOOP_STREAM_METHOD;
|
|
}
|
|
|
|
if (mixer_type != SOUND_MIXER_NONE) {
|
|
m_f_sound_lib_init = 1;
|
|
}
|
|
|
|
// This section initializes the primary buffer
|
|
// software mixer try.
|
|
retry_mixer_init:
|
|
if (mixer_type == SOUND_MIXER_SOFTWARE_16) {
|
|
// test different conditions to see if we really can play sound in software
|
|
hresult = m_lp_ds->SetCooperativeLevel((HWND)m_hwnd_main, DSSCL_WRITEPRIMARY);
|
|
if (hresult != DS_OK) {
|
|
mprintf((0, "SOUND INIT(2): SCL: WritePrimary failed. Attempting DS 8 init.\n"));
|
|
mixer_type = SOUND_MIXER_DS_8;
|
|
goto retry_mixer_init;
|
|
}
|
|
|
|
// Creates a primary buffer and makes sure we are at the specified frequency, bit depth, and that we
|
|
// can to 3d stuff too :)
|
|
m_mixer_type = SOUND_MIXER_SOFTWARE_16;
|
|
f16bit = true;
|
|
hresult = CreateDSBuffer(SBT_PRIMARY, &m_sb_info.m_lp_primary_buffer, NULL, 0,
|
|
22050, // frequency
|
|
true, // stereo
|
|
f16bit); // 16-bit
|
|
if (hresult != DS_OK) {
|
|
if (m_sb_info.m_lp_primary_buffer) {
|
|
m_sb_info.m_lp_primary_buffer->Release();
|
|
m_sb_info.m_lp_primary_buffer = NULL;
|
|
}
|
|
mprintf((0, "SOUND INIT(3): Cannot create primary buffer.\n"));
|
|
mixer_type = SOUND_MIXER_DS_8;
|
|
goto retry_mixer_init;
|
|
} else {
|
|
// Determine if we are running NT or have a shitty Win95 sound card
|
|
DSBCAPS dsbcaps;
|
|
|
|
ASSERT(m_sb_info.m_lp_primary_buffer);
|
|
|
|
dsbcaps.dwSize = sizeof(DSBCAPS);
|
|
m_sb_info.m_lp_primary_buffer->GetCaps(&dsbcaps);
|
|
|
|
// Let's allocate our array of ints for our optimized software mixer!
|
|
if (!Fast_mixer) {
|
|
Fast_mixer = (int *)mem_malloc(dsbcaps.dwBufferBytes * sizeof(int));
|
|
Fast_mixer_len = dsbcaps.dwBufferBytes;
|
|
mprintf((0, "Using %d ints for fast software mixer\n", dsbcaps.dwBufferBytes));
|
|
}
|
|
|
|
// Is you want to see the caps, here is where -- mprintf all you want
|
|
if (!(dsbcaps.dwFlags & DSBCAPS_LOCHARDWARE)) {
|
|
mprintf((0, "SOUND INIT(4): Primary is not in hardware\n"));
|
|
m_sb_info.m_lp_primary_buffer->Release();
|
|
m_sb_info.m_lp_primary_buffer = NULL;
|
|
mixer_type = SOUND_MIXER_DS_8;
|
|
goto retry_mixer_init;
|
|
}
|
|
}
|
|
|
|
// looping buffer is the same as the primary buffer for software mixers.
|
|
m_sb_info.m_lp_looping_buffer = m_sb_info.m_lp_primary_buffer;
|
|
|
|
// Start the primary and have it always play.
|
|
m_f_sound_lib_init = StartStreaming();
|
|
if (!m_f_sound_lib_init) {
|
|
mprintf((0, "SOUND INIT(5): Something went wrong in StartStreaming\n"));
|
|
m_sb_info.m_lp_primary_buffer->Release();
|
|
m_sb_info.m_lp_primary_buffer = NULL;
|
|
mixer_type = SOUND_MIXER_DS_8;
|
|
goto retry_mixer_init;
|
|
}
|
|
|
|
}
|
|
#ifdef SUPPORT_AUREAL
|
|
else if (mixer_type == SOUND_MIXER_AUREAL) {
|
|
m_mixer_type = SOUND_MIXER_AUREAL;
|
|
if (!A3D_CreateListener()) {
|
|
retval = false;
|
|
goto error_sub;
|
|
}
|
|
A3D_SetRolloffFactor(ENV3DVAL_ROLLOFF_DEFAULT);
|
|
A3D_SetUnitsPerMeter(1.0f); // feet per meter.
|
|
}
|
|
#endif
|
|
else if (mixer_type == SOUND_MIXER_NONE) {
|
|
m_mixer_type = mixer_type;
|
|
m_f_sound_lib_init = 0;
|
|
} else {
|
|
// This is for DirectSound Internal Mixers. We let DirectSound do its magic.
|
|
m_mixer_type = mixer_type;
|
|
hresult = m_lp_ds->SetCooperativeLevel((HWND)m_hwnd_main, DSSCL_PRIORITY);
|
|
if (hresult != DS_OK) {
|
|
mprintf((0, "Sound NT: Error 1\n"));
|
|
retval = false;
|
|
goto error_sub;
|
|
}
|
|
|
|
// start primary buffer
|
|
f16bit = (mixer_type == SOUND_MIXER_DS_16 || mixer_type == SOUND_MIXER_DS3D_16 ||
|
|
mixer_type == SOUND_MIXER_CREATIVE_EAX);
|
|
hresult = CreateDSBuffer(SBT_PRIMARY, &m_sb_info.m_lp_primary_buffer, NULL, 0,
|
|
22050, // frequency
|
|
true, // stereo
|
|
f16bit); // 8 or 16 bit
|
|
if (hresult != DS_OK) {
|
|
mprintf((0, "Sound NT: Error 2\n"));
|
|
if (m_sb_info.m_lp_primary_buffer) {
|
|
m_sb_info.m_lp_primary_buffer->Release();
|
|
m_sb_info.m_lp_primary_buffer = NULL;
|
|
}
|
|
retval = false;
|
|
goto error_sub;
|
|
}
|
|
|
|
m_sb_info.m_lp_primary_buffer->Play(0, 0, DSBPLAY_LOOPING);
|
|
}
|
|
|
|
// buffered method doesn't use threads at all.
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16 && m_mixer_type != SOUND_MIXER_NONE &&
|
|
m_sound_mixer.m_loop_method != DSLOOP_BUFFER_METHOD) {
|
|
// start looping thread, failure results in a 'clean exit'
|
|
if (!sb_loop_thread_init(this)) {
|
|
retval = false;
|
|
if (m_sb_info.m_lp_primary_buffer) {
|
|
m_sb_info.m_lp_primary_buffer->Release();
|
|
m_sb_info.m_lp_primary_buffer = NULL;
|
|
}
|
|
goto error_sub;
|
|
}
|
|
}
|
|
|
|
mprintf((0, "Sound mixer: "));
|
|
retval = true;
|
|
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_SOFTWARE_16:
|
|
mprintf((0, "Software 16\n"));
|
|
break;
|
|
case SOUND_MIXER_DS_8:
|
|
mprintf((0, "DS 8\n"));
|
|
break;
|
|
case SOUND_MIXER_DS_16:
|
|
mprintf((0, "DS 16\n"));
|
|
break;
|
|
case SOUND_MIXER_DS3D_16:
|
|
mprintf((0, "DS3D 16\n"));
|
|
break;
|
|
case SOUND_MIXER_AUREAL:
|
|
mprintf((0, "Aureal 3D\n"));
|
|
break;
|
|
case SOUND_MIXER_CREATIVE_EAX:
|
|
mprintf((0, "Creative EAX\n"));
|
|
break;
|
|
case SOUND_MIXER_NONE:
|
|
mprintf((0, "None\n"));
|
|
break;
|
|
default:
|
|
mprintf((0, "LLSound ERROR: Unsupported mixer"));
|
|
Int3();
|
|
retval = false;
|
|
break;
|
|
}
|
|
|
|
// set 3d environment settings
|
|
t3dEnvironmentToggles env_toggles;
|
|
t3dEnvironmentValues env_values;
|
|
|
|
memset(&Env3dValues, 0, sizeof(Env3dValues));
|
|
memset(&Env3dToggles, 0, sizeof(Env3dToggles));
|
|
|
|
env_toggles.flags = 0; // get supported features
|
|
GetEnvironmentToggles(&env_toggles);
|
|
|
|
env_values.flags = 0; // set up values and toggles to be set
|
|
env_toggles.flags = env_toggles.supported;
|
|
|
|
// we may want to check this with configured options later -- samir
|
|
if (CHECK_FLAG(env_toggles.supported, ENV3DVALF_DOPPLER) && ENV3DTOG_DOPPLER) {
|
|
env_values.flags |= ENV3DVALF_DOPPLER;
|
|
env_values.doppler_scalar = ENV3DVAL_DOPPLER_DEFAULT;
|
|
env_toggles.doppler = true;
|
|
}
|
|
if (CHECK_FLAG(env_toggles.supported, ENV3DVALF_GEOMETRY) && ENV3DTOG_GEOMETRY) {
|
|
env_toggles.geometry = true;
|
|
}
|
|
SetEnvironmentToggles(&env_toggles);
|
|
SetEnvironmentValues(&env_values);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
g_emulated_listener = &m_emulated_listener;
|
|
|
|
error_sub:
|
|
if (retval == false) {
|
|
// Only gets here if there was an error
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_Destroy();
|
|
} else
|
|
#endif
|
|
if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
EAX_Destroy();
|
|
} else if (m_lp_ds) {
|
|
m_lp_ds->Release();
|
|
}
|
|
m_lp_ds = NULL;
|
|
mprintf((0, "Sound Warning: Didn't initialize sound library.\n"));
|
|
if (m_sound_mixer.m_sound_cache != NULL)
|
|
delete[] m_sound_mixer.m_sound_cache;
|
|
m_f_sound_lib_init = 0;
|
|
m_mixer_type = SOUND_MIXER_NONE;
|
|
}
|
|
|
|
Global_DS_alloced_sounds = 0;
|
|
|
|
return (m_f_sound_lib_init);
|
|
}
|
|
|
|
// Cleans up after the sound library is done being used
|
|
void win_llsSystem::DestroySoundLib(void) {
|
|
bool f_all_done = false;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
mprintf((0, "Start of sound system close\n"));
|
|
|
|
// kill sound geometry object if any.
|
|
if (m_geometry) {
|
|
m_geometry->Shutdown();
|
|
m_geometry = NULL;
|
|
}
|
|
|
|
StopAllSounds();
|
|
|
|
// Wait till they are all done
|
|
mprintf((0, "Waiting for sounds to stop\n"));
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
while (!f_all_done) {
|
|
f_all_done = true;
|
|
int i;
|
|
|
|
for (i = 0; i < m_sound_mixer.m_max_sounds_played; i++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[i];
|
|
if (sb->s && sb->s->kill_me && !(sb->m_status & (SSF_BUFFERED_LOOP + SSF_BUFFERED_STRM))) {
|
|
cleanup_directsound_looping_sb(&m_sound_mixer.m_sound_cache[i]);
|
|
} else if (sb->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING)) {
|
|
// this should block until the sound is truly free, so we don't need to do a 'all done'
|
|
StopSound(m_sound_mixer.m_sound_cache[i].m_unique_id, SKT_HOLD_UNTIL_STOP);
|
|
// f_all_done = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// buffered method doesn't use threads at all.
|
|
if (m_sound_mixer.m_loop_method != DSLOOP_BUFFER_METHOD) {
|
|
sb_loop_thread_kill();
|
|
}
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_Flush();
|
|
}
|
|
#endif
|
|
}
|
|
mprintf((0, "All sounds stopped\n"));
|
|
|
|
if (m_mixer_type == SOUND_MIXER_SOFTWARE_16) {
|
|
// Kill thread goes here
|
|
if (m_sb_info.thread_handle) {
|
|
m_sb_info.thread_request_kill = true;
|
|
while (m_sb_info.thread_alive) {
|
|
}
|
|
}
|
|
|
|
if (m_mixer_type == SOUND_MIXER_SOFTWARE_16) {
|
|
m_sb_info.m_lp_looping_buffer->Stop();
|
|
m_sb_info.m_lp_looping_buffer->Release();
|
|
}
|
|
}
|
|
|
|
// free audio device;
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_Destroy();
|
|
} else
|
|
#endif
|
|
if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
EAX_Destroy();
|
|
} else if (m_lp_ds) {
|
|
m_lp_ds->Release();
|
|
}
|
|
|
|
m_lp_ds = NULL;
|
|
m_sb_info.m_lp_looping_buffer = NULL;
|
|
|
|
mprintf((0, "End of sound system close\n"));
|
|
|
|
if (m_sound_mixer.m_sound_cache != NULL)
|
|
delete[] m_sound_mixer.m_sound_cache;
|
|
|
|
m_f_sound_lib_init = false;
|
|
g_emulated_listener = NULL;
|
|
m_mixer_type = -1;
|
|
ll_sound_ptr = NULL;
|
|
if (Fast_mixer) {
|
|
mem_free(Fast_mixer);
|
|
Fast_mixer = NULL;
|
|
Fast_mixer_len = 0;
|
|
}
|
|
}
|
|
|
|
// used to clean up direct sound buffer stuff
|
|
void win_llsSystem::cleanup_directsound_looping_sb(sound_buffer_info *sb) {
|
|
if (!sb->s)
|
|
return;
|
|
if (!sb->s->kill_me)
|
|
return;
|
|
|
|
// should occur when kill_me set, sb_stop_buffer(&ll_sound_ptr->m_sound_mixer.m_sound_cache[i]);
|
|
sb_free_buffer(sb);
|
|
|
|
sb->m_sound_buffer = NULL;
|
|
sb->s->playing = 0;
|
|
|
|
void *p = (void *)sb->s;
|
|
sb->s = NULL;
|
|
sb->m_status = SSF_UNUSED;
|
|
mem_free(p);
|
|
}
|
|
|
|
// cleans up a sound -replaces a lot of repeated code whenever we cleanup sounds.
|
|
void win_llsSystem::update_directsound_sb(sound_buffer_info *sb, bool update_looping) {
|
|
if (m_mixer_type == SOUND_MIXER_SOFTWARE_16 || m_mixer_type == SOUND_MIXER_NONE) {
|
|
return;
|
|
}
|
|
|
|
// take care of any streamed threaded method looping sounds that are done.
|
|
// orphan streaming structure, clean it up! (sound buffer is NULL yet sb->s is valid
|
|
if (sb->s && (((sb->m_status & (SSF_BUFFERED_LOOP + SSF_BUFFERED_STRM)) && !sb->m_sound_buffer) || sb->s->kill_me)) {
|
|
cleanup_directsound_looping_sb(sb);
|
|
} else if (sb->m_status && !(sb->m_status & SSF_PAUSED)) {
|
|
// non playing sounds should be stopped always
|
|
int status = sb_get_status(sb);
|
|
|
|
if (update_looping) {
|
|
// int status2 = A3D_GetSourceStatus(sb->m_snd_obj);
|
|
// mprintf((0, "(%x) status (%x)\n", sb->m_unique_id, status2));
|
|
|
|
if (status & SB_STATUS_PLAYING) {
|
|
if ((sb->m_status & (SSF_PLAY_STREAMING + SSF_BUFFERED_STRM)) == (SSF_PLAY_STREAMING + SSF_BUFFERED_STRM)) {
|
|
sb_stream_buffered_update(sb);
|
|
}
|
|
} else {
|
|
if ((sb->m_status & (SSF_PLAY_LOOPING + SSF_BUFFERED_LOOP)) == (SSF_PLAY_LOOPING + SSF_BUFFERED_LOOP)) {
|
|
sb_buffered_loop_step(this, sb);
|
|
status = sb_get_status(sb); // get new status for following checks.
|
|
}
|
|
}
|
|
}
|
|
if (!(status & SB_STATUS_PLAYING)) {
|
|
StopSound(sb->m_unique_id);
|
|
} else if (status & SB_STATUS_INVALID) {
|
|
StopSound(sb->m_unique_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stops the sound from playing -- f_immediately is used for looping samples -- i.e. so we can
|
|
// play the end of loop snipit
|
|
void win_llsSystem::StopSound(int sound_uid, unsigned char f_immediately) {
|
|
int current_slot;
|
|
sound_buffer_info *sb;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
|
|
sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
if (sb->m_status == SSF_UNUSED)
|
|
return;
|
|
|
|
// update sound count.
|
|
m_sound_mixer.m_cur_sounds_played--;
|
|
|
|
if (f_immediately == SKT_STOP_AFTER_LOOP) {
|
|
sb->m_status &= ~SSF_PLAY_LOOPING;
|
|
sb->m_status |= SSF_PLAY_NORMAL;
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16 && (sb->m_status & SSF_BUFFERED_LOOP)) {
|
|
sb_buffered_loop_step(this, sb, DSBUFLOOP_FINISH_STEP);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
if (sb->s && !(sb->m_status & (SSF_BUFFERED_LOOP + SSF_BUFFERED_STRM))) {
|
|
sb_loop_element_kill(sb);
|
|
if (f_immediately == SKT_HOLD_UNTIL_STOP) {
|
|
sb_loop_element_wait_until_dead(sb);
|
|
cleanup_directsound_looping_sb(sb);
|
|
}
|
|
return;
|
|
} else if (sb->m_sound_buffer) // this works for buffered loops too.
|
|
{
|
|
if (sb->m_status & (SSF_BUFFERED_LOOP + SSF_BUFFERED_STRM)) {
|
|
if (sb->m_status & SSF_PLAY_STREAMING) {
|
|
sb_stream_element_kill(sb);
|
|
}
|
|
|
|
void *p = (void *)sb->s;
|
|
if (p) {
|
|
mem_free(p);
|
|
sb->s = NULL;
|
|
}
|
|
}
|
|
GetSoundPos(sb->m_unique_id);
|
|
sb_stop_buffer(sb);
|
|
sb_free_buffer(sb);
|
|
|
|
if (SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count > 0) {
|
|
SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count--;
|
|
// DUPSND if (SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count == 0) {
|
|
// DUPSND Global_DS_alloced_sounds--;
|
|
// DUPSND }
|
|
}
|
|
Global_DS_alloced_sounds--;
|
|
}
|
|
m_sound_mixer.m_sound_cache[current_slot].m_sound_buffer = NULL;
|
|
}
|
|
|
|
// mprintf((0, "SL cleaning slot %d\n", current_slot));
|
|
m_sound_mixer.m_sound_cache[current_slot].m_status = SSF_UNUSED;
|
|
}
|
|
|
|
// Copies sound data from the external sound data block to an individual sound buffer
|
|
long LoadSoundData(LPDIRECTSOUNDBUFFER lp_dsb, char *sound_data_ptr, ulong total_bytes) {
|
|
LPVOID ptr1, ptr2;
|
|
DWORD len1, len2;
|
|
HRESULT result;
|
|
|
|
ASSERT(lp_dsb != NULL && sound_data_ptr != NULL && total_bytes > 0);
|
|
|
|
TryLockAgainLabel:
|
|
result = lp_dsb->Lock(0, total_bytes, &ptr1, &len1, &ptr2, &len2, 0);
|
|
|
|
switch (result) {
|
|
case DS_OK:
|
|
memcpy(ptr1, sound_data_ptr, len1);
|
|
if (ptr2)
|
|
memcpy(ptr2, sound_data_ptr + len1, len2);
|
|
lp_dsb->Unlock(ptr1, len1, ptr2, len2);
|
|
break;
|
|
case DSERR_BUFFERLOST:
|
|
result = lp_dsb->Restore();
|
|
if (result == DS_OK)
|
|
goto TryLockAgainLabel;
|
|
break;
|
|
}
|
|
|
|
return (long)result;
|
|
}
|
|
|
|
// Determines if a sound will play. Takes into account maximum allowable
|
|
// sounds.
|
|
// Also put prioritization code in here
|
|
// ignore reserved slots
|
|
|
|
#ifdef _DEBUG
|
|
short win_llsSystem::FindFreeSoundSlot(int sound_index, float volume, int priority)
|
|
#else
|
|
short win_llsSystem::FindFreeSoundSlot(float volume, int priority)
|
|
#endif
|
|
{
|
|
int current_slot;
|
|
sound_buffer_info *sb;
|
|
|
|
// stop any left over sounds for Direct Sound.
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
update_directsound_sb(sb);
|
|
}
|
|
}
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
if (sb->m_status == SSF_UNUSED) {
|
|
if (!((m_mixer_type == SOUND_MIXER_DS_8 || m_mixer_type == SOUND_MIXER_DS_16 ||
|
|
m_mixer_type == SOUND_MIXER_DS3D_16 || m_mixer_type == SOUND_MIXER_AUREAL ||
|
|
m_mixer_type == SOUND_MIXER_CREATIVE_EAX) &&
|
|
sb->s))
|
|
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 == m_sound_mixer.m_max_sounds_played) {
|
|
int throw_out_slot = -1, equiv_priority_slot = -1;
|
|
float weighted_priorityA, weighted_priorityB;
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sb = &m_sound_mixer.m_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 = m_sound_mixer.m_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 = &m_sound_mixer.m_sound_cache[throw_out_slot];
|
|
win_llsSystem::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;
|
|
}
|
|
|
|
int win_llsSystem::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 (!m_f_sound_lib_init) {
|
|
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 = &m_sound_mixer.m_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
|
|
if (m_mixer_type == SOUND_MIXER_SOFTWARE_16) {
|
|
sb->m_status = (f_looped) ? SSF_PLAY_LOOPING : SSF_PLAY_NORMAL;
|
|
return sb->m_unique_id;
|
|
}
|
|
|
|
if (f_looped) {
|
|
LoopStartStreaming(sb, SBT_2D, f_volume, f_pan, NULL);
|
|
if (sb->m_snd_obj == NULL) {
|
|
sb->m_status = SSF_UNUSED;
|
|
Int3();
|
|
return -1;
|
|
}
|
|
} else {
|
|
tPSBInfo psb;
|
|
|
|
if (SoundFiles[Sounds[sound_index].sample_index].use_count > 0) {
|
|
if (DuplicateSoundBuffer(sb)) {
|
|
goto play_sound;
|
|
}
|
|
}
|
|
if (!CreateSoundBuffer(sb, false)) {
|
|
return -1;
|
|
}
|
|
if (!LoadSoundBuffer(sb)) {
|
|
sb_free_buffer(sb);
|
|
return -1;
|
|
}
|
|
|
|
play_sound:
|
|
sb->m_status = SSF_PLAY_NORMAL;
|
|
|
|
psb.volume = f_volume;
|
|
psb.pan = f_pan;
|
|
psb.freq = 22050;
|
|
psb.looping = false;
|
|
if (!PlaySoundBuffer(sb, &psb)) {
|
|
sb_free_buffer(sb);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
m_sound_mixer.m_cur_sounds_played++;
|
|
|
|
return sb->m_unique_id;
|
|
}
|
|
|
|
void win_llsSystem::LoopStartStreaming(sound_buffer_info *sb, int buffer_type, float volume, float pan,
|
|
pos_state *cur_pos) {
|
|
// unsigned long thread_handle;
|
|
int sound_length, buffer_size;
|
|
int determined_method;
|
|
bool f_sample_16bit;
|
|
char *sample_ptr;
|
|
tPSBInfo psb;
|
|
DSLOOPSTREAM *s;
|
|
|
|
// setup looping buffer. must clear out before assigning to sound buffer for thread security.
|
|
s = (DSLOOPSTREAM *)mem_malloc(sizeof(DSLOOPSTREAM));
|
|
if (s == NULL) {
|
|
sb->s = NULL;
|
|
return;
|
|
}
|
|
memset((void *)s, 0, sizeof(DSLOOPSTREAM));
|
|
sb->s = s;
|
|
|
|
sample_ptr = get_sound_info(sb, &sound_length, &f_sample_16bit);
|
|
sb->s->f_sample_16bit = f_sample_16bit;
|
|
sb->m_buffer_type = buffer_type;
|
|
|
|
if (m_sound_mixer.m_loop_method == DSLOOP_SMART_METHOD) {
|
|
int loop_startb, loop_endb;
|
|
bool b;
|
|
if (sb_get_loop_info(sb, &loop_startb, &loop_endb, &b)) {
|
|
if (SoundFiles[Sounds[sb->m_sound_index].sample_index].np_sample_length == (loop_endb - loop_startb + 1)) {
|
|
determined_method = DSLOOP_BUFFER_METHOD;
|
|
} else {
|
|
determined_method = DSLOOP_STREAM_METHOD;
|
|
}
|
|
} else { // wtf?
|
|
return;
|
|
}
|
|
} else {
|
|
determined_method = m_sound_mixer.m_loop_method;
|
|
}
|
|
|
|
switch (determined_method) {
|
|
case DSLOOP_BUFFER_METHOD:
|
|
sound_length = 0;
|
|
sb->s->loop_step = -1;
|
|
while (!sound_length && sb->s->loop_step < 2) {
|
|
sample_ptr = sb_get_loop_step_info(sb, sb->s->loop_step, f_sample_16bit, &sound_length);
|
|
sb->s->loop_step++;
|
|
}
|
|
if (!sound_length && sb->s->loop_step == 2) {
|
|
// hmm, weird loop. no sample?
|
|
return;
|
|
}
|
|
sb->s->loop_step--; // return to proper step.
|
|
sb->s->loop_timer = 0.0f;
|
|
sb->s->bytes_left = sound_length;
|
|
buffer_size = sound_length;
|
|
sb->m_status = SSF_PLAY_LOOPING | SSF_BUFFERED_LOOP;
|
|
// mprintf((0, "DDSNDLIB: Starting buffered loop %d (step %d).\n", sb->m_unique_id, sb->s->loop_step));
|
|
break;
|
|
default:
|
|
buffer_size = STREAM_BUFFER_SIZE;
|
|
sb->m_status = SSF_PLAY_LOOPING; // must go before buffer is initialized!!!!!!
|
|
}
|
|
|
|
sb->sample_data = sample_ptr;
|
|
|
|
// allocate buffer for playback
|
|
if (!CreateSoundBuffer(sb, false, buffer_size, (sb->m_status & SSF_BUFFERED_LOOP) ? false : true)) {
|
|
return;
|
|
}
|
|
|
|
// remind sb system and play.
|
|
if (determined_method == DSLOOP_BUFFER_METHOD) {
|
|
psb.looping = (sb->s->loop_step == 0) ? true : false;
|
|
if (!sb_load_buffer(sb, sb->sample_data, sound_length))
|
|
return;
|
|
} else if (sb_loop_element_init(sb, sample_ptr, sound_length, STREAM_BUFFER_SIZE)) {
|
|
psb.looping = true;
|
|
} else {
|
|
sb->s->playing = 0;
|
|
return;
|
|
}
|
|
|
|
if (buffer_type == SBT_3D) {
|
|
psb.cur_pos = cur_pos;
|
|
} else {
|
|
ASSERT(buffer_type == SBT_2D);
|
|
psb.pan = pan;
|
|
}
|
|
psb.volume = volume;
|
|
psb.freq = 22050;
|
|
|
|
PlaySoundBuffer(sb, &psb);
|
|
|
|
// must be at end to initiate thread management.
|
|
sb->s->playing = 1;
|
|
}
|
|
|
|
void win_llsSystem::DSStartStreaming(sound_buffer_info *sb, float volume, float pan) {
|
|
tPSBInfo psb;
|
|
int sound_length;
|
|
int stream_buflength;
|
|
int determined_method;
|
|
char *sample_ptr;
|
|
bool f_stereo;
|
|
|
|
sb->s = (DSLOOPSTREAM *)mem_malloc(sizeof(DSLOOPSTREAM));
|
|
ASSERT(sb->s);
|
|
if (sb->s == NULL)
|
|
return;
|
|
|
|
memset((void *)sb->s, 0, sizeof(DSLOOPSTREAM));
|
|
|
|
sample_ptr = (char *)sb->play_info->m_stream_data;
|
|
sound_length = sb->play_info->m_stream_size;
|
|
stream_buflength = sb->play_info->m_stream_bufsize;
|
|
|
|
switch (sb->play_info->m_stream_format) {
|
|
case SIF_STREAMING_16_M:
|
|
sb->s->f_sample_16bit = true;
|
|
f_stereo = false;
|
|
break;
|
|
|
|
case SIF_STREAMING_8_M:
|
|
sb->s->f_sample_16bit = false;
|
|
f_stereo = false;
|
|
break;
|
|
|
|
case SIF_STREAMING_16_S:
|
|
sb->s->f_sample_16bit = true;
|
|
f_stereo = true;
|
|
break;
|
|
|
|
case SIF_STREAMING_8_S:
|
|
sb->s->f_sample_16bit = false;
|
|
f_stereo = true;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
ASSERT(sb->m_buffer_type == SBT_2D);
|
|
sb->m_sound_index = -1;
|
|
|
|
// determine how we will stream this data
|
|
determined_method = m_sound_mixer.m_loop_method;
|
|
if (m_sound_mixer.m_loop_method == DSLOOP_SMART_METHOD) {
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
determined_method = DSLOOP_BUFFER_METHOD;
|
|
}
|
|
}
|
|
|
|
switch (determined_method) {
|
|
case DSLOOP_BUFFER_METHOD:
|
|
sb->m_status = SSF_PLAY_STREAMING | SSF_BUFFERED_STRM;
|
|
break;
|
|
default:
|
|
sb->m_status = SSF_PLAY_STREAMING; // must go before buffer is initialized!!!!!!
|
|
break;
|
|
}
|
|
|
|
if (!CreateSoundBuffer(sb, f_stereo, stream_buflength, true)) {
|
|
mem_free((void *)sb->s);
|
|
sb->s = NULL;
|
|
return;
|
|
}
|
|
|
|
// set wave event or do thread streamed method
|
|
if (sb_stream_element_init(sb, sample_ptr, sound_length, stream_buflength)) {
|
|
psb.pan = pan;
|
|
psb.volume = volume;
|
|
psb.freq = 22050;
|
|
psb.looping = true;
|
|
|
|
PlaySoundBuffer(sb, &psb);
|
|
|
|
// must be at end to initiate thread management.
|
|
sb->s->playing = 1;
|
|
}
|
|
}
|
|
|
|
int win_llsSystem::PlayStream(play_information *play_info) {
|
|
short sound_slot;
|
|
DWORD 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 (!m_f_sound_lib_init)
|
|
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;
|
|
}
|
|
|
|
// mprintf((0, "TS(%d)Playing sound index %d at %d volume,%d pan\n", TotalSoundsPlayed, sound_index, volume, pan));
|
|
|
|
m_total_sounds_played++;
|
|
m_sound_mixer.m_sound_cache[sound_slot].play_info = play_info;
|
|
|
|
m_sound_mixer.m_sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot);
|
|
ASSERT(m_sound_mixer.m_sound_cache[sound_slot].m_unique_id != -1);
|
|
|
|
m_sound_mixer.m_sound_cache[sound_slot].m_buffer_type = SBT_2D;
|
|
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
float volume;
|
|
float pan;
|
|
LPDIRECTSOUNDBUFFER sound_ptr;
|
|
|
|
if (play_info->left_volume > play_info->right_volume) {
|
|
volume = play_info->left_volume;
|
|
pan = -1.0f + (play_info->right_volume / play_info->left_volume);
|
|
} else {
|
|
volume = play_info->right_volume;
|
|
pan = 1.0f - (play_info->left_volume / play_info->right_volume);
|
|
}
|
|
|
|
DSStartStreaming(&m_sound_mixer.m_sound_cache[sound_slot], volume, pan);
|
|
sound_ptr = m_sound_mixer.m_sound_cache[sound_slot].m_sound_buffer;
|
|
if (sound_ptr == NULL) {
|
|
m_sound_mixer.m_sound_cache[sound_slot].m_sound_buffer = NULL;
|
|
return -1;
|
|
}
|
|
} else {
|
|
m_sound_mixer.m_sound_cache[sound_slot].m_status = SSF_PLAY_STREAMING;
|
|
}
|
|
|
|
m_sound_mixer.m_cur_sounds_played++;
|
|
|
|
return (m_sound_mixer.m_sound_cache[sound_slot].m_unique_id);
|
|
}
|
|
|
|
// Checks a Unique Sound ID and determines if that sound is still playing
|
|
bool win_llsSystem::IsSoundInstancePlaying(int sound_uid) {
|
|
int current_slot;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return false;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return false;
|
|
|
|
// mprintf((0, "Checking slot %d of UID %d\n", current_slot, sound_uid));
|
|
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Is this sound placing on any "channel"
|
|
int win_llsSystem::IsSoundPlaying(int sound_index) {
|
|
int current_slot;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return -1;
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
if ((m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED) &&
|
|
(m_sound_mixer.m_sound_cache[current_slot].m_sound_index == sound_index)) {
|
|
return m_sound_mixer.m_sound_cache[current_slot].m_unique_id;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// 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 win_llsSystem::MakeUniqueId(int sound_slot) { return ((((int)m_total_sounds_played) << 8) + sound_slot); }
|
|
|
|
inline int win_llsSystem::ValidateUniqueId(int sound_uid) {
|
|
if (sound_uid == m_sound_mixer.m_sound_cache[sound_uid & 0x00FF].m_unique_id) {
|
|
return sound_uid & 0x00FF;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void win_llsSystem::PauseSounds() {
|
|
int current_slot;
|
|
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
update_directsound_sb(sb);
|
|
|
|
/* if((sb->m_status != SSF_UNUSED) &&
|
|
((sb->m_status & SSF_PAUSED) == 0))
|
|
{
|
|
int status = sb_get_status(sb);
|
|
|
|
if(!(status & SB_STATUS_PLAYING)) {
|
|
ll_sound_ptr->StopSound(sb->m_unique_id);
|
|
}
|
|
else if (status & SB_STATUS_INVALID) {
|
|
ll_sound_ptr->StopSound(sb->m_unique_id);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) {
|
|
sb->m_status |= SSF_PAUSED;
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16 && (sb->m_sound_buffer)) {
|
|
sb_stop_buffer(sb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void win_llsSystem::ResumeSounds() {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
if (sb->m_status != SSF_UNUSED && (sb->m_status & SSF_PAUSED)) {
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
PlaySoundBuffer(sb, NULL);
|
|
}
|
|
|
|
m_sound_mixer.m_sound_cache[current_slot].m_status &= (~SSF_PAUSED);
|
|
}
|
|
}
|
|
}
|
|
|
|
void win_llsSystem::PauseSound(int sound_uid) {
|
|
int current_slot;
|
|
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
if (sb->m_unique_id == sound_uid) {
|
|
update_directsound_sb(sb);
|
|
|
|
/* if((sb->m_status != SSF_UNUSED) && ((sb->m_status & SSF_PAUSED) == 0))
|
|
{
|
|
int status = sb_get_status(sb);
|
|
|
|
if(!(status & SB_STATUS_PLAYING)) {
|
|
ll_sound_ptr->StopSound(sb->m_unique_id);
|
|
}
|
|
else if (status & SB_STATUS_INVALID) {
|
|
ll_sound_ptr->StopSound(sb->m_unique_id);
|
|
}
|
|
}
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_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;
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16 && (sb->m_sound_buffer)) {
|
|
sb_stop_buffer(sb);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void win_llsSystem::ResumeSound(int sound_uid) {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
if (sound_uid == m_sound_mixer.m_sound_cache[current_slot].m_unique_id) {
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED &&
|
|
(m_sound_mixer.m_sound_cache[current_slot].m_status & SSF_PAUSED)) {
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
PlaySoundBuffer(&m_sound_mixer.m_sound_cache[current_slot], NULL);
|
|
}
|
|
|
|
m_sound_mixer.m_sound_cache[current_slot].m_status &= (~SSF_PAUSED);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void win_llsSystem::StopAllSounds() {
|
|
int current_slot;
|
|
|
|
for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED) {
|
|
StopSound(m_sound_mixer.m_sound_cache[current_slot].m_unique_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Begin sound frame
|
|
void win_llsSystem::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"));
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_Flush();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// start mixer dependant frame
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_StartFrame();
|
|
}
|
|
#endif
|
|
|
|
m_in_sound_frame = true;
|
|
m_pending_actions = false;
|
|
|
|
// cleanup sound cache.
|
|
// mprintf((0, "StartCleanup\n"));
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
for (i = 0; i < m_sound_mixer.m_max_sounds_played; i++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[i];
|
|
update_directsound_sb(sb, true);
|
|
}
|
|
}
|
|
// mprintf((0, "EndCleanup\n"));
|
|
|
|
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 < m_sound_mixer.m_max_sounds_played; current_slot++) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_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 < (m_sound_mixer.m_max_sounds_played * 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, m_sound_mixer.m_max_sounds_played));
|
|
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));
|
|
|
|
if (m_sound_mixer.m_loop_method != DSLOOP_STREAM_METHOD && m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
mprintf_at((3, 3, 10, "Bf: %02d", buf_loop_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 win_llsSystem::SoundEndFrame(void) {
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_EndFrame();
|
|
}
|
|
#endif
|
|
|
|
CheckForErrors(); // handles errors.
|
|
|
|
m_in_sound_frame = false;
|
|
}
|
|
|
|
bool win_llsSystem::LockSound(int sound_uid) { return false; }
|
|
|
|
bool win_llsSystem::UnlockSound(int sound_uid) { return false; }
|
|
|
|
// True means it was already loaded, false means that it was not
|
|
bool win_llsSystem::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;
|
|
}
|
|
|
|
bool win_llsSystem::SetSoundQuality(char quality) {
|
|
int i;
|
|
|
|
if (quality == m_sound_quality)
|
|
return true;
|
|
|
|
// pause any sounds that may be playing
|
|
win_llsSystem::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;
|
|
}
|
|
}
|
|
}
|
|
|
|
win_llsSystem::ResumeSounds();
|
|
|
|
return true;
|
|
}
|
|
|
|
char win_llsSystem::GetSoundQuality(void) { return m_sound_quality; }
|
|
|
|
bool win_llsSystem::SetSoundMixer(char mixer_type) {
|
|
if (mixer_type == m_mixer_type)
|
|
return true;
|
|
else {
|
|
// Chris note: This is not the best way to do this. All the currently playing
|
|
// sounds are lost. This shouldn't happen. A real solutions has to take in account
|
|
// for three things: Normal sounds, looping sounds, and streaming audio.
|
|
DestroySoundLib();
|
|
InitSoundLib(mixer_type, SoundApp, m_sound_mixer.m_max_sounds_played);
|
|
SetSoundQuality(m_sound_quality);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char win_llsSystem::GetSoundMixer(void) { return m_mixer_type; }
|
|
|
|
void win_llsSystem::SetListener(pos_state *cur_pos) {
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
m_emulated_listener.orient = *cur_pos->orient;
|
|
m_emulated_listener.position = *cur_pos->position;
|
|
m_emulated_listener.velocity = *cur_pos->velocity;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
A3D_SetListenerOrient(&cur_pos->orient->fvec, &cur_pos->orient->uvec);
|
|
A3D_SetListenerPosition(cur_pos->position->x, cur_pos->position->y, cur_pos->position->z);
|
|
A3D_SetListenerVelocity(cur_pos->velocity->x, cur_pos->velocity->y, cur_pos->velocity->z);
|
|
} else
|
|
#endif
|
|
if (IS_3D_MIXER(m_mixer_type)) {
|
|
m_lp_listener->SetOrientation(cur_pos->orient->fvec.x, cur_pos->orient->fvec.y, cur_pos->orient->fvec.z,
|
|
cur_pos->orient->uvec.x, cur_pos->orient->uvec.y, cur_pos->orient->uvec.z,
|
|
DS3D_DEFERRED);
|
|
m_lp_listener->SetPosition(cur_pos->position->x, cur_pos->position->y, cur_pos->position->z, DS3D_DEFERRED);
|
|
m_lp_listener->SetVelocity(cur_pos->velocity->x, cur_pos->velocity->y, cur_pos->velocity->z, DS3D_DEFERRED);
|
|
m_lp_listener->CommitDeferredSettings();
|
|
}
|
|
}
|
|
|
|
// AdjustSound2d -- adjusts the volume, pan, and freq. of a sound
|
|
void win_llsSystem::AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) {
|
|
int current_slot;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED)
|
|
return;
|
|
|
|
sound_buffer_info *sb = &m_sound_mixer.m_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;
|
|
|
|
sb_adjust_properties_2d(&m_sound_mixer.m_sound_cache[current_slot], f_volume, f_pan, frequency);
|
|
|
|
return;
|
|
}
|
|
|
|
void win_llsSystem::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) {
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
LPDIRECTSOUNDBUFFER lp_dsb;
|
|
// LPDIRECTSOUND3DBUFFER lpDSB3D;
|
|
int current_slot;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED)
|
|
return;
|
|
|
|
// sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
if (IS_3D_MIXER(m_mixer_type)) {
|
|
int sound_index = m_sound_mixer.m_sound_cache[current_slot].m_sound_index;
|
|
lp_dsb = m_sound_mixer.m_sound_cache[current_slot].m_sound_buffer;
|
|
if (lp_dsb == NULL)
|
|
return;
|
|
|
|
sb_adjust_properties_3d(&m_sound_mixer.m_sound_cache[current_slot], adjusted_volume, cur_pos, reverb);
|
|
} else {
|
|
// 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[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].max_distance) {
|
|
volume = 0.0f;
|
|
} else if (dist > Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].min_distance) {
|
|
volume *= (1.0 - ((dist - Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].min_distance) /
|
|
(Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].max_distance -
|
|
Sounds[m_sound_mixer.m_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(m_sound_mixer.m_sound_cache[current_slot].m_unique_id, volume, pan, 22050);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int win_llsSystem::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float adjusted_volume,
|
|
bool f_looped, float reverb) {
|
|
short sound_slot;
|
|
DWORD ds_flags = 0;
|
|
float volume;
|
|
|
|
volume = adjusted_volume; // Adjust base volume by sent volume, let 3d stuff do the rest
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return -1;
|
|
|
|
ASSERT(Sounds[sound_index].used != 0);
|
|
if (Sounds[sound_index].used == 0)
|
|
return -1;
|
|
|
|
if (!IS_3D_MIXER(m_mixer_type)) {
|
|
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);
|
|
}
|
|
|
|
// Out of sound slots
|
|
#ifdef _DEBUG
|
|
sound_slot = FindFreeSoundSlot(sound_index, volume, play_info->priority);
|
|
#else
|
|
sound_slot = FindFreeSoundSlot(volume, play_info->priority);
|
|
#endif
|
|
|
|
if (sound_slot < 0) {
|
|
return -1;
|
|
}
|
|
|
|
m_sound_mixer.m_sound_cache[sound_slot].play_info = play_info;
|
|
m_total_sounds_played++;
|
|
m_sound_mixer.m_sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot);
|
|
ASSERT(m_sound_mixer.m_sound_cache[sound_slot].m_unique_id != -1);
|
|
|
|
// 3-D!!
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[sound_slot];
|
|
|
|
sb->m_buffer_type = SBT_3D;
|
|
sb->m_sound_index = sound_index;
|
|
|
|
if (f_looped) {
|
|
LoopStartStreaming(sb, SBT_3D, adjusted_volume, 0.0, cur_pos);
|
|
if (sb->m_snd_obj == NULL) {
|
|
sb->m_sound_buffer = NULL;
|
|
Int3();
|
|
return -1;
|
|
}
|
|
} else {
|
|
tPSBInfo psb;
|
|
|
|
if (SoundFiles[Sounds[sound_index].sample_index].use_count > 0) {
|
|
if (DuplicateSoundBuffer(sb)) {
|
|
goto play_sound;
|
|
}
|
|
}
|
|
if (!CreateSoundBuffer(sb, false)) {
|
|
return -1;
|
|
}
|
|
if (!LoadSoundBuffer(sb)) {
|
|
sb_free_buffer(sb);
|
|
return -1;
|
|
}
|
|
|
|
play_sound:
|
|
sb->m_status = SSF_PLAY_NORMAL;
|
|
|
|
psb.cur_pos = cur_pos;
|
|
psb.volume = volume;
|
|
psb.freq = 22050;
|
|
psb.reverb = reverb;
|
|
psb.looping = false;
|
|
if (!PlaySoundBuffer(sb, &psb)) {
|
|
sb_free_buffer(sb);
|
|
return -1;
|
|
}
|
|
|
|
// mprintf((0, "SL Play sound on slot %d, TP = %d UI = %X\n", sound_slot, TotalSoundsPlayed,
|
|
// m_sound_mixer.m_sound_cache[sound_slot].m_unique_id));
|
|
}
|
|
|
|
m_sound_mixer.m_cur_sounds_played++;
|
|
|
|
return (sb->m_unique_id);
|
|
}
|
|
|
|
// These work in samples to make things easier in the long run
|
|
int win_llsSystem::SetSoundPos(int sound_uid, int pos) {
|
|
int current_slot;
|
|
|
|
if (pos <= 0)
|
|
return 1;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return -1;
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return -1;
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED)
|
|
return -1;
|
|
|
|
m_sound_mixer.m_sound_cache[current_slot].play_info->m_samples_played = pos;
|
|
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
if (sb->stereo) {
|
|
pos *= 2;
|
|
}
|
|
|
|
if (sb->bps == 16) {
|
|
pos *= 2;
|
|
}
|
|
|
|
sb_set_current_position(sb, pos);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// These work in samples to make things easier in the long run
|
|
int win_llsSystem::GetSoundPos(int sound_uid) {
|
|
int current_slot;
|
|
uint temp, pos;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return -1;
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return -1;
|
|
if (m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED)
|
|
return -1;
|
|
|
|
if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) {
|
|
sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
temp = sb_get_current_position(sb, &pos);
|
|
|
|
if (sb->stereo) {
|
|
pos /= 2;
|
|
}
|
|
if (sb->bps == 16) {
|
|
pos /= 2;
|
|
}
|
|
|
|
// Updates the readable data
|
|
m_sound_mixer.m_sound_cache[current_slot].play_info->m_samples_played = pos;
|
|
|
|
return pos;
|
|
} else {
|
|
return m_sound_mixer.m_sound_cache[current_slot].play_info->m_samples_played;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool win_llsSystem::CreateSoundBuffer(sound_buffer_info *sb, bool f_is_stereo, int size, bool dynamic) {
|
|
// do buffer creation
|
|
int buftype = sb->m_buffer_type;
|
|
bool f_sample_16bit;
|
|
int sound_length;
|
|
|
|
get_sound_info(sb, &sound_length, &f_sample_16bit);
|
|
|
|
if (size != -1) {
|
|
sound_length = size;
|
|
}
|
|
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_SOFTWARE_16:
|
|
Int3();
|
|
break;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
case SOUND_MIXER_AUREAL:
|
|
sb->m_snd_obj = A3D_CreateSSource(sound_length, f_sample_16bit ? 16 : 8, f_is_stereo, (dynamic) ? true : false,
|
|
22050, (buftype == SBT_3D) ? true : false);
|
|
if (!sb->m_snd_obj) {
|
|
Int3();
|
|
sb->m_status = SSF_UNUSED;
|
|
return false;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
CreateDSBuffer(buftype, &sb->m_sound_buffer, &sb->m_sound_buffer_3d, sound_length, 22050, f_is_stereo,
|
|
f_sample_16bit);
|
|
|
|
sb->m_lpksps = NULL;
|
|
if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
EAX_InitSource(sb->m_sound_buffer_3d, &sb->m_lpksps);
|
|
}
|
|
|
|
if (!sb->m_sound_buffer) {
|
|
Int3();
|
|
sb->m_status = SSF_UNUSED;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
sb->m_mixer_type = m_mixer_type;
|
|
sb->bps = f_sample_16bit ? 16 : 8;
|
|
sb->stereo = f_is_stereo;
|
|
|
|
if (sb->m_sound_index > -1) {
|
|
SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count++;
|
|
// Global_DS_alloced_sounds++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool win_llsSystem::LoadSoundBuffer(sound_buffer_info *sb) {
|
|
// play 2d sound
|
|
int buftype = sb->m_buffer_type;
|
|
bool f_sample_16bit;
|
|
char *sample_ptr;
|
|
int sound_length;
|
|
|
|
// get pointer
|
|
sample_ptr = get_sound_info(sb, &sound_length, &f_sample_16bit);
|
|
sb->sample_data = sample_ptr;
|
|
sb->sample_length = (int)sound_length;
|
|
|
|
// do buffer creation
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_SOFTWARE_16:
|
|
Int3();
|
|
break;
|
|
#ifdef SUPPORT_AUREAL
|
|
case SOUND_MIXER_AUREAL:
|
|
if (!A3D_LoadSample(sb->m_snd_obj, sample_ptr, sound_length)) {
|
|
Int3();
|
|
sb->m_status = SSF_UNUSED;
|
|
return false;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
if (LoadSoundData(sb->m_sound_buffer, sample_ptr, sound_length) != DS_OK) {
|
|
Int3();
|
|
sb->m_status = SSF_UNUSED;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool win_llsSystem::PlaySoundBuffer(sound_buffer_info *sb, tPSBInfo *psb) {
|
|
bool f_looping;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return false;
|
|
if (!m_in_sound_frame)
|
|
m_pending_actions = true;
|
|
|
|
TryPlayAgainLabel:
|
|
if (psb) {
|
|
if (sb->m_buffer_type == SBT_2D) {
|
|
AdjustSound(sb->m_unique_id, psb->volume, psb->pan, psb->freq);
|
|
} else if (sb->m_buffer_type == SBT_3D) {
|
|
AdjustSound(sb->m_unique_id, psb->cur_pos, psb->volume, psb->reverb);
|
|
}
|
|
|
|
SetSoundPos(sb->m_unique_id, sb->play_info->m_samples_played);
|
|
|
|
f_looping = psb->looping;
|
|
} else {
|
|
f_looping = ((sb->m_status & SSF_PLAY_LOOPING) || sb->s) ? true : false;
|
|
if (f_looping && m_sound_mixer.m_loop_method == DSLOOP_BUFFER_METHOD && sb->s->loop_step != 0 &&
|
|
(sb->m_status & SSF_BUFFERED_LOOP)) {
|
|
f_looping = false;
|
|
}
|
|
}
|
|
|
|
switch (m_mixer_type) {
|
|
#ifdef SUPPORT_AUREAL
|
|
case SOUND_MIXER_AUREAL: {
|
|
float priority;
|
|
switch (sb->play_info->priority) {
|
|
case SND_PRIORITY_CRITICAL:
|
|
case SND_PRIORITY_HIGHEST:
|
|
priority = 1.0f;
|
|
break;
|
|
case SND_PRIORITY_HIGH:
|
|
priority = 0.85f;
|
|
break;
|
|
case SND_PRIORITY_NORMAL:
|
|
priority = 0.60f;
|
|
break;
|
|
case SND_PRIORITY_LOW:
|
|
priority = 0.30f;
|
|
break;
|
|
case SND_PRIORITY_LOWEST:
|
|
priority = 0.1f;
|
|
break;
|
|
default:
|
|
priority = 0.5f;
|
|
}
|
|
A3D_SetSourcePriority(sb->m_snd_obj, priority);
|
|
A3D_Play(sb->m_snd_obj, f_looping);
|
|
} break;
|
|
#endif
|
|
|
|
case SOUND_MIXER_SOFTWARE_16:
|
|
Int3();
|
|
break;
|
|
|
|
default: {
|
|
LPDIRECTSOUNDBUFFER sound_ptr = sb->m_sound_buffer;
|
|
DWORD ds_flags = f_looping ? DSBPLAY_LOOPING : 0;
|
|
|
|
ASSERT(sound_ptr);
|
|
|
|
if (sound_ptr->Play(0, 0, ds_flags) == DSERR_BUFFERLOST) {
|
|
if (LoadSoundData(sound_ptr, sb->sample_data, sb->sample_length) == DS_OK) {
|
|
goto TryPlayAgainLabel;
|
|
}
|
|
|
|
sb->m_status = SSF_UNUSED;
|
|
|
|
// Some type of error -- we cannot play the sound? -- get chris
|
|
Int3();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool win_llsSystem::DuplicateSoundBuffer(sound_buffer_info *sb) {
|
|
return false; // for now, let's not do this.
|
|
|
|
/*
|
|
sound_buffer_info *source_sb = m_sound_mixer.FindSoundBuffer(sb->m_sound_index);
|
|
|
|
ASSERT(m_mixer_type != SOUND_MIXER_SOFTWARE_16);
|
|
|
|
if (!source_sb) {
|
|
Int3();
|
|
return false;
|
|
}
|
|
|
|
// we have a source sound buffer. let's use it.
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
sb->m_snd_obj = A3D_DuplicateSource(source_sb->m_snd_obj);
|
|
if (!sb->m_snd_obj) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
HRESULT hr;
|
|
hr = m_lp_ds->DuplicateSoundBuffer(source_sb->m_sound_buffer, &sb->m_sound_buffer);
|
|
if (FAILED(hr)) {
|
|
mprintf((0, "DDSNDLIB: Failed to duplicate sound buffer (%x)\n", hr));
|
|
return false;
|
|
}
|
|
|
|
if(IS_3D_MIXER(m_mixer_type)) {
|
|
// a 3d buffer needs a 3d interface pointer
|
|
if(sb->m_buffer_type == SBT_3D)
|
|
{
|
|
hr = sb->m_sound_buffer->QueryInterface(IID_IDirectSound3DBuffer, (void
|
|
**)&sb->m_sound_buffer_3d); if (FAILED(hr)) { mprintf((0, "DDSNDLIB: Failed to acquire 3d interface from duplicate
|
|
buffer (%x)\n", hr)); return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sb->m_mixer_type = source_sb->m_mixer_type;
|
|
sb->bps = source_sb->bps;
|
|
sb->stereo = source_sb->stereo;
|
|
sb->sample_data = source_sb->sample_data;
|
|
sb->sample_length = source_sb->sample_length;
|
|
|
|
// mprintf((0, "Duplicated!!\n"));
|
|
SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count++;
|
|
|
|
return true;
|
|
*/
|
|
}
|
|
|
|
// environmental sound interface
|
|
// volume modifier (0-1), damping(0-1), 1 = complete, 0 = none
|
|
// decay 0.1 to 100 seconds, how long it takes for a sound to die.
|
|
bool win_llsSystem::SetGlobalReverbProperties(float volume, float damping, float decay) {
|
|
#ifdef SUPPORT_AUREAL
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
return A3D_SetEnvironmentalReverb(volume, damping, decay);
|
|
} else
|
|
#endif
|
|
if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) {
|
|
return EAX_SetEnvironmentalReverb(volume, damping, decay);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
sound_buffer_info *sound_buffer_cache::FindSoundBuffer(int sound_index) {
|
|
int i;
|
|
|
|
// is there's no use count for this soudn, then there should be no available buffer.
|
|
if (SoundFiles[Sounds[sound_index].sample_index].use_count == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
// returns a sound buffer with the 'sound_index' if it is still active.
|
|
// we can use the sound buffer to create duplicates.
|
|
for (i = 0; i < m_max_sounds_played; i++) {
|
|
sound_buffer_info *sb = &m_sound_cache[i];
|
|
|
|
if (sb->m_status != SSF_UNUSED) {
|
|
if (sb->m_sound_index == sound_index) {
|
|
return sb;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// set special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void win_llsSystem::SetEnvironmentValues(const t3dEnvironmentValues *env) {
|
|
if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) {
|
|
Env3dValues.doppler_scalar = env->doppler_scalar;
|
|
|
|
#ifdef SUPPORT_AUREAL
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_AUREAL:
|
|
A3D_SetDopplerFactor(Env3dToggles.doppler ? Env3dValues.doppler_scalar : 0.0f);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// get special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void win_llsSystem::GetEnvironmentValues(t3dEnvironmentValues *env) {
|
|
if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) {
|
|
env->doppler_scalar = Env3dValues.doppler_scalar;
|
|
}
|
|
}
|
|
|
|
// enable special parameters for the 3d environment.
|
|
// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify
|
|
void win_llsSystem::SetEnvironmentToggles(const t3dEnvironmentToggles *env) {
|
|
t3dEnvironmentValues values;
|
|
|
|
if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) {
|
|
// set toggle then set doppler again.
|
|
Env3dToggles.doppler = env->doppler;
|
|
values.flags = ENV3DVALF_DOPPLER;
|
|
values.doppler_scalar = Env3dValues.doppler_scalar;
|
|
SetEnvironmentValues(&Env3dValues);
|
|
}
|
|
if (CHECK_FLAG(env->flags, ENV3DVALF_GEOMETRY)) {
|
|
// initialize geometry object if available and true.
|
|
if (m_geometry) {
|
|
delete m_geometry;
|
|
}
|
|
m_geometry = NULL;
|
|
if (env->geometry) {
|
|
if (m_mixer_type == SOUND_MIXER_AUREAL) {
|
|
m_geometry = new llsGeometry;
|
|
if (!m_geometry->Init(this)) {
|
|
delete m_geometry;
|
|
m_geometry = NULL;
|
|
}
|
|
}
|
|
}
|
|
Env3dToggles.geometry = m_geometry ? true : false;
|
|
}
|
|
}
|
|
|
|
// 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 win_llsSystem::GetEnvironmentToggles(t3dEnvironmentToggles *env) {
|
|
if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) {
|
|
env->doppler = Env3dToggles.doppler;
|
|
}
|
|
|
|
env->supported = 0;
|
|
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_AUREAL:
|
|
env->supported |= ENV3DVALF_DOPPLER;
|
|
env->supported |= ENV3DVALF_GEOMETRY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Sound System Error Handler.
|
|
void win_llsSystem::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();
|
|
|
|
// add our default error string.
|
|
char buf[8];
|
|
sprintf(buf, "mix:%d\n", m_mixer_type);
|
|
strcat(m_error_text, buf);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// set auxillary 3d sound properties
|
|
bool win_llsSystem::SoundPropertySupport() const {
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_CREATIVE_EAX:
|
|
if (EAX_Caps() & EAXF_SOURCE_OBSTRUCTION) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// sound obstruction from 0 to 1.0 (1.0 = fully obstructed)
|
|
void win_llsSystem::SetSoundProperties(int sound_uid, float obstruction) {
|
|
sound_buffer_info *sb;
|
|
int current_slot;
|
|
|
|
if (!m_f_sound_lib_init)
|
|
return;
|
|
|
|
if ((current_slot = ValidateUniqueId(sound_uid)) == -1)
|
|
return;
|
|
|
|
sb = &m_sound_mixer.m_sound_cache[current_slot];
|
|
|
|
switch (m_mixer_type) {
|
|
case SOUND_MIXER_CREATIVE_EAX:
|
|
EAX_SetSourceProperties(sb->m_lpksps, obstruction);
|
|
break;
|
|
}
|
|
}
|