[xiph-cvs] cvs commit: theora/win32/experimental/splayer/portaudio/pablio README.txt pablio.c pablio.def pablio.h ringbuffer.c ringbuffer.h

Mauricio Piacentini mauricio at xiph.org
Fri May 23 07:12:13 PDT 2003



mauricio    03/05/23 10:12:13

  Added:       win32/experimental/splayer/portaudio/pa_common pa_convert.c
                        pa_host.h pa_lib.c pa_trace.c pa_trace.h
                        portaudio.h
               win32/experimental/splayer/portaudio/pa_win_wmme
                        pa_win_wmme.c
               win32/experimental/splayer/portaudio/pablio README.txt
                        pablio.c pablio.def pablio.h ringbuffer.c
                        ringbuffer.h
  Log:
  no message

Revision  Changes    Path
1.1                  theora/win32/experimental/splayer/portaudio/pa_common/pa_convert.c

Index: pa_convert.c
===================================================================
/*
 *  pa_conversions.c
 *  portaudio
 *
 *  Created by Phil Burk on Mon Mar 18 2002.
 *
 */
#include <stdio.h>

#include "portaudio.h"
#include "pa_host.h"

#define CLIP( val, min, max )  { val = ((val) < (min)) ? min : (((val) < (max)) ? (max) : (val)); }

