gates_of_skeldal/Windows/Music.cpp
2025-01-24 18:27:22 +01:00

507 lines
No EOL
11 KiB
C++

#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);
}