code:
#include "common.h"

#ifdef AUDIO_OAL
#include "channel.h"
#include "sampman.h"

#ifndef _WIN32
#include <float.h>
#endif

extern bool IsFXSupported();

ALuint alSources[NUM_CHANNELS];
ALuint alFilters[NUM_CHANNELS];
ALuint alBuffers[NUM_CHANNELS];
bool bChannelsCreated = false;

int32 CChannel::channelsThatNeedService = 0;

uint8 tempStereoBuffer[PED_BLOCKSIZE * 2];

void
CChannel::InitChannels()
{
	alGenSources(NUM_CHANNELS, alSources);
	alGenBuffers(NUM_CHANNELS, alBuffers);
	if (IsFXSupported())
		alGenFilters(NUM_CHANNELS, alFilters);
	bChannelsCreated = true;
}

void
CChannel::DestroyChannels()
{
	if (bChannelsCreated)
	{
		alDeleteSources(NUM_CHANNELS, alSources);
		memset(alSources, 0, sizeof(alSources));
		alDeleteBuffers(NUM_CHANNELS, alBuffers);
		memset(alBuffers, 0, sizeof(alBuffers));
		if (IsFXSupported())
		{
			alDeleteFilters(NUM_CHANNELS, alFilters);
			memset(alFilters, 0, sizeof(alFilters));
		}
		bChannelsCreated = false;
	}
}

CChannel::CChannel()
{
	Data = nil;
	DataSize = 0;
	bIs2D = false;
	SetDefault();
}

void CChannel::SetDefault()
{
	Pitch = 1.0f;
	Gain = 1.0f;
	Mix = 0.0f;

	Position[0] = 0.0f; Position[1] = 0.0f; Position[2] = 0.0f;
	Distances[0] = 0.0f; Distances[1] = FLT_MAX;

	LoopCount = 1;
	LastProcessedOffset = UINT32_MAX;
	LoopPoints[0] = 0; LoopPoints[1] = -1;

	Frequency = MAX_FREQ;
}

void CChannel::Reset()
{
	// Here is safe because ctor don't call this
	if (LoopCount > 1)
		channelsThatNeedService--;

	ClearBuffer();
	SetDefault();
}

void CChannel::Init(uint32 _id, bool Is2D)
{
	id = _id;
	if ( HasSource() )
	{
		alSourcei(alSources[id], AL_SOURCE_RELATIVE, AL_TRUE);
		if ( IsFXSupported() )
			alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);

		if ( Is2D )
		{
			bIs2D = true;
			alSource3f(alSources[id], AL_POSITION, 0.0f, 0.0f, 0.0f);
			alSourcef(alSources[id], AL_GAIN, 1.0f);
		}
	}
}

void CChannel::Term()
{
	Stop();
	if ( HasSource() )
	{
		if ( IsFXSupported() )
		{
			alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
		}
	}
}

void CChannel::Start()
{
	if ( !HasSource() ) return;
	if ( !Data ) return;

	if ( bIs2D )
	{
		// convert mono data to stereo
		int16 *monoData = (int16*)Data;
		int16 *stereoData = (int16*)tempStereoBuffer;
		for (size_t i = 0; i < DataSize / 2; i++)
		{
			*(stereoData++) = *monoData;
			*(stereoData++) = *(monoData++);
		}
		alBufferData(alBuffers[id], AL_FORMAT_STEREO16, tempStereoBuffer, DataSize * 2, Frequency);
	}
	else
		alBufferData(alBuffers[id], AL_FORMAT_MONO16, Data, DataSize, Frequency);
	if ( LoopPoints[0] != 0 && LoopPoints[0] != -1 )
		alBufferiv(alBuffers[id], AL_LOOP_POINTS_SOFT, LoopPoints);
	alSourcei(alSources[id], AL_BUFFER, alBuffers[id]);
	alSourcePlay(alSources[id]);
}

void CChannel::Stop()
{
	if ( HasSource() )
		alSourceStop(alSources[id]);

	Reset();
}

bool CChannel::HasSource()
{
	return alSources[id] != AL_NONE;
}

bool CChannel::IsUsed()
{
	if ( HasSource() )
	{
		ALint sourceState;
		alGetSourcei(alSources[id], AL_SOURCE_STATE, &sourceState);
		return sourceState == AL_PLAYING;
	}
	return false;
}

void CChannel::SetPitch(float pitch)
{
	if ( !HasSource() ) return;
	alSourcef(alSources[id], AL_PITCH, pitch);
}

void CChannel::SetGain(float gain)
{
	if ( !HasSource() ) return;
	alSourcef(alSources[id], AL_GAIN, gain);
}

void CChannel::SetVolume(int32 vol)
{
	SetGain(ALfloat(vol) / MAX_VOLUME);
}

void CChannel::SetSampleData(void *_data, size_t _DataSize, int32 freq)
{
	Data = _data;
	DataSize = _DataSize;
	Frequency = freq;
}

void CChannel::SetCurrentFreq(uint32 freq)
{
	SetPitch(ALfloat(freq) / Frequency);
}

void CChannel::SetLoopCount(int32 count)
{
	if ( !HasSource() ) return;

	// 0: loop indefinitely, 1: play one time, 2: play two times etc...
	// only > 1 needs manual processing

	if (LoopCount > 1 && count < 2)
		channelsThatNeedService--;
	else if (LoopCount < 2 && count > 1)
		channelsThatNeedService++;

	alSourcei(alSources[id], AL_LOOPING, count == 1 ? AL_FALSE : AL_TRUE);
	LoopCount = count;
}

bool CChannel::Update()
{
	if (!HasSource()) return false;
	if (LoopCount < 2) return false;

	ALint state;
	alGetSourcei(alSources[id], AL_SOURCE_STATE, &state);
	if (state == AL_STOPPED) {
		debug("Looping channels(%d in this case) shouldn't report AL_STOPPED, but nvm\n", id);
		SetLoopCount(1);
		return true;
	}

	assert(channelsThatNeedService > 0 && "Ref counting is broken");

	ALint offset;
	alGetSourcei(alSources[id], AL_SAMPLE_OFFSET, &offset);

	// Rewound
	if (offset < LastProcessedOffset) {
		LoopCount--;
		if (LoopCount == 1) {
			// Playing last tune...
			channelsThatNeedService--;
			alSourcei(alSources[id], AL_LOOPING, AL_FALSE);
		}
	}
	LastProcessedOffset = offset;
	return true;
}

void CChannel::SetLoopPoints(ALint start, ALint end)
{
	LoopPoints[0] = start;
	LoopPoints[1] = end;
}

void CChannel::SetPosition(float x, float y, float z)
{
	if ( !HasSource() ) return;
	alSource3f(alSources[id], AL_POSITION, x, y, z);
}

void CChannel::SetDistances(float max, float min)
{
	if ( !HasSource() ) return;
	alSourcef   (alSources[id], AL_MAX_DISTANCE,       max);
	alSourcef   (alSources[id], AL_REFERENCE_DISTANCE, min);
	alSourcef   (alSources[id], AL_MAX_GAIN, 1.0f);
	alSourcef   (alSources[id], AL_ROLLOFF_FACTOR, 1.0f);
}

void CChannel::SetPan(int32 pan)
{
	SetPosition((pan-63)/64.0f, 0.0f, Sqrt(1.0f-SQR((pan-63)/64.0f)));
}

void CChannel::ClearBuffer()
{
	if ( !HasSource() ) return;
	alSourcei(alSources[id], AL_LOOPING, AL_FALSE);
	alSourcei(alSources[id], AL_BUFFER, AL_NONE);
	Data = nil;
	DataSize = 0;
}

void CChannel::SetReverbMix(ALuint slot, float mix)
{
	if ( !IsFXSupported() ) return;
	if ( !HasSource() ) return;
	if ( alFilters[id] == AL_FILTER_NULL ) return;

	Mix = mix;
	EAX3_SetReverbMix(alFilters[id], mix);
	alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, slot, 0, alFilters[id]);
}

void CChannel::UpdateReverb(ALuint slot)
{
	if ( !IsFXSupported() ) return;
	if ( !HasSource() ) return;
	if ( alFilters[id] == AL_FILTER_NULL ) return;
	EAX3_SetReverbMix(alFilters[id], Mix);
	alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, slot, 0, alFilters[id]);
}

#endif


code:
#pragma once

#ifdef AUDIO_OAL
#include "oal/oal_utils.h"
#include <AL/al.h>
#include <AL/alext.h>
#include <AL/efx.h>

class CChannel
{
	uint32 id;
	float  Pitch, Gain;
	float  Mix;
	void  *Data;
	size_t DataSize;
	int32  Frequency;
	float  Position[3];
	float  Distances[2];
	int32  LoopCount;
	ALint  LoopPoints[2];
	ALint  LastProcessedOffset;
	bool   bIs2D;
public:
	static int32 channelsThatNeedService;

	static void InitChannels();
	static void DestroyChannels();

	CChannel();
	void SetDefault();
	void Reset();
	void Init(uint32 _id, bool Is2D = false);
	void Term();
	void Start();
	void Stop();
	bool HasSource();
	bool IsUsed();
	void SetPitch(float pitch);
	void SetGain(float gain);
	void SetVolume(int32 vol);
	void SetSampleData(void *_data, size_t _DataSize, int32 freq);
	void SetCurrentFreq(uint32 freq);
	void SetLoopCount(int32 count);
	void SetLoopPoints(ALint start, ALint end);
	void SetPosition(float x, float y, float z);
	void SetDistances(float max, float min);
	void SetPan(int32 pan);
	void ClearBuffer();
	void SetReverbMix(ALuint slot, float mix);
	void UpdateReverb(ALuint slot);
	bool Update();
};

#endif

license: mit


code:
#include "common.h"
#include "oal_utils.h"

#ifdef AUDIO_OAL

/*
 * When linking to a static openal-soft library,
 * the extension function inside the openal library conflict with the variables here.
 * Therefore declare these re3 owned symbols in a private namespace.
 */

namespace re3_openal {

LPALGENEFFECTS alGenEffects;
LPALDELETEEFFECTS alDeleteEffects;
LPALISEFFECT alIsEffect;
LPALEFFECTI alEffecti;
LPALEFFECTIV alEffectiv;
LPALEFFECTF alEffectf;
LPALEFFECTFV alEffectfv;
LPALGETEFFECTI alGetEffecti;
LPALGETEFFECTIV alGetEffectiv;
LPALGETEFFECTF alGetEffectf;
LPALGETEFFECTFV alGetEffectfv;
LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
LPALGENFILTERS alGenFilters;
LPALDELETEFILTERS alDeleteFilters;
LPALISFILTER alIsFilter;
LPALFILTERI alFilteri;
LPALFILTERIV alFilteriv;
LPALFILTERF alFilterf;
LPALFILTERFV alFilterfv;
LPALGETFILTERI alGetFilteri;
LPALGETFILTERIV alGetFilteriv;
LPALGETFILTERF alGetFilterf;
LPALGETFILTERFV alGetFilterfv;

}

using namespace re3_openal;

void EFXInit()
{
	/* Define a macro to help load the function pointers. */
#define LOAD_PROC(T, x)  ((x) = (T)alGetProcAddress(#x))
	LOAD_PROC(LPALGENEFFECTS, alGenEffects);
	LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects);
	LOAD_PROC(LPALISEFFECT, alIsEffect);
	LOAD_PROC(LPALEFFECTI, alEffecti);
	LOAD_PROC(LPALEFFECTIV, alEffectiv);
	LOAD_PROC(LPALEFFECTF, alEffectf);
	LOAD_PROC(LPALEFFECTFV, alEffectfv);
	LOAD_PROC(LPALGETEFFECTI, alGetEffecti);
	LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv);
	LOAD_PROC(LPALGETEFFECTF, alGetEffectf);
	LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv);

	LOAD_PROC(LPALGENFILTERS, alGenFilters);
	LOAD_PROC(LPALDELETEFILTERS, alDeleteFilters);
	LOAD_PROC(LPALISFILTER, alIsFilter);
	LOAD_PROC(LPALFILTERI, alFilteri);
	LOAD_PROC(LPALFILTERIV, alFilteriv);
	LOAD_PROC(LPALFILTERF, alFilterf);
	LOAD_PROC(LPALFILTERFV, alFilterfv);
	LOAD_PROC(LPALGETFILTERI, alGetFilteri);
	LOAD_PROC(LPALGETFILTERIV, alGetFilteriv);
	LOAD_PROC(LPALGETFILTERF, alGetFilterf);
	LOAD_PROC(LPALGETFILTERFV, alGetFilterfv);

	LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots);
	LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots);
	LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot);
	LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti);
	LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv);
	LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf);
	LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv);
	LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti);
	LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv);
	LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf);
	LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv);
#undef LOAD_PROC
}

