import ctypes
import platform
import os
from pyglet.libs.win32.constants import *
from pyglet.libs.win32.types import *
from pyglet.libs.win32 import com
from pyglet.util import debug_print

_debug = debug_print('debug_media')


def load_xaudio2(dll_name):
    """This will attempt to load a version of XAudio2. Versions supported: 2.9, 2.8.
       While Windows 8 ships with 2.8 and Windows 10 ships with version 2.9, it is possible to install 2.9 on 8/8.1.
    """
    xaudio2 = dll_name
    # System32 and SysWOW64 folders are opposite perception in Windows x64.
    # System32 = x64 dll's | SysWOW64 = x86 dlls
    # By default ctypes only seems to look in system32 regardless of Python architecture, which has x64 dlls.
    if platform.architecture()[0] == '32bit':
        if platform.machine().endswith('64'):  # Machine is 64 bit, Python is 32 bit.
            xaudio2 = os.path.join(os.environ['WINDIR'], 'SysWOW64', '{}.dll'.format(xaudio2))

    xaudio2_lib = ctypes.windll.LoadLibrary(xaudio2)

    # Somehow x3d uses different calling structure than the rest of the DLL; Only affects 32 bit? Microsoft...
    x3d_lib = ctypes.cdll.LoadLibrary(xaudio2)
    return xaudio2_lib, x3d_lib


try:
    xaudio2_lib, x3d_lib = load_xaudio2("xaudio2_9")
except OSError:
    _debug("Could not load XAudio2.9 library")
    try:
        xaudio2_lib, x3d_lib = load_xaudio2("xaudio2_8")
    except OSError:
        _debug("Could not load XAudio2.8 library")
        raise ImportError('Could not locate a supported XAudio2 library.')


UINT32 = c_uint32
FLOAT32 = c_float


class XAUDIO2_DEBUG_CONFIGURATION(ctypes.Structure):
    _fields_ = [
        ('TraceMask', UINT32),
        ('BreakMask', UINT32),
        ('LogThreadID', BOOL),
        ('LogFileline', BOOL),
        ('LogFunctionName', BOOL),
        ('LogTiming', BOOL),
    ]


class XAUDIO2_PERFORMANCE_DATA(ctypes.Structure):
    _fields_ = [
        ('AudioCyclesSinceLastQuery', c_uint64),
        ('TotalCyclesSinceLastQuery', c_uint64),
        ('MinimumCyclesPerQuantum', UINT32),
        ('MaximumCyclesPerQuantum', UINT32),
        ('MemoryUsageInBytes', UINT32),
        ('CurrentLatencyInSamples', UINT32),
        ('GlitchesSinceEngineStarted', UINT32),
        ('ActiveSourceVoiceCount', UINT32),
        ('TotalSourceVoiceCount', UINT32),
        ('ActiveSubmixVoiceCount', UINT32),
        ('ActiveResamplerCount', UINT32),
        ('ActiveMatrixMixCount', UINT32),
        ('ActiveXmaSourceVoices', UINT32),
        ('ActiveXmaStreams', UINT32),
    ]

    def __repr__(self):
        return "XAUDIO2PerformanceData(active_voices={}, total_voices={}, glitches={}, latency={} samples, memory_usage={} bytes)".format(self.ActiveSourceVoiceCount, self.TotalSourceVoiceCount, self.GlitchesSinceEngineStarted, self.CurrentLatencyInSamples, self.MemoryUsageInBytes)


class XAUDIO2_VOICE_SENDS(ctypes.Structure):
    _fields_ = [
        ('SendCount', UINT32),
        ('pSends', c_void_p),
    ]


class XAUDIO2_BUFFER(ctypes.Structure):
    _fields_ = [
        ('Flags', UINT32),
        ('AudioBytes', UINT32),
        ('pAudioData', POINTER(c_char)),
        ('PlayBegin', UINT32),
        ('PlayLength', UINT32),
        ('LoopBegin', UINT32),
        ('LoopLength', UINT32),
        ('LoopCount', UINT32),
        ('pContext', c_void_p),
    ]

