xaudio2.c 43.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (c) 2015 Andrew Eikum for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <windows.h>
20
#include <math.h>
21 22 23 24 25

#define COBJMACROS
#include "wine/test.h"
#include "initguid.h"
#include "xaudio2.h"
26 27
#include "xaudio2fx.h"
#include "xapo.h"
28
#include "xapofx.h"
29
#include "mmsystem.h"
30 31 32 33

static BOOL xaudio27;

static HRESULT (WINAPI *pXAudio2Create)(IXAudio2 **, UINT32, XAUDIO2_PROCESSOR) = NULL;
34
static HRESULT (WINAPI *pCreateAudioVolumeMeter)(IUnknown**) = NULL;
35

36
#define XA2CALL_0(method) if(xaudio27) hr = IXAudio27_##method((IXAudio27*)xa); else hr = IXAudio2_##method(xa);
37
#define XA2CALL_0V(method) if(xaudio27) IXAudio27_##method((IXAudio27*)xa); else IXAudio2_##method(xa);
38 39 40 41 42 43 44 45 46 47 48 49 50 51
#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__);

static void fill_buf(float *buf, WAVEFORMATEX *fmt, DWORD hz, DWORD len_frames)
{
    if(winetest_interactive){
        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
        memset(buf, 0, sizeof(float) * len_frames * fmt->nChannels);
}

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
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");
}

static const IXAudio2EngineCallbackVtbl ecb_vtbl = {
    ECB_OnProcessingPassStart,
    ECB_OnProcessingPassEnd,
    ECB_OnCriticalError
};

static IXAudio2EngineCallback ecb = { &ecb_vtbl };

static IXAudio2VoiceCallback vcb1;
static IXAudio2VoiceCallback vcb2;

static void WINAPI VCB_OnVoiceProcessingPassStart(IXAudio2VoiceCallback *This,
        UINT32 BytesRequired)
{
    if(This == &vcb1){
        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 *This)
{
    if(This == &vcb1){
        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 *This)
{
    ok(0, "Unexpected OnStreamEnd\n");
}

static void WINAPI VCB_OnBufferStart(IXAudio2VoiceCallback *This,
        void *pBufferContext)
{
    if(This == &vcb1){
        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 *This,
        void *pBufferContext)
{
    if(This == &vcb1){
        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");
}

static const IXAudio2VoiceCallbackVtbl vcb_vtbl = {
    VCB_OnVoiceProcessingPassStart,
    VCB_OnVoiceProcessingPassEnd,
    VCB_OnStreamEnd,
    VCB_OnBufferStart,
    VCB_OnBufferEnd,
    VCB_OnLoopEnd,
    VCB_OnVoiceError
};

static IXAudio2VoiceCallback vcb1 = { &vcb_vtbl };
static IXAudio2VoiceCallback vcb2 = { &vcb_vtbl };

167 168 169 170 171
static void test_simple_streaming(IXAudio2 *xa)
{
    HRESULT hr;
    IXAudio2MasteringVoice *master;
    IXAudio2SourceVoice *src, *src2;
172
    IUnknown *vumeter;
173 174 175
    WAVEFORMATEX fmt;
    XAUDIO2_BUFFER buf, buf2;
    XAUDIO2_VOICE_STATE state;
176 177
    XAUDIO2_EFFECT_DESCRIPTOR effect;
    XAUDIO2_EFFECT_CHAIN chain;
178
    DWORD chmask;
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    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);

195 196 197 198 199 200
    if(xaudio27)
        hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
    else
        hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0, NULL, NULL, AudioCategory_GameEffects);
    ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);

201 202 203 204 205 206
    if(!xaudio27){
        chmask = 0xdeadbeef;
        IXAudio2MasteringVoice_GetChannelMask(master, &chmask);
        ok(chmask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), "Got unexpected channel mask: 0x%x\n", chmask);
    }

207
    /* create first source voice */
208 209 210 211 212 213 214 215
    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;

216
    XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &vcb1, NULL, NULL);