void SetEffectsLevel(ALuint uiFilter, float level)
{
	alFilteri(uiFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
	alFilterf(uiFilter, AL_LOWPASS_GAIN, 1.0f);
	alFilterf(uiFilter, AL_LOWPASS_GAINHF, level);
}

static inline float gain_to_mB(float gain)
{
    return (gain > 1e-5f) ? (float)(log10f(gain) * 2000.0f) : -10000l;
}

static inline float mB_to_gain(float millibels)
{
    return (millibels > -10000.0f) ? powf(10.0f, millibels/2000.0f) : 0.0f;
}

static inline float clampF(float val, float minval, float maxval)
{
    if(val >= maxval) return maxval;
    if(val <= minval) return minval;
    return val;
}

void EAX3_Set(ALuint effect, const EAXLISTENERPROPERTIES *props)
{
	alEffecti (effect, AL_EFFECT_TYPE,                     AL_EFFECT_EAXREVERB);
	alEffectf (effect, AL_EAXREVERB_DENSITY,               clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f));
	alEffectf (effect, AL_EAXREVERB_DIFFUSION,             props->flEnvironmentDiffusion);
	alEffectf (effect, AL_EAXREVERB_GAIN,                  mB_to_gain((float)props->lRoom));
	alEffectf (effect, AL_EAXREVERB_GAINHF,                mB_to_gain((float)props->lRoomHF));
	alEffectf (effect, AL_EAXREVERB_GAINLF,                mB_to_gain((float)props->lRoomLF));
	alEffectf (effect, AL_EAXREVERB_DECAY_TIME,            props->flDecayTime);
	alEffectf (effect, AL_EAXREVERB_DECAY_HFRATIO,         props->flDecayHFRatio);
	alEffectf (effect, AL_EAXREVERB_DECAY_LFRATIO,         props->flDecayLFRatio);
	alEffectf (effect, AL_EAXREVERB_REFLECTIONS_GAIN,      clampF(mB_to_gain((float)props->lReflections), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN));
	alEffectf (effect, AL_EAXREVERB_REFLECTIONS_DELAY,     props->flReflectionsDelay);
	alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN,       &props->vReflectionsPan.x);
	alEffectf (effect, AL_EAXREVERB_LATE_REVERB_GAIN,      clampF(mB_to_gain((float)props->lReverb), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN));
	alEffectf (effect, AL_EAXREVERB_LATE_REVERB_DELAY,     props->flReverbDelay);
	alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN,       &props->vReverbPan.x);
	alEffectf (effect, AL_EAXREVERB_ECHO_TIME,             props->flEchoTime);
	alEffectf (effect, AL_EAXREVERB_ECHO_DEPTH,            props->flEchoDepth);
	alEffectf (effect, AL_EAXREVERB_MODULATION_TIME,       props->flModulationTime);
	alEffectf (effect, AL_EAXREVERB_MODULATION_DEPTH,      props->flModulationDepth);
	alEffectf (effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, clampF(mB_to_gain(props->flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF));
	alEffectf (effect, AL_EAXREVERB_HFREFERENCE,           props->flHFReference);
	alEffectf (effect, AL_EAXREVERB_LFREFERENCE,           props->flLFReference);
	alEffectf (effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR,   props->flRoomRolloffFactor);
	alEffecti (effect, AL_EAXREVERB_DECAY_HFLIMIT,         (props->ulFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ? AL_TRUE : AL_FALSE);
}

void EFX_Set(ALuint effect, const EAXLISTENERPROPERTIES *props)
{
	alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);

	alEffectf(effect, AL_REVERB_DENSITY,               clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f));
	alEffectf(effect, AL_REVERB_DIFFUSION,             props->flEnvironmentDiffusion);
	alEffectf(effect, AL_REVERB_GAIN,                  mB_to_gain((float)props->lRoom));
	alEffectf(effect, AL_REVERB_GAINHF,                mB_to_gain((float)props->lRoomHF));
	alEffectf(effect, AL_REVERB_DECAY_TIME,            props->flDecayTime);
	alEffectf(effect, AL_REVERB_DECAY_HFRATIO,         props->flDecayHFRatio);
	alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN,      clampF(mB_to_gain((float)props->lReflections), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN));
	alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY,     props->flReflectionsDelay);
	alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN,      clampF(mB_to_gain((float)props->lReverb), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN));
	alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY,     props->flReverbDelay);
	alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, clampF(mB_to_gain(props->flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF));
	alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR,   props->flRoomRolloffFactor);
	alEffecti(effect, AL_REVERB_DECAY_HFLIMIT,         (props->ulFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ? AL_TRUE : AL_FALSE);
}

void EAX3_SetReverbMix(ALuint filter, float mix)
{
	//long vol=(long)linear_to_dB(mix);
	//DSPROPERTY_EAXBUFFER_ROOMHF,
	//DSPROPERTY_EAXBUFFER_ROOM,
	//DSPROPERTY_EAXBUFFER_REVERBMIX,

	long mbvol = gain_to_mB(mix);
	float mb   = mbvol;
	float mbhf = mbvol;

	alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
	alFilterf(filter, AL_LOWPASS_GAIN,   mB_to_gain(Min(mb, 0.0f)));
	alFilterf(filter, AL_LOWPASS_GAINHF, mB_to_gain(mbhf));
}

#endif


code:
#pragma once

#ifdef AUDIO_OAL
#include "eax.h"
#include "AL/efx.h"

void EFXInit();
void EAX3_Set(ALuint effect, const EAXLISTENERPROPERTIES *props);
void EFX_Set(ALuint effect, const EAXLISTENERPROPERTIES *props);
void EAX3_SetReverbMix(ALuint filter, float mix);
void SetEffectsLevel(ALuint uiFilter, float level);

namespace re3_openal {

extern LPALGENEFFECTS alGenEffects;
extern LPALDELETEEFFECTS alDeleteEffects;
extern LPALISEFFECT alIsEffect;
extern LPALEFFECTI alEffecti;
extern LPALEFFECTIV alEffectiv;
extern LPALEFFECTF alEffectf;
extern LPALEFFECTFV alEffectfv;
extern LPALGETEFFECTI alGetEffecti;
extern LPALGETEFFECTIV alGetEffectiv;
extern LPALGETEFFECTF alGetEffectf;
extern LPALGETEFFECTFV alGetEffectfv;
extern LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
extern LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
extern LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
extern LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
extern LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
extern LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
extern LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
extern LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
extern LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
extern LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
extern LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
extern LPALGENFILTERS alGenFilters;
extern LPALDELETEFILTERS alDeleteFilters;
extern LPALISFILTER alIsFilter;
extern LPALFILTERI alFilteri;
extern LPALFILTERIV alFilteriv;
extern LPALFILTERF alFilterf;
extern LPALFILTERFV alFilterfv;
extern LPALGETFILTERI alGetFilteri;
extern LPALGETFILTERIV alGetFilteriv;
extern LPALGETFILTERF alGetFilterf;
extern LPALGETFILTERFV alGetFilterfv;

}

using namespace re3_openal;

#endif


code:
#include "common.h"

#ifdef AUDIO_OAL

#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
#ifdef AUDIO_OAL_USE_SNDFILE
#pragma comment( lib, "libsndfile-1.lib" )
#endif
#ifdef AUDIO_OAL_USE_MPG123
#pragma comment( lib, "libmpg123-0.lib" )
#endif
#endif
#ifdef AUDIO_OAL_USE_SNDFILE
#include <sndfile.h>
#endif
#ifdef AUDIO_OAL_USE_MPG123
#include <mpg123.h>
#endif
#ifdef AUDIO_OAL_USE_OPUS
#include <opusfile.h>
#endif

#include <queue>
#include <utility>

#ifdef MULTITHREADED_AUDIO
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "MusicManager.h"
#include "stream.h"

std::thread	gAudioThread;
std::mutex gAudioThreadQueueMutex;
std::condition_variable gAudioThreadCv;
bool gAudioThreadTerm = false;
std::queue<CStream*> gStreamsToProcess; // values are not unique, we will handle that ourself
std::queue<std::pair<IDecoder*, void*>> gStreamsToClose;
#else
#include "stream.h"
#endif

#include "sampman.h"

#ifndef _WIN32
#include "crossplatform.h"
#endif

/*
As we ran onto an issue of having different volume levels for mono streams
and stereo streams we are now handling all the stereo panning ourselves.
Each stream now has two sources - one panned to the left and one to the right,
and uses two separate buffers to store data for each individual channel.
For that we also have to reshuffle all decoded PCM stereo data from LRLRLRLR to
LLLLRRRR (handled by CSortStereoBuffer).
*/

class CSortStereoBuffer
{
	uint16* PcmBuf;
	size_t BufSize;
//#ifdef MULTITHREADED_AUDIO
//	std::mutex Mutex;
//#endif

public:
	CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
	~CSortStereoBuffer()
	{
		if (PcmBuf)
			free(PcmBuf);
	}

	uint16* GetBuffer(size_t size)
	{
		if (size == 0) return nil;
		if (!PcmBuf)
		{
			BufSize = size;
			PcmBuf = (uint16*)malloc(BufSize);
		}
		else if (BufSize < size)
		{
			BufSize = size;
			PcmBuf = (uint16*)realloc(PcmBuf, size);
		}
		return PcmBuf;
	}

	void SortStereo(void* buf, size_t size)
	{
//#ifdef MULTITHREADED_AUDIO
//		std::lock_guard<std::mutex> lock(Mutex);
//#endif
		uint16* InBuf = (uint16*)buf;
		uint16* OutBuf = GetBuffer(size);

		if (!OutBuf) return;

		size_t rightStart = size / 4;
		for (size_t i = 0; i < size / 4; i++)
		{
			OutBuf[i] = InBuf[i*2];
			OutBuf[i+rightStart] = InBuf[i*2+1];
		}

		memcpy(InBuf, OutBuf, size);
	}

};

CSortStereoBuffer SortStereoBuffer;

class CImaADPCMDecoder
{
	const uint16 StepTable[89] = {
		7, 8, 9, 10, 11, 12, 13, 14,
		16, 17, 19, 21, 23, 25, 28, 31,
		34, 37, 41, 45, 50, 55, 60, 66,
		73, 80, 88, 97, 107, 118, 130, 143,
		157, 173, 190, 209, 230, 253, 279, 307,
		337, 371, 408, 449, 494, 544, 598, 658,
		724, 796, 876, 963, 1060, 1166, 1282, 1411,
		1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
		3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
		7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
		15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
		32767
	};

	int16 Sample, StepIndex;

public:
	CImaADPCMDecoder()
	{
		Init(0, 0);
	}

	void Init(int16 _Sample, int16 _StepIndex)
	{
		Sample = _Sample;
		StepIndex = _StepIndex;
	}

	void Decode(uint8 *inbuf, int16 *_outbuf, size_t size)
	{
		int16* outbuf = _outbuf;
		for (size_t i = 0; i < size; i++)
		{
			*(outbuf++) = DecodeSample(inbuf[i] & 0xF);
			*(outbuf++) = DecodeSample(inbuf[i] >> 4);
		}
	}

	int16 DecodeSample(uint8 adpcm)
	{
		uint16 step = StepTable[StepIndex];

		if (adpcm & 4)
			StepIndex += ((adpcm & 3) + 1) * 2;
		else
			StepIndex--;

		StepIndex = Clamp(StepIndex, 0, 88);

		int delta = step >> 3;
		if (adpcm & 1) delta += step >> 2;
		if (adpcm & 2) delta += step >> 1;
		if (adpcm & 4) delta += step;
		if (adpcm & 8) delta = -delta;

		int newSample = Sample + delta;
		Sample = Clamp(newSample, -32768, 32767);
		return Sample;
	}
};

class CWavFile : public IDecoder
{
	enum
	{
		WAVEFMT_PCM = 1,
		WAVEFMT_IMA_ADPCM = 0x11,
		WAVEFMT_XBOX_ADPCM = 0x69,
	};

	struct tDataHeader
	{
		uint32 ID;
		uint32 Size;
	};

	struct tFormatHeader
	{
		uint16 AudioFormat;
		uint16 NumChannels;
		uint32 SampleRate;
		uint32 ByteRate;
		uint16 BlockAlign;
		uint16 BitsPerSample;
		uint16 extra[2]; // adpcm only

		tFormatHeader() { memset(this, 0, sizeof(*this)); }
	};

	FILE *m_pFile;
	bool  m_bIsOpen;

	tFormatHeader m_FormatHeader;

	uint32 m_DataStartOffset; // TODO: 64 bit?
	uint32 m_nSampleCount;
	uint32 m_nSamplesPerBlock;

	// ADPCM things
	uint8            *m_pAdpcmBuffer;
	int16           **m_ppPcmBuffers;
	CImaADPCMDecoder *m_pAdpcmDecoders;

	void Close()
	{
		if (m_pFile) {
			fclose(m_pFile);
			m_pFile = nil;
		}
		delete[] m_pAdpcmBuffer;
		delete[] m_ppPcmBuffers;
		delete[] m_pAdpcmDecoders;
	}

	uint32 GetCurrentSample() const
	{
		// TODO: 64 bit?
		uint32 FilePos = ftell(m_pFile);
		if (FilePos <= m_DataStartOffset)
			return 0;
		return (FilePos - m_DataStartOffset) / m_FormatHeader.BlockAlign * m_nSamplesPerBlock;
	}

public:
	CWavFile(const char* path) : m_bIsOpen(false), m_DataStartOffset(0), m_nSampleCount(0), m_nSamplesPerBlock(0), m_pAdpcmBuffer(nil), m_ppPcmBuffers(nil), m_pAdpcmDecoders(nil)
	{
		m_pFile = fopen(path, "rb");
		if (!m_pFile) return;

#define CLOSE_ON_ERROR(op)\
			if (op) { \
				Close(); \
				return; \
			}

		tDataHeader DataHeader;

		CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
		CLOSE_ON_ERROR(DataHeader.ID != 'FFIR');

		// TODO? validate filesizes

		int WAVE;
		CLOSE_ON_ERROR(fread(&WAVE, 4, 1, m_pFile) == 0);
		CLOSE_ON_ERROR(WAVE != 'EVAW')
		CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
		CLOSE_ON_ERROR(DataHeader.ID != ' tmf');

		CLOSE_ON_ERROR(fread(&m_FormatHeader, Min(DataHeader.Size, sizeof(tFormatHeader)), 1, m_pFile) == 0);
		CLOSE_ON_ERROR(DataHeader.Size > sizeof(tFormatHeader));

		switch (m_FormatHeader.AudioFormat)
		{
		case WAVEFMT_XBOX_ADPCM:
			m_FormatHeader.AudioFormat = WAVEFMT_IMA_ADPCM;
		case WAVEFMT_IMA_ADPCM:
			m_nSamplesPerBlock = (m_FormatHeader.BlockAlign / m_FormatHeader.NumChannels - 4) * 2 + 1;
			m_pAdpcmBuffer = new uint8[m_FormatHeader.BlockAlign];
			m_ppPcmBuffers = new int16*[m_FormatHeader.NumChannels];
			m_pAdpcmDecoders = new CImaADPCMDecoder[m_FormatHeader.NumChannels];
			break;
		case WAVEFMT_PCM:
			m_nSamplesPerBlock = 1;
			if (m_FormatHeader.BitsPerSample != 16)
			{
				debug("Unsupported PCM (%d bits), only signed 16-bit is supported (%s)\n", m_FormatHeader.BitsPerSample, path);
				Close();
				return;
			}
			break;
		default:
			debug("Unsupported wav format 0x%x (%s)\n", m_FormatHeader.AudioFormat, path);
			Close();
			return;
		}

		while (true) {
			CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
			if (DataHeader.ID == 'atad')
				break;
			fseek(m_pFile, DataHeader.Size, SEEK_CUR);
			// TODO? validate data size
			// maybe check if there no extreme custom headers that might break this
		}

		m_DataStartOffset = ftell(m_pFile);
		m_nSampleCount = DataHeader.Size / m_FormatHeader.BlockAlign * m_nSamplesPerBlock;

		m_bIsOpen = true;
#undef CLOSE_ON_ERROR
	}