class XAUDIO2_VOICE_STATE(ctypes.Structure):
    _fields_ = [
        ('pCurrentBufferContext', c_void_p),
        ('BuffersQueued', UINT32),
        ('SamplesPlayed', UINT32)
    ]

    def __repr__(self):
        return "XAUDIO2_VOICE_STATE(BuffersQueued={0}, SamplesPlayed={1})".format(self.BuffersQueued, self.SamplesPlayed)

class WAVEFORMATEX(ctypes.Structure):
    _fields_ = [
        ('wFormatTag', WORD),
        ('nChannels', WORD),
        ('nSamplesPerSec', DWORD),
        ('nAvgBytesPerSec', DWORD),
        ('nBlockAlign', WORD),
        ('wBitsPerSample', WORD),
        ('cbSize', WORD),
    ]

    def __repr__(self):
        return 'WAVEFORMATEX(wFormatTag={}, nChannels={}, nSamplesPerSec={}, nAvgBytesPersec={}' \
               ', nBlockAlign={}, wBitsPerSample={}, cbSize={})'.format(
            self.wFormatTag, self.nChannels, self.nSamplesPerSec,
            self.nAvgBytesPerSec, self.nBlockAlign, self.wBitsPerSample,
            self.cbSize)

XAUDIO2_USE_DEFAULT_PROCESSOR = 0x00000000  # Win 10+

if WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER:
    XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_USE_DEFAULT_PROCESSOR
else:
    XAUDIO2_DEFAULT_PROCESSOR = 0x00000001  # Windows 8/8.1


XAUDIO2_LOG_ERRORS = 0x0001   # For handled errors with serious effects.
XAUDIO2_LOG_WARNINGS = 0x0002   # For handled errors that may be recoverable.
XAUDIO2_LOG_INFO = 0x0004   # Informational chit-chat (e.g. state changes).
XAUDIO2_LOG_DETAIL = 0x0008   # More detailed chit-chat.
XAUDIO2_LOG_API_CALLS = 0x0010   # Public API function entries and exits.
XAUDIO2_LOG_FUNC_CALLS = 0x0020   # Internal function entries and exits.
XAUDIO2_LOG_TIMING = 0x0040   # Delays detected and other timing data.
XAUDIO2_LOG_LOCKS = 0x0080   # Usage of critical sections and mutexes.
XAUDIO2_LOG_MEMORY = 0x0100   # Memory heap usage information.
XAUDIO2_LOG_STREAMING = 0x1000   # Audio streaming information.


# Some XAUDIO2 global settings, most not used, but useful information
XAUDIO2_MAX_BUFFER_BYTES = 0x80000000   # Maximum bytes allowed in a source buffer
XAUDIO2_MAX_QUEUED_BUFFERS = 64         # Maximum buffers allowed in a voice queue
XAUDIO2_MAX_BUFFERS_SYSTEM = 2          # Maximum buffers allowed for system threads (Xbox 360 only)
XAUDIO2_MAX_AUDIO_CHANNELS = 64         # Maximum channels in an audio stream
XAUDIO2_MIN_SAMPLE_RATE = 1000          # Minimum audio sample rate supported
XAUDIO2_MAX_SAMPLE_RATE = 200000        # Maximum audio sample rate supported
XAUDIO2_MAX_VOLUME_LEVEL = 16777216.0   # Maximum acceptable volume level (2^24)
XAUDIO2_MIN_FREQ_RATIO =  (1/1024.0)    # Minimum SetFrequencyRatio argument
XAUDIO2_MAX_FREQ_RATIO = 1024.0         # Maximum MaxFrequencyRatio argument
XAUDIO2_DEFAULT_FREQ_RATIO  = 2.0       # Default MaxFrequencyRatio argument
XAUDIO2_MAX_FILTER_ONEOVERQ = 1.5       # Maximum XAUDIO2_FILTER_PARAMETERS.OneOverQ
XAUDIO2_MAX_FILTER_FREQUENCY = 1.0      # Maximum XAUDIO2_FILTER_PARAMETERS.Frequency
XAUDIO2_MAX_LOOP_COUNT = 254            # Maximum non-infinite XAUDIO2_BUFFER.LoopCount
XAUDIO2_MAX_INSTANCES = 8               # Maximum simultaneous XAudio2 objects on Xbox 360


