/*
 *  Copyright 2017-2023 Valve Corporation.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#ifndef IPL_PHONON_H
#define IPL_PHONON_H

#include <stddef.h>

#include "phonon_version.h"

#if defined(_WIN32)
#define IPLCALL __stdcall
#else
#define IPLCALL
#endif

#if defined(STEAMAUDIO_BUILDING_CORE)
#if (defined(_WIN32) || defined(_WIN64))
#define IPLAPI __declspec(dllexport)
#else
#define IPLAPI __attribute__((visibility("default")))
#endif
#else
#define IPLAPI
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if defined(IPL_ENABLE_OCTAVE_BANDS)
#define IPL_NUM_BANDS   11
#else
#define IPL_NUM_BANDS   3
#endif

#define DECLARE_OPAQUE_HANDLE(x)    typedef struct _##x##_t* x

/*****************************************************************************************************************/

/** \defgroup types Data Types
    Common data types used throughout the Steam Audio API.
    \{
*/

typedef char                IPLint8;    /**< Signed 8-bit integer. */
typedef unsigned char       IPLuint8;   /**< Unsigned 8-bit integer. */
typedef short               IPLint16;   /**< Signed 16-bit integer. */
typedef unsigned short      IPLuint16;  /**< Unsigned 16-bit integer. */
typedef int                 IPLint32;   /**< Signed 32-bit integer. */
typedef unsigned int        IPLuint32;  /**< Unsigned 32-bit integer. */
typedef long long           IPLint64;   /**< Signed 64-bit integer. */
typedef unsigned long long  IPLuint64;  /**< Unsigned 64-bit integer. */
typedef float               IPLfloat32; /**< Single-precision floating-point number. */
typedef double              IPLfloat64; /**< Double-precision floating-point number. */
typedef unsigned char       IPLbyte;    /**< A single byte. */
typedef size_t              IPLsize;    /**< Unsigned integer of machine-dependent size. Equivalent to \c size_t. */
typedef const char*         IPLstring;  /**< NULL-terminated ASCII or UTF-8 string. */

/** Boolean values. */
typedef enum {
    IPL_FALSE,  /**< The Boolean value \c false. */
    IPL_TRUE    /**< The Boolean value \c true. */
} IPLbool;

/** Status codes returned by Steam Audio API functions. */
typedef enum {
    IPL_STATUS_SUCCESS,         /**< The operation completed successfully. */
    IPL_STATUS_FAILURE,         /**< An unspecified error occurred. */
    IPL_STATUS_OUTOFMEMORY,     /**< The system ran out of memory. */
    IPL_STATUS_INITIALIZATION   /**< An error occurred while initializing an external dependency. */
} IPLerror;

/** Callback for updating the application on the progress of a function.

    You can use this to provide the user with visual feedback, like a progress bar.

    \param  progress    Fraction of the function work that has been completed, between 0.0 and 1.0.
    \param  userData    Pointer to arbitrary user-specified data provided when calling the function that will
                        call this callback.
*/
typedef void (IPLCALL *IPLProgressCallback)(IPLfloat32 progress, void* userData);

/** \} */


/*****************************************************************************************************************/

/** \defgroup context Context
    \{
*/

/** A context object, which controls low-level operations of Steam Audio. Typically, a context is specified once
    during the execution of the client program, before calling any other API functions. */
DECLARE_OPAQUE_HANDLE(IPLContext);

/** Severity levels of log messages generated by Steam Audio. */
typedef enum {
    IPL_LOGLEVEL_INFO,      /**< A normal, informational message. */
    IPL_LOGLEVEL_WARNING,   /**< A warning. The operation that generated this message may not work as expected. */
    IPL_LOGLEVEL_ERROR,     /**< An error. The operation that generated this message failed. */
    IPL_LOGLEVEL_DEBUG,     /**< A detailed message intended for debugging purposes only. */
} IPLLogLevel;

/** SIMD instruction sets that Steam Audio can attempt to use. */
typedef enum {
    IPL_SIMDLEVEL_SSE2,                         /**< Intel Streaming SIMD Extensions 2. Up to 4 simultaneous floating-point operations. */
    IPL_SIMDLEVEL_SSE4,                         /**< Intel Streaming SIMD Extensions 4.2 or older. Up to 4 simultaneous floating-point operations. */
    IPL_SIMDLEVEL_AVX,                          /**< Intel Advanced Vector Extensions or older. Up to 8 simultaneous floating-point operations. */
    IPL_SIMDLEVEL_AVX2,                         /**< Intel Advanced Vector Extensions 2 or older. Up to 8 simultaneous floating-point operations. */
    IPL_SIMDLEVEL_AVX512,                       /**< Intel Advanced Vector Extensions 512 or older. Up to 16 simultaneous floating-point operations. */
    IPL_SIMDLEVEL_NEON = IPL_SIMDLEVEL_SSE2,    /**< ARM NEON. Up to 4 simultaneous floating-point operations. */
} IPLSIMDLevel;

/** Additional flags for modifying the behavior of a Steam Audio context. */
typedef enum {
    IPL_CONTEXTFLAGS_VALIDATION = 1 << 0,       /**< All API functions perform extra validation checks. NOTE: This imposes a significant performance penalty. */
    IPL_CONTEXTFLAGS_FORCE_32BIT = 0x7fffffff,  /**< Force this enum to be 32 bits in size. */
} IPLContextFlags;

/** Prototype of a callback that logs a message generated by Steam Audio. This may be implemented in any suitable way,
    such as appending to a log file, displaying a dialog box, etc. The default behavior is to print to \c stdout.

    \param  level       The severity level of the message.
    \param  message     The message to log.
*/
typedef void (IPLCALL *IPLLogFunction)(IPLLogLevel level, const char* message);

/** Prototype of a callback that allocates memory. This is usually specified to let Steam Audio use a custom memory
    allocator. The default behavior is to use the OS-dependent aligned version of \c malloc.

    \param  size        The number of bytes to allocate.
    \param  alignment   The alignment (in bytes) of the start address of the allocated memory.

    \return Pointer to the allocated block of memory, or \c NULL if allocation failed.
*/
typedef void* (IPLCALL *IPLAllocateFunction)(IPLsize size, IPLsize alignment);

/** Prototype of a callback that frees a block of memory. This is usually specified when using a custom memory
    allocator with Steam Audio. The default behavior is to use the OS-dependent aligned version of \c free.

    \param  memoryBlock Pointer to the block of memory.
*/
typedef void (IPLCALL *IPLFreeFunction)(void* memoryBlock);

/** Settings used to create a context object. */
typedef struct {
    /** The API version used by the caller. Context creation will fail if `phonon.dll` does not implement a
        compatible version of the API. Typically, this should be set to `STEAMAUDIO_VERSION`. */
    IPLuint32 version;

    /** (Optional) If non-NULL, Steam Audio will call this function to record log messages generated by
        certain operations. */
    IPLLogFunction logCallback;

    /** (Optional) If non-NULL, Steam Audio will call this function whenever it needs to allocate
        memory. */
    IPLAllocateFunction allocateCallback;

    /** (Optional) If non-NULL, Steam Audio will call this function whenever it needs to free memory. */
    IPLFreeFunction freeCallback;

    /** The maximum SIMD instruction set level that Steam Audio should use. Steam Audio automatically
        chooses the best instruction set to use based on the user's CPU, but you can prevent it from
        using certain newer instruction sets using this parameter. For example, with some workloads,
        AVX512 instructions consume enough power that the CPU clock speed will be throttled, resulting
        in lower performance than expected. If you observe this in your application, set this
        parameter to `IPL_SIMDLEVEL_AVX2` or lower. */
    IPLSIMDLevel simdLevel;

    /** Additional flags for modifying the behavior of the created context. */
    IPLContextFlags flags;
} IPLContextSettings;

/** Creates a context object. A context must be created before creating any other API objects.

    \param  settings    Pointer to the `IPLContextSettings` struct that specifies context creation parameters.
    \param  context     [out] Handle to the created context object.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplContextCreate(IPLContextSettings* settings, IPLContext* context);

/** Retains an additional reference to a context. The context will not be destroyed until all references are
    released.

    \param  context     The context to retain a reference to.

    \return The additional reference to the context.
*/
IPLAPI IPLContext IPLCALL iplContextRetain(IPLContext context);

/** Releases a reference to a context. The context will not be destroyed until all references are released.

    \param  context     [in, out] The context to release.
*/
IPLAPI void IPLCALL iplContextRelease(IPLContext* context);

/** \} */


/*****************************************************************************************************************/

/** \defgroup geometry Geometry
    \{
*/

/** A point or vector in 3D space. Steam Audio uses a right-handed coordinate system, with the positive x-axis pointing
    right, the positive y-axis pointing up, and the negative z-axis pointing ahead. Position and direction data
    obtained from a game engine or audio engine must be properly transformed before being passed to any Steam Audio API
    function.
*/
typedef struct {
    /** The x-coordinate. */
    IPLfloat32 x;

    /** The y-coordinate. */
    IPLfloat32 y;

    /** The z-coordinate. */
    IPLfloat32 z;
} IPLVector3;

/** A 4x4 matrix used to represent an affine transform. */
typedef struct {
    /** Matrix elements, in row-major order. */
    IPLfloat32 elements[4][4];
} IPLMatrix4x4;

/** An axis-aligned box. Axis-aligned boxes are used to specify a volume of 3D space. */
typedef struct {
    /** The minimum coordinates of any vertex. */
    IPLVector3  minCoordinates;

    /** The maximum coordinates of any vertex. */
    IPLVector3  maxCoordinates;
} IPLBox;

/** A sphere. Spheres are used to define a region of influence around a point. */
typedef struct {
    /** The center. */
    IPLVector3  center;

    /** The radius. */
    IPLfloat32  radius;
} IPLSphere;

/** A 3D coordinate system, expressed relative to a canonical coordinate system. */
typedef struct {
    /** Unit vector pointing to the right (local +x axis). */
    IPLVector3 right;

    /** Unit vector pointing upwards (local +y axis). */
    IPLVector3 up;

    /** Unit vector pointing forwards (local -z axis). */
    IPLVector3 ahead;

    /** The origin, relative to the canonical coordinate system. */
    IPLVector3 origin;
} IPLCoordinateSpace3;

/** Calculates the relative direction from the listener to a sound source. The returned direction
    vector is expressed in the listener's coordinate system.

    \param  context             The context used to initialize Steam Audio.
    \param  sourcePosition      World-space coordinates of the source.
    \param  listenerPosition    World-space coordinates of the listener.
    \param  listenerAhead       World-space unit-length vector pointing ahead relative to the listener.
    \param  listenerUp          World-space unit-length vector pointing up relative to the listener.

    \return A unit-length vector in the listener's coordinate space, pointing from the listener to the source.
*/
IPLAPI IPLVector3 IPLCALL iplCalculateRelativeDirection(IPLContext context, IPLVector3 sourcePosition, IPLVector3 listenerPosition, IPLVector3 listenerAhead, IPLVector3 listenerUp);

/** \} */


/*****************************************************************************************************************/

/** \defgroup serialization Serialization
    \{
*/

/** A serialized representation of an API object, like an \c IPLScene or \c IPLProbeBatch. Create an empty
    serialized object if you want to serialize an existing object to a byte array, or create a serialized
    object that wraps an existing byte array if you want to deserialize it. */
DECLARE_OPAQUE_HANDLE(IPLSerializedObject);

/** Settings used to create a serialized object. */
typedef struct {
    /** If non-NULL, the serialized object will contain the data in this buffer. If NULL,
        the serialized object will start out empty. */
    IPLbyte* data;

    /** The number of bytes in the buffer pointed to by \c data. Set to 0 if \c data is
        NULL. */
    IPLsize size;
} IPLSerializedObjectSettings;

/** Creates a serialized object.

    \param  context             The context used to initialize Steam Audio.
    \param  settings            The settings to use when creating the serialized object.
    \param  serializedObject    [out] The created serialized object.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplSerializedObjectCreate(IPLContext context, IPLSerializedObjectSettings* settings, IPLSerializedObject* serializedObject);

/** Retains an additional reference to a serialized object.

    \param  serializedObject    The serialized object to retain a reference to.

    \return The additional reference to the serialized object.
*/
IPLAPI IPLSerializedObject IPLCALL iplSerializedObjectRetain(IPLSerializedObject serializedObject);

/** Releases a reference to a serialized object.

    \param  serializedObject    The serialized object to release a reference to.
*/
IPLAPI void IPLCALL iplSerializedObjectRelease(IPLSerializedObject* serializedObject);

/** \return The size in bytes of the serialized data contained in a serialized object.

    \param  serializedObject    The serialized object.
*/
IPLAPI IPLsize IPLCALL iplSerializedObjectGetSize(IPLSerializedObject serializedObject);

/** \return A pointer to a byte array of serialized data contained in a serialized object.

    \param  serializedObject    The serialized object.
*/
IPLAPI IPLbyte* IPLCALL iplSerializedObjectGetData(IPLSerializedObject serializedObject);

/** \} */


/*****************************************************************************************************************/

/** \defgroup embree Embree
    \{
*/

/** Application-wide state for the Embree ray tracer. An Embree device must be created before using any of Steam
    Audio's Embree ray tracing functionality. In terms of the Embree API, this object encapsulates an
    \c RTCDevice object. */
DECLARE_OPAQUE_HANDLE(IPLEmbreeDevice);

/** Settings used to create an Embree device. */
#if defined(__cplusplus) || defined(__DOXYGEN__)
typedef struct {
    IPLbyte reserved;
} IPLEmbreeDeviceSettings;
#else
typedef void IPLEmbreeDeviceSettings;
#endif

/** Creates an Embree device.

    \param  context     The context used to initialize Steam Audio.
    \param  settings    The settings to use when creating the Embree device.
    \param  device      [out] The created Embree device.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplEmbreeDeviceCreate(IPLContext context, IPLEmbreeDeviceSettings* settings, IPLEmbreeDevice* device);

/** Retains an additional reference to an Embree device.

    \param  device  The Embree device to retain a reference to.

    \return The additional reference to the Embree device.
*/
IPLAPI IPLEmbreeDevice IPLCALL iplEmbreeDeviceRetain(IPLEmbreeDevice device);

/** Releases a reference to an Embree device.

    \param  device  The Embree device to release a reference to.
*/
IPLAPI void IPLCALL iplEmbreeDeviceRelease(IPLEmbreeDevice* device);

/** \} */


/*****************************************************************************************************************/

/** \defgroup opencl OpenCL
    \{
*/

/** Provides a list of OpenCL devices available on the user's system. Use this to enumerate the available
    OpenCL devices, inspect their capabilities, and select the most suitable one for your application's needs. */
DECLARE_OPAQUE_HANDLE(IPLOpenCLDeviceList);

/** Application-wide state for OpenCL. An OpenCL device must be created before using any of Steam Audio's
    Radeon Rays or TrueAudio Next functionality. In terms of the OpenCL API, this object encapsulates a
    \c cl_context object, along with up to 2 \c cl_command_queue objects. */
DECLARE_OPAQUE_HANDLE(IPLOpenCLDevice);

/** The type of devices to include when listing OpenCL devices. */
typedef enum {
    /** List both CPU and GPU devices. */
    IPL_OPENCLDEVICETYPE_ANY,

    /** Only list CPU devices. */
    IPL_OPENCLDEVICETYPE_CPU,

    /** Only list GPU devices. */
    IPL_OPENCLDEVICETYPE_GPU
} IPLOpenCLDeviceType;

/** Specifies requirements that an OpenCL device must meet in order to be considered when listing
    OpenCL devices. */
typedef struct {
    /** The type of device. Set to \c IPL_OPENCLDEVICETYPE_ANY to consider all available devices. */
    IPLOpenCLDeviceType type;

    /** The number of GPU compute units (CUs) that should be reserved for use by Steam Audio. If set to a
        non-zero value, then a GPU will be included in the device list only if it can reserve at least
        this many CUs. Set to 0 to indicate that Steam Audio can use the entire GPU, in which case all
        available GPUs will be considered.

        Ignored if \c type is \c IPL_OPENCLDEVICETYPE_CPU. */
    IPLint32 numCUsToReserve;

    /** The fraction of reserved CUs that should be used for impulse response (IR) update. IR update
        includes: a) ray tracing using Radeon Rays to simulate sound propagation, and/or b) pre-transformation
        of IRs for convolution using TrueAudio Next. Steam Audio will only list GPU devices that are able
        to subdivide the reserved CUs as per this value. The value must be between 0 and 1.

        For example, if \c numCUsToReserve is \c 8, and \c fractionCUsForIRUpdate is \c 0.5f, then 4 CUs
        will be used for IR update and 4 CUs will be used for convolution. Below are typical scenarios:

        -   Using only TrueAudio Next. Set \c fractionCUsForIRUpdate to \c 0.5f. This ensures that reserved
            CUs are available for IR update as well as convolution.

        -   Using TrueAudio Next and Radeon Rays for real-time simulation and rendering. Choosing
            \c fractionCUsForIRUpdate may require some experimentation to utilize reserved CUs optimally. You
            can start by setting \c fractionCUsForIRUpdate to \c 0.5f. However, if IR calculation has high
            latency with these settings, increase \c fractionCUsForIRUpdate to use more CUs for ray tracing.

        -   Using only Radeon Rays. Set \c fractionCUsForIRUpdate to \c 1, to make sure all the reserved CUs
            are used for ray tracing. If using Steam Audio for preprocessing (e.g. baking reverb), then
            consider setting \c numCUsToReserve to \c 0 to use the entire GPU for accelerated ray tracing.

        Ignored if \c type is \c IPL_OPENCLDEVICETYPE_CPU or \c numCUsToReserve is \c 0. */
    IPLfloat32 fractionCUsForIRUpdate;

    /** If \c IPL_TRUE, then the GPU device must support TrueAudio Next. It is not necessary to set this
        to \c IPL_TRUE if \c numCUsToReserve or \c fractionCUsForIRUpdate are set to non-zero values. */
    IPLbool requiresTAN;
} IPLOpenCLDeviceSettings;

/** Describes the properties of an OpenCL device. This information can be used to select the most suitable
    device for your application. */
typedef struct {
    /** The OpenCL platform id. Can be cast to \c cl_platform_id. */
    void* platform;

    /** The OpenCL platform name. */
    IPLstring platformName;

    /** The OpenCL platform vendor's name. */
    IPLstring platformVendor;

    /** The OpenCL platform version. */
    IPLstring platformVersion;

    /** The OpenCL device id. Can be cast to \c cl_device_id. */
    void* device;

    /** The OpenCL device name. */
    IPLstring deviceName;

    /** The OpenCL device vendor's name. */
    IPLstring deviceVendor;

    /** The OpenCL device version. */
    IPLstring deviceVersion;

    /** The type of OpenCL device. */
    IPLOpenCLDeviceType type;

    /** The number of CUs reserved for convolution. May be \c 0 if CU reservation is not supported. */
    IPLint32 numConvolutionCUs;

    /** The number of CUs reserved for IR update. May be \c 0 if CU reservation is not supported. */
    IPLint32 numIRUpdateCUs;

    /** The CU reservation granularity. CUs can only be reserved on this device in multiples of this number. */
    IPLint32 granularity;

    /** A relative performance score of a single CU of this device. Only applicable to supported AMD GPUs. */
    IPLfloat32 perfScore;
} IPLOpenCLDeviceDesc;

/** Creates an OpenCL device list. This involves listing all available OpenCL devices on the user's system.

    \param  context     The context used to initialize Steam Audio.
    \param  settings    The requirements that all listed OpenCL devices must satisfy.
    \param  deviceList  [out] The created OpenCL device list.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplOpenCLDeviceListCreate(IPLContext context, IPLOpenCLDeviceSettings* settings, IPLOpenCLDeviceList* deviceList);

/** Retains an additional reference to an OpenCL device list.

    \param  deviceList  The OpenCL device list to retain a reference to.

    \return The additional reference to the OpenCL device list.
*/
IPLAPI IPLOpenCLDeviceList IPLCALL iplOpenCLDeviceListRetain(IPLOpenCLDeviceList deviceList);

/** Releases a reference to an OpenCL device list.

    \param  deviceList  The OpenCL device list to release a reference to.
*/
IPLAPI void IPLCALL iplOpenCLDeviceListRelease(IPLOpenCLDeviceList* deviceList);