	void FileOpen()
	{
	}

	~CWavFile()
	{
		Close();
	}

	bool IsOpened()
	{
		return m_bIsOpen;
	}

	uint32 GetSampleSize()
	{
		return sizeof(uint16);
	}

	uint32 GetSampleCount()
	{
		return m_nSampleCount;
	}

	uint32 GetSampleRate()
	{
		return m_FormatHeader.SampleRate;
	}

	uint32 GetChannels()
	{
		return m_FormatHeader.NumChannels;
	}

	void Seek(uint32 milliseconds)
	{
		if (!IsOpened()) return;
		fseek(m_pFile, m_DataStartOffset + ms2samples(milliseconds) / m_nSamplesPerBlock * m_FormatHeader.BlockAlign, SEEK_SET);
	}

	uint32 Tell()
	{
		if (!IsOpened()) return 0;
		return samples2ms(GetCurrentSample());
	}

#define SAMPLES_IN_LINE (8)

	uint32 Decode(void* buffer)
	{
		if (!IsOpened()) return 0;

		if (m_FormatHeader.AudioFormat == WAVEFMT_PCM)
		{
			// just read the file and sort the samples
			uint32 size = fread(buffer, 1, GetBufferSize(), m_pFile);
			if (m_FormatHeader.NumChannels == 2)
				SortStereoBuffer.SortStereo(buffer, size);
			return size;
		}
		else if (m_FormatHeader.AudioFormat == WAVEFMT_IMA_ADPCM)
		{
			// trim the buffer size if we're at the end of our file
			uint32 nMaxSamples = GetBufferSamples() / m_FormatHeader.NumChannels;
			uint32 nSamplesLeft = m_nSampleCount - GetCurrentSample();
			nMaxSamples = Min(nMaxSamples, nSamplesLeft);

			// align sample count to our block
			nMaxSamples = nMaxSamples / m_nSamplesPerBlock * m_nSamplesPerBlock;

			// count the size of output buffer
			uint32 OutBufSizePerChannel = nMaxSamples * GetSampleSize();
			uint32 OutBufSize = OutBufSizePerChannel * m_FormatHeader.NumChannels;

			// calculate the pointers to individual channel buffers
			for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
				m_ppPcmBuffers[i] = (int16*)((int8*)buffer + OutBufSizePerChannel * i);

			uint32 samplesRead = 0;
			while (samplesRead < nMaxSamples)
			{
				// read the file
				uint8 *pAdpcmBuf = m_pAdpcmBuffer;
				if (fread(m_pAdpcmBuffer, 1, m_FormatHeader.BlockAlign, m_pFile) == 0)
					return 0;

				// get the first sample in adpcm block and initialise the decoder(s)
				for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
				{
					int16 Sample = *(int16*)pAdpcmBuf;
					pAdpcmBuf += sizeof(int16);
					int16 Step = *(int16*)pAdpcmBuf;
					pAdpcmBuf += sizeof(int16);
					m_pAdpcmDecoders[i].Init(Sample, Step);
					*(m_ppPcmBuffers[i]) = Sample;
					m_ppPcmBuffers[i]++;
				}
				samplesRead++;

				// decode the rest of the block
				for (uint32 s = 1; s < m_nSamplesPerBlock; s += SAMPLES_IN_LINE)
				{
					for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
					{
						m_pAdpcmDecoders[i].Decode(pAdpcmBuf, m_ppPcmBuffers[i], SAMPLES_IN_LINE / 2);
						pAdpcmBuf += SAMPLES_IN_LINE / 2;
						m_ppPcmBuffers[i] += SAMPLES_IN_LINE;
					}
					samplesRead += SAMPLES_IN_LINE;
				}
			}
			return OutBufSize;
		}
		return 0;
	}
};

#ifdef AUDIO_OAL_USE_SNDFILE
class CSndFile : public IDecoder
{
	SNDFILE *m_pfSound;
	SF_INFO m_soundInfo;
public:
	CSndFile(const char *path) :
		m_pfSound(nil)
	{
		memset(&m_soundInfo, 0, sizeof(m_soundInfo));
		m_pfSound = sf_open(path, SFM_READ, &m_soundInfo);
	}

	void FileOpen()
	{
	}

	~CSndFile()
	{
		if ( m_pfSound )
		{
			sf_close(m_pfSound);
			m_pfSound = nil;
		}
	}

	bool IsOpened()
	{
		return m_pfSound != nil;
	}

	uint32 GetSampleSize()
	{
		return sizeof(uint16);
	}

	uint32 GetSampleCount()
	{
		return m_soundInfo.frames;
	}

	uint32 GetSampleRate()
	{
		return m_soundInfo.samplerate;
	}

	uint32 GetChannels()
	{
		return m_soundInfo.channels;
	}

	void Seek(uint32 milliseconds)
	{
		if ( !IsOpened() ) return;
		sf_seek(m_pfSound, ms2samples(milliseconds), SF_SEEK_SET);
	}

	uint32 Tell()
	{
		if ( !IsOpened() ) return 0;
		return samples2ms(sf_seek(m_pfSound, 0, SF_SEEK_CUR));
	}

	uint32 Decode(void *buffer)
	{
		if ( !IsOpened() ) return 0;

		size_t size = sf_read_short(m_pfSound, (short*)buffer, GetBufferSamples()) * GetSampleSize();
		if (GetChannels()==2)
			SortStereoBuffer.SortStereo(buffer, size);
		return size;
	}
};
#endif

#ifdef AUDIO_OAL_USE_MPG123

class CMP3File : public IDecoder
{
protected:
	mpg123_handle *m_pMH;
	bool m_bOpened;
	uint32 m_nRate;
	uint32 m_nChannels;
	const char* m_pPath;
	bool m_bFileNotOpenedYet;
public:
	CMP3File(const char *path) :
		m_pMH(nil),
		m_bOpened(false),
		m_nRate(0),
		m_nChannels(0),
		m_pPath(path),
		m_bFileNotOpenedYet(false)
	{
		m_pMH = mpg123_new(nil, nil);
		if ( m_pMH )
		{
			mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);

			m_bOpened = true;
			m_bFileNotOpenedYet = true;
			// It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling
#if 1
			FileOpen();
#endif
		}
	}

	void FileOpen()
	{
		if(!m_bFileNotOpenedYet) return;

		long rate = 0;
		int channels = 0;
		int encoding = 0;
		m_bOpened = mpg123_open(m_pMH, m_pPath) == MPG123_OK
		        && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;

		m_nRate = rate;
		m_nChannels = channels;

		if(IsOpened()) {
			mpg123_format_none(m_pMH);
			mpg123_format(m_pMH, rate, channels, encoding);
		}
		m_bFileNotOpenedYet = false;
	}

	~CMP3File()
	{
		if ( m_pMH )
		{
			mpg123_close(m_pMH);
			mpg123_delete(m_pMH);
			m_pMH = nil;
		}
	}

	bool IsOpened()
	{
		return m_bOpened;
	}

	uint32 GetSampleSize()
	{
		return sizeof(uint16);
	}

	uint32 GetSampleCount()
	{
		if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
		return mpg123_length(m_pMH);
	}

	uint32 GetSampleRate()
	{
		return m_nRate;
	}

	uint32 GetChannels()
	{
		return m_nChannels;
	}

	void Seek(uint32 milliseconds)
	{
		if ( !IsOpened() || m_bFileNotOpenedYet ) return;
		mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET);
	}

	uint32 Tell()
	{
		if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
		return samples2ms(mpg123_tell(m_pMH));
	}

	uint32 Decode(void *buffer)
	{
		if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;

		size_t size;
		int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size);
#if defined(__LP64__) || defined(_WIN64)
		assert("We can't handle audio files more then 2 GB yet :shrug:" && (size < UINT32_MAX));
#endif
		if (err != MPG123_OK && err != MPG123_DONE) return 0;
		if (GetChannels() == 2)
			SortStereoBuffer.SortStereo(buffer, size);
		return (uint32)size;
	}
};

#endif
#define VAG_LINE_SIZE (0x10)
#define VAG_SAMPLES_IN_LINE (28)

class CVagDecoder
{
	const double f[5][2] = { { 0.0, 0.0 },
					{  60.0 / 64.0,  0.0 },
					{  115.0 / 64.0, -52.0 / 64.0 },
					{  98.0 / 64.0, -55.0 / 64.0 },
					{  122.0 / 64.0, -60.0 / 64.0 } };

	double s_1;
	double s_2;
public:
	CVagDecoder()
	{
		ResetState();
	}

	void ResetState()
	{
		s_1 = s_2 = 0.0;
	}

	static short quantize(double sample)
	{
		int a = int(sample + 0.5);
		return short(Clamp(a, -32768, 32767));
	}

	void Decode(void* _inbuf, int16* _outbuf, size_t size)
	{
		uint8* inbuf = (uint8*)_inbuf;
		int16* outbuf = _outbuf;
		size &= ~(VAG_LINE_SIZE - 1);

		while (size > 0) {
			double samples[VAG_SAMPLES_IN_LINE];

			int predict_nr, shift_factor, flags;
			predict_nr = *(inbuf++);
			shift_factor = predict_nr & 0xf;
			predict_nr >>= 4;
			flags = *(inbuf++);
			if (flags == 7) // TODO: ignore?
				break;
			for (int i = 0; i < VAG_SAMPLES_IN_LINE; i += 2) {
				int d = *(inbuf++);
				int16 s = int16((d & 0xf) << 12);
				samples[i] = (double)(s >> shift_factor);
				s = int16((d & 0xf0) << 8);
				samples[i + 1] = (double)(s >> shift_factor);
			}

			for (int i = 0; i < VAG_SAMPLES_IN_LINE; i++) {
				samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
				s_2 = s_1;
				s_1 = samples[i];
				*(outbuf++) = quantize(samples[i] + 0.5);
			}
			size -= VAG_LINE_SIZE;
		}
	}
};

#define VB_BLOCK_SIZE (0x2000)
#define NUM_VAG_LINES_IN_BLOCK (VB_BLOCK_SIZE / VAG_LINE_SIZE)
#define NUM_VAG_SAMPLES_IN_BLOCK (NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE)

class CVbFile : public IDecoder
{
	FILE        *m_pFile;
	CVagDecoder *m_pVagDecoders;

	size_t  m_FileSize;
	size_t  m_nNumberOfBlocks;

	uint32  m_nSampleRate;
	uint8   m_nChannels;
	bool    m_bBlockRead;
	uint16  m_LineInBlock;
	size_t  m_CurrentBlock;

	uint8 **m_ppVagBuffers; // buffers that cache actual ADPCM file data
	int16 **m_ppPcmBuffers;

	void ReadBlock(int32 block = -1)
	{
		// just read next block if -1
		if (block != -1)
			fseek(m_pFile, block * m_nChannels * VB_BLOCK_SIZE, SEEK_SET);

		for (int i = 0; i < m_nChannels; i++)
			fread(m_ppVagBuffers[i], VB_BLOCK_SIZE, 1, m_pFile);
		m_bBlockRead = true;
	}

public:
	CVbFile(const char* path, uint32 nSampleRate = 32000, uint8 nChannels = 2) : m_nSampleRate(nSampleRate), m_nChannels(nChannels), m_pVagDecoders(nil), m_ppVagBuffers(nil), m_ppPcmBuffers(nil),
		m_FileSize(0), m_nNumberOfBlocks(0), m_bBlockRead(false), m_LineInBlock(0), m_CurrentBlock(0)
	{
		m_pFile = fopen(path, "rb");
		if (!m_pFile) return;

		fseek(m_pFile, 0, SEEK_END);
		m_FileSize = ftell(m_pFile);
		fseek(m_pFile, 0, SEEK_SET);

		m_nNumberOfBlocks = m_FileSize / (nChannels * VB_BLOCK_SIZE);
		m_pVagDecoders = new CVagDecoder[nChannels];
		m_ppVagBuffers = new uint8*[nChannels];
		m_ppPcmBuffers = new int16*[nChannels];
		for (uint8 i = 0; i < nChannels; i++)
			m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
	}

	void FileOpen()
	{
	}

	~CVbFile()
	{
		if (m_pFile)
		{
			fclose(m_pFile);

			delete[] m_pVagDecoders;
			for (int i = 0; i < m_nChannels; i++)
				delete[] m_ppVagBuffers[i];
			delete[] m_ppVagBuffers;
			delete[] m_ppPcmBuffers;
		}
	}

	bool IsOpened()
	{
		return m_pFile != nil;
	}

	uint32 GetSampleSize()
	{
		return sizeof(uint16);
	}

	uint32 GetSampleCount()
	{
		if (!IsOpened()) return 0;
		return m_nNumberOfBlocks * NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE;
	}

	uint32 GetSampleRate()
	{
		return m_nSampleRate;
	}

	uint32 GetChannels()
	{
		return m_nChannels;
	}

	void Seek(uint32 milliseconds)
	{
		if (!IsOpened()) return;
		uint32 samples = ms2samples(milliseconds);

		// find the block of our sample
		uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK;
		if (block > m_nNumberOfBlocks)
		{
			samples = 0;
			block = 0;
		}
		if (block != m_CurrentBlock)
			m_bBlockRead = false;

		// find a line of our sample within our block
		uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK;
		uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE;

		if (m_CurrentBlock != block || m_LineInBlock != newLine)
		{
			m_CurrentBlock = block;
			m_LineInBlock = newLine;
			for (uint32 i = 0; i < GetChannels(); i++)
				m_pVagDecoders[i].ResetState();
		}

	}