217 218
    ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    if(xaudio27){
        XAUDIO27_VOICE_DETAILS details;
        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;
        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);
    }

234
    memset(&buf, 0, sizeof(buf));
235
    buf.AudioBytes = 22050 * fmt.nBlockAlign;
236
    buf.pAudioData = HeapAlloc(GetProcessHeap(), 0, buf.AudioBytes);
237
    fill_buf((float*)buf.pAudioData, &fmt, 440, 22050);
238 239 240 241 242 243 244

    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);

245
    /* create second source voice */
246
    XA2CALL(CreateSourceVoice, &src2, &fmt, 0, 1.f, &vcb2, NULL, NULL);
247 248
    ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    if(xaudio27){
        XAUDIO27_VOICE_DETAILS details;
        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;
        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);
    }

264
    memset(&buf2, 0, sizeof(buf2));
265
    buf2.AudioBytes = 22050 * fmt.nBlockAlign;
266
    buf2.pAudioData = HeapAlloc(GetProcessHeap(), 0, buf2.AudioBytes);
267
    fill_buf((float*)buf2.pAudioData, &fmt, 220, 22050);
268 269 270 271 272 273 274

    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);

275 276 277
    XA2CALL_0(StartEngine);
    ok(hr == S_OK, "StartEngine failed: %08x\n", hr);

278 279
    /* hook up volume meter */
    if(xaudio27){
280 281
        IXAPO *xapo;

282
        hr = CoCreateInstance(&CLSID_AudioVolumeMeter27, NULL,
283 284
                CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&vumeter);
        ok(hr == S_OK, "CoCreateInstance(AudioVolumeMeter) failed: %08x\n", hr);
285 286 287 288 289

        hr = IUnknown_QueryInterface(vumeter, &IID_IXAPO27, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO27 interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
290
    }else{
291 292
        IXAPO *xapo;

293 294
        hr = pCreateAudioVolumeMeter(&vumeter);
        ok(hr == S_OK, "CreateAudioVolumeMeter failed: %08x\n", hr);
295 296 297 298 299

        hr = IUnknown_QueryInterface(vumeter, &IID_IXAPO, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
300 301 302 303 304 305 306 307 308 309 310 311 312 313
    }

    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);

    IUnknown_Release(vumeter);

314 315 316 317 318
    while(1){
        if(xaudio27)
            IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
        else
            IXAudio2SourceVoice_GetState(src, &state, 0);
319
        if(state.SamplesPlayed >= 22050)
320 321 322 323
            break;
        Sleep(100);
    }

324
    ok(state.SamplesPlayed == 22050, "Got wrong samples played\n");
325 326 327 328 329 330 331 332 333 334 335 336

    HeapFree(GetProcessHeap(), 0, (void*)buf.pAudioData);
    HeapFree(GetProcessHeap(), 0, (void*)buf2.pAudioData);

    if(xaudio27){
        IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
        IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src2);
    }else{
        IXAudio2SourceVoice_DestroyVoice(src);
        IXAudio2SourceVoice_DestroyVoice(src2);
    }
    IXAudio2MasteringVoice_DestroyVoice(master);
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427

    XA2CALL_V(UnregisterForCallbacks, &ecb);
}

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");
}

static const 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 };

428
static int nloopends = 0;
429
static int nstreamends = 0;
430 431 432

static void WINAPI loop_buf_OnStreamEnd(IXAudio2VoiceCallback *This)
{
433
    ++nstreamends;
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
}

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)
{
}

static const 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 };

469 470 471 472 473 474 475 476 477
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];
478
    int i, timeout;
479 480 481 482 483 484 485 486 487 488 489 490

    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, NULL, NULL, AudioCategory_GameEffects);
    ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);

491
    /* test OnBufferStart/End callbacks */
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
    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 = HeapAlloc(GetProcessHeap(), 0, 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);