XAUDIO2_FILTER_TYPE = UINT
LowPassFilter = 0  # Attenuates frequencies above the cutoff frequency (state-variable filter).
BandPassFilter = 1   # Attenuates frequencies outside a given range      (state-variable filter).
HighPassFilter = 2   # Attenuates frequencies below the cutoff frequency (state-variable filter).
NotchFilter = 3   # Attenuates frequencies inside a given range       (state-variable filter).
LowPassOnePoleFilter = 4   # Attenuates frequencies above the cutoff frequency (one-pole filter, XAUDIO2_FILTER_PARAMETERS.OneOverQ has no effect)
HighPassOnePoleFilter = 5   # Attenuates frequencies below the cutoff frequency (one-pole filter, XAUDIO2_FILTER_PARAMETERS.OneOverQ has no effect)

XAUDIO2_NO_LOOP_REGION = 0  # Used in XAUDIO2_BUFFER.LoopCount
XAUDIO2_LOOP_INFINITE = 255  # Used in XAUDIO2_BUFFER.LoopCount
XAUDIO2_DEFAULT_CHANNELS = 0  # Used in CreateMasteringVoice
XAUDIO2_DEFAULT_SAMPLERATE = 0  # Used in CreateMasteringVoice

WAVE_FORMAT_PCM = 1

XAUDIO2_DEBUG_ENGINE =            0x0001    # Used in XAudio2Create
XAUDIO2_VOICE_NOPITCH =           0x0002    # Used in IXAudio2::CreateSourceVoice
XAUDIO2_VOICE_NOSRC =             0x0004    # Used in IXAudio2::CreateSourceVoice
XAUDIO2_VOICE_USEFILTER =         0x0008    # Used in IXAudio2::CreateSource/SubmixVoice
XAUDIO2_PLAY_TAILS =              0x0020    # Used in IXAudio2SourceVoice::Stop
XAUDIO2_END_OF_STREAM =           0x0040    # Used in XAUDIO2_BUFFER.Flags
XAUDIO2_SEND_USEFILTER =          0x0080    # Used in XAUDIO2_SEND_DESCRIPTOR.Flags
XAUDIO2_VOICE_NOSAMPLESPLAYED =   0x0100    # Used in IXAudio2SourceVoice::GetState
XAUDIO2_STOP_ENGINE_WHEN_IDLE  =  0x2000    # Used in XAudio2Create to force the engine to Stop when no source voices are Started, and Start when a voice is Started
XAUDIO2_1024_QUANTUM =            0x8000    # Used in XAudio2Create to specify nondefault processing quantum of 21.33 ms (1024 samples at 48KHz)
XAUDIO2_NO_VIRTUAL_AUDIO_CLIENT = 0x10000   # Used in CreateMasteringVoice to create a virtual audio client


class IXAudio2VoiceCallback(com.Interface):
    _methods_ = [
        ('OnVoiceProcessingPassStart',
         com.VOIDMETHOD(UINT32)),
        ('OnVoiceProcessingPassEnd',
         com.VOIDMETHOD()),
        ('OnStreamEnd',
         com.VOIDMETHOD()),
        ('OnBufferStart',
         com.VOIDMETHOD(ctypes.c_void_p)),
        ('OnBufferEnd',
         com.VOIDMETHOD(ctypes.c_void_p)),
        ('OnLoopEnd',
         com.VOIDMETHOD(ctypes.c_void_p)),
        ('OnVoiceError',
         com.VOIDMETHOD(ctypes.c_void_p, HRESULT))
    ]


class XA2SourceCallback(com.COMObject):
    """Callback class used to trigger when buffers or streams end..
           WARNING: Whenever a callback is running, XAudio2 cannot generate audio.
           Make sure these functions run as fast as possible and do not block/delay more than a few milliseconds.
           MS Recommendation:
           At a minimum, callback functions must not do the following:
                - Access the hard disk or other permanent storage
                - Make expensive or blocking API calls
                - Synchronize with other parts of client code
                - Require significant CPU usage
    """
    _interfaces_ = [IXAudio2VoiceCallback]

    def __init__(self, xa2_player):
        super().__init__()
        self.xa2_player = xa2_player

    def OnBufferEnd(self, pBufferContext):
        """At the end of playing one buffer, attempt to refill again.
        Even if the player is out of sources, it needs to be called to purge all buffers.
        """
        if self.xa2_player:
            self.xa2_player.refill_source_player()

    def OnVoiceError(self, pBufferContext, hresult):
        raise Exception("Error occurred during audio playback.", hresult)


