diff --git a/src/FAudioGMS.c b/src/FAudioGMS.c index dfc64ed..7541012 100644 --- a/src/FAudioGMS.c +++ b/src/FAudioGMS.c @@ -121,6 +121,10 @@ typedef struct FAudioGMS_SoundInstance uint8_t isStatic; uint8_t destroyOnFinish; + uint8_t is3D; + F3DAUDIO_EMITTER* emitter; /* must not be NULL if is3D */ + float stereoAzimuth[2]; + union { FAudioGMS_StaticSound *staticSound; @@ -128,12 +132,20 @@ typedef struct FAudioGMS_SoundInstance } parent; } FAudioGMS_SoundInstance; +static const float DOPPLER_SCALE = 1.0f; +static const float CURVE_DISTANCE_SCALER = 1.0f; + typedef struct FAudioGMS_Device { FAudio* handle; + F3DAUDIO_HANDLE handle3D; + FAudioDeviceDetails deviceDetails; FAudioMasteringVoice *masteringVoice; + F3DAUDIO_LISTENER listener; + float speedOfSound; + FAudioGMS_StaticSound **staticSounds; uint32_t staticSoundCount; IdStack staticSoundIndexStack; @@ -143,9 +155,35 @@ typedef struct FAudioGMS_Device IdStack soundInstanceIndexStack; } FAudioGMS_Device; -static FAudioGMS_Device *device = NULL; +static FAudioGMS_Device* device = NULL; -void FAudioGMS_Init() +static inline FAudioGMS_StaticSound* FAudioGMS_INTERNAL_LookupStaticSound(uint32_t id) +{ + if (id >= 0 && id < device->staticSoundCount) + { + return device->staticSounds[id]; + } + else + { + Log("Invalid StaticSound ID!"); + return NULL; + } +} + +static inline FAudioGMS_SoundInstance* FAudioGMS_INTERNAL_LookupSoundInstance(uint32_t id) +{ + if (id >= 0 && id < device->soundInstanceCount) + { + return device->soundInstances[id]; + } + else + { + Log("Invalid SoundInstance ID!"); + return NULL; + } +} + +void FAudioGMS_Init(double speedOfSound) { device = SDL_malloc(sizeof(FAudioGMS_Device)); @@ -218,6 +256,28 @@ void FAudioGMS_Init() return; } + device->speedOfSound = speedOfSound; + + F3DAudioInitialize( + device->deviceDetails.OutputFormat.dwChannelMask, + device->speedOfSound, + device->handle3D + ); + + device->listener.OrientFront.x = 0; + device->listener.OrientFront.y = 0; + device->listener.OrientFront.z = -1; + device->listener.OrientTop.x = 0; + device->listener.OrientTop.y = 1; + device->listener.OrientTop.z = 0; + device->listener.Position.x = 0; + device->listener.Position.y = 0; + device->listener.Position.z = 0; + device->listener.Velocity.x = 0; + device->listener.Velocity.y = 0; + device->listener.Velocity.z = 0; + device->listener.pCone = NULL; + device->staticSounds = NULL; device->staticSoundCount = 0; IdStack_Init(&device->staticSoundIndexStack); @@ -240,6 +300,10 @@ static void FAudioGMS_INTERNAL_SoundInstance_Destroy(FAudioGMS_SoundInstance* in FAudioSourceVoice_Stop(instance->handle, 0, 0); FAudioSourceVoice_FlushSourceBuffers(instance->handle); FAudioVoice_DestroyVoice(instance->handle); + if (instance->is3D) + { + SDL_free(instance->emitter); + } SDL_free(instance->dspSettings.pMatrixCoefficients); SDL_free(instance); } @@ -247,12 +311,17 @@ static void FAudioGMS_INTERNAL_SoundInstance_Destroy(FAudioGMS_SoundInstance* in void FAudioGMS_SoundInstance_Destroy(double id) { - FAudioGMS_INTERNAL_SoundInstance_Destroy(device->soundInstances[(uint32_t)id]); + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_LookupSoundInstance((uint32_t)id); + + if (instance != NULL) + { + FAudioGMS_INTERNAL_SoundInstance_Destroy(instance); + } } void FAudioGMS_SoundInstance_DestroyWhenFinished(double id) { - FAudioGMS_SoundInstance *instance = device->soundInstances[(uint32_t)id]; + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_LookupSoundInstance((uint32_t)id); if (instance != NULL) { @@ -274,30 +343,13 @@ static void FAudioGMS_INTERNAL_StaticSound_Destroy(FAudioGMS_StaticSound* sound) void FAudioGMS_StaticSound_Destroy(double id) { - FAudioGMS_INTERNAL_StaticSound_Destroy(device->staticSounds[(uint32_t)id]); -} - -void FAudioGMS_Update() -{ - uint32_t i; - - for (i = 0; i < device->soundInstanceCount; i += 1) + if (id >= 0 && id < device->staticSoundCount) { - FAudioGMS_SoundInstance* instance = device->soundInstances[i]; - - if (instance != NULL) - { - if (instance->destroyOnFinish) - { - FAudioVoiceState state; - FAudioSourceVoice_GetState(instance->handle, &state, FAUDIO_VOICE_NOSAMPLESPLAYED); - - if (state.BuffersQueued == 0) - { - FAudioGMS_INTERNAL_SoundInstance_Destroy(instance); - } - } - } + FAudioGMS_INTERNAL_StaticSound_Destroy(device->staticSounds[(uint32_t)id]); + } + else + { + Log("Invalid ID for destroy!"); } } @@ -375,7 +427,7 @@ double FAudioGMS_StaticSound_LoadWAV(char *filePath) sound->buffer.LoopBegin = 0; sound->buffer.LoopCount = 0; sound->buffer.LoopLength = 0; - sound->buffer.pAudioData = pSampleData; + sound->buffer.pAudioData = (uint8_t*) pSampleData; sound->buffer.pContext = NULL; sound->buffer.PlayBegin = 0; sound->buffer.PlayLength = 0; @@ -563,25 +615,67 @@ static void FAudioGMS_INTERNAL_SoundInstance_SetPan(FAudioGMS_SoundInstance* ins ); } -static void FAudioGMS_INTERNAL_SoundInstance_SetPitch(FAudioGMS_SoundInstance* instance, float pitch) +static void FAudioGMS_INTERNAL_SoundInstance_UpdatePitch(FAudioGMS_SoundInstance* instance) { - instance->pitch = pitch; + float doppler = 1.0f; - pitch -= 1.0; /* default pitch is 1.0 as per GM standard */ - pitch = SDL_max(-1.0, SDL_min(1.0, pitch)); + if (!instance->is3D || DOPPLER_SCALE == 0.0f) + { + doppler = 1.0f; + } + else + { + doppler = instance->dspSettings.DopplerFactor * DOPPLER_SCALE; + } FAudioSourceVoice_SetFrequencyRatio( instance->handle, - SDL_powf(2.0f, pitch), + SDL_powf(2.0f, instance->pitch - 1) * doppler, /* FAudio expects pitch range to be -1.0 to 1.0 while GM uses 0.0 to 2.0 so we adjust here */ 0 ); } +static void FAudioGMS_INTERNAL_SoundInstance_SetPitch(FAudioGMS_SoundInstance* instance, float pitch) +{ + pitch = SDL_max(0.0, SDL_min(2.0, pitch)); + instance->pitch = pitch; + FAudioGMS_INTERNAL_SoundInstance_UpdatePitch(instance); +} + static void FAudioGMS_INTERNAL_SoundInstance_SetVolume(FAudioGMS_SoundInstance* instance, float volume) { FAudioVoice_SetVolume(instance->handle, volume, 0); } +static void FAudioGMS_INTERNAL_Apply3D(FAudioGMS_SoundInstance* instance) +{ + F3DAUDIO_EMITTER* emitter = instance->emitter; + + if (emitter == NULL) + { + Log("Sound instance does not have 3D data! Did you forget to initialize?"); + return; + } + + F3DAudioCalculate( + device->handle3D, + &device->listener, + emitter, + F3DAUDIO_CALCULATE_MATRIX | F3DAUDIO_CALCULATE_DOPPLER, + &instance->dspSettings + ); + + FAudioGMS_INTERNAL_SoundInstance_UpdatePitch(instance); + FAudioVoice_SetOutputMatrix( + instance->handle, + device->masteringVoice, + instance->dspSettings.SrcChannelCount, + instance->dspSettings.DstChannelCount, + instance->dspSettings.pMatrixCoefficients, + 0 + ); +} + static FAudioGMS_SoundInstance* FAudioGMS_INTERNAL_SoundInstance_CreateFromStaticSound( FAudioGMS_StaticSound *staticSound, double pan, @@ -646,6 +740,11 @@ static FAudioGMS_SoundInstance* FAudioGMS_INTERNAL_SoundInstance_CreateFromStati instance->soundState = SoundState_Stopped; + instance->is3D = 0; + instance->emitter = NULL; + instance->stereoAzimuth[0] = 0.0f; + instance->stereoAzimuth[1] = 0.0f; + if (device->soundInstanceIndexStack.count > 0) { instance->id = IdStack_Pop(&device->soundInstanceIndexStack); @@ -663,6 +762,47 @@ static FAudioGMS_SoundInstance* FAudioGMS_INTERNAL_SoundInstance_CreateFromStati return instance; } +static void FAudioGMS_INTERNAL_StaticSound_AddEmitter(FAudioGMS_SoundInstance* instance, float x, float y, float z) +{ + instance->is3D = 1; + + instance->emitter = SDL_malloc(sizeof(F3DAUDIO_EMITTER)); + + /* defaults based on XNA behavior */ + instance->emitter->pCone = NULL; + instance->emitter->ChannelRadius = 1.0f; + instance->emitter->pChannelAzimuths = instance->stereoAzimuth; + instance->emitter->pVolumeCurve = NULL; + instance->emitter->pLFECurve = NULL; + instance->emitter->pLPFDirectCurve = NULL; + instance->emitter->pLPFReverbCurve = NULL; + instance->emitter->pReverbCurve = NULL; + instance->emitter->InnerRadius = 0; + instance->emitter->InnerRadiusAngle = 0; + + instance->emitter->ChannelCount = instance->dspSettings.SrcChannelCount; + instance->emitter->CurveDistanceScaler = CURVE_DISTANCE_SCALER; + instance->emitter->DopplerScaler = 1.0f; + + instance->emitter->Position.x = x; + instance->emitter->Position.y = y; + instance->emitter->Position.z = z; + + instance->emitter->Velocity.x = 0; + instance->emitter->Velocity.y = 0; + instance->emitter->Velocity.z = 0; + + instance->emitter->OrientTop.x = 0; + instance->emitter->OrientTop.y = 1; + instance->emitter->OrientTop.z = 0; + + instance->emitter->OrientFront.x = 0; + instance->emitter->OrientFront.y = 0; + instance->emitter->OrientFront.z = 1; + + FAudioGMS_INTERNAL_Apply3D(instance); +} + static void FAudioGMS_INTERNAL_SoundInstance_Play(FAudioGMS_SoundInstance* instance) { if (instance->isStatic) @@ -693,7 +833,7 @@ static void FAudioGMS_INTERNAL_SoundInstance_Play(FAudioGMS_SoundInstance* insta void FAudioGMS_StaticSound_PlayOneOff(double staticSoundID, double pan, double pitch, double volume, double reverb) { - FAudioGMS_StaticSound* staticSound = device->staticSounds[(uint32_t)staticSoundID]; + FAudioGMS_StaticSound* staticSound = FAudioGMS_INTERNAL_LookupStaticSound((uint32_t)staticSoundID); if (staticSound != NULL) { @@ -707,9 +847,26 @@ void FAudioGMS_StaticSound_PlayOneOff(double staticSoundID, double pan, double p } } +void FAudioGMS_StaticSound_PlayOneOffSpatial(double staticSoundID, double x, double y, double z, double pitch, double volume, double reverb) +{ + FAudioGMS_StaticSound* staticSound = FAudioGMS_INTERNAL_LookupStaticSound((uint32_t)staticSoundID); + + if (staticSound != NULL) + { + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_SoundInstance_CreateFromStaticSound(staticSound, 0, pitch, volume, reverb); + instance->destroyOnFinish = 1; + FAudioGMS_INTERNAL_StaticSound_AddEmitter(instance, x, y, z); + FAudioGMS_INTERNAL_SoundInstance_Play(instance); + } + else + { + Log("StaticSound_PlayOneOffSpatial: Invalid static sound ID! Did you destroy this sound?"); + } +} + double FAudioGMS_StaticSound_Play(double staticSoundID, double pan, double pitch, double volume, double reverb, double loop) { - FAudioGMS_StaticSound* staticSound = device->staticSounds[(uint32_t)staticSoundID]; + FAudioGMS_StaticSound* staticSound = FAudioGMS_INTERNAL_LookupStaticSound((uint32_t)staticSoundID); if (staticSound != NULL) { @@ -725,9 +882,28 @@ double FAudioGMS_StaticSound_Play(double staticSoundID, double pan, double pitch } } +double FAudioGMS_StaticSound_PlaySpatial(double staticSoundID, double x, double y, double z, double pitch, double volume, double reverb, double loop) +{ + FAudioGMS_StaticSound* staticSound = FAudioGMS_INTERNAL_LookupStaticSound((uint32_t)staticSoundID); + + if (staticSound != NULL) + { + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_SoundInstance_CreateFromStaticSound(staticSound, 0, pitch, volume, reverb); + instance->loop = (uint8_t)loop; + FAudioGMS_INTERNAL_StaticSound_AddEmitter(instance, x, y, z); + FAudioGMS_INTERNAL_SoundInstance_Play(instance); + return (double)instance->id; + } + else + { + Log("StaticSound_PlaySpatial: Invalid static sound ID! Did you destroy this sound?"); + return -1; + } +} + void FAudioGMS_SoundInstance_Play(double id) { - FAudioGMS_SoundInstance *instance = device->soundInstances[(uint32_t)id]; + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_LookupSoundInstance((uint32_t)id); if (instance != NULL) { @@ -737,7 +913,7 @@ void FAudioGMS_SoundInstance_Play(double id) void FAudioGMS_SoundInstance_Pause(double id) { - FAudioGMS_SoundInstance* instance = device->soundInstances[(uint32_t)id]; + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_LookupSoundInstance((uint32_t)id); if (instance != NULL) { @@ -758,7 +934,7 @@ void FAudioGMS_SoundInstance_Pause(double id) void FAudioGMS_SoundInstance_Stop(double id) { - FAudioGMS_SoundInstance* instance = device->soundInstances[(uint32_t)id]; + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_LookupSoundInstance((uint32_t)id); if (instance != NULL) { @@ -773,19 +949,82 @@ void FAudioGMS_SoundInstance_Stop(double id) } } +void FAudioGMS_SoundInstance_Set3DPosition(double soundInstanceID, double x, double y, double z) +{ + FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_LookupSoundInstance((uint32_t)soundInstanceID); + + if (instance != NULL) + { + if (!instance->is3D) + { + Log("Not a 3D sound!"); + return; + } + + instance->emitter->Velocity.x = x - instance->emitter->Position.x; + instance->emitter->Velocity.y = y - instance->emitter->Position.y; + instance->emitter->Velocity.z = z - instance->emitter->Position.z; + + instance->emitter->Position.x = x; + instance->emitter->Position.y = y; + instance->emitter->Position.z = z; + } +} + +void FAudioGMS_SetListenerPosition(double x, double y, double z) +{ + device->listener.Velocity.x = x - device->listener.Position.x; + device->listener.Velocity.y = y - device->listener.Position.y; + device->listener.Velocity.z = z - device->listener.Position.z; + + device->listener.Position.x = x; + device->listener.Position.y = y; + device->listener.Position.z = z; +} + +void FAudioGMS_Update() +{ + uint32_t i; + + for (i = 0; i < device->soundInstanceCount; i += 1) + { + FAudioGMS_SoundInstance* instance = device->soundInstances[i]; + + if (instance != NULL) + { + if (instance->is3D) + { + FAudioGMS_INTERNAL_Apply3D(instance); + } + + if (instance->destroyOnFinish) + { + FAudioVoiceState state; + FAudioSourceVoice_GetState(instance->handle, &state, FAUDIO_VOICE_NOSAMPLESPLAYED); + + if (state.BuffersQueued == 0) + { + FAudioGMS_INTERNAL_SoundInstance_Destroy(instance); + } + } + } + } +} + void FAudioGMS_Destroy() { uint32_t i = 0; - for (i = 0; i < device->staticSoundCount; i += 1) - { - FAudioGMS_INTERNAL_StaticSound_Destroy(device->staticSounds[i]); - } for (i = 0; i < device->soundInstanceCount; i += 1) { FAudioGMS_INTERNAL_SoundInstance_Destroy(device->soundInstances[i]); } + for (i = 0; i < device->staticSoundCount; i += 1) + { + FAudioGMS_INTERNAL_StaticSound_Destroy(device->staticSounds[i]); + } + FAudio_Release(device->handle); SDL_free(device); device = NULL; diff --git a/src/FAudioGMS.h b/src/FAudioGMS.h index 9fedcda..afdbf2c 100644 --- a/src/FAudioGMS.h +++ b/src/FAudioGMS.h @@ -39,20 +39,25 @@ extern "C" { #endif /* __cplusplus */ -FAUDIOGMSAPI void FAudioGMS_Init(); -FAUDIOGMSAPI void FAudioGMS_Update(); +FAUDIOGMSAPI void FAudioGMS_Init(double speedOfSound); /* recommend about 1500? for 2D game */ FAUDIOGMSAPI double FAudioGMS_StaticSound_LoadWAV(char *filePath); /* returns a static sound ID */ FAUDIOGMSAPI void FAudioGMS_StaticSound_PlayOneOff(double staticSoundID, double pan, double pitch, double volume, double reverb); /* automatically frees itself when done! */ +FAUDIOGMSAPI void FAudioGMS_StaticSound_PlayOneOffSpatial(double staticSoundID, double x, double y, double z, double pitch, double volume, double reverb); /* automatically frees itself when done! */ FAUDIOGMSAPI double FAudioGMS_StaticSound_Play(double staticSoundID, double pan, double pitch, double volume, double reverb, double loop); /* returns a sound instance ID. must be freed! */ +FAUDIOGMSAPI double FAudioGMS_StaticSound_PlaySpatial(double staticSoundID, double x, double y, double z, double pitch, double volume, double reverb, double loop); /* returns a sound instance ID. must be freed! */ +FAUDIOGMSAPI void FAudioGMS_StaticSound_Destroy(double id); FAUDIOGMSAPI void FAudioGMS_SoundInstance_Play(double id); FAUDIOGMSAPI void FAudioGMS_SoundInstance_Pause(double id); FAUDIOGMSAPI void FAudioGMS_SoundInstance_Stop(double id); - +FAUDIOGMSAPI void FAudioGMS_SoundInstance_Set3DPosition(double soundInstanceID, double x, double y, double z); FAUDIOGMSAPI void FAudioGMS_SoundInstance_Destroy(double id); FAUDIOGMSAPI void FAudioGMS_SoundInstance_DestroyWhenFinished(double id); -FAUDIOGMSAPI void FAudioGMS_StaticSound_Destroy(double id); + +FAUDIOGMSAPI void FAudioGMS_SetListenerPosition(double x, double y, double z); + +FAUDIOGMSAPI void FAudioGMS_Update(); FAUDIOGMSAPI void FAudioGMS_Destroy(); #ifdef __cplusplus