Hi there. We are trying to emulate an old linux only sound api based on some official headers we have for it. If I give you our current semi working (it has some audio glitches ocassionally) code and the headers, can you help me rewrite problematic parts?
/*
* This file is part of the OpenParrot project - https://teknoparrot.com / https://github.com/teknogods
*
* See LICENSE and MENTIONS in the root of the source tree for information
* regarding licensing.
*/
extern "C" {
#include "opensegaapi.h"
}

#include <vector>
#define XAUDIO2_HELPER_FUNCTIONS
#include <xaudio2.h>
#include <wrl.h>
#define TSF_IMPLEMENTATION
#include "tsf.h"
#define CHECK_HR(exp) { HRESULT hr = exp; if (FAILED(hr)) { printf("failed %s: %08x\n", #exp, hr); abort(); } }
#pragma comment(lib, "xaudio2.lib")

namespace WRL = Microsoft::WRL;

const GUID OPEN_EAX_NULL_GUID;
const GUID OPEN_EAX_FREQUENCYSHIFTER_EFFECT;
const GUID OPEN_EAX_ECHO_EFFECT;
const GUID OPEN_EAX_REVERB_EFFECT;
const GUID OPEN_EAX_EQUALIZER_EFFECT;
const GUID OPEN_EAX_DISTORTION_EFFECT;
const GUID OPEN_EAX_AGCCOMPRESSOR_EFFECT;
const GUID OPEN_EAX_PITCHSHIFTER_EFFECT;
const GUID OPEN_EAX_FLANGER_EFFECT;
const GUID OPEN_EAX_VOCALMORPHER_EFFECT;
const GUID OPEN_EAX_AUTOWAH_EFFECT;
const GUID OPEN_EAX_RINGMODULATOR_EFFECT;
const GUID OPEN_EAX_CHORUS_EFFECT;

const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot0;
const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot1;
const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot2;
const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot3;

#include <concurrent_queue.h>
#include <functional>

struct OPEN_segaapiBuffer_t;

#ifdef _DEBUG
void info(const char* format, ...)
{
	va_list args;
	char buffer[1024];

	va_start(args, format);
	int len = _vsnprintf_s(buffer, sizeof(buffer), format, args);
	va_end(args);

	buffer[len] = '\n';
	buffer[len + 1] = '\0';

	OutputDebugStringA(buffer);
}
#else
#define info(x) {}
#endif

class XA2Callback : public IXAudio2VoiceCallback
{
public:
	void __stdcall OnVoiceProcessingPassStart(UINT32 BytesRequired) override
	{
	}

	void __stdcall OnVoiceProcessingPassEnd() override
	{
	}

	void __stdcall OnStreamEnd() override
	{
	}

	void __stdcall OnBufferStart(void*) override
	{
	}

	void __stdcall OnLoopEnd(void*) override
	{
	}

	void __stdcall OnVoiceError(void*, HRESULT) override
	{
	}

	void __stdcall OnBufferEnd(void* cxt) override;

public:
	OPEN_segaapiBuffer_t * buffer;
};

struct OPEN_segaapiBuffer_t
{
	void* userData;
	OPEN_HAWOSEGABUFFERCALLBACK callback;
	bool synthesizer;
	bool loop;
	unsigned int channels;
	unsigned int startLoop;
	unsigned int endLoop;
	unsigned int endOffset;
	unsigned int sampleRate;
	unsigned int sampleFormat;
	uint8_t* data;
	size_t size;
	bool playing;
	bool paused;
	bool playWithSetup;

	WAVEFORMATEX xaFormat;

	XAUDIO2_BUFFER xaBuffer;
	IXAudio2SourceVoice* xaVoice;

	float sendVolumes[7];
	int sendChannels[7];
	OPEN_HAROUTING sendRoutes[7];
	float channelVolumes[6];

	tsf* synth;
	tsf_region* region;

	XA2Callback xaCallback;

	concurrency::concurrent_queue<std::function<void()>> defers;
};

void XA2Callback::OnBufferEnd(void* cxt)
{
	std::function<void()> entry;

	while (!buffer->defers.empty())
	{
		XAUDIO2_VOICE_STATE vs;
		buffer->xaVoice->GetState(&vs);

		if (vs.BuffersQueued > 0)
		{
			buffer->xaVoice->FlushSourceBuffers();

			return;
		}

		if (buffer->defers.try_pop(entry))
		{
			entry();
		}
	}
}

template <typename TFn>
void defer_buffer_call(OPEN_segaapiBuffer_t* buffer, const TFn& fn)
{
	if (buffer->xaVoice)
	{
		XAUDIO2_VOICE_STATE vs;
		buffer->xaVoice->GetState(&vs);

		if (vs.BuffersQueued > 0)
		{
			buffer->defers.push(fn);

			buffer->xaVoice->FlushSourceBuffers();

			return;
		}
	}
	fn();
}

static void dumpWaveBuffer(const char* path, unsigned int channels, unsigned int sampleRate, unsigned int sampleBits, void* data, size_t size)
{
	info("dumpWaveBuffer path %s channels %d sampleRate %d sampleBits %d size %d", path, channels, sampleRate, sampleBits, size);

	struct RIFF_Header
	{
		char chunkID[4];
		long chunkSize;
		char format[4];
	};

	struct WAVE_Format
	{
		char subChunkID[4];
		long subChunkSize;
		short audioFormat;
		short numChannels;
		long sampleRate;
		long byteRate;
		short blockAlign;
		short bitsPerSample;
	};

	struct WAVE_Data
	{
		char subChunkID[4];
		long subChunk2Size;
	};

	FILE* soundFile = NULL;
	struct WAVE_Format wave_format;
	struct RIFF_Header riff_header;
	struct WAVE_Data wave_data;

	soundFile = fopen(path, "wb");

	riff_header.chunkID[0] = 'R';
	riff_header.chunkID[1] = 'I';
	riff_header.chunkID[2] = 'F';
	riff_header.chunkID[3] = 'F';
	riff_header.format[0] = 'W';
	riff_header.format[1] = 'A';
	riff_header.format[2] = 'V';
	riff_header.format[3] = 'E';

	fwrite(&riff_header, sizeof(struct RIFF_Header), 1, soundFile);

	wave_format.subChunkID[0] = 'f';
	wave_format.subChunkID[1] = 'm';
	wave_format.subChunkID[2] = 't';
	wave_format.subChunkID[3] = ' ';

	wave_format.audioFormat = 1;
	wave_format.sampleRate = sampleRate;
	wave_format.numChannels = channels;
	wave_format.bitsPerSample = sampleBits;
	wave_format.byteRate = (sampleRate * sampleBits * channels) / 8;
	wave_format.blockAlign = (sampleBits * channels) / 8;
	wave_format.subChunkSize = 16;

	fwrite(&wave_format, sizeof(struct WAVE_Format), 1, soundFile);

	wave_data.subChunkID[0] = 'd';
	wave_data.subChunkID[1] = 'a';
	wave_data.subChunkID[2] = 't';
	wave_data.subChunkID[3] = 'a';

	wave_data.subChunk2Size = size;

	fwrite(&wave_data, sizeof(struct WAVE_Data), 1, soundFile);
	fwrite(data, wave_data.subChunk2Size, 1, soundFile);

	fclose(soundFile);
}

static unsigned int bufferSampleSize(OPEN_segaapiBuffer_t* buffer)
{
	return buffer->channels * ((buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM) ? 2 : 1);
}

static void updateSynthOnPlay(OPEN_segaapiBuffer_t* buffer, unsigned int offset, size_t length)
{
	// TODO
	//// synth
	//if (buffer->synthesizer)
	//{
	//	if (!buffer->synth->voices)
	//	{
	//		struct tsf_voice *voice = new tsf_voice;
	//		memset(voice, 0, sizeof(tsf_voice));

	//		auto region = buffer->region;

	//		TSF_BOOL doLoop; float filterQDB;
	//		voice->playingPreset = -1;

	//		voice->region = region;
	//		voice->noteGainDB = 0.0f - region->volume;

	//		tsf_voice_calcpitchratio(voice, 0, buffer->synth->outSampleRate);
	//		// The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2).
	//		voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan);
	//		voice->panFactorRight = TSF_SQRTF(0.5f + region->pan);

	//		// Offset/end.
	//		voice->sourceSamplePosition = region->offset;

	//		// Loop.
	//		doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end);
	//		voice->loopStart = (doLoop ? region->loop_start : 0);
	//		voice->loopEnd = (doLoop ? region->loop_end : 0);

	//		// Setup envelopes.
	//		tsf_voice_envelope_setup(&voice->ampenv, &region->ampenv, 0, 0, TSF_TRUE, buffer->synth->outSampleRate);
	//		tsf_voice_envelope_setup(&voice->modenv, &region->modenv, 0, 0, TSF_FALSE, buffer->synth->outSampleRate);

	//		// Setup lowpass filter.
	//		filterQDB = region->initialFilterQ / 10.0f;
	//		voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (filterQDB / 20.0));
	//		voice->lowpass.z1 = voice->lowpass.z2 = 0;
	//		voice->lowpass.active = (region->initialFilterFc </*=*/ 13500);
	//		if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, tsf_cents2Hertz((float)region->initialFilterFc) / buffer->synth->outSampleRate);

	//		// Setup LFO filters.
	//		tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, buffer->synth->outSampleRate);
	//		tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, buffer->synth->outSampleRate);

	//		voice->pitchInputTimecents = (log(1.0) / log(2.0) * 1200);
	//		voice->pitchOutputFactor = 1.0f;

	//		buffer->synth->voices = voice;
	//	}

	//	buffer->synth->voices->region = buffer->region;

	//	// make input
	//	buffer->synth->outputmode = TSF_MONO;

	//	auto soffset = offset;
	//	auto slength = length;

	//	if (offset == -1)
	//	{
	//		soffset = 0;
	//	}

	//	if (length == -1)
	//	{
	//		slength = buffer->size;
	//	}

	//	std::vector<float> fontSamples(slength / bufferSampleSize(buffer));
	//	buffer->synth->fontSamples = &fontSamples[0];

	//	buffer->region->end = double(fontSamples.size());

	//	for (int i = 0; i < fontSamples.size(); i++)
	//	{
	//		if (buffer->sampleFormat == OPEN_HASF_UNSIGNED_8PCM)
	//		{
	//			fontSamples[i] = (buffer->data[soffset + i] / 128.0f) - 1.0f;
	//		}
	//		else if (buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM)
	//		{
	//			fontSamples[i] = (*(int16_t*)&buffer->data[soffset + (i * 2)]) / 32768.0f;
	//		}
	//	}

	//	std::vector<float> outSamples(slength / bufferSampleSize(buffer));
	//	tsf_voice_render(buffer->synth, buffer->synth->voices, &outSamples[0], outSamples.size());

	//	for (int i = 0; i < outSamples.size(); i++)
	//	{
	//		if (buffer->sampleFormat == OPEN_HASF_UNSIGNED_8PCM)
	//		{
	//			buffer->data[soffset + i] = (uint8_t)((outSamples[i] + 1.0f) * 128.0f);
	//		}
	//		else if (buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM)
	//		{
	//			*(int16_t*)&buffer->data[soffset + (i * 2)] = outSamples[i] * 32768.0f;
	//		}
	//	}
	//}
}

static void resetBuffer(OPEN_segaapiBuffer_t* buffer)
{
	buffer->startLoop = 0;
	buffer->endOffset = buffer->size;
	buffer->endLoop = buffer->size;
	buffer->loop = false;
	buffer->paused = false;
	buffer->playWithSetup = false;
	buffer->sendRoutes[0] = OPEN_HA_FRONT_LEFT_PORT;
	buffer->sendRoutes[1] = OPEN_HA_FRONT_RIGHT_PORT;
	buffer->sendRoutes[2] = OPEN_HA_UNUSED_PORT;
	buffer->sendRoutes[3] = OPEN_HA_UNUSED_PORT;
	buffer->sendRoutes[4] = OPEN_HA_UNUSED_PORT;
	buffer->sendRoutes[5] = OPEN_HA_UNUSED_PORT;
	buffer->sendRoutes[6] = OPEN_HA_UNUSED_PORT;
	buffer->sendVolumes[0] = 0.0f;
	buffer->sendVolumes[1] = 0.0f;
	buffer->sendVolumes[2] = 0.0f;
	buffer->sendVolumes[3] = 0.0f;
	buffer->sendVolumes[4] = 0.0f;
	buffer->sendVolumes[5] = 0.0f;
	buffer->sendVolumes[6] = 0.0f;
	buffer->channelVolumes[0] = 1.0f;
	buffer->channelVolumes[1] = 1.0f;
	buffer->channelVolumes[2] = 1.0f;
	buffer->channelVolumes[3] = 1.0f;
	buffer->channelVolumes[4] = 1.0f;
	buffer->channelVolumes[5] = 1.0f;
	buffer->sendChannels[0] = 0;
	buffer->sendChannels[1] = 1;
	buffer->sendChannels[2] = 0;
	buffer->sendChannels[3] = 0;
	buffer->sendChannels[4] = 0;
	buffer->sendChannels[5] = 0;
	buffer->sendChannels[6] = 0;

	auto res = (tsf*)TSF_MALLOC(sizeof(tsf));
	TSF_MEMSET(res, 0, sizeof(tsf));
	res->presetNum = 0;
	res->outSampleRate = buffer->sampleRate;

	buffer->synth = res;

	tsf_region* region = new tsf_region;
	memset(region, 0, sizeof(tsf_region));

	tsf_region_clear(region, 0);

	region->ampenv.delay = 0;
	region->ampenv.hold = 300.0f;
	region->ampenv.attack = 0;
	region->ampenv.decay = 0;
	region->ampenv.release = 0;
	region->ampenv.sustain = 0;

	buffer->region = region;
}

