From 85babab2e1fe46d50e45651a9321139cac3d1545 Mon Sep 17 00:00:00 2001 From: Diane Bruce Date: Wed, 9 Aug 2006 21:20:28 +0000 Subject: [PATCH] - old imports no longer needed - Makefile should be generated from configure git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/trunk@252 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- portaudio-v19/Makefile | 197 - portaudio-v19/pa_jack/pa_jack.c | 1714 -------- portaudio-v19/pa_linux_alsa/pa_linux_alsa.c | 3590 ----------------- portaudio-v19/pa_linux_alsa/pa_linux_alsa.h | 64 - portaudio-v19/pa_mac_core/notes.txt | 124 - portaudio-v19/pa_mac_core/pa_mac_core.h | 64 - portaudio-v19/pa_mac_core/pa_mac_core_auhal.c | 1934 --------- portaudio-v19/pa_mac_core/pa_mac_core_old.c | 907 ----- .../pa_mac_core/pa_mac_core_utilities.c | 466 --- portaudio-v19/pa_mac_sm/pa_mac_sm.c | 1656 -------- portaudio-v19/pa_sgi/pa_sgi.c | 1417 ------- portaudio-v19/pa_unix/pa_unix_hostapis.c | 64 - portaudio-v19/pa_unix/pa_unix_util.c | 192 - portaudio-v19/pa_unix/pa_unix_util.h | 73 - portaudio-v19/pa_win/dev-cpp/Makefile-dll | 78 - portaudio-v19/pa_win/dev-cpp/Makefile-static | 75 - .../pa_win/dev-cpp/portaudio-dll.dev | 209 - .../pa_win/dev-cpp/portaudio-static.dev | 209 - portaudio-v19/pa_win/dev-cpp/readme.txt | 23 - portaudio-v19/pa_win/msvc/Makefile.msvc | 159 - portaudio-v19/pa_win/msvc/clean.bat | 7 - portaudio-v19/pa_win/msvc/make.bat | 8 - portaudio-v19/pa_win/msvc/portaudio.def | 43 - portaudio-v19/pa_win/msvc/readme.txt | 56 - portaudio-v19/pa_win/msvc/setenv.bat | 1 - portaudio-v19/pa_win/pa_win_hostapis.c | 86 - portaudio-v19/pa_win/pa_win_util.c | 134 - .../pa_win/pa_x86_plain_converters.c | 1167 ------ .../pa_win/pa_x86_plain_converters.h | 19 - 29 files changed, 14736 deletions(-) delete mode 100644 portaudio-v19/Makefile delete mode 100644 portaudio-v19/pa_jack/pa_jack.c delete mode 100644 portaudio-v19/pa_linux_alsa/pa_linux_alsa.c delete mode 100644 portaudio-v19/pa_linux_alsa/pa_linux_alsa.h delete mode 100644 portaudio-v19/pa_mac_core/notes.txt delete mode 100644 portaudio-v19/pa_mac_core/pa_mac_core.h delete mode 100644 portaudio-v19/pa_mac_core/pa_mac_core_auhal.c delete mode 100644 portaudio-v19/pa_mac_core/pa_mac_core_old.c delete mode 100644 portaudio-v19/pa_mac_core/pa_mac_core_utilities.c delete mode 100644 portaudio-v19/pa_mac_sm/pa_mac_sm.c delete mode 100644 portaudio-v19/pa_sgi/pa_sgi.c delete mode 100644 portaudio-v19/pa_unix/pa_unix_hostapis.c delete mode 100644 portaudio-v19/pa_unix/pa_unix_util.c delete mode 100644 portaudio-v19/pa_unix/pa_unix_util.h delete mode 100644 portaudio-v19/pa_win/dev-cpp/Makefile-dll delete mode 100644 portaudio-v19/pa_win/dev-cpp/Makefile-static delete mode 100644 portaudio-v19/pa_win/dev-cpp/portaudio-dll.dev delete mode 100644 portaudio-v19/pa_win/dev-cpp/portaudio-static.dev delete mode 100644 portaudio-v19/pa_win/dev-cpp/readme.txt delete mode 100644 portaudio-v19/pa_win/msvc/Makefile.msvc delete mode 100755 portaudio-v19/pa_win/msvc/clean.bat delete mode 100755 portaudio-v19/pa_win/msvc/make.bat delete mode 100644 portaudio-v19/pa_win/msvc/portaudio.def delete mode 100644 portaudio-v19/pa_win/msvc/readme.txt delete mode 100755 portaudio-v19/pa_win/msvc/setenv.bat delete mode 100644 portaudio-v19/pa_win/pa_win_hostapis.c delete mode 100644 portaudio-v19/pa_win/pa_win_util.c delete mode 100644 portaudio-v19/pa_win/pa_x86_plain_converters.c delete mode 100644 portaudio-v19/pa_win/pa_x86_plain_converters.h diff --git a/portaudio-v19/Makefile b/portaudio-v19/Makefile deleted file mode 100644 index e09142612..000000000 --- a/portaudio-v19/Makefile +++ /dev/null @@ -1,197 +0,0 @@ -# -# PortAudio V19 Makefile.in -# -# Dominic Mazzoni -# Modifications by Mikael Magnusson -# - -top_srcdir = . -srcdir = . - -top_builddir = . -PREFIX = /usr/local -prefix = $(PREFIX) -exec_prefix = ${prefix} -bindir = ${exec_prefix}/bin -libdir = ${exec_prefix}/lib -includedir = ${prefix}/include -CC = gcc -CFLAGS = -g -O2 -DPA_LITTLE_ENDIAN -pthread -I$(top_srcdir)/include -I$(top_srcdir)/src/common -I$(top_srcdir)/src/os/unix -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DHAVE_CLOCK_GETTIME=1 -DHAVE_NANOSLEEP=1 -DPA_USE_OSS=1 -LIBS = -lm -lpthread -AR = /usr/bin/ar -RANLIB = ranlib -LIBTOOL = $(SHELL) $(top_builddir)/libtool -INSTALL = /usr/bin/install -c -INSTALL_DATA = ${INSTALL} -m 644 -SHARED_FLAGS = -shared -fPIC -LDFLAGS = -DLL_LIBS = -lrt -lm -lpthread -CXXFLAGS = -g -O2 -NASM = -NASMOPT = -LN_S = ln -s -LT_RELEASE=@LT_RELEASE@ -LT_CURRENT=2 -LT_REVISION=0 -LT_AGE=0 - -OTHER_OBJS = src/hostapi/oss/pa_unix_oss.o src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o - -PALIB = libportaudio.la -PAINC = include/portaudio.h - -PA_LDFLAGS = $(LDFLAGS) $(SHARED_FLAGS) -rpath $(libdir) -no-undefined -export-symbols-regex "Pa_.*" -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) -#MAKEFILE = Makefile - -COMMON_OBJS = \ - src/common/pa_allocation.o \ - src/common/pa_converters.o \ - src/common/pa_cpuload.o \ - src/common/pa_dither.o \ - src/common/pa_front.o \ - src/common/pa_process.o \ - src/common/pa_skeleton.o \ - src/common/pa_stream.o \ - src/common/pa_trace.o - -TESTS = \ - bin/paqa_devs \ - bin/paqa_errs \ - bin/patest1 \ - bin/patest_buffer \ - bin/patest_callbackstop \ - bin/patest_clip \ - bin/patest_dither \ - bin/patest_hang \ - bin/patest_in_overflow \ - bin/patest_latency \ - bin/patest_leftright \ - bin/patest_longsine \ - bin/patest_many \ - bin/patest_maxsines \ - bin/patest_multi_sine \ - bin/patest_out_underflow \ - bin/patest_pink \ - bin/patest_prime \ - bin/patest_read_record \ - bin/patest_read_write_wire \ - bin/patest_record \ - bin/patest_ringmix \ - bin/patest_saw \ - bin/patest_sine8 \ - bin/patest_sine \ - bin/patest_sine_formats \ - bin/patest_sine_time \ - bin/patest_start_stop \ - bin/patest_stop \ - bin/patest_toomanysines \ - bin/patest_underflow \ - bin/patest_wire \ - bin/patest_write_sine \ - bin/pa_devs \ - bin/pa_fuzz \ - bin/pa_minlat - -# Most of these don't compile yet. Put them in TESTS, above, if -# you want to try to compile them... -ALL_TESTS = \ - $(TESTS) \ - bin/patest_sync \ - bin/debug_convert \ - bin/debug_dither_calc \ - bin/debug_dual \ - bin/debug_multi_in \ - bin/debug_multi_out \ - bin/debug_record \ - bin/debug_record_reuse \ - bin/debug_sine_amp \ - bin/debug_sine \ - bin/debug_sine_formats \ - bin/debug_srate \ - bin/debug_test1 - -OBJS = $(COMMON_OBJS) $(OTHER_OBJS) - -LTOBJS:= $(OBJS:.o=.lo) - -SUBDIRS = -#SUBDIRS += bindings/cpp - -all: lib/$(PALIB) all-recursive tests - -tests: bin-stamp $(TESTS) - - -lib/$(PALIB): lib-stamp $(LTOBJS) $(MAKEFILE) $(PAINC) - $(LIBTOOL) --mode=link $(CC) $(PA_LDFLAGS) -o lib/$(PALIB) $(LTOBJS) $(DLL_LIBS) - -$(ALL_TESTS): bin/%: lib/$(PALIB) $(MAKEFILE) $(PAINC) test/%.c - $(LIBTOOL) --mode=link $(CC) -o $@ $(CFLAGS) $(top_srcdir)/test/$*.c lib/$(PALIB) $(LIBS) - - -install: lib/$(PALIB) portaudio-2.0.pc - $(INSTALL) -d $(DESTDIR)$(libdir) - $(LIBTOOL) --mode=install $(INSTALL) lib/$(PALIB) $(DESTDIR)$(libdir) - $(INSTALL) -d $(DESTDIR)$(includedir) - $(INSTALL_DATA) -m 644 $(top_srcdir)/$(PAINC) $(DESTDIR)$(includedir)/portaudio.h - $(INSTALL) -d $(DESTDIR)$(libdir)/pkgconfig - $(INSTALL) -m 644 portaudio-2.0.pc $(DESTDIR)$(libdir)/pkgconfig/portaudio-2.0.pc - @echo "" - @echo "------------------------------------------------------------" - @echo "PortAudio was successfully installed." - @echo "" - @echo "On some systems (e.g. Linux) you should run 'ldconfig' now" - @echo "to make the shared object available. You may also need to" - @echo "modify your LD_LIBRARY_PATH environment variable to include" - @echo "the directory $(libdir)" - @echo "------------------------------------------------------------" - @echo "" - $(MAKE) install-recursive - -uninstall: - $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$(PALIB) - $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(includedir)/portaudio.h - $(MAKE) uninstall-recursive - -clean: - $(LIBTOOL) --mode=clean rm -f $(LTOBJS) $(ALL_TESTS) lib/$(PALIB) - rm -f bin-stamp lib-stamp - -rm -rf bin lib - -distclean: clean - rm -f config.log config.status Makefile libtool portaudio-2.0.pc - -%.o: %.c $(MAKEFILE) $(PAINC) - $(CC) -c $(CFLAGS) $< -o $@ - -%.lo: %.c $(MAKEFILE) $(PAINC) - $(LIBTOOL) --mode=compile $(CC) -c $(CFLAGS) $< -o $@ - -%.o: %.cpp $(MAKEFILE) $(PAINC) - $(CXX) -c $(CXXFLAGS) $< -o $@ - -%.o: %.asm - $(NASM) $(NASMOPT) -o $@ $< - -bin-stamp: - -mkdir bin - touch $@ - -lib-stamp: - -mkdir lib - -mkdir -p src/os/win src/os/unix src/os/mac_osx src/common \ -src/hostapi/oss src/hostapi/alsa src/hostapi/jack src/hostapi/asihpi \ -src/hostapi/wmme src/hostapi/wdmks src/hostapi/dsound src/hostapi/wasapi - touch $@ - -Makefile: Makefile.in config.status - $(SHELL) config.status - -all-recursive: - for dir in $(SUBDIRS); do make -C $$dir all; done - -install-recursive: - for dir in $(SUBDIRS); do make -C $$dir install; done - -uninstall-recursive: - for dir in $(SUBDIRS); do make -C $$dir uninstall; done diff --git a/portaudio-v19/pa_jack/pa_jack.c b/portaudio-v19/pa_jack/pa_jack.c deleted file mode 100644 index 17bf3d371..000000000 --- a/portaudio-v19/pa_jack/pa_jack.c +++ /dev/null @@ -1,1714 +0,0 @@ -/* - * $Id$ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * JACK Implementation by Joshua Haberman - * - * Copyright (c) 2004 Stefan Westerfeld - * Copyright (c) 2004 Arve Knudsen - * Copyright (c) 2002 Joshua Haberman - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include /* EBUSY */ -#include /* sig_atomic_t */ -#include -#include - -#include -#include - -#include "pa_util.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_process.h" -#include "pa_allocation.h" -#include "pa_cpuload.h" -#include "../pablio/ringbuffer.c" - -static int aErr_; -static PaError paErr_; /* For use with ENSURE_PA */ -static pthread_t mainThread_; -static char *jackErr_ = NULL; - -#define STRINGIZE_HELPER(expr) #expr -#define STRINGIZE(expr) STRINGIZE_HELPER(expr) - -/* Check PaError */ -#define ENSURE_PA(expr) \ - do { \ - if( (paErr_ = (expr)) < paNoError ) \ - { \ - if( (paErr_) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ - { \ - assert( jackErr_ ); \ - PaUtil_SetLastHostErrorInfo( paJACK, -1, jackErr_ ); \ - } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = paErr_; \ - goto error; \ - } \ - } while( 0 ) - -#define UNLESS(expr, code) \ - do { \ - if( (expr) == 0 ) \ - { \ - if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ - { \ - assert( jackErr_ ); \ - PaUtil_SetLastHostErrorInfo( paJACK, -1, jackErr_ ); \ - } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ) - -#define ASSERT_CALL(expr, success) \ - aErr_ = (expr); \ - assert( aErr_ == success ); - -/* - * Functions that directly map to the PortAudio stream interface - */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -/*static PaTime GetStreamInputLatency( PaStream *stream );*/ -/*static PaTime GetStreamOutputLatency( PaStream *stream );*/ -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); - - -/* - * Data specific to this API - */ - -struct PaJackStream; - -typedef struct -{ - PaUtilHostApiRepresentation commonHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *deviceInfoMemory; - - jack_client_t *jack_client; - int jack_buffer_size; - PaHostApiIndex hostApiIndex; - - pthread_mutex_t mtx; - pthread_cond_t cond; - unsigned long inputBase, outputBase; - - /* For dealing with the process thread */ - volatile int xrun; /* Received xrun notification from JACK? */ - struct PaJackStream * volatile toAdd, * volatile toRemove; - struct PaJackStream *processQueue; - volatile sig_atomic_t jackIsDown; -} -PaJackHostApiRepresentation; - -/* PaJackStream - a stream data structure specifically for this implementation */ - -typedef struct PaJackStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilBufferProcessor bufferProcessor; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaJackHostApiRepresentation *hostApi; - - /* our input and output ports */ - jack_port_t **local_input_ports; - jack_port_t **local_output_ports; - - /* the input and output ports of the client we are connecting to */ - jack_port_t **remote_input_ports; - jack_port_t **remote_output_ports; - - int num_incoming_connections; - int num_outgoing_connections; - - jack_client_t *jack_client; - - /* The stream is running if it's still producing samples. - * The stream is active if samples it produced are still being heard. - */ - volatile sig_atomic_t is_running; - volatile sig_atomic_t is_active; - /* Used to signal processing thread that stream should start or stop, respectively */ - volatile sig_atomic_t doStart, doStop, doAbort; - - jack_nframes_t t0; - - PaUtilAllocationGroup *stream_memory; - - /* These are useful in the process callback */ - - int callbackResult; - int isSilenced; - int xrun; - - /* These are useful for the blocking API */ - - int isBlockingStream; - RingBuffer inFIFO; - RingBuffer outFIFO; - volatile sig_atomic_t data_available; - sem_t data_semaphore; - int bytesPerFrame; - int samplesPerFrame; - - struct PaJackStream *next; -} -PaJackStream; - -#define TRUE 1 -#define FALSE 0 - -/* - * Functions specific to this API - */ - -static int JackCallback( jack_nframes_t frames, void *userData ); - - -/* - * - * Implementation - * - */ - -/* ---- blocking emulation layer ---- */ - -/* Allocate buffer. */ -static PaError BlockingInitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ) -{ - long numBytes = numFrames * bytesPerFrame; - char *buffer = (char *) malloc( numBytes ); - if( buffer == NULL ) return paInsufficientMemory; - memset( buffer, 0, numBytes ); - return (PaError) RingBuffer_Init( rbuf, numBytes, buffer ); -} - -/* Free buffer. */ -static PaError BlockingTermFIFO( RingBuffer *rbuf ) -{ - if( rbuf->buffer ) free( rbuf->buffer ); - rbuf->buffer = NULL; - return paNoError; -} - -static int -BlockingCallback( const void *inputBuffer, - void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - struct PaJackStream *stream = (PaJackStream *)userData; - long numBytes = stream->bytesPerFrame * framesPerBuffer; - - /* This may get called with NULL inputBuffer during initial setup. */ - if( inputBuffer != NULL ) - { - RingBuffer_Write( &stream->inFIFO, inputBuffer, numBytes ); - } - if( outputBuffer != NULL ) - { - int numRead = RingBuffer_Read( &stream->outFIFO, outputBuffer, numBytes ); - /* Zero out remainder of buffer if we run out of data. */ - memset( (char *)outputBuffer + numRead, 0, numBytes - numRead ); - } - - if( !stream->data_available ) - { - stream->data_available = 1; - sem_post( &stream->data_semaphore ); - } - return paContinue; -} - -static PaError -BlockingBegin( PaJackStream *stream, int minimum_buffer_size ) -{ - long doRead = 0; - long doWrite = 0; - PaError result = paNoError; - long numFrames; - - doRead = stream->local_input_ports != NULL; - doWrite = stream->local_output_ports != NULL; - /* */ - stream->samplesPerFrame = 2; - stream->bytesPerFrame = sizeof(float) * stream->samplesPerFrame; - /* */ - numFrames = 32; - while (numFrames < minimum_buffer_size) - numFrames *= 2; - - if( doRead ) - { - ENSURE_PA( BlockingInitFIFO( &stream->inFIFO, numFrames, stream->bytesPerFrame ) ); - } - if( doWrite ) - { - long numBytes; - - ENSURE_PA( BlockingInitFIFO( &stream->outFIFO, numFrames, stream->bytesPerFrame ) ); - - /* Make Write FIFO appear full initially. */ - numBytes = RingBuffer_GetWriteAvailable( &stream->outFIFO ); - RingBuffer_AdvanceWriteIndex( &stream->outFIFO, numBytes ); - } - - stream->data_available = 0; - sem_init( &stream->data_semaphore, 0, 0 ); - -error: - return result; -} - -static void -BlockingEnd( PaJackStream *stream ) -{ - BlockingTermFIFO( &stream->inFIFO ); - BlockingTermFIFO( &stream->outFIFO ); - - sem_destroy( &stream->data_semaphore ); -} - -static PaError BlockingReadStream( PaStream* s, void *data, unsigned long numFrames ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream *)s; - - long bytesRead; - char *p = (char *) data; - long numBytes = stream->bytesPerFrame * numFrames; - while( numBytes > 0 ) - { - bytesRead = RingBuffer_Read( &stream->inFIFO, p, numBytes ); - numBytes -= bytesRead; - p += bytesRead; - if( numBytes > 0 ) - { - /* see write for an explanation */ - if( stream->data_available ) - stream->data_available = 0; - else - sem_wait( &stream->data_semaphore ); - } - } - - return result; -} - -static PaError BlockingWriteStream( PaStream* s, const void *data, unsigned long numFrames ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream *)s; - long bytesWritten; - char *p = (char *) data; - long numBytes = stream->bytesPerFrame * numFrames; - while( numBytes > 0 ) - { - bytesWritten = RingBuffer_Write( &stream->outFIFO, p, numBytes ); - numBytes -= bytesWritten; - p += bytesWritten; - if( numBytes > 0 ) - { - /* we use the following algorithm: - * (1) write data - * (2) if some data didn't fit into the ringbuffer, set data_available to 0 - * to indicate to the audio that if space becomes available, we want to know - * (3) retry to write data (because it might be that between (1) and (2) - * new space in the buffer became available) - * (4) if this failed, we are sure that the buffer is really empty and - * we will definitely receive a notification when it becomes available - * thus we can safely sleep - * - * if the algorithm bailed out in step (3) before, it leaks a count of 1 - * on the semaphore; however, it doesn't matter, because if we block in (4), - * we also do it in a loop - */ - if( stream->data_available ) - stream->data_available = 0; - else - sem_wait( &stream->data_semaphore ); - } - } - - return result; -} - -static signed long -BlockingGetStreamReadAvailable( PaStream* s ) -{ - PaJackStream *stream = (PaJackStream *)s; - - int bytesFull = RingBuffer_GetReadAvailable( &stream->inFIFO ); - return bytesFull / stream->bytesPerFrame; -} - -static signed long -BlockingGetStreamWriteAvailable( PaStream* s ) -{ - PaJackStream *stream = (PaJackStream *)s; - - int bytesEmpty = RingBuffer_GetWriteAvailable( &stream->outFIFO ); - return bytesEmpty / stream->bytesPerFrame; -} - -static PaError -BlockingWaitEmpty( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream *)s; - - while( RingBuffer_GetReadAvailable( &stream->outFIFO ) > 0 ) - { - stream->data_available = 0; - sem_wait( &stream->data_semaphore ); - } - return 0; -} - -/* ---- jack driver ---- */ - -/* BuildDeviceList(): - * - * The process of determining a list of PortAudio "devices" from - * JACK's client/port system is fairly involved, so it is separated - * into its own routine. - */ - -static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) -{ - /* Utility macros for the repetitive process of allocating memory */ - - /* ... MALLOC: allocate memory as part of the device list - * allocation group */ -#define MALLOC(size) \ - (PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, (size) )) - - /* JACK has no concept of a device. To JACK, there are clients - * which have an arbitrary number of ports. To make this - * intelligible to PortAudio clients, we will group each JACK client - * into a device, and make each port of that client a channel */ - - PaError result = paNoError; - PaUtilHostApiRepresentation *commonApi = &jackApi->commonHostApiRep; - - const char **jack_ports = NULL; - char **client_names = NULL; - char *regex_pattern = alloca( jack_client_name_size() + 3 ); - int port_index, client_index, i; - double globalSampleRate; - regex_t port_regex; - unsigned long numClients = 0, numPorts = 0; - char *tmp_client_name = alloca( jack_client_name_size() ); - - commonApi->info.defaultInputDevice = paNoDevice; - commonApi->info.defaultOutputDevice = paNoDevice; - commonApi->info.deviceCount = 0; - - /* Parse the list of ports, using a regex to grab the client names */ - ASSERT_CALL( regcomp( &port_regex, "^[^:]*", REG_EXTENDED ), 0 ); - - /* since we are rebuilding the list of devices, free all memory - * associated with the previous list */ - PaUtil_FreeAllAllocations( jackApi->deviceInfoMemory ); - - /* We can only retrieve the list of clients indirectly, by first - * asking for a list of all ports, then parsing the port names - * according to the client_name:port_name convention (which is - * enforced by jackd) - * A: If jack_get_ports returns NULL, there's nothing for us to do */ - UNLESS( (jack_ports = jack_get_ports( jackApi->jack_client, "", "", 0 )) && jack_ports[0], paNoError ); - /* Find number of ports */ - while( jack_ports[numPorts] ) - ++numPorts; - /* At least there will be one port per client :) */ - UNLESS( client_names = alloca( numPorts * sizeof (char *) ), paInsufficientMemory ); - - /* Build a list of clients from the list of ports */ - for( numClients = 0, port_index = 0; jack_ports[port_index] != NULL; port_index++ ) - { - int client_seen = FALSE; - regmatch_t match_info; - const char *port = jack_ports[port_index]; - - /* extract the client name from the port name, using a regex - * that parses the clientname:portname syntax */ - UNLESS( !regexec( &port_regex, port, 1, &match_info, 0 ), paInternalError ); - assert(match_info.rm_eo - match_info.rm_so < jack_client_name_size()); - memcpy( tmp_client_name, port + match_info.rm_so, - match_info.rm_eo - match_info.rm_so ); - tmp_client_name[match_info.rm_eo - match_info.rm_so] = '\0'; - - /* do we know about this port's client yet? */ - for( i = 0; i < numClients; i++ ) - { - if( strcmp( tmp_client_name, client_names[i] ) == 0 ) - client_seen = TRUE; - } - - if (client_seen) - continue; /* A: Nothing to see here, move along */ - - UNLESS( client_names[numClients] = (char*)MALLOC(strlen(tmp_client_name) + 1), paInsufficientMemory ); - - /* The alsa_pcm client should go in spot 0. If this - * is the alsa_pcm client AND we are NOT about to put - * it in spot 0 put it in spot 0 and move whatever - * was already in spot 0 to the end. */ - if( strcmp( "alsa_pcm", tmp_client_name ) == 0 && numClients > 0 ) - { - /* alsa_pcm goes in spot 0 */ - strcpy( client_names[ numClients ], client_names[0] ); - strcpy( client_names[0], tmp_client_name ); - } - else - { - /* put the new client at the end of the client list */ - strcpy( client_names[ numClients ], tmp_client_name ); - } - ++numClients; - } - - /* Now we have a list of clients, which will become the list of - * PortAudio devices. */ - - /* there is one global sample rate all clients must conform to */ - - globalSampleRate = jack_get_sample_rate( jackApi->jack_client ); - UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)MALLOC( sizeof(PaDeviceInfo*) * - numClients ), paInsufficientMemory ); - - assert( commonApi->info.deviceCount == 0 ); - - /* Create a PaDeviceInfo structure for every client */ - for( client_index = 0; client_index < numClients; client_index++ ) - { - PaDeviceInfo *curDevInfo; - const char **clientPorts = NULL; - - UNLESS( curDevInfo = (PaDeviceInfo*)MALLOC( sizeof(PaDeviceInfo) ), paInsufficientMemory ); - UNLESS( curDevInfo->name = (char*)MALLOC( strlen(client_names[client_index]) + 1 ), paInsufficientMemory ); - strcpy( (char *)curDevInfo->name, client_names[client_index] ); - - curDevInfo->structVersion = 2; - curDevInfo->hostApi = jackApi->hostApiIndex; - - /* JACK is very inflexible: there is one sample rate the whole - * system must run at, and all clients must speak IEEE float. */ - curDevInfo->defaultSampleRate = globalSampleRate; - - /* To determine how many input and output channels are available, - * we re-query jackd with more specific parameters. */ - - sprintf( regex_pattern, "%s:.*", client_names[client_index] ); - - /* ... what are your output ports (that we could input from)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, - NULL, JackPortIsOutput); - curDevInfo->maxInputChannels = 0; - curDevInfo->defaultLowInputLatency = 0.; - curDevInfo->defaultHighInputLatency = 0.; - if( clientPorts ) - { - jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] ); - curDevInfo->defaultLowInputLatency = curDevInfo->defaultHighInputLatency = - jack_port_get_latency( p ) / globalSampleRate; - - for( i = 0; clientPorts[i] != NULL; i++) - { - /* The number of ports returned is the number of output channels. - * We don't care what they are, we just care how many */ - curDevInfo->maxInputChannels++; - } - free(clientPorts); - } - - /* ... what are your input ports (that we could output to)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, - NULL, JackPortIsInput); - curDevInfo->maxOutputChannels = 0; - curDevInfo->defaultLowOutputLatency = 0.; - curDevInfo->defaultHighOutputLatency = 0.; - if( clientPorts ) - { - jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] ); - curDevInfo->defaultLowOutputLatency = curDevInfo->defaultHighOutputLatency = - jack_port_get_latency( p ) / globalSampleRate; - - for( i = 0; clientPorts[i] != NULL; i++) - { - /* The number of ports returned is the number of input channels. - * We don't care what they are, we just care how many */ - curDevInfo->maxOutputChannels++; - } - free(clientPorts); - } - - /* Add this client to the list of devices */ - commonApi->deviceInfos[client_index] = curDevInfo; - ++commonApi->info.deviceCount; - if( commonApi->info.defaultInputDevice == paNoDevice && curDevInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = client_index; - if( commonApi->info.defaultOutputDevice == paNoDevice && curDevInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = client_index; - } - -error: - regfree( &port_regex ); - free( jack_ports ); - return result; -} -#undef MALLOC - -static void UpdateSampleRate( PaJackStream *stream, double sampleRate ) -{ - /* XXX: Maybe not the cleanest way of going about this? */ - stream->cpuLoadMeasurer.samplingPeriod = stream->bufferProcessor.samplePeriod = 1. / sampleRate; - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; -} - -static void JackErrorCallback( const char *msg ) -{ - if( pthread_self() == mainThread_ ) - { - assert( msg ); - free( jackErr_ ); - jackErr_ = malloc( strlen( msg ) ); - sprintf( jackErr_, msg ); - } -} - -static void JackOnShutdown( void *arg ) -{ - PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg; - PaJackStream *stream = jackApi->processQueue; - - PA_DEBUG(( "%s: JACK server is shutting down\n", __FUNCTION__ )); - for( ; stream; stream = stream->next ) - { - stream->is_active = 0; - } - - /* Make sure that the main thread doesn't get stuck waiting on the condition */ - ASSERT_CALL( pthread_mutex_lock( &jackApi->mtx ), 0 ); - jackApi->jackIsDown = 1; - ASSERT_CALL( pthread_cond_signal( &jackApi->cond ), 0 ); - ASSERT_CALL( pthread_mutex_unlock( &jackApi->mtx ), 0 ); - -} - -static int JackSrCb( jack_nframes_t nframes, void *arg ) -{ - PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg; - double sampleRate = (double)nframes; - PaJackStream *stream = jackApi->processQueue; - - /* Update all streams in process queue */ - PA_DEBUG(( "%s: Acting on change in JACK samplerate: %f\n", __FUNCTION__, sampleRate )); - for( ; stream; stream = stream->next ) - { - if( stream->streamRepresentation.streamInfo.sampleRate != sampleRate ) - { - PA_DEBUG(( "%s: Updating samplerate\n", __FUNCTION__ )); - UpdateSampleRate( stream, sampleRate ); - } - } - - return 0; -} - -static int JackXRunCb(void *arg) { - PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)arg; - assert( hostApi ); - hostApi->xrun = TRUE; - PA_DEBUG(( "%s: JACK signalled xrun\n", __FUNCTION__ )); - return 0; -} - -PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, - PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *jackHostApi; - int activated = 0; - char *clientName; - int written; - *hostApi = NULL; /* Initialize to NULL */ - - UNLESS( jackHostApi = (PaJackHostApiRepresentation*) - PaUtil_AllocateMemory( sizeof(PaJackHostApiRepresentation) ), paInsufficientMemory ); - jackHostApi->deviceInfoMemory = NULL; - - mainThread_ = pthread_self(); - ASSERT_CALL( pthread_mutex_init( &jackHostApi->mtx, NULL ), 0 ); - ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 ); - - /* Try to become a client of the JACK server. If we cannot do - * this, then this API cannot be used. */ - - clientName = alloca( jack_client_name_size() ); - written = snprintf( clientName, jack_client_name_size(), "PortAudio-%d", getpid() ); - assert( written < jack_client_name_size() ); - jackHostApi->jack_client = jack_client_new( clientName ); - if( jackHostApi->jack_client == NULL ) - { - /* the V19 development docs say that if an implementation - * detects that it cannot be used, it should return a NULL - * interface and paNoError */ - result = paNoError; - goto error; - } - - UNLESS( jackHostApi->deviceInfoMemory = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - jackHostApi->hostApiIndex = hostApiIndex; - - *hostApi = &jackHostApi->commonHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paJACK; - (*hostApi)->info.name = "JACK Audio Connection Kit"; - - /* Build a device list by querying the JACK server */ - - ENSURE_PA( BuildDeviceList( jackHostApi ) ); - - /* Register functions */ - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &jackHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &jackHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - BlockingReadStream, BlockingWriteStream, - BlockingGetStreamReadAvailable, BlockingGetStreamWriteAvailable ); - - jackHostApi->inputBase = jackHostApi->outputBase = 0; - jackHostApi->xrun = 0; - jackHostApi->toAdd = jackHostApi->toRemove = NULL; - jackHostApi->processQueue = NULL; - jackHostApi->jackIsDown = 0; - - jack_on_shutdown( jackHostApi->jack_client, JackOnShutdown, jackHostApi ); - jack_set_error_function( JackErrorCallback ); - jackHostApi->jack_buffer_size = jack_get_buffer_size ( jackHostApi->jack_client ); - UNLESS( !jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi ), paUnanticipatedHostError ); - UNLESS( !jack_set_xrun_callback( jackHostApi->jack_client, JackXRunCb, jackHostApi ), paUnanticipatedHostError ); - UNLESS( !jack_set_process_callback( jackHostApi->jack_client, JackCallback, jackHostApi ), paUnanticipatedHostError ); - UNLESS( !jack_activate( jackHostApi->jack_client ), paUnanticipatedHostError ); - activated = 1; - - return result; - -error: - if( activated ) - ASSERT_CALL( jack_deactivate( jackHostApi->jack_client ), 0 ); - - if( jackHostApi ) - { - if( jackHostApi->jack_client ) - ASSERT_CALL( jack_client_close( jackHostApi->jack_client ), 0 ); - - if( jackHostApi->deviceInfoMemory ) - { - PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory ); - PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory ); - } - - PaUtil_FreeMemory( jackHostApi ); - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; - - /* note: this automatically disconnects all ports, since a deactivated - * client is not allowed to have any ports connected */ - ASSERT_CALL( jack_deactivate( jackHostApi->jack_client ), 0 ); - - ASSERT_CALL( pthread_mutex_destroy( &jackHostApi->mtx ), 0 ); - ASSERT_CALL( pthread_cond_destroy( &jackHostApi->cond ), 0 ); - - ASSERT_CALL( jack_client_close( jackHostApi->jack_client ), 0 ); - - if( jackHostApi->deviceInfoMemory ) - { - PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory ); - PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory ); - } - - PaUtil_FreeMemory( jackHostApi ); - - free( jackErr_ ); -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - /* - The following check is not necessary for JACK. - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - */ - - /* check that the device supports sampleRate */ - -#define ABS(x) ( (x) > 0 ? (x) : -(x) ) - if( ABS(sampleRate - jack_get_sample_rate(((PaJackHostApiRepresentation *) hostApi)->jack_client )) > 1 ) - return paInvalidSampleRate; -#undef ABS - - return paFormatIsSupported; -} - -/* Basic stream initialization */ -static PaError InitializeStream( PaJackStream *stream, PaJackHostApiRepresentation *hostApi, int numInputChannels, - int numOutputChannels ) -{ - PaError result = paNoError; - assert( stream ); - - memset( stream, 0, sizeof (PaJackStream) ); - UNLESS( stream->stream_memory = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - stream->jack_client = hostApi->jack_client; - stream->hostApi = hostApi; - - if( numInputChannels > 0 ) - { - UNLESS( stream->local_input_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), - paInsufficientMemory ); - memset( stream->local_input_ports, 0, sizeof(jack_port_t*) * numInputChannels ); - UNLESS( stream->remote_output_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), - paInsufficientMemory ); - memset( stream->remote_output_ports, 0, sizeof(jack_port_t*) * numInputChannels ); - } - if( numOutputChannels > 0 ) - { - UNLESS( stream->local_output_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), - paInsufficientMemory ); - memset( stream->local_output_ports, 0, sizeof(jack_port_t*) * numOutputChannels ); - UNLESS( stream->remote_input_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), - paInsufficientMemory ); - memset( stream->remote_input_ports, 0, sizeof(jack_port_t*) * numOutputChannels ); - } - - stream->num_incoming_connections = numInputChannels; - stream->num_outgoing_connections = numOutputChannels; - -error: - return result; -} - -/*! - * Free resources associated with stream, and eventually stream itself. - * - * Frees allocated memory, and closes opened pcms. - */ -static void CleanUpStream( PaJackStream *stream, int terminateStreamRepresentation, int terminateBufferProcessor ) -{ - int i; - assert( stream ); - - if( stream->isBlockingStream ) - BlockingEnd( stream ); - - for( i = 0; i < stream->num_incoming_connections; ++i ) - { - if( stream->local_input_ports[i] ) - ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_input_ports[i] ), 0 ); - } - for( i = 0; i < stream->num_outgoing_connections; ++i ) - { - if( stream->local_output_ports[i] ) - ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_output_ports[i] ), 0 ); - } - - if( terminateStreamRepresentation ) - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - if( terminateBufferProcessor ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - - if( stream->stream_memory ) - { - PaUtil_FreeAllAllocations( stream->stream_memory ); - PaUtil_DestroyAllocationGroup( stream->stream_memory ); - } - PaUtil_FreeMemory( stream ); -} - -static PaError WaitCondition( PaJackHostApiRepresentation *hostApi ) -{ - PaError result = paNoError; - int err = 0; - PaTime pt = PaUtil_GetTime(); - struct timespec ts; - - ts.tv_sec = (time_t) floor( pt + 1 ); - ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000); - /* XXX: Best enclose in loop, in case of spurious wakeups? */ - err = pthread_cond_timedwait( &hostApi->cond, &hostApi->mtx, &ts ); - - /* Make sure we didn't time out */ - UNLESS( err != ETIMEDOUT, paTimedOut ); - UNLESS( !err, paInternalError ); - -error: - return result; -} - -static PaError AddStream( PaJackStream *stream ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *hostApi = stream->hostApi; - /* Add to queue of streams that should be processed */ - ASSERT_CALL( pthread_mutex_lock( &hostApi->mtx ), 0 ); - if( !hostApi->jackIsDown ) - { - hostApi->toAdd = stream; - /* Unlock mutex and await signal from processing thread */ - result = WaitCondition( stream->hostApi ); - } - ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 ); - ENSURE_PA( result ); - - UNLESS( !hostApi->jackIsDown, paDeviceUnavailable ); - -error: - return result; -} - -/* Remove stream from processing queue */ -static PaError RemoveStream( PaJackStream *stream ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *hostApi = stream->hostApi; - - /* Add to queue over streams that should be processed */ - ASSERT_CALL( pthread_mutex_lock( &hostApi->mtx ), 0 ); - if( !hostApi->jackIsDown ) - { - hostApi->toRemove = stream; - /* Unlock mutex and await signal from processing thread */ - result = WaitCondition( stream->hostApi ); - } - ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 ); - ENSURE_PA( result ); - -error: - return result; -} - -/* Add stream to processing queue */ -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; - PaJackStream *stream = NULL; - char *port_string = alloca( jack_port_name_size() ); - unsigned long regexSz = jack_client_name_size() + 3; - char *regex_pattern = alloca( regexSz ); - const char **jack_ports = NULL; - /* int jack_max_buffer_size = jack_get_buffer_size( jackHostApi->jack_client ); */ - int i; - int inputChannelCount, outputChannelCount; - const double jackSr = jack_get_sample_rate( jackHostApi->jack_client ); - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; - int bpInitialized = 0, srInitialized = 0; /* Initialized buffer processor and stream representation? */ - unsigned long ofs; - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - if( (streamFlags & paPrimeOutputBuffersUsingStreamCallback) != 0 ) - { - streamFlags &= ~paPrimeOutputBuffersUsingStreamCallback; - /*return paInvalidFlag;*/ /* This implementation does not support buffer priming */ - } - - if( framesPerBuffer != paFramesPerBufferUnspecified ) - { - /* Jack operates with power of two buffers, and we don't support non-integer buffer adaption (yet) */ - /*UNLESS( !(framesPerBuffer & (framesPerBuffer - 1)), paBufferTooBig );*/ /* TODO: Add descriptive error code? */ - } - - /* Preliminary checks */ - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - /* ... check that the sample rate exactly matches the ONE acceptable rate - * A: This rate isn't necessarily constant though? */ - -#define ABS(x) ( (x) > 0 ? (x) : -(x) ) - if( ABS(sampleRate - jackSr) > 1 ) - return paInvalidSampleRate; -#undef ABS - - UNLESS( stream = (PaJackStream*)PaUtil_AllocateMemory( sizeof(PaJackStream) ), paInsufficientMemory ); - ENSURE_PA( InitializeStream( stream, jackHostApi, inputChannelCount, outputChannelCount ) ); - - /* the blocking emulation, if necessary */ - stream->isBlockingStream = !streamCallback; - if( stream->isBlockingStream ) - { - float latency = 0.001; /* 1ms is the absolute minimum we support */ - int minimum_buffer_frames = 0; - - if( inputParameters && inputParameters->suggestedLatency > latency ) - latency = inputParameters->suggestedLatency; - else if( outputParameters && outputParameters->suggestedLatency > latency ) - latency = outputParameters->suggestedLatency; - - /* the latency the user asked for indicates the minimum buffer size in frames */ - minimum_buffer_frames = (int) (latency * jack_get_sample_rate( jackHostApi->jack_client )); - - /* we also need to be able to store at least three full jack buffers to avoid dropouts */ - if( jackHostApi->jack_buffer_size * 3 > minimum_buffer_frames ) - minimum_buffer_frames = jackHostApi->jack_buffer_size * 3; - - /* setup blocking API data structures (FIXME: can fail) */ - BlockingBegin( stream, minimum_buffer_frames ); - - /* install our own callback for the blocking API */ - streamCallback = BlockingCallback; - userData = stream; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &jackHostApi->blockingStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &jackHostApi->callbackStreamInterface, streamCallback, userData ); - } - srInitialized = 1; - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, jackSr ); - - /* create the JACK ports. We cannot connect them until audio - * processing begins */ - - /* Register a unique set of ports for this stream - * TODO: Robust allocation of new port names */ - - ofs = jackHostApi->inputBase; - for( i = 0; i < inputChannelCount; i++ ) - { - snprintf( port_string, jack_port_name_size(), "in_%lu", ofs + i ); - UNLESS( stream->local_input_ports[i] = jack_port_register( - jackHostApi->jack_client, port_string, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ), paInsufficientMemory ); - } - jackHostApi->inputBase += inputChannelCount; - - ofs = jackHostApi->outputBase; - for( i = 0; i < outputChannelCount; i++ ) - { - snprintf( port_string, jack_port_name_size(), "out_%lu", ofs + i ); - UNLESS( stream->local_output_ports[i] = jack_port_register( - jackHostApi->jack_client, port_string, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ), paInsufficientMemory ); - } - jackHostApi->outputBase += outputChannelCount; - - /* look up the jack_port_t's for the remote ports. We could do - * this at stream start time, but doing it here ensures the - * name lookup only happens once. */ - - if( inputChannelCount > 0 ) - { - int err = 0; - - /* ... remote output ports (that we input from) */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name ); - UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, - NULL, JackPortIsOutput ), paUnanticipatedHostError ); - for( i = 0; i < inputChannelCount && jack_ports[i]; i++ ) - { - if( (stream->remote_output_ports[i] = jack_port_by_name( - jackHostApi->jack_client, jack_ports[i] )) == NULL ) - { - err = 1; - break; - } - } - free( jack_ports ); - UNLESS( !err, paInsufficientMemory ); - - /* Fewer ports than expected? */ - UNLESS( i == inputChannelCount, paInternalError ); - } - - if( outputChannelCount > 0 ) - { - int err = 0; - - /* ... remote input ports (that we output to) */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name ); - UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, - NULL, JackPortIsInput ), paUnanticipatedHostError ); - for( i = 0; i < outputChannelCount && jack_ports[i]; i++ ) - { - if( (stream->remote_input_ports[i] = jack_port_by_name( - jackHostApi->jack_client, jack_ports[i] )) == 0 ) - { - err = 1; - break; - } - } - free( jack_ports ); - UNLESS( !err , paInsufficientMemory ); - - /* Fewer ports than expected? */ - UNLESS( i == outputChannelCount, paInternalError ); - } - - ENSURE_PA( PaUtil_InitializeBufferProcessor( - &stream->bufferProcessor, - inputChannelCount, - inputSampleFormat, - paFloat32, /* hostInputSampleFormat */ - outputChannelCount, - outputSampleFormat, - paFloat32, /* hostOutputSampleFormat */ - jackSr, - streamFlags, - framesPerBuffer, - 0, /* Ignored */ - paUtilUnknownHostBufferSize, /* Buffer size may vary on JACK's discretion */ - streamCallback, - userData ) ); - bpInitialized = 1; - - if( stream->num_incoming_connections > 0 ) - stream->streamRepresentation.streamInfo.inputLatency = (jack_port_get_latency( stream->remote_output_ports[0] ) - - jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */ - + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor )) / sampleRate; - if( stream->num_outgoing_connections > 0 ) - stream->streamRepresentation.streamInfo.outputLatency = (jack_port_get_latency( stream->remote_input_ports[0] ) - - jack_get_buffer_size( jackHostApi->jack_client ) /* One buffer is not counted as latency */ - + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor )) / sampleRate; - - stream->streamRepresentation.streamInfo.sampleRate = jackSr; - stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */ - - ENSURE_PA( AddStream( stream ) ); /* Add to queue over opened streams */ - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - CleanUpStream( stream, srInitialized, bpInitialized ); - - return result; -} - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream*)s; - - /* Remove this stream from the processing queue */ - ENSURE_PA( RemoveStream( stream ) ); - -error: - CleanUpStream( stream, 1, 1 ); - return result; -} - -static PaError RealProcess( PaJackStream *stream, jack_nframes_t frames ) -{ - PaError result = paNoError; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; - int chn; - int framesProcessed; - const double sr = jack_get_sample_rate( stream->jack_client ); /* Shouldn't change during the process callback */ - PaStreamCallbackFlags cbFlags = 0; - - /* If the user has returned !paContinue from the callback we'll want to flush the internal buffers, - * when these are empty we can finally mark the stream as inactive */ - if( stream->callbackResult != paContinue && - PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - { - stream->is_active = 0; - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - PA_DEBUG(( "%s: Callback finished\n", __FUNCTION__ )); - - goto end; - } - - timeInfo.currentTime = (jack_frame_time( stream->jack_client ) - stream->t0) / sr; - if( stream->num_incoming_connections > 0 ) - timeInfo.inputBufferAdcTime = timeInfo.currentTime - jack_port_get_latency( stream->remote_output_ports[0] ) - / sr; - if( stream->num_outgoing_connections > 0 ) - timeInfo.outputBufferDacTime = timeInfo.currentTime + jack_port_get_latency( stream->remote_input_ports[0] ) - / sr; - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - if( stream->xrun ) - { - /* XXX: Any way to tell which of these occurred? */ - cbFlags = paOutputUnderflow | paInputOverflow; - stream->xrun = FALSE; - } - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, - cbFlags ); - - if( stream->num_incoming_connections > 0 ) - PaUtil_SetInputFrameCount( &stream->bufferProcessor, frames ); - if( stream->num_outgoing_connections > 0 ) - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, frames ); - - for( chn = 0; chn < stream->num_incoming_connections; chn++ ) - { - jack_default_audio_sample_t *channel_buf = (jack_default_audio_sample_t*) - jack_port_get_buffer( stream->local_input_ports[chn], - frames ); - - PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor, - chn, - channel_buf ); - } - - for( chn = 0; chn < stream->num_outgoing_connections; chn++ ) - { - jack_default_audio_sample_t *channel_buf = (jack_default_audio_sample_t*) - jack_port_get_buffer( stream->local_output_ports[chn], - frames ); - - PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor, - chn, - channel_buf ); - } - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, - &stream->callbackResult ); - /* We've specified a host buffer size mode where every frame should be consumed by the buffer processor */ - assert( framesProcessed == frames ); - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - -end: - return result; -} - -/* Alter the processing queue if necessary */ -static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi ) -{ - PaError result = paNoError; - int queueModified = 0; - const double jackSr = jack_get_sample_rate( hostApi->jack_client ); - int err; - - if( (err = pthread_mutex_trylock( &hostApi->mtx )) != 0 ) - { - assert( err == EBUSY ); - return paNoError; - } - - if( hostApi->toAdd ) - { - if( hostApi->processQueue ) - { - PaJackStream *node = hostApi->processQueue; - /* Advance to end of queue */ - while( node->next ) - node = node->next; - - node->next = hostApi->toAdd; - } - else - hostApi->processQueue = (PaJackStream *)hostApi->toAdd; - - /* If necessary, update stream state */ - if( hostApi->toAdd->streamRepresentation.streamInfo.sampleRate != jackSr ) - UpdateSampleRate( hostApi->toAdd, jackSr ); - - hostApi->toAdd = NULL; - queueModified = 1; - } - if( hostApi->toRemove ) - { - int removed = 0; - PaJackStream *node = hostApi->processQueue, *prev = NULL; - assert( hostApi->processQueue ); - - while( node ) - { - if( node == hostApi->toRemove ) - { - if( prev ) - prev->next = node->next; - else - hostApi->processQueue = (PaJackStream *)node->next; - - removed = 1; - break; - } - - prev = node; - node = node->next; - } - UNLESS( removed, paInternalError ); - hostApi->toRemove = NULL; - PA_DEBUG(( "%s: Removed stream from processing queue\n", __FUNCTION__ )); - queueModified = 1; - } - - if( queueModified ) - { - /* Signal that we've done what was asked of us */ - ASSERT_CALL( pthread_cond_signal( &hostApi->cond ), 0 ); - } - -error: - ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 ); - - return result; -} - -static int JackCallback( jack_nframes_t frames, void *userData ) -{ - PaError result = paNoError; - PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)userData; - PaJackStream *stream = NULL; - int xrun = hostApi->xrun; - hostApi->xrun = 0; - - assert( hostApi ); - - ENSURE_PA( UpdateQueue( hostApi ) ); - - /* Process each stream */ - stream = hostApi->processQueue; - for( ; stream; stream = stream->next ) - { - if( xrun ) /* Don't override if already set */ - stream->xrun = 1; - - /* See if this stream is to be started */ - if( stream->doStart ) - { - /* If we can't obtain a lock, we'll try next time */ - int err = pthread_mutex_trylock( &stream->hostApi->mtx ); - if( !err ) - { - if( stream->doStart ) /* Could potentially change before obtaining the lock */ - { - stream->is_active = 1; - stream->doStart = 0; - PA_DEBUG(( "%s: Starting stream\n", __FUNCTION__ )); - ASSERT_CALL( pthread_cond_signal( &stream->hostApi->cond ), 0 ); - stream->callbackResult = paContinue; - stream->isSilenced = 0; - } - - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - } - else - assert( err == EBUSY ); - } - else if( stream->doStop || stream->doAbort ) /* Should we stop/abort stream? */ - { - if( stream->callbackResult == paContinue ) /* Ok, make it stop */ - { - PA_DEBUG(( "%s: Stopping stream\n", __FUNCTION__ )); - stream->callbackResult = stream->doStop ? paComplete : paAbort; - } - } - - if( stream->is_active ) - ENSURE_PA( RealProcess( stream, frames ) ); - /* If we have just entered inactive state, silence output */ - if( !stream->is_active && !stream->isSilenced ) - { - int i; - - /* Silence buffer after entering inactive state */ - PA_DEBUG(( "Silencing the output\n" )); - for( i = 0; i < stream->num_outgoing_connections; ++i ) - { - jack_default_audio_sample_t *buffer = jack_port_get_buffer( stream->local_output_ports[i], frames ); - memset( buffer, 0, sizeof (jack_default_audio_sample_t) * frames ); - } - - stream->isSilenced = 1; - } - - if( stream->doStop || stream->doAbort ) - { - /* See if RealProcess has acted on the request */ - if( !stream->is_active ) /* Ok, signal to the main thread that we've carried out the operation */ - { - /* If we can't obtain a lock, we'll try next time */ - int err = pthread_mutex_trylock( &stream->hostApi->mtx ); - if( !err ) - { - stream->doStop = stream->doAbort = 0; - ASSERT_CALL( pthread_cond_signal( &stream->hostApi->cond ), 0 ); - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - } - else - assert( err == EBUSY ); - } - } - } - - return 0; -error: - return -1; -} - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaJackStream *stream = (PaJackStream*)s; - int i; - - /* Ready the processor */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* connect the ports */ - - /* NOTE: I would rather use jack_port_connect which uses jack_port_t's - * instead of port names, but it is not implemented yet. */ - if( stream->num_incoming_connections > 0 ) - { - for( i = 0; i < stream->num_incoming_connections; i++ ) - UNLESS( jack_connect( stream->jack_client, - jack_port_name( stream->remote_output_ports[i] ), - jack_port_name( stream->local_input_ports[i] ) ) == 0, paUnanticipatedHostError ); - } - - if( stream->num_outgoing_connections > 0 ) - { - for( i = 0; i < stream->num_outgoing_connections; i++ ) - UNLESS( jack_connect( stream->jack_client, - jack_port_name( stream->local_output_ports[i] ), - jack_port_name( stream->remote_input_ports[i] ) ) == 0, paUnanticipatedHostError ); - } - - stream->xrun = FALSE; - - /* Enable processing */ - - ASSERT_CALL( pthread_mutex_lock( &stream->hostApi->mtx ), 0 ); - stream->doStart = 1; - - /* Wait for stream to be started */ - result = WaitCondition( stream->hostApi ); - /* - do - { - err = pthread_cond_timedwait( &stream->hostApi->cond, &stream->hostApi->mtx, &ts ); - } while( !stream->is_active && !err ); - */ - if( result != paNoError ) /* Something went wrong, call off the stream start */ - { - stream->doStart = 0; - stream->is_active = 0; /* Cancel any processing */ - } - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - - ENSURE_PA( result ); - - stream->is_running = TRUE; - PA_DEBUG(( "%s: Stream started\n", __FUNCTION__ )); - -error: - return result; -} - -static PaError RealStop( PaJackStream *stream, int abort ) -{ - PaError result = paNoError; - int i; - - if( stream->isBlockingStream ) - BlockingWaitEmpty ( stream ); - - ASSERT_CALL( pthread_mutex_lock( &stream->hostApi->mtx ), 0 ); - if( abort ) - stream->doAbort = 1; - else - stream->doStop = 1; - - /* Wait for stream to be stopped */ - result = WaitCondition( stream->hostApi ); - ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 ); - ENSURE_PA( result ); - - UNLESS( !stream->is_active, paInternalError ); - - PA_DEBUG(( "%s: Stream stopped\n", __FUNCTION__ )); - -error: - stream->is_running = FALSE; - - /* Disconnect ports belonging to this stream */ - - if( !stream->hostApi->jackIsDown ) /* XXX: Well? */ - { - if( stream->num_incoming_connections > 0 ) - { - for( i = 0; i < stream->num_incoming_connections; i++ ) - UNLESS( !jack_disconnect( stream->jack_client, - jack_port_name( stream->remote_output_ports[i] ), - jack_port_name( stream->local_input_ports[i] ) ), paUnanticipatedHostError ); - } - if( stream->num_outgoing_connections > 0 ) - { - for( i = 0; i < stream->num_outgoing_connections; i++ ) - UNLESS( !jack_disconnect( stream->jack_client, - jack_port_name( stream->local_output_ports[i] ), - jack_port_name( stream->remote_input_ports[i] ) ), paUnanticipatedHostError ); - } - } - - return result; -} - -static PaError StopStream( PaStream *s ) -{ - assert(s); - return RealStop( (PaJackStream *)s, 0 ); -} - -static PaError AbortStream( PaStream *s ) -{ - assert(s); - return RealStop( (PaJackStream *)s, 1 ); -} - -static PaError IsStreamStopped( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream*)s; - return !stream->is_running; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream*)s; - return stream->is_active; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - PaJackStream *stream = (PaJackStream*)s; - - /* A: Is this relevant?? --> TODO: what if we're recording-only? */ - return (jack_frame_time( stream->jack_client ) - stream->t0) / (PaTime)jack_get_sample_rate( stream->jack_client ); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaJackStream *stream = (PaJackStream*)s; - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} diff --git a/portaudio-v19/pa_linux_alsa/pa_linux_alsa.c b/portaudio-v19/pa_linux_alsa/pa_linux_alsa.c deleted file mode 100644 index dce232e36..000000000 --- a/portaudio-v19/pa_linux_alsa/pa_linux_alsa.c +++ /dev/null @@ -1,3590 +0,0 @@ -/* - * $Id$ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * ALSA implementation by Joshua Haberman and Arve Knudsen - * - * Copyright (c) 2002 Joshua Haberman - * Copyright (c) 2005 Arve Knudsen - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API -#include -#undef ALSA_PCM_NEW_HW_PARAMS_API -#undef ALSA_PCM_NEW_SW_PARAMS_API - -#include -#include /* strlen() */ -#include -#include -#include -#include -#include -#include -#include /* For sig_atomic_t */ - -#include "portaudio.h" -#include "pa_util.h" -#include "../pa_unix/pa_unix_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "pa_linux_alsa.h" - -/* Check return value of ALSA function, and map it to PaError */ -#define ENSURE_(expr, code) \ - do { \ - if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \ - } \ - PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \ - if( (code) == paUnanticipatedHostError ) \ - PA_DEBUG(( "Host error description: %s\n", snd_strerror( aErr_ ) )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ); - -#define ENSURE_SYSTEM_(expr, success) \ - do { \ - if( UNLIKELY( (aErr_ = (expr)) != success ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( pthread_self() != callbackThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, aErr_, strerror( aErr_ ) ); \ - } \ - PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \ - result = paUnanticipatedHostError; \ - goto error; \ - } \ - } while( 0 ); - -#define ASSERT_CALL_(expr, success) \ - aErr_ = (expr); \ - assert( success == aErr_ ); - -static int aErr_; /* Used with ENSURE_ */ -static pthread_t callbackThread_; - -typedef enum -{ - StreamDirection_In, - StreamDirection_Out -} StreamDirection; - -/* Threading utility struct */ -typedef struct PaAlsaThreading -{ - pthread_t watchdogThread; - pthread_t callbackThread; - int watchdogRunning; - int rtSched; - int rtPrio; - int useWatchdog; - unsigned long throttledSleepTime; - volatile PaTime callbackTime; - volatile PaTime callbackCpuTime; - PaUtilCpuLoadMeasurer *cpuLoadMeasurer; -} PaAlsaThreading; - -typedef struct -{ - PaSampleFormat hostSampleFormat; - unsigned long framesPerBuffer; - int numUserChannels, numHostChannels; - int userInterleaved, hostInterleaved; - - snd_pcm_t *pcm; - snd_pcm_uframes_t bufferSize; - snd_pcm_format_t nativeFormat; - unsigned int nfds; - int ready; /* Marked ready from poll */ - void **userBuffers; - snd_pcm_uframes_t offset; - StreamDirection streamDir; - - snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */ -} PaAlsaStreamComponent; - -/* Implementation specific stream structure */ -typedef struct PaAlsaStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - PaAlsaThreading threading; - - unsigned long framesPerUserBuffer, maxFramesPerHostBuffer; - - int primeBuffers; - int callbackMode; /* bool: are we running in callback mode? */ - int pcmsSynced; /* Have we successfully synced pcms */ - - /* the callback thread uses these to poll the sound device(s), waiting - * for data to be ready/available */ - struct pollfd* pfds; - int pollTimeout; - - /* Used in communication between threads */ - volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */ - volatile sig_atomic_t callbackAbort; /* Drop frames? */ - volatile sig_atomic_t callbackStop; /* Signal a stop */ - volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */ - pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */ - pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */ - pthread_cond_t startCond; /* Wait untill audio is started in callback thread */ - - int neverDropInput; - - PaTime underrun; - PaTime overrun; - - PaAlsaStreamComponent capture, playback; -} -PaAlsaStream; - -/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct PaAlsaHostApiRepresentation -{ - PaUtilHostApiRepresentation commonHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - PaHostApiIndex hostApiIndex; -} -PaAlsaHostApiRepresentation; - -typedef struct PaAlsaDeviceInfo -{ - PaDeviceInfo commonDeviceInfo; - char *alsaName; - int isPlug; - int minInputChannels; - int minOutputChannels; -} -PaAlsaDeviceInfo; - -/* Threading utilities */ - -static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm ) -{ - th->watchdogRunning = 0; - th->rtSched = 0; - th->callbackTime = 0; - th->callbackCpuTime = 0; - th->useWatchdog = 1; - th->throttledSleepTime = 0; - th->cpuLoadMeasurer = clm; - - th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2 - + sched_get_priority_min( SCHED_FIFO ); -} - -static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult ) -{ - PaError result = paNoError; - void *pret; - - if( exitResult ) - *exitResult = paNoError; - if( watchdogExitResult ) - *watchdogExitResult = paNoError; - - if( th->watchdogRunning ) - { - pthread_cancel( th->watchdogThread ); - ENSURE_SYSTEM_( pthread_join( th->watchdogThread, &pret ), 0 ); - - if( pret && pret != PTHREAD_CANCELED ) - { - if( watchdogExitResult ) - *watchdogExitResult = *(PaError *) pret; - free( pret ); - } - } - - /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ - /* TODO: Make join time out */ - if( !wait ) - { - PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, th->callbackThread )); - pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ - } - PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, th->callbackThread )); - ENSURE_SYSTEM_( pthread_join( th->callbackThread, &pret ), 0 ); - - if( pret && pret != PTHREAD_CANCELED ) - { - if( exitResult ) - *exitResult = *(PaError *) pret; - free( pret ); - } - -error: - return result; -} - -/** Lock a pthread_mutex_t. - * - * @concern ThreadCancellation We're disabling thread cancellation while the thread is holding a lock, so mutexes are - * properly unlocked at termination time. - */ -static PaError LockMutex( pthread_mutex_t *mtx ) -{ - PaError result = paNoError; - int oldState; - - ENSURE_SYSTEM_( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 ); - ENSURE_SYSTEM_( pthread_mutex_lock( mtx ), 0 ); - -error: - return result; -} - -/** Unlock a pthread_mutex_t. - * - * @concern ThreadCancellation Thread cancellation is enabled again after the mutex is properly unlocked. - */ -static PaError UnlockMutex( pthread_mutex_t *mtx ) -{ - PaError result = paNoError; - int oldState; - - ENSURE_SYSTEM_( pthread_mutex_unlock( mtx ), 0 ); - ENSURE_SYSTEM_( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 ); - -error: - return result; -} - -static void OnWatchdogExit( void *userData ) -{ - PaAlsaThreading *th = (PaAlsaThreading *) userData; - struct sched_param spm = { 0 }; - assert( th ); - - ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */ - PA_DEBUG(( "Watchdog exiting\n" )); -} - -static PaError BoostPriority( PaAlsaThreading *th ) -{ - PaError result = paNoError; - struct sched_param spm = { 0 }; - spm.sched_priority = th->rtPrio; - - assert( th ); - - if( pthread_setschedparam( th->callbackThread, 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; -} - -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 PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s ) -{ - PaError result = paNoError; - pthread_attr_t attr; - int started = 0; - -#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1) - if( th->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 - - 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( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError ); - started = 1; - - if( th->rtSched ) - { - if( th->useWatchdog ) - { - int err; - struct sched_param wdSpm = { 0 }; - /* Launch watchdog, watchdog sets callback thread priority */ - int prio = PA_MIN( th->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( &th->watchdogThread, &attr, &WatchdogFunc, th )) ) - { - PA_UNLESS( err == EPERM, paInternalError ); - /* Permission error, go on without realtime privileges */ - PA_DEBUG(( "Failed bumping priority\n" )); - } - else - { - int policy; - th->watchdogRunning = 1; - ENSURE_SYSTEM_( pthread_getschedparam( th->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 - PA_ENSURE( BoostPriority( th ) ); - } - -end: - return result; -error: - if( started ) - KillCallbackThread( th, 0, NULL, NULL ); - - goto end; -} - -static void CallbackUpdate( PaAlsaThreading *th ) -{ - th->callbackTime = PaUtil_GetTime(); - th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer ); -} - -/* prototypes for functions declared in this file */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *callback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); -static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); -static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); - -/* Callback prototypes */ -static void *CallbackThreadFunc( void *userData ); - -/* Blocking prototypes */ -static signed long GetStreamReadAvailable( PaStream* s ); -static signed long GetStreamWriteAvailable( PaStream* s ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); - - -static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device ) -{ - return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; -} - -PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaAlsaHostApiRepresentation *alsaHostApi = NULL; - - PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory( - sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); - PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - alsaHostApi->hostApiIndex = hostApiIndex; - - *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paALSA; - (*hostApi)->info.name = "ALSA"; - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PA_ENSURE( BuildDeviceList( alsaHostApi ) ); - - PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, - GetStreamReadAvailable, - GetStreamWriteAvailable ); - - return result; - -error: - if( alsaHostApi ) - { - if( alsaHostApi->allocations ) - { - PaUtil_FreeAllAllocations( alsaHostApi->allocations ); - PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); - } - - PaUtil_FreeMemory( alsaHostApi ); - } - - return result; -} - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; - - assert( hostApi ); - - if( alsaHostApi->allocations ) - { - PaUtil_FreeAllAllocations( alsaHostApi->allocations ); - PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); - } - - PaUtil_FreeMemory( alsaHostApi ); - snd_config_update_free_global(); -} - -/*! Determine max channels and default latencies. - * - * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for - * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero, - * and a suitable result returned. The device is closed before returning. - */ -static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency, - double *defaultHighLatency, double *defaultSampleRate, int isPlug ) -{ - PaError result = paNoError; - snd_pcm_hw_params_t *hwParams; - snd_pcm_uframes_t lowLatency = 512, highLatency = 2048; - unsigned int minChans, maxChans; - double defaultSr = *defaultSampleRate; - - assert( pcm ); - - ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); - - snd_pcm_hw_params_alloca( &hwParams ); - snd_pcm_hw_params_any( pcm, hwParams ); - - if( defaultSr >= 0 ) - { - /* Could be that the device opened in one mode supports samplerates that the other mode wont have, - * so try again .. */ - if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 ) - { - defaultSr = -1.; - PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ )); - } - } - - if( defaultSr < 0. ) /* Default sample rate not set */ - { - unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ - ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError ); - ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError ); - } - - ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); - assert( maxChans <= INT_MAX ); - assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, - resulting in zeroed values */ - - /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */ - if( isPlug && maxChans > 128 ) - { - maxChans = 128; - PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans )); - } - - /* TWEAKME: - * - * Giving values for default min and max latency is not - * straightforward. Here are our objectives: - * - * * for low latency, we want to give the lowest value - * that will work reliably. This varies based on the - * sound card, kernel, CPU, etc. I think it is better - * to give sub-optimal latency than to give a number - * too low and cause dropouts. My conservative - * estimate at this point is to base it on 4096-sample - * latency at 44.1 kHz, which gives a latency of 23ms. - * * for high latency we want to give a large enough - * value that dropouts are basically impossible. This - * doesn't really require as much tweaking, since - * providing too large a number will just cause us to - * select the nearest setting that will work at stream - * config time. - */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); - - /* Have to reset hwParams, to set new buffer size */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); - - *minChannels = (int)minChans; - *maxChannels = (int)maxChans; - *defaultSampleRate = defaultSr; - *defaultLowLatency = (double) lowLatency / *defaultSampleRate; - *defaultHighLatency = (double) highLatency / *defaultSampleRate; - -end: - snd_pcm_close( pcm ); - return result; - -error: - goto end; -} - -/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate - * wether input/output is available) */ -static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo ) -{ - deviceInfo->structVersion = -1; - deviceInfo->name = NULL; - deviceInfo->hostApi = -1; - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - deviceInfo->defaultLowInputLatency = -1.; - deviceInfo->defaultLowOutputLatency = -1.; - deviceInfo->defaultHighInputLatency = -1.; - deviceInfo->defaultHighOutputLatency = -1.; - deviceInfo->defaultSampleRate = -1.; -} - -/* Helper struct */ -typedef struct -{ - char *alsaName; - char *name; - int isPlug; - int hasPlayback; - int hasCapture; -} DeviceNames; - -static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi, - char **dst, - const char *src) -{ - PaError result = paNoError; - int len = strlen( src ) + 1; - - /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */ - - PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - strncpy( *dst, src, len ); - -error: - return result; -} - -/* Disregard standard plugins - * XXX: Might want to make the "default" plugin available, if we can make it work - */ -static int IgnorePlugin( const char *pluginId ) -{ -#define numIgnored 10 - static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee", - "file", "null", "shm"}; - int i; - - for( i = 0; i < numIgnored; ++i ) - { - if( !strcmp( pluginId, ignoredPlugins[i] ) ) - { - return 1; - } - } - - return 0; -} - -/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */ -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) -{ - PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep; - PaAlsaDeviceInfo *deviceInfoArray; - int cardIdx = -1, devIdx = 0; - snd_ctl_card_info_t *cardInfo; - PaError result = paNoError; - size_t numDeviceNames = 0, maxDeviceNames = 1, i; - DeviceNames *deviceNames = NULL; - snd_config_t *topNode = NULL; - snd_pcm_info_t *pcmInfo; - int res; - int blocking = SND_PCM_NONBLOCK; - char alsaCardName[50]; - if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) - blocking = 0; - - /* These two will be set to the first working input and output device, respectively */ - commonApi->info.defaultInputDevice = paNoDevice; - commonApi->info.defaultOutputDevice = paNoDevice; - - /* count the devices by enumerating all the card numbers */ - - /* snd_card_next() modifies the integer passed to it to be: - * the index of the first card if the parameter is -1 - * the index of the next card if the parameter is the index of a card - * -1 if there are no more cards - * - * The function itself returns 0 if it succeeded. */ - cardIdx = -1; - snd_ctl_card_info_alloca( &cardInfo ); - snd_pcm_info_alloca( &pcmInfo ); - while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) - { - char *cardName; - int devIdx = -1; - snd_ctl_t *ctl; - char buf[50]; - - snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); - - /* Acquire name of card */ - if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) - continue; /* Unable to open card :( */ - snd_ctl_card_info( ctl, cardInfo ); - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) ); - - while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) - { - char *alsaDeviceName, *deviceName; - size_t len; - int hasPlayback = 0, hasCapture = 0; - snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx ); - - /* Obtain info about this particular device */ - snd_pcm_info_set_device( pcmInfo, devIdx ); - snd_pcm_info_set_subdevice( pcmInfo, 0 ); - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - hasCapture = 1; - - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - hasPlayback = 1; - - if( !hasPlayback && !hasCapture ) - { - continue; /* Error */ - } - - /* The length of the string written by snprintf plus terminating 0 */ - len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1; - PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - snprintf( deviceName, len, "%s: %s (%s)", cardName, - snd_pcm_info_get_name( pcmInfo ), buf ); - - ++numDeviceNames; - if( !deviceNames || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), - paInsufficientMemory ); - } - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); - - deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName; - deviceNames[ numDeviceNames - 1 ].name = deviceName; - deviceNames[ numDeviceNames - 1 ].isPlug = 0; - deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback; - deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture; - } - snd_ctl_close( ctl ); - } - - /* Iterate over plugin devices */ - if( NULL == snd_config ) - { - /* snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ - ENSURE_( snd_config_update(), paUnanticipatedHostError ); - PA_DEBUG(( "Updating snd_config\n" )); - } - assert( snd_config ); - if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 ) - { - snd_config_iterator_t i, next; - - snd_config_for_each( i, next, topNode ) - { - const char *tpStr = NULL, *idStr = NULL; - char *alsaDeviceName, *deviceName; - snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL; - if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND ) - continue; - - ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError ); - ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); - - ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); - if( IgnorePlugin( idStr ) ) - { - PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr )); - continue; - } - - PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr )); - - PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 6 ), paInsufficientMemory ); - strcpy( alsaDeviceName, idStr ); - PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 1 ), paInsufficientMemory ); - strcpy( deviceName, idStr ); - - ++numDeviceNames; - if( !deviceNames || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), - paInsufficientMemory ); - } - - deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName; - deviceNames[numDeviceNames - 1].name = deviceName; - deviceNames[numDeviceNames - 1].isPlug = 1; - deviceNames[numDeviceNames - 1].hasPlayback = 1; - deviceNames[numDeviceNames - 1].hasCapture = 1; - } - } - else - PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) )); - - /* allocate deviceInfo memory based on the number of devices */ - PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); - - /* allocate all device info structs in a contiguous block */ - PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); - - /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name), - * it's ignored. - */ - /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */ - for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) - { - snd_pcm_t *pcm; - PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx]; - PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo; - - /* Zero fields */ - InitializeDeviceInfo( commonDeviceInfo ); - - /* to determine device capabilities, we must open the device and query the - * hardware parameter configuration space */ - - /* Query capture */ - if( deviceNames[i].hasCapture && - snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 ) - { - if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels, - &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency, - &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError ) - continue; /* Error */ - } - - /* Query playback */ - if( deviceNames[i].hasPlayback && - snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 ) - { - if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels, - &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency, - &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError ) - continue; /* Error */ - } - - commonDeviceInfo->structVersion = 2; - commonDeviceInfo->hostApi = alsaApi->hostApiIndex; - commonDeviceInfo->name = deviceNames[i].name; - deviceInfo->alsaName = deviceNames[i].alsaName; - deviceInfo->isPlug = deviceNames[i].isPlug; - - /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object. - * Should now be safe to add device info, unless the device supports neither capture nor playback - */ - if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 ) - { - if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = devIdx; - if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = devIdx; - - commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo; - } - } - free( deviceNames ); - - commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */ - -end: - return result; - -error: - /* No particular action */ - goto end; -} - -/* Check against known device capabilities */ -static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode ) -{ - PaError result = paNoError; - int maxChans; - const PaAlsaDeviceInfo *deviceInfo = NULL; - assert( parameters ); - - if( parameters->device != paUseHostApiSpecificDeviceSpecification ) - { - assert( parameters->device < hostApi->info.deviceCount ); - PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination ); - deviceInfo = GetDeviceInfo( hostApi, parameters->device ); - } - else - { - const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo; - - PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice ); - PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1, - paIncompatibleHostApiSpecificStreamInfo ); - PA_UNLESS( streamInfo->deviceString != NULL, paInvalidDevice ); - - /* Skip further checking */ - return paNoError; - } - - assert( deviceInfo ); - assert( parameters->hostApiSpecificStreamInfo == NULL ); - maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels : - deviceInfo->commonDeviceInfo.maxOutputChannels); - PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount ); - -error: - return result; -} - -/* Given an open stream, what sample formats are available? */ -static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) -{ - PaSampleFormat available = 0; - snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); - - snd_pcm_hw_params_any( pcm, hwParams ); - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) - available |= paFloat32; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) - available |= paInt32; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0) - available |= paInt24; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) - available |= paInt16; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) - available |= paUInt8; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) - available |= paInt8; - - return available; -} - -static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) -{ - switch( paFormat ) - { - case paFloat32: - return SND_PCM_FORMAT_FLOAT; - - case paInt16: - return SND_PCM_FORMAT_S16; - - case paInt24: - return SND_PCM_FORMAT_S24; - - case paInt32: - return SND_PCM_FORMAT_S32; - - case paInt8: - return SND_PCM_FORMAT_S8; - - case paUInt8: - return SND_PCM_FORMAT_U8; - - default: - return SND_PCM_FORMAT_UNKNOWN; - } -} - -/** Open an ALSA pcm handle. - * - * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a - * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin - * device. - */ -static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection - streamDir, snd_pcm_t **pcm ) -{ - PaError result = paNoError; - int ret; - const char *deviceName = alloca( 50 ); - const PaAlsaDeviceInfo *deviceInfo = NULL; - PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo; - - if( !streamInfo ) - { - int usePlug = 0; - deviceInfo = GetDeviceInfo( hostApi, params->device ); - - /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */ - if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) ) - usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) ); - if( usePlug ) - snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName ); - else - deviceName = deviceInfo->alsaName; - } - else - deviceName = streamInfo->deviceString; - - PA_DEBUG(( "%s: Opening device %s\n", __FUNCTION__, deviceName )); - if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK )) < 0 ) - { - /* Not to be closed */ - *pcm = NULL; - ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination ); - } - ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError ); - -end: - return result; - -error: - goto end; -} - -static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters, - double sampleRate, StreamDirection streamDir ) -{ - PaError result = paNoError; - snd_pcm_t *pcm = NULL; - PaSampleFormat availableFormats; - /* We are able to adapt to a number of channels less than what the device supports */ - unsigned int numHostChannels; - PaSampleFormat hostFormat; - snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); - - if( !parameters->hostApiSpecificStreamInfo ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device ); - numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ? - devInfo->minInputChannels : devInfo->minOutputChannels ); - } - else - numHostChannels = parameters->channelCount; - - PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) ); - - snd_pcm_hw_params_any( pcm, hwParams ); - - if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 ) - { - result = paInvalidSampleRate; - goto error; - } - - if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 ) - { - result = paInvalidChannelCount; - goto error; - } - - /* See if we can find a best possible match */ - availableFormats = GetAvailableFormats( pcm ); - PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); - - { - /* It happens that this call fails because the device is busy */ - int ret = 0; - if( (ret = snd_pcm_hw_params( pcm, hwParams )) < 0) - { - ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paUnanticipatedHostError ); - } - } - -end: - if( pcm ) - { - snd_pcm_close( pcm ); - } - return result; - -error: - goto end; -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaError result = paFormatIsSupported; - - if( inputParameters ) - { - PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); - - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - - if( outputParameters ) - { - PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); - - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - if( inputChannelCount ) - { - if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In )) - != paNoError ) - goto error; - } - if ( outputChannelCount ) - { - if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out )) - != paNoError ) - goto error; - } - - return paFormatIsSupported; - -error: - return result; -} - -static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi, - const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) -{ - PaError result = paNoError; - PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat; - assert( params->channelCount > 0 ); - - /* Make sure things have an initial value */ - memset( self, 0, sizeof (PaAlsaStreamComponent) ); - - if( NULL == params->hostApiSpecificStreamInfo ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device ); - self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels - : devInfo->minOutputChannels ); - } - else - { - /* We're blissfully unaware of the minimum channelCount */ - self->numHostChannels = params->channelCount; - } - - PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) ); - self->nfds = snd_pcm_poll_descriptors_count( self->pcm ); - hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ); - - self->hostSampleFormat = hostSampleFormat; - self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); - self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved); - self->numUserChannels = params->channelCount; - self->streamDir = streamDir; - - if( !callbackMode && !self->userInterleaved ) - { - /* Pre-allocate non-interleaved user provided buffers */ - PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ), - paInsufficientMemory ); - } - -error: - return result; -} - -static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self ) -{ - snd_pcm_close( self->pcm ); - if( self->userBuffers ) - PaUtil_FreeMemory( self->userBuffers ); -} - -int nearbyint_(float value) { - if( value - (int)value > .5 ) - return (int)ceil( value ); - return (int)floor( value ); -} - -/** Initiate configuration, preparing for determining a period size suitable for both capture and playback components. - * - */ -static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *self, const PaStreamParameters *params, - int primeBuffers, snd_pcm_hw_params_t *hwParams, double *sampleRate ) -{ - /* Configuration consists of setting all of ALSA's parameters. - * These parameters come in two flavors: hardware parameters - * and software paramters. Hardware parameters will affect - * the way the device is initialized, software parameters - * affect the way ALSA interacts with me, the user-level client. - */ - - PaError result = paNoError; - snd_pcm_access_t accessMode, alternateAccessMode; - int dir = 0; - snd_pcm_t *pcm = self->pcm; - double sr = *sampleRate; - unsigned int minPeriods = 2; - - /* self->framesPerBuffer = framesPerHostBuffer; */ - - /* ... fill up the configuration space with all possibile - * combinations of parameters this device will accept */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError ); - /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */ - dir = 0; - ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); - - if( self->userInterleaved ) - { - accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; - alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - } - else - { - accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; - } - /* If requested access mode fails, try alternate mode */ - if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 ) - { - ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError ); - /* Flip mode */ - self->hostInterleaved = !self->userInterleaved; - } - - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError ); - - ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate ); - ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError ); - /* reject if there's no sample rate within 1% of the one requested */ - if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 ) - { - PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); - PA_ENSURE( paInvalidSampleRate ); - } - - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount ); - - *sampleRate = sr; - -end: - return result; - -error: - /* No particular action */ - goto end; -} - -static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *self, snd_pcm_hw_params_t* hwParams, - const PaStreamParameters *params, int primeBuffers, double sampleRate, PaTime* returnedLatency ) -{ - PaError result = paNoError; - snd_pcm_sw_params_t* swParams; - snd_pcm_uframes_t bufSz = 0; - *returnedLatency = -1.; - - snd_pcm_sw_params_alloca( &swParams ); - - bufSz = (params->suggestedLatency * sampleRate) + self->framesPerBuffer; /* One period does not count as latency */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_hw_params( self->pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); - /* Latency in seconds, one period is not counted as latency */ - *returnedLatency = (self->bufferSize - self->framesPerBuffer) / sampleRate; - - /* Now software parameters... */ - ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_sw_params_set_start_threshold( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_stop_threshold( self->pcm, swParams, self->bufferSize ), paUnanticipatedHostError ); - - /* Silence buffer in the case of underrun */ - if( !primeBuffers ) /* XXX: Make sense? */ - { - snd_pcm_uframes_t boundary; - ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_threshold( self->pcm, swParams, 0 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_size( self->pcm, swParams, boundary ), paUnanticipatedHostError ); - } - - ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError ); - -error: - return result; -} - -static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams, - const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback, - PaStreamFlags streamFlags, void *userData ) -{ - PaError result = paNoError; - assert( self ); - - memset( self, 0, sizeof (PaAlsaStream) ); - - if( NULL != callback ) - { - PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, - &alsaApi->callbackStreamInterface, - callback, userData ); - self->callbackMode = 1; - } - else - { - PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, - &alsaApi->blockingStreamInterface, - NULL, userData ); - } - - self->framesPerUserBuffer = framesPerUserBuffer; - self->neverDropInput = streamFlags & paNeverDropInput; - /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */ - /* - if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback ) - self->primeBuffers = 1; - */ - memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) ); - memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) ); - if( inParams ) - PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) ); - if( outParams ) - PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) ); - - assert( self->capture.nfds || self->playback.nfds ); - - PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds + - self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory ); - - PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate ); - InitializeThreading( &self->threading, &self->cpuLoadMeasurer ); - ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 ); - ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 ); - ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 ); - -error: - return result; -} - -/** Free resources associated with stream, and eventually stream itself. - * - * Frees allocated memory, and terminates individual StreamComponents. - */ -static void PaAlsaStream_Terminate( PaAlsaStream *self ) -{ - assert( self ); - - if( self->capture.pcm ) - { - PaAlsaStreamComponent_Terminate( &self->capture ); - } - if( self->playback.pcm ) - { - PaAlsaStreamComponent_Terminate( &self->playback ); - } - - PaUtil_FreeMemory( self->pfds ); - ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 ); - ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 ); - ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 ); - - PaUtil_FreeMemory( self ); -} - -/** Calculate polling timeout - * - * @param frames Time to wait - * @return Polling timeout in milliseconds - */ -static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames ) -{ - assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 ); - /* Period in msecs, rounded up */ - return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate ); -} - -static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamComponent* self, const PaStreamParameters* params, - unsigned long framesPerUserBuffer, double sampleRate, snd_pcm_hw_params_t* hwParams ) -{ - PaError result = paNoError; - unsigned long bufferSize = params->suggestedLatency * sampleRate, framesPerHostBuffer; - int dir = 0; - - { - snd_pcm_uframes_t tmp; - snd_pcm_hw_params_get_buffer_size_min(hwParams, &tmp); - bufferSize = PA_MAX(bufferSize, tmp); - snd_pcm_hw_params_get_buffer_size_max(hwParams, &tmp); - bufferSize = PA_MIN(bufferSize, tmp); - } - - assert(bufferSize > 0); - - if( framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - /* Preferably the host buffer size should be a multiple of the user buffer size */ - - if( bufferSize > framesPerUserBuffer ) - { - snd_pcm_uframes_t remainder = bufferSize % framesPerUserBuffer; - if( remainder > framesPerUserBuffer / 2. ) - bufferSize += framesPerUserBuffer - remainder; - else - bufferSize -= remainder; - - assert( bufferSize % framesPerUserBuffer == 0 ); - } - else if( framesPerUserBuffer % bufferSize != 0 ) - { - /* Find a good compromise between user specified latency and buffer size */ - if( bufferSize > framesPerUserBuffer * .75 ) - { - bufferSize = framesPerUserBuffer; - } - else - { - snd_pcm_uframes_t newSz = framesPerUserBuffer; - while( newSz / 2 >= bufferSize ) - { - if( framesPerUserBuffer % (newSz / 2) != 0 ) - { - /* No use dividing any further */ - break; - } - newSz /= 2; - } - bufferSize = newSz; - } - - assert( framesPerUserBuffer % bufferSize == 0 ); - } - } - - /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period), - finding a combination of period/buffer size which best fits these constraints */ - { - unsigned numPeriods = 4, maxPeriods = 0; - /* It may be that the device only supports 2 periods for instance */ - dir = 0; - ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError ); - assert( maxPeriods > 1 ); - /* One period is not counted as latency */ - maxPeriods -= 1; - numPeriods = PA_MIN( maxPeriods, numPeriods ); - - if( framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - framesPerHostBuffer = framesPerUserBuffer; - if( framesPerHostBuffer < bufferSize ) - { - while( bufferSize / framesPerHostBuffer > numPeriods ) - { - framesPerHostBuffer *= 2; - } - } - else - { - while( bufferSize / framesPerHostBuffer < numPeriods ) - { - if( framesPerUserBuffer % (framesPerHostBuffer / 2) != 0 ) - { - /* Can't be divided any further */ - break; - } - framesPerHostBuffer /= 2; - } - } - - if( framesPerHostBuffer < framesPerUserBuffer ) - { - assert( framesPerUserBuffer % framesPerHostBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) - { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) - framesPerHostBuffer *= 2; - else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) - framesPerHostBuffer /= 2; - } - } - else - { - assert( framesPerHostBuffer % framesPerUserBuffer == 0 ); - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) - { - if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) - framesPerHostBuffer += framesPerUserBuffer; - else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) - framesPerHostBuffer -= framesPerUserBuffer; - } - } - } - else - { - framesPerHostBuffer = bufferSize / numPeriods; - } - } - - assert( framesPerHostBuffer > 0 ); - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), paUnanticipatedHostError ); - self->framesPerBuffer = framesPerHostBuffer; - -error: - return result; -} - -/* We need to determine how many frames per host buffer (period) to use. Our - * goals are to provide the best possible performance, but also to - * honor the requested latency settings as closely as we can. Therefore this - * decision is based on: - * - * - the period sizes that playback and/or capture support. The - * host buffer size has to be one of these. - * - the number of periods that playback and/or capture support. - * - * We want to make period_size*(num_periods-1) to be as close as possible - * to latency*rate for both playback and capture. - * - * This method will determine suitable period sizes for capture and playback handles, and report the maximum number of - * frames per host buffer. The latter is relevant, in case we should be so unfortunate that the period size differs - * between capture and playback. If this should happen, the stream's hostBufferSizeMode attribute will be set to - * paUtilBoundedHostBufferSize, because the best we can do is limit the size of individual host buffers to the upper - * bound. The size of host buffers scheduled for processing should only matter if the user has specified a buffer size, - * but when he/she does we must strive for an optimal configuration. By default we'll opt for a fixed host buffer size, - * which should be fine if the period size is the same for capture and playback. In general, if there is a specified user - * buffer size, this method tries it best to determine a period size which is a multiple of the user buffer size. - * - * The framesPerBuffer attributes of the individual capture and playback components of the stream are set to corresponding - * values determined here. Since these should be reported as - * - * This is one of those blocks of code that will just take a lot of - * refinement to be any good. - * - * In the full-duplex case it is possible that the routine was unable - * to find a number of frames per buffer acceptable to both devices - * TODO: Implement an algorithm to find the value closest to acceptance - * by both devices, to minimize difference between period sizes? - */ -static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double sampleRate, const PaStreamParameters* inputParameters, - const PaStreamParameters* outputParameters, unsigned long framesPerUserBuffer, snd_pcm_hw_params_t* hwParamsCapture, - snd_pcm_hw_params_t* hwParamsPlayback, PaUtilHostBufferSizeMode* hostBufferSizeMode ) -{ - PaError result = paNoError; - unsigned long framesPerHostBuffer = 0; - int dir = 0; - - if( self->capture.pcm && self->playback.pcm ) - { - if( framesPerUserBuffer == paFramesPerBufferUnspecified ) - { - snd_pcm_uframes_t desiredLatency, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize, - minCapture, minPlayback, maxCapture, maxPlayback; - - /* Come up with a common desired latency */ - - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); - minPeriodSize = PA_MAX( minPlayback, minCapture ); - maxPeriodSize = PA_MIN( maxPlayback, maxCapture ); - PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination ); - - desiredLatency = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency ) - * sampleRate); - /* Clamp desiredLatency */ - { - snd_pcm_uframes_t maxBufferSize; - snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback; - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError ); - maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback ); - - desiredLatency = PA_MIN( desiredLatency, maxBufferSize ); - } - - /* Find the closest power of 2 */ - e = ilogb( minPeriodSize ); - if( minPeriodSize & (minPeriodSize - 1) ) - e += 1; - periodSize = (snd_pcm_uframes_t)pow( 2, e ); - - while( periodSize <= maxPeriodSize ) - { - if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && - snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) - break; /* Ok! */ - - periodSize *= 2; - } - - /* 4 periods considered optimal */ - optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); - optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); - - /* Find the closest power of 2 */ - e = ilogb( optimalPeriodSize ); - if( optimalPeriodSize & (optimalPeriodSize - 1) ) - e += 1; - optimalPeriodSize = (snd_pcm_uframes_t)pow( 2, e ); - - while( optimalPeriodSize >= periodSize ) - { - if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 ) - continue; - if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 ) - break; - optimalPeriodSize /= 2; - } - if( optimalPeriodSize > periodSize ) - periodSize = optimalPeriodSize; - - if( periodSize <= maxPeriodSize ) - { - /* Looks good, the periodSize _should_ be acceptable by both devices */ - ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ), - paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ), - paUnanticipatedHostError ); - self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize; - framesPerHostBuffer = periodSize; - } - else - { - /* Unable to find a common period size, oh well */ - optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); - optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); - - self->capture.framesPerBuffer = optimalPeriodSize; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ), - paUnanticipatedHostError ); - self->playback.framesPerBuffer = optimalPeriodSize; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( self->playback.pcm, hwParamsPlayback, &self->playback.framesPerBuffer, &dir ), - paUnanticipatedHostError ); - framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer ); - *hostBufferSizeMode = paUtilBoundedHostBufferSize; - } - } - else - { - /* We choose the simple route and determine a suitable number of frames per buffer for one component of - * the stream, then we hope that this will work for the other component too (it should!). - */ - - unsigned maxPeriods = 0; - PaAlsaStreamComponent* first = &self->capture, * second = &self->playback; - const PaStreamParameters* firstStreamParams = inputParameters; - snd_pcm_hw_params_t* firstHwParams = hwParamsCapture, * secondHwParams = hwParamsPlayback; - - dir = 0; - ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError ); - if( maxPeriods < 4 ) - { - /* The playback component is tricker to get right, try that first */ - first = &self->playback; - second = &self->capture; - firstStreamParams = outputParameters; - firstHwParams = hwParamsPlayback; - secondHwParams = hwParamsCapture; - } - - PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( first, firstStreamParams, framesPerUserBuffer, - sampleRate, firstHwParams ) ); - - second->framesPerBuffer = first->framesPerBuffer; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( second->pcm, secondHwParams, &second->framesPerBuffer, &dir ), - paUnanticipatedHostError ); - if( self->capture.framesPerBuffer == self->playback.framesPerBuffer ) - { - framesPerHostBuffer = self->capture.framesPerBuffer; - } - else - { - framesPerHostBuffer = PA_MAX( self->capture.framesPerBuffer, self->playback.framesPerBuffer ); - *hostBufferSizeMode = paUtilBoundedHostBufferSize; - } - } - } - else /* half-duplex is a slightly simpler case */ - { - if( self->capture.pcm ) - { - PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->capture, inputParameters, framesPerUserBuffer, - sampleRate, hwParamsCapture) ); - framesPerHostBuffer = self->capture.framesPerBuffer; - } - else - { - assert( self->playback.pcm ); - PA_ENSURE( PaAlsaStreamComponent_DetermineFramesPerBuffer( &self->playback, outputParameters, framesPerUserBuffer, - sampleRate, hwParamsPlayback ) ); - framesPerHostBuffer = self->playback.framesPerBuffer; - } - } - - PA_UNLESS( framesPerHostBuffer != 0, paInternalError ); - self->maxFramesPerHostBuffer = framesPerHostBuffer; - -error: - return result; -} - -/** Set up ALSA stream parameters. - * - */ -static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters* - outParams, double sampleRate, unsigned long framesPerUserBuffer, double* inputLatency, double* outputLatency, - PaUtilHostBufferSizeMode* hostBufferSizeMode ) -{ - PaError result = paNoError; - double realSr = sampleRate; - snd_pcm_hw_params_t* hwParamsCapture, * hwParamsPlayback; - - snd_pcm_hw_params_alloca( &hwParamsCapture ); - snd_pcm_hw_params_alloca( &hwParamsPlayback ); - - if( self->capture.pcm ) - PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture, - &realSr ) ); - if( self->playback.pcm ) - PA_ENSURE( PaAlsaStreamComponent_InitialConfigure( &self->playback, outParams, self->primeBuffers, hwParamsPlayback, - &realSr ) ); - - PA_ENSURE( PaAlsaStream_DetermineFramesPerBuffer( self, realSr, inParams, outParams, framesPerUserBuffer, - hwParamsCapture, hwParamsPlayback, hostBufferSizeMode ) ); - - if( self->capture.pcm ) - { - assert( self->capture.framesPerBuffer != 0 ); - PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->capture, hwParamsCapture, inParams, self->primeBuffers, realSr, - inputLatency ) ); - PA_DEBUG(( "%s: Capture period size: %lu, latency: %f\n", __FUNCTION__, self->capture.framesPerBuffer, *inputLatency )); - } - if( self->playback.pcm ) - { - assert( self->playback.framesPerBuffer != 0 ); - PA_ENSURE( PaAlsaStreamComponent_FinishConfigure( &self->playback, hwParamsPlayback, outParams, self->primeBuffers, realSr, - outputLatency ) ); - PA_DEBUG(( "%s: Playback period size: %lu, latency: %f\n", __FUNCTION__, self->playback.framesPerBuffer, *outputLatency )); - } - - /* Should be exact now */ - self->streamRepresentation.streamInfo.sampleRate = realSr; - - /* this will cause the two streams to automatically start/stop/prepare in sync. - * We only need to execute these operations on one of the pair. - * A: We don't want to do this on a blocking stream. - */ - if( self->callbackMode && self->capture.pcm && self->playback.pcm ) - { - int err = snd_pcm_link( self->capture.pcm, self->playback.pcm ); - if( err == 0 ) - self->pcmsSynced = 1; - else - PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) )); - } - - { - unsigned long minFramesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX, - self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX ); - self->pollTimeout = CalculatePollTimeout( self, minFramesPerHostBuffer ); /* Period in msecs, rounded up */ - - /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */ - self->threading.throttledSleepTime = (unsigned long) (minFramesPerHostBuffer / sampleRate / 4 * 1000); - } - - if( self->callbackMode ) - { - /* If the user expects a certain number of frames per callback we will either have to rely on block adaption - * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number - * of host buffer frames with what the user specified */ - if( self->framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - /* self->alignFrames = 1; */ - - /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely - * on block adaption */ - /* - if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm && - self->capture.framesPerBuffer != self->playback.framesPerBuffer) ) - self->useBlockAdaption = 1; - else - self->alignFrames = 1; - */ - } - } - -error: - return result; -} - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback* callback, - void *userData ) -{ - PaError result = paNoError; - PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; - PaAlsaStream *stream = NULL; - PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; - int numInputChannels = 0, numOutputChannels = 0; - PaTime inputLatency, outputLatency; - PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilFixedHostBufferSize; - - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; - - if( inputParameters ) - { - PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); - - numInputChannels = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - if( outputParameters ) - { - PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); - - numOutputChannels = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - /* XXX: Why do we support this anyway? */ - if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL ) - { - PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ )); - framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") ); - } - - PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory ); - PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, - framesPerBuffer, callback, streamFlags, userData ) ); - - PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer, - &inputLatency, &outputLatency, &hostBufferSizeMode ) ); - if( stream->capture.pcm && stream->playback.pcm ) - { - /* In full-duplex mode, host buffers should be of a fixed size unless we were unable to match the period - * sizes of the two handles */ - if( hostBufferSizeMode == paUtilFixedHostBufferSize ) - { - PA_UNLESS( stream->capture.framesPerBuffer == stream->playback.framesPerBuffer, paInternalError ); - } - else - { - assert( hostBufferSizeMode == paUtilBoundedHostBufferSize ); - PA_UNLESS( stream->capture.framesPerBuffer != stream->playback.framesPerBuffer, paInternalError ); - } - } - else - { - if( stream->capture.pcm ) - { - PA_UNLESS( stream->maxFramesPerHostBuffer == stream->capture.framesPerBuffer, paInternalError ); - } - else - { - PA_UNLESS( stream->maxFramesPerHostBuffer == stream->playback.framesPerBuffer, paInternalError ); - } - } - hostInputSampleFormat = stream->capture.hostSampleFormat; - hostOutputSampleFormat = stream->playback.hostSampleFormat; - - PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - numInputChannels, inputSampleFormat, hostInputSampleFormat, - numOutputChannels, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer, - hostBufferSizeMode, callback, userData ) ); - - /* Ok, buffer processor is initialized, now we can deduce it's latency */ - if( numInputChannels > 0 ) - stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency( - &stream->bufferProcessor ); - if( numOutputChannels > 0 ) - stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency( - &stream->bufferProcessor ); - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - { - PA_DEBUG(( "%s: Stream in error, terminating\n", __FUNCTION__ )); - PaAlsaStream_Terminate( stream ); - } - - return result; -} - -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - PaAlsaStream_Terminate( stream ); - - return result; -} - -static void SilenceBuffer( PaAlsaStream *stream ) -{ - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset; - - snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames ); - snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat ); - snd_pcm_mmap_commit( stream->playback.pcm, offset, frames ); -} - -/** Start/prepare pcm(s) for streaming. - * - * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply - * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and - * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will - * be started automatically as the user writes to output. - * - * The capture pcm, however, will simply be prepared and started. - * - * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode) - */ -static PaError AlsaStart( PaAlsaStream *stream, int priming ) -{ - PaError result = paNoError; - - if( stream->playback.pcm ) - { - if( stream->callbackMode ) - { - if( !priming ) - { - /* Buffer isn't primed, so prepare and silence */ - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - SilenceBuffer( stream ); - } - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); - } - else - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); - /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */ - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); - } - -end: - return result; -error: - goto end; -} - -/** Utility function for determining if pcms are in running state. - * - */ -static int IsRunning( PaAlsaStream *stream ) -{ - int result = 0; - - LockMutex( &stream->stateMtx ); - if( stream->capture.pcm ) - { - snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm ); - - if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN - || capture_state == SND_PCM_STATE_DRAINING ) - { - result = 1; - goto end; - } - } - - if( stream->playback.pcm ) - { - snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm ); - - if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN - || playback_state == SND_PCM_STATE_DRAINING ) - { - result = 1; - goto end; - } - } - -end: - ASSERT_CALL_( UnlockMutex( &stream->stateMtx ), paNoError ); - - return result; -} - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - int streamStarted = 0; /* So we can know wether we need to take the stream down */ - - /* Ready the processor */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* Set now, so we can test for activity further down */ - stream->isActive = 1; - - if( stream->callbackMode ) - { - int res = 0; - PaTime pt = PaUtil_GetTime(); - struct timespec ts; - - PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) ); - streamStarted = 1; - - /* Wait for stream to be started */ - ts.tv_sec = (time_t) floor( pt + 1 ); - ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000); - - /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking - * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely - * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */ - PA_ENSURE( LockMutex( &stream->startMtx ) ); - - /* Due to possible spurious wakeups, we enclose in a loop */ - while( !IsRunning( stream ) && IsStreamActive( s ) && !res ) - { - res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts ); - } - PA_ENSURE( UnlockMutex( &stream->startMtx ) ); - - PA_UNLESS( !res || res == ETIMEDOUT, paInternalError ); - PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt )); - - if( res == ETIMEDOUT ) - { - PA_ENSURE( paTimedOut ); - } - } - else - { - PA_ENSURE( AlsaStart( stream, 0 ) ); - streamStarted = 1; - } - -end: - return result; -error: - if( streamStarted ) - AbortStream( stream ); - stream->isActive = 0; - - goto end; -} - -static PaError AlsaStop( PaAlsaStream *stream, int abort ) -{ - PaError result = paNoError; - - if( abort ) - { - if( stream->playback.pcm ) - { - ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError ); - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError ); - } - - PA_DEBUG(( "%s: Dropped frames\n", __FUNCTION__ )); - } - else - { - if( stream->playback.pcm ) - { - ENSURE_( snd_pcm_nonblock( stream->playback.pcm, 0 ), paUnanticipatedHostError ); - if( snd_pcm_drain( stream->playback.pcm ) < 0 ) - { - PA_DEBUG(( "%s: Draining playback handle failed!\n", __FUNCTION__ )); - } - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - /* We don't need to retrieve any remaining frames */ - if( snd_pcm_drop( stream->capture.pcm ) < 0 ) - { - PA_DEBUG(( "%s: Draining capture handle failed!\n", __FUNCTION__ )); - } - } - } - -end: - return result; -error: - goto end; -} - -/** Stop or abort stream. - * - * If a stream is in callback mode we will have to inspect wether the background thread has - * finished, or we will have to take it out. In either case we join the thread before - * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish - * buffers (drain) - * - * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function - */ -static PaError RealStop( PaAlsaStream *stream, int abort ) -{ - PaError result = paNoError; - - /* First deal with the callback thread, cancelling and/or joining - * it if necessary - */ - if( stream->callbackMode ) - { - PaError threadRes, watchdogRes; - stream->callbackAbort = abort; - - if( !abort ) - { - PA_DEBUG(( "Stopping callback\n" )); - stream->callbackStop = 1; - } - PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) ); - if( threadRes != paNoError ) - PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); - if( watchdogRes != paNoError ) - PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes )); - - stream->callbackStop = 0; /* The deed is done */ - stream->callback_finished = 0; - } - else - { - PA_ENSURE( AlsaStop( stream, abort ) ); - } - - stream->isActive = 0; - -end: - return result; - -error: - goto end; -} - -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaAlsaStream *) s, 0 ); -} - -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaAlsaStream * ) s, 1 ); -} - -/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback - * returning !paContinue is not considered) - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream *)s; - - /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */ - return !IsStreamActive( s ) && !stream->callback_finished; -} - -static PaError IsStreamActive( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - return stream->isActive; -} - -static PaTime GetStreamTime( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - - snd_timestamp_t timestamp; - snd_pcm_status_t *status; - snd_pcm_status_alloca( &status ); - - /* TODO: what if we have both? does it really matter? */ - - /* TODO: if running in callback mode, this will mean - * libasound routines are being called from multiple threads. - * need to verify that libasound is thread-safe. */ - - if( stream->capture.pcm ) - { - snd_pcm_status( stream->capture.pcm, status ); - } - else if( stream->playback.pcm ) - { - snd_pcm_status( stream->playback.pcm, status ); - } - - snd_pcm_status_get_tstamp( status, ×tamp ); - return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0; -} - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - -static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ) -{ - unsigned long approx = (unsigned long) sampleRate; - int dir = 0; - double fraction = sampleRate - approx; - - assert( pcm && hwParams ); - - if( fraction > 0.0 ) - { - if( fraction > 0.5 ) - { - ++approx; - dir = -1; - } - else - dir = 1; - } - - return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ); -} - -/* Return exact sample rate in param sampleRate */ -static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ) -{ - unsigned int num, den; - int err; - - assert( hwParams ); - - err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); - *sampleRate = (double) num / den; - - return err; -} - -/* Utility functions for blocking/callback interfaces */ - -/* Atomic restart of stream (we don't want the intermediate state visible) */ -static PaError AlsaRestart( PaAlsaStream *stream ) -{ - PaError result = paNoError; - - PA_ENSURE( LockMutex( &stream->stateMtx ) ); - PA_ENSURE( AlsaStop( stream, 0 ) ); - PA_ENSURE( AlsaStart( stream, 0 ) ); - - PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ )); - -error: - PA_ENSURE( UnlockMutex( &stream->stateMtx ) ); - - return result; -} - -/** Recover from xrun state. - * - */ -static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self ) -{ - PaError result = paNoError; - snd_pcm_status_t *st; - PaTime now = PaUtil_GetTime(); - snd_timestamp_t t; - - snd_pcm_status_alloca( &st ); - - if( self->playback.pcm ) - { - snd_pcm_status( self->playback.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) - { - snd_pcm_status_get_trigger_tstamp( st, &t ); - self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); - } - } - if( self->capture.pcm ) - { - snd_pcm_status( self->capture.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) - { - snd_pcm_status_get_trigger_tstamp( st, &t ); - self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); - } - } - - PA_ENSURE( AlsaRestart( self ) ); - -end: - return result; -error: - goto end; -} - -/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout. - * - */ -static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll ) -{ - PaError result = paNoError; - snd_pcm_sframes_t delay, margin; - int err; - const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL; - - *continuePoll = 1; - - if( StreamDirection_In == streamDir ) - { - component = &stream->capture; - otherComponent = &stream->playback; - } - else - { - component = &stream->playback; - otherComponent = &stream->capture; - } - - /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */ - if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 ) - { - if( err == -EPIPE ) - { - /* Xrun */ - *continuePoll = 0; - goto error; - } - - ENSURE_( err, paUnanticipatedHostError ); - } - - if( StreamDirection_Out == streamDir ) - { - /* Number of eligible frames before capture overrun */ - delay = otherComponent->bufferSize - delay; - } - margin = delay - otherComponent->framesPerBuffer / 2; - - if( margin < 0 ) - { - PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" )); - *continuePoll = 0; - } - else if( margin < otherComponent->framesPerBuffer ) - { - *pollTimeout = CalculatePollTimeout( stream, margin ); - PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n", - __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout )); - } - -error: - return result; -} - -/* Callback interface */ - -static void OnExit( void *data ) -{ - PaAlsaStream *stream = (PaAlsaStream *) data; - - assert( data ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */ - PA_DEBUG(( "%s: Stopping ALSA handles\n", __FUNCTION__ )); - AlsaStop( stream, stream->callbackAbort ); - stream->callbackAbort = 0; /* Clear state */ - - PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ )); - - /* Eventually notify user all buffers have played */ - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - stream->isActive = 0; -} - -static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo ) -{ - snd_pcm_status_t *capture_status, *playback_status; - snd_timestamp_t capture_timestamp, playback_timestamp; - PaTime capture_time = 0., playback_time = 0.; - - snd_pcm_status_alloca( &capture_status ); - snd_pcm_status_alloca( &playback_status ); - - if( stream->capture.pcm ) - { - snd_pcm_sframes_t capture_delay; - - snd_pcm_status( stream->capture.pcm, capture_status ); - snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); - - capture_time = capture_timestamp.tv_sec + - ((PaTime)capture_timestamp.tv_usec / 1000000.0); - timeInfo->currentTime = capture_time; - - capture_delay = snd_pcm_status_get_delay( capture_status ); - timeInfo->inputBufferAdcTime = timeInfo->currentTime - - (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; - } - if( stream->playback.pcm ) - { - snd_pcm_sframes_t playback_delay; - - snd_pcm_status( stream->playback.pcm, playback_status ); - snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); - - playback_time = playback_timestamp.tv_sec + - ((PaTime)playback_timestamp.tv_usec / 1000000.0); - - if( stream->capture.pcm ) /* Full duplex */ - { - /* Hmm, we have both a playback and a capture timestamp. - * Hopefully they are the same... */ - if( fabs( capture_time - playback_time ) > 0.01 ) - PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time))); - } - else - timeInfo->currentTime = playback_time; - - playback_delay = snd_pcm_status_get_delay( playback_status ); - timeInfo->outputBufferDacTime = timeInfo->currentTime + - (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; - } -} - -/** Called after buffer processing is finished. - * - * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime. - * - * @param numFrames The number of frames that has been processed - * @param xrun Return whether an xrun has occurred - */ -static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun ) -{ - PaError result = paNoError; - int res; - - /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed - * afterwards - */ - if( !self->ready ) - goto end; - - res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames ); - if( res == -EPIPE || res == -ESTRPIPE ) - { - *xrun = 1; - } - else - { - ENSURE_( res, paUnanticipatedHostError ); - } - -end: -error: - return result; -} - -/* Extract buffer from channel area */ -static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset ) -{ - return (unsigned char *) area->addr + (area->first + offset * area->step) / 8; -} - -/** Do necessary adaption between user and host channels. - * - @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and - duplicating mono information if host outputs come in pairs. - */ -static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames ) -{ - PaError result = paNoError; - unsigned char *p; - int i; - int unusedChans = self->numHostChannels - self->numUserChannels; - unsigned char *src, *dst; - int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0; - - assert( StreamDirection_Out == self->streamDir ); - - if( self->hostInterleaved ) - { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); - unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset ); - - /* Start after the last user channel */ - p = buffer + self->numUserChannels * swidth; - - if( convertMono ) - { - /* Convert the last user channel into stereo pair */ - src = buffer + (self->numUserChannels - 1) * swidth; - for( i = 0; i < numFrames; ++i ) - { - dst = src + swidth; - memcpy( dst, src, swidth ); - src += self->numHostChannels * swidth; - } - - /* Don't touch the channel we just wrote to */ - p += swidth; - --unusedChans; - } - - if( unusedChans > 0 ) - { - /* Silence unused output channels */ - for( i = 0; i < numFrames; ++i ) - { - memset( p, 0, swidth * unusedChans ); - p += self->numHostChannels * swidth; - } - } - } - else - { - /* We extract the last user channel */ - if( convertMono ) - { - ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas + - (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError ); - --unusedChans; - } - if( unusedChans > 0 ) - { - snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames, - self->nativeFormat ); - } - } - -error: - return result; -} - -static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - int xrun = 0; - - if( self->capture.pcm ) - { - PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) ); - } - if( self->playback.pcm ) - { - if( self->playback.numHostChannels > self->playback.numUserChannels ) - PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) ); - PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) ); - } - -error: - *xrunOccurred = xrun; - return result; -} - -/** Update the number of available frames. - * - */ -static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm ); - *xrunOccurred = 0; - - if( -EPIPE == framesAvail ) - { - *xrunOccurred = 1; - framesAvail = 0; - } - else - ENSURE_( framesAvail, paUnanticipatedHostError ); - - *numFrames = framesAvail; - -error: - return result; -} - -/** Fill in pollfd objects. - */ -static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent* self, struct pollfd* pfds ) -{ - PaError result = paNoError; - int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds ); - (void)ret; /* Prevent unused variable warning if asserts are turned off */ - assert( ret == self->nfds ); - - self->ready = 0; - - return result; -} - -/** Examine results from poll(). - * - * @param pfds pollfds to inspect - * @param shouldPoll Should we continue to poll - * @param xrun Has an xrun occurred - */ -static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, struct pollfd* pfds, int* shouldPoll, int* xrun ) -{ - PaError result = paNoError; - unsigned short revents; - - ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError ); - if( revents != 0 ) - { - if( revents & POLLERR ) - { - *xrun = 1; - } - else - self->ready = 1; - - *shouldPoll = 0; - } - -error: - return result; -} - -/** Return the number of available frames for this stream. - * - * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore - * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback. - * - * @param queryCapture Check available for capture - * @param queryPlayback Check available for playback - * @param available The returned number of frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long - *available, int *xrunOccurred ) -{ - PaError result = paNoError; - unsigned long captureFrames, playbackFrames; - *xrunOccurred = 0; - - assert( queryCapture || queryPlayback ); - - if( queryCapture ) - { - assert( self->capture.pcm ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) ); - if( *xrunOccurred ) - goto end; - } - if( queryPlayback ) - { - assert( self->playback.pcm ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) ); - if( *xrunOccurred ) - goto end; - } - - if( queryCapture && queryPlayback ) - { - *available = PA_MIN( captureFrames, playbackFrames ); - /*PA_DEBUG(("capture: %lu, playback: %lu, combined: %lu\n", captureFrames, playbackFrames, *available));*/ - } - else if( queryCapture ) - { - *available = captureFrames; - } - else - { - *available = playbackFrames; - } - -end: -error: - return result; -} - -/** Wait for and report available buffer space from ALSA. - * - * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more. - * Both of these operations can uncover xrun conditions. - * - * @concern Xruns Both polling and querying available frames can report an xrun condition. - * - * @param framesAvail Return the number of available frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred ) -{ - PaError result = paNoError; - int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL; - int pollTimeout = self->pollTimeout; - int xrun = 0; - - assert( self ); - assert( framesAvail ); - - if( !self->callbackMode ) - { - /* In blocking mode we will only wait if necessary */ - PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL, - framesAvail, &xrun ) ); - if( xrun ) - { - goto end; - } - - if( *framesAvail > 0 ) - { - /* Mark pcms ready from poll */ - if( self->capture.pcm ) - self->capture.ready = 1; - if( self->playback.pcm ) - self->playback.ready = 1; - - goto end; - } - } - - while( pollPlayback || pollCapture ) - { - int totalFds = 0; - struct pollfd *capturePfds = NULL, *playbackPfds = NULL; - - pthread_testcancel(); - - if( pollCapture ) - { - capturePfds = self->pfds; - PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) ); - totalFds += self->capture.nfds; - } - if( pollPlayback ) - { - playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0); - PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) ); - totalFds += self->playback.nfds; - } - - if( poll( self->pfds, totalFds, pollTimeout ) < 0 ) - { - /* XXX: Depend on preprocessor condition? */ - if( errno == EINTR ) { /* gdb */ - continue; - } - - /* TODO: Add macro for checking system calls */ - PA_ENSURE( paInternalError ); - } - - /* check the return status of our pfds */ - if( pollCapture ) - { - PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) ); - } - if( pollPlayback ) - { - PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) ); - } - if( xrun ) - { - break; - } - - /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two. - * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will - * stop polling. - */ - if( self->capture.pcm && self->playback.pcm ) - { - if( pollCapture && !pollPlayback ) - { - PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) ); - } - else if( pollPlayback && !pollCapture ) - { - PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) ); - } - } - } - - if( !xrun ) - { - /* Get the number of available frames for the pcms that are marked ready. - * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for - * the other direction is returned. Output is normally preferred over capture however, so capture frames may be - * discarded to avoid overrun unless paNeverDropInput is specified. - */ - int captureReady = self->capture.pcm ? self->capture.ready : 0, - playbackReady = self->playback.pcm ? self->playback.ready : 0; - PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) ); - - if( self->capture.pcm && self->playback.pcm ) - { - if( !self->playback.ready && !self->neverDropInput ) - { - /* Drop input, a period's worth */ - assert( self->capture.ready ); - PaAlsaStreamComponent_EndProcessing( &self->capture, PA_MIN( self->capture.framesPerBuffer, - *framesAvail ), &xrun ); - *framesAvail = 0; - self->capture.ready = 0; - } - } - else if( self->capture.pcm ) - assert( self->capture.ready ); - else - assert( self->playback.ready ); - } - -end: -error: - if( xrun ) - { - /* Recover from the xrun state */ - PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); - *framesAvail = 0; - } - else - { - if( 0 != *framesAvail ) - { - /* If we're reporting frames eligible for processing, one of the handles better be ready */ - PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError ); - } - } - *xrunOccurred = xrun; - - return result; -} - -/** Register per-channel ALSA buffer information with buffer processor. - * - * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the - * number of host and user channels is taken into account. - * - * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames. - */ -static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, - unsigned long *numFrames, int *xrun ) -{ - PaError result = paNoError; - const snd_pcm_channel_area_t *areas, *area; - void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) = - StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel; - unsigned char *buffer, *p; - int i; - unsigned long framesAvail; - - /* This _must_ be called before mmap_begin */ - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) ); - if( *xrun ) - { - *numFrames = 0; - goto end; - } - - ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError ); - - if( self->hostInterleaved ) - { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); - - p = buffer = ExtractAddress( areas, self->offset ); - for( i = 0; i < self->numUserChannels; ++i ) - { - /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */ - setChannel( bp, i, p, self->numHostChannels ); - p += swidth; - } - } - else - { - for( i = 0; i < self->numUserChannels; ++i ) - { - area = areas + i; - buffer = ExtractAddress( area, self->offset ); - setChannel( bp, i, buffer, 1 ); - } - } - - /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */ - self->channelAreas = (snd_pcm_channel_area_t *)areas; - -end: -error: - return result; -} - -/** Initiate buffer processing. - * - * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set. - * - * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is - * calculated. - * - * @param numFrames On entrance the number of available frames, on exit the number of received frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0; - int xrun = 0; - - if( *xrunOccurred ) - { - *numFrames = 0; - return result; - } - /* If we got here at least one of the pcm's should be marked ready */ - PA_UNLESS( self->capture.ready || self->playback.ready, paInternalError ); - - /* Extract per-channel ALSA buffer pointers and register them with the buffer processor. - * It is possible that a direction is not marked ready however, because it is out of sync with the other. - */ - if( self->capture.pcm && self->capture.ready ) - { - captureFrames = *numFrames; - PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames, - &xrun ) ); - } - if( self->playback.pcm && self->playback.ready ) - { - playbackFrames = *numFrames; - PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames, - &xrun ) ); - } - if( xrun ) - { - /* Nothing more to do */ - assert( 0 == commonFrames ); - goto end; - } - - commonFrames = PA_MIN( captureFrames, playbackFrames ); - /* assert( commonFrames <= *numFrames ); */ - if( commonFrames > *numFrames ) - { - /* Hmmm ... how come there are more frames available than we requested!? Blah. */ - PA_DEBUG(( "%s: Common available frames are reported to be more than number requested: %lu, %lu, callbackMode: %d\n", __FUNCTION__, - commonFrames, *numFrames, self->callbackMode )); - if( self->capture.pcm ) - { - PA_DEBUG(( "%s: captureFrames: %lu, capture.ready: %d\n", __FUNCTION__, captureFrames, self->capture.ready )); - } - if( self->playback.pcm ) - { - PA_DEBUG(( "%s: playbackFrames: %lu, playback.ready: %d\n", __FUNCTION__, playbackFrames, self->playback.ready )); - } - - commonFrames = 0; - goto end; - } - - /* Inform PortAudio of the number of frames we got. - * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on - * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply - * discard the excess input or call the callback with paOutputOverflow flagged. - */ - if( self->capture.pcm ) - { - if( self->capture.ready ) - { - PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames ); - } - else - { - /* We have input underflow */ - PaUtil_SetNoInput( &self->bufferProcessor ); - } - } - if( self->playback.pcm ) - { - if( self->playback.ready ) - { - PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames ); - } - else - { - /* We have output underflow, but keeping input data (paNeverDropInput) */ - assert( self->neverDropInput ); - assert( self->capture.pcm != NULL ); - PA_DEBUG(( "%s: Setting output buffers to NULL\n", __FUNCTION__ )); - PaUtil_SetNoOutput( &self->bufferProcessor ); - } - } - -end: - *numFrames = commonFrames; -error: - if( xrun ) - { - PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); - *numFrames = 0; - } - *xrunOccurred = xrun; - - return result; -} - -/** Callback thread's function. - * - * Roughly, the workflow can be described in the following way: The number of available frames that can be processed - * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount - * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with - * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can - * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected). - */ -static void *CallbackThreadFunc( void *userData ) -{ - PaError result = paNoError, *pres = NULL; - PaAlsaStream *stream = (PaAlsaStream*) userData; - PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; - snd_pcm_sframes_t startThreshold = 0; - int callbackResult = paContinue; - PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ - int streamStarted = 0; - - assert( stream ); - - callbackThread_ = pthread_self(); - /* Execute OnExit when exiting */ - pthread_cleanup_push( &OnExit, stream ); - - /* Not implemented */ - assert( !stream->primeBuffers ); - - /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the - * stream is started immediately. The latter involves signaling the waiting main thread. - */ - if( stream->primeBuffers ) - { - snd_pcm_sframes_t avail; - - if( stream->playback.pcm ) - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - if( stream->capture.pcm && !stream->pcmsSynced ) - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); - - /* We can't be certain that the whole ring buffer is available for priming, but there should be - * at least one period */ - avail = snd_pcm_avail_update( stream->playback.pcm ); - startThreshold = avail - (avail % stream->playback.framesPerBuffer); - assert( startThreshold >= stream->playback.framesPerBuffer ); - } - else - { - PA_ENSURE( LockMutex( &stream->startMtx ) ); - PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */ - ENSURE_SYSTEM_( pthread_cond_signal( &stream->startCond ), 0 ); - PA_ENSURE( UnlockMutex( &stream->startMtx ) ); - - streamStarted = 1; - } - - while( 1 ) - { - unsigned long framesAvail, framesGot; - int xrun = 0; - - pthread_testcancel(); - - /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively - * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output). - */ - if( stream->callbackStop && paContinue == callbackResult ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - if( paContinue != callbackResult ) - { - stream->callbackAbort = (paAbort == callbackResult); - if( stream->callbackAbort || - /** @concern BlockAdaption: Go on if adaption buffers are empty */ - PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - goto end; - - PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); - /* There is still buffered output that needs to be processed */ - } - - /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have - * a number of available frames. - */ - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - if( xrun ) - { - assert( 0 == framesAvail ); - continue; - - /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due - * to constant xruns, it might be desirable to notify the user of this. - */ - } - - /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the - * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller - * portions at a time than is available as a whole. Therefore we should be prepared to process several - * chunks successively. The buffers are passed to the PA buffer processor. - */ - while( framesAvail > 0 ) - { - xrun = 0; - - pthread_testcancel(); - - /** @concern Xruns Under/overflows are to be reported to the callback */ - if( stream->underrun > 0.0 ) - { - cbFlags |= paOutputUnderflow; - stream->underrun = 0.0; - } - if( stream->overrun > 0.0 ) - { - cbFlags |= paInputOverflow; - stream->overrun = 0.0; - } - if( stream->capture.pcm && stream->playback.pcm ) - { - /** @concern FullDuplex It's possible that only one direction is being processed to avoid an - * under- or overflow, this should be reported correspondingly */ - if( !stream->capture.ready ) - { - cbFlags |= paInputUnderflow; - PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ )); - } - else if( !stream->playback.ready ) - { - cbFlags |= paOutputOverflow; - PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ )); - } - } - - CallbackUpdate( &stream->threading ); - CalculateTimeInfo( stream, &timeInfo ); - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); - cbFlags = 0; - - /* CPU load measurement should include processing activivity external to the stream callback */ - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - framesGot = framesAvail; - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - /* Check the host buffer size against the buffer processor configuration */ - if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize ) - { - /* We've committed to a fixed host buffer size, stick to that */ - framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0; - } - else - { - /* We've committed to an upper bound on the size of host buffers */ - assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize ); - framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer ); - } - framesAvail -= framesGot; - - if( framesGot > 0 ) - { - assert( !xrun ); - PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - } - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); - - if( framesGot == 0 ) - { - if( !xrun ) - { - PA_DEBUG(( "%s: Received less frames than reported from ALSA, framesAvail: %lu\n", __FUNCTION__, - framesAvail )); - } - - /* Go back to polling for more frames */ - break; - - } - - if( paContinue != callbackResult ) - break; - } - } - - /* Match pthread_cleanup_push */ - pthread_cleanup_pop( 1 ); - -end: - PA_DEBUG(( "%s: Thread %d exiting\n ", __FUNCTION__, pthread_self() )); - pthread_exit( pres ); - -error: - /* Pass on error code */ - pres = malloc( sizeof (PaError) ); - *pres = result; - - goto end; -} - -/* Blocking interface */ - -static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long framesGot, framesAvail; - void *userBuffer; - snd_pcm_t *save = stream->playback.pcm; - - assert( stream ); - - PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream ); - - /* Disregard playback */ - stream->playback.pcm = NULL; - - if( stream->overrun > 0. ) - { - result = paInputOverflowed; - stream->overrun = 0.0; - } - - if( stream->capture.userInterleaved ) - { - userBuffer = buffer; - } - else - { - /* Copy channels into local array */ - userBuffer = stream->capture.userBuffers; - memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels ); - } - - /* Start stream if in prepared state */ - if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) - { - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); - } - - while( frames > 0 ) - { - int xrun = 0; - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); - - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - frames -= framesGot; - } - } - -end: - stream->playback.pcm = save; - return result; -error: - goto end; -} - -static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames ) -{ - PaError result = paNoError; - signed long err; - PaAlsaStream *stream = (PaAlsaStream*)s; - snd_pcm_uframes_t framesGot, framesAvail; - const void *userBuffer; - snd_pcm_t *save = stream->capture.pcm; - - assert( stream ); - - PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream ); - - /* Disregard capture */ - stream->capture.pcm = NULL; - - if( stream->underrun > 0. ) - { - result = paOutputUnderflowed; - stream->underrun = 0.0; - } - - if( stream->playback.userInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->playback.userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels ); - } - - while( frames > 0 ) - { - int xrun = 0; - snd_pcm_uframes_t hwAvail; - - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); - - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - frames -= framesGot; - } - - /* Start stream after one period of samples worth */ - - /* Frames residing in buffer */ - PA_ENSURE( err = GetStreamWriteAvailable( stream ) ); - framesAvail = err; - hwAvail = stream->playback.bufferSize - framesAvail; - - if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED && - hwAvail >= stream->playback.framesPerBuffer ) - { - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); - } - } - -end: - stream->capture.pcm = save; - return result; -error: - goto end; -} - -/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long avail; - int xrun; - - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); - if( xrun ) - { - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); - if( xrun ) - PA_ENSURE( paInputOverflowed ); - } - - return (signed long)avail; - -error: - return result; -} - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long avail; - int xrun; - - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) ); - if( xrun ) - { - snd_pcm_sframes_t savail; - - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - savail = snd_pcm_avail_update( stream->playback.pcm ); - - /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */ - ENSURE_( savail, paUnanticipatedHostError ); - - avail = (unsigned long) savail; - } - - return (signed long)avail; - -error: - return result; -} - -/* Extensions */ - -/* Initialize host api specific structure */ -void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ) -{ - info->size = sizeof (PaAlsaStreamInfo); - info->hostApiType = paALSA; - info->version = 1; - info->deviceString = NULL; -} - -void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ) -{ - PaAlsaStream *stream = (PaAlsaStream *) s; - stream->threading.rtSched = enable; -} - -void PaAlsa_EnableWatchdog( PaStream *s, int enable ) -{ - PaAlsaStream *stream = (PaAlsaStream *) s; - stream->threading.useWatchdog = enable; -} diff --git a/portaudio-v19/pa_linux_alsa/pa_linux_alsa.h b/portaudio-v19/pa_linux_alsa/pa_linux_alsa.h deleted file mode 100644 index b9d43c929..000000000 --- a/portaudio-v19/pa_linux_alsa/pa_linux_alsa.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef PA_LINUX_ALSA_H -#define PA_LINUX_ALSA_H - -/* - * $Id$ - * PortAudio Portable Real-Time Audio Library - * ALSA-specific extensions - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/** @file - * ALSA-specific PortAudio API extension header file. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct PaAlsaStreamInfo -{ - unsigned long size; - PaHostApiTypeId hostApiType; - unsigned long version; - - const char *deviceString; -} -PaAlsaStreamInfo; - -void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ); - -void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ); - -void PaAlsa_EnableWatchdog( PaStream *s, int enable ); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/portaudio-v19/pa_mac_core/notes.txt b/portaudio-v19/pa_mac_core/notes.txt deleted file mode 100644 index 4b9aa0f1d..000000000 --- a/portaudio-v19/pa_mac_core/notes.txt +++ /dev/null @@ -1,124 +0,0 @@ -Notes on status of CoreAudio Implementation of PortAudio - -Document Last Updated December 9, 2005 - -There are currently two implementations of PortAudio for Mac Core Audio. - -The original is in pa_mac_core_old.c, and the newer one is in pa_mac_core_auhal.c. -Only pa_mac_core_auhal.c is currently developed and supported as it uses apple's -current core audio technology. To select one of the implementations, copy the -appropriate file to pa_mac_core.c, (eg. "cp pa_mac_core_auhal.c pa_mac_core.c") -then run configure and make as usual. - ----------------------------------------- - -Notes on Original/Default implementation: - -by Phil Burk and Darren Gibbs - -Last updated March 20, 2002 - -WHAT WORKS - -Output with very low latency, <10 msec. -Half duplex input or output. -Full duplex on the same CoreAudio device. -The paFLoat32, paInt16, paInt8, paUInt8 sample formats. -Pa_GetCPULoad() -Pa_StreamTime() - -KNOWN BUGS OR LIMITATIONS - -We do not yet support simultaneous input and output on different -devices. Note that some CoreAudio devices like the Roland UH30 look -like one device but are actually two different CoreAudio devices. The -Built-In audio is typically one CoreAudio device. - -Mono doesn't work. - -DEVICE MAPPING - -CoreAudio devices can support both input and output. But the sample -rates supported may be different. So we have map one or two PortAudio -device to each CoreAudio device depending on whether it supports -input, output or both. - -When we query devices, we first get a list of CoreAudio devices. Then -we scan the list and add a PortAudio device for each CoreAudio device -that supports input. Then we make a scan for output devices. - -------------------------------------------- - -Notes on Newer AUHAL implementation: - -by Bjorn Roche - -Last Updated December 9, 2005 - -Principle of Operation: - -This implementation uses AUHAL for audio I/O. To some extent, it also -operates at the "HAL" Layer, though this behavior can be limited by -platform specific flags (see pa_mac_core.h for details). The default -settings should be reasonable: they don't change the SR of the device and -don't cause interruptions if other devices are using the device. - -Major Software Elements Used: Apple's HAL AUs provide output SR -conversion transparently, however, only on output, so this -implementation uses AudioConverters to convert the sample rate on input. -A PortAudio ring buffer is used to buffer input when sample rate -conversion is required or when separate audio units are used for duplex -IO. Finally, a PortAudio buffer processor is used to convert formats and -provide additional buffers if needed. Internally, interleaved floating -point data streams are used exclusively - the audio unit converts from -the audio hardware's native format to interleaved float PCM and -PortAudio's Buffer processor is used for conversion to user formats. - -Simplex Input: Simplex input uses a single callback. If sample rate -conversion is required, a ring buffer and AudioConverter are used as -well. - -Simplex output: Simplex output uses a single callback. No ring buffer or -audio converter is used because AUHAL does its own output SR conversion. - -Duplex, one device (no SR conversion): When one device is used, a single -callback is used. This achieves very low latency. - -Duplex, separate devices or SR conversion: When SR conversion is -required, data must be buffered before it is converted and data is not -always available at the same times on input and output, so SR conversion -requires the same treatment as separate devices. The input callback -reads data and puts it in the ring buffer. The output callback reads the -data off the ring buffer, into an audio converter and finally to the -buffer processor. - - - -Known issues: - -- Latency: Latency settings are ignored except when doing I/O between different -devices. Still, latency should be very low. - -- Timing info. It reports on stream time, but I'm probably doing something -wrong since patest_sine_time often reports negative latency numbers. - -- xrun detection: The only xrun detection performed is when reading -and writing the ring buffer. There is probably more that can be done. - -- abort/stop issues: stopping a stream is always a complete operation, -but latency should be low enough to make the lack of a separate abort -unnecessary. Apple clarifies its AudioOutputUnitStop() call here: -http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html - -- blocking interface: Not implemented. - -- multichannel: I don't have a multichannel IO box, so I didn't test it. -It should work though ;) I haven't done anything with "channel maps" -yet, so it's possible that more needs to be done. - -- sample rate conversion quality: the input audio converter is set to -the highest quality. I don't do anything about the output converter, and -I am not sure how AUHAL manages quality. - -- x86: I haven't tested it on an x86 Mac, but I tried to be aware of it, so -it should work without too much trouble. diff --git a/portaudio-v19/pa_mac_core/pa_mac_core.h b/portaudio-v19/pa_mac_core/pa_mac_core.h deleted file mode 100644 index 9dbe51824..000000000 --- a/portaudio-v19/pa_mac_core/pa_mac_core.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Mac spcific flags for PA. - * portaudio.h should be included before this file. - */ - -/* - * A pointer to a paMacCoreStreamInfo may be passed as - * the hostApiSpecificStreamInfo in the PaStreamParameters struct - * when opening a stream. Use NULL, for the defaults. Note that for - * duplex streams, both infos should be the same or behaviour - * is undefined. - */ -typedef struct paMacCoreStreamInfo -{ - unsigned long size; /**< size of whole structure including this header */ - PaHostApiTypeId hostApiType;/**< host API for which this data is intended */ - unsigned long version; /**< structure version */ - unsigned long flags; /* flags to modify behaviour */ -} paMacCoreStreamInfo; - -/* Use this function to initialize a paMacCoreStreamInfo struct - using the requested flags. */ -void paSetupMacCoreStreamInfo( paMacCoreStreamInfo *data, unsigned long flags ) -{ - bzero( data, sizeof( paMacCoreStreamInfo ) ); - data->size = sizeof( paMacCoreStreamInfo ); - data->hostApiType = paCoreAudio; - data->version = 0x01; - data->flags = flags; -} - -/* - * Here is the struct that shoul - */ - -/* - * The following flags alter the behaviour of PA on the mac platform. - * they can be ORed together. These should work both for opening and - * checking a device. - */ -/* Allows PortAudio to change things like the device's frame size, - * which allows for much lower latency, but might disrupt the device - * if other programs are using it. */ -const unsigned long paMacCore_ChangeDeviceParameters = 0x01; - -/* In combination with the above flag, - * causes the stream opening to fail, unless the exact sample rates - * are supported by the device. */ -const unsigned long paMacCore_FailIfConversionRequired = 0x02; - - - -/* - * Here are some "preset" combinations of flags (above) to get to some - * common configurations. THIS IS OVERKILL, but if more flags are added - * it won't be. - */ -/*This is the default setting: do as much sample rate conversion as possible - * and as little mucking with the device as possible. */ -const unsigned long paMacCorePlayNice = 0x00; -/*This setting is tuned for pro audio apps. It allows SR conversion on input - and output, but it tries to set the appropriate SR on the device.*/ -const unsigned long paMacCorePro = 0x01; - diff --git a/portaudio-v19/pa_mac_core/pa_mac_core_auhal.c b/portaudio-v19/pa_mac_core/pa_mac_core_auhal.c deleted file mode 100644 index c447fd5a3..000000000 --- a/portaudio-v19/pa_mac_core/pa_mac_core_auhal.c +++ /dev/null @@ -1,1934 +0,0 @@ -/* - * This is the AUHAL implementation of portaudio. Hopefully this will - * one day replace pa_mac_core. - * - * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. - * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) - * - * Dominic's code was based on code by Phil Burk, Darren Gibbs, - * Gord Peters, Stephane Letz, and Greg Pfiel. - * - * Bjorn Roche and XO Audio LLC reserve no rights to this code. - * The maintainers of PortAudio may redistribute and modify the code and - * licenses as they deam appropriate. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - @file pa_mac_core - @author Bjorn Roche - @brief AUHAL implementation of PortAudio -*/ - -#include /* strlen(), memcmp() etc. */ - -#include -#include - - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" -#include "../pablio/ringbuffer.h" -#include "pa_mac_core.h" - -#ifndef MIN -#define MIN(a, b) (((a)<(b))?(a):(b)) -#endif - -#ifndef MAX -#define MAX(a, b) (((a)<(b))?(b):(a)) -#endif - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#define ERR(mac_error) PaMacCore_SetError(mac_error, __LINE__, 1 ) -#define WARNING(mac_error) PaMacCore_SetError(mac_error, __LINE__, 0 ) - -/* Help keep track of AUHAL element numbers */ -#define INPUT_ELEMENT (1) -#define OUTPUT_ELEMENT (0) - -/* Normal level of debugging */ -/* - */ -#define MAC_CORE_DEBUG -#ifdef MAC_CORE_DEBUG -# define DBUG(MSG) do { printf("||PaMacCore (AUHAL)|| "); printf MSG ; fflush(stdout); } while(0) -#else -# define DBUG(MSG) -#endif - -/* Very verbose debugging */ -/* -#define MAC_CORE_VERBOSE_DEBUG - */ -#ifdef MAC_CORE_VERBOSE_DEBUG -# define VDBUG(MSG) do { printf("||PaMacCore|| "); printf MSG ; fflush(stdout); } while(0) -#else -# define VDBUG(MSG) -#endif - -#define RING_BUFFER_ADVANCE_DENOMINATOR (4) - -/* Some utilities that sort of belong here, but were getting too unweildy */ -#include "pa_mac_core_utilities.c" -/* Special purpose ring buffer just for pa_mac_core input processing. */ -/* #include "pa_mac_core_input_ring_buffer.c" */ -#include "../pablio/ringbuffer.c" - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static OSStatus AudioIOProc( void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); -/* PaMacAUHAL - host api datastructure specific to this implementation */ -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ - long devCount; - AudioDeviceID *devIds; /*array of all audio devices*/ - AudioDeviceID defaultIn; - AudioDeviceID defaultOut; -} -PaMacAUHAL; - -/* stream data structure specifically for this implementation */ -typedef struct PaMacCoreStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - /* implementation specific data goes here */ - bool bufferProcessorIsInitialized; - AudioUnit inputUnit; - AudioUnit outputUnit; - AudioDeviceID inputDevice; - AudioDeviceID outputDevice; - size_t userInChan; - size_t userOutChan; - size_t inputFramesPerBuffer; - size_t outputFramesPerBuffer; - /* We use this ring buffer when input and out devs are different. */ - RingBuffer inputRingBuffer; - /* We may need to do SR conversion on input. */ - AudioConverterRef inputSRConverter; - /* We need to preallocate an inputBuffer for reading data. */ - AudioBufferList inputAudioBufferList; - AudioTimeStamp startTime; - volatile bool isTimeSet; - volatile PaStreamCallbackFlags xrunFlags; - volatile enum { - STOPPED = 0, /* playback is completely stopped, - and the user has called StopStream(). */ - CALLBACK_STOPPED = 1, /* callback has requested stop, - but user has not yet called StopStream(). */ - STOPPING = 2, /* The stream is in the process of closing. - This state is just used internally; - externally it is indistinguishable from - ACTIVE.*/ - ACTIVE = 3 /* The stream is active and running. */ - } state; - double sampleRate; -} -PaMacCoreStream; - -static PaError OpenAndSetupOneAudioUnit( - const PaStreamParameters *inStreamParams, - const PaStreamParameters *outStreamParams, - const unsigned long requestedFramesPerBuffer, - unsigned long *actualInputFramesPerBuffer, - unsigned long *actualOutputFramesPerBuffer, - const PaMacAUHAL *auhalHostApi, - AudioUnit *audioUnit, - AudioConverterRef *srConverter, - AudioDeviceID *audioDevice, - const double sampleRate, - void *refCon ); - -/* for setting errors. */ -#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) - - - - -/*currently, this is only used in initialization, but it might be modified - to be used when the list of devices changes.*/ -static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) -{ - UInt32 size; - UInt32 propsize; - /* -- free any previous allocations -- */ - if( auhalHostApi->devIds ) - PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); - auhalHostApi->devIds = NULL; - - /* -- figure out how many devices there are -- */ - AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, - &propsize, - NULL ); - auhalHostApi->devCount = propsize / sizeof( AudioDeviceID ); - - VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) ); - - /* -- copy the device IDs -- */ - auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, - propsize ); - if( !auhalHostApi->devIds ) - return paInsufficientMemory; - AudioHardwareGetProperty( kAudioHardwarePropertyDevices, - &propsize, - auhalHostApi->devIds ); -#ifdef MAC_CORE_VERBOSE_DEBUG - { - int i; - for( i=0; idevCount; ++i ) - printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] ); - } -#endif - - size = sizeof(AudioDeviceID); - auhalHostApi->defaultIn = kAudioDeviceUnknown; - auhalHostApi->defaultOut = kAudioDeviceUnknown; - /* FEEDBACK: these calls could fail, in which case default in and out will - be unknown devices or could be undefined. Do I need to be more - rigorous here? */ - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, - &size, - &auhalHostApi->defaultIn); - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, - &size, - &auhalHostApi->defaultOut); - VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) ); - VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) ); - - return paNoError; -} - -static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi, - PaDeviceInfo *deviceInfo, - AudioDeviceID macCoreDeviceId, - int isInput) -{ - UInt32 propSize; - PaError err = paNoError; - UInt32 i; - int numChannels = 0; - AudioBufferList *buflist; - UInt32 frameLatency; - - /* Get the number of channels from the stream configuration. - Fail if we can't get this. */ - - err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); - if (err) - return err; - - buflist = PaUtil_AllocateMemory(propSize); - err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); - if (err) - return err; - - for (i = 0; i < buflist->mNumberBuffers; ++i) - numChannels += buflist->mBuffers[i].mNumberChannels; - - if (isInput) - deviceInfo->maxInputChannels = numChannels; - else - deviceInfo->maxOutputChannels = numChannels; - - /* Get the latency. Don't fail if we can't get this. */ - /* default to something reasonable */ - deviceInfo->defaultLowInputLatency = .01; - deviceInfo->defaultHighInputLatency = .01; - propSize = sizeof(UInt32); - err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); - if (!err) { - double secondLatency = frameLatency / deviceInfo->defaultSampleRate; - if (isInput) { - deviceInfo->defaultLowInputLatency = secondLatency; - deviceInfo->defaultHighInputLatency = secondLatency; - } - else { - deviceInfo->defaultLowOutputLatency = secondLatency; - deviceInfo->defaultHighOutputLatency = secondLatency; - } - } - return paNoError; -} - -static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi, - PaDeviceInfo *deviceInfo, - AudioDeviceID macCoreDeviceId, - PaHostApiIndex hostApiIndex ) -{ - Float64 sampleRate; - char *name; - PaError err = paNoError; - UInt32 propSize; - - memset(deviceInfo, 0, sizeof(deviceInfo)); - - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - /* Get the device name. Fail if we can't get it. */ - err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); - if (err) - return err; - - name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize); - if ( !name ) - return paInsufficientMemory; - err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); - if (err) - return err; - deviceInfo->name = name; - - /* Try to get the default sample rate. Don't fail if we can't get this. */ - propSize = sizeof(Float64); - err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); - if (err) - deviceInfo->defaultSampleRate = 0.0; - else - deviceInfo->defaultSampleRate = sampleRate; - - /* Get the maximum number of input and output channels. Fail if we can't get this. */ - - err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1); - if (err) - return err; - - err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0); - if (err) - return err; - - return paNoError; -} - -PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i; - PaMacAUHAL *auhalHostApi; - PaDeviceInfo *deviceInfoArray; - - auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) ); - if( !auhalHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - auhalHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !auhalHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - auhalHostApi->devIds = NULL; - auhalHostApi->devCount = 0; - - /* get the info we need about the devices */ - result = gatherDeviceInfo( auhalHostApi ); - if( result != paNoError ) - goto error; - - *hostApi = &auhalHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paCoreAudio; - (*hostApi)->info.name = "Core Audio"; - - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - - (*hostApi)->info.deviceCount = 0; - - if( auhalHostApi->devCount > 0 ) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < auhalHostApi->devCount; ++i ) - { - int err; - err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], - auhalHostApi->devIds[i], - hostApiIndex ); - if (err == paNoError) - { /* copy some info and set the defaults */ - (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i]; - if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn) - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut) - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - (*hostApi)->info.deviceCount++; - } - else - { /* there was an error. we need to shift the devices down, so we ignore this one */ - int j; - auhalHostApi->devCount--; - for( j=i; jdevCount; ++j ) - auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1]; - i--; - } - } - } - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, - IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, - IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, - GetStreamReadAvailable, - GetStreamWriteAvailable ); - - return result; - -error: - if( auhalHostApi ) - { - if( auhalHostApi->allocations ) - { - PaUtil_FreeAllAllocations( auhalHostApi->allocations ); - PaUtil_DestroyAllocationGroup( auhalHostApi->allocations ); - } - - PaUtil_FreeMemory( auhalHostApi ); - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - TODO: Double check that everything is handled by alloc group - */ - - if( auhalHostApi->allocations ) - { - PaUtil_FreeAllAllocations( auhalHostApi->allocations ); - PaUtil_DestroyAllocationGroup( auhalHostApi->allocations ); - } - - PaUtil_FreeMemory( auhalHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - /** These first checks are standard PA checks. We do some fancier checks - later. */ - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - } - else - { - outputChannelCount = 0; - } - - /* FEEDBACK */ - /* I think the only way to check a given format SR combo is */ - /* to try opening it. This could be disruptive, is that Okay? */ - /* The alternative is to just read off available sample rates, */ - /* but this will not work %100 of the time (eg, a device that */ - /* supports N output at one rate but only N/2 at a higher rate.)*/ - - /* The following code opens the device with the requested parameters to - see if it works. */ - { - PaError err; - PaStream *s; - err = OpenStream( hostApi, &s, inputParameters, outputParameters, - sampleRate, 1024, 0, (PaStreamCallback *)1, NULL ); - if( err != paNoError && err != paInvalidSampleRate ) - DBUG( ( "OpenStream @ %g returned: %d: %s\n", - (float) sampleRate, err, Pa_GetErrorText( err ) ) ); - if( err ) - return err; - err = CloseStream( s ); - if( err ) { - /* FEEDBACK: is this more serious? should we assert? */ - DBUG( ( "WARNING: could not close Stream. %d: %s\n", - err, Pa_GetErrorText( err ) ) ); - } - } - - return paFormatIsSupported; -} - -static PaError OpenAndSetupOneAudioUnit( - const PaStreamParameters *inStreamParams, - const PaStreamParameters *outStreamParams, - const unsigned long requestedFramesPerBuffer, - unsigned long *actualInputFramesPerBuffer, - unsigned long *actualOutputFramesPerBuffer, - const PaMacAUHAL *auhalHostApi, - AudioUnit *audioUnit, - AudioConverterRef *srConverter, - AudioDeviceID *audioDevice, - const double sampleRate, - void *refCon ) -{ - ComponentDescription desc; - Component comp; - /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/ - AudioStreamBasicDescription desiredFormat; - OSErr result = noErr; - PaError paResult = paNoError; - int line; - UInt32 callbackKey; - AURenderCallbackStruct rcbs; - unsigned long macInputStreamFlags = paMacCorePlayNice; - unsigned long macOutputStreamFlags = paMacCorePlayNice; - - /*change the flags here, if desired, for testing.*/ - /*macInputStreamInfo = macOutputStreamInfo = paMacCore_ChangeDeviceParameters ;*/ - - /* -- handle the degenerate case -- */ - if( !inStreamParams && !outStreamParams ) { - *audioUnit = NULL; - *audioDevice = kAudioDeviceUnknown; - return paNoError; - } - - /* -- get the user's api specific info, if they set any -- */ - if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo ) - macInputStreamFlags= - ((paMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo) - ->flags; - if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo ) - macOutputStreamFlags= - ((paMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo) - ->flags; - - /* - * The HAL AU is a Mac OS style "component". - * the first few steps deal with that. - * Later steps work on a combination of Mac OS - * components and the slightly lower level - * HAL. - */ - - /* -- describe the output type AudioUnit -- */ - /* Note: for the default AudioUnit, we could use the - * componentSubType value kAudioUnitSubType_DefaultOutput; - * but I don't think that's relevant here. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - /* -- find the component -- */ - comp = FindNextComponent( NULL, &desc ); - if( !comp ) - { - DBUG( ( "AUHAL component not found." ) ); - *audioUnit = NULL; - *audioDevice = kAudioDeviceUnknown; - return paUnanticipatedHostError; - } - /* -- open it -- */ - result = OpenAComponent( comp, audioUnit ); - if( result ) - { - DBUG( ( "Failed to open AUHAL component." ) ); - *audioUnit = NULL; - *audioDevice = kAudioDeviceUnknown; - return ERR( result ); - } - /* -- prepare a little error handling logic / hackery -- */ -#define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0) - - /* -- if there is input, we have to explicitly enable input -- */ - if( inStreamParams ) - { - UInt32 enableIO; - enableIO = 1; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Input, - INPUT_ELEMENT, - &enableIO, - sizeof(enableIO) ) ); - } - /* -- if there is no output, we must explicitly disable output -- */ - if( !outStreamParams ) - { - UInt32 enableIO; - enableIO = 0; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Output, - OUTPUT_ELEMENT, - &enableIO, - sizeof(enableIO) ) ); - } - /* -- set the devices -- */ - /* make sure input and output are the same device if we are doing input and - output. */ - if( inStreamParams && outStreamParams ) - assert( outStreamParams->device == inStreamParams->device ); - if( inStreamParams ) - { - *audioDevice = auhalHostApi->devIds[inStreamParams->device] ; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - INPUT_ELEMENT, - audioDevice, - sizeof(AudioDeviceID) ) ); - } - if( outStreamParams ) - { - *audioDevice = auhalHostApi->devIds[outStreamParams->device] ; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - OUTPUT_ELEMENT, - audioDevice, - sizeof(AudioDeviceID) ) ); - } - - /* -- set format -- */ - bzero( &desiredFormat, sizeof(desiredFormat) ); - desiredFormat.mFormatID = kAudioFormatLinearPCM ; - desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; - desiredFormat.mFramesPerPacket = 1; - desiredFormat.mBitsPerChannel = sizeof( float ) * 8; - - result = 0; - /* set device format first, but only touch the device if the user asked */ - if( inStreamParams ) { - /*The callback never calls back if we don't set the FPB */ - /*This seems wierd, because I would think setting anything on the device - would be disruptive.*/ - paResult = setBestFramesPerBuffer( *audioDevice, FALSE, - requestedFramesPerBuffer, - actualInputFramesPerBuffer ); - if( paResult ) goto error; - if( macInputStreamFlags & paMacCore_ChangeDeviceParameters ) { - bool requireExact; - requireExact=macInputStreamFlags&paMacCore_FailIfConversionRequired; - paResult = setBestSampleRateForDevice( *audioDevice, FALSE, - requireExact, sampleRate ); - if( paResult ) goto error; - } - if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer ) - *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ; - } - if( outStreamParams && !inStreamParams ) { - /*The callback never calls back if we don't set the FPB */ - /*This seems wierd, because I would think setting anything on the device - would be disruptive.*/ - paResult = setBestFramesPerBuffer( *audioDevice, TRUE, - requestedFramesPerBuffer, - actualOutputFramesPerBuffer ); - if( paResult ) goto error; - if( macOutputStreamFlags & paMacCore_ChangeDeviceParameters ) { - bool requireExact; - requireExact=macOutputStreamFlags&paMacCore_FailIfConversionRequired; - paResult = setBestSampleRateForDevice( *audioDevice, TRUE, - requireExact, sampleRate ); - if( paResult ) goto error; - } - } - /* now set the format on the Audio Units. */ - if( outStreamParams ) - { - desiredFormat.mSampleRate =sampleRate; - desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount; - desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount; - desiredFormat.mChannelsPerFrame = outStreamParams->channelCount; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - OUTPUT_ELEMENT, - &desiredFormat, - sizeof(AudioStreamBasicDescription) ) ); - } - if( inStreamParams ) - { - AudioStreamBasicDescription sourceFormat; - UInt32 size = sizeof( AudioStreamBasicDescription ); - - /* keep the sample rate of the device, or we confuse AUHAL */ - ERR_WRAP( AudioUnitGetProperty( *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - INPUT_ELEMENT, - &sourceFormat, - &size ) ); - desiredFormat.mSampleRate = sourceFormat.mSampleRate; - desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount; - desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount; - desiredFormat.mChannelsPerFrame = inStreamParams->channelCount; - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - INPUT_ELEMENT, - &desiredFormat, - sizeof(AudioStreamBasicDescription) ) ); - } - /* set the maximumFramesPerSlice */ - /* not doing this causes real problems - (eg. the callback might not be called). The idea of setting both this - and the frames per buffer on the device is that we'll be most likely - to actually get the frame size we requested in the callback with the - minimum latency. */ - if( outStreamParams ) { - UInt32 size = sizeof( *actualOutputFramesPerBuffer ); - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Input, - OUTPUT_ELEMENT, - actualOutputFramesPerBuffer, - sizeof(unsigned long) ) ); - ERR_WRAP( AudioUnitGetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - OUTPUT_ELEMENT, - actualOutputFramesPerBuffer, - &size ) ); - } - if( inStreamParams ) { - /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/ - ERR_WRAP( AudioUnitSetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Output, - INPUT_ELEMENT, - actualInputFramesPerBuffer, - sizeof(unsigned long) ) ); -/* Don't know why this causes problems - ERR_WRAP( AudioUnitGetProperty( *audioUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, //Output, - INPUT_ELEMENT, - actualInputFramesPerBuffer, - &size ) ); -*/ - } - - /* -- if we have input, we may need to setup an SR converter -- */ - /* even if we got the sample rate we asked for, we need to do - the conversion in case another program changes the underlying SR. */ - /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */ - if( inStreamParams ) { - AudioStreamBasicDescription desiredFormat; - AudioStreamBasicDescription sourceFormat; - UInt32 sourceSize = sizeof( sourceFormat ); - bzero( &desiredFormat, sizeof(desiredFormat) ); - desiredFormat.mSampleRate = sampleRate; - desiredFormat.mFormatID = kAudioFormatLinearPCM ; - desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; - desiredFormat.mFramesPerPacket = 1; - desiredFormat.mBitsPerChannel = sizeof( float ) * 8; - desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount; - desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount; - desiredFormat.mChannelsPerFrame = inStreamParams->channelCount; - - /* get the source format */ - ERR_WRAP( AudioUnitGetProperty( - *audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - INPUT_ELEMENT, - &sourceFormat, - &sourceSize ) ); - - if( desiredFormat.mSampleRate != sourceFormat.mSampleRate ) - { - UInt32 value = kAudioConverterQuality_Max; - VDBUG(( "Creating sample rate converter for input" - " to convert from %g to %g\n", - (float)sourceFormat.mSampleRate, - (float)desiredFormat.mSampleRate ) ); - /* create our converter */ - ERR_WRAP( AudioConverterNew( - &sourceFormat, - &desiredFormat, - srConverter ) ); - /* Use highest quality */ - ERR_WRAP( AudioConverterSetProperty( - *srConverter, - kAudioConverterSampleRateConverterQuality, - sizeof( value ), - &value ) ); - } - } - /* -- set IOProc (callback) -- */ - callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback - : kAudioOutputUnitProperty_SetInputCallback ; - rcbs.inputProc = AudioIOProc; - rcbs.inputProcRefCon = refCon; - ERR_WRAP( AudioUnitSetProperty( - *audioUnit, - callbackKey, - kAudioUnitScope_Output, - outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT, - &rcbs, - sizeof(rcbs)) ); - - if( inStreamParams && outStreamParams && *srConverter ) - ERR_WRAP( AudioUnitSetProperty( - *audioUnit, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Output, - INPUT_ELEMENT, - &rcbs, - sizeof(rcbs)) ); - - /*IMPLEMENTME: may need to worry about channel mapping.*/ - - /* initialize the audio unit */ - ERR_WRAP( AudioUnitInitialize(*audioUnit) ); - - if( inStreamParams && outStreamParams ) - VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) ); - else if( inStreamParams ) - VDBUG( ("Opened device %ld for input.\n", *audioDevice ) ); - else if( outStreamParams ) - VDBUG( ("Opened device %ld for output.\n", *audioDevice ) ); - return paNoError; -#undef ERR_WRAP - - error: - CloseComponent( *audioUnit ); - *audioUnit = NULL; - if( result ) - return PaMacCore_SetError( result, line, 1 ); - return paResult; -} - -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; - PaMacCoreStream *stream = 0; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - VDBUG( ("Opening Stream.\n") ); - - /*These first few bits of code are from paSkeleton with few modifications.*/ - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* Host supports interleaved float32 */ - hostInputSampleFormat = paFloat32; - } - else - { - inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* Host supports interleaved float32 */ - hostOutputSampleFormat = paFloat32; - } - else - { - outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */ - } - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - /* If we fail after this point, we my be left in a bad state, with - some data structures setup and others not. So, first thing we - do is initialize everything so that if we fail, we know what hasn't - been touched. - */ - - stream->inputAudioBufferList.mBuffers[0].mData = NULL; - stream->inputRingBuffer.buffer = NULL; - stream->inputSRConverter = NULL; - stream->inputUnit = NULL; - stream->outputUnit = NULL; - stream->inputFramesPerBuffer = 0; - stream->outputFramesPerBuffer = 0; - stream->bufferProcessorIsInitialized = FALSE; - - assert( streamCallback ) ; /* only callback mode is implemented */ - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &auhalHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &auhalHostApi->blockingStreamInterface, streamCallback, userData ); - } - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - /* -- Now we actually open and setup streams. -- */ - if( inputParameters && outputParameters && outputParameters->device == inputParameters->device ) - { /* full duplex. One device. */ - result = OpenAndSetupOneAudioUnit( inputParameters, - outputParameters, - framesPerBuffer, - &(stream->inputFramesPerBuffer), - &(stream->outputFramesPerBuffer), - auhalHostApi, - &(stream->inputUnit), - &(stream->inputSRConverter), - &(stream->inputDevice), - sampleRate, - stream ); - stream->outputUnit = stream->inputUnit; - stream->outputDevice = stream->inputDevice; - if( result != paNoError ) - goto error; - } - else - { /* full duplex, different devices OR simplex */ - result = OpenAndSetupOneAudioUnit( NULL, - outputParameters, - framesPerBuffer, - NULL, - &(stream->outputFramesPerBuffer), - auhalHostApi, - &(stream->outputUnit), - NULL, - &(stream->outputDevice), - sampleRate, - stream ); - if( result != paNoError ) - goto error; - result = OpenAndSetupOneAudioUnit( inputParameters, - NULL, - framesPerBuffer, - &(stream->inputFramesPerBuffer), - NULL, - auhalHostApi, - &(stream->inputUnit), - &(stream->inputSRConverter), - &(stream->inputDevice), - sampleRate, - stream ); - if( result != paNoError ) - goto error; - } - - if( stream->inputUnit ) { - const size_t szfl = sizeof(float); - /* setup the AudioBufferList used for input */ - bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) ); - stream->inputAudioBufferList.mNumberBuffers = 1; - stream->inputAudioBufferList.mBuffers[0].mNumberChannels - = inputChannelCount; - stream->inputAudioBufferList.mBuffers[0].mDataByteSize - = stream->inputFramesPerBuffer*inputChannelCount*szfl; - stream->inputAudioBufferList.mBuffers[0].mData - = (float *) calloc( - stream->inputFramesPerBuffer*inputChannelCount, - szfl ); - if( !stream->inputAudioBufferList.mBuffers[0].mData ) - { - result = paInsufficientMemory; - goto error; - } - - /* - * If input and output devs are different or we are doing SR conversion, - * we also need a - * ring buffer to store inpt data while waiting for output - * data. - */ - if( (stream->outputUnit && stream->inputUnit != stream->outputUnit) - || stream->inputSRConverter ) - { - /* May want the ringSize ot initial position in - ring buffer to depend somewhat on sample rate change */ - /* Calculate an appropriate ring buffer size. It must be at least - 3x framesPerBuffer and 2x suggested latency and it must be a - power of 2. FEEDBACK: too liberal/conservative/another way?*/ - double latency; - int index, i; - void *data; - long ringSize; - if( !outputParameters ) - latency = inputParameters->suggestedLatency; - else - latency = MAX( inputParameters->suggestedLatency, - outputParameters->suggestedLatency ); - ringSize = latency * sampleRate * 2 * inputChannelCount; - VDBUG( ( "suggested latency: %d\n", (int) (latency*sampleRate) ) ); - if( ringSize < stream->inputFramesPerBuffer * 3 ) - ringSize = stream->inputFramesPerBuffer * 3 * inputChannelCount; - if( outputParameters && ringSize < stream->outputFramesPerBuffer * 3 ) - ringSize = stream->outputFramesPerBuffer * 3 * inputChannelCount; - VDBUG(("inFramesPerBuffer:%d\n",(int)stream->inputFramesPerBuffer)); - if( outputParameters ) - VDBUG(("outFramesPerBuffer:%d\n", - (int)stream->outputFramesPerBuffer)); - VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize )); - - /* round up to the next power of 2 */ - index = -1; - for( i=0; i> i & 0x01 ) - index = i; - assert( index > 0 ); - if( ringSize <= ( 0x01 << index ) ) - ringSize = 0x01 << index ; - else - ringSize = 0x01 << ( index + 1 ); - - /*ringSize <<= 4; *//*16x bigger, for testing */ - - VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize )); - - /*now, we need to allocate memory for the ring buffer*/ - data = calloc( ringSize, szfl ); - if( !data ) - { - result = paInsufficientMemory; - goto error; - } - - /* now we can initialize the ring buffer */ - assert( 0 == - RingBuffer_Init( &stream->inputRingBuffer, - ringSize*szfl, data ) ); - /* advance the read point a little, so we are reading from the - middle of the buffer */ - if( stream->outputUnit ) - RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR ); - } - } - - /* -- initialize Buffer Processor -- */ - { - unsigned long maxHostFrames = stream->inputFramesPerBuffer; - if( stream->outputFramesPerBuffer > maxHostFrames ) - maxHostFrames = stream->outputFramesPerBuffer; - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, - hostInputSampleFormat, - outputChannelCount, outputSampleFormat, - hostOutputSampleFormat, - sampleRate, - streamFlags, - framesPerBuffer, - /* If sample rate conversion takes place, the buffer size - will not be known. */ - maxHostFrames, - stream->inputSRConverter - ? paUtilUnknownHostBufferSize - : paUtilBoundedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) - goto error; - } - stream->bufferProcessorIsInitialized = TRUE; - - /* - IMPLEMENT ME: initialise the following fields with estimated or actual - values. - I think this is okay the way it is br 12/1/05 - maybe need to change input latency estimate if IO devs differ - */ - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - stream->sampleRate = sampleRate; - stream->userInChan = inputChannelCount; - stream->userOutChan = outputChannelCount; - - stream->isTimeSet = FALSE; - stream->state = STOPPED; - stream->xrunFlags = 0; - - *s = (PaStream*)stream; - - return result; - -error: - CloseStream( stream ); - return result; -} - -PaTime GetStreamTime( PaStream *s ) -{ - /* FIXME: I am not at all sure this timing info stuff is right. - patest_sine_time reports negative latencies, which is wierd.*/ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - AudioTimeStamp timeStamp; - - if ( !stream->isTimeSet ) - return (PaTime)0; - - if ( stream->outputDevice ) - AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp); - else if ( stream->inputDevice ) - AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp); - else - return (PaTime)0; - - return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->sampleRate; -} - -static void setStreamStartTime( PaMacCoreStream *stream ) -{ - /* FIXME: I am not at all sure this timing info stuff is right. - patest_sine_time reports negative latencies, which is wierd.*/ - if( stream->inputDevice ) - AudioDeviceGetCurrentTime( stream->inputDevice, &stream->startTime); - else - AudioDeviceGetCurrentTime( stream->outputDevice, &stream->startTime); -} - - -static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp) -{ - if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid) - return (timeStamp->mSampleTime / stream->sampleRate); - else - return 0; -} - -#define RING_BUFFER_EMPTY (1000) - -static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter, - UInt32*ioDataSize, - void** outData, - void*inUserData ) -{ - void *dummyData; - long dummySize; - RingBuffer *rb = (RingBuffer *) inUserData; - - assert( sizeof( UInt32 ) == sizeof( long ) ); - if( RingBuffer_GetReadAvailable( rb ) == 0 ) { - *outData = NULL; - *ioDataSize = 0; - return RING_BUFFER_EMPTY; - } - RingBuffer_GetReadRegions( rb, *ioDataSize, - outData, (long *)ioDataSize, - &dummyData, &dummySize ); - - assert( *ioDataSize ); - RingBuffer_AdvanceReadIndex( rb, *ioDataSize ); - - return noErr; -} - -/* - * Called by the AudioUnit API to process audio from the sound card. - * This is where the magic happens. - */ -/* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */ -static OSStatus AudioIOProc( void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData ) -{ - unsigned long framesProcessed = 0; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; - PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon; - const bool isRender = inBusNumber == OUTPUT_ELEMENT; - int callbackResult = paContinue ; - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - if( !stream->isTimeSet ) - setStreamStartTime( stream ); - stream->isTimeSet = TRUE; - - - /* -----------------------------------------------------------------*\ - This output may be useful for debugging, - But printing durring the callback is a bad enough idea that - this is not enabled by enableing the usual debugging calls. - \* -----------------------------------------------------------------*/ - /* - static int renderCount = 0; - static int inputCount = 0; - printf( "------------------- starting reder/input\n" ); - if( isRender ) - printf("Render callback (%d):\t", ++renderCount); - else - printf("Input callback (%d):\t", ++inputCount); - printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount ); - - printf( "--- inBusNumber: %lu\n", inBusNumber ); - printf( "--- inNumberFrames: %lu\n", inNumberFrames ); - printf( "--- %x ioData\n", (unsigned) ioData ); - if( ioData ) - { - int i=0; - printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers ); - for( i=0; imNumberBuffers; ++i ) - printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize ); - } - ----------------------------------------------------------------- */ - - if( isRender ) { - AudioTimeStamp currentTime; - timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp); - AudioDeviceGetCurrentTime(stream->outputDevice, ¤tTime); - timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime); - } - if( isRender && stream->inputUnit == stream->outputUnit ) - timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp); - if( !isRender ) { - AudioTimeStamp currentTime; - timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp); - AudioDeviceGetCurrentTime(stream->inputDevice, ¤tTime); - timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime); - } - - - if( isRender && stream->inputUnit == stream->outputUnit - && !stream->inputSRConverter ) - { - /* --------- Full Duplex, One Device, no SR Conversion ------- - * - * This is the lowest latency case, and also the simplest. - * Input data and output data are available at the same time. - * we do not use the input SR converter or the input ring buffer. - * - */ - OSErr err = 0; - unsigned long frames; - int callbackResult; - - /* -- start processing -- */ - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - stream->xrunFlags ); - stream->xrunFlags = 0; - - /* -- compute frames. do some checks -- */ - assert( ioData->mNumberBuffers == 1 ); - assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan ); - frames = ioData->mBuffers[0].mDataByteSize; - frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels; - /* -- copy and process input data -- */ - err= AudioUnitRender(stream->inputUnit, - ioActionFlags, - inTimeStamp, - INPUT_ELEMENT, - inNumberFrames, - &stream->inputAudioBufferList ); - /* FEEDBACK: I'm not sure what to do when this call fails */ - assert( !err ); - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - stream->inputAudioBufferList.mBuffers[0].mData, - stream->inputAudioBufferList.mBuffers[0].mNumberChannels); - /* -- Copy and process output data -- */ - PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor), - 0, - ioData->mBuffers[0].mData, - ioData->mBuffers[0].mNumberChannels); - /* -- complete processing -- */ - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - else if( isRender ) - { - /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion) - * -- OR Simplex Output - * - * This case handles output data as in the full duplex case, - * and, if there is input data, reads it off the ring buffer - * and into the PA buffer processor. If sample rate conversion - * is required on input, that is done here as well. - */ - unsigned long frames; - - /* Sometimes, when stopping a duplex stream we get erroneous - xrun flags, so if this is our last run, clear the flags. */ - int xrunFlags = stream->xrunFlags; - if( xrunFlags & paInputUnderflow ) - printf( "input underflow.\n" ); - if( xrunFlags & paInputOverflow ) - printf( "input overflow.\n" ); - if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED ) - xrunFlags = 0; - - /* -- start processing -- */ - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - xrunFlags ); - stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */ - - /* -- Copy and process output data -- */ - assert( ioData->mNumberBuffers == 1 ); - frames = ioData->mBuffers[0].mDataByteSize; - frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels; - assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan ); - PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor), - 0, - ioData->mBuffers[0].mData, - ioData->mBuffers[0].mNumberChannels); - - /* -- copy and process input data, and complete processing -- */ - if( stream->inputUnit ) { - const int flsz = sizeof( float ); - /* Here, we read the data out of the ring buffer, through the - audio converter. */ - int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels; - if( stream->inputSRConverter ) - { - OSStatus err; - UInt32 size; - float data[ inChan * frames ]; - size = sizeof( data ); - err = AudioConverterFillBuffer( - stream->inputSRConverter, - ringBufferIOProc, - &stream->inputRingBuffer, - &size, - (void *)&data ); - if( err == RING_BUFFER_EMPTY ) - { /*the ring buffer callback underflowed */ - err = 0; - bzero( ((char *)data) + size, sizeof(data)-size ); - stream->xrunFlags |= paInputUnderflow; - } - ERR( err ); - assert( !err ); - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - else - { - /* Without the AudioConverter is actually a bit more complex - because we have to do a little buffer processing that the - AudioConverter would otherwise handle for us. */ - void *data1, *data2; - long size1, size2; - RingBuffer_GetReadRegions( &stream->inputRingBuffer, - inChan*frames*flsz, - &data1, &size1, - &data2, &size2 ); - if( size1 / ( flsz * inChan ) == frames ) { - /* simplest case: all in first buffer */ - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data1, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1 ); - } else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) { - /*we underflowed. take what data we can, zero the rest.*/ - float data[frames*inChan]; - if( size1 ) - memcpy( data, data1, size1 ); - if( size2 ) - memcpy( data+size1, data2, size2 ); - bzero( data+size1+size2, frames*flsz*inChan - size1 - size2 ); - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - RingBuffer_AdvanceReadIndex( &stream->inputRingBuffer, - size1+size2 ); - /* flag underflow */ - stream->xrunFlags |= paInputUnderflow; - } else { - /*we got all the data, but split between buffers*/ - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), - size1 / ( flsz * inChan ) ); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data1, - inChan ); - PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor), - size2 / ( flsz * inChan ) ); - PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data2, - inChan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1+size2 ); - } - } - } else { - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - - } - else - { - /* ------------------ Input - * - * First, we read off the audio data and put it in the ring buffer. - * if this is an input-only stream, we need to process it more, - * otherwise, we let the output case deal with it. - */ - OSErr err = 0; - int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ; - /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */ - do { - err= AudioUnitRender(stream->inputUnit, - ioActionFlags, - inTimeStamp, - INPUT_ELEMENT, - inNumberFrames, - &stream->inputAudioBufferList ); - if( err == -10874 ) - inNumberFrames /= 2; - } while( err == -10874 && inNumberFrames > 1 ); - /* FEEDBACK: I'm not sure what to do when this call fails */ - ERR( err ); - assert( !err ); - if( stream->inputSRConverter || stream->outputUnit ) - { - /* If this is duplex or we use a converter, put the data - into the ring buffer. */ - long bytesIn, bytesOut; - bytesIn = sizeof( float ) * inNumberFrames * chan; - bytesOut = RingBuffer_Write( &stream->inputRingBuffer, - stream->inputAudioBufferList.mBuffers[0].mData, - bytesIn ); - if( bytesIn != bytesOut ) - stream->xrunFlags |= paInputOverflow ; - } - else - { - /* for simplex input w/o SR conversion, - just pop the data into the buffer processor.*/ - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - stream->xrunFlags ); - stream->xrunFlags = 0; - - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames); - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - stream->inputAudioBufferList.mBuffers[0].mData, - chan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - if( !stream->outputUnit && stream->inputSRConverter ) - { - /* ------------------ Simplex Input w/ SR Conversion - * - * if this is a simplex input stream, we need to read off the buffer, - * do our sample rate conversion and pass the results to the buffer - * processor. - * The logic here is complicated somewhat by the fact that we don't - * know how much data is available, so we loop on reasonably sized - * chunks, and let the BufferProcessor deal with the rest. - * - */ - /*This might be too big or small depending on SR conversion*/ - float data[ chan * inNumberFrames ]; - OSStatus err; - do - { /*Run the buffer processor until we are out of data*/ - UInt32 size; - long f; - - size = sizeof( data ); - err = AudioConverterFillBuffer( - stream->inputSRConverter, - ringBufferIOProc, - &stream->inputRingBuffer, - &size, - (void *)data ); - if( err != RING_BUFFER_EMPTY ) - ERR( err ); - assert( err == 0 || err == RING_BUFFER_EMPTY ); - - f = size / ( chan * sizeof(float) ); - PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f ); - if( f ) - { - PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), - &timeInfo, - stream->xrunFlags ); - stream->xrunFlags = 0; - - PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), - 0, - data, - chan ); - framesProcessed = - PaUtil_EndBufferProcessing( &(stream->bufferProcessor), - &callbackResult ); - } - } while( callbackResult == paContinue && !err ); - } - } - - switch( callbackResult ) - { - case paContinue: break; - case paComplete: - case paAbort: - stream->state = CALLBACK_STOPPED ; - if( stream->outputUnit ) - AudioOutputUnitStop(stream->outputUnit); - if( stream->inputUnit ) - AudioOutputUnitStop(stream->inputUnit); - break; - } - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - return noErr; -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - /* This may be called from a failed OpenStream. - Therefore, each piece of info is treated seperately. */ - PaError result = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - VDBUG( ( "Closing stream.\n" ) ); - - if( stream ) { - if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) { - AudioUnitUninitialize( stream->outputUnit ); - CloseComponent( stream->outputUnit ); - } - stream->outputUnit = NULL; - if( stream->inputUnit ) - { - AudioUnitUninitialize( stream->inputUnit ); - CloseComponent( stream->inputUnit ); - stream->inputUnit = NULL; - } - if( stream->inputRingBuffer.buffer ) - free( stream->inputRingBuffer.buffer ); - stream->inputRingBuffer.buffer = NULL; - /*TODO: is there more that needs to be done on error - from AudioConverterDispose?*/ - if( stream->inputSRConverter ) - ERR( AudioConverterDispose( stream->inputSRConverter ) ); - stream->inputSRConverter = NULL; - if( stream->inputAudioBufferList.mBuffers[0].mData ) - free( stream->inputAudioBufferList.mBuffers[0].mData ); - stream->inputAudioBufferList.mBuffers[0].mData = NULL; - - if( stream->bufferProcessorIsInitialized ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - } - - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - OSErr result = noErr; - VDBUG( ( "Starting stream.\n" ) ); - -#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0) - - /*FIXME: maybe want to do this on close/abort for faster start? */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - if( stream->inputSRConverter ) - ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) ); - - /* -- start -- */ - stream->state = ACTIVE; - if( stream->inputUnit ) { - ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) ); - } - if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) { - ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) ); - } - - return paNoError; -#undef ERR_WRAP -} - - -static PaError StopStream( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - OSErr result = noErr; - VDBUG( ( "Stopping stream.\n" ) ); - - stream->state = STOPPING; - -#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0) - /* -- stop and reset -- */ - if( stream->inputUnit == stream->outputUnit && stream->inputUnit ) - { - ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) ); - ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) ); - ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) ); - } - else - { - if( stream->inputUnit ) - { - ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) ); - ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1)); - } - if( stream->outputUnit ) - { - ERR_WRAP(AudioOutputUnitStop(stream->outputUnit)); - ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0)); - } - } - if( stream->inputRingBuffer.buffer ) { - RingBuffer_Flush( &stream->inputRingBuffer ); - bzero(stream->inputRingBuffer.buffer,stream->inputRingBuffer.bufferSize); - /* advance the write point a little, so we are reading from the - middle of the buffer. We'll need extra at the end because - testing has shown that this helps. */ - if( stream->outputUnit ) - RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, - stream->inputRingBuffer.bufferSize - / RING_BUFFER_ADVANCE_DENOMINATOR ); - } - - stream->isTimeSet = FALSE; - stream->xrunFlags = 0; - stream->state = STOPPED; - - VDBUG( ( "Stream Stopped.\n" ) ); - return paNoError; -#undef ERR_WRAP -} - -static PaError AbortStream( PaStream *s ) -{ - VDBUG( ( "Aborting stream.\n" ) ); - /* We have nothing faster than StopStream. */ - return StopStream(s); -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return stream->state == STOPPED ? 1 : 0; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - return ( stream->state == ACTIVE || stream->state == STOPPING ); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. IMPLEMENTME: no blocking interface yet! -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} diff --git a/portaudio-v19/pa_mac_core/pa_mac_core_old.c b/portaudio-v19/pa_mac_core/pa_mac_core_old.c deleted file mode 100644 index d9a2df26c..000000000 --- a/portaudio-v19/pa_mac_core/pa_mac_core_old.c +++ /dev/null @@ -1,907 +0,0 @@ -/* - * $Id$ - * pa_mac_core.c - * Implementation of PortAudio for Mac OS X CoreAudio - * - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include -#include -#include -#include - -#include "portaudio.h" -#include "pa_trace.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -// ===== constants ===== - -// ===== structs ===== -#pragma mark structs - -// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation -typedef struct PaMacCore_HAR -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - AudioDeviceID *macCoreDeviceIds; -} -PaMacCoreHostApiRepresentation; - -typedef struct PaMacCore_DI -{ - PaDeviceInfo inheritedDeviceInfo; -} -PaMacCoreDeviceInfo; - -// PaMacCoreStream - a stream data structure specifically for this implementation -typedef struct PaMacCore_S -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - int primeStreamUsingCallback; - - AudioDeviceID inputDevice; - AudioDeviceID outputDevice; - - // Processing thread management -------------- -// HANDLE abortEvent; -// HANDLE processingThread; -// DWORD processingThreadId; - - char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle - int processingThreadPriority; - int highThreadPriority; - int throttledThreadPriority; - unsigned long throttledSleepMsecs; - - int isStopped; - volatile int isActive; - volatile int stopProcessing; // stop thread once existing buffers have been returned - volatile int abortProcessing; // stop thread immediately - -// DWORD allBuffersDurationMs; // used to calculate timeouts -} -PaMacCoreStream; - -// Data needed by the CoreAudio callback functions -typedef struct PaMacCore_CD -{ - PaMacCoreStream *stream; - PaStreamCallback *callback; - void *userData; - PaUtilConverter *inputConverter; - PaUtilConverter *outputConverter; - void *inputBuffer; - void *outputBuffer; - int inputChannelCount; - int outputChannelCount; - PaSampleFormat inputSampleFormat; - PaSampleFormat outputSampleFormat; - PaUtilTriangularDitherGenerator *ditherGenerator; -} -PaMacClientData; - -// ===== CoreAudio-PortAudio bridge functions ===== -#pragma mark CoreAudio-PortAudio bridge functions - -// Maps CoreAudio OSStatus codes to PortAudio PaError codes -static PaError conv_err(OSStatus error) -{ - PaError result; - - switch (error) { - case kAudioHardwareNoError: - result = paNoError; break; - case kAudioHardwareNotRunningError: - result = paInternalError; break; - case kAudioHardwareUnspecifiedError: - result = paInternalError; break; - case kAudioHardwareUnknownPropertyError: - result = paInternalError; break; - case kAudioHardwareBadPropertySizeError: - result = paInternalError; break; - case kAudioHardwareIllegalOperationError: - result = paInternalError; break; - case kAudioHardwareBadDeviceError: - result = paInvalidDevice; break; - case kAudioHardwareBadStreamError: - result = paBadStreamPtr; break; - case kAudioHardwareUnsupportedOperationError: - result = paInternalError; break; - case kAudioDeviceUnsupportedFormatError: - result = paSampleFormatNotSupported; break; - case kAudioDevicePermissionsError: - result = paDeviceUnavailable; break; - default: - result = paInternalError; - } - - return result; -} - -/* This function is unused -static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate) -{ - struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription)); - streamDescription->mSampleRate = sampleRate; - streamDescription->mFormatID = kAudioFormatLinearPCM; - streamDescription->mFormatFlags = 0; - streamDescription->mFramesPerPacket = 1; - - if (parameters->sampleFormat & paNonInterleaved) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved; - streamDescription->mChannelsPerFrame = 1; - streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat); - streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat); - } - else { - streamDescription->mChannelsPerFrame = parameters->channelCount; - } - - streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame; - streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket; - - if (parameters->sampleFormat & paFloat32) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat; - streamDescription->mBitsPerChannel = 32; - } - else if (parameters->sampleFormat & paInt32) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 32; - } - else if (parameters->sampleFormat & paInt24) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 24; - } - else if (parameters->sampleFormat & paInt16) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 16; - } - else if (parameters->sampleFormat & paInt8) { - streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - streamDescription->mBitsPerChannel = 8; - } - else if (parameters->sampleFormat & paInt32) { - streamDescription->mBitsPerChannel = 8; - } - - return streamDescription; -} -*/ - -static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime) -{ - PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo)); - - timeInfo->inputBufferAdcTime = inputTime->mSampleTime; - timeInfo->currentTime = now->mSampleTime; - timeInfo->outputBufferDacTime = outputTime->mSampleTime; - - return timeInfo; -} - -// ===== support functions ===== -#pragma mark support functions - -static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi) -{ - if( macCoreHostApi->allocations ) - { - PaUtil_FreeAllAllocations( macCoreHostApi->allocations ); - PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations ); - } - - PaUtil_FreeMemory( macCoreHostApi ); -} - -static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput) -{ - UInt32 propSize; - PaError err = paNoError; - UInt32 i; - int numChannels = 0; - AudioBufferList *buflist; - - err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); - buflist = PaUtil_AllocateMemory(propSize); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); - if (!err) { - for (i = 0; i < buflist->mNumberBuffers; ++i) { - numChannels += buflist->mBuffers[i].mNumberChannels; - } - - if (isInput) - deviceInfo->maxInputChannels = numChannels; - else - deviceInfo->maxOutputChannels = numChannels; - - int frameLatency; - propSize = sizeof(UInt32); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); - if (!err) { - double secondLatency = frameLatency / deviceInfo->defaultSampleRate; - if (isInput) { - deviceInfo->defaultLowInputLatency = secondLatency; - deviceInfo->defaultHighInputLatency = secondLatency; - } - else { - deviceInfo->defaultLowOutputLatency = secondLatency; - deviceInfo->defaultHighOutputLatency = secondLatency; - } - } - } - PaUtil_FreeMemory(buflist); - - return err; -} - -static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex ) -{ - PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - PaError err = paNoError; - UInt32 propSize; - - err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); - // FIXME: this allocation should be part of the allocations group - char *name = PaUtil_AllocateMemory(propSize); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); - if (!err) { - deviceInfo->name = name; - } - - Float64 sampleRate; - propSize = sizeof(Float64); - err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); - if (!err) { - deviceInfo->defaultSampleRate = sampleRate; - } - - - // Get channel info - err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1); - err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0); - - return err; -} - -static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaMacCoreDeviceInfo *deviceInfoArray; - - // initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized. - hostApi = &macCoreHostApi->inheritedHostApiRep; - hostApi->info.deviceCount = 0; - hostApi->info.defaultInputDevice = paNoDevice; - hostApi->info.defaultOutputDevice = paNoDevice; - - UInt32 propsize; - AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL); - int numDevices = propsize / sizeof(AudioDeviceID); - hostApi->info.deviceCount = numDevices; - if (numDevices > 0) { - hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); - if( !hostApi->deviceInfos ) - { - return paInsufficientMemory; - } - - // allocate all device info structs in a contiguous block - deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory( - macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices ); - if( !deviceInfoArray ) - { - return paInsufficientMemory; - } - - macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize); - AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds); - - AudioDeviceID defaultInputDevice, defaultOutputDevice; - propsize = sizeof(AudioDeviceID); - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice); - AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice); - - UInt32 i; - for (i = 0; i < numDevices; ++i) { - if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) { - hostApi->info.defaultInputDevice = i; - } - if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) { - hostApi->info.defaultOutputDevice = i; - } - InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex); - hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo); - } - } - - return result; -} - -static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput) -{ - UInt32 propSize = sizeof(AudioStreamBasicDescription); - AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize); - - streamDescription->mSampleRate = sampleRate; - streamDescription->mFormatID = 0; - streamDescription->mFormatFlags = 0; - streamDescription->mBytesPerPacket = 0; - streamDescription->mFramesPerPacket = 0; - streamDescription->mBytesPerFrame = 0; - streamDescription->mChannelsPerFrame = 0; - streamDescription->mBitsPerChannel = 0; - streamDescription->mReserved = 0; - - OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription); - PaUtil_FreeMemory(streamDescription); - return result; -} - -static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount) -{ - int frameSpacing, channelSpacing; - if (destination->inputSampleFormat & paNonInterleaved) { - frameSpacing = 1; - channelSpacing = destination->inputChannelCount; - } - else { - frameSpacing = destination->inputChannelCount; - channelSpacing = 1; - } - - AudioBuffer const *inputBuffer = &source->mBuffers[0]; - void *coreAudioBuffer = inputBuffer->mData; - void *portAudioBuffer = destination->inputBuffer; - UInt32 i, streamNumber, streamChannel; - for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) { - if (streamChannel >= inputBuffer->mNumberChannels) { - ++streamNumber; - inputBuffer = &source->mBuffers[streamNumber]; - coreAudioBuffer = inputBuffer->mData; - streamChannel = 0; - } - destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator); - coreAudioBuffer += sizeof(Float32); - portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing; - } - return noErr; -} - -static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount) -{ - int frameSpacing, channelSpacing; - if (source->outputSampleFormat & paNonInterleaved) { - frameSpacing = 1; - channelSpacing = source->outputChannelCount; - } - else { - frameSpacing = source->outputChannelCount; - channelSpacing = 1; - } - - AudioBuffer *outputBuffer = &destination->mBuffers[0]; - void *coreAudioBuffer = outputBuffer->mData; - void *portAudioBuffer = source->outputBuffer; - UInt32 i, streamNumber, streamChannel; - for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) { - if (streamChannel >= outputBuffer->mNumberChannels) { - ++streamNumber; - outputBuffer = &destination->mBuffers[streamNumber]; - coreAudioBuffer = outputBuffer->mData; - streamChannel = 0; - } - source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL); - coreAudioBuffer += sizeof(Float32); - portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing; - } - return noErr; -} - -static OSStatus AudioIOProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* inClientData) -{ - PaMacClientData *clientData = (PaMacClientData *)inClientData; - PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); - - PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); - - AudioBuffer *outputBuffer = &outOutputData->mBuffers[0]; - unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32)); - - if (clientData->inputBuffer) { - CopyInputData(clientData, inInputData, frameCount); - } - PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); - if (clientData->outputBuffer) { - CopyOutputData(outOutputData, clientData, frameCount); - } - - PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); - - if (result == paComplete || result == paAbort) { - Pa_StopStream(clientData->stream); - } - - PaUtil_FreeMemory( timeInfo ); - return noErr; -} - -// This is not for input-only streams, this is for streams where the input device is different from the output device -static OSStatus AudioInputProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* inClientData) -{ - PaMacClientData *clientData = (PaMacClientData *)inClientData; - PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); - - PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); - - AudioBuffer const *inputBuffer = &inInputData->mBuffers[0]; - unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32)); - - CopyInputData(clientData, inInputData, frameCount); - PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); - - PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); - if( result == paComplete || result == paAbort ) - Pa_StopStream(clientData->stream); - PaUtil_FreeMemory( timeInfo ); - return noErr; -} - -// This is not for output-only streams, this is for streams where the input device is different from the output device -static OSStatus AudioOutputProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* inClientData) -{ - PaMacClientData *clientData = (PaMacClientData *)inClientData; - //PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); - - PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); - - AudioBuffer *outputBuffer = &outOutputData->mBuffers[0]; - unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32)); - - //clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); - - CopyOutputData(outOutputData, clientData, frameCount); - - PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); - return noErr; -} - -static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput) -{ - PaError result = paNoError; - - double actualSampleRate; - UInt32 propSize = sizeof(double); - result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate)); - - result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate)); - - if (result == paNoError && actualSampleRate != sampleRate) { - result = paInvalidSampleRate; - } - - return result; -} - -static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput) -{ - PaError result = paNoError; - UInt32 preferredFramesPerBuffer = framesPerBuffer; - // while (preferredFramesPerBuffer > UINT32_MAX) { - // preferredFramesPerBuffer /= 2; - // } - - UInt32 actualFramesPerBuffer; - UInt32 propSize = sizeof(UInt32); - result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer)); - - result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer)); - - if (result != paNoError) { - // do nothing - } - else if (actualFramesPerBuffer > framesPerBuffer) { - result = paBufferTooSmall; - } - else if (actualFramesPerBuffer < framesPerBuffer) { - result = paBufferTooBig; - } - - return result; -} - -static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput) -{ - PaError err = paNoError; - err = SetSampleRate(device, sampleRate, isInput); - if( err == paNoError ) - err = SetFramesPerBuffer(device, framesPerBuffer, isInput); - return err; -} - -// ===== PortAudio functions ===== -#pragma mark PortAudio functions - -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif // __cplusplus - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; - - CleanUp(macCoreHostApi); -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; - PaDeviceInfo *deviceInfo; - - PaError result = paNoError; - if (inputParameters) { - deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; - if (inputParameters->channelCount > deviceInfo->maxInputChannels) - result = paInvalidChannelCount; - else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) { - result = paInvalidSampleRate; - } - } - if (outputParameters && result == paNoError) { - deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device]; - if (outputParameters->channelCount > deviceInfo->maxOutputChannels) - result = paInvalidChannelCount; - else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) { - result = paInvalidSampleRate; - } - } - - return result; -} - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError err = paNoError; - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi; - PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream)); - stream->isActive = 0; - stream->isStopped = 1; - stream->inputDevice = kAudioDeviceUnknown; - stream->outputDevice = kAudioDeviceUnknown; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - ( (streamCallback) - ? &macCoreHostApi->callbackStreamInterface - : &macCoreHostApi->blockingStreamInterface ), - streamCallback, userData ); - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - *s = (PaStream*)stream; - PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData)); - clientData->stream = stream; - clientData->callback = streamCallback; - clientData->userData = userData; - clientData->inputBuffer = 0; - clientData->outputBuffer = 0; - clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator)); - PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator); - - if (inputParameters != NULL) { - stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device]; - clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags); - clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount); - clientData->inputChannelCount = inputParameters->channelCount; - clientData->inputSampleFormat = inputParameters->sampleFormat; - err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1); - } - - if (err == paNoError && outputParameters != NULL) { - stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device]; - clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags); - clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount); - clientData->outputChannelCount = outputParameters->channelCount; - clientData->outputSampleFormat = outputParameters->sampleFormat; - err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0); - } - - if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) { - AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice; - - AudioDeviceAddIOProc(device, AudioIOProc, clientData); - } - else { - // using different devices for input and output - AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData); - AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData); - } - - return err; -} - -// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted. -static PaError CloseStream( PaStream* s ) -{ - PaError err = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - if (stream->inputDevice != kAudioDeviceUnknown) { - if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { - err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc)); - } - else { - err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc)); - err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc)); - } - } - else { - err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc)); - } - - return err; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError err = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - if (stream->inputDevice != kAudioDeviceUnknown) { - if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { - err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc)); - } - else { - err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc)); - err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc)); - } - } - else { - err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc)); - } - - stream->isActive = 1; - stream->isStopped = 0; - return err; -} - -static PaError AbortStream( PaStream *s ) -{ - PaError err = paNoError; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - if (stream->inputDevice != kAudioDeviceUnknown) { - if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { - err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc)); - } - else { - err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc)); - err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc)); - } - } - else { - err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc)); - } - - stream->isActive = 0; - stream->isStopped = 1; - return err; -} - -static PaError StopStream( PaStream *s ) -{ - // TODO: this should be nicer than abort - return AbortStream(s); -} - -static PaError IsStreamStopped( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return stream->isStopped; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return stream->isActive; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - OSStatus err; - PaTime result; - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp)); - if (stream->inputDevice != kAudioDeviceUnknown) { - err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp); - } - else { - err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp); - } - - result = err ? 0 : timeStamp->mSampleTime; - PaUtil_FreeMemory(timeStamp); - - return result; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaMacCoreStream *stream = (PaMacCoreStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams. - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - return paInternalError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - return paInternalError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - return paInternalError; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - return paInternalError; -} - -// HostAPI-specific initialization function -PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) ); - if( !macCoreHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - macCoreHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !macCoreHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &macCoreHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paCoreAudio; - (*hostApi)->info.name = "CoreAudio"; - - result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex); - if (result != paNoError) { - goto error; - } - - // Set up the proper callbacks to this HostApi's functions - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( macCoreHostApi ) { - CleanUp(macCoreHostApi); - } - - return result; -} diff --git a/portaudio-v19/pa_mac_core/pa_mac_core_utilities.c b/portaudio-v19/pa_mac_core/pa_mac_core_utilities.c deleted file mode 100644 index f2cbd29cb..000000000 --- a/portaudio-v19/pa_mac_core/pa_mac_core_utilities.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * - * pa_mac_core_utilities.c - * - * utilitiy functions for pa_mac_core.c - * - * This functions are more like helper functions than part of the core, - * so I moved them to a separate file so the core code would be cleaner. - * - * by Bjorn Roche. - */ - -/* - * Translates MacOS generated errors into PaErrors - */ -static PaError PaMacCore_SetError(OSStatus error, int line, int isError) -{ - /*FIXME: still need to handle possible ComponentResult values.*/ - /* unfortunately, they don't seem to be documented anywhere.*/ - PaError result; - const char *errorType; - const char *errorText; - - switch (error) { - case kAudioHardwareNoError: - return paNoError; - case kAudioHardwareNotRunningError: - errorText = "Audio Hardware Not Running"; - result = paInternalError; break; - case kAudioHardwareUnspecifiedError: - errorText = "Unspecified Audio Hardware Error"; - result = paInternalError; break; - case kAudioHardwareUnknownPropertyError: - errorText = "Audio Hardware: Unknown Property"; - result = paInternalError; break; - case kAudioHardwareBadPropertySizeError: - errorText = "Audio Hardware: Bad Property Size"; - result = paInternalError; break; - case kAudioHardwareIllegalOperationError: - errorText = "Audio Hardware: Illegal Operation"; - result = paInternalError; break; - case kAudioHardwareBadDeviceError: - errorText = "Audio Hardware: Bad Device"; - result = paInvalidDevice; break; - case kAudioHardwareBadStreamError: - errorText = "Audio Hardware: BadStream"; - result = paBadStreamPtr; break; - case kAudioHardwareUnsupportedOperationError: - errorText = "Audio Hardware: Unsupported Operation"; - result = paInternalError; break; - case kAudioDeviceUnsupportedFormatError: - errorText = "Audio Device: Unsupported Format"; - result = paSampleFormatNotSupported; break; - case kAudioDevicePermissionsError: - errorText = "Audio Device: Permissions Error"; - result = paDeviceUnavailable; break; - /* Audio Unit Errors: http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/audio_units/chapter_5_section_3.html */ - case kAudioUnitErr_InvalidProperty: - errorText = "Audio Unit: Invalid Property"; - result = paInternalError; break; - case kAudioUnitErr_InvalidParameter: - errorText = "Audio Unit: Invalid Parameter"; - result = paInternalError; break; - case kAudioUnitErr_NoConnection: - errorText = "Audio Unit: No Connection"; - result = paInternalError; break; - case kAudioUnitErr_FailedInitialization: - errorText = "Audio Unit: Initialization Failed"; - result = paInternalError; break; - case kAudioUnitErr_TooManyFramesToProcess: - errorText = "Audio Unit: Too Many Frames"; - result = paInternalError; break; - case kAudioUnitErr_IllegalInstrument: - errorText = "Audio Unit: Illegal Instrument"; - result = paInternalError; break; - case kAudioUnitErr_InstrumentTypeNotFound: - errorText = "Audio Unit: Instrument Type Not Found"; - result = paInternalError; break; - case kAudioUnitErr_InvalidFile: - errorText = "Audio Unit: Invalid File"; - result = paInternalError; break; - case kAudioUnitErr_UnknownFileType: - errorText = "Audio Unit: Unknown File Type"; - result = paInternalError; break; - case kAudioUnitErr_FileNotSpecified: - errorText = "Audio Unit: File Not Specified"; - result = paInternalError; break; - case kAudioUnitErr_FormatNotSupported: - errorText = "Audio Unit: Format Not Supported"; - result = paInternalError; break; - case kAudioUnitErr_Uninitialized: - errorText = "Audio Unit: Unitialized"; - result = paInternalError; break; - case kAudioUnitErr_InvalidScope: - errorText = "Audio Unit: Invalid Scope"; - result = paInternalError; break; - case kAudioUnitErr_PropertyNotWritable: - errorText = "Audio Unit: PropertyNotWritable"; - result = paInternalError; break; - case kAudioUnitErr_InvalidPropertyValue: - errorText = "Audio Unit: Invalid Property Value"; - result = paInternalError; break; - case kAudioUnitErr_PropertyNotInUse: - errorText = "Audio Unit: Property Not In Use"; - result = paInternalError; break; - case kAudioUnitErr_Initialized: - errorText = "Audio Unit: Initialized"; - result = paInternalError; break; - case kAudioUnitErr_InvalidOfflineRender: - errorText = "Audio Unit: Invalid Offline Render"; - result = paInternalError; break; - case kAudioUnitErr_Unauthorized: - errorText = "Audio Unit: Unauthorized"; - result = paInternalError; break; - case kAudioUnitErr_CannotDoInCurrentContext: - errorText = "Audio Unit: cannot do in current context"; - result = paInternalError; break; - default: - errorText = "Unknown Error"; - result = paInternalError; - } - - if (isError) - errorType = "Error"; - else - errorType = "Warning"; - - if ((int)error < -99999 || (int)error > 99999) - DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText)); - else - DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText)); - - PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText ); - - return result; -} - - - - -/* - * Durring testing of core audio, I found that serious crashes could occur - * if properties such as sample rate were changed multiple times in rapid - * succession. The function below has some fancy logic to make sure that changes - * are acknowledged before another is requested. That seems to help a lot. - */ - -#include -typedef struct { - bool once; /* I didn't end up using this. bdr */ - pthread_mutex_t mutex; -} MutexAndBool ; - -static OSStatus propertyProc( - AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData ) -{ - MutexAndBool *mab = (MutexAndBool *) inClientData; - mab->once = TRUE; - pthread_mutex_unlock( &(mab->mutex) ); - return noErr; -} - -/* sets the value of the given property and waits for the change to - be acknowledged, and returns the final value, which is not guaranteed - by this function to be the same as the desired value. Obviously, this - function can only be used for data whose input and output are the - same size and format, and their size and format are known in advance.*/ -PaError AudioDeviceSetPropertyNowAndWaitForChange( - AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - UInt32 inPropertyDataSize, - const void *inPropertyData, - void *outPropertyData ) -{ - OSStatus macErr; - int unixErr; - MutexAndBool mab; - UInt32 outPropertyDataSize = inPropertyDataSize; - - /* First, see if it already has that value. If so, return. */ - macErr = AudioDeviceGetProperty( inDevice, inChannel, - isInput, inPropertyID, - &outPropertyDataSize, outPropertyData ); - if( macErr ) - goto failMac2; - if( inPropertyDataSize!=outPropertyDataSize ) - return paInternalError; - if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) - return paNoError; - - /* setup and lock mutex */ - mab.once = FALSE; - unixErr = pthread_mutex_init( &mab.mutex, NULL ); - if( unixErr ) - goto failUnix2; - unixErr = pthread_mutex_lock( &mab.mutex ); - if( unixErr ) - goto failUnix; - - /* add property listener */ - macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc, - &mab ); - if( macErr ) - goto failMac; - /* set property */ - macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel, - isInput, inPropertyID, - inPropertyDataSize, inPropertyData ); - if( macErr ) { - /* we couldn't set the property, so we'll just unlock the mutex - and move on. */ - pthread_mutex_unlock( &mab.mutex ); - } - - /* wait for property to change */ - unixErr = pthread_mutex_lock( &mab.mutex ); - if( unixErr ) - goto failUnix; - - /* now read the property back out */ - macErr = AudioDeviceGetProperty( inDevice, inChannel, - isInput, inPropertyID, - &outPropertyDataSize, outPropertyData ); - if( macErr ) - goto failMac; - /* cleanup */ - AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc ); - unixErr = pthread_mutex_unlock( &mab.mutex ); - if( unixErr ) - goto failUnix2; - unixErr = pthread_mutex_destroy( &mab.mutex ); - if( unixErr ) - goto failUnix2; - - return paNoError; - - failUnix: - pthread_mutex_destroy( &mab.mutex ); - AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc ); - - failUnix2: - DBUG( ("Error #%d while setting a device property: %s\n", unixErr, strerror( unixErr ) ) ); - return paUnanticipatedHostError; - - failMac: - pthread_mutex_destroy( &mab.mutex ); - AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, - inPropertyID, propertyProc ); - failMac2: - return ERR( macErr ); -} - -/* - * Sets the sample rate the HAL device. - * if requireExact: set the sample rate or fail. - * - * otherwise : set the exact sample rate. - * If that fails, check for available sample rates, and choose one - * higher than the requested rate. If there isn't a higher one, - * just use the highest available. - */ -static PaError setBestSampleRateForDevice( const AudioDeviceID device, - const bool isOutput, - const bool requireExact, - const Float64 desiredSrate ) -{ - /*FIXME: changing the sample rate is disruptive to other programs using the - device, so it might be good to offer a custom flag to not change the - sample rate and just do conversion. (in my casual tests, there is - no disruption unless the sample rate really does need to change) */ - const bool isInput = isOutput ? 0 : 1; - Float64 srate; - UInt32 propsize = sizeof( Float64 ); - OSErr err; - AudioValueRange *ranges; - int i=0; - Float64 max = -1; /*the maximum rate available*/ - Float64 best = -1; /*the lowest sample rate still greater than desired rate*/ - VDBUG(("Setting sample rate for device %ld to %g.\n",device,(float)desiredSrate)); - - /* -- try setting the sample rate -- */ - err = AudioDeviceSetPropertyNowAndWaitForChange( - device, 0, isInput, - kAudioDevicePropertyNominalSampleRate, - propsize, &desiredSrate, &srate ); - if( err ) - return err; - - /* -- if the rate agrees, and we got no errors, we are done -- */ - if( !err && srate == desiredSrate ) - return paNoError; - /* -- we've failed if the rates disagree and we are setting input -- */ - if( requireExact ) - return paInvalidSampleRate; - - /* -- generate a list of available sample rates -- */ - err = AudioDeviceGetPropertyInfo( device, 0, isInput, - kAudioDevicePropertyAvailableNominalSampleRates, - &propsize, NULL ); - if( err ) - return ERR( err ); - ranges = (AudioValueRange *)calloc( 1, propsize ); - if( !ranges ) - return paInsufficientMemory; - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyAvailableNominalSampleRates, - &propsize, ranges ); - if( err ) - { - free( ranges ); - return ERR( err ); - } - VDBUG(("Requested sample rate of %g was not available.\n", (float)desiredSrate)); - VDBUG(("%lu Available Sample Rates are:\n",propsize/sizeof(AudioValueRange))); -#ifdef MAC_CORE_VERBOSE_DEBUG - for( i=0; i max ) max = ranges[i].mMaximum; - if( ranges[i].mMinimum > desiredSrate ) { - if( best < 0 ) - best = ranges[i].mMinimum; - else if( ranges[i].mMinimum < best ) - best = ranges[i].mMinimum; - } - } - if( best < 0 ) - best = max; - VDBUG( ("Maximum Rate %g. best is %g.\n", max, best ) ); - free( ranges ); - - /* -- set the sample rate -- */ - propsize = sizeof( best ); - err = AudioDeviceSetPropertyNowAndWaitForChange( - device, 0, isInput, - kAudioDevicePropertyNominalSampleRate, - propsize, &best, &srate ); - if( err ) - return err; - - if( err ) - return ERR( err ); - /* -- if the set rate matches, we are done -- */ - if( srate == best ) - return paNoError; - - /* -- otherwise, something wierd happened: we didn't set the rate, and we got no errors. Just bail. */ - return paInternalError; -} - - -/* - Attempts to set the requestedFramesPerBuffer. If it can't set the exact - value, it settles for something smaller if available. If nothing smaller - is available, it uses the smallest available size. - actualFramesPerBuffer will be set to the actual value on successful return. - OK to pass NULL to actualFramesPerBuffer. - The logic is very simmilar too setBestSampleRate only failure here is - not usually catastrophic. -*/ -static PaError setBestFramesPerBuffer( const AudioDeviceID device, - const bool isOutput, - unsigned long requestedFramesPerBuffer, - unsigned long *actualFramesPerBuffer ) -{ - UInt32 afpb; - const bool isInput = !isOutput; - UInt32 propsize = sizeof(UInt32); - OSErr err; - Float64 min = -1; /*the min blocksize*/ - Float64 best = -1; /*the best blocksize*/ - int i=0; - AudioValueRange *ranges; - - if( actualFramesPerBuffer == NULL ) - actualFramesPerBuffer = &afpb; - - - /* -- try and set exact FPB -- */ - err = AudioDeviceSetProperty( device, NULL, 0, isInput, - kAudioDevicePropertyBufferFrameSize, - propsize, &requestedFramesPerBuffer); - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferFrameSize, - &propsize, actualFramesPerBuffer); - if( err ) - return ERR( err ); - if( *actualFramesPerBuffer == requestedFramesPerBuffer ) - return paNoError; /* we are done */ - - /* -- fetch available block sizes -- */ - err = AudioDeviceGetPropertyInfo( device, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &propsize, NULL ); - if( err ) - return ERR( err ); - ranges = (AudioValueRange *)calloc( 1, propsize ); - if( !ranges ) - return paInsufficientMemory; - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferSizeRange, - &propsize, ranges ); - if( err ) - { - free( ranges ); - return ERR( err ); - } - VDBUG(("Requested block size of %lu was not available.\n", - requestedFramesPerBuffer )); - VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange))); -#ifdef MAC_CORE_VERBOSE_DEBUG - for( i=0; i best ) - best = ranges[i].mMaximum; - } - } - if( best == -1 ) - best = min; - VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) ); - free( ranges ); - - /* --- set the buffer size (ignore errors) -- */ - requestedFramesPerBuffer = (UInt32) best ; - propsize = sizeof( UInt32 ); - err = AudioDeviceSetProperty( device, NULL, 0, isInput, - kAudioDevicePropertyBufferSize, - propsize, &requestedFramesPerBuffer ); - /* --- read the property to check that it was set -- */ - err = AudioDeviceGetProperty( device, 0, isInput, - kAudioDevicePropertyBufferSize, - &propsize, actualFramesPerBuffer ); - - if( err ) - return ERR( err ); - - return paNoError; -} diff --git a/portaudio-v19/pa_mac_sm/pa_mac_sm.c b/portaudio-v19/pa_mac_sm/pa_mac_sm.c deleted file mode 100644 index 7a8e03009..000000000 --- a/portaudio-v19/pa_mac_sm/pa_mac_sm.c +++ /dev/null @@ -1,1656 +0,0 @@ -/* - * $Id$ - * Portable Audio I/O Library for Macintosh - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Phil Burk - * - * Special thanks to Chris Rolfe for his many helpful suggestions, bug fixes, - * and code contributions. - * Thanks also to Tue Haste Andersen, Alberto Ricci, Nico Wald, - * Roelf Toxopeus and Tom Erbe for testing the code and making - * numerous suggestions. - * - * 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 - PLB20010415 - ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID - PLB20010415 - Device Scan was crashing for anything other than siBadSoundInDevice, but some Macs may return other errors! - PLB20010420 - Fix TIMEOUT in record mode. - PLB20010420 - Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON - PLB20010907 - Pass unused event to WaitNextEvent to prevent Mac OSX crash. Thanks Dominic Mazzoni. - PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. - PLB20011009 - Use NewSndCallBackUPP() for CARBON - PLB20020417 - I used to call Pa_GetMinNumBuffers() which doesn't take into account the - variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will - give lower latency when virtual memory is turned off. - Thanks Kristoffer Jensen and Georgios Marentakis for spotting this bug. - PLB20020423 - Use new method to calculate CPU load similar to other ports. Based on num frames calculated. - Fixed Pa_StreamTime(). Now estimates how many frames have played based on MicroSecond timer. - Added PA_MAX_USAGE_ALLOWED to prevent Mac form hanging when CPU load approaches 100%. - PLB20020424 - Fixed return value in Pa_StreamTime -*/ - -/* -COMPATIBILITY -This Macintosh implementation is designed for use with Mac OS 7, 8 and -9 on PowerMacs, and OS X if compiled with CARBON - -OUTPUT -A circular array of CmpSoundHeaders is used as a queue. For low latency situations -there will only be two small buffers used. For higher latency, more and larger buffers -may be used. -To play the sound we use SndDoCommand() with bufferCmd. Each buffer is followed -by a callbackCmd which informs us when the buffer has been processsed. - -INPUT -The SndInput Manager SPBRecord call is used for sound input. If only -input is used, then the PA user callback is called from the Input completion proc. -For full-duplex, or output only operation, the PA callback is called from the -HostBuffer output completion proc. In that case, input sound is passed to the -callback by a simple FIFO. - -TODO: -O- Add support for native sample data formats other than int16. -O- Review buffer sizing. Should it be based on result of siDeviceBufferInfo query? -O- Determine default devices somehow. -*/ -#include -#include -#include -#include -#include - -/* Mac specific includes */ -#include "OSUtils.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "portaudio.h" -#include "pa_host.h" -#include "pa_trace.h" - -#ifndef FALSE - #define FALSE (0) - #define TRUE (!FALSE) -#endif - -/* #define TARGET_API_MAC_CARBON (1) */ - -/* - * Define maximum CPU load that will be allowed. User callback will - * be skipped if load exceeds this limit. This is to prevent the Mac - * from hanging when the CPU is hogged by the sound thread. - * On my PowerBook G3, the mac hung when I used 94% of CPU ( usage = 0.94 ). - */ -#define PA_MAX_USAGE_ALLOWED (0.92) - -/* Debugging output macros. */ -#define PRINT(x) { printf x; fflush(stdout); } -#define ERR_RPT(x) PRINT(x) -#define DBUG(x) /* PRINT(x) /**/ -#define DBUGX(x) /* PRINT(x) /**/ - -#define MAC_PHYSICAL_FRAMES_PER_BUFFER (512) /* Minimum number of stereo frames per SoundManager double buffer. */ -#define MAC_VIRTUAL_FRAMES_PER_BUFFER (4096) /* Need this many when using Virtual Memory for recording. */ -#define PA_MIN_NUM_HOST_BUFFERS (2) -#define PA_MAX_NUM_HOST_BUFFERS (16) /* Do not exceed!! */ -#define PA_MAX_DEVICE_INFO (32) - -/* Conversions for 16.16 fixed point code. */ -#define DoubleToUnsignedFixed(x) ((UnsignedFixed) ((x) * 65536.0)) -#define UnsignedFixedToDouble(fx) (((double)(fx)) * (1.0/(1<<16))) - -/************************************************************************************/ -/****************** Structures ******************************************************/ -/************************************************************************************/ -/* Use for passing buffers from input callback to output callback for processing. */ -typedef struct MultiBuffer -{ - char *buffers[PA_MAX_NUM_HOST_BUFFERS]; - int numBuffers; - int nextWrite; - int nextRead; -} -MultiBuffer; - -/* Define structure to contain all Macintosh specific data. */ -typedef struct PaHostSoundControl -{ - UInt64 pahsc_EntryCount; - double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ - - /* Use char instead of Boolean for atomic operation. */ - volatile char pahsc_IsRecording; /* Recording in progress. Set by foreground. Cleared by background. */ - volatile char pahsc_StopRecording; /* Signal sent to background. */ - volatile char pahsc_IfInsideCallback; - /* Input */ - SPB pahsc_InputParams; - SICompletionUPP pahsc_InputCompletionProc; - MultiBuffer pahsc_InputMultiBuffer; - int32 pahsc_BytesPerInputHostBuffer; - int32 pahsc_InputRefNum; - /* Output */ - CmpSoundHeader pahsc_SoundHeaders[PA_MAX_NUM_HOST_BUFFERS]; - int32 pahsc_BytesPerOutputHostBuffer; - SndChannelPtr pahsc_Channel; - SndCallBackUPP pahsc_OutputCompletionProc; - int32 pahsc_NumOutsQueued; - int32 pahsc_NumOutsPlayed; - PaTimestamp pahsc_NumFramesDone; - UInt64 pahsc_WhenFramesDoneIncremented; - /* Init Time -------------- */ - int32 pahsc_NumHostBuffers; - int32 pahsc_FramesPerHostBuffer; - int32 pahsc_UserBuffersPerHostBuffer; - int32 pahsc_MinFramesPerHostBuffer; /* Can vary depending on virtual memory usage. */ -} -PaHostSoundControl; - -/* Mac specific device information. */ -typedef struct internalPortAudioDevice -{ - long pad_DeviceRefNum; - long pad_DeviceBufferSize; - Component pad_Identifier; - PaDeviceInfo pad_Info; -} -internalPortAudioDevice; - -/************************************************************************************/ -/****************** Data ************************************************************/ -/************************************************************************************/ -static int sNumDevices = 0; -static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 }; -static int32 sPaHostError = 0; -static int sDefaultOutputDeviceID; -static int sDefaultInputDeviceID; - -/************************************************************************************/ -/****************** Prototypes ******************************************************/ -/************************************************************************************/ -static PaError PaMac_TimeSlice( internalPortAudioStream *past, int16 *macOutputBufPtr ); -static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr ); -static PaError PaMac_RecordNext( internalPortAudioStream *past ); -static void PaMac_StartLoadCalculation( internalPortAudioStream *past ); -static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerBuffer, double sampleRate ); -static double *PaMac_GetSampleRatesFromHandle ( int numRates, Handle h ); -static PaError PaMac_ScanInputDevices( void ); -static PaError PaMac_ScanOutputDevices( void ); -static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad ); -static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad ); -static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader ); -static void PaMac_EndLoadCalculation( internalPortAudioStream *past ); -static void PaMac_PlayNext ( internalPortAudioStream *past, int index ); -static long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index ); -static pascal void PaMac_InputCompletionProc(SPBPtr recParams); -static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCmd); -static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index ); -long PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); -static int Mac_IsVirtualMemoryOn( void ); -static void PToCString(unsigned char* inString, char* outString); -char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf ); -char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf ); -int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf ); -int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf ); -int MultiBuffer_IsWriteable( MultiBuffer *mbuf ); -int MultiBuffer_IsReadable( MultiBuffer *mbuf ); -void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf ); -void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf ); - -/************************************************************************* -** Simple FIFO index control for multiple buffers. -** Read and Write indices range from 0 to 2N-1. -** This allows us to distinguish between full and empty. -*/ -char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf ) -{ - return mbuf->buffers[mbuf->nextWrite % mbuf->numBuffers]; -} -char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf ) -{ - return mbuf->buffers[mbuf->nextRead % mbuf->numBuffers]; -} -int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf ) -{ - return mbuf->nextRead % mbuf->numBuffers; -} -int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf ) -{ - return mbuf->nextWrite % mbuf->numBuffers; -} - -int MultiBuffer_IsWriteable( MultiBuffer *mbuf ) -{ - int bufsFull = mbuf->nextWrite - mbuf->nextRead; - if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers); - return (bufsFull < mbuf->numBuffers); -} -int MultiBuffer_IsReadable( MultiBuffer *mbuf ) -{ - int bufsFull = mbuf->nextWrite - mbuf->nextRead; - if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers); - return (bufsFull > 0); -} -void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf ) -{ - int temp = mbuf->nextRead + 1; - mbuf->nextRead = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp; -} -void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf ) -{ - int temp = mbuf->nextWrite + 1; - mbuf->nextWrite = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp; -} - -/************************************************************************* -** String Utility by Chris Rolfe -*/ -static void PToCString(unsigned char* inString, char* outString) -{ - long i; - for(i=0; isampleRates; - if( (rates != NULL) ) free( rates ); /* MEM_011 */ - dev->sampleRates = NULL; - if( dev->name != NULL ) free( (void *) dev->name ); /* MEM_010 */ - dev->name = NULL; - } - sNumDevices = 0; - return paNoError; -} - -/************************************************************************* - PaHost_Init() is the library initialization function - call this before - using the library. -*/ -PaError PaHost_Init( void ) -{ - PaError err; - NumVersionVariant version; - - version.parts = SndSoundManagerVersion(); - DBUG(("SndSoundManagerVersion = 0x%x\n", version.whole)); - - /* Have we already initialized the device info? */ - err = (PaError) Pa_CountDevices(); - if( err < 0 ) return err; - else return paNoError; -} - -/************************************************************************* - PaMac_ScanOutputDevices() queries the properties of all output devices. -*/ -static PaError PaMac_ScanOutputDevices( void ) -{ - PaError err; - Component identifier=0; - ComponentDescription criteria = { kSoundOutputDeviceType, 0, 0, 0, 0 }; - long numComponents, i; - - /* Search the system linked list for output components */ - numComponents = CountComponents (&criteria); - identifier = 0; - sDefaultOutputDeviceID = sNumDevices; /* FIXME - query somehow */ - for (i = 0; i < numComponents; i++) - { - /* passing nil returns first matching component. */ - identifier = FindNextComponent( identifier, &criteria); - sDevices[sNumDevices].pad_Identifier = identifier; - - /* Set up for default OUTPUT devices. */ - err = PaMac_QueryOutputDeviceInfo( identifier, &sDevices[sNumDevices] ); - if( err < 0 ) return err; - else sNumDevices++; - - } - - return paNoError; -} - -/************************************************************************* - PaMac_ScanInputDevices() queries the properties of all input devices. -*/ -static PaError PaMac_ScanInputDevices( void ) -{ - Str255 deviceName; - int count; - Handle iconHandle; - PaError err; - OSErr oserr; - count = 1; - sDefaultInputDeviceID = sNumDevices; /* FIXME - query somehow */ /* PLB20010415 - was setting sDefaultOutputDeviceID */ - while(true) - { - /* Thanks Chris Rolfe and Alberto Ricci for this trick. */ - oserr = SPBGetIndexedDevice(count++, deviceName, &iconHandle); - DBUG(("PaMac_ScanInputDevices: SPBGetIndexedDevice returned %d\n", oserr )); -#if 1 - /* PLB20010415 - was giving error for anything other than siBadSoundInDevice, but some Macs may return other errors! */ - if(oserr != noErr) break; /* Some type of error is expected when count > devices */ -#else - if(oserr == siBadSoundInDevice) - { /* it's expected when count > devices */ - oserr = noErr; - break; - } - if(oserr != noErr) - { - ERR_RPT(("ERROR: SPBGetIndexedDevice(%d,,) returned %d\n", count-1, oserr )); - sPaHostError = oserr; - return paHostError; - } -#endif - DisposeHandle(iconHandle); /* Don't need the icon */ - - err = PaMac_QueryInputDeviceInfo( deviceName, &sDevices[sNumDevices] ); - DBUG(("PaMac_ScanInputDevices: PaMac_QueryInputDeviceInfo returned %d\n", err )); - if( err < 0 ) return err; - else if( err == 1 ) sNumDevices++; - } - - return paNoError; -} - -/* Sample rate info returned by using siSampleRateAvailable selector in SPBGetDeviceInfo() */ -/* Thanks to Chris Rolfe for help with this query. */ -#pragma options align=mac68k -typedef struct -{ - int16 numRates; - UnsignedFixed (**rates)[]; /* Handle created by SPBGetDeviceInfo */ -} -SRateInfo; -#pragma options align=reset - -/************************************************************************* -** PaMac_QueryOutputDeviceInfo() -** Query information about a named output device. -** Clears contents of ipad and writes info based on queries. -** Return one if OK, -** zero if device cannot be used, -** or negative error. -*/ -static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad ) -{ - int len; - OSErr err; - PaDeviceInfo *dev = &ipad->pad_Info; - SRateInfo srinfo = {0}; - int numRates; - ComponentDescription tempD; - Handle nameH=nil, infoH=nil, iconH=nil; - - memset( ipad, 0, sizeof(internalPortAudioDevice) ); - - dev->structVersion = 1; - dev->maxInputChannels = 0; - dev->maxOutputChannels = 2; - dev->nativeSampleFormats = paInt16; /* FIXME - query to see if 24 or 32 bit data can be handled. */ - - /* Get sample rates supported. */ - err = GetSoundOutputInfo(identifier, siSampleRateAvailable, (Ptr) &srinfo); - if(err != noErr) - { - ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetSoundOutputInfo siSampleRateAvailable returned %d\n", err )); - goto error; - } - numRates = srinfo.numRates; - DBUG(("PaMac_QueryOutputDeviceInfo: srinfo.numRates = 0x%x\n", srinfo.numRates )); - if( numRates == 0 ) - { - dev->numSampleRates = -1; - numRates = 2; - } - else - { - dev->numSampleRates = numRates; - } - dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates ); - /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */ - DisposeHandle((Handle) srinfo.rates); - - /* Device name */ - /* we pass an existing handle for the component name; - we don't care about the info (type, subtype, etc.) or icon, so set them to nil */ - infoH = nil; - iconH = nil; - nameH = NewHandle(0); - if(nameH == nil) return paInsufficientMemory; - err = GetComponentInfo(identifier, &tempD, nameH, infoH, iconH); - if (err) - { - ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetComponentInfo returned %d\n", err )); - goto error; - } - len = (*nameH)[0] + 1; - dev->name = (char *) malloc(len); /* MEM_010 */ - if( dev->name == NULL ) - { - DisposeHandle(nameH); - return paInsufficientMemory; - } - else - { - PToCString((unsigned char *)(*nameH), (char *) dev->name); - DisposeHandle(nameH); - } - - DBUG(("PaMac_QueryOutputDeviceInfo: dev->name = %s\n", dev->name )); - return paNoError; - -error: - sPaHostError = err; - return paHostError; - -} - -/************************************************************************* -** PaMac_QueryInputDeviceInfo() -** Query information about a named input device. -** Clears contents of ipad and writes info based on queries. -** Return one if OK, -** zero if device cannot be used, -** or negative error. -*/ -static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad ) -{ - PaError result = paNoError; - int len; - OSErr err; - long mRefNum = 0; - long tempL; - int16 tempS; - Fixed tempF; - PaDeviceInfo *dev = &ipad->pad_Info; - SRateInfo srinfo = {0}; - int numRates; - - memset( ipad, 0, sizeof(internalPortAudioDevice) ); - dev->maxOutputChannels = 0; - - /* Open device based on name. If device is in use, it may not be able to open in write mode. */ - err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum); - if (err) - { - /* If device is in use, it may not be able to open in write mode so try read mode. */ - err = SPBOpenDevice( deviceName, siReadPermission, &mRefNum); - if (err) - { - ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBOpenDevice returned %d\n", err )); - sPaHostError = err; - return paHostError; - } - } - - /* Define macros for printing out device info. */ -#define PrintDeviceInfo(selector,var) \ - err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \ - if (err) { \ - DBUG(("query %s failed\n", #selector )); \ - }\ - else { \ - DBUG(("query %s = 0x%x\n", #selector, var )); \ - } - - PrintDeviceInfo( siContinuous, tempS ); - PrintDeviceInfo( siAsync, tempS ); - PrintDeviceInfo( siNumberChannels, tempS ); - PrintDeviceInfo( siSampleSize, tempS ); - PrintDeviceInfo( siSampleRate, tempF ); - PrintDeviceInfo( siChannelAvailable, tempS ); - PrintDeviceInfo( siActiveChannels, tempL ); - PrintDeviceInfo( siDeviceBufferInfo, tempL ); - - err = SPBGetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL); - if (err == 0) DBUG(("%s = 0x%x\n", "siActiveChannels", tempL )); - /* Can we use this device? */ - err = SPBGetDeviceInfo(mRefNum, siAsync, (Ptr) &tempS); - if (err) - { - ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siAsync returned %d\n", err )); - goto error; - } - if( tempS == 0 ) goto useless; /* Does not support async recording so forget about it. */ - - err = SPBGetDeviceInfo(mRefNum, siChannelAvailable, (Ptr) &tempS); - if (err) - { - ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siChannelAvailable returned %d\n", err )); - goto error; - } - dev->maxInputChannels = tempS; - - /* Get sample rates supported. */ - err = SPBGetDeviceInfo(mRefNum, siSampleRateAvailable, (Ptr) &srinfo); - if (err) - { - ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleRateAvailable returned %d\n", err )); - goto error; - } - - numRates = srinfo.numRates; - DBUG(("numRates = 0x%x\n", numRates )); - if( numRates == 0 ) - { - dev->numSampleRates = -1; - numRates = 2; - } - else - { - dev->numSampleRates = numRates; - } - dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates ); - /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */ - DisposeHandle((Handle) srinfo.rates); - - /* Get size of device buffer. */ - err = SPBGetDeviceInfo(mRefNum, siDeviceBufferInfo, (Ptr) &tempL); - if (err) - { - ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siDeviceBufferInfo returned %d\n", err )); - goto error; - } - ipad->pad_DeviceBufferSize = tempL; - DBUG(("siDeviceBufferInfo = %d\n", tempL )); - - /* Set format based on sample size. */ - err = SPBGetDeviceInfo(mRefNum, siSampleSize, (Ptr) &tempS); - if (err) - { - ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleSize returned %d\n", err )); - goto error; - } - switch( tempS ) - { - case 0x0020: - dev->nativeSampleFormats = paInt32; /* FIXME - warning, code probably won't support this! */ - break; - case 0x0010: - default: /* FIXME - What about other formats? */ - dev->nativeSampleFormats = paInt16; - break; - } - DBUG(("nativeSampleFormats = %d\n", dev->nativeSampleFormats )); - - /* Device name */ - len = deviceName[0] + 1; /* Get length of Pascal string */ - dev->name = (char *) malloc(len); /* MEM_010 */ - if( dev->name == NULL ) - { - result = paInsufficientMemory; - goto cleanup; - } - PToCString(deviceName, (char *) dev->name); - DBUG(("deviceName = %s\n", dev->name )); - result = (PaError) 1; - /* All done so close up device. */ -cleanup: - if( mRefNum ) SPBCloseDevice(mRefNum); - return result; - -error: - if( mRefNum ) SPBCloseDevice(mRefNum); - sPaHostError = err; - return paHostError; - -useless: - if( mRefNum ) SPBCloseDevice(mRefNum); - return (PaError) 0; -} - -/************************************************************************* -** Allocate a double array and fill it with listed sample rates. -*/ -static double * PaMac_GetSampleRatesFromHandle ( int numRates, Handle h ) -{ - OSErr err = noErr; - SInt8 hState; - int i; - UnsignedFixed *fixedRates; - double *rates = (double *) malloc( numRates * sizeof(double) ); /* MEM_011 */ - if( rates == NULL ) return NULL; - /* Save and restore handle state as suggested by TechNote at: - http://developer.apple.com/technotes/tn/tn1122.html - */ - hState = HGetState (h); - if (!(err = MemError ())) - { - HLock (h); - if (!(err = MemError ( ))) - { - fixedRates = (UInt32 *) *h; - for( i=0; i= Pa_CountDevices()) ) return NULL; - return &sDevices[id].pad_Info; -} -/*************************************************************************/ -PaDeviceID Pa_GetDefaultInputDeviceID( void ) -{ - return sDefaultInputDeviceID; -} - -/*************************************************************************/ -PaDeviceID Pa_GetDefaultOutputDeviceID( void ) -{ - return sDefaultOutputDeviceID; -} - -/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ -static void PaMac_StartLoadCalculation( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - UnsignedWide widePad; - if( pahsc == NULL ) return; - /* Query system timer for usage analysis and to prevent overuse of CPU. */ - Microseconds( &widePad ); - pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad ); -} - -/****************************************************************************** -** Measure fractional CPU load based on real-time it took to calculate -** buffers worth of output. -*/ -/**************************************************************************/ -static void PaMac_EndLoadCalculation( internalPortAudioStream *past ) -{ - UnsignedWide widePad; - UInt64 currentCount; - long usecsElapsed; - double newUsage; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return; - - /* Measure CPU utilization during this callback. Note that this calculation - ** assumes that we had the processor the whole time. - */ -#define LOWPASS_COEFFICIENT_0 (0.95) -#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) - Microseconds( &widePad ); - currentCount = UnsignedWideToUInt64( widePad ); - - usecsElapsed = (long) U64Subtract(currentCount, pahsc->pahsc_EntryCount); - - /* Use inverse because it is faster than the divide. */ - newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; - - past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + - (LOWPASS_COEFFICIENT_1 * newUsage); - -} - -/*********************************************************************** -** Called by Pa_StartStream() -*/ -PaError PaHost_StartInput( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - pahsc->pahsc_IsRecording = 0; - pahsc->pahsc_StopRecording = 0; - pahsc->pahsc_InputMultiBuffer.nextWrite = 0; - pahsc->pahsc_InputMultiBuffer.nextRead = 0; - return PaMac_RecordNext( past ); -} - -/*********************************************************************** -** Called by Pa_StopStream(). -** May be called during error recovery or cleanup code -** so protect against NULL pointers. -*/ -PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) -{ - int32 timeOutMsec; - PaError result = paNoError; - OSErr err = 0; - long mRefNum; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paNoError; - - (void) abort; - - mRefNum = pahsc->pahsc_InputRefNum; - - DBUG(("PaHost_StopInput: mRefNum = %d\n", mRefNum )); - if( mRefNum ) - { - DBUG(("PaHost_StopInput: pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording )); - if( pahsc->pahsc_IsRecording ) - { - /* PLB20010420 - Fix TIMEOUT in record mode. */ - pahsc->pahsc_StopRecording = 1; /* Request that we stop recording. */ - err = SPBStopRecording(mRefNum); - DBUG(("PaHost_StopInput: then pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording )); - - /* Calculate timeOut longer than longest time it could take to play one buffer. */ - timeOutMsec = (int32) ((1500.0 * pahsc->pahsc_FramesPerHostBuffer) / past->past_SampleRate); - /* Keep querying sound channel until it is no longer busy playing. */ - while( !err && pahsc->pahsc_IsRecording && (timeOutMsec > 0)) - { - Pa_Sleep(20); - timeOutMsec -= 20; - } - if( timeOutMsec <= 0 ) - { - ERR_RPT(("PaHost_StopInput: timed out!\n")); - return paTimedOut; - } - } - } - if( err ) - { - sPaHostError = err; - result = paHostError; - } - - DBUG(("PaHost_StopInput: finished.\n", mRefNum )); - return result; -} - -/***********************************************************************/ -static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader ) -{ - sndHeader->numChannels = past->past_NumOutputChannels; - sndHeader->sampleRate = DoubleToUnsignedFixed(past->past_SampleRate); - sndHeader->loopStart = 0; - sndHeader->loopEnd = 0; - sndHeader->encode = cmpSH; - sndHeader->baseFrequency = kMiddleC; - sndHeader->markerChunk = nil; - sndHeader->futureUse2 = nil; - sndHeader->stateVars = nil; - sndHeader->leftOverSamples = nil; - sndHeader->compressionID = 0; - sndHeader->packetSize = 0; - sndHeader->snthID = 0; - sndHeader->sampleSize = 8 * sizeof(int16); // FIXME - might be 24 or 32 bits some day; - sndHeader->sampleArea[0] = 0; - sndHeader->format = kSoundNotCompressed; -} - -static void SetFramesDone( PaHostSoundControl *pahsc, PaTimestamp framesDone ) -{ - UnsignedWide now; - Microseconds( &now ); - pahsc->pahsc_NumFramesDone = framesDone; - pahsc->pahsc_WhenFramesDoneIncremented = UnsignedWideToUInt64( now ); -} - -/***********************************************************************/ -PaError PaHost_StartOutput( internalPortAudioStream *past ) -{ - SndCommand pauseCommand; - SndCommand resumeCommand; - int i; - OSErr error; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paInternalError; - if( pahsc->pahsc_Channel == NULL ) return paInternalError; - - past->past_StopSoon = 0; - past->past_IsActive = 1; - pahsc->pahsc_NumOutsQueued = 0; - pahsc->pahsc_NumOutsPlayed = 0; - - SetFramesDone( pahsc, 0.0 ); - - /* Pause channel so it does not do back ground processing while we are still filling the queue. */ - pauseCommand.cmd = pauseCmd; - pauseCommand.param1 = pauseCommand.param2 = 0; - error = SndDoCommand (pahsc->pahsc_Channel, &pauseCommand, true); - if (noErr != error) goto exit; - - /* Queue all of the buffers so we start off full. */ - for (i = 0; ipahsc_NumHostBuffers; i++) - { - PaMac_PlayNext( past, i ); - } - - /* Resume channel now that the queue is full. */ - resumeCommand.cmd = resumeCmd; - resumeCommand.param1 = resumeCommand.param2 = 0; - error = SndDoImmediate( pahsc->pahsc_Channel, &resumeCommand ); - if (noErr != error) goto exit; - - return paNoError; -exit: - past->past_IsActive = 0; - sPaHostError = error; - ERR_RPT(("Error in PaHost_StartOutput: SndDoCommand returned %d\n", error )); - return paHostError; -} - -/*******************************************************************/ -long PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - return (long) (pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer); -} - -/*********************************************************************** -** Called by Pa_StopStream(). -** May be called during error recovery or cleanup code -** so protect against NULL pointers. -*/ -PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) -{ - int32 timeOutMsec; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paNoError; - if( pahsc->pahsc_Channel == NULL ) return paNoError; - - DBUG(("PaHost_StopOutput()\n")); - if( past->past_IsActive == 0 ) return paNoError; - - /* Set flags for callback function to see. */ - if( abort ) past->past_StopNow = 1; - past->past_StopSoon = 1; - /* Calculate timeOut longer than longest time it could take to play all buffers. */ - timeOutMsec = (int32) ((1500.0 * PaHost_GetTotalBufferFrames( past )) / past->past_SampleRate); - /* Keep querying sound channel until it is no longer busy playing. */ - while( past->past_IsActive && (timeOutMsec > 0)) - { - Pa_Sleep(20); - timeOutMsec -= 20; - } - if( timeOutMsec <= 0 ) - { - ERR_RPT(("PaHost_StopOutput: timed out!\n")); - return paTimedOut; - } - else return paNoError; -} - -/***********************************************************************/ -PaError PaHost_StartEngine( internalPortAudioStream *past ) -{ - (void) past; /* Prevent unused variable warnings. */ - return paNoError; -} - -/***********************************************************************/ -PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) -{ - (void) past; /* Prevent unused variable warnings. */ - (void) abort; /* Prevent unused variable warnings. */ - return paNoError; -} -/***********************************************************************/ -PaError PaHost_StreamActive( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - return (PaError) ( past->past_IsActive + pahsc->pahsc_IsRecording ); -} -int Mac_IsVirtualMemoryOn( void ) -{ - long attr; - OSErr result = Gestalt( gestaltVMAttr, &attr ); - DBUG(("gestaltVMAttr : 0x%x\n", attr )); - return ((attr >> gestaltVMHasPagingControl ) & 1); -} - -/******************************************************************* -* Determine number of host Buffers -* and how many User Buffers we can put into each host buffer. -*/ -static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - int32 minNumBuffers; - int32 minFramesPerHostBuffer; - int32 minTotalFrames; - int32 userBuffersPerHostBuffer; - int32 framesPerHostBuffer; - int32 numHostBuffers; - - minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer; - minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; - DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); - - /* Determine number of user buffers based on minimum latency. */ - /* PLB20020417 I used to call Pa_GetMinNumBuffers() which doesn't take into account the - ** variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will - ** gove lower latency when virtual memory is turned off. */ - /* minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); WRONG */ - minNumBuffers = PaMac_GetMinNumBuffers( minFramesPerHostBuffer, past->past_FramesPerUserBuffer, past->past_SampleRate ); - - past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers; - DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); - minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer; - - /* We cannot make the buffers too small because they may not get serviced quickly enough. */ - if( (int32) past->past_FramesPerUserBuffer < minFramesPerHostBuffer ) - { - userBuffersPerHostBuffer = - (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) / - past->past_FramesPerUserBuffer; - } - else - { - userBuffersPerHostBuffer = 1; - } - framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer; - - /* Calculate number of host buffers needed. Round up to cover minTotalFrames. */ - numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; - /* Make sure we have enough host buffers. */ - if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS) - { - numHostBuffers = PA_MIN_NUM_HOST_BUFFERS; - } - else - { - /* If we have too many host buffers, try to put more user buffers in a host buffer. */ - while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) - { - userBuffersPerHostBuffer += 1; - framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer; - numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; - } - } - - pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer; - pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer; - pahsc->pahsc_NumHostBuffers = numHostBuffers; - DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); - DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers )); - DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); - DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); -} - -/*******************************************************************/ -PaError PaHost_OpenStream( internalPortAudioStream *past ) -{ - OSErr err; - PaError result = paHostError; - PaHostSoundControl *pahsc; - int i; - /* Allocate and initialize host data. */ - pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); - if( pahsc == NULL ) - { - return paInsufficientMemory; - } - past->past_DeviceData = (void *) pahsc; - - /* If recording, and virtual memory is turned on, then use bigger buffers to prevent glitches. */ - if( (past->past_NumInputChannels > 0) && Mac_IsVirtualMemoryOn() ) - { - pahsc->pahsc_MinFramesPerHostBuffer = MAC_VIRTUAL_FRAMES_PER_BUFFER; - } - else - { - pahsc->pahsc_MinFramesPerHostBuffer = MAC_PHYSICAL_FRAMES_PER_BUFFER; - } - - PaHost_CalcNumHostBuffers( past ); - - /* Setup constants for CPU load measurement. */ - pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); - - /* ------------------ OUTPUT */ - if( past->past_NumOutputChannels > 0 ) - { - /* Create sound channel to which we can send commands. */ - pahsc->pahsc_Channel = 0L; - err = SndNewChannel(&pahsc->pahsc_Channel, sampledSynth, 0, nil); /* FIXME - use kUseOptionalOutputDevice if not default. */ - if(err != 0) - { - ERR_RPT(("Error in PaHost_OpenStream: SndNewChannel returned 0x%x\n", err )); - goto error; - } - - /* Install our callback function pointer straight into the sound channel structure */ - /* Use new CARBON name for callback procedure. */ -#if TARGET_API_MAC_CARBON - pahsc->pahsc_OutputCompletionProc = NewSndCallBackUPP(PaMac_OutputCompletionProc); -#else - pahsc->pahsc_OutputCompletionProc = NewSndCallBackProc(PaMac_OutputCompletionProc); -#endif - - pahsc->pahsc_Channel->callBack = pahsc->pahsc_OutputCompletionProc; - - pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumOutputChannels * sizeof(int16); - for (i = 0; ipahsc_NumHostBuffers; i++) - { - char *buf = (char *)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer); - if (buf == NULL) - { - ERR_RPT(("Error in PaHost_OpenStream: could not allocate output buffer. Size = \n", pahsc->pahsc_BytesPerOutputHostBuffer )); - goto memerror; - } - - PaMac_InitSoundHeader( past, &pahsc->pahsc_SoundHeaders[i] ); - pahsc->pahsc_SoundHeaders[i].samplePtr = buf; - pahsc->pahsc_SoundHeaders[i].numFrames = (unsigned long) pahsc->pahsc_FramesPerHostBuffer; - - } - } -#ifdef SUPPORT_AUDIO_CAPTURE - /* ------------------ INPUT */ - /* Use double buffer scheme that matches output. */ - if( past->past_NumInputChannels > 0 ) - { - int16 tempS; - long tempL; - Fixed tempF; - long mRefNum; - unsigned char noname = 0; /* FIXME - use real device names. */ -#if TARGET_API_MAC_CARBON - pahsc->pahsc_InputCompletionProc = NewSICompletionUPP((SICompletionProcPtr)PaMac_InputCompletionProc); -#else - pahsc->pahsc_InputCompletionProc = NewSICompletionProc((ProcPtr)PaMac_InputCompletionProc); -#endif - pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumInputChannels * sizeof(int16); - for (i = 0; ipahsc_NumHostBuffers; i++) - { - char *buf = (char *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer); - if ( buf == NULL ) - { - ERR_RPT(("PaHost_OpenStream: could not allocate input buffer. Size = \n", pahsc->pahsc_BytesPerInputHostBuffer )); - goto memerror; - } - pahsc->pahsc_InputMultiBuffer.buffers[i] = buf; - } - pahsc->pahsc_InputMultiBuffer.numBuffers = pahsc->pahsc_NumHostBuffers; - - err = SPBOpenDevice( (const unsigned char *) &noname, siWritePermission, &mRefNum); /* FIXME - use name so we get selected device */ - // FIXME err = SPBOpenDevice( (const unsigned char *) sDevices[past->past_InputDeviceID].pad_Info.name, siWritePermission, &mRefNum); - if (err) goto error; - pahsc->pahsc_InputRefNum = mRefNum; - DBUG(("PaHost_OpenStream: mRefNum = %d\n", mRefNum )); - - /* Set input device characteristics. */ - tempS = 1; - err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS); - if (err) - { - ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siContinuous returned %d\n", err )); - goto error; - } - - tempL = 0x03; - err = SPBSetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL); - if (err) - { - DBUG(("PaHost_OpenStream: setting siActiveChannels returned 0x%x. Error ignored.\n", err )); - } - - /* PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. */ - tempS = past->past_NumInputChannels; - err = SPBSetDeviceInfo(mRefNum, siNumberChannels, (Ptr) &tempS); - if (err) - { - ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siNumberChannels returned %d\n", err )); - goto error; - } - - tempF = ((unsigned long)past->past_SampleRate) << 16; - err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF); - if (err) - { - ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siSampleRate returned %d\n", err )); - goto error; - } - - /* Setup record-parameter block */ - pahsc->pahsc_InputParams.inRefNum = mRefNum; - pahsc->pahsc_InputParams.milliseconds = 0; // not used - pahsc->pahsc_InputParams.completionRoutine = pahsc->pahsc_InputCompletionProc; - pahsc->pahsc_InputParams.interruptRoutine = 0; - pahsc->pahsc_InputParams.userLong = (long) past; - pahsc->pahsc_InputParams.unused1 = 0; - } -#endif /* SUPPORT_AUDIO_CAPTURE */ - DBUG(("PaHost_OpenStream: complete.\n")); - return paNoError; - -error: - PaHost_CloseStream( past ); - ERR_RPT(("PaHost_OpenStream: sPaHostError = 0x%x.\n", err )); - sPaHostError = err; - return paHostError; - -memerror: - PaHost_CloseStream( past ); - return paInsufficientMemory; -} - -/*********************************************************************** -** Called by Pa_CloseStream(). -** May be called during error recovery or cleanup code -** so protect against NULL pointers. -*/ -PaError PaHost_CloseStream( internalPortAudioStream *past ) -{ - PaError result = paNoError; - OSErr err = 0; - int i; - PaHostSoundControl *pahsc; - - DBUG(("PaHost_CloseStream( 0x%x )\n", past )); - - if( past == NULL ) return paBadStreamPtr; - - pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paNoError; - - if( past->past_NumOutputChannels > 0 ) - { - /* TRUE means flush now instead of waiting for quietCmd to be processed. */ - if( pahsc->pahsc_Channel != NULL ) SndDisposeChannel(pahsc->pahsc_Channel, TRUE); - { - for (i = 0; ipahsc_NumHostBuffers; i++) - { - Ptr p = (Ptr) pahsc->pahsc_SoundHeaders[i].samplePtr; - if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerOutputHostBuffer ); - } - } - } - - if( past->past_NumInputChannels > 0 ) - { - if( pahsc->pahsc_InputRefNum ) - { - err = SPBCloseDevice(pahsc->pahsc_InputRefNum); - pahsc->pahsc_InputRefNum = 0; - if( err ) - { - sPaHostError = err; - result = paHostError; - } - } - { - for (i = 0; ipahsc_InputMultiBuffer.numBuffers; i++) - { - Ptr p = (Ptr) pahsc->pahsc_InputMultiBuffer.buffers[i]; - if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerInputHostBuffer ); - } - } - } - - past->past_DeviceData = NULL; - PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); - - DBUG(("PaHost_CloseStream: complete.\n", past )); - return result; -} -/*************************************************************************/ -int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) -{ -/* We use the MAC_VIRTUAL_FRAMES_PER_BUFFER because we might be recording. -** This routine doesn't have enough information to determine the best value -** and is being depracated. */ - return PaMac_GetMinNumBuffers( MAC_VIRTUAL_FRAMES_PER_BUFFER, framesPerUserBuffer, sampleRate ); -} -/*************************************************************************/ -static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerUserBuffer, double sampleRate ) -{ - int minUserPerHost = ( minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; - int numBufs = PA_MIN_NUM_HOST_BUFFERS * minUserPerHost; - if( numBufs < PA_MIN_NUM_HOST_BUFFERS ) numBufs = PA_MIN_NUM_HOST_BUFFERS; - (void) sampleRate; - return numBufs; -} - -/*************************************************************************/ -void Pa_Sleep( int32 msec ) -{ - EventRecord event; - int32 sleepTime, endTime; - /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */ - sleepTime = ((msec * 60) + 999) / 1000; - if( sleepTime < 1 ) sleepTime = 1; - endTime = TickCount() + sleepTime; - do - { - DBUGX(("Sleep for %d ticks.\n", sleepTime )); - /* Use WaitNextEvent() to sleep without getting events. */ - /* PLB20010907 - Pass unused event to WaitNextEvent instead of NULL to prevent - * Mac OSX crash. Thanks Dominic Mazzoni. */ - WaitNextEvent( 0, &event, sleepTime, NULL ); - sleepTime = endTime - TickCount(); - } - while( sleepTime > 0 ); -} -/*************************************************************************/ -int32 Pa_GetHostError( void ) -{ - int32 err = sPaHostError; - sPaHostError = 0; - return err; -} - -/************************************************************************* - * 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 = NewPtrClear( numBytes ); - if( (addr == NULL) || (MemError () != 0) ) return NULL; - -#if (TARGET_API_MAC_CARBON == 0) - if( HoldMemory( addr, numBytes ) != noErr ) - { - DisposePtr( (Ptr) addr ); - return NULL; - } -#endif - 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 ) -{ - if( addr == NULL ) return; -#if TARGET_API_MAC_CARBON - (void) numBytes; -#else - UnholdMemory( addr, numBytes ); -#endif - DisposePtr( (Ptr) addr ); -} - -/*************************************************************************/ -PaTimestamp Pa_StreamTime( PortAudioStream *stream ) -{ - PaTimestamp framesDone1; - PaTimestamp framesDone2; - UInt64 whenIncremented; - UnsignedWide now; - UInt64 now64; - long microsElapsed; - long framesElapsed; - - PaHostSoundControl *pahsc; - internalPortAudioStream *past = (internalPortAudioStream *) stream; - if( past == NULL ) return paBadStreamPtr; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - -/* Capture information from audio thread. - * We have to be careful that we don't get interrupted in the middle. - * So we grab the pahsc_NumFramesDone twice and make sure it didn't change. - */ - do - { - framesDone1 = pahsc->pahsc_NumFramesDone; - whenIncremented = pahsc->pahsc_WhenFramesDoneIncremented; - framesDone2 = pahsc->pahsc_NumFramesDone; - } while( framesDone1 != framesDone2 ); - - /* Calculate how many microseconds have elapsed and convert to frames. */ - Microseconds( &now ); - now64 = UnsignedWideToUInt64( now ); - microsElapsed = U64Subtract( now64, whenIncremented ); - framesElapsed = microsElapsed * past->past_SampleRate * 0.000001; - - return framesDone1 + framesElapsed; -} - -/************************************************************************** -** Callback for Input, SPBRecord() -*/ -int gRecordCounter = 0; -int gPlayCounter = 0; -pascal void PaMac_InputCompletionProc(SPBPtr recParams) -{ - PaError result = paNoError; - int finished = 1; - internalPortAudioStream *past; - PaHostSoundControl *pahsc; - - gRecordCounter += 1; /* debug hack to see if engine running */ - - /* Get our PA data from Mac structure. */ - past = (internalPortAudioStream *) recParams->userLong; - if( past == NULL ) return; - - if( past->past_Magic != PA_MAGIC ) - { - AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, past", (long) past ); - AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, magic", (long) past->past_Magic ); - goto error; - } - pahsc = (PaHostSoundControl *) past->past_DeviceData; - past->past_NumCallbacks += 1; - - /* Have we been asked to stop recording? */ - if( (recParams->error == abortErr) || pahsc->pahsc_StopRecording ) goto error; - - /* If there are no output channels, then we need to call the user callback function from here. - * Otherwise we will call the user code during the output completion routine. - */ - if(past->past_NumOutputChannels == 0) - { - SetFramesDone( pahsc, - pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer ); - result = PaMac_CallUserLoop( past, NULL ); - } - - /* Did user code ask us to stop? If not, issue another recording request. */ - if( (result == paNoError) && (pahsc->pahsc_StopRecording == 0) ) - { - result = PaMac_RecordNext( past ); - if( result != paNoError ) pahsc->pahsc_IsRecording = 0; - } - else goto error; - - return; - -error: - pahsc->pahsc_IsRecording = 0; - pahsc->pahsc_StopRecording = 0; - return; -} - -/*********************************************************************** -** Called by either input or output completion proc. -** Grabs input data if any present, and calls PA conversion code, -** that in turn calls user code. -*/ -static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr ) -{ - PaError result = paNoError; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - int16 *inPtr = NULL; - int i; - - - /* Advance read index for sound input FIFO here, independantly of record/write process. */ - if(past->past_NumInputChannels > 0) - { - if( MultiBuffer_IsReadable( &pahsc->pahsc_InputMultiBuffer ) ) - { - inPtr = (int16 *) MultiBuffer_GetNextReadBuffer( &pahsc->pahsc_InputMultiBuffer ); - MultiBuffer_AdvanceReadIndex( &pahsc->pahsc_InputMultiBuffer ); - } - } - - /* Call user code enough times to fill buffer. */ - if( (inPtr != NULL) || (outPtr != NULL) ) - { - PaMac_StartLoadCalculation( past ); /* CPU usage */ - -#ifdef PA_MAX_USAGE_ALLOWED - /* If CPU usage exceeds limit, skip user callback to prevent hanging CPU. */ - if( past->past_Usage > PA_MAX_USAGE_ALLOWED ) - { - past->past_FrameCount += (PaTimestamp) pahsc->pahsc_FramesPerHostBuffer; - } - else -#endif - { - - for( i=0; ipahsc_UserBuffersPerHostBuffer; i++ ) - { - result = (PaError) Pa_CallConvertInt16( past, inPtr, outPtr ); - if( result != 0) - { - /* Recording might be in another process, so tell it to stop with a flag. */ - pahsc->pahsc_StopRecording = pahsc->pahsc_IsRecording; - break; - } - /* Advance sample pointers. */ - if(inPtr != NULL) inPtr += past->past_FramesPerUserBuffer * past->past_NumInputChannels; - if(outPtr != NULL) outPtr += past->past_FramesPerUserBuffer * past->past_NumOutputChannels; - } - } - - PaMac_EndLoadCalculation( past ); - } - return result; -} - -/*********************************************************************** -** Setup next recording buffer in FIFO and issue recording request to Snd Input Manager. -*/ -static PaError PaMac_RecordNext( internalPortAudioStream *past ) -{ - PaError result = paNoError; - OSErr err; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - /* Get pointer to next buffer to record into. */ - pahsc->pahsc_InputParams.bufferPtr = MultiBuffer_GetNextWriteBuffer( &pahsc->pahsc_InputMultiBuffer ); - - /* Advance write index if there is room. Otherwise keep writing same buffer. */ - if( MultiBuffer_IsWriteable( &pahsc->pahsc_InputMultiBuffer ) ) - { - MultiBuffer_AdvanceWriteIndex( &pahsc->pahsc_InputMultiBuffer ); - } - - AddTraceMessage("PaMac_RecordNext: bufferPtr", (long) pahsc->pahsc_InputParams.bufferPtr ); - AddTraceMessage("PaMac_RecordNext: nextWrite", pahsc->pahsc_InputMultiBuffer.nextWrite ); - - /* Setup parameters and issue an asynchronous recording request. */ - pahsc->pahsc_InputParams.bufferLength = pahsc->pahsc_BytesPerInputHostBuffer; - pahsc->pahsc_InputParams.count = pahsc->pahsc_BytesPerInputHostBuffer; - err = SPBRecord(&pahsc->pahsc_InputParams, true); - if( err ) - { - AddTraceMessage("PaMac_RecordNext: SPBRecord error ", err ); - sPaHostError = err; - result = paHostError; - } - else - { - pahsc->pahsc_IsRecording = 1; - } - return result; -} - -/************************************************************************** -** Callback for Output Playback() -** Return negative error, 0 to continue, 1 to stop. -*/ -long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index ) -{ - PaHostSoundControl *pahsc; - long result = 0; - int finished = 1; - char *outPtr; - - gPlayCounter += 1; /* debug hack */ - - past->past_NumCallbacks += 1; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return -1; - /* Are we nested?! */ - if( pahsc->pahsc_IfInsideCallback ) return 0; - pahsc->pahsc_IfInsideCallback = 1; - /* Get pointer to buffer to fill. */ - outPtr = pahsc->pahsc_SoundHeaders[index].samplePtr; - /* Combine with any sound input, and call user callback. */ - result = PaMac_CallUserLoop( past, (int16 *) outPtr ); - - pahsc->pahsc_IfInsideCallback = 0; - return result; -} - -/************************************************************************************* -** Called by SoundManager when ready for another buffer. -*/ -static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCallBackCmd) -{ - internalPortAudioStream *past; - PaHostSoundControl *pahsc; - (void) theChannel; - (void) theCallBackCmd; - - /* Get our data from Mac structure. */ - past = (internalPortAudioStream *) theCallBackCmd->param2; - if( past == NULL ) return; - - pahsc = (PaHostSoundControl *) past->past_DeviceData; - pahsc->pahsc_NumOutsPlayed += 1; - - SetFramesDone( pahsc, - pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer ); - - PaMac_BackgroundManager( past, theCallBackCmd->param1 ); -} - -/*******************************************************************/ -static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index ) -{ - PaError result = paNoError; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - /* Has someone asked us to abort by calling Pa_AbortStream()? */ - if( past->past_StopNow ) - { - SndCommand command; - /* Clear the queue of any pending commands. */ - command.cmd = flushCmd; - command.param1 = command.param2 = 0; - SndDoImmediate( pahsc->pahsc_Channel, &command ); - /* Then stop currently playing buffer, if any. */ - command.cmd = quietCmd; - SndDoImmediate( pahsc->pahsc_Channel, &command ); - past->past_IsActive = 0; - } - /* Has someone asked us to stop by calling Pa_StopStream() - * OR has a user callback returned '1' to indicate finished. - */ - else if( past->past_StopSoon ) - { - if( (pahsc->pahsc_NumOutsQueued - pahsc->pahsc_NumOutsPlayed) <= 0 ) - { - past->past_IsActive = 0; /* We're finally done. */ - } - } - else - { - PaMac_PlayNext( past, index ); - } - return result; -} - -/************************************************************************************* -** Fill next buffer with sound and queue it for playback. -*/ -static void PaMac_PlayNext ( internalPortAudioStream *past, int index ) -{ - OSErr error; - long result; - SndCommand playCmd; - SndCommand callbackCmd; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - - /* If this was the last buffer, or abort requested, then just be done. */ - if ( past->past_StopSoon ) goto done; - - /* Load buffer with sound. */ - result = PaMac_FillNextOutputBuffer ( past, index ); - if( result > 0 ) past->past_StopSoon = 1; /* Stop generating audio but wait until buffers play. */ - else if( result < 0 ) goto done; - - /* Play the next buffer. */ - playCmd.cmd = bufferCmd; - playCmd.param1 = 0; - playCmd.param2 = (long) &pahsc->pahsc_SoundHeaders[ index ]; - error = SndDoCommand (pahsc->pahsc_Channel, &playCmd, true ); - if( error != noErr ) goto gotError; - - /* Ask for a callback when it is done. */ - callbackCmd.cmd = callBackCmd; - callbackCmd.param1 = index; - callbackCmd.param2 = (long)past; - error = SndDoCommand (pahsc->pahsc_Channel, &callbackCmd, true ); - if( error != noErr ) goto gotError; - pahsc->pahsc_NumOutsQueued += 1; - - return; - -gotError: - sPaHostError = error; -done: - return; -} diff --git a/portaudio-v19/pa_sgi/pa_sgi.c b/portaudio-v19/pa_sgi/pa_sgi.c deleted file mode 100644 index 8b742d1cc..000000000 --- a/portaudio-v19/pa_sgi/pa_sgi.c +++ /dev/null @@ -1,1417 +0,0 @@ -/* - * $Id$ - * PortAudio Portable Real-Time Audio Library. - * Latest Version at: http://www.portaudio.com. - * Silicon Graphics (SGI) IRIX implementation by Pieter Suurmond. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ -/** @file - @brief SGI IRIX AL implementation (according to V19 API version 2.0). - - @note This file started as a copy of pa_skeleton.c (v 1.1.2.35 2003/09/20), it - has nothing to do with the old V18 pa_sgi version: this implementation uses the - newer IRIX AL calls and uses pthreads instead of sproc. - - On IRIX, one may type './configure' followed by 'gmake' from the portaudio root - directory to build the static and shared libraries, as well as all the tests. - - On IRIX 6.5, using 'make' instead of 'gmake' may cause Makefile to fail. (This - happens on my machine: make does not understand syntax with 2 colons on a line, - like this: - $(TESTS): bin/%: [snip] - - Maybe this is due to an old make version(?), my only solution is: use gmake. - Anyway, all the tests compile well now, with GCC 3.3, as well as with MIPSpro 7.2.1. - Tested: - - paqa_devs ok, but at a certain point digital i/o fails: - TestAdvance: INPUT, device = 2, rate = 32000, numChannels = 1, format = 1 - Possibly, this is an illegal sr or number of channels for digital i/o. - - paqa_errs 13 of the tests run ok, but 5 of them give weird results. - + patest1 ok. - + patest_buffer ok. - + patest_callbackstop ok. - - patest_clip ok, but hear no difference between dithering turned OFF and ON. - + patest_hang ok. - + patest_latency ok. - + patest_leftright ok. - + patest_maxsines ok. - + patest_many ok. - + patest_multi_sine ok. - + patest_pink ok. - + patest_prime ok. - - patest_read_record ok, but playback stops a little earlier than 5 seconds it seems(?). - + patest_record ok. - + patest_ringmix ok. - + patest_saw ok. - + patest_sine ok. - + patest_sine8 ok. - - patest_sine_formats ok, FLOAT32 + INT16 + INT18 are OK, but UINT8 IS NOT OK! - + patest_sine_time ok. - + patest_start_stop ok, but under/overflow errors of course in the AL queue monitor. - + patest_stop ok. - - patest_sync ok? - + patest_toomanysines ok. - - patest_underflow ok? (stopping after SleepTime = 91: err=Stream is stopped) - - patest_wire ok. - + patest_write_sine ok. - + pa_devs ok. - Ok on an Indy, in both stereo and quadrophonic mode. - + pa_fuzz ok. - + pa_minlat ok. - - Worked on (or checked) proposals: - - 003: Improve Latency Specification OK, but not 100% sure: plus or minus 1 host buffer? - 004 OK: Allow Callbacks to Accept Variable Number of Frames per Buffer. - Simply using a fixed host buffer size. Very roughly implemented now, the adaption - to limited-requested latencies and samplerate may be improved. At least this - implementation chooses its own internal host buffer size (no coredumps any longer). - 005 OK: Blocking Read/Write Interface. - 006: Non-interleaved buffers seems OK? Covered by the buffer-processor and such?.... - 009 OK: Host error reporting should now be. - 010 OK: State Machine and State Querying Functions. - 011 OK: Renaming done. - 014 Implementation Style Guidelines (sorry, my braces are not ANSI style). - 015 OK: Callback Timestamps (During priming, though, these are still null!). - 016 OK: Use Structs for Pa_OpenStream() Parameters. - 019: Notify Client When All Buffers Have Played (Ok, covered by the buffer processor?) - 020 OK: Allow Callback to prime output stream (StartStream() should do the priming) - Should be tested more thoroughly for full duplex streams. (patest_prime seems ok). - - - @todo Underrun or overflow flags at some more places. - - @todo Callback Timestamps during priming. - - @todo Improve adaption to number of channels, samplerate and such when inventing - some frames per host buffer (when client requests 0). - - @todo Make a complete new version to support 'sproc'-applications. - Or could we manage that with some clever if-defs? - It must be clear which version we use (especially when using pa as lib!): - an irix-sproc() version or pthread version. - - @todo In Makefile.in: 'make clean' does not remove lib/libportaudio.so.0.0.19. - - Note: Even when mono-output is requested, with ALv7, the audio library opens - a outputs stereo. One can observe this in SGI's 'Audio Queue Monitor'. -*/ - -#include /* For strlen() but also for strerror()! */ -#include /* printf() */ -#include /* fabs() */ - -#include /* IRIX AL (audio library). Link with -laudio. */ -#include /* IRIX DL (digital media library), solely for */ - /* function dmGetUST(). Link with -ldmedia. */ -#include /* To catch 'oserror' after AL-calls. */ -#include /* POSIX threads. */ -#include - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - - /* Uncomment for diagnostics: */ -#define DBUG(x) /*{ printf x; fflush(stdout); }*/ - - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *ipp, - const PaStreamParameters *opp, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - - -/* - Apparently, we must use macros for reporting unanticipated host errors. - Only in case we return paUnanticipatedHostError from an Portaudio call, - we have to call one of the following three macro's. - (Constant paAL is defined in pa_common/portaudio.h. See also proposal 009.) - - After an AL error, use this to translate the AL error code to human text: -*/ -#define PA_SGI_SET_LAST_AL_ERROR() \ - {\ - int ee = oserror();\ - PaUtil_SetLastHostErrorInfo(paAL, ee, alGetErrorString(ee));\ - } -/* - But after a generic IRIX error, let strerror() translate the error code from - the operating system and use this (strerror() gives the same as perror()): -*/ -#define PA_SGI_SET_LAST_IRIX_ERROR() \ - {\ - int ee = oserror();\ - PaUtil_SetLastHostErrorInfo(paAL, ee, strerror(ee));\ - } - -/* GOT RID OF calling PaUtil_SetLastHostErrorInfo() with 0 as error number. -- Weird samplerate difference became: paInvalidSampleRate. -- Failing to set AL queue size became: paInternalError - (Because I cannot decide between paBufferTooBig and paBufferTooSmall - because it may even the 'default AL queue size that failed... Or - should we introduce another error-code like 'paInvalidQueueSize'?... NO) -*/ - -/* PaSGIHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - PaUtilAllocationGroup* allocations; - /* implementation specific data goes here. */ - ALvalue* sgiDeviceIDs; /* Array of AL resource device numbers. */ - /* PaHostApiIndex hostApiIndex; Hu? As in the linux and oss files? */ -} -PaSGIHostApiRepresentation; - -/* - Initialises sgiDeviceIDs array. -*/ -PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int e, i, deviceCount, def_in, def_out; - PaSGIHostApiRepresentation* SGIHostApi; - PaDeviceInfo* deviceInfoArray; - static const short numParams = 4; /* Array with name, samplerate, channels */ - ALpv y[numParams]; /* and type. */ - static const short maxDevNameChars = 32; /* Including the terminating null char. */ - char devName[maxDevNameChars]; /* Too lazy for dynamic alloc. */ - - /* DBUG(("PaSGI_Initialize() started.\n")); */ - SGIHostApi = (PaSGIHostApiRepresentation*)PaUtil_AllocateMemory(sizeof(PaSGIHostApiRepresentation)); - if( !SGIHostApi ) - { result = paInsufficientMemory; goto cleanup; } - SGIHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !SGIHostApi->allocations ) - { result = paInsufficientMemory; goto cleanup; } - *hostApi = &SGIHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paAL; /* IRIX AL type id, was paInDevelopment. */ - (*hostApi)->info.name = "SGI IRIX AL"; - (*hostApi)->info.defaultInputDevice = paNoDevice; /* Set later. */ - (*hostApi)->info.defaultOutputDevice = paNoDevice; /* Set later. */ - (*hostApi)->info.deviceCount = 0; /* We 'll increment in the loop below. */ - - /* Determine the total number of input and output devices (thanks to Gary Scavone). */ - deviceCount = alQueryValues(AL_SYSTEM, AL_DEVICES, 0, 0, 0, 0); - if (deviceCount < 0) /* Returns -1 in case of failure. */ - { - DBUG(("Failed to count devices: alQueryValues()=%d; %s.\n", - deviceCount, alGetErrorString(oserror()))); - result = paDeviceUnavailable; /* Is this an appropriate error return code? */ - goto cleanup; - } - if (deviceCount > 0) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - SGIHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount); - if (!(*hostApi)->deviceInfos) - { result = paInsufficientMemory; goto cleanup; } - - /* Allocate all device info structs in a contiguous block. */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - SGIHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount); - if (!deviceInfoArray) - { result = paInsufficientMemory; goto cleanup; } - /* Store all AL device IDs in an array. */ - SGIHostApi->sgiDeviceIDs = (ALvalue*)PaUtil_GroupAllocateMemory(SGIHostApi->allocations, - deviceCount * sizeof(ALvalue)); - if (!SGIHostApi->sgiDeviceIDs) - { result = paInsufficientMemory; goto cleanup; } - /* Same query again, but now store all IDs in array sgiDeviceIDs (still using no qualifiers).*/ - e = alQueryValues(AL_SYSTEM, AL_DEVICES, SGIHostApi->sgiDeviceIDs, deviceCount, 0, 0); - if (e != deviceCount) - { - if (e < 0) /* Sure an AL error really occurred. */ - { PA_SGI_SET_LAST_AL_ERROR() result = paUnanticipatedHostError; } - else /* Seems we lost some devices. */ - { DBUG(("Number of devices suddenly changed!\n")); result = paDeviceUnavailable; } - goto cleanup; - } - y[0].param = AL_DEFAULT_INPUT; - y[1].param = AL_DEFAULT_OUTPUT; - e = alGetParams(AL_SYSTEM, y, 2); /* Get params global to the AL system. */ - if (e != 2) - { - if (e < 0) - { - PA_SGI_SET_LAST_AL_ERROR() /* Calls oserror() and alGetErrorString(). */ - result = paUnanticipatedHostError; /* Sure an AL error really occurred. */ - } - else - { - DBUG(("Default input and/or output could not be found!\n")); - result = paDeviceUnavailable; /* FIX: What if only in or out are available? */ - } - goto cleanup; - } - def_in = y[0].value.i; /* Remember both AL devices for a while. */ - def_out = y[1].value.i; - y[0].param = AL_NAME; - y[0].value.ptr = devName; - y[0].sizeIn = maxDevNameChars; /* Including terminating null char. */ - y[1].param = AL_RATE; - y[2].param = AL_CHANNELS; - y[3].param = AL_TYPE; /* Subtype of AL_INPUT_DEVICE_TYPE or AL_OUTPUT_DEVICE_TYPE? */ - for (i=0; i < deviceCount; ++i) /* Fill allocated deviceInfo structs. */ - { - PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; /* Retrieve name, samplerate, channels and type. */ - e = alGetParams(SGIHostApi->sgiDeviceIDs[i].i, y, numParams); - if (e != numParams) - { - if (e < 0) /* Calls oserror() and alGetErrorString(). */ - { PA_SGI_SET_LAST_AL_ERROR() result = paUnanticipatedHostError; } - else - { DBUG(("alGetParams() could not get all params!\n")); result = paInternalError; } - goto cleanup; - } - deviceInfo->name = (char*)PaUtil_GroupAllocateMemory(SGIHostApi->allocations, strlen(devName) + 1); - if (!deviceInfo->name) - { result = paInsufficientMemory; goto cleanup; } - strcpy((char*)deviceInfo->name, devName); - - /* Determine whether the received number of channels belongs to input or output device. */ - if (alIsSubtype(AL_INPUT_DEVICE_TYPE, y[3].value.i)) - { - deviceInfo->maxInputChannels = y[2].value.i; - deviceInfo->maxOutputChannels = 0; - } - else if (alIsSubtype(AL_OUTPUT_DEVICE_TYPE, y[3].value.i)) - { - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = y[2].value.i; - } - else /* Should never occur. */ - { - DBUG(("AL device is neither input nor output!\n")); - result = paInternalError; - goto cleanup; - } - - /* Determine if this device is the default (in or out). If so, assign. */ - if (def_in == SGIHostApi->sgiDeviceIDs[i].i) - { - if ((*hostApi)->info.defaultInputDevice != paNoDevice) - { - DBUG(("Default input already assigned!\n")); - result = paInternalError; - goto cleanup; - } - (*hostApi)->info.defaultInputDevice = i; - /* DBUG(("Default input assigned to pa device %d (%s).\n", i, deviceInfo->name)); */ - } - else if (def_out == SGIHostApi->sgiDeviceIDs[i].i) - { - if ((*hostApi)->info.defaultOutputDevice != paNoDevice) - { - DBUG(("Default output already assigned!\n")); - result = paInternalError; - goto cleanup; - } - (*hostApi)->info.defaultOutputDevice = i; - /* DBUG(("Default output assigned to pa device %d (%s).\n", i, deviceInfo->name)); */ - } - /*---------------------------------------------- Default latencies set to 'reasonable' values. */ - deviceInfo->defaultLowInputLatency = 0.050; /* 50 milliseconds seems ok. */ - deviceInfo->defaultLowOutputLatency = 0.050; /* These are ALSO ABSOLUTE MINIMA in OpenStream(). */ - deviceInfo->defaultHighInputLatency = 0.500; /* 500 milliseconds a reasonable value? */ - deviceInfo->defaultHighOutputLatency = 0.500; /* Ten times these are ABSOLUTE MAX in OpenStream()). */ - - deviceInfo->defaultSampleRate = alFixedToDouble(y[1].value.ll); /* Read current sr. */ - (*hostApi)->deviceInfos[i] = deviceInfo; - ++(*hostApi)->info.deviceCount; - } - } - /* What if (deviceCount==0)? */ - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface(&SGIHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface(&SGIHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); -cleanup: - if (result != paNoError) - { - if (SGIHostApi) - { - if (SGIHostApi->allocations) - { - PaUtil_FreeAllAllocations(SGIHostApi->allocations); - PaUtil_DestroyAllocationGroup(SGIHostApi->allocations); - } - PaUtil_FreeMemory(SGIHostApi); - } - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaSGIHostApiRepresentation *SGIHostApi = (PaSGIHostApiRepresentation*)hostApi; - - /* Clean up any resources not handled by the allocation group. */ - if( SGIHostApi->allocations ) - { - PaUtil_FreeAllAllocations( SGIHostApi->allocations ); - PaUtil_DestroyAllocationGroup( SGIHostApi->allocations ); - } - PaUtil_FreeMemory( SGIHostApi ); -} - -/* - Check if samplerate is supported for this output device. Called once - or twice by function IsFormatSupported() and one time by OpenStream(). - When paUnanticipatedHostError is returned, the caller does NOT have - to call PA_SGI_SET_LAST_AL_ERROR() or such. -*/ -static PaError sr_supported(int al_device, double sr) -{ - int e; - PaError result; - ALparamInfo pinfo; - long long lsr; /* 64 bit fixed point internal AL samplerate. */ - - if (alGetParamInfo(al_device, AL_RATE, &pinfo)) - { - e = oserror(); - DBUG(("alGetParamInfo(AL_RATE) failed: %s.\n", alGetErrorString(e))); - if (e == AL_BAD_RESOURCE) - result = paInvalidDevice; - else - { - PA_SGI_SET_LAST_AL_ERROR() /* Sure an AL error occured. */ - result = paUnanticipatedHostError; - } - } - else - { - lsr = alDoubleToFixed(sr); /* Within the range? */ - if ((pinfo.min.ll <= lsr) && (lsr <= pinfo.max.ll)) - result = paFormatIsSupported; - else - result = paInvalidSampleRate; - } - /* DBUG(("sr_supported()=%d.\n", result)); */ - return result; -} - - -/* - See common/portaudio.h (suggestedLatency field is ignored). -*/ -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaSGIHostApiRepresentation* SGIHostApi = (PaSGIHostApiRepresentation*)hostApi; - int inputChannelCount, outputChannelCount, result; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - /* Unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification. */ - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - /* Check that input device can support inputChannelCount. */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - /* Validate inputStreamInfo. */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - /* Check if samplerate is supported for this input device. */ - result = sr_supported(SGIHostApi->sgiDeviceIDs[inputParameters->device].i, sampleRate); - if (result != paFormatIsSupported) /* PA_SGI_SET_LAST_AL_ERROR() may already be called. */ - return result; - } - else - { - inputChannelCount = 0; - } - if( outputParameters ) /* As with input above. */ - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - /* Check if samplerate is supported for this output device. */ - result = sr_supported(SGIHostApi->sgiDeviceIDs[outputParameters->device].i, sampleRate); - if (result != paFormatIsSupported) - return result; - } - else - { - outputChannelCount = 0; - } - /* IMPLEMENT ME: - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - */ - /* suppress unused variable warnings */ - (void) inputSampleFormat; - (void) outputSampleFormat; - return paFormatIsSupported; -} - -/** Auxilary struct, embedded twice in the struct below, for inputs and outputs. */ -typedef struct PaSGIhostPortBuffer -{ - /** NULL means IRIX AL port closed. */ - ALport port; - /** NULL means memory not allocated. */ - void* buffer; -} - PaSGIhostPortBuffer; - -/** Stream data structure specifically for this IRIX AL implementation. */ -typedef struct PaSGIStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - unsigned long framesPerHostCallback; - /** Allocated host buffers and AL ports. */ - PaSGIhostPortBuffer hostPortBuffIn, - hostPortBuffOut; - /** Copy of stream flags given to OpenStream(). */ - PaStreamFlags streamFlags; - /** Stream state may be 0 or 1 or 2, but never 3. */ - unsigned char state; - /** Requests to stop or abort may come from the parent, - or from the child itself (user callback result). */ - unsigned char stopAbort; - pthread_t thread; -} - PaSGIStream; - -/** Stream can be in only one of the following three states: stopped (1), active (2), or - callback finshed (0). To prevent 'state 3' from occurring, Setting and testing of the - state bits is done atomically. -*/ -#define PA_SGI_STREAM_FLAG_FINISHED_ (0) /* After callback finished or cancelled queued buffers. */ -#define PA_SGI_STREAM_FLAG_STOPPED_ (1) /* Set by OpenStream(), StopStream() and AbortStream(). */ -#define PA_SGI_STREAM_FLAG_ACTIVE_ (2) /* Set by StartStream. Reset by OpenStream(), */ - /* StopStream() and AbortStream(). */ - -/** Stop requests, via the 'stopAbort' field can be either 1, meaning 'stop' or 2, meaning 'abort'. - When both occur at the same time, 'abort' takes precedence, even after a first 'stop'. -*/ -#define PA_SGI_REQ_CONT_ (0) /* Reset by OpenStream(), StopStream and AbortStream. */ -#define PA_SGI_REQ_STOP_ (1) /* Set by StopStream(). */ -#define PA_SGI_REQ_ABORT_ (2) /* Set by AbortStream(). */ - - -/** Called by OpenStream() once or twice. First, the number of channels, sampleformat, and - queue size are configured. The configuration is then bound to the specified AL device. - Then an AL port is opened. Finally, the samplerate of the device is altered (or at least - set again). - - After successful return, actual latency is written in *latency, and actual samplerate - in *samplerate. - - @param pa_params may be NULL and pa_params->channelCount may also be null, in both - cases the function immediately returns. - @return paNoError if configuration was skipped or if it succeeded. -*/ -static PaError set_sgi_device(ALvalue* sgiDeviceIDs, /* Array built by PaSGI_Initialize(). */ - const PaStreamParameters* pa_params, /* read device and channels. */ - double* latency, /* Read and write in seconds. */ - - PaSampleFormat pasfmt, /* Don't read from pa_params!. */ - char* direction, /* "r" or "w". */ - char* name, - long framesPerHostBuffer, - double* samplerate, /* Also writes back here. */ - PaSGIhostPortBuffer* hostPortBuff) /* Receive pointers here. */ -{ - int bytesPerFrame, sgiDevice, alErr, d, dd, iq_size, default_iq_size; - ALpv pvs[2]; - ALconfig alc = NULL; - PaError result = paNoError; - - if (!pa_params) - goto cleanup; /* Not errors, just not full duplex, skip all. */ - if (!pa_params->channelCount) - goto cleanup; - alc = alNewConfig(); /* Create default config. This defaults to stereo, 16-bit integer data. */ - if (!alc) /* Call alFreeConfig() later, when done with it. */ - { result = paInsufficientMemory; goto cleanup; } - /*----------------------- CONFIGURE NUMBER OF CHANNELS: ---------------------------*/ - if (alSetChannels(alc, pa_params->channelCount)) /* Returns 0 on success. */ - { - if (oserror() == AL_BAD_CHANNELS) - result = paInvalidChannelCount; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - bytesPerFrame = pa_params->channelCount; /* Is multiplied by width below. */ - /*----------------------- CONFIGURE SAMPLE FORMAT: --------------------------------*/ - if (pasfmt == paFloat32) - { - if (alSetSampFmt(alc, AL_SAMPFMT_FLOAT)) - { - if (oserror() == AL_BAD_SAMPFMT) - result = paSampleFormatNotSupported; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - bytesPerFrame *= 4; /* No need to set width for floats. */ - } - else - { - if (alSetSampFmt(alc, AL_SAMPFMT_TWOSCOMP)) - { - if (oserror() == AL_BAD_SAMPFMT) - result = paSampleFormatNotSupported; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - if (pasfmt == paInt8) - { - if (alSetWidth(alc, AL_SAMPLE_8)) - { - if (oserror() == AL_BAD_WIDTH) - result = paSampleFormatNotSupported; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - /* bytesPerFrame *= 1; */ - } - else if (pasfmt == paInt16) - { - if (alSetWidth(alc, AL_SAMPLE_16)) - { - if (oserror() == AL_BAD_WIDTH) - result = paSampleFormatNotSupported; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - bytesPerFrame *= 2; - } - else if (pasfmt == paInt24) - { - if (alSetWidth(alc, AL_SAMPLE_24)) - { - if (oserror() == AL_BAD_WIDTH) - result = paSampleFormatNotSupported; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - bytesPerFrame *= 3; /* OR 4 ???????! */ - } - else return paSampleFormatNotSupported; - } - /*----------------------- SET INTERNAL AL QUEUE SIZE: -------------------------------*/ - /* The AL API doesn't provide a means for querying minimum and maximum buffer sizes. - So, if the requested size fails, try again with a value that is closer to the AL's - default queue size. In this implementation, 'Portaudio latency' corresponds to - the AL queue size minus one buffersize: - AL queue size - framesPerHostBuffer - PA latency = ----------------------------------- - sample rate */ - default_iq_size = alGetQueueSize(alc); - if (default_iq_size < 0) /* So let's first get that 'default size'. */ - { /* Default internal queue size could not be determined. */ - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - goto cleanup; - } - /* AL buffer becomes somewhat bigger than the suggested latency, notice this is */ - /* based on requsted samplerate, not in the actual rate, which is measured later. */ - /* Do NOT read pa_params->suggestedLatency, but use the limited *latency param! */ - - iq_size = (int)(0.5 + ((*latency) * (*samplerate))) + (int)framesPerHostBuffer; - /* The AL buffer becomes somewhat */ - /* bigger than the suggested latency. */ - if (iq_size < (framesPerHostBuffer << 1)) /* Make sure the minimum is twice */ - { /* framesPerHostBuffer. */ - DBUG(("Setting minimum queue size.\n")); - iq_size = (framesPerHostBuffer << 1); - } - d = iq_size - default_iq_size; /* Determine whether we'll decrease */ - while (alSetQueueSize(alc, iq_size)) /* or increase after failing. */ - { /* Size in sample frames. */ - if (oserror() != AL_BAD_QSIZE) /* Stop at AL_BAD_CONFIG. */ - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - goto cleanup; - } - dd = iq_size - default_iq_size; /* Stop when even the default size failed */ - if (((d >= 0) && (dd <= 0)) || /* (dd=0) or when difference flipped sign. */ - ((d <= 0) && (dd >= 0)) || - (iq_size <= framesPerHostBuffer)) /* Also guarentee that framesPerHostBuffer */ - { /* can be subtracted (res>0) after return. */ - DBUG(("Could not set AL queue size to %d sample frames!\n", iq_size)); - result = paInternalError; /* FIX: PROBABLY AN INAPROPRIATE ERROR CODE HERE. */ - goto cleanup; /* As inapropriate as paUnanticipatedHostError was? */ - } - DBUG(("Failed to set internal queue size to %d frames, ", iq_size)); - if (d > 0) - iq_size -= framesPerHostBuffer; /* Try lesser multiple. */ - else - iq_size += framesPerHostBuffer; /* Try larger multiple. */ - DBUG(("trying %d frames now...\n", iq_size)); - } - /* Note: Actual latency is written back to *latency after meausuring actual (not - the requested) samplerate. See below. - */ - /*----------------------- ALLOCATE HOST BUFFER: --------------------------------------*/ - hostPortBuff->buffer = PaUtil_AllocateMemory((long)bytesPerFrame * framesPerHostBuffer); - if (!hostPortBuff->buffer) /* Caller is responsible for cleanup+close after failures! */ - { result = paInsufficientMemory; goto cleanup; } - /*----------------------- BIND CONFIGURATION TO DEVICE: ------------------------------*/ - sgiDevice = sgiDeviceIDs[pa_params->device].i; - if (alSetDevice(alc, sgiDevice)) /* Try to switch the hardware. */ - { - if (oserror() == AL_BAD_DEVICE) - result = paInvalidDevice; - else - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - } - goto cleanup; - } - /*----------------------- OPEN PORT: ----------------------------------------------*/ - hostPortBuff->port = alOpenPort(name, direction, alc); /* Caller is responsible */ - if (!hostPortBuff->port) /* for closing after fail. */ - { - PA_SGI_SET_LAST_AL_ERROR() - result = paUnanticipatedHostError; - goto cleanup; - } /* Maybe set SR earlier? */ - /*----------------------- SET SAMPLERATE: -----------------------------------------*/ - pvs[0].param = AL_MASTER_CLOCK; /* Attempt to set a crystal-based sample- */ - pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE; /* rate on input or output device. */ - pvs[1].param = AL_RATE; - pvs[1].value.ll = alDoubleToFixed(*samplerate); - if (2 != alSetParams(sgiDevice, pvs, 2)) - { - DBUG(("alSetParams() failed to set samplerate to %.4f Hz!\n", *samplerate)); - result = paInvalidSampleRate; - goto cleanup; - } - /*----------------------- GET ACTUAL SAMPLERATE: ---------------------------*/ - alErr = alGetParams(sgiDevice, &pvs[1], 1); /* SEE WHAT WE REALY SET IT TO. */ - if (alErr != 1) /* And return that to caller. */ - { - DBUG(("alGetParams() failed to read samplerate!\n")); - result = paInvalidSampleRate; - goto cleanup; - } - *samplerate = alFixedToDouble(pvs[1].value.ll); /* Between 1 Hz and 1 MHz. */ - if ((*samplerate < 1.0) || (*samplerate > 1000000.0)) - { - DBUG(("alFixedToDouble() resulted a weird samplerate: %.6f Hz!\n", *samplerate)); - result = paInvalidSampleRate; - goto cleanup; - } - /*----------------------- CALC ACTUAL LATENCY (based on actual SR): -----------------------*/ - *latency = (iq_size - framesPerHostBuffer) / (*samplerate); /* FIX: SURE > 0!???? */ -cleanup: - if (alc) - alFreeConfig(alc); /* We no longer need configuration. */ - return result; -} - -/** - Called by OpenStream() if it fails and by CloseStream. Only used here, in this file. - Fields MUST be set to NULL or to a valid value, prior to call. -*/ -static void streamCleanupAndClose(PaSGIStream* stream) -{ - if (stream->hostPortBuffIn.port) alClosePort(stream->hostPortBuffIn.port); /* Close AL ports. */ - if (stream->hostPortBuffIn.buffer) PaUtil_FreeMemory(stream->hostPortBuffIn.buffer); /* Release buffers. */ - if (stream->hostPortBuffOut.port) alClosePort(stream->hostPortBuffOut.port); - if (stream->hostPortBuffOut.buffer) PaUtil_FreeMemory(stream->hostPortBuffOut.buffer); -} - - -/* See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters. */ -static PaError OpenStream(struct PaUtilHostApiRepresentation* hostApi, - PaStream** s, - const PaStreamParameters* ipp, - const PaStreamParameters* opp, - double sampleRate, /* Common to both i and o. */ - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback* streamCallback, - void* userData) -{ - PaError result = paNoError; - PaSGIHostApiRepresentation* SGIHostApi = (PaSGIHostApiRepresentation*)hostApi; - PaSGIStream* stream = 0; - unsigned long framesPerHostBuffer; /* Not necessarily the same as framesPerBuffer. */ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat, - hostInputSampleFormat, hostOutputSampleFormat; - double sr_in, sr_out, - latency_in, latency_out; - static const PaSampleFormat irixFormats = (paInt8 | paInt16 | paInt24 | paFloat32); - /* Constant used by PaUtil_SelectClosestAvailableFormat(). Because IRIX AL does not - provide a way to query for possible formats for a given device, interface or port, - just add together the formats we know that are supported in general by IRIX AL - (at the end of the year 2003): AL_SAMPFMT_TWOSCOMP with AL_SAMPLE_8(=paInt8), - AL_SAMPLE_16(=paInt16) or AL_SAMPLE_24(=paInt24); AL_SAMPFMT_FLOAT(=paFloat32); - AL_SAMPFMT_DOUBLE(=paFloat64); IRIX misses unsigned 8 and 32 bit signed ints. - */ - DBUG(("OpenStream() started.\n")); - if (ipp) - { - inputChannelCount = ipp->channelCount; - inputSampleFormat = ipp->sampleFormat; - /* Unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification. */ - if (ipp->device == paUseHostApiSpecificDeviceSpecification) /* DEVICE CHOOSEN BY CLIENT. */ - return paInvalidDevice; - /* Check that input device can support inputChannelCount. */ - if (inputChannelCount > hostApi->deviceInfos[ipp->device]->maxInputChannels) - return paInvalidChannelCount; - /* Validate inputStreamInfo. */ - if (ipp->hostApiSpecificStreamInfo) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat(irixFormats, inputSampleFormat); - /* Check if samplerate is supported for this input device. */ - result = sr_supported(SGIHostApi->sgiDeviceIDs[ipp->device].i, sampleRate); - if (result != paFormatIsSupported) /* PA_SGI_SET_LAST_AL_ERROR() may already be called. */ - return result; - /* Validate input latency. Use defaults if necessary. */ - if (ipp->suggestedLatency < hostApi->deviceInfos[ipp->device]->defaultLowInputLatency) - latency_in = hostApi->deviceInfos[ipp->device]->defaultLowInputLatency; /* Low = minimum. */ - else if (ipp->suggestedLatency > 10.0 * hostApi->deviceInfos[ipp->device]->defaultHighInputLatency) - latency_in = 10.0 * hostApi->deviceInfos[ipp->device]->defaultHighInputLatency; /* 10*High = max. */ - else - latency_in = ipp->suggestedLatency; - } - else - { - inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - latency_in = 0.0; /* Necessary? */ - } - if (opp) - { - outputChannelCount = opp->channelCount; - outputSampleFormat = opp->sampleFormat; - if (opp->device == paUseHostApiSpecificDeviceSpecification) /* Like input (above). */ - return paInvalidDevice; - if (outputChannelCount > hostApi->deviceInfos[opp->device]->maxOutputChannels) - return paInvalidChannelCount; - if (opp->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat(irixFormats, outputSampleFormat); - /* Check if samplerate is supported for this output device. */ - result = sr_supported(SGIHostApi->sgiDeviceIDs[opp->device].i, sampleRate); - if (result != paFormatIsSupported) - return result; - /* Validate output latency. Use defaults if necessary. */ - if (opp->suggestedLatency < hostApi->deviceInfos[opp->device]->defaultLowOutputLatency) - latency_out = hostApi->deviceInfos[opp->device]->defaultLowOutputLatency; /* Low = minimum. */ - else if (opp->suggestedLatency > 10.0 * hostApi->deviceInfos[opp->device]->defaultHighOutputLatency) - latency_out = 10.0 * hostApi->deviceInfos[opp->device]->defaultHighOutputLatency; /* 10*High = max. */ - else - latency_out = opp->suggestedLatency; - } - else - { - outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialised var' warning. */ - latency_out = 0.0; - } - /* Sure that ipp and opp will never be both NULL. */ - - if( (streamFlags & paPlatformSpecificFlags) != 0 ) /* Validate platform specific flags. */ - return paInvalidFlag; /* Unexpected platform specific flag. */ - - stream = (PaSGIStream*)PaUtil_AllocateMemory( sizeof(PaSGIStream) ); - if (!stream) - { result = paInsufficientMemory; goto cleanup; } - - stream->hostPortBuffIn.port = (ALport)NULL; /* Ports closed. */ - stream->hostPortBuffIn.buffer = NULL; /* No buffers yet. */ - stream->hostPortBuffOut.port = (ALport)NULL; - stream->hostPortBuffOut.buffer = NULL; - - if (streamCallback) - PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, - &SGIHostApi->callbackStreamInterface, streamCallback, userData); - else - PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, - &SGIHostApi->blockingStreamInterface, streamCallback, userData); - /* (NULL.) */ - if (framesPerBuffer == paFramesPerBufferUnspecified) /* Proposal 004. */ - { /* Keep framesPerBuffer zero but come up with some fixed host buffer size. */ - double lowest_lat = 0.0; /* 0.0 to surpress uninit warning, we're sure it will end up higher. */ - if (ipp) - lowest_lat = latency_in; /* Sure > 0.0! */ - if (opp && (latency_out < lowest_lat)) - lowest_lat = latency_out; - /* So that queue size becomes approximately 5 times framesPerHostBuffer: */ - framesPerHostBuffer = (unsigned long)((lowest_lat * sampleRate) / 4.0); - /* But always limit: */ - if (framesPerHostBuffer < 64L) - framesPerHostBuffer = 64L; - else if (framesPerHostBuffer > 32768L) - framesPerHostBuffer = 32768L; - DBUG(("Decided to use a fixed host buffer size of %ld frames.\n", framesPerHostBuffer)); - } - else - framesPerHostBuffer = framesPerBuffer; /* Then just take the requested amount. No buffer-adaption yet? */ - - sr_in = sr_out = sampleRate; - /*-------------------------------------------------------------------------------------------*/ - result = set_sgi_device(SGIHostApi->sgiDeviceIDs, /* Needed by alSetDevice and other functs. */ - ipp, /* Reads channelCount, device but NOT latency. */ - &latency_in, /* Read limited requested latency but also WRITE actual. */ - hostInputSampleFormat, /* For alSetSampFmt and alSetWidth. */ - "r", /* "r" for reading from input port. */ - "portaudio in", /* Name string. */ - framesPerHostBuffer, /* As calculated or as requested by the client. */ - &sr_in, /* Receive actual s.rate after setting it. */ - &stream->hostPortBuffIn); /* Receives ALport and input host buffer. */ - if (result != paNoError) goto cleanup; - /*-------------------------------------------------------------------------------------------*/ - result = set_sgi_device(SGIHostApi->sgiDeviceIDs, - opp, - &latency_out, - hostOutputSampleFormat, - "w", /* "w" for writing. */ - "portaudio out", - framesPerHostBuffer, - &sr_out, - &stream->hostPortBuffOut); - if (result != paNoError) goto cleanup; - /*------------------------------------------------------------------------------------------*/ - if (fabs(sr_in - sr_out) > 0.001) /* Make sure both are the 'same'. */ - { - DBUG(("Weird samplerate difference between input and output!\n")); - result = paInvalidSampleRate; /* Could not come up with a better error code. */ - goto cleanup; - } /* sr_in '==' sr_out. */ - sampleRate = sr_in; /* Following fields set to estimated or actual values: */ - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - stream->streamRepresentation.streamInfo.inputLatency = latency_in; /* 0.0 if output only. */ - stream->streamRepresentation.streamInfo.outputLatency = latency_out; /* 0.0 if input only. */ - - PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate); - result = PaUtil_InitializeBufferProcessor(&stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, - framesPerBuffer, /* As requested by OpenStream(), may be zero! */ - framesPerHostBuffer, /* Use fixed number of frames per host buffer */ - paUtilFixedHostBufferSize, /* to keep things simple. See pa_common/pa_ */ - streamCallback, userData); /* process.h for more hostbuffersize options. */ - if (result != paNoError) - goto cleanup; - - stream->framesPerHostCallback = framesPerHostBuffer; - stream->streamFlags = streamFlags; /* Remember priming request. */ - stream->state = PA_SGI_STREAM_FLAG_STOPPED_; /* After opening, the stream */ - stream->stopAbort = PA_SGI_REQ_CONT_; /* is in the stopped state. */ - *s = (PaStream*)stream; /* Pass object to caller. */ -cleanup: - if (result != paNoError) /* Always set result when jumping to cleanup after failure. */ - { - if (stream) - { - streamCleanupAndClose(stream); /* Frees i/o buffers and closes AL ports. */ - PaUtil_FreeMemory(stream); - } - } - return result; -} - -/** POSIX thread that performs the actual i/o and calls the client's callback, - spawned by StartStream(). -*/ -static void* PaSGIpthread(void *userData) -{ - PaSGIStream* stream = (PaSGIStream*)userData; - int callbackResult = paContinue; - double nanosec_per_frame; - PaStreamCallbackTimeInfo timeInfo = { 0, 0, 0 }; - - stream->state = PA_SGI_STREAM_FLAG_ACTIVE_; /* Parent thread also sets active flag, but we - make no assumption about who does this first. */ - nanosec_per_frame = 1000000000.0 / stream->streamRepresentation.streamInfo.sampleRate; - /*----------------------------------------------- OUTPUT PRIMING: -----------------------------*/ - if (stream->hostPortBuffOut.port) /* Somewhat less than AL queue size so the next */ - { /* output buffer will (probably) not block. */ - unsigned long frames_to_prime = (long)(0.5 + - (stream->streamRepresentation.streamInfo.outputLatency - * stream->streamRepresentation.streamInfo.sampleRate)); - if (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) - { - PaStreamCallbackFlags cbflags = paPrimingOutput; - if (stream->hostPortBuffIn.port) /* Only set this flag in case of full duplex. */ - cbflags |= paInputUnderflow; - DBUG(("Prime with client's callback: < %ld frames.\n", frames_to_prime)); - while (frames_to_prime >= stream->framesPerHostCallback) /* We will not do less (yet). */ - { /* TODO: Timestamps and CPU load */ - PaUtil_BeginBufferProcessing(&stream->bufferProcessor, /* measurement during priming. */ - &timeInfo, - cbflags); /* Pass underflow + priming flags. */ - if (stream->hostPortBuffIn.port) /* Does that provide client's call- */ - PaUtil_SetNoInput(&stream->bufferProcessor); /* back with silent inputbuffers? */ - - PaUtil_SetOutputFrameCount(&stream->bufferProcessor, 0); /* 0=take host buffer size. */ - PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, - stream->hostPortBuffOut.buffer, 0); - callbackResult = paContinue; /* Call the client's callback. */ - frames_to_prime -= PaUtil_EndBufferProcessing(&stream->bufferProcessor, &callbackResult); - if (callbackResult == paAbort) - { /* What should we do in other cases */ - stream->stopAbort = PA_SGI_REQ_ABORT_; /* where (callbackResult!=paContinue). */ - break; /* Don't even output the samples just returned (also skip following while). */ - } - else /* Write interleaved samples to SGI device. */ - alWriteFrames(stream->hostPortBuffOut.port, stream->hostPortBuffOut.buffer, - stream->framesPerHostCallback); - } - } - else /* Prime with silence. */ - { - DBUG(("Prime with silence: %ld frames.\n", frames_to_prime)); - alZeroFrames(stream->hostPortBuffOut.port, frames_to_prime); - } - } - /*------------------------------------------------------ I/O: ---------------------------------*/ - while (!stream->stopAbort) /* Exit loop immediately when 'stop' or 'abort' are raised. */ - { - unsigned long framesProcessed; - stamp_t fn, t, fnd, td; /* Unsigned 64 bit. */ - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - /* IMPLEMENT ME: - handle buffer slips. */ - if (stream->hostPortBuffIn.port) - { - /* Get device sample frame number associated with next audio sample frame - we're going to read from this port. */ - alGetFrameNumber(stream->hostPortBuffIn.port, &fn); - /* Get some recent pair of (frame number, time) from the audio device to - which our port is connected. time is 'UST' which is given in nanoseconds - and shared with the other audio devices and with other media. */ - alGetFrameTime(stream->hostPortBuffIn.port, &fnd, &td); - /* Calculate UST associated with fn, the next sample frame we're going to read or - write. Because this is signed arithmetic, code works for both inputs and outputs. */ - t = td + (stamp_t) ((double)(fn - fnd) * nanosec_per_frame); - /* If port is not in underflow or overflow state, we can alReadFrames() or - alWriteFrames() here and know that t is the time associated with the first - sample frame of the buffer we read or write. */ - timeInfo.inputBufferAdcTime = ((PaTime)t) / 1000000000.0; - /* Read interleaved samples from AL port (I think it will block only the first time). */ - alReadFrames(stream->hostPortBuffIn.port, stream->hostPortBuffIn.buffer, - stream->framesPerHostCallback); - } - if (stream->hostPortBuffOut.port) - { - alGetFrameNumber(stream->hostPortBuffOut.port, &fn); - alGetFrameTime(stream->hostPortBuffOut.port, &fnd, &td); - t = td + (stamp_t) ((double)(fn - fnd) * nanosec_per_frame); - timeInfo.outputBufferDacTime = ((PaTime)t) / 1000000000.0; - } - dmGetUST((unsigned long long*)(&t)); /* Receive time in nanoseconds in t. */ - timeInfo.currentTime = ((PaTime)t) / 1000000000.0; - - /* If you need to byte swap or shift inputBuffer to convert it into a pa format, do it here. */ - PaUtil_BeginBufferProcessing(&stream->bufferProcessor, - &timeInfo, - 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */); - - if (stream->hostPortBuffIn.port) /* Equivalent to (inputChannelCount > 0) */ - { /* We are sure about the amount to transfer (PaUtil_Set before alRead). */ - PaUtil_SetInputFrameCount(&stream->bufferProcessor, 0 /* 0 means take host buffer size */); - PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, - 0, /* first channel of inputBuffer is channel 0 */ - stream->hostPortBuffIn.buffer, - 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ - } - if (stream->hostPortBuffOut.port) - { - PaUtil_SetOutputFrameCount(&stream->bufferProcessor, 0 /* 0 means take host buffer size */); - PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, - 0, /* first channel of outputBuffer is channel 0 */ - stream->hostPortBuffOut.buffer, - 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ - } - /* - You must pass a valid value of callback result to PaUtil_EndBufferProcessing() - in general you would pass paContinue for normal operation, and - paComplete to drain the buffer processor's internal output buffer. - You can check whether the buffer processor's output buffer is empty - using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor ) - */ - callbackResult = paContinue; /* Whoops, lost this somewhere, back again in v 1.2.2.16! */ - framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor, &callbackResult); - /* If you need to byte swap or shift outputBuffer to convert it to host format, do it here. */ - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if (callbackResult != paContinue) - { /* Once finished, call the finished callback. */ - DBUG(("SGI callbackResult = %d.\n", callbackResult)); - if (stream->streamRepresentation.streamFinishedCallback) - stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData); - if (callbackResult == paAbort) - { - stream->stopAbort = PA_SGI_REQ_ABORT_; - break; /* Don't play the last buffer returned. */ - } - else /* paComplete or some other non-zero value. */ - stream->stopAbort = PA_SGI_REQ_STOP_; - } - if (stream->hostPortBuffOut.port) /* Write interleaved samples to SGI device */ - alWriteFrames(stream->hostPortBuffOut.port, /* (like unix_oss, AFTER checking callback result). */ - stream->hostPortBuffOut.buffer, stream->framesPerHostCallback); - } - if (stream->hostPortBuffOut.port) /* Drain output buffer(s), as long as we don't see an 'abort' request. */ - { - while ((!(stream->stopAbort & PA_SGI_REQ_ABORT_)) && /* Assume _STOP_ is set (or meant). */ - (alGetFilled(stream->hostPortBuffOut.port) > 1)) /* In case of ABORT we quickly leave (again). */ - ; /* Don't provide any new (not even silent) samples, but let an underrun [almost] occur. */ - } - if (callbackResult != paContinue) - stream->state = PA_SGI_STREAM_FLAG_FINISHED_; - return NULL; -} - - -/* - When CloseStream() is called, the multi-api layer ensures - that the stream has already been stopped or aborted. -*/ -static PaError CloseStream(PaStream* s) -{ - PaError result = paNoError; - PaSGIStream* stream = (PaSGIStream*)s; - - DBUG(("SGI CloseStream() started.\n")); - streamCleanupAndClose(stream); /* Releases i/o buffers and closes AL ports. */ - PaUtil_TerminateBufferProcessor(&stream->bufferProcessor); - PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation); - PaUtil_FreeMemory(stream); - return result; -} - - -static PaError StartStream(PaStream *s) -{ - PaError result = paNoError; - PaSGIStream* stream = (PaSGIStream*)s; - - DBUG(("StartStream() started.\n")); - PaUtil_ResetBufferProcessor(&stream->bufferProcessor); /* See pa_common/pa_process.h. */ - if (stream->bufferProcessor.streamCallback) - { /* only when callback is used */ - if (pthread_create(&stream->thread, - NULL, /* pthread_attr_t * attr */ - PaSGIpthread, /* Function to spawn. */ - (void*)stream)) /* Pass stream as arg. */ - { - PA_SGI_SET_LAST_IRIX_ERROR() /* Let's hope oserror() tells something useful. */ - result = paUnanticipatedHostError; - } - else - stream->state = PA_SGI_STREAM_FLAG_ACTIVE_; - } /* Set active before returning from this function. */ - else - stream->state = PA_SGI_STREAM_FLAG_ACTIVE_; /* Apparently, setting active for blocking i/o is */ - return result; /* necessary (for patest_write_sine for example). */ -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaSGIStream* stream = (PaSGIStream*)s; - - if (stream->bufferProcessor.streamCallback) /* Only for callback streams. */ - { - stream->stopAbort = PA_SGI_REQ_STOP_; /* Signal and wait for the thread to drain outputs. */ - if (pthread_join(stream->thread, NULL)) /* When succesful, stream->state */ - { /* is still ACTIVE, or FINISHED. */ - PA_SGI_SET_LAST_IRIX_ERROR() - result = paUnanticipatedHostError; - } - else /* Transition from ACTIVE or FINISHED to STOPPED. */ - stream->state = PA_SGI_STREAM_FLAG_STOPPED_; - stream->stopAbort = PA_SGI_REQ_CONT_; /* For possible next start. */ - } -/* else - stream->state = PA_SGI_STREAM_FLAG_STOPPED_; Is this necessary for blocking i/o? */ - return result; -} - - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaSGIStream *stream = (PaSGIStream*)s; - - if (stream->bufferProcessor.streamCallback) /* Only for callback streams. */ - { - stream->stopAbort = PA_SGI_REQ_ABORT_; - if (pthread_join(stream->thread, NULL)) - { - PA_SGI_SET_LAST_IRIX_ERROR() - result = paUnanticipatedHostError; - } - else /* Transition from ACTIVE or FINISHED to STOPPED. */ - stream->state = PA_SGI_STREAM_FLAG_STOPPED_; - stream->stopAbort = PA_SGI_REQ_CONT_; /* For possible next start. */ - } -/* else - stream->state = PA_SGI_STREAM_FLAG_STOPPED_; Is this necessary for blocking i/o? */ - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) /* Not just the opposite of IsStreamActive(): */ -{ /* in the 'callback finished' state, it */ - /* returns zero instead of nonzero. */ - if (((PaSGIStream*)s)->state & PA_SGI_STREAM_FLAG_STOPPED_) - return 1; - return 0; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - if (((PaSGIStream*)s)->state & PA_SGI_STREAM_FLAG_ACTIVE_) - return 1; - return 0; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - stamp_t t; - - (void) s; /* Suppress unused argument warning. */ - dmGetUST((unsigned long long*)(&t)); /* Receive time in nanoseconds in t. */ - return (PaTime)t / 1000000000.0; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaSGIStream *stream = (PaSGIStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaSGIStream* stream = (PaSGIStream*)s; - int n; - -printf("stream->framesPerHostCallback=%ld.\n", stream->framesPerHostCallback); -fflush(stdout); - - while (frames) - { - if (frames > stream->framesPerHostCallback) n = stream->framesPerHostCallback; - else n = frames; - /* Read interleaved samples from SGI device. */ - alReadFrames(stream->hostPortBuffIn.port, /* Port already opened by OpenStream(). */ - stream->hostPortBuffIn.buffer, n); /* Already allocated by OpenStream(). */ - /* alReadFrames() always returns 0. */ - PaUtil_SetInputFrameCount(&stream->bufferProcessor, 0); /* 0 means take host buffer size */ - PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, - 0, /* first channel of inputBuffer is channel 0 */ - stream->hostPortBuffIn.buffer, - 0 ); /* 0 means use inputChannelCount passed at init. */ - /* Copy samples from host input channels set up by the PaUtil_SetInterleavedInputChannels - to a user supplied buffer. */ -printf("frames=%ld, buffer=%ld\n", frames, (long)buffer); -fflush(stdout); - PaUtil_CopyInput(&stream->bufferProcessor, &buffer, n); - frames -= n; - } -printf("DONE: frames=%ld, buffer=%ld\n", frames, (long)buffer); - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaSGIStream* stream = (PaSGIStream*)s; - unsigned long n; - while (frames) - { - PaUtil_SetOutputFrameCount(&stream->bufferProcessor, 0); /* 0 means take host buffer size */ - PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, - 0, /* first channel of inputBuffer is channel 0 */ - stream->hostPortBuffOut.buffer, - 0 ); /* 0 means use inputChannelCount passed at init. */ - /* Copy samples from user supplied buffer to host input channels set up by - PaUtil_SetInterleavedOutputChannels. Copies the minimum of the number of user frames - (specified by the frameCount parameter) and the number of host frames (specified in - a previous call to SetOutputFrameCount()). */ - n = PaUtil_CopyOutput(&stream->bufferProcessor, &buffer, frames); - /* Write interleaved samples to SGI device. */ - alWriteFrames(stream->hostPortBuffOut.port, stream->hostPortBuffOut.buffer, n); - frames -= n; /* alWriteFrames always returns 0. */ - } - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - return (signed long)alGetFilled(((PaSGIStream*)s)->hostPortBuffIn.port); -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - return (signed long)alGetFillable(((PaSGIStream*)s)->hostPortBuffOut.port); -} - - -/* CVS reminder: - To download the 'v19-devel' branch from portaudio's CVS server for the first time, type: - cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs checkout -r v19-devel portaudio - Then 'cd' to the 'portaudio' directory that should have been created. - To commit changes: - cvs -d:pserver:pieter@www.portaudio.com:/home/cvs login - cvs -d:pserver:pieter@www.portaudio.com:/home/cvs commit -m 'blabla.' -r v19-devel pa_sgi/pa_sgi.c - cvs -d:pserver:pieter@www.portaudio.com:/home/cvs logout - To see if someone else worked on something: - cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs update -r v19-devel - To get an older revision of a certain file (without sticky business): - cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs update -p -r 1.1.1.1.2.4 pa_tests/patest1.c >pa_tests/patest1.c-OLD - To see logs: - cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs log pa_common/pa_skeleton.c -*/ diff --git a/portaudio-v19/pa_unix/pa_unix_hostapis.c b/portaudio-v19/pa_unix/pa_unix_hostapis.c deleted file mode 100644 index 570f596e7..000000000 --- a/portaudio-v19/pa_unix/pa_unix_hostapis.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * $Id$ - * Portable Audio I/O Library UNIX initialization table - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - -#include "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 ); - - -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 - 0 /* NULL terminated array */ - }; - -int paDefaultHostApiIndex = 0; - - diff --git a/portaudio-v19/pa_unix/pa_unix_util.c b/portaudio-v19/pa_unix/pa_unix_util.c deleted file mode 100644 index 0aa2d0c65..000000000 --- a/portaudio-v19/pa_unix/pa_unix_util.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * $Id$ - * Portable Audio I/O Library - * UNIX platform-specific support functions - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - -#include -#include -#include -#include -#include -#include -#include /* For memset */ - -#include "pa_util.h" -#include "pa_unix_util.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; -} - -/* -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 ); -} -*/ diff --git a/portaudio-v19/pa_unix/pa_unix_util.h b/portaudio-v19/pa_unix/pa_unix_util.h deleted file mode 100644 index 01dda01e5..000000000 --- a/portaudio-v19/pa_unix/pa_unix_util.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef PA_UNIX_UTIL_H -#define PA_UNIX_UTIL_H - -#include "pa_cpuload.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); - -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 ); -*/ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif diff --git a/portaudio-v19/pa_win/dev-cpp/Makefile-dll b/portaudio-v19/pa_win/dev-cpp/Makefile-dll deleted file mode 100644 index 856af638e..000000000 --- a/portaudio-v19/pa_win/dev-cpp/Makefile-dll +++ /dev/null @@ -1,78 +0,0 @@ -# Project: portaudio-dll -# Makefile created by Dev-C++ 4.9.8.2 - -CPP = g++.exe -CC = gcc.exe -WINDRES = windres.exe -RES = -OBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LINKOBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LIBS = -L"C:/Dev-CPP/lib" -fmessage-length=0 --no-export-all-symbols --add-stdcall-alias ../../../asiosdk2/asiosdk2.a -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -lwinmm -O3 -s -INCS = -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -CXXINCS = -I"C:/Dev-CPP/include/c++" -I"C:/Dev-CPP/include/c++/mingw32" -I"C:/Dev-CPP/include/c++/backward" -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -BIN = portaudio-dll.dll -CXXFLAGS = $(CXXINCS)-O3 -fmessage-length=0 -Wall -CFLAGS = $(INCS)-DBUILDING_DLL=1 -O3 -fmessage-length=0 -Wall - -.PHONY: all all-before all-after clean clean-custom - -all: all-before portaudio-dll.dll all-after - - -clean: clean-custom - rm -f $(OBJ) $(BIN) - -DLLWRAP=dllwrap.exe -DEFFILE=libportaudio-dll.def -STATICLIB=libportaudio-dll.a - -$(BIN): $(LINKOBJ) - $(DLLWRAP) --output-def $(DEFFILE) --driver-name c++ --implib $(STATICLIB) $(LINKOBJ) $(LIBS) -o $(BIN) - -./pa_skeleton.o: ../../pa_common/pa_skeleton.c - $(CPP) -c ../../pa_common/pa_skeleton.c -o ./pa_skeleton.o $(CXXFLAGS) - -./pa_stream.o: ../../pa_common/pa_stream.c - $(CPP) -c ../../pa_common/pa_stream.c -o ./pa_stream.o $(CXXFLAGS) - -./pa_trace.o: ../../pa_common/pa_trace.c - $(CPP) -c ../../pa_common/pa_trace.c -o ./pa_trace.o $(CXXFLAGS) - -./pa_allocation.o: ../../pa_common/pa_allocation.c - $(CPP) -c ../../pa_common/pa_allocation.c -o ./pa_allocation.o $(CXXFLAGS) - -./pa_converters.o: ../../pa_common/pa_converters.c - $(CPP) -c ../../pa_common/pa_converters.c -o ./pa_converters.o $(CXXFLAGS) - -./pa_cpuload.o: ../../pa_common/pa_cpuload.c - $(CPP) -c ../../pa_common/pa_cpuload.c -o ./pa_cpuload.o $(CXXFLAGS) - -./pa_dither.o: ../../pa_common/pa_dither.c - $(CPP) -c ../../pa_common/pa_dither.c -o ./pa_dither.o $(CXXFLAGS) - -./pa_front.o: ../../pa_common/pa_front.c - $(CPP) -c ../../pa_common/pa_front.c -o ./pa_front.o $(CXXFLAGS) - -./pa_process.o: ../../pa_common/pa_process.c - $(CPP) -c ../../pa_common/pa_process.c -o ./pa_process.o $(CXXFLAGS) - -./pa_asio.o: ../../pa_asio/pa_asio.cpp - $(CPP) -c ../../pa_asio/pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -./pa_win_util.o: ../pa_win_util.c - $(CPP) -c ../pa_win_util.c -o ./pa_win_util.o $(CXXFLAGS) - -./pa_win_hostapis.o: ../pa_win_hostapis.c - $(CPP) -c ../pa_win_hostapis.c -o ./pa_win_hostapis.o $(CXXFLAGS) - -./pa_win_ds.o: ../../pa_win_ds/pa_win_ds.c - $(CPP) -c ../../pa_win_ds/pa_win_ds.c -o ./pa_win_ds.o $(CXXFLAGS) - -./dsound_wrapper.o: ../../pa_win_ds/dsound_wrapper.c - $(CPP) -c ../../pa_win_ds/dsound_wrapper.c -o ./dsound_wrapper.o $(CXXFLAGS) - -./pa_win_wmme.o: ../../pa_win_wmme/pa_win_wmme.c - $(CPP) -c ../../pa_win_wmme/pa_win_wmme.c -o ./pa_win_wmme.o $(CXXFLAGS) - -./iasiothiscallresolver.o: ../../pa_asio/iasiothiscallresolver.cpp - $(CPP) -c ../../pa_asio/iasiothiscallresolver.cpp -o ./iasiothiscallresolver.o $(CXXFLAGS) diff --git a/portaudio-v19/pa_win/dev-cpp/Makefile-static b/portaudio-v19/pa_win/dev-cpp/Makefile-static deleted file mode 100644 index 2a1647ddc..000000000 --- a/portaudio-v19/pa_win/dev-cpp/Makefile-static +++ /dev/null @@ -1,75 +0,0 @@ -# Project: portaudio-static -# Makefile created by Dev-C++ 4.9.8.2 - -CPP = g++.exe -CC = gcc.exe -WINDRES = windres.exe -RES = -OBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LINKOBJ = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES) -LIBS = -L"C:/Dev-CPP/lib" -fmessage-length=0 -O3 -s -INCS = -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -CXXINCS = -I"C:/Dev-CPP/include/c++" -I"C:/Dev-CPP/include/c++/mingw32" -I"C:/Dev-CPP/include/c++/backward" -I"C:/Dev-CPP/include" -I"../../../asiosdk2" -I"../../../asiosdk2/common" -I"../../../asiosdk2/host" -I"../../../asiosdk2/host/pc" -I"../../pa_common" -BIN = portaudio-static.a -CXXFLAGS = $(CXXINCS)-O3 -fmessage-length=0 -Wall -CFLAGS = $(INCS)-O3 -fmessage-length=0 -Wall - -.PHONY: all all-before all-after clean clean-custom - -all: all-before portaudio-static.a all-after - - -clean: clean-custom - rm -f $(OBJ) $(BIN) - -$(BIN): $(LINKOBJ) - ar r $(BIN) $(LINKOBJ) - ranlib $(BIN) - -./pa_skeleton.o: ../../pa_common/pa_skeleton.c - $(CPP) -c ../../pa_common/pa_skeleton.c -o ./pa_skeleton.o $(CXXFLAGS) - -./pa_stream.o: ../../pa_common/pa_stream.c - $(CPP) -c ../../pa_common/pa_stream.c -o ./pa_stream.o $(CXXFLAGS) - -./pa_trace.o: ../../pa_common/pa_trace.c - $(CPP) -c ../../pa_common/pa_trace.c -o ./pa_trace.o $(CXXFLAGS) - -./pa_allocation.o: ../../pa_common/pa_allocation.c - $(CPP) -c ../../pa_common/pa_allocation.c -o ./pa_allocation.o $(CXXFLAGS) - -./pa_converters.o: ../../pa_common/pa_converters.c - $(CPP) -c ../../pa_common/pa_converters.c -o ./pa_converters.o $(CXXFLAGS) - -./pa_cpuload.o: ../../pa_common/pa_cpuload.c - $(CPP) -c ../../pa_common/pa_cpuload.c -o ./pa_cpuload.o $(CXXFLAGS) - -./pa_dither.o: ../../pa_common/pa_dither.c - $(CPP) -c ../../pa_common/pa_dither.c -o ./pa_dither.o $(CXXFLAGS) - -./pa_front.o: ../../pa_common/pa_front.c - $(CPP) -c ../../pa_common/pa_front.c -o ./pa_front.o $(CXXFLAGS) - -./pa_process.o: ../../pa_common/pa_process.c - $(CPP) -c ../../pa_common/pa_process.c -o ./pa_process.o $(CXXFLAGS) - -./pa_asio.o: ../../pa_asio/pa_asio.cpp - $(CPP) -c ../../pa_asio/pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -./pa_win_util.o: ../pa_win_util.c - $(CPP) -c ../pa_win_util.c -o ./pa_win_util.o $(CXXFLAGS) - -./pa_win_hostapis.o: ../pa_win_hostapis.c - $(CPP) -c ../pa_win_hostapis.c -o ./pa_win_hostapis.o $(CXXFLAGS) - -./pa_win_ds.o: ../../pa_win_ds/pa_win_ds.c - $(CPP) -c ../../pa_win_ds/pa_win_ds.c -o ./pa_win_ds.o $(CXXFLAGS) - -./dsound_wrapper.o: ../../pa_win_ds/dsound_wrapper.c - $(CPP) -c ../../pa_win_ds/dsound_wrapper.c -o ./dsound_wrapper.o $(CXXFLAGS) - -./pa_win_wmme.o: ../../pa_win_wmme/pa_win_wmme.c - $(CPP) -c ../../pa_win_wmme/pa_win_wmme.c -o ./pa_win_wmme.o $(CXXFLAGS) - -./iasiothiscallresolver.o: ../../pa_asio/iasiothiscallresolver.cpp - $(CPP) -c ../../pa_asio/iasiothiscallresolver.cpp -o ./iasiothiscallresolver.o $(CXXFLAGS) diff --git a/portaudio-v19/pa_win/dev-cpp/portaudio-dll.dev b/portaudio-v19/pa_win/dev-cpp/portaudio-dll.dev deleted file mode 100644 index 086e109ee..000000000 --- a/portaudio-v19/pa_win/dev-cpp/portaudio-dll.dev +++ /dev/null @@ -1,209 +0,0 @@ -[Project] -FileName=portaudio-dll.dev -Name=portaudio-dll -UnitCount=16 -Type=3 -Ver=1 -ObjFiles= -Includes=..\..\..\asiosdk2;..\..\..\asiosdk2\common;..\..\..\asiosdk2\host;..\..\..\asiosdk2\host\pc;..\..\pa_common -Libs= -PrivateResource= -ResourceIncludes= -MakeIncludes= -Compiler=-DBUILDING_DLL=1_@@_-O3_@@_ -CppCompiler=-O3_@@_ -Linker=--no-export-all-symbols --add-stdcall-alias_@@_../../../asiosdk2/asiosdk2.a_@@_-lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -lwinmm_@@_-O3 -s_@@_ -IsCpp=1 -Icon= -ExeOutput=. -ObjectOutput=. -OverrideOutput=0 -OverrideOutputName=portaudio.a -HostApplication= -Folders= -CommandLine= -IncludeVersionInfo=0 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000000000000000 -UseCustomMakefile=0 -CustomMakefile= - -[Unit1] -FileName=..\..\pa_common\pa_skeleton.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_skeleton.c -o ./pa_skeleton.o $(CFLAGS) - -[Unit2] -FileName=..\..\pa_common\pa_stream.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_stream.c -o ./pa_stream.o $(CFLAGS) - -[Unit3] -FileName=..\..\pa_common\pa_trace.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_trace.c -o ./pa_trace.o $(CFLAGS) - -[Unit4] -FileName=..\..\pa_common\pa_allocation.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_allocation.c -o ./pa_allocation.o $(CFLAGS) - -[Unit5] -FileName=..\..\pa_common\pa_converters.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_converters.c -o ./pa_converters.o $(CFLAGS) - -[Unit6] -FileName=..\..\pa_common\pa_cpuload.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_cpuload.c -o ./pa_cpuload.o $(CFLAGS) - -[Unit7] -FileName=..\..\pa_common\pa_dither.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_dither.c -o ./pa_dither.o $(CFLAGS) - -[Unit8] -FileName=..\..\pa_common\pa_front.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_front.c -o ./pa_front.o $(CFLAGS) - -[Unit9] -FileName=..\..\pa_common\pa_process.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_process.c -o ./pa_process.o $(CFLAGS) - -[VersionInfo] -Major=0 -Minor=1 -Release=1 -Build=1 -LanguageID=1033 -CharsetID=1252 -CompanyName= -FileVersion= -FileDescription=Developed using the Dev-C++ IDE -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion= -AutoIncBuildNr=0 - -[Unit10] -FileName=..\..\pa_asio\pa_asio.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -[Unit11] -FileName=..\pa_win_util.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_util.c -o ./pa_win_util.o $(CFLAGS) - -[Unit12] -FileName=..\pa_win_hostapis.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_hostapis.c -o ./pa_win_hostapis.o $(CFLAGS) - -[Unit13] -FileName=..\..\pa_win_ds\pa_win_ds.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_ds.c -o ./pa_win_ds.o $(CFLAGS) - -[Unit14] -FileName=..\..\pa_win_ds\dsound_wrapper.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c dsound_wrapper.c -o ./dsound_wrapper.o $(CFLAGS) - -[Unit15] -FileName=..\..\pa_win_wmme\pa_win_wmme.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_wmme.c -o ./pa_win_wmme.o $(CFLAGS) - -[Unit16] -FileName=..\..\pa_asio\iasiothiscallresolver.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - diff --git a/portaudio-v19/pa_win/dev-cpp/portaudio-static.dev b/portaudio-v19/pa_win/dev-cpp/portaudio-static.dev deleted file mode 100644 index 2aae584df..000000000 --- a/portaudio-v19/pa_win/dev-cpp/portaudio-static.dev +++ /dev/null @@ -1,209 +0,0 @@ -[Project] -FileName=portaudio-static.dev -Name=portaudio-static -UnitCount=16 -Type=2 -Ver=1 -ObjFiles= -Includes=..\..\..\asiosdk2;..\..\..\asiosdk2\common;..\..\..\asiosdk2\host;..\..\..\asiosdk2\host\pc;..\..\pa_common -Libs= -PrivateResource= -ResourceIncludes= -MakeIncludes= -Compiler=-O3_@@_ -CppCompiler=-O3_@@_ -Linker=-O3 -s_@@_ -IsCpp=1 -Icon= -ExeOutput=. -ObjectOutput=. -OverrideOutput=0 -OverrideOutputName=portaudio.a -HostApplication= -Folders= -CommandLine= -IncludeVersionInfo=0 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000000000000000 -UseCustomMakefile=0 -CustomMakefile= - -[Unit1] -FileName=..\..\pa_common\pa_skeleton.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_skeleton.c -o ./pa_skeleton.o $(CFLAGS) - -[Unit2] -FileName=..\..\pa_common\pa_stream.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_stream.c -o ./pa_stream.o $(CFLAGS) - -[Unit3] -FileName=..\..\pa_common\pa_trace.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_trace.c -o ./pa_trace.o $(CFLAGS) - -[Unit4] -FileName=..\..\pa_common\pa_allocation.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_allocation.c -o ./pa_allocation.o $(CFLAGS) - -[Unit5] -FileName=..\..\pa_common\pa_converters.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_converters.c -o ./pa_converters.o $(CFLAGS) - -[Unit6] -FileName=..\..\pa_common\pa_cpuload.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_cpuload.c -o ./pa_cpuload.o $(CFLAGS) - -[Unit7] -FileName=..\..\pa_common\pa_dither.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_dither.c -o ./pa_dither.o $(CFLAGS) - -[Unit8] -FileName=..\..\pa_common\pa_front.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_front.c -o ./pa_front.o $(CFLAGS) - -[Unit9] -FileName=..\..\pa_common\pa_process.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_process.c -o ./pa_process.o $(CFLAGS) - -[VersionInfo] -Major=0 -Minor=1 -Release=1 -Build=1 -LanguageID=1033 -CharsetID=1252 -CompanyName= -FileVersion= -FileDescription=Developed using the Dev-C++ IDE -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion= -AutoIncBuildNr=0 - -[Unit10] -FileName=..\..\pa_asio\pa_asio.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS) - -[Unit11] -FileName=..\..\pa_win\pa_win_util.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_util.c -o ./pa_win_util.o $(CFLAGS) - -[Unit12] -FileName=..\..\pa_win\pa_win_hostapis.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_hostapis.c -o ./pa_win_hostapis.o $(CFLAGS) - -[Unit13] -FileName=..\..\pa_win_ds\pa_win_ds.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_ds.c -o ./pa_win_ds.o $(CFLAGS) - -[Unit14] -FileName=..\..\pa_win_ds\dsound_wrapper.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c dsound_wrapper.c -o ./dsound_wrapper.o $(CFLAGS) - -[Unit15] -FileName=..\..\pa_win_wmme\pa_win_wmme.c -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CC) -c pa_win_wmme.c -o ./pa_win_wmme.o $(CFLAGS) - -[Unit16] -FileName=..\..\pa_asio\iasiothiscallresolver.cpp -CompileCpp=1 -Folder=portaudio -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - diff --git a/portaudio-v19/pa_win/dev-cpp/readme.txt b/portaudio-v19/pa_win/dev-cpp/readme.txt deleted file mode 100644 index 07108a7dd..000000000 --- a/portaudio-v19/pa_win/dev-cpp/readme.txt +++ /dev/null @@ -1,23 +0,0 @@ -From: "Peter L Jones" -Sent: Wednesday, September 17, 2003 5:18 AM -Subject: Dev-C++ project files - -I attach two project files intended for portaudio/pa_win/dev-cpp (i.e. in -parallel with the msvc directory), if you want them. One is for a static -library build and one for a DLL. I've used the static library (in building -a single monolithic DLL) but I can't guarantee the DLL version will build a -working library (I think it's mostly there, though!). - -I also attach the resulting makefiles, which may be of use to other MinGW -users. - -They're rooted in the directory given above and drop their object and -library files in the same place. They assume the asiosdk2 files are in the -same directory as portaudio/ in a sub-directory called asiosdk2/. Oh! The -DLL is built against a static asiosdk2.a library... maybe not the best way -to do it... I ought to figure out how to link against a "home made" dll in -Dev-C++, I guess ;-) - -Cheers, - --- Peter diff --git a/portaudio-v19/pa_win/msvc/Makefile.msvc b/portaudio-v19/pa_win/msvc/Makefile.msvc deleted file mode 100644 index 2f1a104e7..000000000 --- a/portaudio-v19/pa_win/msvc/Makefile.msvc +++ /dev/null @@ -1,159 +0,0 @@ -# Portaudio v1.9-devel VC6 DLL makefile 1.0 -# -# David Viens, davidv@plogue.com -# (im far from a VC6 makefile expert, so please bear with me :) -# -# For more info, look at readme.txt -# -#if you keep the ASIODIR as ".", it will use the SDK files that direclty in "pa_win/msvc" dir -ASIODIR=. -ASIOINC=/I ".\host" /I ".\host\pc" /I ".\common" -# - -LIBZ=kernel32.lib user32.lib gdi32.lib wininet.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib winmm.lib dsound.lib dxguid.lib - -CPP=cl.exe -LINK32=link.exe - -#release -CFLAGS=/nologo /MD /W3 /GX /O2 /Ob2 /I "src" /I "Win32" /I "$(MSVCDir)\Include" /D "WIN32" /D "NDEBUG" /D "_USRDLL" /YX /FD -DLL_LINK_FLAGS= /nologo /dll /incremental:no /libpath:"$(MSVCDir)\Lib" $(LIBZ) /pdb:"portaudio.pdb" /implib:".\portaudio.lib" /machine:I386 /out:"portaudio.dll" - - -COMMONINC=/I "..\..\pa_common" /I "." - -#==================================================================== -# Targets - -ALL : portaudio.dll - -CLEAN : - -@erase "*.obj" - -#==================================================================== - - -LINK32_OBJS= \ - ".\pa_allocation.obj" \ - ".\pa_converters.obj" \ - ".\pa_x86_plain_converters.obj" \ -# ".\pa_cppHelp.obj" \ - ".\pa_cpuload.obj" \ - ".\pa_dither.obj" \ - ".\pa_front.obj" \ - ".\pa_process.obj" \ - ".\pa_skeleton.obj" \ - ".\pa_stream.obj" \ - ".\pa_trace.obj" \ -# - ".\pa_win_hostapis.obj" \ - ".\pa_win_util.obj" \ -# - ".\pa_win_wmme.obj" \ -# - ".\pa_win_ds.obj" \ - ".\dsound_wrapper.obj" \ -# - ".\pa_asio.obj" \ -# - ".\asio.obj" \ - ".\ASIOConvertSamples.obj" \ - ".\asiodrivers.obj" \ - ".\asiolist.obj" \ - ".\combase.obj" \ - ".\debugmessage.obj" \ -# ".\dllentry.obj" \ - ".\register.obj" - - -portaudio.dll : $(LINK32_OBJS) ".\portaudio.def" - $(LINK32) $(DLL_LINK_FLAGS) /def:".\portaudio.def" $(LINK32_OBJS) - -#==================================================================== -# asio files (need to agree to steinberg agreement) -# this makefile assumes all files have being copied in the pa_win/msvc dir (for now) -# see readme.txt for details - -".\asio.obj" : ".\common\asio.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asio.obj" /c ".\common\asio.cpp" - -".\ASIOConvertSamples.obj" : ".\host\ASIOConvertSamples.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\ASIOConvertSamples.obj" /c ".\host\ASIOConvertSamples.cpp" - -".\asiodrivers.obj" : ".\host\asiodrivers.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asiodrivers.obj" /c ".\host\asiodrivers.cpp" - -".\asiolist.obj" : ".\host\pc\asiolist.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asiolist.obj" /c ".\host\pc\asiolist.cpp" - -".\combase.obj" : ".\common\combase.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\combase.obj" /c ".\common\combase.cpp" - -".\debugmessage.obj" : ".\common\debugmessage.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\debugmessage.obj" /c ".\common\debugmessage.cpp" - -".\register.obj" : ".\common\register.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\register.obj" /c ".\common\register.cpp" - -#==================================================================== -# Portaudio Common -# -".\pa_allocation.obj" : "..\..\pa_common\pa_allocation.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_allocation.obj" /c "..\..\pa_common\pa_allocation.c" - -".\pa_converters.obj" : "..\..\pa_common\pa_converters.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_converters.obj" /c "..\..\pa_common\pa_converters.c" - -".\pa_cppHelp.obj" : "..\..\pa_common\pa_cppHelp.cpp" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_cppHelp.obj" /c "..\..\pa_common\pa_cppHelp.cpp" - -".\pa_cpuload.obj" : "..\..\pa_common\pa_cpuload.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_cpuload.obj" /c "..\..\pa_common\pa_cpuload.c" - -".\pa_dither.obj" : "..\..\pa_common\pa_dither.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_dither.obj" /c "..\..\pa_common\pa_dither.c" - -".\pa_front.obj" : "..\..\pa_common\pa_front.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_front.obj" /c "..\..\pa_common\pa_front.c" - -".\pa_process.obj" : "..\..\pa_common\pa_process.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_process.obj" /c "..\..\pa_common\pa_process.c" - -".\pa_skeleton.obj" : "..\..\pa_common\pa_skeleton.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_skeleton.obj" /c "..\..\pa_common\pa_skeleton.c" - -".\pa_stream.obj" : "..\..\pa_common\pa_stream.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_stream.obj" /c "..\..\pa_common\pa_stream.c" - -".\pa_trace.obj" : "..\..\pa_common\pa_trace.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_trace.obj" /c "..\..\pa_common\pa_trace.c" - -#==================================================================== -# Portaudio implementations -# - -".\pa_win_hostapis.obj" : "..\..\pa_win\pa_win_hostapis.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_hostapis.obj" /c "..\..\pa_win\pa_win_hostapis.c" - -".\pa_win_util.obj" : "..\..\pa_win\pa_win_util.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_util.obj" /c "..\..\pa_win\pa_win_util.c" - -".\pa_x86_plain_converters.obj" : "..\..\pa_win\pa_x86_plain_converters.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_x86_plain_converters.obj" /c "..\..\pa_win\pa_x86_plain_converters.c" - -".\pa_asio.obj" : "..\..\pa_asio\pa_asio.cpp" - $(CPP) $(CFLAGS) $(ASIOINC) $(COMMONINC) /Fo".\pa_asio.obj" /c "..\..\pa_asio\pa_asio.cpp" - -".\pa_win_wmme.obj" : "..\..\pa_win_wmme\pa_win_wmme.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_wmme.obj" /c "..\..\pa_win_wmme\pa_win_wmme.c" - -".\pa_win_ds.obj" : "..\..\pa_win_ds\pa_win_ds.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_ds.obj" /c "..\..\pa_win_ds\pa_win_ds.c" - -".\dsound_wrapper.obj" : "..\..\pa_win_ds\dsound_wrapper.c" - $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\dsound_wrapper.obj" /c "..\..\pa_win_ds\dsound_wrapper.c" - - -# End of Makefile -#==================================================================== -# \ No newline at end of file diff --git a/portaudio-v19/pa_win/msvc/clean.bat b/portaudio-v19/pa_win/msvc/clean.bat deleted file mode 100755 index 601c0d314..000000000 --- a/portaudio-v19/pa_win/msvc/clean.bat +++ /dev/null @@ -1,7 +0,0 @@ -del *.obj -del *.dll -del *.lib -del *.exp -del *.pch -del *.idb - diff --git a/portaudio-v19/pa_win/msvc/make.bat b/portaudio-v19/pa_win/msvc/make.bat deleted file mode 100755 index 34d6aed48..000000000 --- a/portaudio-v19/pa_win/msvc/make.bat +++ /dev/null @@ -1,8 +0,0 @@ -CALL C:\progra~1\micros~2\VC98\bin\vcvars32 -del *.dll -del *.lib -nmake Makefile.msvc -del *.obj -del *.exp -del *.pch -del *.idb diff --git a/portaudio-v19/pa_win/msvc/portaudio.def b/portaudio-v19/pa_win/msvc/portaudio.def deleted file mode 100644 index 97a1c3502..000000000 --- a/portaudio-v19/pa_win/msvc/portaudio.def +++ /dev/null @@ -1,43 +0,0 @@ -LIBRARY portaudio.dll -EXPORTS - -; -Pa_GetVersion @1 -Pa_GetVersionText @2 -Pa_GetErrorText @3 -Pa_Initialize @4 -Pa_Terminate @5 -Pa_GetHostApiCount @6 -Pa_GetDefaultHostApi @7 -Pa_GetHostApiInfo @8 -Pa_HostApiTypeIdToHostApiIndex @9 -Pa_HostApiDeviceIndexToDeviceIndex @10 -Pa_GetLastHostErrorInfo @11 -Pa_GetDeviceCount @12 -Pa_GetDefaultInputDevice @13 -Pa_GetDefaultOutputDevice @14 -Pa_GetDeviceInfo @15 -Pa_IsFormatSupported @16 -Pa_OpenStream @17 -Pa_OpenDefaultStream @18 -Pa_CloseStream @19 -Pa_SetStreamFinishedCallback @20 -Pa_StartStream @21 -Pa_StopStream @22 -Pa_AbortStream @23 -Pa_IsStreamStopped @24 -Pa_IsStreamActive @25 -Pa_GetStreamInfo @26 -Pa_GetStreamTime @27 -Pa_GetStreamCpuLoad @28 -Pa_ReadStream @29 -Pa_WriteStream @30 -Pa_GetStreamReadAvailable @31 -Pa_GetStreamWriteAvailable @32 -Pa_GetSampleSize @33 -Pa_Sleep @34 -PaAsio_GetAvailableLatencyValues @50 -PaAsio_ShowControlPanel @51 -PaUtil_InitializeX86PlainConverters @52 -PaAsio_GetInputChannelName @53 -PaAsio_GetOutputChannelName @54 \ No newline at end of file diff --git a/portaudio-v19/pa_win/msvc/readme.txt b/portaudio-v19/pa_win/msvc/readme.txt deleted file mode 100644 index 19ced4c4c..000000000 --- a/portaudio-v19/pa_win/msvc/readme.txt +++ /dev/null @@ -1,56 +0,0 @@ -Hello - - This is a small list of steps in order to build portaudio -(Currently v19-devel) into a VC6 DLL and lib file. -This DLL contains all 3 current win32 PA APIS (MM/DS/ASIO) - -1)Copy the source dirs that comes with the ASIO SDK inside pa_win\msvc - so you should now have: - - pa_win\msvc\common - pa_win\msvc\host - pa_win\msvc\host\sample - pa_win\msvc\host\pc - pa_win\msvc\host\mac (not needed) - - You dont need "driver" - - -2)execure "make.bat", this assumes VC6 is installed in - C:\Program Files\Microsoft Visual Studio\ - - if its not, - - Open a command Prompt and execute "vcvars32.bat" which sets the environment - so that you can use Microsoft's "nmake" - EX: C:\Program Files\Microsoft Visual Studio\VC98\Bin\vcvars32.bat - or (C:\progra~1\micros~2\VC98\bin\vcvars32) dumb de dumb - - You should now have seen a line that said: - "Setting environment for using Microsoft Visual C++ tools." - While in pa_win\msvc , type "nmake makefile.msvc" - this _should_ create portaudio.dll and portaudio.lib - -3)Now in any VC6 project, in which you require portaudio, - you can just link with portaudio.lib, and of course include the - relevant headers - (portaudio.h, and/or pa_asio.h , pa_x86_plain_converters.h) See (*) - -4) Your new exe should now use portaudio.dll. - - -Have fun! - -(*): you may want to add/remove some DLL entry points. -Right now those 3 entries are _not_ from portaudio.h - -(from portaudio.def) -(...) -PaAsio_GetAvailableLatencyValues @50 -PaAsio_ShowControlPanel @51 -PaUtil_InitializeX86PlainConverters @52 - - ------ -last update April 16th 2003 -David Viens, davidv@plogue.com \ No newline at end of file diff --git a/portaudio-v19/pa_win/msvc/setenv.bat b/portaudio-v19/pa_win/msvc/setenv.bat deleted file mode 100755 index f4c815a78..000000000 --- a/portaudio-v19/pa_win/msvc/setenv.bat +++ /dev/null @@ -1 +0,0 @@ -C:\progra~1\micros~2\VC98\bin\vcvars32 diff --git a/portaudio-v19/pa_win/pa_win_hostapis.c b/portaudio-v19/pa_win/pa_win_hostapis.c deleted file mode 100644 index 5d5ac87f3..000000000 --- a/portaudio-v19/pa_win/pa_win_hostapis.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * $Id$ - * 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. - * - * 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. - */ - -/** @file - 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 ); - -#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_WDMKS - PaWinWdm_Initialize, -#endif -*/ - - PaSkeleton_Initialize, /* just for testing */ - - 0 /* NULL terminated array */ - }; - - -int paDefaultHostApiIndex = 0; - diff --git a/portaudio-v19/pa_win/pa_win_util.c b/portaudio-v19/pa_win/pa_win_util.c deleted file mode 100644 index 32d0dbb98..000000000 --- a/portaudio-v19/pa_win/pa_win_util.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * $Id$ - * 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. - * - * 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. - */ - -/** @file - Win32 platform-specific support functions. - - @todo Implement workaround for QueryPerformanceCounter() skipping forward - bug. (see msdn kb Q274323). -*/ - -#include -#include /* 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 - { - return timeGetTime() * .001; - } -} diff --git a/portaudio-v19/pa_win/pa_x86_plain_converters.c b/portaudio-v19/pa_win/pa_x86_plain_converters.c deleted file mode 100644 index 98442a8c6..000000000 --- a/portaudio-v19/pa_win/pa_x86_plain_converters.c +++ /dev/null @@ -1,1167 +0,0 @@ -#include "pa_x86_plain_converters.h" - -#include "pa_converters.h" -#include "pa_dither.h" - -/* - plain intel assemby versions of standard pa converter functions. - - 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< 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; -} - -/* -------------------------------------------------------------------------- */ diff --git a/portaudio-v19/pa_win/pa_x86_plain_converters.h b/portaudio-v19/pa_win/pa_x86_plain_converters.h deleted file mode 100644 index f56c710fd..000000000 --- a/portaudio-v19/pa_win/pa_x86_plain_converters.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 -*/ -void PaUtil_InitializeX86PlainConverters( void ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_X86_PLAIN_CONVERTERS_H */