525 526
    if(xaudio27){
        hr = IXAudio27SourceVoice_SetSourceSampleRate((IXAudio27SourceVoice*)src, 48000);
527
        ok(hr == S_OK, "SetSourceSampleRate failed: %08x\n", hr);
528 529 530 531 532
    }else{
        hr = IXAudio2SourceVoice_SetSourceSampleRate(src, 48000);
        ok(hr == XAUDIO2_E_INVALID_CALL, "SetSourceSampleRate should have failed: %08x\n", hr);
    }

533 534 535 536 537 538 539 540 541 542 543 544
    while(1){
        if(xaudio27)
            IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
        else
            IXAudio2SourceVoice_GetState(src, &state, 0);
        if(state.SamplesPlayed >= 4410 * 5)
            break;
        Sleep(100);
    }

    ok(state.SamplesPlayed == 4410 * 5, "Got wrong samples played\n");

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
    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){
        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");
577 578 579 580 581 582

    if(xaudio27)
        IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
    else
        IXAudio2SourceVoice_DestroyVoice(src);

583

584
    IXAudio2MasteringVoice_DestroyVoice(master);
585 586

    HeapFree(GetProcessHeap(), 0, (void*)buf.pAudioData);
587 588
}

589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
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);
        }
        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, NULL, NULL, AudioCategory_GameEffects);
    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 = HeapAlloc(GetProcessHeap(), 0, 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;
799 800 801 802 803 804 805

    if(xaudio27)
        IXAudio27SourceVoice_DestroyVoice((IXAudio27SourceVoice*)src);
    else
        IXAudio2SourceVoice_DestroyVoice(src);
    IXAudio2MasteringVoice_DestroyVoice(master);
    HeapFree(GetProcessHeap(), 0, (void*)buf.pAudioData);
806 807
}

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
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, NULL, NULL, AudioCategory_GameEffects);
    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);
}

844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
static void test_flush(IXAudio2 *xa)
{
    HRESULT hr;
    IXAudio2MasteringVoice *master;
    IXAudio2SourceVoice *src;
    WAVEFORMATEX fmt;
    XAUDIO2_BUFFER buf;
    XAUDIO2_VOICE_STATE state;

    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, NULL, NULL, AudioCategory_GameEffects);
    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 = HeapAlloc(GetProcessHeap(), 0, 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;
        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);

    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);
    }
    IXAudio2MasteringVoice_DestroyVoice(master);

    HeapFree(GetProcessHeap(), 0, (void*)buf.pAudioData);
}

920
static UINT32 test_DeviceDetails(IXAudio27 *xa)
921 922 923 924 925 926 927 928 929
{
    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)
930
        return 0;
931 932 933 934 935 936 937 938 939 940

    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);
    }
941 942 943 944

    return count;
}