class XAUDIO2_EFFECT_DESCRIPTOR(Structure):
    _fields_ = [
        ('pEffect', com.pIUnknown),
        ('InitialState', c_bool),
        ('OutputChannels', UINT32)
    ]


class XAUDIO2_EFFECT_CHAIN(ctypes.Structure):
    _fields_ = [
        ('EffectCount', UINT32),
        ('pEffectDescriptors', POINTER(XAUDIO2_EFFECT_DESCRIPTOR)),
    ]


class XAUDIO2_FILTER_PARAMETERS(Structure):
    _fields_ = [
        ('Type', XAUDIO2_FILTER_TYPE),
        ('Frequency', FLOAT),
        ('OneOverQ', FLOAT)
    ]


class XAUDIO2_VOICE_DETAILS(Structure):
    _fields_ = [
        ('CreationFlags', UINT32),
        ('ActiveFlags', UINT32),
        ('InputChannels', UINT32),
        ('InputSampleRate', UINT32)
    ]


class IXAudio2Voice(com.pInterface):
    _methods_ = [
        ('GetVoiceDetails',
         com.STDMETHOD(POINTER(XAUDIO2_VOICE_DETAILS))),
        ('SetOutputVoices',
         com.STDMETHOD()),
        ('SetEffectChain',
         com.STDMETHOD(POINTER(XAUDIO2_EFFECT_CHAIN))),
        ('EnableEffect',
         com.STDMETHOD()),
        ('DisableEffect',
         com.STDMETHOD()),
        ('GetEffectState',
         com.STDMETHOD()),
        ('SetEffectParameters',
         com.STDMETHOD()),
        ('GetEffectParameters',
         com.STDMETHOD()),
        ('SetFilterParameters',
         com.STDMETHOD(POINTER(XAUDIO2_FILTER_PARAMETERS), UINT32)),
        ('GetFilterParameters',
         com.STDMETHOD()),
        ('SetOutputFilterParameters',
         com.STDMETHOD()),
        ('GetOutputFilterParameters',
         com.STDMETHOD()),
        ('SetVolume',
         com.STDMETHOD(ctypes.c_float, UINT32)),
        ('GetVolume',
         com.STDMETHOD(POINTER(c_float))),
        ('SetChannelVolumes',
         com.STDMETHOD()),
        ('GetChannelVolumes',
         com.STDMETHOD()),
        ('SetOutputMatrix',
         com.STDMETHOD(c_void_p, UINT32, UINT32, POINTER(FLOAT), UINT32)),
        ('GetOutputMatrix',
         com.STDMETHOD()),
        ('DestroyVoice',
         com.STDMETHOD())
    ]


class IXAudio2SubmixVoice(IXAudio2Voice):
    pass


class IXAudio2SourceVoice(IXAudio2Voice):
    _methods_ = [
        ('Start',
         com.STDMETHOD(UINT32, UINT32)),
        ('Stop',
         com.STDMETHOD(UINT32, UINT32)),
        ('SubmitSourceBuffer',
         com.STDMETHOD(POINTER(XAUDIO2_BUFFER), c_void_p)),
        ('FlushSourceBuffers',
         com.STDMETHOD()),
        ('Discontinuity',
         com.STDMETHOD()),
        ('ExitLoop',
         com.STDMETHOD()),
        ('GetState',
         com.STDMETHOD(POINTER(XAUDIO2_VOICE_STATE), UINT32)),
        ('SetFrequencyRatio',
         com.STDMETHOD(FLOAT, UINT32)),
        ('GetFrequencyRatio',
         com.STDMETHOD(POINTER(c_float))),
        ('SetSourceSampleRate',
         com.STDMETHOD()),
    ]


class IXAudio2MasteringVoice(IXAudio2Voice):
    _methods_ = [
        ('GetChannelMask',
         com.STDMETHOD(POINTER(DWORD)))
    ]


class IXAudio2EngineCallback(com.Interface):
    _methods_ = [
        ('OnProcessingPassStart',
         com.VOIDMETHOD()),
        ('OnProcessingPassEnd',
         com.VOIDMETHOD()),
        ('OnCriticalError',
         com.VOIDMETHOD(HRESULT)),
    ]


