Пример #1
0
static int Open ( vlc_object_t *p_this )
{
    audio_output_t *p_aout = (audio_output_t *)p_this;
    struct aout_sys_t *p_sys = malloc(sizeof(aout_sys_t));
    p_aout->sys = p_sys;

    OSStatus status = 0;

    // Setup the audio device.
    AudioStreamBasicDescription deviceFormat;
    deviceFormat.mSampleRate = 44100;
    deviceFormat.mFormatID = kAudioFormatLinearPCM;
    deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // Signed integer, little endian
    deviceFormat.mBytesPerPacket = 4;
    deviceFormat.mFramesPerPacket = 1;
    deviceFormat.mBytesPerFrame = 4;
    deviceFormat.mChannelsPerFrame = 2;
    deviceFormat.mBitsPerChannel = 16;
    deviceFormat.mReserved = 0;

    // Create a new output AudioQueue for the device.
    status = AudioQueueNewOutput(&deviceFormat,         // Format
                                 AudioQueueCallback,    // Callback
                                 p_aout,                // User data, passed to the callback
                                 CFRunLoopGetMain(),    // RunLoop
                                 kCFRunLoopDefaultMode, // RunLoop mode
                                 0,                     // Flags ; must be zero (per documentation)...
                                 &(p_sys->audioQueue)); // Output

    // This will be used for boosting the audio without the need of a mixer (floating-point conversion is expensive on ARM)
    // AudioQueueSetParameter(p_sys->audioQueue, kAudioQueueParam_Volume, 12.0); // Defaults to 1.0

    msg_Dbg(p_aout, "New AudioQueue output created (status = %i)", status);

    // Allocate buffers for the AudioQueue, and pre-fill them.
    for (int i = 0; i < NUMBER_OF_BUFFERS; ++i) {
        AudioQueueBufferRef buffer = NULL;
        status = AudioQueueAllocateBuffer(p_sys->audioQueue, FRAME_SIZE * 4, &buffer);
        AudioQueueCallback(NULL, p_sys->audioQueue, buffer);
    }

    /* Volume is entirely done in software. */
    aout_SoftVolumeInit( p_aout );

    p_aout->format.i_format = VLC_CODEC_S16L;
    p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    p_aout->format.i_rate = 44100;
    aout_PacketInit(p_aout, &p_sys->packet, FRAME_SIZE);
    p_aout->pf_play = aout_PacketPlay;
    p_aout->pf_pause = aout_PacketPause;
    p_aout->pf_flush = aout_PacketFlush;

    msg_Dbg(p_aout, "Starting AudioQueue (status = %i)", status);
    status = AudioQueueStart(p_sys->audioQueue, NULL);

    return VLC_SUCCESS;
}
Пример #2
0
/*****************************************************************************
 * Open: create a JACK client
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    char psz_name[32];
    audio_output_t *p_aout = (audio_output_t *)p_this;
    struct aout_sys_t *p_sys = NULL;
    int status = VLC_SUCCESS;
    unsigned int i;
    int i_error;

    /* Allocate structure */
    p_sys = calloc( 1, sizeof( aout_sys_t ) );
    if( p_sys == NULL )
    {
        status = VLC_ENOMEM;
        goto error_out;
    }
    p_aout->sys = p_sys;
    p_sys->latency = 0;

    /* Connect to the JACK server */
    snprintf( psz_name, sizeof(psz_name), "vlc_%d", getpid());
    psz_name[sizeof(psz_name) - 1] = '\0';
    p_sys->p_jack_client = jack_client_open( psz_name,
                                             JackNullOption | JackNoStartServer,
                                             NULL );
    if( p_sys->p_jack_client == NULL )
    {
        msg_Err( p_aout, "failed to connect to JACK server" );
        status = VLC_EGENERIC;
        goto error_out;
    }

    /* Set the process callback */
    jack_set_process_callback( p_sys->p_jack_client, Process, p_aout );
    jack_set_graph_order_callback ( p_sys->p_jack_client, GraphChange, p_aout );

    p_aout->pf_play = aout_PacketPlay;
    p_aout->pf_pause = aout_PacketPause;
    p_aout->pf_flush = aout_PacketFlush;
    aout_PacketInit( p_aout, &p_sys->packet,
                     jack_get_buffer_size( p_sys->p_jack_client ) );
    aout_VolumeSoftInit( p_aout );

    /* JACK only supports fl32 format */
    p_aout->format.i_format = VLC_CODEC_FL32;
    // TODO add buffer size callback
    p_aout->format.i_rate = jack_get_sample_rate( p_sys->p_jack_client );

    p_sys->i_channels = aout_FormatNbChannels( &p_aout->format );

    p_sys->p_jack_ports = malloc( p_sys->i_channels *
                                  sizeof(jack_port_t *) );
    if( p_sys->p_jack_ports == NULL )
    {
        status = VLC_ENOMEM;
        goto error_out;
    }

    p_sys->p_jack_buffers = malloc( p_sys->i_channels *
                                    sizeof(jack_sample_t *) );
    if( p_sys->p_jack_buffers == NULL )
    {
        status = VLC_ENOMEM;
        goto error_out;
    }

    /* Create the output ports */
    for( i = 0; i < p_sys->i_channels; i++ )
    {
        snprintf( psz_name, sizeof(psz_name), "out_%d", i + 1);
        psz_name[sizeof(psz_name) - 1] = '\0';
        p_sys->p_jack_ports[i] = jack_port_register( p_sys->p_jack_client,
                psz_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );

        if( p_sys->p_jack_ports[i] == NULL )
        {
            msg_Err( p_aout, "failed to register a JACK port" );
            status = VLC_EGENERIC;
            goto error_out;
        }
    }

    /* Tell the JACK server we are ready */
    i_error = jack_activate( p_sys->p_jack_client );
    if( i_error )
    {
        msg_Err( p_aout, "failed to activate JACK client (error %d)", i_error );
        status = VLC_EGENERIC;
        goto error_out;
    }

    /* Auto connect ports if we were asked to */
    if( var_InheritBool( p_aout, AUTO_CONNECT_OPTION ) )
    {
        unsigned int i_in_ports;
        char *psz_regex = var_InheritString( p_aout, CONNECT_REGEX_OPTION );
        const char **pp_in_ports = jack_get_ports( p_sys->p_jack_client,
                                                   psz_regex, NULL,
                                                   JackPortIsInput );
        free( psz_regex );
        /* Count the number of returned ports */
        i_in_ports = 0;
        while( pp_in_ports && pp_in_ports[i_in_ports] )
        {
            i_in_ports++;
        }

        /* Tie the output ports to JACK input ports */
        for( i = 0; i_in_ports > 0 && i < p_sys->i_channels; i++ )
        {
            const char* psz_in = pp_in_ports[i % i_in_ports];
            const char* psz_out = jack_port_name( p_sys->p_jack_ports[i] );

            i_error = jack_connect( p_sys->p_jack_client, psz_out, psz_in );
            if( i_error )
            {
                msg_Err( p_aout, "failed to connect port %s to port %s (error %d)",
                         psz_out, psz_in, i_error );
            }
            else
            {
                msg_Dbg( p_aout, "connecting port %s to port %s",
                         psz_out, psz_in );
            }
        }
        free( pp_in_ports );
    }

    msg_Dbg( p_aout, "JACK audio output initialized (%d channels, rate=%d)",
             p_sys->i_channels, p_aout->format.i_rate );

