/*
 * Copyright (c) 2015 Mark Harmstone
 *
 * 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
 */

import "unknwn.idl";
import "mmdeviceapi.idl";

[
    uuid(5a508685-a254-4fba-9b82-9a24b00306af)
]
coclass XAudio2 {
    interface IUnknown;
}

[
    uuid(db05ea35-0329-4d4b-a53a-6dead03d3852)
]
coclass XAudio2Debug {
    interface IUnknown;
}

cpp_quote("#if 0")
typedef struct WAVEFORMATEX
{
    WORD wFormatTag;
    WORD nChannels;
    DWORD nSamplesPerSec;
    DWORD nAvgBytesPerSec;
    WORD nBlockAlign;
    WORD wBitsPerSample;
    WORD cbSize;
} WAVEFORMATEX;

typedef struct {
    WAVEFORMATEX    Format;
    union {
        WORD        wValidBitsPerSample;
        WORD        wSamplesPerBlock;
        WORD        wReserved;
    } Samples;
    DWORD           dwChannelMask;
    GUID            SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
cpp_quote("#else")
cpp_quote("#include <mmreg.h>")
cpp_quote("#endif")

interface IXAudio2Voice;

typedef enum XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER
{
    Processor1  = 0x00000001,
    Processor2  = 0x00000002,
    Processor3  = 0x00000004,
    Processor4  = 0x00000008,
    Processor5  = 0x00000010,
    Processor6  = 0x00000020,
    Processor7  = 0x00000040,
    Processor8  = 0x00000080,
    Processor9  = 0x00000100,
    Processor10 = 0x00000200,
    Processor11 = 0x00000400,
    Processor12 = 0x00000800,
    Processor13 = 0x00001000,
    Processor14 = 0x00002000,
    Processor15 = 0x00004000,
    Processor16 = 0x00008000,
    Processor17 = 0x00010000,
    Processor18 = 0x00020000,
    Processor19 = 0x00040000,
    Processor20 = 0x00080000,
    Processor21 = 0x00100000,
    Processor22 = 0x00200000,
    Processor23 = 0x00400000,
    Processor24 = 0x00800000,
    Processor25 = 0x01000000,
    Processor26 = 0x02000000,
    Processor27 = 0x04000000,
    Processor28 = 0x08000000,
    Processor29 = 0x10000000,
    Processor30 = 0x20000000,
    Processor31 = 0x40000000,
    Processor32 = 0x80000000,
    XAUDIO2_ANY_PROCESSOR = 0xffffffff,
    XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
} XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER, XAUDIO2_PROCESSOR;

typedef struct XAUDIO2_PERFORMANCE_DATA
{
    UINT64 AudioCyclesSinceLastQuery;
    UINT64 TotalCyclesSinceLastQuery;
    UINT32 MinimumCyclesPerQuantum;
    UINT32 MaximumCyclesPerQuantum;
    UINT32 MemoryUsageInBytes;
    UINT32 CurrentLatencyInSamples;
    UINT32 GlitchesSinceEngineStarted;
    UINT32 ActiveSourceVoiceCount;
    UINT32 TotalSourceVoiceCount;
    UINT32 ActiveSubmixVoiceCount;
    UINT32 ActiveResamplerCount;
    UINT32 ActiveMatrixMixCount;
    UINT32 ActiveXmaSourceVoices;
    UINT32 ActiveXmaStreams;
} XAUDIO2_PERFORMANCE_DATA;

typedef enum XAUDIO2_DEVICE_ROLE
{
    NotDefaultDevice            = 0x0,
    DefaultConsoleDevice        = 0x1,
    DefaultMultimediaDevice     = 0x2,
    DefaultCommunicationsDevice = 0x4,
    DefaultGameDevice           = 0x8,
    GlobalDefaultDevice         = 0xf,
    InvalidDeviceRole = ~GlobalDefaultDevice
} XAUDIO2_DEVICE_ROLE;

typedef struct XAUDIO2_DEVICE_DETAILS
{
    WCHAR DeviceID[256];
    WCHAR DisplayName[256];
    XAUDIO2_DEVICE_ROLE Role;
    WAVEFORMATEXTENSIBLE OutputFormat;
} XAUDIO2_DEVICE_DETAILS;

typedef struct XAUDIO2_VOICE_DETAILS
{
    UINT32 CreationFlags;
    UINT32 InputChannels;
    UINT32 InputSampleRate;
} XAUDIO2_VOICE_DETAILS;

typedef struct XAUDIO2_SEND_DESCRIPTOR
{
    UINT32 Flags;
    IXAudio2Voice* pOutputVoice;
} XAUDIO2_SEND_DESCRIPTOR;

typedef struct XAUDIO2_VOICE_SENDS
{
    UINT32 SendCount;
    XAUDIO2_SEND_DESCRIPTOR* pSends;
} XAUDIO2_VOICE_SENDS;

typedef struct XAUDIO2_EFFECT_DESCRIPTOR
{
    IUnknown* pEffect;
    BOOL InitialState;
    UINT32 OutputChannels;
} XAUDIO2_EFFECT_DESCRIPTOR;

typedef struct XAUDIO2_EFFECT_CHAIN
{
    UINT32 EffectCount;
    XAUDIO2_EFFECT_DESCRIPTOR* pEffectDescriptors;
} XAUDIO2_EFFECT_CHAIN;

const UINT32 XAUDIO2_MAX_BUFFER_BYTES = 0x80000000;
const UINT32 XAUDIO2_MAX_QUEUED_BUFFERS = 64;
const UINT32 XAUDIO2_MAX_BUFFERS_SYSTEM = 2;
const UINT32 XAUDIO2_MAX_AUDIO_CHANNELS = 64;
const UINT32 XAUDIO2_MIN_SAMPLE_RATE = 1000;
const UINT32 XAUDIO2_MAX_SAMPLE_RATE = 200000;
const float XAUDIO2_MAX_VOLUME_LEVEL = 16777216.0;
const float XAUDIO2_MIN_FREQ_RATIO = (1/1024.0);
const float XAUDIO2_MAX_FREQ_RATIO = 1024.0;
const float XAUDIO2_DEFAULT_FREQ_RATIO = 2.0;
const float XAUDIO2_MAX_FILTER_ONEOVERQ = 1.5;
const float XAUDIO2_MAX_FILTER_FREQUENCY = 1.0;
const UINT32 XAUDIO2_MAX_LOOP_COUNT = 254;

const UINT32 XAUDIO2_COMMIT_NOW = 0;
const UINT32 XAUDIO2_COMMIT_ALL = 0;
const UINT32 XAUDIO2_INVALID_OPSET = 0xffffffff;
const UINT32 XAUDIO2_NO_LOOP_REGION = 0;
const UINT32 XAUDIO2_LOOP_INFINITE = 255;
const UINT32 XAUDIO2_DEFAULT_CHANNELS = 0;
const UINT32 XAUDIO2_DEFAULT_SAMPLERATE = 0;

[
    object,
    local
]
interface IXAudio2EngineCallback
{
    void OnProcessingPassStart();

    void OnProcessingPassEnd();

    void OnCriticalError([in] HRESULT Error);
}

typedef enum XAUDIO2_FILTER_TYPE
{
    LowPassFilter,
    BandPassFilter,
    HighPassFilter,
    NotchFilter
} XAUDIO2_FILTER_TYPE;

typedef struct XAUDIO2_FILTER_PARAMETERS
{
    XAUDIO2_FILTER_TYPE Type;
    float Frequency;
    float OneOverQ;
} XAUDIO2_FILTER_PARAMETERS;

[
    object,
    local
]
interface IXAudio2Voice
{
    void GetVoiceDetails([out] XAUDIO2_VOICE_DETAILS* pVoiceDetails);

    HRESULT SetOutputVoices([in] const XAUDIO2_VOICE_SENDS* pSendList);

    HRESULT SetEffectChain([in] const XAUDIO2_EFFECT_CHAIN* pEffectChain);

    HRESULT EnableEffect(
        [in] UINT32 EffectIndex,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    HRESULT DisableEffect(
        [in] UINT32 EffectIndex,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetEffectState(
        [in] UINT32 EffectIndex,
        [out] BOOL* pEnabled);

    HRESULT SetEffectParameters(
        [in] UINT32 EffectIndex,
        [in] const void* pParameters,
        [in] UINT32 ParametersByteSize,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    HRESULT GetEffectParameters(
        [in] UINT32 EffectIndex,
        [out] void* pParameters,
        [in] UINT32 ParametersByteSize);

    HRESULT SetFilterParameters(
        [in] const XAUDIO2_FILTER_PARAMETERS* pParameters,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetFilterParameters([out] XAUDIO2_FILTER_PARAMETERS* pParameters);

    HRESULT SetOutputFilterParameters(
        [in] IXAudio2Voice* pDestinationVoice,
        [in] const XAUDIO2_FILTER_PARAMETERS* pParameters,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetOutputFilterParameters(
        [in] IXAudio2Voice* pDestinationVoice,
        [out] XAUDIO2_FILTER_PARAMETERS* pParameters);

    HRESULT SetVolume(
        [in] float Volume,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetVolume([out] float* pVolume);

    HRESULT SetChannelVolumes(
        [in] UINT32 Channels,
        [in, size_is(Channels)] const float* pVolumes,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetChannelVolumes(
        [in] UINT32 Channels,
        [out, size_is(Channels)] float* pVolumes);

    HRESULT SetOutputMatrix(
        [in] IXAudio2Voice* pDestinationVoice,
        [in] UINT32 SourceChannels,
        [in] UINT32 DestinationChannels,
        [in, size_is(SourceChannels * DestinationChannels)] const float* pLevelMatrix,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetOutputMatrix(
        [in] IXAudio2Voice* pDestinationVoice,
        [in] UINT32 SourceChannels,
        [in] UINT32 DestinationChannels,
        [out, size_is(SourceChannels * DestinationChannels)] float* pLevelMatrix);

    void DestroyVoice();
}

typedef struct XAUDIO2_BUFFER
{
    UINT32 Flags;
    UINT32 AudioBytes;
    const BYTE* pAudioData;
    UINT32 PlayBegin;
    UINT32 PlayLength;
    UINT32 LoopBegin;
    UINT32 LoopLength;
    UINT32 LoopCount;
    void* pContext;
} XAUDIO2_BUFFER;

typedef struct XAUDIO2_BUFFER_WMA
{
    const UINT32* pDecodedPacketCumulativeBytes;
    UINT32 PacketCount;
} XAUDIO2_BUFFER_WMA;

typedef struct XAUDIO2_VOICE_STATE
{
    void* pCurrentBufferContext;
    UINT32 BuffersQueued;
    UINT64 SamplesPlayed;
} XAUDIO2_VOICE_STATE;

[
    local
]
interface IXAudio2SourceVoice : IXAudio2Voice
{
    HRESULT Start(
        [in, defaultvalue(0)] UINT32 Flags,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    HRESULT Stop(
        [in, defaultvalue(0)] UINT32 Flags,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    HRESULT SubmitSourceBuffer(
        [in] const XAUDIO2_BUFFER* pBuffer,
        [in, defaultvalue(NULL)] const XAUDIO2_BUFFER_WMA* pBufferWMA);

    HRESULT FlushSourceBuffers();

    HRESULT Discontinuity();

    HRESULT ExitLoop([in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetState([out] XAUDIO2_VOICE_STATE* pVoiceState);

    HRESULT SetFrequencyRatio(
        [in] float Ratio,
        [in, defaultvalue(XAUDIO2_COMMIT_NOW)] UINT32 OperationSet);

    void GetFrequencyRatio([out] float* pRatio);

    HRESULT SetSourceSampleRate([in] UINT32 NewSourceSampleRate);
}

[
    local
]
interface IXAudio2SubmixVoice : IXAudio2Voice
{
}

[
    local
]
interface IXAudio2MasteringVoice : IXAudio2Voice
{
}

[
    object,
    local
]
interface IXAudio2VoiceCallback
{
    void OnVoiceProcessingPassStart([in] UINT32 BytesRequired);

    void OnVoiceProcessingPassEnd();

    void OnStreamEnd();

    void OnBufferStart([in] void* pBufferContext);

    void OnBufferEnd([in] void* pBufferContext);

    void OnLoopEnd([in] void* pBufferContext);

    void OnVoiceError(
        [in] void* pBuffercontext,
        [in] HRESULT Error);
}

typedef struct XAUDIO2_DEBUG_CONFIGURATION
{
    UINT32 TraceMask;
    UINT32 BreakMask;
    BOOL LogThreadID;
    BOOL LogFileline;
    BOOL LogFunctionName;
    BOOL LogTiming;
} XAUDIO2_DEBUG_CONFIGURATION;

[
    object,
    uuid(8bcf1f58-9fe7-4583-8ac6-e2adc465c8bb),
]
interface IXAudio2 : IUnknown
{
    HRESULT GetDeviceCount([out] UINT32* pCount);

    HRESULT GetDeviceDetails(
        [in] UINT32 Index,
        [out] XAUDIO2_DEVICE_DETAILS* pDeviceDetails);

    HRESULT Initialize(
        [in, defaultvalue(0)] UINT32 Flags,
        [in, defaultvalue(XAUDIO2_DEFAULT_PROCESSOR)] XAUDIO2_PROCESSOR XAudio2Processor);

    HRESULT RegisterForCallbacks([in] IXAudio2EngineCallback* pCallback);

    void UnregisterForCallbacks([in] IXAudio2EngineCallback* pCallback);

    HRESULT CreateSourceVoice(
        [out] IXAudio2SourceVoice** ppSourceVoice,
        [in] const WAVEFORMATEX* pSourceFormat,
        [in, defaultvalue(0)] UINT32 Flags,
        [in, defaultvalue(XAUDIO2_DEFAULT_FREQ_RATIO)] float MaxFrequencyRatio,
        [in, defaultvalue(NULL)] IXAudio2VoiceCallback* pCallback,
        [in, defaultvalue(NULL)] const XAUDIO2_VOICE_SENDS* pSendList,
        [in, defaultvalue(NULL)] const XAUDIO2_EFFECT_CHAIN* pEffectChain);

    HRESULT CreateSubmixVoice(
        [out] IXAudio2SubmixVoice** ppSubmixVoice,
        [in] UINT32 InputChannels,
        [in] UINT32 InputSampleRate,
        [in, defaultvalue(0)] UINT32 Flags,
        [in, defaultvalue(0)] UINT32 ProcessingStage,
        [in, defaultvalue(NULL)] const XAUDIO2_VOICE_SENDS* pSendList,
        [in, defaultvalue(NULL)] const XAUDIO2_EFFECT_CHAIN* pEffectChain);

    HRESULT CreateMasteringVoice(
        [out] IXAudio2MasteringVoice** ppMasteringVoice,
        [in, defaultvalue(XAUDIO2_DEFAULT_CHANNELS)] UINT32 InputChannels,
        [in, defaultvalue(XAUDIO2_DEFAULT_SAMPLERATE)] UINT32 InputSampleRate,
        [in, defaultvalue(0)] UINT32 Flags,
        [in, defaultvalue(0)] UINT32 DeviceIndex,
        [in, defaultvalue(NULL)] const XAUDIO2_EFFECT_CHAIN* pEffectChain);

    HRESULT StartEngine();

    void StopEngine();

    HRESULT CommitChanges([in] UINT32 OperationSet);

    void GetPerformanceData([out] XAUDIO2_PERFORMANCE_DATA* pPerfData);

    [local] void SetDebugConfiguration(
        [in] const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
        [in, defaultvalue(NULL)] void* pReserved);
}

const UINT32 XAUDIO2_DEBUG_ENGINE = 1;
const UINT32 XAUDIO2_VOICE_NOPITCH = 2;
const UINT32 XAUDIO2_VOICE_NOSRC = 4;
const UINT32 XAUDIO2_VOICE_USEFILTER = 8;
const UINT32 XAUDIO2_VOICE_MUSIC = 16;
const UINT32 XAUDIO2_PLAY_TAILS = 32;
const UINT32 XAUDIO2_END_OF_STREAM = 64;
const UINT32 XAUDIO2_SEND_USEFILTER = 128;

const XAUDIO2_FILTER_TYPE XAUDIO2_DEFAULT_FILTER_TYPE = LowPassFilter;
const float XAUDIO2_DEFAULT_FILTER_FREQUENCY = XAUDIO2_MAX_FILTER_FREQUENCY;
const float XAUDIO2_DEFAULT_FILTER_ONEOVERQ = 1.0;
const UINT32 XAUDIO2_QUANTUM_NUMERATOR = 1;
const UINT32 XAUDIO2_QUANTUM_DENOMINATOR = 100;
const float XAUDIO2_QUANTUM_MS = (1000.0 * XAUDIO2_QUANTUM_NUMERATOR / XAUDIO2_QUANTUM_DENOMINATOR);

const HRESULT XAUDIO2_E_INVALID_CALL = 0x88960001;
const HRESULT XAUDIO2_E_XMA_DECODER_ERROR = 0x88960002;
const HRESULT XAUDIO2_E_XAPO_CREATION_FAILED = 0x88960003;
const HRESULT XAUDIO2_E_DEVICE_INVALIDATED = 0x88960004;

cpp_quote("#ifdef XAUDIO2_HELPER_FUNCTIONS")
cpp_quote("#define _USE_MATH_DEFINES")
cpp_quote("#include <math.h>")
cpp_quote("inline static float XAudio2DecibelsToAmplitudeRatio(float decibels) { return powf(10.0f, decibels/20.0f); }")
cpp_quote("inline static float XAudio2AmplitudeRatioToDecibels(float volume) { if (volume == 0) { return -3.402823466e+38f; } return 20.0f * log10f(volume); }")
cpp_quote("inline static float XAudio2SemitonesToFrequencyRatio(float semitones) { return powf(2.0f, semitones/12.0f); }")
cpp_quote("inline static float XAudio2FrequencyRatioToSemitones(float freqratio) { return 39.86313713864835f * log10f(freqratio); }")
cpp_quote("inline static float XAudio2CutoffFrequencyToRadians(float cutofffreq, UINT32 samplerate) { if ((UINT32)(cutofffreq * 6.0f) >= samplerate) { return XAUDIO2_MAX_FILTER_FREQUENCY; } return 2.0f * sinf((float)M_PI * cutofffreq / samplerate); }")
cpp_quote("inline static float XAudio2RadiansToCutoffFrequency(float radians, float samplerate) { return samplerate * asinf(radians/2.0f) / (float)M_PI; }")
cpp_quote("#endif")

cpp_quote("#ifdef __cplusplus")
cpp_quote("inline static HRESULT XAudio2Create(IXAudio2** pxaudio2, UINT32 flags = 0, XAUDIO2_PROCESSOR processor = XAUDIO2_DEFAULT_PROCESSOR) {")
cpp_quote("    IXAudio2* xaudio2;")
cpp_quote("    HRESULT hr = CoCreateInstance(__uuidof(XAudio2), NULL, CLSCTX_INPROC_SERVER, __uuidof(IXAudio2), (void**)&xaudio2);")
cpp_quote("    if (SUCCEEDED(hr)) {")
cpp_quote("        hr = xaudio2->Initialize(flags, processor);")
cpp_quote("        if (SUCCEEDED(hr))")
cpp_quote("            *pxaudio2 = xaudio2;")
cpp_quote("        else")
cpp_quote("            xaudio2->Release();")
cpp_quote("    }")
cpp_quote("    return hr;")
cpp_quote("}")
cpp_quote("#else")
cpp_quote("inline static HRESULT XAudio2Create(IXAudio2** pxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor) {")
cpp_quote("    IXAudio2* xaudio2;")
cpp_quote("    HRESULT hr = CoCreateInstance(&CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, &IID_IXAudio2, (void**)&xaudio2);")
cpp_quote("    if (SUCCEEDED(hr)) {")
cpp_quote("        hr = xaudio2->lpVtbl->Initialize(xaudio2, flags, processor);")
cpp_quote("        if (SUCCEEDED(hr))")
cpp_quote("            *pxaudio2 = xaudio2;")
cpp_quote("        else")
cpp_quote("            xaudio2->lpVtbl->Release(xaudio2);")
cpp_quote("    }")
cpp_quote("    return hr;")
cpp_quote("}")
cpp_quote("#endif")