class XA2EngineCallback(com.COMObject):
    _interfaces_ = [IXAudio2EngineCallback]

    def OnCriticalError(self, hresult):
        raise Exception("Critical Error:", hresult)



# -------------- 3D Audio Positioning----------
class X3DAUDIO_DISTANCE_CURVE_POINT(ctypes.Structure):
    _fields_ = [
        ('Distance', FLOAT32),
        ('DSPSetting', FLOAT32)
    ]


class X3DAUDIO_DISTANCE_CURVE(ctypes.Structure):
    _fields_ = [
        ('pPoints', POINTER(X3DAUDIO_DISTANCE_CURVE_POINT)),
        ('PointCount', UINT32)
    ]


class X3DAUDIO_VECTOR(ctypes.Structure):
    _fields_ = [
        ('x', c_float),
        ('y', c_float),
        ('z', c_float),
    ]



"""Cone:
   Specifies directionality for a listener or single-channel emitter by
   modifying DSP behaviour with respect to its front orientation.
   This is modeled using two sound cones: an inner cone and an outer cone.
   On/within the inner cone, DSP settings are scaled by the inner values.
   On/beyond the outer cone, DSP settings are scaled by the outer values.
   If on both the cones, DSP settings are scaled by the inner values only.
   Between the two cones, the scaler is linearly interpolated between the
   inner and outer values.  Set both cone angles to 0 or X3DAUDIO_2PI for
   omnidirectionality using only the outer or inner values respectively."""
class X3DAUDIO_CONE(Structure):
    _fields_ = [
        ('InnerAngle', FLOAT32),  # inner cone angle in radians, must be within [0.0f, X3DAUDIO_2PI]
        ('OuterAngle', FLOAT32),  # outer cone angle in radians, must be within [InnerAngle, X3DAUDIO_2PI]
        ('InnerVolume', FLOAT32),  # volume level scaler on/within inner cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used
        ('OuterVolume', FLOAT32),  #  volume level scaler on/beyond outer cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used
        ('InnerLPF', FLOAT32),  # LPF (both direct and reverb paths) coefficient subtrahend on/within inner cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used
        ('OuterLPF', FLOAT32),  # LPF (both direct and reverb paths) coefficient subtrahend on/beyond outer cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used
        ('InnerReverb', FLOAT32),  # reverb send level scaler on/within inner cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used
        ('OuterReverb', FLOAT32)  # reverb send level scaler on/beyond outer cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used
    ]


class X3DAUDIO_LISTENER(Structure):
    _fields_ = [
        ('OrientFront', X3DAUDIO_VECTOR),  # orientation of front direction, used only for matrix and delay calculations or listeners with cones for matrix, LPF (both direct and reverb paths), and reverb calculations, must be normalized when used
        ('OrientTop', X3DAUDIO_VECTOR),  # orientation of top direction, used only for matrix and delay calculations, must be orthonormal with OrientFront when used
        ('Position', X3DAUDIO_VECTOR),  # position in user-defined world units, does not affect Velocity
        ('Velocity', X3DAUDIO_VECTOR),  # velocity vector in user-defined world units/second, used only for doppler calculations, does not affect Position
        ('pCone', POINTER(X3DAUDIO_CONE))  # sound cone, used only for matrix, LPF (both direct and reverb paths), and reverb calculations, NULL specifies omnidirectionality
    ]


