Show More
Commit Description:
Tweak UI appearance.
Commit Description:
Tweak UI appearance.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/FAudio/utils/testreverb/audio_xaudio.cpp
291 lines | 7.9 KiB | text/x-c | CppLexer
291 lines | 7.9 KiB | text/x-c | CppLexer
r0 | ||||
#include "audio.h" | ||||
#ifdef HAVE_XAUDIO2 | ||||
#include <xaudio2.h> | ||||
#include <xaudio2fx.h> | ||||
#include <SDL.h> | ||||
struct AudioContext | ||||
{ | ||||
IXAudio2 *xaudio2; | ||||
uint32_t output_5p1; | ||||
AudioVoiceType effect_on_voice; | ||||
IXAudio2MasteringVoice *mastering_voice; | ||||
IXAudio2SubmixVoice *submix_voice; | ||||
IXAudio2SourceVoice *source_voice; | ||||
IXAudio2Voice *voices[3]; | ||||
unsigned int wav_channels; | ||||
unsigned int wav_samplerate; | ||||
drwav_uint64 wav_sample_count; | ||||
float * wav_samples; | ||||
XAUDIO2_BUFFER buffer; | ||||
XAUDIO2_EFFECT_DESCRIPTOR reverb_effect; | ||||
XAUDIO2_EFFECT_CHAIN effect_chain; | ||||
ReverbParameters reverb_params; | ||||
bool reverb_enabled; | ||||
}; | ||||
void xaudio_destroy_context(AudioContext *context) | ||||
{ | ||||
if (context != NULL) | ||||
{ | ||||
context->source_voice->DestroyVoice(); | ||||
context->submix_voice->DestroyVoice(); | ||||
context->mastering_voice->DestroyVoice(); | ||||
context->xaudio2->Release(); | ||||
delete context; | ||||
} | ||||
} | ||||
void xaudio_create_voice(AudioContext *context, float *buffer, size_t buffer_size, int sample_rate, int num_channels) | ||||
{ | ||||
// create reverb effect | ||||
IUnknown *xapo = NULL; | ||||
HRESULT hr = XAudio2CreateReverb(&xapo); | ||||
if (FAILED(hr)) | ||||
{ | ||||
return; | ||||
} | ||||
// create effect chain | ||||
context->reverb_effect.InitialState = context->reverb_enabled; | ||||
context->reverb_effect.OutputChannels = (context->output_5p1) ? 6 : context->wav_channels; | ||||
context->reverb_effect.pEffect = xapo; | ||||
context->effect_chain.EffectCount = 1; | ||||
context->effect_chain.pEffectDescriptors = &context->reverb_effect; | ||||
XAUDIO2_EFFECT_CHAIN *voice_effect[3] = {NULL, NULL, NULL}; | ||||
voice_effect[context->effect_on_voice] = &context->effect_chain; | ||||
// create a mastering voice | ||||
uint32_t inChannels = context->wav_channels; | ||||
if (context->output_5p1 && context->effect_on_voice != AudioVoiceType_Master) | ||||
{ | ||||
inChannels = 6; | ||||
} | ||||
hr = context->xaudio2->CreateMasteringVoice( | ||||
&context->mastering_voice, | ||||
inChannels, | ||||
XAUDIO2_DEFAULT_SAMPLERATE, | ||||
0, | ||||
0, | ||||
voice_effect[AudioVoiceType_Master] | ||||
); | ||||
if (FAILED(hr)) | ||||
{ | ||||
return; | ||||
} | ||||
context->voices[AudioVoiceType_Master] = context->mastering_voice; | ||||
// create a submix voice | ||||
inChannels = context->wav_channels; | ||||
if (context->output_5p1 && context->effect_on_voice == AudioVoiceType_Source) | ||||
{ | ||||
inChannels = 6; | ||||
} | ||||
hr = context->xaudio2->CreateSubmixVoice( | ||||
&context->submix_voice, | ||||
inChannels, | ||||
SAMPLERATE, | ||||
0, | ||||
0, | ||||
NULL, | ||||
voice_effect[AudioVoiceType_Submix] | ||||
); | ||||
if (FAILED(hr)) | ||||
{ | ||||
return; | ||||
} | ||||
context->voices[AudioVoiceType_Submix] = context->submix_voice; | ||||
context->submix_voice->SetVolume(1.0f); | ||||
// create a source voice | ||||
XAUDIO2_SEND_DESCRIPTOR desc = {0, context->submix_voice}; | ||||
XAUDIO2_VOICE_SENDS sends = {1, &desc}; | ||||
WAVEFORMATEX waveFormat; | ||||
waveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; | ||||
waveFormat.nChannels = num_channels; | ||||
waveFormat.nSamplesPerSec = sample_rate; | ||||
waveFormat.nBlockAlign = num_channels * 4; | ||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; | ||||
waveFormat.wBitsPerSample = 32; | ||||
waveFormat.cbSize = 0; | ||||
hr = context->xaudio2->CreateSourceVoice( | ||||
&context->source_voice, | ||||
&waveFormat, | ||||
0, | ||||
XAUDIO2_DEFAULT_FREQ_RATIO, | ||||
NULL, | ||||
&sends, | ||||
voice_effect[AudioVoiceType_Source] | ||||
); | ||||
if (FAILED(hr)) | ||||
{ | ||||
return; | ||||
} | ||||
context->voices[AudioVoiceType_Source] = context->source_voice; | ||||
context->source_voice->SetVolume(1.0f); | ||||
xapo->Release(); | ||||
// submit the array | ||||
SDL_zero(context->buffer); | ||||
context->buffer.AudioBytes = 4 * buffer_size * num_channels; | ||||
context->buffer.pAudioData = (byte *)buffer; | ||||
context->buffer.Flags = XAUDIO2_END_OF_STREAM; | ||||
context->buffer.PlayBegin = 0; | ||||
context->buffer.PlayLength = buffer_size; | ||||
context->buffer.LoopBegin = 0; | ||||
context->buffer.LoopLength = 0; | ||||
context->buffer.LoopCount = 0; | ||||
} | ||||
void xaudio_reverb_set_params(AudioContext *context) | ||||
{ | ||||
XAUDIO2FX_REVERB_PARAMETERS native_params = { 0 }; | ||||
native_params.WetDryMix = context->reverb_params.WetDryMix; | ||||
native_params.ReflectionsDelay = context->reverb_params.ReflectionsDelay; | ||||
native_params.ReverbDelay = context->reverb_params.ReverbDelay; | ||||
native_params.RearDelay = context->reverb_params.RearDelay; | ||||
native_params.PositionLeft = context->reverb_params.PositionLeft; | ||||
native_params.PositionRight = context->reverb_params.PositionRight; | ||||
native_params.PositionMatrixLeft = context->reverb_params.PositionMatrixLeft; | ||||
native_params.PositionMatrixRight = context->reverb_params.PositionMatrixRight; | ||||
native_params.EarlyDiffusion = context->reverb_params.EarlyDiffusion; | ||||
native_params.LateDiffusion = context->reverb_params.LateDiffusion; | ||||
native_params.LowEQGain = context->reverb_params.LowEQGain; | ||||
native_params.LowEQCutoff = context->reverb_params.LowEQCutoff; | ||||
native_params.HighEQGain = context->reverb_params.HighEQGain; | ||||
native_params.HighEQCutoff = context->reverb_params.HighEQCutoff; | ||||
native_params.RoomFilterFreq = context->reverb_params.RoomFilterFreq; | ||||
native_params.RoomFilterMain = context->reverb_params.RoomFilterMain; | ||||
native_params.RoomFilterHF = context->reverb_params.RoomFilterHF; | ||||
native_params.ReflectionsGain = context->reverb_params.ReflectionsGain; | ||||
native_params.ReverbGain = context->reverb_params.ReverbGain; | ||||
native_params.DecayTime = context->reverb_params.DecayTime; | ||||
native_params.Density = context->reverb_params.Density; | ||||
native_params.RoomSize = context->reverb_params.RoomSize; | ||||
/* 2.8+ only but zero-initialization catches this | ||||
native_params.DisableLateField = 0; */ | ||||
HRESULT hr = context->voices[context->effect_on_voice]->SetEffectParameters( | ||||
0, | ||||
&native_params, | ||||
sizeof(XAUDIO2FX_REVERB_PARAMETERS) | ||||
); | ||||
} | ||||
void xaudio_wave_load(AudioContext *context, AudioSampleWave sample, bool stereo) | ||||
{ | ||||
if (context->source_voice) | ||||
{ | ||||
context->source_voice->DestroyVoice(); | ||||
context->submix_voice->DestroyVoice(); | ||||
context->mastering_voice->DestroyVoice(); | ||||
} | ||||
context->wav_samples = WAVS_Open( | ||||
sample, | ||||
stereo, | ||||
&context->wav_channels, | ||||
&context->wav_samplerate, | ||||
&context->wav_sample_count); | ||||
context->wav_sample_count /= context->wav_channels; | ||||
audio_create_voice(context, context->wav_samples, context->wav_sample_count, context->wav_samplerate, context->wav_channels); | ||||
xaudio_reverb_set_params(context); | ||||
} | ||||
void xaudio_wave_play(AudioContext *context) | ||||
{ | ||||
context->source_voice->Stop(); | ||||
context->source_voice->FlushSourceBuffers(); | ||||
HRESULT hr = context->source_voice->SubmitSourceBuffer(&context->buffer); | ||||
if (FAILED(hr)) | ||||
{ | ||||
return; | ||||
} | ||||
context->source_voice->Start(); | ||||
} | ||||
void xaudio_wave_stop(AudioContext *context) | ||||
{ | ||||
context->source_voice->Stop(XAUDIO2_PLAY_TAILS); | ||||
} | ||||
void xaudio_effect_change(AudioContext *context, bool enabled, ReverbParameters *params) | ||||
{ | ||||
HRESULT hr; | ||||
if (context->reverb_enabled && !enabled) | ||||
{ | ||||
hr = context->voices[context->effect_on_voice]->DisableEffect(0); | ||||
context->reverb_enabled = enabled; | ||||
} | ||||
else if (!context->reverb_enabled && enabled) | ||||
{ | ||||
hr = context->voices[context->effect_on_voice]->EnableEffect(0); | ||||
context->reverb_enabled = enabled; | ||||
} | ||||
memcpy(&context->reverb_params, params, sizeof(ReverbParameters)); | ||||
xaudio_reverb_set_params(context); | ||||
} | ||||
AudioContext *xaudio_create_context(bool output_5p1, AudioVoiceType effect_on_voice) | ||||
{ | ||||
// setup function pointers | ||||
audio_destroy_context = xaudio_destroy_context; | ||||
audio_create_voice = xaudio_create_voice; | ||||
audio_wave_load = xaudio_wave_load; | ||||
audio_wave_play = xaudio_wave_play; | ||||
audio_wave_stop = xaudio_wave_stop; | ||||
audio_effect_change = xaudio_effect_change; | ||||
// create XAudio object | ||||
IXAudio2 *xaudio2; | ||||
HRESULT hr = XAudio2Create(&xaudio2); | ||||
if (FAILED(hr)) | ||||
{ | ||||
return NULL; | ||||
} | ||||
// return a context object | ||||
AudioContext *context = new AudioContext(); | ||||
context->xaudio2 = xaudio2; | ||||
context->output_5p1 = output_5p1; | ||||
context->effect_on_voice = effect_on_voice; | ||||
context->mastering_voice = NULL; | ||||
context->submix_voice = NULL; | ||||
context->source_voice = NULL; | ||||
context->wav_samples = NULL; | ||||
context->reverb_params = audio_reverb_presets[0]; | ||||
context->reverb_enabled = false; | ||||
// load the first wave | ||||
audio_wave_load(context, (AudioSampleWave) 0, false); | ||||
return context; | ||||
} | ||||
#endif // HAVE_XAUDIO2 | ||||