/*************************************************************************/
static void PaConvert_Float32_Int16(
    float *sourceBuffer, int sourceStride,
    short *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
        short samp = (short) (*sourceBuffer * (32767.0f));
        *targetBuffer = samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_Int16_Clip(
    float *sourceBuffer, int sourceStride,
    short *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
        long samp = (long) (*sourceBuffer * (32767.0f));
        CLIP( samp, -0x8000, 0x7FFF );
        *targetBuffer = (short) samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_Int16_ClipDither(
    float *sourceBuffer, int sourceStride,
    short *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
    // use smaller scaler to prevent overflow when we add the dither
        float dither  = PaConvert_TriangularDither() * PA_DITHER_SCALE;
        float dithered = (*sourceBuffer * (32766.0f)) + dither;
        long samp = (long) dithered;
        CLIP( samp, -0x8000, 0x7FFF );
        *targetBuffer = (short) samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_Int16_Dither(
    float *sourceBuffer, int sourceStride,
    short *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
    // use smaller scaler to prevent overflow when we add the dither
        float dither  = PaConvert_TriangularDither() * PA_DITHER_SCALE;
        float dithered = (*sourceBuffer * (32766.0f)) + dither;
        *targetBuffer = (short) dithered;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

<p>/*************************************************************************/
static void PaConvert_Int16_Float32(
    short *sourceBuffer, int sourceStride,
    float *targetBuffer, int targetStride,
    int numSamples )
{
    int i;
        for( i=0; i<numSamples; i++ )
        {
        float samp = *sourceBuffer * (1.0f / 32768.0f);
        *targetBuffer = samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_Int8(
    float *sourceBuffer, int sourceStride,
    char *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
        char samp = (char) (*sourceBuffer * (127.0));
        *targetBuffer = samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

<p>/*************************************************************************/
static void PaConvert_Float32_Int8_Clip(
    float *sourceBuffer, int sourceStride,
    char *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
        long samp = *sourceBuffer * 127.0f;
        CLIP( samp, -0x80, 0x7F );
        *targetBuffer = (char) samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_Int8_ClipDither(
    float *sourceBuffer, int sourceStride,
    char *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
    // use smaller scaler to prevent overflow when we add the dither
        float dither  = PaConvert_TriangularDither() * PA_DITHER_SCALE;
        float dithered = (*sourceBuffer * (126.0f)) + dither;
        long samp = (long) dithered;
        CLIP( samp, -0x80, 0x7F );
        *targetBuffer = (char) samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_Int8_Dither(
    float *sourceBuffer, int sourceStride,
    char *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
    // use smaller scaler to prevent overflow when we add the dither
        float dither  = PaConvert_TriangularDither() * PA_DITHER_SCALE;  //FIXME
        float dithered = (*sourceBuffer * (126.0f)) + dither;
        long samp = (long) dithered;
        *targetBuffer = (char) samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Int8_Float32(
    char *sourceBuffer, int sourceStride,
    float *targetBuffer, int targetStride,
    int numSamples )
{
    int i;
        for( i=0; i<numSamples; i++ )
        {
        float samp = *sourceBuffer * (1.0f / 128.0f);
        *targetBuffer = samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static void PaConvert_Float32_UInt8(
    float *sourceBuffer, int sourceStride,
    unsigned char *targetBuffer, int targetStride,
    int numSamples )
{
        int i;
        for( i=0; i<numSamples; i++ )
        {
        unsigned char samp = 128 + (unsigned char) (*sourceBuffer * (127.0));
        *targetBuffer = samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}
    
/*************************************************************************/
static void PaConvert_UInt8_Float32(
    unsigned char *sourceBuffer, int sourceStride,
    float *targetBuffer, int targetStride,
    int numSamples )
{
    int i;
        for( i=0; i<numSamples; i++ )
        {
        float samp = (*sourceBuffer - 128) * (1.0f / 128.0f);
        *targetBuffer = samp;
        sourceBuffer += sourceStride;
        targetBuffer += targetStride;
    }
}

/*************************************************************************/
static PortAudioConverter *PaConvert_SelectProc( PaSampleFormat sourceFormat,
        PaSampleFormat targetFormat,  int ifClip, int ifDither )
{
    PortAudioConverter *proc = NULL;
    switch( sourceFormat )
    {
    case paUInt8:
        switch( targetFormat )
        {
        case paFloat32:
            proc = (PortAudioConverter *) PaConvert_UInt8_Float32;
            break;
        default:
            break;
        }
        break;
    case paInt8:
        switch( targetFormat )
        {
        case paFloat32:
            proc = (PortAudioConverter *) PaConvert_Int8_Float32;
            break;
        default:
            break;
        }
        break;
    case paInt16:
        switch( targetFormat )
        {
        case paFloat32:
            proc = (PortAudioConverter *) PaConvert_Int16_Float32;
            break;
        default:
            break;
        }
        break;
    case paFloat32:
        switch( targetFormat )
        {
        case paUInt8:
            proc = (PortAudioConverter *) PaConvert_Float32_UInt8;
            break;
        case paInt8:
            if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_ClipDither;
            else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Clip;
            else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Dither;
            else proc = (PortAudioConverter *) PaConvert_Float32_Int8;
            break;
        case paInt16:
            if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_ClipDither;
            else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Clip;
            else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Dither;
            else proc = (PortAudioConverter *) PaConvert_Float32_Int16;
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }
    return proc;
            
}

/*************************************************************************/
PaError PaConvert_SetupInput( internalPortAudioStream   *past,
    PaSampleFormat   nativeInputSampleFormat )
{    
    past->past_NativeInputSampleFormat = nativeInputSampleFormat;
    past->past_InputConversionSourceStride = 1;
    past->past_InputConversionTargetStride = 1;
    
    if( nativeInputSampleFormat != past->past_InputSampleFormat )
    {
        int ifDither = (past->past_Flags & paDitherOff) == 0;
        past->past_InputConversionProc = PaConvert_SelectProc( nativeInputSampleFormat,
             past->past_InputSampleFormat, 0, ifDither );
        if( past->past_InputConversionProc == NULL ) return paSampleFormatNotSupported;
    }
    else
    {
        past->past_InputConversionProc = NULL; /* no conversion necessary */
    }
    
    return paNoError;
}

/*************************************************************************/
PaError PaConvert_SetupOutput( internalPortAudioStream   *past,
    PaSampleFormat   nativeOutputSampleFormat )
{

    past->past_NativeOutputSampleFormat = nativeOutputSampleFormat;
    past->past_OutputConversionSourceStride = 1;
    past->past_OutputConversionTargetStride = 1;
    
    if( nativeOutputSampleFormat != past->past_OutputSampleFormat )
    {
        int ifDither = (past->past_Flags & paDitherOff) == 0;
        int ifClip = (past->past_Flags & paClipOff) == 0;

        past->past_OutputConversionProc = PaConvert_SelectProc( past->past_OutputSampleFormat,
            nativeOutputSampleFormat, ifClip, ifDither );
        if( past->past_OutputConversionProc == NULL ) return paSampleFormatNotSupported;
    }
    else
    {
        past->past_OutputConversionProc = NULL; /* no conversion necessary */
    }
    
    return paNoError;
}

/*************************************************************************
** Called by host code.
** Convert input from native format to user format,
** call user code,
** then convert output to native format.
** Returns result from user callback.
*/
long PaConvert_Process( internalPortAudioStream   *past,
                            void *nativeInputBuffer,
                            void *nativeOutputBuffer )
{
    int               userResult;
    void             *inputBuffer = NULL;
    void             *outputBuffer = NULL;

    /* Get native input data. */
    if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
    {
        if( past->past_InputSampleFormat == past->past_NativeInputSampleFormat )
        {
        /*  Already in native format so just read directly from native buffer. */
            inputBuffer =  nativeInputBuffer;
        }
        else
        {
            inputBuffer = past->past_InputBuffer;
        /* Convert input data to user format. */
            (*past->past_InputConversionProc)(nativeInputBuffer, past->past_InputConversionSourceStride,
                inputBuffer, past->past_InputConversionTargetStride,
                past->past_FramesPerUserBuffer * past->past_NumInputChannels );
        }
    }

    /* Are we doing output? */
    if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
    {
        outputBuffer = (past->past_OutputConversionProc == NULL) ?
                       nativeOutputBuffer : past->past_OutputBuffer;
    }
    /*
     AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
     AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
    */
    /* Call user callback routine. */
    userResult = past->past_Callback(
                     inputBuffer,
                     outputBuffer,
                     past->past_FramesPerUserBuffer,
                     past->past_FrameCount,
                     past->past_UserData );

    /* Advance frame counter for timestamp. */
    past->past_FrameCount += past->past_FramesPerUserBuffer; // FIXME - should this be in here?

    /* Convert to native format if necessary. */
    if( (past->past_OutputConversionProc != NULL ) && (outputBuffer != NULL) )
    {
        (*past->past_OutputConversionProc)( outputBuffer, past->past_OutputConversionSourceStride,
            nativeOutputBuffer, past->past_OutputConversionTargetStride,
            past->past_FramesPerUserBuffer * past->past_NumOutputChannels );
    }

    return userResult;
}

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pa_common/pa_host.h

Index: pa_host.h
===================================================================
#ifndef PA_HOST_H
#define PA_HOST_H

/*
 * $Id: pa_host.h,v 1.1 2003/05/23 14:12:12 mauricio Exp $
 * Host dependant internal API for PortAudio
 *
 * Author: Phil Burk  <philburk at softsynth.com>
 *
 * PortAudio Portable Real-Time Audio Library
 * Latest Version at: http://www.softsynth.com/portaudio/
 * DirectSound and Macintosh Implementation
 * Copyright (c) 1999-2000 Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
 
#include "portaudio.h"

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

#ifndef SUPPORT_AUDIO_CAPTURE
#define SUPPORT_AUDIO_CAPTURE  (1)
#endif

#ifndef int32
    typedef long int32;
#endif
#ifndef uint32
    typedef unsigned long uint32;
#endif
#ifndef int16
    typedef short int16;
#endif
#ifndef uint16
    typedef unsigned short uint16;
#endif

/* Used to convert between various sample formats. */
typedef void (PortAudioConverter)(
    void *inputBuffer, int inputStride,
    void *outputBuffer, int outputStride,
    int numSamples );

#define PA_MAGIC    (0x18273645)

/************************************************************************************/
/****************** Structures ******************************************************/
/************************************************************************************/

typedef struct internalPortAudioStream
{
    uint32                    past_Magic;  /* ID for struct to catch bugs. */
    /* User specified information. */
    uint32                    past_FramesPerUserBuffer;
    uint32                    past_NumUserBuffers;
    double                    past_SampleRate;     /* Closest supported sample rate. */
    int                       past_NumInputChannels;
    int                       past_NumOutputChannels;
    PaDeviceID                past_InputDeviceID;
    PaDeviceID                past_OutputDeviceID;
    PaSampleFormat            past_NativeInputSampleFormat;
    PaSampleFormat            past_InputSampleFormat;
    PaSampleFormat            past_NativeOutputSampleFormat;
    PaSampleFormat            past_OutputSampleFormat;
    void                     *past_DeviceData;
    PortAudioCallback        *past_Callback;
    void                     *past_UserData;
    uint32                    past_Flags;
    /* Flags for communicating between foreground and background. */
    volatile int              past_IsActive;      /* Background is still playing. */
    volatile int              past_StopSoon;      /* Background should keep playing when buffers empty. */
    volatile int              past_StopNow;       /* Background should stop playing now. */
    /* These buffers are used when the native format does not match the user format. */
    void                     *past_InputBuffer;
    uint32                    past_InputBufferSize;
    void                     *past_OutputBuffer;
    uint32                    past_OutputBufferSize;
    /* Measurements */
    uint32                    past_NumCallbacks;
    PaTimestamp               past_FrameCount;    /* Frames output to buffer. */
    /* For measuring CPU utilization. */
    double                    past_AverageInsideCount;
    double                    past_AverageTotalCount;
    double                    past_Usage;
    int                       past_IfLastExitValid;
    /* Format Conversion */
    /* These are setup by PaConversion_Setup() */
    PortAudioConverter       *past_InputConversionProc;
    int                       past_InputConversionSourceStride;
    int                       past_InputConversionTargetStride;
    PortAudioConverter       *past_OutputConversionProc;
    int                       past_OutputConversionSourceStride;
    int                       past_OutputConversionTargetStride;
}
internalPortAudioStream;

/************************************************************************************/
/******** These functions must be provided by a platform implementation. ************/
/************************************************************************************/

PaError PaHost_Init( void );
PaError PaHost_Term( void );

PaError PaHost_OpenStream( internalPortAudioStream   *past );
PaError PaHost_CloseStream( internalPortAudioStream   *past );

PaError PaHost_StartOutput( internalPortAudioStream   *past );
PaError PaHost_StopOutput( internalPortAudioStream   *past, int abort );
PaError PaHost_StartInput( internalPortAudioStream   *past );
PaError PaHost_StopInput( internalPortAudioStream   *past, int abort );
PaError PaHost_StartEngine( internalPortAudioStream   *past );
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort );
PaError PaHost_StreamActive( internalPortAudioStream   *past );

void   *PaHost_AllocateFastMemory( long numBytes );
void    PaHost_FreeFastMemory( void *addr, long numBytes );

/* This only called if PA_VALIDATE_RATE IS CALLED. */
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
                                   double *closestFrameRatePtr );

/**********************************************************************/
/************ Common Utility Routines provided by PA ******************/
/**********************************************************************/

/* PaHost_IsInitialized() returns non-zero if PA is initialized, 0 otherwise */
int PaHost_IsInitialized( void );

internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream );

int PaHost_FindClosestTableEntry( double allowableError,  const double *rateTable,
                                  int numRates, double frameRate );

long Pa_CallConvertInt16( internalPortAudioStream   *past,
                          short *nativeInputBuffer,
                          short *nativeOutputBuffer );
                          
/* Calculate 2 LSB dither signal with a triangular distribution.
** Ranged properly for adding to a 32 bit 1.31 fixed point value prior to >>15.
** Range of output is +/- 65535
** Multiply by PA_DITHER_SCALE to get a float between -2.0 and 2.0. */
#define PA_DITHER_BITS   (15)
#define PA_DITHER_SCALE  (1.0f / ((1<<PA_DITHER_BITS)-1))
long PaConvert_TriangularDither( void );

PaError PaConvert_SetupInput( internalPortAudioStream   *past,
    PaSampleFormat   nativeInputSampleFormat );

PaError PaConvert_SetupOutput( internalPortAudioStream   *past,
    PaSampleFormat   nativeOutputSampleFormat );

long PaConvert_Process( internalPortAudioStream   *past,
            void *nativeInputBuffer,
            void *nativeOutputBuffer );

#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_HOST_H */

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pa_common/pa_lib.c

Index: pa_lib.c
===================================================================
/*
 * $Id: pa_lib.c,v 1.1 2003/05/23 14:12:12 mauricio Exp $
 * Portable Audio I/O Library
 * Host Independant Layer
 *
 * Based on the Open Source API proposed by Ross Bencina
 * Copyright (c) 1999-2000 Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

/* Modification History:
 PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
 PLB20010820 - fix dither and shift for recording PaUInt8 format 
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifdef _WIN32
#ifndef __MWERKS__
#include <memory.h>
#endif  /* __MWERKS__ */
#else   /* !_WIN32 */
#include <memory.h>
#endif  /* _WIN32 */

#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"

/* The reason we might NOT want to validate the rate before opening the stream
 * is because many DirectSound drivers lie about the rates they actually support.
 */
#define PA_VALIDATE_RATE    (0)   /* If true validate sample rate against driver info. */

/*
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
*/

#ifndef FALSE
 #define FALSE  (0)
 #define TRUE   (!FALSE)
#endif

#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x)  /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */

tatic int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */

tatic PaError Pa_KillStream(  PortAudioStream *stream, int abort );

/***********************************************************************/
int PaHost_FindClosestTableEntry( double allowableError,  const double *rateTable, int numRates, double frameRate )
{
    double err, minErr = allowableError;
    int i, bestFit = -1;

    for( i=0; i<numRates; i++ )
    {
        err = fabs( frameRate - rateTable[i] );
        if( err < minErr )
        {
            minErr = err;
            bestFit = i;
        }
    }
    return bestFit;
}

/**************************************************************************
** Make sure sample rate is legal and also convert to enumeration for driver.
*/
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
                                   double *closestFrameRatePtr )
{
    long bestRateIndex;
    const PaDeviceInfo *pdi;
    pdi = Pa_GetDeviceInfo( id );
    if( pdi == NULL )
    {
        return paInvalidDeviceId;
    }

    if( pdi->numSampleRates == -1 )
    {
        /* Is it out of range? */
        if( (requestedFrameRate < pdi->sampleRates[0]) ||
                (requestedFrameRate > pdi->sampleRates[1]) )
        {
            return paInvalidSampleRate;
        }

        *closestFrameRatePtr = requestedFrameRate;
    }
    else
    {
        bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
        if( bestRateIndex < 0 ) return paInvalidSampleRate;
        *closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
    }
    return paNoError;
}

/*************************************************************************/
PaError Pa_OpenStream(
    PortAudioStream** streamPtrPtr,
    PaDeviceID inputDeviceID,
    int numInputChannels,
    PaSampleFormat inputSampleFormat,
    void *inputDriverInfo,
    PaDeviceID outputDeviceID,
    int numOutputChannels,
    PaSampleFormat outputSampleFormat,
    void *outputDriverInfo,
    double sampleRate,
    unsigned long framesPerBuffer,
    unsigned long numberOfBuffers,
    unsigned long streamFlags,
    PortAudioCallback *callback,
    void *userData )
{
    internalPortAudioStream   *past = NULL;
    PaError                    result = paNoError;
    int                        bitsPerInputSample;
    int                        bitsPerOutputSample;
    /* Print passed parameters. */
    DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
          streamPtrPtr, inputDeviceID, numInputChannels,
          inputSampleFormat, inputDriverInfo ));
    DBUG(("               %d, %d, %d, %p, /* output */\n",
          outputDeviceID, numOutputChannels,
          outputSampleFormat, outputDriverInfo ));
    DBUG(("               %g, %d, %d, 0x%x, , %p )\n",
          sampleRate, framesPerBuffer, numberOfBuffers,
          streamFlags, userData ));

    /* Check for parameter errors. */
    if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
    if( streamPtrPtr == NULL ) return paBadStreamPtr;
    if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
    if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
    if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
    if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
    {
        return paInvalidDeviceId;
    }
    if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;

#if SUPPORT_AUDIO_CAPTURE
    if( inputDeviceID >= 0 )
    {
        PaError size = Pa_GetSampleSize( inputSampleFormat );
        if( size < 0 ) return size;
        bitsPerInputSample = 8 * size;
        if( (numInputChannels <= 0) ) return paInvalidChannelCount;
    }
#else
    if( inputDeviceID >= 0 )
    {
        return paInvalidChannelCount;
    }
#endif /* SUPPORT_AUDIO_CAPTURE */
    else
    {
        if( numInputChannels > 0 ) return paInvalidChannelCount;
        bitsPerInputSample = 0;
    }

    if( outputDeviceID >= 0 )
    {
        PaError size = Pa_GetSampleSize( outputSampleFormat );
        if( size < 0 ) return size;
        bitsPerOutputSample = 8 * size;
        if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
    }
    else
    {
        if( numOutputChannels > 0 ) return paInvalidChannelCount;
        bitsPerOutputSample = 0;
    }

    if( callback == NULL ) return paNullCallback;

    /* Allocate and clear stream structure. */
    past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
    if( past == NULL ) return paInsufficientMemory;
    memset( past, 0, sizeof(internalPortAudioStream) );
    AddTraceMessage("Pa_OpenStream: past", (long) past );

    past->past_Magic = PA_MAGIC;  /* Set ID to catch bugs. */
    past->past_FramesPerUserBuffer = framesPerBuffer;
    past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() MUST CHECK FOR ZERO! */
    past->past_Callback = callback;
    past->past_UserData = userData;
    past->past_OutputSampleFormat = outputSampleFormat;
    past->past_InputSampleFormat = inputSampleFormat;
    past->past_OutputDeviceID = outputDeviceID;
    past->past_InputDeviceID = inputDeviceID;
    past->past_NumInputChannels = numInputChannels;
    past->past_NumOutputChannels = numOutputChannels;
    past->past_Flags = streamFlags;

    /* Check for absurd sample rates. */
    if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
    {
        result = paInvalidSampleRate;
        goto cleanup;
    }

    /* Allocate buffers that may be used for format conversion from user to native buffers. */
    if( numInputChannels > 0 )
    {

#if PA_VALIDATE_RATE
        result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
        if( result < 0 )
        {
            goto cleanup;
        }
#else
        past->past_SampleRate = sampleRate;
#endif
        /* Allocate single Input buffer for passing formatted samples to user callback. */
        past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
        past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
        if( past->past_InputBuffer == NULL )
        {
            result = paInsufficientMemory;
            goto cleanup;
        }
    }
    else
    {
        past->past_InputBuffer = NULL;
    }

    /* Allocate single Output buffer. */
    if( numOutputChannels > 0 )
    {
#if PA_VALIDATE_RATE
        result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
        if( result < 0 )
        {
            goto cleanup;
        }
#else
        past->past_SampleRate = sampleRate;
#endif
        past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
        past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
        if( past->past_OutputBuffer == NULL )
        {
            result = paInsufficientMemory;
            goto cleanup;
        }
    }
    else
    {
        past->past_OutputBuffer = NULL;
    }

    result = PaHost_OpenStream( past );
    if( result < 0 ) goto cleanup;

    *streamPtrPtr = (void *) past;

    return result;

cleanup:
    if( past != NULL ) Pa_CloseStream( past );
    *streamPtrPtr = NULL;
    return result;
}

<p>/*************************************************************************/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
                              int numInputChannels,
                              int numOutputChannels,
                              PaSampleFormat sampleFormat,
                              double sampleRate,
                              unsigned long framesPerBuffer,
                              unsigned long numberOfBuffers,
                              PortAudioCallback *callback,
                              void *userData )
{
    return Pa_OpenStream(
               stream,
               ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
               numInputChannels, sampleFormat, NULL,
               ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
               numOutputChannels, sampleFormat, NULL,
               sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
}

/*************************************************************************/
PaError Pa_CloseStream( PortAudioStream* stream)
{
    PaError   result;
    internalPortAudioStream   *past;

    DBUG(("Pa_CloseStream()\n"));
    if( stream == NULL ) return paBadStreamPtr;
    past = (internalPortAudioStream *) stream;

    Pa_AbortStream( past );
    result = PaHost_CloseStream( past );

    if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
    if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
    PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );

    return result;
}

/*************************************************************************/
PaError Pa_StartStream( PortAudioStream *stream )
{
    PaError result = paHostError;
    internalPortAudioStream   *past;

    if( stream == NULL ) return paBadStreamPtr;
    past = (internalPortAudioStream *) stream;

    past->past_FrameCount = 0.0;

    if( past->past_NumInputChannels > 0 )
    {
        result = PaHost_StartInput( past );
        DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
        if( result < 0 ) goto error;
    }

    if( past->past_NumOutputChannels > 0 )
    {
        result = PaHost_StartOutput( past );
        DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
        if( result < 0 ) goto error;
    }

    result = PaHost_StartEngine( past );
    DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
    if( result < 0 ) goto error;

    return paNoError;

error:
    return result;
}

/*************************************************************************/
PaError Pa_StopStream(  PortAudioStream *stream )
{
    return Pa_KillStream( stream, 0 );
}

/*************************************************************************/
PaError Pa_AbortStream(  PortAudioStream *stream )
{
    return Pa_KillStream( stream, 1 );
}

/*************************************************************************/
static PaError Pa_KillStream(  PortAudioStream *stream, int abort )
{
    PaError result = paNoError;
    internalPortAudioStream   *past;

    DBUG(("Pa_StopStream().\n"));
    if( stream == NULL ) return paBadStreamPtr;
    past = (internalPortAudioStream *) stream;

    if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
    {
        result = PaHost_StopEngine( past, abort );
        DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
        if( result < 0 ) goto error;
    }

    if( past->past_NumInputChannels > 0 )
    {
        result = PaHost_StopInput( past, abort );
        DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
        if( result != paNoError ) goto error;
    }

    if( past->past_NumOutputChannels > 0 )
    {
        result = PaHost_StopOutput( past, abort );
        DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
        if( result != paNoError ) goto error;
    }

error:
    past->past_Usage = 0;
    past->past_IfLastExitValid = 0;

    return result;
}

/*************************************************************************/
PaError Pa_StreamActive( PortAudioStream *stream )
{
    internalPortAudioStream   *past;
    if( stream == NULL ) return paBadStreamPtr;
    past = (internalPortAudioStream *) stream;
    return PaHost_StreamActive( past );
}

/*************************************************************************/
const char *Pa_GetErrorText( PaError errnum )
{
    const char *msg;

    switch(errnum)
    {
    case paNoError:                  msg = "Success"; break;
    case paHostError:                msg = "Host error."; break;
    case paInvalidChannelCount:      msg = "Invalid number of channels."; break;
    case paInvalidSampleRate:        msg = "Invalid sample rate."; break;
    case paInvalidDeviceId:          msg = "Invalid device ID."; break;
    case paInvalidFlag:              msg = "Invalid flag."; break;
    case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
    case paBadIODeviceCombination:   msg = "Illegal combination of I/O devices."; break;
    case paInsufficientMemory:       msg = "Insufficient memory."; break;
    case paBufferTooBig:             msg = "Buffer too big."; break;
    case paBufferTooSmall:           msg = "Buffer too small."; break;
    case paNullCallback:             msg = "No callback routine specified."; break;
    case paBadStreamPtr:             msg = "Invalid stream pointer."; break;
    case paTimedOut    :             msg = "Wait Timed Out."; break;
    case paInternalError:            msg = "Internal PortAudio Error."; break;
    case paDeviceUnavailable:        msg = "Device Unavailable."; break;
    default:                         msg = "Illegal error number."; break;
    }
    return msg;
}

/*
 Get CPU Load as a fraction of total CPU time.
 A value of 0.5 would imply that PortAudio and the sound generating
 callback was consuming roughly 50% of the available CPU time.
 The amount may vary depending on CPU load.
 This function may be called from the callback function.
*/
double Pa_GetCPULoad(  PortAudioStream* stream)
{
    internalPortAudioStream   *past;
    if( stream == NULL ) return (double) paBadStreamPtr;
    past = (internalPortAudioStream *) stream;
    return past->past_Usage;
}

/*************************************************************************/
internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream )
{
    internalPortAudioStream* result = (internalPortAudioStream*) stream;

    if( result == NULL || result->past_Magic != PA_MAGIC )
        return NULL;
    else
        return result;
}

/*************************************************************
** Calculate 2 LSB dither signal with a triangular distribution.
** Ranged properly for adding to a 32 bit integer prior to >>15.
** Range of output is +/- 32767
*/
#define PA_DITHER_BITS   (15)
#define PA_DITHER_SCALE  (1.0f / ((1<<PA_DITHER_BITS)-1))
long PaConvert_TriangularDither( void )
{
    static unsigned long previous = 0;
    static unsigned long randSeed1 = 22222;
    static unsigned long randSeed2 = 5555555;
    long current, highPass;
    /* Generate two random numbers. */
    randSeed1 = (randSeed1 * 196314165) + 907633515;
    randSeed2 = (randSeed2 * 196314165) + 907633515;
    /* Generate triangular distribution about 0.
     * Shift before adding to prevent overflow which would skew the distribution.
     * Also shift an extra bit for the high pass filter. 
     */
#define DITHER_SHIFT  ((32 - PA_DITHER_BITS) + 1)
    current = (((long)randSeed1)>>DITHER_SHIFT) + (((long)randSeed2)>>DITHER_SHIFT);
    /* High pass filter to reduce audibility. */
    highPass = current - previous;
    previous = current;
    return highPass;
}

/*************************************************************************
** Called by host code.
** Convert input from Int16, call user code, then convert output
** to Int16 format for native use.
** Assumes host native format is paInt16.
** Returns result from user callback.
*/
long Pa_CallConvertInt16( internalPortAudioStream   *past,
                          short *nativeInputBuffer,
                          short *nativeOutputBuffer )
{
    long              temp;
    int               userResult;
    unsigned int      i;
    void             *inputBuffer = NULL;
    void             *outputBuffer = NULL;

#if SUPPORT_AUDIO_CAPTURE
    /* Get native data from DirectSound. */
    if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
    {
        /* Convert from native format to PA format. */
        unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
        switch(past->past_InputSampleFormat)
        {

        case paFloat32:
            {
                float *inBufPtr = (float *) past->past_InputBuffer;
                inputBuffer = past->past_InputBuffer;
                for( i=0; i<samplesPerBuffer; i++ )
                {
                    inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
                }
                break;
            }

        case paInt32:
            {
                /* Convert 16 bit data to 32 bit integers */
                int *inBufPtr = (int *) past->past_InputBuffer;
                inputBuffer = past->past_InputBuffer;
                for( i=0; i<samplesPerBuffer; i++ )
                {
                    inBufPtr[i] = nativeInputBuffer[i] << 16;
                }
                break;
            }

        case paInt16:
            {
                /* Already in correct format so don't copy. */
                inputBuffer = nativeInputBuffer;
                break;
            }

        case paInt8:
            {
                /* Convert 16 bit data to 8 bit chars */
                char *inBufPtr = (char *) past->past_InputBuffer;
                inputBuffer = past->past_InputBuffer;
                if( past->past_Flags & paDitherOff )
                {
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
                    }
                }
                else
                {
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        temp = nativeInputBuffer[i];
                        temp += PaConvert_TriangularDither() >> 8; /* PLB20010820 */
                        temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
                        inBufPtr[i] = (char)(temp >> 8);
                    }
                }
                break;
            }

        case paUInt8:
            {
                /* Convert 16 bit data to 8 bit unsigned chars */
                unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
                inputBuffer = past->past_InputBuffer;
                if( past->past_Flags & paDitherOff )
                {
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
                    }
                }
                else
                {
                    /* If you dither then you have to clip because dithering could push the signal out of range! */
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        temp = nativeInputBuffer[i];
                        temp += PaConvert_TriangularDither() >> 8; /* PLB20010820 */
                        temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
                        inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
                    }
                }
                break;
            }

        default:
            break;
        }
    }
#endif /* SUPPORT_AUDIO_CAPTURE */

    /* Are we doing output time? */
    if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
    {
        /* May already be in native format so just write directly to native buffer. */
        outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
                       nativeOutputBuffer : past->past_OutputBuffer;
    }
    /*
     AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
     AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
    */
    /* Call user callback routine. */
    userResult = past->past_Callback(
                     inputBuffer,
                     outputBuffer,
                     past->past_FramesPerUserBuffer,
                     past->past_FrameCount,
                     past->past_UserData );

    past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;

    /* Convert to native format if necessary. */
    if( outputBuffer != NULL )
    {
        unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
        switch(past->past_OutputSampleFormat)
        {
        case paFloat32:
            {
                float *outBufPtr = (float *) past->past_OutputBuffer;
                if( past->past_Flags & paDitherOff )
                {
                    if( past->past_Flags & paClipOff ) /* NOTHING */
                    {
                        for( i=0; i<samplesPerBuffer; i++ )
                        {
                            *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
                        }
                    }
                    else /* CLIP */
                    {
                        for( i=0; i<samplesPerBuffer; i++ )
                        {
                            temp = (long)(outBufPtr[i] * 32767.0f);
                            *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
                        }
                    }
                }
                else
                {
                    /* If you dither then you have to clip because dithering could push the signal out of range! */
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        float dither  = PaConvert_TriangularDither()*PA_DITHER_SCALE;
                        float dithered = (outBufPtr[i] * (32767.0f)) + dither;
                        temp = (long) (dithered);
                        *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
                    }
                }
                break;
            }

        case paInt32:
            {
                int *outBufPtr = (int *) past->past_OutputBuffer;
                if( past->past_Flags & paDitherOff )
                {
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
                    }
                }
                else
                {
                    for( i=0; i<samplesPerBuffer; i++ )
                    {
                        /* Shift one bit down before dithering so that we have room for overflow from add. */
                        temp = (outBufPtr[i] >> 1) + PaConvert_TriangularDither();
                        temp = temp >> 15;
                        *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
                    }
                }
                break;
            }

        case paInt8:
            {
                char *outBufPtr = (char *) past->past_OutputBuffer;
                for( i=0; i<samplesPerBuffer; i++ )
                {
                    *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
                }
                break;
            }

        case paUInt8:
            {
                unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
                for( i=0; i<samplesPerBuffer; i++ )
                {
                    *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
                }
                break;
            }

        default:
            break;
        }

    }

    return userResult;
}

