]> git.sesse.net Git - vlc/blob - modules/audio_output/directx.c
* modules/audio_output/directx.c, modules/audio_output/waveout.c:
[vlc] / modules / audio_output / directx.c
1 /*****************************************************************************
2  * directx.c: Windows DirectX audio output method
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: directx.c,v 1.17 2003/04/07 16:02:08 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>                                                 /* ENOMEM */
28 #include <fcntl.h>                                       /* open(), O_WRONLY */
29 #include <string.h>                                            /* strerror() */
30
31 #include <stdlib.h>                            /* calloc(), malloc(), free() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35 #include "aout_internal.h"
36
37 #include <windows.h>
38 #include <mmsystem.h>
39 #include <dsound.h>
40
41 #define FRAME_SIZE 2048              /* The size is in samples, not in bytes */
42 #define FRAMES_NUM 4
43
44 /* frame buffer status */
45 #define FRAME_QUEUED 0
46 #define FRAME_EMPTY 1
47
48 /*****************************************************************************
49  * DirectSound GUIDs.
50  * Defining them here allows us to get rid of the dxguid library during
51  * the linking stage.
52  *****************************************************************************/
53 #include <initguid.h>
54 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
55
56 /*****************************************************************************
57  * Useful macros
58  *****************************************************************************/
59 #ifndef WAVE_FORMAT_IEEE_FLOAT
60 #   define WAVE_FORMAT_IEEE_FLOAT 0x0003
61 #endif
62
63 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
64 #   define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
65 #endif
66
67 #ifndef WAVE_FORMAT_EXTENSIBLE
68 #define  WAVE_FORMAT_EXTENSIBLE   0xFFFE
69 #endif
70
71 #ifndef SPEAKER_FRONT_LEFT
72 #   define SPEAKER_FRONT_LEFT             0x1
73 #   define SPEAKER_FRONT_RIGHT            0x2
74 #   define SPEAKER_FRONT_CENTER           0x4
75 #   define SPEAKER_LOW_FREQUENCY          0x8
76 #   define SPEAKER_BACK_LEFT              0x10
77 #   define SPEAKER_BACK_RIGHT             0x20
78 #   define SPEAKER_FRONT_LEFT_OF_CENTER   0x40
79 #   define SPEAKER_FRONT_RIGHT_OF_CENTER  0x80
80 #   define SPEAKER_BACK_CENTER            0x100
81 #   define SPEAKER_SIDE_LEFT              0x200
82 #   define SPEAKER_SIDE_RIGHT             0x400
83 #   define SPEAKER_TOP_CENTER             0x800
84 #   define SPEAKER_TOP_FRONT_LEFT         0x1000
85 #   define SPEAKER_TOP_FRONT_CENTER       0x2000
86 #   define SPEAKER_TOP_FRONT_RIGHT        0x4000
87 #   define SPEAKER_TOP_BACK_LEFT          0x8000
88 #   define SPEAKER_TOP_BACK_CENTER        0x10000
89 #   define SPEAKER_TOP_BACK_RIGHT         0x20000
90 #   define SPEAKER_RESERVED               0x80000000
91 #endif
92
93 #ifndef DSSPEAKER_HEADPHONE
94 #   define DSSPEAKER_HEADPHONE         0x00000001
95 #endif
96 #ifndef DSSPEAKER_MONO
97 #   define DSSPEAKER_MONO              0x00000002
98 #endif
99 #ifndef DSSPEAKER_QUAD
100 #   define DSSPEAKER_QUAD              0x00000003
101 #endif
102 #ifndef DSSPEAKER_STEREO
103 #   define DSSPEAKER_STEREO            0x00000004
104 #endif
105 #ifndef DSSPEAKER_SURROUND
106 #   define DSSPEAKER_SURROUND          0x00000005
107 #endif
108 #ifndef DSSPEAKER_5POINT1
109 #   define DSSPEAKER_5POINT1           0x00000006
110 #endif
111
112 #ifndef _WAVEFORMATEXTENSIBLE_
113 typedef struct {
114     WAVEFORMATEX    Format;
115     union {
116         WORD wValidBitsPerSample;       /* bits of precision  */
117         WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
118         WORD wReserved;                 /* If neither applies, set to zero. */
119     } Samples;
120     DWORD           dwChannelMask;      /* which channels are */
121                                         /* present in stream  */
122     GUID            SubFormat;
123 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
124 #endif
125
126 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
127 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
128 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
129
130 /*****************************************************************************
131  * notification_thread_t: DirectX event thread
132  *****************************************************************************/
133 typedef struct notification_thread_t
134 {
135     VLC_COMMON_MEMBERS
136
137     aout_instance_t * p_aout;
138     int i_frame_status[FRAMES_NUM];           /* status of each frame buffer */
139     DSBPOSITIONNOTIFY p_events[FRAMES_NUM];      /* play notification events */
140     int i_frame_size;                         /* Size in bytes of one frame */
141
142     mtime_t start_date;
143
144 } notification_thread_t;
145
146 /*****************************************************************************
147  * aout_sys_t: directx audio output method descriptor
148  *****************************************************************************
149  * This structure is part of the audio output thread descriptor.
150  * It describes the direct sound specific properties of an audio device.
151  *****************************************************************************/
152 struct aout_sys_t
153 {
154     HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
155     LPDIRECTSOUND       p_dsobject;              /* main Direct Sound object */
156     LPDIRECTSOUNDBUFFER p_dsbuffer;   /* the sound buffer we use (direct sound
157                                        * takes care of mixing all the
158                                        * secondary buffers into the primary) */
159
160     LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */
161     notification_thread_t *p_notif;                  /* DirectSoundThread id */
162
163     int b_playing;                                         /* playing status */
164
165     int i_frame_size;                         /* Size in bytes of one frame */
166
167     vlc_bool_t b_chan_reorder;              /* do we need channel reordering */
168     int *pi_chan_table;
169     uint32_t i_channel_mask;
170 };
171
172 static const uint32_t pi_channels_in[] =
173     { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
174       AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
175       AOUT_CHAN_CENTER, AOUT_CHAN_LFE };
176 static const uint32_t pi_channels_out[] =
177     { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
178       SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
179       SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY };
180 static const uint32_t pi_channels_ordered[] =
181     { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_CENTER,
182       SPEAKER_LOW_FREQUENCY,
183       SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT };
184
185 /*****************************************************************************
186  * Local prototypes.
187  *****************************************************************************/
188 static int  OpenAudio  ( vlc_object_t * );
189 static void CloseAudio ( vlc_object_t * );
190 static void Play       ( aout_instance_t * );
191
192 /* local functions */
193 static void Probe             ( aout_instance_t * );
194 static int  InitDirectSound   ( aout_instance_t * );
195 static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, vlc_bool_t );
196 static int  CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, vlc_bool_t );
197 static void DestroyDSBuffer   ( aout_instance_t * );
198 static void DirectSoundThread ( notification_thread_t * );
199 static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );
200
201 static void CheckReordering   ( aout_instance_t *, int );
202 static void InterleaveFloat32 ( float *, float *, int *, int );
203 static void InterleaveS16     ( int16_t *, int16_t *, int *, int );
204
205 /*****************************************************************************
206  * Module descriptor
207  *****************************************************************************/
208 vlc_module_begin();
209     set_description( _("DirectX audio output") );
210     set_capability( "audio output", 100 );
211     add_shortcut( "directx" );
212     set_callbacks( OpenAudio, CloseAudio );
213 vlc_module_end();
214
215 /*****************************************************************************
216  * OpenAudio: open the audio device
217  *****************************************************************************
218  * This function opens and setups Direct Sound.
219  *****************************************************************************/
220 static int OpenAudio( vlc_object_t *p_this )
221 {
222     aout_instance_t * p_aout = (aout_instance_t *)p_this;
223     vlc_value_t val;
224     int i;
225
226     msg_Dbg( p_aout, "OpenAudio" );
227
228    /* Allocate structure */
229     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
230     if( p_aout->output.p_sys == NULL )
231     {
232         msg_Err( p_aout, "out of memory" );
233         return VLC_EGENERIC;
234     }
235
236     /* Initialize some variables */
237     p_aout->output.p_sys->p_dsobject = NULL;
238     p_aout->output.p_sys->p_dsbuffer = NULL;
239     p_aout->output.p_sys->p_dsnotify = NULL;
240     p_aout->output.p_sys->p_notif = NULL;
241     p_aout->output.p_sys->b_playing = 0;
242     p_aout->output.p_sys->pi_chan_table = NULL;
243
244     p_aout->output.pf_play = Play;
245     aout_VolumeSoftInit( p_aout );
246
247     /* Initialise DirectSound */
248     if( InitDirectSound( p_aout ) )
249     {
250         msg_Err( p_aout, "cannot initialize DirectSound" );
251         goto error;
252     }
253
254     /* Now we need to setup our DirectSound play notification structure */
255     p_aout->output.p_sys->p_notif =
256         vlc_object_create( p_aout, sizeof(notification_thread_t) );
257     p_aout->output.p_sys->p_notif->p_aout = p_aout;
258
259     if( var_Type( p_aout, "audio-device" ) == 0 )
260     {
261         Probe( p_aout );
262     }
263
264     if( var_Get( p_aout, "audio-device", &val ) < 0 )
265     {
266         /* Probe() has failed. */
267         free( p_aout->output.p_sys );
268         return VLC_EGENERIC;
269     }
270
271     /* Then create the notification events */
272     for( i = 0; i < FRAMES_NUM; i++ )
273         p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
274             CreateEvent( NULL, FALSE, FALSE, NULL );
275
276     /* Open the device */
277     if( !strcmp( val.psz_string, N_("A/52 over S/PDIF") ) )
278     {
279         free( val.psz_string );
280         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
281
282         /* Calculate the frame size in bytes */
283         p_aout->output.i_nb_samples = A52_FRAME_NB;
284         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
285         p_aout->output.output.i_frame_length = A52_FRAME_NB;
286         p_aout->output.p_sys->i_frame_size =
287             p_aout->output.output.i_bytes_per_frame;
288
289         if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
290                             p_aout->output.output.i_physical_channels,
291                             aout_FormatNbChannels( &p_aout->output.output ),
292                             p_aout->output.output.i_rate,
293                             p_aout->output.p_sys->i_frame_size, VLC_FALSE )
294             != VLC_SUCCESS )
295         {
296             msg_Err( p_aout, "cannot open waveout audio device" );
297             free( p_aout->output.p_sys );
298             return VLC_EGENERIC;
299         }
300
301         aout_VolumeNoneInit( p_aout );
302     }
303     else
304     {
305         if( !strcmp( val.psz_string, N_("5.1") ) )
306         {
307             p_aout->output.output.i_physical_channels
308                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
309                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
310                    | AOUT_CHAN_LFE;
311         }
312         else if( !strcmp( val.psz_string, N_("2 Front 2 Rear") ) )
313         {
314             p_aout->output.output.i_physical_channels
315                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
316                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
317         }
318         else if( !strcmp( val.psz_string, "Mono" ) )
319         {
320             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
321         }
322         else
323         {
324             p_aout->output.output.i_physical_channels
325                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
326         }
327         free( val.psz_string );
328
329         if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
330                                p_aout->output.output.i_physical_channels,
331                                aout_FormatNbChannels( &p_aout->output.output ),
332                                p_aout->output.output.i_rate, VLC_FALSE )
333             != VLC_SUCCESS )
334         {
335             msg_Err( p_aout, "cannot open waveout audio device" );
336             free( p_aout->output.p_sys );
337             return VLC_EGENERIC;
338         }
339
340         /* Calculate the frame size in bytes */
341         p_aout->output.i_nb_samples = FRAME_SIZE;
342         aout_FormatPrepare( &p_aout->output.output );
343         p_aout->output.p_sys->i_frame_size =
344             FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
345
346         aout_VolumeSoftInit( p_aout );
347     }
348
349     /* then launch the notification thread */
350     msg_Dbg( p_aout, "creating DirectSoundThread" );
351     if( vlc_thread_create( p_aout->output.p_sys->p_notif,
352                            "DirectSound Notification Thread",
353                            DirectSoundThread,
354                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
355     {
356         msg_Err( p_aout, "cannot create DirectSoundThread" );
357         goto error;
358     }
359
360     vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );
361
362     return VLC_SUCCESS;
363
364  error:
365     CloseAudio( VLC_OBJECT(p_aout) );
366     return VLC_EGENERIC;
367 }
368
369 /*****************************************************************************
370  * Probe: probe the audio device for available formats and channels
371  *****************************************************************************/
372 static void Probe( aout_instance_t * p_aout )
373 {
374     vlc_value_t val;
375     int i_format;
376     unsigned int i_physical_channels;
377     DWORD ui_speaker_config;
378
379     var_Create( p_aout, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
380
381     /* Test for 5.1 support */
382     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
383                           AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
384                           AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
385     if( p_aout->output.output.i_physical_channels == i_physical_channels )
386     {
387         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6,
388                                p_aout->output.output.i_rate, VLC_TRUE )
389             == VLC_SUCCESS )
390         {
391             val.psz_string = N_("5.1");
392             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
393             msg_Dbg( p_aout, "device supports 5.1 channels" );
394         }
395     }
396
397     /* Test for 2 Front 2 Rear support */
398     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
399                           AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
400     if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
401         == i_physical_channels )
402     {
403         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
404                                p_aout->output.output.i_rate, VLC_TRUE )
405             == VLC_SUCCESS )
406         {
407             val.psz_string = N_("2 Front 2 Rear");
408             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
409             msg_Dbg( p_aout, "device supports 4 channels" );
410         }
411     }
412
413     /* Test for stereo support */
414     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
415     if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
416                            p_aout->output.output.i_rate, VLC_TRUE )
417         == VLC_SUCCESS )
418     {
419         val.psz_string = N_("Stereo");
420         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
421         msg_Dbg( p_aout, "device supports 2 channels" );
422     }
423
424     /* Test for mono support */
425     i_physical_channels = AOUT_CHAN_CENTER;
426     if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
427                            p_aout->output.output.i_rate, VLC_TRUE )
428         == VLC_SUCCESS )
429     {
430         val.psz_string = N_("Mono");
431         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );        
432         msg_Dbg( p_aout, "device supports 1 channel" );
433     }
434
435     /* Check the speaker configuration to determine which channel config should
436      * be the default */
437     if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject,
438                                               &ui_speaker_config ) )
439     {
440         ui_speaker_config = DSSPEAKER_STEREO;
441     }
442     switch( DSSPEAKER_CONFIG(ui_speaker_config) )
443     {
444     case DSSPEAKER_5POINT1:
445         val.psz_string = N_("5.1");
446         break;
447     case DSSPEAKER_QUAD:
448         val.psz_string = N_("2 Front 2 Rear");
449         break;
450     case DSSPEAKER_MONO:
451         val.psz_string = N_("Mono");
452         break;
453     case DSSPEAKER_SURROUND:
454     case DSSPEAKER_STEREO:
455     default:
456         val.psz_string = N_("Stereo");
457         break;
458     }
459     var_Set( p_aout, "audio-device", val );
460
461     /* Test for SPDIF support */
462     if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
463     {
464         if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
465                             p_aout->output.output.i_physical_channels,
466                             aout_FormatNbChannels( &p_aout->output.output ),
467                             p_aout->output.output.i_rate,
468                             AOUT_SPDIF_SIZE, VLC_TRUE )
469             == VLC_SUCCESS )
470         {
471             msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
472             val.psz_string = N_("A/52 over S/PDIF");
473             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
474             if( config_GetInt( p_aout, "spdif" ) )
475                 var_Set( p_aout, "audio-device", val );
476         }
477     }
478
479     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
480
481     val.b_bool = VLC_TRUE;
482     var_Set( p_aout, "intf-change", val );
483 }
484
485 /*****************************************************************************
486  * Play: we'll start playing the directsound buffer here because at least here
487  *       we know the first buffer has been put in the aout fifo and we also
488  *       know its date.
489  *****************************************************************************/
490 static void Play( aout_instance_t *p_aout )
491 {
492     if( !p_aout->output.p_sys->b_playing )
493     {
494         aout_buffer_t *p_buffer;
495
496         p_aout->output.p_sys->b_playing = 1;
497
498         /* get the playing date of the first aout buffer */
499         p_aout->output.p_sys->p_notif->start_date =
500             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
501
502         /* fill in the first samples */
503         p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
504         FillBuffer( p_aout, 0, p_buffer );
505
506         /* wake up the audio output thread */
507         SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
508     }
509 }
510
511 /*****************************************************************************
512  * CloseAudio: close the audio device
513  *****************************************************************************/
514 static void CloseAudio( vlc_object_t *p_this )
515 {
516     aout_instance_t * p_aout = (aout_instance_t *)p_this;
517
518     msg_Dbg( p_aout, "CloseAudio" );
519
520     /* kill the position notification thread, if any */
521     if( p_aout->output.p_sys->p_notif )
522     {
523         vlc_object_detach( p_aout->output.p_sys->p_notif );
524         if( p_aout->output.p_sys->p_notif->b_thread )
525         {
526             p_aout->output.p_sys->p_notif->b_die = 1;
527
528             if( !p_aout->output.p_sys->b_playing )
529                 /* wake up the audio thread */
530                 SetEvent(
531                     p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
532
533             vlc_thread_join( p_aout->output.p_sys->p_notif );
534         }
535         vlc_object_destroy( p_aout->output.p_sys->p_notif );
536     }
537
538     /* release the secondary buffer */
539     DestroyDSBuffer( p_aout );
540
541     /* finally release the DirectSound object */
542     if( p_aout->output.p_sys->p_dsobject )
543         IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
544     
545     /* free DSOUND.DLL */
546     if( p_aout->output.p_sys->hdsound_dll )
547        FreeLibrary( p_aout->output.p_sys->hdsound_dll );
548
549     if( p_aout->output.p_sys->pi_chan_table )
550         free( p_aout->output.p_sys->pi_chan_table );
551
552     free( p_aout->output.p_sys );
553 }
554
555 /*****************************************************************************
556  * InitDirectSound: handle all the gory details of DirectSound initialisation
557  *****************************************************************************/
558 static int InitDirectSound( aout_instance_t *p_aout )
559 {
560     HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
561
562     p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
563     if( p_aout->output.p_sys->hdsound_dll == NULL )
564     {
565         msg_Warn( p_aout, "cannot open DSOUND.DLL" );
566         goto error;
567     }
568
569     OurDirectSoundCreate = (void *)GetProcAddress(
570                                            p_aout->output.p_sys->hdsound_dll,
571                                            "DirectSoundCreate" );
572     if( OurDirectSoundCreate == NULL )
573     {
574         msg_Warn( p_aout, "GetProcAddress FAILED" );
575         goto error;
576     }
577
578     /* Create the direct sound object */
579     if FAILED( OurDirectSoundCreate( NULL, &p_aout->output.p_sys->p_dsobject,
580                                      NULL ) )
581     {
582         msg_Warn( p_aout, "cannot create a direct sound device" );
583         goto error;
584     }
585
586     /* Set DirectSound Cooperative level, ie what control we want over Windows
587      * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
588      * settings of the primary buffer, but also that only the sound of our
589      * application will be hearable when it will have the focus.
590      * !!! (this is not really working as intended yet because to set the
591      * cooperative level you need the window handle of your application, and
592      * I don't know of any easy way to get it. Especially since we might play
593      * sound without any video, and so what window handle should we use ???
594      * The hack for now is to use the Desktop window handle - it seems to be
595      * working */
596     if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
597                                           GetDesktopWindow(),
598                                           DSSCL_EXCLUSIVE) )
599     {
600         msg_Warn( p_aout, "cannot set direct sound cooperative level" );
601     }
602
603     return VLC_SUCCESS;
604
605  error:
606     p_aout->output.p_sys->p_dsobject = NULL;
607     if( p_aout->output.p_sys->hdsound_dll )
608     {
609         FreeLibrary( p_aout->output.p_sys->hdsound_dll );
610         p_aout->output.p_sys->hdsound_dll = NULL;
611     }
612     return VLC_EGENERIC;
613
614 }
615
616 /*****************************************************************************
617  * CreateDSBuffer: Creates a direct sound buffer of the required format.
618  *****************************************************************************
619  * This function creates the buffer we'll use to play audio.
620  * In DirectSound there are two kinds of buffers:
621  * - the primary buffer: which is the actual buffer that the soundcard plays
622  * - the secondary buffer(s): these buffers are the one actually used by
623  *    applications and DirectSound takes care of mixing them into the primary.
624  *
625  * Once you create a secondary buffer, you cannot change its format anymore so
626  * you have to release the current one and create another.
627  *****************************************************************************/
628 static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
629                            int i_channels, int i_nb_channels, int i_rate,
630                            int i_bytes_per_frame, vlc_bool_t b_probe )
631 {
632     WAVEFORMATEXTENSIBLE waveformat;
633     DSBUFFERDESC         dsbdesc;
634     unsigned int         i;
635
636     /* First set the sound buffer format */
637     waveformat.dwChannelMask = 0;
638     for( i = 0; i < sizeof(pi_channels_in)/sizeof(uint32_t); i++ )
639     {
640         if( i_channels & pi_channels_in[i] )
641             waveformat.dwChannelMask |= pi_channels_out[i];
642     }
643
644     switch( i_format )
645     {
646     case VLC_FOURCC('s','p','d','i'):
647         i_nb_channels = 2;
648         /* To prevent channel re-ordering */
649         waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
650         waveformat.Format.wBitsPerSample = 16;
651         waveformat.Samples.wValidBitsPerSample =
652             waveformat.Format.wBitsPerSample;
653         waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
654         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
655         break;
656
657     case VLC_FOURCC('f','l','3','2'):
658         waveformat.Format.wBitsPerSample = sizeof(float) * 8;
659         waveformat.Samples.wValidBitsPerSample =
660             waveformat.Format.wBitsPerSample;
661         waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
662         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
663         break;
664
665     case VLC_FOURCC('s','1','6','l'):
666         waveformat.Format.wBitsPerSample = 16;
667         waveformat.Samples.wValidBitsPerSample =
668             waveformat.Format.wBitsPerSample;
669         waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
670         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
671         break;
672     }
673
674     waveformat.Format.nChannels = i_nb_channels;
675     waveformat.Format.nSamplesPerSec = i_rate;
676     waveformat.Format.nBlockAlign =
677         waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
678     waveformat.Format.nAvgBytesPerSec =
679         waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
680
681     /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
682     if( i_nb_channels <= 2 )
683     {
684         waveformat.Format.cbSize = 0;
685     }
686     else
687     {
688         waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
689         waveformat.Format.cbSize =
690             sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
691     }
692
693
694     /* Then fill in the direct sound descriptor */
695     memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
696     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
697     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
698                     | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
699                     | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
700     dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame;   /* buffer size */
701     dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
702
703     if FAILED( IDirectSound_CreateSoundBuffer(
704                    p_aout->output.p_sys->p_dsobject, &dsbdesc,
705                    &p_aout->output.p_sys->p_dsbuffer, NULL) )
706     {
707         goto error;
708     }
709
710     /* Stop here if we were just probing */
711     if( b_probe )
712     {
713         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
714         p_aout->output.p_sys->p_dsbuffer = NULL;
715         return VLC_SUCCESS;
716     }
717
718     /* Backup the size of a frame */
719     p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame;
720
721     /* Now the secondary buffer is created, we need to setup its position
722      * notification */
723     for( i = 0; i < FRAMES_NUM; i++ )
724     {
725         p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
726             p_aout->output.p_sys->p_notif->i_frame_size;
727
728         p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
729     }
730
731     /* Get the IDirectSoundNotify interface */
732     if FAILED( IDirectSoundBuffer_QueryInterface(
733                                 p_aout->output.p_sys->p_dsbuffer,
734                                 &IID_IDirectSoundNotify,
735                                 (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
736     {
737         msg_Err( p_aout, "cannot get IDirectSoundNotify interface" );
738         goto error;
739     }
740
741     if FAILED( IDirectSoundNotify_SetNotificationPositions(
742                                     p_aout->output.p_sys->p_dsnotify,
743                                     FRAMES_NUM,
744                                     p_aout->output.p_sys->p_notif->p_events ) )
745     {
746         msg_Err( p_aout, "cannot set position notification" );
747         goto error;
748     }
749
750     p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
751     CheckReordering( p_aout, i_nb_channels );
752
753     return VLC_SUCCESS;
754
755  error:
756     if( p_aout->output.p_sys->p_dsbuffer )
757     {
758         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
759         p_aout->output.p_sys->p_dsbuffer = NULL;
760     }
761     if( p_aout->output.p_sys->p_dsnotify )
762     {
763         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
764         p_aout->output.p_sys->p_dsnotify = NULL;
765     }
766     return VLC_EGENERIC;
767 }
768
769 /*****************************************************************************
770  * CreateDSBufferPCM: creates a PCM direct sound buffer.
771  *****************************************************************************
772  * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
773  * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
774  ****************************************************************************/
775 static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
776                               int i_channels, int i_nb_channels, int i_rate,
777                               vlc_bool_t b_probe )
778 {
779     if( CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
780                         i_channels, i_nb_channels, i_rate,
781                         FRAME_SIZE * 4 * i_nb_channels, b_probe )
782         != VLC_SUCCESS )
783     {
784         if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'),
785                              i_channels, i_nb_channels, i_rate,
786                              FRAME_SIZE * 2 * i_nb_channels, b_probe )
787              != VLC_SUCCESS )
788         {
789             return VLC_EGENERIC;
790         }
791         else
792         {
793             *i_format = VLC_FOURCC('s','1','6','l');
794             return VLC_SUCCESS;
795         }
796     }
797     else
798     {
799         *i_format = VLC_FOURCC('f','l','3','2');
800         return VLC_SUCCESS;
801     }
802 }
803
804 /*****************************************************************************
805  * DestroyDSBuffer
806  *****************************************************************************
807  * This function destroys the secondary buffer.
808  *****************************************************************************/
809 static void DestroyDSBuffer( aout_instance_t *p_aout )
810 {
811     if( p_aout->output.p_sys->p_dsnotify )
812     {
813         IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
814         p_aout->output.p_sys->p_dsnotify = NULL;
815     }
816
817     if( p_aout->output.p_sys->p_dsbuffer )
818     {
819         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
820         p_aout->output.p_sys->p_dsbuffer = NULL;
821     }
822 }
823
824 /*****************************************************************************
825  * FillBuffer: Fill in one of the direct sound frame buffers.
826  *****************************************************************************
827  * Returns VLC_SUCCESS on success.
828  *****************************************************************************/
829 static int FillBuffer( aout_instance_t *p_aout, int i_frame,
830                        aout_buffer_t *p_buffer )
831 {
832     notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
833     void *p_write_position, *p_wrap_around;
834     long l_bytes1, l_bytes2;
835     HRESULT dsresult;
836
837     /* Before copying anything, we have to lock the buffer */
838     dsresult = IDirectSoundBuffer_Lock(
839                 p_aout->output.p_sys->p_dsbuffer,               /* DS buffer */
840                 i_frame * p_notif->i_frame_size,             /* Start offset */
841                 p_notif->i_frame_size,                    /* Number of bytes */
842                 &p_write_position,                  /* Address of lock start */
843                 &l_bytes1,       /* Count of bytes locked before wrap around */
844                 &p_wrap_around,            /* Buffer adress (if wrap around) */
845                 &l_bytes2,               /* Count of bytes after wrap around */
846                 0 );                                                /* Flags */
847     if( dsresult == DSERR_BUFFERLOST )
848     {
849         IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
850         dsresult = IDirectSoundBuffer_Lock(
851                                p_aout->output.p_sys->p_dsbuffer,
852                                i_frame * p_notif->i_frame_size,
853                                p_notif->i_frame_size,
854                                &p_write_position,
855                                &l_bytes1,
856                                &p_wrap_around,
857                                &l_bytes2,
858                                0 );
859     }
860     if( dsresult != DS_OK )
861     {
862         msg_Warn( p_notif, "cannot lock buffer" );
863         if( p_buffer ) aout_BufferFree( p_buffer );
864         return VLC_EGENERIC;
865     }
866
867     if( p_buffer == NULL )
868     {
869         memset( p_write_position, 0, l_bytes1 );
870     }
871     else if( p_aout->output.p_sys->b_chan_reorder )
872     {
873         /* Do the channel reordering here */
874
875         if( p_aout->output.output.i_format ==  VLC_FOURCC('s','1','6','l') )
876           InterleaveS16( (int16_t *)p_buffer->p_buffer,
877                          (int16_t *)p_write_position,
878                          p_aout->output.p_sys->pi_chan_table,
879                          aout_FormatNbChannels( &p_aout->output.output ) );
880         else
881           InterleaveFloat32( (float *)p_buffer->p_buffer,
882                              (float *)p_write_position,
883                              p_aout->output.p_sys->pi_chan_table,
884                              aout_FormatNbChannels( &p_aout->output.output ) );
885     }
886     else
887     {
888         p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
889                                   l_bytes1 );
890         aout_BufferFree( p_buffer );
891     }
892
893     /* Now the data has been copied, unlock the buffer */
894     IDirectSoundBuffer_Unlock( p_aout->output.p_sys->p_dsbuffer,
895                                p_write_position, l_bytes1,
896                                p_wrap_around, l_bytes2 );
897
898     return VLC_SUCCESS;
899 }
900
901 /*****************************************************************************
902  * DirectSoundThread: this thread will capture play notification events. 
903  *****************************************************************************
904  * We use this thread to emulate a callback mechanism. The thread probes for
905  * event notification and fills up the DS secondary buffer when needed.
906  *****************************************************************************/
907 static void DirectSoundThread( notification_thread_t *p_notif )
908 {
909     HANDLE  notification_events[FRAMES_NUM];
910     HRESULT dsresult;
911     aout_instance_t *p_aout = p_notif->p_aout;
912     int i, i_which_frame, i_last_frame, i_next_frame;
913     mtime_t mtime;
914     vlc_bool_t b_sleek;
915
916     for( i = 0; i < FRAMES_NUM; i++ )
917         notification_events[i] = p_notif->p_events[i].hEventNotify;
918
919     /* We don't want any resampling when using S/PDIF output */
920     b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
921
922     /* Tell the main thread that we are ready */
923     vlc_thread_ready( p_notif );
924
925     msg_Dbg( p_notif, "DirectSoundThread ready" );
926
927     /* Wait here until Play() is called */
928     WaitForSingleObject( notification_events[0], INFINITE );
929
930     if( !p_notif->b_die )
931     {
932         mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
933
934         /* start playing the buffer */
935         dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
936                                         0,                         /* Unused */
937                                         0,                         /* Unused */
938                                         DSBPLAY_LOOPING );          /* Flags */
939         if( dsresult == DSERR_BUFFERLOST )
940         {
941             IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
942             dsresult = IDirectSoundBuffer_Play(
943                                             p_aout->output.p_sys->p_dsbuffer,
944                                             0,                     /* Unused */
945                                             0,                     /* Unused */
946                                             DSBPLAY_LOOPING );      /* Flags */
947         }
948         if( dsresult != DS_OK )
949         {
950             msg_Err( p_aout, "cannot start playing buffer" );
951         }
952     }
953
954     while( !p_notif->b_die )
955     {
956         aout_buffer_t *p_buffer;
957         long l_latency;
958
959         /* wait for the position notification */
960         i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
961                                                 notification_events, 0,
962                                                 INFINITE ) - WAIT_OBJECT_0;
963
964         if( p_notif->b_die )
965             break;
966
967         mtime = mdate();
968
969         /* We take into account the current latency */
970         if SUCCEEDED( IDirectSoundBuffer_GetCurrentPosition(
971                         p_aout->output.p_sys->p_dsbuffer,
972                         &l_latency, NULL ) )
973         {
974             if( l_latency > (i_which_frame * FRAME_SIZE)
975                   && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
976             {
977                 l_latency = - ( l_latency /
978                                 p_aout->output.output.i_bytes_per_frame %
979                                 FRAME_SIZE );
980             }
981             else
982             {
983                 l_latency = FRAME_SIZE - ( l_latency /
984                                       p_aout->output.output.i_bytes_per_frame %
985                                       FRAME_SIZE );
986             }
987         }
988         else
989         {
990             l_latency = 0;
991         }
992
993         /* Mark last frame as empty */
994         i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM;
995         i_next_frame = (i_which_frame + 1) % FRAMES_NUM;
996         p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY;
997
998         /* Try to fill in as many frame buffers as possible */
999         for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
1000         {
1001
1002             /* Check if frame buf is already filled */
1003             if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED )
1004                 continue;
1005
1006             p_buffer = aout_OutputNextBuffer( p_aout,
1007                 mtime + 1000000 / p_aout->output.output.i_rate *
1008                 ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), b_sleek );
1009
1010             /* If there is no audio data available and we have some buffered
1011              * already, then just wait for the next time */
1012             if( !p_buffer && (i != i_next_frame) )
1013             {
1014                 //msg_Err( p_aout, "only %i frame buffers filled!",
1015                 //         i - i_next_frame );
1016                 break;
1017             }
1018
1019             if( FillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
1020                 != VLC_SUCCESS )
1021                 break;
1022
1023             /* Mark the frame buffer as QUEUED */
1024             p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED;
1025         }
1026
1027     }
1028
1029     /* make sure the buffer isn't playing */
1030     IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
1031
1032     /* free the events */
1033     for( i = 0; i < FRAMES_NUM; i++ )
1034         CloseHandle( notification_events[i] );
1035
1036     msg_Dbg( p_notif, "DirectSoundThread exiting" );
1037 }
1038
1039 /*****************************************************************************
1040  * CheckReordering: Check if we need to do some channel re-ordering (the ac3
1041  *                  channel order is different from the one chosen by
1042  *                  Microsoft).
1043  *****************************************************************************/
1044 static void CheckReordering( aout_instance_t *p_aout, int i_nb_channels )
1045 {
1046     int i, j, k, l;
1047
1048 #define i_channel_mask p_aout->output.p_sys->i_channel_mask
1049 #define pi_chan_table p_aout->output.p_sys->pi_chan_table
1050
1051     p_aout->output.p_sys->b_chan_reorder = VLC_FALSE;
1052
1053     pi_chan_table = malloc( i_nb_channels * sizeof(int) );
1054     if( !pi_chan_table )
1055     {
1056         return;
1057     }
1058
1059     for( i = 0, j = 0;
1060          i < (int)(sizeof(pi_channels_out)/sizeof(uint32_t)); i++ )
1061     {
1062         if( i_channel_mask & pi_channels_out[i] )
1063         {
1064             for( k = 0, l = 0;
1065                  pi_channels_out[i] != pi_channels_ordered[k]; k++ )
1066             {
1067                 if( i_channel_mask & pi_channels_ordered[k] )
1068                 {
1069                     l++;
1070                 }
1071             }
1072
1073             pi_chan_table[j] = l;
1074
1075             j++;
1076         }
1077     }
1078
1079     for( i = 0; i < i_nb_channels; i++ )
1080     {
1081         if( pi_chan_table[i] != i )
1082         {
1083             p_aout->output.p_sys->b_chan_reorder = VLC_TRUE;
1084         }
1085     }
1086
1087     if( p_aout->output.p_sys->b_chan_reorder )
1088     {
1089         msg_Dbg( p_aout, "channel reordering needed" );
1090     }
1091
1092 #undef pi_chan_table
1093 #undef waveformat
1094 }
1095
1096 /*****************************************************************************
1097  * InterleaveFloat32/S16: change the channel order to the Microsoft one.
1098  *****************************************************************************/
1099 static void InterleaveFloat32( float *p_buf, float *p_buf_out,
1100                                int *pi_chan_table, int i_nb_channels )
1101 {
1102     int i, j;
1103
1104     for( i = 0; i < FRAME_SIZE; i++ )
1105     {
1106         for( j = 0; j < i_nb_channels; j++ )
1107         {
1108             p_buf_out[i*i_nb_channels + pi_chan_table[j]] =
1109                 p_buf[i*i_nb_channels + j];
1110         }
1111     }
1112 }
1113
1114 static void InterleaveS16( int16_t *p_buf, int16_t *p_buf_out,
1115                            int *pi_chan_table, int i_nb_channels )
1116 {
1117     int i, j;
1118
1119     for( i = 0; i < FRAME_SIZE; i++ )
1120     {
1121         for( j = 0; j < i_nb_channels; j++ )
1122         {
1123             p_buf_out[ i*i_nb_channels + pi_chan_table[j]] =
1124                 p_buf[i*i_nb_channels + j];
1125         }
1126     }
1127 }