	uint32 Tell()
	{
		if (!IsOpened()) return 0;
		uint32 pos = (m_CurrentBlock * NUM_VAG_LINES_IN_BLOCK + m_LineInBlock) * VAG_SAMPLES_IN_LINE;
		return samples2ms(pos);
	}

	uint32 Decode(void* buffer)
	{
		if (!IsOpened()) return 0;

		if (m_CurrentBlock >= m_nNumberOfBlocks) return 0;

		// cache current ADPCM block
		if (!m_bBlockRead)
			ReadBlock(m_CurrentBlock);

		// trim the buffer size if we're at the end of our file
		int numberOfRequiredLines = GetBufferSamples() / m_nChannels / VAG_SAMPLES_IN_LINE;
		int numberOfRemainingLines = (m_nNumberOfBlocks - m_CurrentBlock) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock;
		int bufSizePerChannel = Min(numberOfRequiredLines, numberOfRemainingLines) * VAG_SAMPLES_IN_LINE * GetSampleSize();

		// calculate the pointers to individual channel buffers
		for (uint32 i = 0; i < m_nChannels; i++)
			m_ppPcmBuffers[i] = (int16*)((int8*)buffer + bufSizePerChannel * i);

		int size = 0;
		while (size < bufSizePerChannel)
		{
			// decode the VAG lines
			for (uint32 i = 0; i < m_nChannels; i++)
			{
				m_pVagDecoders[i].Decode(m_ppVagBuffers[i] + m_LineInBlock * VAG_LINE_SIZE, m_ppPcmBuffers[i], VAG_LINE_SIZE);
				m_ppPcmBuffers[i] += VAG_SAMPLES_IN_LINE;
			}
			size += VAG_SAMPLES_IN_LINE * GetSampleSize();
			m_LineInBlock++;

			// block is over, read the next block
			if (m_LineInBlock >= NUM_VAG_LINES_IN_BLOCK)
			{
				m_CurrentBlock++;
				if (m_CurrentBlock >= m_nNumberOfBlocks) // end of file
					break;
				m_LineInBlock = 0;
				ReadBlock();
			}
		}

		return bufSizePerChannel * m_nChannels;
	}
};
#ifdef AUDIO_OAL_USE_OPUS
class COpusFile : public IDecoder
{
	OggOpusFile *m_FileH;
	bool m_bOpened;
	uint32 m_nRate;
	uint32 m_nChannels;
public:
	COpusFile(const char *path) : m_FileH(nil),
		m_bOpened(false),
		m_nRate(0),
		m_nChannels(0)
	{
		int ret;
		m_FileH = op_open_file(path, &ret);

		if (m_FileH) {
			m_nChannels = op_head(m_FileH, 0)->channel_count;
			m_nRate = 48000;
			const OpusTags *tags = op_tags(m_FileH, 0);
			for (int i = 0; i < tags->comments; i++) {
				if (strncmp(tags->user_comments[i], "SAMPLERATE", sizeof("SAMPLERATE")-1) == 0)
				{
					sscanf(tags->user_comments[i], "SAMPLERATE=%i", &m_nRate);
					break;
				}
			}

			m_bOpened = true;
		}
	}

	void FileOpen()
	{
	}

	~COpusFile()
	{
		if (m_FileH)
		{
			op_free(m_FileH);
			m_FileH = nil;
		}
	}

	bool IsOpened()
	{
		return m_bOpened;
	}

	uint32 GetSampleSize()
	{
		return sizeof(uint16);
	}

	uint32 GetSampleCount()
	{
		if ( !IsOpened() ) return 0;
		return op_pcm_total(m_FileH, 0);
	}

	uint32 GetSampleRate()
	{
		return m_nRate;
	}

	uint32 GetChannels()
	{
		return m_nChannels;
	}

	void Seek(uint32 milliseconds)
	{
		if ( !IsOpened() ) return;
		op_pcm_seek(m_FileH, ms2samples(milliseconds) / GetChannels());
	}

	uint32 Tell()
	{
		if ( !IsOpened() ) return 0;
		return samples2ms(op_pcm_tell(m_FileH) * GetChannels());
	}

	uint32 Decode(void *buffer)
	{
		if ( !IsOpened() ) return 0;

		int size = op_read(m_FileH, (opus_int16 *)buffer, GetBufferSamples(), NULL);

		if (size < 0)
			return 0;

		if (GetChannels() == 2)
			SortStereoBuffer.SortStereo(buffer, size * m_nChannels * GetSampleSize());

		return size * m_nChannels * GetSampleSize();
	}
};
#endif

// For multi-thread: Someone always acquire stream's mutex before entering here
void
CStream::BuffersShouldBeFilled()
{
#ifdef MULTITHREADED_AUDIO
	if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
		std::queue<std::pair<ALuint, ALuint>> tempQueue;
		for(int i = 0; i < NUM_STREAMBUFFERS / 2; i++) {
			tempQueue.push(std::pair<ALuint, ALuint>(m_alBuffers[i * 2], m_alBuffers[i * 2 + 1]));
		}
		m_fillBuffers.swap(tempQueue);

		FlagAsToBeProcessed();

		m_bActive = true; // to allow Update() to queue the filled buffers & play
		return;
	}
	std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
#endif
	if ( FillBuffers() != 0 )
	{
		SetPlay(true);
	}
}

// returns whether it's queued (not on multi-thread)
bool
CStream::BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>* bufs)
{
#ifdef MULTITHREADED_AUDIO
	if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE)
		m_fillBuffers.push(*bufs);
	else
#endif
	{
		ALuint alBuffers[2] = {(*bufs).first, (*bufs).second}; // left - right
		if (FillBuffer(alBuffers)) {
			alSourceQueueBuffers(m_pAlSources[0], 1, &alBuffers[0]);
			alSourceQueueBuffers(m_pAlSources[1], 1, &alBuffers[1]);
			return true;
		}
	}
	return false;
}

#ifdef MULTITHREADED_AUDIO
void
CStream::FlagAsToBeProcessed(bool close)
{
	if (!close && MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE)
		return;

	gAudioThreadQueueMutex.lock();
	if (close)
		gStreamsToClose.push(std::pair<IDecoder*, void*>(m_pSoundFile ? m_pSoundFile : nil, m_pBuffer ? m_pBuffer : nil));
	else
		gStreamsToProcess.push(this);

	gAudioThreadQueueMutex.unlock();

	gAudioThreadCv.notify_one();
}

void audioFileOpsThread()
{
	do
	{
		CStream *stream;
		{
			// Just a semaphore
			std::unique_lock<std::mutex> queueMutex(gAudioThreadQueueMutex);
			gAudioThreadCv.wait(queueMutex, [] { return gStreamsToProcess.size() > 0 || gStreamsToClose.size() > 0 || gAudioThreadTerm; });
			if (gAudioThreadTerm)
				return;

			if (!gStreamsToClose.empty()) {
				auto streamToClose = gStreamsToClose.front();
				gStreamsToClose.pop();
				if (streamToClose.first) { // pSoundFile
					delete streamToClose.first;
				}

				if (streamToClose.second) { // pBuffer
					free(streamToClose.second);
				}
			}

			if (!gStreamsToProcess.empty()) {
				stream = gStreamsToProcess.front();
				gStreamsToProcess.pop();
			} else
				continue;
		}

		std::unique_lock<std::mutex> lock(stream->m_mutex);

		std::pair<ALuint, ALuint> buffers, *lastBufAddr;
		bool insertBufsAfterCheck = false;

		do {
			if (!stream->IsOpened()) {
				break;
			}

			if (stream->m_bReset)
				break;

			// We gave up this idea for now
			/*
			stream->m_pSoundFile->FileOpen();

			// Deffered allocation, do it now
			if (stream->m_pBuffer == nil) {
				stream->m_pBuffer = malloc(stream->m_pSoundFile->GetBufferSize());
				ASSERT(stream->m_pBuffer != nil);
			}
			*/

			if (stream->m_bDoSeek) {
				stream->m_bDoSeek = false;
				int pos = stream->m_SeekPos;
				lock.unlock();
				stream->m_pSoundFile->Seek(pos);
				lock.lock();

				continue; // let's do the checks again, make sure we didn't miss anything while Seeking
			}

			if (insertBufsAfterCheck) {
				stream->m_queueBuffers.push(buffers);
				insertBufsAfterCheck = false;
			}

			if (!stream->m_fillBuffers.empty()) {
				lastBufAddr = &stream->m_fillBuffers.front();
				buffers = *lastBufAddr;
				lock.unlock();

				ALuint alBuffers[2] = {buffers.first, buffers.second}; // left - right
				bool filled = stream->FillBuffer(alBuffers);

				lock.lock();

				// Make sure queue isn't touched after we released mutex
				if (!stream->m_fillBuffers.empty() && lastBufAddr == &stream->m_fillBuffers.front()) {
					stream->m_fillBuffers.pop();
					if (filled)
						insertBufsAfterCheck = true; // Also make sure stream's properties aren't changed. So make one more pass, and push it to m_queueBuffers only if it pass checks again.
				}
			} else
				break;

		} while (true);

	} while(true);
}
#endif

void CStream::Initialise()
{
#ifdef AUDIO_OAL_USE_MPG123
	mpg123_init();
#endif
#ifdef MULTITHREADED_AUDIO
	gAudioThread = std::thread(audioFileOpsThread);
#endif
}

void CStream::Terminate()
{
#ifdef AUDIO_OAL_USE_MPG123
	mpg123_exit();
#endif
#ifdef MULTITHREADED_AUDIO
	gAudioThreadQueueMutex.lock();
	gAudioThreadTerm = true;
	gAudioThreadQueueMutex.unlock();

	gAudioThreadCv.notify_one();
	gAudioThread.join();
#endif
}

CStream::CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
	m_pAlSources(sources),
	m_alBuffers(buffers),
	m_pBuffer(nil),
	m_bPaused(false),
	m_bActive(false),
#ifdef MULTITHREADED_AUDIO
	m_bIExist(false),
	m_bDoSeek(false),
	m_SeekPos(0),
#endif
	m_pSoundFile(nil),
	m_bReset(false),
	m_nVolume(0),
	m_nPan(0),
	m_nPosBeforeReset(0),
	m_nLoopCount(1)

{
}

bool CStream::Open(const char* filename, uint32 overrideSampleRate)
{
	if (IsOpened()) return false;

#ifdef MULTITHREADED_AUDIO
	std::unique_lock<std::mutex> lock(m_mutex);

	m_bDoSeek = false;
	m_SeekPos = 0;
#endif

	m_bPaused = false;
	m_bActive = false;
	m_bReset = false;
	m_nVolume = 0;
	m_nPan = 0;
	m_nPosBeforeReset = 0;
	m_nLoopCount = 1;

// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
#if !defined(_WIN32)
	char *real = casepath(filename);
	if (real) {
		strcpy(m_aFilename, real);
		free(real);
	} else {
#else
	{
#endif
		strcpy(m_aFilename, filename);
	}

	DEV("Stream %s\n", m_aFilename);

	if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".wav")], ".wav"))
#ifdef AUDIO_OAL_USE_SNDFILE
		m_pSoundFile = new CSndFile(m_aFilename);
#else
		m_pSoundFile = new CWavFile(m_aFilename);
#endif
#ifdef AUDIO_OAL_USE_MPG123
	else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".mp3")], ".mp3"))
		m_pSoundFile = new CMP3File(m_aFilename);
#endif
	else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".vb")], ".VB"))
		m_pSoundFile = new CVbFile(m_aFilename, overrideSampleRate);
#ifdef AUDIO_OAL_USE_OPUS
	else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".opus")], ".opus"))
		m_pSoundFile = new COpusFile(m_aFilename);
#endif
	else
		m_pSoundFile = nil;

	if ( m_pSoundFile && m_pSoundFile->IsOpened() )
	{
		uint32 bufSize = m_pSoundFile->GetBufferSize();
		if(bufSize != 0) { // Otherwise it's deferred
			m_pBuffer = malloc(bufSize);
			ASSERT(m_pBuffer != nil);

			DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
			DEV("SampleCount: %d\n",      m_pSoundFile->GetSampleCount());
			DEV("SampleRate: %d\n",       m_pSoundFile->GetSampleRate());
			DEV("Channels: %d\n",         m_pSoundFile->GetChannels());
			DEV("Buffer Samples: %d\n",   m_pSoundFile->GetBufferSamples());
			DEV("Buffer sec: %f\n",       (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
			DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
		}
#ifdef MULTITHREADED_AUDIO
		m_bIExist = true;
#endif
		return true;
	}
	return false;
}

CStream::~CStream()
{
	assert(!IsOpened());
}

void CStream::Close()
{
	if(!IsOpened()) return;

#ifdef MULTITHREADED_AUDIO
	{
		std::lock_guard<std::mutex> lock(m_mutex);

		Stop();
		ClearBuffers();
		m_bIExist = false;
		std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
		tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, mutex is acquired
	}

	FlagAsToBeProcessed(true);
#else

	Stop();
	ClearBuffers();

	if ( m_pSoundFile )
	{
		delete m_pSoundFile;
		m_pSoundFile = nil;
	}

	if ( m_pBuffer )
	{
		free(m_pBuffer);
		m_pBuffer = nil;
	}
#endif
}

bool CStream::HasSource()
{
	return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE);
}

// m_bIExist only written in main thread, thus mutex is not needed on main thread
bool CStream::IsOpened()
{
#ifdef MULTITHREADED_AUDIO
	return m_bIExist;
#else
	return m_pSoundFile && m_pSoundFile->IsOpened();
#endif
}

bool CStream::IsPlaying()
{
	if ( !HasSource() || !IsOpened() ) return false;

	if ( !m_bPaused )
	{
		ALint sourceState[2];
		alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]);
		alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
		if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
			return true;

#ifdef MULTITHREADED_AUDIO
		std::lock_guard<std::mutex> lock(m_mutex);

		// Streams are designed in such a way that m_fillBuffers and m_queueBuffers will be *always* filled if audio is playing, and mutex is acquired
		if (!m_fillBuffers.empty() || !m_queueBuffers.emptyNts())
			return true;
#endif
	}

	return false;
}

