aboutsummaryrefslogtreecommitdiff
path: root/Src/nsv/nsvplay/audiostub.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/nsv/nsvplay/audiostub.cpp')
-rw-r--r--Src/nsv/nsvplay/audiostub.cpp575
1 files changed, 575 insertions, 0 deletions
diff --git a/Src/nsv/nsvplay/audiostub.cpp b/Src/nsv/nsvplay/audiostub.cpp
new file mode 100644
index 00000000..83794693
--- /dev/null
+++ b/Src/nsv/nsvplay/audiostub.cpp
@@ -0,0 +1,575 @@
+#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;
+} \ No newline at end of file