Files
WickedEngine/WickedEngine/wiAudio.cpp
T
2025-01-14 12:57:40 +01:00

1338 lines
42 KiB
C++

#include "wiAudio.h"
#include "wiBacklog.h"
#include "wiHelper.h"
#include "wiTimer.h"
#include "wiVector.h"
#define STB_VORBIS_HEADER_ONLY
#include "Utility/stb_vorbis.c"
#include <sstream>
template<typename T>
static constexpr T AlignTo(T value, T alignment)
{
return ((value + alignment - T(1)) / alignment) * alignment;
}
#ifdef _WIN32
#include <wrl/client.h> // ComPtr
#include <xaudio2.h>
#include <xaudio2fx.h>
#include <x3daudio.h>
#pragma comment(lib,"xaudio2.lib")
//Little-Endian things:
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#define xaudio_assert(cond, fname) { wilog_assert(cond, "XAudio2 error: %s failed with %s (%s:%d)", fname, wi::helper::GetPlatformErrorString(hr).c_str(), relative_path(__FILE__), __LINE__); }
#define xaudio_check(call) [&]() { HRESULT hr = call; xaudio_assert(SUCCEEDED(hr), extract_function_name(#call).c_str()); return hr; }()
namespace wi::audio
{
static const XAUDIO2FX_REVERB_I3DL2_PARAMETERS reverbPresets[] =
{
XAUDIO2FX_I3DL2_PRESET_DEFAULT,
XAUDIO2FX_I3DL2_PRESET_GENERIC,
XAUDIO2FX_I3DL2_PRESET_FOREST,
XAUDIO2FX_I3DL2_PRESET_PADDEDCELL,
XAUDIO2FX_I3DL2_PRESET_ROOM,
XAUDIO2FX_I3DL2_PRESET_BATHROOM,
XAUDIO2FX_I3DL2_PRESET_LIVINGROOM,
XAUDIO2FX_I3DL2_PRESET_STONEROOM,
XAUDIO2FX_I3DL2_PRESET_AUDITORIUM,
XAUDIO2FX_I3DL2_PRESET_CONCERTHALL,
XAUDIO2FX_I3DL2_PRESET_CAVE,
XAUDIO2FX_I3DL2_PRESET_ARENA,
XAUDIO2FX_I3DL2_PRESET_HANGAR,
XAUDIO2FX_I3DL2_PRESET_CARPETEDHALLWAY,
XAUDIO2FX_I3DL2_PRESET_HALLWAY,
XAUDIO2FX_I3DL2_PRESET_STONECORRIDOR,
XAUDIO2FX_I3DL2_PRESET_ALLEY,
XAUDIO2FX_I3DL2_PRESET_CITY,
XAUDIO2FX_I3DL2_PRESET_MOUNTAINS,
XAUDIO2FX_I3DL2_PRESET_QUARRY,
XAUDIO2FX_I3DL2_PRESET_PLAIN,
XAUDIO2FX_I3DL2_PRESET_PARKINGLOT,
XAUDIO2FX_I3DL2_PRESET_SEWERPIPE,
XAUDIO2FX_I3DL2_PRESET_UNDERWATER,
XAUDIO2FX_I3DL2_PRESET_SMALLROOM,
XAUDIO2FX_I3DL2_PRESET_MEDIUMROOM,
XAUDIO2FX_I3DL2_PRESET_LARGEROOM,
XAUDIO2FX_I3DL2_PRESET_MEDIUMHALL,
XAUDIO2FX_I3DL2_PRESET_LARGEHALL,
XAUDIO2FX_I3DL2_PRESET_PLATE,
};
struct AudioInternal
{
bool success = false;
Microsoft::WRL::ComPtr<IXAudio2> audioEngine;
IXAudio2MasteringVoice* masteringVoice = nullptr;
XAUDIO2_VOICE_DETAILS masteringVoiceDetails = {};
IXAudio2SubmixVoice* submixVoices[SUBMIX_TYPE_COUNT] = {};
X3DAUDIO_HANDLE audio3D = {};
Microsoft::WRL::ComPtr<IUnknown> reverbEffect;
IXAudio2SubmixVoice* reverbSubmix = nullptr;
uint32_t termination_data = 0;
XAUDIO2_BUFFER termination_mark = {};
AudioInternal()
{
wi::Timer timer;
HRESULT hr;
hr = xaudio_check(CoInitializeEx(NULL, COINIT_MULTITHREADED));
if (!SUCCEEDED(hr))
{
return;
}
hr = xaudio_check(XAudio2Create(&audioEngine, 0, XAUDIO2_USE_DEFAULT_PROCESSOR));
if (!SUCCEEDED(hr))
{
return;
}
#ifdef _DEBUG
XAUDIO2_DEBUG_CONFIGURATION debugConfig = {};
debugConfig.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS;
debugConfig.BreakMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS;
audioEngine->SetDebugConfiguration(&debugConfig);
#endif // _DEBUG
hr = xaudio_check(audioEngine->CreateMasteringVoice(&masteringVoice));
if (!SUCCEEDED(hr))
{
return;
}
masteringVoice->GetVoiceDetails(&masteringVoiceDetails);
// Without clamping sample rate, it was crashing 32bit 192kHz audio devices
if (masteringVoiceDetails.InputSampleRate > 48000)
masteringVoiceDetails.InputSampleRate = 48000;
for (int i = 0; i < SUBMIX_TYPE_COUNT; ++i)
{
hr = xaudio_check(audioEngine->CreateSubmixVoice(
&submixVoices[i],
masteringVoiceDetails.InputChannels,
masteringVoiceDetails.InputSampleRate,
0,
0,
0,
0
));
if (!SUCCEEDED(hr))
{
return;
}
}
DWORD channelMask;
masteringVoice->GetChannelMask(&channelMask);
hr = xaudio_check(X3DAudioInitialize(channelMask, X3DAUDIO_SPEED_OF_SOUND, audio3D));
if (!SUCCEEDED(hr))
{
return;
}
// Reverb setup:
{
hr = xaudio_check(XAudio2CreateReverb(&reverbEffect));
if (!SUCCEEDED(hr))
{
return;
}
XAUDIO2_EFFECT_DESCRIPTOR effects[] = { { reverbEffect.Get(), TRUE, 1 } };
XAUDIO2_EFFECT_CHAIN effectChain = { arraysize(effects), effects };
hr = xaudio_check(audioEngine->CreateSubmixVoice(
&reverbSubmix,
1, // reverb is mono
masteringVoiceDetails.InputSampleRate,
0,
0,
nullptr,
&effectChain
));
if (!SUCCEEDED(hr))
{
return;
}
XAUDIO2FX_REVERB_PARAMETERS native;
ReverbConvertI3DL2ToNative(&reverbPresets[REVERB_PRESET_DEFAULT], &native);
HRESULT hr = xaudio_check(reverbSubmix->SetEffectParameters(0, &native, sizeof(native)));
if (!SUCCEEDED(hr))
{
return;
}
}
termination_mark.Flags = XAUDIO2_END_OF_STREAM;
termination_mark.pAudioData = (const BYTE*)&termination_data;
termination_mark.AudioBytes = sizeof(termination_data);
success = true;
wilog("wi::audio Initialized [XAudio2] (%d ms)", (int)std::round(timer.elapsed()));
}
~AudioInternal()
{
if (reverbSubmix != nullptr)
reverbSubmix->DestroyVoice();
for (int i = 0; i < SUBMIX_TYPE_COUNT; ++i)
{
if (submixVoices[i] != nullptr)
submixVoices[i]->DestroyVoice();
}
if (masteringVoice != nullptr)
masteringVoice->DestroyVoice();
audioEngine->StopEngine();
CoUninitialize();
}
constexpr bool IsValid() const { return success; }
};
static std::shared_ptr<AudioInternal> audio_internal;
void Initialize()
{
audio_internal = std::make_shared<AudioInternal>();
}
struct SoundInternal
{
std::shared_ptr<AudioInternal> audio;
WAVEFORMATEX wfx = {};
wi::vector<uint8_t> audioData;
};
struct SoundInstanceInternal : public IXAudio2VoiceCallback
{
std::shared_ptr<AudioInternal> audio;
std::shared_ptr<SoundInternal> soundinternal;
IXAudio2SourceVoice* sourceVoice = nullptr;
XAUDIO2_VOICE_DETAILS voiceDetails = {};
wi::vector<float> outputMatrix;
wi::vector<float> channelAzimuths;
XAUDIO2_BUFFER buffer = {};
bool ended = true;
~SoundInstanceInternal()
{
sourceVoice->Stop();
sourceVoice->DestroyVoice();
}
// Called just before this voice's processing pass begins.
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired)
{
}
// Called just after this voice's processing pass ends.
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS)
{
}
// Called when this voice has just finished playing a buffer stream
// (as marked with the XAUDIO2_END_OF_STREAM flag on the last buffer).
STDMETHOD_(void, OnStreamEnd) (THIS)
{
ended = true;
}
// Called when this voice is about to start processing a new buffer.
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext)
{
ended = false;
}
// Called when this voice has just finished processing a buffer.
// The buffer can now be reused or destroyed.
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext)
{
}
// Called when this voice has just reached the end position of a loop.
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext)
{
}
// Called in the event of a critical error during voice processing,
// such as a failing xAPO or an error from the hardware XMA decoder.
// The voice may have to be destroyed and re-created to recover from
// the error. The callback arguments report which buffer was being
// processed when the error occurred, and its HRESULT code.
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error)
{
}
};
SoundInternal* to_internal(const Sound* param)
{
return static_cast<SoundInternal*>(param->internal_state.get());
}
SoundInstanceInternal* to_internal(const SoundInstance* param)
{
return static_cast<SoundInstanceInternal*>(param->internal_state.get());
}
bool FindChunk(const uint8_t* data, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition)
{
size_t pos = 0;
DWORD dwChunkType;
DWORD dwChunkDataSize;
DWORD dwRIFFDataSize = 0;
DWORD dwFileType;
DWORD bytesRead = 0;
DWORD dwOffset = 0;
while(true)
{
memcpy(&dwChunkType, data + pos, sizeof(DWORD));
pos += sizeof(DWORD);
memcpy(&dwChunkDataSize, data + pos, sizeof(DWORD));
pos += sizeof(DWORD);
switch (dwChunkType)
{
case fourccRIFF:
dwRIFFDataSize = dwChunkDataSize;
dwChunkDataSize = 4;
memcpy(&dwFileType, data + pos, sizeof(DWORD));
pos += sizeof(DWORD);
break;
default:
pos += dwChunkDataSize;
}
dwOffset += sizeof(DWORD) * 2;
if (dwChunkType == fourcc)
{
dwChunkSize = dwChunkDataSize;
dwChunkDataPosition = dwOffset;
return true;
}
dwOffset += dwChunkDataSize;
if (bytesRead >= dwRIFFDataSize) return false;
}
return true;
}
bool CreateSound(const std::string& filename, Sound* sound)
{
wi::vector<uint8_t> filedata;
bool success = wi::helper::FileRead(filename, filedata);
if (!success)
{
return false;
}
return CreateSound(filedata.data(), filedata.size(), sound);
}
bool CreateSound(const uint8_t* data, size_t size, Sound* sound)
{
if (audio_internal == nullptr || !audio_internal->IsValid())
return false;
std::shared_ptr<SoundInternal> soundinternal = std::make_shared<SoundInternal>();
soundinternal->audio = audio_internal;
sound->internal_state = soundinternal;
DWORD dwChunkSize;
DWORD dwChunkPosition;
bool success;
success = FindChunk(data, fourccRIFF, dwChunkSize, dwChunkPosition);
if (success)
{
// Wav decoder:
DWORD filetype;
memcpy(&filetype, data + dwChunkPosition, sizeof(DWORD));
if (filetype != fourccWAVE)
{
assert(0);
return false;
}
success = FindChunk(data, fourccFMT, dwChunkSize, dwChunkPosition);
if (!success)
{
assert(0);
return false;
}
memcpy(&soundinternal->wfx, data + dwChunkPosition, dwChunkSize);
soundinternal->wfx.wFormatTag = WAVE_FORMAT_PCM;
success = FindChunk(data, fourccDATA, dwChunkSize, dwChunkPosition);
if (!success)
{
assert(0);
return false;
}
soundinternal->audioData.resize(dwChunkSize);
memcpy(soundinternal->audioData.data(), data + dwChunkPosition, dwChunkSize);
}
else
{
// Ogg decoder:
int channels = 0;
int sample_rate = 0;
short* output = nullptr;
int samples = stb_vorbis_decode_memory(data, (int)size, &channels, &sample_rate, &output);
if (samples < 0)
{
assert(0);
return false;
}
// WAVEFORMATEX: https://docs.microsoft.com/en-us/previous-versions/dd757713(v=vs.85)?redirectedfrom=MSDN
soundinternal->wfx.wFormatTag = WAVE_FORMAT_PCM;
soundinternal->wfx.nChannels = (WORD)channels;
soundinternal->wfx.nSamplesPerSec = (DWORD)sample_rate;
soundinternal->wfx.wBitsPerSample = sizeof(short) * 8;
soundinternal->wfx.nBlockAlign = (WORD)channels * sizeof(short); // is this right?
soundinternal->wfx.nAvgBytesPerSec = soundinternal->wfx.nSamplesPerSec * soundinternal->wfx.nBlockAlign;
size_t output_size = size_t(samples * channels) * sizeof(short);
soundinternal->audioData.resize(output_size);
memcpy(soundinternal->audioData.data(), output, output_size);
free(output);
}
return true;
}
bool CreateSoundInstance(const Sound* sound, SoundInstance* instance)
{
if (audio_internal == nullptr || !audio_internal->IsValid())
return false;
if (sound == nullptr || !sound->IsValid())
return false;
HRESULT hr;
const auto& soundinternal = std::static_pointer_cast<SoundInternal>(sound->internal_state);
std::shared_ptr<SoundInstanceInternal> instanceinternal = std::make_shared<SoundInstanceInternal>();
instance->internal_state = instanceinternal;
instanceinternal->audio = audio_internal;
instanceinternal->soundinternal = soundinternal;
XAUDIO2_SEND_DESCRIPTOR SFXSend[] = {
{ XAUDIO2_SEND_USEFILTER, instanceinternal->audio->submixVoices[instance->type] },
{ XAUDIO2_SEND_USEFILTER, instanceinternal->audio->reverbSubmix }, // this should be last to enable/disable reverb simply
};
XAUDIO2_VOICE_SENDS SFXSendList = {
(instance->IsEnableReverb() && instanceinternal->audio->reverbSubmix != nullptr) ? (uint32_t)arraysize(SFXSend) : 1,
SFXSend
};
hr = xaudio_check(instanceinternal->audio->audioEngine->CreateSourceVoice(&instanceinternal->sourceVoice, &soundinternal->wfx,
0, XAUDIO2_DEFAULT_FREQ_RATIO, instanceinternal.get(), &SFXSendList, NULL));
if (FAILED(hr))
{
return false;
}
instanceinternal->sourceVoice->GetVoiceDetails(&instanceinternal->voiceDetails);
instanceinternal->outputMatrix.resize(size_t(instanceinternal->voiceDetails.InputChannels) * size_t(instanceinternal->audio->masteringVoiceDetails.InputChannels));
instanceinternal->channelAzimuths.resize(instanceinternal->voiceDetails.InputChannels);
for (size_t i = 0; i < instanceinternal->channelAzimuths.size(); ++i)
{
instanceinternal->channelAzimuths[i] = X3DAUDIO_2PI * float(i) / float(instanceinternal->channelAzimuths.size());
}
const uint32_t bytes_per_second = soundinternal->wfx.nSamplesPerSec * soundinternal->wfx.nChannels * sizeof(short);
instanceinternal->buffer.pAudioData = soundinternal->audioData.data();
instanceinternal->buffer.AudioBytes = (uint32_t)soundinternal->audioData.size();
if (instance->begin > 0)
{
const uint32_t bytes_from_beginning = AlignTo(std::min(instanceinternal->buffer.AudioBytes, uint32_t(instance->begin * bytes_per_second)), 4u);
instanceinternal->buffer.pAudioData += bytes_from_beginning;
instanceinternal->buffer.AudioBytes -= bytes_from_beginning;
}
if (instance->length > 0)
{
instanceinternal->buffer.AudioBytes = AlignTo(std::min(instanceinternal->buffer.AudioBytes, uint32_t(instance->length * bytes_per_second)), 4u);
}
uint32_t num_remaining_samples = instanceinternal->buffer.AudioBytes / (soundinternal->wfx.nChannels * sizeof(short));
if (instance->loop_begin > 0)
{
instanceinternal->buffer.LoopBegin = AlignTo(std::min(num_remaining_samples, uint32_t(instance->loop_begin * soundinternal->wfx.nSamplesPerSec)), 4u);
num_remaining_samples -= instanceinternal->buffer.LoopBegin;
}
instanceinternal->buffer.LoopLength = AlignTo(std::min(num_remaining_samples, uint32_t(instance->loop_length * soundinternal->wfx.nSamplesPerSec)), 4u);
instanceinternal->buffer.Flags = XAUDIO2_END_OF_STREAM;
instanceinternal->buffer.LoopCount = instance->IsLooped() ? XAUDIO2_LOOP_INFINITE : 0;
hr = xaudio_check(instanceinternal->sourceVoice->SubmitSourceBuffer(&instanceinternal->buffer));
if (FAILED(hr))
{
return false;
}
return true;
}
void Play(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
xaudio_check(instanceinternal->sourceVoice->Start());
}
}
void Pause(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
xaudio_check(instanceinternal->sourceVoice->Stop()); // preserves cursor position
}
}
void Stop(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
xaudio_check(instanceinternal->sourceVoice->Stop()); // preserves cursor position
xaudio_check(instanceinternal->sourceVoice->FlushSourceBuffers()); // reset submitted audio buffer
if (!instanceinternal->ended) // if already ended, don't submit end again, it can cause high pitched jerky sound
{
xaudio_check(instanceinternal->sourceVoice->SubmitSourceBuffer(&audio_internal->termination_mark)); // mark this as terminated, this resets XAUDIO2_VOICE_STATE::SamplesPlayed to zero
}
xaudio_check(instanceinternal->sourceVoice->SubmitSourceBuffer(&instanceinternal->buffer)); // resubmit
}
}
void SetVolume(float volume, SoundInstance* instance)
{
if (instance == nullptr || !instance->IsValid())
{
xaudio_check(audio_internal->masteringVoice->SetVolume(volume));
}
else
{
auto instanceinternal = to_internal(instance);
xaudio_check(instanceinternal->sourceVoice->SetVolume(volume));
}
}
float GetVolume(const SoundInstance* instance)
{
float volume = 0;
if (instance == nullptr || !instance->IsValid())
{
audio_internal->masteringVoice->GetVolume(&volume);
}
else
{
auto instanceinternal = to_internal(instance);
instanceinternal->sourceVoice->GetVolume(&volume);
}
return volume;
}
void ExitLoop(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
if (instanceinternal->buffer.LoopCount == 0)
return;
xaudio_check(instanceinternal->sourceVoice->ExitLoop());
if (instanceinternal->ended)
{
instanceinternal->buffer.LoopCount = 0;
xaudio_check(instanceinternal->sourceVoice->SubmitSourceBuffer(&instanceinternal->buffer));
}
}
}
bool IsEnded(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
return instanceinternal->ended;
}
return false;
}
SampleInfo GetSampleInfo(const Sound* sound)
{
SampleInfo info = {};
if (sound != nullptr && sound->IsValid())
{
auto soundinternal = to_internal(sound);
info.channel_count = soundinternal->wfx.nChannels;
info.samples = (const short*)soundinternal->audioData.data();
info.sample_count = soundinternal->audioData.size() / (info.channel_count * sizeof(short));
info.sample_rate = soundinternal->wfx.nSamplesPerSec;
}
return info;
}
uint64_t GetTotalSamplesPlayed(const SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
XAUDIO2_VOICE_STATE state = {};
instanceinternal->sourceVoice->GetState(&state, 0);
return state.SamplesPlayed;
}
return 0ull;
}
void SetSubmixVolume(SUBMIX_TYPE type, float volume)
{
xaudio_check(audio_internal->submixVoices[type]->SetVolume(volume));
}
float GetSubmixVolume(SUBMIX_TYPE type)
{
float volume;
audio_internal->submixVoices[type]->GetVolume(&volume);
return volume;
}
void Update3D(SoundInstance* instance, const SoundInstance3D& instance3D)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
X3DAUDIO_LISTENER listener = {};
listener.Position = instance3D.listenerPos;
listener.OrientFront = instance3D.listenerFront;
listener.OrientTop = instance3D.listenerUp;
listener.Velocity = instance3D.listenerVelocity;
X3DAUDIO_EMITTER emitter = {};
emitter.Position = instance3D.emitterPos;
emitter.OrientFront = instance3D.emitterFront;
emitter.OrientTop = instance3D.emitterUp;
emitter.Velocity = instance3D.emitterVelocity;
emitter.InnerRadius = instance3D.emitterRadius;
emitter.InnerRadiusAngle = X3DAUDIO_PI / 4.0f;
emitter.ChannelCount = instanceinternal->voiceDetails.InputChannels;
emitter.pChannelAzimuths = instanceinternal->channelAzimuths.data();
emitter.ChannelRadius = 0.1f;
emitter.CurveDistanceScaler = 1;
emitter.DopplerScaler = 1;
UINT32 flags = 0;
flags |= X3DAUDIO_CALCULATE_MATRIX;
flags |= X3DAUDIO_CALCULATE_LPF_DIRECT;
flags |= X3DAUDIO_CALCULATE_REVERB;
flags |= X3DAUDIO_CALCULATE_LPF_REVERB;
flags |= X3DAUDIO_CALCULATE_DOPPLER;
//flags |= X3DAUDIO_CALCULATE_DELAY;
//flags |= X3DAUDIO_CALCULATE_EMITTER_ANGLE;
//flags |= X3DAUDIO_CALCULATE_ZEROCENTER;
//flags |= X3DAUDIO_CALCULATE_REDIRECT_TO_LFE;
X3DAUDIO_DSP_SETTINGS settings = {};
settings.SrcChannelCount = instanceinternal->voiceDetails.InputChannels;
settings.DstChannelCount = instanceinternal->audio->masteringVoiceDetails.InputChannels;
settings.pMatrixCoefficients = instanceinternal->outputMatrix.data();
X3DAudioCalculate(instanceinternal->audio->audio3D, &listener, &emitter, flags, &settings);
xaudio_check(instanceinternal->sourceVoice->SetFrequencyRatio(settings.DopplerFactor));
xaudio_check(instanceinternal->sourceVoice->SetOutputMatrix(
instanceinternal->audio->submixVoices[instance->type],
settings.SrcChannelCount,
settings.DstChannelCount,
settings.pMatrixCoefficients
));
XAUDIO2_FILTER_PARAMETERS FilterParametersDirect = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * settings.LPFDirectCoefficient), 1.0f };
xaudio_check(instanceinternal->sourceVoice->SetOutputFilterParameters(instanceinternal->audio->submixVoices[instance->type], &FilterParametersDirect));
if (instance->IsEnableReverb() && instanceinternal->audio->reverbSubmix != nullptr)
{
xaudio_check(instanceinternal->sourceVoice->SetOutputMatrix(instanceinternal->audio->reverbSubmix, settings.SrcChannelCount, 1, &settings.ReverbLevel));
XAUDIO2_FILTER_PARAMETERS FilterParametersReverb = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * settings.LPFReverbCoefficient), 1.0f };
xaudio_check(instanceinternal->sourceVoice->SetOutputFilterParameters(instanceinternal->audio->reverbSubmix, &FilterParametersReverb));
}
}
}
void SetReverb(REVERB_PRESET preset)
{
XAUDIO2FX_REVERB_PARAMETERS native;
ReverbConvertI3DL2ToNative(&reverbPresets[preset], &native);
xaudio_check(audio_internal->reverbSubmix->SetEffectParameters(0, &native, sizeof(native)));
}
}
#elif SDL2
//FAudio implemetation
#include <FAudio.h>
#include <FAPO.h>
#include <FAudioFX.h>
#include <F3DAudio.h>
#define SPEED_OF_SOUND 343.5f
#define fourccRIFF 0x46464952
#define fourccWAVE 0x45564157
#define fourccFMT 0x20746d66
#define fourccDATA 0x61746164
namespace wi::audio
{
static const FAudioFXReverbI3DL2Parameters reverbPresets[] = {
FAUDIOFX_I3DL2_PRESET_DEFAULT,
FAUDIOFX_I3DL2_PRESET_GENERIC,
FAUDIOFX_I3DL2_PRESET_FOREST,
FAUDIOFX_I3DL2_PRESET_PADDEDCELL,
FAUDIOFX_I3DL2_PRESET_ROOM,
FAUDIOFX_I3DL2_PRESET_BATHROOM,
FAUDIOFX_I3DL2_PRESET_LIVINGROOM,
FAUDIOFX_I3DL2_PRESET_STONEROOM,
FAUDIOFX_I3DL2_PRESET_AUDITORIUM,
FAUDIOFX_I3DL2_PRESET_CONCERTHALL,
FAUDIOFX_I3DL2_PRESET_CAVE,
FAUDIOFX_I3DL2_PRESET_ARENA,
FAUDIOFX_I3DL2_PRESET_HANGAR,
FAUDIOFX_I3DL2_PRESET_CARPETEDHALLWAY,
FAUDIOFX_I3DL2_PRESET_HALLWAY,
FAUDIOFX_I3DL2_PRESET_STONECORRIDOR,
FAUDIOFX_I3DL2_PRESET_ALLEY,
FAUDIOFX_I3DL2_PRESET_CITY,
FAUDIOFX_I3DL2_PRESET_MOUNTAINS,
FAUDIOFX_I3DL2_PRESET_QUARRY,
FAUDIOFX_I3DL2_PRESET_PLAIN,
FAUDIOFX_I3DL2_PRESET_PARKINGLOT,
FAUDIOFX_I3DL2_PRESET_SEWERPIPE,
FAUDIOFX_I3DL2_PRESET_UNDERWATER,
FAUDIOFX_I3DL2_PRESET_SMALLROOM,
FAUDIOFX_I3DL2_PRESET_MEDIUMROOM,
FAUDIOFX_I3DL2_PRESET_LARGEROOM,
FAUDIOFX_I3DL2_PRESET_MEDIUMHALL,
FAUDIOFX_I3DL2_PRESET_LARGEHALL,
FAUDIOFX_I3DL2_PRESET_PLATE,
};
struct AudioInternal{
bool success = false;
FAudio *audioEngine;
FAudioMasteringVoice* masteringVoice = nullptr;
FAudioVoiceDetails masteringVoiceDetails;
FAudioSubmixVoice* submixVoices[SUBMIX_TYPE_COUNT] = {};
F3DAUDIO_HANDLE audio3D = {};
FAPO* reverbEffect;
FAudioSubmixVoice* reverbSubmix = nullptr;
uint32_t termination_data = 0;
FAudioBuffer termination_mark = {};
AudioInternal(){
wi::Timer timer;
uint32_t res;
res = FAudioCreate(&audioEngine, 0, FAUDIO_DEFAULT_PROCESSOR);
if (res != 0)
{
std::stringstream ss("");
ss << "FAudioCreate returned error: " << res;
wi::backlog::post(ss.str(), wi::backlog::LogLevel::Error);
return;
}
res = FAudio_CreateMasteringVoice(
audioEngine,
&masteringVoice,
FAUDIO_DEFAULT_CHANNELS,
FAUDIO_DEFAULT_SAMPLERATE,
0, 0, NULL);
if (res != 0)
{
std::stringstream ss("");
ss << "FAudio_CreateMasteringVoice returned error: " << res;
wi::backlog::post(ss.str(), wi::backlog::LogLevel::Error);
return;
}
FAudioVoice_GetVoiceDetails(masteringVoice, &masteringVoiceDetails);
for (int i=0; i<SUBMIX_TYPE_COUNT; ++i){
res = FAudio_CreateSubmixVoice(
audioEngine,
&submixVoices[i],
masteringVoiceDetails.InputChannels,
masteringVoiceDetails.InputSampleRate,
0, 0, NULL, NULL);
if (res != 0)
{
std::stringstream ss("");
ss << "FAudio_CreateSubmixVoice returned error: " << res;
wi::backlog::post(ss.str(), wi::backlog::LogLevel::Error);
return;
}
}
uint32_t channelMask;
FAudioMasteringVoice_GetChannelMask(masteringVoice, &channelMask);
F3DAudioInitialize(channelMask, SPEED_OF_SOUND, audio3D);
if (res != 0)
{
std::stringstream ss("");
ss << "F3DAudioInitialize returned error: " << res;
wi::backlog::post(ss.str(), wi::backlog::LogLevel::Error);
return;
}
// Reverb setup
{
res = FAudioCreateReverb(&reverbEffect, 0);
if (res != 0)
{
std::stringstream ss("");
ss << "FAudioCreateReverb returned error: " << res;
wi::backlog::post(ss.str(), wi::backlog::LogLevel::Error);
return;
}
FAudioEffectDescriptor effects[] = { { reverbEffect, 1, 1 } };
FAudioEffectChain effectChain = { arraysize(effects), effects };
res = FAudio_CreateSubmixVoice(
audioEngine,
&reverbSubmix,
1,
masteringVoiceDetails.InputSampleRate,
0,
0,
nullptr,
&effectChain
);
if (res != 0)
{
std::stringstream ss("");
ss << "FAudio_CreateSubmixVoice returned error: " << res;
wi::backlog::post(ss.str(), wi::backlog::LogLevel::Error);
return;
}
}
termination_mark.Flags = FAUDIO_END_OF_STREAM;
termination_mark.pAudioData = (const uint8_t*)&termination_data;
termination_mark.AudioBytes = sizeof(termination_data);
success = true;
wilog("wi::audio Initialized [FAudio] (%d ms)", (int)std::round(timer.elapsed()));
}
~AudioInternal(){
if(reverbSubmix != nullptr)
FAudioVoice_DestroyVoice(reverbSubmix);
for (int i = 0; i < SUBMIX_TYPE_COUNT; ++i){
if(submixVoices[i] != nullptr)
FAudioVoice_DestroyVoice(submixVoices[i]);
}
if(masteringVoice != nullptr)
FAudioVoice_DestroyVoice(masteringVoice);
FAudio_StopEngine(audioEngine);
}
constexpr bool IsValid() const { return success; }
};
static std::shared_ptr<AudioInternal> audio_internal;
void Initialize()
{
audio_internal = std::make_shared<AudioInternal>();
}
struct SoundInternal{
std::shared_ptr<AudioInternal> audio;
FAudioWaveFormatEx wfx = {};
wi::vector<uint8_t> audioData;
};
struct SoundInstanceInternal{
std::shared_ptr<AudioInternal> audio;
std::shared_ptr<SoundInternal> soundinternal;
FAudioSourceVoice* sourceVoice = nullptr;
FAudioVoiceDetails voiceDetails = {};
wi::vector<float> outputMatrix;
wi::vector<float> channelAzimuths;
FAudioBuffer buffer = {};
bool ended = true;
~SoundInstanceInternal(){
FAudioSourceVoice_Stop(sourceVoice, 0, FAUDIO_COMMIT_NOW);
FAudioVoice_DestroyVoice(sourceVoice);
}
};
SoundInternal* to_internal(const Sound* param)
{
return static_cast<SoundInternal*>(param->internal_state.get());
}
SoundInstanceInternal* to_internal(const SoundInstance* param)
{
return static_cast<SoundInstanceInternal*>(param->internal_state.get());
}
bool FindChunk(const uint8_t* data, uint32_t fourcc, uint32_t& dwChunkSize, uint32_t& dwChunkDataPosition)
{
size_t pos = 0;
uint32_t dwChunkType;
uint32_t dwChunkDataSize;
uint32_t dwRIFFDataSize = 0;
uint32_t dwFileType;
uint32_t bytesRead = 0;
uint32_t dwOffset = 0;
while(true)
{
memcpy(&dwChunkType, data + pos, sizeof(uint32_t));
pos += sizeof(uint32_t);
memcpy(&dwChunkDataSize, data + pos, sizeof(uint32_t));
pos += sizeof(uint32_t);
switch (dwChunkType)
{
case fourccRIFF: //TODO
dwRIFFDataSize = dwChunkDataSize;
dwChunkDataSize = 4;
memcpy(&dwFileType, data + pos, sizeof(uint32_t));
pos += sizeof(uint32_t);
break;
default:
pos += dwChunkDataSize;
}
dwOffset += sizeof(uint32_t) * 2;
if (dwChunkType == fourcc)
{
dwChunkSize = dwChunkDataSize;
dwChunkDataPosition = dwOffset;
return true;
}
dwOffset += dwChunkDataSize;
if (bytesRead >= dwRIFFDataSize) return false;
}
return true;
}
bool CreateSound(const std::string& filename, Sound* sound) {
wi::vector<uint8_t> filedata;
bool success = wi::helper::FileRead(filename, filedata);
if (!success)
{
return false;
}
return CreateSound(filedata.data(), filedata.size(), sound);
}
bool CreateSound(const uint8_t* data, size_t size, Sound* sound)
{
if (audio_internal == nullptr || !audio_internal->IsValid())
return false;
std::shared_ptr<SoundInternal> soundinternal = std::make_shared<SoundInternal>();
soundinternal->audio = audio_internal;
sound->internal_state = soundinternal;
uint32_t dwChunkSize;
uint32_t dwChunkPosition;
bool success;
success = FindChunk(data, fourccRIFF, dwChunkSize, dwChunkPosition);
if (success)
{
// Wav decoder:
uint32_t filetype;
memcpy(&filetype, data + dwChunkPosition, sizeof(uint32_t));
if (filetype != fourccWAVE)
{
assert(0);
return false;
}
success = FindChunk(data, fourccFMT, dwChunkSize, dwChunkPosition);
if (!success)
{
assert(0);
return false;
}
memcpy(&soundinternal->wfx, data + dwChunkPosition, dwChunkSize);
soundinternal->wfx.wFormatTag = FAUDIO_FORMAT_PCM;
success = FindChunk(data, fourccDATA, dwChunkSize, dwChunkPosition);
if (!success)
{
assert(0);
return false;
}
soundinternal->audioData.resize(dwChunkSize);
memcpy(soundinternal->audioData.data(), data + dwChunkPosition, dwChunkSize);
}
else
{
// Ogg decoder:
int channels = 0;
int sample_rate = 0;
short* output = nullptr;
int samples = stb_vorbis_decode_memory(data, (int)size, &channels, &sample_rate, &output);
if (samples < 0)
{
assert(0);
return false;
}
// WAVEFORMATEX: https://docs.microsoft.com/en-us/previous-versions/dd757713(v=vs.85)?redirectedfrom=MSDN
soundinternal->wfx.wFormatTag = FAUDIO_FORMAT_PCM;
soundinternal->wfx.nChannels = (uint16_t)channels;
soundinternal->wfx.nSamplesPerSec = (uint32_t)sample_rate;
soundinternal->wfx.wBitsPerSample = sizeof(short) * 8;
soundinternal->wfx.nBlockAlign = (uint16_t)channels * sizeof(short); // is this right?
soundinternal->wfx.nAvgBytesPerSec = soundinternal->wfx.nSamplesPerSec * soundinternal->wfx.nBlockAlign;
size_t output_size = size_t(samples * channels) * sizeof(short);
soundinternal->audioData.resize(output_size);
memcpy(soundinternal->audioData.data(), output, output_size);
free(output);
}
return true;
}
bool CreateSoundInstance(const Sound* sound, SoundInstance* instance)
{
if (audio_internal == nullptr || !audio_internal->IsValid())
return false;
if (sound == nullptr || !sound->IsValid())
return false;
uint32_t res;
const auto& soundinternal = std::static_pointer_cast<SoundInternal>(sound->internal_state);
std::shared_ptr<SoundInstanceInternal> instanceinternal = std::make_shared<SoundInstanceInternal>();
instance->internal_state = instanceinternal;
instanceinternal->audio = audio_internal;
instanceinternal->soundinternal = soundinternal;
FAudioSendDescriptor SFXSend[] = {
{ FAUDIO_SEND_USEFILTER, instanceinternal->audio->submixVoices[instance->type] },
{ FAUDIO_SEND_USEFILTER, instanceinternal->audio->reverbSubmix }, // this should be last to enable/disable reverb simply
};
FAudioVoiceSends SFXSendList = {
instance->IsEnableReverb() ? (uint32_t)arraysize(SFXSend) : 1,
SFXSend
};
res = FAudio_CreateSourceVoice(instanceinternal->audio->audioEngine, &instanceinternal->sourceVoice, &soundinternal->wfx,
0, FAUDIO_DEFAULT_FREQ_RATIO, NULL, &SFXSendList, NULL);
if(res != 0){
assert(0);
return false;
}
FAudioVoice_GetVoiceDetails(instanceinternal->sourceVoice, &instanceinternal->voiceDetails);
instanceinternal->outputMatrix.resize(size_t(instanceinternal->voiceDetails.InputChannels) * size_t(instanceinternal->audio->masteringVoiceDetails.InputChannels));
instanceinternal->channelAzimuths.resize(instanceinternal->voiceDetails.InputChannels);
for (size_t i = 0; i < instanceinternal->channelAzimuths.size(); ++i)
{
instanceinternal->channelAzimuths[i] = F3DAUDIO_2PI * float(i) / float(instanceinternal->channelAzimuths.size());
}
const uint32_t bytes_per_second = soundinternal->wfx.nSamplesPerSec * soundinternal->wfx.nChannels * sizeof(short);
instanceinternal->buffer.pAudioData = soundinternal->audioData.data();
instanceinternal->buffer.AudioBytes = (uint32_t)soundinternal->audioData.size();
if (instance->begin > 0)
{
const uint32_t bytes_from_beginning = AlignTo(std::min(instanceinternal->buffer.AudioBytes, uint32_t(instance->begin * bytes_per_second)), 4u);
instanceinternal->buffer.pAudioData += bytes_from_beginning;
instanceinternal->buffer.AudioBytes -= bytes_from_beginning;
}
if (instance->length > 0)
{
instanceinternal->buffer.AudioBytes = AlignTo(std::min(instanceinternal->buffer.AudioBytes, uint32_t(instance->length * bytes_per_second)), 4u);
}
uint32_t num_remaining_samples = instanceinternal->buffer.AudioBytes / (soundinternal->wfx.nChannels * sizeof(short));
if (instance->loop_begin > 0)
{
instanceinternal->buffer.LoopBegin = AlignTo(std::min(num_remaining_samples, uint32_t(instance->loop_begin * soundinternal->wfx.nSamplesPerSec)), 4u);
num_remaining_samples -= instanceinternal->buffer.LoopBegin;
}
instanceinternal->buffer.LoopLength = AlignTo(std::min(num_remaining_samples, uint32_t(instance->loop_length * soundinternal->wfx.nSamplesPerSec)), 4u);
instanceinternal->buffer.Flags = FAUDIO_END_OF_STREAM;
instanceinternal->buffer.LoopCount = instance->IsLooped() ? FAUDIO_LOOP_INFINITE : 0;
res = FAudioSourceVoice_SubmitSourceBuffer(instanceinternal->sourceVoice, &(instanceinternal->buffer), nullptr);
if(res != 0){
assert(0);
return false;
}
return true;
}
void Play(SoundInstance* instance) {
if (instance != nullptr && instance->IsValid()){
auto instanceinternal = to_internal(instance);
uint32_t res = FAudioSourceVoice_Start(instanceinternal->sourceVoice, 0, FAUDIO_COMMIT_NOW);
assert(res == 0);
}
}
void Pause(SoundInstance* instance) {
if (instance != nullptr && instance->IsValid()){
auto instanceinternal = to_internal(instance);
uint32_t res = FAudioSourceVoice_Stop(instanceinternal->sourceVoice, 0, FAUDIO_COMMIT_NOW); // preserves cursor position
assert(res == 0);
}
}
void Stop(SoundInstance* instance) {
if (instance != nullptr && instance->IsValid()){
auto instanceinternal = to_internal(instance);
uint32_t res = FAudioSourceVoice_Stop(instanceinternal->sourceVoice, 0, FAUDIO_COMMIT_NOW); // preserves cursor position
assert(res == 0);
res = FAudioSourceVoice_FlushSourceBuffers(instanceinternal->sourceVoice); // reset submitted audio buffer
assert(res == 0);
res = FAudioSourceVoice_SubmitSourceBuffer(instanceinternal->sourceVoice, &audio_internal->termination_mark, nullptr); // mark this as terminated, this resets XAUDIO2_VOICE_STATE::SamplesPlayed to zero
assert(res == 0);
res = FAudioSourceVoice_SubmitSourceBuffer(instanceinternal->sourceVoice, &(instanceinternal->buffer), nullptr);
assert(res == 0);
}
}
void SetVolume(float volume, SoundInstance* instance) {
if (instance == nullptr || !instance->IsValid()){
uint32_t res = FAudioVoice_SetVolume(audio_internal->masteringVoice, volume, FAUDIO_COMMIT_NOW);
assert(res == 0);
}
else {
auto instanceinternal = to_internal(instance);
uint32_t res = FAudioVoice_SetVolume(instanceinternal->sourceVoice, volume, FAUDIO_COMMIT_NOW);
assert(res == 0);
}
}
float GetVolume(const SoundInstance* instance) {
float volume = 0;
if (instance == nullptr || !instance->IsValid()){
FAudioVoice_GetVolume(audio_internal->masteringVoice, &volume);
}
else {
auto instanceinternal = to_internal(instance);
FAudioVoice_GetVolume(instanceinternal->sourceVoice, &volume);
}
return volume;
}
void ExitLoop(SoundInstance* instance) {
if (instance != nullptr && instance->IsValid()){
auto instanceinternal = to_internal(instance);
if (instanceinternal->buffer.LoopCount == 0)
return;
uint32_t res = FAudioSourceVoice_ExitLoop(instanceinternal->sourceVoice, FAUDIO_COMMIT_NOW);
assert(res == 0);
if (instanceinternal->ended)
{
instanceinternal->buffer.LoopCount = 0;
res = FAudioSourceVoice_SubmitSourceBuffer(instanceinternal->sourceVoice, &(instanceinternal->buffer), nullptr);
assert(res == 0);
}
}
}
bool IsEnded(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
return instanceinternal->ended;
}
return false;
}
SampleInfo GetSampleInfo(const Sound* sound)
{
SampleInfo info = {};
if (sound != nullptr && sound->IsValid())
{
auto soundinternal = to_internal(sound);
info.samples = (const short*)soundinternal->audioData.data();
info.sample_count = soundinternal->audioData.size() / sizeof(short);
info.sample_rate = soundinternal->wfx.nSamplesPerSec;
info.channel_count = soundinternal->wfx.nChannels;
}
return info;
}
uint64_t GetTotalSamplesPlayed(const SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
FAudioVoiceState state = {};
FAudioSourceVoice_GetState(instanceinternal->sourceVoice, &state, 0);
return state.SamplesPlayed;
}
return 0ull;
}
void SetSubmixVolume(SUBMIX_TYPE type, float volume) {
uint32_t res = FAudioVoice_SetVolume(audio_internal->submixVoices[type], volume, FAUDIO_COMMIT_NOW);
assert(res == 0);
}
float GetSubmixVolume(SUBMIX_TYPE type) {
float volume;
FAudioVoice_GetVolume(audio_internal->submixVoices[type], &volume);
return volume;
}
void Update3D(SoundInstance* instance, const SoundInstance3D& instance3D) {
if (instance != nullptr && instance->IsValid()){
auto instanceinternal = to_internal(instance);
F3DAUDIO_LISTENER listener = {};
listener.Position = (F3DAUDIO_VECTOR){ instance3D.listenerPos.x, instance3D.listenerPos.y, instance3D.listenerPos.z };
listener.OrientFront = (F3DAUDIO_VECTOR){ instance3D.listenerFront.x, instance3D.listenerFront.y, instance3D.listenerFront.z };
listener.OrientTop = (F3DAUDIO_VECTOR){ instance3D.listenerUp.x, instance3D.listenerUp.y, instance3D.listenerUp.z };
listener.Velocity = (F3DAUDIO_VECTOR){ instance3D.listenerVelocity.x, instance3D.listenerVelocity.y, instance3D.listenerVelocity.z };
F3DAUDIO_EMITTER emitter = {};
emitter.Position = (F3DAUDIO_VECTOR){ instance3D.emitterPos.x, instance3D.emitterPos.y, instance3D.emitterPos.z };
emitter.OrientFront = (F3DAUDIO_VECTOR){ instance3D.emitterFront.x, instance3D.emitterFront.y, instance3D.emitterFront.z };
emitter.OrientTop = (F3DAUDIO_VECTOR){ instance3D.emitterUp.x, instance3D.emitterUp.y, instance3D.emitterUp.z };
emitter.Velocity = (F3DAUDIO_VECTOR){ instance3D.emitterVelocity.x, instance3D.emitterVelocity.y, instance3D.emitterVelocity.z };
emitter.InnerRadius = instance3D.emitterRadius;
emitter.InnerRadiusAngle = F3DAUDIO_PI / 4.0f;
emitter.ChannelCount = instanceinternal->voiceDetails.InputChannels;
emitter.pChannelAzimuths = instanceinternal->channelAzimuths.data();
emitter.ChannelRadius = 0.1f;
emitter.CurveDistanceScaler = 1;
emitter.DopplerScaler = 1;
uint32_t flags = 0;
flags |= F3DAUDIO_CALCULATE_MATRIX;
flags |= F3DAUDIO_CALCULATE_LPF_DIRECT;
flags |= F3DAUDIO_CALCULATE_REVERB;
flags |= F3DAUDIO_CALCULATE_LPF_REVERB;
flags |= F3DAUDIO_CALCULATE_DOPPLER;
// flags |= F3DAUDIO_CALCULATE_DELAY;
// flags |= F3DAUDIO_CALCULATE_EMITTER_ANGLE;
// flags |= F3DAUDIO_CALCULATE_ZEROCENTER;
// flags |= F3DAUDIO_CALCULATE_REDIRECT_TO_LFE;
F3DAUDIO_DSP_SETTINGS settings = {};
settings.SrcChannelCount = instanceinternal->voiceDetails.InputChannels;
settings.DstChannelCount = instanceinternal->audio->masteringVoiceDetails.InputChannels;
settings.pMatrixCoefficients = instanceinternal->outputMatrix.data();
F3DAudioCalculate(instanceinternal->audio->audio3D, &listener, &emitter, flags, &settings);
uint32_t res;
res = FAudioSourceVoice_SetFrequencyRatio(instanceinternal->sourceVoice, settings.DopplerFactor, FAUDIO_COMMIT_NOW);
assert(res == 0);
res = FAudioVoice_SetOutputMatrix(
instanceinternal->sourceVoice,
instanceinternal->audio->submixVoices[instance->type],
settings.SrcChannelCount,
settings.DstChannelCount,
settings.pMatrixCoefficients,
FAUDIO_COMMIT_NOW);
assert(res == 0);
FAudioFilterParameters FilterParametersDirect = { FAudioLowPassFilter, 2.0f * sinf(F3DAUDIO_PI / 6.0f * settings.LPFDirectCoefficient), 1.0f };
res = FAudioVoice_SetOutputFilterParameters(instanceinternal->sourceVoice, instanceinternal->audio->submixVoices[instance->type], &FilterParametersDirect, FAUDIO_COMMIT_NOW);
assert(res == 0);
if(instance->IsEnableReverb()){
res = FAudioVoice_SetOutputMatrix(instanceinternal->sourceVoice, instanceinternal->audio->reverbSubmix, settings.SrcChannelCount, 1, &settings.ReverbLevel, FAUDIO_COMMIT_NOW);
assert(res == 0);
FAudioFilterParameters FilterParametersReverb = { FAudioLowPassFilter, 2.0f * sinf(F3DAUDIO_PI / 6.0f * settings.LPFReverbCoefficient), 1.0f };
res = FAudioVoice_SetOutputFilterParameters(instanceinternal->sourceVoice, instanceinternal->audio->reverbSubmix, &FilterParametersReverb, FAUDIO_COMMIT_NOW);
assert(res == 0);
}
}
}
void SetReverb(REVERB_PRESET preset) {
FAudioFXReverbParameters native;
ReverbConvertI3DL2ToNative(&reverbPresets[preset], &native);
uint32_t res = FAudioVoice_SetEffectParameters(audio_internal->reverbSubmix, 0, &native, sizeof(native), FAUDIO_COMMIT_NOW);
assert(res == 0);
}
}
#elif __SCE__
// PS5 audio implementation in wiAudio_PS5.cpp extension file
#else
namespace wi::audio
{
void Initialize() {}
bool CreateSound(const std::string& filename, Sound* sound) { return false; }
bool CreateSound(const uint8_t* data, size_t size, Sound* sound) { return false; }
bool CreateSoundInstance(const Sound* sound, SoundInstance* instance) { return false; }
void Play(SoundInstance* instance) {}
void Pause(SoundInstance* instance) {}
void Stop(SoundInstance* instance) {}
void SetVolume(float volume, SoundInstance* instance) {}
float GetVolume(const SoundInstance* instance) { return 0; }
void ExitLoop(SoundInstance* instance) {}
bool IsEnded(SoundInstance* instance) { return true; }
SampleInfo GetSampleInfo(const Sound* sound) { return {}; }
uint64_t GetTotalSamplesPlayed(const SoundInstance* instance) { return 0; }
void SetSubmixVolume(SUBMIX_TYPE type, float volume) {}
float GetSubmixVolume(SUBMIX_TYPE type) { return 0; }
void Update3D(SoundInstance* instance, const SoundInstance3D& instance3D) {}
void SetReverb(REVERB_PRESET preset) {}
}
#endif // _WIN32