/*************************************************************************/
PaError Pa_Initialize( void )
{
    if( gInitCount++ > 0 ) return paNoError;
    ResetTraceMessages();
    return PaHost_Init();
}

PaError Pa_Terminate( void )
{
    PaError result = paNoError;

    if( gInitCount == 0 ) return paNoError;
    else if( --gInitCount == 0 )
    {
        result = PaHost_Term();
        DumpTraceMessages();
    }
    return result;
}

int PaHost_IsInitialized()
{
    return gInitCount;
}

/*************************************************************************/
PaError Pa_GetSampleSize( PaSampleFormat format )
{
    int size;
    switch(format )
    {

    case paUInt8:
    case paInt8:
        size = 1;
        break;

    case paInt16:
        size = 2;
        break;

    case paPackedInt24:
        size = 3;
        break;

    case paFloat32:
    case paInt32:
    case paInt24:
        size = 4;
        break;

    default:
        size = paSampleFormatNotSupported;
        break;
    }
    return (PaError) size;
}

<p><p><p><p>1.1                  theora/win32/experimental/splayer/portaudio/pa_common/pa_trace.c

Index: pa_trace.c
===================================================================
/*
 * $Id: pa_trace.c,v 1.1 2003/05/23 14:12:12 mauricio Exp $
 * Portable Audio I/O Library Trace Facility
 * Store trace information in real-time for later printing.
 *
 * Based on the Open Source API proposed by Ross Bencina
 * Copyright (c) 1999-2000 Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pa_trace.h"

#if TRACE_REALTIME_EVENTS

tatic char *traceTextArray[MAX_TRACE_RECORDS];
static int traceIntArray[MAX_TRACE_RECORDS];
static int traceIndex = 0;
static int traceBlock = 0;

/*********************************************************************/
void ResetTraceMessages()
{
    traceIndex = 0;
}

/*********************************************************************/
void DumpTraceMessages()
{
    int i;
    int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;

    printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
    for( i=0; i<numDump; i++ )
    {
        printf("%3d: %s = 0x%08X\n",
               i, traceTextArray[i], traceIntArray[i] );
    }
    ResetTraceMessages();
    fflush(stdout);
}

/*********************************************************************/
void AddTraceMessage( char *msg, int data )
{
    if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
    {
        traceBlock = 1;
        /*  DumpTraceMessages(); */
    }
    else if( traceIndex < MAX_TRACE_RECORDS )
    {
        traceTextArray[traceIndex] = msg;
        traceIntArray[traceIndex] = data;
        traceIndex++;
    }
}

#endif

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pa_common/pa_trace.h

Index: pa_trace.h
===================================================================
#ifndef PA_TRACE_H
#define PA_TRACE_H
/*
 * $Id: pa_trace.h,v 1.1 2003/05/23 14:12:12 mauricio Exp $
 * Portable Audio I/O Library Trace Facility
 * Store trace information in real-time for later printing.
 *
 * Based on the Open Source API proposed by Ross Bencina
 * Copyright (c) 1999-2000 Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

<p>#define TRACE_REALTIME_EVENTS     (0)   /* Keep log of various real-time events. */
#define MAX_TRACE_RECORDS      (2048)

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

<p>    /************************************************************************************/
    /****************** Prototypes ******************************************************/
    /************************************************************************************/

#if TRACE_REALTIME_EVENTS

    void DumpTraceMessages();
    void ResetTraceMessages();
    void AddTraceMessage( char *msg, int data );

#else

#define AddTraceMessage(msg,data) /* noop */
#define ResetTraceMessages() /* noop */
#define DumpTraceMessages() /* noop */

#endif

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* PA_TRACE_H */

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pa_common/portaudio.h

Index: portaudio.h
===================================================================
#ifndef PORT_AUDIO_H
#define PORT_AUDIO_H

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

/*
 * $Id: portaudio.h,v 1.1 2003/05/23 14:12:12 mauricio Exp $
 * PortAudio Portable Real-Time Audio Library
 * PortAudio API Header File
 * Latest version available at: http://www.audiomulch.com/portaudio/
 *
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

typedef int PaError;
typedef enum {
    paNoError = 0,

    paHostError = -10000,
    paInvalidChannelCount,
    paInvalidSampleRate,
    paInvalidDeviceId,
    paInvalidFlag,
    paSampleFormatNotSupported,
    paBadIODeviceCombination,
    paInsufficientMemory,
    paBufferTooBig,
    paBufferTooSmall,
    paNullCallback,
    paBadStreamPtr,
    paTimedOut,
    paInternalError,
    paDeviceUnavailable
} PaErrorNum;

/*
 Pa_Initialize() is the library initialisation function - call this before
 using the library.

*/

PaError Pa_Initialize( void );

/*
 Pa_Terminate() is the library termination function - call this after
 using the library.

*/

PaError Pa_Terminate( void );

/*
 Pa_GetHostError() returns a host specific error code.
 This can be called after receiving a PortAudio error code of paHostError.

*/

long Pa_GetHostError( void );

/*
 Pa_GetErrorText() translates the supplied PortAudio error number
 into a human readable message.
 
*/

const char *Pa_GetErrorText( PaError errnum );

/*
 Sample formats
 
 These are formats used to pass sound data between the callback and the
 stream. Each device has a "native" format which may be used when optimum
 efficiency or control over conversion is required.
 
 Formats marked "always available" are supported (emulated) by all 
 PortAudio implementations.
 
 The floating point representation (paFloat32) uses +1.0 and -1.0 as the 
 maximum and minimum respectively.

 paUInt8 is an unsigned 8 bit format where 128 is considered "ground"

*/

typedef unsigned long PaSampleFormat;
#define paFloat32      ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16        ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32        ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24        ((PaSampleFormat) (1<<3))
#define paPackedInt24  ((PaSampleFormat) (1<<4))
#define paInt8         ((PaSampleFormat) (1<<5))
#define paUInt8        ((PaSampleFormat) (1<<6))
#define paCustomFormat ((PaSampleFormat) (1<<16))

/*
 Device enumeration mechanism.
 
 Device ids range from 0 to Pa_CountDevices()-1.
 
 Devices may support input, output or both.

*/

typedef int PaDeviceID;
#define paNoDevice -1

int Pa_CountDevices( void );

typedef struct
{
    int structVersion;
    const char *name;
    int maxInputChannels;
    int maxOutputChannels;
    /* Number of discrete rates, or -1 if range supported. */
    int numSampleRates;
    /* Array of supported sample rates, or {min,max} if range supported. */
    const double *sampleRates;
    PaSampleFormat nativeSampleFormats;
}
PaDeviceInfo;

/*
 Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
 default device ids for input and output respectively, or paNoDevice if
 no device is available.
 The result can be passed to Pa_OpenStream().
 
 On the PC, the user can specify a default device by
 setting an environment variable. For example, to use device #1.
 
  set PA_RECOMMENDED_OUTPUT_DEVICE=1
 
 The user should first determine the available device ids by using
 the supplied application "pa_devs".

*/

PaDeviceID Pa_GetDefaultInputDeviceID( void );
PaDeviceID Pa_GetDefaultOutputDeviceID( void );

<p><p>/*
 Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
 for the device specified.
 If the device parameter is out of range the function returns NULL.

 PortAudio manages the memory referenced by the returned pointer, the client
 must not manipulate or free the memory. The pointer is only guaranteed to be
 valid between calls to Pa_Initialize() and Pa_Terminate().

*/

const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );

/*
 PaTimestamp is used to represent a continuous sample clock with arbitrary
 start time that can be used for syncronization. The type is used for the
 outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()

*/

typedef double PaTimestamp;

/*
 PortAudioCallback is implemented by PortAudio clients.
 
 inputBuffer and outputBuffer are arrays of interleaved samples,
 the format, packing and number of channels used by the buffers are
 determined by parameters to Pa_OpenStream() (see below).
 
 framesPerBuffer is the number of sample frames to be processed by the callback.
 
 outTime is the time in samples when the buffer(s) processed by
 this callback will begin being played at the audio output.
 See also Pa_StreamTime()
 
 userData is the value of a user supplied pointer passed to Pa_OpenStream()
 intended for storing synthesis data etc.
 
 return value:
 The callback can return a non-zero value to stop the stream. This may be
 useful in applications such as soundfile players where a specific duration
 of output is required. However, it is not necessary to utilise this mechanism
 as StopStream() will also terminate the stream. A callback returning a
 non-zero value must fill the entire outputBuffer.
 
 NOTE: None of the other stream functions may be called from within the
 callback function except for Pa_GetCPULoad().

*/

typedef int (PortAudioCallback)(
    void *inputBuffer, void *outputBuffer,
    unsigned long framesPerBuffer,
    PaTimestamp outTime, void *userData );

<p>/*
 Stream flags
 
 These flags may be supplied (ored together) in the streamFlags argument to
 the Pa_OpenStream() function.

*/

#define   paNoFlag      (0)
#define   paClipOff     (1<<0)   /* disable default clipping of out of range samples */
#define   paDitherOff   (1<<1)   /* disable default dithering */
#define   paPlatformSpecificFlags (0x00010000)
typedef   unsigned long PaStreamFlags;

/*
 A single PortAudioStream provides multiple channels of real-time
 input and output audio streaming to a client application.
 Pointers to PortAudioStream objects are passed between PortAudio functions.
*/

typedef void PortAudioStream;
#define PaStream PortAudioStream

/*
 Pa_OpenStream() opens a stream for either input, output or both.
 
 stream is the address of a PortAudioStream pointer which will receive
 a pointer to the newly opened stream.
 
 inputDevice is the id of the device used for input (see PaDeviceID above.)
 inputDevice may be paNoDevice to indicate that an input device is not required.
 
 numInputChannels is the number of channels of sound to be delivered to the
 callback. It can range from 1 to the value of maxInputChannels in the
 PaDeviceInfo record for the device specified by the inputDevice parameter.
 If inputDevice is paNoDevice numInputChannels is ignored.
 
 inputSampleFormat is the sample format of inputBuffer provided to the callback
 function. inputSampleFormat may be any of the formats described by the
 PaSampleFormat enumeration (see above). PortAudio guarantees support for
 the device's native formats (nativeSampleFormats in the device info record)
 and additionally 16 and 32 bit integer and 32 bit floating point formats.
 Support for other formats is implementation defined.
 
 inputDriverInfo is a pointer to an optional driver specific data structure
 containing additional information for device setup or stream processing.
 inputDriverInfo is never required for correct operation. If not used
 inputDriverInfo should be NULL.
 
 outputDevice is the id of the device used for output (see PaDeviceID above.)
 outputDevice may be paNoDevice to indicate that an output device is not required.
 
 numOutputChannels is the number of channels of sound to be supplied by the
 callback. See the definition of numInputChannels above for more details.
 
 outputSampleFormat is the sample format of the outputBuffer filled by the
 callback function. See the definition of inputSampleFormat above for more
 details.
 
 outputDriverInfo is a pointer to an optional driver specific data structure
 containing additional information for device setup or stream processing.
 outputDriverInfo is never required for correct operation. If not used
 outputDriverInfo should be NULL.
 
 sampleRate is the desired sampleRate. For full-duplex streams it is the
 sample rate for both input and output
 
 framesPerBuffer is the length in sample frames of all internal sample buffers
 used for communication with platform specific audio routines. Wherever
 possible this corresponds to the framesPerBuffer parameter passed to the
 callback function.
 
 numberOfBuffers is the number of buffers used for multibuffered communication
 with the platform specific audio routines. If you pass zero, then an optimum
 value will be chosen for you internally. This parameter is provided only
 as a guide - and does not imply that an implementation must use multibuffered
 i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
 on the Macintosh.)
 
 streamFlags may contain a combination of flags ORed together.
 These flags modify the behaviour of the streaming process. Some flags may only
 be relevant to certain buffer formats.
 
 callback is a pointer to a client supplied function that is responsible
 for processing and filling input and output buffers (see above for details.)
 
 userData is a client supplied pointer which is passed to the callback
 function. It could for example, contain a pointer to instance data necessary
 for processing the audio buffers.
 
 return value:
 Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
 valid PortAudioStream in the stream argument. The stream is inactive (stopped).
 If a call to Pa_OpenStream() fails a non-zero error code is returned (see
 PaError above) and the value of stream is invalid.
 
*/

PaError Pa_OpenStream( PortAudioStream** stream,
                       PaDeviceID inputDevice,
                       int numInputChannels,
                       PaSampleFormat inputSampleFormat,
                       void *inputDriverInfo,
                       PaDeviceID outputDevice,
                       int numOutputChannels,
                       PaSampleFormat outputSampleFormat,
                       void *outputDriverInfo,
                       double sampleRate,
                       unsigned long framesPerBuffer,
                       unsigned long numberOfBuffers,
                       PaStreamFlags streamFlags,
                       PortAudioCallback *callback,
                       void *userData );

<p>/*
 Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
 the default input and/or output devices. Most parameters have identical meaning
 to their Pa_OpenStream() counterparts, with the following exceptions:
 
 If either numInputChannels or numOutputChannels is 0 the respective device
 is not opened. This has the same effect as passing paNoDevice in the device
 arguments to Pa_OpenStream().
 
 sampleFormat applies to both the input and output buffers.

*/

