text: /*
   * Copyright 2007-2010 (C) - Frictional Games
   *
   * This file is part of OALWrapper
   *
   * For conditions of distribution and use, see copyright notice in LICENSE
   */
  /**
  	@file OAL_Device.cpp
  	@author Luis Rodero
  	@date 2006-10-27
  	@version 0.1
  	Implementation of the cOAL_Device class
  */
  
  #include "OALWrapper/OAL_Device.h"
  #include "OALWrapper/OAL_Source.h"
  #include "OALWrapper/OAL_OggSample.h"
  #include "OALWrapper/OAL_WAVSample.h"
  #include "OALWrapper/OAL_OggStream.h"
  #include "OALWrapper/OAL_CustomStream.h"
  #include "OALWrapper/OAL_SourceManager.h"
  
  #include "OALWrapper/OAL_Filter.h"
  #include "OALWrapper/OAL_Effect_Reverb.h"
  
  #include <cstring>
  
  using namespace std;
  
  //-------------------------------------------------------------------------
  
  //////////////////////////////////////////////
  // Human readable extension names 
  
  string sExtensionNames[NUM_EXTENSIONS+1] = 
  { 
  	string("ALC_EXT_CAPTURE" ),
  	string("ALC_EXT_EFX"),
  	string("ALC_ENUMERATION_EXT"),
  	string("ALC_ENUMERATE_ALL_EXT"),
  	string("AL_EXT_OFFSET"),
  	string("AL_EXT_LINEAR_DISTANCE"),
  	string("AL_EXT_EXPONENT_DISTANCE"),
  	string("EAX"),
  	string("EAX2.0"),
  	string("EAX3.0"),
  	string("EAX4.0"),
  	string("EAX5.0"),
  	string("EAX-RAM"),
  	string("No extension")
  };
  
  //-------------------------------------------------------------------------
  
  cOAL_Device::cOAL_Device ( ) : mpDevice(NULL), 
  							   mpContext(NULL),
  							   mpSourceManager(NULL), 
  							   mpEFXManager(NULL),
  							   mbEFXActive(false),
  							   mlEFXSends(0)
  {
  }
  
  cOAL_Device::~cOAL_Device ()
  {
  }
  
  //-------------------------------------------------------------------------
  
  ///////////////////////////////////////////////////////////
  // Inits the OpenAL device
  ///////////////////////////////////////////////////////////
  
  //-------------------------------------------------------------------------
  
  bool cOAL_Device::Init( cOAL_Init_Params& acParams )
  {
  	DEF_FUNC_NAME(cOAL_Device::Init);
  	FUNC_USES_ALC;
  	FUNC_USES_AL;
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Command, "Initializing device: %s...\n", (acParams.msDeviceName == "")? "\"preferred\"":acParams.msDeviceName.c_str() );
  
  	LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "Configuring streaming options:\n");
  
  	cOAL_Stream::SetBufferSize(acParams.mlStreamingBufferSize);
  	LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "\tSetting buffer size to %d bytes\n",cOAL_Stream::GetBufferSize());
  	cOAL_Stream::SetBufferCount(acParams.mlStreamingBufferCount);
  	LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "\tSetting queue length to %d buffers\n",cOAL_Stream::GetBufferCount());
  
  	
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Attempting to open device...\n" );
  	// Open the device, if fails return false
  	if(acParams.msDeviceName.empty())
  		mpDevice = alcOpenDevice(NULL);
  	else
  		mpDevice = alcOpenDevice( acParams.msDeviceName.c_str() );
  
  	if(mpDevice == NULL)
  	{
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Error, "Error opening device\n" );
  		return false;
  	}
  	
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Success.\n" );
  	
  	// Get ALC extensions ( ie EFX )
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Checking present ALC extensions\n" );
  	for (int i = 0; i < NUM_ALC_EXTENSIONS; ++i)
  	{
  		mvbExtensions[i] = RUN_ALC_FUNC((alcIsExtensionPresent(mpDevice,sExtensionNames[i].c_str()) == ALC_TRUE));
  		if (mvbExtensions[i])
  		{
  			LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "\t%s\n",sExtensionNames[i].c_str() );
  		}
  	}
  
  	ALCint lAttrList[] = 
  	{
  		ALC_FREQUENCY,		acParams.mlOutputFreq,
  #ifdef __APPLE__
  #else
  		ALC_MONO_SOURCES,	acParams.mbVoiceManagement ? 256 : acParams.mlMinMonoSourcesHint,
  		ALC_STEREO_SOURCES,	acParams.mbVoiceManagement ? 0 : acParams.mlMinStereoSourcesHint,
  #endif
  		ALC_MAX_AUXILIARY_SENDS, acParams.mlNumSendsHint,
  		0,
  	};
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Creating context\n");
  	// Create and set a context
  #ifdef __EMSCRIPTEN__
  	mpContext = RUN_ALC_FUNC(alcCreateContext ( mpDevice, NULL ));
  #else
  	mpContext = RUN_ALC_FUNC(alcCreateContext ( mpDevice, lAttrList ));
  #endif
  
  	RUN_ALC_FUNC(alcMakeContextCurrent ( mpContext ));
  
  	if (ALC_ERROR_OCCURED)
  	{
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Error, "Error creating context\n");
  		return false;
  	}
  
  	/////////////////////////////////////////////////
  	//Retrieve device info, such as extensions
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Checking present AL extensions\n" );
  	for (int i = NUM_ALC_EXTENSIONS; i < NUM_EXTENSIONS; ++i)
  	{
  		mvbExtensions[i] = RUN_AL_FUNC((alIsExtensionPresent(sExtensionNames[i].c_str()) == AL_TRUE));
  		if (mvbExtensions[i])
  			LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "\t%s\n",sExtensionNames[i].c_str() );
  	}
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Retrieving Output Devices\n");
  	vector<string> llDevices = GetOutputDevices();
  	vector<string>::iterator it;
  	for (it = llDevices.begin(); it != llDevices.end(); ++it) {
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "\t%s\n", (*it).c_str());
  	}
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Retrieving info\n" );
  
  	// Get device name
  	msDeviceName = RUN_ALC_FUNC(alcGetString(mpDevice, ALC_DEVICE_SPECIFIER));
  
  	// Get vendor name (just fancy stuff,not very useful)
  	msVendorName = RUN_ALC_FUNC(alGetString( AL_VENDOR ));
  
  	//Get renderer info
  	msRenderer = RUN_ALC_FUNC(alGetString ( AL_RENDERER ));
  
  	// Get the OpenAL impl. version
  	RUN_ALC_FUNC(alcGetIntegerv ( mpDevice, ALC_MAJOR_VERSION, sizeof(ALCint), &mlMajorVersion ));
  	RUN_ALC_FUNC(alcGetIntegerv ( mpDevice, ALC_MINOR_VERSION, sizeof(ALCint), &mlMinorVersion ));
  
  	RUN_ALC_FUNC(alcGetIntegerv ( mpDevice, ALC_MAX_AUXILIARY_SENDS, sizeof(ALCint), &mlEFXSends));
  
  	LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "Device name: %s\n", msDeviceName.c_str() );
  	LogMsg("",eOAL_LogVerbose_High, eOAL_LogMsg_Info, "OpenAL version: %d.%d\n", mlMajorVersion, mlMinorVersion );
  
  	// Check device version
  	if ( (mlMajorVersion < acParams.mlMajorVersionReq) ||
  			((mlMajorVersion == acParams.mlMajorVersionReq) && (mlMinorVersion < acParams.mlMinorVersionReq)) )
  	{
  		LogMsg("",eOAL_LogVerbose_None, eOAL_LogMsg_Error, "Version required: %d.%d\n",acParams.mlMajorVersionReq,acParams.mlMinorVersionReq);
  		return false;
  	}
  
  	//	If alSourceNumHint == -1, create as many sources as possible
  	if (acParams.mlNumSourcesHint == -1)
  		acParams.mlNumSourcesHint = 4096;
  
  	/////////////////////////////////////////////////
  	//Start EFX if requested
  	if (acParams.mbUseEFX && IsExtensionAvailable (OAL_ALC_EXT_EFX))
  	{
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Starting EFX on request\n" );
  		mpEFXManager = new cOAL_EFXManager;
  		mbEFXActive = mpEFXManager->Initialize( acParams.mlNumSlotsHint, mlEFXSends, acParams.mbUseThread, acParams.mlSlotUpdateFreq );
  		if (!mbEFXActive)
  		{
  			mpEFXManager->Destroy();
  			delete mpEFXManager;
  		}
  	}
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Creating Source Manager\n" );
  	//Create The source manager
  	mpSourceManager = new cOAL_SourceManager;
  	if ( mpSourceManager->Initialize( acParams.mbVoiceManagement, acParams.mlNumSourcesHint, acParams.mbUseThread, acParams.mlUpdateFreq, mlEFXSends ) == false)
  	{
  		LogMsg("",eOAL_LogVerbose_None, eOAL_LogMsg_Error, "Error creating Source Manager\n");
  		return false;
  	}
  	
  	return true;
  }
  
  //-------------------------------------------------------------------------
  
  ///////////////////////////////////////////////////////////
  //
  //
  ///////////////////////////////////////////////////////////
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::Close ()
  {
  	DEF_FUNC_NAME("cOAL_Device::Close()");
  	FUNC_USES_ALC;
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Command, "Closing device...\n" );
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Cleaning up Samples...\n" );
  	//Delete samples
  	{
  		for(tSampleListIt it = mlstSamples.begin(); it!=mlstSamples.end(); ++it )
  			delete (*it);
  		mlstSamples.clear();
  	}
  	
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Cleaning up Streams...\n" );
  	{
  		for (tStreamListIt it=mlstStreams.begin();it!=mlstStreams.end(); ++it )
  			delete (*it);
  		mlstStreams.clear();
  	}
  
  	if(mpSourceManager) 
  	{
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Cleaning up Source Manager...\n" );
  		mpSourceManager->Destroy();
  		delete mpSourceManager;
  		mpSourceManager = NULL;
  	}
  
  	if (mpEFXManager)
  	{
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Cleaning up EFX Manager...\n" );
  		mpEFXManager->Destroy();
  		delete mpEFXManager;
  		mpEFXManager = NULL;
  	}
  
  	LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Cleaning up Context and closing Low Level Device...\n" );
  	//Delete device and context.
  	RUN_ALC_FUNC(alcMakeContextCurrent ( NULL ));
  	RUN_ALC_FUNC(alcDestroyContext ( mpContext ));
  	RUN_ALC_FUNC(alcCloseDevice ( mpDevice ));
  
  	if (!ALC_ERROR_OCCURED)
  		LogMsg("",eOAL_LogVerbose_Low, eOAL_LogMsg_Info, "Done\n" );
  }
  
  //-------------------------------------------------------------------------
  
  bool cOAL_Device::RegainContext()
  {
  	DEF_FUNC_NAME("cOAL_Device::RegainContext");
  	FUNC_USES_ALC;
  
  	if (mpContext == NULL)
  		return false;
  	bool bSuccess = RUN_ALC_FUNC((alcMakeContextCurrent(mpContext) == AL_TRUE));
  	
  	if (bSuccess)
  	{
  		RUN_ALC_FUNC(alcProcessContext(mpContext));
  	}
  	return bSuccess;
  }
  
  //-------------------------------------------------------------------------
  
  /////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////
  // Loaders
  /////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////
  
  //-------------------------------------------------------------------------
  
  static eOAL_SampleFormat DetectFormatByFileName(const wstring& asFilename)
  {
  	// Check file format and load the sample data according to it
  	wstring strExt = GetExtensionW(asFilename);
  	if(strExt.compare(L"ogg")==0 || strExt.compare(L"oga")==0)
  		return eOAL_SampleFormat_Ogg;	// Load an Ogg Vorbis sample
  	else if(strExt.compare(L"wav")==0)
  		return eOAL_SampleFormat_Wav;	// Load a .WAV sample
  	return eOAL_SampleFormat_Unknown;
  }
  
  static bool CompareBuffer(const char* apBuffer, const char* asMatch, size_t aSize)
  {
  	for (int p= 0; p < aSize; ++p)
  	{
  		if (apBuffer[p] != asMatch[p]) return false;
  	}
  	return true;
  }
  
  static eOAL_SampleFormat DetectFormatByMagic(const void* apBuffer, size_t aSize)
  {
  	const char* buff = (char*)apBuffer;
  	if (aSize >= 35 && CompareBuffer(buff, "OggS", 4) && CompareBuffer(&buff[28], "\x01vorbis", 7))
  	{
  		return eOAL_SampleFormat_Ogg;
  	}
  	if (aSize >= 12 && CompareBuffer(buff, "RIFF", 4) && CompareBuffer(&buff[8], "WAVE", 4))
  	{
  		return eOAL_SampleFormat_Wav;
  	}
  	return eOAL_SampleFormat_Unknown;	
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Sample* cOAL_Device::LoadSample(const string &asFilename, eOAL_SampleFormat format)
  {
  	return LoadSample(String2WString(asFilename), format);
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Sample* cOAL_Device::LoadSample(const wstring& asFilename, eOAL_SampleFormat format)
  {
  	cOAL_Sample *pSample = NULL;
  
  	if (format == eOAL_SampleFormat_Detect) {
  		format = DetectFormatByFileName(asFilename);
  	}
  	switch(format) {
  		case eOAL_SampleFormat_Ogg:
  			pSample = new cOAL_OggSample;
  			break;
  		case eOAL_SampleFormat_Wav:
  			pSample = new cOAL_WAVSample;
  			break;
  		default:
  			return NULL;
  	}
  	
  	if(pSample->CreateFromFile(asFilename) )
  		mlstSamples.push_back(pSample);
  	else
  	{
  		delete pSample;
  		pSample = NULL;
  	}
  
  	return pSample;
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Sample* cOAL_Device::LoadSampleFromBuffer(const void* apBuffer, size_t aSize, eOAL_SampleFormat format)
  {
  	cOAL_Sample *pSample = NULL;
  
  	if (format == eOAL_SampleFormat_Detect) {
  		format = DetectFormatByMagic(apBuffer, aSize);
  	}
  	switch(format) {
  		case eOAL_SampleFormat_Ogg:
  			pSample = new cOAL_OggSample;
  			break;
  		case eOAL_SampleFormat_Wav:
  			pSample = new cOAL_WAVSample;
  			break;
  		default:
  			return NULL;
  	}
  	
  	if(pSample->CreateFromBuffer(apBuffer, aSize) )
  		mlstSamples.push_back(pSample);
  	else
  	{
  		delete pSample;
  		pSample = NULL;
  	}
  
  	return pSample;
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Stream* cOAL_Device::LoadStream(const string& asFilename, eOAL_SampleFormat format)
  {
  	return LoadStream(String2WString(asFilename), format);
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Stream* cOAL_Device::LoadStream(const wstring &asFilename, eOAL_SampleFormat format)
  {
  	cOAL_Stream *pStream = NULL;
  
  	if (format == eOAL_SampleFormat_Detect)
  	{
  		format = DetectFormatByFileName(asFilename);
  	}
  
  	switch(format) {
  		case eOAL_SampleFormat_Ogg:
  			pStream = new cOAL_OggStream;
  			break;
  		default:
  			return NULL;
  	}
  
  	/////////////////////////////////
  	//Create from file
  	if(pStream->CreateFromFile(asFilename))
  	{
  		mlstStreams.push_back(pStream);
  		//hpl::Log("Loaded stream %s\n", pStream->msName.c_str());
  	}
  	else
  	{
  		delete pStream;
  		pStream = NULL;
  	}
  
  	return pStream;
  
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Stream* cOAL_Device::LoadStreamFromBuffer(const void* apBuffer, size_t aSize, eOAL_SampleFormat format)
  {
  	cOAL_Stream *pStream = NULL;
  
  	if (format == eOAL_SampleFormat_Detect) {
  		format = DetectFormatByMagic(apBuffer, aSize);
  	}
  	switch(format) {
  		case eOAL_SampleFormat_Ogg:
  			pStream = new cOAL_OggStream;
  			break;
  		default:
  			return NULL;
  	}
  
  	if(pStream->CreateFromBuffer(apBuffer, aSize) )
  		mlstStreams.push_back(pStream);
  	else
  	{
  		delete pStream;
  		pStream = NULL;
  	}
  
  	return pStream;
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Stream* cOAL_Device::LoadCustomStream(const tStreamCallbacks& aCallback, const tStreamInfo& aInfo, void* apData)
  {
  	cOAL_Stream *pStream = new cOAL_CustomStream(aCallback, aInfo, apData);
  	mlstStreams.push_back(pStream);
  	return pStream;
  }
  
  //-------------------------------------------------------------------------
  
  void  cOAL_Device::UnloadSample(cOAL_Sample* apSample)
  {
  	if(apSample == NULL)
  		return;
  	
  	mlstSamples.remove(apSample);
  	delete apSample;
  }
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::UnloadStream(cOAL_Stream* apStream)
  {
  	if(apStream == NULL)
  		return;
      
  	mlstStreams.remove(apStream);
  	delete apStream;
  }
  
  //-------------------------------------------------------------------------
  
  /////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////
  // Listener Management
  /////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::SetListenerGain ( float afGain )
  {
  	DEF_FUNC_NAME ("cOAL_Device::SetListenerGain()");
  	FUNC_USES_AL;
  
  	if ( afGain < 0 )
  		afGain = 0;
  	if ( afGain > 1 )
  		afGain = 1;
  
  	RUN_AL_FUNC(alListenerf ( AL_GAIN, afGain ));
  }
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::SetListenerPosition (const float* apPos )
  {
  	DEF_FUNC_NAME ("cOAL_Device::SetListenerPosition()");
  	FUNC_USES_AL;
  
  	RUN_AL_FUNC(alListenerfv ( AL_POSITION, apPos ));
  }
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::SetListenerVelocity (const float* apVel )
  {
  	DEF_FUNC_NAME ("cOAL_Device::SetListenerVelocity()");
  	FUNC_USES_AL;
  
  	RUN_AL_FUNC(alListenerfv (AL_VELOCITY,apVel));
  }
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::SetListenerOrientation(const float* apForward, const float* apUp)
  {
  	DEF_FUNC_NAME ("cOAL_Device::SetListenerOrientation()");
  	FUNC_USES_AL;
  
  	float fOrientation[6] = { apForward[0], apForward[1], apForward[2], apUp[0], apUp[1], apUp[2] };
  	
  	RUN_AL_FUNC(alListenerfv ( AL_ORIENTATION, fOrientation ));
  }
  
  //-------------------------------------------------------------------------
  
  /////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////
  // Playback Management
  /////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////
  
  //-------------------------------------------------------------------------
  
  int cOAL_Device::PlaySample( int alSource, cOAL_Sample *apSample, int alPriority, float afVolume, bool abStartPaused )
  {
  	if (apSample == NULL) 
  		return -1;
  
  	cOAL_Source *pSource = NULL;
  	if (alSource == OAL_FREE)
  		pSource = mpSourceManager->GetAvailableSource( alPriority, apSample->GetChannels() );
  	else
  		pSource = mpSourceManager->GetSource(alSource,true);
  
  	if (pSource == NULL)
  		return -1;
  
  	int lSourceHandle;
  
  	pSource->Lock();
  	{
  		lSourceHandle = pSource->BindData(apSample);
  		pSource->SetPriority(alPriority);
  		pSource->SetGain(afVolume);
  		pSource->Pause(abStartPaused);
  		pSource->Play();
  	}
  	pSource->Unlock();
  
  	return lSourceHandle;
  }
  
  //-------------------------------------------------------------------------
  
  int cOAL_Device::PlayStream( int alSource, cOAL_Stream *apStream, float afVolume, bool abStartPaused )
  {
  	if (apStream == NULL)
  		return -1;
  
  	cOAL_Source *pSource = NULL;
  	if (alSource == OAL_FREE)
  		pSource = mpSourceManager->GetAvailableSource( 256, apStream->GetChannels() );
  	else
  		pSource = mpSourceManager->GetSource(alSource,true);
  	
  	if (pSource == NULL)
  		return -1;
  
  	int lSourceHandle;
  
  	pSource->Lock();
  	{
  		lSourceHandle = pSource->BindData(apStream);
  		pSource->SetGain(afVolume);
  		pSource->Pause(abStartPaused);
  		pSource->Play();
  	}
  	pSource->Unlock();
  
  	if (lSourceHandle != -1) mpSourceManager->AddActiveStream(pSource);
  
  	return lSourceHandle;
  }
  
  //-------------------------------------------------------------------------
  
  cOAL_Source* cOAL_Device::GetSource( int alSourceHandle )
  {
  	if (mpSourceManager)
  		return mpSourceManager->GetSource(alSourceHandle);
  
  	return NULL;
  }
  
  //-------------------------------------------------------------------------
  
  string& cOAL_Device::GetExtensionName(int alWhich) 
  { 
  	if( (alWhich >= 0) && (alWhich < NUM_EXTENSIONS)) 
  		return sExtensionNames[alWhich]; 
  	else 
  		return sExtensionNames[NUM_EXTENSIONS];
  }
  
  //-------------------------------------------------------------------------
  
  string cOAL_Device::GetDefaultDeviceName()
  {
  	DEF_FUNC_NAME(cOAL_Device::GetDefaultDeviceName);
  	FUNC_USES_ALC;
  
  	string sDev = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
  	
  	return sDev;
  }
  
  //-------------------------------------------------------------------------
  vector<string> cOAL_Device::GetOutputDevices()
  {
  	DEF_FUNC_NAME(cOAL_Device::GetOutputDevices);
  	FUNC_USES_ALC;
  	vector<string> devices;
  	bool bEnumerate = RUN_ALC_FUNC(alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE);
  	bool bEnumerate_all = RUN_ALC_FUNC(alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATE_ALL_EXT") == AL_TRUE);
  	if (bEnumerate) {
  		const char *s;
  		if (bEnumerate_all) {
  			// walk devices
  			s = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
  		} else {
  			s = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
  		}
  		while (*s != '\0') {
  			devices.push_back(string(s));
  			// find next string
  			while (*s++ != '\0')
  				;
  		}
  	}
  	return devices;
  }
  
  //-------------------------------------------------------------------------
  
  void cOAL_Device::SetDistanceModel(eOAL_DistanceModel aModel)
  {
  	DEF_FUNC_NAME("OAL_SetDistanceModel");
  	FUNC_USES_AL;
  
  	ALenum distModel;
  	switch(aModel)
  	{
  	case eOAL_DistanceModel_Inverse:
  		distModel = AL_INVERSE_DISTANCE;
  		break;
  	case eOAL_DistanceModel_Inverse_Clamped:
  		distModel = AL_INVERSE_DISTANCE_CLAMPED;
  		break;
  	case eOAL_DistanceModel_Linear:
  		distModel = AL_LINEAR_DISTANCE;
  		break;
  	case eOAL_DistanceModel_Linear_Clamped:
  		distModel = AL_LINEAR_DISTANCE_CLAMPED;
  		break;
  	case eOAL_DistanceModel_Exponent:
  		distModel = AL_EXPONENT_DISTANCE;
  		break;
  	case eOAL_DistanceModel_Exponent_Clamped:
  		distModel = AL_EXPONENT_DISTANCE_CLAMPED;
  		break;
  	case eOAL_DistanceModel_None:
  	default:
  		distModel = AL_NONE;
  		break;
  	}
  
  	RUN_AL_FUNC(alDistanceModel(distModel));
  }
  
  //-------------------------------------------------------------------------
  
  /////////////////////////////////////////////////////////////////
  
  //-------------------------------------------------------------------------
  /*
  void cOAL_Device::Log( eOAL_LogVerbose aVerboseLevelReq, eOAL_LogMsg aMessageType, const char* asMessage, ...)
  {
  	if(asMessage == NULL)
  		return;
  	if(!mbLogEnabled)
  		return;
  
  	if(aVerboseLevelReq > mLogVerboseLevel)
  		return;
  
  	string sMessage;
  
  	char text[2048];
  	va_list ap;
  	va_start(ap, asMessage);
  	vsprintf(text, asMessage, ap);
  	va_end(ap);
  
  	
  	switch(aMessageType)
  	{
  	case eOAL_LogMsg_Command:
  		sMessage.append("[COMMAND] ");
  		break;
  	case eOAL_LogMsg_Info:
  		sMessage.append("[INFO] ");
  		break;
  	case eOAL_LogMsg_Error:
  		sMessage.append("[ERROR] ");
  	default:
  		break;
  	}
  
  	sMessage.append("Device - ").append(text);
  
  	Write(sMessage);
  }
  */
  //-------------------------------------------------------------------------
  