⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pa_unix_oss.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
 * PortAudio Portable Real-Time Audio Library
 * Latest Version at: http://www.portaudio.com
 * OSS implementation by:
 *   Douglas Repetto
 *   Phil Burk
 *   Dominic Mazzoni
 *   Arve Knudsen
 *
 * Based on the Open Source API proposed by Ross Bencina
 * Copyright (c) 1999-2002 Ross Bencina, 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 <string.h>
#include <math.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <alloca.h>
#include <malloc.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <limits.h>
#include <semaphore.h>

#ifdef __linux__
# include <linux/soundcard.h>
# define DEVICE_NAME_BASE            "/dev/dsp"
#else
# include <machine/soundcard.h> /* JH20010905 */
# define DEVICE_NAME_BASE            "/dev/audio"
#endif

#include "portaudio.h"
#include "pa_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_unix_util.h"

static int sysErr_;
static pthread_t mainThread_;

/* Check return value of system call, and map it to PaError */
#define ENSURE_(expr, code) \
    do { \
        if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
        { \
            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
            if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
            { \
                PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
            } \
            \
            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
            result = (code); \
            goto error; \
        } \
    } while( 0 );

#ifndef AFMT_S16_NE
#define AFMT_S16_NE  Get_AFMT_S16_NE()
/*********************************************************************
 * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
 * PowerPC is Big Endian. X86 is Little Endian.
 */
static int Get_AFMT_S16_NE( void )
{
    long testData = 1; 
    char *ptr = (char *) &testData;
    int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
    return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
}
#endif

/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */

typedef struct
{
    PaUtilHostApiRepresentation inheritedHostApiRep;
    PaUtilStreamInterface callbackStreamInterface;
    PaUtilStreamInterface blockingStreamInterface;

    PaUtilAllocationGroup *allocations;

    PaHostApiIndex hostApiIndex;
}
PaOSSHostApiRepresentation;

/** Per-direction structure for PaOssStream.
 *
 * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
 * but with different number of channels we will have to adapt between the number of user and host
 * channels for at least one direction, since the configuration space is the same for both directions
 * of an OSS device.
 */
typedef struct
{
    int fd;
    const char *devName;
    int userChannelCount, hostChannelCount;
    int userInterleaved;
    void *buffer;
    PaSampleFormat userFormat, hostFormat;
    double latency;
    unsigned long hostFrames, numBufs;
    void **userBuffers; /* For non-interleaved blocking */
} PaOssStreamComponent;

/** Implementation specific representation of a PaStream.
 *
 */
typedef struct PaOssStream
{
    PaUtilStreamRepresentation streamRepresentation;
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
    PaUtilBufferProcessor bufferProcessor;

    PaUtilThreading threading;

    int sharedDevice;
    unsigned long framesPerHostBuffer;
    int triggered;  /* Have the devices been triggered yet (first start) */

    int isActive;
    int isStopped;

    int lastPosPtr;
    double lastStreamBytes;

    int framesProcessed;

    double sampleRate;

    int callbackMode;
    int callbackStop, callbackAbort;

    PaOssStreamComponent *capture, *playback;
    unsigned long pollTimeout;
    sem_t semaphore;
}
PaOssStream;

typedef enum {
    StreamMode_In,
    StreamMode_Out
} StreamMode;

/* prototypes for functions declared in this file */

static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
                                  const PaStreamParameters *inputParameters,
                                  const PaStreamParameters *outputParameters,
                                  double sampleRate );
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                           PaStream** s,
                           const PaStreamParameters *inputParameters,
                           const PaStreamParameters *outputParameters,
                           double sampleRate,
                           unsigned long framesPerBuffer,
                           PaStreamFlags streamFlags,
                           PaStreamCallback *streamCallback,
                           void *userData );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
static PaError AbortStream( PaStream *stream );
static PaError IsStreamStopped( PaStream *s );
static PaError IsStreamActive( PaStream *stream );
static PaTime GetStreamTime( PaStream *stream );
static double GetStreamCpuLoad( PaStream* stream );
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
static signed long GetStreamReadAvailable( PaStream* stream );
static signed long GetStreamWriteAvailable( PaStream* stream );
static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );


