#include "SNDinc.h" #include "sndthrd.h" #if defined(_DLL_COMPILATION_MODE) #include "snddll.h" #endif #include "sndxd.h" #include "sndres.h" #include "sndstrm.h" typedef struct STREAMING_BUFFER_ *pSTREAMING_BUFFER; typedef struct STREAMING_VOICE_ *pSTREAMING_VOICE; SNDLST2_M_DynamicUseListOf(pSTREAMING_BUFFER); SNDLST2_M_DynamicUseListOf(pSTREAMING_VOICE); typedef struct STREAMING_BUFFER_{ void* ptrData; unsigned long ulNbSamples; long lId; SNDLST2_M_DynamicElementDeclaration(pSTREAMING_BUFFER) } STREAMING_BUFFER; typedef struct STREAMING_VOICE_{ long lIdBufferClient; long lSampleToBytesShift; tdhSndFile hFile; long lLastPushed; //id du dernier data-buffer SndBool bEnding; //TRUE quand il n'y a plus de données à pusher SndBool bEnded; //TRUE quand la lectre est finie SndBool bMustLoop; //TRUE si res loopante unsigned long ulByteLoop; //offset du point de rebouclage dans le fichier unsigned long ulByteMax; //offset de dernier octet dans le fichier unsigned long ulNextPtr; //offset du prochain octet à lire dans le fichier SND_td_pfn_vSoundCallback pfnCallback; long lParCallback; SndBool bWaitingCallback; SndBool bStereo; SNDLST2_M_DynamicAnchorDeclaration(pSTREAMING_BUFFER) pList; SNDLST2_M_DynamicElementDeclaration(pSTREAMING_VOICE) } STREAMING_VOICE; typedef struct LISTE_STREAMING_VOICE_{ SNDLST2_M_DynamicAnchorDeclaration(pSTREAMING_VOICE) pList; } LISTE_STREAMING_VOICE; static LISTE_STREAMING_VOICE ListStreaming; static SndBool bInitDone=FALSE; #define SIZE_DATABUFFER 5120 #define NB_BUFFER_AVANCE 4 //------------ void purge_databuffer(pSTREAMING_BUFFER databuffer) { SNDLST2_M_DynamicIsolate(databuffer); SND_fn_vFreeSnd(databuffer->ptrData); SND_fn_vFreeSndEx(E_ucSndBlockMain,databuffer); } void push_databuffer(pSTREAMING_VOICE streaming) { pSTREAMING_BUFFER databuffer; unsigned long nb_lus,nb_to_read,nb_left; SND_tdstStackBuffer stackbuffer; if (!streaming->bEnding) { databuffer=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(STREAMING_BUFFER)); SNDLST2_M_DynamicInitElement(databuffer); databuffer->ulNbSamples=SIZE_DATABUFFER>>streaming->lSampleToBytesShift; databuffer->ptrData=SND_fn_pvMallocSnd(SIZE_DATABUFFER); nb_to_read=SIZE_DATABUFFER; if ((nb_lus=SND_fn_dwReadFileSnd(streaming->hFile ,min(nb_to_read,streaming->ulByteMax-streaming->ulNextPtr) ,databuffer->ptrData)) != nb_to_read) {//on est arrivé à la fin du fichier if (streaming->bMustLoop) { streaming->ulNextPtr=streaming->ulByteLoop; SND_fn_dwSeekFileSnd(streaming->hFile,streaming->ulNextPtr,SEEKFILESND_BEGIN); nb_left=nb_to_read-nb_lus; while (nb_left>0) {//reboucler tant que le buffer n'est pas plein streaming->ulNextPtr=streaming->ulByteLoop; nb_lus=SND_fn_dwReadFileSnd(streaming->hFile ,min(nb_left,streaming->ulByteMax-streaming->ulNextPtr) ,(char*)databuffer->ptrData+nb_to_read-nb_left); nb_left-=nb_lus; if (!nb_lus) break; } streaming->ulNextPtr=streaming->ulByteLoop+nb_lus; } else { databuffer->ulNbSamples=nb_lus>>streaming->lSampleToBytesShift; streaming->lLastPushed=databuffer->lId; SND_fn_vCloseFileSnd(streaming->hFile); streaming->hFile=OPENFILESND_FAILED; streaming->bEnding=TRUE; } } else {//on n'est pas encore à la fin du fichier streaming->ulNextPtr+=nb_to_read; } SNDLST2_M_DynamicAddTail(&streaming->pList,databuffer); stackbuffer.ulNbSamples=databuffer->ulNbSamples; stackbuffer.ptrData=databuffer->ptrData; databuffer->lId=SND_fn_lPushBufferExSxd(streaming->lIdBufferClient,&stackbuffer); if (streaming->bEnding) streaming->lLastPushed=databuffer->lId; //_RPTF1(_CRT_WARN,"Push %d\n",databuffer->lId); } else { if (streaming->bWaitingCallback) { streaming->bWaitingCallback=FALSE; (*streaming->pfnCallback)(streaming->lParCallback); } } } void SND_CALL StreamingCallback(long user_id,long poped_id) { pSTREAMING_VOICE streaming; pSTREAMING_BUFFER databuffer; int i; SndBool found=FALSE; //_RPTF1(_CRT_WARN,"Callback %d\n",poped_id); SND_fn_vEnterCriticalSectionThreadSnd(); streaming=(pSTREAMING_VOICE)user_id; databuffer=(pSTREAMING_BUFFER)poped_id; SNDLST2_M_DynamicForEachElementOf(&streaming->pList,databuffer,i) { if (databuffer->lId==poped_id) { purge_databuffer(databuffer); found=TRUE; break; } } snd_assert(found); if (!streaming->bEnding) push_databuffer(streaming); else { if (streaming->lLastPushed==poped_id) streaming->bEnded=TRUE; } SND_fn_vQuitCriticalSectionThreadSnd(); } void purge_streaming(pSTREAMING_VOICE streaming) { pSTREAMING_BUFFER pDataBuffer,pDataBufferNext; int i; snd_assert(streaming->bEnded); SND_fn_vDeleteBufferSxd(streaming->lIdBufferClient); streaming->lIdBufferClient=C_PLAY_FAILED; if (streaming->hFile!=OPENFILESND_FAILED) SND_fn_vCloseFileSnd(streaming->hFile); streaming->hFile=OPENFILESND_FAILED; SNDLST2_M_DynamicForEachMovingElementOf(&streaming->pList,pDataBuffer,pDataBufferNext,i) { purge_databuffer(pDataBuffer); } if (streaming->bWaitingCallback) { streaming->bWaitingCallback=FALSE; (*streaming->pfnCallback)(streaming->lParCallback); } SNDLST2_M_DynamicIsolate(streaming); SND_fn_vFreeSndEx(E_ucSndBlockMain,streaming); } /// long SND_fn_lPlaySxdStream(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback) { pSTREAMING_VOICE streaming; SND_tdstFormat format; SND_tdstCallback callback; char filename[256]; int i; SoundParam *pstSoundParam; if (bInitDone) { streaming=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(STREAMING_VOICE)); SNDLST2_M_DynamicInitElement(streaming); SNDLST2_M_DynamicAddTail(&ListStreaming.pList,streaming); SNDLST2_M_DynamicInitAnchor(&streaming->pList); format.eZip=SAMPLE_PCM; format.uFormat.stPCM.ulNbSamples=0; format.uFormat.stPCM.uwResolution=res.pstPtr->uRes.stSample.uwResolution; format.uFormat.stPCM.uwNbChannels=res.pstPtr->uRes.stSample.uwNbChannels; format.uFormat.stPCM.ulFreq=res.pstPtr->uRes.stSample.ulFreq; callback.eType=BUFFER_STACK; callback.uCallback.CallbackStack=StreamingCallback; callback.uInfo.lNbSampleToPush=0; streaming->bMustLoop=res.pstPtr->uRes.stSample.bLoop; streaming->ulByteLoop=res.pstPtr->uRes.stSample.uData.stStream.ulOffsetLoop; streaming->ulByteMax=res.pstPtr->uRes.stSample.uData.stStream.ulOffsetLast; streaming->ulNextPtr=res.pstPtr->uRes.stSample.uData.stStream.ulOffsetFirst; SND_fn_vResolveFileName(res.pstPtr->uRes.stSample.uData.stStream.szFileName,filename); streaming->hFile=SND_fn_hOpenFileReadSnd(filename); SND_fn_dwSeekFileSnd(streaming->hFile,streaming->ulNextPtr,SEEKFILESND_BEGIN); pstSoundParam=SND_fn_pvMallocSnd(SND_M_lGetSizeOfSoundParam(par)); memcpy(pstSoundParam,par,SND_M_lGetSizeOfSoundParam(par)); if (format.uFormat.stPCM.uwNbChannels==2) { pstSoundParam->ucVol=par->ucVol/2; pstSoundParam->iFlags|=C_SOUNDPARAM_COEF_VOL; } if (format.uFormat.stPCM.uwNbChannels==2) streaming->bStereo=TRUE; else streaming->bStereo=FALSE; streaming->lLastPushed=C_PLAY_FAILED; streaming->bEnded=FALSE; streaming->bEnding=FALSE; streaming->lSampleToBytesShift=format.uFormat.stPCM.uwResolution/8+format.uFormat.stPCM.uwNbChannels-2; streaming->lIdBufferClient=SND_fn_lCreateNewBufferExSxd(&format,&callback,pstSoundParam,(long)streaming); SND_fn_vFreeSnd(pstSoundParam); if (fn_callback) { streaming->pfnCallback=fn_callback; streaming->lParCallback=par_callback; streaming->bWaitingCallback=TRUE; } else streaming->bWaitingCallback=FALSE; //lancement des 1° push for (i=0;iuRes.stSample.uData.stStream.szFileName,filename); buffer_dispo_streaming.hFile=SND_fn_hOpenFileReadSnd(filename); buffer_dispo_streaming.bFileOpened=TRUE; if (buffer_dispo_streaming.hFile==OPENFILESND_FAILED) { SND_fn_vDisplayError(E_uwSndCannotOpenFile,filename); return C_PLAY_FAILED; } SND_fn_dwSeekFileSnd(buffer_dispo_streaming.hFile,res.pstPtr->uRes.stSample.uData.stStream.ulOffsetFirst,SEEKFILESND_BEGIN); nb_to_read=2*SIZE_FILE_BUFFER; nb_lus=SND_fn_dwReadFileSnd(buffer_dispo_streaming.hFile,nb_to_read,hFileBuffer[0]); buffer_dispo_streaming.uRes.pstPtr=res.pstPtr; buffer_dispo_streaming.bMustLoop=res.pstPtr->uRes.stSample.bLoop; buffer_dispo_streaming.ulByteLoop=res.pstPtr->uRes.stSample.uData.stStream.ulOffsetLoop; buffer_dispo_streaming.ulByteMax=res.pstPtr->uRes.stSample.uData.stStream.ulOffsetLast; buffer_dispo_streaming.bStarting=TRUE; buffer_dispo_streaming.bStarted=TRUE; buffer_dispo_streaming.bIsEnding=FALSE; buffer_dispo_streaming.bIsLooping=FALSE; buffer_dispo_streaming.lNextPtr=2*SIZE_FILE_BUFFER; buffer_dispo_streaming.iNextFileBuffer=0; if (nb_lus!=nb_to_read) {//fin du sample if (buffer_dispo_streaming.bMustLoop) {//on reboucle nb_to_read-=nb_lus; while (nb_to_read!=0) { SND_fn_dwSeekFileSnd(buffer_dispo_streaming.hFile,buffer_dispo_streaming.ulByteLoop,SEEKFILESND_BEGIN); nb_lus=SND_fn_dwReadFileSnd(buffer_dispo_streaming.hFile,nb_to_read,hFileBuffer[buffer_dispo_streaming.iNextFileBuffer]); if (nb_lus!=nb_to_read) nb_to_read-=nb_lus; else nb_to_read=0; } buffer_dispo_streaming.bIsLooping=TRUE; buffer_dispo_streaming.lNextPtr=buffer_dispo_streaming.ulByteLoop+nb_lus; } else {//début de la fin (prématuré) // buffer_dispo_streaming.bStarting=FALSE; SND_fn_vCloseFileSnd(buffer_dispo_streaming.hFile); buffer_dispo_streaming.bFileOpened=FALSE; memset((char*)(hFileBuffer[buffer_dispo_streaming.iNextFileBuffer])+nb_lus,0,nb_to_read-nb_lus); buffer_dispo_streaming.bIsEnding=TRUE; buffer_dispo_streaming.lCmptEnding=2; if (nb_to_read-nb_lus>SIZE_FILE_BUFFER) //cas juste où le sample est assez petit (à peine partit, on arrêtre) buffer_dispo_streaming.lCmptEnding--; //_RPT0( _CRT_WARN,"bIsEnding mis à TRUE -"); } } if (fn_callback) { buffer_dispo_streaming.fn_callback=fn_callback; buffer_dispo_streaming.par_callback=par_callback; buffer_dispo_streaming.bWaitingCallback=TRUE; } if (fn_bCreateStream(res.pstPtr->uRes.stSample.uwResolution,res.pstPtr->uRes.stSample.uwNbChannels,res.pstPtr->uRes.stSample.ulFreq)) return VOICE_STREAMING; */ } return C_PLAY_FAILED; } void SND_fn_vRemoveCallbackSxdStream(long voice) { } SndBool SND_fn_bSetParamSxdStream(long voice,SoundParam *par) { pSTREAMING_VOICE streaming; SoundParam *pstSoundParam; if (voice==C_PLAY_FAILED) return FALSE; streaming=(pSTREAMING_VOICE)voice; if (streaming->bEnded) { purge_streaming(streaming); return FALSE; } else { pstSoundParam=SND_fn_pvMallocSnd(SND_M_lGetSizeOfSoundParam(par)); memcpy(pstSoundParam,par,SND_M_lGetSizeOfSoundParam(par)); if (streaming->bStereo) { pstSoundParam->ucVol=par->ucVol/2; pstSoundParam->iFlags|=C_SOUNDPARAM_COEF_VOL; } SND_fn_vSetParamBufferSxd(streaming->lIdBufferClient,pstSoundParam); SND_fn_vFreeSnd(pstSoundParam); return TRUE; } } SndBool SND_fn_bTestIsPlayingSxdStream(long voice) { pSTREAMING_VOICE streaming; if (voice==C_PLAY_FAILED) return FALSE; streaming=(pSTREAMING_VOICE)voice; if (streaming->bEnded) { purge_streaming(streaming); return FALSE; } else { return TRUE; } } void SND_fn_vStopSxdStream(long voice) { pSTREAMING_VOICE streaming; if (voice!=C_PLAY_FAILED) { streaming=(pSTREAMING_VOICE)voice; streaming->bEnded=TRUE; if (streaming->bWaitingCallback) { streaming->bWaitingCallback=FALSE; (*streaming->pfnCallback)(streaming->lParCallback); } purge_streaming(streaming); } } void SND_fn_vPauseSxdStream(long voice) { } void SND_fn_vResumeSxdStream(long voice) { } int SND_fn_iInitSxdStream(SND_tdstInitStruct *pInitStruct) { SNDLST2_M_DynamicInitAnchor(&ListStreaming.pList); bInitDone=TRUE; return C_INIT_OK; } SndBool SND_fn_bTestInitSxdStream(void) { return TRUE; } void SND_fn_vDesInitSxdStream(void) { pSTREAMING_VOICE pStreaming,pStreamingNext; int i; SNDLST2_M_DynamicForEachMovingElementOf(&ListStreaming.pList,pStreaming,pStreamingNext,i) { purge_streaming(pStreaming); } } //--------------------------------------------------------- // 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_fn_rGetPosSxdStream(long voie) { pSTREAMING_VOICE streaming; if (voie!=C_PLAY_FAILED) { streaming=(pSTREAMING_VOICE)voie; if (!streaming->bEnded) return SND_fn_rGetPosBufferSxd(streaming->lIdBufferClient); else return SND_C_POS_ENDED; } else 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_fn_rGetLengthSxdStream(long voie) { return SND_C_LENGTH_UNKNOWN; } //----------------------------------------------------------- // ConvertRsvDiskToMem: convertir in BlockResourceDisk en Mem // Entrées:ptrBegin=pointeur sur les données UTILES (jamias d'offset) //------------------------------------------------------------ void SND_fn_vConvertResDiskToMemSxdStream(tdstBlockResourceDisk *disk ,tdstBlockResourceMem *mem ,void* ptrBegin) { #ifndef DISABLE_WAVE snd_assert(disk->eType==TYPE_SAMPLE); snd_assert(disk->uRes.stSample.eZip==SAMPLE_PCM); snd_assert(disk->uRes.stSample.bStream); 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.stSample.eZip=SAMPLE_PCM; mem->uRes.stSample.bPitchable=disk->uRes.stSample.bPitchable; mem->uRes.stSample.bVolable=disk->uRes.stSample.bVolable; mem->uRes.stSample.bPanable=disk->uRes.stSample.bPanable; mem->uRes.stSample.bSpacable=disk->uRes.stSample.bSpacable; mem->uRes.stSample.bReverbable=disk->uRes.stSample.bReverbable; mem->uRes.stSample.bStream=disk->uRes.stSample.bStream; mem->uRes.stSample.bLoop=disk->uRes.stSample.bLoop; //mem->uRes.stSample.ulInc0=(disk->uRes.stSample.ulFreq<uRes.stSample.ulFreq=disk->uRes.stSample.ulFreq; mem->uRes.stSample.uwResolution=disk->uRes.stSample.uwResolution; mem->uRes.stSample.uwNbChannels=disk->uRes.stSample.uwNbChannels; strcpy(mem->uRes.stSample.uData.stStream.szFileName,disk->uRes.stSample.czFileName); mem->uRes.stSample.uData.stStream.ulOffsetFirst=disk->ulDataOffset; if (!disk->uRes.stSample.bLoop) { mem->uRes.stSample.uData.stStream.ulOffsetLoop=0; mem->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataSize+disk->ulDataOffset; } else { mem->uRes.stSample.uData.stStream.ulOffsetLoop=disk->ulDataOffset+disk->uRes.stSample.ulStartLoop; mem->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataOffset+disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength; } //le sample est prêt à l'emplois #endif } SndBool SND_fn_bLoadResScriptSxdStream(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem) { #ifndef DISABLE_WAVE snd_assert(disk->uRes.stSample.eZip!=SAMPLE_AIFF); snd_assert(disk->uRes.stSample.bStream); //stream sample cannot be AIIF type SND_fn_vConvertResDiskToMemSxdStream(disk,mem,NULL); //en script, chaque ressources charge ces propres data (on ne tient pas compte de eStorage) mem->eStorage=TYPE_EXTERNAL; return TRUE; #else return FALSE; #endif } SndBool SND_fn_bLoadResBinarySxdStream(tdstBlockResourceDisk *_pBRDisk,tdstBlockResourceMem *_pBRMem,char *pDataBloc) { #ifndef DISABLE_WAVE snd_assert(_pBRDisk->uRes.stSample.eZip!=SAMPLE_AIFF); snd_assert(_pBRDisk->uRes.stSample.bStream); //binary load is disabled for AIFF sample SND_fn_vConvertResDiskToMemSxdStream(_pBRDisk,_pBRMem,NULL); return TRUE; #else return FALSE; #endif } void SND_fn_vUnLoadResSxdStream(tdstBlockResourceMem* mem) { #ifndef DISABLE_WAVE snd_assert(mem->uRes.stSample.bStream); mem->uRes.stSample.uData.stMem.pvPtrFirst=NULL; mem->eType=TYPE_INVALID; #endif } SndBool SND_fn_bSetResourceStaticVolumeSxdStream(tdstBlockResourceMem* pstRes,unsigned char ucVolume) { #ifndef DISABLE_WAVE pstRes->ucVolume=ucVolume; return TRUE; #else return FALSE; #endif }