/*-------------------------------------------------------------------------------------------
  FILE     SndDMMid.c

  ROLE     standard MIDI resources played via DirectMusic


  LAST MODIFICATIONS:
      - J.Vibert - 21/01/98 - fading
      - J.Vibert - 21/01/98 - volumes modified by tool; Main volume, incremental volume for MDX
      - D.Dumont - 19/01/98 - theme management, MDX files
	  - J.Vibert - Nov/Dec 98 - created

  NB:
      No fading/transitions yet

---------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------
									INCLUDES
---------------------------------------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
#include "DMusici.h"  //MS DirectMusic include files
#include "DMusicc.h"
#include "SndDMMid.h"
#include "SNDinc.h"
#include "sndres.h"
#include "sndxd.h"
#include "sndmidi.h"

#if defined(_DLL_COMPILATION_MODE) 
#include  "SNDDLL.h"
#endif
#include "sndthrd.h"
#include "CMDXTool.h"

#ifndef DISABLE_MIDI
/*-------------------------------------------------------------------------------------------
									DEFINES
---------------------------------------------------------------------------------------------*/
#define MAX_DM_MUSIC_TYPES 4 //DM_STYLE,DM_PERSO,DM_SEGMT,DM_BAND
#define MAX_INSTR				256
#define NO_MIDI_VOICE			-1
#define MIDI_UNIQUE_VOICE	    0x13
#define MIDI_MDX_VOICE			0x14
#define MULTI_TO_WIDE( x,y )	MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, y, -1, x, _MAX_PATH );

#ifdef CPA_DLL 
#define CPA_WANTS_EXPORT 
#endif

#define C_TYPE_MID 0
#define C_TYPE_MDX 1

//identification of DMusic objects
#define DM_EMPTY 0
#define DM_STYLE 1 /* -1 => index */
#define DM_PERSO 2
#define DM_SEGMT 3
#define DM_BAND  4
#define DM_DLS   5

#define DM_INVALIDREF 0xFFFFFFFF



/*------------------------------------------------------------------------------
									TYPEDEFS
--------------------------------------------------------------------------------*/
typedef struct _tdstGlobalMidi
	{
	SndBool						bActive;
	SndBool						bPaused;
	long						lVoice; //active voice
	SoundParam *				pstSoundParam;
	SND_td_pfn_vSoundCallback	pfnSoundCallback;
	long						lParSoundCallback;
	IDirectMusicSegment *		pSegment;   //saved in resource (uiMidiDeviceId)
	IDirectMusicSegmentState *	pState;   //saved in resource (uiMidiDeviceId)	
	unsigned long				ulFadeOutLenInMs;
	}tdstGlobalMidi;

//voice
typedef struct
	{
	long  lId;
	short wUserTempo;
	short wMuteFlag;
	short wVolFlag;
	char  vol[16];
	} tsdtMDXVoice;

union _tdstUnionVoice
	{
	tduRefRes					stRefRes; // le segment est dans le deviceid !!!
	tsdtMDXVoice				stMDXVoice;
	};

typedef union _tdstUnionVoice tdstUnionVoice;
typedef struct 
	{
	BOOL						bIsFree;
	char						cType; // C_TYPE_MID or C_TYPE_MDX	
	tdstUnionVoice				uVoice;
	} tdstRefVoice;

/*-------------------------------------------------------------------------------------------
									VARIABLES
---------------------------------------------------------------------------------------------*/




		UINT								SND_PLAYMIDI_MSG;
		UINT								SND_LOADMIDI_MSG;

static	tdstRefVoice						*pstVoices; // Dynamic array of playable midi resources
static	int									iNbVoices;  // Nb playable resources

static	DWORD								dwMidiThreadId;
static	HANDLE								hMidiThread=NULL;	

static	DWORD								dwDMPlayerThreadId;
static	HANDLE								hDMPlayerThread=NULL;	


		BOOL								bThreadMustLive = FALSE;
		GUID								GUIDForEvent;
		HANDLE								hNotifyEvent;
		HANDLE								hDMPlayerIsReady;
		HANDLE								hMDXEvent;

		HINSTANCE							SND_hGlobalInst;
		IDirectMusicLoader*					g_pLoader;
		IDirectMusicPerformance*			g_pPerformance;
		IDirectMusicComposer*				g_pComposer;
		IDirectMusicGraph*					g_pGraph;
static	IMDXTool *							pMDXTool = NULL;


		BOOL								bIsMidiDriverOk=FALSE;
		HWND								hwndMainSnd=0; //for cd
		HINSTANCE							SND_g_hResourceHandle=NULL;
static	IDirectMusicDownloadedInstrument *	gs_aInstruments[MAX_INSTR]={0};
static	tdstGlobalMidi						stActiveMidiTrack;
static	char								StrMidiDirectory[256];
static	tduRefRes							s_CurrentMidiThemeRes={0};
static	long								s_lCurrentMidiThemeVoice=-1;
static	tduRefRes							s_NextMidiThemeRes={0};
static	SND_td_pfn_vSoundCallback			s_pfnThemeTransitionCB=NULL;
static	long								s_lThemeTransitionCBParam=0;
static	MCIDEVICEID							NO_MIDI_DEVICE  = 0; //127;
		SND_td_pfn_vSoundCallback			pfnTempCallback;
		DWORD								dw_a5_Params[7];
static	long								lMDXCounter = 1;

/*-------------------------------------------------------------------------------------------
									PROTOTYPES
---------------------------------------------------------------------------------------------*/

SndBool SND_fn_bLoadMidiTrack				(tdstBlockResourceMem * pstPtr,char *czFileName);
SndBool SND_fn_bUnloadMidiTrack				(long voice);
long	SND_fn_lGetActiveTrack				(void);
long	SND_fn_lGetCurrentMode				(void);
long	SND_fn_l_GetCurrentPosition			(void);
void	SND_CALL fn_MidiThemeTransition		(long par);
long	SND_fn_lPlayMidiThread				(tduRefRes res,SoundParam *par,
											long prio,SND_td_pfn_vSoundCallback fn_callback,
											long par_callback, unsigned long ulFadeIn, unsigned long ulFadeOut);
SndBool SND_fn_bLoadMidiTrackThread(tdstBlockResourceMem * pstPtr,char *czFileName);

#endif //DISABLE_MIDI

/*-------------------------------------------------------------------------------------------
long fnlGenerateMDXId()
---------------------------------------------------------------------------------------------
Generate a pseudo ID for a MDX voice
---------------------------------------------------------------------------------------------*/
long fnlGenerateMDXId()
{
return (++lMDXCounter);
}

/*-------------------------------------------------------------------------------------------
long fn_lAllocMidiVoice()
---------------------------------------------------------------------------------------------
realloc the dynamic array pstVoices
---------------------------------------------------------------------------------------------*/
long fn_lAllocMidiVoice()
{
	int index;

if (iNbVoices == 0)
	{
	// This is the first resource we want to alloc so we make a real alloc
	pstVoices = (tdstRefVoice *)SND_fn_pvMallocSnd((iNbVoices + 1)*sizeof(tdstRefVoice));	
	pstVoices[iNbVoices].bIsFree = FALSE;
	iNbVoices++;
	return iNbVoices-1;
	}
else
	{
	// we now have to find a free voice
	index = 0;
	while ((pstVoices[index].bIsFree == FALSE)&&(index<iNbVoices)) 
		index++;
	if (index == iNbVoices)
		{
		// There's no free voice so we just have to make a realloc
		pstVoices = (tdstRefVoice *)SND_fn_pvReAllocSnd(pstVoices,(iNbVoices + 1)*sizeof(tdstRefVoice));	
		pstVoices[iNbVoices].bIsFree = FALSE;
		iNbVoices++;
		return iNbVoices-1;
		}
	else
		{
		pstVoices[index].bIsFree = FALSE;
		return index;
		}

	}

}



