diff options
author | Miller Puckette <millerpuckette@users.sourceforge.net> | 2007-08-18 23:49:34 +0000 |
---|---|---|
committer | Miller Puckette <millerpuckette@users.sourceforge.net> | 2007-08-18 23:49:34 +0000 |
commit | 494dc8e9c2ded66121b8d8bf311b58310cd2ba8c (patch) | |
tree | d968fe4ed56c8e2915cad9aa422e20fc862f13c4 /pd/portaudio/src/os | |
parent | c1b10d55375dd8ecdf7b223d1f12541983422764 (diff) |
CVS upload mistakes
svn path=/trunk/; revision=8658
Diffstat (limited to 'pd/portaudio/src/os')
-rw-r--r-- | pd/portaudio/src/os/mac_osx/pa_mac_hostapis.c | 87 | ||||
-rw-r--r-- | pd/portaudio/src/os/unix/pa_unix_hostapis.c | 78 | ||||
-rw-r--r-- | pd/portaudio/src/os/unix/pa_unix_util.c | 666 | ||||
-rw-r--r-- | pd/portaudio/src/os/unix/pa_unix_util.h | 224 | ||||
-rw-r--r-- | pd/portaudio/src/os/win/pa_win_hostapis.c | 100 | ||||
-rw-r--r-- | pd/portaudio/src/os/win/pa_win_util.c | 147 | ||||
-rw-r--r-- | pd/portaudio/src/os/win/pa_x86_plain_converters.c | 1219 | ||||
-rw-r--r-- | pd/portaudio/src/os/win/pa_x86_plain_converters.h | 60 |
8 files changed, 2581 insertions, 0 deletions
diff --git a/pd/portaudio/src/os/mac_osx/pa_mac_hostapis.c b/pd/portaudio/src/os/mac_osx/pa_mac_hostapis.c new file mode 100644 index 00000000..e3e45294 --- /dev/null +++ b/pd/portaudio/src/os/mac_osx/pa_mac_hostapis.c @@ -0,0 +1,87 @@ +/* + * $Id: pa_mac_hostapis.c,v 1.1 2007-08-18 23:49:33 millerpuckette Exp $ + * Portable Audio I/O Library Macintosh 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 macosx_src +Mac OS host API initialization function table. +*/ + + +#include "pa_hostapi.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + PaError PaMacSm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + PaError PaMacAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +PaUtilHostApiInitializer *paHostApiInitializers[] = +{ +#ifdef PA_USE_COREAUDIO + PaMacCore_Initialize, +#endif + +#ifdef PA_USE_SM + PaMacSm_Initialize, +#endif + +#ifdef PA_USE_JACK + PaJack_Initialize, +#endif + +#ifdef PA_USE_ASIO + PaMacAsio_Initialize, +#endif + + PaSkeleton_Initialize, /* just for testing */ + + 0 /* NULL terminated array */ +}; + + +int paDefaultHostApiIndex = 0; diff --git a/pd/portaudio/src/os/unix/pa_unix_hostapis.c b/pd/portaudio/src/os/unix/pa_unix_hostapis.c new file mode 100644 index 00000000..f2439986 --- /dev/null +++ b/pd/portaudio/src/os/unix/pa_unix_hostapis.c @@ -0,0 +1,78 @@ +/* + * $Id: pa_unix_hostapis.c,v 1.1 2007-08-18 23:49:33 millerpuckette Exp $ + * 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 ); + + +PaUtilHostApiInitializer *paHostApiInitializers[] = + { +#ifdef PA_USE_OSS + PaOSS_Initialize, +#endif + +#ifdef PA_USE_ALSA + PaAlsa_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 + 0 /* NULL terminated array */ + }; + +int paDefaultHostApiIndex = 0; diff --git a/pd/portaudio/src/os/unix/pa_unix_util.c b/pd/portaudio/src/os/unix/pa_unix_util.c new file mode 100644 index 00000000..489b062e --- /dev/null +++ b/pd/portaudio/src/os/unix/pa_unix_util.c @@ -0,0 +1,666 @@ +/* + * $Id: pa_unix_util.c,v 1.1 2007-08-18 23:49:33 millerpuckette Exp $ + * 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 diff --git a/pd/portaudio/src/os/unix/pa_unix_util.h b/pd/portaudio/src/os/unix/pa_unix_util.h new file mode 100644 index 00000000..bfcc802e --- /dev/null +++ b/pd/portaudio/src/os/unix/pa_unix_util.h @@ -0,0 +1,224 @@ +/* + * $Id: pa_unix_util.h,v 1.1 2007-08-18 23:49:33 millerpuckette Exp $ + * 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 diff --git a/pd/portaudio/src/os/win/pa_win_hostapis.c b/pd/portaudio/src/os/win/pa_win_hostapis.c new file mode 100644 index 00000000..8a902aa1 --- /dev/null +++ b/pd/portaudio/src/os/win/pa_win_hostapis.c @@ -0,0 +1,100 @@ +/* + * $Id: pa_win_hostapis.c,v 1.1 2007-08-18 23:49:33 millerpuckette Exp $ + * Portable Audio I/O Library Windows 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 win_src + + 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; + diff --git a/pd/portaudio/src/os/win/pa_win_util.c b/pd/portaudio/src/os/win/pa_win_util.c new file mode 100644 index 00000000..fe6167ac --- /dev/null +++ b/pd/portaudio/src/os/win/pa_win_util.c @@ -0,0 +1,147 @@ +/* + * $Id: pa_win_util.c,v 1.1 2007-08-18 23:49:33 millerpuckette Exp $ + * Portable Audio I/O Library + * Win32 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 win_src + + Win32 platform-specific 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 + } +} diff --git a/pd/portaudio/src/os/win/pa_x86_plain_converters.c b/pd/portaudio/src/os/win/pa_x86_plain_converters.c new file mode 100644 index 00000000..63b058df --- /dev/null +++ b/pd/portaudio/src/os/win/pa_x86_plain_converters.c @@ -0,0 +1,1219 @@ +/* + * 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 +*/ + +#include "pa_x86_plain_converters.h" + +#include "pa_converters.h" +#include "pa_dither.h" + +/* + the main reason these versions are faster than the equivalent C versions + is that float -> int casting is expensive in C on x86 because the rounding + mode needs to be changed for every cast. these versions only set + the rounding mode once outside the loop. + + small additional speed gains are made by the way that clamping is + implemented. + +TODO: + o- inline dither code + o- implement Dither only (no-clip) versions + o- implement int8 and uint8 versions + o- test thouroughly + + o- the packed 24 bit functions could benefit from unrolling and avoiding + byte and word sized register access. +*/ + +/* -------------------------------------------------------------------------- */ + +/* +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } +*/ + +/* + the following notes were used to determine whether a floating point + value should be saturated (ie >1 or <-1) by loading it into an integer + register. these should be rewritten so that they make sense. + + an ieee floating point value + + 1.xxxxxxxxxxxxxxxxxxxx? + + + is less than or equal to 1 and greater than or equal to -1 either: + + if the mantissa is 0 and the unbiased exponent is 0 + + OR + + if the unbiased exponent < 0 + + this translates to: + + if the mantissa is 0 and the biased exponent is 7F + + or + + if the biased exponent is less than 7F + + + therefore the value is greater than 1 or less than -1 if + + the mantissa is not 0 and the biased exponent is 7F + + or + + if the biased exponent is greater than 7F + + + in other words, if we mask out the sign bit, the value is + greater than 1 or less than -1 if its integer representation is greater than: + + 0 01111111 0000 0000 0000 0000 0000 000 + + 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000 +*/ + +/* -------------------------------------------------------------------------- */ + +static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/ +static const double int32Scaler_ = 0x7FFFFFFF; +static const double ditheredInt32Scaler_ = 0x7FFFFFFE; +static const double int24Scaler_ = 0x7FFFFF; +static const double ditheredInt24Scaler_ = 0x7FFFFE; +static const double int16Scaler_ = 0x7FFF; +static const double ditheredInt16Scaler_ = 0x7FFE; + +#define PA_DITHER_BITS_ (15) +/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ +#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1<<PA_DITHER_BITS_)-1)) +static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_; +#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1) + +/* -------------------------------------------------------------------------- */ + +#ifdef _WIN64 + +/* + -EMT64/AMD64 uses different asm + -VC2005 doesnt allow _WIN64 with inline assembly either! + */ +void PaUtil_InitializeX86PlainConverters( void ) +{ +} + +#else + + +static void Float32_To_Int32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + // REVIEW + double scaled = *src * 0x7FFFFFFF; + *dest = (signed long) scaled; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int32Scaler_ // stack: (int)0x7FFFFFFF + + Float32_To_Int32_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF + /* + note: we could store to a temporary qword here which would cause + wraparound distortion instead of int indefinite 0x10. that would + be more work, and given that not enabling clipping is only advisable + when you know that your signal isn't going to clip it isn't worth it. + */ + fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // REVIEW + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + *dest = (signed long) scaled; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int32Scaler_ // stack: (int)0x7FFFFFFF + + Float32_To_Int32_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int32_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF + fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF + jmp Float32_To_Int32_Clip_stored + + Float32_To_Int32_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFFFF // convert to maximum range integers + mov dword ptr [edi], edx + + Float32_To_Int32_Clip_stored: + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + /* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + + while( count-- ) + { + // REVIEW + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + // use smaller scaler to prevent overflow when we add the dither + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + *dest = (signed long) dithered; + + + src += sourceStride; + dest += destinationStride; + } + */ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt32Scaler_ // stack: int scaler + + Float32_To_Int32_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int32_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler + fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler + jmp Float32_To_Int32_DitherClip_stored + + Float32_To_Int32_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFFFF // convert to maximum range integers + mov dword ptr [edi], edx + + Float32_To_Int32_DitherClip_stored: + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // convert to 32 bit and drop the low 8 bits + double scaled = *src * 0x7FFFFFFF; + temp = (signed long) scaled; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + signed long tempInt32; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int24Scaler_ // stack: (int)0x7FFFFF + + Float32_To_Int24_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF + fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF + mov edx, tempInt32 + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // convert to 32 bit and drop the low 8 bits + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + temp = (signed long) scaled; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + signed long tempInt32; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int24Scaler_ // stack: (int)0x7FFFFF + + Float32_To_Int24_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int24_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF + fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF + mov edx, tempInt32 + jmp Float32_To_Int24_Clip_store + + Float32_To_Int24_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFF // convert to maximum range integers + + Float32_To_Int24_Clip_store: + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + while( count-- ) + { + // convert to 32 bit and drop the low 8 bits + + // FIXME: the dither amplitude here appears to be too small by 8 bits + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + // use smaller scaler to prevent overflow when we add the dither + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + + temp = (signed long) dithered; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + signed long tempInt32; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt24Scaler_ // stack: int scaler + + Float32_To_Int24_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int24_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler + fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler + mov edx, tempInt32 + jmp Float32_To_Int24_DitherClip_store + + Float32_To_Int24_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFF // convert to maximum range integers + + Float32_To_Int24_DitherClip_store: + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + + short samp = (short) (*src * (32767.0f)); + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int16Scaler_ // stack: (int)0x7FFF + + Float32_To_Int16_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF + fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + long samp = (signed long) (*src * (32767.0f)); + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int16Scaler_ // stack: (int)0x7FFF + + Float32_To_Int16_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int16_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF + fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF + jmp Float32_To_Int16_Clip_stored + + Float32_To_Int16_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add dx, 0x7FFF // convert to maximum range integers + mov word ptr [edi], dx // store clamped into into dest + + Float32_To_Int16_Clip_stored: + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + // use smaller scaler to prevent overflow when we add the dither + float dithered = (*src * (32766.0f)) + dither; + signed long samp = (signed long) dithered; + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt16Scaler_ // stack: int scaler + + Float32_To_Int16_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int16_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler + fistp word ptr [edi] // store scaled int into dest, stack: int scaler + jmp Float32_To_Int16_DitherClip_stored + + Float32_To_Int16_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add dx, 0x7FFF // convert to maximum range integers + mov word ptr [edi], dx // store clamped into into dest + + Float32_To_Int16_DitherClip_stored: + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +void PaUtil_InitializeX86PlainConverters( void ) +{ + paConverters.Float32_To_Int32 = Float32_To_Int32; + paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip; + paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip; + + paConverters.Float32_To_Int24 = Float32_To_Int24; + paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip; + paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip; + + paConverters.Float32_To_Int16 = Float32_To_Int16; + paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip; + paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip; +} + +#endif + +/* -------------------------------------------------------------------------- */ diff --git a/pd/portaudio/src/os/win/pa_x86_plain_converters.h b/pd/portaudio/src/os/win/pa_x86_plain_converters.h new file mode 100644 index 00000000..96b41b85 --- /dev/null +++ b/pd/portaudio/src/os/win/pa_x86_plain_converters.h @@ -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 */ |