static WRL::ComPtr<IXAudio2> g_xa2;
static IXAudio2MasteringVoice* g_masteringVoice;
static IXAudio2SubmixVoice* g_submixVoices[6];

static void updateBufferNew(OPEN_segaapiBuffer_t* buffer, unsigned int offset, size_t length)
{
	// don't update with pending defers
	if (!buffer->defers.empty())
	{
		info("updateBufferNew: DEFER!");
		return;
	}

	CHECK_HR(buffer->xaVoice->FlushSourceBuffers());

	buffer->xaBuffer.Flags = 0;
	buffer->xaBuffer.AudioBytes = buffer->size;
	buffer->xaBuffer.pAudioData = buffer->data;

	if (buffer->loop)
	{
		info("updateBufferNew: loop");

		// Note: Sega uses byte offsets for begin and end
		//       Xaudio2 uses start sample and length in samples
		buffer->xaBuffer.PlayBegin = buffer->startLoop / bufferSampleSize(buffer);
		buffer->xaBuffer.PlayLength = (min(buffer->endLoop, buffer->endOffset) - buffer->startLoop) / bufferSampleSize(buffer);
		buffer->xaBuffer.LoopBegin = buffer->xaBuffer.PlayBegin;
		buffer->xaBuffer.LoopLength = buffer->xaBuffer.PlayLength;
		buffer->xaBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
		buffer->xaBuffer.pContext = NULL;
	}
	else
	{
		info("updateBufferNew: no loop");
		buffer->xaBuffer.PlayBegin = buffer->startLoop / bufferSampleSize(buffer);
		buffer->xaBuffer.PlayLength = (min(buffer->endLoop, buffer->endOffset) - buffer->startLoop) / bufferSampleSize(buffer);
		buffer->xaBuffer.LoopBegin = 0;
		buffer->xaBuffer.LoopLength = 0;
		buffer->xaBuffer.LoopCount = 0;
		buffer->xaBuffer.pContext = NULL;
	}

	buffer->xaVoice->SubmitSourceBuffer(&buffer->xaBuffer);

	// Uncomment to dump audio buffers to wav files (super slow)
	/*auto sampleBits = (buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM) ? 16 : 8;
	char path[255];
	sprintf(path, "C:\\dump\\%08X.wav", &buffer);
	dumpWaveBuffer(path, buffer->channels, buffer->sampleRate, sampleBits, buffer->data, buffer->size);*/
}