/** \return The number of devices in an OpenCL device list.

    \param  deviceList  The OpenCL device list.
*/
IPLAPI IPLint32 IPLCALL iplOpenCLDeviceListGetNumDevices(IPLOpenCLDeviceList deviceList);

/** Retrieves information about a specific device in an OpenCL device list.

    \param  deviceList  The OpenCL device list.
    \param  index       The index of the device within the list.
    \param  deviceDesc  [out] A descriptor for the properties of the specified OpenCL device.
*/
IPLAPI void IPLCALL iplOpenCLDeviceListGetDeviceDesc(IPLOpenCLDeviceList deviceList, IPLint32 index, IPLOpenCLDeviceDesc* deviceDesc);

/** Creates an OpenCL device. The device is specified as an index into an OpenCL device list.

    \param  context     The context used to initialize Steam Audio.
    \param  deviceList  The OpenCL device list.
    \param  index       The index of the device within the list.
    \param  device      [out] The created OpenCL device.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplOpenCLDeviceCreate(IPLContext context, IPLOpenCLDeviceList deviceList, IPLint32 index, IPLOpenCLDevice* device);

/** Creates an OpenCL device from an existing OpenCL device created by your application. Steam Audio will
    use up to two command queues that you provide for enqueuing OpenCL computations.

    \param  context             The context used to initialize Steam Audio.
    \param  convolutionQueue    The \c cl_command_queue to use for enqueueing convolution work.
    \param  irUpdateQueue       The \c cl_command_queue to use for enqueueing IR update work.
    \param  device              [out] The created OpenCL device.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplOpenCLDeviceCreateFromExisting(IPLContext context, void* convolutionQueue, void* irUpdateQueue, IPLOpenCLDevice* device);

/** Retains an additional reference to an OpenCL device.

    \param  device  The OpenCL device to retain a reference to.

    \return The additional reference to the OpenCL device.
*/
IPLAPI IPLOpenCLDevice IPLCALL iplOpenCLDeviceRetain(IPLOpenCLDevice device);

/** Releases a reference to an OpenCL device.

    \param  device  The OpenCL device to release a reference to.
*/
IPLAPI void IPLCALL iplOpenCLDeviceRelease(IPLOpenCLDevice* device);

/** \} */


/*****************************************************************************************************************/

/** \defgroup radeonrays Radeon Rays
    \{
*/

/** Application-wide state for the Radeon Rays ray tracer. A Radeon Rays device must be created before using any of
    Steam Audio's Radeon Rays ray tracing functionality. In terms of the Radeon Rays API, this object encapsulates
    a \c RadeonRays::IntersectionApi object. */
DECLARE_OPAQUE_HANDLE(IPLRadeonRaysDevice);

/** Settings used to create a Radeon Rays device. */
#if defined(__cplusplus) || defined(__DOXYGEN__)
typedef struct {
    IPLbyte reserved;
} IPLRadeonRaysDeviceSettings;
#else
typedef void IPLRadeonRaysDeviceSettings;
#endif

/** Creates a Radeon Rays device.

    \param  openCLDevice    The OpenCL device to use for running Radeon Rays.
    \param  settings        The settings to use when creating the Radeon Rays device.
    \param  rrDevice        [out] The created Radeon Rays device.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplRadeonRaysDeviceCreate(IPLOpenCLDevice openCLDevice, IPLRadeonRaysDeviceSettings* settings, IPLRadeonRaysDevice* rrDevice);

/** Retains an additional reference to a Radeon Rays device.

    \param  device  The Radeon Rays device to retain a reference to.

    \return The additional reference to the Radeon Rays device.
*/
IPLAPI IPLRadeonRaysDevice IPLCALL iplRadeonRaysDeviceRetain(IPLRadeonRaysDevice device);

/** Releases a reference to a Radeon Rays device.

    \param  device  The Radeon Rays device to release a reference to.
*/
IPLAPI void IPLCALL iplRadeonRaysDeviceRelease(IPLRadeonRaysDevice* device);

/** \} */


/*****************************************************************************************************************/

/** \defgroup tan TrueAudio Next
    \{
*/

/** Application-wide state for the TrueAudio Next convolution engine. A TrueAudio Next device must be created
    before using any of Steam Audio's TrueAudio Next convolution functionality. In terms of the TrueAudio Next API,
    this object encapsulates an \c amf::TANContext and amf::TANConvolution object. */
DECLARE_OPAQUE_HANDLE(IPLTrueAudioNextDevice);

/** Settings used to create a TrueAudio Next device. */
typedef struct {
    /** The number of samples in an audio frame. */
    IPLint32 frameSize;

    /** The number of samples in the impulse responses that will be used for convolution. */
    IPLint32 irSize;

    /** The Ambisonic order of the impulse responses that will be used for convolution. */
    IPLint32 order;

    /** The maximum number of sources that will use TrueAudio Next for convolution. */
    IPLint32 maxSources;
} IPLTrueAudioNextDeviceSettings;

/** Creates a TrueAudio Next device.

    \param  openCLDevice    The OpenCL device to use for running TrueAudio Next.
    \param  settings        The settings to use when creating the TrueAudio Next device.
    \param  tanDevice       [out] The created TrueAudio Next device.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplTrueAudioNextDeviceCreate(IPLOpenCLDevice openCLDevice, IPLTrueAudioNextDeviceSettings* settings, IPLTrueAudioNextDevice* tanDevice);

/** Retains an additional reference to a TrueAudio Next device.

    \param  device  The TrueAudio Next device to retain a reference to.

    \return The additional reference to the TrueAudio Next device.
*/
IPLAPI IPLTrueAudioNextDevice IPLCALL iplTrueAudioNextDeviceRetain(IPLTrueAudioNextDevice device);

/** Releases a reference to a TrueAudio Next device.

    \param  device  The TrueAudio Next device to release a reference to.
*/
IPLAPI void IPLCALL iplTrueAudioNextDeviceRelease(IPLTrueAudioNextDevice* device);

/** \} */


/*****************************************************************************************************************/

/** \defgroup scene Scene
    Functions and types for specifying scene information. Before you can use physics-based sound propagation
    features like occlusion or reverb, you must specify the geometry and materials that make up the 3D scene.
    \{
*/

/** A 3D scene, which can contain geometry objects that can interact with acoustic rays. The scene object itself
    doesn't contain any geometry, but is a container for \c IPLStaticMesh and \c IPLInstancedMesh objects, which
    do contain geometry. */
DECLARE_OPAQUE_HANDLE(IPLScene);

/** A triangle mesh that doesn't move or deform in any way. The unchanging portions of a scene should typically
    be collected into a single static mesh object. In addition to the geometry, a static mesh also contains
    acoustic material information for each triangle. */
DECLARE_OPAQUE_HANDLE(IPLStaticMesh);

/** A triangle mesh that can be moved (translated), rotated, or scaled, but cannot deform. Portions of a scene
    that undergo rigid-body motion can be represented as instanced meshes. An instanced mesh is essentially a
    scene (called the "sub-scene") with a transform applied to it. Adding an instanced mesh to a scene places
    the sub-scene into the scene with the transform applied. For example, the sub-scene may be a prefab door,
    and the transform can be used to place it in a doorway and animate it as it opens or closes. */
DECLARE_OPAQUE_HANDLE(IPLInstancedMesh);

/** The types of scenes that can be created. Each scene type corresponds to a different ray tracing
    implementation. */
typedef enum {
    /** Steam Audio's built-in ray tracer. Supports multi-threading. Runs on all platforms that Steam Audio
        supports. */
    IPL_SCENETYPE_DEFAULT,

    /** The Intel Embree ray tracer. Supports multi-threading. This is a highly optimized implementation, and
        is likely to be faster than the default ray tracer. However, Embree requires Windows, Linux, or macOS,
        and a 32-bit x86 or 64-bit x86_64 CPU. */
    IPL_SCENETYPE_EMBREE,

    /** The AMD Radeon Rays ray tracer. This is an OpenCL implementation, and can use either the CPU or any
        GPU that supports OpenCL 1.2 or later. If using the GPU, it is likely to be significantly faster than
        the default ray tracer. However, with heavy real-time simulation workloads, it may impact your
        application's frame rate. On supported AMD GPUs, you can use the Resource Reservation feature to
        mitigate this issue. */
    IPL_SCENETYPE_RADEONRAYS,

    /** Allows you to specify callbacks to your own ray tracer. Useful if your application already uses a
        high-performance ray tracer. This option uses the least amount of memory at run-time, since it does
        not have to build any ray tracing data structures of its own. */
    IPL_SCENETYPE_CUSTOM
} IPLSceneType;

/** A triangle in 3D space.

    Triangles are specified by their three vertices, which are in turn specified using indices into a
    vertex array.

    Steam Audio uses a counter-clockwise winding order. This means that when looking at the triangle such that the
    normal is pointing towards you, the vertices are specified in counter-clockwise order.

    Each triangle must be specified using three vertices; triangle strip or fan representations are
    not supported. */
typedef struct {
    /** Indices of the three vertices of this triangle. */
    IPLint32 indices[3];
} IPLTriangle;

/** The acoustic properties of a surface.

    You can specify the acoustic material properties of each triangle, although typically many triangles will
    share a common material.

    The acoustic material properties are specified for three frequency bands with center frequencies of
    400 Hz, 2.5 KHz, and 15 KHz.

    Below are the acoustic material properties for a few standard materials.

    ```cpp
    {"generic",{0.10f,0.20f,0.30f,0.05f,0.100f,0.050f,0.030f}}
    {"brick",{0.03f,0.04f,0.07f,0.05f,0.015f,0.015f,0.015f}}
    {"concrete",{0.05f,0.07f,0.08f,0.05f,0.015f,0.002f,0.001f}}
    {"ceramic",{0.01f,0.02f,0.02f,0.05f,0.060f,0.044f,0.011f}}
    {"gravel",{0.60f,0.70f,0.80f,0.05f,0.031f,0.012f,0.008f}},
    {"carpet",{0.24f,0.69f,0.73f,0.05f,0.020f,0.005f,0.003f}}
    {"glass",{0.06f,0.03f,0.02f,0.05f,0.060f,0.044f,0.011f}}
    {"plaster",{0.12f,0.06f,0.04f,0.05f,0.056f,0.056f,0.004f}}
    {"wood",{0.11f,0.07f,0.06f,0.05f,0.070f,0.014f,0.005f}}
    {"metal",{0.20f,0.07f,0.06f,0.05f,0.200f,0.025f,0.010f}}
    {"rock",{0.13f,0.20f,0.24f,0.05f,0.015f,0.002f,0.001f}}
    ```
*/
typedef struct {
    /** Fraction of sound energy absorbed at low, middle, high frequencies. Between 0.0 and 1.0. */
    IPLfloat32 absorption[IPL_NUM_BANDS];

    /** Fraction of sound energy scattered in a random direction on reflection. Between 0.0 (pure specular) and 1.0
        (pure diffuse). */
    IPLfloat32 scattering;

    /** Fraction of sound energy transmitted through at low, middle, high frequencies. Between 0.0 and 1.0.
        Only used for direct occlusion calculations. */
    IPLfloat32 transmission[IPL_NUM_BANDS];
} IPLMaterial;

/** A ray in 3D space. */
typedef struct {
    /** Origin of the ray. */
    IPLVector3 origin;

    /** Unit vector direction of the ray. */
    IPLVector3 direction;
} IPLRay;

/** Information about a ray's intersection with 3D geometry.

    This information should be provided by ray tracer callbacks when using \c IPL_SCENETYPE_CUSTOM. Not all
    fields are required. */
typedef struct {
    /** Distance along the ray from origin to hit point. Set to \c INFINITY if nothing was hit. */
    IPLfloat32 distance;

    /** (Optional) Index of the primitive hit by the ray. \c -1 if not provided. */
    IPLint32 triangleIndex;

    /** (Optional) Index of the scene object hit by the ray. \c -1 if not provided. */
    IPLint32 objectIndex;

    /** (Optional) Index of the material associated with the primitive hit by the ray. \c -1 if not provided. */
    IPLint32 materialIndex;

    /** Unit length surface normal at the hit point. Ignored if nothing was hit. */
    IPLVector3 normal;

    /** Pointer to the material at the hit point. Ignored if nothing was hit. */
    IPLMaterial* material;
} IPLHit;

/** Callback for calculating the closest hit along a ray.

    Strictly speaking, the intersection is calculated with a ray _interval_ (equivalent to a line segment). Any ray
    interval may have multiple points of intersection with scene geometry; this function must return information
    about the point of intersection that is closest to the ray's origin.

    \param  ray                 The ray to trace.
    \param  minDistance         The minimum distance from the origin at which an intersection may occur for it
                                to be considered. This function must not return any intersections closer to the
                                origin than this value.
    \param  maxDistance         The maximum distance from the origin at which an intersection may occur for it
                                to be considered. This function must not return any intersections farther from
                                the origin than this value. If this value is less than or equal to \c minDistance,
                                the function should ignore the ray, and return immediately.
    \param  hit                 [out] Information describing the ray's intersection with geometry, if any.
    \param  userData            Pointer to a block of memory containing arbitrary data, specified during the call to
                                \c iplSceneCreate.
*/
typedef void (IPLCALL *IPLClosestHitCallback)(const IPLRay* ray, IPLfloat32 minDistance, IPLfloat32 maxDistance, IPLHit* hit, void* userData);

/** Callback for calculating whether a ray hits any geometry.

    Strictly speaking, the intersection is calculated with a ray _interval_ (equivalent to a line segment).

    \param  ray                 The ray to trace.
    \param  minDistance         The minimum distance from the origin at which an intersection may occur for it
                                to be considered. This function must not return any intersections closer to the
                                origin than this value.
    \param  maxDistance         The maximum distance from the origin at which an intersection may occur for it
                                to be considered. This function must not return any intersections farther from
                                the origin than this value. If this value is less than or equal to \c minDistance,
                                the function should ignore the ray, set \c occluded to 1, and return immediately.
    \param  occluded            [out] An integer indicating whether the ray intersects any geometry. A value of 0
                                indicates no intersection, 1 indicates that an intersection exists.
    \param  userData            Pointer to a block of memory containing arbitrary data, specified during the call to
                                \c iplSceneCreate.
*/
typedef void (IPLCALL *IPLAnyHitCallback)(const IPLRay* ray, IPLfloat32 minDistance, IPLfloat32 maxDistance, IPLuint8* occluded, void* userData);

/** Callback for calculating the closest hit along a batch of rays.

    Strictly speaking, the intersection is calculated with a ray _interval_ (equivalent to a line segment). Any ray
    interval may have multiple points of intersection with scene geometry; this function must return information
    about the point of intersection that is closest to the ray's origin.

    \param  numRays             The number of rays to trace.
    \param  rays                Array containing the rays.
    \param  minDistances        Array containing, for each ray, the minimum distance from the origin at which
                                an intersection may occur for it to be considered.
    \param  maxDistances        Array containing, for each ray, the maximum distance from the origin at which
                                an intersection may occur for it to be considered. If, for some ray with index \c i,
                                `maxDistances[i]` is less than `minDistances[i]`, the function should ignore
                                the ray.
    \param  hits                [out] Information describing each ray's intersection with geometry, if any.
    \param  userData            Pointer to a block of memory containing arbitrary data, specified during the call to
                                \c iplSceneCreate.
*/
typedef void (IPLCALL *IPLBatchedClosestHitCallback)(IPLint32 numRays, const IPLRay* rays, const IPLfloat32* minDistances, const IPLfloat32* maxDistances, IPLHit* hits, void* userData);

/** Callback for calculating for each ray in a batch of rays, whether the ray hits any geometry.

    Strictly speaking, the intersection is calculated with a ray _interval_ (equivalent to a line segment).

    \param  numRays             The number of rays to trace.
    \param  rays                Array containing the rays.
    \param  minDistances        Array containing, for each ray, the minimum distance from the origin at which
                                an intersection may occur for it to be considered.
    \param  maxDistances        Array containing, for each ray, the maximum distance from the origin at which
                                an intersection may occur for it to be considered. If, for some ray with index \c i,
                                `maxDistances[i]` is less than `minDistances[i]`, the function should ignore the
                                ray and set `occluded[i]` to 1.
    \param  occluded            [out] Array of integers indicating, for each ray, whether the ray intersects any
                                geometry. 0 indicates no intersection, 1 indicates that an intersection exists.
    \param  userData            Pointer to a block of memory containing arbitrary data, specified during the call to
                                \c iplSceneCreate.
*/
typedef void (IPLCALL *IPLBatchedAnyHitCallback)(IPLint32 numRays, const IPLRay* rays, const IPLfloat32* minDistances, const IPLfloat32* maxDistances, IPLuint8* occluded, void* userData);

/** Settings used to create a scene. */
typedef struct {
    /** Type of scene to create. */
    IPLSceneType type;

    /** Callback for finding the closest hit along a ray. Only for \c IPL_SCENETYPE_CUSTOM. */
    IPLClosestHitCallback closestHitCallback;

    /** Callback for finding whether a ray hits anything. Only for \c IPL_SCENETYPE_CUSTOM. */
    IPLAnyHitCallback anyHitCallback;

    /** Callback for finding the closest hit along a batch of rays. Only for \c IPL_SCENETYPE_CUSTOM. */
    IPLBatchedClosestHitCallback batchedClosestHitCallback;

    /** Callback for finding whether a batch of rays hits anything. Only for \c IPL_SCENETYPE_CUSTOM. */
    IPLBatchedAnyHitCallback batchedAnyHitCallback;

    /** Arbitrary user-provided data for use by ray tracing callbacks. Only for \c IPL_SCENETYPE_CUSTOM. */
    void* userData;

    /** Handle to an Embree device. Only for \c IPL_SCENETYPE_EMBREE. */
    IPLEmbreeDevice embreeDevice;

    /** Handle to a Radeon Rays device. Only for \c IPL_SCENETYPE_RADEONRAYS. */
    IPLRadeonRaysDevice radeonRaysDevice;
} IPLSceneSettings;

/** Settings used to create a static mesh. */
typedef struct {
    /** Number of vertices. */
    IPLint32 numVertices;

    /** Number of triangles. */
    IPLint32 numTriangles;

    /** Number of materials. */
    IPLint32 numMaterials;

    /** Array containing vertices. */
    IPLVector3* vertices;

    /** Array containing (indexed) triangles. */
    IPLTriangle* triangles;

    /** Array containing, for each triangle, the index of the associated material. */
    IPLint32* materialIndices;

    /** Array of materials. */
    IPLMaterial* materials;
} IPLStaticMeshSettings;

/** Settings used to create an instanced mesh. */
typedef struct {
    /** Handle to the scene to be instantiated. */
    IPLScene subScene;

    /** Local-to-world transform that places the instance within the parent scene. */
    IPLMatrix4x4 transform;
} IPLInstancedMeshSettings;

/** Creates a scene.

    A scene does not store any geometry information on its own; for that you need to create one or more
    static meshes or instanced meshes and add them to the scene.

    \param  context     The context used to initialize Steam Audio.
    \param  settings    The settings to use when creating the scene.
    \param  scene       [out] The created scene.

    \return Status code indicating success or failure.
*/
IPLAPI IPLerror IPLCALL iplSceneCreate(IPLContext context, IPLSceneSettings* settings, IPLScene* scene);

/** Retains an additional reference to a scene.

    \param  scene   The scene to retain a reference to.

    \return The additional reference to the scene.
*/
IPLAPI IPLScene IPLCALL iplSceneRetain(IPLScene scene);

/** Releases a reference to a scene.

    \param  scene   The scene to release a reference to.
*/
IPLAPI void IPLCALL iplSceneRelease(IPLScene* scene);

/** Loads a scene from a serialized object. Typically, the serialized object will be created from a byte array
    loaded from disk or over the network.

    \param  context                     The context used to initialize Steam Audio.
    \param  settings                    The settings to use when creating the scene.
    \param  serializedObject            The serialized object from which to load the scene.
    \param  progressCallback            Callback that reports the percentage of this function's work that has been completed. May be \c NULL.
    \param  progressCallbackUserData    Pointer to arbitrary data that will be passed to the progress callback. May be \c NULL.
    \param  scene                       [out] The created scene.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplSceneLoad(IPLContext context, IPLSceneSettings* settings, IPLSerializedObject serializedObject, IPLProgressCallback progressCallback, void* progressCallbackUserData, IPLScene* scene);

/** Saves a scene to a serialized object. Typically, the serialized object will then be saved to disk.

    **This function can only be called on a scene created with \c IPL_SCENETYPE_DEFAULT.**

    \param  scene               The scene to save.
    \param  serializedObject    The serialized object into which to save the scene.
*/
IPLAPI void IPLCALL iplSceneSave(IPLScene scene, IPLSerializedObject serializedObject);

