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, ¤t_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