class X3DAUDIO_EMITTER(Structure):
    _fields_ = [
        ('pCone', POINTER(X3DAUDIO_CONE)),
        ('OrientFront', X3DAUDIO_VECTOR),
        ('OrientTop', X3DAUDIO_VECTOR),
        ('Position', X3DAUDIO_VECTOR),
        ('Velocity', X3DAUDIO_VECTOR),
        ('InnerRadius', FLOAT32),
        ('InnerRadiusAngle', FLOAT32),
        ('ChannelCount', UINT32),
        ('ChannelRadius', FLOAT32),
        ('pChannelAzimuths', POINTER(FLOAT32)),
        ('pVolumeCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)),
        ('pLFECurve', POINTER(X3DAUDIO_DISTANCE_CURVE)),
        ('pLPFDirectCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)),
        ('pLPFReverbCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)),
        ('pReverbCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)),
        ('CurveDistanceScaler', FLOAT32),
        ('DopplerScaler', FLOAT32)
    ]


class X3DAUDIO_DSP_SETTINGS(Structure):
    _fields_ = [
        ('pMatrixCoefficients', POINTER(FLOAT)),  # float array
        ('pDelayTimes', POINTER(FLOAT32)),
        ('SrcChannelCount', UINT32),
        ('DstChannelCount', UINT32),
        ('LPFDirectCoefficient', FLOAT32),
        ('LPFReverbCoefficient', FLOAT32),
        ('ReverbLevel', FLOAT32),
        ('DopplerFactor', FLOAT32),
        ('EmitterToListenerAngle', FLOAT32),
        ('EmitterToListenerDistance', FLOAT32),
        ('EmitterVelocityComponent', FLOAT32),
        ('ListenerVelocityComponent', FLOAT32)
    ]

# Other constants that may or may not be used in X3D.

SPEAKER_FRONT_LEFT             = 0x00000001
SPEAKER_FRONT_RIGHT            = 0x00000002
SPEAKER_FRONT_CENTER           = 0x00000004
SPEAKER_LOW_FREQUENCY          = 0x00000008
SPEAKER_BACK_LEFT              = 0x00000010
SPEAKER_BACK_RIGHT             = 0x00000020
SPEAKER_FRONT_LEFT_OF_CENTER   = 0x00000040
SPEAKER_FRONT_RIGHT_OF_CENTER  = 0x00000080
SPEAKER_BACK_CENTER            = 0x00000100
SPEAKER_SIDE_LEFT              = 0x00000200
SPEAKER_SIDE_RIGHT             = 0x00000400
SPEAKER_TOP_CENTER             = 0x00000800
SPEAKER_TOP_FRONT_LEFT         = 0x00001000
SPEAKER_TOP_FRONT_CENTER       = 0x00002000
SPEAKER_TOP_FRONT_RIGHT        = 0x00004000
SPEAKER_TOP_BACK_LEFT          = 0x00008000
SPEAKER_TOP_BACK_CENTER        = 0x00010000
SPEAKER_TOP_BACK_RIGHT         = 0x00020000
SPEAKER_RESERVED               = 0x7FFC0000  # bit mask locations reserved for future use
SPEAKER_ALL                    = 0x80000000  # used to specify that any possible permutation of speaker configurations

SPEAKER_MONO = SPEAKER_FRONT_CENTER
SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
SPEAKER_2POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY)
SPEAKER_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)
SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
SPEAKER_4POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
SPEAKER_5POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
SPEAKER_7POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)
SPEAKER_5POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT  | SPEAKER_SIDE_RIGHT)
SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT  | SPEAKER_SIDE_RIGHT)


DBL_DECIMAL_DIG = 17                      # # of decimal digits of rounding precision
DBL_DIG = 15                      # # of decimal digits of precision
DBL_EPSILON = 2.2204460492503131e-016  # smallest such that 1.0+DBL_EPSILON != 1.0
DBL_HAS_SUBNORM = 1                       # type does support subnormal numbers
DBL_MANT_DIG  = 53                      # # of bits in mantissa
DBL_MAX  = 1.7976931348623158e+308  # max value
DBL_MAX_10_EXP = 308                     # max decimal exponent
DBL_MAX_EXP = 1024                    # max binary exponent
DBL_MIN = 2.2250738585072014e-308  # min positive value
DBL_MIN_10_EXP = (-307)                  # min decimal exponent
DBL_MIN_EXP = (-1021)                 # min binary exponent
_DBL_RADIX = 2                       # exponent radix
DBL_TRUE_MIN = 4.9406564584124654e-324  # min positive value

FLT_DECIMAL_DIG = 9                       # # of decimal digits of rounding precision
FLT_DIG  = 6                       # # of decimal digits of precision
FLT_EPSILON = 1.192092896e-07        # smallest such that 1.0+FLT_EPSILON != 1.0
FLT_HAS_SUBNORM = 1                       # type does support subnormal numbers
FLT_GUARD = 0
FLT_MANT_DIG = 24                      # # of bits in mantissa
FLT_MAX = 3.402823466e+38        # max value
FLT_MAX_10_EXP = 38                      # max decimal exponent
FLT_MAX_EXP = 128                     # max binary exponent
FLT_MIN = 1.175494351e-38        # min normalized positive value
FLT_MIN_10_EXP = (-37)                   # min decimal exponent
FLT_MIN_EXP = (-125)                  # min binary exponent
FLT_NORMALIZE = 0
FLT_RADIX = 2                       # exponent radix
FLT_TRUE_MIN = 1.401298464e-45        # min positive value

