d9c29c572fe24414c9db9c2251c979be394bc5d7
[fceu.git] / drivers / win / sound.c
1 /* FCE Ultra - NES/Famicom Emulator\r
2  *\r
3  * Copyright notice for this file:\r
4  *  Copyright (C) 2002 Ben Parnell\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  */\r
20 \r
21 FILE *soundlog=0;\r
22 void WriteWaveData(int32 *Buffer, int Count);\r
23 DWORD WINAPI DSThread(LPVOID lpParam);\r
24 LPDIRECTSOUND ppDS=0;\r
25 LPDIRECTSOUNDBUFFER ppbuf=0;\r
26 LPDIRECTSOUNDBUFFER ppbufsec=0;\r
27 LPDIRECTSOUNDBUFFER ppbufw;\r
28 \r
29 DSBUFFERDESC DSBufferDesc;\r
30 WAVEFORMATEX wfa;\r
31 WAVEFORMATEX wf;\r
32 \r
33 static int DSBufferSize=0;\r
34 static int bittage;\r
35 \r
36 void TrashSound(void)\r
37 {\r
38  FCEUI_Sound(0);\r
39  if(ppbufsec)\r
40  {\r
41   IDirectSoundBuffer_Stop(ppbufsec);\r
42   IDirectSoundBuffer_Release(ppbufsec);\r
43   ppbufsec=0;\r
44  }\r
45  if(ppbuf)\r
46  {\r
47   IDirectSoundBuffer_Stop(ppbuf);\r
48   IDirectSoundBuffer_Release(ppbuf);\r
49   ppbuf=0;\r
50  }\r
51  if(ppDS)\r
52  {\r
53   IDirectSound_Release(ppDS);\r
54   ppDS=0;\r
55  }\r
56 }\r
57 \r
58 \r
59  static VOID *feegle[2];\r
60  static DWORD dook[2];\r
61  static DWORD writepos=0,playpos=0,lplaypos=0;\r
62 void CheckDStatus(void)\r
63 {\r
64   DWORD status;\r
65   status=0;\r
66   IDirectSoundBuffer_GetStatus(ppbufw, &status);\r
67 \r
68   if(status&DSBSTATUS_BUFFERLOST)\r
69   {\r
70    IDirectSoundBuffer_Restore(ppbufw);\r
71   }\r
72 \r
73   if(!(status&DSBSTATUS_PLAYING))\r
74   {\r
75    lplaypos=0;\r
76    writepos=((soundbufsize)<<bittage);\r
77    IDirectSoundBuffer_SetFormat(ppbufw,&wf);\r
78    IDirectSoundBuffer_Play(ppbufw,0,0,DSBPLAY_LOOPING);\r
79   }\r
80 }\r
81 \r
82 static int16 MBuffer[2048];\r
83 void FCEUD_WriteSoundData(int32 *Buffer, int Count)\r
84 {\r
85  int P;\r
86  int k=0;\r
87 \r
88  if(soundlog)\r
89   WriteWaveData(Buffer, Count);\r
90 \r
91  if(!bittage)\r
92  {\r
93   for(P=0;P<Count;P++)\r
94    *(((uint8*)MBuffer)+P)=((int8)(Buffer[P]>>8))^128;\r
95  }\r
96  else\r
97  {\r
98   for(P=0;P<Count;P++)\r
99    MBuffer[P]=Buffer[P];\r
100  }\r
101   ilicpo:\r
102   CheckDStatus();\r
103   IDirectSoundBuffer_GetCurrentPosition(ppbufw,&playpos,0);\r
104 \r
105   if(writepos>=DSBufferSize) \r
106    if(playpos<lplaypos)\r
107     writepos-=DSBufferSize;\r
108   lplaypos=playpos;\r
109 \r
110   /* If the write position is beyond the fill buffer, block. */\r
111   if(writepos>=(playpos+(soundbufsize<<bittage)))\r
112   //if(!(writepos<playpos+((soundbufsize)<<bittage)))\r
113   {\r
114    if(!NoWaiting)\r
115    {\r
116     if(soundsleep==1)\r
117     {\r
118      if(!k)\r
119      {\r
120       int stime;      \r
121 \r
122       stime=writepos-(playpos+(soundbufsize<<bittage));\r
123       stime*=1000;\r
124       stime/=soundrate;\r
125       stime>>=1;\r
126       if(stime>=5)\r
127        Sleep(stime);\r
128       k=1;\r
129      }     \r
130     }\r
131     else if(soundsleep==2)\r
132     {\r
133      int stime;      \r
134      stime=writepos-(playpos+(soundbufsize<<bittage));\r
135      stime*=1000;\r
136      stime/=soundrate;\r
137      stime>>=1;\r
138      if(stime>=2)\r
139       Sleep(stime);\r
140     }\r
141    }\r
142    BlockingCheck();\r
143    if(!soundo || NoWaiting) return;\r
144    goto ilicpo;\r
145   }\r
146 \r
147   if(netplaytype && netplayon)\r
148   {\r
149    if(writepos<=playpos+128)\r
150    writepos=playpos+(soundbufsize<<bittage);\r
151   }\r
152 \r
153   {\r
154    feegle[0]=feegle[1]=0;\r
155    dook[0]=dook[1]=0;\r
156 \r
157    \r
158    ddrval=IDirectSoundBuffer_Lock(ppbufw,(writepos%DSBufferSize),Count<<bittage,&feegle[0],&dook[0],&feegle[1],&dook[1],0);\r
159    if(ddrval!=DS_OK)\r
160     goto nolock;\r
161 \r
162    if(feegle[1]!=0 && feegle[1]!=feegle[0])\r
163    {\r
164     if(soundflush)\r
165     {\r
166      memset(feegle[0],0x80,dook[0]);\r
167      memset(feegle[1],0x80,dook[1]);\r
168     }\r
169     else\r
170     {\r
171      memcpy(feegle[0],(uint8 *)MBuffer,dook[0]);\r
172      memcpy(feegle[1],((uint8 *)MBuffer)+dook[0],dook[1]);\r
173     }\r
174    }\r
175    else\r
176    {\r
177     if(soundflush)\r
178      memset(feegle[0],0x80,dook[0]);\r
179     else\r
180      memcpy(feegle[0],(uint8 *)MBuffer,dook[0]);\r
181    }\r
182 \r
183    IDirectSoundBuffer_Unlock(ppbufw,feegle[0],dook[0],feegle[1],dook[1]);\r
184    writepos+=Count<<bittage;\r
185   }\r
186  nolock:\r
187  ///////// Ending\r
188 }\r
189 \r
190 int InitSound()\r
191 {\r
192  DSCAPS dscaps;\r
193  DSBCAPS dsbcaps;\r
194 \r
195  memset(&wf,0x00,sizeof(wf));\r
196  wf.wFormatTag = WAVE_FORMAT_PCM;\r
197  wf.nChannels = 1;\r
198  wf.nSamplesPerSec = soundrate;\r
199 \r
200  ddrval=DirectSoundCreate(0,&ppDS,0);\r
201  if (ddrval != DS_OK)\r
202  {\r
203   FCEUD_PrintError("DirectSound: Error creating DirectSound object.");\r
204   return 0;\r
205  }\r
206 \r
207  if(soundoptions&SO_SECONDARY)\r
208  {\r
209   trysecondary:\r
210   ddrval=IDirectSound_SetCooperativeLevel(ppDS,hAppWnd,DSSCL_PRIORITY);\r
211   if (ddrval != DS_OK)\r
212   {\r
213    FCEUD_PrintError("DirectSound: Error setting cooperative level to DDSCL_PRIORITY.");\r
214    TrashSound();\r
215    return 0;\r
216   }\r
217  }\r
218  else\r
219  {\r
220   ddrval=IDirectSound_SetCooperativeLevel(ppDS,hAppWnd,DSSCL_WRITEPRIMARY);\r
221   if (ddrval != DS_OK)\r
222   {\r
223    FCEUD_PrintError("DirectSound: Error setting cooperative level to DDSCL_WRITEPRIMARY.  Forcing use of secondary sound buffer and trying again...");\r
224    soundoptions|=SO_SECONDARY;\r
225    goto trysecondary;\r
226   }\r
227  }\r
228  memset(&dscaps,0x00,sizeof(dscaps));\r
229  dscaps.dwSize=sizeof(dscaps);\r
230  ddrval=IDirectSound_GetCaps(ppDS,&dscaps);\r
231  if(ddrval!=DS_OK)\r
232  {\r
233   FCEUD_PrintError("DirectSound: Error getting capabilities.");\r
234   return 0;\r
235  }\r
236 \r
237  if(dscaps.dwFlags&DSCAPS_EMULDRIVER)\r
238   FCEUD_PrintError("DirectSound: Sound device is being emulated through waveform-audio functions.  Sound quality will most likely be awful.  Try to update your sound device's sound drivers.");\r
239 \r
240  IDirectSound_Compact(ppDS);\r
241 \r
242  memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC));\r
243  DSBufferDesc.dwSize=sizeof(DSBufferDesc);\r
244  if(soundoptions&SO_SECONDARY)\r
245   DSBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER;\r
246  else\r
247   DSBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_GETCURRENTPOSITION2;\r
248 \r
249  ddrval=IDirectSound_CreateSoundBuffer(ppDS,&DSBufferDesc,&ppbuf,0);\r
250  if (ddrval != DS_OK)\r
251  {\r
252   FCEUD_PrintError("DirectSound: Error creating primary buffer.");\r
253   TrashSound();\r
254   return 0;\r
255  } \r
256 \r
257  memset(&wfa,0x00,sizeof(wfa));\r
258 \r
259  if(soundoptions&SO_FORCE8BIT)\r
260   bittage=0;\r
261  else\r
262  {\r
263   bittage=1;\r
264   if( (!(dscaps.dwFlags&DSCAPS_PRIMARY16BIT)) ||\r
265       (!(dscaps.dwFlags&DSCAPS_SECONDARY16BIT) && (soundoptions&SO_SECONDARY)))\r
266   {\r
267    FCEUD_PrintError("DirectSound: 16-bit sound is not supported.  Forcing 8-bit sound.");\r
268    bittage=0;\r
269    soundoptions|=SO_FORCE8BIT;\r
270   }\r
271  }\r
272 \r
273  wf.wBitsPerSample=8<<bittage;\r
274  wf.nBlockAlign = bittage+1;\r
275  wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;\r
276  \r
277  ddrval=IDirectSoundBuffer_SetFormat(ppbuf,&wf);\r
278  if (ddrval != DS_OK)\r
279  {\r
280   FCEUD_PrintError("DirectSound: Error setting primary buffer format.");\r
281   TrashSound();\r
282   return 0;\r
283  }\r
284 \r
285  IDirectSoundBuffer_GetFormat(ppbuf,&wfa,sizeof(wfa),0);\r
286 \r
287  if(soundoptions&SO_SECONDARY)\r
288  {\r
289   memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC));  \r
290   DSBufferDesc.dwSize=sizeof(DSBufferDesc);\r
291   DSBufferDesc.dwFlags=DSBCAPS_GETCURRENTPOSITION2;\r
292   if(soundoptions&SO_GFOCUS)\r
293    DSBufferDesc.dwFlags|=DSBCAPS_GLOBALFOCUS;\r
294   DSBufferDesc.dwBufferBytes=32768;\r
295   DSBufferDesc.lpwfxFormat=&wfa;  \r
296   ddrval=IDirectSound_CreateSoundBuffer(ppDS, &DSBufferDesc, &ppbufsec, 0);\r
297   if (ddrval != DS_OK)\r
298   {\r
299    FCEUD_PrintError("DirectSound: Error creating secondary buffer.");\r
300    TrashSound();\r
301    return 0;\r
302   }\r
303  }\r
304 \r
305  //sprintf(TempArray,"%d\n",wfa.nSamplesPerSec);\r
306  //FCEUD_PrintError(TempArray);\r
307 \r
308  if(soundoptions&SO_SECONDARY)\r
309  {\r
310   DSBufferSize=32768;\r
311   IDirectSoundBuffer_SetCurrentPosition(ppbufsec,0);\r
312   ppbufw=ppbufsec;\r
313  }\r
314  else\r
315  {\r
316   memset(&dsbcaps,0,sizeof(dsbcaps));\r
317   dsbcaps.dwSize=sizeof(dsbcaps);\r
318   ddrval=IDirectSoundBuffer_GetCaps(ppbuf,&dsbcaps);\r
319   if (ddrval != DS_OK)\r
320   {\r
321    FCEUD_PrintError("DirectSound: Error getting buffer capabilities.");\r
322    TrashSound();\r
323    return 0;\r
324   }\r
325 \r
326   DSBufferSize=dsbcaps.dwBufferBytes;\r
327 \r
328   if(DSBufferSize<8192)\r
329   {\r
330    FCEUD_PrintError("DirectSound: Primary buffer size is too small!");\r
331    TrashSound();\r
332    return 0;\r
333   }\r
334   ppbufw=ppbuf;\r
335  }\r
336 \r
337  soundbufsize=(soundbuftime*soundrate/1000);\r
338  FCEUI_Sound(soundrate);\r
339  return 1;\r
340 }\r
341 \r
342 BOOL CALLBACK SoundConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
343 {\r
344   int x;\r
345 \r
346   switch(uMsg) {\r
347    case WM_INITDIALOG:\r
348                 if(soundo)\r
349                  CheckDlgButton(hwndDlg,126,BST_CHECKED);\r
350                 if(soundoptions&SO_FORCE8BIT)\r
351                  CheckDlgButton(hwndDlg,122,BST_CHECKED);\r
352                 if(soundoptions&SO_SECONDARY)\r
353                  CheckDlgButton(hwndDlg,123,BST_CHECKED);\r
354                 if(soundoptions&SO_GFOCUS)\r
355                  CheckDlgButton(hwndDlg,124,BST_CHECKED);\r
356                 SetDlgItemInt(hwndDlg,200,soundrate,0);\r
357 \r
358                 /* Volume Trackbar */\r
359                 SendDlgItemMessage(hwndDlg,500,TBM_SETRANGE,1,MAKELONG(0,200));\r
360                 SendDlgItemMessage(hwndDlg,500,TBM_SETTICFREQ,25,0);\r
361                 SendDlgItemMessage(hwndDlg,500,TBM_SETPOS,1,200-soundvolume);\r
362 \r
363                 /* buffer size time trackbar */\r
364                 SendDlgItemMessage(hwndDlg,128,TBM_SETRANGE,1,MAKELONG(15,200));\r
365                 SendDlgItemMessage(hwndDlg,128,TBM_SETTICFREQ,1,0);\r
366                 SendDlgItemMessage(hwndDlg,128,TBM_SETPOS,1,soundbuftime);\r
367 \r
368                 {\r
369                  char tbuf[8];\r
370                  sprintf(tbuf,"%d",soundbuftime);\r
371                  SetDlgItemText(hwndDlg,666,(LPTSTR)tbuf);\r
372                 }\r
373                 \r
374                 SendDlgItemMessage(hwndDlg,129,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Mean");\r
375                 SendDlgItemMessage(hwndDlg,129,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Nice");\r
376                 SendDlgItemMessage(hwndDlg,129,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Nicest");\r
377                 SendDlgItemMessage(hwndDlg,129,CB_SETCURSEL,soundsleep,(LPARAM)(LPSTR)0);\r
378                 break;\r
379    case WM_HSCROLL:\r
380                 // This doesn't seem to work.  Hmm...\r
381                 //if((HWND)lParam==(HWND)128)\r
382                 {\r
383                  char tbuf[8];\r
384                  soundbuftime=SendDlgItemMessage(hwndDlg,128,TBM_GETPOS,0,0);\r
385                  sprintf(tbuf,"%d",soundbuftime);\r
386                  SetDlgItemText(hwndDlg,666,(LPTSTR)tbuf);\r
387                 }\r
388                 break;\r
389    case WM_CLOSE:\r
390    case WM_QUIT: goto gornk;\r
391    case WM_COMMAND:\r
392                 if(!(wParam>>16))\r
393                 switch(wParam&0xFFFF)\r
394                 {\r
395                  case 1:\r
396                         gornk:\r
397                         soundoptions=0;\r
398                         if(IsDlgButtonChecked(hwndDlg,122)==BST_CHECKED)\r
399                          soundoptions|=SO_FORCE8BIT;\r
400                         if(IsDlgButtonChecked(hwndDlg,123)==BST_CHECKED)\r
401                          soundoptions|=SO_SECONDARY;\r
402                         if(IsDlgButtonChecked(hwndDlg,124)==BST_CHECKED)\r
403                          soundoptions|=SO_GFOCUS;\r
404                         if(IsDlgButtonChecked(hwndDlg,126)==BST_CHECKED)\r
405                          soundo=1;\r
406                         else\r
407                          soundo=0;\r
408                         x=GetDlgItemInt(hwndDlg,200,0,0);\r
409                         if(x<8192 || x>65535)\r
410                         {\r
411                          FCEUD_PrintError("Sample rate is out of range(8192-65535).");\r
412                          break;\r
413                         }\r
414                         else\r
415                          soundrate=x;\r
416 \r
417                         soundvolume=200-SendDlgItemMessage(hwndDlg,500,TBM_GETPOS,0,0);\r
418                         FCEUI_SetSoundVolume(soundvolume);\r
419                         soundsleep=SendDlgItemMessage(hwndDlg,129,CB_GETCURSEL,0,(LPARAM)(LPSTR)0);\r
420                         EndDialog(hwndDlg,0);\r
421                         break;\r
422                }\r
423               }\r
424   return 0;\r
425 }\r
426 \r
427 \r
428 void ConfigSound(void)\r
429 {\r
430  int backo=soundo,sr=soundrate;\r
431  int so=soundoptions;\r
432 \r
433  DialogBox(fceu_hInstance,"SOUNDCONFIG",hAppWnd,SoundConCallB);\r
434 \r
435  if(((backo?1:0)!=(soundo?1:0)))\r
436  {\r
437   if(!soundo)\r
438    TrashSound();\r
439   else\r
440    soundo=InitSound();\r
441  }\r
442  else if(( soundoptions!=so || (sr!=soundrate)) && soundo)\r
443  {\r
444     TrashSound();\r
445     soundo=InitSound();\r
446  }\r
447  soundbufsize=(soundbuftime*soundrate/1000);\r
448 }\r
449 \r
450 \r
451 void StopSound(void)\r
452 {\r
453  if(soundo)\r
454   IDirectSoundBuffer_Stop(ppbufw);\r
455 }\r
456 \r
457 #include "wave.c"\r