void CStream::Pause()
{
	if ( !HasSource() ) return;
	ALint sourceState = AL_PAUSED;
	alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
	if (sourceState != AL_PAUSED)
		alSourcePause(m_pAlSources[0]);
	alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
	if (sourceState != AL_PAUSED)
		alSourcePause(m_pAlSources[1]);
}

void CStream::SetPause(bool bPause)
{
	if ( !HasSource() ) return;
	if ( bPause )
	{
		Pause();
		m_bPaused = true;
	}
	else
	{
		if (m_bPaused)
			SetPlay(true);
		m_bPaused = false;
	}
}

void CStream::SetPitch(float pitch)
{
	if ( !HasSource() ) return;
	alSourcef(m_pAlSources[0], AL_PITCH, pitch);
	alSourcef(m_pAlSources[1], AL_PITCH, pitch);
}

void CStream::SetGain(float gain)
{
	if ( !HasSource() ) return;
	alSourcef(m_pAlSources[0], AL_GAIN, gain);
	alSourcef(m_pAlSources[1], AL_GAIN, gain);
}

void CStream::SetPosition(int i, float x, float y, float z)
{
	if ( !HasSource() ) return;
	alSource3f(m_pAlSources[i], AL_POSITION, x, y, z);
}

void CStream::SetVolume(uint32 nVol)
{
	m_nVolume = nVol;
	SetGain(ALfloat(nVol) / MAX_VOLUME);
}

void CStream::SetPan(uint8 nPan)
{
	m_nPan = Clamp((int8)nPan - 63, 0, 63);
	SetPosition(0, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f)));

	m_nPan = Clamp((int8)nPan + 64, 64, 127);
	SetPosition(1, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f)));

	m_nPan = nPan;
}

// Should only be called if source is stopped
void CStream::SetPosMS(uint32 nPos)
{
	if ( !IsOpened() ) return;

#ifdef MULTITHREADED_AUDIO
	std::lock_guard<std::mutex> lock(m_mutex);

	std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
	tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired

	if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
		m_bDoSeek = true;
		m_SeekPos = nPos;
	} else
#endif
	{
		m_pSoundFile->Seek(nPos);
	}
	ClearBuffers();

	// adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS
}

uint32 CStream::GetPosMS()
{
	if ( !HasSource() ) return 0;
	if ( !IsOpened() ) return 0;

	// Deferred init causes division by zero
	if (m_pSoundFile->GetChannels() == 0)
		return 0;

	ALint offset;
	//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
	alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);

	//std::lock_guard<std::mutex> lock(m_mutex);

	return m_pSoundFile->Tell()
		- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
		+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
}

uint32 CStream::GetLengthMS()
{
	if ( !IsOpened() ) return 0;
	return m_pSoundFile->GetLength();
}

bool CStream::FillBuffer(ALuint *alBuffer)
{
#ifndef MULTITHREADED_AUDIO
	if ( !HasSource() )
		return false;
	if ( !IsOpened() )
		return false;
	if ( !(alBuffer[0] != AL_NONE && alIsBuffer(alBuffer[0])) )
		return false;
	if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
		return false;
#endif

	uint32 size = m_pSoundFile->Decode(m_pBuffer);
	if( size == 0 )
		return false;

	uint32 channelSize = size / m_pSoundFile->GetChannels();

	alBufferData(alBuffer[0], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
	// TODO: use just one buffer if we play mono
	if (m_pSoundFile->GetChannels() == 1)
		alBufferData(alBuffer[1], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
	else
		alBufferData(alBuffer[1], AL_FORMAT_MONO16, (uint8*)m_pBuffer + channelSize, channelSize, m_pSoundFile->GetSampleRate());
	return true;
}

#ifdef MULTITHREADED_AUDIO
bool CStream::QueueBuffers()
{
	bool buffersQueued = false;
	std::pair<ALuint, ALuint> buffers;
	while (m_queueBuffers.peekPop(&buffers)) // beware: m_queueBuffers is tsQueue
	{
		ALuint leftBuf = buffers.first;
		ALuint rightBuf = buffers.second;

		alSourceQueueBuffers(m_pAlSources[0], 1, &leftBuf);
		alSourceQueueBuffers(m_pAlSources[1], 1, &rightBuf);

		buffersQueued = true;
	}
	return buffersQueued;
}
#endif

// Only used in single-threaded audio or cutscene audio
int32 CStream::FillBuffers()
{
	int32 i = 0;
	for ( i = 0; i < NUM_STREAMBUFFERS/2; i++ )
	{
		if ( !FillBuffer(&m_alBuffers[i*2]) )
			break;
		alSourceQueueBuffers(m_pAlSources[0], 1, &m_alBuffers[i*2]);
		alSourceQueueBuffers(m_pAlSources[1], 1, &m_alBuffers[i*2+1]);
	}

	return i;
}

void CStream::ClearBuffers()
{
	if ( !HasSource() ) return;

	ALint buffersQueued[2];
	alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued[0]);
	alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &buffersQueued[1]);

	ALuint value;
	while (buffersQueued[0]--)
		alSourceUnqueueBuffers(m_pAlSources[0], 1, &value);
	while (buffersQueued[1]--)
		alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
}

bool CStream::Setup(bool imSureQueueIsEmpty, bool lock)
{
	if ( IsOpened() )
	{
#ifdef MULTITHREADED_AUDIO
		if (lock)
			m_mutex.lock();
#endif

		if (!imSureQueueIsEmpty) {
			Stop();
			ClearBuffers();
		}
#ifdef MULTITHREADED_AUDIO
		if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) {
			m_pSoundFile->Seek(0);
		} else {
			m_bDoSeek = true;
			m_SeekPos = 0;
		}

		if (lock)
			m_mutex.unlock();
#else
		m_pSoundFile->Seek(0);
#endif

		//SetPosition(0.0f, 0.0f, 0.0f);
		SetPitch(1.0f);
		//SetPan(m_nPan);
		//SetVolume(100);
	}

	return IsOpened();
}

void CStream::SetLoopCount(int32 count)
{
	if ( !HasSource() ) return;

	m_nLoopCount = count;
}

void CStream::SetPlay(bool state)
{
	if ( !HasSource() ) return;
	if ( state )
	{
		ALint sourceState = AL_PLAYING;
		alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
		if (sourceState != AL_PLAYING )
			alSourcePlay(m_pAlSources[0]);

		sourceState = AL_PLAYING;
		alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
		if (sourceState != AL_PLAYING)
			alSourcePlay(m_pAlSources[1]);

		m_bActive = true;
	}
	else
	{
		ALint sourceState = AL_STOPPED;
		alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
		if (sourceState != AL_STOPPED)
			alSourceStop(m_pAlSources[0]);

		sourceState = AL_STOPPED;
		alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
		if (sourceState != AL_STOPPED)
			alSourceStop(m_pAlSources[1]);

		m_bActive = false;
	}
}

void CStream::Start()
{
	if ( !HasSource() ) return;

#ifdef MULTITHREADED_AUDIO
	std::lock_guard<std::mutex> lock(m_mutex);
	tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
#endif
	BuffersShouldBeFilled();
}

void CStream::Stop()
{
	if ( !HasSource() ) return;
	SetPlay(false);
}

void CStream::Update()
{
	if ( !IsOpened() )
		return;

	if ( !HasSource() )
		return;

	if ( m_bReset )
		return;

	if ( !m_bPaused )
	{

		bool buffersQueuedAndStarted = false;
		bool buffersQueuedButNotStarted = false;
#ifdef MULTITHREADED_AUDIO
		// Put it in here because we need totalBuffers after queueing to decide when to loop audio
		if (m_bActive)
		{
			buffersQueuedAndStarted = QueueBuffers();
			if(buffersQueuedAndStarted) {
				SetPlay(true);
			}
		}
#endif

		ALint totalBuffers[2] = {0, 0};
		ALint buffersProcessed[2] = {0, 0};

		// Relying a lot on left buffer states in here

		do
		{
			//alSourcef(m_pAlSources[0], AL_ROLLOFF_FACTOR, 0.0f);
			alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &totalBuffers[0]);
			alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]);
			//alSourcef(m_pAlSources[1], AL_ROLLOFF_FACTOR, 0.0f);
			alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &totalBuffers[1]);
			alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]);
		} while (buffersProcessed[0] != buffersProcessed[1]);

		assert(buffersProcessed[0] == buffersProcessed[1]);

		// Correcting OpenAL concepts here:
		// AL_BUFFERS_QUEUED = Number of *all* buffers in queue, including processed, processing and pending
		// AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers.
		// which means: totalBuffers[0] - buffersProcessed[0] = pending buffers

		// We should wait queue to be cleared to loop track, because position calculation relies on queue.
		if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0)
		{
#ifdef MULTITHREADED_AUDIO
			std::lock_guard<std::mutex> lock(m_mutex);

			if (m_fillBuffers.empty() && m_queueBuffers.emptyNts()) // we already acquired stream mutex, which is enough for second thread. thus Nts variant
#endif
			{
				Setup(true, false);
				BuffersShouldBeFilled(); // will also call SetPlay(true)
				if (m_nLoopCount != 0)
					m_nLoopCount--;
			}
		}
		else
		{
			static std::queue<std::pair<ALuint, ALuint>> tempFillBuffer;

			while ( buffersProcessed[0]-- )
			{
				ALuint buffer[2];

				alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
				alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);

				if (m_bActive)
				{
					tempFillBuffer.push(std::pair<ALuint, ALuint>(buffer[0], buffer[1]));
				}
			}

			if (m_bActive && buffersProcessed[1])
			{
#ifdef MULTITHREADED_AUDIO
				m_mutex.lock();
#endif
				while (!tempFillBuffer.empty()) {
					auto elem = tempFillBuffer.front();
					tempFillBuffer.pop();
					buffersQueuedButNotStarted = BufferShouldBeFilledAndQueued(&elem);
				}
#ifdef MULTITHREADED_AUDIO
				m_mutex.unlock();
				FlagAsToBeProcessed();
#endif

			}
		}

		// Source may be starved to audio and stopped itself
		if (m_bActive && !buffersQueuedAndStarted && (buffersQueuedButNotStarted || (totalBuffers[1] - buffersProcessed[1] != 0)))
			SetPlay(true);
	}
}

void CStream::ProviderInit()
{
	if ( m_bReset )
	{
		if ( Setup(true, false) ) // lock not needed, thread can't process streams with m_bReset set
		{
			SetPan(m_nPan);
			SetVolume(m_nVolume);
			SetLoopCount(m_nLoopCount);
			SetPosMS(m_nPosBeforeReset);
#ifdef MULTITHREADED_AUDIO
			std::unique_lock<std::mutex> lock(m_mutex);
#endif
			if(m_bActive)
				BuffersShouldBeFilled();

			if (m_bPaused)
				Pause();

			m_bReset = false;

		} else {
#ifdef MULTITHREADED_AUDIO
			std::unique_lock<std::mutex> lock(m_mutex);
#endif
			m_bReset = false;
		}
	}
}

void CStream::ProviderTerm()
{
#ifdef MULTITHREADED_AUDIO
	std::lock_guard<std::mutex> lock(m_mutex);

	// unlike Close() we will reuse this stream, so clearing queues are important.
	std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
	tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // stream mutex is already acquired, thus Nts variant
#endif
	m_bReset = true;
	m_nPosBeforeReset = GetPosMS();

	Stop();
	ClearBuffers();
}

#endif


code:
#pragma once

#ifdef AUDIO_OAL
#include <AL/al.h>

#define NUM_STREAMBUFFERS 8

class IDecoder
{
public:
	virtual ~IDecoder() { }

	virtual bool   IsOpened() = 0;
	virtual void   FileOpen() = 0;

	virtual uint32 GetSampleSize() = 0;
	virtual uint32 GetSampleCount() = 0;
	virtual uint32 GetSampleRate() = 0;
	virtual uint32 GetChannels() = 0;

	uint32 GetAvgSamplesPerSec()
	{
		return GetChannels() * GetSampleRate();
	}

	uint32 ms2samples(uint32 ms)
	{
		return float(ms) / 1000.0f * float(GetSampleRate());
	}

	uint32 samples2ms(uint32 sm)
	{
		return float(sm) * 1000.0f / float(GetSampleRate());
	}

	uint32 GetBufferSamples()
	{
		//return (GetAvgSamplesPerSec() >> 2) - (GetSampleCount() % GetChannels());
		return (GetAvgSamplesPerSec() / 4); // 250ms
	}

	uint32 GetBufferSize()
	{
		return GetBufferSamples() * GetSampleSize();
	}

	virtual void   Seek(uint32 milliseconds) = 0;
	virtual uint32 Tell() = 0;

	uint32 GetLength()
	{
		FileOpen(); // abort deferred init, we need length now - game has to cache audio file sizes
		return float(GetSampleCount()) * 1000.0f / float(GetSampleRate());
	}

	virtual uint32 Decode(void *buffer) = 0;
};
#ifdef MULTITHREADED_AUDIO
template <typename T> class tsQueue
{
public:
	tsQueue() : count(0) { }

	void push(const T &value)
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		m_queue.push(value);
		count++;
	}

    bool peekPop(T *retVal)
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (count == 0)
            return false;

        *retVal = m_queue.front();
        m_queue.pop();
        count--;
        return true;
    }

	void swapNts(tsQueue<T> &replaceWith)
	{
		m_queue.swap(replaceWith.m_queue);
		replaceWith.count = count;
	}

	/*
	void swapTs(tsQueue<T> &replaceWith)
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		std::lock_guard<std::mutex> lock2(replaceWith.m_mutex);
		swapNts(replaceWith);
	}
	*/

	bool emptyNts()
	{
		return count == 0;
	}

	/*
	bool emptyTs()
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		return emptyNts();
	}
	*/

	std::queue<T> m_queue;
	int count;
	mutable std::mutex m_mutex;
};
#endif
class CStream
{
	char     m_aFilename[128];
	ALuint  *m_pAlSources;
	ALuint (&m_alBuffers)[NUM_STREAMBUFFERS];