PaError Pa_OpenDefaultStream( PortAudioStream** stream,
                              int numInputChannels,
                              int numOutputChannels,
                              PaSampleFormat sampleFormat,
                              double sampleRate,
                              unsigned long framesPerBuffer,
                              unsigned long numberOfBuffers,
                              PortAudioCallback *callback,
                              void *userData );

/*
 Pa_CloseStream() closes an audio stream, flushing any pending buffers.

*/

PaError Pa_CloseStream( PortAudioStream* );

/*
 Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
 Pa_StopStream() waits until all pending audio buffers have been played.
 Pa_AbortStream() stops playing immediately without waiting for pending
 buffers to complete.
    
*/

PaError Pa_StartStream( PortAudioStream *stream );

PaError Pa_StopStream( PortAudioStream *stream );

PaError Pa_AbortStream( PortAudioStream *stream );

/*
 Pa_StreamActive() returns one (1) when the stream is active (ie playing
 or recording audio), zero (0) when not playing, or a negative error number
 if the stream is invalid.
 The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
 but may also become inactive if the callback returns a non-zero value.
 In the latter case, the stream is considered inactive after the last
 buffer has finished playing.
 
*/

PaError Pa_StreamActive( PortAudioStream *stream );

/*
 Pa_StreamTime() returns the current output time in samples for the stream.
 This time may be used as a time reference (for example synchronizing audio to
 MIDI).
 
*/

PaTimestamp Pa_StreamTime( PortAudioStream *stream );

/*
 Pa_GetCPULoad() returns the CPU Load for the stream.
 The "CPU Load" is a fraction of total CPU time consumed by the stream's
 audio processing routines including, but not limited to the client supplied
 callback.
 A value of 0.5 would imply that PortAudio and the sound generating
 callback was consuming roughly 50% of the available CPU time.
 This function may be called from the callback function or the application.
 
*/

double Pa_GetCPULoad( PortAudioStream* stream );

/*
 Pa_GetMinNumBuffers() returns the minimum number of buffers required by
 the current host based on minimum latency.
 On the PC, for the DirectSound implementation, latency can be optionally set
 by user by setting an environment variable.
 For example, to set latency to 200 msec, put:
 
    set PA_MIN_LATENCY_MSEC=200
 
 in the AUTOEXEC.BAT file and reboot.
 If the environment variable is not set, then the latency will be determined
 based on the OS. Windows NT has higher latency than Win95.
 
*/

int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );

/*
 Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
 You may sleep longer than the requested time so don't rely on this for
 accurate musical timing.
 
 Pa_Sleep() is provided as a convenience for authors of portable code (such as
 the tests and examples in the PortAudio distribution.)
 
*/

void Pa_Sleep( long msec );

/*
 Pa_GetSampleSize() returns the size in bytes of a single sample in the
 supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
 no supported.
  
*/

PaError Pa_GetSampleSize( PaSampleFormat format );

<p>#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PORT_AUDIO_H */

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pa_win_wmme/pa_win_wmme.c

Index: pa_win_wmme.c
===================================================================
/*
 * $Id: pa_win_wmme.c,v 1.1 2003/05/23 14:12:13 mauricio Exp $
 * pa_win_wmme.c
 * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
 *
 * PortAudio Portable Real-Time Audio Library
 * Latest Version at: http://www.portaudio.com
 *
 * Authors: Ross Bencina and Phil Burk
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtainingF
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
/*
  All memory allocations and frees are marked with MEM for quick review.
*/

/* Modification History:
 PLB = Phil Burk
 JM = Julien Maillard
 RDB = Ross Bencina
 PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
 PLB20010413 - check for excessive numbers of channels
 PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
               including condition including of memory.h,
               and explicit typecasting on memory allocation
 PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
 PLB20010816 - pass process instead of thread to SetPriorityClass()
 PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
 JM20020118 - prevent hung thread when buffers underflow.
 PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
 RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
 RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
               refactoring, renaming and fixed a few edge case bugs
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <mmsystem.h>
#include <process.h>
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifndef __MWERKS__
#include <malloc.h>
#include <memory.h>
#endif /* __MWERKS__ */
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"

/************************************************* Constants ********/
#define PA_TRACK_MEMORY          (0)

#define PA_USE_TIMER_CALLBACK    (0)  /* Select between two options for background task. 0=thread, 1=timer */
/* Switches for debugging. */
#define PA_SIMULATE_UNDERFLOW    (0)  /* Set to one to force an underflow of the output buffer. */

/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
#define PA_TRACE_RUN             (0)
#define PA_TRACE_START_STOP      (1)

#define PA_USE_HIGH_LATENCY      (0)  /* For debugging glitches. */

#if PA_USE_HIGH_LATENCY
 #define PA_MIN_MSEC_PER_HOST_BUFFER  (100)
 #define PA_MAX_MSEC_PER_HOST_BUFFER  (300) /* Do not exceed unless user buffer exceeds */
 #define PA_MIN_NUM_HOST_BUFFERS      (4)
 #define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
 #define PA_WIN_9X_LATENCY            (400)
#else
 #define PA_MIN_MSEC_PER_HOST_BUFFER  (10)
 #define PA_MAX_MSEC_PER_HOST_BUFFER  (100) /* Do not exceed unless user buffer exceeds */
 #define PA_MIN_NUM_HOST_BUFFERS      (3)
 #define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
 #define PA_WIN_9X_LATENCY            (200)
#endif
#define MIN_TIMEOUT_MSEC                 (1000)

/*
** Use higher latency for NT because it is even worse at real-time
** operation than Win9x.
*/
#define PA_WIN_NT_LATENCY        (PA_WIN_9X_LATENCY * 2)
#define PA_WIN_WDM_LATENCY       (PA_WIN_9X_LATENCY)

#if PA_SIMULATE_UNDERFLOW
static  gUnderCallbackCounter = 0;
#define UNDER_SLEEP_AT       (40)
#define UNDER_SLEEP_FOR      (500)
#endif

#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x)  /* PRINT(x) /**/
#define DBUGX(x) /* PRINT(x) */
/************************************************* Definitions ********/
/**************************************************************
 * Structure for internal host specific stream data.
 * This is allocated on a per stream basis.
 */
typedef struct PaWMMEStreamData
{
    /* Input -------------- */
    HWAVEIN            hWaveIn;
    WAVEHDR           *inputBuffers;
    int                currentInputBuffer;
    int                bytesPerHostInputBuffer;
    int                bytesPerUserInputBuffer;    /* native buffer size in bytes */
    /* Output -------------- */
    HWAVEOUT           hWaveOut;
    WAVEHDR           *outputBuffers;
    int                currentOutputBuffer;
    int                bytesPerHostOutputBuffer;
    int                bytesPerUserOutputBuffer;    /* native buffer size in bytes */
    /* Run Time -------------- */
    PaTimestamp        framesPlayed;
    long               lastPosition;                /* used to track frames played. */
    /* For measuring CPU utilization. */
    LARGE_INTEGER      entryCount;
    double             inverseTicksPerHostBuffer;
    /* Init Time -------------- */
    int                numHostBuffers;
    int                framesPerHostBuffer;
    int                userBuffersPerHostBuffer;
    CRITICAL_SECTION   streamLock;                  /* Mutext to prevent threads from colliding. */
    INT                streamLockInited;
#if PA_USE_TIMER_CALLBACK
    BOOL               ifInsideCallback;            /* Test for reentrancy. */
    MMRESULT           timerID;
#else
    HANDLE             abortEvent;
    int                abortEventInited;
    HANDLE             bufferEvent;
    int                bufferEventInited;
    HANDLE             engineThread;
    DWORD              engineThreadID;
#endif
}
PaWMMEStreamData;
/************************************************* Shared Data ********/
/* FIXME - put Mutex around this shared data. */
static int sNumInputDevices = 0;
static int sNumOutputDevices = 0;
static int sNumDevices = 0;
static PaDeviceInfo **sDevicePtrs = NULL;
static int sDefaultInputDeviceID = paNoDevice;
static int sDefaultOutputDeviceID = paNoDevice;
static int sPaHostError = 0;
static const char sMapperSuffixInput[] = " - Input";
static const char sMapperSuffixOutput[] = " - Output";

#if PA_TRACK_MEMORY
static int sNumAllocations = 0;
#endif

/************************************************* Macros ********/
/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
/************************************************* Prototypes **********/

void Pa_InitializeNumDevices( void );
PaError Pa_AllocateDevicePtrs( void );

tatic void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
                                      DWORD dwUser, DWORD dw1, DWORD dw2);
PaError PaHost_GetTotalBufferFrames( internalPortAudioStream   *past );
static PaError PaHost_UpdateStreamTime( PaWMMEStreamData *wmmeStreamData );
static PaError PaHost_BackgroundManager( internalPortAudioStream   *past );

tatic void *PaHost_AllocateTrackedMemory( long numBytes );
static void PaHost_FreeTrackedMemory( void *addr );

/*******************************************************************/
static PaError PaHost_AllocateWMMEStreamData( internalPortAudioStream *stream )
{
    PaError             result = paNoError;
    PaWMMEStreamData *wmmeStreamData;
    
    wmmeStreamData = (PaWMMEStreamData *) PaHost_AllocateFastMemory(sizeof(PaWMMEStreamData)); /* MEM */
    if( wmmeStreamData == NULL )
    {
        result = paInsufficientMemory;
        goto error;
    }
    memset( wmmeStreamData, 0, sizeof(PaWMMEStreamData) );
    stream->past_DeviceData = (void *) wmmeStreamData;

    return result;
    
error:
    return result;
}

/*******************************************************************/
static void PaHost_FreeWMMEStreamData( internalPortAudioStream *internalStream )
{
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) internalStream->past_DeviceData;

    PaHost_FreeFastMemory( wmmeStreamData, sizeof(PaWMMEStreamData) ); /* MEM */
    internalStream->past_DeviceData = NULL;
}
/*************************************************************************/
static PaWMMEStreamData* PaHost_GetWMMEStreamData( internalPortAudioStream* internalStream )
{
    PaWMMEStreamData *result = NULL;

    if( internalStream != NULL )
    {
        result = (PaWMMEStreamData *) internalStream->past_DeviceData;
    }
    return result;
}
/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
/* FIXME: the cpu usage code should be factored out into a common module */
static void Pa_InitializeCpuUsageScalar( internalPortAudioStream   *stream )
{
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;

    LARGE_INTEGER frequency;
    if( QueryPerformanceFrequency( &frequency ) == 0 )
    {
        wmmeStreamData->inverseTicksPerHostBuffer = 0.0;
    }
    else
    {
        wmmeStreamData->inverseTicksPerHostBuffer = stream->past_SampleRate /
                ( (double)frequency.QuadPart * stream->past_FramesPerUserBuffer * wmmeStreamData->userBuffersPerHostBuffer );
        DBUG(("inverseTicksPerHostBuffer = %g\n", wmmeStreamData->inverseTicksPerHostBuffer ));
    }
}
static void Pa_StartUsageCalculation( internalPortAudioStream   *stream )
{
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;

    if( wmmeStreamData == NULL ) return;
    /* Query system timer for usage analysis and to prevent overuse of CPU. */
    QueryPerformanceCounter( &wmmeStreamData->entryCount );
}
static void Pa_EndUsageCalculation( internalPortAudioStream   *stream )
{
    LARGE_INTEGER CurrentCount;
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;

    if( wmmeStreamData == NULL ) return;
    /*
     * Measure CPU utilization during this callback. Note that this calculation
     * assumes that we had the processor the whole time.
     */
#define LOWPASS_COEFFICIENT_0   (0.9)
#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
    if( QueryPerformanceCounter( &CurrentCount ) )
    {
        LONGLONG InsideCount = CurrentCount.QuadPart - wmmeStreamData->entryCount.QuadPart;
        double newUsage = InsideCount * wmmeStreamData->inverseTicksPerHostBuffer;
        stream->past_Usage = (LOWPASS_COEFFICIENT_0 * stream->past_Usage) +
                             (LOWPASS_COEFFICIENT_1 * newUsage);
    }
}
/****************************************** END CPU UTILIZATION *******/

tatic void Pa_InitializeNumDevices( void )
{
    sNumInputDevices = waveInGetNumDevs();
    if( sNumInputDevices > 0 )
    {
        sNumInputDevices += 1; /* add one extra for the WAVE_MAPPER */
        sDefaultInputDeviceID = 0;
    }
    else
    {
        sDefaultInputDeviceID = paNoDevice;
    }

    sNumOutputDevices = waveOutGetNumDevs();
    if( sNumOutputDevices > 0 )
    {
        sNumOutputDevices += 1; /* add one extra for the WAVE_MAPPER */
        sDefaultOutputDeviceID = sNumInputDevices;
    }
    else
    {
        sDefaultOutputDeviceID = paNoDevice;
    }

    sNumDevices = sNumInputDevices + sNumOutputDevices;
}