LDBL_DIG = DBL_DIG                 # # of decimal digits of precision
LDBL_EPSILON = DBL_EPSILON             # smallest such that 1.0+LDBL_EPSILON != 1.0
LDBL_HAS_SUBNORM = DBL_HAS_SUBNORM         # type does support subnormal numbers
LDBL_MANT_DIG = DBL_MANT_DIG            # # of bits in mantissa
LDBL_MAX = DBL_MAX                 # max value
LDBL_MAX_10_EXP =  DBL_MAX_10_EXP          # max decimal exponent
LDBL_MAX_EXP = DBL_MAX_EXP             # max binary exponent
LDBL_MIN = DBL_MIN                 # min normalized positive value
LDBL_MIN_10_EXP = DBL_MIN_10_EXP          # min decimal exponent
LDBL_MIN_EXP = DBL_MIN_EXP             # min binary exponent
_LDBL_RADIX = _DBL_RADIX              # exponent radix
LDBL_TRUE_MIN = DBL_TRUE_MIN            # min positive value

DECIMAL_DIG = DBL_DECIMAL_DIG


X3DAUDIO_HANDLE_BYTESIZE = 20
X3DAUDIO_HANDLE = (BYTE * X3DAUDIO_HANDLE_BYTESIZE)


# speed of sound in meters per second for dry air at approximately 20C, used with X3DAudioInitialize
X3DAUDIO_SPEED_OF_SOUND = 343.5


X3DAUDIO_CALCULATE_MATRIX = 0x00000001  # enable matrix coefficient table calculation
X3DAUDIO_CALCULATE_DELAY = 0x00000002  # enable delay time array calculation (stereo final mix only)
X3DAUDIO_CALCULATE_LPF_DIRECT = 0x00000004  # enable LPF direct-path coefficient calculation
X3DAUDIO_CALCULATE_LPF_REVERB = 0x00000008  # enable LPF reverb-path coefficient calculation
X3DAUDIO_CALCULATE_REVERB = 0x00000010  # enable reverb send level calculation
X3DAUDIO_CALCULATE_DOPPLER = 0x00000020  # enable doppler shift factor calculation
X3DAUDIO_CALCULATE_EMITTER_ANGLE = 0x00000040  # enable emitter-to-listener interior angle calculation
X3DAUDIO_CALCULATE_ZEROCENTER = 0x00010000  # do not position to front center speaker, signal positioned to remaining speakers instead, front center destination channel will be zero in returned matrix coefficient table, valid only for matrix calculations with final mix formats that have a front center channel
X3DAUDIO_CALCULATE_REDIRECT_TO_LFE = 0x00020000  # apply equal mix of all source channels to LFE destination channel, valid only for matrix calculations with sources that have no LFE channel and final mix formats that have an LFE channel

default_dsp_calculation = X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER

X3DAudioInitialize = x3d_lib.X3DAudioInitialize
X3DAudioInitialize.restype = HRESULT
X3DAudioInitialize.argtypes = [c_int, c_float, c_void_p]


X3DAudioCalculate = x3d_lib.X3DAudioCalculate
X3DAudioCalculate.restype = c_void
X3DAudioCalculate.argtypes = [POINTER(X3DAUDIO_HANDLE), POINTER(X3DAUDIO_LISTENER), POINTER(X3DAUDIO_EMITTER), UINT32, POINTER(X3DAUDIO_DSP_SETTINGS)]


AudioCategory_Other = 0
AudioCategory_ForegroundOnlyMedia = 1
AudioCategory_Communications = 3
AudioCategory_Alerts = 4
AudioCategory_SoundEffects = 5
AudioCategory_GameEffects = 6
AudioCategory_GameMedia = 7
AudioCategory_GameChat = 8
AudioCategory_Speech = 9
AudioCategory_Movie = 10
AudioCategory_Media = 11