error_out:
    /* Clean up, if an error occurred */
    if( status != VLC_SUCCESS && p_sys != NULL)
    {
        if( p_sys->p_jack_client )
        {
            jack_deactivate( p_sys->p_jack_client );
            jack_client_close( p_sys->p_jack_client );
            aout_PacketDestroy( p_aout );
        }
        free( p_sys->p_jack_ports );
        free( p_sys->p_jack_buffers );
        free( p_sys );
    }
    return status;
}
Пример #3
0
Файл: kai.c Проект: cspowart/vlc
/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************/
static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt )
{
    aout_sys_t *p_sys = p_aout->sys;
    char *psz_mode;
    ULONG i_kai_mode;
    KAISPEC ks_wanted, ks_obtained;
    int i_nb_channels;
    int i_bytes_per_frame;
    vlc_value_t val, text;
    audio_sample_format_t format = *fmt;

    if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR )
    {
        /* The user has selected an audio device. */
        if ( val.i_int == AOUT_VAR_STEREO )
        {
            format.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
        }
        else if ( val.i_int == AOUT_VAR_MONO )
        {
            format.i_physical_channels = AOUT_CHAN_CENTER;
        }
    }

    psz_mode = var_InheritString( p_aout, "kai-audio-device" );
    if( !psz_mode )
        psz_mode = ( char * )ppsz_kai_audio_device[ 0 ];  // "auto"

    i_kai_mode = KAIM_AUTO;
    if( strcmp( psz_mode, "dart" ) == 0 )
        i_kai_mode = KAIM_DART;
    else if( strcmp( psz_mode, "uniaud" ) == 0 )
        i_kai_mode = KAIM_UNIAUD;
    msg_Dbg( p_aout, "selected mode = %s", psz_mode );

    if( psz_mode != ppsz_kai_audio_device[ 0 ])
        free( psz_mode );

    i_nb_channels = aout_FormatNbChannels( &format );
    if ( i_nb_channels > 2 )
    {
        /* KAI doesn't support more than two channels. */
        i_nb_channels = 2;
        format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    }

    /* Support s16l only */
    format.i_format = VLC_CODEC_S16L;

    aout_FormatPrepare( &format );

    i_bytes_per_frame = format.i_bytes_per_frame;

    /* Initialize library */
    if( kaiInit( i_kai_mode ))
    {
        msg_Err( p_aout, "cannot initialize KAI");

        return VLC_EGENERIC;
    }

    ks_wanted.usDeviceIndex   = 0;
    ks_wanted.ulType          = KAIT_PLAY;
    ks_wanted.ulBitsPerSample = BPS_16;
    ks_wanted.ulSamplingRate  = format.i_rate;
    ks_wanted.ulDataFormat    = MCI_WAVE_FORMAT_PCM;
    ks_wanted.ulChannels      = i_nb_channels;
    ks_wanted.ulNumBuffers    = 2;
    ks_wanted.ulBufferSize    = FRAME_SIZE * i_bytes_per_frame;
    ks_wanted.fShareable      = !var_InheritBool( p_aout,
                                                  "kai-audio-exclusive-mode");
    ks_wanted.pfnCallBack     = KaiCallback;
    ks_wanted.pCallBackData   = p_aout;
    msg_Dbg( p_aout, "requested ulBufferSize = %ld", ks_wanted.ulBufferSize );

    /* Open the sound device. */
    if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai ))
    {
        msg_Err( p_aout, "cannot open KAI device");

        goto exit_kai_done;
    }

    msg_Dbg( p_aout, "open in %s mode",
             ks_obtained.fShareable ? "shareable" : "exclusive" );
    msg_Dbg( p_aout, "obtained i_nb_samples = %lu",
             ks_obtained.ulBufferSize / i_bytes_per_frame );
    msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d",
             format.i_bytes_per_frame );

    p_sys->format = *fmt = format;

    p_aout->time_get = aout_PacketTimeGet;
    p_aout->play  = Play;
    p_aout->pause = NULL;
    p_aout->flush = aout_PacketFlush;

    aout_SoftVolumeStart( p_aout );

    aout_PacketInit( p_aout, &p_sys->packet,
                     ks_obtained.ulBufferSize / i_bytes_per_frame, &format );

    if ( var_Type( p_aout, "audio-device" ) == 0 )
    {
        /* First launch. */
        var_Create( p_aout, "audio-device",
                    VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
        text.psz_string = _("Audio Device");
        var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );

        val.i_int = AOUT_VAR_STEREO;
        text.psz_string = _("Stereo");
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
        val.i_int = AOUT_VAR_MONO;
        text.psz_string = _("Mono");
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
        if ( i_nb_channels == 2 )
        {
            val.i_int = AOUT_VAR_STEREO;
        }
        else
        {
            val.i_int = AOUT_VAR_MONO;
        }
        var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
        var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
    }

    /* Prevent SIG_FPE */
    _control87(MCW_EM, MCW_EM);

    return VLC_SUCCESS;