tatic PaError Pa_AllocateDevicePtrs( void )
{
    int numBytes;
    int i;

    /* Allocate structures to hold device info. */
    /* PLB20010402 - was allocating too much memory. */
    /* numBytes = sNumDevices * sizeof(PaDeviceInfo);  // PLB20010402 */

    if( sNumDevices > 0 )
    {
        numBytes = sNumDevices * sizeof(PaDeviceInfo *); /* PLB20010402 */
        sDevicePtrs = (PaDeviceInfo **) PaHost_AllocateTrackedMemory( numBytes ); /* MEM */
        if( sDevicePtrs == NULL ) return paInsufficientMemory;

        for( i = 0; i < sNumDevices; i++ )
            sDevicePtrs[i] = NULL;  /* RDB20020417 explicitly set each ptr to NULL */
    }
    else
    {
        sDevicePtrs = NULL;
    }
    
    return paNoError;
}
/*************************************************************************/
long Pa_GetHostError()
{
    return sPaHostError;
}
/*************************************************************************/
int Pa_CountDevices()
{
    if( PaHost_IsInitialized() )
        return sNumDevices;
    else
        return 0;
}
/*************************************************************************
 * If a PaDeviceInfo structure has not already been created,
 * then allocate one and fill it in for the selected device.
 *
 * We create one extra input and one extra output device for the WAVE_MAPPER.
 * [Does anyone know how to query the default device and get its name?]
 */
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
{
#define NUM_STANDARDSAMPLINGRATES   3   /* 11.025, 22.05, 44.1 */
#define NUM_CUSTOMSAMPLINGRATES     5   /* must be the same number of elements as in the array below */
#define MAX_NUMSAMPLINGRATES        (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
    static DWORD customSamplingRates[] = { 32000, 48000, 64000, 88200, 96000 };
    PaDeviceInfo *deviceInfo;
    double *sampleRates; /* non-const ptr */
    int i;
    char *s;

    if( id < 0 || id >= sNumDevices )
        return NULL;
    if( sDevicePtrs[ id ] != NULL )
    {
        return sDevicePtrs[ id ];
    }
    deviceInfo = (PaDeviceInfo *)PaHost_AllocateTrackedMemory( sizeof(PaDeviceInfo) ); /* MEM */
    if( deviceInfo == NULL ) return NULL;
    deviceInfo->structVersion = 1;
    deviceInfo->maxInputChannels = 0;
    deviceInfo->maxOutputChannels = 0;
    deviceInfo->numSampleRates = 0;
    sampleRates = (double*)PaHost_AllocateTrackedMemory( MAX_NUMSAMPLINGRATES * sizeof(double) ); /* MEM */
    deviceInfo->sampleRates = sampleRates;
    deviceInfo->nativeSampleFormats = paInt16;       /* should query for higher bit depths below */
    if( id < sNumInputDevices )
    {
        /* input device */
        int inputMmID = PaDeviceIdToWinId(id);
        WAVEINCAPS wic;
        if( waveInGetDevCaps( inputMmID, &wic, sizeof( WAVEINCAPS ) ) != MMSYSERR_NOERROR )
            goto error;

        /* Append I/O suffix to WAVE_MAPPER device. */
        if( inputMmID == WAVE_MAPPER )
        {
            s = (char *) PaHost_AllocateTrackedMemory( strlen( wic.szPname ) + 1 + sizeof(sMapperSuffixInput) ); /* MEM */
            strcpy( s, wic.szPname );
            strcat( s, sMapperSuffixInput );
        }
        else
        {
            s = (char *) PaHost_AllocateTrackedMemory( strlen( wic.szPname ) + 1 ); /* MEM */
            strcpy( s, wic.szPname );
        }
        deviceInfo->name = s;
        deviceInfo->maxInputChannels = wic.wChannels;
        /* Sometimes a device can return a rediculously large number of channels.
         * This happened with an SBLive card on a Windows ME box.
         * If that happens, then force it to 2 channels.  PLB20010413
         */
        if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
        {
            ERR_RPT(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
            deviceInfo->maxInputChannels = 2;
        }
        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
         * based on the format flags. */
        if( wic.dwFormats & WAVE_FORMAT_1M16 ||wic.dwFormats & WAVE_FORMAT_1S16 )
            sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
        if( wic.dwFormats & WAVE_FORMAT_2M16 ||wic.dwFormats & WAVE_FORMAT_2S16 )
            sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
        if( wic.dwFormats & WAVE_FORMAT_4M16 ||wic.dwFormats & WAVE_FORMAT_4S16 )
            sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
         * based on opening the device successfully. */
        for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
        {
            WAVEFORMATEX wfx;
            wfx.wFormatTag = WAVE_FORMAT_PCM;
            wfx.nSamplesPerSec = customSamplingRates[i];
            wfx.wBitsPerSample = 16;
            wfx.cbSize = 0; /* ignored */
            wfx.nChannels = (WORD)deviceInfo->maxInputChannels;
            wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
            wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
            if( waveInOpen( NULL, inputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
            {
                sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
            }
        }

    }
    else if( id - sNumInputDevices < sNumOutputDevices )
    {
        /* output device */
        int outputMmID = PaDeviceIdToWinId(id);
        WAVEOUTCAPS woc;
        if( waveOutGetDevCaps( outputMmID, &woc, sizeof( WAVEOUTCAPS ) ) != MMSYSERR_NOERROR )
            goto error;
        /* Append I/O suffix to WAVE_MAPPER device. */
        if( outputMmID == WAVE_MAPPER )
        {
            s = (char *) PaHost_AllocateTrackedMemory( strlen( woc.szPname ) + 1 + sizeof(sMapperSuffixOutput) );  /* MEM */
            strcpy( s, woc.szPname );
            strcat( s, sMapperSuffixOutput );
        }
        else
        {
            s = (char *) PaHost_AllocateTrackedMemory( strlen( woc.szPname ) + 1 );  /* MEM */
            strcpy( s, woc.szPname );
        }
        deviceInfo->name = s;
        deviceInfo->maxOutputChannels = woc.wChannels;
        /* Sometimes a device can return a rediculously large number of channels.
         * This happened with an SBLive card on a Windows ME box.
         * It also happens on Win XP!
         */
        if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
        {
#if 1
            deviceInfo->maxOutputChannels = 2;
#else
        /* If channel max is goofy, then query for max channels. PLB20020228
         * This doesn't seem to help. Disable code for now. Remove it later.
         */
            ERR_RPT(("Pa_GetDeviceInfo: Num output channels reported as %d!", deviceInfo->maxOutputChannels ));
            deviceInfo->maxOutputChannels = 0;
                        /* Attempt to find the correct maximum by querying the device. */
                        for( i=2; i<16; i += 2 )
                        {
                                WAVEFORMATEX wfx;
                                wfx.wFormatTag = WAVE_FORMAT_PCM;
                                wfx.nSamplesPerSec = 44100;
                                wfx.wBitsPerSample = 16;
                                wfx.cbSize = 0; /* ignored */
                                wfx.nChannels = (WORD) i;
                                wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
                                wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
                                if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
                                {
                                        deviceInfo->maxOutputChannels = i;
                                }
                                else
                                {
                                        break;
                                }
                        }
#endif
            ERR_RPT((" Changed to %d.\n", deviceInfo->maxOutputChannels ));
        }

        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
         * based on the format flags. */
        if( woc.dwFormats & WAVE_FORMAT_1M16 ||woc.dwFormats & WAVE_FORMAT_1S16 )
            sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
        if( woc.dwFormats & WAVE_FORMAT_2M16 ||woc.dwFormats & WAVE_FORMAT_2S16 )
            sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
        if( woc.dwFormats & WAVE_FORMAT_4M16 ||woc.dwFormats & WAVE_FORMAT_4S16 )
            sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;

        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
         * based on opening the device successfully. */
        for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
        {
            WAVEFORMATEX wfx;
            wfx.wFormatTag = WAVE_FORMAT_PCM;
            wfx.nSamplesPerSec = customSamplingRates[i];
            wfx.wBitsPerSample = 16;
            wfx.cbSize = 0; /* ignored */
            wfx.nChannels = (WORD)deviceInfo->maxOutputChannels;
            wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
            wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
            if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
            {
                sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
            }
        }
    }
    sDevicePtrs[ id ] = deviceInfo;
    return deviceInfo;

error:
    PaHost_FreeTrackedMemory( sampleRates ); /* MEM */
    PaHost_FreeTrackedMemory( deviceInfo ); /* MEM */

    return NULL;
}
/*************************************************************************
 * Returns recommended device ID.
 * On the PC, the recommended device can be specified by the user by
 * setting an environment variable. For example, to use device #1.
 *
 *    set PA_RECOMMENDED_OUTPUT_DEVICE=1
 *
 * The user should first determine the available device ID by using
 * the supplied application "pa_devs".
 */
#define PA_ENV_BUF_SIZE  (32)
#define PA_REC_IN_DEV_ENV_NAME  ("PA_RECOMMENDED_INPUT_DEVICE")
#define PA_REC_OUT_DEV_ENV_NAME  ("PA_RECOMMENDED_OUTPUT_DEVICE")
static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
{
    DWORD   hresult;
    char    envbuf[PA_ENV_BUF_SIZE];
    PaDeviceID recommendedID = paNoDevice;

    /* Let user determine default device by setting environment variable. */
    hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
    {
        recommendedID = atoi( envbuf );
    }
    return recommendedID;
}
/**********************************************************************
 * Check for environment variable, else query devices and use result.
 */
PaDeviceID Pa_GetDefaultInputDeviceID( void )
{
    PaDeviceID result;

    result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
    if( result == paNoDevice || result < 0 || result >= sNumInputDevices )
    {
        result = sDefaultInputDeviceID;
    }
    return result;
}
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
{
    PaDeviceID result;

    result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
    if( result == paNoDevice || result < sNumInputDevices || result >= sNumDevices )
    {
        result = sDefaultOutputDeviceID;
    }
    return result;
}
/**********************************************************************
 * Initialize Host dependant part of API.
 */
PaError PaHost_Init( void )
{

#if PA_TRACK_MEMORY
    PRINT(("PaHost_Init: sNumAllocations = %d\n", sNumAllocations ));
#endif

#if PA_SIMULATE_UNDERFLOW
    PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
#endif
   

    Pa_InitializeNumDevices();

    return Pa_AllocateDevicePtrs();
}

/**********************************************************************
 * Check WAVE buffers to see if they are done.
 * Fill any available output buffers and use any available
 * input buffers by calling user callback.
 *
 * This routine will loop until:
 *    user callback returns !=0 OR
 *    all output buffers are filled OR
 *    past->past_StopSoon is set OR
 *    an error occurs when calling WMME.
 *
 * Returns >0 when user requests a stop, <0 on error.
 *
 */
static PaError Pa_TimeSlice( internalPortAudioStream *stream )
{
    PaError           result = paNoError;
    MMRESULT          mmresult;
    char             *inBufPtr;
    char             *outBufPtr;
    int               gotInput = 0;
    int               gotOutput = 0;
    int               i;
    int               buffersProcessed = 0;
    int               done = 0;
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;

    if( wmmeStreamData == NULL ) return paInternalError;

    stream->past_NumCallbacks += 1;
#if PA_TRACE_RUN
    AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", stream->past_NumCallbacks );
#endif

    /* JM20020118 - prevent hung thread when buffers underflow. */
    /* while( !done ) /* BAD */
    while( !done && !stream->past_StopSoon ) /* GOOD */
    {
#if PA_SIMULATE_UNDERFLOW
        if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
        {
            Sleep(UNDER_SLEEP_FOR);
        }
#endif

        /* If we are using output, then we need an empty output buffer. */
        gotOutput = 0;
        outBufPtr = NULL;
        if( stream->past_NumOutputChannels > 0 )
        {
            if((wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ].dwFlags & WHDR_DONE) == 0)
            {
                break;  /* If none empty then bail and try again later. */
            }
            else
            {
                outBufPtr = wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ].lpData;
                gotOutput = 1;
            }
        }
        /* Use an input buffer if one is available. */
        gotInput = 0;
        inBufPtr = NULL;
        if( ( stream->past_NumInputChannels > 0 ) &&
                (wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ].dwFlags & WHDR_DONE) )
        {
            inBufPtr = wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ].lpData;
            gotInput = 1;
#if PA_TRACE_RUN
            AddTraceMessage("Pa_TimeSlice: got input buffer at ", (int)inBufPtr );
            AddTraceMessage("Pa_TimeSlice: got input buffer # ", wmmeStreamData->currentInputBuffer );
#endif

        }
        /* If we can't do anything then bail out. */
        if( !gotInput && !gotOutput ) break;
        buffersProcessed += 1;
        /* Each Wave buffer contains multiple user buffers so do them all now. */
        /* Base Usage on time it took to process one host buffer. */
        Pa_StartUsageCalculation( stream );
        for( i=0; i<wmmeStreamData->userBuffersPerHostBuffer; i++ )
        {
            if( done )
            {
                if( gotOutput )
                {
                    /* Clear remainder of wave buffer if we are waiting for stop. */
                    AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
                    memset( outBufPtr, 0, wmmeStreamData->bytesPerUserOutputBuffer );
                }
            }
            else
            {
                /* Convert 16 bit native data to user data and call user routine. */
                result = Pa_CallConvertInt16( stream, (short *) inBufPtr, (short *) outBufPtr );
                if( result != 0) done = 1;
            }
            if( gotInput ) inBufPtr += wmmeStreamData->bytesPerUserInputBuffer;
            if( gotOutput) outBufPtr += wmmeStreamData->bytesPerUserOutputBuffer;
        }
        Pa_EndUsageCalculation( stream );
        /* Send WAVE buffer to Wave Device to be refilled. */
        if( gotInput )
        {
            mmresult = waveInAddBuffer( wmmeStreamData->hWaveIn,
                                       &wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ],
                                       sizeof(WAVEHDR) );
            if( mmresult != MMSYSERR_NOERROR )
            {
                sPaHostError = mmresult;
                result = paHostError;
                break;
            }
            wmmeStreamData->currentInputBuffer = (wmmeStreamData->currentInputBuffer+1 >= wmmeStreamData->numHostBuffers) ?
                                              0 : wmmeStreamData->currentInputBuffer+1;
        }
        /* Write WAVE buffer to Wave Device. */
        if( gotOutput )
        {
#if PA_TRACE_START_STOP
            AddTraceMessage( "Pa_TimeSlice: writing buffer ", wmmeStreamData->currentOutputBuffer );
#endif
            mmresult = waveOutWrite( wmmeStreamData->hWaveOut,
                                    &wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ],
                                    sizeof(WAVEHDR) );
            if( mmresult != MMSYSERR_NOERROR )
            {
                sPaHostError = mmresult;
                result = paHostError;
                break;
            }
            wmmeStreamData->currentOutputBuffer = (wmmeStreamData->currentOutputBuffer+1 >= wmmeStreamData->numHostBuffers) ?
                                               0 : wmmeStreamData->currentOutputBuffer+1;
        }

    }

#if PA_TRACE_RUN
    AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
#endif
    return (result != 0) ? result : done;
}

/*******************************************************************/
static PaError PaHost_BackgroundManager( internalPortAudioStream *stream )
{
    PaError      result = paNoError;
    int          i;
    int          numQueuedoutputBuffers = 0;
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;

    /* Has someone asked us to abort by calling Pa_AbortStream()? */
    if( stream->past_StopNow )
    {
        stream->past_IsActive = 0; /* Will cause thread to return. */
    }
    /* Has someone asked us to stop by calling Pa_StopStream()
     * OR has a user callback returned '1' to indicate finished.
     */
    else if( stream->past_StopSoon )
    {
        /* Poll buffer and when all have played then exit thread. */
        /* Count how many output buffers are queued. */
        numQueuedoutputBuffers = 0;
        if( stream->past_NumOutputChannels > 0 )
        {
            for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
            {
                if( !( wmmeStreamData->outputBuffers[ i ].dwFlags & WHDR_DONE) )
                {
#if PA_TRACE_START_STOP
                    AddTraceMessage( "PaHost_BackgroundManager: waiting for buffer ", i );
#endif
                    numQueuedoutputBuffers++;
                }
            }
        }
#if PA_TRACE_START_STOP
        AddTraceMessage( "PaHost_BackgroundManager: numQueuedoutputBuffers ", numQueuedoutputBuffers );
#endif
        if( numQueuedoutputBuffers == 0 )
        {
            stream->past_IsActive = 0; /* Will cause thread to return. */
        }
    }
    else
    {
        /* Process full input buffer and fill up empty output buffers. */
        if( (result = Pa_TimeSlice( stream )) != 0)
        {
            /* User callback has asked us to stop. */
#if PA_TRACE_START_STOP
            AddTraceMessage( "PaHost_BackgroundManager: TimeSlice() returned ", result );
#endif
            stream->past_StopSoon = 1; /* Request that audio play out then stop. */
            result = paNoError;
        }
    }

    PaHost_UpdateStreamTime( wmmeStreamData );
    return result;
}

#if PA_USE_TIMER_CALLBACK
/*******************************************************************/
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
    internalPortAudioStream   *stream;
    PaWMMEStreamData        *wmmeStreamData;
    PaError                    result;

    stream = (internalPortAudioStream *) dwUser;
    if( stream == NULL ) return;
    wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
    if( wmmeStreamData == NULL ) return;
    if( wmmeStreamData->ifInsideCallback )
    {
        if( wmmeStreamData->timerID != 0 )
        {
            timeKillEvent(wmmeStreamData->timerID);  /* Stop callback timer. */
            wmmeStreamData->timerID = 0;
        }
        return;
    }
    wmmeStreamData->ifInsideCallback = 1;
    /* Manage flags and audio processing. */
    result = PaHost_BackgroundManager( stream );
    if( result != paNoError )
    {
        stream->past_IsActive = 0;
    }
    wmmeStreamData->ifInsideCallback = 0;
}
#else /* PA_USE_TIMER_CALLBACK */
/*******************************************************************/
static DWORD WINAPI WinMMPa_OutputThreadProc( void *pArg )
{
    internalPortAudioStream *stream;
    PaWMMEStreamData      *wmmeStreamData;
    HANDLE       events[2];
    int          numEvents = 0;
    DWORD        result = 0;
    DWORD        waitResult;
    DWORD        numTimeouts = 0;
    DWORD        timeOut;
    stream = (internalPortAudioStream *) pArg;
    wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
#if PA_TRACE_START_STOP
    AddTraceMessage( "WinMMPa_OutputThreadProc: timeoutPeriod", timeoutPeriod );
    AddTraceMessage( "WinMMPa_OutputThreadProc: past_NumUserBuffers", stream->past_NumUserBuffers );
#endif
    /* Calculate timeOut as half the time it would take to play all buffers. */
    timeOut = (DWORD) (500.0 * PaHost_GetTotalBufferFrames( stream ) / stream->past_SampleRate);
    /* Get event(s) ready for wait. */
    events[numEvents++] = wmmeStreamData->bufferEvent;
    if( wmmeStreamData->abortEventInited ) events[numEvents++] = wmmeStreamData->abortEvent;
    /* Stay in this thread as long as we are "active". */
    while( stream->past_IsActive )
    {
        /*******************************************************************/
        /******** WAIT here for an event from WMME or PA *******************/
        /*******************************************************************/
        waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeOut );
        /* Error? */
        if( waitResult == WAIT_FAILED )
        {
            sPaHostError = GetLastError();
            result = paHostError;
            stream->past_IsActive = 0;
        }
        /* Timeout? Don't stop. Just keep polling for DONE.*/
        else if( waitResult == WAIT_TIMEOUT )
        {
#if PA_TRACE_START_STOP
            AddTraceMessage( "WinMMPa_OutputThreadProc: timed out ", numQueuedoutputBuffers );
#endif
            numTimeouts += 1;
        }
        /* Manage flags and audio processing. */
        result = PaHost_BackgroundManager( stream );
        if( result != paNoError )
        {
            stream->past_IsActive = 0;
        }
    }
    return result;
}
#endif