/*------------------------------------------------------------------------------------------
HRESULT fn_hrLoadExtDMObject(unsigned short uwType, char *szName, void **pObj)
--------------------------------------------------------------------------------------------

 load any DirectMusic object from a file 
--------------------------------------------------------------------------------------------*/
HRESULT fn_hrLoadExtDMObject(unsigned short uwType, char *szName, void **pObj)
{
	DMUS_OBJECTDESC     ObjDesc;
	
	WCHAR				wszDir[_MAX_PATH];
	int					pos,l;


if ((szName==NULL) ||(szName[0]==0))
	return E_FAIL;

if ((uwType<=0)||((uwType>MAX_DM_MUSIC_TYPES)&&(uwType!=DM_DLS))||(g_pLoader==NULL)) 
	return E_FAIL;


//extract directory
l=pos=strlen(szName);
if (pos>=MAX_PATH)
	pos=MAX_PATH-1;

while ((pos>0)&&(szName[pos]!='\\'))	
	pos--;
	
mbstowcs(wszDir,szName,  pos);
wszDir[pos]=0;
    
switch(uwType)
	{
	case DM_SEGMT:
		g_pLoader->lpVtbl->SetSearchDirectory(g_pLoader,&CLSID_DirectMusicSegment,wszDir,FALSE);
		break;
	case DM_DLS:
		g_pLoader->lpVtbl->SetSearchDirectory(g_pLoader,&CLSID_DirectMusicCollection,wszDir,FALSE);
		break;
	default:
		return E_FAIL;
	}
/* wide string for DirectMusic */
mbstowcs(ObjDesc.wszFileName, &szName[pos+1],  MAX_PATH);
ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);    
ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;

switch(uwType)
	{
	case DM_SEGMT:
	ObjDesc.guidClass = CLSID_DirectMusicSegment;
	return g_pLoader->lpVtbl->GetObject(g_pLoader,&ObjDesc,&IID_IDirectMusicSegment, pObj);
	case DM_DLS:
	ObjDesc.guidClass = CLSID_DirectMusicCollection;
	return g_pLoader->lpVtbl->GetObject(g_pLoader,&ObjDesc,&IID_IDirectMusicCollection, pObj);
	default:
		return E_FAIL;
	}
}

/*------------------------------------------------------------------------------------------
// Function: fn_vCallCallback
--------------------------------------------------------------------------------------------
 Description: 
--------------------------------------------------------------------------------------------*/
void fn_vCallCallback()
{
if (stActiveMidiTrack.pfnSoundCallback)		
	{
	
	pfnTempCallback = stActiveMidiTrack.pfnSoundCallback;
	stActiveMidiTrack.pfnSoundCallback = NULL;
	(*pfnTempCallback)(stActiveMidiTrack.lParSoundCallback);							
	}

}
/*------------------------------------------------------------------------------------------
 Function: fn_vFreeDirectMusic
--------------------------------------------------------------------------------------------
 Description: 
      free voices+all global DirectMusic objects 
      (software synthesizer, performance, composer, loader).
Fiability Level 80%
--------------------------------------------------------------------------------------------*/
void fn_vFreeDirectMusic()
{
	IDirectMusicPerformance *pTmpPerf;



//Release the composer (transitions)

if (g_pComposer)
	{
	g_pComposer->lpVtbl->Release(g_pComposer);		
	}

// If there is any music playing, stop it.
if (g_pPerformance)
	{
	g_pPerformance->lpVtbl->Stop(g_pPerformance, NULL, NULL, 0, 0);

	// CloseDown and Release the performance object
	g_pPerformance->lpVtbl->CloseDown(g_pPerformance);
	
	pTmpPerf=g_pPerformance; //to avoid trouble with the destructor when shutting down
	g_pPerformance=NULL;
	pTmpPerf->lpVtbl->Release(pTmpPerf);
	}

// Release the loader object
if (g_pLoader)
	{
	g_pLoader->lpVtbl->Release(g_pLoader);
	}

fnvReleaseTool();
// Release COM
CoUninitialize();
}

/*-------------------------------------------------------------------------------------------
 Function: InitializeSynth
---------------------------------------------------------------------------------------------
 Description: 
      Initialize the software synthesizer into the performance.
      This function also calls IDirectMusicPerformance::Init to
      initialize the performance and create the DirectMusic object.

---------------------------------------------------------------------------------------------*/
HRESULT fn_hrInitializeSynth(HWND hWnd)
{
    HRESULT           hr;
    IDirectMusic*     pDM;
    IDirectMusicPort* pPort;
    DMUS_PORTPARAMS   dmos;
    GUID              guidSink;
	DMUS_PORTCAPS		dmpc;


// Initialize the performance. Have the performance create the
// DirectMusic object by setting pDM to NULL. It is needed to
// create the port.
pPort = NULL;
pDM   = NULL;

hr=g_pPerformance->lpVtbl->Init(g_pPerformance,&pDM,NULL,hWnd);
if ( FAILED(hr) )
    return hr;

hr =pDM->lpVtbl->GetDefaultPort(pDM,&guidSink);
if ( FAILED(hr) )
    return hr;

// Create the software synth port.
// An alternate, easier method is to call
// pPerf->AddPort(NULL), which automatically
// creates the synth port, adds it to the
// performance, and assigns PChannels.
ZeroMemory( &dmos, sizeof(DMUS_PORTPARAMS) );
dmos.dwSize          = sizeof(DMUS_PORTPARAMS);  
dmos.dwChannelGroups = 1; // create 1 channel groups on the port
dmos.dwValidParams   = DMUS_PORTPARAMS_CHANNELGROUPS; 




hr=pDM->lpVtbl->CreatePort(pDM,&guidSink, &dmos, &pPort, NULL );
if ( FAILED(hr))
	{
    return hr;
	}
else
	{
	ZeroMemory( &dmpc, sizeof(dmpc) );
	dmpc.dwSize = sizeof(DMUS_PORTCAPS);

	if( SUCCEEDED( pPort->lpVtbl->GetCaps(pPort,&dmpc ) ) )
		{
		if( ( dmpc.dwClass != DMUS_PC_OUTPUTCLASS ) ||
		   !( dmpc.dwFlags & DMUS_PC_DLS ) ||
		   !( dmpc.dwFlags & DMUS_PC_DIRECTSOUND ) )
			{
			pPort->lpVtbl->Release(pPort);
			pPort = NULL;
			}
		}
	else
		{
		pPort->lpVtbl->Release(pPort);
		pPort = NULL;
		}
	}

pPort->lpVtbl->Activate(pPort,TRUE);

// Succeeded in creating the port. Add the port to the
// Performance with five groups of 16 midi channels.
//hr = g_pPerformance->AddPort( pPort );
hr=g_pPerformance->lpVtbl->AddPort(g_pPerformance, pPort);
if (FAILED(hr))
    return hr;

// Assign a block of 16 PChannels to this port.
// Block 0, port pPort, and group 1 means to assign
// PChannels 0-15 to group 1 on port pPort.
// PChannels 0-15 correspond to the standard 16
// MIDI channels.
g_pPerformance->lpVtbl->AssignPChannelBlock(g_pPerformance, 0, pPort, 1);

// Release the port since the performance now has its own reference.
pPort->lpVtbl->Release(pPort);

// release the DirectMusic object. The performance has its
// own reference and we just needed it to call CreatePort.

pDM->lpVtbl->Release(pDM);

return S_OK;
}



/*-------------------------------------------------------------------------------------------
 Function: fn_iInit_DMusic
---------------------------------------------------------------------------------------------
 Description: 
      Initialize voices+all global DirectMusic objects 
      (software synthesizer, performance, composer, loader).
Fiability Level 80%
---------------------------------------------------------------------------------------------*/
int fn_iInit_DMusic(HWND hwnd)
{
    HRESULT		hr;
	int			iInitStatus;
	char		fileLocation[310];


iInitStatus=C_INIT_RUNNING;
bIsMidiDriverOk=FALSE;

g_pLoader      = NULL;
g_pPerformance = NULL;
g_pComposer    = NULL;

// Initialize COM


hr = CoInitialize(NULL);

if ( FAILED(hr) )	
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}
	
// Create loader object

hr = CoCreateInstance(&CLSID_DirectMusicLoader,NULL,CLSCTX_INPROC,&IID_IDirectMusicLoader,(void**)&g_pLoader);
if (FAILED(hr))	
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}
// Create performance object


hr = CoCreateInstance(&CLSID_DirectMusicPerformance,NULL,CLSCTX_INPROC,&IID_IDirectMusicPerformance,(void**)&g_pPerformance);
if (FAILED(hr))	
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}
//create composer

hr=CoCreateInstance(&CLSID_DirectMusicComposer,NULL,CLSCTX_INPROC,&IID_IDirectMusicComposer,(void**)&g_pComposer);
if(FAILED(hr))
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}

//////////////

pMDXTool = fnpGetTool();
if(pMDXTool == NULL)
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		

	}

/////////////
hr=CoCreateInstance(&CLSID_DirectMusicGraph,NULL,CLSCTX_INPROC,&IID_IDirectMusicGraph,(void**)&g_pGraph);
if(FAILED(hr))
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}