exit_kai_done :
    kaiDone();

    return VLC_EGENERIC;
}
Пример #4
0
Файл: oss.c Проект: CSRedRat/vlc
/*****************************************************************************
 * Open: open the audio device (the digital sound processor)
 *****************************************************************************
 * This function opens the DSP as a usual non-blocking write-only file, and
 * modifies the p_aout->p_sys->i_fd with the file's descriptor.
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    audio_output_t * p_aout = (audio_output_t *)p_this;
    struct aout_sys_t * p_sys;
    char * psz_device;
    vlc_value_t val;

    /* Allocate structure */
    p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;

    /* Get device name */
    if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL )
    {
        msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" );
        free( p_sys );
        return VLC_EGENERIC;
    }

    /* Open the sound device in non-blocking mode, because ALSA's OSS
     * emulation and some broken OSS drivers would make a blocking call
     * wait forever until the device is available. Since this breaks the
     * OSS spec, we immediately put it back to blocking mode if the
     * operation was successful. */
    p_sys->i_fd = vlc_open( psz_device, O_WRONLY | O_NDELAY );
    if( p_sys->i_fd < 0 )
    {
        msg_Err( p_aout, "cannot open audio device (%s)", psz_device );
        free( psz_device );
        free( p_sys );
        return VLC_EGENERIC;
    }

    /* if the opening was ok, put the device back in blocking mode */
    fcntl( p_sys->i_fd, F_SETFL,
            fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY );

    free( psz_device );

    p_aout->pf_play = aout_PacketPlay;
    p_aout->pf_pause = aout_PacketPause;
    p_aout->pf_flush = aout_PacketFlush;

    if ( var_Type( p_aout, "audio-device" ) == 0 )
        Probe( p_aout );
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );

    if ( var_Get( p_aout, "audio-device", &val ) < 0 )
        /* Probe() has failed. */
        goto error;

    if ( val.i_int == AOUT_VAR_SPDIF )
    {
        p_aout->format.i_format = VLC_CODEC_SPDIFL;
    }
    else if ( val.i_int == AOUT_VAR_5_1 )
    {
        p_aout->format.i_format = VLC_CODEC_S16N;
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
               | AOUT_CHAN_LFE;
    }
    else if ( val.i_int == AOUT_VAR_2F2R )
    {
        p_aout->format.i_format = VLC_CODEC_S16N;
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
    }
    else if ( val.i_int == AOUT_VAR_STEREO )
    {
        p_aout->format.i_format = VLC_CODEC_S16N;
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    }
    else if ( val.i_int == AOUT_VAR_MONO )
    {
        p_aout->format.i_format = VLC_CODEC_S16N;
        p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
    }
    else
    {
        /* This should not happen ! */
        msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
                 val.i_int );
        goto error;
    }

    var_TriggerCallback( p_aout, "intf-change" );

    /* Reset the DSP device */
    if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
    {
        msg_Err( p_aout, "cannot reset OSS audio device" );
        goto error;
    }

    /* Set the output format */
    if ( AOUT_FMT_SPDIF( &p_aout->format ) )
    {
        int i_format = AFMT_AC3;

        if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
             || i_format != AFMT_AC3 )
        {
            msg_Err( p_aout, "cannot reset OSS audio device" );
            goto error;
        }

        p_aout->format.i_format = VLC_CODEC_SPDIFL;
        p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
        p_aout->format.i_frame_length = A52_FRAME_NB;

        aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB );
        aout_VolumeNoneInit( p_aout );
    }
    else
    {
        unsigned int i_format = AFMT_S16_NE;
        unsigned int i_frame_size, i_fragments;
        unsigned int i_rate;
        unsigned int i_nb_channels;
        audio_buf_info audio_buf;

        if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
        {
            msg_Err( p_aout, "cannot set audio output format" );
            goto error;
        }

        switch ( i_format )
        {
        case AFMT_U8:
            p_aout->format.i_format = VLC_CODEC_U8;
            break;
        case AFMT_S8:
            p_aout->format.i_format = VLC_CODEC_S8;
            break;
        case AFMT_U16_LE:
            p_aout->format.i_format = VLC_CODEC_U16L;
            break;
        case AFMT_S16_LE:
            p_aout->format.i_format = VLC_CODEC_S16L;
            break;
        case AFMT_U16_BE:
            p_aout->format.i_format = VLC_CODEC_U16B;
            break;
        case AFMT_S16_BE:
            p_aout->format.i_format = VLC_CODEC_S16B;
            break;
        default:
            msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
                     i_format );
            goto error;
        }

        i_nb_channels = aout_FormatNbChannels( &p_aout->format );

        /* Set the number of channels */
        if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 ||
            i_nb_channels != aout_FormatNbChannels( &p_aout->format ) )
        {
            msg_Err( p_aout, "cannot set number of audio channels (%s)",
                     aout_FormatPrintChannels( &p_aout->format) );
            goto error;
        }

        /* Set the output rate */
        i_rate = p_aout->format.i_rate;
        if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 )
        {
            msg_Err( p_aout, "cannot set audio output rate (%i)",
                             p_aout->format.i_rate );
            goto error;
        }

        if( i_rate != p_aout->format.i_rate )
        {
            p_aout->format.i_rate = i_rate;
        }

        /* Set the fragment size */
        aout_FormatPrepare( &p_aout->format );

        /* i_fragment = xxxxyyyy where: xxxx        is fragtotal
         *                              1 << yyyy   is fragsize */
        i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT;
        i_fragments = 4;
        while( i_fragments < 12 && (1U << i_fragments) < i_frame_size )
        {
            ++i_fragments;
        }
        i_fragments |= FRAME_COUNT << 16;
        if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 )
        {
            msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments );
        }

        if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
        {
            msg_Err( p_aout, "cannot get fragment size" );
            goto error;
        }

        /* Number of fragments actually allocated */
        p_aout->sys->i_fragstotal = audio_buf.fragstotal;

        /* Maximum duration the soundcard's buffer can hold */
        p_aout->sys->max_buffer_duration =
                (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
                / p_aout->format.i_bytes_per_frame
                / p_aout->format.i_rate
                * p_aout->format.i_frame_length;

        aout_PacketInit( p_aout, &p_sys->packet,
                         audio_buf.fragsize/p_aout->format.i_bytes_per_frame );
        aout_VolumeSoftInit( p_aout );
    }

    /* Create OSS thread and wait for its readiness. */
    if( vlc_clone( &p_sys->thread, OSSThread, p_aout,
                   VLC_THREAD_PRIORITY_OUTPUT ) )
    {
        msg_Err( p_aout, "cannot create OSS thread (%m)" );
        aout_PacketDestroy( p_aout );
        goto error;
    }

    return VLC_SUCCESS;