/** Saves a scene to an OBJ file.

    An OBJ file is a widely-supported 3D model file format, that can be displayed using a variety of software
    on most PC platforms. The OBJ file generated by this function can be useful for detecting problems that
    occur when exporting scene data from your application to Steam Audio.

    **This function can only be called on a scene created with \c IPL_SCENETYPE_DEFAULT or \c IPL_SCENETYPE_EMBREE.**

    \param  scene               The scene to save.
    \param  fileBaseName        Absolute or relative path to the OBJ file to generate.
*/
IPLAPI void IPLCALL iplSceneSaveOBJ(IPLScene scene, IPLstring fileBaseName);

/** Commits any changes to the scene.

    This function should be called after any calls to the following functions, for the changes to take effect:

    -   \c iplStaticMeshAdd
    -   \c iplStaticMeshRemove
    -   \c iplInstancedMeshAdd
    -   \c iplInstancedMeshRemove
    -   \c iplInstancedMeshUpdateTransform

    For best performance, call this function once after all changes have been made for a given frame.

    **This function cannot be called concurrently with any simulation functions.**

    \param  scene   The scene to commit changes to.
*/
IPLAPI void IPLCALL iplSceneCommit(IPLScene scene);

/** Creates a static mesh.

    A static mesh represents a triangle mesh that does not change after it is created. A static mesh also contains
    an array of acoustic material properties, and a mapping between each of its triangles and their acoustic material
    properties.

    Static mesh objects should be used for scene geometry that is guaranteed to never change, such as rooms,
    buildings, or triangulated terrain. A scene may contain multiple static meshes, although typically one
    is sufficient.

    \param  scene       The scene in which the static mesh should be created.
    \param  settings    The settings to use when creating the static mesh.
    \param  staticMesh  [out] The created static mesh.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplStaticMeshCreate(IPLScene scene, IPLStaticMeshSettings* settings, IPLStaticMesh* staticMesh);

/** Retains an additional reference to a static mesh.

    \param  staticMesh  The static mesh to retain a reference to.

    \return The additional reference to the static mesh.
*/
IPLAPI IPLStaticMesh IPLCALL iplStaticMeshRetain(IPLStaticMesh staticMesh);

/** Releases a reference to a static mesh.

    \param  staticMesh  The static mesh to release a reference to.
*/
IPLAPI void IPLCALL iplStaticMeshRelease(IPLStaticMesh* staticMesh);

/** Loads a static mesh from a serialized object. Typically, the serialized object will be created from a byte array
    loaded from disk or over the network.

    \param  scene                       The scene in which the static mesh should be created.
    \param  serializedObject            The serialized object from which to load the scene.
    \param  progressCallback            Callback that reports the percentage of this function's work that has been completed. May be \c NULL.
    \param  progressCallbackUserData    Pointer to arbitrary data that will be passed to the progress callback. May be \c NULL.
    \param  staticMesh                  [out] The created static mesh.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplStaticMeshLoad(IPLScene scene, IPLSerializedObject serializedObject, IPLProgressCallback progressCallback, void* progressCallbackUserData, IPLStaticMesh* staticMesh);

/** Saves a static mesh to a serialized object. Typically, the serialized object will then be saved to disk.

    This function can only be called on a static mesh that is part of a scene created with \c IPL_SCENETYPE_DEFAULT.

    \param  staticMesh          The static mesh to save.
    \param  serializedObject    The serialized object into which to save the static mesh.
*/
IPLAPI void IPLCALL iplStaticMeshSave(IPLStaticMesh staticMesh, IPLSerializedObject serializedObject);

/** Adds a static mesh to a scene.

    This function should be called after \c iplStaticMeshCreate, or at any point after \c iplStaticMeshRemove,
    for the static mesh to start affecting sound propagation.

    After calling this function, \c iplSceneCommit must be called for the changes to take effect.

    \param  staticMesh  The static mesh to add.
    \param  scene       The scene to which to add the static mesh. This must be the scene which was passed when
                        calling \c iplStaticMeshCreate.
*/
IPLAPI void IPLCALL iplStaticMeshAdd(IPLStaticMesh staticMesh, IPLScene scene);

/** Removes a static mesh from a scene.

    After this function is called, the static mesh will stop affecting sound propagation, until it is
    added back using \c iplStaticMeshAdd.

    After calling this function, \c iplSceneCommit must be called for the changes to take effect.

    \param  staticMesh  The static mesh to remove.
    \param  scene       The scene from which to remove the static mesh. This must be the scene which was passed when
                        calling \c iplStaticMeshCreate.
*/
IPLAPI void IPLCALL iplStaticMeshRemove(IPLStaticMesh staticMesh, IPLScene scene);

/** Loads the material data of specified object in the iplStaticMesh.

    \param  staticMesh  The static mesh to update.
    \param  scene       The scene from which to update the static mesh. This must be the scene which was passed when
                        calling \c iplStaticMeshCreate.
    \param  newMaterial  The material data of specified object.
    \param  index  The index of specified object. It means the object's index in the order it was exported.
*/
IPLAPI void IPLCALL iplStaticMeshSetMaterial(IPLStaticMesh staticMesh, IPLScene scene, IPLMaterial* newMaterial, IPLint32 index);

/** Creates an instanced mesh.

    An instanced mesh takes one scene and positions it within another scene. This is useful if you have the
    same object, like a pillar, that you want to instantiate multiple times within the same scene. A scene
    can be instantiated multiple times within another scene, without incurring any significant memory overhead.

    The instanced mesh can be moved, rotated, and scaled freely at any time, providing an easy way to implement
    dynamic objects whose motion can be described purely in terms of rigid-body transformations.

    \param  scene           The scene in which the instanced mesh should be created.
    \param  settings        The settings used to create the instanced mesh.
    \param  instancedMesh   [out] The created instanced mesh.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplInstancedMeshCreate(IPLScene scene, IPLInstancedMeshSettings* settings, IPLInstancedMesh* instancedMesh);

/** Retains an additional reference to a instanced mesh.

    \param  instancedMesh   The instanced mesh to retain a reference to.

    \return The additional reference to the instanced mesh.
*/
IPLAPI IPLInstancedMesh IPLCALL iplInstancedMeshRetain(IPLInstancedMesh instancedMesh);

/** Releases a reference to a instanced mesh.

    \param  instancedMesh   The instanced mesh to release a reference to.
*/
IPLAPI void IPLCALL iplInstancedMeshRelease(IPLInstancedMesh* instancedMesh);

/** Adds an instanced mesh to a scene.

    This function should be called after \c iplInstancedMeshCreate, or at any point after \c iplInstancedMeshRemove,
    for the instanced mesh to start affecting sound propagation.

    After calling this function, \c iplSceneCommit must be called for the changes to take effect.

    \param  instancedMesh   The instanced mesh to add.
    \param  scene           The scene to which to add the instanced mesh. This must be the scene which was passed when
                            calling \c iplInstancedMeshCreate.
*/
IPLAPI void IPLCALL iplInstancedMeshAdd(IPLInstancedMesh instancedMesh, IPLScene scene);

/** Removes an instanced mesh from a scene.

    After this function is called, the instanced mesh will stop affecting sound propagation, until it is
    added back using \c iplInstancedMeshAdd.

    After calling this function, \c iplSceneCommit must be called for the changes to take effect.

    \param  instancedMesh   The instanced mesh to remove.
    \param  scene           The scene from which to remove the instanced mesh. This must be the scene which was passed when
                            calling \c iplInstancedMeshCreate.
*/
IPLAPI void IPLCALL iplInstancedMeshRemove(IPLInstancedMesh instancedMesh, IPLScene scene);

/** Updates the local-to-world transform of an instanced mesh within its parent scene.

    This function allows the instanced mesh to be moved, rotated, and scaled dynamically.

    After calling this function, \c iplSceneCommit must be called for the changes to take effect.

    \param  instancedMesh   The instanced mesh whose transform is to be updated.
    \param  scene           The parent scene that contains the instanced mesh.
    \param  transform       The new 4x4 local-to-world transform matrix.
*/
IPLAPI void IPLCALL iplInstancedMeshUpdateTransform(IPLInstancedMesh instancedMesh, IPLScene scene, IPLMatrix4x4 transform);

/** \} */


/*********************************************************************************************************************/

/** \defgroup audiobuffers Audio Buffers
    \{
*/

/** Supported speaker layouts. */
typedef enum {
    /** Mono. */
    IPL_SPEAKERLAYOUTTYPE_MONO,

    /** Stereo (left, right). */
    IPL_SPEAKERLAYOUTTYPE_STEREO,

    /** Front left, front right, rear left, rear right. */
    IPL_SPEAKERLAYOUTTYPE_QUADRAPHONIC,

    /** Front left, front right, front center, LFE, rear left, rear right. */
    IPL_SPEAKERLAYOUTTYPE_SURROUND_5_1,

    /** Front left, front right, front center, LFE, rear left, rear right, side left, side right. */
    IPL_SPEAKERLAYOUTTYPE_SURROUND_7_1,

    /** User-defined speaker layout. See \c IPLSpeakerLayout. */
    IPL_SPEAKERLAYOUTTYPE_CUSTOM
} IPLSpeakerLayoutType;

/** Supported channel ordering and normalization schemes for Ambisonic audio. */
typedef enum {
    /** ACN channel ordering, orthonormal spherical harmonics. */
    IPL_AMBISONICSTYPE_N3D,

    /** ACN channel ordering, semi-normalized spherical harmonics. AmbiX format. */
    IPL_AMBISONICSTYPE_SN3D,

    /** Furse-Malham (B-format). */
    IPL_AMBISONICSTYPE_FUMA,
} IPLAmbisonicsType;

/** States that an audio effect can be left in after processing a frame of audio. */
typedef enum {
    /** One or more samples of tail remain in the effect's internal buffers. */
    IPL_AUDIOEFFECTSTATE_TAILREMAINING,

    /** No tail remains in the effect's internal buffers. */
    IPL_AUDIOEFFECTSTATE_TAILCOMPLETE,
} IPLAudioEffectState;

/** Describes a standard or custom speaker layout. */
typedef struct {
    /** See \c IPLSpeakerLayoutType. */
    IPLSpeakerLayoutType type;

    /** Number of speakers. Only for IPL_SPEAKERLAYOUTTYPE_CUSTOM. */
    IPLint32 numSpeakers;

    /** Array of unit-length directions for each speaker. Only for IPL_SPEAKERLAYOUTTYPE_CUSTOM. */
    IPLVector3* speakers;
} IPLSpeakerLayout;

/** Global settings for audio signal processing. */
typedef struct {
    /** Sampling rate, in Hz. */
    IPLint32 samplingRate;

    /** Frame size, in samples. Independent of number of channels. */
    IPLint32 frameSize;
} IPLAudioSettings;

/** Describes an audio buffer. All audio buffers passed to Steam Audio must be deinterleaved. */
typedef struct {
    /** Number of channels. */
    IPLint32 numChannels;

    /** Number of samples per channel. */
    IPLint32 numSamples;

    /** Array of pointers to sample data for each channel. Allocation of sample data is up to the user. */
    IPLfloat32** data;
} IPLAudioBuffer;

/** Allocates an audio buffer.

    All audio buffers are uncompressed PCM with 32-bit floating-point samples.

    Internally, all audio buffers are stored deinterleaved for performance reasons. If your audio engine provides
    interleaved audio buffers, you must use \c iplAudioBufferInterleave and \c iplAudioBufferDeinterleave to explicitly
    convert to/from deinterleaved format. If your audio engine provides deinterleaved audio buffers, you can
    pass them directly using \c IPLAudioBuffer, thus avoiding the processing and memory overhead of an extra audio buffer.

    \param  context         The context used to initialize Steam Audio.
    \param  numChannels     Number of channels.
    \param  numSamples      Number of samples per channel.
    \param  audioBuffer     The audio buffer to allocate.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplAudioBufferAllocate(IPLContext context, IPLint32 numChannels, IPLint32 numSamples, IPLAudioBuffer* audioBuffer);

/** Frees an audio buffer.

    \param  context         The context used to initialize Steam Audio.
    \param  audioBuffer     The audio buffer to free.
*/
IPLAPI void IPLCALL iplAudioBufferFree(IPLContext context, IPLAudioBuffer* audioBuffer);

/** Reads samples from an audio buffer and interleaves them into a user-provided array.

    \param  context         The context used to initialize Steam Audio.
    \param  src             The audio buffer to read from.
    \param  dst             The interleaved array to write into.
*/
IPLAPI void IPLCALL iplAudioBufferInterleave(IPLContext context, IPLAudioBuffer* src, IPLfloat32* dst);

/** Writes interleaved samples from a user-provided array into an audio buffer.

    \param  context         The context used to initialize Steam Audio.
    \param  src             The interleaved array to read from.
    \param  dst             The audio buffer to write into.
*/
IPLAPI void IPLCALL iplAudioBufferDeinterleave(IPLContext context, IPLfloat32* src, IPLAudioBuffer* dst);

/** Mixes one audio buffer into another.

    Both audio buffers must have the same number of channels and samples.

    \param  context     The context used to initialize Steam Audio.
    \param  in          The source audio buffer.
    \param  mix         The destination audio buffer, into which the source should be mixed.
*/
IPLAPI void IPLCALL iplAudioBufferMix(IPLContext context, IPLAudioBuffer* in, IPLAudioBuffer* mix);