hr = g_pGraph->lpVtbl->InsertTool(g_pGraph,(IDirectMusicTool*)pMDXTool, 
                NULL,   // Apply to all PChannels
                0,      // How many PChannels otherwise
                0);    // Index of tool in graph

if ((g_pPerformance)&&(g_pGraph))
	{
	g_pPerformance->lpVtbl->SetGraph(g_pPerformance,g_pGraph);
	}
if (g_pGraph)
	{
	g_pGraph->lpVtbl->Release(g_pGraph);
	}

// Initialize the software synthesizer

hr = fn_hrInitializeSynth(hwnd);
if(FAILED(hr))
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}


hNotifyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if (hNotifyEvent == NULL)
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}

hMDXEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if (hMDXEvent == NULL)
	{
	fn_vFreeDirectMusic();
	iInitStatus=C_INIT_FAILED;
	return iInitStatus;		
	}


g_pPerformance->lpVtbl->SetNotificationHandle(g_pPerformance,hNotifyEvent, 0 );
GUIDForEvent = GUID_NOTIFICATION_SEGMENT;
g_pPerformance->lpVtbl->AddNotificationType(g_pPerformance,&GUIDForEvent);

strncpy(fileLocation,SND_fn_szGetSoftDirectory(),300);
strcat(fileLocation,"\\sndDM.dls");
fn_hrDownloadDLS(fileLocation,2);

bIsMidiDriverOk = TRUE;
iInitStatus     = C_INIT_OK;
return iInitStatus;
}

/*-------------------------------------------------------------------------------------------
	ConvertRsvDiskToMem
---------------------------------------------------------------------------------------------
	But			: Convertir un BlockResourceDisk en Mem non opérationel
	Entrées : reference au structs ressources mem et disk
	Sorties : neant
---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vConvertResDiskToMemMidi(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,void * ptrBegin)
{
#ifndef DISABLE_MIDI
mem->Id=disk->Id;
mem->eType=disk->eType;
mem->eStorage=disk->eStorage;
mem->ucVolume=disk->ucVolume;
mem->bIsLoaded=TRUE;
    
//partie spécifique au type de ressource
mem->uRes.stMidi.bStream = disk->uRes.stMidi.bStream;
mem->uRes.stMidi.bVolable = disk->uRes.stMidi.bVolable;
mem->uRes.stMidi.bLoop = disk->uRes.stMidi.bLoop;
mem->uRes.stMidi.ulNbLoops = disk->uRes.stMidi.ulNbLoops;
if (mem->uRes.stMidi.bLoop)
	{
    mem->uRes.stMidi.ulStartLoop = disk->uRes.stMidi.ulStartLoop;
    mem->uRes.stMidi.ulEndLoop = disk->uRes.stMidi.ulEndLoop;
	}
else
	{
	mem->uRes.stMidi.ulStartLoop = mem->uRes.stMidi.ulEndLoop = 0;
	}
if (mem->uRes.stMidi.bStream)
	{
	strcpy(mem->uRes.stMidi.uData.stStream.fichier,"TOTO.MID");
	mem->uRes.stMidi.uData.stStream.Offset = 0;
	}
mem->uRes.stMidi.uData.stMem.uiMidiDeviceId=NO_MIDI_DEVICE;
// seul LoadResFromdisk doit rendre la ressource opérationelle
#endif //DISABLE_MIDI
}

/*-------------------------------------------------------------------------------------------
SndBool SND_CALL SND_fn_bLoadResScriptMidi(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bLoadResScriptMidi(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem)
{
#ifndef DISABLE_MIDI
SND_fn_vConvertResDiskToMemMidi(disk,mem,NULL);
mem->bIsLoaded = TRUE;
if (SND_fn_bLoadMidiTrack(mem,disk->uRes.stMidi.czFileName) == FALSE)
	{
    mem->bIsLoaded = FALSE;
    SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"From Script");
	return FALSE;
	}
else 
	return TRUE;
#else
	return FALSE;
#endif //DISABLE_MIDI
}

/*-------------------------------------------------------------------------------------------
SndBool SND_CALL SND_fn_bLoadResBinaryMidi(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,char *pDataBloc)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bLoadResBinaryMidi(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,char *pDataBloc)
{
#ifndef DISABLE_MIDI
SND_fn_vConvertResDiskToMemMidi(disk,mem,NULL);

mem->bIsLoaded = TRUE;
if (SND_fn_bLoadMidiTrack(mem,disk->uRes.stMidi.czFileName) == FALSE)
	{
    mem->bIsLoaded = FALSE;
	SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"From Binary");
	return FALSE;
	}
return TRUE;
#endif //DISABLE_MIDI
	return FALSE;
}

/*-------------------------------------------------------------------------------------------
DWORD WINAPI SND_fn_iMidiThreadProc(HANDLE hEvent)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
DWORD WINAPI SND_fn_iMidiThreadProc(HANDLE hEvent)
{
	    DMUS_NOTIFICATION_PMSG* pEvent;
		HANDLE aHandles[2];
		DWORD ret;
while(bThreadMustLive)
	{
	aHandles[0] = hEvent;
	aHandles[1] = hMDXEvent;
	//WaitForSingleObject(hEvent,INFINITE);
	ret = WaitForMultipleObjects(2,aHandles,FALSE,INFINITE); 
	if (ret == WAIT_OBJECT_0)
		{
		if (hEvent && g_pPerformance)
			{
			if ( S_OK == g_pPerformance->lpVtbl->GetNotificationPMsg(g_pPerformance,&pEvent))
				{
				if (pEvent->dwType ==DMUS_PMSGT_NOTIFICATION)
					{
					switch(pEvent->dwNotificationOption)
						{

						case DMUS_NOTIFICATION_SEGSTART:
							
							break;

						case DMUS_NOTIFICATION_SEGEND:

							SND_fn_vStopMidi(MIDI_UNIQUE_VOICE);
							break;

						case DMUS_NOTIFICATION_SEGALMOSTEND:
							
							break;


						case DMUS_NOTIFICATION_SEGLOOP:
							
							break;
						}

					}
				g_pPerformance->lpVtbl->FreePMsg(g_pPerformance,(DMUS_PMSG*)pEvent);   
				}
			}
		}
	else
		{
		OutputDebugString(">>> EVENT MDX\n"); 
		SND_fn_vStopMidi(MIDI_MDX_VOICE);
		}
	}
ExitThread(0);
return 0;
}

/*-------------------------------------------------------------------------------------------
DWORD WINAPI fndwDMPlayer(HANDLE hEvent)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
DWORD WINAPI fndwDMPlayer(HANDLE hEvent)
{

	MSG msg;
	DWORD *	pdwParams;
	

fn_iInit_DMusic(hwndMainSnd);

PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
SetEvent(hDMPlayerIsReady);

while (GetMessage(&msg,NULL,0,0))
	{
	if (msg.message == SND_PLAYMIDI_MSG) 
		{
		pdwParams = (DWORD *)msg.wParam;
		pdwParams[0] = SND_fn_lPlayMidiThread(*(tduRefRes *)pdwParams[0],(SoundParam *)pdwParams[1],(long)pdwParams[2] ,(SND_td_pfn_vSoundCallback)pdwParams[3] ,(long)pdwParams[4],
			pdwParams[5], pdwParams[6]);
		// on debloque le thread appelant
		SetEvent(hDMPlayerIsReady);
		}
	else
	if (msg.message == SND_LOADMIDI_MSG) 
		{
		pdwParams = (DWORD *)msg.wParam;
		pdwParams[0] = SND_fn_bLoadMidiTrackThread((tdstBlockResourceMem *)pdwParams[0],(char *)pdwParams[1]);
		// on debloque le thread appelant
		SetEvent(hDMPlayerIsReady);
		}
	else
		{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		}
		//WaitMessage();
	}

ExitThread(0);
return 0;
}
/*-------------------------------------------------------------------------------------------
int SND_CALL SND_fn_iInitMidi(SND_tdstInitStruct *pInitStruct)
---------------------------------------------------------------------------------------------
	Role	: Low level initialization for MIDI
	Input	: init structure
	Output	: C_INIT_OK or C_INIT_FAILED
---------------------------------------------------------------------------------------------*/
int SND_CALL SND_fn_iInitMidi(SND_tdstInitStruct *pInitStruct)
{
#ifndef DISABLE_MIDI
	int						i;
	DWORD					dwExitCode = 0;
	SECURITY_ATTRIBUTES		sa;

if (pInitStruct)
	hwndMainSnd=pInitStruct->hMainWindow; //for cd

#if defined(_DLL_COMPILATION_MODE)
    SND_DllInitEntryPoints_StaticFunctions((HMODULE)pInitStruct->hProcessInstance );
#endif


bIsMidiDriverOk = FALSE;
SND_hGlobalInst=pInitStruct->hProcessInstance;

#ifdef _DLL_COMPILATION_MODE
	SND_DllInitEntryPoints_StaticFunctions((HMODULE)SND_hGlobalInst);
#endif

iNbVoices = 0;


stActiveMidiTrack.bActive = FALSE;
stActiveMidiTrack.bPaused = FALSE;
stActiveMidiTrack.lVoice = NO_MIDI_VOICE;
stActiveMidiTrack.pstSoundParam = NULL;
stActiveMidiTrack.pSegment=NULL;
	



for (i=0; i<MAX_INSTR; i++) 
	{
	gs_aInstruments[i]=NULL;
	}

	

hMidiThread = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;

bThreadMustLive = TRUE;

SND_PLAYMIDI_MSG = RegisterWindowMessage("PlayMIDI");
SND_LOADMIDI_MSG = RegisterWindowMessage("LoadMIDI");

hDMPlayerIsReady= CreateEvent( NULL, FALSE, FALSE, NULL );
if (hDMPlayerIsReady==NULL)
	{
	SND_fn_vDisplayError(E_uwSndMIDICreateThread,"");
	return C_INIT_FAILED;
	}
if (!(hDMPlayerThread = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)fndwDMPlayer,hNotifyEvent,0,&dwDMPlayerThreadId)))
	{
	SND_fn_vDisplayError(E_uwSndMIDICreateThread,"");
	return C_INIT_FAILED;
	}