	bool     m_bPaused;
	bool     m_bActive;

public:
#ifdef MULTITHREADED_AUDIO
	std::mutex	m_mutex;
	std::queue<std::pair<ALuint, ALuint>> m_fillBuffers; // left and right buffer
	tsQueue<std::pair<ALuint, ALuint>> m_queueBuffers;
//	std::condition_variable m_closeCv;
	bool     m_bDoSeek;
	uint32   m_SeekPos;
	bool	 m_bIExist;
#endif

	void    *m_pBuffer;

	bool     m_bReset;
	uint32   m_nVolume;
	uint8    m_nPan;
	uint32   m_nPosBeforeReset;
	int32   m_nLoopCount;

	IDecoder *m_pSoundFile;

	void BuffersShouldBeFilled(); // all
	bool BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>*); // two (left-right)
#ifdef MULTITHREADED_AUDIO
	void FlagAsToBeProcessed(bool close = false);
	bool QueueBuffers();
#endif

	bool HasSource();
	void SetPosition(int i, float x, float y, float z);
	void SetPitch(float pitch);
	void SetGain(float gain);
	void   Pause();
	void   SetPlay(bool state);

	bool   FillBuffer(ALuint *alBuffer);
	int32	FillBuffers();
	void   ClearBuffers();
//public:
	static void Initialise();
	static void Terminate();

	CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]);
	~CStream();
	void   Delete();
	bool   Open(const char *filename, uint32 overrideSampleRate = 32000);
	void   Close();

	bool   IsOpened();
	bool   IsPlaying();
	void   SetPause (bool bPause);
	void   SetVolume(uint32 nVol);
	void   SetPan   (uint8 nPan);
	void   SetPosMS (uint32 nPos);
	uint32 GetPosMS();
	uint32 GetLengthMS();

	bool Setup(bool imSureQueueIsEmpty = false, bool lock = true);
	void Start();
	void Stop();
	void Update(void);
	void SetLoopCount(int32);

	void ProviderInit();
	void ProviderTerm();
};

#endif


code:
#pragma once
#include "AudioSamples.h"
#include "audio_enums.h"

#define MAX_VOLUME 127
#define MAX_FREQ DIGITALRATE

struct tSample {
	uint32 nOffset;
	uint32 nSize;
	uint32 nFrequency;
	uint32 nLoopStart;
	int32 nLoopEnd;
};

#ifdef GTA_PS2
#define PS2BANK(e) e
#else
#define PS2BANK(e) e = SFX_BANK_0
#endif // GTA_PS2

enum
{
	SFX_BANK_0,

	CAR_SFX_BANKS_OFFSET,
	SFX_BANK_PACARD = CAR_SFX_BANKS_OFFSET,
	SFX_BANK_PATHFINDER,
	SFX_BANK_PORSCHE,
	SFX_BANK_SPIDER,
	SFX_BANK_MERC,
	SFX_BANK_TRUCK,
	SFX_BANK_HOTROD,
	SFX_BANK_COBRA,
	SFX_BANK_NONE,

	PS2BANK(SFX_BANK_FRONT_END_MENU),

	PS2BANK(SFX_BANK_TRAIN),

	PS2BANK(SFX_BANK_BUILDING_CLUB_1),
	PS2BANK(SFX_BANK_BUILDING_CLUB_2),
	PS2BANK(SFX_BANK_BUILDING_CLUB_3),
	PS2BANK(SFX_BANK_BUILDING_CLUB_4),
	PS2BANK(SFX_BANK_BUILDING_CLUB_5),
	PS2BANK(SFX_BANK_BUILDING_CLUB_6),
	PS2BANK(SFX_BANK_BUILDING_CLUB_7),
	PS2BANK(SFX_BANK_BUILDING_CLUB_8),
	PS2BANK(SFX_BANK_BUILDING_CLUB_9),
	PS2BANK(SFX_BANK_BUILDING_CLUB_10),
	PS2BANK(SFX_BANK_BUILDING_CLUB_11),
	PS2BANK(SFX_BANK_BUILDING_CLUB_12),
	PS2BANK(SFX_BANK_BUILDING_CLUB_RAGGA),
	PS2BANK(SFX_BANK_BUILDING_STRIP_CLUB_1),
	PS2BANK(SFX_BANK_BUILDING_STRIP_CLUB_2),
	PS2BANK(SFX_BANK_BUILDING_WORKSHOP),
	PS2BANK(SFX_BANK_BUILDING_PIANO_BAR),
	PS2BANK(SFX_BANK_BUILDING_SAWMILL),
	PS2BANK(SFX_BANK_BUILDING_DOG_FOOD_FACTORY),
	PS2BANK(SFX_BANK_BUILDING_LAUNDERETTE),
	PS2BANK(SFX_BANK_BUILDING_RESTAURANT_CHINATOWN),
	PS2BANK(SFX_BANK_BUILDING_RESTAURANT_ITALY),
	PS2BANK(SFX_BANK_BUILDING_RESTAURANT_GENERIC_1),
	PS2BANK(SFX_BANK_BUILDING_RESTAURANT_GENERIC_2),
	PS2BANK(SFX_BANK_BUILDING_AIRPORT),
	PS2BANK(SFX_BANK_BUILDING_SHOP),
	PS2BANK(SFX_BANK_BUILDING_CINEMA),
	PS2BANK(SFX_BANK_BUILDING_DOCKS),
	PS2BANK(SFX_BANK_BUILDING_HOME),
	PS2BANK(SFX_BANK_BUILDING_PORN_1),
	PS2BANK(SFX_BANK_BUILDING_PORN_2),
	PS2BANK(SFX_BANK_BUILDING_PORN_3),
	PS2BANK(SFX_BANK_BUILDING_POLICE_BALL),
	PS2BANK(SFX_BANK_BUILDING_BANK_ALARM),
	PS2BANK(SFX_BANK_BUILDING_RAVE_INDUSTRIAL),
	PS2BANK(SFX_BANK_BUILDING_RAVE_COMMERCIAL),
	PS2BANK(SFX_BANK_BUILDING_RAVE_SUBURBAN),
	PS2BANK(SFX_BANK_BUILDING_RAVE_COMMERCIAL_2),

	PS2BANK(SFX_BANK_BUILDING_39),
	PS2BANK(SFX_BANK_BUILDING_40),
	PS2BANK(SFX_BANK_BUILDING_41),
	PS2BANK(SFX_BANK_BUILDING_42),
	PS2BANK(SFX_BANK_BUILDING_43),
	PS2BANK(SFX_BANK_BUILDING_44),
	PS2BANK(SFX_BANK_BUILDING_45),
	PS2BANK(SFX_BANK_BUILDING_46),
	PS2BANK(SFX_BANK_BUILDING_47),

	PS2BANK(SFX_BANK_GENERIC_EXTRA),

	SFX_BANK_PED_COMMENTS,
	MAX_SFX_BANKS,
	INVALID_SFX_BANK
};

#define MAX_PEDSFX                 7
#define PED_BLOCKSIZE              79000

#define MAXPROVIDERS               64

#ifdef EXTERNAL_3D_SOUND
#define MAXCHANNELS                (NUM_CHANNELS_GENERIC+1)
#define MAXCHANNELS_SURROUND       (MAXCHANNELS-4)
#define MAX2DCHANNELS              1
#else
#define MAXCHANNELS                0
#define MAXCHANNELS_SURROUND       0
#define MAX2DCHANNELS              NUM_CHANNELS
#endif

#define MAX_STREAMS                2

#define DIGITALRATE                32000
#define DIGITALBITS                16
#define DIGITALCHANNELS            2

#ifdef FIX_BUGS
#define MAX_DIGITAL_MIXER_CHANNELS (MAXCHANNELS+MAX_STREAMS*2+MAX2DCHANNELS)
#else
#define MAX_DIGITAL_MIXER_CHANNELS (MAXCHANNELS+MAX_STREAMS*2)
#endif

static_assert( NUM_CHANNELS == MAXCHANNELS + MAX2DCHANNELS, "The number of channels doesn't match with an enum" );

class cSampleManager
{
	uint8   m_nEffectsVolume;
	uint8   m_nMusicVolume;
	uint8   m_nEffectsFadeVolume;
	uint8   m_nMusicFadeVolume;
	bool8   m_nMonoMode;
	char	unk;
	char    m_szCDRomRootPath[80];
	bool8   m_bInitialised;
	uint8   m_nNumberOfProviders;
	char   *m_aAudioProviders[MAXPROVIDERS];
	tSample m_aSamples[TOTAL_AUDIO_SAMPLES];

public:

	cSampleManager(void);
	~cSampleManager(void);

#ifdef EXTERNAL_3D_SOUND
	void SetSpeakerConfig(int32 nConfig);
	uint32 GetMaximumSupportedChannels(void);

	uint32 GetNum3DProvidersAvailable(void);
	void SetNum3DProvidersAvailable(uint32 num);

	char *Get3DProviderName(uint8 id);
	void Set3DProviderName(uint8 id, char *name);

	int8 GetCurrent3DProviderIndex(void);
	int8 SetCurrent3DProvider(uint8 which);
#endif

	bool8 IsMP3RadioChannelAvailable(void);

	void ReleaseDigitalHandle  (void);
	void ReacquireDigitalHandle(void);

	bool8 Initialise(void);
	void  Terminate (void);

	bool8 CheckForAnAudioFileOnCD(void);
	char  GetCDAudioDriveLetter  (void);

	void UpdateEffectsVolume(void);

	void SetEffectsMasterVolume(uint8 nVolume);
	void SetMusicMasterVolume  (uint8 nVolume);
	void SetEffectsFadeVolume  (uint8 nVolume);
	void SetMusicFadeVolume    (uint8 nVolume);
	void SetMonoMode           (bool8 nMode);

	bool8 LoadSampleBank    (uint8 nBank);
	void  UnloadSampleBank  (uint8 nBank);
	int8  IsSampleBankLoaded(uint8 nBank);

	uint8 IsPedCommentLoaded(uint32 nComment);
	bool8 LoadPedComment    (uint32 nComment);
	int32 GetBankContainingSound(uint32 offset);

	int32 _GetPedCommentSlot(uint32 nComment);

	uint32 GetSampleBaseFrequency  (uint32 nSample);
	uint32 GetSampleLoopStartOffset(uint32 nSample);
	int32  GetSampleLoopEndOffset  (uint32 nSample);
	uint32 GetSampleLength         (uint32 nSample);

	bool8 UpdateReverb(void);

	void  SetChannelReverbFlag    (uint32 nChannel, bool8 nReverbFlag);
	bool8 InitialiseChannel       (uint32 nChannel, uint32 nSfx, uint8 nBank);
#ifdef EXTERNAL_3D_SOUND
	void  SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume);
	void  SetChannel3DPosition    (uint32 nChannel, float fX, float fY, float fZ);
	void  SetChannel3DDistances   (uint32 nChannel, float fMax, float fMin);
#endif
	void  SetChannelVolume        (uint32 nChannel, uint32 nVolume);
	void  SetChannelPan           (uint32 nChannel, uint32 nPan);
	void  SetChannelFrequency     (uint32 nChannel, uint32 nFreq);
	void  SetChannelLoopPoints    (uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd);
	void  SetChannelLoopCount     (uint32 nChannel, uint32 nLoopCount);
	bool8 GetChannelUsedFlag      (uint32 nChannel);
	void  StartChannel            (uint32 nChannel);
	void  StopChannel             (uint32 nChannel);

	void  PreloadStreamedFile                                     (uint8 nFile, uint8 nStream = 0);
	void  PauseStream                                        (bool8 nPauseFlag, uint8 nStream = 0);
	void  StartPreloadedStreamedFile                                           (uint8 nStream = 0);
	bool8 StartStreamedFile                          (uint8 nFile, uint32 nPos, uint8 nStream = 0);
	void  StopStreamedFile                                                     (uint8 nStream = 0);
	int32 GetStreamedFilePosition                                              (uint8 nStream = 0);
	void  SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream = 0);
	int32 GetStreamedFileLength                                                (uint8 nStream = 0);
	bool8 IsStreamPlaying                                                      (uint8 nStream = 0);
#ifdef AUDIO_OAL
	void  Service(void);
#endif
	bool8 InitialiseSampleBanks(void);
};

extern cSampleManager SampleManager;
extern uint32 BankStartOffset[MAX_SFX_BANKS];

#ifdef AUDIO_OAL
extern int defaultProvider;
#endif

