1 /*
2  * Copyright (C) 2001-2018 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * audio_directx_out.c, direct sound audio output plugin for xine
21  * by Matthew Grooms <elon@altavista.com>
22  */
23 
24 /*
25  * TODO:
26  *   - stop looping on stop (requires AO_CTRL_PLAY_STOP?)
27  *   - posibility of bad state when buffer overrun
28  */
29 
30 typedef unsigned char boolean;
31 
32 #include <windows.h>
33 #include <dsound.h>
34 
35 #define LOG_MODULE "audio_directx_out"
36 #define LOG_VERBOSE
37 /*
38 #define LOG
39 */
40 
41 #include <xine/audio_out.h>
42 #include <xine/xine_internal.h>
43 
44 
45 #define MAX_CHANNELS	          6
46 #define MAX_BITS		  16
47 #define MAX_SAMPLE_RATE		  44100
48 #define SOUND_BUFFER_DIV	  32
49 #define SOUND_BUFFER_MAX	  MAX_CHANNELS * (MAX_BITS / 8) * (((MAX_SAMPLE_RATE / SOUND_BUFFER_DIV) + 1) & ~1)
50 
51 #define DSBUFF_INIT		  0
52 #define DSBUFF_LEFT		  1
53 #define DSBUFF_RIGHT	          2
54 
55 #define AO_DIRECTX_IFACE_VERSION  9
56 
57 /*****************************************************************************
58  * DirectDraw GUIDs.
59  * Defining them here allows us to get rid of the dxguid library during
60  * the linking stage.
61  *****************************************************************************/
62 #if 1
63 static const GUID xine_IID_IDirectSoundNotify = {
64 	0xB0210783,0x89CD,0x11D0,{0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16}
65 };
66 #ifdef IID_IDirectSoundNotify
67 #  undef IID_IDirectSoundNotify
68 #endif
69 #define IID_IDirectSoundNotify xine_IID_IDirectSoundNotify
70 #endif
71 
72 
73 /* -----------------------------------------
74  *
75  * ao_directx driver struct
76  *
77  * ----------------------------------------- */
78 
79 typedef struct {
80   ao_driver_t		ao_driver;
81   int			capabilities;
82 
83   xine_t               *xine;
84 
85   /* directx objects */
86   LPDIRECTSOUND         dsobj;
87   LPDIRECTSOUNDBUFFER   dsbuffer;
88   DSBCAPS		dsbcaps;
89   LPDIRECTSOUNDNOTIFY   notify;
90   DSBPOSITIONNOTIFY	notify_events[ 2 ];
91 
92   /* buffer vars */
93   long                  buffer_size;
94   int                   write_status;
95   unsigned long         write_pos;
96 
97   uint8_t               prebuff[ SOUND_BUFFER_MAX ];
98   uint32_t              prebuff_size;
99 
100   /* current buffer properties */
101   int		        bits;
102   int		        rate;
103   int		        chnn;
104   int		        frsz;
105 
106   /* current mixer settings */
107   int                   mute;
108   int		        volume;
109 } ao_directx_t;
110 
111 typedef struct {
112   audio_driver_class_t  driver_class;
113   xine_t               *xine;
114 } audiox_class_t;
115 
116 /* -------------------------------------------
117  *
118  * BEGIN : Direct Sound and win32 handlers
119  *         for xine audio output plugins.
120  *
121  * ------------------------------------------- */
122 
123 /* Display formatted error message in
124  * popup message box. */
125 
Error(HWND hwnd,LPCSTR szfmt,...)126 static void Error( HWND hwnd, LPCSTR szfmt, ... )
127 {
128   char tempbuff[ 256 ];
129   *tempbuff = 0;
130   wvsprintf(	&tempbuff[ strlen( tempbuff ) ], szfmt, ( char * )( &szfmt + 1 ) );
131   MessageBox( hwnd, tempbuff, "Error", MB_ICONERROR | MB_OK | MB_APPLMODAL | MB_SYSTEMMODAL );
132 }
133 
134 /* Create our direct sound object and
135  * set the cooperative level. */
136 
CreateDirectSound(ao_directx_t * ao_directx)137 static boolean CreateDirectSound( ao_directx_t * ao_directx )
138 {
139   DSCAPS        dscaps;
140   HWND          hxinewnd;
141 
142   lprintf("CreateDirectSound(%p) Enter\n", (void *)ao_directx);
143 
144   /* create direct sound object */
145 
146   if( DirectSoundCreate( 0, &ao_directx->dsobj, 0 ) != DS_OK )
147     {
148       Error( 0, "DirectSoundCreate : Unable to create direct sound object" );
149       lprintf("CreateDirectSound() Exit! Returning False\n");
150       return FALSE;
151     }
152 
153 
154   /* try to get our current xine window */
155 
156   hxinewnd = FindWindow( "xinectrlwindow", "xine" );
157   if( !hxinewnd )
158     hxinewnd = GetDesktopWindow();
159 
160   /* set direct sound cooperative level */
161 
162   if( IDirectSound_SetCooperativeLevel( ao_directx->dsobj, hxinewnd, DSSCL_EXCLUSIVE ) != DS_OK )
163     {
164       Error( 0, "IDirectSound_SetCooperativeLevel : could not set direct sound cooperative level" );
165       lprintf("CreateDirectSound() Exit! Returning False\n");
166       return FALSE;
167     }
168 
169   /* get the direct sound device caps */
170 
171   memset( &dscaps, 0, sizeof( dscaps ) );
172   dscaps.dwSize = sizeof( dscaps );
173   if( IDirectSound_GetCaps( ao_directx->dsobj, &dscaps ) != DS_OK )
174     {
175       Error( 0, "IDirectSound_GetCaps : Unable to get direct sound device capabilities" );
176       lprintf("CreateDirectSound() Exit! Returning False\n");
177       return FALSE;
178     }
179 
180   lprintf("CreateDirectSound() Exit! Returning True\n");
181   return TRUE;
182 }
183 
184 /* Destroy all direct sound allocated
185  * resources. */
186 
DestroyDirectSound(ao_directx_t * ao_directx)187 static void DestroyDirectSound( ao_directx_t * ao_directx )
188 {
189 
190   lprintf("DestroyDirectSound(%p) Enter\n", (void *)ao_directx);
191 
192   if( ao_directx->dsobj )
193     {
194       lprintf("IDirectSound_Release()\n");
195 
196       IDirectSound_Release( ao_directx->dsobj );
197       ao_directx->dsobj = 0;
198     }
199 
200   lprintf("DestroyDirectSound() Exit\n");
201 }
202 
203 /* Used to fill our looping sound buffer
204  * with data. */
205 
FillSoundBuffer(ao_directx_t * ao_directx,int code,unsigned char * samples)206 static uint32_t FillSoundBuffer( ao_directx_t * ao_directx, int code, unsigned char * samples )
207 {
208   uint8_t *     buff_pointer;   /* pointer inside circular buffer */
209   DWORD         buff_length;    /* bytes locked by pointer */
210   uint32_t      half_size;      /* half our sound buffer size */
211   uint32_t      result;         /* error result */
212 
213 #ifdef LOG
214   if ((void*)samples != (void*)0)
215     printf("audio_directx_out: FillSoundBuffer(%08x, %d, Null) Enter\n", (unsigned long)ao_directx, code);
216   else
217     printf("audio_directx_out: FillSoundBuffer(%08x, %d, Null) Enter\n", (unsigned long)ao_directx, code);
218 #endif
219 
220   half_size = ao_directx->buffer_size / 2;
221 
222   if( code == DSBUFF_INIT )
223     {
224       lprintf("FillSoundBuffer: DSBUFF_INIT\n");
225 
226       /* set our new status code */
227 
228       ao_directx->write_status = DSBUFF_RIGHT;
229 
230       /* lock our sound buffer for write access */
231 
232       result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer,
233 					0, 0,
234 					(LPVOID *)&buff_pointer, &buff_length,
235 					NULL, 0, DSBLOCK_ENTIREBUFFER );
236       if( result  != DS_OK )
237 	{
238 	  Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" );
239 	  return 0;
240 	}
241 
242       /* clear our entire sound buffer */
243 
244       memset( buff_pointer, 0, buff_length );
245 
246       /* unlock our sound buffer */
247 
248       if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer,
249 				     buff_pointer, buff_length,
250 				     0, 0 ) != DS_OK )
251 	{
252 	  Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" );
253 	  return 0;
254 	}
255 
256       /* start the buffer playing */
257 
258       if( IDirectSoundBuffer_Play( ao_directx->dsbuffer, 0, 0, DSBPLAY_LOOPING ) != DS_OK )
259 	{
260 	  Error( 0, "IDirectSoundBuffer_Play : could not play sound buffer" );
261 	  return 0 ;
262 	}
263       else
264 	IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume );
265     }
266   else if( code == DSBUFF_LEFT )
267     {
268       lprintf("FillSoundBuffer: DSBUFF_LEFT\n");
269       /* set our new status code */
270 
271       ao_directx->write_status = DSBUFF_RIGHT;
272 
273       /* lock our sound buffer for write access */
274 
275       result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer,
276 					0, half_size,
277 					(LPVOID *)&buff_pointer, &buff_length,
278 					0, 0, 0 );
279       if( result  != DS_OK )
280 	{
281 	  Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" );
282 	  return 0;
283 	}
284 
285       /* write data to our sound buffer */
286 
287       memcpy( buff_pointer, samples, buff_length );
288 
289       /* unlock our sound buffer */
290 
291       if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer,
292 				     buff_pointer, buff_length,
293 				     0, 0 ) != DS_OK )
294 	{
295 	  Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" );
296 	  return 0;
297 	}
298 
299     }
300   else if( code == DSBUFF_RIGHT )
301     {
302       lprintf("FillSoundBuffer: DSBUFF_RIGHT\n");
303       /* set our new status code */
304 
305       ao_directx->write_status = DSBUFF_LEFT;
306 
307       /* lock our sound buffer for write access */
308 
309       result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer,
310 					half_size, half_size,
311 					(LPVOID *)&buff_pointer, &buff_length,
312 					0, 0, 0 );
313       if( result  != DS_OK )
314 	{
315 	  Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" );
316 	  return 0;
317 	}
318 
319       /* write data to our sound buffer */
320 
321       memcpy( buff_pointer, samples, buff_length );
322 
323       /* unlock our sound buffer */
324 
325       if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer,
326 				     buff_pointer, buff_length,
327 				     0, 0 ) != DS_OK )
328 	{
329 	  Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" );
330 	  return 0;
331 	}
332     }
333 
334   lprintf("FillSoundBuffer() Exit\n");
335 
336   return buff_length;
337 }
338 
339 
340 /* Destroy all direct sound buffer allocated
341  * resources. */
342 
DestroySoundBuffer(ao_directx_t * ao_directx)343 static void DestroySoundBuffer( ao_directx_t * ao_directx )
344 {
345   lprintf("DestroySoundBuffer(%p) Enter\n", (void *)ao_directx);
346 
347   /* stop our buffer and zero it out */
348 
349   if( ao_directx->dsbuffer )
350     {
351       IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN );
352       IDirectSoundBuffer_Stop( ao_directx->dsbuffer );
353       FillSoundBuffer( ao_directx, DSBUFF_INIT, 0 );
354     }
355 
356   /* release our notification events */
357 
358   if( ao_directx->notify_events[ 0 ].hEventNotify )
359     {
360       CloseHandle( ao_directx->notify_events[ 0 ].hEventNotify );
361       ao_directx->notify_events[ 0 ].hEventNotify = 0;
362     }
363 
364   if( ao_directx->notify_events[ 1 ].hEventNotify )
365     {
366       CloseHandle( ao_directx->notify_events[ 1 ].hEventNotify );
367       ao_directx->notify_events[ 1 ].hEventNotify = 0;
368     }
369 
370   /* release our buffer notification interface */
371 
372   if( ao_directx->notify )
373     {
374       IDirectSoundNotify_Release( ao_directx->notify );
375       ao_directx->notify = 0;
376     }
377 
378   /* release our direct sound buffer */
379 
380   if( ao_directx->dsbuffer )
381     {
382       IDirectSoundBuffer_Release( ao_directx->dsbuffer );
383       ao_directx->dsbuffer = 0;
384     }
385 
386   lprintf("DestroySoundBuffer() Exit\n");
387 }
388 
389 /* Used to create directx sound buffer,
390  * notification events, and initialize
391  * buffer to null sample data. */
392 
CreateSoundBuffer(ao_directx_t * ao_directx)393 static boolean CreateSoundBuffer( ao_directx_t * ao_directx )
394 {
395   DSBUFFERDESC	dsbdesc;
396   PCMWAVEFORMAT	pcmwf;
397 
398   lprintf("CreateSoundBuffer(%p) Enter\n", (void *)ao_directx);
399 
400   /* calculate buffer and frame size */
401 
402   ao_directx->frsz        = ( ao_directx->bits / 8 ) * ao_directx->chnn;
403   /* buffer size, must be even and aligned to frame size */
404   ao_directx->buffer_size = (ao_directx->frsz * ((ao_directx->rate / SOUND_BUFFER_DIV + 1) & ~1));
405 
406   /* release any existing sound buffer
407    * related resources */
408 
409   DestroySoundBuffer( ao_directx );
410 
411   /* create a secondary sound buffer */
412 
413   memset( &pcmwf, 0, sizeof( PCMWAVEFORMAT ) );
414   pcmwf.wBitsPerSample     = ( unsigned short ) ao_directx->bits;
415   pcmwf.wf.wFormatTag      = WAVE_FORMAT_PCM;
416   pcmwf.wf.nChannels       = ao_directx->chnn;
417   pcmwf.wf.nSamplesPerSec  = ao_directx->rate;
418   pcmwf.wf.nBlockAlign     = ao_directx->frsz;
419   pcmwf.wf.nAvgBytesPerSec = ao_directx->rate * ao_directx->frsz;
420 
421   memset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) );
422   dsbdesc.dwSize        = sizeof( DSBUFFERDESC );
423   dsbdesc.dwFlags       = (DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS |
424 			   DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY);
425   dsbdesc.dwBufferBytes = ao_directx->buffer_size;
426   dsbdesc.lpwfxFormat   = ( LPWAVEFORMATEX ) &pcmwf;
427 
428   if( IDirectSound_CreateSoundBuffer( ao_directx->dsobj, &dsbdesc,
429 				      &ao_directx->dsbuffer, 0 ) != DS_OK )
430     {
431       Error( 0, "IDirectSound_CreateSoundBuffer : Unable to create secondary sound buffer" );
432       return FALSE;
433     }
434 
435   /* get the buffer capabilities */
436 
437   memset( &ao_directx->dsbcaps, 0, sizeof( DSBCAPS ) );
438   ao_directx->dsbcaps.dwSize = sizeof( DSBCAPS );
439 
440   if( IDirectSound_GetCaps( ao_directx->dsbuffer, &ao_directx->dsbcaps ) != DS_OK )
441     {
442       Error( 0, "IDirectSound_GetCaps : Unable to get secondary sound buffer capabilities" );
443       return FALSE;
444     }
445 
446   /* create left side notification ( non-signaled ) */
447 
448   ao_directx->notify_events[ 0 ].hEventNotify = CreateEvent( NULL, FALSE, FALSE, NULL );
449 
450   /* create right side notification ( signaled ) */
451 
452   ao_directx->notify_events[ 1 ].hEventNotify = CreateEvent( NULL, FALSE, FALSE, NULL );
453 
454   if( !ao_directx->notify_events[ 0 ].hEventNotify || !ao_directx->notify_events[ 1 ].hEventNotify )
455     {
456       Error( 0, "CreateEvent : Unable to create sound notification events" );
457       return FALSE;
458     }
459 
460   /* get the direct sound notification interface */
461 
462   if( IDirectSoundBuffer_QueryInterface( ao_directx->dsbuffer,
463 					 &IID_IDirectSoundNotify,
464 					 (LPVOID *)&ao_directx->notify ) != DS_OK )
465     {
466       Error( 0, "IDirectSoundBuffer_QueryInterface : Unable to get notification interface" );
467       return FALSE;
468     }
469 
470   /* set notification events */
471 
472   ao_directx->notify_events[ 0 ].dwOffset = 0;
473   ao_directx->notify_events[ 1 ].dwOffset = ao_directx->buffer_size / 2;
474 
475   if( IDirectSoundNotify_SetNotificationPositions(  ao_directx->notify, 2,
476 						    ao_directx->notify_events ) != DS_OK )
477     {
478       Error( 0, "IDirectSoundNotify_SetNotificationPositions : Unable to set notification positions" );
479       return FALSE;
480     }
481 
482   /* DEBUG : set sound buffer volume */
483 
484   if( IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MAX ) != DS_OK )
485     {
486       Error( 0, "IDirectSoundBuffer_SetVolume : Unable to set sound buffer volume" );
487       return FALSE;
488     }
489 
490   /* initialize our sound buffer */
491 
492   IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN );
493   FillSoundBuffer( ao_directx, DSBUFF_INIT, 0 );
494 
495   return TRUE;
496 
497   lprintf("CreateSoundBuffer() Exit\n");
498 }
499 
500 /* -----------------------------------------
501  *
502  * BEGIN : Xine driver audio output plugin
503  *         handlers.
504  *
505  * ----------------------------------------- */
506 
ao_directx_control(ao_driver_t * this_gen,int cmd,...)507 static int ao_directx_control(ao_driver_t *this_gen, int cmd, ...) {
508   switch (cmd)
509     {
510 
511     case AO_CTRL_PLAY_PAUSE:
512       break;
513 
514     case AO_CTRL_PLAY_RESUME:
515       break;
516 
517     case AO_CTRL_FLUSH_BUFFERS:
518       break;
519     }
520 
521   return 0;
522 }
523 
524 
ao_directx_open(ao_driver_t * ao_driver,uint32_t bits,uint32_t rate,int mode)525 static int ao_directx_open( ao_driver_t * ao_driver, uint32_t bits, uint32_t rate, int mode )
526 {
527   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
528 
529   lprintf("ao_directx_open(%p, %u, %u, %u) Enter\n", (void *)ao_directx, bits, rate, mode);
530 
531   /* store input rate and bits */
532 
533   ao_directx->bits = bits;
534   ao_directx->rate = rate;
535 
536   /* store channel count */
537 
538   switch( mode )
539     {
540     case AO_CAP_MODE_MONO:
541       ao_directx->chnn = 1;
542       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_MONO mode\n" );
543       break;
544 
545     case AO_CAP_MODE_STEREO:
546       ao_directx->chnn = 2;
547       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_STEREO mode\n" );
548       break;
549 
550     case AO_CAP_MODE_4CHANNEL:
551       ao_directx->chnn = 4;
552       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_4CHANNEL mode\n" );
553       break;
554 
555     case AO_CAP_MODE_5CHANNEL:
556       ao_directx->chnn = 5;
557       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_5CHANNEL mode\n" );
558       break;
559 
560     case AO_CAP_MODE_5_1CHANNEL:
561       ao_directx->chnn = 6;
562       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_5_1CHANNEL mode\n" );
563       break;
564 
565     case AO_CAP_MODE_A52:
566     case AO_CAP_MODE_AC5:
567       return 0;
568     }
569 
570   if (!CreateSoundBuffer( ao_directx )) return 0;
571 
572   lprintf("ao_directx_open() Exit! Returning ao_directx->rate=%d\n", ao_directx->rate);
573 
574   return ao_directx->rate;
575 }
576 
ao_directx_num_channels(ao_driver_t * ao_driver)577 static int ao_directx_num_channels( ao_driver_t * ao_driver )
578 {
579   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
580   return ao_directx->chnn;
581 }
582 
ao_directx_bytes_per_frame(ao_driver_t * ao_driver)583 static int ao_directx_bytes_per_frame( ao_driver_t * ao_driver )
584 {
585   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
586   return ao_directx->frsz;
587 }
588 
ao_directx_get_gap_tolerance(ao_driver_t * ao_driver)589 static int ao_directx_get_gap_tolerance( ao_driver_t * ao_driver )
590 {
591   return 5000;
592 }
593 
ao_directx_delay(ao_driver_t * ao_driver)594 static int ao_directx_delay( ao_driver_t * ao_driver )
595 {
596   DWORD	         current_read;
597   DWORD	         bytes_left;
598   DWORD	         frames_left;
599   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
600 
601   lprintf("ao_directx_delay(%p) Enter\n", (void *)ao_directx);
602 
603   IDirectSoundBuffer_GetCurrentPosition( ao_directx->dsbuffer, &current_read, 0 );
604 
605   if( ao_directx->write_pos > current_read )
606     bytes_left = ( ao_directx->write_pos - current_read );
607   else
608     bytes_left = ( ao_directx->write_pos + ao_directx->buffer_size - current_read );
609 
610   frames_left = ( ao_directx->prebuff_size + bytes_left ) / ao_directx->frsz;
611 
612   lprintf("ao_directx_delay() Exit! Returning frames_left=%lu\n", (unsigned long)frames_left);
613 
614   return frames_left;
615 }
616 
ao_directx_write(ao_driver_t * ao_driver,int16_t * frame_buffer,uint32_t num_frames)617 static int ao_directx_write( ao_driver_t * ao_driver, int16_t * frame_buffer, uint32_t num_frames )
618 {
619   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
620   uint32_t	 frame_bytes;   /* how many bytes to lock */
621   uint32_t	 wrote;	        /* number of bytes written */
622   uint32_t	 half_size;     /* half our sound buffer size */
623 
624   lprintf("ao_directx_write(%p, %p, %u) Enter\n",
625 	  (void *)ao_directx, (void *)frame_buffer, num_frames);
626 
627   /* zero write counter */
628 
629   wrote = 0;
630 
631   /* calculate how many bytes in frame_buffer */
632 
633   frame_bytes = num_frames * ao_directx->frsz;
634 
635   /* calculate half our buffer size */
636 
637   half_size = ao_directx->buffer_size / 2;
638 
639   /* fill audio prebuff */
640 
641   memcpy( ao_directx->prebuff + ao_directx->prebuff_size, frame_buffer, frame_bytes );
642   ao_directx->prebuff_size = ao_directx->prebuff_size + frame_bytes;
643 
644   /* check to see if we have enough in prebuff to
645    * fill half of our sound buffer */
646   while( ao_directx->prebuff_size >= half_size )
647     {
648       /* write to our sound buffer */
649 
650       if( ao_directx->write_status == DSBUFF_LEFT )
651 	{
652 	  /* wait for our read pointer to reach the right half
653 	   * of our sound buffer, we only want to write to the
654 	   * left side */
655 
656 	  WaitForSingleObject( ao_directx->notify_events[ 1 ].hEventNotify, INFINITE );
657 
658 	  /* fill left half of our buffer */
659 
660 	  wrote = FillSoundBuffer( ao_directx, DSBUFF_LEFT, ao_directx->prebuff );
661 	}
662       else if( ao_directx->write_status == DSBUFF_RIGHT )
663 	{
664 	  /* wait for our read pointer to reach the left half,
665 	   * of our sound buffer, we only want to write to the
666 	   * right side */
667 
668 	  WaitForSingleObject( ao_directx->notify_events[ 0 ].hEventNotify, INFINITE );
669 
670 	  /* fill right half of our buffer */
671 
672 	  wrote = FillSoundBuffer( ao_directx, DSBUFF_RIGHT, ao_directx->prebuff );
673 	}
674 
675       /* calc bytes written and store position for next write */
676 
677       ao_directx->write_pos = ( ao_directx->write_pos + wrote ) % ao_directx->buffer_size;
678 
679       /* copy remaining contents of prebuff and recalc size */
680 
681       memcpy( ao_directx->prebuff, ao_directx->prebuff + wrote, ao_directx->prebuff_size - wrote );
682       ao_directx->prebuff_size = ao_directx->prebuff_size - wrote;
683     }
684 
685   lprintf("ao_directx_write() Exit! Returning num_frames=%u\n", num_frames);
686 
687   return num_frames;
688 }
689 
ao_directx_close(ao_driver_t * ao_driver)690 static void ao_directx_close( ao_driver_t * ao_driver )
691 {
692   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
693 
694   lprintf("ao_directx_close(%p) Enter\n", (void *)ao_directx);
695 
696   /* release any existing sound buffer
697    * related resources */
698 
699   DestroySoundBuffer( ao_directx );
700 
701   lprintf("ao_directx_close() Exit!\n");
702 }
703 
ao_directx_get_capabilities(ao_driver_t * ao_driver)704 static uint32_t ao_directx_get_capabilities( ao_driver_t * ao_driver )
705 {
706   return AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL;
707 }
708 
ao_directx_exit(ao_driver_t * ao_driver)709 static void ao_directx_exit( ao_driver_t * ao_driver )
710 {
711   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
712 
713   lprintf("ao_directx_exit(%p) Enter\n", (void *)ao_directx);
714 
715   /* release any existing sound buffer
716    * related resources */
717 
718   DestroySoundBuffer( ao_directx );
719 
720   /* release any existing direct sound
721    * related resources */
722 
723   DestroyDirectSound( ao_directx );
724 
725   /* free our driver */
726 
727   free( ao_directx );
728 
729   lprintf("ao_directx_exit() Exit!\n");
730 }
731 
ao_directx_get_property(ao_driver_t * ao_driver,int property)732 static int ao_directx_get_property( ao_driver_t * ao_driver, int property )
733 {
734   return 0;
735 }
736 
ao_directx_set_property(ao_driver_t * ao_driver,int property,int value)737 static int ao_directx_set_property( ao_driver_t * ao_driver, int property, int value )
738 {
739   ao_directx_t  *ao_directx = ( ao_directx_t * ) ao_driver;
740 
741   lprintf("ao_directx_set_property(%p, %d, %d) Enter\n",
742           (void *)ao_directx, property, value);
743 
744   switch( property )
745     {
746     case AO_PROP_PCM_VOL:
747     case AO_PROP_MIXER_VOL:
748       lprintf("ao_directx_set_property: AO_PROP_PCM_VOL|AO_PROP_MIXER_VOL\n");
749 
750       ao_directx->volume = value * ( DSBVOLUME_MIN / 100 / 3);
751 
752       if( !ao_directx->mute && ao_directx->dsbuffer )
753 	IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume );
754 
755       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG,
756 	      "ao_directx : volume set to %d - directX volume = %d\n", value, ao_directx->volume);
757 
758       return value;
759 
760       break;
761 
762     case AO_PROP_MUTE_VOL:
763       lprintf("ao_directx_set_property: AO_PROP_MUTE_VOL\n");
764 
765       ao_directx->mute = value;
766 
767       if( !ao_directx->mute && ao_directx->dsbuffer )
768 	IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume );
769 
770       if( ao_directx->mute && ao_directx->dsbuffer )
771 	IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN );
772 
773       xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : mute toggled" );
774 
775       return value;
776 
777       break;
778     }
779 
780   lprintf("ao_directx_set_property() Exit! Returning ~value=%d\n", ~value);
781 
782   return ~value;
783 }
784 
open_plugin(audio_driver_class_t * class_gen,const void * data)785 static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data)
786 {
787   audiox_class_t  *class = (audiox_class_t *) class_gen;
788   ao_directx_t    *ao_directx;
789 
790   ao_directx = calloc(1, sizeof(ao_directx_t));
791   if (!ao_directx)
792     return NULL;
793 
794   lprintf("open_plugin(%p, %p) Enter\n", (void *)class_gen, (void *)data);
795   lprintf("open_plugin: ao_directx=%p\n", (void *)ao_directx);
796 
797   ao_directx->xine                              = class->xine;
798 
799   ao_directx->ao_driver.get_capabilities        = ao_directx_get_capabilities;
800   ao_directx->ao_driver.get_property	        = ao_directx_get_property;
801   ao_directx->ao_driver.set_property            = ao_directx_set_property;
802   ao_directx->ao_driver.open	                = ao_directx_open;
803   ao_directx->ao_driver.num_channels            = ao_directx_num_channels;
804   ao_directx->ao_driver.bytes_per_frame	        = ao_directx_bytes_per_frame;
805   ao_directx->ao_driver.delay                   = ao_directx_delay;
806   ao_directx->ao_driver.write                   = ao_directx_write;
807   ao_directx->ao_driver.close                   = ao_directx_close;
808   ao_directx->ao_driver.exit                    = ao_directx_exit;
809   ao_directx->ao_driver.get_gap_tolerance       = ao_directx_get_gap_tolerance;
810   ao_directx->ao_driver.control                 = ao_directx_control;
811 
812   CreateDirectSound( ao_directx );
813 
814   lprintf("open_plugin() Exit! Returning ao_directx=%p\n", (void *)ao_directx);
815 
816   return &ao_directx->ao_driver;
817 }
818 
init_class(xine_t * xine,const void * data)819 static void *init_class (xine_t *xine, const void *data) {
820   audiox_class_t    *audiox;
821 
822   lprintf("init_class() Enter\n");
823 
824   /*
825    * from this point on, nothing should go wrong anymore
826    */
827   audiox = calloc(1, sizeof (audiox_class_t));
828   if (!audiox)
829     return NULL;
830 
831   audiox->driver_class.open_plugin     = open_plugin;
832   audiox->driver_class.identifier      = "DirectX";
833   audiox->driver_class.description     = N_("xine audio output plugin for win32 using directx");
834   audiox->driver_class.dispose         = default_audio_driver_class_dispose;
835 
836   audiox->xine                         = xine;
837 
838   lprintf("init_class() Exit! Returning audiox=%p\n", (void *)audiox);
839 
840   return audiox;
841 }
842 
843 static const ao_info_t ao_info_directx = {
844   .priority = 1,
845 };
846 
847 /*
848  * exported plugin catalog entry
849  */
850 const plugin_info_t xine_plugin_info[] EXPORTED = {
851   /* type, API, "name", version, special_info, init_function */
852   { PLUGIN_AUDIO_OUT, AO_DIRECTX_IFACE_VERSION, "directx", XINE_VERSION_CODE, &ao_info_directx, init_class },
853   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
854 };
855