WaitForSingleObject(hDMPlayerIsReady,INFINITE);

if (!(hMidiThread = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iMidiThreadProc,hNotifyEvent,0,&dwMidiThreadId)))
	{
	SND_fn_vDisplayError(E_uwSndMIDICreateThread,"");
	return C_INIT_FAILED;
	}


 
 
bIsMidiDriverOk = TRUE;

return C_INIT_OK;


#else
    return C_INIT_FAILED;
#endif //DISABLE_MIDI
}

/*-------------------------------------------------------------------------------------------
	SND_fn_iTestInitMidi
---------------------------------------------------------------------------------------------
Role	: Check if MIDI is initialized
Input	: none
Output  : True or False according to the state
Fiability Level : 100%
---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bTestInitMidi(void)
{
#ifndef DISABLE_MIDI
	return (bIsMidiDriverOk);
#else
    return FALSE;
#endif //DISABLE_MIDI
}


/*-------------------------------------------------------------------------------------------
	SND_fn_iDesInitMidi
---------------------------------------------------------------------------------------------
	Role	: Low level uninitialization for MIDI
	Input	: none
	Output  : none
---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vDesInitMidi(void)
{
#ifndef DISABLE_MIDI
	int i;
for (i=0; i<iNbVoices; i++)
	SND_fn_bUnloadMidiTrack(i);


stActiveMidiTrack.bActive = FALSE;
stActiveMidiTrack.bPaused = FALSE;
stActiveMidiTrack.lVoice = NO_MIDI_VOICE;
stActiveMidiTrack.pstSoundParam = NULL;


for (i=0; i<MAX_INSTR; i++)
	{
	if (gs_aInstruments[i])
		{
		gs_aInstruments[i]->lpVtbl->Release(gs_aInstruments[i]);
		gs_aInstruments[i]=NULL;
		}
	}

iNbVoices = 0;
SND_fn_vFreeSnd(pstVoices);


  
bThreadMustLive = FALSE; // To Kill the thread for callback

if(PostThreadMessage(dwDMPlayerThreadId,WM_QUIT,0,0)==0) // To Kill the player thread
	{
	OutputDebugString("DMPLAYER ERROR"); 
	} 



fn_vFreeDirectMusic();
CloseHandle(hDMPlayerIsReady);
CloseHandle(hMDXEvent);

bIsMidiDriverOk = FALSE;
#endif //DISABLE_MIDI
}


//-----------------------------------------------------------
//	SND_fn_vRemoveCallbackMidi
//
//	Role	: Reset to zero the resource callback of a given voice
//	Input   : voice index
//	Output  : none
//------------------------------------------------------------
void SND_CALL SND_fn_vRemoveCallbackMidi(long lVoice)
{
#ifndef DISABLE_MIDI
if (lVoice == 0)	   
	{
	stActiveMidiTrack.pfnSoundCallback= NULL;
	stActiveMidiTrack.lParSoundCallback= 0;
	}
	
#endif //DISABLE_MIDI
}

//-----------------------------------------------------------
//	SND_fn_bSetParamMidi
//
//	But			: Faire varier en temps reel les parametres
//						de la ressource midi
//	Entrées : voie jouee, parametres sonores
//	Sorties : TRUE si maj possible FALSE sinon
//------------------------------------------------------------
SndBool SND_CALL SND_fn_bSetParamMidi(long voice,SoundParam *par)
{
#ifndef DISABLE_MIDI
	if (stActiveMidiTrack.bActive)
	{
		/*if (par)
		{
			pMDXTool->lpVtbl->SetMainVolValue(pMDXTool,par->ucVol); //test
		}
		*/
		return TRUE;		
	}
	//return (stActiveMidiTrack.bActive);
#endif //DISABLE_MIDI
	return FALSE;
}

//-----------------------------------------------------------
//	SND_fn_bTestIsPlayingMidi
//
//	Role	: Inidicates if a MIDI resource is active
//	Input	: voice
//	Output  : TRUE if active, FALSE otherwise
//------------------------------------------------------------
SndBool SND_CALL SND_fn_bTestIsPlayingMidi(long voice)
{
#ifndef DISABLE_MIDI	
	long lIndexVoice=voice&0xFFF;

if (stActiveMidiTrack.bActive)
	{
	if (voice = MIDI_UNIQUE_VOICE)
		{
		if (stActiveMidiTrack.pSegment)
			{
			if (g_pPerformance->lpVtbl->IsPlaying(g_pPerformance,stActiveMidiTrack.pSegment,stActiveMidiTrack.pState)==S_OK) 
				{
				return TRUE;
				}
			else
				{
				return FALSE;
				}
			}
		}
	}
else
	{
	return FALSE;
	}
#else
    return FALSE;
#endif //DISABLE_MIDI
}

#ifndef DISABLE_MIDI
//-----------------------------------------------------------
//	SND_fn_lGetMidiTrack
//
//	Role	: Returns the low-level voice that corresponds to a resource
//	Input   : id (DirectMusic segment)
//	Output  : index of voice if the resource is loaded, NO_MIDI_VOICE otherwise
//------------------------------------------------------------
long SND_fn_lGetMidiTrack(MCIDEVICEID Id)
{
	int i = 0;
do
	{
	if (pstVoices[i].cType == C_TYPE_MID)
		{

		if  (
			(pstVoices[i].bIsFree == TRUE)||
			(pstVoices[i].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId != Id)
			)
			{
			i++;
			}
		else
			break;
		}
	else
		{
		if  (
			(pstVoices[i].bIsFree == TRUE)||
			(pstVoices[i].uVoice.stMDXVoice.lId != (long)Id)
			)
			{
			i++;
			}
		else
			break;
		}
	}
while (i < iNbVoices);

if (i == iNbVoices)
	return (NO_MIDI_VOICE);
else
	return (i);
}
/*-------------------------------------------------------------------------------------------
	SND_fn_lGetActiveTrack
---------------------------------------------------------------------------------------------
	Role	: Returns the active voice
	Input   : none
	Output  : voice if active, NO_MIDI_VOICE otherwise
---------------------------------------------------------------------------------------------*/
long SND_fn_lGetActiveTrack()
{
if (stActiveMidiTrack.bActive)
	{
	return (stActiveMidiTrack.lVoice);
	}
else
	{
	return (NO_MIDI_VOICE);
	}
}
//-----------------------------------------------------------
//	SND_fn_lGetCurrentMode
//
//	But			: Renvoie la callback midi
//	Entrées : voie bas niveau
//	Sorties : Mode du device correspondant a la voie
//------------------------------------------------------------
long SND_fn_lGetCurrentMode(void)
{
	return 0;
}

//-----------------------------------------------------------
//	SND_fn_lGetCurrentPosition
//
//	Role	: Returns the current reading position for the active voice
//	Input   : none
//	Otput   : position in milliseconds
//------------------------------------------------------------
long SND_fn_lGetCurrentPosition(void)
{
	return (0);
}


