Files
WickedEngine/WickedEngine/wiAudio.cpp
T
Turanszki Janos 4fcf2020af added linux build
2020-06-13 19:38:49 +01:00

526 lines
15 KiB
C++

#include "wiAudio.h"
#include "wiBackLog.h"
#include "wiHelper.h"
#include <vector>
#ifdef _WIN32
#include <wrl/client.h> // ComPtr
#include <xaudio2.h>
#include <xaudio2fx.h>
#include <x3daudio.h>
#pragma comment(lib,"xaudio2.lib")
#ifdef _XBOX //Big-Endian
#define fourccRIFF 'RIFF'
#define fourccDATA 'data'
#define fourccFMT 'fmt '
#define fourccWAVE 'WAVE'
#define fourccXWMA 'XWMA'
#define fourccDPDS 'dpds'
#endif
#ifndef _XBOX //Little-Endian
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#endif
namespace wiAudio
{
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;
AudioInternal()
{
HRESULT hr;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
assert(SUCCEEDED(hr));
hr = XAudio2Create(&audioEngine, 0, XAUDIO2_DEFAULT_PROCESSOR);
assert(SUCCEEDED(hr));
#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 = audioEngine->CreateMasteringVoice(&masteringVoice);
assert(SUCCEEDED(hr));
masteringVoice->GetVoiceDetails(&masteringVoiceDetails);
for (int i = 0; i < SUBMIX_TYPE_COUNT; ++i)
{
hr = audioEngine->CreateSubmixVoice(
&submixVoices[i],
masteringVoiceDetails.InputChannels,
masteringVoiceDetails.InputSampleRate,
0, 0, 0, 0);
assert(SUCCEEDED(hr));
}
DWORD channelMask;
masteringVoice->GetChannelMask(&channelMask);
hr = X3DAudioInitialize(channelMask, X3DAUDIO_SPEED_OF_SOUND, audio3D);
assert(SUCCEEDED(hr));
// Reverb setup:
{
hr = XAudio2CreateReverb(&reverbEffect);
assert(SUCCEEDED(hr));
XAUDIO2_EFFECT_DESCRIPTOR effects[] = { { reverbEffect.Get(), TRUE, 1 } };
XAUDIO2_EFFECT_CHAIN effectChain = { arraysize(effects), effects };
hr = audioEngine->CreateSubmixVoice(
&reverbSubmix,
1, // reverb is mono
masteringVoiceDetails.InputSampleRate,
0, 0, nullptr, &effectChain);
assert(SUCCEEDED(hr));
XAUDIO2FX_REVERB_PARAMETERS native;
ReverbConvertI3DL2ToNative(&reverbPresets[REVERB_PRESET_DEFAULT], &native);
HRESULT hr = reverbSubmix->SetEffectParameters(0, &native, sizeof(native));
assert(SUCCEEDED(hr));
}
success = SUCCEEDED(hr);
}
~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();
}
};
std::shared_ptr<AudioInternal> audio;
struct SoundInternal
{
std::shared_ptr<AudioInternal> audio;
WAVEFORMATEX wfx = {};
std::vector<uint8_t> audioData;
};
struct SoundInstanceInternal
{
std::shared_ptr<AudioInternal> audio;
std::shared_ptr<SoundInternal> soundinternal;
IXAudio2SourceVoice* sourceVoice = nullptr;
XAUDIO2_VOICE_DETAILS voiceDetails = {};
std::vector<float> outputMatrix;
std::vector<float> channelAzimuths;
XAUDIO2_BUFFER buffer = {};
~SoundInstanceInternal()
{
sourceVoice->Stop();
sourceVoice->DestroyVoice();
}
};
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());
}
void Initialize()
{
audio = std::make_shared<AudioInternal>();
if (audio->success)
{
wiBackLog::post("wiAudio Initialized");
}
}
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)
{
std::vector<uint8_t> filedata;
bool success = wiHelper::FileRead(filename, filedata);
if (!success)
{
return false;
}
return CreateSound(filedata, sound);
}
bool CreateSound(const std::vector<uint8_t>& data, Sound* sound)
{
std::shared_ptr<SoundInternal> soundinternal = std::make_shared<SoundInternal>();
sound->internal_state = soundinternal;
DWORD dwChunkSize;
DWORD dwChunkPosition;
bool success;
success = FindChunk(data.data(), fourccRIFF, dwChunkSize, dwChunkPosition);
assert(success);
DWORD filetype;
memcpy(&filetype, data.data() + dwChunkPosition, sizeof(DWORD));
assert(filetype == fourccWAVE);
soundinternal->audio = audio;
success = FindChunk(data.data(), fourccFMT, dwChunkSize, dwChunkPosition);
assert(success);
memcpy(&soundinternal->wfx, data.data() + dwChunkPosition, dwChunkSize);
soundinternal->wfx.wFormatTag = WAVE_FORMAT_PCM;
success = FindChunk(data.data(), fourccDATA, dwChunkSize, dwChunkPosition);
assert(success);
soundinternal->audioData.resize(dwChunkSize);
memcpy(soundinternal->audioData.data(), data.data() + dwChunkPosition, dwChunkSize);
return true;
}
bool CreateSoundInstance(const Sound* sound, SoundInstance* instance)
{
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;
instanceinternal->soundinternal = soundinternal;
XAUDIO2_SEND_DESCRIPTOR SFXSend[] = {
{ XAUDIO2_SEND_USEFILTER, audio->submixVoices[instance->type] },
{ XAUDIO2_SEND_USEFILTER, audio->reverbSubmix },
};
XAUDIO2_VOICE_SENDS SFXSendList = { arraysize(SFXSend), SFXSend };
hr = audio->audioEngine->CreateSourceVoice(&instanceinternal->sourceVoice, &soundinternal->wfx,
0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, &SFXSendList, NULL);
if (FAILED(hr))
{
assert(0);
return false;
}
instanceinternal->sourceVoice->GetVoiceDetails(&instanceinternal->voiceDetails);
instanceinternal->outputMatrix.resize(size_t(instanceinternal->voiceDetails.InputChannels) * size_t(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());
}
instanceinternal->buffer.AudioBytes = (UINT32)soundinternal->audioData.size();
instanceinternal->buffer.pAudioData = soundinternal->audioData.data();
instanceinternal->buffer.Flags = XAUDIO2_END_OF_STREAM;
instanceinternal->buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
instanceinternal->buffer.LoopBegin = UINT32(instance->loop_begin * audio->masteringVoiceDetails.InputSampleRate);
instanceinternal->buffer.LoopLength = UINT32(instance->loop_length * audio->masteringVoiceDetails.InputSampleRate);
hr = instanceinternal->sourceVoice->SubmitSourceBuffer(&instanceinternal->buffer);
if (FAILED(hr))
{
assert(0);
return false;
}
return true;
}
void Play(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
HRESULT hr = instanceinternal->sourceVoice->Start();
assert(SUCCEEDED(hr));
}
}
void Pause(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
HRESULT hr = instanceinternal->sourceVoice->Stop(); // preserves cursor position
assert(SUCCEEDED(hr));
}
}
void Stop(SoundInstance* instance)
{
if (instance != nullptr && instance->IsValid())
{
auto instanceinternal = to_internal(instance);
HRESULT hr = instanceinternal->sourceVoice->Stop(); // preserves cursor position
assert(SUCCEEDED(hr));
hr = instanceinternal->sourceVoice->FlushSourceBuffers(); // reset submitted audio buffer
assert(SUCCEEDED(hr));
hr = instanceinternal->sourceVoice->SubmitSourceBuffer(&instanceinternal->buffer); // resubmit
assert(SUCCEEDED(hr));
}
}
void SetVolume(float volume, SoundInstance* instance)
{
if (instance == nullptr || !instance->IsValid())
{
HRESULT hr = audio->masteringVoice->SetVolume(volume);
assert(SUCCEEDED(hr));
}
else
{
auto instanceinternal = to_internal(instance);
HRESULT hr = instanceinternal->sourceVoice->SetVolume(volume);
assert(SUCCEEDED(hr));
}
}
float GetVolume(const SoundInstance* instance)
{
float volume = 0;
if (instance == nullptr || !instance->IsValid())
{
audio->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);
HRESULT hr = instanceinternal->sourceVoice->ExitLoop();
assert(SUCCEEDED(hr));
}
}
void SetSubmixVolume(SUBMIX_TYPE type, float volume)
{
HRESULT hr = audio->submixVoices[type]->SetVolume(volume);
assert(SUCCEEDED(hr));
}
float GetSubmixVolume(SUBMIX_TYPE type)
{
float volume;
audio->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 = audio->masteringVoiceDetails.InputChannels;
settings.pMatrixCoefficients = instanceinternal->outputMatrix.data();
X3DAudioCalculate(audio->audio3D, &listener, &emitter, flags, &settings);
HRESULT hr;
hr = instanceinternal->sourceVoice->SetFrequencyRatio(settings.DopplerFactor);
assert(SUCCEEDED(hr));
hr = instanceinternal->sourceVoice->SetOutputMatrix(
audio->submixVoices[instance->type],
settings.SrcChannelCount,
settings.DstChannelCount,
settings.pMatrixCoefficients
);
assert(SUCCEEDED(hr));
hr = instanceinternal->sourceVoice->SetOutputMatrix(audio->reverbSubmix, settings.SrcChannelCount, 1, &settings.ReverbLevel);
assert(SUCCEEDED(hr));
XAUDIO2_FILTER_PARAMETERS FilterParametersDirect = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * settings.LPFDirectCoefficient), 1.0f };
hr = instanceinternal->sourceVoice->SetOutputFilterParameters(audio->submixVoices[instance->type], &FilterParametersDirect);
assert(SUCCEEDED(hr));
XAUDIO2_FILTER_PARAMETERS FilterParametersReverb = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * settings.LPFReverbCoefficient), 1.0f };
hr = instanceinternal->sourceVoice->SetOutputFilterParameters(audio->reverbSubmix, &FilterParametersReverb);
assert(SUCCEEDED(hr));
}
}
void SetReverb(REVERB_PRESET preset)
{
XAUDIO2FX_REVERB_PARAMETERS native;
ReverbConvertI3DL2ToNative(&reverbPresets[preset], &native);
HRESULT hr = audio->reverbSubmix->SetEffectParameters(0, &native, sizeof(native));
assert(SUCCEEDED(hr));
}
}
#else
namespace wiAudio
{
void Initialize() {}
bool CreateSound(const std::string& filename, Sound* sound) {}
bool CreateSound(const std::vector<uint8_t>& data, Sound* sound) {}
bool CreateSoundInstance(const Sound* sound, SoundInstance* instance) {}
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) {}
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