#include "skeldal_win.h" #include #include #include #define DWORD_PTR DWORD * #include #include "types.h" #include "zvuk.h" #include #include extern "C" { #include } #include "Music.h" #define BUFFER_SIZE (512*1024) #define LOCK_GRANUALITY 16384 MusicPlayer::MusicPlayer() { _ds8Buffer=0; _volume=255; InitializeCriticalSection(&_lock); } MusicPlayer::~MusicPlayer() { Done(); DeleteCriticalSection(&_lock); } HRESULT MusicPlayer::InitBuffer(IDirectSound8 *ds8, int *linvoltable) { HRESULT hres; WAVEFORMATEX wfex; wfex.cbSize=sizeof(wfex); wfex.wBitsPerSample=16; wfex.nBlockAlign=4; wfex.nSamplesPerSec=44100; wfex.nAvgBytesPerSec=wfex.nSamplesPerSec*wfex.nBlockAlign; wfex.wFormatTag=WAVE_FORMAT_PCM; wfex.nChannels=2; DSBUFFERDESC desc; desc.dwSize=sizeof(desc); desc.dwBufferBytes=BUFFER_SIZE; desc.dwReserved=0; desc.dwFlags=DSBCAPS_CTRLVOLUME ; desc.lpwfxFormat=&wfex; IDirectSoundBuffer *dsbf; hres=ds8->CreateSoundBuffer(&desc,&dsbf,NULL); hres=dsbf->QueryInterface(IID_IDirectSoundBuffer8,(void **)&_ds8Buffer); dsbf->Release(); _linvoltable=linvoltable; return hres; } HRESULT MusicPlayer::Play() { _lastWritePos=0; DWORD size; void *ptr; _ds8Buffer->Lock(0,0,&ptr,&size,NULL,NULL,DSBLOCK_ENTIREBUFFER); memset(ptr,0,size); _ds8Buffer->Unlock(ptr,size,NULL,NULL); _ds8Buffer->SetVolume(_linvoltable[_volume]); HRESULT res=_ds8Buffer->Play(0,0,DSBPLAY_LOOPING); _crossfadebytes=0; _minorpos=0; return res; } class AutoCloseCriticalSection { LPCRITICAL_SECTION lcrit; public: AutoCloseCriticalSection(LPCRITICAL_SECTION l):lcrit(l) { EnterCriticalSection(lcrit); } ~AutoCloseCriticalSection() { LeaveCriticalSection(lcrit); } }; int MusicPlayer::Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms) { AutoCloseCriticalSection lsect(&_lock); if (numchannels<1 || numchannels>2) return -1; if (bitspersamp!=8 && bitspersamp!=16) return -1; _stereo=numchannels==2; _bit16=bitspersamp==16; _speed=samplerate*1024/44100; if (_speed<128) return -1; _opened=true; return 0; } HRESULT MusicPlayer::Done() { if (_ds8Buffer) _ds8Buffer->Release(); _ds8Buffer=0; return 0; } DWORD MusicPlayer::GetSafeXFadePos() { DWORD curPos; _ds8Buffer->GetCurrentPosition(0,&curPos); int curpos=curPos; if (curpos>(int)_lastWritePos) curpos-=BUFFER_SIZE; curpos+=BUFFER_SIZE/4; if (curpos>(int)_lastWritePos) curpos=_lastWritePos; if (curpos<0) curpos+=BUFFER_SIZE; if (curpos>=BUFFER_SIZE) curpos-=BUFFER_SIZE; return curpos; } static const float Inv2=0.5; static const float Snapper=3<<22; static inline short toInt( float fval ) { fval += Snapper; return (short)( (*(int *)&fval)&0x007fffff ) - 0x00400000; } void MusicPlayer::Close() { if (TryEnterCriticalSection(&_lock)==FALSE) { DWORD status; _ds8Buffer->GetStatus(&status); _ds8Buffer->Play(0,0,DSBPLAY_LOOPING); EnterCriticalSection(&_lock); if ((status & DSBSTATUS_PLAYING)==0) _ds8Buffer->Stop(); } if (_crossfadebytes==0) { DWORD xfadepos=GetSafeXFadePos(); DWORD xfadesz=xfadepos>_lastWritePos?(_lastWritePos+BUFFER_SIZE-xfadepos):(_lastWritePos-xfadepos); void *ptr[2]; DWORD sz[2]; float ffadesz=(float)xfadesz*0.5f; float ffadecnt=0; if (xfadesz && _ds8Buffer->Lock(xfadepos,xfadesz,ptr+0,sz+0,ptr+1,sz+1,0)==0) { for (int i=0;i<2;i++) if (ptr[i]) { for (DWORD j=0;jffadesz) ffadecnt=ffadesz; } } _ds8Buffer->Unlock(ptr[0],sz[0],ptr[1],sz[1]); } _crossfadebytes=xfadesz; _lastWritePos=xfadepos; } _opened=false; LeaveCriticalSection(&_lock); } int MusicPlayer::Write(const char *buf, int len) { EnterCriticalSection(&_lock); if (!_opened) { LeaveCriticalSection(&_lock); return 1; } DWORD step=(_stereo?2:1) * (_bit16?2:1); void *lockptr[2]={0,0}; DWORD locksz[2]={0,0}; void *wrtptr=0; DWORD remainspace=0; int stage=0; while (len>0) { short sample[2]; if (_bit16) if (_stereo) { sample[0]=*((short *)buf); sample[1]=*((short *)buf+1); } else { sample[0]=*((short *)buf); sample[1]=*((short *)buf); } else if (_stereo) { sample[0]=(*buf)*256; sample[1]=(*(buf+1))*256; } else { sample[0]=(*buf)*256; sample[1]=(*buf)*256; } while (remainspace<4) { if (stage<1) { stage++; remainspace=locksz[stage]; wrtptr=lockptr[stage]; } else { if (lockptr[0]) { _ds8Buffer->Unlock(lockptr[0],locksz[0],lockptr[1],locksz[1]); _lastWritePos+=LOCK_GRANUALITY; while (_lastWritePos>=BUFFER_SIZE) _lastWritePos-=BUFFER_SIZE; } DWORD playCursor; _ds8Buffer->GetCurrentPosition(&playCursor,0); if (playCursor<_lastWritePos) playCursor+=BUFFER_SIZE; while (playCursor-_lastWritePosGetCurrentPosition(&playCursor,0); if (playCursor<_lastWritePos) playCursor+=BUFFER_SIZE; } HRESULT res=_ds8Buffer->Lock(_lastWritePos,LOCK_GRANUALITY,lockptr+0,locksz+0,lockptr+1,locksz+1,0); assert(res==0); stage=0; remainspace=locksz[stage]; wrtptr=lockptr[stage]; } } if (_crossfadebytes) { short *data=(short *)wrtptr; long a=data[0]+sample[0]; if (a<-32767) a=-32767; if (a>32767) a=32767; data[0]=(short)a; a=data[1]+sample[1]; if (a<-32767) a=-32767; if (a>32767) a=32767; data[1]=(short)a; if (_crossfadebytes<4) _crossfadebytes=0;else _crossfadebytes-=4; } else memcpy(wrtptr,sample,4); wrtptr=(void *)((char *)wrtptr+4); remainspace-=4; _minorpos+=_speed; while (_minorpos>=1024) { buf+=step; len-=step; _minorpos-=1024; } } if (stage==0) {locksz[1]=0;locksz[0]-=remainspace;} else {locksz[1]-=remainspace;} _ds8Buffer->Unlock(lockptr[0],locksz[0],lockptr[1],locksz[1]); _lastWritePos+=locksz[0]+locksz[1]; while (_lastWritePos>=BUFFER_SIZE) _lastWritePos-=BUFFER_SIZE; LeaveCriticalSection(&_lock); return 0; } int MusicPlayer::CanWrite() { return LOCK_GRANUALITY; } int MusicPlayer::IsPlaying() { return 0; } int MusicPlayer::Pause(int pause) { int lastState=_paused?1:0; if (pause) {if (!_paused) {_ds8Buffer->Stop();_paused=true;}} else {if (_paused) {_ds8Buffer->Play(0,0,DSBPLAY_LOOPING);_paused=false;}} return lastState; } void MusicPlayer::SetVolume(int volume) { if (volume<0) return; _ds8Buffer->SetVolume(_linvoltable[volume]); if (volume==0) Pause(1); else Pause(0); } MusDecoder::MusDecoder() { _playing=false; _thread=0; _stop=0; } MusDecoder::~MusDecoder() { Stop(); } void MusDecoder::AttachOutput(IWAOutput *o) { if (o) o->AddRef(); if (_output) _output->Release(); _output=o; } bool MusDecoder::Play(const char *filename) { DWORD res=0; if (filename[0]=='?') { if (_output->Open(44100,1,8,-1,-1)<0) { return false; } _stop=false; _playing=true; _thread=CreateThread(0,0,MusDecoder::StartSilentWritter,this,0,&res); return true; } _file=CreateFile(filename,GENERIC_READ,0,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0); if (_file==0) return false; ReadFile(_file,&_header,sizeof(_header),&res,0); if (res!=sizeof(_header)) {CloseHandle(_file);return false;} if (_output->Open(_header.freq,_header.channels,16,-1,-1)<0) { CloseHandle(_file);return false; } _stop=false; _playing=true; _thread=CreateThread(0,0,MusDecoder::StartMusDecoder,this,0,&res); return true; } void MusDecoder::Stop() { if (_thread) { _stop=true; _output->Pause(0); WaitForSingleObject(_thread,INFINITE); CloseHandle(_thread); CloseHandle(_file); _output->Close(); _file=0; _thread=0; } } DWORD WINAPI MusDecoder::StartMusDecoder(LPVOID data) { MusDecoder *self=reinterpret_cast(data); return self->MusDecodingThread(); } UINT MusDecoder::MusDecodingThread() { long blocksRemain=_header.blocks; char *packbuf=0; short *unpackbuf=0; long packbufsz=0; long unpackbufsz=0; for (int i=0;i=_header.channels) channel=0; unpackbuf[i]=val; } _output->Write(reinterpret_cast(unpackbuf),unpackedSize); } _playing=false; return 0; } DWORD WINAPI MusDecoder::StartSilentWritter(LPVOID data) { MusDecoder *self=reinterpret_cast(data); return self->SilentWritterThread(); } UINT MusDecoder::SilentWritterThread() { char empty[1024]; memset(empty,0,sizeof(empty)); for (int i=0;i<1024;i++) { if (_stop) break; _output->Write(empty,1024); } _playing=false; return 0; } WinAmpDecoder::WinAmpDecoder() { _currPlugin=0; } WinAmpDecoder::~WinAmpDecoder() { Stop(); } void WinAmpDecoder::AttachOutput(IWAOutput *o) { if (o) o->AddRef(); if (_output) _output->Release(); _output=o; } bool WinAmpDecoder::Play(const char *filename) { Stop(); _currPlugin=SelectBestPlugin(filename); if (_currPlugin==0) return false; _currPlugin->AttachOutput(_output); if (_currPlugin->Play(filename)!=_currPlugin->errOk) { _currPlugin=0; return false; } int playtm=_currPlugin->GetOutputTime(); int nexttm=playtm; int c=0; MusicPlayer *q=static_cast(_output); while (!q->IsOpenned() && playtm==nexttm && c<2000) {Sleep(1);c++;nexttm=_currPlugin->GetOutputTime();} _nooutput=!q->IsOpenned(); return true; } void WinAmpDecoder::Stop() { if (_currPlugin==0) return; _currPlugin->Stop(); _currPlugin->AttachOutput(0); _currPlugin=0; } bool WinAmpDecoder::IsPlaying() { if (_currPlugin==0) return false; return !_currPlugin->IsFinished(); } void WinAmpDecoder::SetVolume(int volume, int main) { _currPlugin->SetVolume(volume); }