/*-------------------------------------------------------------------------------------------
	SND_ulGetMidiTrackLengthInMs
---------------------------------------------------------------------------------------------
	But		: Returns the track duration in milliseconds
	Input   : Active voice
	Output  : Position in tmsf 
---------------------------------------------------------------------------------------------*/
unsigned long SND_fn_ulGetMidiTrackLengthInMs(unsigned long ulVoice)
{
	unsigned long ulRealLength;
if (ulVoice == MIDI_UNIQUE_VOICE)
	{
	ulRealLength = pstVoices[stActiveMidiTrack.lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.ulLength;
	}
else
	{
	ulRealLength = 0;
	}
return (ulRealLength);
}


#endif //DISABLE_MIDI

/*-------------------------------------------------------------------------------------------
	SND_fn_lPlayMidi
---------------------------------------------------------------------------------------------
	Role	: Play a standard MIDI resource with DirectMusic
	Input   : Resource, sound parameters, priority and callback
	Output  : allocated voice
---------------------------------------------------------------------------------------------*/
long SND_CALL SND_fn_lPlayMidi(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
#ifndef DISABLE_MIDI

if (bIsMidiDriverOk)
	{
	dw_a5_Params[0] = (DWORD)&res;
	dw_a5_Params[1] = (DWORD)par;
	dw_a5_Params[2] = (DWORD)prio;
	dw_a5_Params[3] = (DWORD)fn_callback;
	dw_a5_Params[4] = (DWORD)par_callback;

	dw_a5_Params[5] = 0; //no fade in
	dw_a5_Params[6] = 0; //no fade out

	if (!PostThreadMessage(dwDMPlayerThreadId,SND_PLAYMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)0))
   		SND_fn_vDisplayError(E_uwSndMIDIPlay,"PostThreadMessage");
	WaitForSingleObject(hDMPlayerIsReady,INFINITE);

	// dans ce cas, on retourne le numero de voie dans dw_a5_Params[0]
	return (dw_a5_Params[0]);
	}
else
#endif //DISABLE_MIDI
	return (C_PLAY_FAILED);
}

/*-------------------------------------------------------------------------------------------
long SND_fn_lPlayMidiThread(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
---------------------------------------------------------------------------------------------
	Role	: Play a standard MIDI resource with DirectMusic
	Input   : Resource, sound parameters, priority and callback
	Output  : allocated voice
---------------------------------------------------------------------------------------------*/
long SND_fn_lPlayMidiThread(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback,
				unsigned long ulFadeIn, unsigned long ulFadeOut)
{
#ifndef DISABLE_MIDI
	//IDirectMusicSegment *pSegment;
	long    lVoice;
	DWORD   dwFlags;
	HRESULT hr;

lVoice=SND_fn_lGetMidiTrack(res.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId);

if (lVoice<0) 
	return C_PLAY_FAILED;

if (pstVoices[lVoice].cType==C_TYPE_MID)
	{

	/*----------------------------
	- cas d'un vrai fichier midi -
	------------------------------*/
	OutputDebugString("PLAYMIDITHREAD WITH A REAL MIDI RESOURCE\n"); 
	// On bride volontairement à un fichier midi à la fois
	SND_fn_vStopMidi(MIDI_UNIQUE_VOICE);


	stActiveMidiTrack.lVoice = lVoice;			
	stActiveMidiTrack.pSegment = (IDirectMusicSegment *)pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId;
	if (g_pPerformance&&stActiveMidiTrack.pSegment)
		{
		stActiveMidiTrack.bActive = TRUE; 
		stActiveMidiTrack.bPaused = FALSE;
		stActiveMidiTrack.pstSoundParam = par;		
		stActiveMidiTrack.pfnSoundCallback= fn_callback;
		stActiveMidiTrack.ulFadeOutLenInMs=ulFadeOut;

		stActiveMidiTrack.lParSoundCallback= par_callback;
		//dwFlags=DMUS_SEGF_BEAT|DMUS_SEGF_SECONDARY;
		dwFlags=DMUS_SEGF_BEAT;
		if (pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.bLoop)
			{
			stActiveMidiTrack.pSegment->lpVtbl->SetRepeats(stActiveMidiTrack.pSegment,pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.ulNbLoops-1);
			}

		if (ulFadeIn)
			pMDXTool->lpVtbl->DoFade(pMDXTool, g_pPerformance, 0, 127, ulFadeIn, NULL);

		hr=	g_pPerformance->lpVtbl->PlaySegment(g_pPerformance,stActiveMidiTrack.pSegment,dwFlags,0,&stActiveMidiTrack.pState);
			
		if (hr!=S_OK)
			{
			switch(hr)
				{
				case E_FAIL :
				OutputDebugString("!!!!!! PLAYSEGMENT RETURNS E_FAIL\n"); 
				break;
				case E_INVALIDARG :
				break;
				case E_OUTOFMEMORY :
				break;
				case E_POINTER :
				break;

				case DMUS_E_NO_MASTER_CLOCK :
				break;
				}
			stActiveMidiTrack.bActive = FALSE;
			return C_PLAY_FAILED; //(NO_MIDI_VOICE);
			}

		return MIDI_UNIQUE_VOICE; 
		}
	return C_PLAY_FAILED;
	}		
else
	{
		char tempString[200];
		int i;
	OutputDebugString("PLAYMIDITHREAD WITH A MDX RESOURCE\n"); 
	sprintf(tempString,"======> SetMuteFlag %x\n",pstVoices[lVoice].uVoice.stMDXVoice.wMuteFlag);
	OutputDebugString(tempString);
	pMDXTool->lpVtbl->SetUserTempo(pMDXTool,pstVoices[lVoice].uVoice.stMDXVoice.wUserTempo);
	pMDXTool->lpVtbl->SetMuteFlag(pMDXTool,pstVoices[lVoice].uVoice.stMDXVoice.wMuteFlag);
	pMDXTool->lpVtbl->SetVolFlag(pMDXTool,pstVoices[lVoice].uVoice.stMDXVoice.wVolFlag);
	for(i=0;i<16;i++)
		pMDXTool->lpVtbl->SetVolValue(pMDXTool,i,pstVoices[lVoice].uVoice.stMDXVoice.vol[i]);


	stActiveMidiTrack.lVoice = lVoice;			
	stActiveMidiTrack.bActive = TRUE; 
	stActiveMidiTrack.bPaused = FALSE;
	stActiveMidiTrack.pstSoundParam = par;		
	stActiveMidiTrack.pfnSoundCallback= fn_callback;

	return MIDI_MDX_VOICE; 
	}
#endif //DISABLE_MIDI

return (C_PLAY_FAILED);

}

#ifndef DISABLE_MIDI


//-----------------------------------------------------------
//	SND_fn_lPlayMidiWithFade
//
//	Role	: Play the MIDI resource with a fade
//	Inputs  : Ressource, sound parameter, la priority and callback 
//
//	Output  : allocated voice to play the resource
//------------------------------------------------------------
long SND_CALL SND_fn_lPlayMidiWithFade(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback,unsigned long ulFadeIn,unsigned long ulFadeOut)
{
#ifndef DISABLE_MIDI

if (bIsMidiDriverOk)
	{
	dw_a5_Params[0] = (DWORD)&res;
	dw_a5_Params[1] = (DWORD)par;
	dw_a5_Params[2] = (DWORD)prio;
	dw_a5_Params[3] = (DWORD)fn_callback;
	dw_a5_Params[4] = (DWORD)par_callback;

	dw_a5_Params[5] = (DWORD)ulFadeIn;
	dw_a5_Params[6] = (DWORD)ulFadeOut;
	
	if (!PostThreadMessage(dwDMPlayerThreadId,SND_PLAYMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)0))
   		SND_fn_vDisplayError(E_uwSndMIDIPlay,"PostThreadMessage");
	WaitForSingleObject(hDMPlayerIsReady,INFINITE);

	// dans ce cas, on retourne le numero de voie dans dw_a5_Params[0]
	return (dw_a5_Params[0]);
	}
else
#endif //DISABLE_MIDI
	return (C_PLAY_FAILED);
}
#endif //DISABLE_MIDI

