/* ////////////////////////////////////////////////////////////// // SNDTRANS.C // ////////////////////////////////////////////////////////////// // Module d'exploitation des THEMEs PCM // ////////////////////////////////////////////////////////////// */ #include "SNDinc.h" #include "sndres.h" #ifndef DISABLE_THEME_WAVE #if defined(_DLL_COMPILATION_MODE) #include "snddll.h" #endif #include "sndxd.h" #include "sndthrd.h" #ifndef NO_ACP_SOUND typedef struct TRANSITION_BUFFER_ *pTRANSITION_BUFFER; typedef struct TRANSITION_VOICE_ *pTRANSITION_VOICE; SNDLST2_M_DynamicUseListOf(pTRANSITION_BUFFER); SNDLST2_M_DynamicUseListOf(pTRANSITION_VOICE); typedef struct TRANSITION_BUFFER_{ void* ptrData; unsigned long ulNbSamples; long lId; SNDLST2_M_DynamicElementDeclaration(pTRANSITION_BUFFER) } TRANSITION_BUFFER; typedef struct TRANSITION_VOICE_{ long lIdBufferClient; SndBool_field_decl(bEnded);/*TRUE quand la voie doit s'arreter*/ SndBool_field_decl(bFading); /*TRUE si transition avec Fade en cours*/ long lSampleToBytesShift;/*nb de bits par echantillon*/ tdhSndFile hFile; long lLastPushed; /*id du dernier data-buffer*/ unsigned long ulByteMax; /*offset de dernier octet dans le fichier*/ unsigned long ulNextPtr; /*offset du prochain octet à lire dans le fichier*/ tduRefRes uCurRes; /*current resource*/ tduRefRes uNextRes; /*next ressource*/ tduRefRes uAfterFadeRes; /*resource to be played after fade*/ unsigned long ulCoefFade; /*coef du fade en cours*/ long lCoefShift; SND_td_pfn_vSoundCallback pfnCallback; long lParCallback; SNDLST2_M_DynamicAnchorDeclaration(pTRANSITION_BUFFER) pList; SNDLST2_M_DynamicElementDeclaration(pTRANSITION_VOICE) } TRANSITION_VOICE; typedef struct LISTE_TRANSITION_VOICE_{ SNDLST2_M_DynamicAnchorDeclaration(pTRANSITION_VOICE) pList; } LISTE_TRANSITION_VOICE; static LISTE_TRANSITION_VOICE ListTransition={NULL,NULL,0}; static SndBool bInitDone=FALSE; #define SIZE_DATABUFFER 5120 #define NB_BUFFER_AVANCE 3 #define M_FADE_DURATION_WAVE ( 1 << 16 ) HANDLE hMutexSndTrans=NULL; void SND_fn_vEnterCriticalSectionThreadSndTrans() { DWORD retWait; if (hMutexSndTrans) if ((retWait=WaitForSingleObject(hMutexSndTrans,INFINITE))==WAIT_TIMEOUT) /*DEADLOCK !!!!!!!!!!!!!!!!!!!!*/ DebugBreak(); /*DEADLOCK !!!!!!!!!!!!!!!!!!!!*/ } void SND_fn_vQuitCriticalSectionThreadSndTrans() { if (hMutexSndTrans) ReleaseMutex(hMutexSndTrans); } /*------------*/ void purge_databuffer_transition(pTRANSITION_BUFFER databuffer) { SNDLST2_M_DynamicIsolate(databuffer); SND_fn_vFreeSnd(databuffer->ptrData); SND_fn_vFreeSndEx(E_ucSndBlockMain,databuffer); } void push_databuffer_transition(pTRANSITION_VOICE transition) { pTRANSITION_BUFFER databuffer; unsigned long nb_lus,nb_to_read,nb_left,nb_wished; SND_tdstStackBuffer stackbuffer; char filename[256]; SndBool bFadeToDo=FALSE; int i; long lTmp,lTmp1,lTmp2; if (!transition->bEnded) { databuffer=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(TRANSITION_BUFFER)); SNDLST2_M_DynamicInitElement(databuffer); databuffer->ulNbSamples=SIZE_DATABUFFER>>transition->lSampleToBytesShift; databuffer->ptrData=SND_fn_pvMallocSnd(SIZE_DATABUFFER); nb_to_read=SIZE_DATABUFFER; nb_left=nb_to_read; while (nb_left>0) {/*tant que le buffer n'est pas plein*/ if ((transition->hFile==OPENFILESND_FAILED) && (transition->uNextRes.pstPtr==SND_C_RES_FANTOME_PTR)) {/*lack of data (no data to read)*/ memset(databuffer->ptrData,0,nb_left); nb_left=0; } else { if (transition->hFile==OPENFILESND_FAILED) {/*theme has been re-started*/ transition->uCurRes.pstPtr=transition->uNextRes.pstPtr; transition->uNextRes.pstPtr=SND_C_RES_FANTOME_PTR; transition->ulByteMax=transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.ulOffsetLast; transition->ulNextPtr=transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.ulOffsetFirst; (*transition->pfnCallback)(transition->lParCallback); SND_fn_vResolveFileName(transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.szFileName,filename); transition->hFile=SND_fn_hOpenFileReadSnd(filename); SND_fn_dwSeekFileSnd(transition->hFile,transition->ulNextPtr,SEEKFILESND_BEGIN); } nb_wished=min(nb_to_read,transition->ulByteMax-transition->ulNextPtr); if (transition->bFading) { nb_wished=min(nb_wished,transition->ulCoefFade); bFadeToDo=TRUE; } if ((nb_lus=SND_fn_dwReadFileSnd(transition->hFile ,nb_wished ,databuffer->ptrData)) != nb_to_read) {/*on est arrive à la fin du fichier ou du Fade*/ SND_fn_vCloseFileSnd(transition->hFile); transition->hFile=OPENFILESND_FAILED; if ((transition->bFading) && (nb_lus==transition->ulCoefFade)) {/*fade is finished*/ transition->uNextRes.pstPtr=transition->uAfterFadeRes.pstPtr; transition->uAfterFadeRes.pstPtr=NULL; } if (transition->uNextRes.pstPtr!=SND_C_RES_FANTOME_PTR) { transition->uCurRes.pstPtr=transition->uNextRes.pstPtr; transition->uNextRes.pstPtr=SND_C_RES_FANTOME_PTR; transition->ulByteMax=transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.ulOffsetLast; transition->ulNextPtr=transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.ulOffsetFirst; SND_fn_vResolveFileName(transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.szFileName,filename); transition->hFile=SND_fn_hOpenFileReadSnd(filename); SND_fn_dwSeekFileSnd(transition->hFile,transition->ulNextPtr,SEEKFILESND_BEGIN); }/*if (transition->uNextRes)*/ else { //databuffer->ulNbSamples=(nb_to_read-nb_left)>>transition->lSampleToBytesShift; //transition->lLastPushed=databuffer->lId; /*no more data to read*/ memset((char*)databuffer->ptrData+nb_lus,0,nb_left-nb_lus); nb_left=0; } /*user's callback*/ if (!(transition->bFading) || (nb_lus==transition->ulCoefFade)) { (*transition->pfnCallback)(transition->lParCallback); transition->bFading=FALSE; } }/*if ((nb_lus=SND_fn_dwReadFileSnd*/ else {/*on n'est pas encore à la fin du fichier*/ transition->ulNextPtr+=nb_to_read; } if (bFadeToDo) {/*fade*/ for (i=0;i<(long)(databuffer->ulNbSamples<lSampleToBytesShift)/4;i++) { lTmp=*((long*)databuffer->ptrData+i); lTmp1=(lTmp<<16)>>16; lTmp2=lTmp>>16; lTmp1*=transition->ulCoefFade; lTmp2*=transition->ulCoefFade; lTmp1>>=transition->lCoefShift; lTmp2>>=transition->lCoefShift; lTmp=((lTmp2<<16)&0xFFFF0000) | (lTmp1 & 0xFFFF); *((long*)databuffer->ptrData+i)=lTmp; if (transition->uCurRes.pstPtr->uRes.stSample.uwNbChannels==2) transition->ulCoefFade-=2; else transition->ulCoefFade--; if (!transition->ulCoefFade) break; } } nb_left=nb_to_read-nb_lus; } }/*while*/ SNDLST2_M_DynamicAddTail(&transition->pList,databuffer); stackbuffer.ulNbSamples=databuffer->ulNbSamples; stackbuffer.ptrData=databuffer->ptrData; databuffer->lId=SND_fn_lPushBufferExSxd(transition->lIdBufferClient,&stackbuffer); } } void SND_CALL TransitionCallback(long user_id,long poped_id) { pTRANSITION_VOICE transition; pTRANSITION_BUFFER databuffer; int i; SndBool found=FALSE; SND_fn_vEnterCriticalSectionThreadSndTrans(); transition=(pTRANSITION_VOICE)user_id; databuffer=(pTRANSITION_BUFFER)poped_id; SNDLST2_M_DynamicForEachElementOf(&transition->pList,databuffer,i) { if (databuffer->lId==poped_id) { purge_databuffer_transition(databuffer); found=TRUE; break; } } snd_assert(found); push_databuffer_transition(transition); SND_fn_vQuitCriticalSectionThreadSndTrans(); } void purge_transition(pTRANSITION_VOICE transition) { pTRANSITION_BUFFER pDataBuffer,pDataBufferNext; int i; snd_assert(transition->bEnded); SND_fn_vDeleteBufferSxd(transition->lIdBufferClient); transition->lIdBufferClient=C_PLAY_FAILED; if (transition->hFile!=OPENFILESND_FAILED) SND_fn_vCloseFileSnd(transition->hFile); transition->hFile=OPENFILESND_FAILED; SNDLST2_M_DynamicForEachMovingElementOf(&transition->pList,pDataBuffer,pDataBufferNext,i) { purge_databuffer_transition(pDataBuffer); } SNDLST2_M_DynamicIsolate(transition); SND_fn_vFreeSndEx(E_ucSndBlockMain,transition); } /*---themes*/ /*-------------------------------------------------------------------------------- PlayTransition:lance une melodie dynamique (suite continue de samples s'enchainant Entrees:FirstRes=1° ressource à jouee immediatement NextRes=ressource devant s'enchainer à la fin de la 1° sauf contre-ordre par SetNextTrtansition callback=fonction a appeler chaque fois qu'une tranistion à lieu (c.a.d. qu'en on vodrait savoir quel sera le prochain element à jouee apres celui qui vient d'etre lance) Sortie:ID de la voie (pour les fonctions suivantes) -------------------------------------------------------------------------------------*/ long SND_fn_lPlayTransitionSxdWave(tduRefRes FirstRes,tduRefRes NextRes,SND_td_pfn_vSoundCallback transition_callback,long param_callback,SoundParam* par) { pTRANSITION_VOICE transition; SND_tdstFormat format; SND_tdstCallback callback; char filename[256]; int i; snd_assert(FirstRes.pstPtr->uRes.stSample.bStream); snd_assert(NextRes.pstPtr->uRes.stSample.bStream); if (!FirstRes.pstPtr->uRes.stSample.bStream || !NextRes.pstPtr->uRes.stSample.bStream) return C_PLAY_FAILED; SND_fn_vEnterCriticalSectionThreadSndTrans(); transition=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(TRANSITION_VOICE)); SNDLST2_M_DynamicInitElement(transition); SNDLST2_M_DynamicAddTail(&ListTransition.pList,transition); SNDLST2_M_DynamicInitAnchor(&transition->pList); /* * Format of the client buffer */ format.eZip = SAMPLE_PCM; format.uFormat.stPCM.ulNbSamples = 0; format.uFormat.stPCM.uwResolution = FirstRes.pstPtr->uRes.stSample.uwResolution; format.uFormat.stPCM.uwNbChannels = FirstRes.pstPtr->uRes.stSample.uwNbChannels; format.uFormat.stPCM.ulFreq = FirstRes.pstPtr->uRes.stSample.ulFreq; /* * Callback parameters */ callback.eType = BUFFER_STACK; callback.uCallback.CallbackStack = TransitionCallback; callback.uInfo.lNbSampleToPush = 0; /* * Open and affect the theme voice */ transition->pfnCallback = transition_callback; transition->lParCallback = param_callback; transition->lLastPushed = C_PLAY_FAILED; transition->lIdBufferClient = SND_fn_lCreateNewBufferExSxd( &format, &callback, par, (long)transition ); SND_fn_vResolveFileName( FirstRes.pstPtr->uRes.stSample.uData.stStream.szFileName, filename ); transition->uNextRes.pstPtr = NextRes.pstPtr; transition->uCurRes.pstPtr = FirstRes.pstPtr; transition->ulByteMax = transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.ulOffsetLast; transition->ulNextPtr = transition->uCurRes.pstPtr->uRes.stSample.uData.stStream.ulOffsetFirst; transition->bFading = FALSE; transition->hFile = SND_fn_hOpenFileReadSnd( filename ); transition->bEnded = FALSE; transition->lSampleToBytesShift = format.uFormat.stPCM.uwResolution/8 + format.uFormat.stPCM.uwNbChannels - 2; SND_fn_dwSeekFileSnd( transition->hFile, transition->ulNextPtr, SEEKFILESND_BEGIN ); /*lancement des 1° push*/ for (i=0;ibEnded) { purge_transition(transition); bRet=FALSE; } else { SND_fn_vSetParamBufferSxd(transition->lIdBufferClient,par); bRet=TRUE; } } SND_fn_vQuitCriticalSectionThreadSndTrans(); return bRet; } /*-------------------------------------------------------------------------------- SetNextTransition:Modifie la ressource devant s’enchainer à la fin de la ressource en cours de restitution ; l’effet de cette fonction ne se fera donc sentir qu’à la fin de la ressource courante. De plus, si plusieurs appels ont lieu durant la restitution de la meme ressource, le dernier annule les effets des precedents. Entrees:voice=voie concernee par la remise à jour new_res=nouvelle ressource à lancer par defaut à la fin de celle en cours Retour:TRUE si OK, FALSE si voie morte (plus d'autre SetNextTransition necessaire) -----------------------------------------------------------------------------------*/ SndBool SND_fn_bSetNextTransitionSxdWave(long voice,tduRefRes new_res) { pTRANSITION_VOICE transition; snd_assert(new_res.pstPtr->uRes.stSample.bStream); if (!new_res.pstPtr->uRes.stSample.bStream) return FALSE; SND_fn_vEnterCriticalSectionThreadSndTrans(); transition=(pTRANSITION_VOICE)voice; transition->uNextRes.pstPtr=new_res.pstPtr; SND_fn_vQuitCriticalSectionThreadSndTrans(); return TRUE; } SndBool SND_fn_bDoTransitionWithFadeSxdWave2( long voice, tduRefRes new_res, SndReal rDuration ) { pTRANSITION_VOICE transition; int cmpt; snd_assert( new_res.pstPtr->uRes.stSample.bStream ); if( !new_res.pstPtr->uRes.stSample.bStream ) return FALSE; SND_fn_vEnterCriticalSectionThreadSndTrans(); transition=(pTRANSITION_VOICE)voice; transition->uAfterFadeRes.pstPtr=new_res.pstPtr; if (!transition->bFading) { transition->bFading=TRUE; #if 0 transition->ulCoefFade=transition->uCurRes.pstPtr->uRes.stSample.ulFreq;/*1 sec de fade*/ #endif transition->ulCoefFade = M_RealToIntSnd( M_MulIntRealSnd( rDuration, transition->uCurRes.pstPtr->uRes.stSample.ulFreq ) ); cmpt=0; while (transition->ulCoefFade>0) { cmpt++;transition->ulCoefFade>>=1; } transition->ulCoefFade=1< transition->lCoefFade=coef de mixage*/ transition->lCoefShift=cmpt; } SND_fn_vQuitCriticalSectionThreadSndTrans(); return FALSE; } SndBool SND_fn_bDoTransitionWithFadeSxdWave( long voice, tduRefRes new_res ) { return SND_fn_bDoTransitionWithFadeSxdWave2( voice, new_res, M_FADE_DURATION_WAVE ); } /*-------------------------------------------------------------------------------- StopTransition:Stop une melodie/theme en cours Entrees:voice=voie devant etre stoppee -----------------------------------------------------------------------------------*/ void SND_fn_vStopTransitionSxdWave(long voice) { pTRANSITION_VOICE transition; SND_fn_vEnterCriticalSectionThreadSndTrans(); if (voice!=C_PLAY_FAILED) { transition=(pTRANSITION_VOICE)voice; transition->bEnded=TRUE; purge_transition(transition); } SND_fn_vQuitCriticalSectionThreadSndTrans(); } void SND_fn_vPauseTransitionSxdWave(long voice) { pTRANSITION_VOICE transition; SND_fn_vEnterCriticalSectionThreadSndTrans(); transition=(pTRANSITION_VOICE)voice; SND_fn_vPauseBufferSxd(transition->lIdBufferClient); SND_fn_vQuitCriticalSectionThreadSndTrans(); } void SND_fn_vResumeTransitionSxdWave(long voice) { pTRANSITION_VOICE transition; SND_fn_vEnterCriticalSectionThreadSndTrans(); transition=(pTRANSITION_VOICE)voice; SND_fn_vResumeBufferSxd(transition->lIdBufferClient); SND_fn_vQuitCriticalSectionThreadSndTrans(); } int SND_fn_iInitTransitionSxdWave(SND_tdstInitStruct *pInitStruct) { hMutexSndTrans=CreateMutex(NULL,FALSE,NULL); if (hMutexSndTrans) return C_INIT_OK; else return C_INIT_FAILED; } #endif /*NO_ACP_SOUND*/ #endif /* DISABLE_THEME_WAVE*/