extern "C" {
	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_CreateBuffer(OPEN_HAWOSEBUFFERCONFIG* pConfig, OPEN_HAWOSEGABUFFERCALLBACK pCallback, unsigned int dwFlags, void* * phHandle)
	{
		if (phHandle == NULL || pConfig == NULL)
		{
			info("SEGAAPI_CreateBuffer: Handle: %08X, Status: OPEN_SEGAERR_BAD_POINTER", phHandle);
			return OPEN_SEGAERR_BAD_POINTER;
		}

		OPEN_segaapiBuffer_t* buffer = new OPEN_segaapiBuffer_t;

		info("SEGAAPI_CreateBuffer: hHandle: %08X synth: %d, mem caller: %d, mem last: %d, mem alloc: %d, size: %d SampleRate: %d, byNumChans: %d, dwPriority: %d, dwSampleFormat: %d", buffer, (dwFlags & OPEN_HABUF_SYNTH_BUFFER), (dwFlags & OPEN_HABUF_ALLOC_USER_MEM) >> 1, (dwFlags & OPEN_HABUF_USE_MAPPED_MEM) >> 2, dwFlags == 0, pConfig->mapData.dwSize, pConfig->dwSampleRate, pConfig->byNumChans, pConfig->dwPriority, pConfig->dwSampleFormat);

		buffer->playing = false;
		buffer->callback = pCallback;
		buffer->synthesizer = dwFlags & OPEN_HABUF_SYNTH_BUFFER;
		buffer->sampleFormat = pConfig->dwSampleFormat;
		buffer->sampleRate = pConfig->dwSampleRate;
		buffer->channels = pConfig->byNumChans;
		buffer->userData = pConfig->hUserData;
		buffer->size = pConfig->mapData.dwSize;
		pConfig->mapData.dwOffset = 0;

		// Use buffer supplied by caller
		if (dwFlags & OPEN_HABUF_ALLOC_USER_MEM)
		{
			buffer->data = (uint8_t*)pConfig->mapData.hBufferHdr;
		}
		// Reuse buffer
		else if (dwFlags & OPEN_HABUF_USE_MAPPED_MEM)
		{
			buffer->data = (uint8_t*)pConfig->mapData.hBufferHdr;
		}
		// Allocate new buffer (caller will fill it later)
		else
		{
			buffer->data = (uint8_t*)malloc(buffer->size);
		}

		pConfig->mapData.hBufferHdr = buffer->data;

		auto sampleRate = pConfig->dwSampleRate;
		auto sampleBits = (pConfig->dwSampleFormat == OPEN_HASF_SIGNED_16PCM) ? 16 : 8;
		auto channels = pConfig->byNumChans;

		buffer->xaFormat.cbSize = sizeof(WAVEFORMATEX);
		buffer->xaFormat.nAvgBytesPerSec = (sampleRate * sampleBits * channels) / 8;
		buffer->xaFormat.nSamplesPerSec = sampleRate;
		buffer->xaFormat.wBitsPerSample = sampleBits;
		buffer->xaFormat.nChannels = channels;
		buffer->xaFormat.wFormatTag = 1;
		buffer->xaFormat.nBlockAlign = (sampleBits * channels) / 8;
		buffer->xaCallback.buffer = buffer;

		CHECK_HR(g_xa2->CreateSourceVoice(&buffer->xaVoice, &buffer->xaFormat, 0, 2.0f, &buffer->xaCallback));

		buffer->xaBuffer = { 0 };

		if (buffer->synthesizer)
		{
			// Not supported
		}
		resetBuffer(buffer);

		*phHandle = buffer;
		
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetUserData(void* hHandle, void* hUserData)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetUserData: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetUserData: Handle: %08X UserData: %08X", hHandle, hUserData);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->userData = hUserData;
		return OPEN_SEGA_SUCCESS;
	}


	__declspec(dllexport) void* SEGAAPI_GetUserData(void* hHandle)
	{
		if (hHandle == NULL)
		{
			return nullptr;
		}

		info("SEGAAPI_GetUserData: Handle: %08X", hHandle);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		return buffer->userData;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_UpdateBuffer(void* hHandle, unsigned int dwStartOffset, unsigned int dwLength)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_UpdateBuffer: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_UpdateBuffer: Handle: %08X dwStartOffset: %08X, dwLength: %08X", hHandle, dwStartOffset, dwLength);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		updateBufferNew(buffer, dwStartOffset, dwLength);
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndOffset(void* hHandle, unsigned int dwOffset)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetEndOffset: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetEndOffset: Handle: %08X dwOffset: %08X", hHandle, dwOffset);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->endOffset = dwOffset;
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndLoopOffset(void* hHandle, unsigned int dwOffset)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetEndLoopOffset: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetEndLoopOffset: Handle: %08X dwOffset: %08X", hHandle, dwOffset);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->endLoop = dwOffset;
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetStartLoopOffset(void* hHandle, unsigned int dwOffset)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetStartLoopOffset: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetStartLoopOffset: Handle: %08X dwOffset: %08X", hHandle, dwOffset);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->startLoop = dwOffset;
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSampleRate(void* hHandle, unsigned int dwSampleRate)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetSampleRate: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetSampleRate: Handle: %08X dwSampleRate: %08X", hHandle, dwSampleRate);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->sampleRate = dwSampleRate;

		defer_buffer_call(buffer, [=]()
		{
			buffer->xaVoice->SetSourceSampleRate(dwSampleRate);
		});

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetLoopState(void* hHandle, int bDoContinuousLooping)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetLoopState: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetLoopState: Handle: %08X bDoContinuousLooping: %d", hHandle, bDoContinuousLooping);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->loop = bDoContinuousLooping;

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetPlaybackPosition(void* hHandle, unsigned int dwPlaybackPos)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetPlaybackPosition: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetPlaybackPosition: Handle: %08X dwPlaybackPos: %08X", hHandle, dwPlaybackPos);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		if (dwPlaybackPos != 0)
		{
		}

		// XA2 TODO
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) unsigned int SEGAAPI_GetPlaybackPosition(void* hHandle)
	{
		if (hHandle == NULL)
		{
			return 0;
		}

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		XAUDIO2_VOICE_STATE vs;
		buffer->xaVoice->GetState(&vs);

		unsigned int result = (vs.SamplesPlayed * (buffer->xaFormat.wBitsPerSample / 8) * buffer->xaFormat.nChannels) % buffer->size;
		
		info("SEGAAPI_GetPlaybackPosition: Handle: %08X Samples played: %08d BitsPerSample %08d/%08d nChannels %08d bufferSize %08d Result: %08X", hHandle, vs.SamplesPlayed, buffer->xaFormat.wBitsPerSample, (buffer->xaFormat.wBitsPerSample / 8), buffer->xaFormat.nChannels, buffer->size, result);

		return result;
	}

	static void updateRouting(OPEN_segaapiBuffer_t* buffer);

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Play(void* hHandle)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_Play: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_Play: Handle: %08X", hHandle);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		updateRouting(buffer);
		updateBufferNew(buffer, -1, -1);

		buffer->playing = true;
		buffer->paused = false;

		// Uncomment to mute music
		//if (buffer->playWithSetup)
		//{
			CHECK_HR(buffer->xaVoice->Start());
		//}
			
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Stop(void* hHandle)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_Stop: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_Stop: Handle: %08X", hHandle);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->playing = false;
		buffer->paused = false;
		CHECK_HR(buffer->xaVoice->Stop());
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_HAWOSTATUS SEGAAPI_GetPlaybackStatus(void* hHandle)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_INVALID", hHandle);
			return OPEN_HAWOSTATUS_INVALID;
		}

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		if (buffer->paused)
		{
			info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_PAUSE, buffer is paused", hHandle);
			return OPEN_HAWOSTATUS_PAUSE;
		}

		// XA2 TODO
		XAUDIO2_VOICE_STATE vs;
		buffer->xaVoice->GetState(&vs);

		if (vs.BuffersQueued == 0)
		{
			info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_STOP, buffersqueued is 0", hHandle);
			return OPEN_HAWOSTATUS_STOP;
		}

		if (!buffer->loop && vs.SamplesPlayed >= (min(buffer->size, buffer->endOffset) / bufferSampleSize(buffer)))
		{
			info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_STOP, Loop false and samples played bigger", hHandle);
			return OPEN_HAWOSTATUS_STOP;
		}

		if (buffer->playing)
		{
			info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_ACTIVE, playing true!", hHandle);
			return OPEN_HAWOSTATUS_ACTIVE;
		}
		else
		{
			info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_STOP, Playing false!", hHandle);
			return OPEN_HAWOSTATUS_STOP;
		}
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetReleaseState(void* hHandle, int bSet)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetReleaseState: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetReleaseState: Handle: %08X bSet: %08X", hHandle, bSet);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		if (bSet)
		{
			buffer->playing = false;
			buffer->xaVoice->FlushSourceBuffers();
			buffer->xaVoice->Stop();
		}

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_DestroyBuffer(void* hHandle)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_DestroyBuffer: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_DestroyBuffer: Handle: %08X", hHandle);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		buffer->xaVoice->DestroyVoice();
		delete buffer;
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) int SEGAAPI_SetGlobalEAXProperty(GUID * guid, unsigned long ulProperty, void * pData, unsigned long ulDataSize)
	{
		info("SEGAAPI_SetGlobalEAXProperty:");

		// Everything is fine
		return TRUE;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Init(void)
	{
		info("SEGAAPI_Init");

		CoInitialize(nullptr);

		CHECK_HR(XAudio2Create(&g_xa2));

		XAUDIO2_DEBUG_CONFIGURATION cfg = { 0 };
		cfg.TraceMask = XAUDIO2_LOG_ERRORS;
		//cfg.BreakMask = XAUDIO2_LOG_ERRORS;
		g_xa2->SetDebugConfiguration(&cfg);

		CHECK_HR(g_xa2->CreateMasteringVoice(&g_masteringVoice));

		XAUDIO2_VOICE_DETAILS vd;
		g_masteringVoice->GetVoiceDetails(&vd);

		for (auto& g_submixVoice : g_submixVoices)
		{
			CHECK_HR(g_xa2->CreateSubmixVoice(&g_submixVoice, 1, vd.InputSampleRate));
		}

		int numChannels = vd.InputChannels;

		auto setSubmixVoice = [=](OPEN_HAROUTING index, float frontLeft, float frontRight, float frontCenter, float lfe,
			float rearLeft, float rearRight)
		{
			float levelMatrix[12] = { 0 };
			levelMatrix[0] = frontLeft + rearLeft;
			levelMatrix[1] = frontRight + rearRight;

			if (numChannels == 2)
			{
				// TODO
			}

			levelMatrix[2] = lfe;

			// TODO: surround data - SetOutputMatrix order is somewhat unclear :/

			g_submixVoices[index]->SetOutputMatrix(g_masteringVoice, 1, numChannels, levelMatrix);
		};

		setSubmixVoice(OPEN_HA_FRONT_LEFT_PORT, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
		setSubmixVoice(OPEN_HA_FRONT_RIGHT_PORT, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
		setSubmixVoice(OPEN_HA_FRONT_CENTER_PORT, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
		setSubmixVoice(OPEN_HA_REAR_LEFT_PORT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
		setSubmixVoice(OPEN_HA_REAR_RIGHT_PORT, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
		setSubmixVoice(OPEN_HA_LFE_PORT, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Exit(void)
	{
		info("SEGAAPI_Exit");

		for (auto& g_submixVoice : g_submixVoices)
		{
			g_submixVoice->DestroyVoice();
		}

		// TODO: deinit XA2
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Reset(void)
	{
		info("SEGAAPI_Reset");
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetIOVolume(OPEN_HAPHYSICALIO dwPhysIO, unsigned int dwVolume)
	{
		info("SEGAAPI_SetIOVolume: dwPhysIO: %08X dwVolume: %08X", dwPhysIO, dwVolume);
		g_masteringVoice->SetVolume(dwVolume / (float)0xFFFFFFFF);
		return OPEN_SEGA_SUCCESS;
	}

	static void updateRouting(OPEN_segaapiBuffer_t* buffer)
	{
		float levels[7 * 2];
		IXAudio2SubmixVoice* outVoices[7];

		int numRoutes = 0;

		for (int i = 0; i < /*7*/2; i++)
		{
			if (buffer->sendRoutes[i] != OPEN_HA_UNUSED_PORT && buffer->sendRoutes[i] < 6)
			{
				outVoices[numRoutes] = g_submixVoices[buffer->sendRoutes[i]];

				int levelOff = numRoutes * buffer->channels;

				for (int ch = 0; ch < buffer->channels; ch++)
				{
					levels[levelOff + ch] = 0;
				}

				float level = buffer->sendVolumes[i] * buffer->channelVolumes[buffer->sendChannels[i]];
				levels[levelOff + buffer->sendChannels[i]] = level;

				++numRoutes;
			}
		}

		// can't set no routes
		if (numRoutes == 0)
		{
			return;
		}

		XAUDIO2_SEND_DESCRIPTOR sendDescs[7];
		for (int i = 0; i < numRoutes; i++)
		{
			sendDescs[i].Flags = 0;
			sendDescs[i].pOutputVoice = outVoices[i];
		}

		XAUDIO2_VOICE_SENDS sends;
		sends.SendCount = numRoutes;
		sends.pSends = sendDescs;
		CHECK_HR(buffer->xaVoice->SetOutputVoices(&sends));

		for (int i = 0; i < numRoutes; i++)
		{
			CHECK_HR(buffer->xaVoice->SetOutputMatrix(outVoices[i], buffer->channels, 1, &levels[i * buffer->channels]));
		}
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendRouting(void* hHandle, unsigned int dwChannel, unsigned int dwSend, OPEN_HAROUTING dwDest)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetSendRouting: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetSendRouting: hHandle: %08X dwChannel: %08X dwSend: %08X dwDest: %08X", hHandle, dwChannel, dwSend, dwDest);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->sendRoutes[dwSend] = dwDest;
		buffer->sendChannels[dwSend] = dwChannel;

		updateRouting(buffer);
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendLevel(void* hHandle, unsigned int dwChannel, unsigned int dwSend, unsigned int dwLevel)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetSendLevel: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetSendLevel: hHandle: %08X dwChannel: %08X dwSend: %08X dwLevel: %08X", hHandle, dwChannel, dwSend, dwLevel);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->sendVolumes[dwSend] = dwLevel / (float)0xFFFFFFFF;
		buffer->sendChannels[dwSend] = dwChannel;

		updateRouting(buffer);
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSynthParam(void* hHandle, OPEN_HASYNTHPARAMSEXT param, int lPARWValue)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetSynthParam: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetSynthParam: hHandle: %08X OPEN_HASYNTHPARAMSEXT: %08X lPARWValue: %08X", hHandle, param, lPARWValue);

		enum
		{
			StartAddrsOffset,
			EndAddrsOffset,
			StartloopAddrsOffset,
			EndloopAddrsOffset,
			StartAddrsCoarseOffset,
			ModLfoToPitch,
			VibLfoToPitch,
			ModEnvToPitch,
			InitialFilterFc,
			InitialFilterQ,
			ModLfoToFilterFc,
			ModEnvToFilterFc,
			EndAddrsCoarseOffset,
			ModLfoToVolume,
			Unused1,
			ChorusEffectsSend,
			ReverbEffectsSend,
			Pan,
			Unused2,
			Unused3,
			Unused4,
			DelayModLFO,
			FreqModLFO,
			DelayVibLFO,
			FreqVibLFO,
			DelayModEnv,
			AttackModEnv,
			HoldModEnv,
			DecayModEnv,
			SustainModEnv,
			ReleaseModEnv,
			KeynumToModEnvHold,
			KeynumToModEnvDecay,
			DelayVolEnv,
			AttackVolEnv,
			HoldVolEnv,
			DecayVolEnv,
			SustainVolEnv,
			ReleaseVolEnv,
			KeynumToVolEnvHold,
			KeynumToVolEnvDecay,
			Instrument,
			Reserved1,
			KeyRange,
			VelRange,
			StartloopAddrsCoarseOffset,
			Keynum,
			Velocity,
			InitialAttenuation,
			Reserved2,
			EndloopAddrsCoarseOffset,
			CoarseTune,
			FineTune,
			SampleID,
			SampleModes,
			Reserved3,
			ScaleTuning,
			ExclusiveClass,
			OverridingRootKey,
			Unused5,
			EndOper
		};

		int mapping[26] = {
			InitialAttenuation, ///< 0,         0x00,  initialAttenuation
			FineTune, ///< 1,         0x01,  fineTune + coarseTune * 100
			InitialFilterFc, ///< 2,         0x02,  initialFilterFc
			InitialFilterQ, ///< 3,         0x03,  initialFilterQ
			DelayVolEnv, ///< 4,         0x04,  delayVolEnv
			AttackVolEnv, ///< 5,         0x05,  attackVolEnv
			HoldVolEnv, ///< 6,         0x06,  holdVolEnv
			DecayVolEnv, ///< 7,         0x07,  decayVolEnv
			SustainVolEnv, ///< 8,         0x08,  sustainVolEnv
			ReleaseVolEnv, ///< 9,         0x09,  releaseVolEnv
			DelayModEnv, ///< 10,        0x0A,  delayModEnv
			AttackModEnv, ///< 11,        0x0B,  attackModEnv
			HoldModEnv, ///< 12,        0x0C,  holdModEnv
			DecayModEnv, ///< 13,        0x0D,  decayModEnv
			SustainModEnv, ///< 14,        0x0E,  sustainModEnv
			ReleaseModEnv, ///< 15,        0x0F,  releaseModEnv
			DelayModLFO, ///< 16,        0x10,  delayModLFO
			FreqModLFO, ///< 17,        0x11,  freqModLFO
			DelayVibLFO, ///< 18,        0x12,  delayVibLFO
			FreqVibLFO, ///< 19,        0x13,  freqVibLFO
			ModLfoToPitch, ///< 20,        0x14,  modLfoToPitch
			VibLfoToPitch, ///< 21,        0x15,  vibLfoToPitch
			ModLfoToFilterFc, ///< 22,        0x16,  modLfoToFilterFc
			ModLfoToVolume, ///< 23,        0x17,  modLfoToVolume
			ModEnvToPitch, ///< 24,        0x18,  modEnvToPitch
			ModEnvToFilterFc ///< 25,        0x19,  modEnvToFilterFc
		};

		int realParam = mapping[param];

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		//tsf_hydra_genamount amount;
		//amount.shortAmount = lPARWValue;
		//tsf_region_operator(buffer->region, realParam, &amount);

		if (param == OPEN_HAVP_ATTENUATION)
		{
			float volume = tsf_decibelsToGain(0.0f - lPARWValue / 10.0f);

			buffer->xaVoice->SetVolume(volume);
			info("SEGAAPI_SetSynthParam: OPEN_HAVP_ATTENUATION gain: %f dB: %d", volume, lPARWValue);
		}
		else if (param == OPEN_HAVP_PITCH)
		{
			float semiTones = lPARWValue / 100.0f;
			float freqRatio = XAudio2SemitonesToFrequencyRatio(semiTones);

			buffer->xaVoice->SetFrequencyRatio(freqRatio);
			info("SEGAAPI_SetSynthParam: OPEN_HAVP_PITCH hHandle: %08X semitones: %f freqRatio: %f", hHandle, semiTones, freqRatio);
		}

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) int SEGAAPI_GetSynthParam(void * hHandle, OPEN_HASYNTHPARAMSEXT param)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_GetSynthParam: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_GetSynthParam: hHandle: %08X OPEN_HASYNTHPARAMSEXT: %08X", hHandle, param);

		return 0; //todo not sure if actually used
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSynthParamMultiple(void* hHandle, unsigned int dwNumParams, OPEN_SynthParamSet* pSynthParams)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetSynthParamMultiple: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetSynthParamMultiple: hHandle: %08X dwNumParams: %08X pSynthParams: %08X", hHandle, dwNumParams, pSynthParams);

		for (int i = 0; i < dwNumParams; i++)
		{
			SEGAAPI_SetSynthParam(hHandle, pSynthParams[i].param, pSynthParams[i].lPARWValue);
		}

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetChannelVolume(void* hHandle, unsigned int dwChannel, unsigned int dwVolume)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_SetChannelVolume: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_SetChannelVolume: hHandle: %08X dwChannel: %08X dwVolume: %08X", hHandle, dwChannel, dwVolume);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->channelVolumes[dwChannel] = dwVolume / (float)0xFFFFFFFF;
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) unsigned int SEGAAPI_GetChannelVolume(void* hHandle, unsigned int dwChannel)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_GetChannelVolume: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_GetChannelVolume: hHandle: %08X dwChannel: %08X", hHandle, dwChannel);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		return buffer->channelVolumes[dwChannel];
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Pause(void* hHandle)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_Pause: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_Pause: hHandle: %08X", hHandle);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;

		buffer->playing = false;
		buffer->paused = true;
		CHECK_HR(buffer->xaVoice->Stop());
		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_PlayWithSetup(
		void* hHandle,
		unsigned int dwNumSendRouteParams, OPEN_SendRouteParamSet* pSendRouteParams,
		unsigned int dwNumSendLevelParams, OPEN_SendLevelParamSet* pSendLevelParams,
		unsigned int dwNumVoiceParams, OPEN_VoiceParamSet* pVoiceParams,
		unsigned int dwNumSynthParams, OPEN_SynthParamSet* pSynthParams
	)
	{
		if (hHandle == NULL)
		{
			info("SEGAAPI_PlayWithSetup: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
			return OPEN_SEGAERR_BAD_HANDLE;
		}

		info("SEGAAPI_PlayWithSetup: hHandle: %08X dwNumSendRouteParams: %d pSendRouteParams: %08X dwNumSendLevelParams: %d pSendLevelParams: %08X dwNumVoiceParams: %d pVoiceParams: %08X dwNumSynthParams: %d pSynthParams: %08X", hHandle, dwNumSendRouteParams, *pSendRouteParams, dwNumSendLevelParams, *pSendLevelParams, dwNumVoiceParams, *pVoiceParams, dwNumSynthParams, *pSynthParams);
		info("dwNumSynthParams: %d", dwNumSynthParams);

		OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
		buffer->playWithSetup = true;

		for (int i = 0; i < dwNumSendRouteParams; i++)
		{
			SEGAAPI_SetSendRouting(hHandle, pSendRouteParams[i].dwChannel, pSendRouteParams[i].dwSend, pSendRouteParams[i].dwDest);
		}

		for (int i = 0; i < dwNumSendLevelParams; i++)
		{
			SEGAAPI_SetSendLevel(hHandle, pSendLevelParams[i].dwChannel, pSendLevelParams[i].dwSend, pSendLevelParams[i].dwLevel);
		}

		unsigned int loopStart = 0;
		unsigned int loopEnd = 0;
		unsigned int loopState = 0;
		unsigned int endOffset = 0;

		for (int i = 0; i < dwNumVoiceParams; i++)
		{
			switch (pVoiceParams[i].VoiceIoctl)
			{
			case OPEN_VOICEIOCTL_SET_START_LOOP_OFFSET:
				SEGAAPI_SetStartLoopOffset(hHandle, pVoiceParams[i].dwParam1);
				loopStart = pVoiceParams[i].dwParam1;
				break;
			case OPEN_VOICEIOCTL_SET_END_LOOP_OFFSET:
				SEGAAPI_SetEndLoopOffset(hHandle, pVoiceParams[i].dwParam1);
				loopEnd = pVoiceParams[i].dwParam1;
				break;
			case OPEN_VOICEIOCTL_SET_END_OFFSET:
				SEGAAPI_SetEndOffset(hHandle, pVoiceParams[i].dwParam1);
				endOffset = pVoiceParams[i].dwParam1;
				break;
			case OPEN_VOICEIOCTL_SET_LOOP_STATE:
				SEGAAPI_SetLoopState(hHandle, pVoiceParams[i].dwParam1);
				loopState = pVoiceParams[i].dwParam1;
				break;
			case OPEN_VOICEIOCTL_SET_NOTIFICATION_POINT:
				info("Unimplemented! OPEN_VOICEIOCTL_SET_NOTIFICATION_POINT");
				break;
			case OPEN_VOICEIOCTL_CLEAR_NOTIFICATION_POINT:
				info("Unimplemented! OPEN_VOICEIOCTL_CLEAR_NOTIFICATION_POINT");
				break;
			case OPEN_VOICEIOCTL_SET_NOTIFICATION_FREQUENCY:
				info("Unimplemented! OPEN_VOICEIOCTL_SET_NOTIFICATION_FREQUENCY");
				break;
			}
		}

		info("Loopdata: hHandle: %08X, loopStart: %08X, loopEnd: %08X, endOffset: %08X, loopState: %d, size: %d", hHandle, loopStart, loopEnd, endOffset, loopState, buffer->size);

		for (int i = 0; i < dwNumSynthParams; i++)
		{
			SEGAAPI_SetSynthParam(hHandle, pSynthParams[i].param, pSynthParams[i].lPARWValue);
		}

		SEGAAPI_Play(hHandle);

		return OPEN_SEGA_SUCCESS;
	}

	__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetLastStatus(void)
	{
		info("SEGAAPI_GetLastStatus");
		return OPEN_SEGA_SUCCESS;
	}
}
#pragma optimize("", on)

here's our own header
/*
* This file is part of the OpenParrot project - https://teknoparrot.com / https://github.com/teknogods
*
* See LICENSE and MENTIONS in the root of the source tree for information
* regarding licensing.
*/

#include <guiddef.h>

// TODO: DOCUMENT ALL THESE ACCORDING TO ORIGINAL DOCUMENTS!!!!

#define OPEN_SEGA_SUCCESS 0L
typedef int OPEN_SEGASTATUS;
#define OPEN_SEGARESULT_FAILURE(_x)      ((1 << 31) | 0xA000 | (_x))
#define OPEN_SEGAERR_FAIL                          OPEN_SEGARESULT_FAILURE(0)  // -2147442688
#define OPEN_SEGAERR_BAD_POINTER                   OPEN_SEGARESULT_FAILURE(3)  // -2147442685
#define OPEN_SEGAERR_BAD_PARAM                     OPEN_SEGARESULT_FAILURE(9)  // -2147442679
#define OPEN_SEGAERR_INVALID_SEND                  OPEN_SEGARESULT_FAILURE(11) // -2147442677
#define OPEN_SEGAERR_BAD_HANDLE                    OPEN_SEGARESULT_FAILURE(18) // -2147442670
#define OPEN_SEGAERR_BAD_SAMPLERATE                OPEN_SEGARESULT_FAILURE(28) // -2147442660
// SEGA custom EAX40 properties.
// GUID: {A7FEEC3F-2BFD-4a40-891F-7423E38BAC1F}
DEFINE_GUID(EAXPROPERTYID_EAX40_SEGA_Custom,
	0xa7feec3f, 0x2bfd, 0x4a40, 0x89, 0x1f, 0x74, 0x23, 0xe3, 0x8b, 0xac, 0x1f);

typedef enum
{
	EAXOPENSEGA_STEREO_RETURN_FX2 = 0, 	// front L/R (default)
	EAXOPENSEGA_STEREO_RETURN_FX3 = 1 // rear L/R
} EAXOPENSEGA_PROPERTY;

typedef enum
{
	OPEN_HAWOS_RESOURCE_STOLEN = 0,
	OPEN_HAWOS_NOTIFY = 2
} OPEN_HAWOSMESSAGETYPE;

typedef enum
{
	OPEN_HAWOSTATUS_STOP,
	OPEN_HAWOSTATUS_ACTIVE,
	OPEN_HAWOSTATUS_PAUSE,
	OPEN_HAWOSTATUS_INVALID = -1
} OPEN_HAWOSTATUS;

#define OPEN_HABUF_SYNTH_BUFFER      0x00000001
#define OPEN_HABUF_ALLOC_USER_MEM    0x00000002
#define OPEN_HABUF_USE_MAPPED_MEM    0x00000004
#define OPEN_HASF_UNSIGNED_8PCM      0x0004
#define OPEN_HASF_SIGNED_16PCM       0x0020

typedef struct
{
	unsigned int dwSampleRate;
	unsigned int dwSampleFormat;
	unsigned int byNumChans;
} OPEN_HAWOSEFORMAT;

typedef struct
{
	unsigned int dwSize;
	unsigned int dwOffset;
	void* hBufferHdr;
} OPEN_HAWOSEMAPDATA;

typedef struct
{
	unsigned int dwPriority;
	unsigned int dwSampleRate;
	unsigned int dwSampleFormat;
	unsigned int byNumChans;
	unsigned int dwReserved;
	void* hUserData;
	OPEN_HAWOSEMAPDATA mapData;
} OPEN_HAWOSEBUFFERCONFIG;

#define OPEN_HAWOSEVOL_MAX      0xFFFFFFFF
#define OPEN_HAWOSEP_MINIMUM    0
#define OPEN_HAWOSEP_MAXIMUM    0xFFFFFFFF
#define OPEN_HAWOSE_UNUSED_SEND 0xFFFF0001

typedef enum OPEN_HAROUTING
{
	OPEN_HA_UNUSED_PORT = OPEN_HAWOSE_UNUSED_SEND,
	OPEN_HA_FRONT_LEFT_PORT = 0,
	OPEN_HA_FRONT_RIGHT_PORT = 1,
	OPEN_HA_FRONT_CENTER_PORT = 2,
	OPEN_HA_LFE_PORT = 3,
	OPEN_HA_REAR_LEFT_PORT = 4,
	OPEN_HA_REAR_RIGHT_PORT = 5,
	OPEN_HA_FXSLOT0_PORT = 10,
	OPEN_HA_FXSLOT1_PORT = 11,
	OPEN_HA_FXSLOT2_PORT = 12,
	OPEN_HA_FXSLOT3_PORT = 13
} OPEN_HAROUTING;

typedef enum
{
	OPEN_HASPDIFOUT_44_1KHZ = 0,
	OPEN_HASPDIFOUT_48KHZ,
	OPEN_HASPDIFOUT_96KHZ
} OPEN_HASPDIFOUTRATE;

typedef enum OPEN_HAPHYSICALIO
{
	OPEN_HA_OUT_FRONT_LEFT = 0,
	OPEN_HA_OUT_FRONT_RIGHT = 1,
	OPEN_HA_OUT_FRONT_CENTER = 2,
	OPEN_HA_OUT_LFE_PORT = 3,
	OPEN_HA_OUT_REAR_LEFT = 4,
	OPEN_HA_OUT_REAR_RIGHT = 5,
	OPEN_HA_OUT_OPTICAL_LEFT = 10,
	OPEN_HA_OUT_OPTICAL_RIGHT = 11,
	OPEN_HA_IN_LINEIN_LEFT = 20,
	OPEN_HA_IN_LINEIN_RIGHT = 21
} OPEN_HAPHYSICALIO;

typedef enum OPEN_HASYNTHPARAMSEXT
{
	OPEN_HAVP_ATTENUATION,
	OPEN_HAVP_PITCH,
	OPEN_HAVP_FILTER_CUTOFF,
	OPEN_HAVP_FILTER_Q,
	OPEN_HAVP_DELAY_VOL_ENV,
	OPEN_HAVP_ATTACK_VOL_ENV,
	OPEN_HAVP_HOLD_VOL_ENV,
	OPEN_HAVP_DECAY_VOL_ENV,
	OPEN_HAVP_SUSTAIN_VOL_ENV,
	OPEN_HAVP_RELEASE_VOL_ENV,
	OPEN_HAVP_DELAY_MOD_ENV,
	OPEN_HAVP_ATTACK_MOD_ENV,
	OPEN_HAVP_HOLD_MOD_ENV,
	OPEN_HAVP_DECAY_MOD_ENV,
	OPEN_HAVP_SUSTAIN_MOD_ENV,
	OPEN_HAVP_RELEASE_MOD_ENV,
	OPEN_HAVP_DELAY_MOD_LFO,
	OPEN_HAVP_FREQ_MOD_LFO,
	OPEN_HAVP_DELAY_VIB_LFO,
	OPEN_HAVP_FREQ_VIB_LFO,
	OPEN_HAVP_MOD_LFO_TO_PITCH,
	OPEN_HAVP_VIB_LFO_TO_PITCH,
	OPEN_HAVP_MOD_LFO_TO_FILTER_CUTOFF,
	OPEN_HAVP_MOD_LFO_TO_ATTENUATION,
	OPEN_HAVP_MOD_ENV_TO_PITCH,
	OPEN_HAVP_MOD_ENV_TO_FILTER_CUTOFF
} OPEN_HASYNTHPARAMSEXT;

typedef enum
{
	OPEN_VOICEIOCTL_SET_START_LOOP_OFFSET = 0x100,
	OPEN_VOICEIOCTL_SET_END_LOOP_OFFSET,
	OPEN_VOICEIOCTL_SET_END_OFFSET,
	OPEN_VOICEIOCTL_SET_PLAY_POSITION,
	OPEN_VOICEIOCTL_SET_LOOP_STATE,
	OPEN_VOICEIOCTL_SET_NOTIFICATION_POINT,
	OPEN_VOICEIOCTL_CLEAR_NOTIFICATION_POINT,
	OPEN_VOICEIOCTL_SET_NOTIFICATION_FREQUENCY
} OPEN_VOICEIOCTL;

typedef struct OPEN_SendRouteParamSetExt
{
	unsigned int dwChannel;
	unsigned int dwSend;
	OPEN_HAROUTING dwDest;
} OPEN_SendRouteParamSet;

typedef struct OPEN_SendLevelParamSetExt
{
	unsigned int dwChannel;
	unsigned int dwSend;
	unsigned int dwLevel;
} OPEN_SendLevelParamSet;

typedef struct OPEN_VoiceParamSetExt
{
	OPEN_VOICEIOCTL VoiceIoctl;
	unsigned int dwParam1;
	unsigned int dwParam2;
} OPEN_VoiceParamSet;

typedef struct OPEN_SynthParamSetExt
{
	OPEN_HASYNTHPARAMSEXT param;
	int lPARWValue;
} OPEN_SynthParamSet;

__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Play(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Pause(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Stop(void* hHandle);
__declspec(dllexport) OPEN_HAWOSTATUS SEGAAPI_GetPlaybackStatus(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetFormat(void* hHandle, OPEN_HAWOSEFORMAT* pFormat);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetFormat(void* hHandle, OPEN_HAWOSEFORMAT* pFormat);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSampleRate(void* hHandle, unsigned int dwSampleRate);
__declspec(dllexport) unsigned int SEGAAPI_GetSampleRate(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetPriority(void* hHandle, unsigned int dwPriority);
__declspec(dllexport) unsigned int SEGAAPI_GetPriority(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetUserData(void* hHandle, void* hUserData);
__declspec(dllexport) void* SEGAAPI_GetUserData(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendRouting(void* hHandle, unsigned int dwChannel, unsigned int dwSend,
	OPEN_HAROUTING dwDest);
__declspec(dllexport) OPEN_HAROUTING SEGAAPI_GetSendRouting(void* hHandle, unsigned int dwChannel, unsigned int dwSend);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendLevel(void* hHandle, unsigned int dwChannel, unsigned int dwSend,
	unsigned int dwLevel);
__declspec(dllexport) unsigned int SEGAAPI_GetSendLevel(void* hHandle, unsigned int dwChannel, unsigned int dwSend);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetChannelVolume(void* hHandle, unsigned int dwChannel,
	unsigned int dwVolume);
__declspec(dllexport) unsigned int SEGAAPI_GetChannelVolume(void* hHandle, unsigned int dwChannel);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetPlaybackPosition(void* hHandle, unsigned int dwPlaybackPos);
__declspec(dllexport) unsigned int SEGAAPI_GetPlaybackPosition(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetNotificationFrequency(void* hHandle, unsigned int dwFrameCount);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetNotificationPoint(void* hHandle, unsigned int dwBufferOffset);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_ClearNotificationPoint(void* hHandle, unsigned int dwBufferOffset);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetStartLoopOffset(void* hHandle, unsigned int dwOffset);
__declspec(dllexport) unsigned int SEGAAPI_GetStartLoopOffset(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndLoopOffset(void* hHandle, unsigned int dwOffset);
__declspec(dllexport) unsigned int SEGAAPI_GetEndLoopOffset(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndOffset(void* hHandle, unsigned int dwOffset);
__declspec(dllexport) unsigned int SEGAAPI_GetEndOffset(void* hHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetLoopState(void* hHandle, int bDoContinuousLooping);
__declspec(dllexport)int SEGAAPI_GetLoopState(void* hHandle);
__declspec(dllexport)OPEN_SEGASTATUS SEGAAPI_UpdateBuffer(void* hHandle, unsigned int dwStartOffset,
	unsigned int dwLength);
__declspec(dllexport)OPEN_SEGASTATUS SEGAAPI_SetSynthParam(void* hHandle, OPEN_HASYNTHPARAMSEXT param, int lPARWValue);
__declspec(dllexport) int SEGAAPI_GetSynthParam(void* hHandle, OPEN_HASYNTHPARAMSEXT param);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSynthParamMultiple(void* hHandle, unsigned int dwNumParams,
	OPEN_SynthParamSet* pSynthParams);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetSynthParamMultiple(void* hHandle, unsigned int dwNumParams,
	OPEN_SynthParamSet* pSynthParams);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetReleaseState(void* hHandle, int bSet);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_PlayWithSetup(
	void* hHandle,
	unsigned int dwNumSendRouteParams, OPEN_SendRouteParamSet* pSendRouteParams,
	unsigned int dwNumSendLevelParams, OPEN_SendLevelParamSet* pSendLevelParams,
	unsigned int dwNumVoiceParams, OPEN_VoiceParamSet* pVoiceParams,
	unsigned int dwNumSynthParams, OPEN_SynthParamSet* pSynthParams
);
typedef void(*OPEN_HAWOSEGABUFFERCALLBACK)(void* hHandle,
	OPEN_HAWOSMESSAGETYPE message);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_CreateBuffer(OPEN_HAWOSEBUFFERCONFIG* pConfig,
	OPEN_HAWOSEGABUFFERCALLBACK pCallback,
	unsigned int dwFlags,
	void* * phHandle);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_DestroyBuffer(void* hHandle);
__declspec(dllexport) int SEGAAPI_SetGlobalEAXProperty(GUID* guid, unsigned long ulProperty, void* pData,
	unsigned long ulDataSize);
__declspec(dllexport) int SEGAAPI_GetGlobalEAXProperty(GUID* guid, unsigned long ulProperty, void* pData,
	unsigned long ulDataSize);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSPDIFOutChannelStatus(
	unsigned int dwChannelStatus,
	unsigned int dwExtChannelStatus);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetSPDIFOutChannelStatus(
	unsigned int* pdwChannelStatus,
	unsigned int* pdwExtChannelStatus);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSPDIFOutSampleRate(OPEN_HASPDIFOUTRATE dwSamplingRate);
__declspec(dllexport) OPEN_HASPDIFOUTRATE SEGAAPI_GetSPDIFOutSampleRate(void);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSPDIFOutChannelRouting(
	unsigned int dwChannel,
	OPEN_HAROUTING dwSource);
__declspec(dllexport) OPEN_HAROUTING SEGAAPI_GetSPDIFOutChannelRouting(unsigned int dwChannel);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetIOVolume(OPEN_HAPHYSICALIO dwPhysIO, unsigned int dwVolume);
__declspec(dllexport) unsigned int SEGAAPI_GetIOVolume(OPEN_HAPHYSICALIO dwPhysIO);
__declspec(dllexport) void SEGAAPI_SetLastStatus(OPEN_SEGASTATUS LastStatus);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetLastStatus(void);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Reset(void);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Init(void);
__declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Exit(void);


and here is the main official header
/**
 * Copyright (C) 2004-2005  Creative Technology Ltd.  All rights reserved.
 *
 ****************************************************************************
 *  \file               segaapi.h
 *  \brief
 *  This file contains the definition of the interfaces that requested by SEGA
 *  for audio output support.
 *
 *
 * @author      Creative
 * 
 * $Date: 2006/08/08 03:46:11 $
 * 
 ****************************************************************************
 * Revision History:
 *
 * 0.5  
 * 1st release to SEGA for review.
 *
 * 0.51
 * Added other interfaces to control effects, volume, optical-out etc.
 * Cleanup and added more information on the wave playback interfaces.
 *
 * 0.52
 * Added correlation between HASYNTHPARAMSEXT enumerators with SoundFont parameters.
 *
 * 0.53
 * Change name to segaapi.h (was WaveOutSegaAPI.h) as the API covers more than
 * just wave out.
 * Add prefix SEGAAPI_ to each of the functions to avoid names conflict.
 * Change HAERR_ to SEGAERR_, HA_SUCCESS to SEGA_SUCCESS
 *
 * 0.54
 * Added more documentation.
 *
 * 0.80
 * Added SetLastStatus and GetLastStatus functions.  
 * Change version to 0.80 to indicate close to final. 
 *
 * 0.9
 * Made the header file both gcc and g++ compliance as per request. 
 * 
 * 0.91
 * Updated voice priority description.  
 *
 * 0.92
 * Added SEGAAPI_Reset, and updated CreateBuffer() to support synthesizer buffer.  
 * 
 * 0.93
 * Added SEGAAPI_GetSendRouting and SEGAAPI_GetSetLevel per request.  
 * 
 * 0.94
 * Changed default send levels in SEGAAPI_CreateBuffer to 0.  
 *
 * 1.00
 * Added SEGAAPI_Init() and SEGAAPI_Exit() per request.
 *
 * 1.01
 * Updated SEGAAPI_SetReleaseState() document. 
 *
 * 1.02
 * Added SEGAAPI_SetSynthParamMultiple() and SEGAAPI_GetSynthParamMultiple().
 * Updated SEGAAPI_CreateBuffer() for user-mode buffer support.
 *
 *  1.03
 *  Added SEGAAPI_PlayWithSeup() .
 *
 * 1.04
 * Updated SEGAAPI_CreateBuffer() for HABUF_USE_MAPPED_MEM buffer support.
 * 
 ****************************************************************************
 * Released under NDA. 
 *
 * This document has been reviewed by SEGA.
 ****************************************************************************
 */

#ifndef __SEGAAPI_H
#define __SEGAAPI_H

// INCLUDES
#include "segadef.h"
#include "segaerr.h"


#ifdef __cplusplus
extern "C" {
#endif

/**
 * The following defines SEGA custom EAX40 properties.
 */

// {A7FEEC3F-2BFD-4a40-891F-7423E38BAC1F}
DEFINE_GUID(EAXPROPERTYID_EAX40_SEGA_Custom, 
0xa7feec3f, 0x2bfd, 0x4a40, 0x89, 0x1f, 0x74, 0x23, 0xe3, 0x8b, 0xac, 0x1f);

// SEGA custom EAX40 properties
/*
 * The only property now is to switch the FX returns for 
 * FXSlot2 and FXSlot3 when non-reverb is loaded to these slots.
 *
 * EAXSEGA_STEREO_RETURN
 *    ulDataSize = CTDWORD
 *      value = 0 denotes route to front L/R (default)
 *      value = 1 denotes route to Rear L/R 
 */
typedef enum
{
    EAXSEGA_STEREO_RETURN_FX2 = 0,   
    EAXSEGA_STEREO_RETURN_FX3 = 1  
} EAXSEGA_PROPERTY;

/**
 * The following defines all of the messages wave output clients
 * can receive as part of their callback routines.
 */
typedef enum {
    HAWOS_RESOURCE_STOLEN = 0,
    HAWOS_NOTIFY = 2
} HAWOSMESSAGETYPE;


/*
 * The Playback status.
 */
typedef enum {
    HAWOSTATUS_STOP,                      /* The voice is stopped */
    HAWOSTATUS_ACTIVE,                    /* The voice is playing */
    HAWOSTATUS_PAUSE,                     /* The voice is paused */
    HAWOSTATUS_INVALID = -1               /* Invalid state */    
} HAWOSTATUS;


/*
 * dwFlags use in CreateBuffer.
 */
#define HABUF_SYNTH_BUFFER		0x00000001	// indicate to create a synth buffer
#define HABUF_ALLOC_USER_MEM    0x00000002	// indicate that caller allocate memory
#define HABUF_USE_MAPPED_MEM    0x00000004	// indicate to use previous caller allocated memory

/*
 * The HAWOSEFORMAT structure is used to change the format of an output client.
 */

#ifndef __HAWAVE_H 
#define HASF_UNSIGNED_8PCM      0x0004      /* Unsigned (offset 128) 8-bit PCM */
#define HASF_SIGNED_16PCM       0x0020      /* Signed 16-bit PCM */ 
#endif
 
typedef struct {
    CTDWORD     dwSampleRate;           /* The sample rate the client desires (in Hz) */
    CTDWORD     dwSampleFormat;         /* The sample format the client will use */
    CTDWORD     byNumChans;             /* The number of samples in the sample
                                         *  frame (1 = mono, 2 = stereo).  */
} HAWOSEFORMAT;



/*
 * HAWOSEMAPDATA contains
 */
typedef struct {
    CTDWORD     dwSize;       /* Supply by caller. Size (in bytes) of the valid sample data */
    CTDWORD     dwOffset;     /* Return by driver. Offset of buffer where the the first valid sample should be written to */
    CTHANDLE    hBufferHdr;   /* Memory address that user-space application can access, or mapped memory handle. */
} HAWOSEMAPDATA;


/*
 * The HAWOSEBUFFERCONFIG structure is used to describe how an input or
 * output buffer client wishes to configure the device when it opens it.
 */
typedef struct {
    CTDWORD     dwPriority;         /* The priority with which the voices
                                     *  should be allocated.  This is used
                                     *  when voices need to be ripped off. */
    CTDWORD     dwSampleRate;       /* The sample rate the voice desires */
    CTDWORD     dwSampleFormat;     /* The sample format the voice will use */
    CTDWORD     byNumChans;         /* The number of samples in the sample
                                     *  frame. (1 = mono, 2 = stereo). */
    CTDWORD     dwReserved;         /* Reserved field */
    CTHANDLE    hUserData;          /* User data */
    HAWOSEMAPDATA mapData;          /* The sample memory mapping for the buffer. */
} HAWOSEBUFFERCONFIG;



/**
 * Default values
 */
#define HAWOSEVOL_MAX      0xFFFFFFFF  /* Maximum volume; no attenuation */


/**
 * Since Tina has up to 64- voices, voice priorities typically ranging 
 * from 0 to 63, where 0 is lower priority (more likely to get ripped off)
 * than 63.
 *
 * Set voice priority to HAWOSEP_MAXIMUM if a voice must never get ripped
 * off under any circumstances.
 */
#define HAWOSEP_MINIMUM    0
#define HAWOSEP_MAXIMUM    0xFFFFFFFF


/** @brief Routing List
 * 
 * voice sends routing to speakers or effects ports.
 *
 */
#define HAWOSE_UNUSED_SEND 0xFFFF0001


typedef enum HAROUTING{
    HA_UNUSED_PORT=HAWOSE_UNUSED_SEND,

    // Dry multi-channel outputs
    HA_FRONT_LEFT_PORT =0,
    HA_FRONT_RIGHT_PORT=1,
    HA_FRONT_CENTER_PORT=2,
    HA_LFE_PORT=3,
    HA_REAR_LEFT_PORT=4,
    HA_REAR_RIGHT_PORT=5,

    // effect outputs
    HA_FXSLOT0_PORT=10,
    HA_FXSLOT1_PORT=11,
    HA_FXSLOT2_PORT=12,
    HA_FXSLOT3_PORT=13

} HAROUTING;  


/**
 * The following defines SPDIF-Out sampling rate.
 */
typedef enum {
    HASPDIFOUT_44_1KHZ=0,
    HASPDIFOUT_48KHZ,
    HASPDIFOUT_96KHZ
} HASPDIFOUTRATE;


/**
 * The following defines inputs and outputs of SEGA sound board.
 */
typedef enum HAPHYSICALIO {
    // analog outputs
    HA_OUT_FRONT_LEFT =0,
    HA_OUT_FRONT_RIGHT=1,
    HA_OUT_FRONT_CENTER=2,
    HA_OUT_LFE_PORT=3,
    HA_OUT_REAR_LEFT=4,
    HA_OUT_REAR_RIGHT=5,

    // optical Outputs
    HA_OUT_OPTICAL_LEFT=10,
    HA_OUT_OPTICAL_RIGHT=11,

    // Line In
    HA_IN_LINEIN_LEFT=20,
    HA_IN_LINEIN_RIGHT=21

}HAPHYSICALIO ;  


/** @brief Synth parameters enumeration list
 *
 * This table defines the most common (and hardware-supported)
 * control routings in Real World Unit.
 *
 * Refers to DLS spec or SoundFont spec for details of these Parameters,
 * their units and their ranges.
 */
typedef enum HASYNTHPARAMSEXT {
    HAVP_ATTENUATION,                           ///< 0,         0x00,  initialAttenuation
    HAVP_PITCH,                                 ///< 1,         0x01,  fineTune + coarseTune * 100
    HAVP_FILTER_CUTOFF,                         ///< 2,         0x02,  initialFilterFc
    HAVP_FILTER_Q,                              ///< 3,         0x03,  initialFilterQ
    HAVP_DELAY_VOL_ENV,                         ///< 4,         0x04,  delayVolEnv
    HAVP_ATTACK_VOL_ENV,                        ///< 5,         0x05,  attackVolEnv
    HAVP_HOLD_VOL_ENV,                          ///< 6,         0x06,  holdVolEnv
    HAVP_DECAY_VOL_ENV,                         ///< 7,         0x07,  decayVolEnv
    HAVP_SUSTAIN_VOL_ENV,                       ///< 8,         0x08,  sustainVolEnv
    HAVP_RELEASE_VOL_ENV,                       ///< 9,         0x09,  releaseVolEnv
    HAVP_DELAY_MOD_ENV,                         ///< 10,        0x0A,  delayModEnv
    HAVP_ATTACK_MOD_ENV,                        ///< 11,        0x0B,  attackModEnv
    HAVP_HOLD_MOD_ENV,                          ///< 12,        0x0C,  holdModEnv
    HAVP_DECAY_MOD_ENV,                         ///< 13,        0x0D,  decayModEnv
    HAVP_SUSTAIN_MOD_ENV,                       ///< 14,        0x0E,  sustainModEnv
    HAVP_RELEASE_MOD_ENV,                       ///< 15,        0x0F,  releaseModEnv
    HAVP_DELAY_MOD_LFO,                         ///< 16,        0x10,  delayModLFO
    HAVP_FREQ_MOD_LFO,                          ///< 17,        0x11,  freqModLFO
    HAVP_DELAY_VIB_LFO,                         ///< 18,        0x12,  delayVibLFO
    HAVP_FREQ_VIB_LFO,                          ///< 19,        0x13,  freqVibLFO
    HAVP_MOD_LFO_TO_PITCH,                      ///< 20,        0x14,  modLfoToPitch
    HAVP_VIB_LFO_TO_PITCH,                      ///< 21,        0x15,  vibLfoToPitch
    HAVP_MOD_LFO_TO_FILTER_CUTOFF,              ///< 22,        0x16,  modLfoToFilterFc
    HAVP_MOD_LFO_TO_ATTENUATION,                ///< 23,        0x17,  modLfoToVolume
    HAVP_MOD_ENV_TO_PITCH,                      ///< 24,        0x18,  modEnvToPitch
    HAVP_MOD_ENV_TO_FILTER_CUTOFF               ///< 25,        0x19,  modEnvToFilterFc

} HASYNTHPARAMSEXT;



typedef enum {
    VOICEIOCTL_SET_START_LOOP_OFFSET=0x100,
    VOICEIOCTL_SET_END_LOOP_OFFSET,
    VOICEIOCTL_SET_END_OFFSET,
    VOICEIOCTL_SET_PLAY_POSITION,
    VOICEIOCTL_SET_LOOP_STATE,
    VOICEIOCTL_SET_NOTIFICATION_POINT,
    VOICEIOCTL_CLEAR_NOTIFICATION_POINT,
    VOICEIOCTL_SET_NOTIFICATION_FREQUENCY
} VOICEIOCTL;


#ifndef __SENDROUTEPARAMSET_
#   define __SENDROUTEPARAMSET_
typedef struct SendRouteParamSetExt {
    CTDWORD		dwChannel;
    CTDWORD		dwSend;
    HAROUTING	dwDest;
} SendRouteParamSet;
#endif

#ifndef __SENDLEVELPARAMSET_
#   define __SENDLEVELPARAMSET_
typedef struct SendLevelParamSetExt {
    CTDWORD		dwChannel;
    CTDWORD		dwSend;
    CTDWORD		dwLevel;
} SendLevelParamSet;
#endif

#ifndef __VOICEPARAMSET_
#   define __VOICEPARAMSET_
typedef struct VoiceParamSetExt {
    VOICEIOCTL	VoiceIoctl;
    CTDWORD		dwParam1;		// parameter value
    CTDWORD		dwParam2;		// reserved
} VoiceParamSet;
#endif

#ifndef __SYNTHPARAMSET_
#   define __SYNTHPARAMSET_
typedef struct SynthParamSetExt {
    HASYNTHPARAMSEXT param;
    CTLONG  lPARWValue;
} SynthParamSet;
#endif


/*  
How this SYNTH PARAMS EXT maps to Sega API requests:

    HAVP_ATTENUATION,                           SetVolume()                     
    HAVP_PITCH,                                 SetPitch()                              
    HAVP_FILTER_CUTOFF,                         SetFilter()                     
    HAVP_FILTER_Q,                              SetFilter()     
    HAVP_DELAY_VOL_ENV,                         SetEG()                 
    HAVP_ATTACK_VOL_ENV,                        SetEG()         
    HAVP_HOLD_VOL_ENV,                          SetEG()                 
    HAVP_DECAY_VOL_ENV,                         SetEG()         
    HAVP_SUSTAIN_VOL_ENV,                       SetEG()         
    HAVP_RELEASE_VOL_ENV,                       SetEG()                 
    HAVP_DELAY_MOD_ENV,                         SetEG()         
    HAVP_ATTACK_MOD_ENV,                        SetEG() 
    HAVP_HOLD_MOD_ENV,                          SetEG()                 
    HAVP_DECAY_MOD_ENV,                         SetEG()         
    HAVP_SUSTAIN_MOD_ENV,                       SetEG()         
    HAVP_RELEASE_MOD_ENV,                       SetEG()         
    HAVP_DELAY_MOD_LFO,                         SetLFO()                        
    HAVP_FREQ_MOD_LFO,                          SetLFO()                
    HAVP_DELAY_VIB_LFO,                         SetLFO()                
    HAVP_FREQ_VIB_LFO,                          SetLFO()                
    HAVP_MOD_LFO_TO_PITCH,                      SetLFO()                
    HAVP_VIB_LFO_TO_PITCH,                      SetLFO()        
    HAVP_MOD_LFO_TO_FILTER_CUTOFF,              SetLFO()
    HAVP_MOD_LFO_TO_ATTENUATION,                SetLFO()        
    HAVP_MOD_ENV_TO_PITCH,                      SetEG()         
    HAVP_MOD_ENV_TO_FILTER_CUTOFF,              SetEG() 

*/



/*
 * Interfaces expose.  These interfaces will be exposed in user mode.
 * 
 * Note:
 * 1. hHandle that passes into these functions is An opaque identifier 
 *    obtained from CreateBuffer.
 *
 * 2. A mono buffer uses one voice.  A stereo buffer uses two voices.
 *
 */


/***********************************************************
 * @section 
 * API for playback operation controls.
 * 
 */
  
/**
 * Starts sample playback of a buffer.
 * Playback position is not modified when Play is called and will 
 * start incrementing from its previous value at the sample
 * rate.
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return 
 * Returns SEGA_SUCCESS if playback can start.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_Play(CTHANDLE hHandle);


/**
 * Halts playback and freezes the current counter at its last
 * value.
 * 
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return 
 * Returns SEGA_SUCCESS if playback was successfully paused. 
 * Otherwise, returns an appropriate error code.
 */
SEGASTATUS SEGAAPI_Pause(CTHANDLE hHandle);


/**
 * Stops playback and resets the sample counter.
 *
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 * 
 * @return 
 * Returns SEGA_SUCCESS if playback was successfully paused. 
 * Otherwise, returns an appropriate error code.
 *
 */
SEGASTATUS SEGAAPI_Stop(CTHANDLE hHandle);


/**
 * Returns a current playback status of a buffer.
 *
 * CALL LEVELS: DPC, PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return 
 * One of the playback status defined in the HAWOSTATUS enumration type.
 * If the returned status is HAWOSTATUS_INVALID, use GetLastStatus() to check the error code.
 */
HAWOSTATUS SEGAAPI_GetPlaybackStatus(CTHANDLE hHandle);




/***********************************************************
 * @section 
 * API for playback format controls.
 * 
 */

/**
 * Changes the buffer's format to the values specified in
 * the new format pFormat.  In some cases, it is possible that
 * an attempt to change the configuration may fail, as the change may
 * require more resources than were previously allocated.
 *
 * The playback buffer configuration may not be changed while the buffer is
 * playing.
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 * 
 * @param pFormat 
 * The new format to change to.
 * 
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 *
 * @retval SEGAERR_PLAYING if the buffer is currently playing
 * @retval SEGAERR_BAD_POINTER if pFormat is NULL
 * @retval SEGAERR_NO_RESOURCES if insufficient resources are available
 * @retval SEGAERR_BAD_CONFIG if something in the given configuration is
 * invalid.
 */
SEGASTATUS SEGAAPI_SetFormat(CTHANDLE hHandle, HAWOSEFORMAT *pFormat);


/**
 * Returns the current format of the buffer.
 * 
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   pFormat
 * Pointer to an address where the current format to return to.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_GetFormat(CTHANDLE hHandle, HAWOSEFORMAT *pFormat);


/**
 * Changes the playback sample rate for the current client to the
 * value specified.  The new value only pertains to this buffer.
 * If hardware cannot support changing sample rates for individual
 * buffers, it can return an error in response to this routine.
 * 
 * CALL LEVELS: PASSIVE
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param  dwSampleRate
 * The desired sample rate.
 * 
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetSampleRate(CTHANDLE hHandle, CTDWORD dwSampleRate);


/**
 * Returns the current sample rate.
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return 
 * Returns the current sample rate.
 * If the returned value is 0, use GetLastStatus() to check the error code.
 */
CTDWORD SEGAAPI_GetSampleRate(CTHANDLE hHandle);



/***********************************************************
 * @section 
 * API for Voice priority management.
 *
 */

 /**
 * Changes the buffer's priority to the specified value.
 *
 * If all the voices are set to HAWOSEP_MAXIMUM (0xFFFFFFFF) priority, 
 * CreateBuffer() call will return failure when running out of voices.
 *
 * CALL LEVEL: DPC, PASSIVE
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param dwPriority
 * The new priority for the buffer.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetPriority(CTHANDLE hHandle, CTDWORD dwPriority);


/**
 * Returns the buffer's current priority.
 *
 * CALL LEVEL: DPC, PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns the buffer's current priority.
 * Note that returned value is also set to 0 if error invoked this function.
 */
CTDWORD SEGAAPI_GetPriority(CTHANDLE hHandle);


/***********************************************************
 * @section 
 * API for storing an User-defined data.
 * 
 */

/**
 * Stores a handle to user-defined data defined by the client structure.
 * This allows caller to associate caller-specific data.  The caller is
 * responsible for managing this data area.
 *
 * Note that caller can specify an user-defined data in the CreateBuffer()
 * call.  Caller can use SetUserData() to update the user-defined data.
 *
 * CALL LEVELS: DPC, PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param  hUserData   
 * A handle to user-defined data.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetUserData(CTHANDLE hHandle, CTHANDLE hUserData);


/**
 * Returns the last user-defined data set by the caller.
 *
 * CALL LEVELS: DPC, PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns the user-defined data.
 * Note that returned value is set to 0 if error invoked this function.
 */
CTHANDLE SEGAAPI_GetUserData(CTHANDLE hHandle);


/***********************************************************
 * @section 
 * API for Send routing and Send Level controls.
 * 
 */

/**
 * Changes the destination to which a channel send is connected.
 * Each channel has the possibility of supporting multiple sends.
 * For Tina chip, each channel has seven sends.
 * Each of these sends can be connected to a destination.  
 *
 * Note that it is invalid to have more than one sends routed to
 * a same destination.  For example, if send 0 is previously routed
 * to front-left, send 0 will need to be disconnected before another
 * send can route to front-left. 
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwChannel
 *  The channel to reroute.
 *
 * @param   dwSend
 *  The send number being addressed.
 *
 * @param   dwDest
 *  The destination to which the send should be connected.
 *  If the send doesn't need to be connected to anything,
 *  specify HAWOSE_UNUSED_SEND.  See HAROUTING for the details
 *  of the destination enumeration list.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 *
 * @retval  SEGAERR_UNSUPPORTED if channels can't be rerouted.
 * @retval  SEGAERR_INVALID_CHANNEL if the specified channel isn't in use.
 * @retval  SEGAERR_INVALID_SEND if the specified send isn't supported.
 */
SEGASTATUS SEGAAPI_SetSendRouting(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend,
                           HAROUTING dwDest);

/**
 * Returns the destination of which a channel send is connected to.
 *
 * CALL LEVELS: PASSIVE
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwChannel
 *  The channel requested.
 *
 * @param   dwSend
 *  The send number being addressed.
 *
 * @return  
 * Returns the current destination.
 * Note that returned value is set to HA_UNUSED_PORT if error invoked this function.  
 */
HAROUTING SEGAAPI_GetSendRouting(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend); 
                           
/**
 * Sets the output level of a particular send on a channel to the specified
 * level.
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 * 
 * @param dwChannel
 * The channel whose output level is to be set.
 *
 * @param dwSend
 * The send being addressed.
 *
 * @param dwLevel
 * The output level.  Output levels are specified
 * in linear values.  A value of 0xFFFFFFFF indicates
 * full on (0 attenuation).  A value of 0x0 indicates
 * infinite attenuation.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 * 
 * @retval   SEGAERR_UNSUPPORTED if the device doesn't support channel levels in
 * general.
 * @retval   SEGAERR_INVALID_CHANNEL if the specified channel isn't valid.
 * @retval   SEGAERR_INVALID_SEND if the specified send isn't valid.
 */
SEGASTATUS SEGAAPI_SetSendLevel(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend,
                       CTDWORD dwLevel);

/**
 * Returns the output level of a particular send on a channel.
 *
 * CALL LEVELS: PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 * 
 * @param   dwChannel
 *  The channel requested.
 *
 * @param   dwSend
 *  The send number being addressed.
 *
 * @return  
 * Returns the current send level.
 * Note that returned value is set to 0 if error invoked this function. 
 */
CTDWORD SEGAAPI_GetSendLevel(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwSend); 



/***********************************************************
 * @section 
 * API for volume level controls.
 * 
 */

/**
 * Sets the volume to a specific linear value.  Volumes are specified
 * as fractional fixed-point linear values between HAWOSEVOL_MAX (0xFFFFFFFF)
 * and 0x0. The dwVolume is specified in linear increments from 0 to 1 
 * (actually to 65535 divided by 65536).  A 0 value represents 96db of attenuation, 
 * while a 1 value represents full volume. Default is full volume.
 *
 * Volume is a global value and is applied pre-send.
 *
 * CALL LEVELS: PASSIVE
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param dwChannel
 * The channel to change.
 *
 * @param dwVolume
 * The new volume level to change to.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 *
 * @retval   SEGAERR_UNSUPPORTED if the device can't change volume.
 * @retval   SEGAERR_INVALID_CHANNEL if the given send isn't valid.
 */
SEGASTATUS SEGAAPI_SetChannelVolume(CTHANDLE hHandle, CTDWORD dwChannel, CTDWORD dwVolume);

/**
 * Returns the current volume level for the requested channel.
 *
 * CALL LEVELS: PASSIVE
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwChannel
 * The channel requested.
 *
 * @return  
 * Returns the current volume.
 * Note that returned value is set to 0 if error invoked this function.
 */
CTDWORD SEGAAPI_GetChannelVolume(CTHANDLE hHandle, CTDWORD dwChannel);



/***********************************************************
 * @section * API for playback position controls.
 * 
 */

/**
 * Changes the buffer position pointer from which hardware
 * is fetching samples.   Changes take place immediately and
 * can be made while playback is occurring.
 *
 * CALL LEVELS: DPC, PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param  dwPlaybackPos
 * The buffer position (IN BYTES) where the playback
 * pointer should be moved.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetPlaybackPosition(CTHANDLE hHandle, CTDWORD dwPlaybackPos);


/**
 * Returns the position in the buffer (IN BYTES) where the
 * hardware is currently playing samples from.
 *
 * CALL LEVELS: DPC, PASSIVE
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns the current playback position.
 * Note that returned value is set to 0 if error invoked this function.
 */
CTDWORD SEGAAPI_GetPlaybackPosition(CTHANDLE hHandle);


/***********************************************************
 * @section 
 * API for buffer update, loop regions and notification controls.
 * 
 */

/**
 * This function sets the frequency at which a callback will be generated.
 * The callback will be invoked periodically at a fixed interval
 * specified by the dwFrameCount.  dwFrameCount is in the units of 
 * sample frames.
 *
 * This notification method is typically used for ring buffer that need 
 * periodic notification to update the ring buffer data.
 *
 * Note that callback execution is scheduled at the later time (DPC), not at   
 * the interrupt time.
 *
 *
 * CALL LEVELS: PASSIVE
 *
 * @param   hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwFrameCount
 * The frequency, in sample frames, at which the the notification is invoked.
 * Specifying a value of zero cancels the callback.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetNotificationFrequency(CTHANDLE hHandle, CTDWORD dwFrameCount);

/**
 * This function can be used to set a notification point in the ring
 * buffer.  Whenever the play position passes over a notification point
 * the device will schedule a callback to the function indicated when
 * the ring buffer was created.
 *
 * Note that callback execution is scheduled at the later time (DPC), not at   
 * the interrupt time.
 *
 * CALL LEVELS: PASSIVE, DPC
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwBufferOffset
 * The offset (in bytes) in the buffer where the notification
 * point is to be set.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetNotificationPoint(CTHANDLE hHandle, CTDWORD dwBufferOffset);

/**
 * Removes a previously set notification point.
 *
 * CALL LEVELS: PASSIVE, DPC
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwBufferOffset
 * The offset (in bytes) of the notification point to
 * remove.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_ClearNotificationPoint(CTHANDLE hHandle, CTDWORD dwBufferOffset);


/**
 * Sets the start loop offset.  The start loop offset controls where
 * the play pointer will jump when it crosses the End Loop Offset.
 * There is no requirement that the start loop offset preceed the
 * End Loop Offset.  It is illegal to set Start Loop Offset and
 * End Loop Offset to the same value, however, and in general it is
 * a bad idea to have the difference between the two be less than
 * 16 samples.
 *
 * GetStartLoopOffset() just returns the current start loop offset
 * value and may be called at any interrupt level.
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwOffset
 * The offset in bytes from the beginning of the buffer of
 * the loop start point.
 *
 * @return
 * Returns SEGA_SUCCESS if the loop offset is changed successful.  
 * Otherwise, returns an appropriate error code.
 */
SEGASTATUS SEGAAPI_SetStartLoopOffset(CTHANDLE hHandle, CTDWORD dwOffset);


/**
 * Returns the current start loop offest.
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns the current start loop offset.
 * Note that returned value is set to 0 if error invoked this function.
 */
CTDWORD SEGAAPI_GetStartLoopOffset(CTHANDLE hHandle);


/**
 * Sets the End Loop Offset position.  When the play pointer crosses
 * the End Loop Offset it will jump to the Start Loop Offset
 * position if buffer is in looping state (bDoContinuousLooping
 * is set to TRUE).
 *
 * GetEndLoopOffset() just returns the current value and may be called
 * at any interrupt level.
 *
 * Note that EndLoopOffset must not be larger than EndOffset. 
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param  dwOffset
 * An offset in bytes from the beginning of the buffer,
 * dwOffset specifies the location for the End Loop point.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetEndLoopOffset(CTHANDLE hHandle, CTDWORD dwOffset);


/**
 * Returns the current end loop offest.
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return 
 * Returns the current end loop offest.
 * Note that returned value is set to 0 if error invoked this function.
 */
CTDWORD SEGAAPI_GetEndLoopOffset(CTHANDLE hHandle);


/**
 * Sets the End Offset position.  When the play pointer crosses
 * the End Offset (assuming the buffer isn't currently looping)
 * the buffer will halt.
 *
 * Only change the End offset position when buffer is not at
 * HAWOSTATUS_ACTIVE state.  End Offset must be sample frame aligned.  
 * For example, 16-bit 1 channel is WORD aligned, 16-bit 2 channel 
 * is DWORD aligned.
 *
 * Note that EndOffset must not be larger than pConfig->mapdata.dwSize
 * specified in the CreateBuffer(). 
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   dwOffset
 * An offset in bytes from the beginning of the buffer,
 * dwOffset specifies the location for the End point.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetEndOffset(CTHANDLE hHandle, CTDWORD dwOffset);

/**
 * Returns the current end offest.
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns the current end offest.
 * Note that returned value is set to 0 if error invoked this function.
 */
CTDWORD SEGAAPI_GetEndOffset(CTHANDLE hHandle);

/**
 * Allows the user to control whether the voice loops back to the
 * Start Loop Offset when it crosses the End Loop Point or whether
 * it goes into the release phase (i.e. post end loop).  Note that
 * setting the loop state doesn't actually cause the device
 * to transition to the stopped state.
 * 
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   bDoContinuousLooping
 * If TRUE, the buffer will loop from start-loop to end
 * loop and back again. If FALSE, loop points are ignored
 * and buffer will play until the sample end, as programmed
 * with SetEndOffset. This may be programmed when as the buffer
 * is playing.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetLoopState(CTHANDLE hHandle, CTBOOL bDoContinuousLooping);

/**
 * Returns the current loop status. 
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns TRUE if it is in loop state. Otherwise, returns FALSE. 
 * Note that returned value is set to FALSE if error invoked this function.
 */
CTBOOL SEGAAPI_GetLoopState(CTHANDLE hHandle);


/**
 * Advises the driver code that some portion of the buffer
 * has been written with new data.  This method is required for devices
 * which don't support a memory-mapped ring buffer and allows the
 * underlying software to perform any necessary copying or format
 * conversion.
 *
 * The caller should call this function *after* they have filled
 * the data into the ring buffer that they passed
 * to the driver in the CreateBuffer() call.  
 *
 * Although this routine takes it values in bytes, the caller
 * is responsible for insuring that the starting offset and
 * length are a integer multiple of the sample size.
 *
 * CALL LEVELS: DPC, PASSIVE.
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param  dwStartOffset
 *  The offset of the first byte in the buffer which
 *  has changed (between 0 and bufferSize-1)
 * @param  dwLength
 *  The number of bytes which have changed.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_UpdateBuffer(CTHANDLE hHandle, CTDWORD dwStartOffset, CTDWORD dwLength);


/***********************************************************
 * @section 
 * Low level API to control Synth buffer parameters
 *
 */

/**
 * Sets and stores a synthesizer parameter, in Perceptually-Additive Real-World (PARW) units
 *
 * The parameter is applied for mono buffer only.
 *
 * CALL LEVELS: DPC, PASSIVE.
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param param
 * The parameter to apply
 *
 * @param lPARWValue
 * The value in PARW units
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetSynthParam(CTHANDLE hHandle, HASYNTHPARAMSEXT param, CTLONG lPARWValue);


/**
 * Returns the most recent call to SetSynthParam() in PARW units.  This is the cache value
 * of the most recent PARW value set by SetSynthParam(). If the parameter has not been set 
 * before, this function will return 0.  
 *
 * The parameter is applied for mono buffer only.
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param param
 * The parameter to retrieve
 *
 * @return
 * The returned value in PARW units.
 * Note that returned value is set to -1 if error invoked this function.
 */
CTLONG SEGAAPI_GetSynthParam(CTHANDLE hHandle, HASYNTHPARAMSEXT param);


/**
 * Sets and stores an array of synthesizer parameters, in Perceptually-Additive Real-World (PARW) units.
 *
 * The parameter is applied for mono buffer only.
 *
 * CALL LEVELS: DPC, PASSIVE.
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param dwNumParams
 * Number of parameters to apply
 *
 * @param pSynthParams
 * Pointer to the Synth Parameters array to apply.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetSynthParamMultiple(CTHANDLE hHandle, CTDWORD dwNumParams, SynthParamSet *pSynthParams);


/**
 * Retrieves an array of synthesizer parameters, in Perceptually-Additive Real-World (PARW) units.
 *
 * The parameter is applied for mono buffer only.
 *
 * CALL LEVELS: DPC, PASSIVE.
 * 
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param dwNumParams
 * Number of parameters to retrieve.
 *
 * @param pSynthParams
 * Pointer to the Synth Parameters array to retrieve.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_GetSynthParamMultiple(CTHANDLE hHandle, CTDWORD dwNumParams, SynthParamSet *pSynthParams);


/**
 * Set the voice into the release phase of the volume envelope engines when set to TRUE.  
 * This will automatically stop the voice when the voice reaches end of release phase.
 *
 * CALL LEVELS: DPC, PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param bSet
 * TRUE for enter releaes phase
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetReleaseState(CTHANDLE hHandle, CTBOOL bSet);


/**
 * Setup send route, send level, voice and synthesizer parameters that store in arrays and 
 *  play the buffer immediately.
 *
 * The function is applied for synth buffer only.
 * It is used only when the voice is not at playing state.
 *
 * CALL LEVELS: PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param dwNumSendRouteParams
 * Number of  send route parameters to apply.
 *
 * @param pSendRouteParams
 * Pointer to the Send Route Parameters array to apply.
 *
 * @param dwNumSendLevelParams
 * Number of  Send Level parameters to apply.
 *
 * @param pSendLevelParams
 * Pointer to the Send Level Parameters array to apply.
 *
 * @param dwNumVoiceParams
 * Number of  Voice parameters to apply.
 *
 * @param pVoiceParams
 * Pointer to the Voice Parameters array to apply.
 *
 * @param dwNumSynthParams
 * Number of  Synth parameters to apply.
 *
 * @param pSynthParams
 * Pointer to the Synth Parameters array to apply.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS  SEGAAPI_PlayWithSetup(
					CTHANDLE hHandle,
					CTDWORD dwNumSendRouteParams, SendRouteParamSet *pSendRouteParams, 
					CTDWORD dwNumSendLevelParams, SendLevelParamSet *pSendLevelParams, 
					CTDWORD dwNumVoiceParams, VoiceParamSet *pVoiceParams, 
					CTDWORD dwNumSynthParams, SynthParamSet *pSynthParams
);


/***********************************************************
 * @section 
 * Playback buffers (voices) callback notification function.  
 * 
 */

/**
 * A callback function of this type is passed into the
 * CreateBuffer() function and gets invoked when a WaveOutBuffer
 * client needs to notify the OS of some event.  Currently, the following event
 * types are defined:
 *
 *  HAWOS_RESOURCE_STOLEN -- Indicates the resources used to
 *  play the audio have been stolen.
 *
 *  HAWOS_NOTIFY -- Indicates that the current play position
 *  has passed over one of the notification points set
 *  with SetNotificationPoint.
 * 
 * CALL LEVELS: The callback is invoked at DPC level.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @param   HAWOSMESSAGETYPE message
 * The callback message.
 */
typedef void (*HAWOSEGABUFFERCALLBACK)(CTHANDLE hHandle,
   HAWOSMESSAGETYPE message);


/***********************************************************
 * @section 
 * API to create playback buffers (voices).  
 * 
 * This is the entry call to obtain the voice handle
 * and perform further voice operations.
 */

/**
 * Creates a new buffer (voice) of the output device using 
 * the configuration specified in pConfig.
 *
 * There are two types of buffers: normal buffer and synthesizer buffer.  
 * Typically, a buffer is created as normal buffer.  Synthesizer buffer 
 * is used if a buffer requires synthesizer type of parameter controls.
 *
 * Caller specifies the configuration to create in pConfig structure.
 * Driver will allocate memory and hardware resources needed for this 
 * voice creation.  If successful, the pConfig->mapdata.hBufferHdr contains  
 * the memory address that user-space application can access.  Caller 
 * should write the valid sound data into this memory buffer.
 *
 * Caller can indicate to the sound driver to use the caller allocated sound 
 * data memory buffer for efficiency purpose if the same buffer is re-used frequently.
 * In such a case, caller needs to provide the sound buffer address in the  
 * pConfig->mapdata.hBufferHdr and sets the HABUF_ALLOC_USER_MEM bit
 * of dwFlags parameter.  Caller needs to ensure that the sound buffer
 * memory is page-aligned and locked.  If successful, the pConfig->mapdata.hBufferHdr 
 * contains the mapped memory handle that application can use to create new voices. 
 *
 * Caller can indicate to the sound driver to use the previously caller allocated sound 
 * data memory buffer to conserve the use of memory if the same memory is re-used
 * for multiple voices.  In such a case, caller needs to provide previous buffer mapped 
 * memory handle in the pConfig->mapdata.hBufferHdr, sets the pConfig->mapdata.dwSize
 * and the pConfig->mapdata.dwOffset to the same values as per previous buffer and 
 * sets the HABUF_USE_MAPPED_MEM bit of dwFlags parameter.  
 *
 * HABUF_USE_MAPPED_MEM can only be used for sound data memory buffer that 
 * created on the same application process/thread space.  
 *
 * Note that it is invalid to set both HABUF_ALLOC_USER_MEM and 
 * HABUF_USE_MAPPED_MEM bits at the same time. 
 *
 * pConfig->mapdata.dwSize must be sample frame aligned.  For example, 
 * 16-bit 1 channel is WORD aligned, 16-bit 2 channel is DWORD aligned.
 * For more efficient memory management, pConfig->mapdata.dwSize is 
 * recommended to be page aligned.
 *
 * The size of the ring buffer is fixed throughout the lifetime of the 
 * buffer until it is destroyed.  During the lifetime of the buffer, 
 * caller can periodically update the data of the buffer if necessary or 
 * modulate the buffer with SetSynthParam or SetSynthParamMultiple functions 
 * if the buffer is created as synthesizer buffer. 
 *
 * If all the voices are currently in use, CreateBuffer will perform voice-stealing 
 * to fulfill the request. Note that voice stealing may fail if all voices that are 
 * currently in use are set to HAWOSEP_MAXIMUM priority.
 *
 * The followings are default values for a newly created buffer:
 *   - Send Routing 
 *      - for 1 channel buffer, channel is routed to Front-Left and Front-Right.
 *      - for 2 channel buffer, channel 0 is routed Front-Left, channel 1 is routed Front-Right  
 *   - Send Levels are set to 0 (infinite attenuation)
 *   - Channel Volume is set to 0xFFFFFFFF (no attenuation)
 *   - No notification.
 *   - StartLoopOffset is set to 0.
 *   - EndLoopOffset and EndOffset are set to pConfig->mapdata.dwSize.
 *   - No loop.
 *   - Buffer is in the stop state. 
 *   - Play position is set to 0.
 *
 * CALL LEVELS: PASSIVE.
 * 
 * @param   pConfig
 * A pointers to configuration structures containing
 * information about how the clients should be opened.
 *
 * @param   pCallback
 * A pointer to the callback function.
 *
 * @param   dwFlags 
 * HABUF_SYNTH_BUFFER bit when set indicates synthesizer buffer. 
 * HABUF_ALLOC_USER_MEM bit when set indicates caller allocate sound data memory buffer.  
 * HABUF_USE_MAPPED_MEM bit when set indicates to use previous caller allocated memory.
 * 
 * @param   phHandle
 * A pointer to a memory address where the token of new client identifier
 * should be placed.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 * 
 * @retval  SEGAERR_BAD_POINTER if either the pConfig or phHandle pointers are
 * NULL.
 * @retval  SEGAERR_OUT_OF_MEMORY if buffer size requested in pConfig cannot be
 * allocated. 
 * @retval  SEGAERR_BAD_CONFIG if the device can't support the configuration
 * requested.
 * @retval  SEGAERR_NO_RESOURCES if no resources are available for creating
 * the device.  Generally, increasing the client's priority
 * will allow a subsequent creation request to succeed.
 */
SEGASTATUS SEGAAPI_CreateBuffer(HAWOSEBUFFERCONFIG * pConfig,
  HAWOSEGABUFFERCALLBACK pCallback,
  CTDWORD dwFlags,
  CTHANDLE *phHandle);


/**
 * Destroys the buffer previously created with CreateBuffer().
 * This will free all the resources previously allocated to this buffer.
 *
 * This function will stop the buffer if it is not at the stop state before
 * freeing all the resources.
 *
 * Note that HABUF_ALLOC_USER_MEM buffer must be destroyed last
 * if there are mapped memory buffer voices also use it.
 *
 * CALL LEVELS: PASSIVE.
 *
 * @param hHandle
 * An opaque identifier obtained from CreateBuffer. 
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_DestroyBuffer(CTHANDLE hHandle);





/***********************************************************
 * @section 
 * API to control effect slots and effects parameters.
 * 
 * Refers to the EAX4 Programmer's Guide for the details of
 * controlling FX Slots and controlling Effect parameters.
 * 
 * Only the EAX4 FX Slots and Effect Parameters property controls
 * will be implemented for this project.
 *
 * Need to add property to switch the FX returns for 
 * FXSlot2 and FXSlot3 when non-reverb is loaded to these slots
 * as per SEGA request.
 */
 
/**
 * Sets global EAX property.
 *
 * This function sets the EAX4 FX Slots and Effect Parameters property 
 * controls as defined in EAX4 EAX4 Programmer's Guide.
 *
 * @param guid
 * EAX Object GUID
 *
 * @param ulProperty
 * Property enumeration value of each object
 *
 * @param pData
 * A pointer to a memory address where the data will be accessed 
 *
 * @param ulDataSize
 * An unsigned integer indicating the size of the data pointed to by the pData.
 *
 * @return
 * Returns TRUE if successful.  Otherwise, returns FALSE.
 */ 
CTBOOL SEGAAPI_SetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize);

/**
 * Gets global EAX property.
 *
 * This function gets the EAX4 FX Slots and Effect Parameters property 
 * controls as defined in EAX4 EAX4 Programmer's Guide.
 *
 * @param guid
 * EAX Object GUID
 *
 * @param ulProperty
 * Property enumeration value of each object
 *
 * @param pData
 * A pointer to a memory address where the data will be accessed 
 *
 * @param ulDataSize
 * An unsigned integer indicating the size of the data pointed to by the pData.
 *
 * @return
 * Returns TRUE if successful.  Otherwise, returns FALSE.
 */ 
CTBOOL SEGAAPI_GetGlobalEAXProperty(GUID *guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize);


/***********************************************************
 * @section 
 * API to control SPDIF Output channel status, sampling rate
 * and output routing matrix.
 * 
 */

/**
 * Sets SPDIF Out channel status.
 *
 * @param dwChannelStatus
 * Channel Status
 *
 * @param dwExtChannelStatus
 * Extended Channel Status
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetSPDIFOutChannelStatus(
 CTDWORD dwChannelStatus,
 CTDWORD dwExtChannelStatus);

/**
 * Gets SPDIF Out channel status.
 *
 * @param pdwChannelStatus
 * Pointer to address where Channel Status is returned to.
 *
 * @param pdwExtChannelStatus
 * Pointer to address where Extended Channel Status is returned to.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_GetSPDIFOutChannelStatus(
 CTDWORD *pdwChannelStatus,
 CTDWORD *pdwExtChannelStatus);


/**
 * Sets the SPDIF Out sampling rate.  This function also updates the
 * SPDIF-Out channel status to reflect the correct sampling rate.
 * The default SPDIF Out sampling rate is 48KHz.
 *
 * @param dwSamplingRate
 * Sampling rate enumeration type
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetSPDIFOutSampleRate(HASPDIFOUTRATE dwSamplingRate);

/**
 * Gets SPDIF Out sampling rate
 *
 * @return 
 * Returns sampling rate enum.
 * Note that returned value is set to HASPDIFOUT_48KHZ if error invoked this function. 
 */
HASPDIFOUTRATE SEGAAPI_GetSPDIFOutSampleRate(void);


/**
 * Sets SPDIF Out channel routing.
 *
 * @param   dwChannel
 *  The channel to route.  
 *  0 for Left channel, 1 for right channel.
 *
 * @param   dwSource
 *  The source to which the channel should be received signal from.
 *  If the channel doesn't need to be connected to anything,
 *  specify HA_UNUSED_PORT. 
 *  HA_FXSLOTx_PORT is not a valid source.  Routes from these ports 
 *  will return failure.
 *
 *  See HAROUTING for the details of the source enumeration list.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetSPDIFOutChannelRouting(
  CTDWORD dwChannel,
  HAROUTING dwSource);

 /**
 * Gets SPDIF Out channel routing
 *
 * @param   dwChannel
 *  The channel to route.  
 *  0 for Left channel, 1 for right channel.
 *
 * @return  
 *  The source to which the channel is received signal from.
  * Note that returned value is set to HA_UNUSED_PORT if error invoked this function.  
 *
 *  See HAROUTING for the details of the source enumeration list.
 */
HAROUTING SEGAAPI_GetSPDIFOutChannelRouting(CTDWORD dwChannel);



/***********************************************************
 * @section 
 * API to control global inputs and outputs volume.
 * For outputs, these volume controls are post-routing.
 */

/**
 * Sets the volume to a specific linear value.  Volumes are specified
 * as fractional fixed-point linear values between HAWOSEVOL_MAX (0xFFFFFFFF)
 * and 0x0.  Note that only the upper word is effective.
 *
 * @param dwPhysIO
 * The Physical IO being addressed.
 *
 * @param dwVolume
 * The new volume level.
 *
 * @return
 * Returns SEGA_SUCCESS if successful.  Otherwise, returns an appropriate 
 * error code.
 */
SEGASTATUS SEGAAPI_SetIOVolume(HAPHYSICALIO dwPhysIO, CTDWORD dwVolume);

/**
 * Returns the current volume level for the requested physical IO.
 *
 * @param dwPhysIO
 * The Physical IO being addressed.
 *
 * @return  
 * The current volume.
 * Note that returned value is set to 0xffffffff if error invoked this function.  
 */
CTDWORD SEGAAPI_GetIOVolume(HAPHYSICALIO dwPhysIO);

/**
 * Sets the last status code manually.
 *
 * Typically, this is use to reset the last status code.
 * Note that the last status code will be reset whenever a function is invoked.
 *
 * @param LastStatus
 * The last status code to change to.
 *
 * @return  
 * None.  
 */
void SEGAAPI_SetLastStatus(SEGASTATUS LastStatus);


/**
 * Returns the last status code for the function that just invoked.
 * The last status code will be reset whenever a function is invoked.
 * Therefore, The last status code should be checked immediately after a function
 * is invoked.  
 *
 * For functions that return SEGASTATUS, caller can check the return code 
 * immediately without needing to call GetLastStatus function.   
 *
 * @return  
 * The SEGASTATUS code.  
 */
SEGASTATUS SEGAAPI_GetLastStatus(void);


/**
 * Resets the driver to its default states.
 * 
 * This includes but not limited to the followings:
 *  - Stop and destroy all the currently playing buffers. All previous buffer 
 *    handles are no longer valid after returning from this call.
 *  - Resets all volume levels to its default.
 *  - Resets EAX property values to their defaults.
 *  - Resets SPDIF Out sampling rate and routing to its default. 
 *   
 *
 * @return  
 * The SEGASTATUS code.  
 */
SEGASTATUS SEGAAPI_Reset(void);


/**
 * Initializes the SEGAAPI Library.
 * 
 * This must be the first function to call before using any of the SEGAAPI functions.
 *   
 *
 * @return  
 * The SEGASTATUS code.  
 */
SEGASTATUS SEGAAPI_Init(void);


/**
 * Exits from the SEGAAPI Library.
 * 
 * This function performs cleanup on the SEGAAPI Library.  
 * It must be the last function to call.  
 *   
 *
 * @return  
 * The SEGASTATUS code.  
 */
SEGASTATUS SEGAAPI_Exit(void);


#ifdef __cplusplus
}
#endif

#endif  /* __SEGAAPI_H */