directx.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:46k
源码类别:

midi

开发平台:

Unix_Linux

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