/*******************************************************************/
PaError PaHost_OpenInputStream( internalPortAudioStream   *stream )
{
    PaError          result = paNoError;
    MMRESULT         mmresult;
    PaWMMEStreamData *wmmeStreamData;
    int              i;
    int              inputMmId;
    int              bytesPerInputFrame;
    WAVEFORMATEX     wfx;
    const PaDeviceInfo  *deviceInfo;

    wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
    DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", stream->past_InputDeviceID));
    deviceInfo = Pa_GetDeviceInfo( stream->past_InputDeviceID );
    if( deviceInfo == NULL ) return paInternalError;

    switch( deviceInfo->nativeSampleFormats  )
    {
    case paInt32:
    case paFloat32:
        bytesPerInputFrame = sizeof(float) * stream->past_NumInputChannels;
        break;
    default:
        bytesPerInputFrame = sizeof(short) * stream->past_NumInputChannels;
        break;
    }
    wfx.wFormatTag = WAVE_FORMAT_PCM;
    wfx.nChannels = (WORD) stream->past_NumInputChannels;
    wfx.nSamplesPerSec = (DWORD) stream->past_SampleRate;
    wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * stream->past_SampleRate);
    wfx.nBlockAlign = (WORD)bytesPerInputFrame;
    wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/stream->past_NumInputChannels) * 8);
    wfx.cbSize = 0;
    inputMmId = PaDeviceIdToWinId( stream->past_InputDeviceID );
#if PA_USE_TIMER_CALLBACK
    mmresult = waveInOpen( &wmmeStreamData->hWaveIn, inputMmId, &wfx,
                     0, 0, CALLBACK_NULL );
#else
    mmresult = waveInOpen( &wmmeStreamData->hWaveIn, inputMmId, &wfx,
                     (DWORD)wmmeStreamData->bufferEvent, (DWORD) stream, CALLBACK_EVENT );
#endif
    if( mmresult != MMSYSERR_NOERROR )
    {
        ERR_RPT(("PortAudio: PaHost_OpenInputStream() failed!\n"));
        result = paHostError;
        sPaHostError = mmresult;
        goto error;
    }
    /* Allocate an array to hold the buffer pointers. */
    wmmeStreamData->inputBuffers = (WAVEHDR *) PaHost_AllocateTrackedMemory( sizeof(WAVEHDR)*wmmeStreamData->numHostBuffers ); /* MEM */
    if( wmmeStreamData->inputBuffers == NULL )
    {
        result = paInsufficientMemory;
        goto error;
    }
    /* Allocate each buffer. */
    for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
    {
        wmmeStreamData->inputBuffers[i].lpData = (char *)PaHost_AllocateTrackedMemory( wmmeStreamData->bytesPerHostInputBuffer ); /* MEM */
        if( wmmeStreamData->inputBuffers[i].lpData == NULL )
        {
            result = paInsufficientMemory;
            goto error;
        }
        wmmeStreamData->inputBuffers[i].dwBufferLength = wmmeStreamData->bytesPerHostInputBuffer;
        wmmeStreamData->inputBuffers[i].dwUser = i;
        if( ( mmresult = waveInPrepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
        {
            result = paHostError;
            sPaHostError = mmresult;
            goto error;
        }
    }
    return result;

error:
    return result;
}
/*******************************************************************/
PaError PaHost_OpenOutputStream( internalPortAudioStream *stream )
{
    PaError          result = paNoError;
    MMRESULT         mmresult;
    PaWMMEStreamData *wmmeStreamData;
    int              i;
    int              outputMmID;
    int              bytesPerOutputFrame;
    WAVEFORMATEX     wfx;
    const PaDeviceInfo *deviceInfo;

    wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
    DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", stream->past_OutputDeviceID));

    deviceInfo = Pa_GetDeviceInfo( stream->past_OutputDeviceID );
    if( deviceInfo == NULL ) return paInternalError;

    switch( deviceInfo->nativeSampleFormats  )
    {
    case paInt32:
    case paFloat32:
        bytesPerOutputFrame = sizeof(float) * stream->past_NumOutputChannels;
        break;
    default:
        bytesPerOutputFrame = sizeof(short) * stream->past_NumOutputChannels;
        break;
    }
    wfx.wFormatTag = WAVE_FORMAT_PCM;
    wfx.nChannels = (WORD) stream->past_NumOutputChannels;
    wfx.nSamplesPerSec = (DWORD) stream->past_SampleRate;
    wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * stream->past_SampleRate);
    wfx.nBlockAlign = (WORD)bytesPerOutputFrame;
    wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/stream->past_NumOutputChannels) * 8);
    wfx.cbSize = 0;
    outputMmID = PaDeviceIdToWinId( stream->past_OutputDeviceID );
#if PA_USE_TIMER_CALLBACK
    mmresult = waveOutOpen( &wmmeStreamData->hWaveOut, outputMmID, &wfx,
                      0, 0, CALLBACK_NULL );
#else

    wmmeStreamData->abortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    if( wmmeStreamData->abortEvent == NULL )
    {
        result = paHostError;
        sPaHostError = GetLastError();
        goto error;
    }
    wmmeStreamData->abortEventInited = 1;
    mmresult = waveOutOpen( &wmmeStreamData->hWaveOut, outputMmID, &wfx,
                      (DWORD)wmmeStreamData->bufferEvent, (DWORD) stream, CALLBACK_EVENT );
#endif
    if( mmresult != MMSYSERR_NOERROR )
    {
        ERR_RPT(("PortAudio: PaHost_OpenOutputStream() failed!\n"));
        result = paHostError;
        sPaHostError = mmresult;
        goto error;
    }
    /* Allocate an array to hold the buffer pointers. */
    wmmeStreamData->outputBuffers = (WAVEHDR *) PaHost_AllocateTrackedMemory( sizeof(WAVEHDR)*wmmeStreamData->numHostBuffers ); /* MEM */
    if( wmmeStreamData->outputBuffers == NULL )
    {
        result = paInsufficientMemory;
        goto error;
    }
    /* Allocate each buffer. */
    for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
    {
        wmmeStreamData->outputBuffers[i].lpData = (char *) PaHost_AllocateTrackedMemory( wmmeStreamData->bytesPerHostOutputBuffer ); /* MEM */
        if( wmmeStreamData->outputBuffers[i].lpData == NULL )
        {
            result = paInsufficientMemory;
            goto error;
        }
        wmmeStreamData->outputBuffers[i].dwBufferLength = wmmeStreamData->bytesPerHostOutputBuffer;
        wmmeStreamData->outputBuffers[i].dwUser = i;
        if( (mmresult = waveOutPrepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
        {
            result = paHostError;
            sPaHostError = mmresult;
            goto error;
        }
    }
    return result;

error:
    return result;
}
/*******************************************************************/
PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *stream )
{
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
    return wmmeStreamData->numHostBuffers * wmmeStreamData->framesPerHostBuffer;
}
/*******************************************************************
 * Determine number of WAVE Buffers
 * and how many User Buffers we can put into each WAVE buffer.
 */
static void PaHost_CalcNumHostBuffers( internalPortAudioStream *stream )
{
    PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
    unsigned int  minNumBuffers;
    int           minframesPerHostBuffer;
    int           maxframesPerHostBuffer;
    int           minTotalFrames;
    int           userBuffersPerHostBuffer;
    int           framesPerHostBuffer;
    int           numHostBuffers;

    /* Calculate minimum and maximum sizes based on timing and sample rate. */
    minframesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * stream->past_SampleRate * 0.001);
    minframesPerHostBuffer = (minframesPerHostBuffer + 7) & ~7;
    DBUG(("PaHost_CalcNumHostBuffers: minframesPerHostBuffer = %d\n", minframesPerHostBuffer ));
    maxframesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * stream->past_SampleRate * 0.001);
    maxframesPerHostBuffer = (maxframesPerHostBuffer + 7) & ~7;
    DBUG(("PaHost_CalcNumHostBuffers: maxframesPerHostBuffer = %d\n", maxframesPerHostBuffer ));
    /* Determine number of user buffers based on minimum latency. */
    minNumBuffers = Pa_GetMinNumBuffers( stream->past_FramesPerUserBuffer, stream->past_SampleRate );
    stream->past_NumUserBuffers = ( minNumBuffers > stream->past_NumUserBuffers ) ? minNumBuffers : stream->past_NumUserBuffers;
    DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", stream->past_NumUserBuffers ));
    minTotalFrames = stream->past_NumUserBuffers * stream->past_FramesPerUserBuffer;
    /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
    if( (int) stream->past_FramesPerUserBuffer < minframesPerHostBuffer )
    {
        userBuffersPerHostBuffer =
            (minframesPerHostBuffer + stream->past_FramesPerUserBuffer - 1) /
            stream->past_FramesPerUserBuffer;
    }
    else
    {
        userBuffersPerHostBuffer = 1;
    }
    framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
    /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
    numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
    /* Make sure we have anough WAVE buffers. */
    if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
    {
        numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
    }
    else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) &&
             ((int) stream->past_FramesPerUserBuffer < (maxframesPerHostBuffer/2) ) )
    {
        /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
        while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
        {
            userBuffersPerHostBuffer += 1;
            framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
            numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
            /* If we have gone too far, back up one. */
            if( (framesPerHostBuffer > maxframesPerHostBuffer) ||
                    (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) )
            {
                userBuffersPerHostBuffer -= 1;
                framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
                numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
                break;
            }
        }
    }

    wmmeStreamData->userBuffersPerHostBuffer = userBuffersPerHostBuffer;
    wmmeStreamData->framesPerHostBuffer = framesPerHostBuffer;
    wmmeStreamData->numHostBuffers = numHostBuffers;
    DBUG(("PaHost_CalcNumHostBuffers: userBuffersPerHostBuffer = %d\n", wmmeStreamData->userBuffersPerHostBuffer ));
    DBUG(("PaHost_CalcNumHostBuffers: numHostBuffers = %d\n", wmmeStreamData->numHostBuffers ));
    DBUG(("PaHost_CalcNumHostBuffers: framesPerHostBuffer = %d\n", wmmeStreamData->framesPerHostBuffer ));
    DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", stream->past_NumUserBuffers ));
}
/*******************************************************************/
PaError PaHost_OpenStream( internalPortAudioStream *stream )
{
    PaError             result = paNoError;
    PaWMMEStreamData *wmmeStreamData;

    result = PaHost_AllocateWMMEStreamData( stream );
    if( result != paNoError ) return result;

    wmmeStreamData = PaHost_GetWMMEStreamData( stream );

    /* Figure out how user buffers fit into WAVE buffers. */
    PaHost_CalcNumHostBuffers( stream );
    {
        int msecLatency = (int) ((PaHost_GetTotalBufferFrames(stream) * 1000) / stream->past_SampleRate);
        DBUG(("PortAudio on WMME - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(stream), msecLatency ));
    }
    InitializeCriticalSection( &wmmeStreamData->streamLock );
    wmmeStreamData->streamLockInited = 1;

#if (PA_USE_TIMER_CALLBACK == 0)
    wmmeStreamData->bufferEventInited = 0;
    wmmeStreamData->bufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
    if( wmmeStreamData->bufferEvent == NULL )
    {
        result = paHostError;
        sPaHostError = GetLastError();
        goto error;
    }
    wmmeStreamData->bufferEventInited = 1;
#endif /* (PA_USE_TIMER_CALLBACK == 0) */
    /* ------------------ OUTPUT */
    wmmeStreamData->bytesPerUserOutputBuffer = stream->past_FramesPerUserBuffer * stream->past_NumOutputChannels * sizeof(short);
    wmmeStreamData->bytesPerHostOutputBuffer = wmmeStreamData->userBuffersPerHostBuffer * wmmeStreamData->bytesPerUserOutputBuffer;
    if( (stream->past_OutputDeviceID != paNoDevice) && (stream->past_NumOutputChannels > 0) )
    {
        result = PaHost_OpenOutputStream( stream );
        if( result < 0 ) goto error;
    }
    /* ------------------ INPUT */
    wmmeStreamData->bytesPerUserInputBuffer = stream->past_FramesPerUserBuffer * stream->past_NumInputChannels * sizeof(short);
    wmmeStreamData->bytesPerHostInputBuffer = wmmeStreamData->userBuffersPerHostBuffer * wmmeStreamData->bytesPerUserInputBuffer;
    if( (stream->past_InputDeviceID != paNoDevice) && (stream->past_NumInputChannels > 0) )
    {
        result = PaHost_OpenInputStream( stream );
        if( result < 0 ) goto error;
    }

    Pa_InitializeCpuUsageScalar( stream );

    return result;

error:
    PaHost_CloseStream( stream );
    return result;
}
/*************************************************************************/
PaError PaHost_StartOutput( internalPortAudioStream *stream )
{
    PaError          result = paNoError;
    MMRESULT         mmresult;
    int              i;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );

    if( wmmeStreamData == NULL ) return paInternalError;

    if( stream->past_OutputDeviceID != paNoDevice )
    {
        if( (mmresult = waveOutPause( wmmeStreamData->hWaveOut )) != MMSYSERR_NOERROR )
        {
            result = paHostError;
            sPaHostError = mmresult;
            goto error;
        }
        for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
        {
            ZeroMemory( wmmeStreamData->outputBuffers[i].lpData, wmmeStreamData->outputBuffers[i].dwBufferLength );
            mmresult = waveOutWrite( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) );
            if( mmresult != MMSYSERR_NOERROR )
            {
                result = paHostError;
                sPaHostError = mmresult;
                goto error;
            }
            stream->past_FrameCount += wmmeStreamData->framesPerHostBuffer;
        }
        wmmeStreamData->currentOutputBuffer = 0;
        if( (mmresult = waveOutRestart( wmmeStreamData->hWaveOut )) != MMSYSERR_NOERROR )
        {
            result = paHostError;
            sPaHostError = mmresult;
            goto error;
        }
    }

error:
    DBUG(("PaHost_StartOutput: wave returned mmresult = 0x%X.\n", mmresult));
    return result;
}
/*************************************************************************/
PaError PaHost_StartInput( internalPortAudioStream *internalStream )
{
    PaError          result = paNoError;
    MMRESULT         mmresult;
    int              i;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );

    if( wmmeStreamData == NULL ) return paInternalError;

    if( internalStream->past_InputDeviceID != paNoDevice )
    {
        for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
        {
            mmresult = waveInAddBuffer( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) );
            if( mmresult != MMSYSERR_NOERROR )
            {
                result = paHostError;
                sPaHostError = mmresult;
                goto error;
            }
        }
        wmmeStreamData->currentInputBuffer = 0;
        mmresult = waveInStart( wmmeStreamData->hWaveIn );
        DBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
        if( mmresult != MMSYSERR_NOERROR )
        {
            result = paHostError;
            sPaHostError = mmresult;
            goto error;
        }
    }