# Reverb not implemented but if someone wants to take a stab at it.
class XAUDIO2FX_REVERB_PARAMETERS(Structure):
    _fields_ = [
        ('WetDryMix', c_float),  #  ratio of wet (processed) signal to dry (original) signal

        # Delay times
        ('ReflectionsDelay', UINT32),  #  [0, 300] in ms
        ('ReverbDelay', BYTE),  # [0, 85] in ms
        ('RearDelay', UINT32),  # 7.1: [0, 20] in ms, all other: [0, 5] in ms
        ('SideDelay', UINT32),  # .1: [0, 5] in ms, all other: not used, but still validated  # WIN 10 only.

        # Indexed Paremeters
        ('PositionLeft', BYTE),  # [0, 30] no units
        ('PositionRight', BYTE),  # 0, 30] no units, ignored when configured to mono
        ('PositionMatrixLeft', BYTE),  # [0, 30] no units
        ('PositionMatrixRight', BYTE),  # [0, 30] no units, ignored when configured to mono
        ('EarlyDiffusion', BYTE), # [0, 15] no units
        ('LateDiffusion', BYTE),  # [0, 15] no units
        ('LowEQGain', BYTE),  # [0, 12] no units
        ('LowEQCutoff', BYTE),  # [0, 9] no units
        ('LowEQCutoff', BYTE),  # [0, 8] no units
        ('HighEQCutoff', BYTE),  # [0, 14] no units

        # Direct parameters
        ('RoomFilterFreq', c_float),  # [20, 20000] in Hz
        ('RoomFilterMain', c_float),  # [-100, 0] in dB
        ('RoomFilterHF', c_float),  # [-100, 0] in dB
        ('ReflectionsGain', c_float),  # [-100, 20] in dB
        ('ReverbGain', c_float),  # [-100, 20] in dB
        ('DecayTime', c_float),  # [0.1, inf] in seconds
        ('Density', c_float),  # [0, 100] (percentage)
        ('RoomSize', c_float),  # [1, 100] in feet

        # component control
        ('DisableLateField', c_bool),  # TRUE to disable late field reflections
    ]


class IXAudio2(com.pIUnknown):
    _methods_ = [
        ('RegisterForCallbacks',
           com.STDMETHOD(POINTER(IXAudio2EngineCallback))),
        ('UnregisterForCallbacks',
           com.METHOD(ctypes.c_void_p, POINTER(IXAudio2EngineCallback))),
        ('CreateSourceVoice',
         com.STDMETHOD(POINTER(IXAudio2SourceVoice), POINTER(WAVEFORMATEX), UINT32, c_float,
                       POINTER(IXAudio2VoiceCallback), POINTER(XAUDIO2_VOICE_SENDS), POINTER(XAUDIO2_EFFECT_CHAIN))),
        ('CreateSubmixVoice',
         com.STDMETHOD(POINTER(IXAudio2SubmixVoice), UINT32, UINT32, UINT32, UINT32,
                       POINTER(XAUDIO2_VOICE_SENDS), POINTER(XAUDIO2_EFFECT_CHAIN))),
        ('CreateMasteringVoice',
         com.STDMETHOD(POINTER(IXAudio2MasteringVoice), UINT32, UINT32, UINT32, LPCWSTR, POINTER(XAUDIO2_EFFECT_CHAIN),
                       UINT32)),
        ('StartEngine',
         com.STDMETHOD()),
        ('StopEngine',
         com.STDMETHOD()),
        ('CommitChanges',
         com.STDMETHOD(UINT32)),
        ('GetPerformanceData',
         com.METHOD(c_void, POINTER(XAUDIO2_PERFORMANCE_DATA))),
        ('SetDebugConfiguration',
         com.STDMETHOD(POINTER(XAUDIO2_DEBUG_CONFIGURATION), c_void_p)),
    ]


XAudio2Create = xaudio2_lib.XAudio2Create
XAudio2Create.restype = HRESULT
XAudio2Create.argtypes = [POINTER(IXAudio2), UINT32, UINT32]

CreateAudioReverb = xaudio2_lib.CreateAudioReverb
CreateAudioReverb.restype = HRESULT
CreateAudioReverb.argtypes = [POINTER(com.pIUnknown)]

