Implement doppler effect

This commit is contained in:
James Lambert 2023-02-13 22:00:24 -07:00
parent f612ba79fa
commit 8c14455096
7 changed files with 81 additions and 36 deletions

View file

@ -12,19 +12,24 @@ ALSndPlayer gSoundPlayer;
#define SOUND_FLAGS_3D (1 << 0)
#define SOUND_FLAGS_LOOPING (1 << 1)
#define SOUND_HAS_STARTED (1 << 2)
#define SPEED_OF_SOUND 343.2f
struct ActiveSound {
ALSndId soundId;
u16 flags;
u16 newSoundTicks;
float estimatedTimeLeft;
struct Vector3 pos3D;
struct Vector3 velocity3D;
float volume;
float basePitch;
};
struct SoundListener {
struct Vector3 worldPos;
struct Vector3 rightVector;
struct Vector3 velocity;
};
struct ActiveSound gActiveSounds[MAX_ACTIVE_SOUNDS];
@ -33,7 +38,7 @@ int gActiveSoundCount = 0;
struct SoundListener gSoundListeners[MAX_SOUND_LISTENERS];
int gActiveListenerCount = 0;
void soundPlayerDetermine3DSound(struct Vector3* at, float* volumeIn, float* volumeOut, int* panOut) {
void soundPlayerDetermine3DSound(struct Vector3* at, struct Vector3* velocity, float* volumeIn, float* volumeOut, int* panOut, float* pitchBend) {
if (!gActiveListenerCount) {
*volumeOut = *volumeIn;
*panOut = 64;
@ -69,7 +74,16 @@ void soundPlayerDetermine3DSound(struct Vector3* at, float* volumeIn, float* vol
struct Vector3 offset;
vector3Sub(at, &nearestListener->worldPos, &offset);
float pan = -vector3Dot(&offset, &nearestListener->rightVector) / sqrtf(distance);
struct Vector3 relativeVelocity;
vector3Sub(velocity, &nearestListener->velocity, &relativeVelocity);
float invDist = 1.0f / sqrtf(distance);
float directionalVelocity = -vector3Dot(&offset, &relativeVelocity) * invDist;
*pitchBend = (SPEED_OF_SOUND + directionalVelocity) * (1.0f / SPEED_OF_SOUND);
float pan = -vector3Dot(&offset, &nearestListener->rightVector) * invDist;
pan = pan * 64.0f + 64.0f;
@ -132,7 +146,7 @@ float soundPlayerEstimateLength(ALSound* sound, float speed) {
return sampleCount * (1.0f / OUTPUT_RATE) / speed;
}
ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch, struct Vector3* at) {
ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch, struct Vector3* at, struct Vector3* velocity) {
if (gActiveSoundCount == MAX_ACTIVE_SOUNDS || soundClipId < 0 || soundClipId >= gSoundClipArray->soundCount) {
return SOUND_ID_NONE;
}
@ -148,10 +162,10 @@ ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch, struct Vecto
struct ActiveSound* sound = &gActiveSounds[gActiveSoundCount];
sound->soundId = result;
sound->newSoundTicks = 10;
sound->flags = 0;
sound->estimatedTimeLeft = soundPlayerEstimateLength(alSound, pitch);
sound->volume = volume;
sound->basePitch = pitch;
float startingVolume = volume;
int panning = 64;
@ -159,7 +173,10 @@ ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch, struct Vecto
if (at) {
sound->flags |= SOUND_FLAGS_3D;
sound->pos3D = *at;
soundPlayerDetermine3DSound(at, &startingVolume, &startingVolume, &panning);
sound->velocity3D = *velocity;
float pitchBend;
soundPlayerDetermine3DSound(at, velocity, &startingVolume, &startingVolume, &panning, &pitchBend);
pitch = pitch * pitchBend;
}
if (soundPlayerIsLooped(alSound)) {
@ -184,25 +201,29 @@ void soundPlayerUpdate() {
while (index < gActiveSoundCount) {
struct ActiveSound* sound = &gActiveSounds[index];
if (sound->newSoundTicks) {
--sound->newSoundTicks;
}
sound->estimatedTimeLeft -= FIXED_DELTA_TIME;
alSndpSetSound(&gSoundPlayer, sound->soundId);
if (alSndpGetState(&gSoundPlayer) == AL_STOPPED && !sound->newSoundTicks) {
int soundState = alSndpGetState(&gSoundPlayer);
if (soundState == AL_STOPPED && (sound->flags & SOUND_HAS_STARTED) != 0) {
alSndpDeallocate(&gSoundPlayer, sound->soundId);
sound->soundId = SOUND_ID_NONE;
} else {
if (soundState == AL_PLAYING || sound->estimatedTimeLeft < 0.0) {
sound->flags |= SOUND_HAS_STARTED;
}
if (sound->flags & SOUND_FLAGS_3D) {
float volume;
float pitch;
int panning;
soundPlayerDetermine3DSound(&sound->pos3D, &sound->volume, &volume, &panning);
soundPlayerDetermine3DSound(&sound->pos3D, &sound->velocity3D, &sound->volume, &volume, &panning, &pitch);
alSndpSetVol(&gSoundPlayer, (short)(32767 * volume));
alSndpSetPan(&gSoundPlayer, panning);
alSndpSetPitch(&gSoundPlayer, sound->basePitch * pitch);
}
++writeIndex;
@ -254,12 +275,13 @@ void soundPlayerStopAll() {
}
}
void soundPlayerUpdatePosition(ALSndId soundId, struct Vector3* at) {
void soundPlayerUpdatePosition(ALSndId soundId, struct Vector3* at, struct Vector3* velocity) {
struct ActiveSound* activeSound = soundPlayerFindActiveSound(soundId);
if (activeSound) {
activeSound->flags |= SOUND_FLAGS_3D;
activeSound->pos3D = *at;
activeSound->velocity3D = *velocity;
}
}
@ -270,7 +292,7 @@ int soundPlayerIsPlaying(ALSndId soundId) {
return 0;
}
if (activeSound->newSoundTicks) {
if (!(activeSound->flags & SOUND_HAS_STARTED)) {
return 1;
}
@ -278,8 +300,9 @@ int soundPlayerIsPlaying(ALSndId soundId) {
return activeSound->estimatedTimeLeft > 0.0f && alSndpGetState(&gSoundPlayer) != AL_STOPPED;
}
void soundListenerUpdate(struct Vector3* position, struct Quaternion* rotation, int listenerIndex) {
void soundListenerUpdate(struct Vector3* position, struct Quaternion* rotation, struct Vector3* velocity, int listenerIndex) {
gSoundListeners[listenerIndex].worldPos = *position;
gSoundListeners[listenerIndex].velocity = *velocity;
quatMultVector(rotation, &gRight, &gSoundListeners[listenerIndex].rightVector);
}

View file

@ -20,15 +20,15 @@ extern char _soundsTblSegmentRomEnd[];
void soundPlayerInit();
void soundPlayerUpdate();
ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch, struct Vector3* at);
ALSndId soundPlayerPlay(int soundClipId, float volume, float pitch, struct Vector3* at, struct Vector3* velocity);
void soundPlayerStop(ALSndId soundId);
void soundPlayerStopAll();
void soundPlayerUpdatePosition(ALSndId soundId, struct Vector3* at);
void soundPlayerUpdatePosition(ALSndId soundId, struct Vector3* at, struct Vector3* velocity);
int soundPlayerIsPlaying(ALSndId soundId);
void soundListenerUpdate(struct Vector3* position, struct Quaternion* rotation, int listenerIndex);
void soundListenerUpdate(struct Vector3* position, struct Quaternion* rotation, struct Vector3* velocity, int listenerIndex);
void soundListenerSetCount(int count);
#endif

View file

@ -95,7 +95,7 @@ void decorObjectInit(struct DecorObject* object, struct DecorObjectDefinition* d
dynamicSceneSetRoomFlags(object->dynamicId, ROOM_FLAG_FROM_INDEX(room));
if (definition->soundClipId != -1) {
object->playingSound = soundPlayerPlay(definition->soundClipId, 1.0f, 1.0f, &object->rigidBody.transform.position);
object->playingSound = soundPlayerPlay(definition->soundClipId, 1.0f, 1.0f, &object->rigidBody.transform.position, &object->rigidBody.velocity);
} else {
object->playingSound = SOUND_ID_NONE;
}
@ -113,7 +113,11 @@ void decorObjectDelete(struct DecorObject* decorObject) {
int decorObjectUpdate(struct DecorObject* decorObject) {
if (decorObject->playingSound != SOUND_ID_NONE) {
soundPlayerUpdatePosition(decorObject->playingSound, &decorObject->rigidBody.transform.position);
soundPlayerUpdatePosition(
decorObject->playingSound,
&decorObject->rigidBody.transform.position,
&decorObject->rigidBody.velocity
);
}
if (decorObject->rigidBody.flags & RigidBodyFizzled) {

View file

@ -95,6 +95,7 @@ ALSndId cutsceneRunnerPlaySound(struct CutsceneStep* step) {
step->playSound.soundId,
step->playSound.volume * (1.0f / 255.0f),
step->playSound.pitch * (1.0f / 64.0f),
NULL,
NULL
);
}
@ -331,7 +332,7 @@ void cutscenesUpdateSounds() {
if (gCutsceneSoundQueues[i]) {
struct QueuedSound* curr = gCutsceneSoundQueues[i];
gCutsceneCurrentSound[i] = soundPlayerPlay(curr->soundId, curr->volume, gCutsceneChannelPitch[i], NULL);
gCutsceneCurrentSound[i] = soundPlayerPlay(curr->soundId, curr->volume, gCutsceneChannelPitch[i], NULL, NULL);
gCutsceneSoundQueues[i] = curr->next;
curr->next = gCutsceneNextFreeSound;

View file

@ -533,8 +533,8 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) {
quatIdent(&player->body.transform.rotation);
if (didPassThroughPortal) {
soundPlayerPlay(soundsPortalEnter[didPassThroughPortal - 1], 0.75f, 1.0f, NULL);
soundPlayerPlay(soundsPortalExit[2 - didPassThroughPortal], 0.75f, 1.0f, NULL);
soundPlayerPlay(soundsPortalEnter[didPassThroughPortal - 1], 0.75f, 1.0f, NULL, NULL);
soundPlayerPlay(soundsPortalExit[2 - didPassThroughPortal], 0.75f, 1.0f, NULL, NULL);
}
OSContPad* controllerInput = controllersGetControllerData(0);

View file

@ -169,7 +169,7 @@ int elevatorUpdate(struct Elevator* elevator, struct Player* player) {
}
if ((elevator->openAmount == 0.0f && shouldBeOpen) || (elevator->openAmount && !shouldBeOpen)) {
soundPlayerPlay(soundsElevatorDoor, 1.0f, 0.5f, &elevator->rigidBody.transform.position);
soundPlayerPlay(soundsElevatorDoor, 1.0f, 0.5f, &elevator->rigidBody.transform.position, &gZeroVec);
}
elevator->openAmount = mathfMoveTowards(elevator->openAmount, shouldBeOpen ? 1.0f : 0.0f, OPEN_SPEED * FIXED_DELTA_TIME);

View file

@ -272,12 +272,12 @@ void sceneCheckPortals(struct Scene* scene) {
if (controllerGetButtonDown(0, Z_TRIG) && (scene->player.flags & PlayerHasSecondPortalGun)) {
sceneFirePortal(scene, &raycastRay, &playerUp, 0, scene->player.body.currentRoom, 1);
soundPlayerPlay(soundsPortalgunShoot[0], 1.0f, 1.0f, NULL);
soundPlayerPlay(soundsPortalgunShoot[0], 1.0f, 1.0f, NULL, NULL);
}
if (controllerGetButtonDown(0, R_TRIG | L_TRIG) && (scene->player.flags & PlayerHasFirstPortalGun)) {
sceneFirePortal(scene, &raycastRay, &playerUp, 1, scene->player.body.currentRoom, 1);
soundPlayerPlay(soundsPortalgunShoot[1], 1.0f, 1.0f, NULL);
soundPlayerPlay(soundsPortalgunShoot[1], 1.0f, 1.0f, NULL, NULL);
}
if (scene->player.body.flags & RigidBodyFizzled) {
@ -298,7 +298,13 @@ void sceneCheckPortals(struct Scene* scene) {
portalCheckForHoles(scene->portals);
}
void sceneUpdatePortalListener(struct Scene* scene, int portalIndex, int listenerIndex) {
#define MAX_LISTEN_THROUGH_PORTAL_DISTANCE 3.0f
int sceneUpdatePortalListener(struct Scene* scene, int portalIndex, int listenerIndex) {
if (vector3DistSqrd(&scene->player.lookTransform.position, &scene->portals[portalIndex].transform.position) > MAX_LISTEN_THROUGH_PORTAL_DISTANCE * MAX_LISTEN_THROUGH_PORTAL_DISTANCE) {
return 0;
}
struct Transform otherInverse;
transformInvert(&scene->portals[1 - portalIndex].transform, &otherInverse);
struct Transform portalCombined;
@ -307,19 +313,30 @@ void sceneUpdatePortalListener(struct Scene* scene, int portalIndex, int listene
struct Transform relativeTransform;
transformConcat(&portalCombined, &scene->player.lookTransform, &relativeTransform);
soundListenerUpdate(&relativeTransform.position, &relativeTransform.rotation, listenerIndex);
struct Vector3 velocity;
quatMultVector(&relativeTransform.rotation, &scene->player.body.velocity, &velocity);
soundListenerUpdate(&relativeTransform.position, &relativeTransform.rotation, &velocity, listenerIndex);
return 1;
}
void sceneUpdateListeners(struct Scene* scene) {
soundListenerUpdate(&scene->player.lookTransform.position, &scene->player.lookTransform.rotation, 0);
soundListenerUpdate(&scene->player.lookTransform.position, &scene->player.lookTransform.rotation, &scene->player.body.velocity, 0);
int listenerCount = 1;
if (collisionSceneIsPortalOpen()) {
soundListenerSetCount(3);
sceneUpdatePortalListener(scene, 0, 1);
sceneUpdatePortalListener(scene, 1, 2);
} else {
soundListenerSetCount(1);
if (sceneUpdatePortalListener(scene, 0, listenerCount)) {
++listenerCount;
}
if (sceneUpdatePortalListener(scene, 1, listenerCount)) {
++listenerCount;
}
}
soundListenerSetCount(listenerCount);
}
struct Transform gRelativeElevatorTransform = {
@ -551,7 +568,7 @@ int sceneOpenPortal(struct Scene* scene, struct Transform* at, int transformInde
struct Portal* portal = &scene->portals[portalIndex];
if (portalAttachToSurface(portal, existingSurface, surfaceIndex, &finalAt)) {
soundPlayerPlay(soundsPortalOpen2, 1.0f, 1.0f, &at->position);
soundPlayerPlay(soundsPortalOpen2, 1.0f, 1.0f, &at->position, &gZeroVec);
// the portal position may have been adjusted
if (transformIndex != NO_TRANSFORM_INDEX) {
@ -660,7 +677,7 @@ int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* player
void sceneClosePortal(struct Scene* scene, int portalIndex) {
if (gCollisionScene.portalTransforms[portalIndex]) {
soundPlayerPlay(soundsPortalFizzle, 1.0f, 1.0f, &gCollisionScene.portalTransforms[portalIndex]->position);
soundPlayerPlay(soundsPortalFizzle, 1.0f, 1.0f, &gCollisionScene.portalTransforms[portalIndex]->position, &gZeroVec);
gCollisionScene.portalTransforms[portalIndex] = NULL;
scene->portals[portalIndex].flags |= PortalFlagsNeedsNewHole;
scene->portals[portalIndex].portalSurfaceIndex = -1;