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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * portaudio.c : portaudio (v19) audio output plugin
  3.  *****************************************************************************
  4.  * Copyright (C) 2002, 2006 the VideoLAN team
  5.  * $Id: f73ce9bf7eacfdebcb4814eedb5934e40f30757b $
  6.  *
  7.  * Authors: Frederic Ruget <frederic.ruget@free.fr>
  8.  *          Gildas Bazin <gbazin@videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #ifdef HAVE_CONFIG_H
  28. # include "config.h"
  29. #endif
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_aout.h>
  33. #include <portaudio.h>
  34. #define FRAME_SIZE 1024              /* The size is in samples, not in bytes */
  35. #ifdef WIN32
  36. #   define PORTAUDIO_IS_SERIOUSLY_BROKEN 1
  37. #endif
  38. /*****************************************************************************
  39.  * aout_sys_t: portaudio audio output method descriptor
  40.  *****************************************************************************/
  41. typedef struct pa_thread_t
  42. {
  43.     VLC_COMMON_MEMBERS
  44.     aout_instance_t *p_aout;
  45.     vlc_cond_t  wait;
  46.     vlc_mutex_t lock_wait;
  47.     bool  b_wait;
  48.     vlc_cond_t  signal;
  49.     vlc_mutex_t lock_signal;
  50.     bool  b_signal;
  51. } pa_thread_t;
  52. struct aout_sys_t
  53. {
  54.     aout_instance_t *p_aout;
  55.     PaStream *p_stream;
  56.     PaDeviceIndex i_devices;
  57.     int i_sample_size;
  58.     PaDeviceIndex i_device_id;
  59.     const PaDeviceInfo *deviceInfo;
  60.     bool b_chan_reorder;              /* do we need channel reordering */
  61.     int pi_chan_table[AOUT_CHAN_MAX];
  62.     uint32_t i_channel_mask;
  63.     uint32_t i_bits_per_sample;
  64.     uint32_t i_channels;
  65. };
  66. static const uint32_t pi_channels_out[] =
  67.     { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
  68.       AOUT_CHAN_CENTER, AOUT_CHAN_LFE,
  69.       AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
  70.       AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };
  71. #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
  72. static bool b_init = 0;
  73. static pa_thread_t *pa_thread;
  74. static void* PORTAUDIOThread( vlc_object_t * );
  75. #endif
  76. /*****************************************************************************
  77.  * Local prototypes.
  78.  *****************************************************************************/
  79. static int  Open        ( vlc_object_t * );
  80. static void Close       ( vlc_object_t * );
  81. static void Play        ( aout_instance_t * );
  82. static int PAOpenDevice( aout_instance_t * );
  83. static int PAOpenStream( aout_instance_t * );
  84. /*****************************************************************************
  85.  * Module descriptor
  86.  *****************************************************************************/
  87. #define DEVICE_TEXT N_("Output device")
  88. #define DEVICE_LONGTEXT N_("Portaudio identifier for the output device")
  89. vlc_module_begin ()
  90.     set_shortname( "PortAudio" )
  91.     set_description( N_("PORTAUDIO audio output") )
  92.     set_category( CAT_AUDIO )
  93.     set_subcategory( SUBCAT_AUDIO_AOUT )
  94.     add_integer( "portaudio-audio-device", 0, NULL,
  95.                  DEVICE_TEXT, DEVICE_LONGTEXT, false )
  96.         add_deprecated_alias( "portaudio-device" )   /* deprecated since 0.9.3 */
  97.     set_capability( "audio output", 0 )
  98.     set_callbacks( Open, Close )
  99. vlc_module_end ()
  100. /* This routine will be called by the PortAudio engine when audio is needed.
  101.  * It may called at interrupt level on some machines so don't do anything
  102.  * that could mess up the system like calling malloc() or free().
  103.  */
  104. static int paCallback( const void *inputBuffer, void *outputBuffer,
  105.                        unsigned long framesPerBuffer,
  106.                        const PaStreamCallbackTimeInfo *paDate,
  107.                        PaStreamCallbackFlags statusFlags, void *p_cookie )
  108. {
  109.     struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie;
  110.     aout_instance_t   *p_aout = p_sys->p_aout;
  111.     aout_buffer_t     *p_buffer;
  112.     mtime_t out_date;
  113.     out_date = mdate() + (mtime_t) ( 1000000 *
  114.         ( paDate->outputBufferDacTime - paDate->currentTime ) );
  115.     p_buffer = aout_OutputNextBuffer( p_aout, out_date, true );
  116.     if ( p_buffer != NULL )
  117.     {
  118.         if( p_sys->b_chan_reorder )
  119.         {
  120.             /* Do the channel reordering here */
  121.             aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
  122.                                  p_sys->i_channels, p_sys->pi_chan_table,
  123.                                  p_sys->i_bits_per_sample );
  124.         }
  125.         vlc_memcpy( outputBuffer, p_buffer->p_buffer,
  126.                     framesPerBuffer * p_sys->i_sample_size );
  127.         /* aout_BufferFree may be dangereous here, but then so is
  128.          * aout_OutputNextBuffer (calls aout_BufferFree internally).
  129.          * one solution would be to link the no longer useful buffers
  130.          * in a second fifo (in aout_OutputNextBuffer too) and to
  131.          * wait until we are in Play to do the actual free.
  132.          */
  133.         aout_BufferFree( p_buffer );
  134.     }
  135.     else
  136.         /* Audio output buffer shortage -> stop the fill process and wait */
  137.     {
  138.         vlc_memset( outputBuffer, 0, framesPerBuffer * p_sys->i_sample_size );
  139.     }
  140.     return 0;
  141. }
  142. /*****************************************************************************
  143.  * Open: open the audio device
  144.  *****************************************************************************/
  145. static int Open( vlc_object_t * p_this )
  146. {
  147.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  148.     struct aout_sys_t * p_sys;
  149.     int i_err;
  150.     msg_Dbg( p_aout, "entering Open()");
  151.     /* Allocate p_sys structure */
  152.     p_sys = malloc( sizeof(aout_sys_t) );
  153.     if( p_sys == NULL )
  154.         return VLC_ENOMEM;
  155.     p_sys->p_aout = p_aout;
  156.     p_sys->p_stream = 0;
  157.     p_aout->output.p_sys = p_sys;
  158.     p_aout->output.pf_play = Play;
  159.     /* Retrieve output device id from config */
  160.     p_sys->i_device_id = var_CreateGetInteger( p_aout, "portaudio-audio-device" );
  161. #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
  162.     if( !b_init )
  163.     {
  164.         /* Test device */
  165.         if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
  166.         {
  167.             msg_Err( p_aout, "cannot open portaudio device" );
  168.             free( p_sys );
  169.             return VLC_EGENERIC;
  170.         }
  171.         /* Close device for now. We'll re-open it later on */
  172.         if( ( i_err = Pa_Terminate() ) != paNoError )
  173.         {
  174.             msg_Err( p_aout, "closing the device returned %d", i_err );
  175.         }
  176.         b_init = true;
  177.         /* Now we need to setup our DirectSound play notification structure */
  178.         pa_thread = vlc_object_create( p_aout, sizeof(pa_thread_t) );
  179.         pa_thread->p_aout = p_aout;
  180.         pa_thread->b_error = false;
  181.         vlc_mutex_init( &pa_thread->lock_wait );
  182.         vlc_cond_init( &pa_thread->wait );
  183.         pa_thread->b_wait = false;
  184.         vlc_mutex_init( &pa_thread->lock_signal );
  185.         vlc_cond_init( &pa_thread->signal );
  186.         pa_thread->b_signal = false;
  187.         /* Create PORTAUDIOThread */
  188.         if( vlc_thread_create( pa_thread, "aout", PORTAUDIOThread,
  189.                                VLC_THREAD_PRIORITY_OUTPUT ) )
  190.         {
  191.             msg_Err( p_aout, "cannot create PORTAUDIO thread" );
  192.             return VLC_EGENERIC;
  193.         }
  194.     }
  195.     else
  196.     {
  197.         pa_thread->p_aout = p_aout;
  198.         pa_thread->b_wait = false;
  199.         pa_thread->b_signal = false;
  200.         pa_thread->b_error = false;
  201.     }
  202.     /* Signal start of stream */
  203.     vlc_mutex_lock( &pa_thread->lock_signal );
  204.     pa_thread->b_signal = true;
  205.     vlc_cond_signal( &pa_thread->signal );
  206.     vlc_mutex_unlock( &pa_thread->lock_signal );
  207.     /* Wait until thread is ready */
  208.     vlc_mutex_lock( &pa_thread->lock_wait );
  209.     if( !pa_thread->b_wait )
  210.         vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
  211.     vlc_mutex_unlock( &pa_thread->lock_wait );
  212.     pa_thread->b_wait = false;
  213.     if( pa_thread->b_error )
  214.     {
  215.         msg_Err( p_aout, "PORTAUDIO thread failed" );
  216.         Close( p_this );
  217.         return VLC_EGENERIC;
  218.     }
  219.     return VLC_SUCCESS;
  220. #else
  221.     if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
  222.     {
  223.         msg_Err( p_aout, "cannot open portaudio device" );
  224.         free( p_sys );
  225.         return VLC_EGENERIC;
  226.     }
  227.     if( PAOpenStream( p_aout ) != VLC_SUCCESS )
  228.     {
  229.         msg_Err( p_aout, "cannot open portaudio device" );
  230.     }
  231.     return VLC_SUCCESS;
  232. #endif
  233. }
  234. /*****************************************************************************
  235.  * Close: close the audio device
  236.  *****************************************************************************/
  237. static void Close ( vlc_object_t *p_this )
  238. {
  239.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  240.     aout_sys_t *p_sys = p_aout->output.p_sys;
  241.     msg_Dbg( p_aout, "closing portaudio");
  242. #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
  243.     /* Signal end of stream */
  244.     vlc_mutex_lock( &pa_thread->lock_signal );
  245.     pa_thread->b_signal = true;
  246.     vlc_cond_signal( &pa_thread->signal );
  247.     vlc_mutex_unlock( &pa_thread->lock_signal );
  248.     /* Wait until thread is ready */
  249.     vlc_mutex_lock( &pa_thread->lock_wait );
  250.     if( !pa_thread->b_wait )
  251.         vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
  252.     vlc_mutex_unlock( &pa_thread->lock_wait );
  253.     pa_thread->b_wait = false;
  254. #else
  255.     int i_err = Pa_StopStream( p_sys->p_stream );
  256.     if( i_err != paNoError )
  257.     {
  258.         msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
  259.                  Pa_GetErrorText( i_err ) );
  260.     }
  261.     i_err = Pa_CloseStream( p_sys->p_stream );
  262.     if( i_err != paNoError )
  263.     {
  264.         msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
  265.                  Pa_GetErrorText( i_err ) );
  266.     }
  267.     i_err = Pa_Terminate();
  268.     if( i_err != paNoError )
  269.     {
  270.         msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
  271.                  Pa_GetErrorText( i_err ) );
  272.     }
  273. #endif
  274.     msg_Dbg( p_aout, "portaudio closed");
  275.     free( p_sys );
  276. }
  277. static int PAOpenDevice( aout_instance_t *p_aout )
  278. {
  279.     aout_sys_t *p_sys = p_aout->output.p_sys;
  280.     const PaDeviceInfo *p_pdi;
  281.     PaError i_err;
  282.     vlc_value_t val, text;
  283.     int i;
  284.     /* Initialize portaudio */
  285.     i_err = Pa_Initialize();
  286.     if( i_err != paNoError )
  287.     {
  288.         msg_Err( p_aout, "Pa_Initialize returned %d : %s",
  289.                  i_err, Pa_GetErrorText( i_err ) );
  290.         return VLC_EGENERIC;
  291.     }
  292.     p_sys->i_devices = Pa_GetDeviceCount();
  293.     if( p_sys->i_devices < 0 )
  294.     {
  295.         i_err = p_sys->i_devices;
  296.         msg_Err( p_aout, "Pa_GetDeviceCount returned %d : %s", i_err,
  297.                  Pa_GetErrorText( i_err ) );
  298.         goto error;
  299.     }
  300.     /* Display all devices info */
  301.     msg_Dbg( p_aout, "number of devices = %d", p_sys->i_devices );
  302.     for( i = 0; i < p_sys->i_devices; i++ )
  303.     {
  304.         p_pdi = Pa_GetDeviceInfo( i );
  305.         msg_Dbg( p_aout, "------------------------------------- #%d", i );
  306.         msg_Dbg( p_aout, "Name         = %s", p_pdi->name );
  307.         msg_Dbg( p_aout, "Max Inputs   = %d, Max Outputs = %d",
  308.                   p_pdi->maxInputChannels, p_pdi->maxOutputChannels );
  309.     }
  310.     msg_Dbg( p_aout, "-------------------------------------" );
  311.     msg_Dbg( p_aout, "requested device is #%d", p_sys->i_device_id );
  312.     if( p_sys->i_device_id >= p_sys->i_devices )
  313.     {
  314.         msg_Err( p_aout, "device %d does not exist", p_sys->i_device_id );
  315.         goto error;
  316.     }
  317.     p_sys->deviceInfo = Pa_GetDeviceInfo( p_sys->i_device_id );
  318.     if( p_sys->deviceInfo->maxOutputChannels < 1 )
  319.     {
  320.         msg_Err( p_aout, "no channel available" );
  321.         goto error;
  322.     }
  323.     if( var_Type( p_aout, "audio-device" ) == 0 )
  324.     {
  325.         var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
  326.         text.psz_string = _("Audio Device");
  327.         var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
  328.         if( p_sys->deviceInfo->maxOutputChannels >= 1 )
  329.         {
  330.             val.i_int = AOUT_VAR_MONO;
  331.             text.psz_string = _("Mono");
  332.             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
  333.                         &val, &text );
  334.             msg_Dbg( p_aout, "device supports 1 channel" );
  335.         }
  336.         if( p_sys->deviceInfo->maxOutputChannels >= 2 )
  337.         {
  338.             val.i_int = AOUT_VAR_STEREO;
  339.             text.psz_string = _("Stereo");
  340.             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
  341.                         &val, &text );
  342.             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT,
  343.                         &val, NULL );
  344.             var_Set( p_aout, "audio-device", val );
  345.             msg_Dbg( p_aout, "device supports 2 channels" );
  346.         }
  347.         if( p_sys->deviceInfo->maxOutputChannels >= 4 )
  348.         {
  349.             val.i_int = AOUT_VAR_2F2R;
  350.             text.psz_string = _("2 Front 2 Rear");
  351.             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
  352.                         &val, &text );
  353.             msg_Dbg( p_aout, "device supports 4 channels" );
  354.         }
  355.         if( p_sys->deviceInfo->maxOutputChannels >= 5 )
  356.         {
  357.             val.i_int = AOUT_VAR_3F2R;
  358.             text.psz_string = _("3 Front 2 Rear");
  359.             var_Change( p_aout, "audio-device",
  360.                         VLC_VAR_ADDCHOICE, &val, &text );
  361.             msg_Dbg( p_aout, "device supports 5 channels" );
  362.         }
  363.         if( p_sys->deviceInfo->maxOutputChannels >= 6 )
  364.         {
  365.             val.i_int = AOUT_VAR_5_1;
  366.             text.psz_string = "5.1";
  367.             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
  368.                         &val, &text );
  369.             msg_Dbg( p_aout, "device supports 5.1 channels" );
  370.         }
  371.         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
  372.         var_SetBool( p_aout, "intf-change", true );
  373.     }
  374.     /* Audio format is paFloat32 (always supported by portaudio v19) */
  375.     p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
  376.     return VLC_SUCCESS;
  377.  error:
  378.     if( ( i_err = Pa_Terminate() ) != paNoError )
  379.     {
  380.         msg_Err( p_aout, "Pa_Terminate returned %d", i_err );
  381.     }
  382.     return VLC_EGENERIC;
  383. }
  384. static int PAOpenStream( aout_instance_t *p_aout )
  385. {
  386.     aout_sys_t *p_sys = p_aout->output.p_sys;
  387.     const PaHostErrorInfo* paLastHostErrorInfo = Pa_GetLastHostErrorInfo();
  388.     PaStreamParameters paStreamParameters;
  389.     vlc_value_t val;
  390.     int i_channels, i_err;
  391.     uint32_t i_channel_mask;
  392.     if( var_Get( p_aout, "audio-device", &val ) < 0 )
  393.     {
  394.         return VLC_EGENERIC;
  395.     }
  396.     if( val.i_int == AOUT_VAR_5_1 )
  397.     {
  398.         p_aout->output.output.i_physical_channels
  399.             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
  400.               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
  401.               | AOUT_CHAN_LFE;
  402.     }
  403.     else if( val.i_int == AOUT_VAR_3F2R )
  404.     {
  405.         p_aout->output.output.i_physical_channels
  406.             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
  407.             | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
  408.     }
  409.     else if( val.i_int == AOUT_VAR_2F2R )
  410.     {
  411.         p_aout->output.output.i_physical_channels
  412.             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
  413.             | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
  414.     }
  415.     else if( val.i_int == AOUT_VAR_MONO )
  416.     {
  417.         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
  418.     }
  419.     else
  420.     {
  421.         p_aout->output.output.i_physical_channels
  422.             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
  423.     }
  424.     i_channels = aout_FormatNbChannels( &p_aout->output.output );
  425.     msg_Dbg( p_aout, "nb_channels requested = %d", i_channels );
  426.     i_channel_mask = p_aout->output.output.i_physical_channels;
  427.     /* Calculate the frame size in bytes */
  428.     p_sys->i_sample_size = 4 * i_channels;
  429.     p_aout->output.i_nb_samples = FRAME_SIZE;
  430.     aout_FormatPrepare( &p_aout->output.output );
  431.     aout_VolumeSoftInit( p_aout );
  432.     /* Check for channel reordering */
  433.     p_aout->output.p_sys->i_channel_mask = i_channel_mask;
  434.     p_aout->output.p_sys->i_bits_per_sample = 32; /* forced to paFloat32 */
  435.     p_aout->output.p_sys->i_channels = i_channels;
  436.     p_aout->output.p_sys->b_chan_reorder =
  437.         aout_CheckChannelReorder( NULL, pi_channels_out,
  438.                                   i_channel_mask, i_channels,
  439.                                   p_aout->output.p_sys->pi_chan_table );
  440.     if( p_aout->output.p_sys->b_chan_reorder )
  441.     {
  442.         msg_Dbg( p_aout, "channel reordering needed" );
  443.     }
  444.     paStreamParameters.device = p_sys->i_device_id;
  445.     paStreamParameters.channelCount = i_channels;
  446.     paStreamParameters.sampleFormat = paFloat32;
  447.     paStreamParameters.suggestedLatency =
  448.         p_sys->deviceInfo->defaultLowOutputLatency;
  449.     paStreamParameters.hostApiSpecificStreamInfo = NULL;
  450.     i_err = Pa_OpenStream( &p_sys->p_stream, NULL /* no input */,
  451.                 &paStreamParameters, (double)p_aout->output.output.i_rate,
  452.                 FRAME_SIZE, paClipOff, paCallback, p_sys );
  453.     if( i_err != paNoError )
  454.     {
  455.         msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err,
  456.                  Pa_GetErrorText( i_err ) );
  457.         if( i_err == paUnanticipatedHostError )
  458.         {
  459.             msg_Err( p_aout, "type %d code %ld : %s",
  460.                      paLastHostErrorInfo->hostApiType,
  461.                      paLastHostErrorInfo->errorCode,
  462.                      paLastHostErrorInfo->errorText );
  463.         }
  464.         p_sys->p_stream = 0;
  465.         return VLC_EGENERIC;
  466.     }
  467.     i_err = Pa_StartStream( p_sys->p_stream );
  468.     if( i_err != paNoError )
  469.     {
  470.         msg_Err( p_aout, "Pa_StartStream() failed" );
  471.         Pa_CloseStream( p_sys->p_stream );
  472.         return VLC_EGENERIC;
  473.     }
  474.     return VLC_SUCCESS;
  475. }
  476. /*****************************************************************************
  477.  * Play: play sound
  478.  *****************************************************************************/
  479. static void Play( aout_instance_t * p_aout )
  480. {
  481.     VLC_UNUSED( p_aout );
  482. }
  483. #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
  484. /*****************************************************************************
  485.  * PORTAUDIOThread: all interactions with libportaudio.a are handled
  486.  * in this single thread.  Otherwise libportaudio.a is _not_ happy :-(
  487.  *****************************************************************************/
  488. static void* PORTAUDIOThread( vlc_object_t *p_this )
  489. {
  490.     pa_thread_t *pa_thread = (pa_thread_t*)p_this;
  491.     aout_instance_t *p_aout;
  492.     aout_sys_t *p_sys;
  493.     int i_err;
  494.     int canc = vlc_savecancel ();
  495.     while( vlc_object_alive (pa_thread) )
  496.     {
  497.         /* Wait for start of stream */
  498.         vlc_mutex_lock( &pa_thread->lock_signal );
  499.         if( !pa_thread->b_signal )
  500.             vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
  501.         vlc_mutex_unlock( &pa_thread->lock_signal );
  502.         pa_thread->b_signal = false;
  503.         p_aout = pa_thread->p_aout;
  504.         p_sys = p_aout->output.p_sys;
  505.         if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
  506.         {
  507.             msg_Err( p_aout, "cannot open portaudio device" );
  508.             pa_thread->b_error = true;
  509.         }
  510.         if( !pa_thread->b_error && PAOpenStream( p_aout ) != VLC_SUCCESS )
  511.         {
  512.             msg_Err( p_aout, "cannot open portaudio device" );
  513.             pa_thread->b_error = true;
  514.             i_err = Pa_Terminate();
  515.             if( i_err != paNoError )
  516.             {
  517.                 msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
  518.                          Pa_GetErrorText( i_err ) );
  519.             }
  520.         }
  521.         /* Tell the main thread that we are ready */
  522.         vlc_mutex_lock( &pa_thread->lock_wait );
  523.         pa_thread->b_wait = true;
  524.         vlc_cond_signal( &pa_thread->wait );
  525.         vlc_mutex_unlock( &pa_thread->lock_wait );
  526.         /* Wait for end of stream */
  527.         vlc_mutex_lock( &pa_thread->lock_signal );
  528.         if( !pa_thread->b_signal )
  529.             vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
  530.         vlc_mutex_unlock( &pa_thread->lock_signal );
  531.         pa_thread->b_signal = false;
  532.         if( pa_thread->b_error ) continue;
  533.         i_err = Pa_StopStream( p_sys->p_stream );
  534.         if( i_err != paNoError )
  535.         {
  536.             msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
  537.                      Pa_GetErrorText( i_err ) );
  538.         }
  539.         i_err = Pa_CloseStream( p_sys->p_stream );
  540.         if( i_err != paNoError )
  541.         {
  542.             msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
  543.                      Pa_GetErrorText( i_err ) );
  544.         }
  545.         i_err = Pa_Terminate();
  546.         if( i_err != paNoError )
  547.         {
  548.             msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
  549.                      Pa_GetErrorText( i_err ) );
  550.         }
  551.         /* Tell the main thread that we are ready */
  552.         vlc_mutex_lock( &pa_thread->lock_wait );
  553.         pa_thread->b_wait = true;
  554.         vlc_cond_signal( &pa_thread->wait );
  555.         vlc_mutex_unlock( &pa_thread->lock_wait );
  556.     }
  557.     vlc_restorecancel (canc);
  558.     return NULL;
  559. }
  560. #endif