/******************************************************************************/
/*! \file  Q3DAudioClient.h */
/*
* Copyright (c) 2017-2017 Qualcomm Technologies, Inc.
* All Rights Reserved
* Confidential and Proprietary - Qualcomm Technologies, Inc.
*
******************************************************************************/

#ifdef __ANDROID__

#pragma once
#ifndef Q3D_AUDIO_CLIENT_H
#define Q3D_AUDIO_CLIENT_H

#include<assert.h>
#include<dlfcn.h>
#include"q3d_audio.h"//include core interface to avoid duplicating enumeration values, etc


#ifdef __cplusplus
extern "C" 
{
#endif//#ifdef __cplusplus

    //Application Progamming Interface starts here -- these are the functions to call to make use of q3d_audio
    ///@TODO: fill out for convenience
    //Application Progamming Interface ends here -- below are details users usually don't need to worry about

    typedef q3d_audio_context* (*pfn_q3d_audio_create_t) (JNIEnv* env, jobject android_context, jobject class_loader, q3d_audio_rendering_mode rendering_mode);
    typedef q3d_audio_version(*pfn_q3d_audio_get_version_t) ();
    typedef const char* (*pfn_q3d_audio_get_version_string_t) ();
    typedef q3d_audio_result(*pfn_q3d_audio_destroy_t) (q3d_audio_context** api);
    typedef q3d_audio_result(*pfn_q3d_audio_resume_t) (q3d_audio_context* api);
    typedef q3d_audio_result(*pfn_q3d_audio_pause_t) (q3d_audio_context* api);
    typedef q3d_audio_result(*pfn_q3d_audio_update_t) (q3d_audio_context* api);
    typedef q3d_audio_result(*pfn_q3d_audio_get_rendering_delay_t) (q3d_audio_context* api, uint32_t* period_ms);
    typedef bool(*pfn_q3d_audio_preload_soundfile_t) (q3d_audio_context* api, const char* filename);
    typedef q3d_audio_result(*pfn_q3d_audio_unload_soundfile_t) (q3d_audio_context* api, const char* filename);
    typedef q3d_audio_sound_id(*pfn_q3d_audio_create_sound_object_t) (q3d_audio_context* api, const char* filename);
    typedef q3d_audio_sound_id(*pfn_q3d_audio_create_soundfield_t) (q3d_audio_context* api, const char* filename);
    typedef q3d_audio_sound_id(*pfn_q3d_audio_create_sound_channel_t) (q3d_audio_context* api, const char* filename);
    typedef q3d_audio_result(*pfn_q3d_audio_play_sound_t) (q3d_audio_context* api, q3d_audio_sound_id sound_id, bool looping_enabled);
    typedef q3d_audio_result(*pfn_q3d_audio_pause_sound_t) (q3d_audio_context* api, q3d_audio_sound_id sound_id);
    typedef q3d_audio_result(*pfn_q3d_audio_resume_sound_t) (q3d_audio_context* api, q3d_audio_sound_id sound_id);
    typedef q3d_audio_result(*pfn_q3d_audio_stop_sound_t) (q3d_audio_context* api, q3d_audio_sound_id sound_id);
    typedef q3d_audio_result(*pfn_q3d_audio_set_sound_volume_t) (q3d_audio_context* api, q3d_audio_sound_id sound_id, float volume);
    typedef q3d_audio_result(*pfn_q3d_audio_get_sound_volume_t) (q3d_audio_context* api, q3d_audio_sound_id sound_id, float& volume);
    typedef bool(*pfn_q3d_audio_is_sound_playing_t) (const q3d_audio_context* api, q3d_audio_sound_id sound_id);
    typedef q3d_audio_result(*pfn_q3d_audio_set_sound_object_position_t) (q3d_audio_context* api, q3d_audio_sound_id sound_object_id, float x, float y, float z);
    typedef q3d_audio_result(*pfn_q3d_audio_set_sound_object_position_in_vec3f_t) (q3d_audio_context* api, q3d_audio_sound_id sound_object_id, const q3d_audio_vec3f& position);
    typedef q3d_audio_result(*pfn_q3d_audio_get_sound_object_position_in_vec3f_t) (q3d_audio_context* api, q3d_audio_sound_id sound_object_id, q3d_audio_vec3f& position);
    typedef q3d_audio_result(*pfn_q3d_audio_set_sound_object_distance_rolloff_model_t) (q3d_audio_context* api, q3d_audio_sound_id sound_object_id, int32_t rolloff_model, float min_distance, float max_distance);
    typedef q3d_audio_result(*pfn_q3d_audio_set_soundfield_rotation_t) (q3d_audio_context* api, q3d_audio_sound_id soundfield_id, const q3d_audio_quatf& soundfield_rotation);
    typedef q3d_audio_result(*pfn_q3d_audio_get_soundfield_rotation_t) (q3d_audio_context* api, q3d_audio_sound_id soundfield_id, q3d_audio_quatf& soundfield_rotation);
    typedef q3d_audio_result(*pfn_q3d_audio_set_head_position_in_vec3f_t) (q3d_audio_context* api, const q3d_audio_vec3f& position);
    typedef q3d_audio_result(*pfn_q3d_audio_set_head_position_t) (q3d_audio_context* api, float x, float y, float z);
    typedef q3d_audio_result(*pfn_q3d_audio_get_head_position_in_vec3f_t) (q3d_audio_context* api, q3d_audio_vec3f& position);
    typedef q3d_audio_result(*pfn_q3d_audio_set_head_rotation_in_quatf_t) (q3d_audio_context* api, const q3d_audio_quatf& rotation);
    typedef q3d_audio_result(*pfn_q3d_audio_set_head_rotation_t) (q3d_audio_context* api, float x, float y, float z, float w);
    typedef q3d_audio_result(*pfn_q3d_audio_get_head_rotation_in_quatf_t) (q3d_audio_context* api, q3d_audio_quatf& rotation);
    typedef q3d_audio_result(*pfn_q3d_audio_enable_room_t) (q3d_audio_context* api, bool enable);
    typedef q3d_audio_result(*pfn_q3d_audio_set_room_properties_t) (q3d_audio_context* api, float size_x, float size_y, float size_z, q3d_audio_material_type wall_material, q3d_audio_material_type ceiling_material, q3d_audio_material_type floor_material);
    typedef q3d_audio_result(*pfn_q3d_audio_set_room_reverb_adjustments_t) (q3d_audio_context* api, float gain, float time_adjust, float brightness_adjust);

    typedef struct vr_audio_fp_t
    {
        pfn_q3d_audio_create_t q3d_audio_create;
        pfn_q3d_audio_get_version_t q3d_audio_get_version;
        pfn_q3d_audio_get_version_string_t q3d_audio_get_version_string;
        pfn_q3d_audio_destroy_t q3d_audio_destroy;
        pfn_q3d_audio_resume_t q3d_audio_resume;
        pfn_q3d_audio_pause_t q3d_audio_pause;
        pfn_q3d_audio_update_t q3d_audio_update;
        pfn_q3d_audio_get_rendering_delay_t q3d_audio_get_rendering_delay;
        pfn_q3d_audio_preload_soundfile_t q3d_audio_preload_soundfile;
        pfn_q3d_audio_unload_soundfile_t q3d_audio_unload_soundfile;
        pfn_q3d_audio_create_sound_object_t q3d_audio_create_sound_object;
        pfn_q3d_audio_create_soundfield_t q3d_audio_create_soundfield;
        pfn_q3d_audio_create_sound_channel_t q3d_audio_create_sound_channel;
        pfn_q3d_audio_play_sound_t q3d_audio_play_sound;
        pfn_q3d_audio_pause_sound_t q3d_audio_pause_sound;
        pfn_q3d_audio_resume_sound_t q3d_audio_resume_sound;
        pfn_q3d_audio_stop_sound_t q3d_audio_stop_sound;
        pfn_q3d_audio_set_sound_volume_t q3d_audio_set_sound_volume;
        pfn_q3d_audio_get_sound_volume_t q3d_audio_get_sound_volume;
        pfn_q3d_audio_is_sound_playing_t q3d_audio_is_sound_playing;
        pfn_q3d_audio_set_sound_object_position_t q3d_audio_set_sound_object_position;
        pfn_q3d_audio_set_sound_object_position_in_vec3f_t q3d_audio_set_sound_object_position_in_vec3f;
        pfn_q3d_audio_get_sound_object_position_in_vec3f_t q3d_audio_get_sound_object_position_in_vec3f;
        pfn_q3d_audio_set_sound_object_distance_rolloff_model_t q3d_audio_set_sound_object_distance_rolloff_model;
        pfn_q3d_audio_set_soundfield_rotation_t q3d_audio_set_soundfield_rotation;
        pfn_q3d_audio_get_soundfield_rotation_t q3d_audio_get_soundfield_rotation;
        pfn_q3d_audio_set_head_position_in_vec3f_t q3d_audio_set_head_position_in_vec3f;
        pfn_q3d_audio_set_head_position_t q3d_audio_set_head_position;
        pfn_q3d_audio_set_head_rotation_in_quatf_t q3d_audio_set_head_rotation_in_quatf;
        pfn_q3d_audio_get_head_position_in_vec3f_t q3d_audio_get_head_position_in_vec3f;
        pfn_q3d_audio_set_head_rotation_t q3d_audio_set_head_rotation;
        pfn_q3d_audio_get_head_rotation_in_quatf_t q3d_audio_get_head_rotation_in_quatf;
        pfn_q3d_audio_enable_room_t q3d_audio_enable_room;
        pfn_q3d_audio_set_room_properties_t q3d_audio_set_room_properties;
        pfn_q3d_audio_set_room_reverb_adjustments_t q3d_audio_set_room_reverb_adjustments;
        /*extern q3d_audio_result q3d_audio_set_head_pose(q3d_audio_context* client,
        const q3d_audio_mat4f& head_pose_matrix);*/
    } vr_audio_fp;

    typedef struct q3d_audio_client_t
    {
        vr_audio_fp functionPtrs;
        q3d_audio_context* context;
    } q3d_audio_client;

    static q3d_audio_result FunctionPointersInitialize(vr_audio_fp*const vr_audio)
    {
        assert(vr_audio);

        void *vr_audio_shim = dlopen("/vendor/lib/libvraudio_client.so", RTLD_NOW | RTLD_LOCAL);
        if (!vr_audio_shim) 
        {
            fprintf(stderr, "FATAL ERROR: Could not load libvraudio_client lib from /system/vendor/lib/ folder \n");
            return Q3D_AUDIO_ERROR_NOT_SUPPORTED;
        }
        vr_audio->q3d_audio_create = (pfn_q3d_audio_create_t)dlsym(vr_audio_shim, "q3d_audio_create");
        vr_audio->q3d_audio_get_version = (pfn_q3d_audio_get_version_t)dlsym(vr_audio_shim, "q3d_audio_get_version");
        vr_audio->q3d_audio_get_version_string = (pfn_q3d_audio_get_version_string_t)dlsym(vr_audio_shim, "q3d_audio_get_version_string");
        vr_audio->q3d_audio_destroy = (pfn_q3d_audio_destroy_t)dlsym(vr_audio_shim, "q3d_audio_destroy");
        vr_audio->q3d_audio_resume = (pfn_q3d_audio_resume_t)dlsym(vr_audio_shim, "q3d_audio_resume");
        vr_audio->q3d_audio_pause = (pfn_q3d_audio_pause_t)dlsym(vr_audio_shim, "q3d_audio_pause");
        vr_audio->q3d_audio_update = (pfn_q3d_audio_update_t)dlsym(vr_audio_shim, "q3d_audio_update");
        vr_audio->q3d_audio_get_rendering_delay = (pfn_q3d_audio_get_rendering_delay_t)dlsym(vr_audio_shim, "q3d_audio_get_rendering_delay");
        vr_audio->q3d_audio_preload_soundfile = (pfn_q3d_audio_preload_soundfile_t)dlsym(vr_audio_shim, "q3d_audio_preload_soundfile");
        vr_audio->q3d_audio_unload_soundfile = (pfn_q3d_audio_unload_soundfile_t)dlsym(vr_audio_shim, "q3d_audio_unload_soundfile");

        vr_audio->q3d_audio_create_sound_object = (pfn_q3d_audio_create_sound_object_t)dlsym(vr_audio_shim, "q3d_audio_create_sound_object");
        vr_audio->q3d_audio_create_soundfield = (pfn_q3d_audio_create_soundfield_t)dlsym(vr_audio_shim, "q3d_audio_create_soundfield");
        vr_audio->q3d_audio_create_sound_channel = (pfn_q3d_audio_create_sound_channel_t)dlsym(vr_audio_shim, "q3d_audio_create_sound_channel");
        vr_audio->q3d_audio_play_sound = (pfn_q3d_audio_play_sound_t)dlsym(vr_audio_shim, "q3d_audio_play_sound");
        vr_audio->q3d_audio_pause_sound = (pfn_q3d_audio_pause_sound_t)dlsym(vr_audio_shim, "q3d_audio_pause_sound");
        vr_audio->q3d_audio_resume_sound = (pfn_q3d_audio_resume_sound_t)dlsym(vr_audio_shim, "q3d_audio_resume_sound");
        vr_audio->q3d_audio_stop_sound = (pfn_q3d_audio_stop_sound_t)dlsym(vr_audio_shim, "q3d_audio_stop_sound");
        vr_audio->q3d_audio_set_sound_volume = (pfn_q3d_audio_set_sound_volume_t)dlsym(vr_audio_shim, "q3d_audio_set_sound_volume");
        vr_audio->q3d_audio_get_sound_volume = (pfn_q3d_audio_get_sound_volume_t)dlsym(vr_audio_shim, "q3d_audio_get_sound_volume");
        vr_audio->q3d_audio_is_sound_playing = (pfn_q3d_audio_is_sound_playing_t)dlsym(vr_audio_shim, "q3d_audio_is_sound_playing");

        vr_audio->q3d_audio_set_sound_object_position
            = (pfn_q3d_audio_set_sound_object_position_t)dlsym(vr_audio_shim, "q3d_audio_set_sound_object_position");

        vr_audio->q3d_audio_set_sound_object_position_in_vec3f
            = (pfn_q3d_audio_set_sound_object_position_in_vec3f_t)dlsym(vr_audio_shim, "q3d_audio_set_sound_object_position_in_vec3f");

        vr_audio->q3d_audio_get_sound_object_position_in_vec3f
            = (pfn_q3d_audio_get_sound_object_position_in_vec3f_t)dlsym(vr_audio_shim, "q3d_audio_get_sound_object_position_in_vec3f");

        vr_audio->q3d_audio_set_sound_object_distance_rolloff_model
            = (pfn_q3d_audio_set_sound_object_distance_rolloff_model_t)dlsym(vr_audio_shim, "q3d_audio_set_sound_object_distance_rolloff_model");

        vr_audio->q3d_audio_set_soundfield_rotation
            = (pfn_q3d_audio_set_soundfield_rotation_t)dlsym(vr_audio_shim, "q3d_audio_set_soundfield_rotation");

        vr_audio->q3d_audio_get_soundfield_rotation
            = (pfn_q3d_audio_get_soundfield_rotation_t)dlsym(vr_audio_shim, "q3d_audio_get_soundfield_rotation");

        vr_audio->q3d_audio_set_head_position_in_vec3f
            = (pfn_q3d_audio_set_head_position_in_vec3f_t)dlsym(vr_audio_shim, "q3d_audio_set_head_position_in_vec3f");

        vr_audio->q3d_audio_set_head_position
            = (pfn_q3d_audio_set_head_position_t)dlsym(vr_audio_shim, "q3d_audio_set_head_position");

        vr_audio->q3d_audio_set_head_rotation_in_quatf
            = (pfn_q3d_audio_set_head_rotation_in_quatf_t)dlsym(vr_audio_shim, "q3d_audio_set_head_rotation_in_quatf");

        vr_audio->q3d_audio_get_head_position_in_vec3f
            = (pfn_q3d_audio_get_head_position_in_vec3f_t)dlsym(vr_audio_shim, "q3d_audio_get_head_position_in_vec3f");


        vr_audio->q3d_audio_set_head_rotation
            = (pfn_q3d_audio_set_head_rotation_t)dlsym(vr_audio_shim, "q3d_audio_set_head_rotation");

        vr_audio->q3d_audio_get_head_rotation_in_quatf
            = (pfn_q3d_audio_get_head_rotation_in_quatf_t)dlsym(vr_audio_shim, "q3d_audio_get_head_rotation_in_quatf");

        vr_audio->q3d_audio_enable_room
            = (pfn_q3d_audio_enable_room_t)dlsym(vr_audio_shim, "q3d_audio_enable_room");

        vr_audio->q3d_audio_set_room_properties
            = (pfn_q3d_audio_set_room_properties_t)dlsym(vr_audio_shim, "q3d_audio_set_room_properties");

        vr_audio->q3d_audio_set_room_reverb_adjustments
            = (pfn_q3d_audio_set_room_reverb_adjustments_t)dlsym(vr_audio_shim, "q3d_audio_set_room_reverb_adjustments");

        return Q3D_AUDIO_SUCCESS;
    }

///reduce code duplication in querying for a function pointer, and handling its nonexistence
#define Q3D_AUDIO_FUNC_PTR_HELPER(functionName, returnValOnNotSupported) assert(client); pfn_##functionName##_t macroDefinedFunctionPtr = client->functionPtrs.functionName; if(!macroDefinedFunctionPtr) {return returnValOnNotSupported;}

    ///@todo: assert_message on nonexistence of function
    static inline q3d_audio_result q3dclient_audio_create(
        q3d_audio_client** clientPtr,
        JNIEnv* env, 
        jobject android_context,
        jobject class_loader,
        q3d_audio_rendering_mode rendering_mode)
    {
        assert(clientPtr);
        assert(env);

        q3d_audio_client*& client = *clientPtr;
        client = (q3d_audio_client*)malloc(sizeof(q3d_audio_client));
        if (!client)
        {
            return Q3D_AUDIO_ERROR_NOMEMORY;
        }
        FunctionPointersInitialize(&client->functionPtrs);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_create, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        client->context = macroDefinedFunctionPtr(env, android_context,class_loader, rendering_mode);
        return client->context != 0 ? Q3D_AUDIO_SUCCESS : Q3D_AUDIO_ERROR_UNKNOWN;
    }

    static inline q3d_audio_version q3dclient_audio_get_version(q3d_audio_client* client)
    {
        assert(client);

        q3d_audio_version notSupportedVersion;
        notSupportedVersion.major = notSupportedVersion.minor = 0;
        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_version, notSupportedVersion);
        return macroDefinedFunctionPtr();
    }

    static inline const char* q3dclient_audio_get_version_string(q3d_audio_client* client)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_version_string, "[0.0]");
        return macroDefinedFunctionPtr();
    }

    static inline q3d_audio_result q3dclient_audio_destroy(q3d_audio_client** clientPtr)
    {
        assert(clientPtr);
        q3d_audio_client*& client = *clientPtr;
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_destroy, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        const q3d_audio_result ret = macroDefinedFunctionPtr(&client->context);
        if (ret == Q3D_AUDIO_SUCCESS)
        {
            free(client);
            client = 0;
        }
        return ret;
    }

    static inline q3d_audio_result q3dclient_audio_resume(q3d_audio_client* client)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_resume, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context);
    }

    static inline q3d_audio_result q3dclient_audio_pause(q3d_audio_client* client)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_pause, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context);
    }

    static inline q3d_audio_result q3dclient_audio_update(q3d_audio_client* client)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_update, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context);
    }

    static inline q3d_audio_result q3dclient_audio_get_rendering_delay(q3d_audio_client* client, uint32_t* period_ms)
    {
        assert(client);
        assert(period_ms);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_rendering_delay, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, period_ms);
    }

    static inline bool q3dclient_audio_preload_soundfile(q3d_audio_client* client, const char* filename)
    {
        assert(client);
        assert(filename && filename[0]);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_preload_soundfile, false);
        return macroDefinedFunctionPtr(client->context, filename);
    }

    static inline q3d_audio_result q3dclient_audio_unload_soundfile(q3d_audio_client* client, const char* filename)
    {
        assert(client);
        assert(filename && filename[0]);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_unload_soundfile, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, filename);
    }

    static inline q3d_audio_sound_id q3dclient_audio_create_sound_object(q3d_audio_client* client, const char* filename)
    {
        assert(client);
        assert(filename && filename[0]);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_create_sound_object, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, filename);
    }

    static inline q3d_audio_sound_id q3dclient_audio_create_soundfield(q3d_audio_client* client, const char* filename)
    {
        assert(client);
        assert(filename && filename[0]);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_create_soundfield, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, filename);
    }

    static inline q3d_audio_sound_id q3dclient_audio_create_sound_channel(q3d_audio_client* client,const char* filename)
    {
        assert(client);
        assert(filename && filename[0]);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_create_sound_channel, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, filename);
    }

    static inline q3d_audio_result q3dclient_audio_play_sound(q3d_audio_client* client,q3d_audio_sound_id sound_id,bool looping_enabled)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_play_sound, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_id, looping_enabled);
    }

    static inline q3d_audio_result q3dclient_audio_pause_sound(q3d_audio_client* client, q3d_audio_sound_id sound_id)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_pause_sound, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_id);
    }

    static inline q3d_audio_result q3dclient_audio_resume_sound(q3d_audio_client* client,q3d_audio_sound_id sound_id)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_resume_sound, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_id);
    }

    static inline q3d_audio_result q3dclient_audio_stop_sound(q3d_audio_client* client,q3d_audio_sound_id sound_id)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_stop_sound, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_id);
    }

    static inline q3d_audio_result q3dclient_audio_set_sound_volume(q3d_audio_client* client,q3d_audio_sound_id sound_id,float volume)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_sound_volume, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_id, volume);
    }

    static inline q3d_audio_result q3dclient_audio_get_sound_volume(q3d_audio_client* client,q3d_audio_sound_id sound_id, float& volume)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_sound_volume, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_id, volume);
    }

    static inline bool q3dclient_audio_is_sound_playing(const q3d_audio_client* client, q3d_audio_sound_id sound_id)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_is_sound_playing, false);
        return macroDefinedFunctionPtr(client->context, sound_id);
    }

    static inline q3d_audio_result q3dclient_audio_set_sound_object_position(
        q3d_audio_client* client,
        q3d_audio_sound_id sound_object_id,
        float x, float y, float z) 
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_sound_object_position, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_object_id, x, y, z);
    }

    static inline q3d_audio_result q3dclient_audio_set_sound_object_position_in_vec3f(
        q3d_audio_client* client,
        q3d_audio_sound_id sound_object_id,
        const q3d_audio_vec3f& position)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_sound_object_position_in_vec3f, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_object_id, position);
    }

    static inline q3d_audio_result q3dclient_audio_get_sound_object_position_in_vec3f(
        q3d_audio_client* client,
        q3d_audio_sound_id sound_object_id,
        q3d_audio_vec3f& position)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_sound_object_position_in_vec3f, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_object_id, position);
    }

    static inline q3d_audio_result q3dclient_audio_set_sound_object_distance_rolloff_model(
        q3d_audio_client* client, 
        q3d_audio_sound_id sound_object_id,
        int32_t rolloff_model, 
        float min_distance, 
        float max_distance)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_sound_object_distance_rolloff_model, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, sound_object_id, rolloff_model, min_distance, max_distance);
    }

    static inline q3d_audio_result q3dclient_audio_set_soundfield_rotation(
        q3d_audio_client* client,
        q3d_audio_sound_id soundfield_id,
        const q3d_audio_quatf& soundfield_rotation)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_soundfield_rotation, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, soundfield_id, soundfield_rotation);
    }

    static inline q3d_audio_result q3dclient_audio_get_soundfield_rotation(
        q3d_audio_client* client,
        q3d_audio_sound_id soundfield_id,
        q3d_audio_quatf& soundfield_rotation)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_soundfield_rotation, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, soundfield_id, soundfield_rotation);
    }

    static inline q3d_audio_result q3dclient_audio_set_head_position_in_vec3f(q3d_audio_client* client,const q3d_audio_vec3f& position)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_head_position_in_vec3f, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, position);
    }

    static inline q3d_audio_result q3dclient_audio_set_head_position(q3d_audio_client* client,float x,float y,float z)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_head_position, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, x, y, z);
    }

    static inline q3d_audio_result q3dclient_audio_get_head_position_in_vec3f(q3d_audio_client* client,q3d_audio_vec3f& position)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_head_position_in_vec3f, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, position);
    }

    static inline q3d_audio_result q3dclient_audio_set_head_rotation_in_quatf(q3d_audio_client* client,const q3d_audio_quatf& rotation)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_head_rotation_in_quatf, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, rotation);
    }

    static inline q3d_audio_result q3dclient_audio_set_head_rotation(q3d_audio_client* client, float x,float y,float z,float w)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_head_rotation, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, x,y,z,w);
    }

    static inline q3d_audio_result q3dclient_audio_get_head_rotation_in_quatf(q3d_audio_client* client, q3d_audio_quatf& rotation)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_get_head_rotation_in_quatf, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, rotation);
    }

    static inline q3d_audio_result q3dclient_audio_enable_room(q3d_audio_client* client, bool enable)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_enable_room, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, enable);
    }

    static inline q3d_audio_result q3dclient_audio_set_room_properties(
        q3d_audio_client* client,
        float size_x,
        float size_y, 
        float size_z,
        q3d_audio_material_type wall_material,
        q3d_audio_material_type ceiling_material,
        q3d_audio_material_type floor_material)
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_room_properties, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, size_x, size_y, size_z, wall_material, ceiling_material, floor_material);
    }

    static inline q3d_audio_result q3dclient_audio_set_room_reverb_adjustments(
        q3d_audio_client* client,
        float gain,
        float time_adjust,
        float brightness_adjust) 
    {
        assert(client);

        Q3D_AUDIO_FUNC_PTR_HELPER(q3d_audio_set_room_reverb_adjustments, Q3D_AUDIO_ERROR_NOT_SUPPORTED);
        return macroDefinedFunctionPtr(client->context, gain, time_adjust, brightness_adjust);
    }

#ifdef __cplusplus
}
#endif//#ifdef __cplusplus

#endif//#ifndef Q3D_AUDIO_CLIENT_H
#endif//#ifdef __ANDROID__