/** Initialize the OSS API implementation.
 *
 * This function will initialize host API datastructures and query host devices for information.
 *
 * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
 *
 * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
 * this happens with the usual "error" label.
 */
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
    PaError result = paNoError;
    PaOSSHostApiRepresentation *ossHostApi = NULL;

    PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
            paInsufficientMemory );
    PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
    ossHostApi->hostApiIndex = hostApiIndex;

    /* Initialize host API structure */
    *hostApi = &ossHostApi->inheritedHostApiRep;
    (*hostApi)->info.structVersion = 1;
    (*hostApi)->info.type = paOSS;
    (*hostApi)->info.name = "OSS";
    (*hostApi)->Terminate = Terminate;
    (*hostApi)->OpenStream = OpenStream;
    (*hostApi)->IsFormatSupported = IsFormatSupported;

    PA_ENSURE( BuildDeviceList( ossHostApi ) );

    PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
                                      GetStreamTime, GetStreamCpuLoad,
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
                                      PaUtil_DummyGetReadAvailable,
                                      PaUtil_DummyGetWriteAvailable );

    PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );

    mainThread_ = pthread_self();

    return result;

error:
    if( ossHostApi )
    {
        if( ossHostApi->allocations )
        {
            PaUtil_FreeAllAllocations( ossHostApi->allocations );
            PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
        }
                
        PaUtil_FreeMemory( ossHostApi );
    }
    return result;
}

PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
        int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
        PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations  )
{
    PaError result = paNoError;
    
    deviceInfo->structVersion = 2;
    if( allocations )
    {
        size_t len = strlen( name ) + 1;
        PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
        strncpy( (char *)deviceInfo->name, name, len );
    }
    else
        deviceInfo->name = name;

    deviceInfo->hostApi = hostApiIndex;
    deviceInfo->maxInputChannels = maxInputChannels;
    deviceInfo->maxOutputChannels = maxOutputChannels;
    deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
    deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
    deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
    deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
    deviceInfo->defaultSampleRate = defaultSampleRate;

error:
    return result;
}

static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
        double *defaultLowLatency, double *defaultHighLatency )
{
    PaError result = paNoError;
    int numChannels, maxNumChannels;
    int busy = 0;
    int devHandle = -1;
    int sr;
    *maxChannelCount = 0;  /* Default value in case this fails */

    if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK ))  < 0 )
    {
        if( errno == EBUSY || errno == EAGAIN )
        {
            PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
        }
        else
        {
            PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
        }

        return paDeviceUnavailable;
    }

    /* Negotiate for the maximum number of channels for this device. PLB20010927
     * Consider up to 16 as the upper number of channels.
     * Variable maxNumChannels should contain the actual upper limit after the call.
     * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
     */
    maxNumChannels = 0;
    for( numChannels = 1; numChannels <= 16; numChannels++ )
    {
        int temp = numChannels;
        if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
        {
            busy = EAGAIN == errno || EBUSY == errno;
            /* ioctl() failed so bail out if we already have stereo */
            if( maxNumChannels >= 2 )
                break;
        }
        else
        {
            /* ioctl() worked but bail out if it does not support numChannels.
             * We don't want to leave gaps in the numChannels supported.
             */
            if( (numChannels > 2) && (temp != numChannels) )
                break;
            if( temp > maxNumChannels )
                maxNumChannels = temp; /* Save maximum. */
        }
    }
    /* A: We're able to open a device for capture if it's busy playing back and vice versa,
     * but we can't configure anything */
    if( 0 == maxNumChannels && busy )
    {
        result = paDeviceUnavailable;
        goto error;
    }

    /* The above negotiation may fail for an old driver so try this older technique. */
    if( maxNumChannels < 1 )
    {
        int stereo = 1;
        if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
        {
            maxNumChannels = 1;
        }
        else
        {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -