forked from MoonsideGames/FAudioGMS
736 lines
21 KiB
C
736 lines
21 KiB
C
/* FAudioGMS - Game Maker FAudio bindings in C
|
|
*
|
|
* Copyright (c) 2021 Evan Hemsley
|
|
*
|
|
* This software is provided 'as-is', without any express or implied warranty.
|
|
* In no event will the authors be held liable for any damages arising from
|
|
* the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software in a
|
|
* product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
*
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
*
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
* Evan "cosmonaut" Hemsley <evan@moonside.games>
|
|
*
|
|
*/
|
|
|
|
#include "../lib/SDL/include/SDL.h"
|
|
|
|
#include "FAudioGMS.h"
|
|
#include "../lib/FAudio/include/FAPOBase.h"
|
|
#include "../lib/FAudio/include/FAudioFX.h"
|
|
#include "../lib/FAudio/include/F3DAudio.h"
|
|
#include "../lib/FAudio/include/FAudio.h"
|
|
|
|
#define DR_WAV_IMPLEMENTATION
|
|
#include "../lib/dr_wav.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
static inline void Log(char *string)
|
|
{
|
|
printf("%s\n", string);
|
|
fflush(stdout);
|
|
}
|
|
|
|
typedef enum FAudioGMS_SoundState
|
|
{
|
|
SoundState_Playing,
|
|
SoundState_Paused,
|
|
SoundState_Stopped
|
|
} FAudioGMS_SoundState;
|
|
|
|
typedef struct FAudioGMS_StaticSound
|
|
{
|
|
uint32_t id;
|
|
FAudioBuffer buffer;
|
|
uint32_t channels;
|
|
uint32_t samplesPerSecond;
|
|
uint32_t loopStart;
|
|
uint32_t loopLength;
|
|
} FAudioGMS_StaticSound;
|
|
|
|
typedef struct FAudioGMS_SoundInstance
|
|
{
|
|
uint32_t id;
|
|
FAudioSourceVoice *handle;
|
|
FAudioWaveFormatEx format;
|
|
uint8_t loop; /* bool */
|
|
FAudioGMS_SoundState soundState;
|
|
F3DAUDIO_DSP_SETTINGS dspSettings;
|
|
|
|
float pan;
|
|
float pitch;
|
|
float volume;
|
|
float reverb;
|
|
float lowPassFilter;
|
|
float highPassFilter;
|
|
float bandPassFilter;
|
|
|
|
uint8_t reverbAttached;
|
|
FAudioSubmixVoice* reverbVoice;
|
|
FAudioVoiceSends reverbSends;
|
|
|
|
uint8_t isStatic;
|
|
uint8_t destroyOnFinish;
|
|
|
|
union
|
|
{
|
|
FAudioGMS_StaticSound *staticSound;
|
|
/* TODO: streaming */
|
|
} parent;
|
|
} FAudioGMS_SoundInstance;
|
|
|
|
typedef struct FAudioGMS_Device
|
|
{
|
|
FAudio* handle;
|
|
FAudioDeviceDetails deviceDetails;
|
|
FAudioMasteringVoice *masteringVoice;
|
|
|
|
FAudioGMS_StaticSound **staticSounds;
|
|
uint32_t staticSoundCount;
|
|
|
|
FAudioGMS_SoundInstance **soundInstances;
|
|
uint32_t soundInstanceCount;
|
|
} FAudioGMS_Device;
|
|
|
|
static FAudioGMS_Device *device = NULL;
|
|
|
|
void FAudioGMS_Init()
|
|
{
|
|
device = SDL_malloc(sizeof(FAudioGMS_Device));
|
|
|
|
uint32_t result = FAudioCreate(&device->handle, 0, FAUDIO_DEFAULT_PROCESSOR);
|
|
|
|
if (result != 0)
|
|
{
|
|
Log("Failed to create device! Bailing!");
|
|
SDL_free(device);
|
|
device = NULL;
|
|
return;
|
|
}
|
|
|
|
/* Find a suitable device */
|
|
|
|
uint32_t deviceCount;
|
|
FAudio_GetDeviceCount(device->handle, &deviceCount);
|
|
|
|
if (deviceCount == 0)
|
|
{
|
|
Log("No audio devices found! Bailing!");
|
|
FAudio_Release(device->handle);
|
|
SDL_free(device);
|
|
device = NULL;
|
|
return;
|
|
}
|
|
|
|
FAudioDeviceDetails deviceDetails;
|
|
|
|
uint32_t i = 0;
|
|
for (i = 0; i < deviceCount; i += 1)
|
|
{
|
|
FAudio_GetDeviceDetails(
|
|
device->handle,
|
|
i,
|
|
&deviceDetails);
|
|
|
|
if ((deviceDetails.Role & FAudioDefaultGameDevice) == FAudioDefaultGameDevice)
|
|
{
|
|
device->deviceDetails = deviceDetails;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == deviceCount)
|
|
{
|
|
i = 0; /* whatever we'll just use the first one i guess */
|
|
FAudio_GetDeviceDetails(
|
|
device->handle,
|
|
i,
|
|
&deviceDetails);
|
|
|
|
device->deviceDetails = deviceDetails;
|
|
}
|
|
|
|
if (FAudio_CreateMasteringVoice(
|
|
device->handle,
|
|
&device->masteringVoice,
|
|
FAUDIO_DEFAULT_CHANNELS,
|
|
FAUDIO_DEFAULT_SAMPLERATE,
|
|
0,
|
|
i,
|
|
NULL
|
|
) != 0)
|
|
{
|
|
Log("No mastering voice found! Bailing!");
|
|
FAudio_Release(device->handle);
|
|
SDL_free(device);
|
|
device = NULL;
|
|
return;
|
|
}
|
|
|
|
device->staticSounds = NULL;
|
|
device->staticSoundCount = 0;
|
|
|
|
device->soundInstances = NULL;
|
|
device->soundInstanceCount = 0;
|
|
|
|
Log("FAudio initialized successfully!");
|
|
printf("Device: %ls", device->deviceDetails.DisplayName);
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_Destroy(FAudioGMS_SoundInstance* instance)
|
|
{
|
|
if (instance != NULL)
|
|
{
|
|
device->soundInstances[instance->id] = NULL;
|
|
FAudioSourceVoice_Stop(instance->handle, 0, 0);
|
|
FAudioSourceVoice_FlushSourceBuffers(instance->handle);
|
|
FAudioVoice_DestroyVoice(instance->handle);
|
|
SDL_free(instance->dspSettings.pMatrixCoefficients);
|
|
SDL_free(instance);
|
|
}
|
|
}
|
|
|
|
void FAudioGMS_SoundInstance_Destroy(double id)
|
|
{
|
|
FAudioGMS_INTERNAL_SoundInstance_Destroy(device->soundInstances[(uint32_t)id]);
|
|
}
|
|
|
|
void FAudioGMS_SoundInstance_DestroyWhenFinished(double id)
|
|
{
|
|
FAudioGMS_SoundInstance *instance = device->soundInstances[(uint32_t)id];
|
|
|
|
if (instance != NULL)
|
|
{
|
|
instance->destroyOnFinish = 1;
|
|
}
|
|
}
|
|
|
|
/* FIXME: this will die horribly if a sound is playing */
|
|
static void FAudioGMS_INTERNAL_StaticSound_Destroy(FAudioGMS_StaticSound* sound)
|
|
{
|
|
if (sound != NULL)
|
|
{
|
|
device->staticSounds[sound->id] = NULL;
|
|
SDL_free((void*)sound->buffer.pAudioData);
|
|
SDL_free(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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Taken from https://github.com/FNA-XNA/FNA/blob/master/src/Audio/SoundEffectInstance.cs */
|
|
static void SetPanMatrixCoefficients(FAudioGMS_SoundInstance *instance)
|
|
{
|
|
/* Two major things to notice:
|
|
* 1. The spec assumes any speaker count >= 2 has Front Left/Right.
|
|
* 2. Stereo panning is WAY more complicated than you think.
|
|
* The main thing is that hard panning does NOT eliminate an
|
|
* entire channel; the two channels are blended on each side.
|
|
* -flibit
|
|
*/
|
|
|
|
float* outputMatrix = (float*) instance->dspSettings.pMatrixCoefficients;
|
|
if (instance->dspSettings.SrcChannelCount == 1)
|
|
{
|
|
if (instance->dspSettings.DstChannelCount == 1)
|
|
{
|
|
outputMatrix[0] = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
outputMatrix[0] = (instance->pan > 0.0f) ? (1.0f - instance->pan) : 1.0f;
|
|
outputMatrix[1] = (instance->pan < 0.0f) ? (1.0f + instance->pan) : 1.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (instance->dspSettings.DstChannelCount == 1)
|
|
{
|
|
outputMatrix[0] = 1.0f;
|
|
outputMatrix[1] = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
if (instance->pan <= 0.0f)
|
|
{
|
|
// Left speaker blends left/right channels
|
|
outputMatrix[0] = 0.5f * instance->pan + 1.0f;
|
|
outputMatrix[1] = 0.5f * -instance->pan;
|
|
// Right speaker gets less of the right channel
|
|
outputMatrix[2] = 0.0f;
|
|
outputMatrix[3] = instance->pan + 1.0f;
|
|
}
|
|
else
|
|
{
|
|
// Left speaker gets less of the left channel
|
|
outputMatrix[0] = -instance->pan + 1.0f;
|
|
outputMatrix[1] = 0.0f;
|
|
// Right speaker blends right/left channels
|
|
outputMatrix[2] = 0.5f * instance->pan;
|
|
outputMatrix[3] = 0.5f * -instance->pan + 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
double FAudioGMS_StaticSound_LoadWAV(char *filePath)
|
|
{
|
|
drwav_uint64 frameCount;
|
|
|
|
FAudioGMS_StaticSound *sound = SDL_malloc(sizeof(FAudioGMS_StaticSound));
|
|
float *pSampleData = drwav_open_file_and_read_pcm_frames_f32(filePath, &sound->channels, &sound->samplesPerSecond, &frameCount, NULL);
|
|
if (pSampleData == NULL)
|
|
{
|
|
Log("Error opening WAV file: ");
|
|
Log(filePath);
|
|
}
|
|
|
|
sound->buffer.AudioBytes = (uint32_t)(frameCount * sizeof(float));
|
|
sound->buffer.Flags = FAUDIO_END_OF_STREAM;
|
|
sound->buffer.LoopBegin = 0;
|
|
sound->buffer.LoopCount = 0;
|
|
sound->buffer.LoopLength = 0;
|
|
sound->buffer.pAudioData = (uint8_t*) pSampleData;
|
|
sound->buffer.pContext = NULL;
|
|
sound->buffer.PlayBegin = 0;
|
|
sound->buffer.PlayLength = 0;
|
|
|
|
sound->loopStart = 0;
|
|
sound->loopLength = 0;
|
|
|
|
/* FIXME: id re-use system */
|
|
sound->id = device->staticSoundCount;
|
|
|
|
device->staticSounds = SDL_realloc(device->staticSounds, (device->staticSoundCount + 1) * sizeof(FAudioGMS_StaticSound*));
|
|
device->staticSounds[device->staticSoundCount] = sound;
|
|
device->staticSoundCount += 1;
|
|
|
|
return (double)sound->id;
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_AttachReverb(FAudioGMS_SoundInstance* instance)
|
|
{
|
|
if (!instance->reverbAttached)
|
|
{
|
|
/* Init reverb */
|
|
|
|
FAPO* reverb;
|
|
FAudioCreateReverb(&reverb, 0);
|
|
|
|
FAudioEffectChain* reverbChain = SDL_malloc(sizeof(FAudioEffectChain));
|
|
|
|
reverbChain->EffectCount = 1;
|
|
reverbChain->pEffectDescriptors = SDL_malloc(sizeof(FAudioEffectDescriptor));
|
|
|
|
FAudioEffectDescriptor* reverbDescriptor = reverbChain->pEffectDescriptors;
|
|
|
|
reverbDescriptor->InitialState = 1;
|
|
reverbDescriptor->OutputChannels = device->deviceDetails.OutputFormat.Format.nChannels == 6 ? 6 : 1;
|
|
reverbDescriptor->pEffect = reverb;
|
|
|
|
FAudio_CreateSubmixVoice(
|
|
device->handle,
|
|
&instance->reverbVoice,
|
|
1, /* omnidirectional reverb */
|
|
device->deviceDetails.OutputFormat.Format.nSamplesPerSec,
|
|
0,
|
|
0,
|
|
NULL,
|
|
reverbChain);
|
|
|
|
FAPOBase_Release((FAPOBase*)reverb);
|
|
SDL_free(reverbChain->pEffectDescriptors);
|
|
SDL_free(reverbChain);
|
|
|
|
FAudioFXReverbParameters* reverbParams = SDL_malloc(sizeof(FAudioFXReverbParameters));
|
|
|
|
reverbParams->WetDryMix = 100.0f;
|
|
reverbParams->ReflectionsDelay = 7;
|
|
reverbParams->ReverbDelay = 11;
|
|
reverbParams->RearDelay = FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
|
|
reverbParams->PositionLeft = FAUDIOFX_REVERB_DEFAULT_POSITION;
|
|
reverbParams->PositionRight = FAUDIOFX_REVERB_DEFAULT_POSITION;
|
|
reverbParams->PositionMatrixLeft = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
|
|
reverbParams->PositionMatrixRight = FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
|
|
reverbParams->EarlyDiffusion = 15;
|
|
reverbParams->LateDiffusion = 15;
|
|
reverbParams->LowEQGain = 8;
|
|
reverbParams->LowEQCutoff = 4;
|
|
reverbParams->HighEQGain = 8;
|
|
reverbParams->HighEQCutoff = 6;
|
|
reverbParams->RoomFilterFreq = 5000.0f;
|
|
reverbParams->RoomFilterMain = -10.0f;
|
|
reverbParams->RoomFilterHF = -1.0f;
|
|
reverbParams->ReflectionsGain = -26.0200005f;
|
|
reverbParams->ReverbGain = 10.0f;
|
|
reverbParams->DecayTime = 1.49000001f;
|
|
reverbParams->Density = 100.0f;
|
|
reverbParams->RoomSize = FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
|
|
|
|
FAudioVoice_SetEffectParameters(
|
|
instance->reverbVoice,
|
|
0,
|
|
reverbParams,
|
|
sizeof(FAudioFXReverbParameters),
|
|
0);
|
|
|
|
SDL_free(reverbParams);
|
|
|
|
/* Init reverb sends */
|
|
|
|
instance->reverbSends.SendCount = 2;
|
|
instance->reverbSends.pSends = SDL_malloc(2 * sizeof(FAudioSendDescriptor));
|
|
|
|
instance->reverbSends.pSends[0].Flags = 0;
|
|
instance->reverbSends.pSends[0].pOutputVoice = device->masteringVoice;
|
|
instance->reverbSends.pSends[1].Flags = 0;
|
|
instance->reverbSends.pSends[1].pOutputVoice = instance->reverbVoice;
|
|
|
|
instance->reverbAttached = 1;
|
|
}
|
|
|
|
FAudioVoice_SetOutputVoices(
|
|
instance->handle,
|
|
&instance->reverbSends);
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetReverb(FAudioGMS_SoundInstance *instance, float reverb)
|
|
{
|
|
if (!instance->reverbAttached)
|
|
{
|
|
FAudioGMS_INTERNAL_SoundInstance_AttachReverb(instance);
|
|
}
|
|
|
|
float* outputMatrix = instance->dspSettings.pMatrixCoefficients;
|
|
|
|
outputMatrix[0] = reverb;
|
|
if (instance->dspSettings.SrcChannelCount == 2)
|
|
{
|
|
outputMatrix[1] = reverb;
|
|
}
|
|
|
|
FAudioVoice_SetOutputMatrix(
|
|
instance->handle,
|
|
instance->reverbVoice,
|
|
instance->dspSettings.SrcChannelCount,
|
|
1,
|
|
outputMatrix,
|
|
0);
|
|
|
|
instance->reverb = reverb;
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetLowPassFilter(FAudioGMS_SoundInstance* instance, float lowPassFilter)
|
|
{
|
|
FAudioFilterParameters p;
|
|
p.Type = FAudioLowPassFilter;
|
|
p.Frequency = lowPassFilter;
|
|
p.OneOverQ = 1.0f;
|
|
|
|
FAudioVoice_SetFilterParameters(instance->handle, &p, 0);
|
|
|
|
instance->lowPassFilter = lowPassFilter;
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetHighPassFilter(FAudioGMS_SoundInstance* instance, float highPassFilter)
|
|
{
|
|
FAudioFilterParameters p;
|
|
p.Type = FAudioHighPassFilter;
|
|
p.Frequency = highPassFilter;
|
|
p.OneOverQ = 1.0f;
|
|
|
|
FAudioVoice_SetFilterParameters(instance->handle, &p, 0);
|
|
|
|
instance->highPassFilter = highPassFilter;
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetBandPassFilter(FAudioGMS_SoundInstance* instance, float bandPassFilter)
|
|
{
|
|
FAudioFilterParameters p;
|
|
p.Type = FAudioBandPassFilter;
|
|
p.Frequency = bandPassFilter;
|
|
p.OneOverQ = 1.0f;
|
|
|
|
FAudioVoice_SetFilterParameters(instance->handle, &p, 0);
|
|
|
|
instance->bandPassFilter = bandPassFilter;
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetPan(FAudioGMS_SoundInstance* instance, float pan)
|
|
{
|
|
instance->pan = pan;
|
|
SetPanMatrixCoefficients(instance);
|
|
|
|
FAudioVoice_SetOutputMatrix(
|
|
instance->handle,
|
|
device->masteringVoice,
|
|
instance->dspSettings.SrcChannelCount,
|
|
instance->dspSettings.DstChannelCount,
|
|
instance->dspSettings.pMatrixCoefficients,
|
|
0
|
|
);
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetPitch(FAudioGMS_SoundInstance* instance, float pitch)
|
|
{
|
|
instance->pitch = pitch;
|
|
|
|
pitch -= 1.0; /* default pitch is 1.0 as per GM standard */
|
|
pitch = SDL_max(-1.0, SDL_min(1.0, pitch));
|
|
|
|
FAudioSourceVoice_SetFrequencyRatio(
|
|
instance->handle,
|
|
SDL_powf(2.0f, pitch),
|
|
0
|
|
);
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_SetVolume(FAudioGMS_SoundInstance* instance, float volume)
|
|
{
|
|
FAudioVoice_SetVolume(instance->handle, volume, 0);
|
|
}
|
|
|
|
static FAudioGMS_SoundInstance* FAudioGMS_INTERNAL_SoundInstance_CreateFromStaticSound(
|
|
FAudioGMS_StaticSound *staticSound,
|
|
double pan,
|
|
double pitch,
|
|
double volume,
|
|
double reverb
|
|
) {
|
|
FAudioGMS_SoundInstance *instance = SDL_malloc(sizeof(FAudioGMS_SoundInstance));
|
|
|
|
instance->handle = NULL;
|
|
instance->parent.staticSound = staticSound;
|
|
instance->isStatic = 1;
|
|
instance->loop = 0;
|
|
instance->destroyOnFinish = 0;
|
|
|
|
instance->format.wFormatTag = 3;
|
|
instance->format.wBitsPerSample = 32;
|
|
instance->format.nChannels = staticSound->channels;
|
|
instance->format.nBlockAlign = (uint16_t)(4 * staticSound->channels);
|
|
instance->format.nSamplesPerSec = staticSound->samplesPerSecond;
|
|
instance->format.nAvgBytesPerSec = instance->format.nBlockAlign * staticSound->samplesPerSecond;
|
|
|
|
FAudio_CreateSourceVoice(
|
|
device->handle,
|
|
&instance->handle,
|
|
&instance->format,
|
|
FAUDIO_VOICE_USEFILTER,
|
|
FAUDIO_DEFAULT_FREQ_RATIO,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (instance->handle == NULL)
|
|
{
|
|
Log("SoundInstance failed to initialize!");
|
|
SDL_free(instance);
|
|
return NULL;
|
|
}
|
|
|
|
instance->dspSettings.DopplerFactor = 1.0f;
|
|
instance->dspSettings.SrcChannelCount = staticSound->channels;
|
|
instance->dspSettings.DstChannelCount = device->deviceDetails.OutputFormat.Format.nChannels;
|
|
|
|
uint32_t memsize = 4 * instance->dspSettings.SrcChannelCount * instance->dspSettings.DstChannelCount;
|
|
instance->dspSettings.pMatrixCoefficients = SDL_malloc(memsize);
|
|
SDL_memset(instance->dspSettings.pMatrixCoefficients, 0, memsize);
|
|
|
|
FAudioGMS_INTERNAL_SoundInstance_SetPan(instance, pan);
|
|
FAudioGMS_INTERNAL_SoundInstance_SetPitch(instance, pitch);
|
|
FAudioGMS_INTERNAL_SoundInstance_SetVolume(instance, volume);
|
|
|
|
instance->reverbAttached = 0;
|
|
instance->reverb = 0.0f;
|
|
instance->lowPassFilter = 0.0f;
|
|
instance->highPassFilter = 0.0f;
|
|
instance->bandPassFilter = 0.0f;
|
|
|
|
if (reverb > 0.0f)
|
|
{
|
|
FAudioGMS_INTERNAL_SoundInstance_SetReverb(instance, reverb);
|
|
}
|
|
|
|
instance->soundState = SoundState_Stopped;
|
|
|
|
/* FIXME: id re-use system */
|
|
instance->id = device->soundInstanceCount;
|
|
|
|
device->soundInstances = SDL_realloc(device->soundInstances, (device->soundInstanceCount + 1) * sizeof(FAudioGMS_SoundInstance*));
|
|
device->soundInstances[device->soundInstanceCount] = instance;
|
|
device->soundInstanceCount += 1;
|
|
|
|
return instance;
|
|
}
|
|
|
|
static void FAudioGMS_INTERNAL_SoundInstance_Play(FAudioGMS_SoundInstance* instance)
|
|
{
|
|
if (instance->isStatic)
|
|
{
|
|
if (instance->soundState == SoundState_Playing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (instance->loop)
|
|
{
|
|
instance->parent.staticSound->buffer.LoopCount = FAUDIO_LOOP_INFINITE;
|
|
instance->parent.staticSound->buffer.LoopBegin = instance->parent.staticSound->loopStart;
|
|
instance->parent.staticSound->buffer.LoopLength = instance->parent.staticSound->loopLength;
|
|
}
|
|
else
|
|
{
|
|
instance->parent.staticSound->buffer.LoopCount = 0;
|
|
instance->parent.staticSound->buffer.LoopBegin = 0;
|
|
instance->parent.staticSound->buffer.LoopLength = 0;
|
|
}
|
|
|
|
FAudioSourceVoice_SubmitSourceBuffer(instance->handle, &instance->parent.staticSound->buffer, NULL);
|
|
FAudioSourceVoice_Start(instance->handle, 0, 0);
|
|
instance->soundState = SoundState_Playing;
|
|
}
|
|
}
|
|
|
|
void FAudioGMS_StaticSound_PlayOneOff(double staticSoundID, double pan, double pitch, double volume, double reverb)
|
|
{
|
|
FAudioGMS_StaticSound* staticSound = device->staticSounds[(uint32_t)staticSoundID];
|
|
|
|
if (staticSound != NULL)
|
|
{
|
|
FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_SoundInstance_CreateFromStaticSound(staticSound, pan, pitch, volume, reverb);
|
|
instance->destroyOnFinish = 1;
|
|
FAudioGMS_INTERNAL_SoundInstance_Play(instance);
|
|
}
|
|
else
|
|
{
|
|
Log("StaticSound_PlayOneOff: 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];
|
|
|
|
if (staticSound != NULL)
|
|
{
|
|
FAudioGMS_SoundInstance* instance = FAudioGMS_INTERNAL_SoundInstance_CreateFromStaticSound(staticSound, pan, pitch, volume, reverb);
|
|
instance->loop = (uint8_t)loop;
|
|
FAudioGMS_INTERNAL_SoundInstance_Play(instance);
|
|
return (double)instance->id;
|
|
}
|
|
else
|
|
{
|
|
Log("StaticSound_Play: 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];
|
|
|
|
if (instance != NULL)
|
|
{
|
|
FAudioGMS_INTERNAL_SoundInstance_Play(instance);
|
|
}
|
|
}
|
|
|
|
void FAudioGMS_SoundInstance_Pause(double id)
|
|
{
|
|
FAudioGMS_SoundInstance* instance = device->soundInstances[(uint32_t)id];
|
|
|
|
if (instance != NULL)
|
|
{
|
|
if (instance->isStatic)
|
|
{
|
|
if (instance->soundState == SoundState_Playing)
|
|
{
|
|
FAudioSourceVoice_Stop(instance->handle, 0, 0);
|
|
instance->soundState = SoundState_Paused;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log("SoundInstance_Pause: Invalid sound instance ID! Did you destroy this instance?");
|
|
}
|
|
}
|
|
|
|
void FAudioGMS_SoundInstance_Stop(double id)
|
|
{
|
|
FAudioGMS_SoundInstance* instance = device->soundInstances[(uint32_t)id];
|
|
|
|
if (instance != NULL)
|
|
{
|
|
if (instance->isStatic)
|
|
{
|
|
FAudioSourceVoice_ExitLoop(instance->handle, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log("SoundInstance_Stop: Invalid sound instance ID! Did you destroy this 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]);
|
|
}
|
|
|
|
FAudio_Release(device->handle);
|
|
SDL_free(device);
|
|
device = NULL;
|
|
|
|
Log("FAudio cleaned up successfully!");
|
|
}
|