#if defined(OPUS_AUDIO_PATHS)
static char StreamedNameTable[][25] = {
    "AUDIO\\HEAD.OPUS",    "AUDIO\\CLASS.OPUS",   "AUDIO\\KJAH.OPUS",    "AUDIO\\RISE.OPUS",    "AUDIO\\LIPS.OPUS",    "AUDIO\\GAME.OPUS",
    "AUDIO\\MSX.OPUS",     "AUDIO\\FLASH.OPUS",   "AUDIO\\CHAT.OPUS",    "AUDIO\\HEAD.OPUS",    "AUDIO\\POLICE.OPUS",  "AUDIO\\CITY.OPUS",
    "AUDIO\\WATER.OPUS",   "AUDIO\\COMOPEN.OPUS", "AUDIO\\SUBOPEN.OPUS", "AUDIO\\JB.OPUS",      "AUDIO\\BET.OPUS",     "AUDIO\\L1_LG.OPUS",
    "AUDIO\\L2_DSB.OPUS",  "AUDIO\\L3_DM.OPUS",   "AUDIO\\L4_PAP.OPUS",  "AUDIO\\L5_TFB.OPUS",  "AUDIO\\J0_DM2.OPUS",  "AUDIO\\J1_LFL.OPUS",
    "AUDIO\\J2_KCL.OPUS",  "AUDIO\\J3_VH.OPUS",   "AUDIO\\J4_ETH.OPUS",  "AUDIO\\J5_DST.OPUS",  "AUDIO\\J6_TBJ.OPUS",  "AUDIO\\T1_TOL.OPUS",
    "AUDIO\\T2_TPU.OPUS",  "AUDIO\\T3_MAS.OPUS",  "AUDIO\\T4_TAT.OPUS",  "AUDIO\\T5_BF.OPUS",   "AUDIO\\S0_MAS.OPUS",  "AUDIO\\S1_PF.OPUS",
    "AUDIO\\S2_CTG.OPUS",  "AUDIO\\S3_RTC.OPUS",  "AUDIO\\S5_LRQ.OPUS",  "AUDIO\\S4_BDBA.OPUS", "AUDIO\\S4_BDBB.OPUS", "AUDIO\\S2_CTG2.OPUS",
    "AUDIO\\S4_BDBD.OPUS", "AUDIO\\S5_LRQB.OPUS", "AUDIO\\S5_LRQC.OPUS", "AUDIO\\A1_SSO.OPUS",  "AUDIO\\A2_PP.OPUS",   "AUDIO\\A3_SS.OPUS",
    "AUDIO\\A4_PDR.OPUS",  "AUDIO\\A5_K2FT.OPUS", "AUDIO\\K1_KBO.OPUS",  "AUDIO\\K2_GIS.OPUS",  "AUDIO\\K3_DS.OPUS",   "AUDIO\\K4_SHI.OPUS",
    "AUDIO\\K5_SD.OPUS",   "AUDIO\\R0_PDR2.OPUS", "AUDIO\\R1_SW.OPUS",   "AUDIO\\R2_AP.OPUS",   "AUDIO\\R3_ED.OPUS",   "AUDIO\\R4_GF.OPUS",
    "AUDIO\\R5_PB.OPUS",   "AUDIO\\R6_MM.OPUS",   "AUDIO\\D1_STOG.OPUS", "AUDIO\\D2_KK.OPUS",   "AUDIO\\D3_ADO.OPUS",  "AUDIO\\D5_ES.OPUS",
    "AUDIO\\D7_MLD.OPUS",  "AUDIO\\D4_GTA.OPUS",  "AUDIO\\D4_GTA2.OPUS", "AUDIO\\D6_STS.OPUS",  "AUDIO\\A6_BAIT.OPUS", "AUDIO\\A7_ETG.OPUS",
    "AUDIO\\A8_PS.OPUS",   "AUDIO\\A9_ASD.OPUS",  "AUDIO\\K4_SHI2.OPUS", "AUDIO\\C1_TEX.OPUS",  "AUDIO\\EL_PH1.OPUS",  "AUDIO\\EL_PH2.OPUS",
    "AUDIO\\EL_PH3.OPUS",  "AUDIO\\EL_PH4.OPUS",  "AUDIO\\YD_PH1.OPUS",  "AUDIO\\YD_PH2.OPUS",  "AUDIO\\YD_PH3.OPUS",  "AUDIO\\YD_PH4.OPUS",
    "AUDIO\\HD_PH1.OPUS",  "AUDIO\\HD_PH2.OPUS",  "AUDIO\\HD_PH3.OPUS",  "AUDIO\\HD_PH4.OPUS",  "AUDIO\\HD_PH5.OPUS",  "AUDIO\\MT_PH1.OPUS",
    "AUDIO\\MT_PH2.OPUS",  "AUDIO\\MT_PH3.OPUS",  "AUDIO\\MT_PH4.OPUS",  "AUDIO\\MISCOM.OPUS",  "AUDIO\\END.OPUS",     "AUDIO\\lib_a1.OPUS",
    "AUDIO\\lib_a2.OPUS",  "AUDIO\\lib_a.OPUS",   "AUDIO\\lib_b.OPUS",   "AUDIO\\lib_c.OPUS",   "AUDIO\\lib_d.OPUS",   "AUDIO\\l2_a.OPUS",
    "AUDIO\\j4t_1.OPUS",   "AUDIO\\j4t_2.OPUS",   "AUDIO\\j4t_3.OPUS",   "AUDIO\\j4t_4.OPUS",   "AUDIO\\j4_a.OPUS",    "AUDIO\\j4_b.OPUS",
    "AUDIO\\j4_c.OPUS",    "AUDIO\\j4_d.OPUS",    "AUDIO\\j4_e.OPUS",    "AUDIO\\j4_f.OPUS",    "AUDIO\\j6_1.OPUS",    "AUDIO\\j6_a.OPUS",
    "AUDIO\\j6_b.OPUS",    "AUDIO\\j6_c.OPUS",    "AUDIO\\j6_d.OPUS",    "AUDIO\\t4_a.OPUS",    "AUDIO\\s1_a.OPUS",    "AUDIO\\s1_a1.OPUS",
    "AUDIO\\s1_b.OPUS",    "AUDIO\\s1_c.OPUS",    "AUDIO\\s1_c1.OPUS",   "AUDIO\\s1_d.OPUS",    "AUDIO\\s1_e.OPUS",    "AUDIO\\s1_f.OPUS",
    "AUDIO\\s1_g.OPUS",    "AUDIO\\s1_h.OPUS",    "AUDIO\\s1_i.OPUS",    "AUDIO\\s1_j.OPUS",    "AUDIO\\s1_k.OPUS",    "AUDIO\\s1_l.OPUS",
    "AUDIO\\s3_a.OPUS",    "AUDIO\\s3_b.OPUS",    "AUDIO\\el3_a.OPUS",   "AUDIO\\mf1_a.OPUS",   "AUDIO\\mf2_a.OPUS",   "AUDIO\\mf3_a.OPUS",
    "AUDIO\\mf3_b.OPUS",   "AUDIO\\mf3_b1.OPUS",  "AUDIO\\mf3_c.OPUS",   "AUDIO\\mf4_a.OPUS",   "AUDIO\\mf4_b.OPUS",   "AUDIO\\mf4_c.OPUS",
    "AUDIO\\a1_a.OPUS",    "AUDIO\\a3_a.OPUS",    "AUDIO\\a5_a.OPUS",    "AUDIO\\a4_a.OPUS",    "AUDIO\\a4_b.OPUS",    "AUDIO\\a4_c.OPUS",
    "AUDIO\\a4_d.OPUS",    "AUDIO\\k1_a.OPUS",    "AUDIO\\k3_a.OPUS",    "AUDIO\\r1_a.OPUS",    "AUDIO\\r2_a.OPUS",    "AUDIO\\r2_b.OPUS",
    "AUDIO\\r2_c.OPUS",    "AUDIO\\r2_d.OPUS",    "AUDIO\\r2_e.OPUS",    "AUDIO\\r2_f.OPUS",    "AUDIO\\r2_g.OPUS",    "AUDIO\\r2_h.OPUS",
    "AUDIO\\r5_a.OPUS",    "AUDIO\\r6_a.OPUS",    "AUDIO\\r6_a1.OPUS",   "AUDIO\\r6_b.OPUS",    "AUDIO\\lo2_a.OPUS",   "AUDIO\\lo6_a.OPUS",
    "AUDIO\\yd2_a.OPUS",   "AUDIO\\yd2_b.OPUS",   "AUDIO\\yd2_c.OPUS",   "AUDIO\\yd2_c1.OPUS",  "AUDIO\\yd2_d.OPUS",   "AUDIO\\yd2_e.OPUS",
    "AUDIO\\yd2_f.OPUS",   "AUDIO\\yd2_g.OPUS",   "AUDIO\\yd2_h.OPUS",   "AUDIO\\yd2_ass.OPUS", "AUDIO\\yd2_ok.OPUS",  "AUDIO\\h5_a.OPUS",
    "AUDIO\\h5_b.OPUS",    "AUDIO\\h5_c.OPUS",    "AUDIO\\ammu_a.OPUS",  "AUDIO\\ammu_b.OPUS",  "AUDIO\\ammu_c.OPUS",  "AUDIO\\door_1.OPUS",
    "AUDIO\\door_2.OPUS",  "AUDIO\\door_3.OPUS",  "AUDIO\\door_4.OPUS",  "AUDIO\\door_5.OPUS",  "AUDIO\\door_6.OPUS",  "AUDIO\\t3_a.OPUS",
    "AUDIO\\t3_b.OPUS",    "AUDIO\\t3_c.OPUS",    "AUDIO\\k1_b.OPUS",    "AUDIO\\cat1.OPUS"};
#else
#ifdef PS2_AUDIO_PATHS
static char PS2StreamedNameTable[][25]=
{
	"AUDIO\\MUSIC\\HEAD.VB",
	"AUDIO\\MUSIC\\CLASS.VB",
	"AUDIO\\MUSIC\\KJAH.VB",
	"AUDIO\\MUSIC\\RISE.VB",
	"AUDIO\\MUSIC\\LIPS.VB",
	"AUDIO\\MUSIC\\GAME.VB",
	"AUDIO\\MUSIC\\MSX.VB",
	"AUDIO\\MUSIC\\FLASH.VB",
	"AUDIO\\MUSIC\\CHAT.VB",
	"AUDIO\\MUSIC\\HEAD.VB",
	"AUDIO\\MUSIC\\POLICE.VB",
	"AUDIO\\MUSIC\\CITY.VB",
	"AUDIO\\MUSIC\\WATER.VB",
	"AUDIO\\MUSIC\\COMOPEN.VB",
	"AUDIO\\MUSIC\\SUBOPEN.VB",
	"AUDIO\\OTHER\\JB.VB",
	"AUDIO\\OTHER\\BET.VB",
	"AUDIO\\LUIGI\\L1_LG.VB",
	"AUDIO\\LUIGI\\L2_DSB.VB",
	"AUDIO\\LUIGI\\L3_DM.VB",
	"AUDIO\\LUIGI\\L4_PAP.VB",
	"AUDIO\\LUIGI\\L5_TFB.VB",
	"AUDIO\\JOEY\\J0_DM2.VB",
	"AUDIO\\JOEY\\J1_LFL.VB",
	"AUDIO\\JOEY\\J2_KCL.VB",
	"AUDIO\\JOEY\\J3_VH.VB",
	"AUDIO\\JOEY\\J4_ETH.VB",
	"AUDIO\\JOEY\\J5_DST.VB",
	"AUDIO\\JOEY\\J6_TBJ.VB",
	"AUDIO\\TONI\\T1_TOL.VB",
	"AUDIO\\TONI\\T2_TPU.VB",
	"AUDIO\\TONI\\T3_MAS.VB",
	"AUDIO\\TONI\\T4_TAT.VB",
	"AUDIO\\TONI\\T5_BF.VB",
	"AUDIO\\SAL\\S0_MAS.VB",
	"AUDIO\\SAL\\S1_PF.VB",
	"AUDIO\\SAL\\S2_CTG.VB",
	"AUDIO\\SAL\\S3_RTC.VB",
	"AUDIO\\SAL\\S5_LRQ.VB",
	"AUDIO\\EBALL\\S4_BDBA.VB",
	"AUDIO\\EBALL\\S4_BDBB.VB",
	"AUDIO\\SAL\\S2_CTG2.VB",
	"AUDIO\\SAL\\S4_BDBD.VB",
	"AUDIO\\SAL\\S5_LRQB.VB",
	"AUDIO\\SAL\\S5_LRQC.VB",
	"AUDIO\\ASUKA\\A1_SSO.VB",
	"AUDIO\\ASUKA\\A2_PP.VB",
	"AUDIO\\ASUKA\\A3_SS.VB",
	"AUDIO\\ASUKA\\A4_PDR.VB",
	"AUDIO\\ASUKA\\A5_K2FT.VB",
	"AUDIO\\KENJI\\K1_KBO.VB",
	"AUDIO\\KENJI\\K2_GIS.VB",
	"AUDIO\\KENJI\\K3_DS.VB",
	"AUDIO\\KENJI\\K4_SHI.VB",
	"AUDIO\\KENJI\\K5_SD.VB",
	"AUDIO\\RAY\\R0_PDR2.VB",
	"AUDIO\\RAY\\R1_SW.VB",
	"AUDIO\\RAY\\R2_AP.VB",
	"AUDIO\\RAY\\R3_ED.VB",
	"AUDIO\\RAY\\R4_GF.VB",
	"AUDIO\\RAY\\R5_PB.VB",
	"AUDIO\\RAY\\R6_MM.VB",
	"AUDIO\\LOVE\\D1_STOG.VB",
	"AUDIO\\LOVE\\D2_KK.VB",
	"AUDIO\\LOVE\\D3_ADO.VB",
	"AUDIO\\LOVE\\D5_ES.VB",
	"AUDIO\\LOVE\\D7_MLD.VB",
	"AUDIO\\LOVE\\D4_GTA.VB",
	"AUDIO\\LOVE\\D4_GTA2.VB",
	"AUDIO\\LOVE\\D6_STS.VB",
	"AUDIO\\ASUKA\\A6_BAIT.VB",
	"AUDIO\\ASUKA\\A7_ETG.VB",
	"AUDIO\\ASUKA\\A8_PS.VB",
	"AUDIO\\ASUKA\\A9_ASD.VB",
	"AUDIO\\SHOP\\K4_SHI2.VB",
	"AUDIO\\OTHER\\C1_TEX.VB",
	"AUDIO\\PHONE\\EL_PH1.VB",
	"AUDIO\\PHONE\\EL_PH2.VB",
	"AUDIO\\PHONE\\EL_PH3.VB",
	"AUDIO\\PHONE\\EL_PH4.VB",
	"AUDIO\\PHONE\\YD_PH1.VB",
	"AUDIO\\PHONE\\YD_PH2.VB",
	"AUDIO\\PHONE\\YD_PH3.VB",
	"AUDIO\\PHONE\\YD_PH4.VB",
	"AUDIO\\PHONE\\HD_PH1.VB",
	"AUDIO\\PHONE\\HD_PH2.VB",
	"AUDIO\\PHONE\\HD_PH3.VB",
	"AUDIO\\PHONE\\HD_PH4.VB",
	"AUDIO\\PHONE\\HD_PH5.VB",
	"AUDIO\\PHONE\\MT_PH1.VB",
	"AUDIO\\PHONE\\MT_PH2.VB",
	"AUDIO\\PHONE\\MT_PH3.VB",
	"AUDIO\\PHONE\\MT_PH4.VB",
	"AUDIO\\MUSIC\\MISCOM.VB",
	"AUDIO\\MUSIC\\END.VB",
	"AUDIO\\lib_a1.WAV",
	"AUDIO\\lib_a2.WAV",
	"AUDIO\\lib_a.WAV",
	"AUDIO\\lib_b.WAV",
	"AUDIO\\lib_c.WAV",
	"AUDIO\\lib_d.WAV",
	"AUDIO\\l2_a.WAV",
	"AUDIO\\j4t_1.WAV",
	"AUDIO\\j4t_2.WAV",
	"AUDIO\\j4t_3.WAV",
	"AUDIO\\j4t_4.WAV",
	"AUDIO\\j4_a.WAV",
	"AUDIO\\j4_b.WAV",
	"AUDIO\\j4_c.WAV",
	"AUDIO\\j4_d.WAV",
	"AUDIO\\j4_e.WAV",
	"AUDIO\\j4_f.WAV",
	"AUDIO\\j6_1.WAV",
	"AUDIO\\j6_a.WAV",
	"AUDIO\\j6_b.WAV",
	"AUDIO\\j6_c.WAV",
	"AUDIO\\j6_d.WAV",
	"AUDIO\\t4_a.WAV",
	"AUDIO\\s1_a.WAV",
	"AUDIO\\s1_a1.WAV",
	"AUDIO\\s1_b.WAV",
	"AUDIO\\s1_c.WAV",
	"AUDIO\\s1_c1.WAV",
	"AUDIO\\s1_d.WAV",
	"AUDIO\\s1_e.WAV",
	"AUDIO\\s1_f.WAV",
	"AUDIO\\s1_g.WAV",
	"AUDIO\\s1_h.WAV",
	"AUDIO\\s1_i.WAV",
	"AUDIO\\s1_j.WAV",
	"AUDIO\\s1_k.WAV",
	"AUDIO\\s1_l.WAV",
	"AUDIO\\s3_a.WAV",
	"AUDIO\\s3_b.WAV",
	"AUDIO\\el3_a.WAV",
	"AUDIO\\mf1_a.WAV",
	"AUDIO\\mf2_a.WAV",
	"AUDIO\\mf3_a.WAV",
	"AUDIO\\mf3_b.WAV",
	"AUDIO\\mf3_b1.WAV",
	"AUDIO\\mf3_c.WAV",
	"AUDIO\\mf4_a.WAV",
	"AUDIO\\mf4_b.WAV",
	"AUDIO\\mf4_c.WAV",
	"AUDIO\\a1_a.WAV",
	"AUDIO\\a3_a.WAV",
	"AUDIO\\a5_a.WAV",
	"AUDIO\\a4_a.WAV",
	"AUDIO\\a4_b.WAV",
	"AUDIO\\a4_c.WAV",
	"AUDIO\\a4_d.WAV",
	"AUDIO\\k1_a.WAV",
	"AUDIO\\k3_a.WAV",
	"AUDIO\\r1_a.WAV",
	"AUDIO\\r2_a.WAV",
	"AUDIO\\r2_b.WAV",
	"AUDIO\\r2_c.WAV",
	"AUDIO\\r2_d.WAV",
	"AUDIO\\r2_e.WAV",
	"AUDIO\\r2_f.WAV",
	"AUDIO\\r2_g.WAV",
	"AUDIO\\r2_h.WAV",
	"AUDIO\\r5_a.WAV",
	"AUDIO\\r6_a.WAV",
	"AUDIO\\r6_a1.WAV",
	"AUDIO\\r6_b.WAV",
	"AUDIO\\lo2_a.WAV",
	"AUDIO\\lo6_a.WAV",
	"AUDIO\\yd2_a.WAV",
	"AUDIO\\yd2_b.WAV",
	"AUDIO\\yd2_c.WAV",
	"AUDIO\\yd2_c1.WAV",
	"AUDIO\\yd2_d.WAV",
	"AUDIO\\yd2_e.WAV",
	"AUDIO\\yd2_f.WAV",
	"AUDIO\\yd2_g.WAV",
	"AUDIO\\yd2_h.WAV",
	"AUDIO\\yd2_ass.WAV",
	"AUDIO\\yd2_ok.WAV",
	"AUDIO\\h5_a.WAV",
	"AUDIO\\h5_b.WAV",
	"AUDIO\\h5_c.WAV",
	"AUDIO\\ammu_a.WAV",
	"AUDIO\\ammu_b.WAV",
	"AUDIO\\ammu_c.WAV",
	"AUDIO\\door_1.WAV",
	"AUDIO\\door_2.WAV",
	"AUDIO\\door_3.WAV",
	"AUDIO\\door_4.WAV",
	"AUDIO\\door_5.WAV",
	"AUDIO\\door_6.WAV",
	"AUDIO\\t3_a.WAV",
	"AUDIO\\t3_b.WAV",
	"AUDIO\\t3_c.WAV",
	"AUDIO\\k1_b.WAV",
	"AUDIO\\cat1.WAV"
};
#endif