//-----------------------------------------------------------
//	SND_fn_vStopMidi
//
//	Role	: Stop a MIDI voice
//	Input   : Voice to stop
//	Output  : none
//------------------------------------------------------------
void SND_CALL SND_fn_vStopMidi(long voice)
{
#ifndef DISABLE_MIDI

if (bIsMidiDriverOk == FALSE) return;

if (voice == MIDI_UNIQUE_VOICE)
	{
		
	if ((stActiveMidiTrack.pSegment != NULL) && (stActiveMidiTrack.pState != NULL))
		{
		
		g_pPerformance->lpVtbl->Stop(g_pPerformance,stActiveMidiTrack.pSegment,stActiveMidiTrack.pState,0,0); 			
		stActiveMidiTrack.pState->lpVtbl->Release(stActiveMidiTrack.pState);				
		stActiveMidiTrack.pState=NULL; 
		stActiveMidiTrack.pSegment	= NULL;
		stActiveMidiTrack.lVoice = NO_MIDI_VOICE;		
		stActiveMidiTrack.bPaused = FALSE;
		stActiveMidiTrack.bActive = FALSE;
		fn_vCallCallback();
		}
	else
		{
		//OutputDebugString("STOP CALLED ON AN ALREADY STOPPED VOICE\n"); 
		}
	
	}
else
	{
	// MDX STOP

		stActiveMidiTrack.pState=NULL; 
		stActiveMidiTrack.pSegment	= NULL;
		stActiveMidiTrack.lVoice = NO_MIDI_VOICE;		
		stActiveMidiTrack.bPaused = FALSE;
		stActiveMidiTrack.bActive = FALSE;
		fn_vCallCallback();	
		}
#endif //DISABLE_MIDI
}


//-----------------------------------------------------------
//	SND_fn_vPauseMidi
//
//	Role	: Pause a voice
//	Input   : Voice to pause
//	Output  : none
//------------------------------------------------------------
void SND_CALL SND_fn_vPauseMidi(long voice)
{
#ifndef DISABLE_MIDI
/*
if (bIsMidiDriverOk == FALSE) return;

if (voice == MIDI_UNIQUE_VOICE)
	{
	if ((stActiveMidiTrack.pSegment != NULL) && (stActiveMidiTrack.pState != NULL))
		{
		g_pPerformance->lpVtbl->Stop(g_pPerformance,stActiveMidiTrack.pSegment,stActiveMidiTrack.pState,0,0);
		}
	}
stActiveMidiTrack.bPaused = TRUE;
*/
#endif //DISABLE_MIDI
}



//-----------------------------------------------------------
//	SND_fn_vResumeMidi
//
//	Role	: Resume a voice
//	Input	: Voice to resume
//	Output  : True or false
//------------------------------------------------------------
void SND_CALL SND_fn_vResumeMidi(long voice)
{
#ifndef DISABLE_MIDI
/*
	MUSIC_TIME time;
	HRESULT hr;
	DWORD dwFlags;

if (bIsMidiDriverOk == FALSE) return;

if (voice == MIDI_UNIQUE_VOICE)
	{
	if ((stActiveMidiTrack.pSegment != NULL) && (stActiveMidiTrack.pState != NULL))
			{							
			hr=stActiveMidiTrack.pState->lpVtbl->GetSeek(stActiveMidiTrack.pState,&time);
			dwFlags=DMUS_SEGF_BEAT|DMUS_SEGF_SECONDARY|DMUS_SEGF_REFTIME;
			hr=g_pPerformance->lpVtbl->PlaySegment(g_pPerformance,stActiveMidiTrack.pSegment,dwFlags,time,	&stActiveMidiTrack.pState);
			stActiveMidiTrack.pState->lpVtbl->Release(stActiveMidiTrack.pState);
			if (hr!=S_OK)
				{
				stActiveMidiTrack.bActive = FALSE;
				}
			
			}
	}

stActiveMidiTrack.bPaused = FALSE;
*/
#endif //DISABLE_MIDI
}


SndBool SND_fn_bLoadMidiTrack(tdstBlockResourceMem * pstPtr,char *czFileName)
{

	if (bIsMidiDriverOk)
	{
		
		dw_a5_Params[0] = (DWORD)pstPtr;
		dw_a5_Params[1] = (DWORD)czFileName;

		if (!PostThreadMessage(dwDMPlayerThreadId,SND_LOADMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)0))
   			SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"PostThreadMessage");
		WaitForSingleObject(hDMPlayerIsReady,INFINITE);
	
		// dans ce cas, on retourne le numero de voie dans dw_a5_Params[0]
		return (dw_a5_Params[0]);
	}
	else
		return FALSE;
}

/*-------------------------------------------------------------------------------------------
SndBool SND_fn_bLoadMidiTrackThread(tdstBlockResourceMem * pstPtr,char *czFileName)
---------------------------------------------------------------------------------------------
Role	: Load a midi track, from the resource block
Inputs  : Resource block and MIDI file name
Output  : True or false
Fiability Level : ???
---------------------------------------------------------------------------------------------*/
SndBool SND_fn_bLoadMidiTrackThread(tdstBlockResourceMem * pstPtr,char *czFileName)
{
	int						iNumTrack = 0;
	IDirectMusicSegment *	pSeg;
	long					musicTime; 
	__int64					refTime;
	HRESULT					hr;
	short					tempShort;
	int						i;

iNumTrack=fn_lAllocMidiVoice();
if (iNumTrack<0)
	{
	SND_fn_vDisplayError(E_uwSndMIDIAllTracksUsed,"");
	return FALSE;
	}

// on en a trouve une
// on reference le bloc midi a la liste des pistes midi

pstVoices[iNumTrack].uVoice.stRefRes.pstPtr  = pstPtr;
SND_fn_vResolveFileName(czFileName,StrMidiDirectory);

strlwr(StrMidiDirectory);

if (strstr(StrMidiDirectory,".mdx")!=NULL)
	{
	// Chargement d'un fichier mdx
		tdhSndFile myMDX;

	pstVoices[iNumTrack].cType = C_TYPE_MDX;

	myMDX = SND_fn_hOpenFileReadSnd(StrMidiDirectory);
	SND_fn_dwSeekFileSnd(myMDX,8,SEEK_SET);	
	SND_fn_dwReadFileSnd(myMDX,2,&tempShort);
	_swab((char *)&tempShort,(char *)&(pstVoices[iNumTrack].uVoice.stMDXVoice.wUserTempo),2);
	SND_fn_dwSeekFileSnd(myMDX,1,SEEK_CUR); // on passe le reverb type
	SND_fn_dwReadFileSnd(myMDX,2,&tempShort);
	_swab((char *)&tempShort,(char *)&(pstVoices[iNumTrack].uVoice.stMDXVoice.wMuteFlag),2);
	SND_fn_dwReadFileSnd(myMDX,2,&tempShort);
	_swab((char *)&tempShort,(char *)&(pstVoices[iNumTrack].uVoice.stMDXVoice.wVolFlag),2);
	SND_fn_dwSeekFileSnd(myMDX,4,SEEK_CUR); // on passe le pan et fx flag
	
	for(i=0; i<16; i++)
	{
		if (pstVoices[iNumTrack].uVoice.stMDXVoice.wVolFlag&(1<<i))
		{
			SND_fn_dwReadFileSnd(myMDX,2,&pstVoices[iNumTrack].uVoice.stMDXVoice.vol[i]);
		}
	}

	SND_fn_vCloseFileSnd(myMDX) ;
	pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId=fnlGenerateMDXId();
	pstVoices[iNumTrack].uVoice.stMDXVoice.lId=pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId;
	}
else
	{
	// Chargement d'un fichier midi normal
	pstVoices[iNumTrack].cType = C_TYPE_MID;
	hr=fn_hrLoadExtDMObject(DM_SEGMT, StrMidiDirectory,(void **)&pSeg);
	if (hr!=S_OK) 
		{
		pstVoices[iNumTrack].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId = 0; /*open.wDeviceID;*/
		return FALSE;
		}

	pstVoices[iNumTrack].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId=(unsigned int)pSeg;
	hr=pSeg->lpVtbl->GetLength(pSeg,&musicTime);

	if (hr!=S_OK)
		{
		pstVoices[iNumTrack].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.ulLength = 0; /*status.dwReturn;*/
		}
	else
		{
		hr=g_pPerformance->lpVtbl->MusicToReferenceTime(g_pPerformance,musicTime,&refTime);
		pstVoices[iNumTrack].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.ulLength = 	(unsigned long)(refTime/10000); //unit: 100 ns->1 ms
		}

	pstVoices[iNumTrack].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.ulNbLoops = pstPtr->uRes.stMidi.ulNbLoops;
	//looping managed by DrectMusic
	if (pstPtr->uRes.stMidi.ulNbLoops>1)
		{
		pSeg->lpVtbl->SetRepeats(pSeg,pstPtr->uRes.stMidi.ulNbLoops-1);
		}

	}

return TRUE;
}


