123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- #define STRICT
- #include <windows.h>
- #include "out_wave.h"
- #include "api.h"
- #include "waveout.h"
- #include "resource.h"
- #include <mmreg.h>
- #pragma intrinsic(memset, memcpy)
- #define SYNC_IN EnterCriticalSection(&sync);
- #define SYNC_OUT LeaveCriticalSection(&sync);
- #define GET_TIME timeGetTime()
- static const int kMaxChannelsToMask = 8;
- static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = {
- 0,
- // 1 = Mono
- SPEAKER_FRONT_CENTER,
- // 2 = Stereo
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
- // 3 = Stereo + Center
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
- // 4 = Quad
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
- // 5 = 5.0
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
- SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
- // 6 = 5.1
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
- // 7 = 6.1
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
- SPEAKER_BACK_CENTER,
- // 8 = 7.1
- SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
- // TODO(fbarchard): Add additional masks for 7.2 and beyond.
- };
- WaveOut::~WaveOut()
- {
- if (hThread)
- {
- SYNC_IN
- die=1;
-
- SYNC_OUT;
- SetEvent(hEvent);
- WaitForSingleObject(hThread,INFINITE);
- }
- if (hEvent) CloseHandle(hEvent);
- DeleteCriticalSection(&sync);
- killwaveout();
- if (buffer) LocalFree(buffer);
- hdr_free_list(hdrs);
- hdr_free_list(hdrs_free);
- }
-
- DWORD WINAPI WaveOut::ThreadProc(WaveOut * p)
- {
- p->thread();
- return 0;
- }
- void WaveOut::thread()
- {
- SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
- while(hWo)
- {
- WaitForSingleObject(hEvent,INFINITE);
- SYNC_IN;
- if (die) {SYNC_OUT; break;}
-
- for(HEADER * h=hdrs;h;)
- {
- if (h->hdr.dwFlags&WHDR_DONE)
- {
- n_playing--;
- buf_size_used-=h->hdr.dwBufferLength;
- last_time=GET_TIME;
- waveOutUnprepareHeader(hWo,&h->hdr,sizeof(WAVEHDR));
- HEADER* f=h;
- h=h->next;
- hdr_free(f);
- }
- else h=h->next;
- }
- /* if (needflush)
- {
- flush();
- if (!paused) waveOutRestart(hWo);
- needflush=0;
- }*/
- if (!paused && newpause)
- {
- paused=1;
- if (hWo) waveOutPause(hWo);
- p_time=GET_TIME-last_time;
- }
- if (paused && !newpause)
- {
- paused=0;
- if (hWo) waveOutRestart(hWo);
- last_time = GET_TIME-p_time;
- }
- UINT limit;
- if (needplay) limit=0;
- else if (!n_playing)
- {
- limit=prebuf;
- if (limit<avgblock) limit=avgblock;
- }
- else if (buf_size_used<(buf_size>>1) || n_playing<3) limit=minblock;//skipping warning, blow whatever we have
- else limit=avgblock;//just a block
- while(data_written>limit)
- {
- UINT d=(data_written > maxblock) ? maxblock : data_written;
- d-=d%fmt_align;
- if (!d) break;
- data_written-=d;
- buf_size_used+=d;
-
- HEADER * h=hdr_alloc();
- h->hdr.dwBytesRecorded=h->hdr.dwBufferLength=d;
- h->hdr.lpData=buffer+write_ptr;
- write_ptr+=d;
- if (write_ptr>buf_size)
- {
- write_ptr-=buf_size;
- memcpy(buffer+buf_size,buffer,write_ptr);
- }
-
- n_playing++;
- if (use_altvol) do_altvol(h->hdr.lpData,d);
- waveOutPrepareHeader(hWo,&h->hdr,sizeof(WAVEHDR));
- waveOutWrite(hWo,&h->hdr,sizeof(WAVEHDR));//important: make all waveOutWrite calls from *our* thread to keep win2k/xp happy
- if (n_playing==1) last_time=GET_TIME;
- #if 0
- {
- char t[128] = {0};
- wsprintf(t,"block size: %u, limit used %u\n", d,limit);
- OutputDebugString(t);
- }
- #endif
- }
- needplay=0;
-
- if (!data_written && !n_playing && closeonstop) killwaveout();
- SYNC_OUT;
- }
- killwaveout();
- }
- int WaveOut::WriteData(const void * _data,UINT size)
- {
- SYNC_IN;
- if (paused) //$!#@!
- {
- SYNC_OUT;
- return 0;
- }
- const char * data=(const char*)_data;
- {
- UINT cw=CanWrite();
- if (size>cw)
- {
- size=cw;
- }
- }
- UINT written=0;
- while(size>0)
- {
- UINT ptr=(data_written + write_ptr)%buf_size;
- UINT delta=size;
- if (ptr+delta>buf_size) delta=buf_size-ptr;
- memcpy(buffer+ptr,data,delta);
- data+=delta;
- size-=delta;
- written+=delta;
- data_written+=delta;
- }
- SYNC_OUT; // sync out first to prevent a ping-pong condition
- if (written) SetEvent(hEvent);//new shit, time to update
- return (int)written;
- }
- void WaveOut::flush()//in sync
- {
- waveOutReset(hWo);
- while(hdrs)
- {
- if (hdrs->hdr.dwFlags & WHDR_PREPARED)
- {
- waveOutUnprepareHeader(hWo,&hdrs->hdr,sizeof(WAVEHDR));
- }
- hdr_free(hdrs);
- }
- reset_shit();
- }
- void WaveOut::Flush()
- {
- /* SYNC_IN;
- needflush=1;
- SetEvent(hEvent);
- SYNC_OUT;
- while(needflush) Sleep(1);*/
- SYNC_IN;//no need to sync this to our thread
- flush();
- if (!paused) waveOutRestart(hWo);
- SYNC_OUT;
- }
- void WaveOut::ForcePlay()
- {
- SYNC_IN;//needs to be done in our thread
- if (!paused) {needplay=1;SetEvent(hEvent);}
- SYNC_OUT;
- // while(needplay) Sleep(1);
- }
- WaveOut::WaveOut()
- {
- #ifndef TINY_DLL //TINY_DLL has its own new operator with zeroinit
- memset(&hWo,0,sizeof(*this)-((char*)&hWo-(char*)this));
- #endif
- myvol=-666;
- mypan=-666;
- InitializeCriticalSection(&sync);
- }
- int WaveOut::open(WaveOutConfig * cfg)
- {
- fmt_sr = cfg->sr;
- fmt_bps = cfg->bps;
- fmt_nch = cfg->nch;
- fmt_align = ( fmt_bps >> 3 ) * fmt_nch;
- fmt_mul = fmt_align * fmt_sr;
- use_volume=cfg->use_volume;
- use_altvol=cfg->use_altvol;
- use_resetvol=cfg->resetvol;
- if (!use_volume)
- use_altvol=use_resetvol=0;
- else if (use_altvol)
- {
- use_resetvol=0;
- use_volume=0;
- }
- WAVEFORMATEX wfx=
- {
- WAVE_FORMAT_PCM,
- (WORD)fmt_nch,
- fmt_sr,
- fmt_mul,
- (WORD)fmt_align,
- (WORD)fmt_bps,
- 0
- };
- if (!hEvent) hEvent=CreateEvent(0,0,0,0);
- WAVEFORMATEXTENSIBLE wfxe = { 0 };
- wfxe.Format = wfx;
- wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wfxe.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- wfxe.Format.nChannels = fmt_nch;
- wfxe.Format.nBlockAlign = (wfxe.Format.nChannels *
- wfxe.Format.wBitsPerSample) / 8;
- wfxe.Format.nAvgBytesPerSec = wfxe.Format.nBlockAlign *
- wfxe.Format.nSamplesPerSec;
- wfxe.Samples.wReserved = 0;
- if (fmt_nch > kMaxChannelsToMask) {
- wfxe.dwChannelMask = kChannelsToMask[kMaxChannelsToMask];
- }
- else {
- wfxe.dwChannelMask = kChannelsToMask[fmt_nch];
- }
- wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- wfxe.Samples.wValidBitsPerSample = wfxe.Format.wBitsPerSample;
- MMRESULT mr = waveOutOpen(&hWo, (UINT)(cfg->dev-1), reinterpret_cast<LPCWAVEFORMATEX>(&wfxe), (DWORD_PTR)hEvent, 0, CALLBACK_EVENT);
- if (mr)
- {
- WCHAR full_error[1024], * _fe = full_error;
- WCHAR poo[MAXERRORLENGTH] = { 0 };
- WCHAR* e = poo;
- if (waveOutGetErrorTextW(mr,poo,MAXERRORLENGTH)!=MMSYSERR_NOERROR)
- {
- WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN_MMSYSTEM_ERROR, poo, MAXERRORLENGTH);
- }
- char * e2=0, e2Buf[1024] = {0};
- switch(mr)
- {
- case 32:
- wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_UNSUPPORTED_PCM_FORMAT), fmt_sr, fmt_bps, fmt_nch);
- //fixme: some broken drivers blow mmsystem032 for no reason, with "standard" 44khz/16bps/stereo, need better error message when pcm format isnt weird
- while(_fe && *_fe) _fe++;
- e2="";
- break;
- case 4:
- e2=WASABI_API_LNGSTRING_BUF(IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD,e2Buf,1024);
- break;
- case 2:
- e2=WASABI_API_LNGSTRING_BUF(IDS_NO_SOUND_DEVICES_FOUND,e2Buf,1024);
- break;
- case 20:
- e2=WASABI_API_LNGSTRING_BUF(IDS_INTERNAL_DRIVER_ERROR,e2Buf,1024);
- break;
- case 7:
- e2=WASABI_API_LNGSTRING_BUF(IDS_REINSTALL_SOUNDCARD_DRIVERS,e2Buf,1024);
- break;
- //case 8: fixme
- }
- if (e2)
- {
- wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE), e2, mr, e);
- }
- else
- {
- wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_ERROR_CODE), e, mr);
- }
- cfg->SetError(full_error);
- return 0;
- }
-
- buf_size=MulDiv(cfg->buf_ms,fmt_mul,1000);
-
- maxblock = 0x10000;
- minblock = 0x100;
- avgblock = buf_size>>4;
- if (maxblock>buf_size>>2) maxblock=buf_size>>2;
- if (avgblock>maxblock) avgblock=maxblock;
- if (maxblock<minblock) maxblock=minblock;
- if (avgblock<minblock) avgblock=minblock;
-
- buffer = (char*)LocalAlloc(LPTR,buf_size+maxblock);//extra space at the end of the buffer
- prebuf = MulDiv(cfg->prebuf,fmt_mul,1000);
- if (prebuf>buf_size) prebuf=buf_size;
- n_playing=0;
- waveOutRestart(hWo);
- reset_shit();
-
- if (use_resetvol) waveOutGetVolume(hWo,&orgvol);
- if (myvol!=-666 || mypan!=-666) update_vol();
- {
- DWORD dw;
- hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadProc,this,0,&dw);
- }
- return 1;
- }
- int WaveOut::GetLatency(void)
- {
- SYNC_IN;
- int r=0;
- if (hWo)
- {
- r=MulDiv(buf_size_used+data_written,1000,(fmt_bps>>3)*fmt_nch*fmt_sr);
- if (paused) r-=p_time;
- else if (n_playing) r-=GET_TIME-last_time;
- if (r<0) r=0;
- }
- SYNC_OUT;
- return r;
- }
- void WaveOut::SetVolume(int v)
- {
- SYNC_IN;
- myvol=v;
- update_vol();
- SYNC_OUT;
- }
- void WaveOut::SetPan(int p)
- {
- SYNC_IN;
- mypan=p;
- update_vol();
- SYNC_OUT;
- }
- void WaveOut::update_vol()
- {
- if (hWo && use_volume)
- {
- if (myvol==-666) myvol=255;
- if (mypan==-666) mypan=0;
- DWORD left,right;
- left=right=myvol|(myvol<<8);
- if (mypan<0) right=(right*(128+mypan))>>7;
- else if (mypan>0) left=(left*(128-mypan))>>7;
- waveOutSetVolume(hWo,left|(right<<16));
- }
- }
- void WaveOut::reset_shit()
- {
- n_playing=0;
- data_written=0;
- buf_size_used=0;
- last_time=0;
- //last_time=GET_TIME;
- }
- void WaveOut::Pause(int s)
- {
- SYNC_IN;
- newpause=s?1:0;//needs to be done in our thread to keep stupid win2k/xp happy
- SYNC_OUT;
- SetEvent(hEvent);
- while(paused!=newpause) Sleep(1);
- }
- void WaveOut::killwaveout()
- {
- if (hWo)
- {
- flush();
- if (use_resetvol) waveOutSetVolume(hWo,orgvol);
- waveOutClose(hWo);
- hWo=0;
- }
- }
- int WaveOut::CanWrite()
- {
- SYNC_IN;
- int rv=paused ? 0 : buf_size-buf_size_used-data_written;
- SYNC_OUT;
- return rv;
- }
- WaveOut * WaveOut::Create(WaveOutConfig * cfg)
- {
- WaveOut * w=new WaveOut;
- if (w->open(cfg)<=0)
- {
- delete w;
- w=0;
- }
- return w;
- }
- WaveOut::HEADER * WaveOut::hdr_alloc()
- {
- HEADER * r;
- if (hdrs_free)
- {
- r=hdrs_free;
- hdrs_free=hdrs_free->next;
- }
- else
- {
- r=new HEADER;
- }
- r->next=hdrs;
- hdrs=r;
- memset(&r->hdr,0,sizeof(WAVEHDR));
- return r;
- }
- void WaveOut::hdr_free(HEADER * h)
- {
- HEADER ** p=&hdrs;
- while(p && *p)
- {
- if (*p==h)
- {
- *p = (*p)->next;
- break;
- }
- else p=&(*p)->next;
- }
- h->next=hdrs_free;
- hdrs_free=h;
- }
- void WaveOut::hdr_free_list(HEADER * h)
- {
- while(h)
- {
- HEADER * t=h->next;
- delete h;
- h=t;
- }
- }
- bool WaveOut::PrintState(char * z)
- {
- bool rv;
- SYNC_IN;
- if (!hWo) rv=0;
- else
- {
- rv=1;
- wsprintfA(z,WASABI_API_LNGSTRING(IDS_DATA_FORMAT),fmt_sr,fmt_bps,fmt_nch);
- while(z && *z) z++;
- wsprintfA(z,WASABI_API_LNGSTRING(IDS_BUFFER_STATUS),buf_size,n_playing);
- while(z && *z) z++;
- wsprintfA(z,WASABI_API_LNGSTRING(IDS_LATENCY),GetLatency());
- // while(z && *z) z++;
- // wsprintf(z,"Data written: %u KB",MulDiv((int)total_written,(fmt_bps>>3)*fmt_nch,1024));
- }
- SYNC_OUT;
- return rv;
- }
- void WaveOutConfig::SetError(const WCHAR * x)
- {
- error=(WCHAR*)LocalAlloc(LPTR,lstrlenW(x+1));
- lstrcpyW(error,x);
- }
- void WaveOut::do_altvol_i(char * ptr,UINT max,UINT start,UINT d,int vol)
- {
- UINT p=start*(fmt_bps>>3);
- while(p<max)
- {
- void * z=ptr+p;
- switch(fmt_bps)
- {
- case 8:
- *(BYTE*)z=0x80^(BYTE)MulDiv(0x80^*(BYTE*)z,vol,255);
- break;
- case 16:
- *(short*)z=(short)MulDiv(*(short*)z,vol,255);
- break;
- case 24:
- {
- long l=0;
- memcpy(&l,z,3);
- if (l&0x800000) l|=0xFF000000;
- l=MulDiv(l,vol,255);
- memcpy(z,&l,3);
- }
- break;
- case 32:
- *(long*)z=MulDiv(*(long*)z,vol,255);
- break;
- }
- p+=d*(fmt_bps>>3);
- }
- }
- void WaveOut::do_altvol(char * ptr,UINT s)
- {
- int mixvol=(myvol==-666) ? 255 : myvol;
- int mixpan=(mypan==-666) ? 0 : mypan;
- if (mixvol==255 && (fmt_nch!=2 || mixpan==0)) return;
- if (fmt_nch==2)
- {
- int rv=mixvol,lv=mixvol;
- if (mixpan<0)
- {//-128..0
- rv=MulDiv(rv,mixpan+128,128);
- }
- else if (mixpan>0)
- {
- lv=MulDiv(rv,128-mixpan,128);
- }
- do_altvol_i(ptr,s,0,2,lv);
- do_altvol_i(ptr,s,1,2,rv);
- }
- else
- {
- do_altvol_i(ptr,s,0,1,mixvol);
- }
- }
- bool WaveOut::IsClosed()
- {
- SYNC_IN;
- bool rv=hWo ? 0 : 1;
- SYNC_OUT;
- return rv;
- }
|