error:
    return result;
}
/*************************************************************************/
PaError PaHost_StartEngine( internalPortAudioStream *stream )
{
    PaError             result = paNoError;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
#if PA_USE_TIMER_CALLBACK
    int                 resolution;
    int                 bufsPerTimerCallback;
    int                 msecPerBuffer;
#endif /* PA_USE_TIMER_CALLBACK */

    if( wmmeStreamData == NULL ) return paInternalError;

    stream->past_StopSoon = 0;
    stream->past_StopNow = 0;
    stream->past_IsActive = 1;
    wmmeStreamData->framesPlayed = 0.0;
    wmmeStreamData->lastPosition = 0;
#if PA_TRACE_START_STOP
    AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
#endif
#if PA_USE_TIMER_CALLBACK
    /* Create timer that will wake us up so we can fill the DSound buffer. */
    bufsPerTimerCallback = wmmeStreamData->numHostBuffers/4;
    if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
    if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
    msecPerBuffer = (1000 * bufsPerTimerCallback *
                     wmmeStreamData->userBuffersPerHostBuffer *
                     internalStream->past_FramesPerUserBuffer ) / (int) internalStream->past_SampleRate;
    if( msecPerBuffer < 10 ) msecPerBuffer = 10;
    else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
    resolution = msecPerBuffer/4;
    wmmeStreamData->timerID = timeSetEvent( msecPerBuffer, resolution,
                                         (LPTIMECALLBACK) Pa_TimerCallback,
                                         (DWORD) stream, TIME_PERIODIC );
    if( wmmeStreamData->timerID == 0 )
    {
        result = paHostError;
        sPaHostError = GetLastError();;
        goto error;
    }
#else /* PA_USE_TIMER_CALLBACK */
    ResetEvent( wmmeStreamData->abortEvent );
    /* Create thread that waits for audio buffers to be ready for processing. */
    wmmeStreamData->engineThread = CreateThread( 0, 0, WinMMPa_OutputThreadProc, stream, 0, &wmmeStreamData->engineThreadID );
    if( wmmeStreamData->engineThread == NULL )
    {
        result = paHostError;
        sPaHostError = GetLastError();;
        goto error;
    }
#if PA_TRACE_START_STOP
    AddTraceMessage( "PaHost_StartEngine: thread ", (int) wmmeStreamData->engineThread );
#endif
    /* I used to pass the thread which was failing. I now pass GetCurrentProcess().
     * This fix could improve latency for some applications. It could also result in CPU
     * starvation if the callback did too much processing.
     * I also added result checks, so we might see more failures at initialization.
     * Thanks to Alberto di Bene for spotting this.
     */
    if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */
    {
        result = paHostError;
        sPaHostError = GetLastError();;
        goto error;
    }
    if( !SetThreadPriority( wmmeStreamData->engineThread, THREAD_PRIORITY_HIGHEST ) )
    {
        result = paHostError;
        sPaHostError = GetLastError();;
        goto error;
    }
#endif

error:
    return result;
}
/*************************************************************************/
PaError PaHost_StopEngine( internalPortAudioStream *internalStream, int abort )
{
    int timeOut;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );

    if( wmmeStreamData == NULL ) return paNoError;

    /* Tell background thread to stop generating more data and to let current data play out. */
    internalStream->past_StopSoon = 1;
    /* If aborting, tell background thread to stop NOW! */
    if( abort ) internalStream->past_StopNow = 1;

    /* Calculate timeOut longer than longest time it could take to play all buffers. */
    timeOut = (DWORD) (1500.0 * PaHost_GetTotalBufferFrames( internalStream ) / internalStream->past_SampleRate);
    if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;

#if PA_USE_TIMER_CALLBACK
    if( (internalStream->past_OutputDeviceID != paNoDevice) &&
            internalStream->past_IsActive &&
            (wmmeStreamData->timerID != 0) )
    {
        /* Wait for IsActive to drop. */
        while( (internalStream->past_IsActive) && (timeOut > 0) )
        {
            Sleep(10);
            timeOut -= 10;
        }
        timeKillEvent( wmmeStreamData->timerID );  /* Stop callback timer. */
        wmmeStreamData->timerID = 0;
    }
#else /* PA_USE_TIMER_CALLBACK */
#if PA_TRACE_START_STOP
    AddTraceMessage( "PaHost_StopEngine: thread ", (int) wmmeStreamData->engineThread );
#endif
    if( (internalStream->past_OutputDeviceID != paNoDevice) &&
            (internalStream->past_IsActive) &&
            (wmmeStreamData->engineThread != NULL) )
    {
        DWORD got;
        /* Tell background thread to stop generating more data and to let current data play out. */
        DBUG(("PaHost_StopEngine: waiting for background thread.\n"));
        got = WaitForSingleObject( wmmeStreamData->engineThread, timeOut );
        if( got == WAIT_TIMEOUT )
        {
            ERR_RPT(("PaHost_StopEngine: timed out while waiting for background thread to finish.\n"));
            return paTimedOut;
        }
        CloseHandle( wmmeStreamData->engineThread );
        wmmeStreamData->engineThread = NULL;
    }
#endif /* PA_USE_TIMER_CALLBACK */

    internalStream->past_IsActive = 0;
    return paNoError;
}
/*************************************************************************/
PaError PaHost_StopInput( internalPortAudioStream *stream, int abort )
{
    MMRESULT mmresult;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );

    if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return paNoError? */
    (void) abort; /* unused parameter */

    if( wmmeStreamData->hWaveIn != NULL )
    {
        mmresult = waveInReset( wmmeStreamData->hWaveIn );
        if( mmresult != MMSYSERR_NOERROR )
        {
            sPaHostError = mmresult;
            return paHostError;
        }
    }
    return paNoError;
}
/*************************************************************************/
PaError PaHost_StopOutput( internalPortAudioStream *internalStream, int abort )
{
    MMRESULT mmresult;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );

    if( wmmeStreamData == NULL ) return paNoError;    /* FIXME: why return paNoError? */
    (void) abort;   /* unused parameter */

#if PA_TRACE_START_STOP
    AddTraceMessage( "PaHost_StopOutput: hWaveOut ", (int) wmmeStreamData->hWaveOut );
#endif
    if( wmmeStreamData->hWaveOut != NULL )
    {
        mmresult = waveOutReset( wmmeStreamData->hWaveOut );
        if( mmresult != MMSYSERR_NOERROR )
        {
            sPaHostError = mmresult;
            return paHostError;
        }
    }
    return paNoError;
}
/*******************************************************************/
PaError PaHost_CloseStream( internalPortAudioStream *stream )
{
    int i;
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );

    if( stream == NULL ) return paBadStreamPtr;
    if( wmmeStreamData == NULL ) return paNoError;   /* FIXME: why return no error? */

#if PA_TRACE_START_STOP
    AddTraceMessage( "PaHost_CloseStream: hWaveOut ", (int) wmmeStreamData->hWaveOut );
#endif
    /* Free data and device for output. */
    if( wmmeStreamData->hWaveOut )
    {
        if( wmmeStreamData->outputBuffers )
        {
            for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
            {
                waveOutUnprepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) );
                PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers[i].lpData ); /* MEM */
            }
            PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers ); /* MEM */
        }
        waveOutClose( wmmeStreamData->hWaveOut );
    }
    /* Free data and device for input. */
    if( wmmeStreamData->hWaveIn )
    {
        if( wmmeStreamData->inputBuffers )
        {
            for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
            {
                waveInUnprepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) );
                PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers[i].lpData ); /* MEM */
            }
            PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers ); /* MEM */
        }
        waveInClose( wmmeStreamData->hWaveIn );
    }
#if (PA_USE_TIMER_CALLBACK == 0)
    if( wmmeStreamData->abortEventInited ) CloseHandle( wmmeStreamData->abortEvent );
    if( wmmeStreamData->bufferEventInited ) CloseHandle( wmmeStreamData->bufferEvent );
#endif
    if( wmmeStreamData->streamLockInited )
        DeleteCriticalSection( &wmmeStreamData->streamLock );

    PaHost_FreeWMMEStreamData( stream );

    return paNoError;
}
/*************************************************************************
 * Determine minimum number of buffers required for this host based
 * on minimum latency. Latency can be optionally set by user by setting
 * an environment variable. For example, to set latency to 200 msec, put:
 *
 *    set PA_MIN_LATENCY_MSEC=200
 *
 * in the AUTOEXEC.BAT file and reboot.
 * If the environment variable is not set, then the latency will be determined
 * based on the OS. Windows NT has higher latency than Win95.
 */
#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
{
    char      envbuf[PA_ENV_BUF_SIZE];
    DWORD     hresult;
    int       minLatencyMsec = 0;
    double    msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
    int       minBuffers;

    /* Let user determine minimal latency by setting environment variable. */
    hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
    {
        minLatencyMsec = atoi( envbuf );   /* REVIEW: will we crash if the environment variable contains some nasty value? */
    }
    else
    {
        /* Set minimal latency based on whether NT or other OS.
         * NT has higher latency.
         */
        OSVERSIONINFO osvi;
                osvi.dwOSVersionInfoSize = sizeof( osvi );
                GetVersionEx( &osvi );
        DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
        DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
        DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
        /* Check for NT */
                if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
                {
                        minLatencyMsec = PA_WIN_NT_LATENCY;
                }
                else if(osvi.dwMajorVersion >= 5)
                {
                        minLatencyMsec = PA_WIN_WDM_LATENCY;
                }
                else
                {
                        minLatencyMsec = PA_WIN_9X_LATENCY;
                }
#if PA_USE_HIGH_LATENCY
        PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
#endif

    }
    DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
    minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
    if( minBuffers < 2 ) minBuffers = 2;
    return minBuffers;
}
/*************************************************************************
 * Cleanup device info.
 */
PaError PaHost_Term( void )
{
    int i;

    if( sNumDevices > 0 )
    {
        if( sDevicePtrs != NULL )
        {
            for( i=0; i<sNumDevices; i++ )
            {
                if( sDevicePtrs[i] != NULL )
                {
                    PaHost_FreeTrackedMemory( (char*)sDevicePtrs[i]->name ); /* MEM */
                    PaHost_FreeTrackedMemory( (void*)sDevicePtrs[i]->sampleRates ); /* MEM */
                    PaHost_FreeTrackedMemory( sDevicePtrs[i] ); /* MEM */
                }
            }
            PaHost_FreeTrackedMemory( sDevicePtrs ); /* MEM */
            sDevicePtrs = NULL;
        }
        sNumDevices = 0;
    }

#if PA_TRACK_MEMORY
    PRINT(("PaHost_Term: sNumAllocations = %d\n", sNumAllocations ));
#endif

    return paNoError;
}
/*************************************************************************/
void Pa_Sleep( long msec )
{
    Sleep( msec );
}
/*************************************************************************
FIXME: the following memory allocation routines should not be declared here
 * Allocate memory that can be accessed in real-time.
 * This may need to be held in physical memory so that it is not
 * paged to virtual memory.
 * This call MUST be balanced with a call to PaHost_FreeFastMemory().
 * Memory will be set to zero.
 */
void *PaHost_AllocateFastMemory( long numBytes )
{
    return PaHost_AllocateTrackedMemory( numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
}
/*************************************************************************
 * Free memory that could be accessed in real-time.
 * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
 */
void PaHost_FreeFastMemory( void *addr, long numBytes )
{
    (void) numBytes; /* unused parameter */
    
    PaHost_FreeTrackedMemory( addr ); /* MEM */
}

/*************************************************************************
 * Track memory allocations to avoid leaks.
 */
static void *PaHost_AllocateTrackedMemory( long numBytes )
{
    void *result = GlobalAlloc( GPTR, numBytes ); /* MEM */

#if PA_TRACK_MEMORY
    if( result != NULL ) sNumAllocations += 1;
#endif
    return result;
}

tatic void PaHost_FreeTrackedMemory( void *addr )
{
    if( addr != NULL )
    {
        GlobalFree( addr ); /* MEM */
#if PA_TRACK_MEMORY
        sNumAllocations -= 1;
#endif
    }
}

/***********************************************************************/
PaError PaHost_StreamActive( internalPortAudioStream *internalStream )
{
    if( internalStream == NULL ) return paBadStreamPtr;

    return (PaError) internalStream->past_IsActive;
}
/*************************************************************************
 * This must be called periodically because mmtime.u.sample
 * is a DWORD and can wrap and lose sync after a few hours.
 */
static PaError PaHost_UpdateStreamTime( PaWMMEStreamData *wmmeStreamData )
{
    MMRESULT  mmresult;
    MMTIME    mmtime;
    mmtime.wType = TIME_SAMPLES;

    if( wmmeStreamData->hWaveOut != NULL )
    {
        mmresult = waveOutGetPosition( wmmeStreamData->hWaveOut, &mmtime, sizeof(mmtime) );
    }
    else
    {
        mmresult = waveInGetPosition( wmmeStreamData->hWaveIn, &mmtime, sizeof(mmtime) );
    }
    
    if( mmresult != MMSYSERR_NOERROR )
    {
        sPaHostError = mmresult;
        return paHostError;
    }
    
    /* This data has two variables and is shared by foreground and background.
     * So we need to make it thread safe. */
    EnterCriticalSection( &wmmeStreamData->streamLock );
    wmmeStreamData->framesPlayed += ((long)mmtime.u.sample) - wmmeStreamData->lastPosition;
    wmmeStreamData->lastPosition = (long)mmtime.u.sample;
    LeaveCriticalSection( &wmmeStreamData->streamLock );
    
    return paNoError;
}
/*************************************************************************/
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
    internalPortAudioStream *internalStream = PaHost_GetStreamRepresentation( stream );
    PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );

    if( internalStream == NULL ) return paBadStreamPtr;
    if( wmmeStreamData == NULL ) return paInternalError;

    PaHost_UpdateStreamTime( wmmeStreamData );
    return wmmeStreamData->framesPlayed;
}
/*************************************************************************/

<p><p><p><p><p>1.1                  theora/win32/experimental/splayer/portaudio/pablio/README.txt

Index: README.txt
===================================================================
README for PABLIO
Portable Audio Blocking I/O Library
Author: Phil Burk

PABLIO is a simplified interface to PortAudio that provide
read/write style blocking I/O.

Please see the .DOC file for documentation.

/*
 * More information on PortAudio at: http://www.portaudio.com
 * Copyright (c) 1999-2000 Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

<p><p><p><p>1.1                  theora/win32/experimental/splayer/portaudio/pablio/pablio.c

Index: pablio.c
===================================================================
/*
 * $Id: pablio.c,v 1.1 2003/05/23 14:12:13 mauricio Exp $
 * pablio.c
 * Portable Audio Blocking Input/Output utility.
 *
 * Author: Phil Burk, http://www.softsynth.com
 *
 * This program uses the PortAudio Portable Audio Library.
 * For more information see: http://www.audiomulch.com/portaudio/
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "portaudio.h"
#include "ringbuffer.h"
#include "pablio.h"
#include <string.h>

/************************************************************************/
/******** Constants *****************************************************/
/************************************************************************/

#define FRAMES_PER_BUFFER    (256)

/************************************************************************/
/******** Prototypes ****************************************************/
/************************************************************************/

tatic int blockingIOCallback( void *inputBuffer, void *outputBuffer,
                               unsigned long framesPerBuffer,
                               PaTimestamp outTime, void *userData );
static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
static PaError PABLIO_TermFIFO( RingBuffer *rbuf );

/************************************************************************/
/******** Functions *****************************************************/
/************************************************************************/

/* Called from PortAudio.
 * Read and write data only if there is room in FIFOs.
 */