error:
    var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
    close( p_sys->i_fd );
    free( p_sys );
    return VLC_EGENERIC;
}
Пример #5
0
static int PAOpenStream( audio_output_t *p_aout )
{
    aout_sys_t *p_sys = p_aout->sys;
    const PaHostErrorInfo* paLastHostErrorInfo = Pa_GetLastHostErrorInfo();
    PaStreamParameters paStreamParameters;
    vlc_value_t val;
    int i_channels, i_err;
    uint32_t i_channel_mask;

    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
        return VLC_EGENERIC;
    }

    if( val.i_int == AOUT_VAR_5_1 )
    {
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
              | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
              | AOUT_CHAN_LFE;
    }
    else if( val.i_int == AOUT_VAR_3F2R )
    {
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
            | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
    }
    else if( val.i_int == AOUT_VAR_2F2R )
    {
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
            | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
    }
    else if( val.i_int == AOUT_VAR_MONO )
    {
        p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
    }
    else
    {
        p_aout->format.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    }

    i_channels = aout_FormatNbChannels( &p_aout->format );
    msg_Dbg( p_aout, "nb_channels requested = %d", i_channels );
    i_channel_mask = p_aout->format.i_physical_channels;

    /* Calculate the frame size in bytes */
    p_sys->i_sample_size = 4 * i_channels;
    aout_FormatPrepare( &p_aout->format );
    aout_PacketInit( p_aout, &p_sys->packet, FRAME_SIZE );
    aout_VolumeSoftInit( p_aout );

    /* Check for channel reordering */
    p_aout->sys->i_channel_mask = i_channel_mask;
    p_aout->sys->i_bits_per_sample = 32; /* forced to paFloat32 */
    p_aout->sys->i_channels = i_channels;

    p_aout->sys->b_chan_reorder =
        aout_CheckChannelReorder( NULL, pi_channels_out,
                                  i_channel_mask, i_channels,
                                  p_aout->sys->pi_chan_table );

    if( p_aout->sys->b_chan_reorder )
    {
        msg_Dbg( p_aout, "channel reordering needed" );
    }

    paStreamParameters.device = p_sys->i_device_id;
    paStreamParameters.channelCount = i_channels;
    paStreamParameters.sampleFormat = paFloat32;
    paStreamParameters.suggestedLatency =
        p_sys->deviceInfo->defaultLowOutputLatency;
    paStreamParameters.hostApiSpecificStreamInfo = NULL;

    i_err = Pa_OpenStream( &p_sys->p_stream, NULL /* no input */,
                &paStreamParameters, (double)p_aout->format.i_rate,
                FRAME_SIZE, paClipOff, paCallback, p_sys );
    if( i_err != paNoError )
    {
        msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err,
                 Pa_GetErrorText( i_err ) );
        if( i_err == paUnanticipatedHostError )
        {
            msg_Err( p_aout, "type %d code %ld : %s",
                     paLastHostErrorInfo->hostApiType,
                     paLastHostErrorInfo->errorCode,
                     paLastHostErrorInfo->errorText );
        }
        p_sys->p_stream = 0;
        aout_PacketDestroy( p_aout );
        return VLC_EGENERIC;
    }

    i_err = Pa_StartStream( p_sys->p_stream );
    if( i_err != paNoError )
    {
        msg_Err( p_aout, "Pa_StartStream() failed" );
        Pa_CloseStream( p_sys->p_stream );
        aout_PacketDestroy( p_aout );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}