From 8c14455096b609084d5d17fed76aba135853d4f8 Mon Sep 17 00:00:00 2001 From: James Lambert Date: Mon, 13 Feb 2023 22:00:24 -0700 Subject: [PATCH] Implement doppler effect --- src/audio/soundplayer.c | 53 ++++++++++++++++++++++++++---------- src/audio/soundplayer.h | 6 ++-- src/decor/decor_object.c | 8 ++++-- src/levels/cutscene_runner.c | 3 +- src/player/player.c | 4 +-- src/scene/elevator.c | 2 +- src/scene/scene.c | 41 ++++++++++++++++++++-------- 7 files changed, 81 insertions(+), 36 deletions(-) diff --git a/src/audio/soundplayer.c b/src/audio/soundplayer.c index 2ddb7f5..e31ff59 100644 --- a/src/audio/soundplayer.c +++ b/src/audio/soundplayer.c @@ -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); } diff --git a/src/audio/soundplayer.h b/src/audio/soundplayer.h index 3bf623b..e309cd2 100644 --- a/src/audio/soundplayer.h +++ b/src/audio/soundplayer.h @@ -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 \ No newline at end of file diff --git a/src/decor/decor_object.c b/src/decor/decor_object.c index 62db85c..fc0b073 100644 --- a/src/decor/decor_object.c +++ b/src/decor/decor_object.c @@ -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) { diff --git a/src/levels/cutscene_runner.c b/src/levels/cutscene_runner.c index b316144..9b3da9b 100644 --- a/src/levels/cutscene_runner.c +++ b/src/levels/cutscene_runner.c @@ -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; diff --git a/src/player/player.c b/src/player/player.c index 50273e8..6277eef 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -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); diff --git a/src/scene/elevator.c b/src/scene/elevator.c index 19ea59a..7b19bad 100644 --- a/src/scene/elevator.c +++ b/src/scene/elevator.c @@ -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); diff --git a/src/scene/scene.c b/src/scene/scene.c index 27b46ab..ff74c16 100644 --- a/src/scene/scene.c +++ b/src/scene/scene.c @@ -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;