Beispiel #1
0
BMO_state_t *bmo_pa_start(
    BMO_state_t *state,
    uint32_t channels,
    uint32_t rate,
    uint32_t buf_len,
    uint32_t flags
)
{
    (void)flags; // TODO
    if (!state->ringbuffer) {
        state->ringbuffer = bmo_init_rb(buf_len, channels);
    }
    state->driver_id = BMO_PORTAUDIO_DRIVER;
    state->driver.pa.error_num = Pa_Initialize();

    if (state->driver.pa.error_num) {
        bmo_err(
            "Cannot initialise PortAudio:\n"
            "%s\n",
            Pa_GetErrorText(state->driver.pa.error_num)
        );
        return NULL;
    }

    /* TODO should be enumerating available devices, and choosing best
    option, rather than falling back on given defaults */
    state->driver.pa.output_params.device = Pa_GetDefaultOutputDevice();
    if (state->driver.pa.output_params.device == paNoDevice) {
        bmo_err("No default portaudio output device available.\n");
    }

    // portaudio uses a signed int here. We are passing an unisgned int
    assert(((int64_t)channels) < INT_MAX);
    state->driver.pa.output_params.channelCount = (int)channels;
    state->n_playback_ch = channels;
    state->driver.pa.output_params.sampleFormat = paFloat32 | paNonInterleaved;
    state->driver.pa.output_params.suggestedLatency = \
        Pa_GetDeviceInfo(state->driver.pa.output_params.device)->defaultLowOutputLatency;
    state->driver.pa.output_params.hostApiSpecificStreamInfo = NULL;

    /* Open an audio I/O stream. */
    bmo_debug("opening stream\n");
    state->driver.pa.error_num = Pa_OpenStream(
        &(state->driver.pa.stream),
        NULL, // input parameters aren't accounted for yet TODO
        &(state->driver.pa.output_params),
        (double)rate,
        (unsigned long)buf_len,
        paClipOff,
        _bmo_pa_process_cb,
        state
    );

    if (state->driver.pa.error_num != paNoError) {
        bmo_err(
            "couldn't open portaudio stream:"
            "%s\n",
            Pa_GetErrorText(state->driver.pa.error_num)
        );
        return NULL;
    }

    bmo_debug("setting callbacks\n");
    state->driver.pa.error_num = Pa_SetStreamFinishedCallback(
        state->driver.pa.stream,
        _bmo_pa_finished_cb
    );

    if (state->driver.pa.error_num != paNoError) {
        bmo_err(
            "couldn't set exit conditions for portaudio stream:"
            "%s\n",
            Pa_GetErrorText(state->driver.pa.error_num)
        );
        return NULL;
    }

    bmo_debug("starting stream\n");
    state->driver.pa.error_num = Pa_StartStream(state->driver.pa.stream);
    if (state->driver.pa.error_num != paNoError) {
        bmo_err(
            "couldn't start portaudio stream:"
            "%s\n",
            Pa_GetErrorText(state->driver.pa.error_num)
        );
        return NULL;
    }
    return state;
}
int main(void)
{
    PaStreamParameters outputParameters;
    PaStream *stream;
    PaError err;
    TestData data;
    int i, j;

    
    printf( "PortAudio Test: output sine wave. SR = %d, BufSize = %d\n",
            SAMPLE_RATE, FRAMES_PER_BUFFER );
    
    /* initialise sinusoidal wavetable */
    for( i=0; i<TABLE_SIZE; i++ )
    {
        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
    }
    
    err = Pa_Initialize();
    if( err != paNoError ) goto error;

    outputParameters.device                    = Pa_GetDefaultOutputDevice();
    if (outputParameters.device == paNoDevice) {
      fprintf(stderr,"Error: No default output device.\n");
      goto error;
    }
    outputParameters.channelCount              = 2;               /* stereo output */
    outputParameters.sampleFormat              = paFloat32;       /* 32 bit floating point output */
    outputParameters.suggestedLatency          = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    err = Pa_OpenStream(
              &stream,
              NULL, /* no input */
              &outputParameters,
              SAMPLE_RATE,
              FRAMES_PER_BUFFER,
              paClipOff,      /* output will be in-range, so no need to clip */
              TestCallback,
              &data );
    if( err != paNoError ) goto error;

    sprintf( data.message, "Loop: XX" );
    err = Pa_SetStreamFinishedCallback( stream, &StreamFinished );
    if( err != paNoError ) goto error;

    printf("Repeating test %d times.\n", NUM_LOOPS );
    
    for( i=0; i < NUM_LOOPS; ++i )
    {
        data.phase = 0;
        data.generatedFramesCount = 0;
        data.callbackReturnedPaComplete = 0;
        data.callbackInvokedAfterReturningPaComplete = 0;
        sprintf( data.message, "Loop: %d", i );

        err = Pa_StartStream( stream );
        if( err != paNoError ) goto error;

        printf("Play for %d seconds.\n", NUM_SECONDS );

        /* wait for the callback to complete generating NUM_SECONDS of tone */

        do
        {
            Pa_Sleep( 500 );
        }
        while( !data.callbackReturnedPaComplete );

        printf( "Callback returned paComplete.\n" );
        printf( "Waiting for buffers to finish playing...\n" );

        /* wait for stream to become inactive,
           or for a timeout of approximately NUM_SECONDS
         */
     
        j = 0;
        while( (err = Pa_IsStreamActive( stream )) == 1 && j < NUM_SECONDS * 2 )
        {
            printf(".\n" );
            Pa_Sleep( 500 );
            ++j;
        }

        if( err < 0 )
        {
            goto error;
        }
        else if( err == 1 )
        {
            printf( "TEST FAILED: Timed out waiting for buffers to finish playing.\n" );
        }
        else
        {
            printf("Buffers finished.\n" );
        }

        if( data.callbackInvokedAfterReturningPaComplete )
        {
            printf( "TEST FAILED: Callback was invoked after returning paComplete.\n" );
        }


        err = Pa_StopStream( stream );
        if( err != paNoError ) goto error;

        printf( "sleeping for 1 second...\n" );
        Pa_Sleep( 1000 );
    }

    err = Pa_CloseStream( stream );
    if( err != paNoError ) goto error;

    Pa_Terminate();
    printf("Test finished.\n");
    
    return err;
error:
    Pa_Terminate();
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
    return err;
}