/* MODULE DE GESTION DU MIDI pour WIN95 en mode MCI UBI SOUND STUDIOS Olivier Amiaud */ /***************************************************************************** Prototype de la fonction de gestion d'erreur void SND_fn_vDisplayError(unsigned short uwErrNum,char *message) Liste des erreurs ----------------- E_uwSndMIDILoadTrack E_uwSndMIDIStatus E_uwSndMIDISet E_uwSndMIDIPlay E_uwSndMIDIStop E_uwSndMIDINotifyFailure E_uwSndMIDINotifyAborted E_uwSndMIDINotifySuperseded E_uwSndMIDICreateThread E_uwSndMIDIOpen E_uwSndMIDIClose E_uwSndMIDIFade E_uwSndMIDINbTracks E_uwSndMIDIAllTracksUsed ******************************************************************************/ #include #include #include "SNDinc.h" #include "sndres.h" #include "sndwin95.h" #include "sndmidi.h" #if defined(_DLL_COMPILATION_MODE) #include "SNDDLL.h" #endif #include "sndthrd.h" //#define MIDI_MCI #ifndef DISABLE_MIDI // Gestion du mixer midi, volume , muet.... static MIXERCAPS mixerCapsMidi; static HMIXER hMixerMidi; static UINT mixerIdentifierMidi; static UINT uiNbrMixersMidi; static DWORD dwNbrAudioLinesMidi; static MIXERLINE mixerLineMidi; static MIXERLINECONTROLS mixerLineControlsMidi; static MIXERCONTROL mixerControlMidi; static MIXERCONTROLDETAILS mixerControlDetailsMidi; static MIXERCONTROLDETAILS_UNSIGNED valeurMidi; static BOOL MidiControlAvailable = FALSE; // Utilitaires de transformation d'unité de temps #define SND_MAKE_TMSF(t,m,s,f) ((DWORD)t)|(((DWORD)m)<<8)|(((DWORD)s)<<16)|(((DWORD)f)<<24) #define SND_TMSF_T(tmsf) ((BYTE)(tmsf)) #define SND_TMSF_M(tmsf) ((BYTE)(((WORD)(tmsf)) >> 8)) #define SND_TMSF_S(tmsf) ((BYTE)((tmsf)>>16)) #define SND_TMSF_F(tmsf) ((BYTE)((tmsf)>>24)) #define SND_TMSF_MS(tmsf) \ ((((((((tmsf & 0xff00) >> 8) * 60) + ((tmsf & 0xff0000) >> 16)) * 75) + ((tmsf & 0xff000000)>>24)) * 1000) / 75) //(DWORD) (((((((DWORD)tmsf>>8)&0xff) * 60)+(((DWORD)tmsf>>16)&0xff)) * 75)+(((DWORD)tmsf>>24)&0xff)) #define MSG_ERR_MIDI "Midi Device Error" #define MSG_ERR_MIDI_FILE "Midi File Error" #define NB_MAX_MIDI_TRACKS 50 //#define VOL_MAX_MIDI 60 #define OFFSET_MIDI_VOICE 0x2000 #define MASK_OFFSET_MIDI_VOICE 0x0FFF #define NO_MIDI_VOICE 0x1000 #define MIDI_UNAVAILABLE 0x0008 #define FADE_AND_STOP_MIDI 12 //#ifdef MIDI_MCI // VARIABLES GLOBALES EXTERNES // TYPEDEF LOCAUX //typedef struct _tdstMidiTrack //{ // tduRefRes uMidiTrack; // MCIDEVICEID uiMidiDeviceId; // unsigned long ulLength; // unsigned long ulNbLoops; // unsigned long ulNb; //} //tdstMidiTrack; typedef struct _tdstActiveMidiTrack { SndBool bActive; SndBool bPaused; SndBool bLoop; MCIDEVICEID uiMidiDeviceId; unsigned long ulLength; unsigned long ulNbLoops; unsigned long ulTrackLengthInMs; long lVoice; long lIndexVoice; SoundParam * pstSoundParam; long lPrio; SND_td_pfn_vSoundCallback pfnSoundCallback; long lParSoundCallback; }tdstActiveMidiTrack; typedef struct stMidiDriver { SndBool bMidiDriverOk; int iLastMidiVolume; }tdstMidiDriver; // ------------------------------------------- // VARIABLES CONCERNANT LA GESTION DES THREADS UINT SND_PLAYMIDI_MSG; UINT SND_STOPMIDI_MSG; UINT SND_LOADMIDI_MSG; UINT SND_UNLOADMIDI_MSG; UINT SND_PAUSEMIDI_MSG; UINT SND_RESUMEMIDI_MSG; UINT SND_NEXTTRACKMIDI_MSG; UINT SND_DESINITMIDI_MSG; UINT SND_RELEASEMIDI_MSG; UINT SND_RESTOREMIDI_MSG; static DWORD dw_a5_Params[5]; // VARIABLES GLOBALES LOCALES static SndBool bMidiFadeIn = FALSE; static UINT uiTimerMidiFadeIn; // Gestion du fade-in static unsigned long ulCountDownFadeInMidi; static unsigned long ulTimerPeriodFadeInMidi; static unsigned long ulFadeInDurationMidi; static long lNbIterFadeInMidi; static SndBool bMidiFadeOut= FALSE; static UINT uiTimerMidiFadeOut; // Gestion du fade-out static unsigned long ulCountDownFadeOutMidi; static unsigned long ulTimerPeriodFadeOutMidi; static unsigned long ulFadeOutDurationMidi; static long lNbIterFadeOutMidi; static unsigned char ucFacteurVolumeMidi; // Volume global Midi static MCIDEVICEID NO_MIDI_DEVICE = 127; // Pistes Midi du jeu et piste active static tduRefRes stMidiTracks[NB_MAX_MIDI_TRACKS]; //tdstMidiTrack stMidiTracks[NB_MAX_MIDI_TRACKS]; static tdstActiveMidiTrack stActiveMidiTrack; static tdstMidiDriver stMidiDriver; // Midi driver static int iNbTracks; static char StrMidiDirectory[256]; static char texte_erreur_Midi[256]; // ------------------------------------------- // HANDLE ET ID DE THREADS ET FENETRES static HWND hSndMidiWnd;// handle de la fenetre de gestion des messages static DWORD dwMidiThreadId; static HANDLE hMidiThread=NULL; static DWORD dw_a2_threadParams[2]; static HANDLE hMidiThreadCallBack; static DWORD dwMidiThreadIdCallBack; int SND_fn_iInitMidiThread(void); void SND_fn_vDesInitMidiThread(void); SndBool SND_fn_bLoadMidiTrackThread(tdstBlockResourceMem * pstPtr,char *czFileName); SndBool SND_fn_bUnloadMidiTrackThread(long lVoice); long SND_fn_lPlayMidiThread(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback); void SND_fn_vStopMidiThread(long voice); void SND_fn_vPauseMidiThread(long voice); void SND_fn_vResumeMidiThread(long voice); void SND_CALL SND_fn_vReleaseDriverMidiThread(); void SND_CALL SND_fn_vRestoreDriverMidiThread(); // ------------------------------------------- // PROTOTYPES DES FONCTIONS LOCALES 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); LRESULT CALLBACK SND_fn_vMidiMSG(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); int SND_fn_iMixerInitMidi(void); int SND_fn_iMixerDesInitMidi(void); DWORD SND_fn_dwSND_fn_dwGetMidiMax(void); DWORD SND_fn_dwSND_fn_dwGetMidiMin(void); DWORD SND_fn_dwGetMidiValue(void); void SND_fn_vSetMidiValue(DWORD val); #endif //DISABLE_MIDI //----------------------------------------------------------- // 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 } //------------------------------------------------- // LoadResFromDiskMidi:rendre opérationel une ressource // Entrées:blockdisk à exploiter // blockmem à remplir (déjà alloué) //-------------------------------------------------------- /* void SND_CALL SND_fn_vLoadResFromDiskMidi(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)) { mem->bIsLoaded = FALSE; SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"From Disk"); } #endif //DISABLE_MIDI } */ char SND_gszMidiMDXFakePartName[13]="fakepart.mid"; #if !defined(NO_ACP_SCRIPT) || defined(_DLL_COMPILATION_MODE) /*this function must be defined in Script/Hybrid or DLL mode*/ SndBool SND_CALL SND_fn_bLoadResScriptMidi(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem) { #ifndef DISABLE_MIDI char czFileName[13]; SND_fn_vConvertResDiskToMemMidi(disk,mem,NULL); mem->bIsLoaded = TRUE; // Case mdx fakepart: : // change filename to lowercase and search the ".mdx" extension: strncpy(czFileName,disk->uRes.stMidi.czFileName,13); strlwr(czFileName); // it's a mdx: replace it by the fakepart.mid file if (strstr(czFileName,".mdx")!=NULL) { strcpy(disk->uRes.stMidi.czFileName,SND_gszMidiMDXFakePartName); } if (!SND_fn_bLoadMidiTrack(mem,disk->uRes.stMidi.czFileName)) { mem->bIsLoaded = FALSE; SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"From Script"); return FALSE; } else return TRUE; #else return FALSE; #endif //DISABLE_MIDI } #endif 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)) { mem->bIsLoaded = FALSE; SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"From Binary"); return FALSE; } else return TRUE; #else return FALSE; #endif //DISABLE_MIDI } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_iMidiThreadCallBack // // But : Procédure de la thread qui contient la fenetre qui traite les messages MCI // Entrées : void * : paramètre inutilisé // Sorties : void //------------------------------------------------------------ int WINAPI SND_fn_iMidiThreadCallBack(long arglist) { long * pArg = (void *)arglist; SND_td_pfn_vSoundCallback pfnCallback = (SND_td_pfn_vSoundCallback)pArg[0]; long lParCallback = pArg[1]; SND_fn_vEnterCriticalSectionThreadSnd(); if (pfnCallback != NULL) (*pfnCallback)(lParCallback); SND_fn_vQuitCriticalSectionThreadSnd(); return (0); } //----------------------------------------------------------- // SND_fn_vMidiMSG // // But : Effectuer la gestion des messages du MIDI // Entrées : Messages et leurs parametres // Sorties : Ok ou Failed //------------------------------------------------------------ LRESULT CALLBACK SND_fn_vMidiMSG(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { char texte[256]; SECURITY_ATTRIBUTES sa; switch(message) { case WM_CREATE : break; case WM_COMMAND : break; case MM_MCINOTIFY : switch (wParam) { case MCI_NOTIFY_FAILURE: sprintf(texte,"MM_MCINOTIFY FAILURE wpar=%d lpar=%d",wParam,lParam); SND_fn_vDisplayError(E_uwSndMIDINotifyFailure,texte); break; case MCI_NOTIFY_ABORTED: // sprintf(texte,"MM_MCINOTIFY ABORTED wpar=%d lpar=%d",wParam,lParam); // SND_fn_vDisplayError(E_uwSndMIDINotifyAborted,texte); break; case MCI_NOTIFY_SUPERSEDED: sprintf(texte,"MM_MCINOTIFY SUPERSEDED wpar=%d lpar=%d",wParam,lParam); SND_fn_vDisplayError(E_uwSndMIDINotifyFailure,texte); break; case MCI_NOTIFY_SUCCESSFUL: switch (SND_fn_lGetCurrentMode()) { case MCI_MODE_STOP : // cas ou la piste est terminee de lire, on lance la callback // si elle n'est pas nulle if (!stActiveMidiTrack.bPaused) { if ((stActiveMidiTrack.bLoop) && (--stActiveMidiTrack.ulNbLoops)) { SND_fn_lPlayMidiThread(stMidiTracks[stActiveMidiTrack.lVoice],stActiveMidiTrack.pstSoundParam,stActiveMidiTrack.lPrio,stActiveMidiTrack.pfnSoundCallback,stActiveMidiTrack.lParSoundCallback); } else { if (!stActiveMidiTrack.bPaused) { stActiveMidiTrack.lPrio = 0; stActiveMidiTrack.lVoice = NO_MIDI_VOICE; stActiveMidiTrack.pstSoundParam = NULL; stActiveMidiTrack.bPaused = FALSE; stActiveMidiTrack.bActive = FALSE; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; dw_a2_threadParams[0] = (DWORD)stActiveMidiTrack.pfnSoundCallback; dw_a2_threadParams[1] = (DWORD)stActiveMidiTrack.lParSoundCallback; if (!(hMidiThreadCallBack = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iMidiThreadCallBack,(LPVOID)dw_a2_threadParams,0,&dwMidiThreadIdCallBack))) SND_fn_vDisplayError(E_uwSndMIDICreateThread,""); /* if (stActiveMidiTrack.pfnSoundCallback!= NULL) (*stActiveMidiTrack.pfnSoundCallback)(stActiveMidiTrack.lParSoundCallback); */ } } } break; case MCI_MODE_PAUSE : case MCI_MODE_OPEN : case MCI_MODE_NOT_READY : case MCI_MODE_PLAY : case MCI_MODE_SEEK : case MCI_MODE_RECORD : break; } break; } return 0; case WM_DESTROY: if (uiTimerMidiFadeIn) timeKillEvent(uiTimerMidiFadeIn); if (uiTimerMidiFadeOut) timeKillEvent(uiTimerMidiFadeOut); PostQuitMessage(0); break; default: return DefWindowProc(hwnd,message,wParam,lParam); } return 0; } //----------------------------------------------------------- // SND_fn_iMidiThreadProc // // But : Procédure de la thread qui contient la fenetre qui traite les messages MCI // Entrées : void * : paramètre inutilisé // Sorties : void //------------------------------------------------------------ int __stdcall SND_fn_iMidiThreadProc(HANDLE arglist) { MSG msg; WNDCLASS wcMidiWndClass; DWORD * pdwParams; PeekMessage(&msg,NULL,0,0,PM_NOREMOVE); if (SND_fn_iInitMidiThread() == C_INIT_FAILED) { stMidiDriver.bMidiDriverOk = FALSE; ExitThread(C_INIT_FAILED); } //definit la window-classe //definit la window-classe wcMidiWndClass.hInstance = SND_hGlobalInst; wcMidiWndClass.lpszClassName = "SoundAcpMidi"; wcMidiWndClass.lpfnWndProc = SND_fn_vMidiMSG;//fonction associée à la fonction wcMidiWndClass.style = 0; wcMidiWndClass.hIcon = 0; wcMidiWndClass.hCursor = 0; wcMidiWndClass.lpszMenuName = "\0"; wcMidiWndClass.cbClsExtra = 0; wcMidiWndClass.cbWndExtra = 0; wcMidiWndClass.hbrBackground = 0; //enregistrement de la classe CdWnd if (!RegisterClass(&wcMidiWndClass)) SND_fn_vDisplayError(E_uwSndCDRegisterWndClass,""); //création de la fenetre midi hSndMidiWnd = CreateWindow(wcMidiWndClass.lpszClassName,"MidiWindow",WS_OVERLAPPEDWINDOW,0,0,0,0,HWND_DESKTOP,NULL,SND_hGlobalInst,NULL); // synchro d'initialisation du thread et de la pompe a messages SetEvent(arglist); 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] ); // on debloque le thread appelant SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_STOPMIDI_MSG) { pdwParams = (DWORD *)msg.wParam; SND_fn_vStopMidiThread((long)pdwParams[0]); SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_LOADMIDI_MSG) { pdwParams = (DWORD *)msg.wParam; pdwParams[0] = SND_fn_bLoadMidiTrackThread((tdstBlockResourceMem *)pdwParams[0],(char *)pdwParams[1]); SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_UNLOADMIDI_MSG) { pdwParams = (DWORD *)msg.wParam; pdwParams[0] = SND_fn_bUnloadMidiTrackThread((long)pdwParams[0]); SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_PAUSEMIDI_MSG) { pdwParams = (DWORD *)msg.wParam; SND_fn_vPauseMidiThread((long)pdwParams[0]); SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_RESUMEMIDI_MSG) { pdwParams = (DWORD *)msg.wParam; SND_fn_vResumeMidiThread((long)pdwParams[0]); SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_DESINITMIDI_MSG) { SND_fn_vDesInitMidiThread(); SetEvent((HANDLE)msg.lParam); ExitThread(0); } else if (msg.message == SND_RELEASEMIDI_MSG) { SND_fn_vReleaseDriverMidiThread(); SetEvent((HANDLE)msg.lParam); } else if (msg.message == SND_RESTOREMIDI_MSG) { SND_fn_vRestoreDriverMidiThread(); SetEvent((HANDLE)msg.lParam); } else { TranslateMessage(&msg); DispatchMessage(&msg); } //WaitMessage(); } return 0; } //----------------------------------------------------------- // SND_fn_lTimerIn() // // But : Callback de gestion du temps pour les fade-in. // Entrées : neant // Sorties : neant //------------------------------------------------------------ void CALLBACK SND_fn_lMidiTimerIn(UINT IDEvent,UINT msg,DWORD par,DWORD dw1,DWORD dw2) { if (!stActiveMidiTrack.bPaused && stActiveMidiTrack.bActive) if (bMidiFadeIn) { if (lNbIterFadeInMidi--) SND_fn_vSetMidiValue(ucFacteurVolumeMidi++); else { bMidiFadeIn = FALSE; ulCountDownFadeInMidi = 0; } } } //----------------------------------------------------------- // SND_fn_lTimerOut() // // But : Callback de gestion du temps pour les fade-in. // Entrées : neant // Sorties : neant //------------------------------------------------------------ void CALLBACK SND_fn_lMidiTimerOut(UINT IDEvent,UINT msg,DWORD par,DWORD dw1,DWORD dw2) { unsigned long ulRealTime = 0; if (!stActiveMidiTrack.bPaused && stActiveMidiTrack.bActive) if (bMidiFadeOut) { ulCountDownFadeOutMidi += ulTimerPeriodFadeOutMidi; ulRealTime = stActiveMidiTrack.ulTrackLengthInMs - lNbIterFadeOutMidi * ulTimerPeriodFadeOutMidi; if (ulCountDownFadeOutMidi > ulRealTime) { SND_fn_vSetMidiValue(ucFacteurVolumeMidi); if (ucFacteurVolumeMidi-- == 0) { bMidiFadeOut = FALSE; ulCountDownFadeOutMidi = 0; if (par == FADE_AND_STOP_MIDI) { SND_fn_vStopMidiThread(0); PostMessage(hSndMidiWnd,MM_MCINOTIFY,MCI_NOTIFY_SUCCESSFUL,0); } } } } } #endif //DISABLE_MIDI //----------------------------------------------------------- // SND_fn_iInitMidi // // But : Realiser l'init bas niveau du driver midi // Entrées : neant // Sorties : C_INIT_OK ou C_INIT_FAILED //------------------------------------------------------------ int SND_CALL SND_fn_iInitMidi(SND_tdstInitStruct *pInitStruct) { #ifndef DISABLE_MIDI DWORD dwExitCode = 0; HANDLE hEventSynchro; SECURITY_ATTRIBUTES sa; HANDLE hHandlesForWait[2]; #if defined(_DLL_COMPILATION_MODE) SND_DllInitEntryPoints_StaticFunctions((HMODULE)pInitStruct->hProcessInstance ); #endif stMidiDriver.bMidiDriverOk = MIDI_UNAVAILABLE; uiTimerMidiFadeIn = uiTimerMidiFadeOut = 0; // Init toutes pistes CD //init de hwndSnd et hinst ,et SND_hResourceHandle: hwndMainSnd=pInitStruct->hMainWindow; SND_hGlobalInst=pInitStruct->hProcessInstance; if (SND_g_hResourceHandle==NULL) SND_g_hResourceHandle=SND_hGlobalInst; // on init les points d'entree fonctions externes DLL #ifdef _DLL_COMPILATION_MODE SND_DllInitEntryPoints_StaticFunctions((HMODULE)SND_hGlobalInst); #endif // on enregistre les differents types de messages recus par le thread SND_PLAYMIDI_MSG = RegisterWindowMessage("PlayMIDI"); SND_STOPMIDI_MSG = RegisterWindowMessage("StopMIDI"); SND_LOADMIDI_MSG = RegisterWindowMessage("LoadMIDI"); SND_UNLOADMIDI_MSG = RegisterWindowMessage("UnloadMIDI"); SND_PAUSEMIDI_MSG = RegisterWindowMessage("PauseMIDI"); SND_RESUMEMIDI_MSG = RegisterWindowMessage("ResumeMIDI"); SND_NEXTTRACKMIDI_MSG = RegisterWindowMessage("NextTrackMIDI"); SND_DESINITMIDI_MSG = RegisterWindowMessage("DesinitMIDI"); SND_RELEASEMIDI_MSG = RegisterWindowMessage("ReleaseMIDI"); SND_RESTOREMIDI_MSG = RegisterWindowMessage("RestoreMIDI"); // on cree le thread de gestion CD l'ID du thread est dwCDThreadId hMidiThread = NULL; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); while (TRUE) { if (!(hMidiThread = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iMidiThreadProc,hEventSynchro,0,&dwMidiThreadId))) { SND_fn_vDisplayError(E_uwSndMIDICreateThread,""); return C_INIT_FAILED; } else { hHandlesForWait[0] = hEventSynchro; hHandlesForWait[1] = hMidiThread; // on attend la synchronisation d'init., l'attente se termine quand un des deux events arrive WaitForMultipleObjects(2,hHandlesForWait,FALSE,INFINITE); GetExitCodeThread(hMidiThread,&dwExitCode); if (dwExitCode == STILL_ACTIVE) { stMidiDriver.bMidiDriverOk = TRUE; CloseHandle(hEventSynchro); return C_INIT_OK; } else { if (dwExitCode == C_INIT_FAILED) { // on joue sans Midi CloseHandle(hEventSynchro); return C_INIT_FAILED; } } } } return C_INIT_OK; #else return C_INIT_FAILED; #endif //DISABLE_MIDI } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_iInitMidi // // But : Realiser l'init bas niveau du driver midi // Entrées : neant // Sorties : C_INIT_OK ou C_INIT_FAILED //------------------------------------------------------------ int SND_fn_iInitMidiThread() { int i; for (i=0;i < NB_MAX_MIDI_TRACKS;i++) stMidiTracks[i].pstPtr = NULL; stActiveMidiTrack.bLoop = FALSE; stActiveMidiTrack.bActive = FALSE; stActiveMidiTrack.bPaused = FALSE; stActiveMidiTrack.ulNbLoops = 0; stActiveMidiTrack.ulLength = 0; stActiveMidiTrack.lVoice = NO_MIDI_VOICE; stActiveMidiTrack.pstSoundParam = NULL; stActiveMidiTrack.lPrio = 0; stActiveMidiTrack.pfnSoundCallback= NULL; stActiveMidiTrack.lParSoundCallback= 0; stMidiDriver.bMidiDriverOk = TRUE; iNbTracks = 0; if (!SND_fn_iMixerInitMidi()) { // On sauve l'ancienne valeur du volume CD stMidiDriver.iLastMidiVolume = SND_fn_dwGetMidiValue(); ucFacteurVolumeMidi = stMidiDriver.iLastMidiVolume; SND_fn_vSetMidiValue(ucFacteurVolumeMidi); } return C_INIT_OK; } #endif //DISABLE_MIDI //----------------------------------------------------------- // SND_fn_iTestInitMidi // // But : Verifier l'etat d'init du driver midi // Entrées : neant // Sorties : True ou False suivant l'etat //------------------------------------------------------------ SndBool SND_CALL SND_fn_bTestInitMidi(void) { #ifndef DISABLE_MIDI return (stMidiDriver.bMidiDriverOk); #else return FALSE; #endif //DISABLE_MIDI } //----------------------------------------------------------- // SND_fn_iDesInitMidi // // But : Realiser la desnit bas niveau du driver midi // Entrées : neant // Sorties : neant //------------------------------------------------------------ void SND_CALL SND_fn_vDesInitMidi(void) { #ifndef DISABLE_MIDI HANDLE hEventSynchro; if (hMidiThread) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_DESINITMIDI_MSG,(WPARAM)0,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIClose,"PostThreadMessage Desinit"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); WaitForSingleObject(hMidiThread,INFINITE); CloseHandle(hMidiThread); } #endif //DISABLE_MIDI } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_iDesInitMidiThread // // But : Realiser la desnit bas niveau du driver midi // Entrées : neant // Sorties : neant //------------------------------------------------------------ void SND_fn_vDesInitMidiThread(void) { int i; for (i=0;iuRes.stMidi.uData.stMem.uiMidiDeviceId != Id) i++; else break; } while (i < NB_MAX_MIDI_TRACKS); if (i == NB_MAX_MIDI_TRACKS) return (NO_MIDI_VOICE); else return (i); } //----------------------------------------------------------- // SND_fn_lGetActiveTrack // // But : Renvoie la voie active // Entrées : neant // Sorties : voie bas niveau active, NO_MIDI_VOICE sinon //------------------------------------------------------------ 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) { MCI_STATUS_PARMS status; MCIERROR mciReturn; // Mode actuel de la piste status.dwItem = MCI_STATUS_MODE; if (stActiveMidiTrack.uiMidiDeviceId != NO_MIDI_DEVICE) { if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIStatus,texte_erreur_Midi); return (0); } } else return (0); return(status.dwReturn); } //----------------------------------------------------------- // SND_fn_lGetCurrentPosition // // But : Renvoie la position courante de lecture // de la voie active // Entrées : neant // Sorties : position en millisecondes //------------------------------------------------------------ long SND_fn_lGetCurrentPosition(void) { MCI_STATUS_PARMS status; MCIERROR mciReturn; // Mode actuel de la piste status.dwItem = MCI_STATUS_MODE; if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIStatus,texte_erreur_Midi); return (0); } if ((status.dwReturn == MCI_MODE_PLAY)||(status.dwReturn == MCI_MODE_PAUSE)) { // Si la piste est en mode play ou en pause status.dwItem = MCI_STATUS_POSITION; if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIStatus,texte_erreur_Midi); return (0); } return (status.dwReturn); } else return (0); } //----------------------------------------------------------- // SND_ulGetMidiTrackLengthInMs // // But : Calcule la duree exacte de la piste en millisecondes // Entrées : La voie active // Sorties : La position en tmsf //------------------------ unsigned long SND_fn_ulGetMidiTrackLengthInMs(unsigned long ulVoice) { unsigned long ulRealLength; ulRealLength = stMidiTracks[stActiveMidiTrack.lVoice].pstPtr->uRes.stMidi.uData.stMem.ulLength; return (ulRealLength); } //----------------------------------------------------------- // SND_fn_ulInitFadeMidi // // But : Initialise les variables de fading // Entrées : // Sorties : //------------------------ void SND_fn_ulInitFadeMidi( unsigned long ulFadeInDurationMidi,unsigned long ulFadeOutDurationMidi) { lNbIterFadeInMidi = ucFacteurVolumeMidi; lNbIterFadeOutMidi = ucFacteurVolumeMidi; if (ulFadeInDurationMidi+ulFadeOutDurationMidi < stActiveMidiTrack.ulTrackLengthInMs) { bMidiFadeIn = (SndBool)ulFadeInDurationMidi; bMidiFadeOut = (SndBool)ulFadeOutDurationMidi; if (bMidiFadeIn) { ulTimerPeriodFadeInMidi = ulFadeInDurationMidi / lNbIterFadeInMidi; ulCountDownFadeInMidi = 0; ucFacteurVolumeMidi = 0; // init du timer cross-fade uiTimerMidiFadeIn = timeSetEvent(ulTimerPeriodFadeInMidi,10,(LPTIMECALLBACK)SND_fn_lMidiTimerIn,(DWORD)0,TIME_PERIODIC); } if (bMidiFadeOut) { ulTimerPeriodFadeOutMidi = ulFadeOutDurationMidi / lNbIterFadeInMidi; ulCountDownFadeOutMidi = 0; if (!bMidiFadeIn) { ucFacteurVolumeMidi = ucFacteurVolumeMidi; } // init du timer cross-fade uiTimerMidiFadeOut = timeSetEvent(ulTimerPeriodFadeOutMidi,10,(LPTIMECALLBACK)SND_fn_lMidiTimerOut,(DWORD)0,TIME_PERIODIC); } SND_fn_vSetMidiValue(ucFacteurVolumeMidi); } else { SND_fn_vSetMidiValue(ucFacteurVolumeMidi); SND_fn_vDisplayError(E_uwSndMIDIFade,"Bad Fading Parameters"); } } #endif //DISABLE_MIDI //----------------------------------------------------------- // SND_fn_lPlayMidi // // But : Joue la ressource midi res // Entrées : La ressource, parametres son, la priorite et // la callback // Sorties : voie allouee pour jouee la ressource //------------------------------------------------------------ long SND_CALL SND_fn_lPlayMidi(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback) { #ifndef DISABLE_MIDI HANDLE hEventSynchro; // Case mdx part: fail: char czFileName[13]; // change filename to lowercase and search the ".mdx" extension: strncpy(czFileName,res.pstPtr->uRes.stMidi.uData.stStream.fichier,13); strlwr(czFileName); if (strstr(czFileName,".mdx")!=NULL) return C_PLAY_FAILED; if (stMidiDriver.bMidiDriverOk) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); 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; if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_PLAYMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIPlay,"PostThreadMessage"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); // 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); } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_lPlayMidiThread // // But : Joue la ressource midi res // Entrées : La ressource, parametres son, la priorite et // la callback // Sorties : voie allouee pour jouee la ressource //------------------------------------------------------------ long SND_fn_lPlayMidiThread(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback) { MCIERROR mciReturn; MCI_PLAY_PARMS play; long lVoice = 0,lIndexVoice; play.dwCallback = (DWORD)hSndMidiWnd; play.dwFrom = 0; if ((lVoice = SND_fn_lGetMidiTrack(res.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId)) != NO_MIDI_VOICE) { debut: lIndexVoice = SND_fn_lGetMidiTrack(res.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId); if (stActiveMidiTrack.lVoice == NO_MIDI_VOICE) { stActiveMidiTrack.bLoop = res.pstPtr->uRes.stMidi.bLoop; stActiveMidiTrack.lPrio = prio; stActiveMidiTrack.lVoice = lVoice; stActiveMidiTrack.lIndexVoice = lIndexVoice; stActiveMidiTrack.ulNbLoops = stMidiTracks[lIndexVoice].pstPtr->uRes.stMidi.uData.stMem.ulNbLoops; stActiveMidiTrack.ulTrackLengthInMs = SND_fn_ulGetMidiTrackLengthInMs(lIndexVoice); } else { // on a un ressource bouclante qui en est a son deuxieme tout minimum if ((stActiveMidiTrack.bActive && stActiveMidiTrack.bLoop)&& (stActiveMidiTrack.ulNbLoops != stMidiTracks[stActiveMidiTrack.lIndexVoice].pstPtr->uRes.stMidi.uData.stMem.ulNbLoops)) lVoice = stActiveMidiTrack.lVoice; // on a une ressource pas bouclante mais il y a eu un play sans stop de la ressource precedente else { // on arrete spontanement une voix, on doit reallouer la meme voie pour le haut niveau lVoice = stActiveMidiTrack.lVoice; SND_fn_vStopMidiThread(stActiveMidiTrack.lVoice); goto debut; } } if (stMidiTracks[lIndexVoice].pstPtr != NULL) stActiveMidiTrack.uiMidiDeviceId = stMidiTracks[lIndexVoice].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId; else { SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"no unload before loading"); return (NO_MIDI_VOICE); } if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_PLAY,MCI_NOTIFY|MCI_FROM,(DWORD)(LPVOID)&play)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIPlay,texte_erreur_Midi); return (NO_MIDI_VOICE); } stActiveMidiTrack.bActive = TRUE; stActiveMidiTrack.bPaused = FALSE; stActiveMidiTrack.pstSoundParam = par; stActiveMidiTrack.pfnSoundCallback=fn_callback; stActiveMidiTrack.lParSoundCallback=par_callback; return (lVoice | OFFSET_MIDI_VOICE); } return C_PLAY_FAILED; } //----------------------------------------------------------- // SND_fn_lPlayMidiWithFade // // But : Joue la ressource Midi res avec un fade // Entrées : La ressource, parametres son, la priorite et // la callback // Sorties : voie allouee pour jouee la ressource //------------------------------------------------------------ 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) { long result; result = SND_fn_lPlayMidi(res,par,prio,fn_callback,par_callback); SND_fn_ulInitFadeMidi(ulFadeIn,ulFadeOut); return (result); } #endif //DISABLE_MIDI //----------------------------------------------------------- // SND_fn_vStopMidi // // But : Stopper une voie // Entrées : la voie a stopper // Sorties : neant //------------------------------------------------------------ void SND_CALL SND_fn_vStopMidi(long voice) { #ifndef DISABLE_MIDI HANDLE hEventSynchro; dw_a5_Params[0] = (DWORD)voice; if (stMidiDriver.bMidiDriverOk) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_STOPMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIStop,"PostThreadMessage"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); } #endif //DISABLE_MIDI } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_vStopMidiThread // // But : Stopper une voie // Entrées : la voie a stopper // Sorties : neant //------------------------------------------------------------ void SND_fn_vStopMidiThread(long voice) { MCIERROR mciReturn; MCI_GENERIC_PARMS stop; SECURITY_ATTRIBUTES sa; stop.dwCallback = (DWORD)hSndMidiWnd; if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_STOP,MCI_NOTIFY,(DWORD)&stop)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIStop,"Stopping Midi Error"); } stActiveMidiTrack.lPrio = 0; stActiveMidiTrack.lVoice = NO_MIDI_VOICE; stActiveMidiTrack.uiMidiDeviceId = NO_MIDI_DEVICE; stActiveMidiTrack.bPaused = FALSE; stActiveMidiTrack.bActive = FALSE; if (stActiveMidiTrack.pfnSoundCallback) { sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; dw_a2_threadParams[0] = (DWORD)stActiveMidiTrack.pfnSoundCallback; dw_a2_threadParams[1] = (DWORD)stActiveMidiTrack.lParSoundCallback; if (!(hMidiThreadCallBack = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iMidiThreadCallBack,(LPVOID)dw_a2_threadParams,0,&dwMidiThreadIdCallBack))) SND_fn_vDisplayError(E_uwSndCDCreateThread,""); } stActiveMidiTrack.pfnSoundCallback = NULL; stActiveMidiTrack.lParSoundCallback= 0; stActiveMidiTrack.pstSoundParam = NULL; } #endif //DISABLE_MIDI //----------------------------------------------------------- // SND_fn_vPauseMidi // // But : Pauser une voie // Entrées : la voie a pauser // Sorties : true,false //------------------------------------------------------------ void SND_CALL SND_fn_vPauseMidi(long voice) { #ifndef DISABLE_MIDI HANDLE hEventSynchro; dw_a5_Params[0] = (DWORD)voice; if (stMidiDriver.bMidiDriverOk) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_PAUSEMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIStop,"PostThreadMessage Pause"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); } #endif //DISABLE_MIDI } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_vPauseMidiThread // // But : Pauser une voie // Entrées : la voie a pauser // Sorties : true,false //------------------------------------------------------------ void SND_fn_vPauseMidiThread(long voice) { MCIERROR mciReturn; MCI_GENERIC_PARMS pause; pause.dwCallback = (DWORD)hSndMidiWnd; if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_STOP,MCI_NOTIFY,(DWORD)&pause)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIStop,texte_erreur_Midi); return; } stActiveMidiTrack.bPaused = TRUE; } #endif //DISABLE_MIDI //----------------------------------------------------------- // SND_fn_vResumeMidi // // But : Resumer une voie // Entrées : la voie a resumer // Sorties : True,false //------------------------------------------------------------ void SND_CALL SND_fn_vResumeMidi(long voice) { #ifndef DISABLE_MIDI HANDLE hEventSynchro; dw_a5_Params[0] = (DWORD)voice; if (stMidiDriver.bMidiDriverOk) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_RESUMEMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIPlay,"PostThreadMessage Resume"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); } #endif //DISABLE_MIDI } #ifndef DISABLE_MIDI //----------------------------------------------------------- // SND_fn_vResumeMidi // // But : Resumer une voie // Entrées : la voie a resumer // Sorties : True,false //------------------------------------------------------------ void SND_fn_vResumeMidiThread(long voice) { MCIERROR mciReturn; MCI_GENERIC_PARMS resume; MCI_PLAY_PARMS play; resume.dwCallback = (DWORD)hSndMidiWnd; play.dwCallback = (DWORD)hSndMidiWnd; if (stActiveMidiTrack.bPaused) if (mciReturn=mciSendCommand(stActiveMidiTrack.uiMidiDeviceId,MCI_PLAY,MCI_NOTIFY,(DWORD)&play)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIPlay,texte_erreur_Midi); return; } stActiveMidiTrack.bPaused = FALSE; } //----------------------------------------------------------- // SND_fn_bLoadMidiTrack // // But : Charge une piste midi, a partir du block ressource // Entrées : Le block ressource et le nom du fichier midi // Sorties : true,false //------------------------------------------------------------ SndBool SND_fn_bLoadMidiTrack(tdstBlockResourceMem * pstPtr,char *czFileName) { HANDLE hEventSynchro; if (stMidiDriver.bMidiDriverOk) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); dw_a5_Params[0] = (DWORD)pstPtr; dw_a5_Params[1] = (DWORD)czFileName; if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_LOADMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDILoadTrack,"PostThreadMessage"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); // dans ce cas, on retourne le numero de voie dans dw_a5_Params[0] return (dw_a5_Params[0]); } else return FALSE; } //----------------------------------------------------------- // SND_fn_bLoadMidiTrackThread // // But : Charge une piste midi, a partir du block ressource // Entrées : Le block ressource et le nom du fichier midi // Sorties : true,false //------------------------------------------------------------ SndBool SND_fn_bLoadMidiTrackThread(tdstBlockResourceMem * pstPtr,char *czFileName) { MCI_OPEN_PARMS open; MCI_STATUS_PARMS status; MCI_SET_PARMS set; MCIERROR mciReturn; int iNumTrack = 0; // on cherche une track libre dans le tableau de stMidiTracks while (stMidiTracks[iNumTrack].pstPtr != NULL) { if (iNumTrack > NB_MAX_MIDI_TRACKS) { // on en trouve pas SND_fn_vDisplayError(E_uwSndMIDIAllTracksUsed,""); return FALSE; } iNumTrack++; } // on en a trouve une // on reference le bloc midi a la liste des pistes midi stMidiTracks[iNumTrack].pstPtr = pstPtr; open.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_SEQUENCER; open.dwCallback = (DWORD)hSndMidiWnd; SND_fn_vResolveFileName(czFileName,StrMidiDirectory); //SND_fn_vGetDataDirectory(StrMidiDirectory,sizeof(StrMidiDirectory)); //strncat((LPSTR)StrMidiDirectory,(LPCSTR)czFileName,sizeof(StrMidiDirectory)-strlen(StrMidiDirectory)); open.lpstrElementName = (LPSTR)StrMidiDirectory; if (mciReturn=mciSendCommand( stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId, MCI_OPEN, MCI_WAIT|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT, (DWORD)(LPVOID)&open)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIOpen,texte_erreur_Midi); return FALSE; } stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId = open.wDeviceID; status.dwItem = MCI_SEQ_STATUS_PORT; if (mciReturn=mciSendCommand(stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); stMidiDriver.bMidiDriverOk = FALSE; SND_fn_vDisplayError(E_uwSndMIDIStatus,texte_erreur_Midi); mciSendCommand(stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId,MCI_CLOSE,0,0); return FALSE; } // Par défaut, l'unité de temps MIDI est la milliseconde set.dwTimeFormat = MCI_FORMAT_MILLISECONDS; if (mciReturn=mciSendCommand( stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&set)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDISet,texte_erreur_Midi); return FALSE; } // Par défaut, l'unité de temps MIDI est la milliseconde status.dwItem = MCI_STATUS_LENGTH; // On calcule la longueur de la piste en millisecondes if (mciReturn=mciSendCommand( stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&status)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIStatus,texte_erreur_Midi); return FALSE; } stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.ulLength = status.dwReturn; stMidiTracks[iNumTrack].pstPtr->uRes.stMidi.uData.stMem.ulNbLoops = pstPtr->uRes.stMidi.ulNbLoops; iNbTracks = iNumTrack+1; return TRUE; } /* SndBool SND_fn_bUnloadMidiRes(tduRefRes RR) { long voice = SND_fn_lGetMidiTrack(RR.pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId); if (SND_fn_bUnloadMidiTrack(voice)) return TRUE; else return FALSE; } */ //----------------------------------------------------------- // SND_fn_bUnloadMidiTrack // // But : Decharge une piste midi, a partir de la voie // Entrées : La voie a decharger // Sorties : true,false //------------------------------------------------------------ SndBool SND_fn_bUnloadMidiTrack(long lVoice) { HANDLE hEventSynchro; if (stMidiDriver.bMidiDriverOk) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); dw_a5_Params[0] = (DWORD)lVoice; if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_UNLOADMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIUnloadTrack,"PostThreadMessage"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); // dans ce cas, on retourne le numero de voie dans dw_a5_Params[0] return (dw_a5_Params[0]); } else return FALSE; } //----------------------------------------------------------- // SND_fn_bUnloadMidiTrack // // But : Decharge une piste midi, a partir de la voie // Entrées : La voie a decharger // Sorties : true,false //------------------------------------------------------------ SndBool SND_fn_bUnloadMidiTrackThread(long lVoice) { MCIERROR mciReturn; if ((lVoice < NB_MAX_MIDI_TRACKS)&& (stMidiTracks[lVoice].pstPtr != NULL)) { if ((stMidiTracks[lVoice].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId != NO_MIDI_DEVICE)&& (stMidiTracks[lVoice].pstPtr->bIsLoaded)) { if (mciReturn=mciSendCommand(stMidiTracks[lVoice].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId,MCI_CLOSE,0,0)) { mciGetErrorString(mciReturn,texte_erreur_Midi,sizeof(texte_erreur_Midi)); SND_fn_vDisplayError(E_uwSndMIDIClose,texte_erreur_Midi); return FALSE; } stMidiTracks[lVoice].pstPtr->uRes.stMidi.uData.stMem.uiMidiDeviceId= NO_MIDI_DEVICE; stMidiTracks[lVoice].pstPtr = NULL; iNbTracks--; return TRUE; } else return FALSE; } else return FALSE; } #endif //DISABLE_MIDI void SND_CALL SND_fn_vUnLoadResMidi(tdstBlockResourceMem *mem) { #ifndef DISABLE_MIDI long voice = SND_fn_lGetMidiTrack(mem->uRes.stMidi.uData.stMem.uiMidiDeviceId); SND_fn_bUnloadMidiTrack(voice); mem->eType = TYPE_INVALID; #endif //DISABLE_MIDI } //----------------------------------------------------------- // SND_fn_bIsResLoadedMidi // // Goal : Tell if the binary data associated with a midi resource are loaded. // Input : The block Resource to test // Output : true,false //------------------------------------------------------------ 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 } /* void SND_CALL SND_fn_vSetResUnloadedMidi(tdstBlockResourceMem *pResMem) { #ifndef DISABLE_MIDI pResMem->uRes.stMidi.bStream=FALSE; pResMem->uRes.stMidi.uData.stMem.uiMidiDeviceId=NO_MIDI_DEVICE; #endif //DISABLE_MIDI } */ #ifndef DISABLE_MIDI //#endif //MIDI_MCI int SND_fn_iMixerInitMidi(void) { uiNbrMixersMidi = mixerGetNumDevs(); //OK if (uiNbrMixersMidi == 0) { SND_fn_vDisplayError(E_uwSndMixerNoMixer,""); return 1; } if(mixerGetID((HMIXEROBJ)0,&mixerIdentifierMidi,MIXER_OBJECTF_MIXER) !=MMSYSERR_NOERROR) { SND_fn_vDisplayError(E_uwSndMixerNoId,""); return 1; } if(mixerOpen(&hMixerMidi,mixerIdentifierMidi,0,0,MIXER_OBJECTF_MIXER/* MIXER_OBJECTF_HWAVEOUT*/) != MMSYSERR_NOERROR) { SND_fn_vDisplayError(E_uwSndMixerOpen,""); return 1; } if(mixerGetDevCaps((UINT)hMixerMidi,&mixerCapsMidi,sizeof(MIXERCAPS)) != MMSYSERR_NOERROR) { SND_fn_vDisplayError(E_uwSndMixerCapacities,""); return 1; } dwNbrAudioLinesMidi = mixerCapsMidi.cDestinations; //================== // MIXERLINE MIDI //================== mixerLineMidi.cbStruct = sizeof(MIXERLINE); mixerLineMidi.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER; if (mixerGetLineInfo((HMIXEROBJ)hMixerMidi,&mixerLineMidi,MIXER_GETLINEINFOF_COMPONENTTYPE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) { //return MIXER_ERR_GETLINEINFOSAMPLE; MidiControlAvailable = FALSE; } else { MidiControlAvailable = TRUE; } //====================== // MIXERLINECONTROL MIDI //====================== mixerLineControlsMidi.cbStruct = sizeof(MIXERLINECONTROLS); mixerLineControlsMidi.dwLineID = mixerLineMidi.dwLineID; mixerLineControlsMidi.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mixerLineControlsMidi.cControls = 1; mixerLineControlsMidi.cbmxctrl = sizeof(MIXERCONTROL); mixerLineControlsMidi.pamxctrl =(MIXERCONTROL *) &mixerControlMidi; if(mixerGetLineControls((HMIXEROBJ)hMixerMidi,&mixerLineControlsMidi,MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) MidiControlAvailable = FALSE; else MidiControlAvailable = TRUE; //============================= // MIXERLINECONTROLDETAILS MIDI //============================= mixerControlDetailsMidi.cbStruct = sizeof(MIXERCONTROLDETAILS); mixerControlDetailsMidi.dwControlID = mixerControlMidi.dwControlID; mixerControlDetailsMidi.cChannels = 1/*mixerLineMidi.cChannels 1*/; mixerControlDetailsMidi.cMultipleItems = mixerControlMidi.cMultipleItems; mixerControlDetailsMidi.cbDetails =sizeof(MIXERCONTROLDETAILS_UNSIGNED); mixerControlDetailsMidi.paDetails= &valeurMidi; return 0; } DWORD SND_fn_dwGetMidiMax(void) { return mixerControlMidi.Bounds.dwMaximum; } DWORD SND_fn_dwGetMidiMin(void) { return mixerControlMidi.Bounds.dwMinimum; } //======================================= // void SND_fn_vSetMidiValue(DWORD val) //======================================= // Positionne le volume du Midi en % // val entre 0 et 100 //======================================= void SND_fn_vSetMidiValue(DWORD val) { DWORD inc; if (MidiControlAvailable) { //snd_assert(val>=0); snd_assert(val<=100); inc = (SND_fn_dwGetMidiMax()-SND_fn_dwGetMidiMin())/100; val = (val* inc) + SND_fn_dwGetMidiMin(); snd_assert(val>=SND_fn_dwGetMidiMin()); snd_assert(val<=SND_fn_dwGetMidiMax()); valeurMidi.dwValue = val; if(mixerSetControlDetails((HMIXEROBJ)hMixerMidi,&mixerControlDetailsMidi,MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) { //return 1; } } } //================================= // SND_fn_dwGetMidiValue //================================= // Retourne la valeur du mixer Midi // en % //================================= DWORD SND_fn_dwGetMidiValue(void) { DWORD inc; DWORD result; if (MidiControlAvailable) { inc = (SND_fn_dwGetMidiMax()-SND_fn_dwGetMidiMin())/100; if(mixerGetControlDetails((HMIXEROBJ)hMixerMidi,&mixerControlDetailsMidi,MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) { return 1; } result = (valeurMidi.dwValue - SND_fn_dwGetMidiMin())/inc; //snd_assert(result>=0); snd_assert(result<=100); return result; } else return 0; } BOOL SND_fn_bIsMidiControlAvailable(void) { return MidiControlAvailable; } int SND_fn_iMixerDesInitMidi(void) { if(mixerClose(hMixerMidi) != MMSYSERR_NOERROR) { SND_fn_vDisplayError(E_uwSndMixerDesinit,""); return 1; } return 0; } #endif //DISABLE_MIDI #ifndef NO_ACP_LDBIN SndBool SND_CALL SND_fn_bCanFreeDataMidi(void) { return FALSE; } #endif /*-------------------------------------------------- relache-reprise a chaud des drives ---------------------------------------------------------*/ #define PAUSED_BY_RESTORE_RELEASE 2 void SND_CALL SND_fn_vReleaseDriverMidiThread() { #ifndef DISABLE_MIDI if (! stActiveMidiTrack.bPaused) { SND_fn_vPauseMidiThread(stActiveMidiTrack.lVoice); stActiveMidiTrack.bPaused = (BOOL)PAUSED_BY_RESTORE_RELEASE; } #endif //DISABLE_MIDI } void SND_CALL SND_fn_vReleaseDriverMidi() { #ifndef DISABLE_MIDI HANDLE hEventSynchro; if (stMidiDriver.bMidiDriverOk && stActiveMidiTrack.bActive) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_RELEASEMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIStop,"PostThreadMessage Release"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); } #endif //DISABLE_MIDI } void SND_CALL SND_fn_vRestoreDriverMidiThread() { #ifndef DISABLE_MIDI if (stActiveMidiTrack.bPaused == (BOOL)PAUSED_BY_RESTORE_RELEASE) SND_fn_vResumeMidiThread(stActiveMidiTrack.lVoice); #endif //DISABLE_MIDI } void SND_CALL SND_fn_vRestoreDriverMidi() { #ifndef DISABLE_MIDI HANDLE hEventSynchro; if (stMidiDriver.bMidiDriverOk && stActiveMidiTrack.bActive) { hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL); if (!PostThreadMessage(dwMidiThreadId,(UINT)SND_RESTOREMIDI_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro)) SND_fn_vDisplayError(E_uwSndMIDIStop,"PostThreadMessage Restore"); WaitForSingleObject(hEventSynchro,INFINITE); CloseHandle(hEventSynchro); } #endif //DISABLE_MIDI } //--------------------------------------------------------- // 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 voie) { return SND_C_POS_UNKNOWN; } //--------------------------------------------------------- // GetLength: retourne la durée "théorique" de la ressource // exprimé en seconde (constant pour une ressource donnée) // Entrée: voie concernée // Sortie: durée en S (SndReal) //---------------------------------------------------------- SndReal SND_CALL SND_fn_rGetLengthMidi(long voie) { return SND_C_POS_UNKNOWN; } //------------------------------------------------- // Fonction de synchro moteur // ne sert à rien pour Midi //------------------------------------------------- void SND_CALL SND_fn_vSynchroMidi() { } //------------------------------------------------------ // Fonctions pour thems NON IMPLEMENTéES //------------------------------------------------------ static tduRefRes s_CurrentMidiThemeRes={0}; static long s_lCurrentMidiThemeVoice; static tduRefRes s_NextMidiThemeRes={0}; static SND_td_pfn_vSoundCallback s_pfnThemeTransitionCB; static long s_lThemeTransitionCBParam; static SndBool s_bChangingTheme=FALSE; void SND_CALL fn_MidiThemeTransition(long par) { // A midi theme part just ended (either normally, or it was ended). // s_bChangingTheme is true when a transition is forced. if (s_bChangingTheme==TRUE) { s_bChangingTheme=FALSE; s_lCurrentMidiThemeVoice= SND_fn_lPlayMidi(s_CurrentMidiThemeRes,(SoundParam *)par,0,fn_MidiThemeTransition,par); if (s_NextMidiThemeRes.pstPtr==NULL) s_pfnThemeTransitionCB(s_lThemeTransitionCBParam); return; } s_CurrentMidiThemeRes=s_NextMidiThemeRes; if (s_CurrentMidiThemeRes.pstPtr!=NULL) { s_lCurrentMidiThemeVoice= SND_fn_lPlayMidi(s_CurrentMidiThemeRes,(SoundParam *)par,0,fn_MidiThemeTransition,par); s_pfnThemeTransitionCB(s_lThemeTransitionCBParam); } } long SND_CALL SND_fn_lPlayTransitionMidi(tduRefRes FirstRes,tduRefRes NextRes,SND_td_pfn_vSoundCallback transition_callback,long param_callback,SoundParam* par) { if (s_CurrentMidiThemeRes.pstPtr!=NULL) s_bChangingTheme=TRUE; else s_bChangingTheme=FALSE; s_CurrentMidiThemeRes=FirstRes; s_NextMidiThemeRes=NextRes; s_pfnThemeTransitionCB=transition_callback; s_lThemeTransitionCBParam=param_callback; // If a theme is already playing, stopping the current part will trigger the midi CB, // and start the next part. // Otherwise, we must launch the first part... if (s_bChangingTheme==TRUE) SND_fn_vStopMidi(s_lCurrentMidiThemeVoice); // stop current midi part else { s_lCurrentMidiThemeVoice= SND_fn_lPlayMidi(s_CurrentMidiThemeRes,par,0,fn_MidiThemeTransition,(long)par); } return s_lCurrentMidiThemeVoice; } SndBool SND_CALL SND_fn_bSetParamTransitionMidi(long voice,SoundParam* par) { return FALSE; } SndBool SND_CALL SND_fn_bSetNextTransitionMidi(long voice,tduRefRes new_res) { s_NextMidiThemeRes=new_res; return TRUE; } void SND_CALL SND_fn_vStopTransitionMidi(long voice) { s_NextMidiThemeRes.pstPtr=NULL; if (s_CurrentMidiThemeRes.pstPtr!=NULL) SND_fn_vStopMidi(s_lCurrentMidiThemeVoice); // stop current midi part } void SND_CALL SND_fn_vResumeTransitionMidi(long voice) { if (s_CurrentMidiThemeRes.pstPtr!=NULL) SND_fn_vResumeMidi(s_lCurrentMidiThemeVoice); } void SND_CALL SND_fn_vPauseTransitionMidi(long voice) { if (s_CurrentMidiThemeRes.pstPtr!=NULL) SND_fn_vPauseMidi(s_lCurrentMidiThemeVoice); } SndBool SND_CALL SND_fn_bDoTransitionWithFadeMidi(long voice,tduRefRes new_res) { s_bChangingTheme=TRUE; s_CurrentMidiThemeRes=new_res; s_NextMidiThemeRes.pstPtr=NULL; SND_fn_vStopMidi(s_lCurrentMidiThemeVoice); // stop current midi part return TRUE; } SndBool SND_CALL SND_fn_bDoTransitionWithFadeMidi2( long voice, tduRefRes new_res, SndReal rDuration ) { s_bChangingTheme=TRUE; s_CurrentMidiThemeRes=new_res; s_NextMidiThemeRes.pstPtr=NULL; SND_fn_vStopMidi(s_lCurrentMidiThemeVoice); // stop current midi part return TRUE; } SndBool SND_CALL SND_fn_bCheckVersionResourceMidi(tdstBlockResourceDisk* disk) { return TRUE; } SndBool SND_CALL SND_fn_bSetResourceStaticVolumeMidi(tdstBlockResourceMem* pstRes,unsigned char ucVolume) { pstRes->ucVolume=ucVolume; return TRUE; }