//-----------------------------------------------------------
//	SND_fn_bUnloadMidiTrack
//
//	Role	: Unloads a MIDI track, from the voice
//	Input   : Voice to unload
//	Output  : True or false
//------------------------------------------------------------
SndBool SND_fn_bUnloadMidiTrack(long lVoice)
{
if (pstVoices[lVoice].bIsFree == TRUE)
	{
	if (pstVoices[lVoice].cType == C_TYPE_MID)
		{
		/*---------------------------------
		  The resource is a real midi file
		-----------------------------------*/
		if (pstVoices[lVoice].uVoice.stRefRes.pstPtr != NULL)
			{
			if	(
				(pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId != NO_MIDI_DEVICE)
				&&
				(pstVoices[lVoice].uVoice.stRefRes.pstPtr->bIsLoaded)
				)
				{
				// free segment
				((IDirectMusicSegment *)pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId)->lpVtbl->Release((IDirectMusicSegment *)pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId);
				pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId = NO_MIDI_DEVICE;
				pstVoices[lVoice].uVoice.stRefRes.pstPtr->bIsLoaded = FALSE;

				//free segment state				
				if (stActiveMidiTrack.pState!=NULL)
					{
					stActiveMidiTrack.pState->lpVtbl->Release(stActiveMidiTrack.pState);
					stActiveMidiTrack.pState=NULL;
					}				
				pstVoices[lVoice].uVoice.stRefRes.pstPtr = NULL;			
				return TRUE;
				}	
			}	
		}
	else
		{
		/*-------------------------------------------------
		  The resource is a mdx file there's nothing to do
		---------------------------------------------------*/
		pstVoices[lVoice].uVoice.stRefRes.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId = NO_MIDI_DEVICE;
		}
	pstVoices[lVoice].bIsFree = FALSE;
	}
return FALSE;
}


/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vUnLoadResMidi(tdstBlockResourceMem *mem)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vUnLoadResMidi(tdstBlockResourceMem *mem)
{
#ifndef DISABLE_MIDI
long voice;
	
	voice = SND_fn_lGetMidiTrack(mem->uRes.stMidi.uData.stMem.uiMidiDeviceId);
	SND_fn_bUnloadMidiTrack(voice);
	mem->eType = TYPE_INVALID;
#endif //DISABLE_MIDI
}


/*-------------------------------------------------------------------------------------------
	SND_fn_bIsResLoadedMidi
---------------------------------------------------------------------------------------------
Role	: Tell if the binary data associated with a midi resource are loaded.
Input	: The block Resource to test
Output	: True or false
Fiability Level : 100%
---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bIsResLoadedMidi(tdstBlockResourceMem* _pBRMem)
{
#ifndef DISABLE_MIDI
if (_pBRMem->uRes.stMidi.bStream)
	{
    return TRUE;// Streaming sample is always "loaded"
	}
else
	{
    return (_pBRMem->uRes.stMidi.uData.stMem.uiMidiDeviceId!=NO_MIDI_DEVICE);
	}
#else
return FALSE;
#endif //DISABLE_MIDI
}

#ifndef NO_ACP_LDBIN

/*-------------------------------------------------------------------------------------------
SndBool SND_CALL SND_fn_bCanFreeDataMidi(void)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bCanFreeDataMidi(void)
{
return FALSE;
}
#endif


/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vReleaseDriverMidi()
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vReleaseDriverMidi()
{
#ifndef DISABLE_Midi

#endif //DISABLE_Midi
}


/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vRestoreDriverMidi()
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vRestoreDriverMidi()
{
#ifndef DISABLE_Midi

#endif //DISABLE_Midi
}

/*-------------------------------------------------------------------------------------------
SndReal SND_CALL SND_fn_rGetPosMidi(long lVoice)
---------------------------------------------------------------------------------------------
	GetPos: retourne la position "théorique" de la ressource
			exprimé en seconde depuis le début
			différent du temps absolu (car pitch, dérive..)
	Entrée: voie concernée
	Sortie: temps en S (SndReal)
--------------------------------------------------------------------------------------------*/
SndReal SND_CALL SND_fn_rGetPosMidi(long lVoice)
{
	HRESULT		hr;
	long		musicTime;
	__int64		refTime;

if (bIsMidiDriverOk == FALSE) 
	{
	return SND_C_POS_UNKNOWN;
	}

if (lVoice == MIDI_UNIQUE_VOICE)
	{
	if (stActiveMidiTrack.pSegment!= NULL)
		{
		
		if (stActiveMidiTrack.pState==NULL) 
			hr=~S_OK;
		else 
			hr=stActiveMidiTrack.pState->lpVtbl->GetSeek(stActiveMidiTrack.pState,&musicTime);

		if (hr==S_OK)
			{
			hr=g_pPerformance->lpVtbl->MusicToReferenceTime(g_pPerformance,musicTime,&refTime);
			if (hr==S_OK)
				return 	(unsigned long)(refTime/10000*65); //unit: 100 ns->1 s 16:16
			}
		
		}
	}
return SND_C_POS_UNKNOWN;
}

/*-------------------------------------------------------------------------------------------
SndReal SND_CALL SND_fn_rGetLengthMidi(long voie)
---------------------------------------------------------------------------------------------
Fiability Level : 100% ;-)
---------------------------------------------------------------------------------------------*/
SndReal SND_CALL SND_fn_rGetLengthMidi(long voie)
{
	return SND_C_POS_UNKNOWN;
}

