moved trunk into place
This commit is contained in:
commit
c1e1d067d2
260 changed files with 96276 additions and 0 deletions
92
src/os/unix/pa_unix_hostapis.c
Normal file
92
src/os/unix/pa_unix_hostapis.c
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library UNIX initialization table
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup unix_src
|
||||
*/
|
||||
|
||||
#include "pa_hostapi.h"
|
||||
|
||||
PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
/* Added for IRIX, Pieter, oct 2, 2003: */
|
||||
PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
/* Linux AudioScience HPI */
|
||||
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
|
||||
/** Note that ALSA is placed before OSS so that the latter is preferred over the
|
||||
* latter on Linux.
|
||||
*/
|
||||
|
||||
PaUtilHostApiInitializer *paHostApiInitializers[] =
|
||||
{
|
||||
#ifdef PA_USE_ALSA
|
||||
PaAlsa_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_OSS
|
||||
PaOSS_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_JACK
|
||||
PaJack_Initialize,
|
||||
#endif
|
||||
/* Added for IRIX, Pieter, oct 2, 2003: */
|
||||
#ifdef PA_USE_SGI
|
||||
PaSGI_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_ASIHPI
|
||||
PaAsiHpi_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_COREAUDIO
|
||||
PaMacCore_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_SKELETON
|
||||
PaSkeleton_Initialize,
|
||||
#endif
|
||||
|
||||
0 /* NULL terminated array */
|
||||
};
|
||||
|
||||
int paDefaultHostApiIndex = 0;
|
||||
666
src/os/unix/pa_unix_util.c
Normal file
666
src/os/unix/pa_unix_util.c
Normal file
|
|
@ -0,0 +1,666 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* UNIX platform-specific support functions
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2000 Ross Bencina
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup unix_src
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
#include <string.h> /* For memset */
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "pa_util.h"
|
||||
#include "pa_unix_util.h"
|
||||
#include "pa_debugprint.h"
|
||||
|
||||
/*
|
||||
Track memory allocations to avoid leaks.
|
||||
*/
|
||||
|
||||
#if PA_TRACK_MEMORY
|
||||
static int numAllocations_ = 0;
|
||||
#endif
|
||||
|
||||
|
||||
void *PaUtil_AllocateMemory( long size )
|
||||
{
|
||||
void *result = malloc( size );
|
||||
|
||||
#if PA_TRACK_MEMORY
|
||||
if( result != NULL ) numAllocations_ += 1;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PaUtil_FreeMemory( void *block )
|
||||
{
|
||||
if( block != NULL )
|
||||
{
|
||||
free( block );
|
||||
#if PA_TRACK_MEMORY
|
||||
numAllocations_ -= 1;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int PaUtil_CountCurrentlyAllocatedBlocks( void )
|
||||
{
|
||||
#if PA_TRACK_MEMORY
|
||||
return numAllocations_;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Pa_Sleep( long msec )
|
||||
{
|
||||
#ifdef HAVE_NANOSLEEP
|
||||
struct timespec req = {0}, rem = {0};
|
||||
PaTime time = msec / 1.e3;
|
||||
req.tv_sec = (time_t)time;
|
||||
assert(time - req.tv_sec < 1.0);
|
||||
req.tv_nsec = (long)((time - req.tv_sec) * 1.e9);
|
||||
nanosleep(&req, &rem);
|
||||
/* XXX: Try sleeping the remaining time (contained in rem) if interrupted by a signal? */
|
||||
#else
|
||||
while( msec > 999 ) /* For OpenBSD and IRIX, argument */
|
||||
{ /* to usleep must be < 1000000. */
|
||||
usleep( 999000 );
|
||||
msec -= 999;
|
||||
}
|
||||
usleep( msec * 1000 );
|
||||
#endif
|
||||
}
|
||||
|
||||
/* *** NOT USED YET: ***
|
||||
static int usePerformanceCounter_;
|
||||
static double microsecondsPerTick_;
|
||||
*/
|
||||
|
||||
void PaUtil_InitializeClock( void )
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
|
||||
PaTime PaUtil_GetTime( void )
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
struct timespec tp;
|
||||
clock_gettime(CLOCK_REALTIME, &tp);
|
||||
return (PaTime)(tp.tv_sec + tp.tv_nsec / 1.e9);
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday( &tv, NULL );
|
||||
return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
|
||||
#endif
|
||||
}
|
||||
|
||||
PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
|
||||
{
|
||||
(void) paUtilErr_;
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
void PaUtil_TerminateThreading( PaUtilThreading *threading )
|
||||
{
|
||||
}
|
||||
|
||||
PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
|
||||
{
|
||||
pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
void *pret;
|
||||
|
||||
if( exitResult )
|
||||
*exitResult = paNoError;
|
||||
|
||||
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
|
||||
if( !wait )
|
||||
pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
|
||||
pthread_join( threading->callbackThread, &pret );
|
||||
|
||||
#ifdef PTHREAD_CANCELED
|
||||
if( pret && PTHREAD_CANCELED != pret )
|
||||
#else
|
||||
/* !wait means the thread may have been canceled */
|
||||
if( pret && wait )
|
||||
#endif
|
||||
{
|
||||
if( exitResult )
|
||||
*exitResult = *(PaError *) pret;
|
||||
free( pret );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Threading */
|
||||
/* paUnixMainThread
|
||||
* We have to be a bit careful with defining this global variable,
|
||||
* as explained below. */
|
||||
#ifdef __apple__
|
||||
/* apple/gcc has a "problem" with global vars and dynamic libs.
|
||||
Initializing it seems to fix the problem.
|
||||
Described a bit in this thread:
|
||||
http://gcc.gnu.org/ml/gcc/2005-06/msg00179.html
|
||||
*/
|
||||
pthread_t paUnixMainThread = 0;
|
||||
#else
|
||||
/*pthreads are opaque. We don't know that asigning it an int value
|
||||
always makes sense, so we don't initialize it unless we have to.*/
|
||||
pthread_t paUnixMainThread = 0;
|
||||
#endif
|
||||
|
||||
PaError PaUnixThreading_Initialize()
|
||||
{
|
||||
paUnixMainThread = pthread_self();
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
static PaError BoostPriority( PaUnixThread* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
struct sched_param spm = { 0 };
|
||||
/* Priority should only matter between contending FIFO threads? */
|
||||
spm.sched_priority = 1;
|
||||
|
||||
assert( self );
|
||||
|
||||
if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 )
|
||||
{
|
||||
PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
|
||||
PA_DEBUG(( "Failed bumping priority\n" ));
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 1; /* Success */
|
||||
}
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
|
||||
int rtSched )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
pthread_attr_t attr;
|
||||
int started = 0;
|
||||
|
||||
memset( self, 0, sizeof (PaUnixThread) );
|
||||
PaUnixMutex_Initialize( &self->mtx );
|
||||
PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 );
|
||||
|
||||
self->parentWaiting = 0 != waitForChild;
|
||||
|
||||
/* Spawn thread */
|
||||
|
||||
/* Temporarily disabled since we should test during configuration for presence of required mman.h header */
|
||||
#if 0
|
||||
#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
|
||||
if( rtSched )
|
||||
{
|
||||
if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
|
||||
{
|
||||
int savedErrno = errno; /* In case errno gets overwritten */
|
||||
assert( savedErrno != EINVAL ); /* Most likely a programmer error */
|
||||
PA_UNLESS( (savedErrno == EPERM), paInternalError );
|
||||
PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
|
||||
}
|
||||
else
|
||||
PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
|
||||
/* Priority relative to other processes */
|
||||
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
|
||||
|
||||
PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError );
|
||||
started = 1;
|
||||
|
||||
if( rtSched )
|
||||
{
|
||||
#if 0
|
||||
if( self->useWatchdog )
|
||||
{
|
||||
int err;
|
||||
struct sched_param wdSpm = { 0 };
|
||||
/* Launch watchdog, watchdog sets callback thread priority */
|
||||
int prio = PA_MIN( self->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
|
||||
wdSpm.sched_priority = prio;
|
||||
|
||||
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
|
||||
PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
|
||||
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
|
||||
PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
|
||||
PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
|
||||
if( (err = pthread_create( &self->watchdogThread, &attr, &WatchdogFunc, self )) )
|
||||
{
|
||||
PA_UNLESS( err == EPERM, paInternalError );
|
||||
/* Permission error, go on without realtime privileges */
|
||||
PA_DEBUG(( "Failed bumping priority\n" ));
|
||||
}
|
||||
else
|
||||
{
|
||||
int policy;
|
||||
self->watchdogRunning = 1;
|
||||
PA_ENSURE_SYSTEM( pthread_getschedparam( self->watchdogThread, &policy, &wdSpm ), 0 );
|
||||
/* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
|
||||
if( wdSpm.sched_priority != prio )
|
||||
{
|
||||
PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
|
||||
PA_ENSURE( paInternalError );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
PA_ENSURE( BoostPriority( self ) );
|
||||
|
||||
{
|
||||
int policy;
|
||||
struct sched_param spm;
|
||||
pthread_getschedparam(self->thread, &policy, &spm);
|
||||
}
|
||||
}
|
||||
|
||||
if( self->parentWaiting )
|
||||
{
|
||||
PaTime till;
|
||||
struct timespec ts;
|
||||
int res = 0;
|
||||
PaTime now;
|
||||
|
||||
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
|
||||
|
||||
/* Wait for stream to be started */
|
||||
now = PaUtil_GetTime();
|
||||
till = now + waitForChild;
|
||||
|
||||
while( self->parentWaiting && !res )
|
||||
{
|
||||
if( waitForChild > 0 )
|
||||
{
|
||||
ts.tv_sec = (time_t) floor( till );
|
||||
ts.tv_nsec = (long) ((till - floor( till )) * 1e9);
|
||||
res = pthread_cond_timedwait( &self->cond, &self->mtx.mtx, &ts );
|
||||
}
|
||||
else
|
||||
{
|
||||
res = pthread_cond_wait( &self->cond, &self->mtx.mtx );
|
||||
}
|
||||
}
|
||||
|
||||
PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
|
||||
|
||||
PA_UNLESS( !res || ETIMEDOUT == res, paInternalError );
|
||||
PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - now ));
|
||||
if( ETIMEDOUT == res )
|
||||
{
|
||||
PA_ENSURE( paTimedOut );
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return result;
|
||||
error:
|
||||
if( started )
|
||||
{
|
||||
PaUnixThread_Terminate( self, 0, NULL );
|
||||
}
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
void* pret;
|
||||
|
||||
if( exitResult )
|
||||
{
|
||||
*exitResult = paNoError;
|
||||
}
|
||||
#if 0
|
||||
if( watchdogExitResult )
|
||||
*watchdogExitResult = paNoError;
|
||||
|
||||
if( th->watchdogRunning )
|
||||
{
|
||||
pthread_cancel( th->watchdogThread );
|
||||
PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 );
|
||||
|
||||
if( pret && pret != PTHREAD_CANCELED )
|
||||
{
|
||||
if( watchdogExitResult )
|
||||
*watchdogExitResult = *(PaError *) pret;
|
||||
free( pret );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
|
||||
/* TODO: Make join time out */
|
||||
self->stopRequested = wait;
|
||||
if( !wait )
|
||||
{
|
||||
PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, self->thread ));
|
||||
/* XXX: Safe to call this if the thread has exited on its own? */
|
||||
pthread_cancel( self->thread );
|
||||
}
|
||||
PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread ));
|
||||
PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 );
|
||||
|
||||
if( pret && PTHREAD_CANCELED != pret )
|
||||
{
|
||||
if( exitResult )
|
||||
{
|
||||
*exitResult = *(PaError*)pret;
|
||||
}
|
||||
free( pret );
|
||||
}
|
||||
|
||||
error:
|
||||
PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError );
|
||||
PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PaError PaUnixThread_PrepareNotify( PaUnixThread* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
PA_UNLESS( self->parentWaiting, paInternalError );
|
||||
|
||||
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
|
||||
self->locked = 1;
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
PaError PaUnixThread_NotifyParent( PaUnixThread* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
PA_UNLESS( self->parentWaiting, paInternalError );
|
||||
|
||||
if( !self->locked )
|
||||
{
|
||||
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
|
||||
self->locked = 1;
|
||||
}
|
||||
self->parentWaiting = 0;
|
||||
pthread_cond_signal( &self->cond );
|
||||
PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
|
||||
self->locked = 0;
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
int PaUnixThread_StopRequested( PaUnixThread* self )
|
||||
{
|
||||
return self->stopRequested;
|
||||
}
|
||||
|
||||
PaError PaUnixMutex_Initialize( PaUnixMutex* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
PA_ASSERT_CALL( pthread_mutex_init( &self->mtx, NULL ), 0 );
|
||||
return result;
|
||||
}
|
||||
|
||||
PaError PaUnixMutex_Terminate( PaUnixMutex* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
PA_ASSERT_CALL( pthread_mutex_destroy( &self->mtx ), 0 );
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Lock mutex.
|
||||
*
|
||||
* We're disabling thread cancellation while the thread is holding a lock, so mutexes are
|
||||
* properly unlocked at termination time.
|
||||
*/
|
||||
PaError PaUnixMutex_Lock( PaUnixMutex* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
int oldState;
|
||||
|
||||
PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
|
||||
PA_ENSURE_SYSTEM( pthread_mutex_lock( &self->mtx ), 0 );
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Unlock mutex.
|
||||
*
|
||||
* Thread cancellation is enabled again after the mutex is properly unlocked.
|
||||
*/
|
||||
PaError PaUnixMutex_Unlock( PaUnixMutex* self )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
int oldState;
|
||||
|
||||
PA_ENSURE_SYSTEM( pthread_mutex_unlock( &self->mtx ), 0 );
|
||||
PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void OnWatchdogExit( void *userData )
|
||||
{
|
||||
PaAlsaThreading *th = (PaAlsaThreading *) userData;
|
||||
struct sched_param spm = { 0 };
|
||||
assert( th );
|
||||
|
||||
PA_ASSERT_CALL( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
|
||||
PA_DEBUG(( "Watchdog exiting\n" ));
|
||||
}
|
||||
|
||||
static void *WatchdogFunc( void *userData )
|
||||
{
|
||||
PaError result = paNoError, *pres = NULL;
|
||||
int err;
|
||||
PaAlsaThreading *th = (PaAlsaThreading *) userData;
|
||||
unsigned intervalMsec = 500;
|
||||
const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
|
||||
PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
|
||||
double cpuLoad, avgCpuLoad = 0.;
|
||||
int throttled = 0;
|
||||
|
||||
assert( th );
|
||||
|
||||
/* Execute OnWatchdogExit when exiting */
|
||||
pthread_cleanup_push( &OnWatchdogExit, th );
|
||||
|
||||
/* Boost priority of callback thread */
|
||||
PA_ENSURE( result = BoostPriority( th ) );
|
||||
if( !result )
|
||||
{
|
||||
/* Boost failed, might as well exit */
|
||||
pthread_exit( NULL );
|
||||
}
|
||||
|
||||
cpuTimeThen = th->callbackCpuTime;
|
||||
{
|
||||
int policy;
|
||||
struct sched_param spm = { 0 };
|
||||
pthread_getschedparam( pthread_self(), &policy, &spm );
|
||||
PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
|
||||
}
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
|
||||
|
||||
/* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
|
||||
pthread_testcancel();
|
||||
Pa_Sleep( intervalMsec );
|
||||
pthread_testcancel();
|
||||
|
||||
if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
|
||||
{
|
||||
PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
|
||||
/* Tell thread to terminate */
|
||||
err = pthread_kill( th->callbackThread, SIGKILL );
|
||||
pthread_exit( NULL );
|
||||
}
|
||||
|
||||
PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
|
||||
|
||||
/* Check if we should throttle, or unthrottle :P */
|
||||
cpuTimeNow = th->callbackCpuTime;
|
||||
cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
|
||||
cpuTimeThen = cpuTimeNow;
|
||||
|
||||
timeNow = PaUtil_GetTime();
|
||||
timeElapsed = timeNow - timeThen;
|
||||
timeThen = timeNow;
|
||||
cpuLoad = cpuTimeElapsed / timeElapsed;
|
||||
avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
|
||||
/*
|
||||
if( throttled )
|
||||
PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
|
||||
*/
|
||||
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
|
||||
{
|
||||
static int policy;
|
||||
static struct sched_param spm = { 0 };
|
||||
static const struct sched_param defaultSpm = { 0 };
|
||||
PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
|
||||
|
||||
pthread_getschedparam( th->callbackThread, &policy, &spm );
|
||||
if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
|
||||
{
|
||||
throttled = 1;
|
||||
}
|
||||
else
|
||||
PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
|
||||
|
||||
/* Give other processes a go, before raising priority again */
|
||||
PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
|
||||
Pa_Sleep( th->throttledSleepTime );
|
||||
|
||||
/* Reset callback priority */
|
||||
if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
|
||||
{
|
||||
PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
|
||||
}
|
||||
|
||||
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
|
||||
intervalMsec = 50;
|
||||
else
|
||||
intervalMsec = 100;
|
||||
|
||||
/*
|
||||
lowpassCoeff = .97;
|
||||
lowpassCoeff1 = .99999 - lowpassCoeff;
|
||||
*/
|
||||
}
|
||||
else if( throttled && avgCpuLoad < .8 )
|
||||
{
|
||||
intervalMsec = 500;
|
||||
throttled = 0;
|
||||
|
||||
/*
|
||||
lowpassCoeff = .9;
|
||||
lowpassCoeff1 = .99999 - lowpassCoeff;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
|
||||
|
||||
error:
|
||||
/* Shouldn't get here in the normal case */
|
||||
|
||||
/* Pass on error code */
|
||||
pres = malloc( sizeof (PaError) );
|
||||
*pres = result;
|
||||
|
||||
pthread_exit( pres );
|
||||
}
|
||||
|
||||
static void CallbackUpdate( PaAlsaThreading *th )
|
||||
{
|
||||
th->callbackTime = PaUtil_GetTime();
|
||||
th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
|
||||
}
|
||||
|
||||
/*
|
||||
static void *CanaryFunc( void *userData )
|
||||
{
|
||||
const unsigned intervalMsec = 1000;
|
||||
PaUtilThreading *th = (PaUtilThreading *) userData;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
th->canaryTime = PaUtil_GetTime();
|
||||
|
||||
pthread_testcancel();
|
||||
Pa_Sleep( intervalMsec );
|
||||
}
|
||||
|
||||
pthread_exit( NULL );
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
224
src/os/unix/pa_unix_util.h
Normal file
224
src/os/unix/pa_unix_util.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* UNIX platform-specific support functions
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2000 Ross Bencina
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup unix_src
|
||||
*/
|
||||
|
||||
#ifndef PA_UNIX_UTIL_H
|
||||
#define PA_UNIX_UTIL_H
|
||||
|
||||
#include "pa_cpuload.h"
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
|
||||
#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
|
||||
|
||||
/* Utilize GCC branch prediction for error tests */
|
||||
#if defined __GNUC__ && __GNUC__ >= 3
|
||||
#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
|
||||
#else
|
||||
#define UNLIKELY(expr) (expr)
|
||||
#endif
|
||||
|
||||
#define STRINGIZE_HELPER(expr) #expr
|
||||
#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
|
||||
|
||||
#define PA_UNLESS(expr, code) \
|
||||
do { \
|
||||
if( UNLIKELY( (expr) == 0 ) ) \
|
||||
{ \
|
||||
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
|
||||
result = (code); \
|
||||
goto error; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
static PaError paUtilErr_; /* Used with PA_ENSURE */
|
||||
|
||||
/* Check PaError */
|
||||
#define PA_ENSURE(expr) \
|
||||
do { \
|
||||
if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
|
||||
{ \
|
||||
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
|
||||
result = paUtilErr_; \
|
||||
goto error; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define PA_ASSERT_CALL(expr, success) \
|
||||
paUtilErr_ = (expr); \
|
||||
assert( success == paUtilErr_ );
|
||||
|
||||
#define PA_ENSURE_SYSTEM(expr, success) \
|
||||
do { \
|
||||
if( UNLIKELY( (paUtilErr_ = (expr)) != success ) ) \
|
||||
{ \
|
||||
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
|
||||
if( pthread_equal(pthread_self(), paUnixMainThread) ) \
|
||||
{ \
|
||||
PaUtil_SetLastHostErrorInfo( paALSA, paUtilErr_, strerror( paUtilErr_ ) ); \
|
||||
} \
|
||||
PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \
|
||||
result = paUnanticipatedHostError; \
|
||||
goto error; \
|
||||
} \
|
||||
} while( 0 );
|
||||
|
||||
typedef struct {
|
||||
pthread_t callbackThread;
|
||||
} PaUtilThreading;
|
||||
|
||||
PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
|
||||
void PaUtil_TerminateThreading( PaUtilThreading *threading );
|
||||
PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
|
||||
PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
|
||||
|
||||
/* State accessed by utility functions */
|
||||
|
||||
/*
|
||||
void PaUnix_SetRealtimeScheduling( int rt );
|
||||
|
||||
void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
|
||||
|
||||
PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
|
||||
|
||||
PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
|
||||
|
||||
void PaUtil_CallbackUpdate( PaUtilThreading *th );
|
||||
*/
|
||||
|
||||
extern pthread_t paUnixMainThread;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t mtx;
|
||||
} PaUnixMutex;
|
||||
|
||||
PaError PaUnixMutex_Initialize( PaUnixMutex* self );
|
||||
PaError PaUnixMutex_Terminate( PaUnixMutex* self );
|
||||
PaError PaUnixMutex_Lock( PaUnixMutex* self );
|
||||
PaError PaUnixMutex_Unlock( PaUnixMutex* self );
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_t thread;
|
||||
int parentWaiting;
|
||||
int stopRequested;
|
||||
int locked;
|
||||
PaUnixMutex mtx;
|
||||
pthread_cond_t cond;
|
||||
volatile sig_atomic_t stopRequest;
|
||||
} PaUnixThread;
|
||||
|
||||
/** Initialize global threading state.
|
||||
*/
|
||||
PaError PaUnixThreading_Initialize();
|
||||
|
||||
/** Perish, passing on eventual error code.
|
||||
*
|
||||
* A thin wrapper around pthread_exit, will automatically pass on any error code to the joining thread.
|
||||
* If the result indicates an error, i.e. it is not equal to paNoError, this function will automatically
|
||||
* allocate a pointer so the error is passed on with pthread_exit. If the result indicates that all is
|
||||
* well however, only a NULL pointer will be handed to pthread_exit. Thus, the joining thread should
|
||||
* check whether a non-NULL result pointer is obtained from pthread_join and make sure to free it.
|
||||
* @param result: The error code to pass on to the joining thread.
|
||||
*/
|
||||
#define PaUnixThreading_EXIT(result) \
|
||||
do { \
|
||||
PaError* pres = NULL; \
|
||||
if( paNoError != (result) ) \
|
||||
{ \
|
||||
pres = malloc( sizeof (PaError) ); \
|
||||
*pres = (result); \
|
||||
} \
|
||||
pthread_exit( pres ); \
|
||||
} while (0);
|
||||
|
||||
/** Spawn a thread.
|
||||
*
|
||||
* Intended for spawning the callback thread from the main thread. This function can even block (for a certain
|
||||
* time or indefinitely) untill notified by the callback thread (using PaUnixThread_NotifyParent), which can be
|
||||
* useful in order to make sure that callback has commenced before returning from Pa_StartStream.
|
||||
* @param threadFunc: The function to be executed in the child thread.
|
||||
* @param waitForChild: If not 0, wait for child thread to call PaUnixThread_NotifyParent. Less than 0 means
|
||||
* wait for ever, greater than 0 wait for the specified time.
|
||||
* @param rtSched: Enable realtime scheduling?
|
||||
* @return: If timed out waiting on child, paTimedOut.
|
||||
*/
|
||||
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
|
||||
int rtSched );
|
||||
|
||||
/** Terminate thread.
|
||||
*
|
||||
* @param wait: If true, request that background thread stop and wait untill it does, else cancel it.
|
||||
* @param exitResult: If non-null this will upon return contain the exit status of the thread.
|
||||
*/
|
||||
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult );
|
||||
|
||||
/** Prepare to notify waiting parent thread.
|
||||
*
|
||||
* An internal lock must be held before the parent is notified in PaUnixThread_NotifyParent, call this to
|
||||
* acquire it beforehand.
|
||||
* @return: If parent is not waiting, paInternalError.
|
||||
*/
|
||||
PaError PaUnixThread_PrepareNotify( PaUnixThread* self );
|
||||
|
||||
/** Notify waiting parent thread.
|
||||
*
|
||||
* @return: If parent timed out waiting, paTimedOut. If parent was never waiting, paInternalError.
|
||||
*/
|
||||
PaError PaUnixThread_NotifyParent( PaUnixThread* self );
|
||||
|
||||
/** Has the parent thread requested this thread to stop?
|
||||
*/
|
||||
int PaUnixThread_StopRequested( PaUnixThread* self );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
100
src/os/win/pa_win_hostapis.c
Normal file
100
src/os/win/pa_win_hostapis.c
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library Windows initialization table
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2008 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup win_src
|
||||
|
||||
@brief Win32 host API initialization function table.
|
||||
|
||||
@todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
|
||||
the Unix version does, we should consider being consistent.
|
||||
*/
|
||||
|
||||
|
||||
#include "pa_hostapi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
PaUtilHostApiInitializer *paHostApiInitializers[] =
|
||||
{
|
||||
|
||||
#ifndef PA_NO_WMME
|
||||
PaWinMme_Initialize,
|
||||
#endif
|
||||
|
||||
#ifndef PA_NO_DS
|
||||
PaWinDs_Initialize,
|
||||
#endif
|
||||
|
||||
#ifndef PA_NO_ASIO
|
||||
PaAsio_Initialize,
|
||||
#endif
|
||||
|
||||
/*
|
||||
#ifndef PA_NO_WASAPI
|
||||
PaWinWasapi_Initialize,
|
||||
#endif
|
||||
|
||||
#ifndef PA_NO_WDMKS
|
||||
PaWinWdm_Initialize,
|
||||
#endif
|
||||
*/
|
||||
|
||||
PaSkeleton_Initialize, /* just for testing */
|
||||
|
||||
0 /* NULL terminated array */
|
||||
};
|
||||
|
||||
|
||||
int paDefaultHostApiIndex = 0;
|
||||
|
||||
147
src/os/win/pa_win_util.c
Normal file
147
src/os/win/pa_win_util.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* Win32 platform-specific support functions
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2008 Ross Bencina
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup win_src
|
||||
|
||||
@brief Win32 implementation of platform-specific PaUtil support functions.
|
||||
|
||||
@todo Implement workaround for QueryPerformanceCounter() skipping forward
|
||||
bug. (see msdn kb Q274323).
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h> /* for timeGetTime() */
|
||||
|
||||
#include "pa_util.h"
|
||||
|
||||
|
||||
/*
|
||||
Track memory allocations to avoid leaks.
|
||||
*/
|
||||
|
||||
#if PA_TRACK_MEMORY
|
||||
static int numAllocations_ = 0;
|
||||
#endif
|
||||
|
||||
|
||||
void *PaUtil_AllocateMemory( long size )
|
||||
{
|
||||
void *result = GlobalAlloc( GPTR, size );
|
||||
|
||||
#if PA_TRACK_MEMORY
|
||||
if( result != NULL ) numAllocations_ += 1;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PaUtil_FreeMemory( void *block )
|
||||
{
|
||||
if( block != NULL )
|
||||
{
|
||||
GlobalFree( block );
|
||||
#if PA_TRACK_MEMORY
|
||||
numAllocations_ -= 1;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int PaUtil_CountCurrentlyAllocatedBlocks( void )
|
||||
{
|
||||
#if PA_TRACK_MEMORY
|
||||
return numAllocations_;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Pa_Sleep( long msec )
|
||||
{
|
||||
Sleep( msec );
|
||||
}
|
||||
|
||||
static int usePerformanceCounter_;
|
||||
static double secondsPerTick_;
|
||||
|
||||
void PaUtil_InitializeClock( void )
|
||||
{
|
||||
LARGE_INTEGER ticksPerSecond;
|
||||
|
||||
if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
|
||||
{
|
||||
usePerformanceCounter_ = 1;
|
||||
secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
|
||||
}
|
||||
else
|
||||
{
|
||||
usePerformanceCounter_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double PaUtil_GetTime( void )
|
||||
{
|
||||
LARGE_INTEGER time;
|
||||
|
||||
if( usePerformanceCounter_ )
|
||||
{
|
||||
/* FIXME:
|
||||
according to this knowledge-base article, QueryPerformanceCounter
|
||||
can skip forward by seconds!
|
||||
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
|
||||
|
||||
it may be better to use the rtdsc instruction using inline asm,
|
||||
however then a method is needed to calculate a ticks/seconds ratio.
|
||||
*/
|
||||
QueryPerformanceCounter( &time );
|
||||
return time.QuadPart * secondsPerTick_;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef UNDER_CE
|
||||
return timeGetTime() * .001;
|
||||
#else
|
||||
return GetTickCount() * .001;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
159
src/os/win/pa_win_waveformat.c
Normal file
159
src/os/win/pa_win_waveformat.c
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Windows WAVEFORMAT* data structure utilities
|
||||
* portaudio.h should be included before this file.
|
||||
*
|
||||
* Copyright (c) 2007 Ross Bencina
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_win_waveformat.h"
|
||||
|
||||
|
||||
#if !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
||||
#endif
|
||||
|
||||
#if !defined(WAVE_FORMAT_IEEE_FLOAT)
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
static GUID pawin_ksDataFormatSubtypePcm =
|
||||
{ (USHORT)(WAVE_FORMAT_PCM), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
|
||||
|
||||
static GUID pawin_ksDataFormatSubtypeIeeeFloat =
|
||||
{ (USHORT)(WAVE_FORMAT_IEEE_FLOAT), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
|
||||
|
||||
|
||||
|
||||
void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat,
|
||||
int numChannels, PaSampleFormat sampleFormat, double sampleRate )
|
||||
{
|
||||
WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat;
|
||||
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
|
||||
unsigned long bytesPerFrame = numChannels * bytesPerSample;
|
||||
|
||||
if( sampleFormat == paFloat32 )
|
||||
waveFormatEx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
else
|
||||
waveFormatEx->wFormatTag = WAVE_FORMAT_PCM;
|
||||
|
||||
waveFormatEx->nChannels = (WORD)numChannels;
|
||||
waveFormatEx->nSamplesPerSec = (DWORD)sampleRate;
|
||||
waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame;
|
||||
waveFormatEx->nBlockAlign = (WORD)bytesPerFrame;
|
||||
waveFormatEx->wBitsPerSample = bytesPerSample * 8;
|
||||
waveFormatEx->cbSize = 0;
|
||||
}
|
||||
|
||||
|
||||
void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat,
|
||||
int numChannels, PaSampleFormat sampleFormat, double sampleRate,
|
||||
PaWinWaveFormatChannelMask channelMask )
|
||||
{
|
||||
WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat;
|
||||
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
|
||||
unsigned long bytesPerFrame = numChannels * bytesPerSample;
|
||||
|
||||
waveFormatEx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
waveFormatEx->nChannels = (WORD)numChannels;
|
||||
waveFormatEx->nSamplesPerSec = (DWORD)sampleRate;
|
||||
waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame;
|
||||
waveFormatEx->nBlockAlign = (WORD)bytesPerFrame;
|
||||
waveFormatEx->wBitsPerSample = bytesPerSample * 8;
|
||||
waveFormatEx->cbSize = 22;
|
||||
|
||||
*((WORD*)&waveFormat->fields[PAWIN_INDEXOF_WVALIDBITSPERSAMPLE]) =
|
||||
waveFormatEx->wBitsPerSample;
|
||||
|
||||
*((DWORD*)&waveFormat->fields[PAWIN_INDEXOF_DWCHANNELMASK]) = channelMask;
|
||||
|
||||
if( sampleFormat == paFloat32 )
|
||||
*((GUID*)&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT]) =
|
||||
pawin_ksDataFormatSubtypeIeeeFloat;
|
||||
else
|
||||
*((GUID*)&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT]) =
|
||||
pawin_ksDataFormatSubtypePcm;
|
||||
}
|
||||
|
||||
|
||||
PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels )
|
||||
{
|
||||
switch( numChannels ){
|
||||
case 1:
|
||||
return PAWIN_SPEAKER_MONO;
|
||||
case 2:
|
||||
return PAWIN_SPEAKER_STEREO;
|
||||
case 3:
|
||||
return PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_FRONT_RIGHT;
|
||||
case 4:
|
||||
return PAWIN_SPEAKER_QUAD;
|
||||
case 5:
|
||||
return PAWIN_SPEAKER_QUAD | PAWIN_SPEAKER_FRONT_CENTER;
|
||||
case 6:
|
||||
/* The meaning of the PAWIN_SPEAKER_5POINT1 flag has changed over time:
|
||||
http://msdn2.microsoft.com/en-us/library/aa474707.aspx
|
||||
We use PAWIN_SPEAKER_5POINT1 (not PAWIN_SPEAKER_5POINT1_SURROUND)
|
||||
because on some cards (eg Audigy) PAWIN_SPEAKER_5POINT1_SURROUND
|
||||
results in a virtual mixdown placing the rear output in the
|
||||
front _and_ rear speakers.
|
||||
*/
|
||||
return PAWIN_SPEAKER_5POINT1;
|
||||
/* case 7: */
|
||||
case 8:
|
||||
return PAWIN_SPEAKER_7POINT1;
|
||||
}
|
||||
|
||||
/* Apparently some Audigy drivers will output silence
|
||||
if the direct-out constant (0) is used. So this is not ideal.
|
||||
*/
|
||||
return PAWIN_SPEAKER_DIRECTOUT;
|
||||
|
||||
/* Note that Alec Rogers proposed the following as an alternate method to
|
||||
generate the default channel mask, however it doesn't seem to be an improvement
|
||||
over the above, since some drivers will matrix outputs mapping to non-present
|
||||
speakers accross multiple physical speakers.
|
||||
|
||||
if(nChannels==1) {
|
||||
pwfFormat->dwChannelMask = SPEAKER_FRONT_CENTER;
|
||||
}
|
||||
else {
|
||||
pwfFormat->dwChannelMask = 0;
|
||||
for(i=0; i<nChannels; i++)
|
||||
pwfFormat->dwChannelMask = (pwfFormat->dwChannelMask << 1) | 0x1;
|
||||
}
|
||||
*/
|
||||
}
|
||||
260
src/os/win/pa_win_wdmks_utils.c
Normal file
260
src/os/win/pa_win_wdmks_utils.c
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Windows WDM KS utilities
|
||||
*
|
||||
* Copyright (c) 1999 - 2007 Andrew Baldwin, Ross Bencina
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
#include <ks.h>
|
||||
#include <ksmedia.h>
|
||||
#include <stdio.h> // just for some development printfs
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_util.h"
|
||||
#include "pa_win_wdmks_utils.h"
|
||||
|
||||
|
||||
static PaError WdmGetPinPropertySimple(
|
||||
HANDLE handle,
|
||||
unsigned long pinId,
|
||||
unsigned long property,
|
||||
void* value,
|
||||
unsigned long valueSize )
|
||||
{
|
||||
DWORD bytesReturned;
|
||||
KSP_PIN ksPProp;
|
||||
ksPProp.Property.Set = KSPROPSETID_Pin;
|
||||
ksPProp.Property.Id = property;
|
||||
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
|
||||
ksPProp.PinId = pinId;
|
||||
ksPProp.Reserved = 0;
|
||||
|
||||
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
|
||||
value, valueSize, &bytesReturned, NULL ) == 0 || bytesReturned != valueSize )
|
||||
{
|
||||
return paUnanticipatedHostError;
|
||||
}
|
||||
else
|
||||
{
|
||||
return paNoError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PaError WdmGetPinPropertyMulti(
|
||||
HANDLE handle,
|
||||
unsigned long pinId,
|
||||
unsigned long property,
|
||||
KSMULTIPLE_ITEM** ksMultipleItem)
|
||||
{
|
||||
unsigned long multipleItemSize = 0;
|
||||
KSP_PIN ksPProp;
|
||||
DWORD bytesReturned;
|
||||
|
||||
*ksMultipleItem = 0;
|
||||
|
||||
ksPProp.Property.Set = KSPROPSETID_Pin;
|
||||
ksPProp.Property.Id = property;
|
||||
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
|
||||
ksPProp.PinId = pinId;
|
||||
ksPProp.Reserved = 0;
|
||||
|
||||
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp.Property,
|
||||
sizeof(KSP_PIN), NULL, 0, &multipleItemSize, NULL ) == 0 && GetLastError() != ERROR_MORE_DATA )
|
||||
{
|
||||
return paUnanticipatedHostError;
|
||||
}
|
||||
|
||||
*ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
|
||||
if( !*ksMultipleItem )
|
||||
{
|
||||
return paInsufficientMemory;
|
||||
}
|
||||
|
||||
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
|
||||
(void*)*ksMultipleItem, multipleItemSize, &bytesReturned, NULL ) == 0 || bytesReturned != multipleItemSize )
|
||||
{
|
||||
PaUtil_FreeMemory( ksMultipleItem );
|
||||
return paUnanticipatedHostError;
|
||||
}
|
||||
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
|
||||
static int GetKSFilterPinCount( HANDLE deviceHandle )
|
||||
{
|
||||
DWORD result;
|
||||
|
||||
if( WdmGetPinPropertySimple( deviceHandle, 0, KSPROPERTY_PIN_CTYPES, &result, sizeof(result) ) == paNoError ){
|
||||
return result;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static KSPIN_COMMUNICATION GetKSFilterPinPropertyCommunication( HANDLE deviceHandle, int pinId )
|
||||
{
|
||||
KSPIN_COMMUNICATION result;
|
||||
|
||||
if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_COMMUNICATION, &result, sizeof(result) ) == paNoError ){
|
||||
return result;
|
||||
}else{
|
||||
return KSPIN_COMMUNICATION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static KSPIN_DATAFLOW GetKSFilterPinPropertyDataflow( HANDLE deviceHandle, int pinId )
|
||||
{
|
||||
KSPIN_DATAFLOW result;
|
||||
|
||||
if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_DATAFLOW, &result, sizeof(result) ) == paNoError ){
|
||||
return result;
|
||||
}else{
|
||||
return (KSPIN_DATAFLOW)0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int KSFilterPinPropertyIdentifiersInclude(
|
||||
HANDLE deviceHandle, int pinId, unsigned long property, const GUID *identifierSet, unsigned long identifierId )
|
||||
{
|
||||
KSMULTIPLE_ITEM* item = NULL;
|
||||
KSIDENTIFIER* identifier;
|
||||
int i;
|
||||
int result = 0;
|
||||
|
||||
if( WdmGetPinPropertyMulti( deviceHandle, pinId, property, &item) != paNoError )
|
||||
return 0;
|
||||
|
||||
identifier = (KSIDENTIFIER*)(item+1);
|
||||
|
||||
for( i = 0; i < (int)item->Count; i++ )
|
||||
{
|
||||
if( !memcmp( (void*)&identifier[i].Set, (void*)identifierSet, sizeof( GUID ) ) &&
|
||||
( identifier[i].Id == identifierId ) )
|
||||
{
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PaUtil_FreeMemory( item );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* return the maximum channel count supported by any pin on the device.
|
||||
if isInput is non-zero we query input pins, otherwise output pins.
|
||||
*/
|
||||
int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput )
|
||||
{
|
||||
HANDLE deviceHandle;
|
||||
int pinCount, pinId, i;
|
||||
int result = 0;
|
||||
KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN );
|
||||
|
||||
if( !wcharDevicePath )
|
||||
return 0;
|
||||
|
||||
deviceHandle = CreateFileW( (LPCWSTR)wcharDevicePath, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
|
||||
if( deviceHandle == INVALID_HANDLE_VALUE )
|
||||
return 0;
|
||||
|
||||
pinCount = GetKSFilterPinCount( deviceHandle );
|
||||
for( pinId = 0; pinId < pinCount; ++pinId )
|
||||
{
|
||||
KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication( deviceHandle, pinId );
|
||||
KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow( deviceHandle, pinId );
|
||||
if( ( dataflow == requiredDataflowDirection ) &&
|
||||
(( communication == KSPIN_COMMUNICATION_SINK) ||
|
||||
( communication == KSPIN_COMMUNICATION_BOTH))
|
||||
&& ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
|
||||
KSPROPERTY_PIN_INTERFACES, &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING )
|
||||
|| KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
|
||||
KSPROPERTY_PIN_INTERFACES, &KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) )
|
||||
&& KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
|
||||
KSPROPERTY_PIN_MEDIUMS, &KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) )
|
||||
{
|
||||
KSMULTIPLE_ITEM* item = NULL;
|
||||
if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError )
|
||||
{
|
||||
KSDATARANGE *dataRange = (KSDATARANGE*)(item+1);
|
||||
|
||||
for( i=0; i < item->Count; ++i ){
|
||||
|
||||
if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat)
|
||||
|| memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0
|
||||
|| memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0
|
||||
|| ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 )
|
||||
&& ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) )
|
||||
{
|
||||
KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange;
|
||||
|
||||
/*
|
||||
printf( ">>> %d %d %d %d %S\n", isInput, dataflow, communication, dataRangeAudio->MaximumChannels, devicePath );
|
||||
|
||||
if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX, sizeof(GUID) ) == 0 )
|
||||
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WAVEFORMATEX\n" );
|
||||
else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_DSOUND, sizeof(GUID) ) == 0 )
|
||||
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_DSOUND\n" );
|
||||
else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WILDCARD, sizeof(GUID) ) == 0 )
|
||||
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WILDCARD\n" );
|
||||
else
|
||||
printf( "\tspecifier: ?\n" );
|
||||
*/
|
||||
|
||||
/*
|
||||
We assume that very high values for MaximumChannels are not useful and indicate
|
||||
that the driver isn't prepared to tell us the real number of channels which it supports.
|
||||
*/
|
||||
if( dataRangeAudio->MaximumChannels < 0xFFFFUL && (int)dataRangeAudio->MaximumChannels > result )
|
||||
result = (int)dataRangeAudio->MaximumChannels;
|
||||
}
|
||||
|
||||
dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
|
||||
}
|
||||
|
||||
PaUtil_FreeMemory( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle( deviceHandle );
|
||||
return result;
|
||||
}
|
||||
65
src/os/win/pa_win_wdmks_utils.h
Normal file
65
src/os/win/pa_win_wdmks_utils.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef PA_WIN_WDMKS_UTILS_H
|
||||
#define PA_WIN_WDMKS_UTILS_H
|
||||
|
||||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Windows WDM KS utilities
|
||||
*
|
||||
* Copyright (c) 1999 - 2007 Ross Bencina, Andrew Baldwin
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@brief Utilities for working with the Windows WDM KS API
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Query for the maximum number of channels supported by any pin of the
|
||||
specified device. Returns 0 if the query fails for any reason.
|
||||
|
||||
@param wcharDevicePath A system level PnP interface path, supplied as a WCHAR unicode string.
|
||||
Declard as void* to avoid introducing a dependency on wchar_t here.
|
||||
|
||||
@param isInput A flag specifying whether to query for input (non-zero) or output (zero) channels.
|
||||
*/
|
||||
int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* PA_WIN_WDMKS_UTILS_H */
|
||||
1219
src/os/win/pa_x86_plain_converters.c
Normal file
1219
src/os/win/pa_x86_plain_converters.c
Normal file
File diff suppressed because it is too large
Load diff
60
src/os/win/pa_x86_plain_converters.h
Normal file
60
src/os/win/pa_x86_plain_converters.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Plain Intel IA32 assembly implementations of PortAudio sample converter functions.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* 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. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup win_src
|
||||
*/
|
||||
|
||||
#ifndef PA_X86_PLAIN_CONVERTERS_H
|
||||
#define PA_X86_PLAIN_CONVERTERS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
/**
|
||||
@brief Install optimised converter functions suitable for all IA32 processors
|
||||
|
||||
It is recommended to call PaUtil_InitializeX86PlainConverters prior to calling Pa_Initialize
|
||||
*/
|
||||
void PaUtil_InitializeX86PlainConverters( void );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PA_X86_PLAIN_CONVERTERS_H */
|
||||
Loading…
Reference in a new issue