aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio_v18/pa_unix_oss
diff options
context:
space:
mode:
authorGuenter Geiger <ggeiger@users.sourceforge.net>2004-02-02 11:28:02 +0000
committerGuenter Geiger <ggeiger@users.sourceforge.net>2004-02-02 11:28:02 +0000
commitae6b5d89ea93b95c2990895077cf5e8f0bba9ad9 (patch)
tree1e7570f11ac688e94342968e90301c4684e61193 /pd/portaudio_v18/pa_unix_oss
parentf26399eba6ee6ce9eb7bae9a4b60a90dc2ebca94 (diff)
This commit was generated by cvs2svn to compensate for changes in r1301,
which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=1302
Diffstat (limited to 'pd/portaudio_v18/pa_unix_oss')
-rw-r--r--pd/portaudio_v18/pa_unix_oss/Makefile32
-rw-r--r--pd/portaudio_v18/pa_unix_oss/Makefile_freebsd36
-rw-r--r--pd/portaudio_v18/pa_unix_oss/low_latency_tip.txtbin0 -> 3111 bytes
-rw-r--r--pd/portaudio_v18/pa_unix_oss/pa_unix.c1122
-rw-r--r--pd/portaudio_v18/pa_unix_oss/pa_unix.h141
-rw-r--r--pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c385
-rw-r--r--pd/portaudio_v18/pa_unix_oss/pa_unix_solaris.c397
-rw-r--r--pd/portaudio_v18/pa_unix_oss/recplay.c114
8 files changed, 2227 insertions, 0 deletions
diff --git a/pd/portaudio_v18/pa_unix_oss/Makefile b/pd/portaudio_v18/pa_unix_oss/Makefile
new file mode 100644
index 00000000..3603816a
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/Makefile
@@ -0,0 +1,32 @@
+# Make PortAudio for Linux
+#
+# by Phil Burk
+#
+# To compile a test program, make a target with a .app suffix.
+# For example to compile "../pa_tests/pa_devs.c", enter:
+# gmake pa_devs.app
+#
+# To compile and execute a test program, make a target with a .run suffix.
+# For example to build and run "../pa_tests/pa_devs.c", enter:
+# gmake pa_devs.run
+
+VPATH = ../pa_common ../pa_tests
+CFLAGS = -g -Wall -I../pa_common
+CC = gcc
+
+PAOBJS = pa_lib.o pa_unix_oss.o pa_unix.o
+PAINC = portaudio.h pa_unix.h
+PALIBS = -lm -lpthread
+
+all: patest_sine.run
+
+%.app: %.o $(PAOBJS) $(PAINC) Makefile
+ gcc $(CFLAGS) $*.o $(PAOBJS) $(PALIBS) -o $@
+
+%.run: %.app
+ ./$*.app
+
+clean:
+ -rm *.app
+ -rm *.o
+
diff --git a/pd/portaudio_v18/pa_unix_oss/Makefile_freebsd b/pd/portaudio_v18/pa_unix_oss/Makefile_freebsd
new file mode 100644
index 00000000..fc8b14db
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/Makefile_freebsd
@@ -0,0 +1,36 @@
+# Make PortAudio for FreeBSD
+
+LIBS = -lm -pthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+PASRC = ../pa_common/pa_lib.c pa_freebsd.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work.
+#TESTC = $(PASRC) ../pa_tests/patest_sine.c
+TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
+#TESTC = $(PASRC) ../pa_tests/patest_stop.c
+#TESTC = $(PASRC) ../pa_tests/patest_sync.c
+#TESTC = $(PASRC) ../pa_tests/patest_pink.c
+#TESTC = $(PASRC) ../pa_tests/patest_leftright.c
+#TESTC = $(PASRC) ../pa_tests/patest_clip.c
+#TESTC = $(PASRC) ../pa_tests/patest_dither.c
+#TESTC = $(PASRC) ../pa_tests/pa_devs.c
+#TESTC = $(PASRC) ../pa_tests/patest_many.c
+#TESTC = $(PASRC) ../pa_tests/patest_record.c
+#TESTC = $(PASRC) ../pa_tests/patest_wire.c
+#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
+
+# Tests that do not yet work.
+
+TESTH = $(PAINC)
+
+all: patest
+
+patest: $(TESTC) $(TESTH) Makefile
+ gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+ ./patest
+
diff --git a/pd/portaudio_v18/pa_unix_oss/low_latency_tip.txt b/pd/portaudio_v18/pa_unix_oss/low_latency_tip.txt
new file mode 100644
index 00000000..2d982b79
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/low_latency_tip.txt
Binary files differ
diff --git a/pd/portaudio_v18/pa_unix_oss/pa_unix.c b/pd/portaudio_v18/pa_unix_oss/pa_unix.c
new file mode 100644
index 00000000..8d652b73
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/pa_unix.c
@@ -0,0 +1,1122 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * Linux OSS Implementation by douglas repetto and Phil Burk
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+Modification History
+ 1/2001 - Phil Burk - initial hack for Linux
+ 2/2001 - Douglas Repetto - many improvements, initial query support
+ 4/2/2001 - Phil - stop/abort thread control, separate in/out native buffers
+ 5/28/2001 - Phil - use pthread_create() instead of clone(). Thanks Stephen Brandon!
+ use pthread_join() after thread shutdown.
+ 5/29/2001 - Phil - query for multiple devices, multiple formats,
+ input mode and input+output mode working,
+ Pa_GetCPULoad() implemented.
+ PLB20010817 - Phil & Janos Haber - don't halt if test of sample rate fails.
+ SB20010904 - Stephen Brandon - mods needed for GNUSTEP and SndKit
+ JH20010905 - Janos Haber - FreeBSD mods
+ 2001-09-22 - Heiko - (i.e. Heiko Purnhagen <purnhage@tnt.uni-hannover.de> ;-)
+ added 24k and 16k to ratesToTry[]
+ fixed Pa_GetInternalDevice()
+ changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp
+ handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful
+ fixed Pa_StreamTime() for paqa_errs.c
+ fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat()
+ grep also for HP20010922 ...
+ PLB20010924 - Phil - merged Heiko's changes
+ removed sNumDevices and potential related bugs,
+ use getenv("PA_MIN_LATENCY_MSEC") to set desired latency,
+ simplify CPU Load calculation by comparing real-time to framesPerBuffer,
+ always close device when querying even if error occurs,
+ PLB20010927 - Phil - Improved negotiation for numChannels.
+ SG20011005 - Stewart Greenhill - set numChannels back to reasonable value after query.
+ DH20010115 - David Herring - fixed uninitialized handle.
+
+ DM20020218 - Dominic Mazzoni - Try to open in nonblocking mode first, in case
+ the device is already open. New implementation of
+ Pa_StreamTime that uses SNDCTL_DSP_GETOPTR but
+ uses our own counter to avoid wraparound.
+ PLB20020222 - Phil Burk - Added WatchDog proc if audio running at high priority.
+ Check error return from read() and write().
+ Check CPU endianness instead of assuming Little Endian.
+ 20020621 - pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by
+ Augustus Saunders. Return values from usleep() ignored by Sam Bayer
+ because not cross-platform compatible (at least until we get configure
+ going). Pa_SetupDeviceFormat split into input and output sides to
+ reflect capabilities of Solaris.
+
+ 20030206 - Martin Rohrbach - various mods for Solaris
+
+ 20030410 - Bjorn Dittmer-Roche - fixed numerous problems associated with pthread_t
+
+ 20030630 - Thomas Richter - eliminated unused variable warnings.
+
+TODO
+O- put semaphore lock around shared data?
+O- handle native formats better
+O- handle stereo-only device better ???
+O- what if input and output of a device capabilities differ (e.g. es1371) ???
+*/
+
+
+#include "pa_unix.h"
+
+typedef void *(*pthread_function_t)(void *);
+
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static internalPortAudioDevice *sDeviceList = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sPaHostError = 0;
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ gettimeofday( &pahsc->pahsc_EntryTime, NULL );
+}
+
+static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
+{
+ long secs = timeA->tv_sec - timeB->tv_sec;
+ long usecs = secs * 1000000;
+ usecs += (timeA->tv_usec - timeB->tv_usec);
+ return usecs;
+}
+
+/******************************************************************************
+** Measure fractional CPU load based on real-time it took to calculate
+** buffers worth of output.
+*/
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ struct timeval currentTime;
+ long usecsElapsed;
+ double newUsage;
+
+#define LOWPASS_COEFFICIENT_0 (0.95)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+
+ if( gettimeofday( &currentTime, NULL ) == 0 )
+ {
+ usecsElapsed = SubtractTime_AminusB( &currentTime, &pahsc->pahsc_EntryTime );
+ /* Use inverse because it is faster than the divide. */
+ newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
+
+ past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+ (LOWPASS_COEFFICIENT_1 * newUsage);
+
+ }
+}
+/****************************************** END CPU UTILIZATION *******/
+
+/*********************************************************************
+ * Determines the number of available devices by trying to open
+ * each "/dev/dsp#" or "/dsp/audio#" in order until it fails.
+ * Add each working device to a singly linked list of devices.
+ */
+PaError Pa_QueryDevices( void )
+{
+ internalPortAudioDevice *pad, *lastPad;
+ int go = 1;
+ int numDevices = 0;
+ PaError testResult;
+ PaError result = paNoError;
+ char *envdev;
+
+ sDefaultInputDeviceID = paNoDevice;
+ sDefaultOutputDeviceID = paNoDevice;
+
+ lastPad = NULL;
+
+ while( go )
+ {
+ /* Allocate structure to hold device info. */
+ pad = (internalPortAudioDevice *)
+ PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
+ if( pad == NULL ) return paInsufficientMemory;
+ memset( pad, 0, sizeof(internalPortAudioDevice) );
+
+ /* Build name for device. */
+ if( numDevices == 0 )
+ {
+ sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
+ }
+ else
+ {
+ sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
+ }
+
+ DBUG(("Try device %s\n", pad->pad_DeviceName ));
+ testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
+ DBUG(("Pa_QueryDevice returned %d\n", testResult ));
+ if( testResult != paNoError )
+ {
+ if( lastPad == NULL )
+ {
+ result = testResult; /* No good devices! */
+ }
+ go = 0;
+ PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+ }
+ else
+ {
+ numDevices += 1;
+ /* Add to linked list of devices. */
+ if( lastPad )
+ {
+ lastPad->pad_Next = pad;
+ }
+ else
+ {
+ sDeviceList = pad; /* First element in linked list. */
+ }
+ lastPad = pad;
+ }
+ }
+
+ /* I'm sitting at a SunRay1 and I neither have /dev/audio# nor /dev/dsp#.
+ Instead, the correct audio device is stored in the environment variable
+ AUDIODEV and/or UTAUDIODEV, so check these devices as well if we haven't
+ checked them yet above - MR */
+
+ DBUG(("Checking for AUDIODEV and UTAUDIODEV\n"));
+ envdev = getenv("AUDIODEV");
+ if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE)) {
+ result = paNoError;
+
+ /* Allocate structure to hold device info. */
+ pad = (internalPortAudioDevice *)
+ PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
+ if( pad == NULL ) return paInsufficientMemory;
+ memset( pad, 0, sizeof(internalPortAudioDevice) );
+
+ /* Build name for device. */
+ strcpy(pad->pad_DeviceName, envdev);
+
+ DBUG(("Try device %s\n", pad->pad_DeviceName ));
+ testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
+ DBUG(("Pa_QueryDevice returned %d\n", testResult ));
+ if( testResult != paNoError )
+ {
+ if( lastPad == NULL )
+ {
+ result = testResult; /* No good devices! */
+ }
+ PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+ }
+ else
+ {
+ numDevices += 1;
+ /* Add to linked list of devices. */
+ if( lastPad )
+ {
+ lastPad->pad_Next = pad;
+ }
+ else
+ {
+ sDeviceList = pad; /* First element in linked list. */
+ }
+ lastPad = pad;
+ }
+ }
+
+ envdev = getenv("UTAUDIODEV");
+ if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE) && getenv("AUDIODEV") != NULL && strcmp(envdev, getenv("AUDIODEV"))) {
+ result = paNoError;
+
+ /* Allocate structure to hold device info. */
+ pad = (internalPortAudioDevice *)
+ PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
+ if( pad == NULL ) return paInsufficientMemory;
+ memset( pad, 0, sizeof(internalPortAudioDevice) );
+
+ /* Build name for device. */
+ strcpy(pad->pad_DeviceName, envdev);
+
+ DBUG(("Try device %s\n", pad->pad_DeviceName ));
+ testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
+ DBUG(("Pa_QueryDevice returned %d\n", testResult ));
+ if( testResult != paNoError )
+ {
+ if( lastPad == NULL )
+ {
+ result = testResult; /* No good devices! */
+ }
+ PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+ }
+ else
+ {
+ numDevices += 1;
+ /* Add to linked list of devices. */
+ if( lastPad )
+ {
+ lastPad->pad_Next = pad;
+ }
+ else
+ {
+ sDeviceList = pad; /* First element in linked list. */
+ }
+ lastPad = pad;
+ }
+ }
+
+ return result;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ int numDevices = 0;
+ internalPortAudioDevice *pad;
+
+ if( sDeviceList == NULL ) Pa_Initialize();
+ /* Count devices in list. */
+ pad = sDeviceList;
+ while( pad != NULL )
+ {
+ pad = pad->pad_Next;
+ numDevices++;
+ }
+
+ return numDevices;
+}
+
+/*************************************************************************/
+internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
+{
+ internalPortAudioDevice *pad;
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ pad = sDeviceList;
+ while( id > 0 )
+ {
+ pad = pad->pad_Next;
+ id--;
+ }
+ return pad;
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ internalPortAudioDevice *pad;
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ pad = Pa_GetInternalDevice( id );
+ return &pad->pad_Info ;
+}
+
+static PaError Pa_MaybeQueryDevices( void )
+{
+ if( sDeviceList == NULL )
+ {
+ return Pa_QueryDevices();
+ }
+ return 0;
+}
+
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ /* return paNoDevice; */
+ return 0;
+}
+
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ return 0;
+}
+
+/**********************************************************************
+** Make sure that we have queried the device capabilities.
+*/
+
+PaError PaHost_Init( void )
+{
+ return Pa_MaybeQueryDevices();
+}
+
+/*******************************************************************************************
+ * The ol' Canary in a Coal Mine trick.
+ * Just update the time periodically.
+ * Runs at low priority so if audio thread runs wild, this thread will get starved
+ * and the watchdog will detect it.
+ */
+
+#define SCHEDULER_POLICY SCHED_RR
+#define WATCHDOG_MAX_SECONDS (3)
+#define WATCHDOG_INTERVAL_USEC (1000000)
+
+static int PaHost_CanaryProc( PaHostSoundControl *pahsc )
+{
+ int result = 0;
+
+#ifdef GNUSTEP
+ GSRegisterCurrentThread(); /* SB20010904 */
+#endif
+
+ while( pahsc->pahsc_CanaryRun) {
+ usleep( WATCHDOG_INTERVAL_USEC );
+ gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
+ }
+
+ DBUG(("PaHost_CanaryProc: exiting.\n"));
+
+#ifdef GNUSTEP
+ GSUnregisterCurrentThread(); /* SB20010904 */
+#endif
+
+ return result;
+}
+
+/*******************************************************************************************
+ * Monitor audio thread and lower its it if it hogs the CPU.
+ * To prevent getting killed, the audio thread must update a
+ * variable with a timer value.
+ * If the value is not recent enough, then the
+ * thread will get killed.
+ */
+
+static PaError PaHost_WatchDogProc( PaHostSoundControl *pahsc )
+{
+ struct sched_param schp = { 0 };
+ int maxPri;
+
+#ifdef GNUSTEP
+ GSRegisterCurrentThread(); /* SB20010904 */
+#endif
+
+/* Run at a priority level above audio thread so we can still run if it hangs. */
+/* Rise more than 1 because of rumored off-by-one scheduler bugs. */
+ schp.sched_priority = pahsc->pahsc_AudioPriority + 4;
+ maxPri = sched_get_priority_max(SCHEDULER_POLICY);
+ if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri;
+
+ if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
+ {
+ ERR_RPT(("PaHost_WatchDogProc: cannot set watch dog priority!\n"));
+ goto killAudio;
+ }
+
+ /* Compare watchdog time with audio and canary thread times. */
+ /* Sleep for a while or until thread cancelled. */
+ while( pahsc->pahsc_WatchDogRun )
+ {
+
+ int delta;
+ struct timeval currentTime;
+
+ usleep( WATCHDOG_INTERVAL_USEC );
+ gettimeofday( &currentTime, NULL );
+
+ /* If audio thread is not advancing, then it must be hung so kill it. */
+ delta = currentTime.tv_sec - pahsc->pahsc_EntryTime.tv_sec;
+ DBUG(("PaHost_WatchDogProc: audio delta = %d\n", delta ));
+ if( delta > WATCHDOG_MAX_SECONDS )
+ {
+ goto killAudio;
+ }
+
+ /* If canary died, then lower audio priority and halt canary. */
+ delta = currentTime.tv_sec - pahsc->pahsc_CanaryTime.tv_sec;
+ if( delta > WATCHDOG_MAX_SECONDS )
+ {
+ ERR_RPT(("PaHost_WatchDogProc: canary died!\n"));
+ goto lowerAudio;
+ }
+ }
+
+ DBUG(("PaHost_WatchDogProc: exiting.\n"));
+#ifdef GNUSTEP
+ GSUnregisterCurrentThread(); /* SB20010904 */
+#endif
+ return 0;
+
+lowerAudio:
+ {
+ struct sched_param schat = { 0 };
+ if( sched_setscheduler(pahsc->pahsc_AudioThreadPID, SCHED_OTHER, &schat) != 0)
+ {
+ ERR_RPT(("PaHost_WatchDogProc: failed to lower audio priority. errno = %d\n", errno ));
+ /* Fall through into killing audio thread. */
+ }
+ else
+ {
+ ERR_RPT(("PaHost_WatchDogProc: lowered audio priority to prevent hogging of CPU.\n"));
+ goto cleanup;
+ }
+ }
+
+killAudio:
+ ERR_RPT(("PaHost_WatchDogProc: killing hung audio thread!\n"));
+ pthread_kill( pahsc->pahsc_AudioThread, SIGKILL );
+
+cleanup:
+ pahsc->pahsc_CanaryRun = 0;
+ DBUG(("PaHost_WatchDogProc: cancel Canary\n"));
+ pthread_cancel( pahsc->pahsc_CanaryThread );
+ DBUG(("PaHost_WatchDogProc: join Canary\n"));
+ pthread_join( pahsc->pahsc_CanaryThread, NULL );
+ DBUG(("PaHost_WatchDogProc: forget Canary\n"));
+ pahsc->pahsc_IsCanaryThreadValid = 0;
+
+#ifdef GNUSTEP
+ GSUnregisterCurrentThread(); /* SB20010904 */
+#endif
+ return 0;
+}
+
+/*******************************************************************************************/
+static void PaHost_StopWatchDog( PaHostSoundControl *pahsc )
+{
+/* Cancel WatchDog thread if there is one. */
+ if( pahsc->pahsc_IsWatchDogThreadValid )
+ {
+ pahsc->pahsc_WatchDogRun = 0;
+ DBUG(("PaHost_StopWatchDog: cancel WatchDog\n"));
+ pthread_cancel( pahsc->pahsc_WatchDogThread );
+ pthread_join( pahsc->pahsc_WatchDogThread, NULL );
+ pahsc->pahsc_IsWatchDogThreadValid = 0;
+ }
+/* Cancel Canary thread if there is one. */
+ if( pahsc->pahsc_IsCanaryThreadValid )
+ {
+ pahsc->pahsc_CanaryRun = 0;
+ DBUG(("PaHost_StopWatchDog: cancel Canary\n"));
+ pthread_cancel( pahsc->pahsc_CanaryThread );
+ DBUG(("PaHost_StopWatchDog: join Canary\n"));
+ pthread_join( pahsc->pahsc_CanaryThread, NULL );
+ pahsc->pahsc_IsCanaryThreadValid = 0;
+ }
+}
+
+/*******************************************************************************************/
+static PaError PaHost_StartWatchDog( PaHostSoundControl *pahsc )
+{
+ int hres;
+ PaError result = 0;
+
+ /* The watch dog watches for these timer updates */
+ gettimeofday( &pahsc->pahsc_EntryTime, NULL );
+ gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
+
+ /* Launch a canary thread to detect priority abuse. */
+ pahsc->pahsc_CanaryRun = 1;
+ hres = pthread_create(&(pahsc->pahsc_CanaryThread),
+ NULL /*pthread_attr_t * attr*/,
+ (pthread_function_t)PaHost_CanaryProc, pahsc);
+ if( hres != 0 )
+ {
+ pahsc->pahsc_IsCanaryThreadValid = 0;
+ result = paHostError;
+ sPaHostError = hres;
+ goto error;
+ }
+ pahsc->pahsc_IsCanaryThreadValid = 1;
+
+ /* Launch a watchdog thread to prevent runaway audio thread. */
+ pahsc->pahsc_WatchDogRun = 1;
+ hres = pthread_create(&(pahsc->pahsc_WatchDogThread),
+ NULL /*pthread_attr_t * attr*/,
+ (pthread_function_t)PaHost_WatchDogProc, pahsc);
+ if( hres != 0 )
+ {
+ pahsc->pahsc_IsWatchDogThreadValid = 0;
+ result = paHostError;
+ sPaHostError = hres;
+ goto error;
+ }
+ pahsc->pahsc_IsWatchDogThreadValid = 1;
+ return result;
+
+error:
+ PaHost_StopWatchDog( pahsc );
+ return result;
+}
+
+/*******************************************************************************************
+ * Bump priority of audio thread if running with superuser priveledges.
+ * if priority bumped then launch a watchdog.
+ */
+static PaError PaHost_BoostPriority( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ struct sched_param schp = { 0 };
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+
+ pahsc->pahsc_AudioThreadPID = getpid();
+ DBUG(("PaHost_BoostPriority: audio PID = %d\n", pahsc->pahsc_AudioThreadPID ));
+
+ /* Choose a priority in the middle of the range. */
+ pahsc->pahsc_AudioPriority = (sched_get_priority_max(SCHEDULER_POLICY) -
+ sched_get_priority_min(SCHEDULER_POLICY)) / 2;
+ schp.sched_priority = pahsc->pahsc_AudioPriority;
+
+ if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
+ {
+ DBUG(("PortAudio: only superuser can use real-time priority.\n"));
+ }
+ else
+ {
+ DBUG(("PortAudio: audio callback priority set to level %d!\n", schp.sched_priority));
+ /* We are running at high priority so we should have a watchdog in case audio goes wild. */
+ result = PaHost_StartWatchDog( pahsc );
+ }
+
+ return result;
+}
+
+/*******************************************************************************************/
+static PaError Pa_AudioThreadProc( internalPortAudioStream *past )
+{
+ PaError result;
+ PaHostSoundControl *pahsc;
+ ssize_t bytes_read, bytes_written;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+
+#ifdef GNUSTEP
+ GSRegisterCurrentThread(); /* SB20010904 */
+#endif
+
+ result = PaHost_BoostPriority( past );
+ if( result < 0 ) goto error;
+
+ past->past_IsActive = 1;
+ DBUG(("entering thread.\n"));
+
+ while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
+ {
+ /* Read data from device */
+ if(pahsc->pahsc_NativeInputBuffer)
+ {
+ unsigned int totalread = 0;
+ DBUG(("Pa_AudioThreadProc: attempt to read %d bytes\n", pahsc->pahsc_BytesPerInputBuffer));
+ do
+ {
+ bytes_read = read(pahsc->pahsc_InputHandle,
+ (char *)pahsc->pahsc_NativeInputBuffer + totalread,
+ pahsc->pahsc_BytesPerInputBuffer - totalread);
+
+ if (bytes_read < 0)
+ {
+ ERR_RPT(("PortAudio: read interrupted!\n"));
+ break;
+ }
+
+ totalread += bytes_read;
+ } while( totalread < pahsc->pahsc_BytesPerInputBuffer);
+ }
+
+ /* Convert 16 bit native data to user data and call user routine. */
+ DBUG(("converting...\n"));
+ Pa_StartUsageCalculation( past );
+ result = Pa_CallConvertInt16( past,
+ pahsc->pahsc_NativeInputBuffer,
+ pahsc->pahsc_NativeOutputBuffer );
+ Pa_EndUsageCalculation( past );
+ if( result != 0)
+ {
+ DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
+ result));
+ break;
+ }
+
+ /* Write data to device. */
+ if( pahsc->pahsc_NativeOutputBuffer )
+ {
+ unsigned int totalwritten = 0;
+ do
+ {
+ bytes_written = write(pahsc->pahsc_OutputHandle,
+ (void *)pahsc->pahsc_NativeOutputBuffer,
+ pahsc->pahsc_BytesPerOutputBuffer);
+ if( bytes_written < 0 )
+ {
+ ERR_RPT(("PortAudio: write interrupted!"));
+ break;
+ }
+
+ totalwritten += bytes_written;
+ } while( totalwritten < pahsc->pahsc_BytesPerOutputBuffer);
+ }
+
+ Pa_UpdateStreamTime(pahsc);
+ }
+ DBUG(("Pa_AudioThreadProc: left audio loop.\n"));
+
+ past->past_IsActive = 0;
+ PaHost_StopWatchDog( pahsc );
+
+error:
+ DBUG(("leaving audio thread.\n"));
+#ifdef GNUSTEP
+ GSUnregisterCurrentThread(); /* SB20010904 */
+#endif
+ return result;
+}
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the cshrc file.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
+{
+ int minBuffers;
+ int minLatencyMsec = MIN_LATENCY_MSEC;
+ char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
+ if( minLatencyText != NULL )
+ {
+ PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
+ minLatencyMsec = atoi( minLatencyText );
+ if( minLatencyMsec < 1 ) minLatencyMsec = 1;
+ else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
+ }
+
+ minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
+ if( minBuffers < 2 ) minBuffers = 2;
+ return minBuffers;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ unsigned int minNumBuffers;
+ internalPortAudioDevice *pad;
+ DBUG(("PaHost_OpenStream() called.\n" ));
+
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
+ if( pahsc == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+
+ pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */
+ pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
+ pahsc->pahsc_IsAudioThreadValid = 0;
+ pahsc->pahsc_IsWatchDogThreadValid = 0;
+
+ /* Allocate native buffers. */
+ pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
+ past->past_NumInputChannels * sizeof(short);
+ if( past->past_NumInputChannels > 0)
+ {
+ pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
+ if( pahsc->pahsc_NativeInputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+ pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer *
+ past->past_NumOutputChannels * sizeof(short);
+ if( past->past_NumOutputChannels > 0)
+ {
+ pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
+ if( pahsc->pahsc_NativeOutputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
+ minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+
+ pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
+ DBUG(("past_SampleRate = %g\n", past->past_SampleRate ));
+ DBUG(("past_FramesPerUserBuffer = %d\n", past->past_FramesPerUserBuffer ));
+ DBUG(("pahsc_InverseMicrosPerBuffer = %g\n", pahsc->pahsc_InverseMicrosPerBuffer ));
+
+ /* ------------------------- OPEN DEVICE -----------------------*/
+
+ /* just output */
+ if (past->past_OutputDeviceID == past->past_InputDeviceID)
+ {
+
+ if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) )
+ {
+ pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+ DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
+
+ /* dmazzoni: test it first in nonblocking mode to
+ make sure the device is not busy */
+ pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDWR|O_NONBLOCK);
+ if(pahsc->pahsc_InputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ close(pahsc->pahsc_InputHandle);
+
+ pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle =
+ open(pad->pad_DeviceName,O_RDWR);
+ if(pahsc->pahsc_InputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ Pa_SetLatency( pahsc->pahsc_OutputHandle,
+ past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+ past->past_NumOutputChannels );
+ result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
+ past->past_NumOutputChannels, (int)past->past_SampleRate );
+ }
+ }
+ else
+ {
+ if (past->past_NumOutputChannels > 0)
+ {
+ pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+ DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName ));
+ /* dmazzoni: test it first in nonblocking mode to
+ make sure the device is not busy */
+ pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY|O_NONBLOCK);
+ if(pahsc->pahsc_OutputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ close(pahsc->pahsc_OutputHandle);
+
+ pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY);
+ if(pahsc->pahsc_OutputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ Pa_SetLatency( pahsc->pahsc_OutputHandle,
+ past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+ past->past_NumOutputChannels );
+ result = Pa_SetupOutputDeviceFormat( pahsc->pahsc_OutputHandle,
+ past->past_NumOutputChannels, (int)past->past_SampleRate );
+ }
+
+ if (past->past_NumInputChannels > 0)
+ {
+ pad = Pa_GetInternalDevice( past->past_InputDeviceID );
+ DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName ));
+ /* dmazzoni: test it first in nonblocking mode to
+ make sure the device is not busy */
+ pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY|O_NONBLOCK);
+ if(pahsc->pahsc_InputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ close(pahsc->pahsc_InputHandle);
+
+ pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY);
+ if(pahsc->pahsc_InputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */
+ past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+ past->past_NumInputChannels );
+ result = Pa_SetupInputDeviceFormat( pahsc->pahsc_InputHandle,
+ past->past_NumInputChannels, (int)past->past_SampleRate );
+ }
+ }
+
+
+ DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result ));
+ return result;
+
+error:
+ ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result ));
+ PaHost_CloseStream( past );
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ past = past; /* unused */
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ past = past; /* unused */
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ int hres;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ past->past_StopSoon = 0;
+ past->past_StopNow = 0;
+ past->past_IsActive = 1;
+
+ /* Use pthread_create() instead of __clone() because:
+ * - pthread_create also works for other UNIX systems like Solaris,
+ * - the Java HotSpot VM crashes in pthread_setcanceltype() when using __clone()
+ */
+ hres = pthread_create(&(pahsc->pahsc_AudioThread),
+ NULL /*pthread_attr_t * attr*/,
+ (pthread_function_t)Pa_AudioThreadProc, past);
+ if( hres != 0 )
+ {
+ result = paHostError;
+ sPaHostError = hres;
+ pahsc->pahsc_IsAudioThreadValid = 0;
+ goto error;
+ }
+ pahsc->pahsc_IsAudioThreadValid = 1;
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ int hres;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ if( pahsc == NULL ) return paNoError;
+
+ /* Tell background thread to stop generating more data and to let current data play out. */
+ past->past_StopSoon = 1;
+ /* If aborting, tell background thread to stop NOW! */
+ if( abort ) past->past_StopNow = 1;
+
+ /* Join thread to recover memory resources. */
+ if( pahsc->pahsc_IsAudioThreadValid )
+ {
+ /* This check is needed for GNUSTEP - SB20010904 */
+ if ( !pthread_equal( pahsc->pahsc_AudioThread, pthread_self() ) )
+ {
+ hres = pthread_join( pahsc->pahsc_AudioThread, NULL );
+ }
+ else
+ {
+ DBUG(("Play thread was stopped from itself - can't do pthread_join()\n"));
+ hres = 0;
+ }
+
+ if( hres != 0 )
+ {
+ result = paHostError;
+ sPaHostError = hres;
+ }
+ pahsc->pahsc_IsAudioThreadValid = 0;
+ }
+
+ past->past_IsActive = 0;
+
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ past = past; /* unused */
+ abort = abort; /* unused */
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ past = past; /* unused */
+ abort = abort; /* unused */
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID )
+ {
+ int err = 0;
+ DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n",
+ pahsc->pahsc_OutputHandle ));
+
+ Pa_FlushStream(pahsc->pahsc_OutputHandle);
+
+ err = close(pahsc->pahsc_OutputHandle);
+ if( err < 0 )
+ {
+ ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n"));
+ }
+ }
+
+ if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) &&
+ (pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) )
+ {
+ int err = 0;
+ DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n",
+ pahsc->pahsc_InputHandle ));
+
+ Pa_FlushStream(pahsc->pahsc_InputHandle);
+
+ err = close(pahsc->pahsc_InputHandle);
+ if( err < 0 )
+ {
+ ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n"));
+ }
+ }
+ pahsc->pahsc_OutputHandle = BAD_DEVICE_ID;
+ pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
+
+ if( pahsc->pahsc_NativeInputBuffer )
+ {
+ free( pahsc->pahsc_NativeInputBuffer );
+ pahsc->pahsc_NativeInputBuffer = NULL;
+ }
+ if( pahsc->pahsc_NativeOutputBuffer )
+ {
+ free( pahsc->pahsc_NativeOutputBuffer );
+ pahsc->pahsc_NativeOutputBuffer = NULL;
+ }
+
+ free( pahsc );
+ past->past_DeviceData = NULL;
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+ /* Free all of the linked devices. */
+ internalPortAudioDevice *pad, *nextPad;
+ pad = sDeviceList;
+ while( pad != NULL )
+ {
+ nextPad = pad->pad_Next;
+ DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName ));
+ PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+ pad = nextPad;
+ }
+ sDeviceList = NULL;
+ return 0;
+}
+
+/*************************************************************************
+ * Sleep for the requested number of milliseconds.
+ */
+void Pa_Sleep( long msec )
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select( 0, NULL, NULL, NULL, &timeout );
+#else
+ long usecs = msec * 1000;
+ usleep( usecs );
+#endif
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical, wired, non-virtual memory? */
+ if( addr != NULL ) memset( addr, 0, numBytes );
+ return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ numBytes = numBytes; /* unused */
+ if( addr != NULL ) free( addr );
+}
+
+
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) (past->past_IsActive != 0);
+}
+
+/***********************************************************************/
+long Pa_GetHostError( void )
+{
+ return (long) sPaHostError;
+}
diff --git a/pd/portaudio_v18/pa_unix_oss/pa_unix.h b/pd/portaudio_v18/pa_unix_oss/pa_unix.h
new file mode 100644
index 00000000..55b16d50
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/pa_unix.h
@@ -0,0 +1,141 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * Linux OSS Implementation by douglas repetto and Phil Burk
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* Modification history:
+ 20020621: pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by
+ Augustus Saunders. See pa_unix.c for previous history. */
+
+/*
+ PROPOSED - should we add this to "portaudio.h". Problem with
+ Pa_QueryDevice() not having same driver name os Pa_OpenStream().
+
+ A PaDriverInfo structure can be passed to the underlying device
+ on the Pa_OpenStream() call. The contents and interpretation of
+ the structure is determined by the PA implementation.
+*/
+typedef struct PaDriverInfo /* PROPOSED */
+{
+ /* Size of structure. Allows driver to extend the structure without breaking existing applications. */
+ int size;
+ /* Can be used to request a specific device name. */
+ const char *name;
+ unsigned long data;
+}
+PaDriverInfo;
+
+#include <stdio.h>
+#include <stdlib.h>
+//#include <malloc.h>
+#include <memory.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sched.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+#define BAD_DEVICE_ID (-1)
+
+#define MIN_LATENCY_MSEC (100)
+#define MIN_TIMEOUT_MSEC (100)
+#define MAX_TIMEOUT_MSEC (1000)
+
+/************************************************* Definitions ********/
+#ifdef __linux__
+ #define DEVICE_NAME_BASE "/dev/dsp"
+#else
+ #define DEVICE_NAME_BASE "/dev/audio"
+#endif
+
+#define MAX_CHARS_DEVNAME (32)
+#define MAX_SAMPLE_RATES (10)
+typedef struct internalPortAudioDevice
+{
+ struct internalPortAudioDevice *pad_Next; /* Singly linked list. */
+ double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
+ char pad_DeviceName[MAX_CHARS_DEVNAME];
+ PaDeviceInfo pad_Info;
+}
+internalPortAudioDevice;
+
+/* Define structure to contain all OSS and Linux specific data. */
+typedef struct PaHostSoundControl
+{
+ int pahsc_OutputHandle;
+ int pahsc_InputHandle;
+ int pahsc_AudioPriority; /* priority of background audio thread */
+ pthread_t pahsc_AudioThread; /* background audio thread */
+ int pahsc_IsAudioThreadValid; /* Is pahsc_AudioThread valid?*/ pid_t pahsc_AudioThreadPID; /* background audio thread */
+ pthread_t pahsc_WatchDogThread; /* highest priority thread that protects system */
+ int pahsc_IsWatchDogThreadValid; /* Is pahsc_WatchDogThread valid?*/
+ int pahsc_WatchDogRun; /* Ask WatchDog to stop. */
+ pthread_t pahsc_CanaryThread; /* low priority thread that detects abuse by audio */
+ int pahsc_IsCanaryThreadValid; /* Is pahsc_CanaryThread valid?*/
+ struct timeval pahsc_CanaryTime;
+ int pahsc_CanaryRun; /* Ask Canary to stop. */
+ short *pahsc_NativeInputBuffer;
+ short *pahsc_NativeOutputBuffer;
+ unsigned int pahsc_BytesPerInputBuffer; /* native buffer size in bytes */
+ unsigned int pahsc_BytesPerOutputBuffer; /* native buffer size in bytes */
+ /* For measuring CPU utilization. */
+ struct timeval pahsc_EntryTime;
+ double pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
+
+ /* For calculating stream time */
+ int pahsc_LastPosPtr;
+ double pahsc_LastStreamBytes;
+}
+PaHostSoundControl;
+
+/************************************************* Prototypes **********/
+
+internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
+PaError Pa_QueryDevices( void );
+PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad );
+PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate );
+PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate );
+PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate );
+void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame );
+void Pa_UpdateStreamTime(PaHostSoundControl *pahsc);
+int Pa_FlushStream(int devHandle);
diff --git a/pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c b/pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c
new file mode 100644
index 00000000..386cd75b
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c
@@ -0,0 +1,385 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * Linux OSS Implementation by douglas repetto and Phil Burk
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* Modification history:
+ 20020621: pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by
+ Augustus Saunders. See pa_unix.c for previous history. Pa_FlushStream
+ added by Augustus Saunders for Solaris compatibility.
+ PLB20021018 - Fill device info table with actual sample rates instead of wished for rates.
+ - Allow stream to open if sample rate within 10% of desired rate.
+ 20030630 - Thomas Richter - eliminated unused variable warnings.
+*/
+
+#include "pa_unix.h"
+
+#ifdef __linux__
+#include <linux/soundcard.h>
+#else
+#include <machine/soundcard.h> /* JH20010905 */
+#endif
+
+
+#ifndef AFMT_S16_NE
+#define AFMT_S16_NE Get_AFMT_S16_NE()
+/*********************************************************************
+ * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
+ * PowerPC is Big Endian. X86 is Little Endian.
+ */
+int Get_AFMT_S16_NE( void )
+{
+ long testData = 1;
+ char *ptr = (char *) &testData;
+ int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
+ return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
+}
+#endif /* AFMT_S16_NE */
+
+
+/*********************************************************************
+ * Try to open the named device.
+ * If it opens, try to set various rates and formats and fill in
+ * the device info structure.
+ */
+PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad )
+{
+ int result = paHostError;
+ int tempDevHandle;
+ int numChannels, maxNumChannels;
+ int format;
+ int numSampleRates;
+ int sampleRate;
+ int numRatesToTry;
+ int lastRate;
+ int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
+ int i;
+
+ /* douglas:
+ we have to do this querying in a slightly different order. apparently
+ some sound cards will give you different info based on their settings.
+ e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+ the correct order for OSS is: format, channels, sample rate
+
+ */
+ if ( (tempDevHandle = open(deviceName,O_WRONLY|O_NONBLOCK)) == -1 )
+ {
+ DBUG(("Pa_QueryDevice: could not open %s\n", deviceName ));
+ return paHostError;
+ }
+
+ /* Ask OSS what formats are supported by the hardware. */
+ pad->pad_Info.nativeSampleFormats = 0;
+
+ if (ioctl(tempDevHandle, SNDCTL_DSP_GETFMTS, &format) == -1)
+ {
+ ERR_RPT(("Pa_QueryDevice: could not get format info\n" ));
+ goto error;
+ }
+ if( format & AFMT_U8 ) pad->pad_Info.nativeSampleFormats |= paUInt8;
+ if( format & AFMT_S16_NE ) pad->pad_Info.nativeSampleFormats |= paInt16;
+
+ /* Negotiate for the maximum number of channels for this device. PLB20010927
+ * Consider up to 16 as the upper number of channels.
+ * Variable numChannels should contain the actual upper limit after the call.
+ * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+ */
+ maxNumChannels = 0;
+ for( numChannels = 1; numChannels <= 16; numChannels++ )
+ {
+ int temp = numChannels;
+ DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
+ if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 )
+ {
+ /* ioctl() failed so bail out if we already have stereo */
+ if( numChannels > 2 ) break;
+ }
+ else
+ {
+ /* ioctl() worked but bail out if it does not support numChannels.
+ * We don't want to leave gaps in the numChannels supported.
+ */
+ if( (numChannels > 2) && (temp != numChannels) ) break;
+ DBUG(("Pa_QueryDevice: temp = %d\n", temp ))
+ if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */
+ }
+ }
+
+ /* The above negotiation may fail for an old driver so try this older technique. */
+ if( maxNumChannels < 1 )
+ {
+ int stereo = 1;
+ if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0)
+ {
+ maxNumChannels = 1;
+ }
+ else
+ {
+ maxNumChannels = (stereo) ? 2 : 1;
+ }
+ DBUG(("Pa_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels ))
+ }
+
+ pad->pad_Info.maxOutputChannels = maxNumChannels;
+ DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
+
+ /* During channel negotiation, the last ioctl() may have failed. This can
+ * also cause sample rate negotiation to fail. Hence the following, to return
+ * to a supported number of channels. SG20011005 */
+ {
+ int temp = maxNumChannels;
+ if( temp > 2 ) temp = 2; /* use most reasonable default value */
+ ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp);
+ }
+
+ /* FIXME - for now, assume maxInputChannels = maxOutputChannels.
+ * Eventually do separate queries for O_WRONLY and O_RDONLY
+ */
+ pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels;
+
+ DBUG(("Pa_QueryDevice: maxInputChannels = %d\n",
+ pad->pad_Info.maxInputChannels))
+
+
+ /* Determine available sample rates by trying each one and seeing result.
+ * OSS often supports funky rates such as 44188 instead of 44100!
+ */
+ numSampleRates = 0;
+ lastRate = 0;
+ numRatesToTry = sizeof(ratesToTry)/sizeof(int);
+ for (i = 0; i < numRatesToTry; i++)
+ {
+ sampleRate = ratesToTry[i];
+
+ if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) >= 0 ) /* PLB20010817 */
+ {
+ /* Use whatever rate OSS tells us. PLB20021018 */
+ if (sampleRate != lastRate)
+ {
+ DBUG(("Pa_QueryDevice: adding sample rate: %d\n", sampleRate))
+ pad->pad_SampleRates[numSampleRates] = (float)sampleRate;
+ numSampleRates++;
+ lastRate = sampleRate;
+ }
+ else
+ {
+ DBUG(("Pa_QueryDevice: dang - got sample rate %d again!\n", sampleRate))
+ }
+ }
+ }
+
+ DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates))
+ if (numSampleRates==0) /* HP20010922 */
+ {
+ /* Desparate attempt to keep running even though no good rates found! */
+ ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed). Force 44100 Hz\n" ));
+ pad->pad_SampleRates[numSampleRates++] = 44100;
+ }
+
+ pad->pad_Info.numSampleRates = numSampleRates;
+ pad->pad_Info.sampleRates = pad->pad_SampleRates; /* use pointer to embedded array */
+
+ pad->pad_Info.name = deviceName;
+
+ result = paNoError;
+
+error:
+ /* We MUST close the handle here or we won't be able to reopen it later!!! */
+ close(tempDevHandle);
+
+ return result;
+}
+
+/*******************************************************************************************/
+PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ PaError result = paNoError;
+ int tmp;
+
+ /* Set format, channels, and rate in this order to keep OSS happy. */
+ /* Set data format. FIXME - handle more native formats. */
+ tmp = AFMT_S16_NE;
+ if( ioctl(devHandle,SNDCTL_DSP_SETFMT,&tmp) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SETFMT\n" ));
+ return paHostError;
+ }
+ if( tmp != AFMT_S16_NE )
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: HW does not support AFMT_S16_NE\n" ));
+ return paHostError;
+ }
+
+
+ /* Set number of channels. */
+ tmp = numChannels;
+ if (ioctl(devHandle, SNDCTL_DSP_CHANNELS, &numChannels) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_CHANNELS\n" ));
+ return paHostError;
+ }
+ if( tmp != numChannels)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d channels\n", numChannels ));
+ return paHostError;
+ }
+
+ /* Set playing frequency. */
+ tmp = sampleRate;
+ if( ioctl(devHandle,SNDCTL_DSP_SPEED,&tmp) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SPEED\n" ));
+ return paHostError;
+ }
+ else if( tmp != sampleRate )
+ {
+ int percentError = abs( (100 * (sampleRate - tmp)) / sampleRate );
+ PRINT(("Pa_SetupDeviceFormat: warning - requested sample rate = %d Hz - closest = %d\n",
+ sampleRate, tmp ));
+ /* Allow sample rate within 10% off of requested rate. PLB20021018
+ * Sometimes OSS uses a funky rate like 44188 instead of 44100.
+ */
+ if( percentError > 10 )
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d Hz sample rate\n",sampleRate ));
+ return paHostError;
+ }
+ }
+
+ return result;
+}
+
+PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ return Pa_SetupDeviceFormat(devHandle, numChannels, sampleRate);
+}
+
+PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ return Pa_SetupDeviceFormat(devHandle, numChannels, sampleRate);
+}
+
+
+/*******************************************************************************************
+** Set number of fragments and size of fragments to achieve desired latency.
+*/
+
+static int CalcHigherLogTwo( int n )
+{
+ int log2 = 0;
+ while( (1<<log2) < n ) log2++;
+ return log2;
+}
+
+void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame )
+{
+ int tmp;
+ int bufferSize, powerOfTwo;
+
+ /* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
+ while( numBuffers > 8 )
+ {
+ numBuffers = (numBuffers + 1) >> 1;
+ framesPerBuffer = framesPerBuffer << 1;
+ }
+
+ /* calculate size of buffers in bytes */
+ bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME - other sizes? */
+
+ /* Calculate next largest power of two */
+ powerOfTwo = CalcHigherLogTwo( bufferSize );
+ DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
+ numBuffers, framesPerBuffer, powerOfTwo ));
+
+ /* Encode info into a single int */
+ tmp=(numBuffers<<16) + powerOfTwo;
+
+ if(ioctl(devHandle,SNDCTL_DSP_SETFRAGMENT,&tmp) == -1)
+ {
+ ERR_RPT(("Pa_SetLatency: could not SNDCTL_DSP_SETFRAGMENT\n" ));
+ /* Don't return an error. Best to just continue and hope for the best. */
+ ERR_RPT(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
+ numBuffers, framesPerBuffer, powerOfTwo ));
+ }
+}
+
+/***********************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ PaHostSoundControl *pahsc;
+
+ count_info info;
+ int delta;
+
+ if( past == NULL ) return paBadStreamPtr;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ if( pahsc->pahsc_NativeOutputBuffer )
+ {
+ ioctl(pahsc->pahsc_OutputHandle, SNDCTL_DSP_GETOPTR, &info);
+ delta = (info.bytes - pahsc->pahsc_LastPosPtr) & 0x000FFFFF;
+ return (pahsc->pahsc_LastStreamBytes + delta) / (past->past_NumOutputChannels * sizeof(short));
+ }
+ else
+ {
+ ioctl(pahsc->pahsc_InputHandle, SNDCTL_DSP_GETIPTR, &info);
+ delta = (info.bytes - pahsc->pahsc_LastPosPtr) & 0x000FFFFF;
+ return (pahsc->pahsc_LastStreamBytes + delta) / (past->past_NumInputChannels * sizeof(short));
+ }
+}
+
+void Pa_UpdateStreamTime(PaHostSoundControl *pahsc)
+{
+ count_info info;
+ int delta;
+
+ /* Update current stream time (using a double so that
+ we don't wrap around like info.bytes does) */
+ if( pahsc->pahsc_NativeOutputBuffer )
+ {
+ ioctl(pahsc->pahsc_OutputHandle, SNDCTL_DSP_GETOPTR, &info);
+ }
+ else
+ {
+ ioctl(pahsc->pahsc_InputHandle, SNDCTL_DSP_GETIPTR, &info);
+ }
+ delta = (info.bytes - pahsc->pahsc_LastPosPtr) & 0x000FFFFF;
+ pahsc->pahsc_LastStreamBytes += delta;
+ pahsc->pahsc_LastPosPtr = info.bytes;
+}
+
+PaError Pa_FlushStream(int devHandle)
+{
+ /* AS: This doesn't do anything under OSS; it was added for Solaris.*/
+ devHandle = devHandle; /* unused */
+ return paNoError;
+}
diff --git a/pd/portaudio_v18/pa_unix_oss/pa_unix_solaris.c b/pd/portaudio_v18/pa_unix_oss/pa_unix_solaris.c
new file mode 100644
index 00000000..1e1846b3
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/pa_unix_solaris.c
@@ -0,0 +1,397 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * Linux OSS Implementation by douglas repetto and Phil Burk
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* Modification history:
+ 20020621: Initial cut at Solaris modifications jointly by Sam Bayer
+ and Augustus Saunders.
+ 20030206 - Martin Rohrbach - various mods for Solaris
+ */
+
+#define __solaris_native__
+
+#include "pa_unix.h"
+
+/* SAM 6/2/02: Docs say we should include sys/audio.h, but
+ that doesn't exist pre Solaris 2.8. These headers work fine. */
+
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+
+/*********************************************************************
+ * Try to open the named device.
+ * If it opens, try to set various rates and formats and fill in
+ * the device info structure.
+ */
+PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad )
+{
+ int result = paHostError;
+ int tempDevHandle;
+ int numChannels, maxNumChannels;
+ int numSampleRates;
+ int sampleRate;
+ int numRatesToTry;
+ int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
+ int i;
+ audio_info_t solaris_info;
+ audio_device_t device_info;
+
+ /* douglas:
+ we have to do this querying in a slightly different order. apparently
+ some sound cards will give you different info based on their settins.
+ e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+ the correct order for OSS is: format, channels, sample rate
+
+ */
+ /*
+ to check a device for it's capabilities, it's probably better to use the
+ equivalent "-ctl"-descriptor - MR
+ */
+ char devname[strlen(deviceName) + 4];
+ if ( (tempDevHandle = open(strcat(strcpy(devname, deviceName), "ctl"), O_WRONLY|O_NONBLOCK)) == -1 )
+ {
+ DBUG(("Pa_QueryDevice: could not open %s\n", deviceName ));
+ return paHostError;
+ }
+
+ /* Ask OSS what formats are supported by the hardware. */
+ pad->pad_Info.nativeSampleFormats = 0;
+ AUDIO_INITINFO(&solaris_info);
+
+ /* SAM 12/31/01: Sparc native does mulaw, alaw and PCM.
+ I think PCM is signed. */
+
+ for (i = 8; i <= 32; i += 8) {
+ solaris_info.play.precision = i;
+ solaris_info.play.encoding = AUDIO_ENCODING_LINEAR;
+ /* If there are no errors, add the format. */
+ if (ioctl(tempDevHandle, AUDIO_SETINFO, &solaris_info) > -1) {
+ switch (i) {
+ case 8:
+ pad->pad_Info.nativeSampleFormats |= paInt8;
+ break;
+ case 16:
+ pad->pad_Info.nativeSampleFormats |= paInt16;
+ break;
+ case 24:
+ pad->pad_Info.nativeSampleFormats |= paInt24;
+ break;
+ case 32:
+ pad->pad_Info.nativeSampleFormats |= paInt32;
+ break;
+ }
+ }
+ }
+
+ maxNumChannels = 0;
+ for( numChannels = 1; numChannels <= 16; numChannels++ )
+ {
+ int temp = numChannels;
+ DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
+ AUDIO_INITINFO(&solaris_info);
+ solaris_info.play.channels = temp;
+ if (ioctl(tempDevHandle, AUDIO_SETINFO, &solaris_info) < 0)
+ {
+ /* ioctl() failed so bail out if we already have stereo */
+ if( numChannels > 2 ) break;
+ }
+ else
+ {
+ /* ioctl() worked but bail out if it does not support numChannels.
+ * We don't want to leave gaps in the numChannels supported.
+ */
+ if( (numChannels > 2) && (temp != numChannels) ) break;
+ DBUG(("Pa_QueryDevice: temp = %d\n", temp ))
+ if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */
+ }
+ }
+
+ pad->pad_Info.maxOutputChannels = maxNumChannels;
+ DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
+
+ /* FIXME - for now, assume maxInputChannels = maxOutputChannels.
+ * Eventually do separate queries for O_WRONLY and O_RDONLY
+ */
+ pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels;
+
+ DBUG(("Pa_QueryDevice: maxInputChannels = %d\n",
+ pad->pad_Info.maxInputChannels))
+
+
+ /* Determine available sample rates by trying each one and seeing result.
+ */
+ numSampleRates = 0;
+
+ AUDIO_INITINFO(&solaris_info);
+
+ numRatesToTry = sizeof(ratesToTry)/sizeof(int);
+ for (i = 0; i < numRatesToTry; i++)
+ {
+ sampleRate = ratesToTry[i];
+
+ solaris_info.play.sample_rate = sampleRate; /* AS: We opened for Write, so set play */
+ if (ioctl(tempDevHandle, AUDIO_SETINFO, &solaris_info) >= 0 ) /* PLB20010817 */
+ {
+ if (sampleRate == ratesToTry[i])
+ {
+ DBUG(("Pa_QueryDevice: got sample rate: %d\n", sampleRate))
+ pad->pad_SampleRates[numSampleRates] = (float)ratesToTry[i];
+ numSampleRates++;
+ }
+ }
+ }
+
+ DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates))
+ if (numSampleRates==0) /* HP20010922 */
+ {
+ ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed).\n" ));
+ goto error;
+ }
+
+ pad->pad_Info.numSampleRates = numSampleRates;
+ pad->pad_Info.sampleRates = pad->pad_SampleRates;
+
+ /* query for the device name instead of using the filesystem-device - MR */
+ if (ioctl(tempDevHandle, AUDIO_GETDEV, &device_info) == -1) {
+ pad->pad_Info.name = deviceName;
+ } else {
+ char *pt = (char *)PaHost_AllocateFastMemory(strlen(device_info.name));
+ strcpy(pt, device_info.name);
+ pad->pad_Info.name = pt;
+ }
+
+ result = paNoError;
+
+error:
+ /* We MUST close the handle here or we won't be able to reopen it later!!! */
+ close(tempDevHandle);
+
+ return result;
+}
+
+/*******************************************************************************************/
+
+PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ audio_info_t solaris_info;
+ AUDIO_INITINFO(&solaris_info);
+
+ /* Sam Bayer/Bryan George 1/10/02: Various folks have
+ reported that on Solaris Ultra II, the not-right thing
+ happens on read unless you make sure the audio device is
+ flushed. The folks who wrote the Robust Audio Tool say:
+ + XXX driver issue - on Ultra II's if you don't drain
+ * the device before reading commences then the device
+ * reads in blocks of 500ms irrespective of the
+ * blocksize set. After a minute or so it flips into the
+ * correct mode, but obviously this is too late to be + * useful for most apps. grrr.
+ */
+ /* AS: And the Solaris man audio pages say you should flush before changing formats
+ anyway. So there you go. */
+ if (Pa_FlushStream(devHandle) != paNoError)
+ return paHostError;
+
+ solaris_info.record.encoding = AUDIO_ENCODING_LINEAR;
+ solaris_info.record.sample_rate = sampleRate;
+ solaris_info.record.precision = 16;
+ solaris_info.record.channels = numChannels;
+
+ if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not set audio info\n" ));
+ return paHostError;
+ }
+
+ return paNoError;
+}
+
+PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ audio_info_t solaris_info;
+ AUDIO_INITINFO(&solaris_info);
+
+ /* Sam Bayer/Bryan George 1/10/02: Various folks have
+ reported that on Solaris Ultra II, the not-right thing
+ happens on read unless you make sure the audio device is
+ flushed. The folks who wrote the Robust Audio Tool say:
+ + XXX driver issue - on Ultra II's if you don't drain
+ * the device before reading commences then the device
+ * reads in blocks of 500ms irrespective of the
+ * blocksize set. After a minute or so it flips into the
+ * correct mode, but obviously this is too late to be + * useful for most apps. grrr.
+ */
+ /* AS: And the Solaris man audio pages say you should flush before changing formats
+ anyway. So there you go. */
+ if (Pa_FlushStream(devHandle) != paNoError)
+ return paHostError;
+
+ solaris_info.play.encoding = AUDIO_ENCODING_LINEAR;
+ solaris_info.play.sample_rate = sampleRate;
+ solaris_info.play.precision = 16;
+ solaris_info.play.channels = numChannels;
+
+ if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not set audio info\n" ));
+ return paHostError;
+ }
+
+ return paNoError;
+}
+
+PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ PaError result = paNoError;
+
+ result = Pa_SetupOutputDeviceFormat(devHandle, numChannels, sampleRate);
+ if (result != paNoError)
+ return result;
+ return Pa_SetupInputDeviceFormat(devHandle, numChannels, sampleRate);
+}
+
+/*******************************************************************************************
+** Set number of fragments and size of fragments to achieve desired latency.
+*/
+
+static PaError Pa_Unpause(int devHandle);
+static PaError Pa_PauseAndFlush(int devHandle);
+
+void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame )
+{
+ int bufferSize;
+ audio_info_t solaris_info;
+
+ /* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
+ while( numBuffers > 8 )
+ {
+ numBuffers = (numBuffers + 1) >> 1;
+ framesPerBuffer = framesPerBuffer << 1;
+ }
+
+ /* calculate size of buffers in bytes */
+ bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME - other sizes? */
+
+ DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d\n",
+ numBuffers, framesPerBuffer));
+
+ /* SAM 6/6/02: Documentation says to pause and flush before
+ changing buffer size. */
+
+ if (Pa_PauseAndFlush(devHandle) != paNoError) {
+ ERR_RPT(("Pa_SetLatency: could not pause audio\n" ));
+ return;
+ }
+
+ AUDIO_INITINFO(&solaris_info);
+
+ /* AS: Doesn't look like solaris has multiple buffers,
+ so I'm being conservative and
+ making one buffer. Might not be what we want... */
+
+ solaris_info.play.buffer_size = solaris_info.record.buffer_size = bufferSize;
+
+ if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
+ {
+ ERR_RPT(("Pa_SetLatency: could not set audio info\n" ));
+ }
+ Pa_Unpause(devHandle);
+}
+
+/***********************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ PaHostSoundControl *pahsc;
+ audio_info_t solaris_info;
+
+ if( past == NULL ) return paBadStreamPtr;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ ioctl(pahsc->pahsc_OutputHandle, AUDIO_GETINFO, &solaris_info);
+ return solaris_info.play.samples;
+}
+
+void Pa_UpdateStreamTime(PaHostSoundControl *pahsc)
+{
+ /* AS: Don't need to do anytying for this under Solaris.*/
+}
+
+static PaError Pa_PauseAndFlush(int devHandle)
+{
+ audio_info_t solaris_info;
+ AUDIO_INITINFO(&solaris_info);
+
+ solaris_info.play.pause = solaris_info.record.pause = 1;
+
+ if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
+ {
+ ERR_RPT(("Pa_FlushStream failed.\n"));
+ return paHostError;
+ }
+
+ if (ioctl(devHandle, I_FLUSH, FLUSHRW) == -1)
+ {
+ ERR_RPT(("Pa_FlushStream failed.\n"));
+
+ /* Unpause! */
+ AUDIO_INITINFO(&solaris_info);
+ solaris_info.play.pause = solaris_info.record.pause = 0;
+ ioctl(devHandle, AUDIO_SETINFO, &solaris_info);
+
+ return paHostError;
+ }
+ return paNoError;
+}
+
+static PaError Pa_Unpause(int devHandle)
+{
+ audio_info_t solaris_info;
+ AUDIO_INITINFO(&solaris_info);
+
+ solaris_info.play.pause = solaris_info.record.pause = 0;
+
+ if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
+ {
+ ERR_RPT(("Pa_FlushStream failed.\n"));
+ return paHostError;
+ }
+
+ return paNoError;
+}
+
+PaError Pa_FlushStream(int devHandle)
+{
+ PaError res = Pa_PauseAndFlush(devHandle);
+ if (res == paNoError)
+ return Pa_Unpause(devHandle);
+ else return res;
+}
diff --git a/pd/portaudio_v18/pa_unix_oss/recplay.c b/pd/portaudio_v18/pa_unix_oss/recplay.c
new file mode 100644
index 00000000..9d4c78cf
--- /dev/null
+++ b/pd/portaudio_v18/pa_unix_oss/recplay.c
@@ -0,0 +1,114 @@
+/*
+ * recplay.c
+ * Phil Burk
+ * Minimal record and playback test.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifndef __STDC__
+/* #include <getopt.h> */
+#endif /* __STDC__ */
+#include <fcntl.h>
+#ifdef __STDC__
+#include <string.h>
+#else /* __STDC__ */
+#include <strings.h>
+#endif /* __STDC__ */
+#include <sys/soundcard.h>
+
+#define NUM_BYTES (64*1024)
+#define BLOCK_SIZE (4*1024)
+
+#define AUDIO "/dev/dsp"
+
+char buffer[NUM_BYTES];
+
+int audioDev = 0;
+
+main (int argc, char *argv[])
+{
+ int numLeft;
+ char *ptr;
+ int num;
+ int samplesize;
+
+ /********** RECORD ********************/
+ /* Open audio device. */
+ audioDev = open (AUDIO, O_RDONLY, 0);
+ if (audioDev == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ /* Set to 16 bit samples. */
+ samplesize = 16;
+ ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+ if (samplesize != 16)
+ {
+ perror("Unable to set the sample size.");
+ exit(-1);
+ }
+
+ /* Record in blocks */
+ printf("Begin recording.\n");
+ numLeft = NUM_BYTES;
+ ptr = buffer;
+ while( numLeft >= BLOCK_SIZE )
+ {
+ if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 )
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ else
+ {
+ printf("Read %d bytes\n", num);
+ ptr += num;
+ numLeft -= num;
+ }
+ }
+
+ close( audioDev );
+
+ /********** PLAYBACK ********************/
+ /* Open audio device for writing. */
+ audioDev = open (AUDIO, O_WRONLY, 0);
+ if (audioDev == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ /* Set to 16 bit samples. */
+ samplesize = 16;
+ ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+ if (samplesize != 16)
+ {
+ perror("Unable to set the sample size.");
+ exit(-1);
+ }
+
+ /* Play in blocks */
+ printf("Begin playing.\n");
+ numLeft = NUM_BYTES;
+ ptr = buffer;
+ while( numLeft >= BLOCK_SIZE )
+ {
+ if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 )
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ else
+ {
+ printf("Wrote %d bytes\n", num);
+ ptr += num;
+ numLeft -= num;
+ }
+ }
+
+ close( audioDev );
+}