/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vSynchroMidi()
---------------------------------------------------------------------------------------------
Fonction de synchro moteur
ne sert à rien pour Midi
Fiability Level : 100% ;-)
---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vSynchroMidi()
{
}
/*-------------------------------------------------------------------------------------------
void SND_CALL fn_MidiThemeTransition(long par)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL fn_MidiThemeTransition(long par)
{
// A midi theme part just ended (either normally, or it was ended). 
if(pMDXTool->lpVtbl->IsFading(pMDXTool)==0)
	{
	s_CurrentMidiThemeRes=s_NextMidiThemeRes; // on passe à la part suivante
	s_NextMidiThemeRes.pstPtr=NULL; // new and dangerous

	if (s_CurrentMidiThemeRes.pstPtr!=NULL)
		{
		OutputDebugString(">>>> PLAY IN CB THEME\n"); 
		s_lCurrentMidiThemeVoice=SND_fn_lPlayMidi(s_CurrentMidiThemeRes,(SoundParam *)par,0,fn_MidiThemeTransition,par);

		OutputDebugString(">>>> CB THEME WARNS SND MOTOR\n"); 
		s_pfnThemeTransitionCB(s_lThemeTransitionCBParam);
		

		if (s_lCurrentMidiThemeVoice==C_PLAY_FAILED)
			{
			// si le play échoue, on force l'appel de la callback
			//(*fn_MidiThemeTransition)(par);
			}
		else
			if (s_lCurrentMidiThemeVoice==MIDI_MDX_VOICE)
				{
				SetEvent(hMDXEvent);
				}
		}
	else
		{
		OutputDebugString(">>>> Theme Stopped because s_NextMidiThemeRes.pstPtr==NULL");
		}
	}
else
	{
	OutputDebugString(">>>> FADE IS ACTIVE");
	}
}

/*-------------------------------------------------------------------------------------------
long SND_CALL SND_fn_lPlayTransitionMidi(tduRefRes FirstRes,tduRefRes NextRes,SND_td_pfn_vSoundCallback transition_callback,long param_callback,SoundParam* par)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
long SND_CALL SND_fn_lPlayTransitionMidi(tduRefRes FirstRes,tduRefRes NextRes,SND_td_pfn_vSoundCallback transition_callback,long param_callback,SoundParam* par)
{
OutputDebugString(">>>> PlayTransitionMidi IN\n"); 
if (bIsMidiDriverOk == FALSE) return C_PLAY_FAILED;


s_CurrentMidiThemeRes=FirstRes;
s_NextMidiThemeRes=NextRes;
s_pfnThemeTransitionCB=transition_callback;
s_lThemeTransitionCBParam=param_callback;

//we must launch the first part...
s_lCurrentMidiThemeVoice= SND_fn_lPlayMidi(s_CurrentMidiThemeRes,par,0,fn_MidiThemeTransition,(long)par);
if (s_lCurrentMidiThemeVoice==C_PLAY_FAILED)
	{
	// si le play échoue, on force l'appel de la callback
	//(*fn_MidiThemeTransition)(par);
	}
else
if (s_lCurrentMidiThemeVoice==MIDI_MDX_VOICE)
		{
		SetEvent(hMDXEvent);
		}


return s_lCurrentMidiThemeVoice;
}


/*-------------------------------------------------------------------------------------------
SndBool SND_CALL SND_fn_bSetParamTransitionMidi(long voice,SoundParam* par)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bSetParamTransitionMidi(long voice,SoundParam* par)
{

return TRUE;
}


/*-------------------------------------------------------------------------------------------
SndBool	SND_CALL SND_fn_bSetNextTransitionMidi(long voice,tduRefRes new_res)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool	SND_CALL SND_fn_bSetNextTransitionMidi(long voice,tduRefRes new_res)
{
OutputDebugString(">>>> SetNExtTransitionMidi IN\n"); 
s_NextMidiThemeRes=new_res;
return TRUE;
}


/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vStopTransitionMidi(long voice)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vStopTransitionMidi(long voice)
{
if (bIsMidiDriverOk == FALSE) return;

s_NextMidiThemeRes.pstPtr=NULL; 
if (s_CurrentMidiThemeRes.pstPtr!=NULL)
	{
	SND_fn_vStopMidi(s_lCurrentMidiThemeVoice); // stop current midi part	
	s_CurrentMidiThemeRes.pstPtr = NULL;
	}

}

/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vResumeTransitionMidi(long voice)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vResumeTransitionMidi(long voice)
{
if (bIsMidiDriverOk == FALSE) return;

if (s_CurrentMidiThemeRes.pstPtr!=NULL)
	 SND_fn_vResumeMidi(s_lCurrentMidiThemeVoice);
}

/*-------------------------------------------------------------------------------------------
void SND_CALL SND_fn_vPauseTransitionMidi(long voice)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vPauseTransitionMidi(long voice)
{
if (bIsMidiDriverOk == FALSE) return;

if (s_CurrentMidiThemeRes.pstPtr!=NULL)
	SND_fn_vPauseMidi(s_lCurrentMidiThemeVoice);
}




void _stdcall CallBackEndFadeOut()
{
OutputDebugString(">>>> Callback du fade appelee\n"); 
pMDXTool->lpVtbl->EndFade(pMDXTool);
fn_vCallCallback();
}

/*-------------------------------------------------------------------------------------------
SndBool	SND_CALL SND_fn_bDoTransitionWithFadeMidi(long voice,tduRefRes new_res)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool	SND_CALL SND_fn_bDoTransitionWithFadeMidi(long voice,tduRefRes new_res)
{
OutputDebugString(">>>> DotransitionWithFade IN\n"); 
if (bIsMidiDriverOk == FALSE) return FALSE;

s_NextMidiThemeRes=new_res;
pMDXTool->lpVtbl->DoFade(pMDXTool, g_pPerformance, 127, 0, 1000, CallBackEndFadeOut);
return TRUE;
}

/*-------------------------------------------------------------------------------------------
SndBool	SND_CALL SND_fn_bDoTransitionWithFadeMidi2( long voice, tduRefRes new_res, SndReal rDuration )
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool	SND_CALL SND_fn_bDoTransitionWithFadeMidi2( long voice, tduRefRes new_res, SndReal rDuration )
{
OutputDebugString(">>>> DotransitionWithFade2 IN\n"); 
if (bIsMidiDriverOk == FALSE) return FALSE;
s_NextMidiThemeRes = new_res;
pMDXTool->lpVtbl->DoFade(pMDXTool, g_pPerformance, 127, 0, rDuration*655/1000, CallBackEndFadeOut);
return TRUE;
}

/*-------------------------------------------------------------------------------------------
SndBool SND_CALL SND_fn_bCheckVersionResourceMidi(tdstBlockResourceDisk* disk)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bCheckVersionResourceMidi(tdstBlockResourceDisk* disk)
{
return TRUE;
}




/*-------------------------------------------------------------------------------------------
HRESULT fn_hrDownloadInstr(IDirectMusicCollection *pCollection,IDirectMusicDownloadedInstrument **ppDLInstrument,DWORD dwPatch,DWORD dwNewPatch)
---------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------*/
HRESULT fn_hrDownloadInstr(IDirectMusicCollection *pCollection,IDirectMusicDownloadedInstrument **ppDLInstrument,DWORD dwPatch,DWORD dwNewPatch)
{
    HRESULT hr;
    IDirectMusicInstrument* pInstrument;
	IDirectMusicPort* pMyPort;
	DWORD  dwGroup;
	DWORD  dwMChannel;

pInstrument=NULL;
hr = pCollection->lpVtbl->GetInstrument(pCollection,dwPatch, &pInstrument);
//bad patch number=>88781114	
if (SUCCEEDED(hr))
    {
       // DMUS_NOTERANGE NoteRange[1];         // Optional note range
        //NoteRange[0].dwLowNote = dwLowNote;
        //NoteRange[0].dwHighNote = dwHighNote;
		
	if (g_pPerformance)
		{
		pMyPort=NULL;
		hr=pInstrument->lpVtbl->SetPatch(pInstrument,dwNewPatch);
		hr=g_pPerformance->lpVtbl->DownloadInstrument(g_pPerformance,pInstrument,0,ppDLInstrument,NULL,0,&pMyPort,&dwGroup,&dwMChannel);
		if (FAILED(hr))
			{
			OutputDebugString("FAIL\n"); 
			}
		if (pMyPort) 
			{
			pMyPort->lpVtbl->Release(pMyPort);			
			}
		}

	if (pInstrument)
		{
		pInstrument->lpVtbl->Release(pInstrument);
		}
    }
else
	{
	OutputDebugString("FAIL\n"); 
	}
    return hr;
}

/*-------------------------------------------------------------------------------------------
HRESULT fn_hrDownloadDLS(char *szDlsName,DWORD dwBank)
---------------------------------------------------------------------------------------------
Role   : Download all the instruments from a .DLS file.
Inputs : * dwBank identifies the bank (2 if they come from BankEditor)
         The instruments are downloaded to the common MIDI instruments.
		 * port on which to download the instruments.
		 * szDlsName: name of the .dls file.
Warning: The instruments belonging to a band will be ignored
---------------------------------------------------------------------------------------------*/
HRESULT fn_hrDownloadDLS(char *szDlsName,DWORD dwBank)
 
{
    HRESULT					hr,hr1;
	unsigned long			dwPatch;
	WCHAR					wszName[MAX_PATH];
	DWORD					dwIndex;
	int						i;
  	IDirectMusicCollection *pCollection;   

//load collection
hr=fn_hrLoadExtDMObject(DM_DLS, szDlsName, (void**)&pCollection);

if (hr==S_OK)	//loaded=>unload previous instruments and load new ones
	{
	for (i=0; i<MAX_INSTR; i++) //unload instruments
		{
		if (gs_aInstruments[i])
			{
			gs_aInstruments[i]->lpVtbl->Release(gs_aInstruments[i]);
			gs_aInstruments[i]=NULL;
			}
		}
		
	if (pCollection)
		{
		hr = S_OK;
		for (dwIndex = 0; hr == S_OK; dwIndex++) //enumerate instruments to load them
			{
			hr1 = pCollection->lpVtbl->EnumInstrument(pCollection,dwIndex, &dwPatch, wszName, MAX_PATH);
			if (hr1 == S_OK)
				{
				//load instrument
				hr1=fn_hrDownloadInstr(pCollection,&gs_aInstruments[dwIndex],dwPatch,dwPatch-(dwBank<<8));
				if (SUCCEEDED(hr1))
					{
					}
				else
					{ 
					if (SUCCEEDED(hr))
						{
						hr=hr1; //return 1st error
						}
					}
				} 
			else
				{
				if (SUCCEEDED(hr))
					hr=hr1; //return 1st error
				}
			}
		pCollection->lpVtbl->Release(pCollection); //useful for loading only
		} //pCollection    		
	}
else
	{
	switch(hr)
		{
		case E_FAIL :
		OutputDebugString("FAIL\n"); 
		break;
		case E_INVALIDARG :
		OutputDebugString("FAIL\n"); 
		break;
		case E_OUTOFMEMORY :
		OutputDebugString("FAIL\n"); 
		break;
		case E_POINTER:
		OutputDebugString("FAIL\n"); 
		break; 
		case DMUS_E_LOADER_NOCLASSID :
		OutputDebugString("FAIL\n"); 
		break;
		case DMUS_E_LOADER_FAILEDOPEN :
		OutputDebugString("FAIL\n"); 
		break;
		case DMUS_E_LOADER_FORMATNOTSUPPORTED :
		OutputDebugString("FAIL\n"); 

		break;
		case REGDB_E_CLASSNOTREG :
		break;

		}
	}
return hr;
}

/*--------------------END INSTRUMENT MANAGEMENT-------------------------------*/