static char StreamedNameTable[][25] =
{
	"AUDIO\\HEAD.WAV",
	"AUDIO\\CLASS.WAV",
	"AUDIO\\KJAH.WAV",
	"AUDIO\\RISE.WAV",
	"AUDIO\\LIPS.WAV",
	"AUDIO\\GAME.WAV",
	"AUDIO\\MSX.WAV",
	"AUDIO\\FLASH.WAV",
	"AUDIO\\CHAT.WAV",
	"AUDIO\\HEAD.WAV",
	"AUDIO\\POLICE.WAV",
	"AUDIO\\CITY.WAV",
	"AUDIO\\WATER.WAV",
	"AUDIO\\COMOPEN.WAV",
	"AUDIO\\SUBOPEN.WAV",
	"AUDIO\\JB.MP3",
	"AUDIO\\BET.MP3",
	"AUDIO\\L1_LG.MP3",
	"AUDIO\\L2_DSB.MP3",
	"AUDIO\\L3_DM.MP3",
	"AUDIO\\L4_PAP.MP3",
	"AUDIO\\L5_TFB.MP3",
	"AUDIO\\J0_DM2.MP3",
	"AUDIO\\J1_LFL.MP3",
	"AUDIO\\J2_KCL.MP3",
	"AUDIO\\J3_VH.MP3",
	"AUDIO\\J4_ETH.MP3",
	"AUDIO\\J5_DST.MP3",
	"AUDIO\\J6_TBJ.MP3",
	"AUDIO\\T1_TOL.MP3",
	"AUDIO\\T2_TPU.MP3",
	"AUDIO\\T3_MAS.MP3",
	"AUDIO\\T4_TAT.MP3",
	"AUDIO\\T5_BF.MP3",
	"AUDIO\\S0_MAS.MP3",
	"AUDIO\\S1_PF.MP3",
	"AUDIO\\S2_CTG.MP3",
	"AUDIO\\S3_RTC.MP3",
	"AUDIO\\S5_LRQ.MP3",
	"AUDIO\\S4_BDBA.MP3",
	"AUDIO\\S4_BDBB.MP3",
	"AUDIO\\S2_CTG2.MP3",
	"AUDIO\\S4_BDBD.MP3",
	"AUDIO\\S5_LRQB.MP3",
	"AUDIO\\S5_LRQC.MP3",
	"AUDIO\\A1_SSO.WAV",
	"AUDIO\\A2_PP.WAV",
	"AUDIO\\A3_SS.WAV",
	"AUDIO\\A4_PDR.WAV",
	"AUDIO\\A5_K2FT.WAV",
	"AUDIO\\K1_KBO.MP3",
	"AUDIO\\K2_GIS.MP3",
	"AUDIO\\K3_DS.MP3",
	"AUDIO\\K4_SHI.MP3",
	"AUDIO\\K5_SD.MP3",
	"AUDIO\\R0_PDR2.MP3",
	"AUDIO\\R1_SW.MP3",
	"AUDIO\\R2_AP.MP3",
	"AUDIO\\R3_ED.MP3",
	"AUDIO\\R4_GF.MP3",
	"AUDIO\\R5_PB.MP3",
	"AUDIO\\R6_MM.MP3",
	"AUDIO\\D1_STOG.MP3",
	"AUDIO\\D2_KK.MP3",
	"AUDIO\\D3_ADO.MP3",
	"AUDIO\\D5_ES.MP3",
	"AUDIO\\D7_MLD.MP3",
	"AUDIO\\D4_GTA.MP3",
	"AUDIO\\D4_GTA2.MP3",
	"AUDIO\\D6_STS.MP3",
	"AUDIO\\A6_BAIT.WAV",
	"AUDIO\\A7_ETG.WAV",
	"AUDIO\\A8_PS.WAV",
	"AUDIO\\A9_ASD.WAV",
	"AUDIO\\K4_SHI2.MP3",
	"AUDIO\\C1_TEX.MP3",
	"AUDIO\\EL_PH1.MP3",
	"AUDIO\\EL_PH2.MP3",
	"AUDIO\\EL_PH3.MP3",
	"AUDIO\\EL_PH4.MP3",
	"AUDIO\\YD_PH1.MP3",
	"AUDIO\\YD_PH2.MP3",
	"AUDIO\\YD_PH3.MP3",
	"AUDIO\\YD_PH4.MP3",
	"AUDIO\\HD_PH1.MP3",
	"AUDIO\\HD_PH2.MP3",
	"AUDIO\\HD_PH3.MP3",
	"AUDIO\\HD_PH4.MP3",
	"AUDIO\\HD_PH5.MP3",
	"AUDIO\\MT_PH1.MP3",
	"AUDIO\\MT_PH2.MP3",
	"AUDIO\\MT_PH3.MP3",
	"AUDIO\\MT_PH4.MP3",
	"AUDIO\\MISCOM.WAV",
	"AUDIO\\END.MP3",
	"AUDIO\\lib_a1.WAV",
	"AUDIO\\lib_a2.WAV",
	"AUDIO\\lib_a.WAV",
	"AUDIO\\lib_b.WAV",
	"AUDIO\\lib_c.WAV",
	"AUDIO\\lib_d.WAV",
	"AUDIO\\l2_a.WAV",
	"AUDIO\\j4t_1.WAV",
	"AUDIO\\j4t_2.WAV",
	"AUDIO\\j4t_3.WAV",
	"AUDIO\\j4t_4.WAV",
	"AUDIO\\j4_a.WAV",
	"AUDIO\\j4_b.WAV",
	"AUDIO\\j4_c.WAV",
	"AUDIO\\j4_d.WAV",
	"AUDIO\\j4_e.WAV",
	"AUDIO\\j4_f.WAV",
	"AUDIO\\j6_1.WAV",
	"AUDIO\\j6_a.WAV",
	"AUDIO\\j6_b.WAV",
	"AUDIO\\j6_c.WAV",
	"AUDIO\\j6_d.WAV",
	"AUDIO\\t4_a.WAV",
	"AUDIO\\s1_a.WAV",
	"AUDIO\\s1_a1.WAV",
	"AUDIO\\s1_b.WAV",
	"AUDIO\\s1_c.WAV",
	"AUDIO\\s1_c1.WAV",
	"AUDIO\\s1_d.WAV",
	"AUDIO\\s1_e.WAV",
	"AUDIO\\s1_f.WAV",
	"AUDIO\\s1_g.WAV",
	"AUDIO\\s1_h.WAV",
	"AUDIO\\s1_i.WAV",
	"AUDIO\\s1_j.WAV",
	"AUDIO\\s1_k.WAV",
	"AUDIO\\s1_l.WAV",
	"AUDIO\\s3_a.WAV",
	"AUDIO\\s3_b.WAV",
	"AUDIO\\el3_a.WAV",
	"AUDIO\\mf1_a.WAV",
	"AUDIO\\mf2_a.WAV",
	"AUDIO\\mf3_a.WAV",
	"AUDIO\\mf3_b.WAV",
	"AUDIO\\mf3_b1.WAV",
	"AUDIO\\mf3_c.WAV",
	"AUDIO\\mf4_a.WAV",
	"AUDIO\\mf4_b.WAV",
	"AUDIO\\mf4_c.WAV",
	"AUDIO\\a1_a.WAV",
	"AUDIO\\a3_a.WAV",
	"AUDIO\\a5_a.WAV",
	"AUDIO\\a4_a.WAV",
	"AUDIO\\a4_b.WAV",
	"AUDIO\\a4_c.WAV",
	"AUDIO\\a4_d.WAV",
	"AUDIO\\k1_a.WAV",
	"AUDIO\\k3_a.WAV",
	"AUDIO\\r1_a.WAV",
	"AUDIO\\r2_a.WAV",
	"AUDIO\\r2_b.WAV",
	"AUDIO\\r2_c.WAV",
	"AUDIO\\r2_d.WAV",
	"AUDIO\\r2_e.WAV",
	"AUDIO\\r2_f.WAV",
	"AUDIO\\r2_g.WAV",
	"AUDIO\\r2_h.WAV",
	"AUDIO\\r5_a.WAV",
	"AUDIO\\r6_a.WAV",
	"AUDIO\\r6_a1.WAV",
	"AUDIO\\r6_b.WAV",
	"AUDIO\\lo2_a.WAV",
	"AUDIO\\lo6_a.WAV",
	"AUDIO\\yd2_a.WAV",
	"AUDIO\\yd2_b.WAV",
	"AUDIO\\yd2_c.WAV",
	"AUDIO\\yd2_c1.WAV",
	"AUDIO\\yd2_d.WAV",
	"AUDIO\\yd2_e.WAV",
	"AUDIO\\yd2_f.WAV",
	"AUDIO\\yd2_g.WAV",
	"AUDIO\\yd2_h.WAV",
	"AUDIO\\yd2_ass.WAV",
	"AUDIO\\yd2_ok.WAV",
	"AUDIO\\h5_a.WAV",
	"AUDIO\\h5_b.WAV",
	"AUDIO\\h5_c.WAV",
	"AUDIO\\ammu_a.WAV",
	"AUDIO\\ammu_b.WAV",
	"AUDIO\\ammu_c.WAV",
	"AUDIO\\door_1.WAV",
	"AUDIO\\door_2.WAV",
	"AUDIO\\door_3.WAV",
	"AUDIO\\door_4.WAV",
	"AUDIO\\door_5.WAV",
	"AUDIO\\door_6.WAV",
	"AUDIO\\t3_a.WAV",
	"AUDIO\\t3_b.WAV",
	"AUDIO\\t3_c.WAV",
	"AUDIO\\k1_b.WAV",
	"AUDIO\\cat1.WAV"
};
#endif