945 946 947 948 949 950 951 952 953 954
static void test_xapo_creation_legacy(const char *module, unsigned int version)
{
    HANDLE xapofxdll;
    HRESULT hr;
    IUnknown *fx_unk;
    unsigned int i;

    HRESULT (CDECL *pCreateFX)(REFCLSID,IUnknown**) = NULL;

    /* CLSIDs are the same across all versions */
955 956 957 958 959
    static const GUID *const_clsids[] = {
        &CLSID_FXEQ27,
        &CLSID_FXMasteringLimiter27,
        &CLSID_FXReverb27,
        &CLSID_FXEcho27,
960
        /* older versions of xapofx actually have support for new clsids */
961 962 963 964
        &CLSID_FXEQ,
        &CLSID_FXMasteringLimiter,
        &CLSID_FXReverb,
        &CLSID_FXEcho
965 966 967 968 969 970 971 972 973 974 975
    };

    /* different CLSID for each version */
    static const GUID *avm_clsids[] = {
        &CLSID_AudioVolumeMeter20,
        &CLSID_AudioVolumeMeter21,
        &CLSID_AudioVolumeMeter22,
        &CLSID_AudioVolumeMeter23,
        &CLSID_AudioVolumeMeter24,
        &CLSID_AudioVolumeMeter25,
        &CLSID_AudioVolumeMeter26,
976
        &CLSID_AudioVolumeMeter27
977 978 979 980 981 982 983 984 985 986
    };

    static const GUID *ar_clsids[] = {
        &CLSID_AudioReverb20,
        &CLSID_AudioReverb21,
        &CLSID_AudioReverb22,
        &CLSID_AudioReverb23,
        &CLSID_AudioReverb24,
        &CLSID_AudioReverb25,
        &CLSID_AudioReverb26,
987
        &CLSID_AudioReverb27
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
    };

    xapofxdll = LoadLibraryA(module);
    if(xapofxdll){
        pCreateFX = (void*)GetProcAddress(xapofxdll, "CreateFX");
        ok(pCreateFX != NULL, "%s did not have CreateFX?\n", module);
        if(!pCreateFX){
            FreeLibrary(xapofxdll);
            return;
        }
    }else{
        win_skip("Couldn't load %s\n", module);
        return;
    }

1003 1004 1005
    for(i = 0; i < ARRAY_SIZE(const_clsids); ++i){
        hr = pCreateFX(const_clsids[i], &fx_unk);
        ok(hr == S_OK, "%s: CreateFX(%s) failed: %08x\n", module, wine_dbgstr_guid(const_clsids[i]), hr);
1006 1007 1008 1009 1010 1011 1012 1013 1014
        if(SUCCEEDED(hr)){
            IXAPO *xapo;
            hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO27, (void**)&xapo);
            ok(hr == S_OK, "Couldn't get IXAPO27 interface: %08x\n", hr);
            if(SUCCEEDED(hr))
                IXAPO_Release(xapo);
            IUnknown_Release(fx_unk);
        }

1015 1016 1017 1018
        hr = CoCreateInstance(const_clsids[i], NULL, CLSCTX_INPROC_SERVER,
                &IID_IUnknown, (void**)&fx_unk);
        ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance should have failed: %08x\n", hr);
        if(SUCCEEDED(hr))
1019
            IUnknown_Release(fx_unk);
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
    }

    hr = pCreateFX(avm_clsids[version - 20], &fx_unk);
    ok(hr == S_OK, "%s: CreateFX(%s) failed: %08x\n", module, wine_dbgstr_guid(avm_clsids[version - 20]), hr);
    if(SUCCEEDED(hr)){
        IXAPO *xapo;
        hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO27, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO27 interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
        IUnknown_Release(fx_unk);
    }

    hr = pCreateFX(ar_clsids[version - 20], &fx_unk);
    ok(hr == S_OK, "%s: CreateFX(%s) failed: %08x\n", module, wine_dbgstr_guid(ar_clsids[version - 20]), hr);
    if(SUCCEEDED(hr)){
        IXAPO *xapo;
        hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO27, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO27 interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
        IUnknown_Release(fx_unk);
1042 1043 1044 1045 1046
    }

    FreeLibrary(xapofxdll);
}

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
static void test_xapo_creation_modern(const char *module)
{
    HANDLE xaudio2dll;
    HRESULT hr;
    IUnknown *fx_unk;
    unsigned int i;

    HRESULT (CDECL *pCreateFX)(REFCLSID,IUnknown**,void*,UINT32) = NULL;
    HRESULT (WINAPI *pCAVM)(IUnknown**) = NULL;
    HRESULT (WINAPI *pCAR)(IUnknown**) = NULL;

    /* CLSIDs are the same across all versions */
1059 1060 1061 1062 1063 1064 1065 1066 1067
    static const GUID *const_clsids[] = {
        &CLSID_FXEQ27,
        &CLSID_FXMasteringLimiter27,
        &CLSID_FXReverb27,
        &CLSID_FXEcho27,
        &CLSID_FXEQ,
        &CLSID_FXMasteringLimiter,
        &CLSID_FXReverb,
        &CLSID_FXEcho
1068 1069
    };

1070

1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
    xaudio2dll = LoadLibraryA(module);
    if(xaudio2dll){
        pCreateFX = (void*)GetProcAddress(xaudio2dll, "CreateFX");
        ok(pCreateFX != NULL, "%s did not have CreateFX?\n", module);
        if(!pCreateFX){
            FreeLibrary(xaudio2dll);
            return;
        }
    }else{
        win_skip("Couldn't load %s\n", module);
        return;
    }

1084 1085 1086
    for(i = 0; i < ARRAY_SIZE(const_clsids); ++i){
        hr = pCreateFX(const_clsids[i], &fx_unk, NULL, 0);
        ok(hr == S_OK, "%s: CreateFX(%s) failed: %08x\n", module, wine_dbgstr_guid(const_clsids[i]), hr);
1087 1088 1089 1090 1091 1092 1093 1094
        if(SUCCEEDED(hr)){
            IXAPO *xapo;
            hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO, (void**)&xapo);
            ok(hr == S_OK, "Couldn't get IXAPO interface: %08x\n", hr);
            if(SUCCEEDED(hr))
                IXAPO_Release(xapo);
            IUnknown_Release(fx_unk);
        }
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112

        hr = CoCreateInstance(const_clsids[i], NULL, CLSCTX_INPROC_SERVER,
                &IID_IUnknown, (void**)&fx_unk);
        ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance should have failed: %08x\n", hr);
        if(SUCCEEDED(hr))
            IUnknown_Release(fx_unk);
    }

    /* test legacy CLSID */
    hr = pCreateFX(&CLSID_AudioVolumeMeter27, &fx_unk, NULL, 0);
    ok(hr == S_OK, "%s: CreateFX(CLSID_AudioVolumeMeter) failed: %08x\n", module, hr);
    if(SUCCEEDED(hr)){
        IXAPO *xapo;
        hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
        IUnknown_Release(fx_unk);
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
    }

    pCAVM = (void*)GetProcAddress(xaudio2dll, "CreateAudioVolumeMeter");
    ok(pCAVM != NULL, "%s did not have CreateAudioVolumeMeter?\n", module);

    hr = pCAVM(&fx_unk);
    ok(hr == S_OK, "CreateAudioVolumeMeter failed: %08x\n", hr);
    if(SUCCEEDED(hr)){
        IXAPO *xapo;
        hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
        IUnknown_Release(fx_unk);
    }

    pCAR = (void*)GetProcAddress(xaudio2dll, "CreateAudioReverb");
    ok(pCAR != NULL, "%s did not have CreateAudioReverb?\n", module);

    hr = pCAR(&fx_unk);
    ok(hr == S_OK, "CreateAudioReverb failed: %08x\n", hr);
    if(SUCCEEDED(hr)){
        IXAPO *xapo;
        hr = IUnknown_QueryInterface(fx_unk, &IID_IXAPO, (void**)&xapo);
        ok(hr == S_OK, "Couldn't get IXAPO interface: %08x\n", hr);
        if(SUCCEEDED(hr))
            IXAPO_Release(xapo);
        IUnknown_Release(fx_unk);
    }

    FreeLibrary(xaudio2dll);
}