/** Downmixes a multi-channel audio buffer into a mono audio buffer.

    Both audio buffers must have the same number of samples.

    Downmixing is performed by summing up the source channels and dividing the result by the
    number of source channels. If this is not the desired downmixing behavior, we recommend
    that downmixing be performed manually.

    \param  context The context used to initialize Steam Audio.
    \param  in      The source audio buffer.
    \param  out     The destination audio buffer.
*/
IPLAPI void IPLCALL iplAudioBufferDownmix(IPLContext context, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Converts an Ambisonic audio buffer from one Ambisonic format to another.

    Both audio buffers must have the same number of samples.

    This conversion can be applied in-place, i.e., \c in and \c out can be the same
    audio buffer.

    Steam Audio's "native" Ambisonic format is N3D, so for best performance, keep all
    Ambisonic data in N3D format except when exchanging data with your audio engine.

    \param  context     The context used to initialize Steam Audio.
    \param  inType      Ambisonic format of \c in.
    \param  outType     Ambisonic format that \c out should be in.
    \param  in          The source audio buffer.
    \param  out         The destination audio buffer.
*/
IPLAPI void IPLCALL iplAudioBufferConvertAmbisonics(IPLContext context, IPLAmbisonicsType inType, IPLAmbisonicsType outType, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup hrtf HRTFs
    \{
*/

/** A Head-Related Transfer Function (HRTF). HRTFs describe how sound from different directions is perceived by a
    each of a listener's ears, and are a crucial component of spatial audio. Steam Audio includes a built-in HRTF,
    while also allowing developers and users to import their own custom HRTFs. */
DECLARE_OPAQUE_HANDLE(IPLHRTF);

/** The type of HRTF to use. */
typedef enum {
    /** The built-in HRTF. */
    IPL_HRTFTYPE_DEFAULT,

    /** An HRTF loaded from a SOFA file. */
    IPL_HRTFTYPE_SOFA
} IPLHRTFType;

/** Volume normalization types to use. */
typedef enum {
    /** No normalization. */
    IPL_HRTFNORMTYPE_NONE,

    /** Root-mean squared normalization. Normalize HRTF volume to ensure similar volume from all directions
        based on root-mean-square value of each HRTF. */
    IPL_HRTFNORMTYPE_RMS
} IPLHRTFNormType;

/** Settings used to create an HRTF object. */
typedef struct {
    /** The type of HRTF to create. */
    IPLHRTFType type;

    /** SOFA file from which to load HRTF data. Either \c sofaFileName or \c sofaData should be non-NULL.
        Only for \c IPL_HRTFTYPE_SOFA. */
    const char* sofaFileName;

    /** Pointer to a buffer containing SOFA file data from which to load HRTF data. Either \c sofaFileName
        or \c sofaData should be non-NULL. Only for \c IPL_HRTFTYPE_SOFA. */
    const IPLuint8* sofaData;

    /** Size (in bytes) of the buffer pointed to by \c sofaData. Only for \c IPL_HRTFTYPE_SOFA. */
    int sofaDataSize;

    /** Volume correction factor to apply to the loaded HRTF data. A value of 1.0 means the HRTF data will be used
        without any change. */
    float volume;

    /** Normalization setting. No normalization will be applied when choosing \c IPL_HRTFNORMTYPE_NONE. */
    IPLHRTFNormType normType;
} IPLHRTFSettings;

/** Creates an HRTF.

    Calling this function is somewhat expensive; avoid creating HRTF objects in your audio thread at all
    if possible.

    This function is not thread-safe. Do not simultaneously call it from multiple threads.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  hrtfSettings    The settings used to create the HRTF object.
    \param  hrtf            [out] The created HRTF object.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplHRTFCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLHRTFSettings* hrtfSettings, IPLHRTF* hrtf);

/** Retains an additional reference to an HRTF object.

    \param  hrtf    The HRTF object to retain a reference to.

    \return The additional reference to the HRTF object.
*/
IPLAPI IPLHRTF IPLCALL iplHRTFRetain(IPLHRTF hrtf);

/** Releases a reference to an HRTF object.

    \param  hrtf    The HRTF object to release a reference to.
*/
IPLAPI void IPLCALL iplHRTFRelease(IPLHRTF* hrtf);

/** \} */


/*********************************************************************************************************************/

/** \defgroup panningeffect Panning Effect
    \{
*/

/** Pans a single-channel point source to a multi-channel speaker layout based on the 3D position of the source
    relative to the listener. */
DECLARE_OPAQUE_HANDLE(IPLPanningEffect);

/** Settings used to create a panning effect. */
typedef struct {
    /** The speaker layout to pan input audio to. */
    IPLSpeakerLayout speakerLayout;
} IPLPanningEffectSettings;

/** Parameters for applying a panning effect to an audio buffer. */
typedef struct {
    /** Unit vector pointing from the listener towards the source. */
    IPLVector3 direction;
} IPLPanningEffectParams;

/** Creates a panning effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the panning effect.
    \param  effect          [out] The created panning effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplPanningEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLPanningEffectSettings* effectSettings, IPLPanningEffect* effect);

/** Retains an additional reference to a panning effect.

    \param  effect  The panning effect to retain a reference to.

    \return The additional reference to the panning effect.
*/
IPLAPI IPLPanningEffect IPLCALL iplPanningEffectRetain(IPLPanningEffect effect);

/** Releases a reference to a panning effect.

    \param  effect  The panning effect to release a reference to.
*/
IPLAPI void IPLCALL iplPanningEffectRelease(IPLPanningEffect* effect);

/** Resets the internal processing state of a panning effect.

    \param  effect  The panning effect to reset.
*/
IPLAPI void IPLCALL iplPanningEffectReset(IPLPanningEffect effect);

/** Applies a panning effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The panning effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must be 1-channel.
    \param  out     The output audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the panning effect. For example, if the speaker layout is
                    \c IPL_SPEAKERLAYOUTTYPE_SURROUND_5_1, the output buffer must contain 6 channels.

    \return \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE to indicate that this effect does not generate any tail samples.
*/
IPLAPI IPLAudioEffectState IPLCALL iplPanningEffectApply(IPLPanningEffect effect, IPLPanningEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in a panning effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The panning effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplPanningEffectGetTailSize(IPLPanningEffect effect);

/** Retrieves a single frame of tail samples from a panning effect's internal buffers.

    After the input to the panning effect has stopped, this function must be called instead of
    \c iplPanningEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The panning effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the panning effect. For example, if the speaker layout is
                    \c IPL_SPEAKERLAYOUTTYPE_SURROUND_5_1, the output buffer must contain 6 channels.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplPanningEffectGetTail(IPLPanningEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup binauraleffect Binaural Effect
    \{
*/

/** Spatializes a point source using an HRTF, based on the 3D position of the source relative to the listener. The
    source audio can be 1- or 2-channel; in either case all input channels are spatialized from the same position. */
DECLARE_OPAQUE_HANDLE(IPLBinauralEffect);

/** Techniques for interpolating HRTF data. This is used when rendering a point source whose position relative to
    the listener is not contained in the measured HRTF data. */
typedef enum {
    /** Nearest-neighbor filtering, i.e., no interpolation. Selects the measurement location that is closest to
        the source's actual location. */
    IPL_HRTFINTERPOLATION_NEAREST,

    /** Bilinear filtering. Incurs a relatively high CPU overhead as compared to nearest-neighbor filtering, so use
        this for sounds where it has a significant benefit. Typically, bilinear filtering is most useful for wide-band
        noise-like sounds, such as radio static, mechanical noise, fire, etc. */
    IPL_HRTFINTERPOLATION_BILINEAR
} IPLHRTFInterpolation;

/** Settings used to create a binaural effect. */
typedef struct {
    /** The HRTF to use. */
    IPLHRTF hrtf;
} IPLBinauralEffectSettings;

/** Parameters for applying a binaural effect to an audio buffer. */
typedef struct {
    /** Unit vector pointing from the listener towards the source. */
    IPLVector3 direction;

    /** The interpolation technique to use. */
    IPLHRTFInterpolation interpolation;

    /** Amount to blend input audio with spatialized audio. When set to 0, output audio is not spatialized at all
        and is close to input audio. If set to 1, output audio is fully spatialized. */
    IPLfloat32 spatialBlend;

    /** The HRTF to use. */
    IPLHRTF hrtf;

    /** Base address of an array into which to write the left- and right-ear peak delays for the HRTF used
        to spatialize the input audio. Memory for this array must be allocated and managed by the caller.
        Can be NULL, in which case peak delays will not be written. */
    IPLfloat32* peakDelays;
} IPLBinauralEffectParams;

/** Creates a binaural effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the binaural effect.
    \param  effect          [out] The created binaural effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplBinauralEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLBinauralEffectSettings* effectSettings, IPLBinauralEffect* effect);

/** Retains an additional reference to a binaural effect.

    \param  effect  The binaural effect to retain a reference to.

    \return The additional reference to the binaural effect.
*/
IPLAPI IPLBinauralEffect IPLCALL iplBinauralEffectRetain(IPLBinauralEffect effect);

/** Releases a reference to a binaural effect.

    \param  effect  The binaural effect to release a reference to.
*/
IPLAPI void IPLCALL iplBinauralEffectRelease(IPLBinauralEffect* effect);

/** Resets the internal processing state of a binaural effect.

    \param  effect  The binaural effect to reset.
*/
IPLAPI void IPLCALL iplBinauralEffectReset(IPLBinauralEffect effect);

/** Applies a binaural effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The binaural effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must be 1- or 2-channel.
    \param  out     The output audio buffer. Must be 2-channel.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplBinauralEffectApply(IPLBinauralEffect effect, IPLBinauralEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in a binaural effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The binaural effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplBinauralEffectGetTailSize(IPLBinauralEffect effect);

/** Retrieves a single frame of tail samples from a binaural effect's internal buffers.

    After the input to the binaural effect has stopped, this function must be called instead of
    \c iplBinauralEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The binaural effect.
    \param  out     The output audio buffer. Must be 2-channel.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplBinauralEffectGetTail(IPLBinauralEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup virtualsurround Virtual Surround Effect
    \{
*/

/** Spatializes multi-channel speaker-based audio (e.g., stereo, quadraphonic, 5.1, or 7.1) using HRTF-based binaural
    rendering. The audio signal for each speaker is spatialized from a point in space corresponding to the speaker's
    location. This allows users to experience a surround sound mix over regular stereo headphones.

    Virtual surround is also a fast way to get approximate binaural rendering. All sources can be panned to some
    surround format (say, 7.1). After the sources are mixed, the mix can be rendered using virtual surround. This can
    reduce CPU usage, at the cost of spatialization accuracy. */
DECLARE_OPAQUE_HANDLE(IPLVirtualSurroundEffect);

/** Settings used to create a virtual surround effect. */
typedef struct {
    /** The speaker layout that will be used by input audio buffers. */
    IPLSpeakerLayout speakerLayout;

    /** The HRTF to use. */
    IPLHRTF hrtf;
} IPLVirtualSurroundEffectSettings;

/** Parameters for applying a virtual surround effect to an audio buffer. */
typedef struct {
    /** The HRTF to use. */
    IPLHRTF hrtf;
} IPLVirtualSurroundEffectParams;

/** Creates a virtual surround effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the virtual surround effect.
    \param  effect          [out] The created virtual surround effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplVirtualSurroundEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLVirtualSurroundEffectSettings* effectSettings, IPLVirtualSurroundEffect* effect);

/** Retains an additional reference to a virtual surround effect.

    \param  effect  The virtual surround effect to retain a reference to.

    \return The additional reference to the virtual surround effect.
*/
IPLAPI IPLVirtualSurroundEffect IPLCALL iplVirtualSurroundEffectRetain(IPLVirtualSurroundEffect effect);

/** Releases a reference to a virtual surround effect.

    \param  effect  The virtual surround effect to release a reference to.
*/
IPLAPI void IPLCALL iplVirtualSurroundEffectRelease(IPLVirtualSurroundEffect* effect);

/** Resets the internal processing state of a virtual surround effect.

    \param  effect  The virtual surround effect to reset.
*/
IPLAPI void IPLCALL iplVirtualSurroundEffectReset(IPLVirtualSurroundEffect effect);

/** Applies a virtual surround effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The virtual surround effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the virtual surround effect.
    \param  out     The output audio buffer. Must be 2-channel.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplVirtualSurroundEffectApply(IPLVirtualSurroundEffect effect, IPLVirtualSurroundEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in a virtual surround effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The virtual surround effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplVirtualSurroundEffectGetTailSize(IPLVirtualSurroundEffect effect);

/** Retrieves a single frame of tail samples from a virtual surround effect's internal buffers.

    After the input to the virtual surround effect has stopped, this function must be called instead of
    \c iplVirtualSurroundEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The virtual surround effect.
    \param  out     The output audio buffer. Must be 2-channel.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplVirtualSurroundEffectGetTail(IPLVirtualSurroundEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup ambisonicsencode Ambisonics Encode Effect
    \{
*/

/** Encodes a point source into Ambisonics. Given a point source with some direction relative to the listener, this
    effect generates an Ambisonic audio buffer that approximates a point source in the given direction. This allows
    multiple point sources and ambiences to mixed to a single Ambisonics buffer before being spatialized. */
DECLARE_OPAQUE_HANDLE(IPLAmbisonicsEncodeEffect);

/** Settings used to create an Ambisonics encode effect. */
typedef struct {
    /** Maximum Ambisonics order to encode audio buffers to. */
    IPLint32 maxOrder;
} IPLAmbisonicsEncodeEffectSettings;

/** Parameters for applying an Ambisonics encode effect to an audio buffer. */
typedef struct {
    /** Vector pointing from the listener towards the source. Need not be normalized; Steam Audio will automatically
        normalize this vector. If a zero-length vector is passed, the output will be order 0 (omnidirectional). */
    IPLVector3 direction;

    /** Ambisonic order of the output buffer. May be less than the \c maxOrder specified when creating the effect,
        in which case the effect will generate fewer output channels, reducing CPU usage. */
    IPLint32 order;
} IPLAmbisonicsEncodeEffectParams;

/** Creates an Ambisonics encode effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the Ambisonics encode effect.
    \param  effect          [out] The created Ambisonics encode effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplAmbisonicsEncodeEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLAmbisonicsEncodeEffectSettings* effectSettings, IPLAmbisonicsEncodeEffect* effect);

/** Retains an additional reference to an Ambisonics encode effect.

    \param  effect  The Ambisonics encode effect to retain a reference to.

    \return The additional reference to the Ambisonics encode effect.
*/
IPLAPI IPLAmbisonicsEncodeEffect IPLCALL iplAmbisonicsEncodeEffectRetain(IPLAmbisonicsEncodeEffect effect);

/** Releases a reference to an Ambisonics encode effect.

    \param  effect  The Ambisonics encode effect to release a reference to.
*/
IPLAPI void IPLCALL iplAmbisonicsEncodeEffectRelease(IPLAmbisonicsEncodeEffect* effect);

/** Resets the internal processing state of an Ambisonics encode effect.

    \param  effect  The Ambisonics encode effect to reset.
*/
IPLAPI void IPLCALL iplAmbisonicsEncodeEffectReset(IPLAmbisonicsEncodeEffect effect);

/** Applies an Ambisonics encode effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The Ambisonics encode effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must be 1-channel.
    \param  out     The output audio buffer. Must have as many channels as needed for the
                    Ambisonics order specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE to indicate that this effect does not generate any tail samples.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsEncodeEffectApply(IPLAmbisonicsEncodeEffect effect, IPLAmbisonicsEncodeEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in an Ambisonics encode effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The Ambisonics encode effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplAmbisonicsEncodeEffectGetTailSize(IPLAmbisonicsEncodeEffect effect);

/** Retrieves a single frame of tail samples from an Ambisonics encode effect's internal buffers.

    After the input to the Ambisonics encode effect has stopped, this function must be called instead of
    \c iplAmbisonicsEncodeEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The Ambisonics encode effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the
                    Ambisonics order specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsEncodeEffectGetTail(IPLAmbisonicsEncodeEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup ambisonicspanning Ambisonics Panning Effect
    \{
*/

/** Renders Ambisonic audio by panning it to a standard speaker layout. This involves calculating signals to emit
    from each speaker so as to approximate the Ambisonic sound field. */
DECLARE_OPAQUE_HANDLE(IPLAmbisonicsPanningEffect);

/** Settings used to create an Ambisonics panning effect. */
typedef struct {
    /** The speaker layout that will be used by output audio buffers. */
    IPLSpeakerLayout speakerLayout;

    /** The maximum Ambisonics order that will be used by input audio buffers. */
    IPLint32 maxOrder;
} IPLAmbisonicsPanningEffectSettings;

/** Parameters for applying an Ambisonics panning effect to an audio buffer. */
typedef struct {
    /** Ambisonic order of the input buffer. May be less than the \c maxOrder specified when creating the effect,
        in which case the effect will process fewer input channels, reducing CPU usage. */
    IPLint32 order;
} IPLAmbisonicsPanningEffectParams;

/** Creates an Ambisonics panning effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the Ambisonics panning effect.
    \param  effect          [out] The created Ambisonics panning effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplAmbisonicsPanningEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLAmbisonicsPanningEffectSettings* effectSettings, IPLAmbisonicsPanningEffect* effect);

/** Retains an additional reference to an Ambisonics panning effect.

    \param  effect  The Ambisonics panning effect to retain a reference to.

    \return The additional reference to the Ambisonics panning effect.
*/
IPLAPI IPLAmbisonicsPanningEffect IPLCALL iplAmbisonicsPanningEffectRetain(IPLAmbisonicsPanningEffect effect);

/** Releases a reference to an Ambisonics panning effect.

    \param  effect  The Ambisonics panning effect to release a reference to.
*/
IPLAPI void IPLCALL iplAmbisonicsPanningEffectRelease(IPLAmbisonicsPanningEffect* effect);

/** Resets the internal processing state of an Ambisonics panning effect.

    \param  effect  The Ambisonics panning effect to reset.
*/
IPLAPI void IPLCALL iplAmbisonicsPanningEffectReset(IPLAmbisonicsPanningEffect effect);

/** Applies an Ambisonics panning effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The Ambisonics panning effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have as many channels as needed for the Ambisonics order
                    specified in the parameters.
    \param  out     The output audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE to indicate that this effect does not generate any tail samples.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsPanningEffectApply(IPLAmbisonicsPanningEffect effect, IPLAmbisonicsPanningEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in an Ambisonics panning effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The Ambisonics panning effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplAmbisonicsPanningEffectGetTailSize(IPLAmbisonicsPanningEffect effect);

/** Retrieves a single frame of tail samples from a Ambisonics panning effect's internal buffers.

    After the input to the Ambisonics panning effect has stopped, this function must be called instead of
    \c iplAmbisonicsPanningEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The Ambisonics panning effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsPanningEffectGetTail(IPLAmbisonicsPanningEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup ambisonics Ambisonics Binaural Effect
    \{
*/

/** Renders Ambisonic audio using HRTF-based binaural rendering. This results in more immersive spatialization of the
    Ambisonic audio as compared to using an Ambisonics panning effect, at the cost of slightly increased CPU usage. */
DECLARE_OPAQUE_HANDLE(IPLAmbisonicsBinauralEffect);

/** Settings used to create an Ambisonics binaural effect. */
typedef struct {
    /** The HRTF to use. */
    IPLHRTF hrtf;

    /** The maximum Ambisonics order that will be used by input audio buffers. */
    IPLint32 maxOrder;
} IPLAmbisonicsBinauralEffectSettings;

/** Parameters for applying an Ambisonics binaural effect to an audio buffer. */
typedef struct {
    /** The HRTF to use. */
    IPLHRTF hrtf;

    /** Ambisonic order of the input buffer. May be less than the \c maxOrder specified when creating the effect,
        in which case the effect will process fewer input channels, reducing CPU usage. */
    IPLint32 order;
} IPLAmbisonicsBinauralEffectParams;

/** Creates an Ambisonics binaural effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the Ambisonics binaural effect.
    \param  effect          [out] The created Ambisonics binaural effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplAmbisonicsBinauralEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLAmbisonicsBinauralEffectSettings* effectSettings, IPLAmbisonicsBinauralEffect* effect);

/** Retains an additional reference to an Ambisonics binaural effect.

    \param  effect  The Ambisonics binaural effect to retain a reference to.

    \return The additional reference to the Ambisonics binaural effect.
*/
IPLAPI IPLAmbisonicsBinauralEffect IPLCALL iplAmbisonicsBinauralEffectRetain(IPLAmbisonicsBinauralEffect effect);

/** Releases a reference to an Ambisonics binaural effect.

    \param  effect  The Ambisonics binaural effect to release a reference to.
*/
IPLAPI void IPLCALL iplAmbisonicsBinauralEffectRelease(IPLAmbisonicsBinauralEffect* effect);

/** Resets the internal processing state of an Ambisonics binaural effect.

    \param  effect  The Ambisonics binaural effect to reset.
*/
IPLAPI void IPLCALL iplAmbisonicsBinauralEffectReset(IPLAmbisonicsBinauralEffect effect);

/** Applies an Ambisonics binaural effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The Ambisonics binaural effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have as many channels as needed for the Ambisonics order
                    specified in the parameters.
    \param  out     The output audio buffer. Must have 2 channels.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsBinauralEffectApply(IPLAmbisonicsBinauralEffect effect, IPLAmbisonicsBinauralEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in an Ambisonics binaural effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The Ambisonics binaural effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplAmbisonicsBinauralEffectGetTailSize(IPLAmbisonicsBinauralEffect effect);

/** Retrieves a single frame of tail samples from an Ambisonics binaural effect's internal buffers.

    After the input to the Ambisonics binaural effect has stopped, this function must be called instead of
    \c iplAmbisonicsBinauralEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The Ambisonics binaural effect.
    \param  out     The output audio buffer. Must have 2 channels.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsBinauralEffectGetTail(IPLAmbisonicsBinauralEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup ambisonicsrotation Ambisonics Rotation Effect
    \{
*/

/** Applies a rotation to an Ambisonics audio buffer. The input buffer is assumed to describe a sound field in
    "world space". The output buffer is then the same sound field, but expressed relative to the listener's
    orientation. */
DECLARE_OPAQUE_HANDLE(IPLAmbisonicsRotationEffect);

/** Settings used to create an Ambisonics rotation effect. */
typedef struct {
    /** The maximum Ambisonics order that will be used by input audio buffers. */
    IPLint32 maxOrder;
} IPLAmbisonicsRotationEffectSettings;

/** Parameters for applying an Ambisonics rotation effect to an audio buffer. */
typedef struct {
    /** The orientation of the listener. */
    IPLCoordinateSpace3 orientation;

    /** Ambisonic order of the input and output buffers. May be less than the \c maxOrder specified when creating the
        effect, in which case the effect will process fewer channels, reducing CPU usage. */
    IPLint32 order;
} IPLAmbisonicsRotationEffectParams;

/** Creates an Ambisonics rotation effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the Ambisonics rotation effect.
    \param  effect          [out] The created Ambisonics rotation effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplAmbisonicsRotationEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLAmbisonicsRotationEffectSettings* effectSettings, IPLAmbisonicsRotationEffect* effect);

/** Retains an additional reference to an Ambisonics rotation effect.

    \param  effect  The Ambisonics rotation effect to retain a reference to.

    \return The additional reference to the Ambisonics rotation effect.
*/
IPLAPI IPLAmbisonicsRotationEffect IPLCALL iplAmbisonicsRotationEffectRetain(IPLAmbisonicsRotationEffect effect);

/** Releases a reference to an Ambisonics rotation effect.

    \param  effect  The Ambisonics rotation effect to release a reference to.
*/
IPLAPI void IPLCALL iplAmbisonicsRotationEffectRelease(IPLAmbisonicsRotationEffect* effect);

/** Resets the internal processing state of an Ambisonics rotation effect.

    \param  effect  The Ambisonics rotation effect to reset.
*/
IPLAPI void IPLCALL iplAmbisonicsRotationEffectReset(IPLAmbisonicsRotationEffect effect);

/** Applies an Ambisonics rotation effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The Ambisonics rotation effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have as many channels as needed for the Ambisonics order
                    specified when creating the effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the Ambisonics order
                    specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE to indicate that this effect does not generate any tail samples.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsRotationEffectApply(IPLAmbisonicsRotationEffect effect, IPLAmbisonicsRotationEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in an Ambisonics rotation effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The Ambisonics rotation effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplAmbisonicsRotationEffectGetTailSize(IPLAmbisonicsRotationEffect effect);

/** Retrieves a single frame of tail samples from an Ambisonics rotation effect's internal buffers.

    After the input to the Ambisonics rotation effect has stopped, this function must be called instead of
    \c iplAmbisonicsRotationEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The Ambisonics rotation effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the Ambisonics order
                    specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsRotationEffectGetTail(IPLAmbisonicsRotationEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup ambisonicsdecode Ambisonics Decode Effect
    \{
*/

/** Applies a rotation to an Ambisonics audio buffer, then decodes it using panning or binaural rendering. This is
    essentially an Ambisonics rotate effect followed by either an Ambisonics panning effect or an Ambisonics binaural
    effect. */
DECLARE_OPAQUE_HANDLE(IPLAmbisonicsDecodeEffect);

/** Settings used to create an Ambisonics decode effect. */
typedef struct {
    /** The speaker layout that will be used by output audio buffers. */
    IPLSpeakerLayout speakerLayout;

    /** The HRTF to use. */
    IPLHRTF hrtf;

    /** The maximum Ambisonics order that will be used by input audio buffers. */
    IPLint32 maxOrder;
} IPLAmbisonicsDecodeEffectSettings;

/** Parameters for applying an Ambisonics decode effect to an audio buffer. */
typedef struct {
    /** Ambisonic order of the input buffer. May be less than the \c maxOrder specified when creating the effect,
        in which case the effect will process fewer input channels, reducing CPU usage. */
    IPLint32 order;

    /** The HRTF to use. */
    IPLHRTF hrtf;

    /** The orientation of the listener. */
    IPLCoordinateSpace3 orientation;

    /** Whether to use binaural rendering or panning. */
    IPLbool binaural;
} IPLAmbisonicsDecodeEffectParams;

/** Creates an Ambisonics rotation effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the Ambisonics rotation effect.
    \param  effect          [out] The created Ambisonics rotation effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplAmbisonicsDecodeEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLAmbisonicsDecodeEffectSettings* effectSettings, IPLAmbisonicsDecodeEffect* effect);

/** Retains an additional reference to an Ambisonics rotation effect.

    \param  effect  The Ambisonics rotation effect to retain a reference to.

    \return The additional reference to the Ambisonics rotation effect.
*/
IPLAPI IPLAmbisonicsDecodeEffect IPLCALL iplAmbisonicsDecodeEffectRetain(IPLAmbisonicsDecodeEffect effect);

/** Releases a reference to an Ambisonics rotation effect.

    \param  effect  The Ambisonics rotation effect to release a reference to.
*/
IPLAPI void IPLCALL iplAmbisonicsDecodeEffectRelease(IPLAmbisonicsDecodeEffect* effect);

/** Resets the internal processing state of an Ambisonics decode effect.

    \param  effect  The Ambisonics decode effect to reset.
*/
IPLAPI void IPLCALL iplAmbisonicsDecodeEffectReset(IPLAmbisonicsDecodeEffect effect);

/** Applies an Ambisonics decode effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The Ambisonics decode effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have as many channels as needed for the Ambisonics order
                    specified when creating the effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the effect (if using panning) or 2 channels (if using
                    binaural rendering).

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsDecodeEffectApply(IPLAmbisonicsDecodeEffect effect, IPLAmbisonicsDecodeEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in an Ambisonics decode effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The Ambisonics decode effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplAmbisonicsDecodeEffectGetTailSize(IPLAmbisonicsDecodeEffect effect);

/** Retrieves a single frame of tail samples from an Ambisonics decode effect's internal buffers.

    After the input to the Ambisonics decode effect has stopped, this function must be called instead of
    \c iplAmbisonicsDecodeEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The Ambisonics decode effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the speaker layout
                    specified when creating the effect (if using panning) or 2 channels (if using
                    binaural rendering).

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplAmbisonicsDecodeEffectGetTail(IPLAmbisonicsDecodeEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup directeffect Direct Effect
    \{
*/

/** Filters and attenuates an audio signal based on various properties of the direct path between a point source and
    the listener. */
DECLARE_OPAQUE_HANDLE(IPLDirectEffect);

/** Flags indicating which direct path parameters to apply. */
typedef enum {
    /** Apply frequency-independent distance attenuation. */
    IPL_DIRECTEFFECTFLAGS_APPLYDISTANCEATTENUATION  = 1 << 0,

    /** Apply frequency-dependent air absorption as a function of distance. */
    IPL_DIRECTEFFECTFLAGS_APPLYAIRABSORPTION        = 1 << 1,

    /** Apply attenuation due to source directivity pattern. */
    IPL_DIRECTEFFECTFLAGS_APPLYDIRECTIVITY          = 1 << 2,

    /** Apply occlusion. */
    IPL_DIRECTEFFECTFLAGS_APPLYOCCLUSION            = 1 << 3,

    /** Apply transmission along with occlusion. */
    IPL_DIRECTEFFECTFLAGS_APPLYTRANSMISSION         = 1 << 4
} IPLDirectEffectFlags;

/** Modes of applying transmission effects. */
typedef enum {
    /** Transmission is frequency-independent. */
    IPL_TRANSMISSIONTYPE_FREQINDEPENDENT,

    /** Transmission is frequency-dependent. */
    IPL_TRANSMISSIONTYPE_FREQDEPENDENT
} IPLTransmissionType;

/** Settings used to create a direct effect. */
typedef struct {
    /** Number of channels that will be used by input and output buffers. */
    IPLint32 numChannels;
} IPLDirectEffectSettings;

/** Parameters for applying a direct effect to an audio buffer. */
typedef struct {
    /** Flags indicating which direct path effects to apply. */
    IPLDirectEffectFlags flags;

    /** Mode of applying transmission effect, if \c IPL_DIRECTEFFECTFLAGS_APPLYTRANSMISSION is enabled. */
    IPLTransmissionType transmissionType;

    /** Value of distance attenuation, between 0 and 1. */
    IPLfloat32 distanceAttenuation;

    /** 3-band EQ coefficients for air absorption, each between 0 and 1. */
    IPLfloat32 airAbsorption[IPL_NUM_BANDS];

    /** Value of directivity term, between 0 and 1. */
    IPLfloat32 directivity;

    /** Value of occlusion factor, between 0 and 1. */
    IPLfloat32 occlusion;

    /** 3-band EQ coefficients for transmission, each between 0 and 1. */
    IPLfloat32 transmission[IPL_NUM_BANDS];
} IPLDirectEffectParams;

/** Creates a direct effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the direct effect.
    \param  effect          [out] The created direct effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplDirectEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLDirectEffectSettings* effectSettings, IPLDirectEffect* effect);

/** Retains an additional reference to a direct effect.

    \param  effect  The direct effect to retain a reference to.

    \return The additional reference to the direct effect.
*/
IPLAPI IPLDirectEffect IPLCALL iplDirectEffectRetain(IPLDirectEffect effect);

/** Releases a reference to a direct effect.

    \param  effect  The direct effect to release a reference to.
*/
IPLAPI void IPLCALL iplDirectEffectRelease(IPLDirectEffect* effect);

/** Resets the internal processing state of a direct effect.

    \param  effect  The direct effect to reset.
*/
IPLAPI void IPLCALL iplDirectEffectReset(IPLDirectEffect effect);

/** Applies a direct effect to an audio buffer.

    This effect CAN be applied in-place.

    \param  effect  The direct effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have as many channels as specified when creating the effect.
    \param  out     The output audio buffer. Must have as many channels as specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplDirectEffectApply(IPLDirectEffect effect, IPLDirectEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in a direct effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The direct effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplDirectEffectGetTailSize(IPLDirectEffect effect);

/** Retrieves a single frame of tail samples from a direct effect's internal buffers.

    After the input to the direct effect has stopped, this function must be called instead of
    \c iplDirectEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The direct effect.
    \param  out     The output audio buffer. Must have as many channels as specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplDirectEffectGetTail(IPLDirectEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup reflectioneffect Reflection Effect
    \{
*/

/** A multi-channel impulse response for use with a reflection effect. Steam Audio creates and manages objects of
    this type internally, your application only needs to pass handles to these objects to the appropriate Steam Audio
    API functions. */
DECLARE_OPAQUE_HANDLE(IPLReflectionEffectIR);

/** Applies the result of physics-based reflections simulation to an audio buffer. The result is encoded in
    Ambisonics, and can be decoded using an Ambisonics decode effect. */
DECLARE_OPAQUE_HANDLE(IPLReflectionEffect);

/** Mixes the outputs of multiple reflection effects, and generates a single sound field containing all the
    reflected sound reaching the listener. Using this is optional. Depending on the reflection effect algorithm used,
    a reflection mixer may provide a reduction in CPU usage. */
DECLARE_OPAQUE_HANDLE(IPLReflectionMixer);

/** Type of reflection effect algorithm to use. */
typedef enum {
    /** Multi-channel convolution reverb. Reflections reaching the listener are encoded in an Impulse Response (IR),
        which is a filter that records each reflection as it arrives. This algorithm renders reflections with the most
        detail, but may result in significant CPU usage. Using a reflection mixer with this algorithm provides a
        reduction in CPU usage. */
    IPL_REFLECTIONEFFECTTYPE_CONVOLUTION,

    /** Parametric (or artificial) reverb, using feedback delay networks. The reflected sound field is reduced to a few
        numbers that describe how reflected energy decays over time. This is then used to drive an approximate model
        of reverberation in an indoor space. This algorithm results in lower CPU usage, but cannot render individual
        echoes, especially in outdoor spaces. A reflection mixer cannot be used with this algorithm. */
    IPL_REFLECTIONEFFECTTYPE_PARAMETRIC,

    /** A hybrid of convolution and parametric reverb. The initial portion of the IR is rendered using convolution
        reverb, but the later part is used to estimate a parametric reverb. The point in the IR where this transition
        occurs can be controlled. This algorithm allows a trade-off between rendering quality and CPU usage. An
        reflection mixer cannot be used with this algorithm. */
    IPL_REFLECTIONEFFECTTYPE_HYBRID,

    /** Multi-channel convolution reverb, using AMD TrueAudio Next for GPU acceleration. This algorithm is similar
        to \c IPL_REFLECTIONEFFECTYPE_CONVOLUTION, but uses the GPU instead of the CPU for processing, allowing
        significantly more sources to be processed. A reflection mixer must be used with this algorithm, because
        the GPU will process convolution reverb at a single point in your audio processing pipeline. */
    IPL_REFLECTIONEFFECTTYPE_TAN,
} IPLReflectionEffectType;

/** Settings used to create a reflection effect. */
typedef struct {
    /** Type of reflection effect algorithm to use. */
    IPLReflectionEffectType type;

    /** Number of samples per channel in the IR. */
    IPLint32 irSize;

    /** Number of channels in the IR. */
    IPLint32 numChannels;
} IPLReflectionEffectSettings;

/** Parameters for applying a reflection effect to an audio buffer. */
typedef struct {
    /** Type of reflection effect algorithm to use. */
    IPLReflectionEffectType type;

    /** The impulse response. For \c IPL_REFLECTIONEFFECTTYPE_CONVOLUTION or \c IPL_REFLECTIONEFFECTTYPE_HYBRID. */
    IPLReflectionEffectIR ir;

    /** 3-band reverb decay times (RT60). For \c IPL_REFLECTIONEFFECTTYPE_PARAMETRIC or
        \c IPL_REFLECTIONEFFECTTYPE_HYBRID. */
    IPLfloat32 reverbTimes[IPL_NUM_BANDS];

    /** 3-band EQ coefficients applied to the parametric part to ensure smooth transition.
        For \c IPL_REFLECTIONEFFECTTYPE_HYBRID. */
    IPLfloat32 eq[IPL_NUM_BANDS];

    /** Samples after which parametric part starts. For \c IPL_REFLECTIONEFFECTTYPE_HYBRID. */
    IPLint32 delay;

    /** Number of IR channels to process. May be less than the number of channels specified when creating the effect,
        in which case CPU usage will be reduced. */
    IPLint32 numChannels;

    /** Number of IR samples per channel to process. May be less than the number of samples specified when creating
        the effect, in which case CPU usage will be reduced. */
    IPLint32 irSize;

    /** The TrueAudio Next device to use for convolution processing. For \c IPL_REFLECTIONEFFECTTYPE_TAN. */
    IPLTrueAudioNextDevice tanDevice;

    /** The TrueAudio Next slot index to use for convolution processing. The slot identifies the IR to use. For
        \c IPL_REFLECTIONEFFECTTYPE_TAN. */
    IPLint32 tanSlot;
} IPLReflectionEffectParams;

/** Creates a reflection effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the reflection effect.
    \param  effect          [out] The created reflection effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplReflectionEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLReflectionEffectSettings* effectSettings, IPLReflectionEffect* effect);

/** Retains an additional reference to a reflection effect.

    \param  effect  The reflection effect to retain a reference to.

    \return The additional reference to the reflection effect.
*/
IPLAPI IPLReflectionEffect IPLCALL iplReflectionEffectRetain(IPLReflectionEffect effect);

/** Releases a reference to a reflection effect.

    \param  effect  The reflection effect to release a reference to.
*/
IPLAPI void IPLCALL iplReflectionEffectRelease(IPLReflectionEffect* effect);

/** Resets the internal processing state of a reflection effect.

    \param  effect  The reflection effect to reset.
*/
IPLAPI void IPLCALL iplReflectionEffectReset(IPLReflectionEffect effect);

/** Applies a reflection effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The reflection effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have 1 channel.
    \param  out     The output audio buffer. Must have as many channels as the impulse response
                    specified when creating the effect (for convolution, hybrid, and TAN) or at
                    least 1 channel (for parametric).
    \param  mixer   If this is non-null, then the output of this effect will be mixed into the given
                    mixer object instead of being returned in the \c out parameter. The mixed output can
                    be retrieved elsewhere in the audio pipeline using \c iplReflectionMixerApply. This
                    can have a performance benefit if using convolution. If using TAN, specifying
                    a mixer is required.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplReflectionEffectApply(IPLReflectionEffect effect, IPLReflectionEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out, IPLReflectionMixer mixer);

/** Returns the number of tail samples remaining in a reflection effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The reflection effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplReflectionEffectGetTailSize(IPLReflectionEffect effect);

/** Retrieves a single frame of tail samples from a reflection effect's internal buffers.

    After the input to the reflection effect has stopped, this function must be called instead of
    \c iplReflectionEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The reflection effect.
    \param  out     The output audio buffer. Must have as many channels as the impulse response
                    specified when creating the effect (for convolution, hybrid, and TAN) or at
                    least 1 channel (for parametric).
    \param  mixer   If this is non-null, then the tail samples will be mixed into the given
                    mixer object instead of being returned in the \c out parameter. The mixed output can
                    be retrieved elsewhere in the audio pipeline using \c iplReflectionMixerApply. This
                    can have a performance benefit if using convolution. If using TAN, specifying
                    a mixer is required.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplReflectionEffectGetTail(IPLReflectionEffect effect, IPLAudioBuffer* out, IPLReflectionMixer mixer);

/** Creates a reflection effect mixer.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings used when creating the reflection effects that will be mixed into
                            this reflection mixer.
    \param  mixer           [out] The created reflection mixer.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplReflectionMixerCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLReflectionEffectSettings* effectSettings, IPLReflectionMixer* mixer);

/** Retains an additional reference to a reflection mixer.

    \param  mixer   The reflection mixer to retain a reference to.

    \return The additional reference to the reflection mixer.
*/
IPLAPI IPLReflectionMixer IPLCALL iplReflectionMixerRetain(IPLReflectionMixer mixer);

/** Releases a reference to a reflection mixer.

    \param  mixer   The reflection mixer to release a reference to.
*/
IPLAPI void IPLCALL iplReflectionMixerRelease(IPLReflectionMixer* mixer);

/** Resets the internal processing state of a reflection mixer.

    \param  mixer   The reflection mixer to reset.
*/
IPLAPI void IPLCALL iplReflectionMixerReset(IPLReflectionMixer mixer);

/** Retrieves the contents of a reflection mixer and places it into an audio buffer.

    \param  mixer   The reflection mixer to retrieve audio from.
    \param  params  Parameters for applying the effect.
    \param  out     The output audio buffer. Must have as many channels as the impulse response
                    specified when creating the mixer.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplReflectionMixerApply(IPLReflectionMixer mixer, IPLReflectionEffectParams* params, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup patheffect Path Effect
    \{
*/

/** Applies the result of simulating sound paths from the source to the listener. Multiple paths that sound can take
    as it propagates from the source to the listener are combined into an Ambisonic sound field. */
DECLARE_OPAQUE_HANDLE(IPLPathEffect);

/** Settings used to create a path effect. */
typedef struct {
    /** The maximum Ambisonics order that will be used by output audio buffers. */
    IPLint32 maxOrder;

    /** If \c IPL_TRUE, then this effect will render spatialized audio into the output buffer. If \c IPL_FALSE,
        this effect will render un-spatialized (and un-rotated) Ambisonic audio. Setting this to \c IPL_FALSE is
        mainly useful only if you plan to mix multiple Ambisonic buffers and/or apply additional processing to
        the Ambisonic audio before spatialization. If you plan to immediately spatialize the output of the path
        effect, setting this value to \c IPL_TRUE can result in significant performance improvements. */
    IPLbool spatialize;

    /** The speaker layout to use when spatializing. Only used if \c spatialize is \c IPL_TRUE. */
    IPLSpeakerLayout speakerLayout;

    /** The HRTF to use when spatializing. Only used if \c spatialize is \c IPL_TRUE. */
    IPLHRTF hrtf;
} IPLPathEffectSettings;

/** Parameters for applying a path effect to an audio buffer. */
typedef struct {
    /** 3-band EQ coefficients for modeling frequency-dependent attenuation caused by paths bending around
        obstacles. */
    IPLfloat32 eqCoeffs[IPL_NUM_BANDS];

    /** Ambisonic coefficients for modeling the directional distribution of sound reaching the listener.
        The coefficients are specified in world-space, and must be rotated to match the listener's orientation
        separately. */
    IPLfloat32* shCoeffs;

    /** Ambisonic order of the output buffer. May be less than the maximum order specified when creating the effect,
        in which case higher-order \c shCoeffs will be ignored, and CPU usage will be reduced. */
    IPLint32 order;

    /** If \c IPL_TRUE, spatialize using HRTF-based binaural rendering. Only used if \c spatialize was set to
        \c IPL_TRUE in \c IPLPathEffectSettings. */
    IPLbool binaural;

    /** The HRTF to use when spatializing. Only used if \c spatialize was set to \c IPL_TRUE in
        \c IPLPathEffectSettings and \c binaural is set to \c IPL_TRUE. */
    IPLHRTF hrtf;

    /** The position and orientation of the listener. Only used if \c spatialize was set to \c IPL_TRUE in
        \c IPLPathEffectSettings and \c binaural is set to \c IPL_TRUE. */
    IPLCoordinateSpace3 listener;

    /** If \c IPL_TRUE, the values in \c eqCoeffs will be normalized before being used, i.e., each value in
     *  \c eqCoeffs will be divided by the largest value in \c eqCoeffs. This can help counteract overly-aggressive
     *  filtering due to a physics-based deviation model. If \c IPL_FALSE, the values in \c eqCoeffs will be
     *  used as-is.
     */
    IPLbool normalizeEQ;
} IPLPathEffectParams;

/** Creates a path effect.

    \param  context         The context used to initialize Steam Audio.
    \param  audioSettings   Global audio processing settings.
    \param  effectSettings  The settings to use when creating the path effect.
    \param  effect          [out] The created path effect.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplPathEffectCreate(IPLContext context, IPLAudioSettings* audioSettings, IPLPathEffectSettings* effectSettings, IPLPathEffect* effect);

/** Retains an additional reference to a path effect.

    \param  effect  The path effect to retain a reference to.

    \return The additional reference to the path effect.
*/
IPLAPI IPLPathEffect IPLCALL iplPathEffectRetain(IPLPathEffect effect);

/** Releases a reference to a path effect.

    \param  effect  The path effect to release a reference to.
*/
IPLAPI void IPLCALL iplPathEffectRelease(IPLPathEffect* effect);

/** Resets the internal processing state of a path effect.

    \param  effect  The path effect to reset.
*/
IPLAPI void IPLCALL iplPathEffectReset(IPLPathEffect effect);

/** Applies a path effect to an audio buffer.

    This effect CANNOT be applied in-place.

    \param  effect  The path effect to apply.
    \param  params  Parameters for applying the effect.
    \param  in      The input audio buffer. Must have 1 channel.
    \param  out     The output audio buffer. Must have as many channels as needed for the
                    Ambisonics order specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplPathEffectApply(IPLPathEffect effect, IPLPathEffectParams* params, IPLAudioBuffer* in, IPLAudioBuffer* out);

/** Returns the number of tail samples remaining in a path effect's internal buffers.

    Tail samples are audio samples that should be played even after the input to the effect has stopped
    playing and no further input samples are available.

    \param  effect  The path effect.

    \return The number of tail samples remaining.
*/
IPLAPI IPLint32 IPLCALL iplPathEffectGetTailSize(IPLPathEffect effect);

/** Retrieves a single frame of tail samples from a path effect's internal buffers.

    After the input to the path effect has stopped, this function must be called instead of
    \c iplPathEffectApply until the return value indicates that no more tail samples remain.

    \param  effect  The path effect.
    \param  out     The output audio buffer. Must have as many channels as needed for the
                    Ambisonics order specified when creating the effect.

    \return \c IPL_AUDIOEFFECTSTATE_TAILREMAINING if any tail samples remain in the effect's internal buffers, or
            \c IPL_AUDIOEFFECTSTATE_TAILCOMPLETE otherwise.
*/
IPLAPI IPLAudioEffectState IPLCALL iplPathEffectGetTail(IPLPathEffect effect, IPLAudioBuffer* out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup energyfield Energy Field
 *  \{
 */

/** An energy field. Energy fields represent a histogram of sound energy arriving at a point, as a function of
    incident direction, frequency band, and arrival time.
    
    Time is subdivided into "bins" of the histogram, with each bin corresponding to 10ms. For each bin, incident energy is 
    stored separately for each frequency band. For a given frequency band and time bin, we store an Ambisonic 
    representation of the variation of incident energy as a function of direction.
    
    Energy field data is stored as a 3D array of size #channels * #bands * #bins, in row-major order. */
DECLARE_OPAQUE_HANDLE(IPLEnergyField);

/** Settings used to create an energy field. */
typedef struct {
    /** Total duration (in seconds) of the energy field. This determines the number of bins in each channel and band. */
    IPLfloat32 duration;

    /** The Ambisonic order. This determines the number of channels. */
    IPLint32 order;
} IPLEnergyFieldSettings;

/** Creates an energy field.
    
    \param  context         The context used to initialize Steam Audio.
    \param  settings        The settings to use when creating the energy field.
    \param  energyField     [out] The created energy field.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplEnergyFieldCreate(IPLContext context, IPLEnergyFieldSettings* settings, IPLEnergyField* energyField);

/** Retains an additional reference to an energy field.

    \param  energyField     The energy field to retain a reference to.

    \return The additional reference to the energy field.
*/
IPLAPI IPLEnergyField IPLCALL iplEnergyFieldRetain(IPLEnergyField energyField);

/** Releases a reference to an energy field.

    \param  energyField     The energy field to release a reference to.
*/
IPLAPI void IPLCALL iplEnergyFieldRelease(IPLEnergyField* energyField);

/** Returns the number of channels in the energy field.

    \param  energyField     The energy field.

    \return The number of channels in the energy field.
*/
IPLAPI IPLint32 IPLCALL iplEnergyFieldGetNumChannels(IPLEnergyField energyField);

/** Returns the number of bins in the energy field.

    \param  energyField     The energy field.

    \return The number of bins in the energy field.
*/
IPLAPI IPLint32 IPLCALL iplEnergyFieldGetNumBins(IPLEnergyField energyField);

/** Returns a pointer to the data stored in the energy field.

    \param  energyField     The energy field.

    \return Pointer to #channels * #bands * #bins IPLfloat32 values stored in the energy field, in row-major order.
*/
IPLAPI IPLfloat32* IPLCALL iplEnergyFieldGetData(IPLEnergyField energyField);

/** Returns a pointer to the data stored in the energy field for the given channel.

    \param  energyField     The energy field.
    \param  channelIndex    Index of the channel.

    \return Pointer to #bands * #bins IPLfloat32 values stored in the energy field for the given channel, in 
            row-major order.
*/
IPLAPI IPLfloat32* IPLCALL iplEnergyFieldGetChannel(IPLEnergyField energyField, IPLint32 channelIndex);

/** Returns a pointer to the data stored in the energy field for the given channel and band.

    \param  energyField     The energy field.
    \param  channelIndex    Index of the channel.
    \param  bandIndex       Index of the band.

    \return Pointer to #bins IPLfloat32 values stored in the energy field for the given channel and band, in row-major 
            order.
*/
IPLAPI IPLfloat32* IPLCALL iplEnergyFieldGetBand(IPLEnergyField energyField, IPLint32 channelIndex, IPLint32 bandIndex);

/** Resets all values stored in an energy field to zero.

    \param  energyField     The energy field.
*/
IPLAPI void IPLCALL iplEnergyFieldReset(IPLEnergyField energyField);

/** Copies data from one energy field into another.

    If the source and destination energy fields have different numbers of channels, only the smaller of the two
    numbers of channels will be copied.

    If the source and destination energy fields have different numbers of bins, only the smaller of the two numbers of
    bins will be copied.

    \param  src             The source energy field.
    \param  dst             The destination energy field.
*/
IPLAPI void IPLCALL iplEnergyFieldCopy(IPLEnergyField src, IPLEnergyField dst);

/** Swaps the data contained in one energy field with the data contained in another energy field.

    The two energy fields may contain different numbers of channels or bins.

    \param  a               The first energy field.
    \param  b               The second energy field.
*/
IPLAPI void IPLCALL iplEnergyFieldSwap(IPLEnergyField a, IPLEnergyField b);

/** Adds the values stored in two energy fields, and stores the result in a third energy field.

    If the energy fields have different numbers of channels, only the smallest of the three numbers of channels will
    be added.

    If the energy fields have different numbers of bins, only the smallest of the three numbers of bins will
    be added.

    This function can be used for in-place addition, i.e. \c out may be equal to \c in1 or \c in2.

    \param  in1             The first input energy field.
    \param  in2             The second input energy field.
    \param  out             The output energy field.
*/
IPLAPI void IPLCALL iplEnergyFieldAdd(IPLEnergyField in1, IPLEnergyField in2, IPLEnergyField out);

/** Scales the values stored in an energy field by a scalar, and stores the result in a second energy field.

    If the energy fields have different numbers of channels, only the smallest of the two numbers of channels will
    be scaled.

    If the energy fields have different numbers of bins, only the smallest of the two numbers of bins will
    be scaled.

    This function can be used for in-place scaling, i.e. \c out may be equal to \c in.

    \param  in              The input energy field.
    \param  scalar          The scalar value.
    \param  out             The output energy field.
*/
IPLAPI void IPLCALL iplEnergyFieldScale(IPLEnergyField in, IPLfloat32 scalar, IPLEnergyField out);

/** Scales the values stored in an energy field by a scalar, and adds the result to a second energy field.

    If the energy fields have different numbers of channels, only the smallest of the two numbers of channels will
    be added.

    If the energy fields have different numbers of bins, only the smallest of the two numbers of bins will
    be added.

    This function can be used for in-place operation, i.e. \c out may be equal to \c in.

    \param  in              The input energy field.
    \param  scalar          The scalar value.
    \param  out             The output energy field.
*/
IPLAPI void IPLCALL iplEnergyFieldScaleAccum(IPLEnergyField in, IPLfloat32 scalar, IPLEnergyField out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup impulseresponse Impulse Response
 *  \{
 */

/** An impulse response. Impulse responses are represented in Ambisonics to allow for directional variation of
    propagated sound.
    
    Impulse response data is stored as a 2D array of size #channels * #samples, in row-major order. */
DECLARE_OPAQUE_HANDLE(IPLImpulseResponse);

/** Settings used to create an impulse response. */
typedef struct {
    /** Total duration (in seconds) of the impulse response. This determines the number of samples in each channel. */
    IPLfloat32 duration;

    /** The Ambisonic order. This determines the number of channels. */
    IPLint32 order;

    /** The sampling rate. This, together with the duration, determines the number of samples in each channel. */
    IPLint32 samplingRate;
} IPLImpulseResponseSettings;

/** Creates an impulse response.

    \param  context         The context used to initialize Steam Audio.
    \param  settings        The settings to use when creating the impulse response.
    \param  impulseResponse [out] The created impulse response.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplImpulseResponseCreate(IPLContext context, IPLImpulseResponseSettings* settings, IPLImpulseResponse* impulseResponse);

/** Retains an additional reference to an impulse response.

    \param  impulseResponse The impulse response to retain a reference to.

    \return The additional reference to the impulse response.
*/
IPLAPI IPLImpulseResponse IPLCALL iplImpulseResponseRetain(IPLImpulseResponse impulseResponse);

/** Releases a reference to an impulse response.

    \param  impulseResponse The impulse response to release a reference to.
*/
IPLAPI void IPLCALL iplImpulseResponseRelease(IPLImpulseResponse* impulseResponse);

/** Returns the number of channels in the impulse response.

    \param  impulseResponse The impulse response.

    \return The number of channels in the impulse response.
*/
IPLAPI IPLint32 IPLCALL iplImpulseResponseGetNumChannels(IPLImpulseResponse impulseResponse);

/** Returns the number of samples in the impulse response.

    \param  impulseResponse The impulse response.

    \return The number of samples in the impulse response.
*/
IPLAPI IPLint32 IPLCALL iplImpulseResponseGetNumSamples(IPLImpulseResponse impulseResponse);

/** Returns a pointer to the data stored in the impulse response.

    \param  impulseResponse The impulse response.

    \return Pointer to #channels * #samples IPLfloat32 values stored in the impulse response, in row-major order.
*/
IPLAPI IPLfloat32* IPLCALL iplImpulseResponseGetData(IPLImpulseResponse impulseResponse);

/** Returns a pointer to the data stored in the impulse response for the given channel.

    \param  impulseResponse The impulse response.
    \param  channelIndex    Index of the channel.

    \return Pointer to #samples IPLfloat32 values stored in the impulse response for the given channel, in
            row-major order.
*/
IPLAPI IPLfloat32* IPLCALL iplImpulseResponseGetChannel(IPLImpulseResponse impulseResponse, int channelIndex);

/** Resets all values stored in an impulse response to zero.

    \param  impulseResponse The impulse response.
*/
IPLAPI void IPLCALL iplImpulseResponseReset(IPLImpulseResponse impulseResponse);

/** Copies data from one impulse response into another.

    If the source and destination impulse responses have different numbers of channels, only the smaller of the two
    numbers of channels will be copied.

    If the source and destination impulse responses have different numbers of samples, only the smaller of the two numbers of
    samples will be copied.

    \param  src             The source impulse response.
    \param  dst             The destination impulse response.
*/
IPLAPI void IPLCALL iplImpulseResponseCopy(IPLImpulseResponse src, IPLImpulseResponse dst);

/** Swaps the data contained in one impulse response with the data contained in another impulse response.

    The two impulse responses may contain different numbers of channels or samples.

    \param  a               The first impulse response.
    \param  b               The second impulse response.
*/
IPLAPI void IPLCALL iplImpulseResponseSwap(IPLImpulseResponse ir1, IPLImpulseResponse ir2);

/** Adds the values stored in two impulse responses, and stores the result in a third impulse response.

    If the impulse responses have different numbers of channels, only the smallest of the three numbers of channels will
    be added.

    If the impulse responses have different numbers of samples, only the smallest of the three numbers of samples will
    be added.

    This function can be used for in-place addition, i.e. \c out may be equal to \c in1 or \c in2.

    \param  in1             The first input impulse response.
    \param  in2             The second input impulse response.
    \param  out             The output impulse response.
*/
IPLAPI void IPLCALL iplImpulseResponseAdd(IPLImpulseResponse in1, IPLImpulseResponse in2, IPLImpulseResponse out);

/** Scales the values stored in an impulse response by a scalar, and stores the result in a second impulse response.

    If the impulse responses have different numbers of channels, only the smallest of the two numbers of channels will
    be scaled.

    If the impulse responses have different numbers of samples, only the smallest of the two numbers of samples will
    be scaled.

    This function can be used for in-place scaling, i.e. \c out may be equal to \c in.

    \param  in              The input impulse response.
    \param  scalar          The scalar value.
    \param  out             The output impulse response.
*/
IPLAPI void IPLCALL iplImpulseResponseScale(IPLImpulseResponse in, IPLfloat32 scalar, IPLImpulseResponse out);

/** Scales the values stored in an impulse response by a scalar, and adds the result to a second impulse response.

    If the impulse responses have different numbers of channels, only the smallest of the two numbers of channels will
    be added.

    If the impulse responses have different numbers of samples, only the smallest of the two numbers of samples will
    be added.

    This function can be used for in-place operation, i.e. \c out may be equal to \c in.

    \param  in              The input impulse response.
    \param  scalar          The scalar value.
    \param  out             The output impulse response.
*/
IPLAPI void IPLCALL iplImpulseResponseScaleAccum(IPLImpulseResponse in, IPLfloat32 scalar, IPLImpulseResponse out);

/** \} */


/*********************************************************************************************************************/

/** \defgroup reconstructor Reconstructor
 *  \{
 */

/** An object that can convert energy fields to impulse responses. Energy fields are typically much smaller in size
    than impulse responses, and therefore are used when storing baked reflections or reverb in a probe batch, but
    impulse responses are what is needed at runtime for convolution. */
DECLARE_OPAQUE_HANDLE(IPLReconstructor);

/** Settings used to create a reconstructor. */
typedef struct {
    /** The largest possible duration (in seconds) of any impulse response that will be reconstructed using this 
        reconstructor. */
    IPLfloat32 maxDuration;

    /** The largest possible Ambisonic order of any impulse response that will be reconstructed using this
        reconstructor. */
    IPLint32 maxOrder;

    /** The sampling rate of impulse responses reconstructed using this reconstructor. */
    IPLint32 samplingRate;
} IPLReconstructorSettings;

/** The inputs for a single reconstruction operation. */
typedef struct {
    /** The energy field from which to reconstruct an impulse response. */
    IPLEnergyField energyField;
} IPLReconstructorInputs;

/** Inputs common to all reconstruction operations specified in a single call to \c iplReconstructorReconstruct. */
typedef struct {
    /** Duration of impulse responses to reconstruct. Must be less than or equal to \c maxDuration specified in
        \c IPLReconstructorSettings. */
    IPLfloat32 duration;

    /** Ambisonic order of impulse responses to reconstruct. Must be less than or equal to \c maxOrder specified in
        \c IPLReconstructorSettings. */
    IPLint32 order;
} IPLReconstructorSharedInputs;

/** The outputs for a single reconstruction operation. */
typedef struct {
    /** The impulse response object into which the reconstructed impulse repsonse should be stored. */
    IPLImpulseResponse impulseResponse;
} IPLReconstructorOutputs;

/** Creates a reconstructor.

    \param  context         The context used to initialize Steam Audio.
    \param  settings        The settings to use when creating the reconstructor.
    \param  reconstructor   [out] The created reconstructor.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplReconstructorCreate(IPLContext context, IPLReconstructorSettings* settings, IPLReconstructor* reconstructor);

/** Retains an additional reference to a reconstructor.

    \param  reconstructor   The reconstructor to retain a reference to.

    \return The additional reference to the reconstructor.
*/
IPLAPI IPLReconstructor IPLCALL iplReconstructorRetain(IPLReconstructor reconstructor);

/** Releases a reference to a reconstructor.

    \param  reconstructor   The reconstructor to release a reference to.
*/
IPLAPI void IPLCALL iplReconstructorRelease(IPLReconstructor* reconstructor);

/** Reconstructs one or more impulse responses as a single batch of work.

    \param  reconstructor   The reconstructor to use.
    \param  numInputs       The number of impulse responses to reconstruct.
    \param  inputs          Pointer to \c numInputs input structures, each describing a single energy field from which
                            to reconstruct a corresponding impulse response.
    \param  sharedInputs    Pointer to a single shared input structure, describing common inputs used across all
                            impulse response reconstruction operations in this call.
    \param  outputs         Pointer to \c numInputs output structures, each describing a single impulse response object
                            into which to write the corresponding reconstructed impulse response.
*/
IPLAPI void IPLCALL iplReconstructorReconstruct(IPLReconstructor reconstructor, IPLint32 numInputs, IPLReconstructorInputs* inputs, IPLReconstructorSharedInputs* sharedInputs, IPLReconstructorOutputs* outputs);

/** \} */


/*********************************************************************************************************************/

/** \defgroup probes Probes
    \{
*/

/** An array of sound probes. Each probe has a position and a radius of influence. */
DECLARE_OPAQUE_HANDLE(IPLProbeArray);

/** A batch of sound probes, along with associated data. The associated data may include reverb, reflections from a
    static source position, pathing, and more. This data is loaded and unloaded as a unit, either from disk or over
    the network. */
DECLARE_OPAQUE_HANDLE(IPLProbeBatch);

/** The different algorithms for generating probes. */
typedef enum {
    /** Generates a single probe at the center of the specified box. */
    IPL_PROBEGENERATIONTYPE_CENTROID,

    /** Generates probes that are uniformly-spaced, at a fixed height above solid geometry. A probe will never be
        generated above another probe unless there is a solid object between them. The goal is to model floors or
        terrain, and generate probes that are a fixed height above the floor or terrain, and uniformly-spaced along
        the horizontal plane. This algorithm is not suitable for scenarios where the listener may fly into a region
        with no probes; if this happens, the listener will not be influenced by any of the baked data. */
    IPL_PROBEGENERATIONTYPE_UNIFORMFLOOR
} IPLProbeGenerationType;

/** The different ways in which the source and listener positions used to generate baked data can vary as a function
    of probe position. */
typedef enum {
    /** At each probe, baked data is calculated with both the source and the listener at the probe position. This
        is useful for modeling traditional reverbs, which depend only on the listener's position (or only on the
        source's position). */
    IPL_BAKEDDATAVARIATION_REVERB,

    /** At each probe, baked data is calculated with the source at some fixed position (specified separately),
        and the listener at the probe position. This is used for modeling reflections from a static source to any
        point within the probe batch. */
    IPL_BAKEDDATAVARIATION_STATICSOURCE,

    /** At each probe, baked data is calculated with the source at the probe position, and the listener at some
        fixed position (specified separately). This is used for modeling reflections from a moving source to a
        static listener. */
    IPL_BAKEDDATAVARIATION_STATICLISTENER,

    /** Baked data is calculated for each pair of probes. For example, this is used for calculating paths between
        every pair of probes in a batch. */
    IPL_BAKEDDATAVARIATION_DYNAMIC
} IPLBakedDataVariation;

/** The types of baked data that can be stored in a probe batch. */
typedef enum {
    /** Reflections. The source and listener positions used to compute the reflections data stored at each probe
        depends on the \c IPLBakedDataVariation selected. */
    IPL_BAKEDDATATYPE_REFLECTIONS,

    /** Pathing. The probe batch stores data about the shortest paths between any pair of probes in the batch. */
    IPL_BAKEDDATATYPE_PATHING
} IPLBakedDataType;

/** Settings used to generate probes. */
typedef struct {
    /** The algorithm to use for generating probes. */
    IPLProbeGenerationType type;

    /** Spacing (in meters) between two neighboring probes. Only for \c IPL_PROBEGENERATIONTYPE_UNIFORMFLOOR. */
    IPLfloat32 spacing;

    /** Height (in meters) above the floor at which probes will be generated. Only for
        \c IPL_PROBEGENERATIONTYPE_UNIFORMFLOOR. */
    IPLfloat32 height;

    /** A transformation matrix that transforms an axis-aligned unit cube, with minimum and maximum vertices
        at (0, 0, 0) and (1, 1, 1), into a parallelopiped volume. Probes will be generated within this
        volume. */
    IPLMatrix4x4 transform;
} IPLProbeGenerationParams;

/** Identifies a "layer" of data stored in a probe batch. Each probe batch may store multiple layers of data,
    such as reverb, static source reflections, or pathing. Each layer can be accessed using an identifier. */
typedef struct {
    /** The type of data stored. */
    IPLBakedDataType type;

    /** The way in which source and listener positions depend on probe position. */
    IPLBakedDataVariation variation;

    /** The static source (for \c IPL_BAKEDDATAVARIATION_STATICSOURCE) or static listener (for
        \c IPL_BAKEDDATAVARIATION_STATICLISTENER) used to generate baked data. Baked data is only stored for
        probes that lie within the radius of this sphere. */
    IPLSphere endpointInfluence;
} IPLBakedDataIdentifier;

/** Creates an empty probe array.

    \param  context     The context used to initialize Steam Audio.
    \param  probeArray  [out] The created probe array.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplProbeArrayCreate(IPLContext context, IPLProbeArray* probeArray);

/** Retains an additional reference to a probe array.

    \param  probeArray  The probe array to retain a reference to.

    \return The additional reference to the probe array.
*/
IPLAPI IPLProbeArray IPLCALL iplProbeArrayRetain(IPLProbeArray probeArray);

/** Releases a reference to a probe array.

    \param  probeArray  The probe array to release a reference to.
*/
IPLAPI void IPLCALL iplProbeArrayRelease(IPLProbeArray* probeArray);

/** Generates probes and adds them to a probe array.

    \param  scene       The scene in which to generate probes.
    \param  params      Parameters to use for generating probes.
    \param  probeArray  The array into which to add the generated probes.
*/
IPLAPI void IPLCALL iplProbeArrayGenerateProbes(IPLProbeArray probeArray, IPLScene scene, IPLProbeGenerationParams* params);

/** \return The number of probes in a probe array.

    \param  probeArray  The probe array.
*/
IPLAPI IPLint32 IPLCALL iplProbeArrayGetNumProbes(IPLProbeArray probeArray);

/** \return The probe at a given index in a probe array.

    \param  probeArray  The probe array.
    \param  index       Index of the probe within the array.
*/
IPLAPI IPLSphere IPLCALL iplProbeArrayGetProbe(IPLProbeArray probeArray, IPLint32 index);

/** Creates an empty probe batch.

    \param  context     The context used to initialize Steam Audio.
    \param  probeBatch  [out] The created probe batch.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplProbeBatchCreate(IPLContext context, IPLProbeBatch* probeBatch);

/** Retains an additional reference to a probe batch.

    \param  probeBatch  The probe batch to retain a reference to.

    \return The additional reference to the probe batch.
*/
IPLAPI IPLProbeBatch IPLCALL iplProbeBatchRetain(IPLProbeBatch probeBatch);

/** Releases a reference to a probe batch.

    \param  probeBatch  The probe batch to release a reference to.
*/
IPLAPI void IPLCALL iplProbeBatchRelease(IPLProbeBatch* probeBatch);

/** Loads a probe batch from a serialized object. Typically, the serialized object will be created from a byte array
    loaded from disk or over the network.

    \param  context             The context used to initialize Steam Audio.
    \param  serializedObject    The serialized object from which to load the probe batch.
    \param  probeBatch          [out] The created probe batch.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplProbeBatchLoad(IPLContext context, IPLSerializedObject serializedObject, IPLProbeBatch* probeBatch);

/** Saves a probe batch to a serialized object. Typically, the serialized object will then be saved to disk.

    \param  probeBatch          The probe batch to save.
    \param  serializedObject    The serialized object into which to save the probe batch.
*/
IPLAPI void IPLCALL iplProbeBatchSave(IPLProbeBatch probeBatch, IPLSerializedObject serializedObject);

/** \return The number of probes in a probe batch.

    \param  probeBatch  The probe batch.
*/
IPLAPI IPLint32 IPLCALL iplProbeBatchGetNumProbes(IPLProbeBatch probeBatch);

/** Adds a probe to a batch. The new probe will be added as the last probe in the batch.

    \param  probeBatch  The probe batch.
    \param  probe       The probe to add.
*/
IPLAPI void IPLCALL iplProbeBatchAddProbe(IPLProbeBatch probeBatch, IPLSphere probe);

/** Adds every probe in an array to a batch. The new probes will be added, in order, at the end of the batch.

    \param  probeBatch  The probe batch.
    \param  probeArray  The probe array containing the probes to add.
*/
IPLAPI void IPLCALL iplProbeBatchAddProbeArray(IPLProbeBatch probeBatch, IPLProbeArray probeArray);

/** Removes a probe from a batch.

    \param  probeBatch  The probe batch.
    \param  index       Index of the probe to remove.
*/
IPLAPI void IPLCALL iplProbeBatchRemoveProbe(IPLProbeBatch probeBatch, IPLint32 index);

/** Commits all changes made to a probe batch since this function was last called (or since the probe batch was
    first created, if this function was never called). This function must be called after adding, removing, or
    updating any probes in the batch, for the changes to take effect.

    \param  probeBatch  The probe batch.
*/
IPLAPI void IPLCALL iplProbeBatchCommit(IPLProbeBatch probeBatch);

/** Deletes a specific layer of data from a probe batch.

    \param  probeBatch  The probe batch.
    \param  identifier  The identifier of the baked data layer to delete.
*/
IPLAPI void IPLCALL iplProbeBatchRemoveData(IPLProbeBatch probeBatch, IPLBakedDataIdentifier* identifier);

/** \return The size (in bytes) of a specific baked data layer in a probe batch.

    \param  probeBatch  The probe batch.
    \param  identifier  The identifier of the baked data layer.
*/
IPLAPI IPLsize IPLCALL iplProbeBatchGetDataSize(IPLProbeBatch probeBatch, IPLBakedDataIdentifier* identifier);

/** Retrieves a single energy field in a specific baked data layer of a specific probe in a probe batch.

    \param  probeBatch  The probe batch.
    \param  identifier  The identifier of the baked data layer.
    \param  probeIndex  The index of the probe.
    \param  energyField The energy field into which to copy the baked energy field.
*/
IPLAPI void IPLCALL iplProbeBatchGetEnergyField(IPLProbeBatch probeBatch, IPLBakedDataIdentifier* identifier, IPLint32 probeIndex, IPLEnergyField energyField);

/** Retrieves a single array of parametric reverb times in a specific baked data layer of a specific probe in a probe batch.

    \param  probeBatch  The probe batch.
    \param  identifier  The identifier of the baked data layer.
    \param  probeIndex  The index of the probe.
    \param  reverbTimes Pointer to an array containing at least 3 IPLfloat32 values, into which the baked reverb times
                        will be copied.
*/
IPLAPI void IPLCALL iplProbeBatchGetReverb(IPLProbeBatch probeBatch, IPLBakedDataIdentifier* identifier, IPLint32 probeIndex, IPLfloat32* reverbTimes);

/** \} */


/*********************************************************************************************************************/

/** \defgroup baking Baking
    \{
*/

/** Flags for specifying what types of reflections data to bake. */
typedef enum {
    /** Bake impulse responses for \c IPL_REFLECTIONEFFECTTYPE_CONVOLUTION, \c IPL_REFLECTIONEFFECTTYPE_HYBRID,
        or \c IPL_REFLECTIONEFFECTTYPE_TAN. */
    IPL_REFLECTIONSBAKEFLAGS_BAKECONVOLUTION = 1 << 0,

    /** Bake parametric reverb for \c IPL_REFLECTIONEFFECTTYPE_PARAMETRIC or \c IPL_REFLECTIONEFFECTTYPE_HYBRID. */
    IPL_REFLECTIONSBAKEFLAGS_BAKEPARAMETRIC = 1 << 1,
} IPLReflectionsBakeFlags;

/** Parameters used to control how reflections data is baked. */
typedef struct {
    /** The scene in which the probes exist. */
    IPLScene scene;

    /** A probe batch containing the probes at which reflections data should be baked. */
    IPLProbeBatch probeBatch;

    /** The type of scene being used. */
    IPLSceneType sceneType;

    /** An identifier for the data layer that should be baked. The identifier determines what data is simulated and
        stored at each probe. If the probe batch already contains data with this identifier, it will be overwritten. */
    IPLBakedDataIdentifier identifier;

    /** The types of data to save for each probe. */
    IPLReflectionsBakeFlags bakeFlags;

    /** The number of rays to trace from each listener position when baking. Increasing this number results in
        improved accuracy, at the cost of increased bake times. */
    IPLint32 numRays;

    /** The number of directions to consider when generating diffusely-reflected rays when baking. Increasing
        this number results in slightly improved accuracy of diffuse reflections. */
    IPLint32 numDiffuseSamples;

    /** The number of times each ray is reflected off of solid geometry. Increasing this number results in
        longer reverb tails and improved accuracy, at the cost of increased bake times. */
    IPLint32 numBounces;

    /** The length (in seconds) of the impulse responses to simulate. Increasing this number allows the baked
        data to represent longer reverb tails (and hence larger spaces), at the cost of increased memory
        usage while baking. */
    IPLfloat32 simulatedDuration;

    /** The length (in seconds) of the impulse responses to save at each probe. Increasing this number allows
        the baked data to represent longer reverb tails (and hence larger spaces), at the cost of increased
        disk space usage and memory usage at run-time.

        It may be useful to set \c savedDuration to be less than \c simulatedDuration, especially if you plan
        to use hybrid reverb for rendering baked reflections. This way, the parametric reverb data is
        estimated using a longer IR, resulting in more accurate estimation, but only the early part of the IR
        can be saved for subsequent rendering. */
    IPLfloat32 savedDuration;

    /** Ambisonic order of the baked IRs. */
    IPLint32 order;

    /** Number of threads to use for baking. */
    IPLint32 numThreads;

    /** If using custom ray tracer callbacks, this the number of rays that will be passed to the callbacks
        every time rays need to be traced. */
    IPLint32 rayBatchSize;

    /** When calculating how much sound energy reaches a surface directly from a source, any source that is
        closer than \c irradianceMinDistance to the surface is assumed to be at a distance of
        \c irradianceMinDistance, for the purposes of energy calculations. */
    IPLfloat32 irradianceMinDistance;

    /** If using Radeon Rays or if \c identifier.variation is \c IPL_BAKEDDATAVARIATION_STATICLISTENER, this is the
        number of probes for which data is baked simultaneously. */
    IPLint32 bakeBatchSize;

    /** The OpenCL device, if using Radeon Rays. */
    IPLOpenCLDevice openCLDevice;

    /** The Radeon Rays device, if using Radeon Rays. */
    IPLRadeonRaysDevice radeonRaysDevice;
} IPLReflectionsBakeParams;

/** Parameters used to control how pathing data is baked. */
typedef struct {
    /** The scene in which the probes exist. */
    IPLScene                scene;

    /** A probe batch containing the probes for which pathing data should be baked. */
    IPLProbeBatch           probeBatch;

    /** An identifier for the data layer that should be baked. The identifier determines what data is simulated and
        stored at each probe. If the probe batch already contains data with this identifier, it will be overwritten. */
    IPLBakedDataIdentifier  identifier;

    /** Number of point samples to use around each probe when testing whether one probe can see another. To
        determine if two probes are mutually visible, numSamples * numSamples rays are traced, from each
        point sample of the first probe, to every other point sample of the second probe. */
    IPLint32                numSamples;

    /** When testing for mutual visibility between a pair of probes, each probe is treated as a sphere of
        this radius (in meters), and point samples are generated within this sphere. */
    IPLfloat32              radius;

    /** When tracing rays to test for mutual visibility between a pair of probes, the fraction of rays that
        are unoccluded must be greater than this threshold for the pair of probes to be considered
        mutually visible. */
    IPLfloat32              threshold;

    /** If the distance between two probes is greater than this value, the probes are not considered mutually
        visible. Increasing this value can result in simpler paths, at the cost of increased bake times. */
    IPLfloat32              visRange;

    /** If the length of the path between two probes is greater than this value, the probes are considered to
        not have any path between them. Increasing this value allows sound to propagate over greater
        distances, at the cost of increased bake times and memory usage. */
    IPLfloat32              pathRange;

    /** Number of threads to use for baking. */
    IPLint32                numThreads;
} IPLPathBakeParams;

/** Bakes a single layer of reflections data in a probe batch.

    Only one bake can be in progress at any point in time.

    \param  context             The context used to initialize Steam Audio.
    \param  params              Parameters to use for baking reflections data.
    \param  progressCallback    (Optional) This function will be called by Steam Audio to notify your application
                                as the bake progresses. Use this to display a progress bar or some other indicator
                                that the bake is running.
    \param  userData            (Optional) Pointer to arbitrary data that will be sent to the progress callback
                                when Steam Audio calls it.
*/
IPLAPI void IPLCALL iplReflectionsBakerBake(IPLContext context, IPLReflectionsBakeParams* params, IPLProgressCallback progressCallback, void* userData);

/** Cancels any running bakes of reflections data.

    \param  context             The context used to initialize Steam Audio.
*/
IPLAPI void IPLCALL iplReflectionsBakerCancelBake(IPLContext context);

/** Bakes a single layer of pathing data in a probe batch.

    Only one bake can be in progress at any point in time.

    \param  context             The context used to initialize Steam Audio.
    \param  params              Parameters to use for baking pathing data.
    \param  progressCallback    (Optional) This function will be called by Steam Audio to notify your application
                                as the bake progresses. Use this to display a progress bar or some other indicator
                                that the bake is running.
    \param  userData            (Optional) Pointer to arbitrary data that will be sent to the progress callback
                                when Steam Audio calls it.
*/
IPLAPI void IPLCALL iplPathBakerBake(IPLContext context, IPLPathBakeParams* params, IPLProgressCallback progressCallback, void* userData);

/** Cancels any running bakes of pathing data.

    \param  context             The context used to initialize Steam Audio.
*/
IPLAPI void IPLCALL iplPathBakerCancelBake(IPLContext context);

/** \} */


/*********************************************************************************************************************/

/** \defgroup simulation Real-Time Simulation
    \{
*/

/** A sound source, for the purposes of simulation. This object is used to specify various parameters for direct
    and indirect sound propagation simulation, and to retrieve the simulation results. */
DECLARE_OPAQUE_HANDLE(IPLSource);

/** Manages direct and indirect sound propagation simulation for multiple sources. Your application will typically
    create one simulator object and use it to run simulations with different source and listener parameters between
    consecutive simulation runs. The simulator can also be reused across scene changes. */
DECLARE_OPAQUE_HANDLE(IPLSimulator);

/** Flags indicating which types of simulation should be enabled for a given \c IPLSource. */
typedef enum {
    /** Enable direct simulation. This includes distance attenuation, air absorption, directivity, occlusion, and
        transmission. */
    IPL_SIMULATIONFLAGS_DIRECT = 1 << 0,

    /** Enable reflections simulation. This includes both real-time and baked simulation. */
    IPL_SIMULATIONFLAGS_REFLECTIONS = 1 << 1,

    /** Enable pathing simulation. */
    IPL_SIMULATIONFLAGS_PATHING = 1 << 2
} IPLSimulationFlags;

/** Flags indicating which types of direct simulation should be enabled for a given \c IPLSource. */
typedef enum {
    /** Enable distance attenuation calculations. */
    IPL_DIRECTSIMULATIONFLAGS_DISTANCEATTENUATION = 1 << 0,

    /** Enable air absorption calculations. */
    IPL_DIRECTSIMULATIONFLAGS_AIRABSORPTION = 1 << 1,

    /** Enable directivity calculations. */
    IPL_DIRECTSIMULATIONFLAGS_DIRECTIVITY = 1 << 2,

    /** Enable occlusion simulation. */
    IPL_DIRECTSIMULATIONFLAGS_OCCLUSION = 1 << 3,

    /** Enable transmission simulation. Requires occlusion to also be enabled. */
    IPL_DIRECTSIMULATIONFLAGS_TRANSMISSION = 1 << 4
} IPLDirectSimulationFlags;

/** The types of distance attenuation that can be used. */
typedef enum {
    /** The default distance attenuation model. This is an inverse distance falloff, with all sounds within 1 meter
        of the listener rendered without distance attenuation. */
    IPL_DISTANCEATTENUATIONTYPE_DEFAULT,

    /** An inverse distance falloff. You can configure the minimum distance, within which distance attenuation is not
        applied. */
    IPL_DISTANCEATTENUATIONTYPE_INVERSEDISTANCE,

    /** An arbitrary distance falloff function, defined by a callback function. */
    IPL_DISTANCEATTENUATIONTYPE_CALLBACK
} IPLDistanceAttenuationModelType;

/** The types of air absorption that can be used. */
typedef enum {
    /** The default air absorption model. This is an exponential falloff, with decay rates derived from physical
        properties of air. */
    IPL_AIRABSORPTIONTYPE_DEFAULT,

    /** An exponential falloff. You can configure the decay rates for each frequency band. */
    IPL_AIRABSORPTIONTYPE_EXPONENTIAL,

    /** An arbitrary air absorption model, defined by a callback function. */
    IPL_AIRABSORPTIONTYPE_CALLBACK
} IPLAirAbsorptionModelType;

/** The types of deviation model that can be used. */
typedef enum {
    /** The default deviation model. This is a physics-based model, based on the Uniform Theory of Diffraction,
     *  with various additional assumptions.
     */
    IPL_DEVIATIONTYPE_DEFAULT,

    /** An arbitrary deviation model, defined by a callback function. */
    IPL_DEVIATIONTYPE_CALLBACK,
} IPLDeviationModelType;

/** The different algorithms for simulating occlusion. */
typedef enum {
    /** Raycast occlusion. A single ray is traced from the listener to the source. If the ray hits a solid object
        before it reaches the source, the source is considered occluded. */
    IPL_OCCLUSIONTYPE_RAYCAST,

    /** A volumetric occlusion algorithm that can model partial occlusion. The source is modeled as a sphere with
        a configurable radius. Multiple points are sampled within the volume of this sphere. Rays are then traced
        from each sample point to both the source and the listener. A sample point is considered occluded if either
        of these two rays is occluded. The occlusion value for the source is calculated as the fraction of
        sample points that are unoccluded. This algorithm allows for smoother transitions in and out of occlusion. */
    IPL_OCCLUSIONTYPE_VOLUMETRIC
} IPLOcclusionType;

/** Callback for calculating how much attenuation should be applied to a sound based on its distance from the listener.

    \param  distance    The distance (in meters) between the source and the listener.
    \param  userData    Pointer to the arbitrary data specified in the \c IPLDistanceAttenuationModel.

    \return The distance attenuation to apply, between \c 0 and \c 1. \c 0 = the sound is not audible, \c 1 = the sound
            is as loud as it would be if it were emitted from the listener's position.
*/
typedef float (IPLCALL *IPLDistanceAttenuationCallback)(IPLfloat32 distance, void* userData);

/** Callback for calculating how much air absorption should be applied to a sound based on its distance from the
    listener.

    \param  distance    The distance (in meters) between the source and the listener.
    \param  band        Index of the frequency band for which to calculate air absorption. \c 0 = low frequencies,
                        \c 1 = middle frequencies, \c 2 = high frequencies.
    \param  userData    Pointer to the arbitrary data specified in the \c IPLAirAbsorptionModel.

    \return The air absorption to apply, between \c 0 and \c 1. \c 0 = sound in the frequency band \c band is
            not audible, \c 1 = sound in the frequency band \c band is not attenuated.
*/
typedef float (IPLCALL *IPLAirAbsorptionCallback)(IPLfloat32 distance, IPLint32 band, void* userData);

/** Callback for calculating how much to attenuate a sound based on its directivity pattern and orientation in
    world space.

    \param  direction   Unit vector (in world space) pointing forwards from the source. This is the direction
                        that the source is "pointing towards".
    \param  userData    Pointer to the arbitrary data specified in the \c IPLDirectivity.

    \return The directivity value to apply, between \c 0 and \c 1. \c 0 = the sound is not audible, \c 1 = the sound
            is as loud as it would be if it had a uniform (omnidirectional) directivity pattern.
*/
typedef float (IPLCALL *IPLDirectivityCallback)(IPLVector3 direction, void* userData);

/** Callback for calculating how much to attenuate sound in a given frequency band based on the angle of deviation
 *  when the sound path bends around a corner as it propagated from the source to the listener.
 * 
 *  \param[in]  angle       Angle (in radians) that the sound path deviates (bends) by.
 *  \param[in]  band        Index of the frequency band for which to calculate air absorption.
 *  \param[in]  userData    Pointer to the arbitrary data specified in the \c IPLDeviationModel.
 * 
 *  \return The frequency-dependent attenuation to apply, between \c 0 and \c 1. \c 0 = sound in the frequency
 *          band \c band is not audible; \c 1 = sound in the frequency band \c band is not attenuated.
 */
typedef float (IPLCALL *IPLDeviationCallback)(IPLfloat32 angle, IPLint32 band, void* userData);

/** A distance attenuation model that can be used for modeling attenuation of sound over distance. Can be used
    with both direct and indirect sound propagation. */
typedef struct {
    /** The type of distance attenuation model to use. */
    IPLDistanceAttenuationModelType type;

    /** When \c type is \c IPL_DISTANCEATTENUATIONTYPE_INVERSEDISTANCE, no distance attenuation is applied to
        any sound whose distance from the listener is less than this value. */
    IPLfloat32 minDistance;

    /** When \c type is \c IPL_DISTANCEATTENUATIONTYPE_CALLBACK, this function will be called whenever Steam
        Audio needs to evaluate distance attenuation. */
    IPLDistanceAttenuationCallback callback;

    /** Pointer to arbitrary data that will be provided to the \c callback function whenever it is called.
        May be \c NULL. */
    void* userData;

    /** Set to \c IPL_TRUE to indicate that the distance attenuation model defined by the \c callback function
        has changed since the last time simulation was run. For example, the callback may be evaluating a
        curve defined in a GUI. If the user is editing the curve in real-time, set this to \c IPL_TRUE whenever
        the curve changes, so Steam Audio can update simulation results to match. */
    IPLbool dirty;
} IPLDistanceAttenuationModel;

/** An air absorption model that can be used for modeling frequency-dependent attenuation of sound over
    distance. */
typedef struct {
    /** The type of air absorption model to use. */
    IPLAirAbsorptionModelType type;

    /** The exponential falloff coefficients to use when \c type is \c IPL_AIRABSORPTIONTYPE_EXPONENTIAL. */
    IPLfloat32 coefficients[IPL_NUM_BANDS];

    /** When \c type is \c IPL_AIRABSORPTIONTYPE_CALLBACK, this function will be called whenever Steam
        Audio needs to evaluate air absorption. */
    IPLAirAbsorptionCallback callback;

    /** Pointer to arbitrary data that will be provided to the \c callback function whenever it is called.
        May be \c NULL. */
    void* userData;

    /** Set to \c IPL_TRUE to indicate that the air absorption model defined by the \c callback function
        has changed since the last time simulation was run. For example, the callback may be evaluating a set of
        curves defined in a GUI. If the user is editing the curves in real-time, set this to \c IPL_TRUE whenever
        the curves change, so Steam Audio can update simulation results to match. */
    IPLbool dirty;
} IPLAirAbsorptionModel;

/** A directivity pattern that can be used to model changes in sound intensity as a function of the source's
    orientation. Can be used with both direct and indirect sound propagation.

    The default directivity model is a weighted dipole. This is a linear blend between an omnidirectional
    source (which emits sound with equal intensity in all directions), and a dipole oriented along the z-axis
    in the source's coordinate system (which focuses sound along the +z and -z axes). A callback function
    can be specified to implement any other arbitrary directivity pattern. */
typedef struct {
    /** How much of the dipole to blend into the directivity pattern. \c 0 = pure omnidirectional, \c 1 = pure
        dipole. \c 0.5f results in a cardioid directivity pattern. */
    IPLfloat32 dipoleWeight;

    /** How "sharp" the dipole is. Higher values result in sound being focused within a narrower range of
        directions. */
    IPLfloat32 dipolePower;

    /** If non \c NULL, this function will be called whenever Steam Audio needs to evaluate a directivity
        pattern. */
    IPLDirectivityCallback callback;

    /** Pointer to arbitrary data that will be provided to the \c callback function whenever it is called.
        May be \c NULL. */
    void* userData;
} IPLDirectivity;

/** A deviation model that can be used for modeling frequency-dependent attenuation of sound as it
 *  bends along the path from the source to the listener.
 */
typedef struct {
    /** The type of deviation model to use. */
    IPLDeviationModelType type;

    /** When \c type is \c IPL_DEVIATIONTYPE_CALLBACK, this function will be called whenever Steam Audio
     *  needs to evaluate deviation-based attenuation.
     */
    IPLDeviationCallback callback;

    /** Pointer to arbitrary data that will be provided to the \c callback function whenever it is called.
     *  May be \c NULL.
     */
    void* userData;
} IPLDeviationModel;

/** Settings used to create a simulator. */
typedef struct {
    /** The types of simulation that this simulator will be used for. */
    IPLSimulationFlags flags;

    /** The type of scene that will be used for simulations via \c iplSimulatorSetScene. The scene type
        cannot change during the lifetime of a simulator object. */
    IPLSceneType sceneType;

    /** The type of reflections effect that will be used to render the results of reflections simulation.
        The reflections effect type cannot change during the lifetime of a simulator object. */
    IPLReflectionEffectType reflectionType;

    /** The maximum number of point samples to consider when calculating occlusion using the
        volumetric occlusion algorithm. Different sources can use different numbers of samples, and the
        number of samples can change between simulation runs, but this is the maximum value. Increasing
        this value results in smoother occlusion transitions, at the cost of increased CPU usage. */
    IPLint32 maxNumOcclusionSamples;

    /** The maximum number of rays to trace from the listener when simulating reflections. You can use
        different numbers of rays between simulation runs, but this is the maximum value. Increasing
        this value results in more accurate reflections, at the cost of increased CPU usage. */
    IPLint32 maxNumRays;

    /** The number of directions to sample when generating diffusely reflected rays. Increasing this
        value may increase the accuracy of diffuse reflections. */
    IPLint32 numDiffuseSamples;

    /** The maximum length (in seconds) of impulse responses generated by reflection simulations. You
        can change this value betweeen simulation runs, but this is the maximum value. Increasing this
        value results in longer, more accurate reverb tails, at the cost of increased CPU and memory usage. */
    IPLfloat32 maxDuration;

    /** The maximum Ambisonic order of impulse responses generated by reflection simulations. You can
        change this value between simulation runs, but this is the maximum value. Increasing this
        value results in more accurate directional variations in the impulse responses, at the cost of
        increased CPU and memory usage. */
    IPLint32 maxOrder;

    /** The maximum number of sources for which reflection simulations will be run at any given time. */
    IPLint32 maxNumSources;

    /** The number of threads used for real-time reflection simulations. */
    IPLint32 numThreads;

    /** If using custom ray tracer callbacks, this the number of rays that will be passed to the callbacks
        every time rays need to be traced. */
    IPLint32 rayBatchSize;

    /** The number of point samples to consider when calculating probe-to-probe visibility for pathing
        simulations. Baked paths may end up being occluded by dynamic objects, in which case you can configure
        the simulator to look for alternate paths in real time. This process will involve checking visibility
        between probes. */
    IPLint32 numVisSamples;

    /** The sampling rate (in Hz) used for audio processing. */
    IPLint32 samplingRate;

    /** The size (in samples) of the audio buffers used for audio processing. */
    IPLint32 frameSize;

    /** The OpenCL device being used. Only necessary if \sceneType is \c IPL_SCENETYPE_RADEONRAYS, or \c reflectionType
        is \c IPL_REFLECTIONEFFECTTYPE_TAN. */
    IPLOpenCLDevice openCLDevice;

    /** The Radeon Rays device being used. Only necessary if \sceneType is \c IPL_SCENETYPE_RADEONRAYS. */
    IPLRadeonRaysDevice radeonRaysDevice;

    /** The TrueAudio Next device being used. Only necessary if \c reflectionType is \c IPL_REFLECTIONEFFECTTYPE_TAN. */
    IPLTrueAudioNextDevice tanDevice;
} IPLSimulationSettings;

/** Settings used to create a source. */
typedef struct {
    /** The types of simulation that may be run for this source. */
    IPLSimulationFlags flags;
} IPLSourceSettings;

/** Simulation parameters for a source. */
typedef struct {
    /** The types of simulation to run for this source. */
    IPLSimulationFlags flags;

    /** The types of direct simulation to run for this source. */
    IPLDirectSimulationFlags directFlags;

    /** The position and orientation of this source. */
    IPLCoordinateSpace3 source;

    /** The distance attenuation model to use for this source. */
    IPLDistanceAttenuationModel distanceAttenuationModel;

    /** The air absorption model to use for this source. */
    IPLAirAbsorptionModel airAbsorptionModel;

    /** The directivity pattern to use for this source. */
    IPLDirectivity directivity;

    /** The occlusion algorithm to use for this source. */
    IPLOcclusionType occlusionType;

    /** If using volumetric occlusion, the source is modeled as a sphere with this radius. */
    IPLfloat32 occlusionRadius;

    /** If using volumetric occlusion, this is the number of point samples to consider when
        tracing rays. This value can change between simulation runs. */
    IPLint32 numOcclusionSamples;

    /** If using parametric or hybrid reverb for rendering reflections, the reverb decay times
        for each frequency band are scaled by these values. Set to \c {1.0f, 1.0f, 1.0f} to use
        the simulated values without modification. */
    IPLfloat32 reverbScale[IPL_NUM_BANDS];

    /** If using hybrid reverb for rendering reflections, this is the length (in seconds) of
        impulse response to use for convolution reverb. The rest of the impulse response will
        be used for parametric reverb estimation only. Increasing this value results in more
        accurate reflections, at the cost of increased CPU usage. */
    IPLfloat32 hybridReverbTransitionTime;

    /** If using hybrid reverb for rendering reflections, this is the amount of overlap between
        the convolution and parametric parts. To ensure smooth transitions from the early
        convolution part to the late parametric part, the two are cross-faded towards the end of
        the convolution part. For example, if \c hybridReverbTransitionTime is \c 1.0f, and
        \c hybridReverbOverlapPercent is \c 0.25f, then the first 0.75 seconds are pure convolution,
        the next 0.25 seconds are a blend between convolution and parametric, and the portion of
        the tail beyond 1.0 second is pure parametric. */
    IPLfloat32 hybridReverbOverlapPercent;

    /** If \c IPL_TRUE, this source will used baked data for reflections simulation. */
    IPLbool baked;

    /** The identifier used to specify which layer of baked data to use for simulating reflections
        for this source. */
    IPLBakedDataIdentifier bakedDataIdentifier;

    /** The probe batch within which to find paths from this source to the listener. */
    IPLProbeBatch pathingProbes;

    /** When testing for mutual visibility between a pair of probes, each probe is treated as a sphere of
        this radius (in meters), and point samples are generated within this sphere. */
    IPLfloat32 visRadius;

    /** When tracing rays to test for mutual visibility between a pair of probes, the fraction of rays that
        are unoccluded must be greater than this threshold for the pair of probes to be considered
        mutually visible. */
    IPLfloat32 visThreshold;

    /** If the distance between two probes is greater than this value, the probes are not considered mutually
        visible. Increasing this value can result in simpler paths, at the cost of increased CPU usage. */
    IPLfloat32 visRange;

    /** If simulating pathing, this is the Ambisonic order used for representing path directionality. Higher
        values result in more precise spatialization of paths, at the cost of increased CPU usage. */
    IPLint32 pathingOrder;

    /** If \c IPL_TRUE, baked paths are tested for visibility. This is useful if your scene has dynamic
        objects that might occlude baked paths. */
    IPLbool enableValidation;

    /** If \c IPL_TRUE, and \c enableValidation is \c IPL_TRUE, then if a baked path is occluded by dynamic
        geometry, path finding is re-run in real-time to find alternate paths that take into account the
        dynamic geometry. */
    IPLbool findAlternatePaths;

    /** If simulating transmission, this is the maximum number of surfaces, starting from the closest
        surface to the listener, whose transmission coefficients will be considered when calculating
        the total amount of sound transmitted. Increasing this value will result in more accurate
        results when multiple surfaces lie between the source and the listener, at the cost of
        increased CPU usage. */
    IPLint32 numTransmissionRays;

    /** The deviation model to use for this source. Only used when simulating pathing. */
    IPLDeviationModel* deviationModel;
} IPLSimulationInputs;

/** Callback for visualizing valid path segments during call to \c iplSimulatorRunPathing.

    You can use this to provide the user with visual feedback, like drawing each segment of a path.

    \param  from        Position of starting probe.
    \param  to          Position of ending probe.
    \param  occluded    Occlusion status of ray segment between \c from to \c to.
    \param  userData    Pointer to arbitrary user-specified data provided when calling the function that will
                        call this callback.
*/
typedef void (IPLCALL* IPLPathingVisualizationCallback)(IPLVector3 from, IPLVector3 to, IPLbool occluded, void* userData);

/** Simulation parameters that are not specific to any source. */
typedef struct {
    /** The position and orientation of the listener. */
    IPLCoordinateSpace3 listener;

    /** The number of rays to trace from the listener. Increasing this value results in more accurate
        reflections, at the cost of increased CPU usage. */
    IPLint32 numRays;

    /** The number of times each ray traced from the listener is reflected when it encounters a solid
        object. Increasing this value results in longer, more accurate reverb tails, at the cost of
        increased CPU usage during simulation. */
    IPLint32 numBounces;

    /** The duration (in seconds) of the impulse responses generated when simulating reflections.
        Increasing this value results in longer, more accurate reverb tails, at the cost of increased
        CPU usage during audio processing. */
    IPLfloat32 duration;

    /** The Ambisonic order of the impulse responses generated when simulating reflections. Increasing
        this value results in more accurate directional variation of reflected sound, at the cost
        of increased CPU usage during audio processing. */
    IPLint32 order;

    /** When calculating how much sound energy reaches a surface directly from a source, any source that is
        closer than \c irradianceMinDistance to the surface is assumed to be at a distance of
        \c irradianceMinDistance, for the purposes of energy calculations. */
    IPLfloat32 irradianceMinDistance;

    /** Callback for visualizing valid path segments during call to \c iplSimulatorRunPathing.*/
    IPLPathingVisualizationCallback pathingVisCallback;

    /** Pointer to arbitrary user-specified data provided when calling the function that will
        call this callback.*/
    void* pathingUserData;
} IPLSimulationSharedInputs;

/** Simulation results for a source. */
typedef struct {
    /** Direct path simulation results. */
    IPLDirectEffectParams direct;

    /** Reflection simulation results. */
    IPLReflectionEffectParams reflections;

    /** Pathing simulation results. */
    IPLPathEffectParams pathing;
} IPLSimulationOutputs;

/** Creates a simulator.

    \param  context     The context used to initialize Steam Audio.
    \param  settings    The settings to use when creating the simulator.
    \param  simulator   [out] The created simulator.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplSimulatorCreate(IPLContext context, IPLSimulationSettings* settings, IPLSimulator* simulator);

/** Retains an additional reference to a simulator.

    \param  simulator   The simulator to retain a reference to.

    \return The additional reference to the simulator.
*/
IPLAPI IPLSimulator IPLCALL iplSimulatorRetain(IPLSimulator simulator);

/** Releases a reference to a simulator.

    \param  simulator   The simulator to release a reference to.
*/
IPLAPI void IPLCALL iplSimulatorRelease(IPLSimulator* simulator);

/** Specifies the scene within which all subsequent simulations should be run.

    Call \c iplSimulatorCommit after calling this function for the changes to take effect.

    This function cannot be called while any simulation is running.

    \param  simulator   The simulator being used.
    \param  scene       The scene to use for simulations.
*/
IPLAPI void IPLCALL iplSimulatorSetScene(IPLSimulator simulator, IPLScene scene);

/** Adds a probe batch for use in subsequent simulations. Sources that require baked data can then use the
    data contained in the specified probe batch.

    Call \c iplSimulatorCommit after calling this function for the changes to take effect.

    This function cannot be called while any simulation is running.

    \param  probeBatch  The probe batch to add.
    \param  simulator   The simulator being used.
*/
IPLAPI void IPLCALL iplSimulatorAddProbeBatch(IPLSimulator simulator, IPLProbeBatch probeBatch);

/** Removed a probe batch from use in subsequent simulations. Sources that require baked data will then stop using the
    data contained in the specified probe batch.

    Call \c iplSimulatorCommit after calling this function for the changes to take effect.

    This function cannot be called while any simulation is running.

    \param  probeBatch  The probe batch to remove.
    \param  simulator   The simulator being used.
*/
IPLAPI void IPLCALL iplSimulatorRemoveProbeBatch(IPLSimulator simulator, IPLProbeBatch probeBatch);

/** Specifies simulation parameters that are not associated with any particular source.

    \param  simulator       The simulator being used.
    \param  flags           The types of simulation for which to specify shared inputs. If, for example, direct
                            and reflections simulations are being run on separate threads, you can call this
                            function on the direct simulation thread with \c IPL_SIMULATIONFLAGS_DIRECT, and on the
                            reflections simulation thread with \c IPL_SIMULATIONFLAGS_REFLECTIONS, without requiring
                            any synchronization between the calls.
    \param  sharedInputs    The shared input parameters to set.
*/
IPLAPI void IPLCALL iplSimulatorSetSharedInputs(IPLSimulator simulator, IPLSimulationFlags flags, IPLSimulationSharedInputs* sharedInputs);

/** Commits changes to the scene or probe batches used for simulation.

    Call this function after calling \c iplSimulatorSetScene, \c iplSimulatorAddProbeBatch, or
    \c iplSimulatorRemoveProbeBatch for the changes to take effect.

    This function cannot be called while any simulation is running.

    \param  simulator   The simulator being used.
*/
IPLAPI void IPLCALL iplSimulatorCommit(IPLSimulator simulator);

/** Runs a direct simulation for all sources added to the simulator. This may include distance attenuation,
    air absorption, directivity, occlusion, and transmission.

    This function should not be called from the audio processing thread if occlusion and/or transmission
    are enabled.

    \param  simulator   The simulator being used.
*/
IPLAPI void IPLCALL iplSimulatorRunDirect(IPLSimulator simulator);

/** Runs a reflections simulation for all sources added to the simulator.

    This function can be CPU intensive, and should be called from a separate thread in order to not
    block either the audio processing thread or the game's main update thread.

    \param  simulator   The simulator being used.
*/
IPLAPI void IPLCALL iplSimulatorRunReflections(IPLSimulator simulator);

/** Runs a pathing simulation for all sources added to the simulator.

    This function can be CPU intensive, and should be called from a separate thread in order to not
    block either the audio processing thread or the game's main update thread.

    \param  simulator       The simulator being used.
*/
IPLAPI void IPLCALL iplSimulatorRunPathing(IPLSimulator simulator);


/** Creates a simulation source.

    \param  simulator   The simulator with which this source will be used.
    \param  settings    The settings to use for creating the source.
    \param  source      [out] The created source.

    \return Status code indicating whether or not the operation succeeded.
*/
IPLAPI IPLerror IPLCALL iplSourceCreate(IPLSimulator simulator, IPLSourceSettings* settings, IPLSource* source);

/** Retains an additional reference to a source.

    \param  source  The source to retain a reference to.

    \return The additional reference to the source.
*/
IPLAPI IPLSource IPLCALL iplSourceRetain(IPLSource source);

/** Releases a reference to a source.

    \param  source  The source to release a reference to.
*/
IPLAPI void IPLCALL iplSourceRelease(IPLSource* source);

/** Adds a source to the set of sources processed by a simulator in subsequent simulations.

    Call \c iplSimulatorCommit after calling this function for the changes to take effect.

    \param  simulator   The simulator being used.
    \param  source      The source to add.
*/
IPLAPI void IPLCALL iplSourceAdd(IPLSource source, IPLSimulator simulator);

/** Removes a source from the set of sources processed by a simulator in subsequent simulations.

    Call \c iplSimulatorCommit after calling this function for the changes to take effect.

    \param  simulator   The simulator being used.
    \param  source      The source to remove.
*/
IPLAPI void IPLCALL iplSourceRemove(IPLSource source, IPLSimulator simulator);

/** Specifies simulation parameters for a source.

    \param  source  The source to specify parameters for.
    \param  flags   The types of simulation for which to specify inputs. If, for example, direct
                    and reflections simulations are being run on separate threads, you can call this
                    function on the direct simulation thread with \c IPL_SIMULATIONFLAGS_DIRECT, and on the
                    reflections simulation thread with \c IPL_SIMULATIONFLAGS_REFLECTIONS, without requiring
                    any synchronization between the calls.
    \param  inputs  The input parameters to set.
*/
IPLAPI void IPLCALL iplSourceSetInputs(IPLSource source, IPLSimulationFlags flags, IPLSimulationInputs* inputs);

/** Retrieves simulation results for a source.

    \param  source  The source to retrieve results for.
    \param  flags   The types of simulation for which to retrieve results.
    \param  outputs [out] The simulation results.
*/
IPLAPI void IPLCALL iplSourceGetOutputs(IPLSource source, IPLSimulationFlags flags, IPLSimulationOutputs* outputs);

/** \} */

/*********************************************************************************************************************/

/** \defgroup advancedsim Advanced Simulation API
 *  \{
 */

/** Calculates the distance attenuation between a source and a listener.

    \param  context     The context used to initialize Steam Audio.
    \param  source      The position of the source.
    \param  listener    The position of the listener.
    \param  model       The distance attenuation model to use.

    \return The distance attenuation to apply, between \c 0 and \c 1.
*/
IPLAPI IPLfloat32 IPLCALL iplDistanceAttenuationCalculate(IPLContext context, IPLVector3 source, IPLVector3 listener, IPLDistanceAttenuationModel* model);

/** Calculates the air absorption coefficients between a source and a listener.

    \param  context         The context used to initialize Steam Audio.
    \param  source          The position of the source.
    \param  listener        The position of the listener.
    \param  model           The air absorption model to use.
    \param  airAbsorption   [out] The 3-band air absorption coefficients, each between \c 0 and \c 1.
*/
IPLAPI void IPLCALL iplAirAbsorptionCalculate(IPLContext context, IPLVector3 source, IPLVector3 listener, IPLAirAbsorptionModel* model, IPLfloat32* airAbsorption);

/** Calculates the attenuation of a source due to its directivity pattern and orientation relative to a listener.

    \param  context     The context used to initialize Steam Audio.
    \param  source      The position and orientation of the source.
    \param  listener    The position of the listener.
    \param  model       The directivity pattern to use.

    \return The directivity value to apply, between \c 0 and \c 1.
*/
IPLAPI IPLfloat32 IPLCALL iplDirectivityCalculate(IPLContext context, IPLCoordinateSpace3 source, IPLVector3 listener, IPLDirectivity* model);

/** \} */

#ifdef __cplusplus
}
#endif

#endif
