1
0

waveout.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. #define STRICT
  2. #include <windows.h>
  3. #include "out_wave.h"
  4. #include "api.h"
  5. #include "waveout.h"
  6. #include "resource.h"
  7. #include <mmreg.h>
  8. #pragma intrinsic(memset, memcpy)
  9. #define SYNC_IN EnterCriticalSection(&sync);
  10. #define SYNC_OUT LeaveCriticalSection(&sync);
  11. #define GET_TIME timeGetTime()
  12. static const int kMaxChannelsToMask = 8;
  13. static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = {
  14. 0,
  15. // 1 = Mono
  16. SPEAKER_FRONT_CENTER,
  17. // 2 = Stereo
  18. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
  19. // 3 = Stereo + Center
  20. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
  21. // 4 = Quad
  22. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
  23. SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
  24. // 5 = 5.0
  25. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
  26. SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
  27. // 6 = 5.1
  28. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
  29. SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
  30. SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
  31. // 7 = 6.1
  32. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
  33. SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
  34. SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
  35. SPEAKER_BACK_CENTER,
  36. // 8 = 7.1
  37. SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
  38. SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
  39. SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
  40. SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
  41. // TODO(fbarchard): Add additional masks for 7.2 and beyond.
  42. };
  43. WaveOut::~WaveOut()
  44. {
  45. if (hThread)
  46. {
  47. SYNC_IN
  48. die=1;
  49. SYNC_OUT;
  50. SetEvent(hEvent);
  51. WaitForSingleObject(hThread,INFINITE);
  52. }
  53. if (hEvent) CloseHandle(hEvent);
  54. DeleteCriticalSection(&sync);
  55. killwaveout();
  56. if (buffer) LocalFree(buffer);
  57. hdr_free_list(hdrs);
  58. hdr_free_list(hdrs_free);
  59. }
  60. DWORD WINAPI WaveOut::ThreadProc(WaveOut * p)
  61. {
  62. p->thread();
  63. return 0;
  64. }
  65. void WaveOut::thread()
  66. {
  67. SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
  68. while(hWo)
  69. {
  70. WaitForSingleObject(hEvent,INFINITE);
  71. SYNC_IN;
  72. if (die) {SYNC_OUT; break;}
  73. for(HEADER * h=hdrs;h;)
  74. {
  75. if (h->hdr.dwFlags&WHDR_DONE)
  76. {
  77. n_playing--;
  78. buf_size_used-=h->hdr.dwBufferLength;
  79. last_time=GET_TIME;
  80. waveOutUnprepareHeader(hWo,&h->hdr,sizeof(WAVEHDR));
  81. HEADER* f=h;
  82. h=h->next;
  83. hdr_free(f);
  84. }
  85. else h=h->next;
  86. }
  87. /* if (needflush)
  88. {
  89. flush();
  90. if (!paused) waveOutRestart(hWo);
  91. needflush=0;
  92. }*/
  93. if (!paused && newpause)
  94. {
  95. paused=1;
  96. if (hWo) waveOutPause(hWo);
  97. p_time=GET_TIME-last_time;
  98. }
  99. if (paused && !newpause)
  100. {
  101. paused=0;
  102. if (hWo) waveOutRestart(hWo);
  103. last_time = GET_TIME-p_time;
  104. }
  105. UINT limit;
  106. if (needplay) limit=0;
  107. else if (!n_playing)
  108. {
  109. limit=prebuf;
  110. if (limit<avgblock) limit=avgblock;
  111. }
  112. else if (buf_size_used<(buf_size>>1) || n_playing<3) limit=minblock;//skipping warning, blow whatever we have
  113. else limit=avgblock;//just a block
  114. while(data_written>limit)
  115. {
  116. UINT d=(data_written > maxblock) ? maxblock : data_written;
  117. d-=d%fmt_align;
  118. if (!d) break;
  119. data_written-=d;
  120. buf_size_used+=d;
  121. HEADER * h=hdr_alloc();
  122. h->hdr.dwBytesRecorded=h->hdr.dwBufferLength=d;
  123. h->hdr.lpData=buffer+write_ptr;
  124. write_ptr+=d;
  125. if (write_ptr>buf_size)
  126. {
  127. write_ptr-=buf_size;
  128. memcpy(buffer+buf_size,buffer,write_ptr);
  129. }
  130. n_playing++;
  131. if (use_altvol) do_altvol(h->hdr.lpData,d);
  132. waveOutPrepareHeader(hWo,&h->hdr,sizeof(WAVEHDR));
  133. waveOutWrite(hWo,&h->hdr,sizeof(WAVEHDR));//important: make all waveOutWrite calls from *our* thread to keep win2k/xp happy
  134. if (n_playing==1) last_time=GET_TIME;
  135. #if 0
  136. {
  137. char t[128] = {0};
  138. wsprintf(t,"block size: %u, limit used %u\n", d,limit);
  139. OutputDebugString(t);
  140. }
  141. #endif
  142. }
  143. needplay=0;
  144. if (!data_written && !n_playing && closeonstop) killwaveout();
  145. SYNC_OUT;
  146. }
  147. killwaveout();
  148. }
  149. int WaveOut::WriteData(const void * _data,UINT size)
  150. {
  151. SYNC_IN;
  152. if (paused) //$!#@!
  153. {
  154. SYNC_OUT;
  155. return 0;
  156. }
  157. const char * data=(const char*)_data;
  158. {
  159. UINT cw=CanWrite();
  160. if (size>cw)
  161. {
  162. size=cw;
  163. }
  164. }
  165. UINT written=0;
  166. while(size>0)
  167. {
  168. UINT ptr=(data_written + write_ptr)%buf_size;
  169. UINT delta=size;
  170. if (ptr+delta>buf_size) delta=buf_size-ptr;
  171. memcpy(buffer+ptr,data,delta);
  172. data+=delta;
  173. size-=delta;
  174. written+=delta;
  175. data_written+=delta;
  176. }
  177. SYNC_OUT; // sync out first to prevent a ping-pong condition
  178. if (written) SetEvent(hEvent);//new shit, time to update
  179. return (int)written;
  180. }
  181. void WaveOut::flush()//in sync
  182. {
  183. waveOutReset(hWo);
  184. while(hdrs)
  185. {
  186. if (hdrs->hdr.dwFlags & WHDR_PREPARED)
  187. {
  188. waveOutUnprepareHeader(hWo,&hdrs->hdr,sizeof(WAVEHDR));
  189. }
  190. hdr_free(hdrs);
  191. }
  192. reset_shit();
  193. }
  194. void WaveOut::Flush()
  195. {
  196. /* SYNC_IN;
  197. needflush=1;
  198. SetEvent(hEvent);
  199. SYNC_OUT;
  200. while(needflush) Sleep(1);*/
  201. SYNC_IN;//no need to sync this to our thread
  202. flush();
  203. if (!paused) waveOutRestart(hWo);
  204. SYNC_OUT;
  205. }
  206. void WaveOut::ForcePlay()
  207. {
  208. SYNC_IN;//needs to be done in our thread
  209. if (!paused) {needplay=1;SetEvent(hEvent);}
  210. SYNC_OUT;
  211. // while(needplay) Sleep(1);
  212. }
  213. WaveOut::WaveOut()
  214. {
  215. #ifndef TINY_DLL //TINY_DLL has its own new operator with zeroinit
  216. memset(&hWo,0,sizeof(*this)-((char*)&hWo-(char*)this));
  217. #endif
  218. myvol=-666;
  219. mypan=-666;
  220. InitializeCriticalSection(&sync);
  221. }
  222. int WaveOut::open(WaveOutConfig * cfg)
  223. {
  224. fmt_sr = cfg->sr;
  225. fmt_bps = cfg->bps;
  226. fmt_nch = cfg->nch;
  227. fmt_align = ( fmt_bps >> 3 ) * fmt_nch;
  228. fmt_mul = fmt_align * fmt_sr;
  229. use_volume=cfg->use_volume;
  230. use_altvol=cfg->use_altvol;
  231. use_resetvol=cfg->resetvol;
  232. if (!use_volume)
  233. use_altvol=use_resetvol=0;
  234. else if (use_altvol)
  235. {
  236. use_resetvol=0;
  237. use_volume=0;
  238. }
  239. WAVEFORMATEX wfx=
  240. {
  241. WAVE_FORMAT_PCM,
  242. (WORD)fmt_nch,
  243. fmt_sr,
  244. fmt_mul,
  245. (WORD)fmt_align,
  246. (WORD)fmt_bps,
  247. 0
  248. };
  249. if (!hEvent) hEvent=CreateEvent(0,0,0,0);
  250. WAVEFORMATEXTENSIBLE wfxe = { 0 };
  251. wfxe.Format = wfx;
  252. wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  253. wfxe.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  254. wfxe.Format.nChannels = fmt_nch;
  255. wfxe.Format.nBlockAlign = (wfxe.Format.nChannels *
  256. wfxe.Format.wBitsPerSample) / 8;
  257. wfxe.Format.nAvgBytesPerSec = wfxe.Format.nBlockAlign *
  258. wfxe.Format.nSamplesPerSec;
  259. wfxe.Samples.wReserved = 0;
  260. if (fmt_nch > kMaxChannelsToMask) {
  261. wfxe.dwChannelMask = kChannelsToMask[kMaxChannelsToMask];
  262. }
  263. else {
  264. wfxe.dwChannelMask = kChannelsToMask[fmt_nch];
  265. }
  266. wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  267. wfxe.Samples.wValidBitsPerSample = wfxe.Format.wBitsPerSample;
  268. MMRESULT mr = waveOutOpen(&hWo, (UINT)(cfg->dev-1), reinterpret_cast<LPCWAVEFORMATEX>(&wfxe), (DWORD_PTR)hEvent, 0, CALLBACK_EVENT);
  269. if (mr)
  270. {
  271. WCHAR full_error[1024], * _fe = full_error;
  272. WCHAR poo[MAXERRORLENGTH] = { 0 };
  273. WCHAR* e = poo;
  274. if (waveOutGetErrorTextW(mr,poo,MAXERRORLENGTH)!=MMSYSERR_NOERROR)
  275. {
  276. WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWN_MMSYSTEM_ERROR, poo, MAXERRORLENGTH);
  277. }
  278. char * e2=0, e2Buf[1024] = {0};
  279. switch(mr)
  280. {
  281. case 32:
  282. wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_UNSUPPORTED_PCM_FORMAT), fmt_sr, fmt_bps, fmt_nch);
  283. //fixme: some broken drivers blow mmsystem032 for no reason, with "standard" 44khz/16bps/stereo, need better error message when pcm format isnt weird
  284. while(_fe && *_fe) _fe++;
  285. e2="";
  286. break;
  287. case 4:
  288. e2=WASABI_API_LNGSTRING_BUF(IDS_ANOTHER_PROGRAM_IS_USING_THE_SOUNDCARD,e2Buf,1024);
  289. break;
  290. case 2:
  291. e2=WASABI_API_LNGSTRING_BUF(IDS_NO_SOUND_DEVICES_FOUND,e2Buf,1024);
  292. break;
  293. case 20:
  294. e2=WASABI_API_LNGSTRING_BUF(IDS_INTERNAL_DRIVER_ERROR,e2Buf,1024);
  295. break;
  296. case 7:
  297. e2=WASABI_API_LNGSTRING_BUF(IDS_REINSTALL_SOUNDCARD_DRIVERS,e2Buf,1024);
  298. break;
  299. //case 8: fixme
  300. }
  301. if (e2)
  302. {
  303. wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_ERROR_CODE_WINDOWS_ERROR_MESSAGE), e2, mr, e);
  304. }
  305. else
  306. {
  307. wsprintfW(_fe, WASABI_API_LNGSTRINGW(IDS_ERROR_CODE), e, mr);
  308. }
  309. cfg->SetError(full_error);
  310. return 0;
  311. }
  312. buf_size=MulDiv(cfg->buf_ms,fmt_mul,1000);
  313. maxblock = 0x10000;
  314. minblock = 0x100;
  315. avgblock = buf_size>>4;
  316. if (maxblock>buf_size>>2) maxblock=buf_size>>2;
  317. if (avgblock>maxblock) avgblock=maxblock;
  318. if (maxblock<minblock) maxblock=minblock;
  319. if (avgblock<minblock) avgblock=minblock;
  320. buffer = (char*)LocalAlloc(LPTR,buf_size+maxblock);//extra space at the end of the buffer
  321. prebuf = MulDiv(cfg->prebuf,fmt_mul,1000);
  322. if (prebuf>buf_size) prebuf=buf_size;
  323. n_playing=0;
  324. waveOutRestart(hWo);
  325. reset_shit();
  326. if (use_resetvol) waveOutGetVolume(hWo,&orgvol);
  327. if (myvol!=-666 || mypan!=-666) update_vol();
  328. {
  329. DWORD dw;
  330. hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadProc,this,0,&dw);
  331. }
  332. return 1;
  333. }
  334. int WaveOut::GetLatency(void)
  335. {
  336. SYNC_IN;
  337. int r=0;
  338. if (hWo)
  339. {
  340. r=MulDiv(buf_size_used+data_written,1000,(fmt_bps>>3)*fmt_nch*fmt_sr);
  341. if (paused) r-=p_time;
  342. else if (n_playing) r-=GET_TIME-last_time;
  343. if (r<0) r=0;
  344. }
  345. SYNC_OUT;
  346. return r;
  347. }
  348. void WaveOut::SetVolume(int v)
  349. {
  350. SYNC_IN;
  351. myvol=v;
  352. update_vol();
  353. SYNC_OUT;
  354. }
  355. void WaveOut::SetPan(int p)
  356. {
  357. SYNC_IN;
  358. mypan=p;
  359. update_vol();
  360. SYNC_OUT;
  361. }
  362. void WaveOut::update_vol()
  363. {
  364. if (hWo && use_volume)
  365. {
  366. if (myvol==-666) myvol=255;
  367. if (mypan==-666) mypan=0;
  368. DWORD left,right;
  369. left=right=myvol|(myvol<<8);
  370. if (mypan<0) right=(right*(128+mypan))>>7;
  371. else if (mypan>0) left=(left*(128-mypan))>>7;
  372. waveOutSetVolume(hWo,left|(right<<16));
  373. }
  374. }
  375. void WaveOut::reset_shit()
  376. {
  377. n_playing=0;
  378. data_written=0;
  379. buf_size_used=0;
  380. last_time=0;
  381. //last_time=GET_TIME;
  382. }
  383. void WaveOut::Pause(int s)
  384. {
  385. SYNC_IN;
  386. newpause=s?1:0;//needs to be done in our thread to keep stupid win2k/xp happy
  387. SYNC_OUT;
  388. SetEvent(hEvent);
  389. while(paused!=newpause) Sleep(1);
  390. }
  391. void WaveOut::killwaveout()
  392. {
  393. if (hWo)
  394. {
  395. flush();
  396. if (use_resetvol) waveOutSetVolume(hWo,orgvol);
  397. waveOutClose(hWo);
  398. hWo=0;
  399. }
  400. }
  401. int WaveOut::CanWrite()
  402. {
  403. SYNC_IN;
  404. int rv=paused ? 0 : buf_size-buf_size_used-data_written;
  405. SYNC_OUT;
  406. return rv;
  407. }
  408. WaveOut * WaveOut::Create(WaveOutConfig * cfg)
  409. {
  410. WaveOut * w=new WaveOut;
  411. if (w->open(cfg)<=0)
  412. {
  413. delete w;
  414. w=0;
  415. }
  416. return w;
  417. }
  418. WaveOut::HEADER * WaveOut::hdr_alloc()
  419. {
  420. HEADER * r;
  421. if (hdrs_free)
  422. {
  423. r=hdrs_free;
  424. hdrs_free=hdrs_free->next;
  425. }
  426. else
  427. {
  428. r=new HEADER;
  429. }
  430. r->next=hdrs;
  431. hdrs=r;
  432. memset(&r->hdr,0,sizeof(WAVEHDR));
  433. return r;
  434. }
  435. void WaveOut::hdr_free(HEADER * h)
  436. {
  437. HEADER ** p=&hdrs;
  438. while(p && *p)
  439. {
  440. if (*p==h)
  441. {
  442. *p = (*p)->next;
  443. break;
  444. }
  445. else p=&(*p)->next;
  446. }
  447. h->next=hdrs_free;
  448. hdrs_free=h;
  449. }
  450. void WaveOut::hdr_free_list(HEADER * h)
  451. {
  452. while(h)
  453. {
  454. HEADER * t=h->next;
  455. delete h;
  456. h=t;
  457. }
  458. }
  459. bool WaveOut::PrintState(char * z)
  460. {
  461. bool rv;
  462. SYNC_IN;
  463. if (!hWo) rv=0;
  464. else
  465. {
  466. rv=1;
  467. wsprintfA(z,WASABI_API_LNGSTRING(IDS_DATA_FORMAT),fmt_sr,fmt_bps,fmt_nch);
  468. while(z && *z) z++;
  469. wsprintfA(z,WASABI_API_LNGSTRING(IDS_BUFFER_STATUS),buf_size,n_playing);
  470. while(z && *z) z++;
  471. wsprintfA(z,WASABI_API_LNGSTRING(IDS_LATENCY),GetLatency());
  472. // while(z && *z) z++;
  473. // wsprintf(z,"Data written: %u KB",MulDiv((int)total_written,(fmt_bps>>3)*fmt_nch,1024));
  474. }
  475. SYNC_OUT;
  476. return rv;
  477. }
  478. void WaveOutConfig::SetError(const WCHAR * x)
  479. {
  480. error=(WCHAR*)LocalAlloc(LPTR,lstrlenW(x+1));
  481. lstrcpyW(error,x);
  482. }
  483. void WaveOut::do_altvol_i(char * ptr,UINT max,UINT start,UINT d,int vol)
  484. {
  485. UINT p=start*(fmt_bps>>3);
  486. while(p<max)
  487. {
  488. void * z=ptr+p;
  489. switch(fmt_bps)
  490. {
  491. case 8:
  492. *(BYTE*)z=0x80^(BYTE)MulDiv(0x80^*(BYTE*)z,vol,255);
  493. break;
  494. case 16:
  495. *(short*)z=(short)MulDiv(*(short*)z,vol,255);
  496. break;
  497. case 24:
  498. {
  499. long l=0;
  500. memcpy(&l,z,3);
  501. if (l&0x800000) l|=0xFF000000;
  502. l=MulDiv(l,vol,255);
  503. memcpy(z,&l,3);
  504. }
  505. break;
  506. case 32:
  507. *(long*)z=MulDiv(*(long*)z,vol,255);
  508. break;
  509. }
  510. p+=d*(fmt_bps>>3);
  511. }
  512. }
  513. void WaveOut::do_altvol(char * ptr,UINT s)
  514. {
  515. int mixvol=(myvol==-666) ? 255 : myvol;
  516. int mixpan=(mypan==-666) ? 0 : mypan;
  517. if (mixvol==255 && (fmt_nch!=2 || mixpan==0)) return;
  518. if (fmt_nch==2)
  519. {
  520. int rv=mixvol,lv=mixvol;
  521. if (mixpan<0)
  522. {//-128..0
  523. rv=MulDiv(rv,mixpan+128,128);
  524. }
  525. else if (mixpan>0)
  526. {
  527. lv=MulDiv(rv,128-mixpan,128);
  528. }
  529. do_altvol_i(ptr,s,0,2,lv);
  530. do_altvol_i(ptr,s,1,2,rv);
  531. }
  532. else
  533. {
  534. do_altvol_i(ptr,s,0,1,mixvol);
  535. }
  536. }
  537. bool WaveOut::IsClosed()
  538. {
  539. SYNC_IN;
  540. bool rv=hWo ? 0 : 1;
  541. SYNC_OUT;
  542. return rv;
  543. }