mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-07 07:00:35 -04:00
github publish
This commit is contained in:
commit
506e23bf32
542 changed files with 120675 additions and 0 deletions
507
Windows/Music.cpp
Normal file
507
Windows/Music.cpp
Normal file
|
@ -0,0 +1,507 @@
|
|||
#include "skeldal_win.h"
|
||||
#include <malloc.h>
|
||||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#define DWORD_PTR DWORD *
|
||||
#include <dsound.h>
|
||||
#include "types.h"
|
||||
#include "zvuk.h"
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
extern "C" {
|
||||
#include <memman.h>
|
||||
}
|
||||
#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;j<sz[i];j+=2)
|
||||
{
|
||||
short *sample=(short *)((char *)ptr[i]+j);
|
||||
sample[0]=toInt(sample[0]-sample[0]*ffadecnt/ffadesz);
|
||||
ffadecnt=ffadecnt+1.0f;
|
||||
if (ffadecnt>ffadesz) 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-_lastWritePos<LOCK_GRANUALITY)
|
||||
{
|
||||
Sleep(200);
|
||||
_ds8Buffer->GetCurrentPosition(&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<MusDecoder *>(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<blocksRemain;i++)
|
||||
{
|
||||
if (_stop) break;
|
||||
long packedSize;
|
||||
long unpackedSize;
|
||||
DWORD bytesread;
|
||||
if (ReadFile(_file,&packedSize,sizeof(packedSize),&bytesread,NULL)==FALSE) break;
|
||||
if (ReadFile(_file,&unpackedSize,sizeof(unpackedSize),&bytesread,NULL)==FALSE) break;
|
||||
if (packbufsz<packedSize)
|
||||
{
|
||||
free(packbuf);
|
||||
packbuf=(char *)malloc(packedSize);
|
||||
packbufsz=packedSize;
|
||||
}
|
||||
if (unpackbufsz<unpackedSize || unpackbuf==0)
|
||||
{
|
||||
free(unpackbuf);
|
||||
unpackbuf=(short *)malloc(unpackedSize);
|
||||
unpackbufsz=unpackedSize;
|
||||
}
|
||||
if (ReadFile(_file,packbuf,packedSize,&bytesread,0)==FALSE) break;
|
||||
if (packedSize!=bytesread) break;
|
||||
short accum[2]={0,0};
|
||||
int channel=0;
|
||||
for (int i=0;i<packedSize;i++)
|
||||
{
|
||||
short val=accum[channel]+_header.ampltable[packbuf[i]];
|
||||
accum[channel]=val;
|
||||
if (packbuf[i]==0) //pridano jako provizorni reseni pro korekci chyby komprimacniho programu
|
||||
{
|
||||
val-=31767;
|
||||
}
|
||||
channel++;
|
||||
if (channel>=_header.channels) channel=0;
|
||||
unpackbuf[i]=val;
|
||||
}
|
||||
_output->Write(reinterpret_cast<char *>(unpackbuf),unpackedSize);
|
||||
}
|
||||
_playing=false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI MusDecoder::StartSilentWritter(LPVOID data)
|
||||
{
|
||||
MusDecoder *self=reinterpret_cast<MusDecoder *>(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<MusicPlayer *>(_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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue