123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- #include <windows.h>
- #include "audiostub.h"
- #define CAPTION "NSV Player Sound Output Error"
- #define MAX(x,y) (( y ) < ( x ) ? ( x ) : ( y ))
- #define MIN(x,y) (( x ) < ( y ) ? ( x ) : ( y ))
- #define S_MINSIZE (1<<28)
- #define MAX_NUM_BLOCKS 8
- #define NUM_BLOCKS 8
- #define BUFSIZE_MS 1500
- #define BLOCKSIZE_MAX 32768
- #define BLOCKSIZE_MIN 8192
- int g_audio_use_mixer=0;
- class PCM_AudioOut : public IAudioOutput
- {
- public:
- PCM_AudioOut(int samplerate, int numchannels, int bitspersamp);
- ~PCM_AudioOut();
- int canwrite(); // returns bytes writeable
- void write(void *_buf, int len);
- unsigned int getpos();
- unsigned int getwritepos();
- void flush(unsigned int time_ms);
- void pause(int pause);
- int isplaying(void);
- void setvolume(int volume);
- void setpan(int pan);
- int open_success() { return init; }
- void getdescstr(char *buf)
- {
- *buf=0;
- if (g_srate && g_nch) wsprintf(buf,"%dkHz %s",g_srate/1000,g_nch==2?"stereo":"mono");
- }
- private:
- DWORD ThreadP();
- void _setvol(void);
- static DWORD WINAPI _threadproc(LPVOID p);
- static void CALLBACK cbFunc(HWAVEOUT hwo,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2)
- {
- if (uMsg == WOM_DONE) { ReleaseSemaphore((HANDLE)dwInstance,1,NULL);}
- }
- void do_set_blocksizes(void);
- void do_samples_altvol(char *in, int blen);
- int init;
- int min_blocksize;
- int max_blocksize;
- int ispcnt;
- int num_blocks;
- char *g_buffer, *g_buffer_write, *g_buffer_read;
- int g_buffer_length, g_buffer_valid,g_buffer_inlength;
- HWAVEOUT g_hWaveOut;
- WAVEHDR g_wave_headers[MAX_NUM_BLOCKS];
- int g_bps,g_nch,g_srate;
- volatile int g_pause, g_wavecnt, g_prebuf,g_writeall, g_quit_flag,g_writetime_bytes, g_outtime_bytes, g_outtime_interp;
- HANDLE g_hSem,g_hEvent, g_hThread;
- CRITICAL_SECTION g_cs;
- int g_bytes_per_sec;
- int a_v,a_p;
- unsigned char *g_vol_table;
- };
- PCM_AudioOut::PCM_AudioOut(int samplerate, int numchannels, int bitspersamp)
- {
- init=0;
- a_v=255;
- a_p=0;
- num_blocks=0;
- g_buffer_valid=g_buffer_inlength=0;
- g_hWaveOut=NULL;
- memset(g_wave_headers,0,sizeof(g_wave_headers));
- g_hSem=g_hEvent=g_hThread=0;
- int x;
- DWORD id;
- MMRESULT res;
- WAVEFORMATEX wfx={WAVE_FORMAT_PCM,numchannels,samplerate,samplerate*numchannels*(bitspersamp/8),
- numchannels*(bitspersamp/8),bitspersamp};
-
- g_bps=bitspersamp;
- g_nch=numchannels;
- g_srate=samplerate;
- g_bytes_per_sec=wfx.nAvgBytesPerSec;
- {
- char *p=(char*)g_wave_headers;
- int n=sizeof(g_wave_headers);
- while (n--) *p++=0;
- }
- g_buffer_length = MulDiv(g_bytes_per_sec, BUFSIZE_MS, 1000);
- g_buffer_length &= ~1023;
- if (g_buffer_length < 4096) g_buffer_length=4096;
- g_buffer=(char *)GlobalAlloc(GMEM_FIXED,g_buffer_length+min(65536,g_buffer_length));
- if (g_buffer == NULL)
- {
- MessageBox(NULL,"Error allocating buffer", CAPTION,MB_OK|MB_ICONSTOP);
- return;
- }
- g_prebuf=g_buffer_length/4;
- g_buffer_read=g_buffer_write=g_buffer;
- g_wavecnt=g_pause=g_writeall=g_buffer_valid=g_writetime_bytes=g_outtime_bytes=g_buffer_inlength=0;
- g_quit_flag=0;
-
- g_vol_table=NULL;
- do_set_blocksizes();
- g_hSem=CreateSemaphore(NULL,0,256,NULL);
- for (x = 0; (res=waveOutOpen(&g_hWaveOut,WAVE_MAPPER,&wfx,(DWORD)cbFunc,(DWORD)g_hSem,CALLBACK_FUNCTION))==MMSYSERR_ALLOCATED && x<10; x ++)
- Sleep(100);
- if (res != MMSYSERR_NOERROR)
- {
- char t[512];
- waveOutGetErrorText(res,t,sizeof(t));
- MessageBox(NULL,t, CAPTION,MB_OK|MB_ICONSTOP);
- GlobalFree((HGLOBAL) g_buffer);
- CloseHandle(g_hSem);
- g_buffer = NULL;
- return;
- }
- ispcnt=0;
- g_outtime_interp=GetTickCount();
- g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
- InitializeCriticalSection(&g_cs);
- g_hThread=CreateThread(NULL,0,_threadproc,(void*)this,0,&id);
- SetThreadPriority(g_hThread,THREAD_PRIORITY_HIGHEST);
- init=1;
- }
- void PCM_AudioOut::do_set_blocksizes(void)
- {
- int t,t2,t4;
- t=(MulDiv(BLOCKSIZE_MIN,g_bytes_per_sec,44100)+1023)&~1023;
- if (t<1024) t=1024;
- if (t>32768) t=32768;
- t2=(MulDiv(BLOCKSIZE_MAX,g_bytes_per_sec,44100*4)+1023)&~1023;
- if (t2 < t) t2 = t;
- if (t2 > 65536) t2=65536;
- t4 = NUM_BLOCKS;
- num_blocks=t4;
- max_blocksize=t2;
- min_blocksize=t;
- }
- PCM_AudioOut::~PCM_AudioOut(void)
- {
- if (init)
- {
- int x;
- g_quit_flag=1;
- SetEvent(g_hEvent);
- while (g_quit_flag == 1) Sleep(70);
- waveOutReset(g_hWaveOut);
- for (x = 0; x < MAX_NUM_BLOCKS; x++)
- if (g_wave_headers[x].dwFlags & WHDR_PREPARED)
- waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
- if (waveOutClose(g_hWaveOut) != MMSYSERR_NOERROR)
- {
- MessageBox(NULL,"Error closing sound device.",CAPTION,MB_OK);
- }
- if (g_buffer) GlobalFree((HGLOBAL) g_buffer);
- DeleteCriticalSection(&g_cs);
- CloseHandle(g_hThread);
- CloseHandle(g_hSem);
- CloseHandle(g_hEvent);
- if(g_vol_table) GlobalFree((HGLOBAL)g_vol_table);
- }
- }
- void PCM_AudioOut::write(void *_buf, int len)
- {
- char *buf=(char *)_buf;
- int l2;
- if (len > 8192) len=8192;
- l2=(g_buffer_write+len)-(g_buffer+g_buffer_length);
- if (len <= 0 || !buf)
- {
- g_writeall=1;
- //g_prebuf=0;
- SetEvent(g_hEvent);
- return;
- }
- ispcnt=0;
- g_writeall=0;
- if (l2 > 0)
- {
- int l1=len-l2;
- memcpy(g_buffer_write,buf,l1);
- memcpy(g_buffer,buf+l1,l2);
- g_buffer_write=g_buffer+l2;
- }
- else
- {
- memcpy(g_buffer_write,buf,len);
- g_buffer_write += len;
- if (g_buffer_write == g_buffer+g_buffer_length) g_buffer_write=g_buffer;
- }
- EnterCriticalSection(&g_cs);
- g_buffer_valid+=len;
- LeaveCriticalSection(&g_cs);
- g_writetime_bytes+=len;
- if (g_wavecnt < num_blocks)
- {
- SetEvent(g_hEvent);
- }
- return;
- }
- int PCM_AudioOut::canwrite(void)
- {
- int t=(g_buffer_length-g_buffer_valid);
- if (g_wavecnt==0)
- {
- SetEvent(g_hEvent);
- }
- if (t>8192) t=8192; // RG: since write() caps the # of bytes at 8192, this should reflect that! otherwise, if we call write() with too many bytes, it throws the extra away and we'll never know it.
- return t;
- }
- int PCM_AudioOut::isplaying(void)
- {
- if (g_wavecnt==0)
- {
- SetEvent(g_hEvent);
- }
- if (ispcnt < 7) ispcnt++;
- if (g_buffer_valid < MIN(g_buffer_length/2,min_blocksize) && ispcnt==7)
- {
- g_writeall=1;
- g_prebuf=0;
- }
- return (g_wavecnt>0) || (g_buffer_valid>0);
- }
- void PCM_AudioOut::pause(int pause)
- {
- if (g_hWaveOut)
- {
- int lastp=g_pause;
- g_pause=pause;
- if (pause == lastp) return;
- if (g_pause)
- waveOutPause(g_hWaveOut);
- else
- {
- waveOutRestart(g_hWaveOut);
- g_outtime_interp=GetTickCount();
- SetEvent(g_hEvent);
- }
- }
- }
- void PCM_AudioOut::_setvol(void)
- {
- DWORD vol, vo2, gv;
- if (g_hWaveOut)
- {
- a_v=MIN(255,MAX(a_v,0));
- a_p=MIN(127,MAX(a_p,-127));
- vo2 = vol = (a_v*65535) / 255;
- if (a_p > 0)
- {
- vol *= (127-a_p);
- vol /= 127;
- }
- else if (a_p < 0)
- {
- vo2 *= (127+a_p);
- vo2 /= 127;
- }
- gv=vol|(vo2<<16);
- if(g_audio_use_mixer) {
- if(g_vol_table) {
- EnterCriticalSection(&g_cs);
- GlobalFree((HGLOBAL)g_vol_table);
- g_vol_table=NULL;
- LeaveCriticalSection(&g_cs);
- }
- waveOutSetVolume(g_hWaveOut,gv);
- } else {
- EnterCriticalSection(&g_cs);
- if(!g_vol_table) {
- int l=(g_bps==8)?512:(4*32769);
- g_vol_table=(unsigned char *)GlobalAlloc(GPTR,l);
- }
- //compute volume lookup table
- int x;
- if (g_bps==8) {
- if (g_nch==1) for (x = 0; x < 256; x ++) g_vol_table[x] = (x*a_v)/256;
- else for (x = 0; x < 256; x ++)
- {
- g_vol_table[x] = (x*(int)vol)/65536;
- g_vol_table[x+256] = (x*(int)vo2)/65536;
- }
- } else {
- short *vol_tab16 = (short *)g_vol_table;
- if (g_nch==1) for (x = 0; x < 32769; x ++) vol_tab16[x] = -(x*a_v)/256;
- else for (x = 0; x < 32769; x ++)
- {
- vol_tab16[x] = -(x*(int)vol)/65536;
- vol_tab16[x+32769] = -(x*(int)vo2)/65536;
- }
- }
- LeaveCriticalSection(&g_cs);
- }
- }
- }
- void PCM_AudioOut::flush(unsigned int time_ms)
- {
- int x;
- EnterCriticalSection(&g_cs);
- g_outtime_bytes=g_writetime_bytes=MulDiv(time_ms,g_bytes_per_sec,1000);
- waveOutReset(g_hWaveOut);
- for (x = 0; x < MAX_NUM_BLOCKS; x++)
- if ((g_wave_headers[x].dwFlags & WHDR_PREPARED))
- {
- waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
- g_wave_headers[x].dwFlags =0;
- }
- while (WaitForSingleObject(g_hSem,0)==WAIT_OBJECT_0);
- g_prebuf=g_buffer_length/8;
- g_buffer_read=g_buffer_write=g_buffer;
- g_writeall=g_wavecnt=g_buffer_valid=g_buffer_inlength=0;
- ispcnt=0;
- LeaveCriticalSection(&g_cs);
- }
- unsigned int PCM_AudioOut::getwritepos(void)
- {
- return MulDiv(g_writetime_bytes,1000,g_bytes_per_sec);
- }
- unsigned int PCM_AudioOut::getpos(void)
- {
- unsigned int t;
- if (!g_pause)
- {
- t=GetTickCount()-g_outtime_interp;
- if (t > 1000) t=1000;
- }
- else t=0;
- return t+MulDiv(g_outtime_bytes,1000,g_bytes_per_sec);
- }
- DWORD WINAPI PCM_AudioOut::_threadproc(LPVOID p)
- {
- return ((PCM_AudioOut *)p)->ThreadP();
- }
- DWORD PCM_AudioOut::ThreadP()
- {
- HANDLE hs[2]={g_hSem,g_hEvent};
- while (1)
- {
- int i;
- i=WaitForMultipleObjects(2,hs,FALSE,INFINITE);
- if (g_quit_flag) break;
- if (i == WAIT_OBJECT_0)
- {
- int x;
- for (x = 0; x < MAX_NUM_BLOCKS && !(g_wave_headers[x].dwFlags & WHDR_DONE); x++);
- if (x < MAX_NUM_BLOCKS)
- {
- EnterCriticalSection(&g_cs);
- if (g_wave_headers[x].dwFlags & WHDR_DONE)
- {
- int r=g_wave_headers[x].dwBufferLength;
- g_outtime_interp=GetTickCount();
- g_buffer_valid -=r;
- g_buffer_inlength-=r;
- g_outtime_bytes +=r;
- waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
- g_wave_headers[x].dwFlags=0;
- g_wavecnt--;
- i++;
- }
- LeaveCriticalSection(&g_cs);
- }
- }
- if (i == WAIT_OBJECT_0+1 && !g_pause)
- {
- int l;
- int m;
- int t=num_blocks;
- EnterCriticalSection(&g_cs);
- l=g_buffer_valid-g_buffer_inlength;
- LeaveCriticalSection(&g_cs);
- again:
- if (g_quit_flag) break;
- if (g_writeall && l<max_blocksize)
- {
- ispcnt=0;
- g_writeall=0;
- m=1;
- }
- else
- {
- m=MAX(MIN(g_buffer_length/2,min_blocksize),g_prebuf);
- l&=~1023;
- }
- if (l >= m && g_wavecnt < t)
- {
- int x;
- if (l > max_blocksize) l=max_blocksize;
- for (x = 0; x < t && (g_wave_headers[x].dwFlags & WHDR_PREPARED); x++);
- if (x < t)
- {
- int ml=(g_buffer+g_buffer_length)-g_buffer_read;
- if (l > g_buffer_length) l=g_buffer_length;
- if (l>ml)
- {
- int addlen=l-ml;
- if (addlen > 65536) addlen=65536;
- if (addlen > g_buffer_length) addlen=g_buffer_length;
- memcpy(g_buffer+g_buffer_length,g_buffer,addlen);
- }
- g_wave_headers[x].dwBufferLength=l;
- g_wave_headers[x].lpData=g_buffer_read;
- if (waveOutPrepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0])) == MMSYSERR_NOERROR)
- {
- do_samples_altvol(g_wave_headers[x].lpData,g_wave_headers[x].dwBufferLength);
- if (waveOutWrite(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0])) == MMSYSERR_NOERROR)
- {
- g_prebuf=0;
- g_wavecnt++;
- g_buffer_inlength += l;
- g_buffer_read += l;
-
- if (g_buffer_read >= g_buffer+g_buffer_length) g_buffer_read-=g_buffer_length;
- if (g_wavecnt < t)
- {
- EnterCriticalSection(&g_cs);
- l=g_buffer_valid-g_buffer_inlength;
- LeaveCriticalSection(&g_cs);
- if (l >= m) goto again;
- }
- }
- else
- {
- waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
- g_wave_headers[x].dwFlags=0;
- }
- }
- else g_wave_headers[x].dwFlags=0;
- }
- }
- }
- }
- g_quit_flag=2;
- return 0;
- }
- void PCM_AudioOut::do_samples_altvol(char *in, int blen)
- {
- if ((a_v != 255 || a_p) && g_vol_table)
- {
- EnterCriticalSection(&g_cs);
- if (g_bps == 8)
- {
- unsigned char *i=(unsigned char *)in;
- int x = blen;
- if (g_nch==1)
- {
- while (x--) { *i = g_vol_table[*i]; i ++; }
- }
- else
- {
- x>>=1;
- while (x--)
- {
- i[0] = g_vol_table[i[0]];
- i[1] = g_vol_table[i[1] + 256];
- i+=2;
- }
- }
- }
- else if (g_bps == 16)
- {
- short *i = (short *) in;
- short *tab= (short *)g_vol_table;
- int x = blen>>1;
- if (g_nch==1)
- {
- while (x--)
- {
- int a = i[0];
- if (a <= 0) i[0] = tab[-a];
- else i[0] = -tab[a];
- i++;
- }
- }
- else
- {
- x>>=1;
- while (x--)
- {
- int a = i[0];
- if (a <= 0) i[0] = tab[-a];
- else i[0] = -tab[a];
- a=i[1];
- if (a <= 0) i[1] = tab[32769-a];
- else i[1] = -tab[32769+a];
- i+=2;
- }
- }
- }
- LeaveCriticalSection(&g_cs);
- }
- }
- void PCM_AudioOut::setvolume(int volume)
- {
- if (volume >= 0 && volume <= 255) a_v=volume;
- _setvol();
- }
- void PCM_AudioOut::setpan(int pan)
- {
- if (pan >= -255 && pan <= 255) a_p=pan;
- _setvol();
- }
- IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8])
- {
- if (outfmt[0] != NSV_MAKETYPE('P','C','M',' ') ||
- !outfmt[1] || !outfmt[2] || !outfmt[3]) return NULL;
- PCM_AudioOut *p=new PCM_AudioOut(outfmt[1],outfmt[2],outfmt[3]);
- if (p->open_success()) return p;
- delete p;
- return NULL;
- }
|