1146 1147 1148 1149 1150 1151 1152 1153
static void test_xapo_creation(void)
{
    test_xapo_creation_legacy("xapofx1_1.dll", 22);
    test_xapo_creation_legacy("xapofx1_2.dll", 23);
    test_xapo_creation_legacy("xapofx1_3.dll", 24);
    test_xapo_creation_legacy("xapofx1_3.dll", 25);
    test_xapo_creation_legacy("xapofx1_4.dll", 26);
    test_xapo_creation_legacy("xapofx1_5.dll", 27);
1154
    test_xapo_creation_modern("xaudio2_8.dll");
1155 1156
}

1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
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, NULL, NULL, AudioCategory_GameEffects);
    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);
}

1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
static UINT32 check_has_devices(IXAudio2 *xa)
{
    HRESULT hr;
    IXAudio2MasteringVoice *master;

    hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0, NULL, NULL, AudioCategory_GameEffects);
    if(hr != S_OK)
        return 0;

    IXAudio2MasteringVoice_DestroyVoice(master);

    return 1;
1239 1240 1241 1242 1243 1244 1245 1246
}

START_TEST(xaudio2)
{
    HRESULT hr;
    IXAudio27 *xa27 = NULL;
    IXAudio2 *xa = NULL;
    HANDLE xa28dll;
1247
    UINT32 has_devices;
1248 1249 1250 1251

    CoInitialize(NULL);

    xa28dll = LoadLibraryA("xaudio2_8.dll");
1252
    if(xa28dll){
1253
        pXAudio2Create = (void*)GetProcAddress(xa28dll, "XAudio2Create");
1254 1255
        pCreateAudioVolumeMeter = (void*)GetProcAddress(xa28dll, "CreateAudioVolumeMeter");
    }
1256

1257 1258
    test_xapo_creation();

1259
    /* XAudio 2.7 (Jun 2010 DirectX) */
1260
    hr = CoCreateInstance(&CLSID_XAudio27, NULL, CLSCTX_INPROC_SERVER,
1261 1262 1263 1264
            &IID_IXAudio27, (void**)&xa27);
    if(hr == S_OK){
        xaudio27 = TRUE;

1265 1266 1267
        hr = IXAudio27_QueryInterface(xa27, &IID_IXAudio28, (void**) &xa);
        ok(hr != S_OK, "QueryInterface with IID_IXAudio28 on IXAudio27 object returned success. Expected to fail\n");

1268 1269 1270
        hr = IXAudio27_Initialize(xa27, 0, XAUDIO2_ANY_PROCESSOR);
        ok(hr == S_OK, "Initialize failed: %08x\n", hr);

1271 1272 1273
        has_devices = test_DeviceDetails(xa27);
        if(has_devices){
            test_simple_streaming((IXAudio2*)xa27);
1274
            test_buffer_callbacks((IXAudio2*)xa27);
1275
            test_looping((IXAudio2*)xa27);
1276
            test_submix((IXAudio2*)xa27);
1277
            test_flush((IXAudio2*)xa27);
1278
            test_setchannelvolumes((IXAudio2*)xa27);
1279 1280
        }else
            skip("No audio devices available\n");
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292

        IXAudio27_Release(xa27);
    }else
        win_skip("XAudio 2.7 not available\n");

    /* XAudio 2.8 (Win8+) */
    if(pXAudio2Create){
        xaudio27 = FALSE;

        hr = pXAudio2Create(&xa, 0, XAUDIO2_DEFAULT_PROCESSOR);
        ok(hr == S_OK, "XAudio2Create failed: %08x\n", hr);

1293 1294 1295
        hr = IXAudio2_QueryInterface(xa, &IID_IXAudio27, (void**)&xa27);
        ok(hr == E_NOINTERFACE, "XA28 object should support IXAudio27, gave: %08x\n", hr);

1296 1297 1298
        has_devices = check_has_devices(xa);
        if(has_devices){
            test_simple_streaming(xa);
1299
            test_buffer_callbacks(xa);
1300
            test_looping(xa);
1301
            test_submix(xa);
1302
            test_flush(xa);
1303
            test_setchannelvolumes(xa);
1304 1305 1306
        }else
            skip("No audio devices available\n");

1307 1308 1309 1310 1311 1312
        IXAudio2_Release(xa);
    }else
        win_skip("XAudio 2.8 not available\n");

    CoUninitialize();
}