Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
File last commit:
Show/Diff file:
Action:
FNA/lib/FAudio/tests/xaudio2.c
1536 lines | 50.4 KiB | text/x-c | CLexer
/* XAudio2 tests
*
* This tests behavior of Microsoft's XAudio2. Tests in this file should be
* written to the XAudio2 API. FAudio_compat.h provides conversion from XAudio2
* to FAudio to verify FAudio's behavior.
*
* Copyright (c) 2015-2018 Andrew Eikum for CodeWeavers
* Copyright (c) 2018 Masanori Kakura
*
* 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.
*/
#ifdef _WIN32
/* cross-compile for win32 executable */
#define COBJMACROS
#include "initguid.h"
#include "xaudio2.h"
#include "xaudio2fx.h"
#include "xapo.h"
#include "xapofx.h"
#else
/* native build against FAudio */
#include "FAudio.h"
#include "FAudioFX.h"
#include "FAPO.h"
#include "FAudio_compat.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#endif
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
static void *FAtest_malloc(size_t len)
{
#ifdef _WIN32
return HeapAlloc(GetProcessHeap(), 0, len);
#else
return malloc(len);
#endif
}
static void FAtest_free(void *p)
{
#ifdef _WIN32
HeapFree(GetProcessHeap(), 0, p);
#else
free(p);
#endif
}
static void FAtest_sleep(uint64_t millis)
{
#ifdef _WIN32
Sleep(millis);
#else
usleep(millis * 1000);
#endif
}
#define XA2CALL_0(method) if(xaudio27) hr = IXAudio27_##method((IXAudio27*)xa); else hr = IXAudio2_##method(xa);
#define XA2CALL_0V(method) if(xaudio27) IXAudio27_##method((IXAudio27*)xa); else IXAudio2_##method(xa);
#define XA2CALL_V(method, ...) if(xaudio27) IXAudio27_##method((IXAudio27*)xa, __VA_ARGS__); else IXAudio2_##method(xa, __VA_ARGS__);
#define XA2CALL(method, ...) if(xaudio27) hr = IXAudio27_##method((IXAudio27*)xa, __VA_ARGS__); else hr = IXAudio2_##method(xa, __VA_ARGS__);
#ifdef _WIN32
static HRESULT (WINAPI *pXAudio2Create)(IXAudio2 **, UINT32, XAUDIO2_PROCESSOR) = NULL;
static HRESULT (WINAPI *pCreateAudioVolumeMeter)(IUnknown**) = NULL;
#endif
static int failure_count = 0;
static int success_count = 0;
static void ok_(const char *file, int line, BOOL success, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
#define ok(success, fmt, ...) ok_(__FILE__, __LINE__, success, fmt, ##__VA_ARGS__)
static void ok_(const char *file, int line, BOOL success, const char *fmt, ...)
{
if(!success){
va_list va;
va_start(va, fmt);
fprintf(stdout, "test failed (%s:%u): ", file, line);
vfprintf(stdout, fmt, va);
va_end(va);
++failure_count;
}else
++success_count;
}
static int xaudio27;
#ifdef _WIN32
DWORD main_thread_id;
#else
pthread_t main_thread_id;
#endif
static int is_main_thread(void)
{
#ifdef _WIN32
return GetCurrentThreadId() == main_thread_id;
#else
return pthread_equal(pthread_self(), main_thread_id);
#endif
}
static void fill_buf(float *buf, WAVEFORMATEX *fmt, DWORD hz, DWORD len_frames)
{
#if 0
/* make sound */
DWORD offs, c;
for(offs = 0; offs < len_frames; ++offs)
for(c = 0; c < fmt->nChannels; ++c)
buf[offs * fmt->nChannels + c] = sinf(offs * hz * 2 * M_PI / fmt->nSamplesPerSec);
#else
/* silence */
memset(buf, 0, sizeof(float) * len_frames * fmt->nChannels);
#endif
}
static struct _cb_state {
BOOL start_called, end_called;
} ecb_state, src1_state, src2_state;
static int pass_state = 0;
static BOOL buffers_called = FALSE;
static void WINAPI ECB_OnProcessingPassStart(IXAudio2EngineCallback *This)
{
ok(!xaudio27 || pass_state == 0, "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}
static void WINAPI ECB_OnProcessingPassEnd(IXAudio2EngineCallback *This)
{
ok(!xaudio27 || pass_state == (buffers_called ? 7 : 5), "Callbacks called out of order: %u\n", pass_state);
pass_state = 0;
buffers_called = FALSE;
}
static void WINAPI ECB_OnCriticalError(IXAudio2EngineCallback *This, HRESULT Error)
{
ok(0, "Unexpected OnCriticalError\n");
}
#if _WIN32
static IXAudio2EngineCallbackVtbl ecb_vtbl = {
ECB_OnProcessingPassStart,
ECB_OnProcessingPassEnd,
ECB_OnCriticalError
};
static IXAudio2EngineCallback ecb = { &ecb_vtbl };
#else
static FAudioEngineCallback ecb = {
ECB_OnCriticalError,
ECB_OnProcessingPassEnd,
ECB_OnProcessingPassStart
};
#endif
struct simple_streaming_callback {
IXAudio2VoiceCallback IXAudio2VoiceCallback_iface;
IXAudio27SourceVoice *xa27src;
IXAudio2SourceVoice *xa2src;
};
static struct simple_streaming_callback vcb1, vcb2;
static void WINAPI VCB_OnVoiceProcessingPassStart(IXAudio2VoiceCallback *iface,
UINT32 BytesRequired)
{
struct simple_streaming_callback *This = (struct simple_streaming_callback *)iface;
XAUDIO2_VOICE_STATE state;
if(xaudio27)
IXAudio27SourceVoice_GetState(This->xa27src, &state);
else
IXAudio2SourceVoice_GetState(This->xa2src, &state, 0);
/* Underrun allowance may vary, but 0.03 seconds buffered should be enough
* to not require more bytes. */
if (state.BuffersQueued == 0){
ok(BytesRequired > 0, "No buffers queued, but no more bytes requested.\n");
}else if(state.SamplesPlayed < 22050 - 3 * 441){
ok(BytesRequired == 0, "Plenty of data buffered, but more bytes requested. Buffered: %"PRIu64" samples, requested: %u bytes\n",
22050 - state.SamplesPlayed, BytesRequired);
}else if(state.SamplesPlayed == 22050){
ok(BytesRequired > 0, "End of buffer reached, but no more bytes requested.\n");
}
if(iface == &vcb1.IXAudio2VoiceCallback_iface){
ok(!xaudio27 || pass_state == (buffers_called ? 4 : 3), "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}else{
ok(!xaudio27 || pass_state == 1, "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}
}
static void WINAPI VCB_OnVoiceProcessingPassEnd(IXAudio2VoiceCallback *iface)
{
if(iface == &vcb1.IXAudio2VoiceCallback_iface){
ok(!xaudio27 || pass_state == (buffers_called ? 6 : 4), "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}else{
ok(!xaudio27 || pass_state == (buffers_called ? 3 : 2), "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}
}
static void WINAPI VCB_OnStreamEnd(IXAudio2VoiceCallback *iface)
{
if(iface == &vcb1.IXAudio2VoiceCallback_iface){
ok(0, "Unexpected OnStreamEnd\n");
}else{
ok(!xaudio27 || pass_state == 3, "Callbacks called out of order: %u\n", pass_state);
}
}
static void WINAPI VCB_OnBufferStart(IXAudio2VoiceCallback *iface,
void *pBufferContext)
{
if(iface == &vcb1.IXAudio2VoiceCallback_iface){
ok(!xaudio27 || pass_state == 5, "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}else{
ok(!xaudio27 || pass_state == 2, "Callbacks called out of order: %u\n", pass_state);
++pass_state;
buffers_called = TRUE;
}
}
static void WINAPI VCB_OnBufferEnd(IXAudio2VoiceCallback *iface,
void *pBufferContext)
{
if(iface == &vcb1.IXAudio2VoiceCallback_iface){
ok(!xaudio27 || pass_state == 5, "Callbacks called out of order: %u\n", pass_state);
++pass_state;
}else{
ok(!xaudio27 || pass_state == 2, "Callbacks called out of order: %u\n", pass_state);
++pass_state;
buffers_called = TRUE;
}
}
static void WINAPI VCB_OnLoopEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
ok(0, "Unexpected OnLoopEnd\n");
}
static void WINAPI VCB_OnVoiceError(IXAudio2VoiceCallback *This,
void *pBuffercontext, HRESULT Error)
{
ok(0, "Unexpected OnVoiceError\n");
}
#ifdef _WIN32
static IXAudio2VoiceCallbackVtbl vcb_vtbl = {
VCB_OnVoiceProcessingPassStart,
VCB_OnVoiceProcessingPassEnd,
VCB_OnStreamEnd,
VCB_OnBufferStart,
VCB_OnBufferEnd,
VCB_OnLoopEnd,
VCB_OnVoiceError
};
static struct simple_streaming_callback vcb1 = { {&vcb_vtbl} };
static struct simple_streaming_callback vcb2 = { {&vcb_vtbl} };
#else
static struct simple_streaming_callback vcb1 = {
{
VCB_OnBufferEnd,
VCB_OnBufferStart,
VCB_OnLoopEnd,
VCB_OnStreamEnd,
VCB_OnVoiceError,
VCB_OnVoiceProcessingPassEnd,
VCB_OnVoiceProcessingPassStart
}
};
static struct simple_streaming_callback vcb2 = {
{
VCB_OnBufferEnd,
VCB_OnBufferStart,
VCB_OnLoopEnd,
VCB_OnStreamEnd,
VCB_OnVoiceError,
VCB_OnVoiceProcessingPassEnd,
VCB_OnVoiceProcessingPassStart
}
};
#endif
static void test_simple_streaming(IXAudio2 *xa)
{
HRESULT hr;
IXAudio2MasteringVoice *master;
IXAudio2SourceVoice *src, *src2;
#ifdef _WIN32
IUnknown *vumeter;
#else
FAPO *vumeter;
#endif
WAVEFORMATEX fmt;
XAUDIO2_BUFFER buf, buf2;
XAUDIO2_VOICE_STATE state;
XAUDIO2_EFFECT_DESCRIPTOR effect;
XAUDIO2_EFFECT_CHAIN chain;
XAUDIO2_PERFORMANCE_DATA perfdata;
DWORD chmask;
memset(&perfdata, 0, sizeof(perfdata));
XA2CALL_V(GetPerformanceData, &perfdata);
ok(perfdata.ActiveSourceVoiceCount == 0, "Got wrong ActiveSourceVoiceCount: %u\n",
perfdata.ActiveSourceVoiceCount);
ok(perfdata.TotalSourceVoiceCount == 0, "Got wrong TotalSourceVoiceCount: %u\n",
perfdata.TotalSourceVoiceCount);
ok(perfdata.CurrentLatencyInSamples == 0, "Expected zero latency before mastering voice creation, got %u\n", perfdata.CurrentLatencyInSamples);
memset(&ecb_state, 0, sizeof(ecb_state));
memset(&src1_state, 0, sizeof(src1_state));
memset(&src2_state, 0, sizeof(src2_state));
XA2CALL_0V(StopEngine);
/* Tests show in native XA2.8, ECB is called from a mixer thread, but VCBs
* may be called from other threads in any order. So we can't rely on any
* sequencing between VCB calls.
*
* XA2.7 does all mixing from a single thread, so call sequence can be
* tested. */
XA2CALL(RegisterForCallbacks, &ecb);
ok(hr == S_OK, "RegisterForCallbacks failed: %08x\n", hr);
if(xaudio27)
hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
else
hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0,
#ifdef _WIN32
NULL /*WCHAR *deviceID*/, NULL, AudioCategory_GameEffects);
#else
0 /*int deviceIndex*/, NULL);
#endif
ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
if(!xaudio27){
chmask = 0xdeadbeef;
IXAudio2MasteringVoice_GetChannelMask(master, &chmask);
ok(chmask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), "Got unexpected channel mask: 0x%x\n", chmask);
}
/* create first source voice */
fmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
fmt.nChannels = 2;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 32;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
fmt.cbSize = 0;
XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &vcb1.IXAudio2VoiceCallback_iface, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
if(xaudio27){
XAUDIO27_VOICE_DETAILS details;
vcb1.xa27src = (IXAudio27SourceVoice*)src;
IXAudio27SourceVoice_GetVoiceDetails((IXAudio27SourceVoice*)src, &details);
ok(details.CreationFlags == 0, "Got wrong flags: 0x%x\n", details.CreationFlags);
ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels);
ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate);
}else{
XAUDIO2_VOICE_DETAILS details;
vcb1.xa2src = src;
IXAudio2SourceVoice_GetVoiceDetails(src, &details);
ok(details.CreationFlags == 0, "Got wrong creation flags: 0x%x\n", details.CreationFlags);
ok(details.ActiveFlags == 0, "Got wrong active flags: 0x%x\n", details.CreationFlags);
ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels);
ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate);
}
memset(&buf, 0, sizeof(buf));
buf.AudioBytes = 22050 * fmt.nBlockAlign;
buf.pAudioData = FAtest_malloc(buf.AudioBytes);
fill_buf((float*)buf.pAudioData, &fmt, 440, 22050);
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
/* create second source voice */
XA2CALL(CreateSourceVoice, &src2, &fmt, 0, 1.f, &vcb2.IXAudio2VoiceCallback_iface, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
if(xaudio27){
XAUDIO27_VOICE_DETAILS details;
vcb2.xa27src = (IXAudio27SourceVoice*)src2;
IXAudio27SourceVoice_GetVoiceDetails((IXAudio27SourceVoice*)src2, &details);
ok(details.CreationFlags == 0, "Got wrong flags: 0x%x\n", details.CreationFlags);
ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels);
ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate);
}else{
XAUDIO2_VOICE_DETAILS details;
vcb2.xa2src = src2;
IXAudio2SourceVoice_GetVoiceDetails(src2, &details);
ok(details.CreationFlags == 0, "Got wrong creation flags: 0x%x\n", details.CreationFlags);
ok(details.ActiveFlags == 0, "Got wrong active flags: 0x%x\n", details.CreationFlags);
ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels);
ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate);
}
memset(&buf2, 0, sizeof(buf2));
buf2.Flags = XAUDIO2_END_OF_STREAM;
buf2.AudioBytes = 22050 * fmt.nBlockAlign;
buf2.pAudioData = FAtest_malloc(buf2.AudioBytes);
fill_buf((float*)buf2.pAudioData, &fmt, 220, 22050);
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src2, &buf2, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
hr = IXAudio2SourceVoice_Start(src2, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
XA2CALL_0(StartEngine);
ok(hr == S_OK, "StartEngine failed: %08x\n", hr);
/* hook up volume meter */
#ifdef _WIN32
if(xaudio27){
hr = CoCreateInstance(&CLSID_AudioVolumeMeter27, NULL,
CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&vumeter);
ok(hr == S_OK, "CoCreateInstance(AudioVolumeMeter) failed: %08x\n", hr);
}else{
hr = pCreateAudioVolumeMeter(&vumeter);
ok(hr == S_OK, "CreateAudioVolumeMeter failed: %08x\n", hr);
}
#else
hr = FAudioCreateVolumeMeter(&vumeter, 0);
ok(hr == S_OK, "FAudioCreateVolumeMeter failed: %08x\n", hr);
#endif
effect.InitialState = TRUE;
effect.OutputChannels = 2;
effect.pEffect = vumeter;
chain.EffectCount = 1;
chain.pEffectDescriptors = &effect;
hr = IXAudio2MasteringVoice_SetEffectChain(master, &chain);
ok(hr == S_OK, "SetEffectchain failed: %08x\n", hr);
#ifdef _WIN32
IUnknown_Release(vumeter);
#else
vumeter->Release(vumeter);
#endif
while(1){
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
if(state.SamplesPlayed >= 22050)
break;
FAtest_sleep(100);
}
ok(state.SamplesPlayed == 22050, "Got wrong samples played\n");
memset(&perfdata, 0, sizeof(perfdata));
XA2CALL_V(GetPerformanceData, &perfdata);
ok(perfdata.ActiveSourceVoiceCount == 2, "Got wrong ActiveSourceVoiceCount: %u\n",
perfdata.ActiveSourceVoiceCount);
ok(perfdata.TotalSourceVoiceCount == 2, "Got wrong TotalSourceVoiceCount: %u\n",
perfdata.TotalSourceVoiceCount);
ok(perfdata.CurrentLatencyInSamples > 0, "Got zero latency?\n");
IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
ok(state.SamplesPlayed == 22050, "Got wrong samples played after stop\n");
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
ok(state.SamplesPlayed == 22050, "Got wrong samples played after stop and start\n");
IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
IXAudio2SourceVoice_FlushSourceBuffers(src);
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
ok(state.SamplesPlayed == 22050, "Got wrong samples played after stop start and flush\n");
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
ok(state.SamplesPlayed == 22050, "Got wrong samples played after stop start flush and start\n");
FAtest_free((void*)buf.pAudioData);
FAtest_free((void*)buf2.pAudioData);
if(xaudio27){
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src2);
}else{
IXAudio2SourceVoice_DestroyVoice(src);
IXAudio2SourceVoice_DestroyVoice(src2);
}
IXAudio2MasteringVoice_DestroyVoice(master);
XA2CALL_V(UnregisterForCallbacks, &ecb);
}
static UINT32 test_DeviceDetails(IXAudio27 *xa)
{
HRESULT hr;
XAUDIO2_DEVICE_DETAILS dd;
UINT32 count, i;
hr = IXAudio27_GetDeviceCount(xa, &count);
ok(hr == S_OK, "GetDeviceCount failed: %08x\n", hr);
if(count == 0)
return 0;
for(i = 0; i < count; ++i){
hr = IXAudio27_GetDeviceDetails(xa, i, &dd);
ok(hr == S_OK, "GetDeviceDetails failed: %08x\n", hr);
if(i == 0)
ok(dd.Role == GlobalDefaultDevice, "Got wrong role for index 0: 0x%x\n", dd.Role);
else
ok(dd.Role == NotDefaultDevice, "Got wrong role for index %u: 0x%x\n", i, dd.Role);
}
return count;
}
static void WINAPI vcb_buf_OnVoiceProcessingPassStart(IXAudio2VoiceCallback *This,
UINT32 BytesRequired)
{
}
static void WINAPI vcb_buf_OnVoiceProcessingPassEnd(IXAudio2VoiceCallback *This)
{
}
static void WINAPI vcb_buf_OnStreamEnd(IXAudio2VoiceCallback *This)
{
ok(0, "Unexpected OnStreamEnd\n");
}
struct vcb_buf_testdata {
int idx;
IXAudio2SourceVoice *src;
};
static int obs_calls = 0;
static int obe_calls = 0;
static void WINAPI vcb_buf_OnBufferStart(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
struct vcb_buf_testdata *data = pBufferContext;
XAUDIO2_VOICE_STATE state;
ok(data->idx == obs_calls, "Buffer callback out of order: %u\n", data->idx);
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)data->src, &state);
else
IXAudio2SourceVoice_GetState(data->src, &state, 0);
ok(state.BuffersQueued == 5 - obs_calls, "Got wrong number of buffers remaining: %u\n", state.BuffersQueued);
ok(state.pCurrentBufferContext == pBufferContext, "Got wrong buffer from GetState\n");
++obs_calls;
}
static void WINAPI vcb_buf_OnBufferEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
struct vcb_buf_testdata *data = pBufferContext;
XAUDIO2_VOICE_STATE state;
ok(data->idx == obe_calls, "Buffer callback out of order: %u\n", data->idx);
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)data->src, &state);
else
IXAudio2SourceVoice_GetState(data->src, &state, 0);
ok(state.BuffersQueued == 5 - obe_calls - 1, "Got wrong number of buffers remaining: %u\n", state.BuffersQueued);
if(state.BuffersQueued == 0)
ok(state.pCurrentBufferContext == NULL, "Got wrong buffer from GetState: %p\n", state.pCurrentBufferContext);
else
ok(state.pCurrentBufferContext == data + 1, "Got wrong buffer from GetState: %p\n", state.pCurrentBufferContext);
++obe_calls;
}
static void WINAPI vcb_buf_OnLoopEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
ok(0, "Unexpected OnLoopEnd\n");
}
static void WINAPI vcb_buf_OnVoiceError(IXAudio2VoiceCallback *This,
void *pBuffercontext, HRESULT Error)
{
ok(0, "Unexpected OnVoiceError\n");
}
#ifdef _WIN32
static IXAudio2VoiceCallbackVtbl vcb_buf_vtbl = {
vcb_buf_OnVoiceProcessingPassStart,
vcb_buf_OnVoiceProcessingPassEnd,
vcb_buf_OnStreamEnd,
vcb_buf_OnBufferStart,
vcb_buf_OnBufferEnd,
vcb_buf_OnLoopEnd,
vcb_buf_OnVoiceError
};
static IXAudio2VoiceCallback vcb_buf = { &vcb_buf_vtbl };
#else
static FAudioVoiceCallback vcb_buf = {
vcb_buf_OnBufferEnd,
vcb_buf_OnBufferStart,
vcb_buf_OnLoopEnd,
vcb_buf_OnStreamEnd,
vcb_buf_OnVoiceError,
vcb_buf_OnVoiceProcessingPassEnd,
vcb_buf_OnVoiceProcessingPassStart
};
#endif
static int nloopends = 0;
static int nstreamends = 0;
static void WINAPI loop_buf_OnStreamEnd(IXAudio2VoiceCallback *This)
{
++nstreamends;
}
static void WINAPI loop_buf_OnBufferStart(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
}
static void WINAPI loop_buf_OnBufferEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
}
static void WINAPI loop_buf_OnLoopEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
++nloopends;
}
static void WINAPI loop_buf_OnVoiceError(IXAudio2VoiceCallback *This,
void *pBuffercontext, HRESULT Error)
{
}
#ifdef _WIN32
static IXAudio2VoiceCallbackVtbl loop_buf_vtbl = {
vcb_buf_OnVoiceProcessingPassStart,
vcb_buf_OnVoiceProcessingPassEnd,
loop_buf_OnStreamEnd,
loop_buf_OnBufferStart,
loop_buf_OnBufferEnd,
loop_buf_OnLoopEnd,
loop_buf_OnVoiceError
};
static IXAudio2VoiceCallback loop_buf = { &loop_buf_vtbl };
#else
static FAudioVoiceCallback loop_buf = {
loop_buf_OnBufferEnd,
loop_buf_OnBufferStart,
loop_buf_OnLoopEnd,
loop_buf_OnStreamEnd,
loop_buf_OnVoiceError,
vcb_buf_OnVoiceProcessingPassEnd,
vcb_buf_OnVoiceProcessingPassStart
};
#endif
static void test_buffer_callbacks(IXAudio2 *xa)
{
HRESULT hr;
IXAudio2MasteringVoice *master;
IXAudio2SourceVoice *src;
WAVEFORMATEX fmt;
XAUDIO2_BUFFER buf;
XAUDIO2_VOICE_STATE state;
struct vcb_buf_testdata testdata[5];
int i, timeout;
obs_calls = 0;
obe_calls = 0;
XA2CALL_0V(StopEngine);
if(xaudio27)
hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
else
hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0,
#ifdef _WIN32
NULL /*WCHAR *deviceID*/, NULL, AudioCategory_GameEffects);
#else
0 /*int deviceIndex*/, NULL);
#endif
ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
/* test OnBufferStart/End callbacks */
fmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
fmt.nChannels = 2;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 32;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
fmt.cbSize = 0;
XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &vcb_buf, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
memset(&buf, 0, sizeof(buf));
buf.AudioBytes = 4410 * fmt.nBlockAlign;
buf.pAudioData = FAtest_malloc(buf.AudioBytes);
fill_buf((float*)buf.pAudioData, &fmt, 440, 4410);
/* submit same buffer fragment 5 times */
for(i = 0; i < 5; ++i){
testdata[i].idx = i;
testdata[i].src = src;
buf.pContext = &testdata[i];
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
}
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
XA2CALL_0(StartEngine);
ok(hr == S_OK, "StartEngine failed: %08x\n", hr);
if(xaudio27){
hr = IXAudio27SourceVoice_SetSourceSampleRate((IXAudio27SourceVoice*)src, 48000);
ok(hr == S_OK, "SetSourceSampleRate failed: %08x\n", hr);
}else{
hr = IXAudio2SourceVoice_SetSourceSampleRate(src, 48000);
ok(hr == XAUDIO2_E_INVALID_CALL, "SetSourceSampleRate should have failed: %08x\n", hr);
}
while(1){
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
if(state.SamplesPlayed >= 4410 * 5)
break;
FAtest_sleep(100);
}
ok(state.SamplesPlayed == 4410 * 5, "Got wrong samples played\n");
if(xaudio27)
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
else
IXAudio2SourceVoice_DestroyVoice(src);
/* test OnStreamEnd callback */
XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &loop_buf, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
buf.Flags = XAUDIO2_END_OF_STREAM;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
timeout = 0;
while(nstreamends == 0 && timeout < 1000){
FAtest_sleep(100);
timeout += 100;
}
ok(nstreamends == 1, "Got wrong number of OnStreamEnd calls: %u\n", nstreamends);
/* xaudio resets SamplesPlayed after processing an end-of-stream buffer */
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
ok(state.SamplesPlayed == 0, "Got wrong samples played\n");
if(xaudio27)
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
else
IXAudio2SourceVoice_DestroyVoice(src);
IXAudio2MasteringVoice_DestroyVoice(master);
FAtest_free((void*)buf.pAudioData);
}
static UINT32 play_to_completion(IXAudio2SourceVoice *src, UINT32 max_samples)
{
XAUDIO2_VOICE_STATE state;
HRESULT hr;
nloopends = 0;
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
while(1){
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
if(state.BuffersQueued == 0)
break;
if(state.SamplesPlayed >= max_samples){
if(xaudio27)
IXAudio27SourceVoice_ExitLoop((IXAudio27SourceVoice*)src, XAUDIO2_COMMIT_NOW);
else
IXAudio2SourceVoice_ExitLoop(src, XAUDIO2_COMMIT_NOW);
}
FAtest_sleep(100);
}
hr = IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
return state.SamplesPlayed;
}
static void test_looping(IXAudio2 *xa)
{
HRESULT hr;
IXAudio2MasteringVoice *master;
IXAudio2SourceVoice *src;
WAVEFORMATEX fmt;
XAUDIO2_BUFFER buf;
UINT32 played, running_total = 0;
XA2CALL_0V(StopEngine);
if(xaudio27)
hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
else
hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0,
#ifdef _WIN32
NULL /*WCHAR *deviceID*/, NULL, AudioCategory_GameEffects);
#else
0 /*int deviceIndex*/, NULL);
#endif
ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
fmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
fmt.nChannels = 2;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 32;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
fmt.cbSize = 0;
XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &loop_buf, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
memset(&buf, 0, sizeof(buf));
buf.AudioBytes = 44100 * fmt.nBlockAlign;
buf.pAudioData = FAtest_malloc(buf.AudioBytes);
fill_buf((float*)buf.pAudioData, &fmt, 440, 44100);
XA2CALL_0(StartEngine);
ok(hr == S_OK, "StartEngine failed: %08x\n", hr);
/* play from middle to end */
buf.PlayBegin = 22050;
buf.PlayLength = 0;
buf.LoopBegin = 0;
buf.LoopLength = 0;
buf.LoopCount = 0;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 22050, "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 0, "Got wrong OnLoopEnd calls: %u\n", nloopends);
/* play 4410 samples from middle */
buf.PlayBegin = 22050;
buf.PlayLength = 4410;
buf.LoopBegin = 0;
buf.LoopLength = 0;
buf.LoopCount = 0;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 4410, "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 0, "Got wrong OnLoopEnd calls: %u\n", nloopends);
/* loop 4410 samples in middle */
buf.PlayBegin = 0;
buf.PlayLength = 0;
buf.LoopBegin = 22050;
buf.LoopLength = 4410;
buf.LoopCount = 1;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 44100 + 4410, "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
/* play last half, then loop the whole buffer */
buf.PlayBegin = 22050;
buf.PlayLength = 0;
buf.LoopBegin = 0;
buf.LoopLength = 0;
buf.LoopCount = 1;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 22050 + 44100, "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
/* play short segment from middle, loop to the beginning, and end at PlayEnd */
buf.PlayBegin = 22050;
buf.PlayLength = 4410;
buf.LoopBegin = 0;
buf.LoopLength = 0;
buf.LoopCount = 1;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 4410 + (22050 + 4410), "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
/* invalid: LoopEnd must be <= PlayEnd
* xaudio27: play until LoopEnd, loop to beginning, play until PlayEnd */
buf.PlayBegin = 22050;
buf.PlayLength = 4410;
buf.LoopBegin = 0;
buf.LoopLength = 22050 + 4410 * 2;
buf.LoopCount = 1;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
if(xaudio27){
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 4410 + (22050 + 4410 * 2), "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
}else
ok(hr == XAUDIO2_E_INVALID_CALL, "SubmitSourceBuffer should have failed: %08x\n", hr);
/* invalid: LoopEnd must be within play range
* xaudio27: plays only play range */
buf.PlayBegin = 22050;
buf.PlayLength = 0; /* == until end of buffer */
buf.LoopBegin = 0;
buf.LoopLength = 22050;
buf.LoopCount = 1;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
if(xaudio27){
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, -1);
ok(played - running_total == 22050, "Got wrong samples played: %u\n", played - running_total);
running_total = played;
ok(nloopends == 0, "Got wrong OnLoopEnd calls: %u\n", nloopends);
}else
ok(hr == XAUDIO2_E_INVALID_CALL, "SubmitSourceBuffer should have failed: %08x\n", hr);
/* invalid: LoopBegin must be before PlayEnd
* xaudio27: crashes */
if(!xaudio27){
buf.PlayBegin = 0;
buf.PlayLength = 4410;
buf.LoopBegin = 22050;
buf.LoopLength = 4410;
buf.LoopCount = 1;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == XAUDIO2_E_INVALID_CALL, "SubmitSourceBuffer should have failed: %08x\n", hr);
}
/* infinite looping buffer */
buf.PlayBegin = 22050;
buf.PlayLength = 0;
buf.LoopBegin = 0;
buf.LoopLength = 0;
buf.LoopCount = 255;
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
played = play_to_completion(src, running_total + 88200);
ok(played - running_total == 22050 + 44100 * 2, "Got wrong samples played: %u\n", played - running_total);
ok(nloopends == (played - running_total) / 88200 + 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
running_total = played;
if(xaudio27)
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
else
IXAudio2SourceVoice_DestroyVoice(src);
IXAudio2MasteringVoice_DestroyVoice(master);
FAtest_free((void*)buf.pAudioData);
}
static void test_submix(IXAudio2 *xa)
{
HRESULT hr;
IXAudio2MasteringVoice *master;
IXAudio2SubmixVoice *sub;
XA2CALL_0V(StopEngine);
if(xaudio27)
hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
else
hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0,
#ifdef _WIN32
NULL /*WCHAR *deviceID*/, NULL, AudioCategory_GameEffects);
#else
0 /*int deviceIndex*/, NULL);
#endif
ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
XA2CALL(CreateSubmixVoice, &sub, 2, 44100, 0, 0, NULL, NULL);
ok(hr == S_OK, "CreateSubmixVoice failed: %08x\n", hr);
if(xaudio27){
XAUDIO27_VOICE_DETAILS details;
IXAudio27SubmixVoice_GetVoiceDetails((IXAudio27SubmixVoice*)sub, &details);
ok(details.CreationFlags == 0, "Got wrong flags: 0x%x\n", details.CreationFlags);
ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels);
ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate);
}else{
XAUDIO2_VOICE_DETAILS details;
IXAudio2SubmixVoice_GetVoiceDetails(sub, &details);
ok(details.CreationFlags == 0, "Got wrong creation flags: 0x%x\n", details.CreationFlags);
ok(details.ActiveFlags == 0, "Got wrong active flags: 0x%x\n", details.CreationFlags);
ok(details.InputChannels == 2, "Got wrong channel count: 0x%x\n", details.InputChannels);
ok(details.InputSampleRate == 44100, "Got wrong sample rate: 0x%x\n", details.InputSampleRate);
}
IXAudio2SubmixVoice_DestroyVoice(sub);
IXAudio2MasteringVoice_DestroyVoice(master);
}
static void WINAPI flush_buf_OnVoiceProcessingPassStart(IXAudio2VoiceCallback *This,
UINT32 BytesRequired)
{
}
static void WINAPI flush_buf_OnVoiceProcessingPassEnd(IXAudio2VoiceCallback *This)
{
}
static void WINAPI flush_buf_OnStreamEnd(IXAudio2VoiceCallback *This)
{
ok(0, "Unexpected OnStreamEnd\n");
}
static void WINAPI flush_buf_OnBufferStart(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
}
struct flush_buf_testdata {
IXAudio2SourceVoice *src;
int idx;
int test;
};
static int flush_buf_tested;
static void WINAPI flush_buf_OnBufferEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
struct flush_buf_testdata *data = pBufferContext;
XAUDIO2_VOICE_STATE state;
XAUDIO2_BUFFER buf;
HRESULT hr;
ok(!is_main_thread(), "Buffer callback called from main thread!\n");
if(!data)
return;
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)data->src, &state);
switch(data->test){
case 0:
ok(state.BuffersQueued == data->idx ? 0 : 1,
"Got wrong number of buffers remaining for index %u: %u\n", data->idx, state.BuffersQueued);
ok(data->idx == flush_buf_tested, "Wrong index %u\n", data->idx);
break;
case 1:
ok(state.BuffersQueued == 1, "Got wrong number of buffers remaining: %u\n", state.BuffersQueued);
ok(data->idx == 1, "Wrong index %u\n", data->idx);
break;
case 2:
ok(state.BuffersQueued == 1, "Got wrong number of buffers remaining: %u\n", state.BuffersQueued);
/* Avoid it when first buffer is flushed */
if(data->idx == 0)
return;
/* FlushSourceBuffers is not executed immediately even when called from a callback */
memset(&buf, 0, sizeof(buf));
buf.AudioBytes = 22050 * 2 * 4;
buf.pAudioData = FAtest_malloc(buf.AudioBytes);
memset((float*)buf.pAudioData, 0, buf.AudioBytes);
hr = IXAudio2SourceVoice_SubmitSourceBuffer(data->src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)data->src, &state);
ok(state.BuffersQueued == 2, "Got wrong number of buffers remaining: %u\n", state.BuffersQueued);
hr = IXAudio2SourceVoice_FlushSourceBuffers(data->src);
ok(hr == S_OK, "FlushSourceBuffers failed: %08x\n", hr);
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)data->src, &state);
ok(state.BuffersQueued == 2, "Got wrong number of buffers remaining: %u\n", state.BuffersQueued);
break;
}
flush_buf_tested++;
}
static void WINAPI flush_buf_OnLoopEnd(IXAudio2VoiceCallback *This,
void *pBufferContext)
{
ok(0, "Unexpected OnLoopEnd\n");
}
static void WINAPI flush_buf_OnVoiceError(IXAudio2VoiceCallback *This,
void *pBuffercontext, HRESULT Error)
{
ok(0, "Unexpected OnVoiceError\n");
}
#ifdef _WIN32
static IXAudio2VoiceCallbackVtbl flush_buf_vtbl = {
flush_buf_OnVoiceProcessingPassStart,
flush_buf_OnVoiceProcessingPassEnd,
flush_buf_OnStreamEnd,
flush_buf_OnBufferStart,
flush_buf_OnBufferEnd,
flush_buf_OnLoopEnd,
flush_buf_OnVoiceError
};
static IXAudio2VoiceCallback flush_buf = { &flush_buf_vtbl };
#else
static FAudioVoiceCallback flush_buf = {
flush_buf_OnBufferEnd,
flush_buf_OnBufferStart,
flush_buf_OnLoopEnd,
flush_buf_OnStreamEnd,
flush_buf_OnVoiceError,
flush_buf_OnVoiceProcessingPassEnd,
flush_buf_OnVoiceProcessingPassStart
};
#endif
static void test_flush(IXAudio2 *xa)
{
HRESULT hr;
IXAudio2MasteringVoice *master;
IXAudio2SourceVoice *src;
WAVEFORMATEX fmt;
XAUDIO2_BUFFER buf;
XAUDIO2_VOICE_STATE state;
struct flush_buf_testdata testdata[2];
int i, j;
XA2CALL_0V(StopEngine);
if(xaudio27)
hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
else
hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0,
#ifdef _WIN32
NULL /*WCHAR *deviceID*/, NULL, AudioCategory_GameEffects);
#else
0 /*int deviceIndex*/, NULL);
#endif
ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
fmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
fmt.nChannels = 2;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 32;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
fmt.cbSize = 0;
XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, NULL, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
memset(&buf, 0, sizeof(buf));
buf.AudioBytes = 22050 * fmt.nBlockAlign;
buf.pAudioData = FAtest_malloc(buf.AudioBytes);
fill_buf((float*)buf.pAudioData, &fmt, 440, 22050);
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
XA2CALL_0(StartEngine);
ok(hr == S_OK, "StartEngine failed: %08x\n", hr);
while(1){
if(xaudio27)
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
else
IXAudio2SourceVoice_GetState(src, &state, 0);
if(state.SamplesPlayed >= 2205)
break;
FAtest_sleep(10);
}
hr = IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Stop failed: %08x\n", hr);
hr = IXAudio2SourceVoice_FlushSourceBuffers(src);
ok(hr == S_OK, "FlushSourceBuffers failed: %08x\n", hr);
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
FAtest_sleep(100);
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
if(xaudio27){
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
}else{
IXAudio2SourceVoice_DestroyVoice(src);
}
/* In XA2.7 FlushSourceBuffers is always asynchronous. We also test Stop both
* before and after FlushSourceBuffers, with two buffers submitted. During the
* FlushSourceBuffers callbacks, even if Stop was called first (and thus both
* buffers get OnBufferEnd events), the BuffersQueued is still 1 for the first
* event. The game Legend of Heroes: Trails of Cold Steel 2 relies on this. */
if(xaudio27){
for(i = 0; i < 3; ++i){
flush_buf_tested = 0;
XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &flush_buf, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
for(j = 0; j < 2; j++){
testdata[j].idx = j;
testdata[j].test = i;
testdata[j].src = src;
buf.pContext = &testdata[j];
hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
}
hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Start failed: %08x\n", hr);
while(1){
IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
if(state.SamplesPlayed >= 2205)
break;
FAtest_sleep(10);
}
switch(i){
case 0:
hr = IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Stop failed: %08x\n", hr);
hr = IXAudio2SourceVoice_FlushSourceBuffers(src);
ok(hr == S_OK, "FlushSourceBuffers failed: %08x\n", hr);
while(1){
if(flush_buf_tested >= 2)
break;
FAtest_sleep(10);
}
ok(flush_buf_tested == 2, "Wrong number of OnBufferEnd callbacks tested: %u\n", flush_buf_tested);
break;
case 1:
case 2:
hr = IXAudio2SourceVoice_FlushSourceBuffers(src);
ok(hr == S_OK, "FlushSourceBuffers failed: %08x\n", hr);
hr = IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "Stop failed: %08x\n", hr);
while(1){
if(flush_buf_tested >= 1)
break;
FAtest_sleep(10);
}
ok(flush_buf_tested == 1, "Wrong number of OnBufferEnd callbacks tested: %u\n", flush_buf_tested);
break;
}
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
}
}
IXAudio2MasteringVoice_DestroyVoice(master);
FAtest_free((void*)buf.pAudioData);
}
static void test_setchannelvolumes(IXAudio2 *xa)
{
HRESULT hr;
IXAudio2MasteringVoice *master;
IXAudio2SourceVoice *src_2ch, *src_8ch;
WAVEFORMATEX fmt_2ch, fmt_8ch;
float volumes[] = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f};
if(xaudio27)
hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 8, 44100, 0, 0, NULL);
else
hr = IXAudio2_CreateMasteringVoice(xa, &master, 8, 44100, 0,
#ifdef _WIN32
NULL /*WCHAR *deviceID*/, NULL, AudioCategory_GameEffects);
#else
0 /*int deviceIndex*/, NULL);
#endif
ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
fmt_2ch.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
fmt_2ch.nChannels = 2;
fmt_2ch.nSamplesPerSec = 44100;
fmt_2ch.wBitsPerSample = 32;
fmt_2ch.nBlockAlign = fmt_2ch.nChannels * fmt_2ch.wBitsPerSample / 8;
fmt_2ch.nAvgBytesPerSec = fmt_2ch.nSamplesPerSec * fmt_2ch.nBlockAlign;
fmt_2ch.cbSize = 0;
fmt_8ch.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
fmt_8ch.nChannels = 8;
fmt_8ch.nSamplesPerSec = 44100;
fmt_8ch.wBitsPerSample = 32;
fmt_8ch.nBlockAlign = fmt_8ch.nChannels * fmt_8ch.wBitsPerSample / 8;
fmt_8ch.nAvgBytesPerSec = fmt_8ch.nSamplesPerSec * fmt_8ch.nBlockAlign;
fmt_8ch.cbSize = 0;
XA2CALL(CreateSourceVoice, &src_2ch, &fmt_2ch, 0, 1.f, NULL, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
XA2CALL(CreateSourceVoice, &src_8ch, &fmt_8ch, 0, 1.f, NULL, NULL, NULL);
ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
hr = IXAudio2SourceVoice_SetChannelVolumes(src_2ch, 2, volumes, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "SetChannelVolumes failed: %08x\n", hr);
hr = IXAudio2SourceVoice_SetChannelVolumes(src_8ch, 8, volumes, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "SetChannelVolumes failed: %08x\n", hr);
if(xaudio27){
/* XAudio 2.7 doesn't check the number of channels */
hr = IXAudio2SourceVoice_SetChannelVolumes(src_8ch, 2, volumes, XAUDIO2_COMMIT_NOW);
ok(hr == S_OK, "SetChannelVolumes failed: %08x\n", hr);
}else{
/* the number of channels must be the same as the number of channels on the source voice */
hr = IXAudio2SourceVoice_SetChannelVolumes(src_8ch, 2, volumes, XAUDIO2_COMMIT_NOW);
ok(hr == XAUDIO2_E_INVALID_CALL, "SetChannelVolumes should have failed: %08x\n", hr);
hr = IXAudio2SourceVoice_SetChannelVolumes(src_2ch, 8, volumes, XAUDIO2_COMMIT_NOW);
ok(hr == XAUDIO2_E_INVALID_CALL, "SetChannelVolumes should have failed: %08x\n", hr);
/* volumes must not be NULL, XAudio 2.7 doesn't check this */
hr = IXAudio2SourceVoice_SetChannelVolumes(src_2ch, 2, NULL, XAUDIO2_COMMIT_NOW);
ok(hr == XAUDIO2_E_INVALID_CALL, "SetChannelVolumes should have failed: %08x\n", hr);
}
if(xaudio27){
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src_2ch);
IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src_8ch);
}else{
IXAudio2SourceVoice_DestroyVoice(src_2ch);
IXAudio2SourceVoice_DestroyVoice(src_8ch);
}
IXAudio2MasteringVoice_DestroyVoice(master);
}
int main(int argc, char **argv)
{
HRESULT hr;
IXAudio2 *xa;
IXAudio27 *xa27 = NULL;
UINT32 has_devices;
#ifdef _WIN32
HANDLE xa28dll;
main_thread_id = GetCurrentThreadId();
CoInitialize(NULL);
hr = CoCreateInstance(&CLSID_XAudio27, NULL, CLSCTX_INPROC_SERVER,
&IID_IXAudio27, (void**)&xa27);
#else
main_thread_id = pthread_self();
hr = FAudioCOMConstructEXT(&xa27, 7);
ok(hr == S_OK, "Failed to create FAudio object\n");
#endif
if(hr == S_OK){
xaudio27 = TRUE;
hr = IXAudio27_Initialize(xa27, 0, XAUDIO2_ANY_PROCESSOR);
ok(hr == S_OK, "Initialize failed: %08x\n", hr);
has_devices = test_DeviceDetails(xa27);
if(has_devices){
test_simple_streaming((IXAudio2*)xa27);
test_buffer_callbacks((IXAudio2*)xa27);
test_looping((IXAudio2*)xa27);
test_submix((IXAudio2*)xa27);
test_flush((IXAudio2*)xa27);
test_setchannelvolumes((IXAudio2*)xa27);
}else
fprintf(stdout, "No audio devices available\n");
IXAudio27_Release(xa27);
}else
fprintf(stdout, "XAudio2.7 not available, tests skipped\n");
#ifdef _WIN32
hr = E_FAIL;
xa28dll = LoadLibraryA("xaudio2_8.dll");
if(xa28dll){
pXAudio2Create = (void*)GetProcAddress(xa28dll, "XAudio2Create");
pCreateAudioVolumeMeter = (void*)GetProcAddress(xa28dll, "CreateAudioVolumeMeter");
ok(pXAudio2Create != NULL && pCreateAudioVolumeMeter != NULL,
"xaudio2_8 doesn't have expected exports?\n");
if(pXAudio2Create)
hr = pXAudio2Create(&xa, 0, XAUDIO2_DEFAULT_PROCESSOR);
}
#else
hr = FAudioCreate(&xa, 0, FAUDIO_DEFAULT_PROCESSOR);
ok(hr == S_OK, "Failed to create FAudio object\n");
#endif
if(hr == S_OK){
xaudio27 = FALSE;
has_devices = test_DeviceDetails((IXAudio27*)xa);
if(has_devices){
test_simple_streaming(xa);
test_buffer_callbacks(xa);
test_looping(xa);
test_submix(xa);
test_flush(xa);
test_setchannelvolumes(xa);
}else
fprintf(stdout, "No audio devices available\n");
IXAudio2_Release(xa);
}else
fprintf(stdout, "XAudio2.8 not available, tests skipped\n");
fprintf(stdout, "Finished with %u successful tests and %u failed tests.\n",
success_count, failure_count);
return failure_count > 0;
}