static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
                               unsigned long framesPerBuffer,
                               PaTimestamp outTime, void *userData )
{
    PABLIO_Stream *data = (PABLIO_Stream*)userData;
    long numBytes = data->bytesPerFrame * framesPerBuffer;
    (void) outTime;

    /* This may get called with NULL inputBuffer during initial setup. */
    if( inputBuffer != NULL )
    {
        RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
    }
    if( outputBuffer != NULL )
    {
        int i;
        int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
        /* Zero out remainder of buffer if we run out of data. */
        for( i=numRead; i<numBytes; i++ )
        {
            ((char *)outputBuffer)[i] = 0;
        }
    }

    return 0;
}

/* Allocate buffer. */
static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
{
    long numBytes = numFrames * bytesPerFrame;
    char *buffer = (char *) malloc( numBytes );
    if( buffer == NULL ) return paInsufficientMemory;
    memset( buffer, 0, numBytes );
    return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
}

/* Free buffer. */
static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
{
    if( rbuf->buffer ) free( rbuf->buffer );
    rbuf->buffer = NULL;
    return paNoError;
}

/************************************************************
 * Write data to ring buffer.
 * Will not return until all the data has been written.
 */
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
{
    long bytesWritten;
    char *p = (char *) data;
    long numBytes = aStream->bytesPerFrame * numFrames;
    while( numBytes > 0)
    {
        bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
        numBytes -= bytesWritten;
        p += bytesWritten;
        if( numBytes > 0) Pa_Sleep(10);
    }
    return numFrames;
}

/************************************************************
 * Read data from ring buffer.
 * Will not return until all the data has been read.
 */
long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
{
    long bytesRead;
    char *p = (char *) data;
    long numBytes = aStream->bytesPerFrame * numFrames;
    while( numBytes > 0)
    {
        bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
        numBytes -= bytesRead;
        p += bytesRead;
        if( numBytes > 0) Pa_Sleep(10);
    }
    return numFrames;
}

/************************************************************
 * Return the number of frames that could be written to the stream without
 * having to wait.
 */
long GetAudioStreamWriteable( PABLIO_Stream *aStream )
{
    int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
    return bytesEmpty / aStream->bytesPerFrame;
}

/************************************************************
 * Return the number of frames that are available to be read from the
 * stream without having to wait.
 */
long GetAudioStreamReadable( PABLIO_Stream *aStream )
{
    int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
    return bytesFull / aStream->bytesPerFrame;
}

/************************************************************/
static unsigned long RoundUpToNextPowerOf2( unsigned long n )
{
    long numBits = 0;
    if( ((n-1) & n) == 0) return n; /* Already Power of two. */
    while( n > 0 )
    {
        n= n>>1;
        numBits++;
    }
    return (1<<numBits);
}

/************************************************************
 * Opens a PortAudio stream with default characteristics.
 * Allocates PABLIO_Stream structure.
 *
 * flags parameter can be an ORed combination of:
 *    PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
 *    and either PABLIO_MONO or PABLIO_STEREO
 */
PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
                         PaSampleFormat format, long flags )
{
    long   bytesPerSample;
    long   doRead = 0;
    long   doWrite = 0;
    PaError err;
    PABLIO_Stream *aStream;
    long   minNumBuffers;
    long   numFrames;

    /* Allocate PABLIO_Stream structure for caller. */
    aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
    if( aStream == NULL ) return paInsufficientMemory;
    memset( aStream, 0, sizeof(PABLIO_Stream) );

    /* Determine size of a sample. */
    bytesPerSample = Pa_GetSampleSize( format );
    if( bytesPerSample < 0 )
    {
        err = (PaError) bytesPerSample;
        goto error;
    }
    aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;
    aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;

    /* Initialize PortAudio  */
    err = Pa_Initialize();
    if( err != paNoError ) goto error;

    /* Warning: numFrames must be larger than amount of data processed per interrupt
     *    inside PA to prevent glitches. Just to be safe, adjust size upwards.
     */
    minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
    numFrames = minNumBuffers * FRAMES_PER_BUFFER;
    numFrames = RoundUpToNextPowerOf2( numFrames );

    /* Initialize Ring Buffers */
    doRead = ((flags & PABLIO_READ) != 0);
    doWrite = ((flags & PABLIO_WRITE) != 0);
    if(doRead)
    {
        err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
        if( err != paNoError ) goto error;
    }
    if(doWrite)
    {
        long numBytes;
        err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
        if( err != paNoError ) goto error;
        /* Make Write FIFO appear full initially. */
        numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
        RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
    }

    /* Open a PortAudio stream that we will use to communicate with the underlying
     * audio drivers. */
    err = Pa_OpenStream(
              &aStream->stream,
              (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice),
              (doRead ? aStream->samplesPerFrame : 0 ),
              format,
              NULL,
              (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
              (doWrite ? aStream->samplesPerFrame : 0 ),
              format,
              NULL,
              sampleRate,
              FRAMES_PER_BUFFER,
              minNumBuffers,
              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
              blockingIOCallback,
              aStream );
    if( err != paNoError ) goto error;

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

    *rwblPtr = aStream;
    return paNoError;

error:
    CloseAudioStream( aStream );
    *rwblPtr = NULL;
    return err;
}

/************************************************************/
PaError CloseAudioStream( PABLIO_Stream *aStream )
{
    PaError err;
    int bytesEmpty;
    int byteSize = aStream->outFIFO.bufferSize;

    /* If we are writing data, make sure we play everything written. */
    if( byteSize > 0 )
    {
        bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
        while( bytesEmpty < byteSize )
        {
            Pa_Sleep( 10 );
            bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
        }
    }

    err = Pa_StopStream( aStream->stream );
    if( err != paNoError ) goto error;
    err = Pa_CloseStream( aStream->stream );
    if( err != paNoError ) goto error;
    Pa_Terminate();

error:
    PABLIO_TermFIFO( &aStream->inFIFO );
    PABLIO_TermFIFO( &aStream->outFIFO );
    free( aStream );
    return err;
}

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pablio/pablio.def

Index: pablio.def
===================================================================
LIBRARY      PABLIO
DESCRIPTION  'PABLIO   Portable Audio Blocking I/O'

EXPORTS
    ; Explicit exports can go here
        Pa_Initialize                   @1
        Pa_Terminate                    @2
        Pa_GetHostError                 @3
        Pa_GetErrorText                 @4
        Pa_CountDevices                 @5
        Pa_GetDefaultInputDeviceID      @6
        Pa_GetDefaultOutputDeviceID     @7
        Pa_GetDeviceInfo                @8
        Pa_OpenStream                   @9
        Pa_OpenDefaultStream            @10
        Pa_CloseStream                  @11
        Pa_StartStream                  @12
        Pa_StopStream                   @13
        Pa_StreamActive                 @14
        Pa_StreamTime                   @15
        Pa_GetCPULoad                   @16
        Pa_GetMinNumBuffers             @17
        Pa_Sleep                        @18

        OpenAudioStream                 @19
        CloseAudioStream                @20
        WriteAudioStream                @21
        ReadAudioStream                 @22

        Pa_GetSampleSize                @23

   ;123456789012345678901234567890123456
   ;000000000111111111122222222223333333

<p><p><p><p>1.1                  theora/win32/experimental/splayer/portaudio/pablio/pablio.h

Index: pablio.h
===================================================================
#ifndef _PABLIO_H
#define _PABLIO_H

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

/*
 * $Id: pablio.h,v 1.1 2003/05/23 14:12:13 mauricio Exp $
 * PABLIO.h
 * Portable Audio Blocking read/write utility.
 *
 * Author: Phil Burk, http://www.softsynth.com/portaudio/
 *
 * Include file for PABLIO, the Portable Audio Blocking I/O Library.
 * PABLIO is built on top of PortAudio, the Portable Audio Library.
 * For more information see: http://www.audiomulch.com/portaudio/
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "portaudio.h"
#include "ringbuffer.h"
#include <string.h>

typedef struct
{
    RingBuffer   inFIFO;
    RingBuffer   outFIFO;
    PortAudioStream *stream;
    int          bytesPerFrame;
    int          samplesPerFrame;
}
PABLIO_Stream;

/* Values for flags for OpenAudioStream(). */
#define PABLIO_READ     (1<<0)
#define PABLIO_WRITE    (1<<1)
#define PABLIO_READ_WRITE    (PABLIO_READ|PABLIO_WRITE)
#define PABLIO_MONO     (1<<2)
#define PABLIO_STEREO   (1<<3)

/************************************************************
 * Write data to ring buffer.
 * Will not return until all the data has been written.
 */
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );

/************************************************************
 * Read data from ring buffer.
 * Will not return until all the data has been read.
 */
long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );

/************************************************************
 * Return the number of frames that could be written to the stream without
 * having to wait.
 */
long GetAudioStreamWriteable( PABLIO_Stream *aStream );

/************************************************************
 * Return the number of frames that are available to be read from the
 * stream without having to wait.
 */
long GetAudioStreamReadable( PABLIO_Stream *aStream );

/************************************************************
 * Opens a PortAudio stream with default characteristics.
 * Allocates PABLIO_Stream structure.
 *
 * flags parameter can be an ORed combination of:
 *    PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
 *    and either PABLIO_MONO or PABLIO_STEREO
 */
PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate,
                         PaSampleFormat format, long flags );

PaError CloseAudioStream( PABLIO_Stream *aStream );

#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _PABLIO_H */

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pablio/ringbuffer.c

Index: ringbuffer.c
===================================================================
/*
 * $Id: ringbuffer.c,v 1.1 2003/05/23 14:12:13 mauricio Exp $
 * ringbuffer.c
 * Ring Buffer utility..
 *
 * Author: Phil Burk, http://www.softsynth.com
 *
 * This program uses the PortAudio Portable Audio Library.
 * For more information see: http://www.audiomulch.com/portaudio/
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ringbuffer.h"
#include <string.h>

/***************************************************************************
 * Initialize FIFO.
 * numBytes must be power of 2, returns -1 if not.
 */
long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
{
    if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
    rbuf->bufferSize = numBytes;
    rbuf->buffer = (char *)dataPtr;
    RingBuffer_Flush( rbuf );
    rbuf->bigMask = (numBytes*2)-1;
    rbuf->smallMask = (numBytes)-1;
    return 0;
}
/***************************************************************************
** Return number of bytes available for reading. */
long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
{
    return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
}
/***************************************************************************
** Return number of bytes available for writing. */
long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
{
    return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
}

/***************************************************************************
** Clear buffer. Should only be called when buffer is NOT being read. */
void RingBuffer_Flush( RingBuffer *rbuf )
{
    rbuf->writeIndex = rbuf->readIndex = 0;
}

/***************************************************************************
** Get address of region(s) to which we can write data.
** If the region is contiguous, size2 will be zero.
** If non-contiguous, size2 will be the size of second region.
** Returns room available to be written or numBytes, whichever is smaller.
*/
long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
                                 void **dataPtr1, long *sizePtr1,
                                 void **dataPtr2, long *sizePtr2 )
{
    long   index;
    long   available = RingBuffer_GetWriteAvailable( rbuf );
    if( numBytes > available ) numBytes = available;
    /* Check to see if write is not contiguous. */
    index = rbuf->writeIndex & rbuf->smallMask;
    if( (index + numBytes) > rbuf->bufferSize )
    {
        /* Write data in two blocks that wrap the buffer. */
        long   firstHalf = rbuf->bufferSize - index;
        *dataPtr1 = &rbuf->buffer[index];
        *sizePtr1 = firstHalf;
        *dataPtr2 = &rbuf->buffer[0];
        *sizePtr2 = numBytes - firstHalf;
    }
    else
    {
        *dataPtr1 = &rbuf->buffer[index];
        *sizePtr1 = numBytes;
        *dataPtr2 = NULL;
        *sizePtr2 = 0;
    }
    return numBytes;
}

<p>/***************************************************************************
*/
long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
{
    return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
}

/***************************************************************************
** Get address of region(s) from which we can read data.
** If the region is contiguous, size2 will be zero.
** If non-contiguous, size2 will be the size of second region.
** Returns room available to be written or numBytes, whichever is smaller.
*/
long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
                                void **dataPtr1, long *sizePtr1,
                                void **dataPtr2, long *sizePtr2 )
{
    long   index;
    long   available = RingBuffer_GetReadAvailable( rbuf );
    if( numBytes > available ) numBytes = available;
    /* Check to see if read is not contiguous. */
    index = rbuf->readIndex & rbuf->smallMask;
    if( (index + numBytes) > rbuf->bufferSize )
    {
        /* Write data in two blocks that wrap the buffer. */
        long firstHalf = rbuf->bufferSize - index;
        *dataPtr1 = &rbuf->buffer[index];
        *sizePtr1 = firstHalf;
        *dataPtr2 = &rbuf->buffer[0];
        *sizePtr2 = numBytes - firstHalf;
    }
    else
    {
        *dataPtr1 = &rbuf->buffer[index];
        *sizePtr1 = numBytes;
        *dataPtr2 = NULL;
        *sizePtr2 = 0;
    }
    return numBytes;
}
/***************************************************************************
*/
long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
{
    return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
}

/***************************************************************************
** Return bytes written. */
long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
{
    long size1, size2, numWritten;
    void *data1, *data2;
    numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
    if( size2 > 0 )
    {

        memcpy( data1, data, size1 );
        data = ((char *)data) + size1;
        memcpy( data2, data, size2 );
    }
    else
    {
        memcpy( data1, data, size1 );
    }
    RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
    return numWritten;
}

/***************************************************************************
** Return bytes read. */
long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
{
    long size1, size2, numRead;
    void *data1, *data2;
    numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
    if( size2 > 0 )
    {
        memcpy( data, data1, size1 );
        data = ((char *)data) + size1;
        memcpy( data, data2, size2 );
    }
    else
    {
        memcpy( data, data1, size1 );
    }
    RingBuffer_AdvanceReadIndex( rbuf, numRead );
    return numRead;
}

<p><p>1.1                  theora/win32/experimental/splayer/portaudio/pablio/ringbuffer.h

Index: ringbuffer.h
===================================================================
#ifndef _RINGBUFFER_H
#define _RINGBUFFER_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

/*
 * $Id: ringbuffer.h,v 1.1 2003/05/23 14:12:13 mauricio Exp $
 * ringbuffer.h
 * Ring Buffer utility..
 *
 * Author: Phil Burk, http://www.softsynth.com
 *
 * This program is distributed with the PortAudio Portable Audio Library.
 * For more information see: http://www.audiomulch.com/portaudio/
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ringbuffer.h"
#include <string.h>

typedef struct
{
    long   bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
    long   writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
    long   readIndex;  /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
    long   bigMask;    /* Used for wrapping indices with extra bit to distinguish full/empty. */
    long   smallMask;  /* Used for fitting indices to buffer. */
    char *buffer;
}
RingBuffer;
/*
 * Initialize Ring Buffer.
 * numBytes must be power of 2, returns -1 if not.
 */
long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );

/* Clear buffer. Should only be called when buffer is NOT being read. */
void RingBuffer_Flush( RingBuffer *rbuf );

/* Return number of bytes available for writing. */
long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
/* Return number of bytes available for read. */
long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
/* Return bytes written. */
long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
/* Return bytes read. */
long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );

/* Get address of region(s) to which we can write data.
** If the region is contiguous, size2 will be zero.
** If non-contiguous, size2 will be the size of second region.
** Returns room available to be written or numBytes, whichever is smaller.
*/
long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
                                 void **dataPtr1, long *sizePtr1,
                                 void **dataPtr2, long *sizePtr2 );
long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );

/* Get address of region(s) from which we can read data.
** If the region is contiguous, size2 will be zero.
** If non-contiguous, size2 will be the size of second region.
** Returns room available to be written or numBytes, whichever is smaller.
*/
long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
                                void **dataPtr1, long *sizePtr1,
                                void **dataPtr2, long *sizePtr2 );

long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );

#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _RINGBUFFER_H */

<p><p>--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list