1
2 /*
3 # Sfront, a SAOL to C translator
4 # This file: Win32 DirectSound audio driver for sfront
5 #
6 # Copyright (c) 1999-2006, Regents of the University of California
7 # All rights reserved.
8 #
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions are
11 # met:
12 #
13 # Redistributions of source code must retain the above copyright
14 # notice, this list of conditions and the following disclaimer.
15 #
16 # Redistributions in binary form must reproduce the above copyright
17 # notice, this list of conditions and the following disclaimer in the
18 # documentation and/or other materials provided with the distribution.
19 #
20 # Neither the name of the University of California, Berkeley nor the
21 # names of its contributors may be used to endorse or promote products
22 # derived from this software without specific prior written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #
36 # Author: Professor John Wawrzynek, UCB CS Division
37 # Maintainance Email To: John Lazzaro, lazzaro@cs.berkeley.edu
38 */
39
40 //////////////////////////
41 // dsound output
42 // by Vincent Siliakus & Thomas Jongepier
43 // november 1999
44 //////////////////////////
45 #include <windows.h>
46 #include <windowsx.h>
47 #include <mmsystem.h>
48 #include <dsound.h>
49 #include <objbase.h>
50 #include <initguid.h>
51
52 #include <stdio.h>
53
54 #define ASYS_SUCCESS ASYS_DONE
55
56 #if defined(ASYS_HASOUTPUT)
57
58
59 /* Global variables for audio output.
60 */
61
62
63 char asyso_playing = 0;
64 int asyso_wrtCursor = 0;
65 int asyso_bufsize; /* buffer size in
66 bytes */
67 int asyso_blksize; /* size preferred by
68 sfront */
69
70 double asyso_speriod;
71 LPDIRECTSOUND lpDirectSound;
72 LPDIRECTSOUNDBUFFER lpDsb;
73 LPDIRECTSOUNDBUFFER lpPrDsb;
74 LPDIRECTSOUNDNOTIFY lpDsNotify;
75 HANDLE *hBuffPos;
76 DSBPOSITIONNOTIFY *PositionNotify;
77 PCMWAVEFORMAT pcmwfp;
78 PCMWAVEFORMAT pcmwf;
79 DSBUFFERDESC dsprbdescp;
80 DSBUFFERDESC dsbdesc;
81 DSCAPS dscaps;
82 DSBCAPS pr,sec;
83 HRESULT hr;
84 unsigned int ds_numofsubbuff;
85 unsigned int ds_subbuffercounter=0;
86 unsigned int ds_writeposition=0;
87 unsigned int ds_counter=0;
88 unsigned int ds_resetwritecrspoint=0;
89 unsigned int SECBUFFBYTESIZE;
90 unsigned int SUBBUFFBYTESIZE;
91 unsigned int SUBBUFFSAMPLESIZE;
92 unsigned int WRITEPOSITIONOFFSET;
93 unsigned int TIMES;
94 unsigned int EXCEPTIONTIMES;
95
96
97 void DevCaps(DSCAPS *);
98 void PrBuffCaps(DSBCAPS *);
99 void SecBuffCaps(DSBCAPS *);
100 void WaitForNotification();
101 void Close();
102 void Play();
103 void Stop();
104 int InitOutput(unsigned int);
105
DevCaps(DSCAPS * DSCaps)106 void DevCaps(DSCAPS *DSCaps)
107 {
108 DSCaps->dwSize = sizeof(DSCAPS);
109 IDirectSound_GetCaps(lpDirectSound,DSCaps);
110 }
111
PrBuffCaps(DSBCAPS * PrBCaps)112 void PrBuffCaps(DSBCAPS *PrBCaps)
113 {
114 PrBCaps->dwSize = sizeof(DSBCAPS);
115 IDirectSoundBuffer_GetCaps(lpPrDsb,PrBCaps);
116 }
117
SecBuffCaps(DSBCAPS * SecBCaps)118 void SecBuffCaps(DSBCAPS *SecBCaps)
119 {
120 SecBCaps->dwSize = sizeof(DSBCAPS);
121 IDirectSoundBuffer_GetCaps(lpDsb,SecBCaps);
122 }
123
WaitForNotification()124 void WaitForNotification()
125 {
126 DWORD EventNumber = (WaitForMultipleObjects
127 (ds_numofsubbuff, hBuffPos, FALSE, INFINITE));
128 //ResetEvent(hBuffPos[EventNumber]); //for manual ResetEvent.
129 }
130
Close()131 void Close()
132 {
133 for(ds_counter=0;ds_counter<ds_numofsubbuff;ds_counter++)
134 CloseHandle(hBuffPos[ds_counter]);
135 IDirectSoundBuffer_Release(lpDsb);
136 IDirectSound_Release(lpDirectSound);
137 }
138
Play()139 void Play()
140 {
141 IDirectSoundBuffer_Play(lpDsb,0,0,DSBPLAY_LOOPING );
142 }
143
Stop()144 void Stop()
145 {
146 IDirectSoundBuffer_Stop(lpDsb);
147 }
148
149
InitOutput(unsigned int sys_osize,unsigned int samplerate,unsigned int numchannels)150 int InitOutput(unsigned int sys_osize,
151 unsigned int samplerate,
152 unsigned int numchannels)
153 {
154 HWND hwnd = GetForegroundWindow();
155 if(hwnd==NULL)hwnd = GetDesktopWindow();
156 SECBUFFBYTESIZE = sys_osize*16;//use 16 or 32
157 SUBBUFFBYTESIZE = sys_osize;
158 SUBBUFFSAMPLESIZE= sys_osize/2;
159 WRITEPOSITIONOFFSET = SUBBUFFBYTESIZE;
160 TIMES=SECBUFFBYTESIZE/SUBBUFFBYTESIZE;
161 EXCEPTIONTIMES = (WRITEPOSITIONOFFSET/SUBBUFFBYTESIZE);
162 ds_resetwritecrspoint =
163 SECBUFFBYTESIZE-(EXCEPTIONTIMES*SUBBUFFBYTESIZE);
164 ds_numofsubbuff=SECBUFFBYTESIZE/SUBBUFFBYTESIZE;
165 ////////////////////////////////
166 //dsound init
167 ////////////////////////////////
168 //Create DirectSound Object
169 if FAILED(DirectSoundCreate(NULL, &lpDirectSound, NULL))
170 return ASYS_ERROR;
171 //
172 //Set Cooperative level of DirectSound Object
173 if FAILED(IDirectSound_SetCooperativeLevel(lpDirectSound,hwnd,
174 DSSCL_PRIORITY))return ASYS_ERROR;
175 //
176 ////////////////////////////////
177 // Set up wave format structure.
178 memset(&pcmwfp, 0, sizeof(PCMWAVEFORMAT));
179 pcmwfp.wf.wFormatTag = WAVE_FORMAT_PCM;
180 pcmwfp.wf.nChannels = numchannels;
181 pcmwfp.wf.nSamplesPerSec = samplerate;
182 pcmwfp.wf.nBlockAlign = 2*numchannels;
183 pcmwfp.wf.nAvgBytesPerSec =
184 pcmwfp.wf.nSamplesPerSec * pcmwfp.wf.nBlockAlign;
185 pcmwfp.wBitsPerSample = 16;
186 //
187 ////////////////////////////////
188 // Set up DSBUFFERDESC structure.
189 memset(&dsprbdescp, 0, sizeof(DSBUFFERDESC)); // Zero it out.
190 dsprbdescp.dwSize = sizeof(DSBUFFERDESC);
191 dsprbdescp.dwFlags = DSBCAPS_PRIMARYBUFFER;
192 dsprbdescp.dwBufferBytes = 0;
193 dsprbdescp.lpwfxFormat = NULL;
194 //
195 ////////////////////////////////
196 // Create Primary buffer.
197 if FAILED(IDirectSound_CreateSoundBuffer(lpDirectSound,&dsprbdescp,
198 &lpPrDsb, NULL))return ASYS_ERROR;
199 // Set primary buffer to desired format.
200 if FAILED(IDirectSoundBuffer_SetFormat(lpPrDsb,
201 (LPWAVEFORMATEX)&pcmwfp))return ASYS_ERROR;
202 //start playing (no sound yet)
203 IDirectSoundBuffer_Play(lpPrDsb,0,0,DSBPLAY_LOOPING);
204 //
205 // Set up wave format structure.
206 memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
207 pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM;
208 pcmwf.wf.nChannels = numchannels;
209 pcmwf.wf.nSamplesPerSec = samplerate;
210 pcmwf.wf.nBlockAlign = 2*numchannels;
211 pcmwf.wf.nAvgBytesPerSec =
212 pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign;
213 pcmwf.wBitsPerSample = 16;
214 //
215 // Set up DSBUFFERDESC structure.
216 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); // Zero it out.
217 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
218 // Need default controls (pan, volume, frequency).
219 dsbdesc.dwFlags =
220 DSBCAPS_GLOBALFOCUS
221 |DSBCAPS_GETCURRENTPOSITION2
222 |DSBCAPS_CTRLPOSITIONNOTIFY ;
223 dsbdesc.dwBufferBytes = SECBUFFBYTESIZE;
224 dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
225 //
226 ////////////////////////////////
227 if FAILED(IDirectSound_CreateSoundBuffer(lpDirectSound,&dsbdesc,
228 &lpDsb, NULL))return ASYS_ERROR;
229 //
230 PositionNotify=(DSBPOSITIONNOTIFY*)malloc(ds_numofsubbuff*
231 (sizeof(DSBPOSITIONNOTIFY)));
232 hBuffPos=(HANDLE*)malloc(ds_numofsubbuff*sizeof(HANDLE));
233 //
234 for(ds_counter=0;ds_counter<ds_numofsubbuff;ds_counter++){
235 hBuffPos[ds_counter]=CreateEvent(NULL,FALSE,FALSE,NULL);
236 PositionNotify[ds_counter].dwOffset = SUBBUFFBYTESIZE*ds_counter;
237 PositionNotify[ds_counter].hEventNotify=hBuffPos[ds_counter];
238 }
239 //
240 if FAILED(IDirectSoundBuffer_QueryInterface(lpDsb,
241 &IID_IDirectSoundNotify,(LPVOID *)&lpDsNotify))return ASYS_ERROR;
242 //
243 IDirectSoundNotify_SetNotificationPositions(lpDsNotify,ds_numofsubbuff,
244 PositionNotify);
245 //\\\\\\\\\\\\\\\
246 //
247 Play();
248 return ASYS_SUCCESS;
249 }
250
251
252 #endif
253
254 #if (defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT))
255
256 /* Opens waveform output for a given srate/channels.
257 */
258
asys_osetup(int srate,int ochannels,int osample,char * oname,int toption)259 int asys_osetup(int srate, int ochannels, int osample,
260 char * oname, int toption)
261 {
262
263 int osize = ASYS_OCHAN*ACYCLE;
264
265 InitOutput(osize,srate,ochannels);
266 return ASYS_SUCCESS;
267 }
268
269 #endif
270
271 #if defined(ASYS_HASOUTPUT)
272
273 /*
274 * Shuts down audio system.
275 */
276
asys_oshutdown(void)277 void asys_oshutdown(void)
278
279 {
280 Stop();
281 Close();
282 }
283
284 #endif
285
286 #if defined(ASYS_HASOUTPUT)
287
asys_putbuf(short ** buffer,int * osize)288 int asys_putbuf(short **buffer, int * osize)
289
290 {
291 DWORD dwBytes;
292 *osize=SUBBUFFSAMPLESIZE;
293 ds_writeposition=ds_subbuffercounter*SUBBUFFBYTESIZE;
294 WaitForNotification();
295 if(ds_writeposition>ds_resetwritecrspoint)
296 {
297 ds_subbuffercounter=0;
298 ds_writeposition=0;
299 }
300 hr = IDirectSoundBuffer_Unlock(lpDsb,*buffer,
301 SUBBUFFBYTESIZE, NULL,NULL);
302 hr = IDirectSoundBuffer_Lock(lpDsb,ds_writeposition,
303 SUBBUFFBYTESIZE, (LPVOID*)buffer,&dwBytes, NULL, 0, NULL);
304 ds_subbuffercounter++;
305 return ASYS_DONE;
306 }
307
308 /****************************************************************/
309 /* creates buffer, and generates starting silence */
310 /****************************************************************/
311
asys_preamble(ASYS_OTYPE * asys_obuf[],int * osize)312 int asys_preamble(ASYS_OTYPE * asys_obuf[], int * osize)
313
314 {
315 int i;
316
317 /* emulates old interface, since I don't understand asys_putbuf */
318 /* well enough to make direct modifications */
319
320 /* set up first call, which allocates buffer */
321
322 *asys_obuf = NULL;
323 *osize = 0;
324
325 if (asys_putbuf(asys_obuf, osize) == ASYS_ERROR)
326 return ASYS_ERROR;
327
328 /* clear allocated buffer */
329
330 for (i = 0; i < (*osize); i++)
331 (*asys_obuf)[i] = 0;
332
333 /* send the 4 silence putbuf's of old API */
334
335 for(i = 0; i < 4; i++)
336 if (asys_putbuf(asys_obuf, osize) == ASYS_ERROR)
337 return ASYS_ERROR;
338
339 return ASYS_DONE;
340 }
341
342
343 #endif
344
345
346 /* Currently not supported audio input. All routines are dummies.
347 */
348
349 #if (!defined(ASYS_HASOUTPUT) && defined(ASYS_HASINPUT))
350
351 /* Initializes audio input.
352 */
asys_isetup(int srate,int ichannels,int isample,char * iname,int toption)353 int asys_isetup(int srate, int ichannels, int isample,
354 char * iname, int toption)
355
356 {
357 return ASYS_ERROR;
358 }
359
360 #endif
361
362 #if (defined(ASYS_HASOUTPUT) && defined(ASYS_HASINPUT))
363
364 /* Initialized bidirectional audio flows. */
asys_iosetup(int srate,int ichannels,int ochannels,int isample,int osample,char * iname,char * oname,int toption)365 int asys_iosetup(int srate, int ichannels, int ochannels,
366 int isample, int osample,
367 char * iname, char * oname, int toption)
368
369 {
370 return ASYS_ERROR;
371 }
372
373 #endif
374
375 #if defined(ASYS_HASINPUT)
376
377 /* Shuts down audio input device.
378 */
379
asys_ishutdown(void)380 void asys_ishutdown(void)
381
382 {
383 }
384
385 #endif
386
387 #if defined(ASYS_HASINPUT)
388
389 /* Gets one frame of audio from input.
390 */
391
asys_getbuf(ASYS_ITYPE * asys_ibuf[],int * isize)392 int asys_getbuf(ASYS_ITYPE * asys_ibuf[], int * isize)
393 {
394 return ASYS_ERROR;
395 }
396
397 #endif
398
399
400 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
401 /* ksync() and ksyncinit() for -playback and -timesync modes */
402 /*_____________________________________________________________*/
403
404 #if defined(ASYS_KSYNC)
405
406 #if (ASYS_TIMEOPTION != ASYS_TIMESYNC)
407
408 #include <windows.h>
409 #include <windowsx.h>
410 #include <winbase.h>
411 #include <mmsystem.h>
412
413 #define SYNC_MAXCOUNT 4294967296L
414 #define SYNC_KMTIME EV(KUTIME)/1000
415
416 DWORD sync_last, sync_this, sync_delay;
417 TIMECAPS sync_tc;
418 BOOL sync_waitFlag;
419
420 /***********************************************************/
421 /* support routines */
422 /***********************************************************/
423
sync_callBack(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)424 void CALLBACK sync_callBack(UINT uID, UINT uMsg,
425 DWORD dwUser, DWORD dw1, DWORD dw2)
426 {
427 sync_waitFlag = TRUE;
428 }
429
sync_sleep(UINT ms)430 BOOL sync_sleep(UINT ms)
431 {
432 sync_waitFlag = FALSE;
433 if FAILED(timeSetEvent(ms, (UINT) 1, &sync_callBack, 0L, TIME_ONESHOT))
434 {
435 fprintf(stderr, "timeSetEvent error.\n");
436 exit(-1);
437 }
438 // spin wait
439 while (!sync_waitFlag) {}
440 return TRUE;
441 }
442
443 /***********************************************************/
444 /* synchronizes on k-rate boundaries */
445 /***********************************************************/
446
ksync()447 float ksync()
448
449 {
450 float ret;
451
452 sync_this = timeGetTime();
453
454 if (sync_this<sync_last) // timer wrap around
455 sync_last = (DWORD) (sync_last - SYNC_MAXCOUNT) + SYNC_KMTIME;
456 else
457 sync_last += SYNC_KMTIME;
458
459 sync_delay = sync_last - sync_this;
460 if (sync_delay>0)
461 {
462 /* sync_sleep(sync_delay); */ /* can this be safely commented out? */
463 ret = (SYNC_KMTIME - sync_delay)/SYNC_KMTIME;
464 }
465 else
466 ret = 1.0F;
467
468 return ret;
469 }
470
471 /***********************************************************/
472 /* initializes k-rate boundaries sync */
473 /***********************************************************/
474
ksyncinit()475 void ksyncinit()
476
477 {
478 // Set 1ms time resolution
479 if FAILED(timeGetDevCaps(&sync_tc, sizeof(TIMECAPS)))
480 {
481 fprintf(stderr, "TimeGetDevCaps error.\n");
482 exit(-1);
483 }
484 sync_last = timeGetTime();
485 }
486
487 #endif
488
489
490 #if (ASYS_TIMEOPTION == ASYS_TIMESYNC)
491
492 #include <windows.h>
493 #include <windowsx.h>
494 #include <winbase.h>
495 #include <mmsystem.h>
496
497 #define SYNC_MAXCOUNT 4294967296L
498 #define SYNC_KMTIME EV(KUTIME)/1000
499
500 DWORD sync_last, sync_this, sync_delay;
501 TIMECAPS sync_tc;
502 BOOL sync_waitFlag;
503
504 /***********************************************************/
505 /* support routines */
506 /***********************************************************/
507
sync_callBack(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)508 void CALLBACK sync_callBack(UINT uID, UINT uMsg,
509 DWORD dwUser, DWORD dw1, DWORD dw2)
510 {
511 sync_waitFlag = TRUE;
512 }
513
sync_sleep(UINT ms)514 BOOL sync_sleep(UINT ms)
515 {
516 sync_waitFlag = FALSE;
517 if FAILED(timeSetEvent(ms, (UINT) 1, &sync_callBack, 0L, TIME_ONESHOT))
518 {
519 fprintf(stderr, "timeSetEvent error.\n");
520 exit(-1);
521 }
522 // spin wait
523 while (!sync_waitFlag) {}
524 return TRUE;
525 }
526
527 /***********************************************************/
528 /* synchronizes on k-rate boundaries */
529 /***********************************************************/
530
ksync()531 float ksync()
532
533 {
534 float ret;
535
536 sync_this = timeGetTime();
537
538 if (sync_this<sync_last) // timer wrap around
539 sync_last = (DWORD) (sync_last - SYNC_MAXCOUNT) + SYNC_KMTIME;
540 else
541 sync_last += SYNC_KMTIME;
542
543 sync_delay = sync_last - sync_this;
544 if (sync_delay>0)
545 {
546 sync_sleep(sync_delay);
547 ret = (SYNC_KMTIME - sync_delay)/SYNC_KMTIME;
548 }
549 else
550 ret = 1.0F;
551
552 return ret;
553 }
554
555 /***********************************************************/
556 /* initializes k-rate boundaries sync */
557 /***********************************************************/
558
ksyncinit()559 void ksyncinit()
560
561 {
562 // Set 1ms time resolution
563 if FAILED(timeGetDevCaps(&sync_tc, sizeof(TIMECAPS)))
564 {
565 fprintf(stderr, "TimeGetDevCaps error.\n");
566 exit(-1);
567 }
568 sync_last = timeGetTime();
569 }
570
571 #endif
572
573 #endif /* ASYS_KSYNC */
574