#define INITGUID #include #include #include #include #define DWORD_PTR DWORD * #include #include "types.h" #include "zvuk.h" #include extern "C" { #include } #define MAXCHANNELS 32 #define TIMEIDLESTOPCHANNEL 250 #define DEFAULTBUFFERSIZE (256*1024) #define FADELENGTH 3000 #define MUS_ERRORWAIT 5000 #include "Music.h" static int cur_device=0; static int cur_mixfreq=0; static bool swap_chans=false; class CheckRes { public: HRESULT operator=(HRESULT other) { if (other==0) return 0; char buff[256]; sprintf(buff,"DirectSound error HRESULT %08X, code %d",other,other & 0xFFFF); int id=MessageBox(NULL,buff,NULL,MB_SYSTEMMODAL|MB_ABORTRETRYIGNORE); if (id==IDRETRY) { __asm int 3; return other; } if (id==IDIGNORE) return other; exit(-1); } }; static CheckRes hres; static HWND hWndDS8=NULL; static IDirectSound8 *ds8=NULL; static IDirectSoundBuffer *ds_primary; static WAVEFORMATEX waveformat; static int gfx_volume=255; static int music_volume=127; static int glob_volume=255; static int linvoltable[256]; static MusicPlayer GMusicPlayer; static MusDecoder GMusDecoder; static WinAmpDecoder GWinAmpPlayer; static IDecoder *GCurrentDecoder=&GMusDecoder; class SoundChannelInfo { public: CRITICAL_SECTION sect; char *play_pos, *start_loop, *end_loop; IDirectSoundBuffer8 *buffer; uint32_t chantype; uint32_t idsPlayPos; uint32_t idsWritePos; uint32_t idsBuffSize; int32_t volume; //volume for DS int32_t pan; //pan for DS int volleft; //left volume for skeldal int volright; //right volume for skeldal uint32_t preload; uint32_t stopTime; //time, when buffer reached end. For calculating idle time uint32_t startTime; //time, when buffer started. For calculating idle time, use 0 to sticky buffer SoundChannelInfo() { buffer=NULL; play_pos=start_loop=end_loop=NULL; InitializeCriticalSection(§); pan=DSBPAN_CENTER; volume=DSBVOLUME_MAX; } ~SoundChannelInfo() {if (buffer) buffer->Release();DeleteCriticalSection(§);} uint32_t CalculateLockSize(); void ChannelMaintaince(); bool IsPlaying() {return play_pos!=NULL;} // bool IsFree(char type) {return play_pos==NULL || (type==chantype && play_pos==end_loop);} void Reset(); void InitChannel(char type, char *ppos, char *bloop,char *eloop, int freq); void Lock() {EnterCriticalSection(§);} void Unlock() {LeaveCriticalSection(§);} bool TryLock() {return TryEnterCriticalSection(§)!=FALSE;} void SetVolume(int32_t vol) { Lock(); if (volume!=vol) if (buffer && play_pos ) hres=buffer->SetVolume(vol); volume=vol; Unlock(); } void SetPan(int32_t p) { Lock(); if (pan!=p) if (buffer && play_pos ) hres=buffer->SetPan(p); pan=p; Unlock(); } void Mute() { Lock(); Stop(); Unlock(); } void Stop() { if (buffer) buffer->Stop(); play_pos=NULL; } void BreakLoop() {start_loop=end_loop;} void BreakLoopEx(char *end_sample) {start_loop=end_loop=end_sample;} }; static bool shutMaintaince=false; static SoundChannelInfo channels[MAXCHANNELS]; static HANDLE maintainceThread; ULONG __stdcall MaintainceThread(void *); static void DSStart(int mixfreq) { DSBUFFERDESC desc; for (int i=1;i<256;i++) { double rang=pow(i/255.0,15); linvoltable[i]=100*log(rang); } linvoltable[0]=-10000; hres=DirectSoundCreate8(&DSDEVID_DefaultPlayback,&ds8,NULL); if (hWndDS8) hres=ds8->SetCooperativeLevel(hWndDS8,DSSCL_PRIORITY); memset(&desc,0,sizeof(desc)); desc.dwSize=sizeof(desc); desc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRLVOLUME; hres=ds8->CreateSoundBuffer(&desc,&ds_primary,NULL); waveformat.cbSize=sizeof(waveformat); waveformat.nBlockAlign=4; waveformat.nChannels=2; waveformat.nSamplesPerSec=mixfreq; waveformat.nAvgBytesPerSec=mixfreq*4; waveformat.wBitsPerSample=16; waveformat.wFormatTag=WAVE_FORMAT_PCM; hres=ds_primary->SetFormat(&waveformat); DWORD id; shutMaintaince=false; maintainceThread=CreateThread(NULL,0,MaintainceThread,NULL,0,&id); SetThreadPriority(maintainceThread,THREAD_PRIORITY_HIGHEST); GMusicPlayer.InitBuffer(ds8,linvoltable); GMusicPlayer.Play(); } void init_winamp_plugins(const char *path) { GWinAmpPlayer.LoadPlugins(path); } static void DSStop() { GCurrentDecoder->Stop(); GMusDecoder.Stop(); GMusicPlayer.Done(); GWinAmpPlayer.ClearList(); shutMaintaince=true; WaitForSingleObject(maintainceThread,INFINITE); CloseHandle(maintainceThread); for (int i=0;iRelease();channels[i].buffer=NULL;} if (ds_primary) ds_primary->Release(); ds_primary=NULL; if (ds8) ds8->Release(); ds8=NULL; } uint32_t SoundChannelInfo::CalculateLockSize() { if (buffer==NULL) return 0; uint32_t playpos; buffer->GetCurrentPosition(NULL,&playpos); int32_t diff=(signed)playpos-(signed)idsPlayPos; if (diff<0) diff+=idsBuffSize; uint32_t wendpos=playpos+preload; if (wendpos>=idsBuffSize) wendpos-=idsBuffSize; if (wendposplaypos) return 0; if (wendposidsBuffSize/2) sz=idsBuffSize/2; idsPlayPos=playpos; return sz; } void SoundChannelInfo::ChannelMaintaince() { Lock(); if (play_pos!=NULL) { if (play_pos!=end_loop) stopTime=GetTickCount(); uint32_t lockSize=CalculateLockSize(); if (lockSize) { // printf("%8d\r",lockSize); void *audioptrs[2]; uint32_t sizes[2]; hres=buffer->Lock(idsWritePos,lockSize,audioptrs,sizes,audioptrs+1,sizes+1,0); for (int i=0;i<2 && audioptrs[i];i++) { char *wrt=(char *)audioptrs[i]; for (uint32_t j=0;jUnlock(audioptrs[0],sizes[0],audioptrs[1],sizes[1]); idsWritePos+=lockSize; if (idsWritePos>=idsBuffSize) idsWritePos-=idsBuffSize; } if (play_pos==end_loop && GetTickCount()-stopTime>TIMEIDLESTOPCHANNEL) { Stop(); } } Unlock(); } static void MixMaintaince() { for (int i=0;iRelease(); buffer=NULL; Unlock(); } void SoundChannelInfo::InitChannel(char type, char *ppos, char *bloop,char *eloop, int freq) { Lock(); uint32_t newchantype=type+freq*4; if (chantype!=newchantype || buffer==NULL) { Reset(); DSBUFFERDESC desc; WAVEFORMATEX wfex; wfex.cbSize=sizeof(wfex); wfex.wFormatTag=WAVE_FORMAT_PCM; wfex.wBitsPerSample=type==1?8:16; wfex.nSamplesPerSec=freq; wfex.nChannels=1; wfex.nBlockAlign=type==1?1:2; wfex.nAvgBytesPerSec=wfex.nBlockAlign*wfex.nSamplesPerSec; memset(&desc,0,sizeof(desc)); desc.dwSize=sizeof(desc); desc.dwFlags=DSBCAPS_CTRLFREQUENCY|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME|DSBCAPS_GETCURRENTPOSITION2; desc.dwBufferBytes=DEFAULTBUFFERSIZE; desc.lpwfxFormat=&wfex; preload=wfex.nAvgBytesPerSec/5; IDirectSoundBuffer *bufold; hres=ds8->CreateSoundBuffer(&desc,&bufold,NULL); hres=bufold->QueryInterface(IID_IDirectSoundBuffer8,(void **)&buffer); bufold->Release(); idsPlayPos=DEFAULTBUFFERSIZE-cur_mixfreq; idsWritePos=0; idsBuffSize=DEFAULTBUFFERSIZE; play_pos=ppos; start_loop=bloop; end_loop=eloop; chantype=newchantype; hres=buffer->SetVolume(volume); hres=buffer->SetPan(pan); ChannelMaintaince(); hres=buffer->Play(0,0,DSBPLAY_LOOPING); stopTime=startTime=GetTickCount(); } else { buffer->Stop(); idsBuffSize=DEFAULTBUFFERSIZE; buffer->SetCurrentPosition(0); buffer->GetCurrentPosition(&idsPlayPos,&idsWritePos); hres=buffer->SetVolume(volume); hres=buffer->SetPan(pan); play_pos=ppos; start_loop=bloop; end_loop=eloop; ChannelMaintaince(); buffer->Play(0,0,DSBPLAY_LOOPING); stopTime=startTime=GetTickCount(); } Unlock(); } /* static int FindFreeChannel(char type) { int older=0; DWORD m_age=0; DWORD timval=GetTickCount(); for (int i=0;im_age) {older=i;m_age=age;} } return older; } */ extern "C" { int bvolume; //background volume void (*konec_skladby)(char **jmeno)=NULL; //pointer to function to notify that end of song has been reached void DSReportWindowCreation(HWND hWindow) { hWndDS8=hWindow; if (ds8!=NULL) hres=ds8->SetCooperativeLevel(hWndDS8,DSSCL_PRIORITY); } int sound_detect(int *dev,int *port,int *dma, int *irq) { *dev=DEV_DIRECTSOUND; *port=0; *dma=0; *irq=0; return 0; } void set_mixing_device(int mix_dev,int mix_freq,...) { cur_device=mix_dev; cur_mixfreq=mix_freq; } char start_mixing() { if (cur_device!=DEV_DIRECTSOUND) { MessageBox(hWndDS8,"Invalid sound device! Check SKELDAL.INI. Only device 9 (DirectSound) can be used.",NULL,MB_SYSTEMMODAL); exit(1); } if (cur_mixfreq==0) return FALSE; DSStart(cur_mixfreq); return TRUE; } void stop_mixing() { DSStop(); } void play_sample(int channel,void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type) { char *start=(char *)sample; channels[channel].InitChannel(type,start,start+lstart,start+size,sfreq); } void set_channel_volume(int channel,int left,int right) { if (left>32767) left=32767; if (left<0) left=0; if (right>32767) right=32767; if (right<0) right=0; int volleft=linvoltable[(left>>7)*gfx_volume/255]; int volright=linvoltable[(right>>7)*gfx_volume/255]; int volsum=__max(volleft,volright); channels[channel].SetVolume(volsum); channels[channel].SetPan(swap_chans?(volright-volleft):(volleft-volright)); channels[channel].volleft=left; channels[channel].volright=right; } char get_channel_state(int channel) { return channels[channel].IsPlaying()==true; } void get_channel_volume(int channel,int *left,int *right) { if (left) *left=channels[channel].volleft; if (right) *right=channels[channel].volright; } void mute_channel(int channel) { channels[channel].Mute(); } void chan_break_loop(int channel) { channels[channel].BreakLoop(); } void chan_break_ext(int channel,void *org_sample,int32_t size_sample) //zrusi loop s moznosti dohrat zvu { char *end_sample=(char *)org_sample+size_sample; channels[channel].BreakLoopEx(end_sample); } char set_snd_effect(int funct,int data) { switch (funct) { case SND_PING: return 1; case SND_SWAP: swap_chans=(data & 1)!=0;return 1; case SND_GFX: gfx_volume=data;return 1; case SND_MUSIC: GCurrentDecoder->SetVolume(music_volume=data,get_snd_effect(SND_GVOLUME));return 1; case SND_GVOLUME: hres=ds_primary->SetVolume(linvoltable[glob_volume=data]);return 1; default: return 0; } } int get_snd_effect(int funct) { switch (funct) { case SND_PING: return 1; case SND_SWAP: return swap_chans; case SND_GFX: return gfx_volume; case SND_MUSIC: return music_volume; case SND_GVOLUME: return glob_volume; default: return 0; } } char check_snd_effect(int funct) { switch (funct) { case SND_PING: case SND_SWAP: case SND_MUSIC: case SND_GVOLUME: case SND_GFX: return 1; default: return 0; } } static DWORD Mus_buffSize; /* static HANDLE music_file=NULL; static HANDLE next_music_file=NULL; static bool fading=false; static DWORD fadetime; static DWORD Mus_lastWritePos; static DWORD Mus_nextBlockSize; static DWORD Mus_nextBlockRead; static DWORD Mus_errorWait=0; static DWORD Mus_silentPlay=0; #pragma pack (1) struct MusFile { short channels; int32_t freq; int32_t ssize; int32_t blocks; int32_t reserved1; int32_t reserved2; short ampltable[256]; }; static MusFile curMus; #pragma pack() static char OpenMus(HANDLE music_file) { DWORD bytesread; SetFilePointer(music_file,0,NULL,FILE_BEGIN); if (ReadFile(music_file,&curMus,sizeof(curMus),&bytesread,NULL)==TRUE && bytesread==sizeof(curMus)) { if (ReadFile(music_file,&Mus_nextBlockRead,sizeof(Mus_nextBlockRead),&bytesread,NULL)==FALSE) return 0; if (ReadFile(music_file,&Mus_nextBlockSize,sizeof(Mus_nextBlockSize),&bytesread,NULL)==FALSE) return 0; if (ds_music!=NULL) ds_music->Release(); WAVEFORMATEX wfex; wfex.cbSize=sizeof(wfex); wfex.wBitsPerSample=16; wfex.nBlockAlign=2*curMus.channels; wfex.nSamplesPerSec=curMus.freq; wfex.nAvgBytesPerSec=curMus.freq*wfex.nBlockAlign; wfex.wFormatTag=WAVE_FORMAT_PCM; wfex.nChannels=curMus.channels; DSBUFFERDESC desc; desc.dwSize=sizeof(desc); desc.dwBufferBytes=Mus_buffSize=wfex.nAvgBytesPerSec*4; desc.dwReserved=0; desc.dwFlags=DSBCAPS_CTRLVOLUME ; desc.lpwfxFormat=&wfex; IDirectSoundBuffer *dsbf; hres=ds8->CreateSoundBuffer(&desc,&dsbf,NULL); hres=dsbf->QueryInterface(IID_IDirectSoundBuffer8,(void **)&ds_music); dsbf->Release(); void *ptr; DWORD size; ds_music->Lock(0,0,&ptr,&size,NULL,NULL,DSBLOCK_ENTIREBUFFER); memset(ptr,0,size); ds_music->Unlock(ptr,size,NULL,NULL); ds_music->SetVolume(linvoltable[music_volume]); ds_music->Play(0,0,DSBPLAY_LOOPING); fadetime=0; Mus_lastWritePos=wfex.nAvgBytesPerSec; Mus_silentPlay=0; return 1; } return 0; } static void PrepareMusFile(const char *filename) { CloseHandle(next_music_file); next_music_file=CreateFile(filename,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (next_music_file==INVALID_HANDLE_VALUE) next_music_file=NULL; } static char music_decompres_block() { DWORD bytesread; char *data=(char *)alloca(Mus_nextBlockRead); short accum[2]={0,0}; char curchan=0; DWORD lockSizes[2]; void *lockPtrs[2]; if (ReadFile(music_file,data,Mus_nextBlockRead,&bytesread,NULL)==FALSE) return 0; ds_music->Lock(Mus_lastWritePos,Mus_nextBlockSize,lockPtrs,lockSizes,lockPtrs+1,lockSizes+1,0); for (int j=0;j<2;j++) { short *target=(short *)lockPtrs[j]; for (DWORD i=0;i=curMus.channels) curchan=0; } } ds_music->Unlock(lockPtrs[0],lockSizes[0],lockPtrs[1],lockSizes[1]); Mus_lastWritePos+=Mus_nextBlockSize; if (Mus_lastWritePos>=Mus_buffSize) Mus_lastWritePos-=Mus_buffSize; return 1; } static char music_silent_play() { DWORD lockSizes[2]; void *lockPtrs[2]; ds_music->Lock(Mus_lastWritePos,Mus_nextBlockSize,lockPtrs,lockSizes,lockPtrs+1,lockSizes+1,0); for (int j=0;j<2;j++) { short *target=(short *)lockPtrs[j]; for (DWORD i=0;iUnlock(lockPtrs[0],lockSizes[0],lockPtrs[1],lockSizes[1]); Mus_lastWritePos+=Mus_nextBlockSize; if (Mus_lastWritePos>=Mus_buffSize) Mus_lastWritePos-=Mus_buffSize; return 1; } void fade_music() { if (fadetime==0) fadetime=music_file?GetTickCount():0; } static int mix_back_sound_worker() { DWORD bytesread; if (music_file==NULL) return -2; if (ds_music==NULL) if (OpenMus(music_file)==0) return -2; DWORD play; ds_music->GetCurrentPosition(&play,NULL); while (true) { if (Mus_lastWritePos<=play) { if (Mus_lastWritePos+Mus_nextBlockSize>=play) return 0; } else { if (Mus_lastWritePos+Mus_nextBlockSize>play+Mus_buffSize) return 0; } if (Mus_silentPlay) { if (Mus_silentPlay>GetTickCount()) { music_silent_play(); return 0; } else return -2; } if (music_decompres_block()==0) return -2; curMus.blocks--; if (curMus.blocks<0) { Mus_silentPlay=GetTickCount()+4000; return 0; } if (ReadFile(music_file,&Mus_nextBlockRead,sizeof(Mus_nextBlockRead),&bytesread,NULL)==FALSE) return -2; if (ReadFile(music_file,&Mus_nextBlockSize,sizeof(Mus_nextBlockSize),&bytesread,NULL)==FALSE) return -2; if (fadetime && fadetime+FADELENGTHIsPlaying()) return 0; char *next_music; konec_skladby(&next_music); change_music(next_music); return 0; } void change_music(const char *mus_filename) { if (GCurrentDecoder->NotUsingOutput()) GMusDecoder.Stop(); GCurrentDecoder->Stop(); if (mus_filename==0) { mus_filename="?"; GCurrentDecoder=&GMusDecoder; } else { const char *c=strrchr(mus_filename,'.'); if (c!=0 && stricmp(c,".mus")==0) GCurrentDecoder=&GMusDecoder; else GCurrentDecoder=&GWinAmpPlayer; } GCurrentDecoder->AttachOutput(&GMusicPlayer); if (GCurrentDecoder->Play(mus_filename)==false) change_music(0); GCurrentDecoder->SetVolume(music_volume,get_snd_effect(SND_GVOLUME)); if (GCurrentDecoder->NotUsingOutput()) GMusDecoder.Play("?"); } int get_timer_value() { return GetTickCount()/TIMERSPEED } char *device_name(int device) { if (device!=DEV_DIRECTSOUND) return "Unknown device!"; else return "DirectSound 8"; } void force_music_volume(int volume) { } void set_backsnd_freq(int bfreq) { } void *PrepareVideoSound(int mixfreq, int buffsize) { WAVEFORMATEX wfex; wfex.cbSize=sizeof(wfex); wfex.wBitsPerSample=16; wfex.nBlockAlign=4; wfex.nSamplesPerSec=mixfreq; wfex.nAvgBytesPerSec=mixfreq*wfex.nBlockAlign; wfex.wFormatTag=WAVE_FORMAT_PCM; wfex.nChannels=2; DSBUFFERDESC desc; desc.dwSize=sizeof(desc); desc.dwBufferBytes=Mus_buffSize=buffsize; desc.dwReserved=0; desc.dwFlags=DSBCAPS_CTRLVOLUME ; desc.lpwfxFormat=&wfex; IDirectSoundBuffer *dsbf; IDirectSoundBuffer8 *ds_music; hres=ds8->CreateSoundBuffer(&desc,&dsbf,NULL); hres=dsbf->QueryInterface(IID_IDirectSoundBuffer8,(void **)&ds_music); dsbf->Release(); void *ptr; DWORD size; ds_music->Lock(0,0,&ptr,&size,NULL,NULL,DSBLOCK_ENTIREBUFFER); memset(ptr,0,size); ds_music->Unlock(ptr,size,NULL,NULL); ds_music->SetVolume(0); ds_music->Play(0,0,DSBPLAY_LOOPING); return (void *)ds_music; } char LoadNextVideoFrame(void *buffer, char *data, int size, short *xlat,short *accnums, int32_t *writepos) { IDirectSoundBuffer8 *ds_music=(IDirectSoundBuffer8 *)buffer; DSBCAPS caps; caps.dwSize=sizeof(caps); ds_music->GetCaps(&caps); DWORD play; ds_music->GetCurrentPosition(&play,NULL); int32_t remain=play-*writepos; if (remain<0) remain+=caps.dwBufferBytes; if (remainLock(*writepos,size*2,lockPtrs,lockSizes,lockPtrs+1,lockSizes+1,0); for (int j=0;j<2;j++) { short *target=(short *)lockPtrs[j]; for (DWORD i=0;i32767) {accnums[curchan]-=val-32768;val=32767;} if (val<-32767) {accnums[curchan]-=val+32768;val=-32767;} accnums[curchan]=val; /* if (data[-1]==0) //pridano jako provizorni reseni pro korekci chyby komprimacniho programu { val-=31767; }*/ *target++=val; curchan++; if (curchan>=2) curchan=0; } } ds_music->Unlock(lockPtrs[0],lockSizes[0],lockPtrs[1],lockSizes[1]); writepos[0]+=size*2; if (writepos[0]>caps.dwBufferBytes) writepos[0]-=caps.dwBufferBytes; return 1; } void DoneVideoSound(void *buffer) { IDirectSoundBuffer8 *ds_music=(IDirectSoundBuffer8 *)buffer; ds_music->Stop(); ds_music->Release(); } }