Merge pull request #722 from 0lvin/gamepad

Sync haptic feedback with sound track
This commit is contained in:
Yamagi 2021-06-30 07:28:51 +02:00 committed by GitHub
commit 8358e386fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 361 additions and 232 deletions

View File

@ -316,7 +316,10 @@ void Key_ReadConsoleHistory();
void Key_WriteConsoleHistory(); void Key_WriteConsoleHistory();
void Key_SetBinding(int keynum, char *binding); void Key_SetBinding(int keynum, char *binding);
void Key_MarkAllUp(void); void Key_MarkAllUp(void);
void Haptic_Feedback(char *name, int effect_volume, int effect_x, int effect_y, int effect_z); void Haptic_Feedback(char *name, int effect_volume, int effect_duration,
int effect_begin, int effect_end,
int effect_attack, int effect_fade,
int effect_x, int effect_y, int effect_z);
int Key_GetMenuKey(int key); int Key_GetMenuKey(int key);
#endif #endif

View File

@ -78,33 +78,13 @@ static cvar_t *windowed_mouse;
// ---- // ----
/* Haptic feedback types */
enum QHARPICTYPES {
HAPTIC_EFFECT_UNKNOWN = -1,
HAPTIC_EFFECT_BLASTER = 0,
HAPTIC_EFFECT_MENY,
HAPTIC_EFFECT_HYPER_BLASTER,
HAPTIC_EFFECT_MACHINEGUN,
HAPTIC_EFFECT_SHOTGUN,
HAPTIC_EFFECT_SSHOTGUN,
HAPTIC_EFFECT_RAILGUN,
HAPTIC_EFFECT_ROCKETGUN,
HAPTIC_EFFECT_GRENADE,
HAPTIC_EFFECT_BFG,
HAPTIC_EFFECT_PALANX,
HAPTIC_EFFECT_IONRIPPER,
HAPTIC_EFFECT_ETFRIFLE,
HAPTIC_EFFECT_SHOTGUN2,
HAPTIC_EFFECT_TRACKER,
HAPTIC_EFFECT_PAIN,
HAPTIC_EFFECT_STEP,
HAPTIC_EFFECT_TRAPCOCK,
HAPTIC_EFFECT_LAST
};
struct hapric_effects_cache { struct hapric_effects_cache {
int effect_type;
int effect_volume; int effect_volume;
int effect_duration;
int effect_begin;
int effect_end;
int effect_attack;
int effect_fade;
int effect_id; int effect_id;
int effect_x; int effect_x;
int effect_y; int effect_y;
@ -117,10 +97,12 @@ static SDL_Haptic *joystick_haptic = NULL;
static SDL_Joystick *joystick = NULL; static SDL_Joystick *joystick = NULL;
static SDL_GameController *controller = NULL; static SDL_GameController *controller = NULL;
#define HAPTIC_EFFECT_LIST_SIZE 16
static int last_haptic_volume = 0; static int last_haptic_volume = 0;
static int last_haptic_efffect_size = HAPTIC_EFFECT_LAST; static int last_haptic_efffect_size = HAPTIC_EFFECT_LIST_SIZE;
static int last_haptic_efffect_pos = 0; static int last_haptic_efffect_pos = 0;
static struct hapric_effects_cache last_haptic_efffect[HAPTIC_EFFECT_LAST]; static struct hapric_effects_cache last_haptic_efffect[HAPTIC_EFFECT_LIST_SIZE];
// Joystick sensitivity // Joystick sensitivity
cvar_t *joy_yawsensitivity; cvar_t *joy_yawsensitivity;
@ -990,17 +972,22 @@ static void IN_Haptic_Shutdown(void);
* Init haptic effects * Init haptic effects
*/ */
static int static int
IN_Haptic_Effect_Init(int effect_x, int effect_y, int effect_z, int period, int magnitude, int length, int attack, int fade) IN_Haptic_Effect_Init(int effect_x, int effect_y, int effect_z,
int period, int magnitude,
int delay, int attack, int fade)
{ {
/*
* Direction:
* North - 0
* East - 9000
* South - 18000
* West - 27000
*/
static SDL_HapticEffect haptic_effect; static SDL_HapticEffect haptic_effect;
/* limit magnitude */
if (magnitude > SHRT_MAX)
{
magnitude = SHRT_MAX;
}
else if (magnitude < 0)
{
magnitude = 0;
}
SDL_memset(&haptic_effect, 0, sizeof(SDL_HapticEffect)); // 0 is safe default SDL_memset(&haptic_effect, 0, sizeof(SDL_HapticEffect)); // 0 is safe default
haptic_effect.type = SDL_HAPTIC_SINE; haptic_effect.type = SDL_HAPTIC_SINE;
@ -1010,7 +997,8 @@ IN_Haptic_Effect_Init(int effect_x, int effect_y, int effect_z, int period, int
haptic_effect.periodic.direction.dir[2] = effect_z; haptic_effect.periodic.direction.dir[2] = effect_z;
haptic_effect.periodic.period = period; haptic_effect.periodic.period = period;
haptic_effect.periodic.magnitude = magnitude; haptic_effect.periodic.magnitude = magnitude;
haptic_effect.periodic.length = length; haptic_effect.periodic.length = period;
haptic_effect.periodic.delay = delay;
haptic_effect.periodic.attack_length = attack; haptic_effect.periodic.attack_length = attack;
haptic_effect.periodic.fade_length = fade; haptic_effect.periodic.fade_length = fade;
@ -1027,97 +1015,6 @@ IN_Haptic_Effect_Init(int effect_x, int effect_y, int effect_z, int period, int
return effect_id; return effect_id;
} }
static int
IN_Haptic_Effects_To_Id(int haptic_effect, int effect_volume, int effect_x, int effect_y, int effect_z)
{
if ((SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE)==0)
{
return -1;
}
int hapric_volume = joy_haptic_magnitude->value * effect_volume * 16; // * 128 = 32767 max strength;
if (hapric_volume > 255)
{
hapric_volume = 255;
}
else if (hapric_volume < 0)
{
hapric_volume = 0;
}
switch(haptic_effect) {
case HAPTIC_EFFECT_MENY:
case HAPTIC_EFFECT_TRAPCOCK:
case HAPTIC_EFFECT_STEP:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 48,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_PAIN:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 196,
300/* 0.3 seconds long */, 200/* Takes 0.2 second to get max strength */,
200/* Takes 0.2 second to fade away */);
case HAPTIC_EFFECT_BLASTER:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_HYPER_BLASTER:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_ETFRIFLE:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_TRACKER:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_MACHINEGUN:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 800/* 800 ms*/, hapric_volume * 88,
600/* 0.6 seconds long */, 200/* Takes 0.2 second to get max strength */,
400/* Takes 0.4 second to fade away */);
case HAPTIC_EFFECT_SHOTGUN:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 100,
500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */,
200/* Takes 0.2 second to fade away */);
case HAPTIC_EFFECT_SHOTGUN2:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 96,
500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_SSHOTGUN:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 96,
500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_RAILGUN:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 64,
400/* 0.4 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_ROCKETGUN:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 700/* 700 ms*/, hapric_volume * 128,
400/* 0.4 seconds long */, 300/* Takes 0.3 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_GRENADE:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_BFG:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 800/* 800 ms*/, hapric_volume * 100,
600/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_PALANX:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
case HAPTIC_EFFECT_IONRIPPER:
return IN_Haptic_Effect_Init(effect_x, effect_y, effect_z, 500/* 500 ms*/, hapric_volume * 64,
200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */,
100/* Takes 0.1 second to fade away */);
default:
return -1;
}
}
static void static void
IN_Haptic_Effects_Info(void) IN_Haptic_Effects_Info(void)
{ {
@ -1134,16 +1031,20 @@ IN_Haptic_Effects_Init(void)
{ {
last_haptic_efffect_size = SDL_HapticNumEffectsPlaying(joystick_haptic); last_haptic_efffect_size = SDL_HapticNumEffectsPlaying(joystick_haptic);
if (last_haptic_efffect_size > HAPTIC_EFFECT_LAST) if (last_haptic_efffect_size > HAPTIC_EFFECT_LIST_SIZE)
{ {
last_haptic_efffect_size = HAPTIC_EFFECT_LAST; last_haptic_efffect_size = HAPTIC_EFFECT_LIST_SIZE;
} }
for (int i=0; i<HAPTIC_EFFECT_LAST; i++) for (int i=0; i<HAPTIC_EFFECT_LIST_SIZE; i++)
{ {
last_haptic_efffect[i].effect_type = HAPTIC_EFFECT_UNKNOWN;
last_haptic_efffect[i].effect_id = -1; last_haptic_efffect[i].effect_id = -1;
last_haptic_efffect[i].effect_volume = 0; last_haptic_efffect[i].effect_volume = 0;
last_haptic_efffect[i].effect_duration = 0;
last_haptic_efffect[i].effect_begin = 0;
last_haptic_efffect[i].effect_end = 0;
last_haptic_efffect[i].effect_attack = 0;
last_haptic_efffect[i].effect_fade = 0;
last_haptic_efffect[i].effect_x = 0; last_haptic_efffect[i].effect_x = 0;
last_haptic_efffect[i].effect_y = 0; last_haptic_efffect[i].effect_y = 0;
last_haptic_efffect[i].effect_z = 0; last_haptic_efffect[i].effect_z = 0;
@ -1172,10 +1073,14 @@ IN_Haptic_Effect_Shutdown(int * effect_id)
static void static void
IN_Haptic_Effects_Shutdown(void) IN_Haptic_Effects_Shutdown(void)
{ {
for (int i=0; i<HAPTIC_EFFECT_LAST; i++) for (int i=0; i<HAPTIC_EFFECT_LIST_SIZE; i++)
{ {
last_haptic_efffect[i].effect_type = HAPTIC_EFFECT_UNKNOWN;
last_haptic_efffect[i].effect_volume = 0; last_haptic_efffect[i].effect_volume = 0;
last_haptic_efffect[i].effect_duration = 0;
last_haptic_efffect[i].effect_begin = 0;
last_haptic_efffect[i].effect_end = 0;
last_haptic_efffect[i].effect_attack = 0;
last_haptic_efffect[i].effect_fade = 0;
last_haptic_efffect[i].effect_x = 0; last_haptic_efffect[i].effect_x = 0;
last_haptic_efffect[i].effect_y = 0; last_haptic_efffect[i].effect_y = 0;
last_haptic_efffect[i].effect_z = 0; last_haptic_efffect[i].effect_z = 0;
@ -1186,14 +1091,20 @@ IN_Haptic_Effects_Shutdown(void)
/* /*
* Haptic Feedback: * Haptic Feedback:
* effect_volume=0..16 * effect_volume=0..SHRT_MAX
* effect{x,y,z} - effect direction * effect{x,y,z} - effect direction
* name - sound file name * name - sound file name
*/ */
void void
Haptic_Feedback(char *name, int effect_volume, int effect_x, int effect_y, int effect_z) Haptic_Feedback(char *name, int effect_volume, int effect_duration,
int effect_begin, int effect_end,
int effect_attack, int effect_fade,
int effect_x, int effect_y, int effect_z)
{ {
int effect_type = HAPTIC_EFFECT_UNKNOWN; if (!joystick_haptic)
{
return;
}
if (joy_haptic_magnitude->value <= 0) if (joy_haptic_magnitude->value <= 0)
{ {
@ -1205,7 +1116,7 @@ Haptic_Feedback(char *name, int effect_volume, int effect_x, int effect_y, int e
return; return;
} }
if (!joystick_haptic) if (effect_duration <= 0)
{ {
return; return;
} }
@ -1218,105 +1129,63 @@ Haptic_Feedback(char *name, int effect_volume, int effect_x, int effect_y, int e
last_haptic_volume = joy_haptic_magnitude->value * 255; last_haptic_volume = joy_haptic_magnitude->value * 255;
if (strstr(name, "misc/menu")) if (
{ strstr(name, "misc/menu") ||
effect_type = HAPTIC_EFFECT_MENY; strstr(name, "weapons/") ||
} /* detect pain for any player model */
else if (strstr(name, "weapons/blastf1a")) ((
{ strstr(name, "player/") ||
effect_type = HAPTIC_EFFECT_BLASTER; strstr(name, "players/")
} ) && (
else if (strstr(name, "weapons/hyprbf1a")) strstr(name, "/pain")
{ )) ||
effect_type = HAPTIC_EFFECT_HYPER_BLASTER; strstr(name, "player/step") ||
} strstr(name, "player/land")
else if (strstr(name, "weapons/machgf")) )
{
effect_type = HAPTIC_EFFECT_MACHINEGUN;
}
else if (strstr(name, "weapons/shotgf1b"))
{
effect_type = HAPTIC_EFFECT_SHOTGUN;
}
else if (strstr(name, "weapons/sshotf1b"))
{
effect_type = HAPTIC_EFFECT_SSHOTGUN;
}
else if (strstr(name, "weapons/railgf1a"))
{
effect_type = HAPTIC_EFFECT_RAILGUN;
}
else if (strstr(name, "weapons/rocklf1a") ||
strstr(name, "weapons/rocklx1a"))
{
effect_type = HAPTIC_EFFECT_ROCKETGUN;
}
else if (strstr(name, "weapons/grenlf1a") ||
strstr(name, "weapons/grenlx1a") ||
strstr(name, "weapons/hgrent1a"))
{
effect_type = HAPTIC_EFFECT_GRENADE;
}
else if (strstr(name, "weapons/bfg__f1y"))
{
effect_type = HAPTIC_EFFECT_BFG;
}
else if (strstr(name, "weapons/plasshot"))
{
effect_type = HAPTIC_EFFECT_PALANX;
}
else if (strstr(name, "weapons/rippfire"))
{
effect_type = HAPTIC_EFFECT_IONRIPPER;
}
else if (strstr(name, "weapons/nail1"))
{
effect_type = HAPTIC_EFFECT_ETFRIFLE;
}
else if (strstr(name, "weapons/shotg2"))
{
effect_type = HAPTIC_EFFECT_SHOTGUN2;
}
else if (strstr(name, "weapons/disint2"))
{
effect_type = HAPTIC_EFFECT_TRACKER;
}
else if (strstr(name, "player/male/pain") ||
strstr(name, "player/female/pain") ||
strstr(name, "players/male/pain") ||
strstr(name, "players/female/pain"))
{
effect_type = HAPTIC_EFFECT_PAIN;
}
else if (strstr(name, "player/step") ||
strstr(name, "player/land"))
{
effect_type = HAPTIC_EFFECT_STEP;
}
else if (strstr(name, "weapons/trapcock"))
{
effect_type = HAPTIC_EFFECT_TRAPCOCK;
}
if (effect_type != HAPTIC_EFFECT_UNKNOWN)
{ {
// check last effect for reuse // check last effect for reuse
if (last_haptic_efffect[last_haptic_efffect_pos].effect_type != effect_type || if (
last_haptic_efffect[last_haptic_efffect_pos].effect_volume != effect_volume || last_haptic_efffect[last_haptic_efffect_pos].effect_volume != effect_volume ||
last_haptic_efffect[last_haptic_efffect_pos].effect_duration != effect_duration ||
last_haptic_efffect[last_haptic_efffect_pos].effect_begin != effect_begin ||
last_haptic_efffect[last_haptic_efffect_pos].effect_end != effect_end ||
last_haptic_efffect[last_haptic_efffect_pos].effect_attack != effect_attack ||
last_haptic_efffect[last_haptic_efffect_pos].effect_fade != effect_fade ||
last_haptic_efffect[last_haptic_efffect_pos].effect_x != effect_x || last_haptic_efffect[last_haptic_efffect_pos].effect_x != effect_x ||
last_haptic_efffect[last_haptic_efffect_pos].effect_y != effect_y || last_haptic_efffect[last_haptic_efffect_pos].effect_y != effect_y ||
last_haptic_efffect[last_haptic_efffect_pos].effect_z != effect_z) last_haptic_efffect[last_haptic_efffect_pos].effect_z != effect_z)
{ {
if ((SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE)==0)
{
return;
}
int hapric_volume = joy_haptic_magnitude->value * effect_volume; // 32767 max strength;
if (effect_duration <= 0)
{
return;
}
Com_Printf("%s: volume %d: %d ms %d:%d:%d ms speed: %.2f\n",
name, effect_volume, effect_duration - effect_end,
effect_begin, effect_attack, effect_fade,
(float)effect_volume / effect_fade);
// FIFO for effects // FIFO for effects
last_haptic_efffect_pos = (last_haptic_efffect_pos+1) % last_haptic_efffect_size; last_haptic_efffect_pos = (last_haptic_efffect_pos+1) % last_haptic_efffect_size;
IN_Haptic_Effect_Shutdown(&last_haptic_efffect[last_haptic_efffect_pos].effect_id); IN_Haptic_Effect_Shutdown(&last_haptic_efffect[last_haptic_efffect_pos].effect_id);
last_haptic_efffect[last_haptic_efffect_pos].effect_volume = effect_volume; last_haptic_efffect[last_haptic_efffect_pos].effect_volume = effect_volume;
last_haptic_efffect[last_haptic_efffect_pos].effect_type = effect_type; last_haptic_efffect[last_haptic_efffect_pos].effect_duration = effect_duration;
last_haptic_efffect[last_haptic_efffect_pos].effect_attack = effect_attack;
last_haptic_efffect[last_haptic_efffect_pos].effect_fade = effect_fade;
last_haptic_efffect[last_haptic_efffect_pos].effect_x = effect_x; last_haptic_efffect[last_haptic_efffect_pos].effect_x = effect_x;
last_haptic_efffect[last_haptic_efffect_pos].effect_y = effect_y; last_haptic_efffect[last_haptic_efffect_pos].effect_y = effect_y;
last_haptic_efffect[last_haptic_efffect_pos].effect_z = effect_z; last_haptic_efffect[last_haptic_efffect_pos].effect_z = effect_z;
last_haptic_efffect[last_haptic_efffect_pos].effect_id = IN_Haptic_Effects_To_Id( last_haptic_efffect[last_haptic_efffect_pos].effect_id = IN_Haptic_Effect_Init(
effect_type, effect_volume, effect_x, effect_y, effect_z); effect_x, effect_y, effect_z,
effect_duration - effect_end, hapric_volume,
effect_begin, effect_attack, effect_fade);
} }
SDL_HapticRunEffect(joystick_haptic, last_haptic_efffect[last_haptic_efffect_pos].effect_id, 1); SDL_HapticRunEffect(joystick_haptic, last_haptic_efffect[last_haptic_efffect_pos].effect_id, 1);

View File

@ -54,6 +54,14 @@ typedef struct
int bufnum; int bufnum;
#endif #endif
int stereo; int stereo;
/* effect length */
/* begin<->attack..fade<->end */
int begin;
int end;
int attack;
int fade;
/* effect volume */
short volume;
byte data[1]; byte data[1];
} sfxcache_t; } sfxcache_t;
@ -251,7 +259,9 @@ void SDL_ClearBuffer(void);
* Caches an sample for use * Caches an sample for use
* the SDL backend * the SDL backend
*/ */
qboolean SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data); qboolean SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data, short volume,
int begin_length, int end_length,
int attack_length, int fade_length);
/* /*
* Performs all sound calculations * Performs all sound calculations
@ -303,7 +313,9 @@ void AL_Shutdown(void);
* Upload ("cache") one sample * Upload ("cache") one sample
* into OpenAL * into OpenAL
*/ */
sfxcache_t *AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data); sfxcache_t *AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data, short volume,
int begin_length, int end_length,
int attack_length, int fade_length);
/* /*
* Deletes one sample from OpenAL * Deletes one sample from OpenAL

View File

@ -167,7 +167,9 @@ AL_GetFormat(int width, int channels)
* frontend to work. * frontend to work.
*/ */
sfxcache_t * sfxcache_t *
AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data) AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data, short volume,
int begin_length, int end_length,
int attack_length, int fade_length)
{ {
sfxcache_t *sc; sfxcache_t *sc;
ALsizei size; ALsizei size;
@ -202,6 +204,11 @@ AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data)
sc->size = size; sc->size = size;
sc->bufnum = name; sc->bufnum = name;
sc->stereo = s_info->channels - 1; sc->stereo = s_info->channels - 1;
sc->volume = volume;
sc->begin = begin_length * 1000 / s_info->rate;
sc->end = end_length * 1000 / s_info->rate;
sc->fade = fade_length * 1000 / s_info->rate;
sc->attack = attack_length * 1000 / s_info->rate;
return sc; return sc;
} }

View File

@ -910,7 +910,9 @@ SDL_UpdateScaletable(void)
* performed. * performed.
*/ */
qboolean qboolean
SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data) SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data, short volume,
int begin_length, int end_length,
int attack_length, int fade_length)
{ {
float stepscale; float stepscale;
int i; int i;
@ -940,6 +942,11 @@ SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data)
sc->stereo = info->channels - 1; sc->stereo = info->channels - 1;
sc->length = (int)(info->samples / stepscale); sc->length = (int)(info->samples / stepscale);
sc->speed = sound.speed; sc->speed = sound.speed;
sc->volume = volume;
sc->begin = begin_length * 1000 / info->rate;
sc->end = end_length * 1000 / info->rate;
sc->fade = fade_length * 1000 / info->rate;
sc->attack = attack_length * 1000 / info->rate;
if ((int)(info->samples / stepscale) == 0) if ((int)(info->samples / stepscale) == 0)
{ {

View File

@ -245,6 +245,195 @@ S_LoadVorbis(const char *path, const char* name, wavinfo_t *info, void **buffer)
OGG_LoadAsWav(filename, info, buffer); OGG_LoadAsWav(filename, info, buffer);
} }
static void
S_GetVolume(const byte *data, int sound_length, int width, double *sound_volume)
{
/* update sound volume */
*sound_volume = 0;
if (width == 2)
{
short *sound_data = (short *)data;
short *sound_end = sound_data + sound_length;
while (sound_data < sound_end)
{
short sound_sample = LittleShort(*sound_data);
*sound_volume += (sound_sample * sound_sample);
sound_data ++;
}
}
else if (width == 1)
{
byte *sound_data = (byte *)data;
byte *sound_end = sound_data + sound_length;
while (sound_data < sound_end)
{
// normilize to 16bit sound;
short sound_sample = *sound_data << 8;
*sound_volume += (sound_sample * sound_sample);
sound_data ++;
}
}
if (sound_length != 0)
{
*sound_volume /= sound_length;
*sound_volume = sqrtf(*sound_volume);
}
}
static void
S_GetStatistics(const byte *data, int sound_length, int width, int channels,
double sound_volume, int *begin_length, int *end_length,
int *attack_length, int *fade_length)
{
/* attack length */
short sound_max = 0;
/* calculate max value*/
if (width == 2)
{
short *sound_data = (short *)data;
short *sound_end = sound_data + sound_length;
while (sound_data < sound_end)
{
short sound_sample = LittleShort(*sound_data);
if (sound_max < abs(sound_sample))
{
sound_max = abs(sound_sample);
}
sound_data ++;
}
}
else if (width == 1)
{
byte *sound_data = (byte *)data;
byte *sound_end = sound_data + sound_length;
while (sound_data < sound_end)
{
// normilize to 16bit sound;
short sound_sample = *sound_data << 8;
if (sound_max < abs(sound_sample))
{
sound_max = abs(sound_sample);
}
sound_data ++;
}
}
// use something in middle
sound_max = (sound_max + sound_volume) / 2;
// calculate attack/fade length
if (width == 2)
{
// calculate attack/fade length
short *sound_data = (short *)data;
short *delay_data = sound_data;
short *fade_data = sound_data;
short *sound_end = sound_data + sound_length;
short sound_sample = 0;
short sound_treshold = sound_max / 2;
/* delay calculate */
do
{
sound_sample = LittleShort(*sound_data);
sound_data ++;
}
while (sound_data < sound_end && abs(sound_sample) < sound_treshold);
/* delay_data == (short *)(data + info.dataofs) */
*begin_length = (sound_data - delay_data) / channels;
delay_data = sound_data;
fade_data = sound_data;
/* attack calculate */
do
{
sound_sample = LittleShort(*sound_data);
sound_data ++;
}
while (sound_data < sound_end && abs(sound_sample) < sound_max);
/* fade_data == delay_data */
*attack_length = (sound_data - delay_data) / channels;
fade_data = sound_data;
/* end calculate */
sound_data = sound_end;
do
{
sound_data --;
sound_sample = LittleShort(*sound_data);
}
while (sound_data > fade_data && abs(sound_sample) < sound_treshold);
*end_length = (sound_end - sound_data) / channels;
sound_end = sound_data;
/* fade calculate */
do
{
sound_data --;
sound_sample = LittleShort(*sound_data);
}
while (sound_data > fade_data && abs(sound_sample) < sound_max);
*fade_length = (sound_end - sound_data) / channels;
}
else if (width == 1)
{
// calculate attack/fade length
byte *sound_data = (byte *)data;
byte *delay_data = sound_data;
byte *fade_data = sound_data;
byte *sound_end = sound_data + sound_length;
short sound_sample = 0;
short sound_treshold = sound_max / 2;
/* delay calculate */
do
{
// normilize to 16bit sound;
sound_sample = *sound_data << 8;
sound_data ++;
}
while (sound_data < sound_end && abs(sound_sample) < sound_treshold);
/* delay_data == (short *)(data + info.dataofs) */
*begin_length = (sound_data - delay_data) / channels;
delay_data = sound_data;
fade_data = sound_data;
/* attack calculate */
do
{
// normilize to 16bit sound;
sound_sample = *sound_data << 8;
sound_data ++;
}
while (sound_data < sound_end && abs(sound_sample) < sound_max);
/* fade_data == delay_data */
*attack_length = (sound_data - delay_data) / channels;
fade_data = sound_data;
/* end calculate */
sound_data = sound_end;
do
{
sound_data --;
// normilize to 16bit sound;
sound_sample = *sound_data << 8;
}
while (sound_data > fade_data && abs(sound_sample) < sound_treshold);
*end_length = (sound_end - sound_data) / channels;
sound_end = sound_data;
/* fade calculate */
do
{
sound_data --;
// normilize to 16bit sound;
sound_sample = *sound_data << 8;
}
while (sound_data > fade_data && abs(sound_sample) < sound_max);
*fade_length = (sound_end - sound_data) / channels;
}
}
/* /*
* Loads one sample into memory * Loads one sample into memory
*/ */
@ -255,6 +444,11 @@ S_LoadSound(sfx_t *s)
byte *data = NULL; byte *data = NULL;
wavinfo_t info; wavinfo_t info;
sfxcache_t *sc; sfxcache_t *sc;
double sound_volume = 0;
int begin_length = 0;
int attack_length = 0;
int fade_length = 0;
int end_length = 0;
char *name; char *name;
if (s->name[0] == '*') if (s->name[0] == '*')
@ -327,17 +521,28 @@ S_LoadSound(sfx_t *s)
s->is_silenced_muzzle_flash = true; s->is_silenced_muzzle_flash = true;
} }
S_GetVolume(data + info.dataofs, info.samples * info.channels,
info.width, &sound_volume);
S_GetStatistics(data + info.dataofs, info.samples * info.channels,
info.width, info.channels, sound_volume, &begin_length, &end_length,
&attack_length, &fade_length);
#if USE_OPENAL #if USE_OPENAL
if (sound_started == SS_OAL) if (sound_started == SS_OAL)
{ {
sc = AL_UploadSfx(s, &info, data + info.dataofs); sc = AL_UploadSfx(s, &info, data + info.dataofs, sound_volume,
begin_length, end_length,
attack_length, fade_length);
} }
else else
#endif #endif
{ {
if (sound_started == SS_SDL) if (sound_started == SS_SDL)
{ {
if (!SDL_Cache(s, &info, data + info.dataofs)) if (!SDL_Cache(s, &info, data + info.dataofs, sound_volume,
begin_length, end_length,
attack_length, fade_length))
{ {
Com_Printf("Pansen!\n"); Com_Printf("Pansen!\n");
FS_FreeFile(data); FS_FreeFile(data);
@ -870,6 +1075,8 @@ S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx,
vec3_t orientation, direction; vec3_t orientation, direction;
vec_t distance_direction; vec_t distance_direction;
int dir_x, dir_y, dir_z; int dir_x, dir_y, dir_z;
int effect_duration = 0;
int effect_volume = -1;
VectorSubtract(listener_forward, listener_up, orientation); VectorSubtract(listener_forward, listener_up, orientation);
@ -893,7 +1100,25 @@ S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx,
dir_y = 16 * orientation[1] * direction[1]; dir_y = 16 * orientation[1] * direction[1];
dir_z = 16 * orientation[2] * direction[2]; dir_z = 16 * orientation[2] * direction[2];
Haptic_Feedback(sfx->name, 16 - distance_direction / 32, dir_x, dir_y, dir_z); if (sfx->cache)
{
effect_duration = sfx->cache->length;
if (sfx->cache->stereo)
{
effect_duration /= 2;
}
/* sound near player has 16 points */
effect_volume = sfx->cache->volume / 16;
}
Haptic_Feedback(
sfx->name, (16 - distance_direction / 32) * effect_volume,
effect_duration,
sfx->cache->begin, sfx->cache->end,
sfx->cache->attack, sfx->cache->fade,
dir_x, dir_y, dir_z);
} }
ps->entnum = entnum; ps->entnum = entnum;
@ -1195,10 +1420,16 @@ S_SoundList(void)
{ {
size = sc->length * sc->width * (sc->stereo + 1); size = sc->length * sc->width * (sc->stereo + 1);
total += size; total += size;
Com_Printf("%s(%2db) %8i(%d ch) : %s\n", Com_Printf("%s(%2db) %8i(%d ch) %s %2.1f dB %.1fs:%.1f..%.1f..%.1f..%.1f\n",
sc->loopstart != -1 ? "L" : " ", sc->loopstart != -1 ? "L" : " ",
sc->width * 8, size, sc->width * 8, size,
(sc->stereo + 1), sfx->name); (sc->stereo + 1), sfx->name,
10 * log10((float)sc->volume / (2 << 15)),
(float)sc->length / 1000,
(float)sc->begin / 1000,
(float)sc->attack / 1000,
(float)sc->fade / 1000,
(float)sc->end / 1000);
} }
else else
{ {