diff options
Diffstat (limited to 'pd')
-rw-r--r-- | pd/doc/1.manual/x1.htm | 6 | ||||
-rw-r--r-- | pd/doc/1.manual/x5.htm | 37 | ||||
-rw-r--r-- | pd/portaudio/pa_mac_core/notes.txt | 14 | ||||
-rw-r--r-- | pd/portaudio/pa_mac_core/pa_mac_core.c | 1512 | ||||
-rwxr-xr-x | pd/src/configure | 4 | ||||
-rw-r--r-- | pd/src/configure.in | 4 | ||||
-rw-r--r-- | pd/src/g_all_guis.c | 53 | ||||
-rw-r--r-- | pd/src/g_all_guis.h | 2 | ||||
-rw-r--r-- | pd/src/g_array.c | 10 | ||||
-rw-r--r-- | pd/src/g_canvas.c | 13 | ||||
-rw-r--r-- | pd/src/g_canvas.h | 4 | ||||
-rw-r--r-- | pd/src/g_editor.c | 10 | ||||
-rw-r--r-- | pd/src/g_hdial.c | 46 | ||||
-rw-r--r-- | pd/src/g_numbox.c | 4 | ||||
-rw-r--r-- | pd/src/g_text.c | 65 | ||||
-rw-r--r-- | pd/src/g_vdial.c | 2 | ||||
-rw-r--r-- | pd/src/m_binbuf.c | 7 | ||||
-rw-r--r-- | pd/src/m_pd.h | 4 | ||||
-rw-r--r-- | pd/src/makefile.nt | 10 | ||||
-rw-r--r-- | pd/src/notes.txt | 19 | ||||
-rw-r--r-- | pd/src/s_inter.c | 14 | ||||
-rw-r--r-- | pd/src/s_mac.c | 2 | ||||
-rw-r--r-- | pd/src/s_main.c | 13 | ||||
-rw-r--r-- | pd/src/t_tkcmd.c | 41 | ||||
-rw-r--r-- | pd/src/u_main.tk | 82 | ||||
-rw-r--r-- | pd/src/u_pdreceive.c | 42 | ||||
-rw-r--r-- | pd/src/u_pdsend.c | 19 | ||||
-rw-r--r-- | pd/src/z.pd | 40 | ||||
-rw-r--r-- | pd/src/z2.pd | 28 | ||||
-rw-r--r-- | pd/src/z3.pd | 29 |
30 files changed, 1337 insertions, 799 deletions
diff --git a/pd/doc/1.manual/x1.htm b/pd/doc/1.manual/x1.htm index 946949e9..6a78040e 100644 --- a/pd/doc/1.manual/x1.htm +++ b/pd/doc/1.manual/x1.htm @@ -107,6 +107,10 @@ Pd objects</a><br> <a href="http://suita.chopin.edu.pl/~czaja/miXed/externs/xeq.html"> Krzysztof Czaja's MIDI file support </a><br> <a href="http://www.davesabine.com/media/puredata.asp?action=pddp"> -David Sabine's Pd Documentation Project: new, highly detailed help windows</a><br> +David Sabine's Pd Documentation Project: +new, highly detailed help windows</a><br> +<a href="http://www-ccrma/planetccrma/software/soundapps.html#pd"> +Fernando Pablo Lopez's augmented Pd RPMs from Planet CCRMA</a><br> + </BODY> </HTML> diff --git a/pd/doc/1.manual/x5.htm b/pd/doc/1.manual/x5.htm index 2203ebc7..767ffc3e 100644 --- a/pd/doc/1.manual/x5.htm +++ b/pd/doc/1.manual/x5.htm @@ -16,6 +16,43 @@ This section tracks changes in Pd's current implementation. <H4> <A name=s2> 5.1. release notes </A> </H4> +<P> ------------------ 0.36 ------------------------------- + +<P> There's now an "undo" for most editing operations. Undoing is only +available in the window that was most recently edited. (One gotcha remains, +that "stretching" (in the font menu) affects all windows and you can't undo any +but the last one it touched.) Also, there's no "undo" for run-time operations, +only editing ones. That might be worth thinking about. + +<P> Some bugs were fixed that affected "flipped" canvases (ones whose +"properties show a positive "y" increment per pixel.) Also, the coordinates +are now saved and restored correctly. "text" objects (comments) now stick to +the bottom of the window for flipped canvases. + +<P> Signal lines now show up fatter than control lines. (Now I have to go +through the figures in the HTML doc again... drat) + +<P> "Classic" number boxes now can have labels and send/receive signals, which +work in the same way as the IEMGUI controls do. I think "$1" style +label/send/receive names work too. I fixed a related bug +in the IEM code (typing at boxes sometimes crashed Pd). + +<P> "vdial" and "hdial" were renamed "vradio" and "hradio", and fixed to +output numbers, not lists, like the other GUIs. The old ones are still around +for compatibility with old patches. + +<P> "Make install" should now actually make Pd before trying to install it. + +<P> "expr" is updated to Shahrokh's 0.4test3 version (which I modified somewhat +to get it to compile.) + +<P> The problem of CPU usage skyrocketing on underflows in P4s should +be fixed. + +<P> Compiled "pdsend" and "pdreceive" for Windows. + +<P> "PD_VERSION" macro added to m_pd.h + <P> ------------------ 0.35 ------------------------------- <P> An experimental new feature called graph-on-parent allows subpatches and abstractions to show diff --git a/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt index c79b90e6..3b557d9a 100644 --- a/pd/portaudio/pa_mac_core/notes.txt +++ b/pd/portaudio/pa_mac_core/notes.txt @@ -2,25 +2,25 @@ Notes on Core Audio Implementation of PortAudio by Phil Burk and Darren Gibbs -Document last updated March 20, 2002 +Document last updated October 18, 2002 WHAT WORKS Output with very low latency, <10 msec. Half duplex input or output. -Full duplex on the same CoreAudio device. +Full duplex 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 -BuiltIn audio is typically one CoreAudio device. +The iMic supports multiple sample rates. +But there is a bug when changing sample rates: + Run patest_record.c at rate A - it works. + Then run patest_record.c at rate B - it FAIL! + Then run patest_record.c again at rate B - it works! -Mono doesn't work. DEVICE MAPPING diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c index 9a4b1488..de781d09 100644 --- a/pd/portaudio/pa_mac_core/pa_mac_core.c +++ b/pd/portaudio/pa_mac_core/pa_mac_core.c @@ -1,5 +1,5 @@ /* - * $Id: pa_mac_core.c,v 1.8 2002/04/12 18:07:20 philburk Exp $ + * $Id: pa_mac_core.c,v 1.8.4.3 2002/10/18 01:29:06 philburk Exp $ * pa_mac_core.c * Implementation of PortAudio for Mac OS X Core Audio * @@ -7,7 +7,18 @@ * Latest Version at: http://www.portaudio.com * * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * Copyright (c) 1999-2002 Ross Bencina and Phil Burk + * + * Theory of Operation + * + * This code uses the HAL (Hardware Access Layer) of the Apple CoreAudio library. + * This is the layer closes to the hardware. + * The HAL layer only supports the native HW supported sample rates. + * So if the chip only supports 44100 Hz, then the HAL only supports 44100. + * To provide other rates we use the handy Apple AUConverter which provides + * sample rate conversion, mono-to-stereo conversion, and buffer size adaptation. + * + * License * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files @@ -43,18 +54,28 @@ audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices). 2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7 3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file. - Return error if opened in mono mode cuz not supported. + Return error if opened in mono mode cuz not supported. [Supported 10.12.2002] Add support for Pa_GetCPULoad(); Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!) Check for invalid sample rates and return an error. - Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally. + Check for getenv("PA_MIN_LATENCY_MSEC") to set latency externally. Better error checking for invalid channel counts and invalid devices. 3.29.2002 - Phil Burk - Fixed Pa_GetCPULoad() for small buffers. 3.31.2002 - Phil Burk - Use getrusage() instead of gettimeofday() for CPU Load calculation. + 10.12.2002 - Phil Burk - Use AudioConverter to allow wide range of sample rates, and mono. + Use FIFO (from pablio/rinbuffer.h) so that we can pull data through converter. + Added PaOSX_FixVolumeScalar() to make iMic audible. + 10.17.2002 - Phil Burk - Support full duplex between two different devices. + Name internal functions PaOSX_* + Dumped useless PA_MIN_LATENCY_MSEC environment variable. + Use kAudioDevicePropertyStreamFormatMatch to determine max channels. TODO: -O- how do mono output? -O- FIFO between input and output callbacks if different devices, like in pa_mac.c +O- debug problem when changing sample rates on iMic +O- add support for paInt32 format +O- Why does iMic have grunge for the first second or two then clears up? +O- request notification when formats change or device unplugged +O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? */ #include <CoreServices/CoreServices.h> @@ -62,10 +83,14 @@ O- FIFO between input and output callbacks if different devices, like in pa_mac. #include <sys/time.h> #include <sys/resource.h> #include <unistd.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/DefaultAudioOutput.h> +#include <AudioToolbox/AudioConverter.h> #include "portaudio.h" #include "pa_host.h" #include "pa_trace.h" +#include "ringbuffer.h" /************************************************* Constants ********/ @@ -73,37 +98,45 @@ O- FIFO between input and output callbacks if different devices, like in pa_mac. #define PA_TRACE_RUN (0) #define PA_TRACE_START_STOP (1) -#define PA_MIN_LATENCY_MSEC (8) +#define PA_MIN_LATENCY_MSEC (1) #define MIN_TIMEOUT_MSEC (1000) #define PRINT(x) { printf x; fflush(stdout); } -#define ERR_RPT(x) PRINT(x) -#define DBUG(x) /* PRINT(x) /**/ -#define DBUGX(x) /* PRINT(x) /**/ +#define PRINT_ERR( msg, err ) PRINT(( msg ": error = 0x%0lX = '%s'\n", (err), ErrorToString(err)) ) +#define DBUG(x) /* PRINT(x) */ +#define DBUGBACK(x) /* if( sMaxBackgroundErrorMessages-- > 0 ) PRINT(x) */ +#define DBUGX(x) // define value of isInput passed to CoreAudio routines #define IS_INPUT (true) #define IS_OUTPUT (false) +typedef struct PaHostInOut +{ + AudioDeviceID audioDeviceID; // CoreAudio specific ID + int bytesPerUserNativeBuffer; /* User buffer size in native host format. Depends on numChannels. */ + AudioConverterRef converter; + void *converterBuffer; +} PaHostInOut; + /************************************************************** * Structure for internal host specific stream data. * This is allocated on a per stream basis. */ typedef struct PaHostSoundControl { - AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now. - /* Input -------------- */ - int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */ - /* Output -------------- */ - int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */ - /* Init Time -------------- */ - int pahsc_FramesPerHostBuffer; - int pahsc_UserBuffersPerHostBuffer; + PaHostInOut input; + PaHostInOut output; + AudioDeviceID primaryDeviceID; + Boolean usingSecondDevice; + int framesPerHostBuffer; + /* For sample rate, format conversion, or when using two devices. */ + RingBuffer ringBuffer; + char *ringBufferData; /* For measuring CPU utilization. */ - struct rusage pahsc_EntryRusage; - double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ -} -PaHostSoundControl; + struct rusage entryRusage; + double inverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ +} PaHostSoundControl; /************************************************************** * Structure for internal extended device info. @@ -125,14 +158,18 @@ static int sNumOutputDevices = 0; static PaHostDeviceInfo *sDeviceInfos = NULL; static int sDefaultInputDeviceID = paNoDevice; static int sDefaultOutputDeviceID = paNoDevice; -static int sPaHostError = 0; - +static int sSavedHostError = 0; static int sNumCoreDevices = 0; static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs +static const double supportedSampleRateRange[] = { 8000.0, 96000.0 }; static const char sMapperSuffixInput[] = " - Input"; static const char sMapperSuffixOutput[] = " - Output"; +/* Debug support. */ +//static int sMaxBackgroundErrorMessages = 100; +//static int sCoverageCounter = 1; // used to check code coverage during validation + /* We index the input devices first, then the output devices. */ #define LOWEST_INPUT_DEVID (0) #define HIGHEST_INPUT_DEVID (sNumInputDevices - 1) @@ -143,15 +180,64 @@ static const char sMapperSuffixOutput[] = " - Output"; /************************************************* Prototypes **********/ -static PaError Pa_QueryDevices( void ); -PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); +static PaError PaOSX_QueryDevices( void ); +static int PaOSX_ScanDevices( Boolean isInput ); +static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); +static PaDeviceID PaOSX_QueryDefaultInputDevice( void ); +static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ); +static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ); + +/**********************************************************************/ +/* OS X errors are 4 character ID that can be printed. + * Note that uses a static pad so result must be printed immediately. + */ +static OSStatus statusText[2] = { 0, 0 }; +static const char *ErrorToString( OSStatus err ) +{ + const char *str; + + switch (err) + { + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + default: + str = "Unknown CoreAudio Error!"; + statusText[0] = err; + str = (const char *)statusText; + break; + } -static int PaHost_ScanDevices( Boolean isInput ); -static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); + return str; +} -static PaDeviceID Pa_QueryDefaultInputDevice( void ); -static PaDeviceID Pa_QueryDefaultOutputDevice( void ); -static void PaHost_CalcHostBufferSize( internalPortAudioStream *past ); +/**********************************************************************/ +static unsigned long RoundUpToNextPowerOf2( unsigned long n ) +{ + long numBits = 0; + if( ((n-1) & n) == 0) return n; /* Already Power of two. */ + while( n > 0 ) + { + n= n>>1; + numBits++; + } + return (1<<numBits); +} /********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ static void Pa_StartUsageCalculation( internalPortAudioStream *past ) @@ -159,7 +245,7 @@ static void Pa_StartUsageCalculation( internalPortAudioStream *past ) PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; if( pahsc == NULL ) return; /* Query user CPU timer for usage analysis and to prevent overuse of CPU. */ - getrusage( RUSAGE_SELF, &pahsc->pahsc_EntryRusage ); + getrusage( RUSAGE_SELF, &pahsc->entryRusage ); } static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB ) @@ -188,10 +274,10 @@ static void Pa_EndUsageCalculation( internalPortAudioStream *past ) if( getrusage( RUSAGE_SELF, ¤tRusage ) == 0 ) { - usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->pahsc_EntryRusage.ru_utime ); + usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->entryRusage.ru_utime ); /* Use inverse because it is faster than the divide. */ - newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; + newUsage = usecsElapsed * pahsc->inverseMicrosPerHostBuffer; past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + (LOWPASS_COEFFICIENT_1 * newUsage); @@ -200,92 +286,104 @@ static void Pa_EndUsageCalculation( internalPortAudioStream *past ) /****************************************** END CPU UTILIZATION *******/ /************************************************************************/ -static PaDeviceID Pa_QueryDefaultInputDevice( void ) +static PaDeviceID PaOSX_QueryDefaultInputDevice( void ) { - OSStatus err = noErr; - UInt32 count; - int i; + OSStatus err = noErr; + UInt32 count; + int i; AudioDeviceID tempDeviceID = kAudioDeviceUnknown; - PaDeviceID defaultDeviceID = paNoDevice; + PaDeviceID defaultDeviceID = paNoDevice; // get the default output device for the HAL // it is required to pass the size of the data to be returned count = sizeof(tempDeviceID); err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID); - if (err != noErr) goto Bail; + if (err != noErr) goto error; // scan input devices to see which one matches this device defaultDeviceID = paNoDevice; for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ ) { - DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); + DBUG(("PaOSX_QueryDefaultInputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) { defaultDeviceID = i; break; } } -Bail: +error: return defaultDeviceID; } /************************************************************************/ -static PaDeviceID Pa_QueryDefaultOutputDevice( void ) +static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ) { - OSStatus err = noErr; - UInt32 count; - int i; + OSStatus err = noErr; + UInt32 count; + int i; AudioDeviceID tempDeviceID = kAudioDeviceUnknown; - PaDeviceID defaultDeviceID = paNoDevice; + PaDeviceID defaultDeviceID = paNoDevice; // get the default output device for the HAL // it is required to pass the size of the data to be returned count = sizeof(tempDeviceID); err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID); - if (err != noErr) goto Bail; + if (err != noErr) goto error; // scan output devices to see which one matches this device defaultDeviceID = paNoDevice; for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ ) { - DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); + DBUG(("PaOSX_QueryDefaultOutputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) { defaultDeviceID = i; break; } } -Bail: +error: return defaultDeviceID; } /******************************************************************/ -static PaError Pa_QueryDevices( void ) +static PaError PaOSX_QueryDevices( void ) { OSStatus err = noErr; UInt32 outSize; Boolean outWritable; - int numBytes; + int numBytes; // find out how many Core Audio devices there are, if any + outSize = sizeof(outWritable); err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable); if (err != noErr) - ERR_RPT(("Couldn't get info about list of audio devices\n")); - + { + PRINT_ERR("Couldn't get info about list of audio devices", err); + sSavedHostError = err; + return paHostError; + } + // calculate the number of device available sNumCoreDevices = outSize / sizeof(AudioDeviceID); // Bail if there aren't any devices if (sNumCoreDevices < 1) - ERR_RPT(("No Devices Available\n")); - + { + PRINT(("No Devices Available")); + return paHostError; + } + // make space for the devices we are about to get sCoreDeviceIDs = (AudioDeviceID *)malloc(outSize); // get an array of AudioDeviceIDs err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sCoreDeviceIDs); if (err != noErr) - ERR_RPT(("Couldn't get list of audio device IDs\n")); + { + PRINT_ERR("Couldn't get list of audio device IDs", err); + sSavedHostError = err; + return paHostError; + } // Allocate structures to hold device info pointers. // There will be a maximum of two Pa devices per Core Audio device, input and/or output. @@ -295,35 +393,22 @@ static PaError Pa_QueryDevices( void ) // Scan all the Core Audio devices to see which support input and allocate a // PaHostDeviceInfo structure for each one. - PaHost_ScanDevices( IS_INPUT ); + PaOSX_ScanDevices( IS_INPUT ); sNumInputDevices = sNumPaDevices; // Now scan all the output devices. - PaHost_ScanDevices( IS_OUTPUT ); + PaOSX_ScanDevices( IS_OUTPUT ); sNumOutputDevices = sNumPaDevices - sNumInputDevices; // Figure out which of the devices that we scanned is the default device. - sDefaultInputDeviceID = Pa_QueryDefaultInputDevice(); - sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice(); + sDefaultInputDeviceID = PaOSX_QueryDefaultInputDevice(); + sDefaultOutputDeviceID = PaOSX_QueryDefaultOutputDevice(); return paNoError; } -/************************************************************************************/ -long Pa_GetHostError() -{ - return sPaHostError; -} - -/*************************************************************************/ -int Pa_CountDevices() -{ - if( sNumPaDevices <= 0 ) Pa_Initialize(); - return sNumPaDevices; -} - /*************************************************************************/ /* Allocate a string containing the device name. */ -static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) +static char *PaOSX_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) { OSStatus err = noErr; UInt32 outSize; @@ -339,87 +424,18 @@ static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) { err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, deviceName); if (err != noErr) - ERR_RPT(("Couldn't get audio device name.\n")); + PRINT_ERR("Couldn't get audio device name", err); } } return deviceName; } -/*************************************************************************/ -// An AudioStreamBasicDescription is passed in to query whether or not -// the format is supported. A kAudioDeviceUnsupportedFormatError will -// be returned if the format is not supported and kAudioHardwareNoError -// will be returned if it is supported. AudioStreamBasicDescription -// fields set to 0 will be ignored in the query, but otherwise values -// must match exactly. - -Boolean deviceDoesSupportFormat(AudioDeviceID deviceID, AudioStreamBasicDescription *desc, Boolean isInput ) -{ - OSStatus err = noErr; - UInt32 outSize; - - outSize = sizeof(*desc); - err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &outSize, desc); - - if (err == kAudioHardwareNoError) - return true; - else - return false; -} - -/*************************************************************************/ -// return an error string -char* coreAudioErrorString (int errCode ) -{ - char *str; - - switch (errCode) - { - case kAudioHardwareUnspecifiedError: - str = "kAudioHardwareUnspecifiedError"; - break; - case kAudioHardwareNotRunningError: - str = "kAudioHardwareNotRunningError"; - break; - case kAudioHardwareUnknownPropertyError: - str = "kAudioHardwareUnknownPropertyError"; - break; - case kAudioDeviceUnsupportedFormatError: - str = "kAudioDeviceUnsupportedFormatError"; - break; - case kAudioHardwareBadPropertySizeError: - str = "kAudioHardwareBadPropertySizeError"; - break; - case kAudioHardwareIllegalOperationError: - str = "kAudioHardwareIllegalOperationError"; - break; - default: - str = "Unknown CoreAudio Error!"; - break; - } - - return str; -} - -/************************************************************************* -** PaDeviceInfo structures have already been created -** so just return the pointer. -** -*/ -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) -{ - if( id < 0 || id >= sNumPaDevices ) - return NULL; - - return &sDeviceInfos[id].paInfo; -} - /************************************************************************* ** Scan all of the Core Audio devices to see which support input or output. ** Changes sNumDevices, and fills in sDeviceInfos. */ -static int PaHost_ScanDevices( Boolean isInput ) +static int PaOSX_ScanDevices( Boolean isInput ) { int coreDeviceIndex; int result; @@ -430,8 +446,8 @@ static int PaHost_ScanDevices( Boolean isInput ) { // try to fill in next PaHostDeviceInfo hostDeviceInfo = &sDeviceInfos[sNumPaDevices]; - result = PaHost_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); - DBUG(("PaHost_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, hostDeviceInfo->audioDeviceID )); + result = PaOSX_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); + DBUGX(("PaOSX_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, coreDeviceIndex )); if( result > 0 ) { sNumPaDevices += 1; // bump global counter if we got one @@ -442,6 +458,7 @@ static int PaHost_ScanDevices( Boolean isInput ) return numAdded; } + /************************************************************************* ** Try to fill in the device info for this device. ** Return 1 if a good device that PA can use. @@ -449,59 +466,55 @@ static int PaHost_ScanDevices( Boolean isInput ) ** or return negative error. ** */ -static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) +static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) { - OSErr err; - int index; - UInt32 outSize; + OSStatus err; + UInt32 outSize; AudioStreamBasicDescription formatDesc; - Boolean result; AudioDeviceID devID; - double *sampleRates = NULL; /* non-const ptr */ - - PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; - double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0}; - int maxNumSampleRates = sizeof( possibleSampleRates ) / sizeof( double ); + PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; deviceInfo->structVersion = 1; deviceInfo->maxInputChannels = 0; deviceInfo->maxOutputChannels = 0; + + deviceInfo->sampleRates = supportedSampleRateRange; // because we use sample rate converter to get continuous rates deviceInfo->numSampleRates = -1; devID = sCoreDeviceIDs[ coreDeviceIndex ]; hostDeviceInfo->audioDeviceID = devID; + DBUG(("PaOSX_QueryDeviceInfo: coreDeviceIndex = %d, devID = %d, isInput = %d\n", + coreDeviceIndex, devID, isInput )); + // Get data format info from the device. + outSize = sizeof(formatDesc); + err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); + // This just may not be an appropriate device for input or output so leave quietly. + if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; - // Figure out supported sample rates - // Make room in case device supports all rates. - sampleRates = (double*)PaHost_AllocateFastMemory( maxNumSampleRates * sizeof(double) ); - if( sampleRates == NULL ) return paInsufficientMemory; - - deviceInfo->sampleRates = sampleRates; - deviceInfo->numSampleRates = 0; - - // Loop through the possible sampling rates and check each to see if the device supports it. - for (index = 0; index < maxNumSampleRates; index ++) + // Right now the Core Audio headers only define one formatID: LinearPCM + // Apparently LinearPCM must be Float32 for now. + if( (formatDesc.mFormatID == kAudioFormatLinearPCM) && + (formatDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat) ) { - memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); - formatDesc.mSampleRate = possibleSampleRates[index]; - result = deviceDoesSupportFormat( devID, &formatDesc, isInput ); - - if (result == true) - { - deviceInfo->numSampleRates += 1; - *sampleRates = possibleSampleRates[index]; - sampleRates++; - } + deviceInfo->nativeSampleFormats = paFloat32; + } + else + { + return paSampleFormatNotSupported; } - // If no sample rates supported, then not a very good device. - if( deviceInfo->numSampleRates == 0 ) goto error; - // Get data format info from the device. + // Determine maximum number of channels supported. + memset( &formatDesc, 0, sizeof(formatDesc)); + formatDesc.mChannelsPerFrame = 256; // FIXME - what about device with > 256 channels outSize = sizeof(formatDesc); - err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); - - // If no channels supported, then not a very good device. - if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; + err = AudioDeviceGetProperty( devID, 0, + isInput, kAudioDevicePropertyStreamFormatMatch, &outSize, &formatDesc); + if( err != noErr ) + { + PRINT_ERR("PaOSX_QueryDeviceInfo: Could not get device format match", err); + sSavedHostError = err; + return paHostError; + } if( isInput ) { @@ -512,205 +525,268 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame; } - // FIXME - where to put current sample rate?: formatDesc.mSampleRate - - // Right now the Core Audio headers only define one formatID: LinearPCM - // Apparently LinearPCM must be Float32 for now. - switch (formatDesc.mFormatID) - { - case kAudioFormatLinearPCM: - deviceInfo->nativeSampleFormats = paFloat32; - - // FIXME - details about the format are in these flags. - // formatDesc.mFormatFlags - - // here are the possibilities - // kLinearPCMFormatFlagIsFloat // set for floating point, clear for integer - // kLinearPCMFormatFlagIsBigEndian // set for big endian, clear for little - // kLinearPCMFormatFlagIsSignedInteger // set for signed integer, clear for unsigned integer, - // only valid if kLinearPCMFormatFlagIsFloat is clear - // kLinearPCMFormatFlagIsPacked // set if the sample bits are packed as closely together as possible, - // clear if they are high or low aligned within the channel - // kLinearPCMFormatFlagIsAlignedHigh // set if the sample bits are placed - break; - - default: - deviceInfo->nativeSampleFormats = paFloat32; // FIXME - break; - } - // Get the device name - deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput ); + deviceInfo->name = PaOSX_DeviceNameFromID( devID, isInput ); return 1; error: - if( sampleRates != NULL ) free( sampleRates ); return 0; } -/************************************************************************* -** Returns recommended device ID. -** On the PC, the recommended device can be specified by the user by -** setting an environment variable. For example, to use device #1. -** -** set PA_RECOMMENDED_OUTPUT_DEVICE=1 -** -** The user should first determine the available device ID by using -** the supplied application "pa_devs". -*/ -#define PA_ENV_BUF_SIZE (32) -#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE") -#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE") - -static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName ) -{ -#if 0 - UInt32 hresult; - char envbuf[PA_ENV_BUF_SIZE]; - PaDeviceID recommendedID = paNoDevice; - - /* Let user determine default device by setting environment variable. */ - hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); - if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) - { - recommendedID = atoi( envbuf ); - } - - return recommendedID; -#endif - return paNoDevice; -} - -static PaError Pa_MaybeQueryDevices( void ) +/**********************************************************************/ +static PaError PaOSX_MaybeQueryDevices( void ) { if( sNumPaDevices == 0 ) { - return Pa_QueryDevices(); + return PaOSX_QueryDevices(); } return 0; } +static char zeroPad[256] = { 0 }; + /********************************************************************** -** Check for environment variable, else query devices and use result. +** This is the proc that supplies the data to the AudioConverterFillBuffer call. +** We can pass back arbitrarily sized blocks so if the FIFO region is split +** just pass back the first half. */ -PaDeviceID Pa_GetDefaultInputDeviceID( void ) +static OSStatus PaOSX_InputConverterCallbackProc (AudioConverterRef inAudioConverter, + UInt32* outDataSize, + void** outData, + void* inUserData) { - PaError result; - result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME ); - if( result < 0 ) + internalPortAudioStream *past = (internalPortAudioStream *) inUserData; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + void *dataPtr1; + long size1; + void *dataPtr2; + long size2; + + /* Pass contiguous region from FIFO directly to converter. */ + RingBuffer_GetReadRegions( &pahsc->ringBuffer, *outDataSize, + &dataPtr1, &size1, &dataPtr2, &size2 ); + + if( size1 > 0 ) { - result = Pa_MaybeQueryDevices(); - if( result < 0 ) return result; - result = sDefaultInputDeviceID; + *outData = dataPtr1; + *outDataSize = size1; + RingBuffer_AdvanceReadIndex( &pahsc->ringBuffer, size1 ); + DBUGX(("PaOSX_InputConverterCallbackProc: read %ld bytes from FIFO.\n", size1 )); } - return result; + else + { + DBUGBACK(("PaOSX_InputConverterCallbackProc: got no data!\n")); + *outData = zeroPad; // Give it zero data to keep it happy. + *outDataSize = sizeof(zeroPad); + } + return noErr; } -PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +/***************************************************************************** +** Get audio input, if any, from passed in buffer, or from converter or from FIFO +** and run PA callback. +*/ +static OSStatus PaOSX_LoadAndProcess( internalPortAudioStream *past, + void *inputBuffer, void *outputBuffer ) { - PaError result; - result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME ); - if( result < 0 ) + OSStatus err = noErr; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + if( past->past_StopSoon ) { - result = Pa_MaybeQueryDevices(); - if( result < 0 ) return result; - result = sDefaultOutputDeviceID; + if( outputBuffer ) + { + /* Clear remainder of audio buffer if we are waiting for stop. */ + AddTraceMessage("PaOSX_HandleInputOutput: zero rest of wave buffer ", i ); + memset( outputBuffer, 0, pahsc->output.bytesPerUserNativeBuffer ); + } } - return result; + else + { + /* Do we need data from the converted input? */ + if( pahsc->input.converter != NULL ) + { + UInt32 size = pahsc->input.bytesPerUserNativeBuffer; + err = AudioConverterFillBuffer( + pahsc->input.converter, + PaOSX_InputConverterCallbackProc, + past, + &size, + pahsc->input.converterBuffer); + if( err != noErr ) return err; + inputBuffer = pahsc->input.converterBuffer; + } + /* Or should just get the data directly from the FIFO? */ + else if( pahsc->ringBufferData != NULL ) + { + if( RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= pahsc->input.bytesPerUserNativeBuffer) + { + RingBuffer_Read( &pahsc->ringBuffer, pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); + inputBuffer = pahsc->input.converterBuffer; + } + } + + /* Fill part of audio converter buffer by converting input to user format, + * calling user callback, then converting output to native format. */ + if( PaConvert_Process( past, inputBuffer, outputBuffer )) + { + past->past_StopSoon = 1; + } + } + return err; } -/********************************************************************** -** Initialize Host dependant part of API. +/***************************************************************************** +** This is the proc that supplies the data to the AudioConverterFillBuffer call */ - -PaError PaHost_Init( void ) +static OSStatus PaOSX_OutputConverterCallbackProc (AudioConverterRef inAudioConverter, + UInt32* outDataSize, + void** outData, + void* inUserData) { - return Pa_MaybeQueryDevices(); + internalPortAudioStream *past = (internalPortAudioStream *) inUserData; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + *outData = pahsc->output.converterBuffer; + *outDataSize = pahsc->output.bytesPerUserNativeBuffer; + + return PaOSX_LoadAndProcess ( past, pahsc->input.converterBuffer, pahsc->output.converterBuffer ); } /********************************************************************** ** Fill any available output buffers and use any available ** input buffers by calling user callback. +** Will set past->past_StopSoon if user callback indicates that it is finished. */ -static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferList* inInputData, - AudioBufferList* outOutputData ) +static OSStatus PaOSX_HandleInputOutput( internalPortAudioStream *past, + const AudioBufferList* inInputData, + AudioBufferList* outOutputData ) { - PaError result = 0; - char *inputNativeBufferfPtr = NULL; - char *outputNativeBufferfPtr = NULL; - int i; - int buffersProcessed = 0; - int done = 0; + OSStatus err = noErr; + char *inputNativeBufferfPtr = NULL; + char *outputNativeBufferfPtr = NULL; + int i; PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paInternalError; - - past->past_NumCallbacks += 1; - -#if PA_TRACE_RUN - AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks ); -#endif - - Pa_StartUsageCalculation( past ); /* If we are using output, then we need an empty output buffer. */ - if( past->past_NumOutputChannels > 0 ) + if( outOutputData->mNumberBuffers > 0 ) { outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData; } - /* If we are using input, then we need a full input buffer. */ - if( past->past_NumInputChannels > 0 ) + if( inInputData->mNumberBuffers > 0 ) { inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData; + + /* If there is a FIFO for input then write to it. */ + if( (pahsc->ringBufferData != NULL) && !pahsc->usingSecondDevice ) + { + long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); + long numBytes = inInputData->mBuffers[0].mDataByteSize; + if( numBytes <= writeRoom ) + { + RingBuffer_Write( &pahsc->ringBuffer, inputNativeBufferfPtr, numBytes ); + DBUGBACK(("PaOSX_HandleInputOutput: wrote %ld bytes to FIFO.\n", inInputData->mBuffers[0].mDataByteSize)); + } // FIXME else ??? + } } - - buffersProcessed += 1; - - /* Each host buffer contains multiple user buffers so do them all now. */ - for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ ) + + if( pahsc->output.converter != NULL ) { - if( done ) + /* Using output and input converter. */ + UInt32 size = outOutputData->mBuffers[0].mDataByteSize; + err = AudioConverterFillBuffer( + pahsc->output.converter, + PaOSX_OutputConverterCallbackProc, + past, + &size, + outputNativeBufferfPtr); + if( err != noErr ) { - if( outputNativeBufferfPtr ) - { - /* Clear remainder of wave buffer if we are waiting for stop. */ - AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i ); - memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer ); - } + PRINT_ERR("PaOSX_HandleInputOutput: AudioConverterFillBuffer failed", err); + goto error; } - else + } + else if( (pahsc->input.converter != NULL) && !pahsc->usingSecondDevice) + { + /* Using just an input converter. */ + /* Generate user buffers as long as we have a half full input FIFO. */ + long gotHalf = pahsc->ringBuffer.bufferSize / 2; + while( (RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= gotHalf) && + (past->past_StopSoon == 0) ) { - /* Convert 32 bit native data to user data and call user routine. */ - result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); - if( result != 0) done = 1; + err = PaOSX_LoadAndProcess ( past, NULL, outputNativeBufferfPtr ); + if( err != noErr ) goto error; + if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->output.bytesPerUserNativeBuffer; } - if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer; - if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer; } - - Pa_EndUsageCalculation( past ); - -#if PA_TRACE_RUN - AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed ); -#endif - - return (result != 0) ? result : done; + else + { + /* No AUConverters used. */ + /* Each host buffer contains multiple user buffers so do them all now. */ + for( i=0; i<past->past_NumUserBuffers; i++ ) + { + err = PaOSX_LoadAndProcess ( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); + if( err != noErr ) goto error; + if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->input.bytesPerUserNativeBuffer; + if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->output.bytesPerUserNativeBuffer; + } + } + +error: + return err; } -OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, +/****************************************************************** + * This callback is used when two separate devices are used for input and output. + * This often happens when using USB devices which present as two devices: input and output. + * It just writes its data to a FIFO so that it can be read by the main callback + * proc PaOSX_CoreAudioIOCallback(). + */ +static OSStatus PaOSX_CoreAudioInputCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr) { + internalPortAudioStream *past = (internalPortAudioStream *) contextPtr; + PaHostSoundControl *pahsc; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* If there is a FIFO for input then write to it. */ + if( pahsc->ringBufferData != NULL ) + { + long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); + long numBytes = inInputData->mBuffers[0].mDataByteSize; + if( numBytes <= writeRoom ) + { + RingBuffer_Write( &pahsc->ringBuffer, inInputData->mBuffers[0].mData, inInputData->mBuffers[0].mDataByteSize ); + } + else + { + DBUGBACK(("PaOSX_CoreAudioInputCallback: FIFO too full to write!\n")); + } + } + + return noErr; +} - PaError result = 0; +/****************************************************************** + * This is the primary callback for CoreAudio. + * It can handle input and/or output for a single device. + * It takes input from CoreAudio, converts it and passes it to the + * PortAudio callback. Then takes the PA results and passes it back to CoreAudio. + */ +static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, + void* contextPtr) +{ + OSStatus err = noErr; internalPortAudioStream *past; PaHostSoundControl *pahsc; past = (internalPortAudioStream *) contextPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; -// printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers); - /* Has someone asked us to abort by calling Pa_AbortStream()? */ if( past->past_StopNow ) { @@ -721,7 +797,7 @@ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, */ else if( past->past_StopSoon ) { - // FIXME - pretend all done + // FIXME - Pretend all done. Should wait for audio to play out but CoreAudio latency very low. past->past_IsActive = 0; /* Will cause thread to return. */ } else @@ -732,63 +808,220 @@ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, past->past_FrameCount = inOutputTime->mSampleTime; } + /* Measure CPU load. */ + Pa_StartUsageCalculation( past ); + past->past_NumCallbacks += 1; + /* Process full input buffer and fill up empty output buffers. */ - if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0) - { - /* User callback has asked us to stop. */ -#if PA_TRACE_START_STOP - AddTraceMessage( "Pa_OutputThreadProc: TimeSlice() returned ", result ); -#endif - past->past_StopSoon = 1; /* Request that audio play out then stop. */ - result = paNoError; - } + err = PaOSX_HandleInputOutput( past, inInputData, outOutputData ); + + Pa_EndUsageCalculation( past ); } - // FIXME PaHost_UpdateStreamTime( pahsc ); - - return result; -} + // FIXME PaOSX_UpdateStreamTime( pahsc ); + if( err != 0 ) DBUG(("PaOSX_CoreAudioIOCallback: returns %ld.\n", err )); -#if 0 -static int PaHost_CalcTimeOut( internalPortAudioStream *past ) -{ - /* Calculate timeOut longer than longest time it could take to play all buffers. */ - int timeOut = (UInt32) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate); - if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC; - return timeOut; + return err; } -#endif - /*******************************************************************/ /* Attempt to set device sample rate. */ -static PaError PaHost_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) +static PaError PaOSX_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) { AudioStreamBasicDescription formatDesc; + PaError result = paNoError; OSStatus err; + UInt32 dataSize; + + // try to set to desired rate memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mSampleRate = sampleRate; err = AudioDeviceSetProperty( devID, 0, 0, isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + if (err != noErr) + { + result = paInvalidSampleRate; + + /* Could not set to desired rate so query for closest match. */ + DBUG(("PaOSX_SetSampleRate: couldn't set to %f. Try to find match.\n", sampleRate )); + dataSize = sizeof(formatDesc); + AudioDeviceGetProperty( devID, 0, + isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); + formatDesc.mSampleRate = sampleRate; + err = AudioDeviceGetProperty( devID, 0, + isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); + if (err == noErr) + { + /* Set to that matching rate. */ + sampleRate = formatDesc.mSampleRate; + DBUG(("PaOSX_SetSampleRate: match succeeded, set to %f instead\n", sampleRate )); + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mSampleRate = sampleRate; + AudioDeviceSetProperty( devID, 0, 0, + isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + } + } + return result; +} + +/******************************************************************* + * Check volume level of device. If below threshold, then set to newLevel. + * Using volume instead of decibels because decibel range varies by device. + */ +static void PaOSX_FixVolumeScalars( AudioDeviceID devID, Boolean isInput, + int numChannels, double threshold, double newLevel ) +{ + OSStatus err = noErr; + UInt32 dataSize; + int iChannel; + +/* The master channel is 0. Left and right are channels 1 and 2. */ +/* Fix volume. */ + for( iChannel = 0; iChannel<=numChannels; iChannel++ ) + { + Float32 fdata32; + dataSize = sizeof( fdata32 ); + err = AudioDeviceGetProperty( devID, iChannel, isInput, + kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); + if( err == noErr ) + { + DBUG(("kAudioDevicePropertyVolumeScalar for channel %d = %f\n", iChannel, fdata32)); + if( fdata32 <= (Float32) threshold ) + { + dataSize = sizeof( fdata32 ); + fdata32 = (Float32) newLevel; + err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, + kAudioDevicePropertyVolumeScalar, dataSize, &fdata32 ); + if( err != noErr ) + { + PRINT(("Warning: audio volume is very low and could not be turned up.\n")); + } + else + { + PRINT(("Volume for audio channel %d was <= %4.2f so set to %4.2f by PortAudio!\n", + iChannel, threshold, newLevel )); + } + } + } + } +/* Unmute if muted. */ + for( iChannel = 0; iChannel<=numChannels; iChannel++ ) + { + UInt32 uidata32; + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, iChannel, isInput, + kAudioDevicePropertyMute, &dataSize, &uidata32 ); + if( err == noErr ) + { + DBUG(("uidata32 for channel %d = %ld\n", iChannel, uidata32)); + if( uidata32 == 1 ) // muted? + { + dataSize = sizeof( uidata32 ); + uidata32 = 0; // unmute + err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, + kAudioDevicePropertyMute, dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT(("Warning: audio is muted and could not be unmuted!\n")); + } + else + { + PRINT(("Audio channel %d was unmuted by PortAudio!\n", iChannel )); + } + } + } + } - if (err != kAudioHardwareNoError) return paInvalidSampleRate; - else return paNoError; } +#if 0 +static void PaOSX_DumpDeviceInfo( AudioDeviceID devID, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 dataSize; + UInt32 uidata32; + Float32 fdata32; + AudioValueRange audioRange; + + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyLatency, &dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading kAudioDevicePropertyLatency", err); + return; + } + PRINT(("kAudioDevicePropertyLatency = %d\n", (int)uidata32 )); + + dataSize = sizeof( fdata32 ); + err = AudioDeviceGetProperty( devID, 1, isInput, + kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading kAudioDevicePropertyVolumeScalar", err); + return; + } + PRINT(("kAudioDevicePropertyVolumeScalar = %f\n", fdata32 )); + + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferSize, &dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size", err); + return; + } + PRINT(("kAudioDevicePropertyBufferSize = %d bytes\n", (int)uidata32 )); + + dataSize = sizeof( audioRange ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferSizeRange, &dataSize, &audioRange ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size range", err); + return; + } + PRINT(("kAudioDevicePropertyBufferSizeRange = %g to %g bytes\n", audioRange.mMinimum, audioRange.mMaximum )); + + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferFrameSize, &dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size", err); + return; + } + PRINT(("kAudioDevicePropertyBufferFrameSize = %d frames\n", (int)uidata32 )); + + dataSize = sizeof( audioRange ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferFrameSizeRange, &dataSize, &audioRange ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size range", err); + return; + } + PRINT(("kAudioDevicePropertyBufferFrameSizeRange = %g to %g frames\n", audioRange.mMinimum, audioRange.mMaximum )); + + return; +} +#endif + /*******************************************************************/ -PaError PaHost_OpenInputStream( internalPortAudioStream *past ) +static PaError PaOSX_OpenInputDevice( internalPortAudioStream *past ) { PaHostSoundControl *pahsc; const PaHostDeviceInfo *hostDeviceInfo; PaError result = paNoError; - UInt32 bytesPerHostBuffer; UInt32 dataSize; OSStatus err = noErr; - int bytesPerInputFrame; + int needConverter = 0; + double deviceRate = past->past_SampleRate; + + DBUG(("PaOSX_OpenInputDevice: -------------\n")); pahsc = (PaHostSoundControl *) past->past_DeviceData; - DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID)); if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) || (past->past_InputDeviceID > HIGHEST_INPUT_DEVID) ) { @@ -796,68 +1029,134 @@ PaError PaHost_OpenInputStream( internalPortAudioStream *past ) } hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID]; + PaOSX_FixVolumeScalars( pahsc->input.audioDeviceID, IS_INPUT, + hostDeviceInfo->paInfo.maxInputChannels, 0.1, 0.9 ); + /* Try to set sample rate. */ - result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_INPUT, past->past_SampleRate ); - if( result != paNoError ) return result; + result = PaOSX_SetSampleRate( pahsc->input.audioDeviceID, IS_INPUT, past->past_SampleRate ); + if( result != paNoError ) + { + DBUG(("PaOSX_OpenInputDevice: Need converter for sample rate = %f\n", past->past_SampleRate )); + needConverter = 1; + result = paNoError; + } + else + { + DBUG(("PaOSX_OpenInputDevice: successfully set sample rate to %f\n", past->past_SampleRate )); + } - if( past->past_NumInputChannels != hostDeviceInfo->paInfo.maxInputChannels ) + /* Try to set number of channels. */ + if( past->past_NumInputChannels > hostDeviceInfo->paInfo.maxInputChannels ) { -#if 1 - return paInvalidChannelCount; // FIXME - how support mono? -#else -FIXME - should this be set on a stream basis? Is it possible to change? - /* Attempt to set number of channels. */ + return paInvalidChannelCount; /* Too many channels! */ + } + else if( past->past_NumInputChannels < hostDeviceInfo->paInfo.maxInputChannels ) + { + AudioStreamBasicDescription formatDesc; OSStatus err; memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mChannelsPerFrame = past->past_NumInputChannels; - - err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, + err = AudioDeviceSetProperty( pahsc->input.audioDeviceID, 0, 0, IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - if (err != kAudioHardwareNoError) + if (err != noErr) { - result = paInvalidChannelCount; - goto error; + needConverter = 1; } -#endif } - // calculate native buffer sizes in bytes - bytesPerInputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; - pahsc->pahsc_BytesPerUserNativeInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame; - bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame; - - // Change the bufferSize of the device! Is this per device or just for our stream? - dataSize = sizeof(UInt32); - err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT, - kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); + /* Try to set the I/O bufferSize of the device. */ + dataSize = sizeof(pahsc->framesPerHostBuffer); + err = AudioDeviceSetProperty( pahsc->input.audioDeviceID, 0, 0, IS_INPUT, + kAudioDevicePropertyBufferFrameSize, dataSize, + &pahsc->framesPerHostBuffer); if( err != noErr ) { - ERR_RPT(("Could not force buffer size!")); - result = paHostError; - goto error; + DBUG(("PaOSX_OpenInputDevice: Need converter for buffer size = %d\n", pahsc->framesPerHostBuffer)); + needConverter = 1; } - - // setup conversion procedure + + // setup PA conversion procedure result = PaConvert_SetupInput( past, paFloat32 ); + + if( needConverter ) + { + AudioStreamBasicDescription sourceStreamFormat, destStreamFormat; + + /* Get source device format */ + dataSize = sizeof(sourceStreamFormat); + err = AudioDeviceGetProperty(pahsc->input.audioDeviceID, 0, IS_INPUT, + kAudioDevicePropertyStreamFormat, &dataSize, &sourceStreamFormat); + if( err != noErr ) + { + PRINT_ERR("PaOSX_OpenInputDevice: Could not get input device format", err); + sSavedHostError = err; + return paHostError; + } + deviceRate = sourceStreamFormat.mSampleRate; + DBUG(("PaOSX_OpenInputDevice: current device sample rate = %f\n", deviceRate )); + + /* Set target user format. */ + destStreamFormat = sourceStreamFormat; + destStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code + destStreamFormat.mChannelsPerFrame = past->past_NumInputChannels; // the number of channels in each frame + + err = AudioConverterNew ( + &sourceStreamFormat, + &destStreamFormat, + &pahsc->input.converter); + if( err != noErr ) + { + PRINT_ERR("Could not create input format converter", err); + sSavedHostError = err; + return paHostError; + } + } + + /* Allocate FIFO between Device callback and Converter callback so that device can push data + * and converter can pull data. + */ + if( needConverter || pahsc->usingSecondDevice ) + { + double sampleRateRatio; + long minSize, numBytes; + + /* Allocate an input buffer because we need it between the user callback and the converter. */ + pahsc->input.converterBuffer = PaHost_AllocateFastMemory( pahsc->input.bytesPerUserNativeBuffer ); + if( pahsc->input.converterBuffer == NULL ) + { + return paInsufficientMemory; + } - return result; - -error: + sampleRateRatio = deviceRate / past->past_SampleRate; + minSize = pahsc->input.bytesPerUserNativeBuffer * 4 * sampleRateRatio; + numBytes = RoundUpToNextPowerOf2( minSize ); + DBUG(("PaOSX_OpenInputDevice: FIFO numBytes = %ld\n", numBytes)); + pahsc->ringBufferData = PaHost_AllocateFastMemory( numBytes ); + if( pahsc->ringBufferData == NULL ) + { + return paInsufficientMemory; + } + RingBuffer_Init( &pahsc->ringBuffer, numBytes, pahsc->ringBufferData ); + // make it look full at beginning + RingBuffer_AdvanceWriteIndex( &pahsc->ringBuffer, numBytes ); + } + return result; } /*******************************************************************/ -PaError PaHost_OpenOutputStream( internalPortAudioStream *past ) +static PaError PaOSX_OpenOutputDevice( internalPortAudioStream *past ) { PaHostSoundControl *pahsc; const PaHostDeviceInfo *hostDeviceInfo; PaError result = paNoError; - UInt32 bytesPerHostBuffer; UInt32 dataSize; OSStatus err = noErr; - int bytesPerOutputFrame; + int needConverter = 0; + DBUG(("PaOSX_OpenOutputDevice: -------------\n")); + pahsc = (PaHostSoundControl *) past->past_DeviceData; DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID)); @@ -866,101 +1165,141 @@ PaError PaHost_OpenOutputStream( internalPortAudioStream *past ) { return paInvalidDeviceId; } + hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID]; + + //PaOSX_DumpDeviceInfo( pahsc->output.audioDeviceID, IS_OUTPUT ); + PaOSX_FixVolumeScalars( pahsc->output.audioDeviceID, IS_OUTPUT, + hostDeviceInfo->paInfo.maxOutputChannels, 0.1, 0.9 ); + /* Try to set sample rate. */ - result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_OUTPUT, past->past_SampleRate ); - if( result != paNoError ) return result; + result = PaOSX_SetSampleRate( pahsc->output.audioDeviceID, IS_OUTPUT, past->past_SampleRate ); + if( result != paNoError ) + { + DBUG(("PaOSX_OpenOutputDevice: Need converter for sample rate = %f\n", past->past_SampleRate )); + needConverter = 1; + result = paNoError; + } + else + { + DBUG(("PaOSX_OpenOutputDevice: successfully set sample rate to %f\n", past->past_SampleRate )); + } - if( past->past_NumOutputChannels != hostDeviceInfo->paInfo.maxOutputChannels ) + if( past->past_NumOutputChannels > hostDeviceInfo->paInfo.maxOutputChannels ) + { + return paInvalidChannelCount; /* Too many channels! */ + } + else { -#if 1 - return paInvalidChannelCount; // FIXME - how support mono? -#else /* Attempt to set number of channels. */ AudioStreamBasicDescription formatDesc; OSStatus err; memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mChannelsPerFrame = past->past_NumOutputChannels; - err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, + err = AudioDeviceSetProperty( pahsc->output.audioDeviceID, 0, 0, IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - if (err != kAudioHardwareNoError) { - result = paInvalidChannelCount; - goto error; + DBUG(("PaOSX_OpenOutputDevice: Need converter for num channels.\n")); + needConverter = 1; } -#endif } - // calculate buffer sizes in bytes - bytesPerOutputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; - pahsc->pahsc_BytesPerUserNativeOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame; - bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame; - - // Change the bufferSize of the device! Is this per device or just for our stream? - dataSize = sizeof(bytesPerHostBuffer); - err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT, - kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); + /* Change the I/O bufferSize of the device. */ + dataSize = sizeof(pahsc->framesPerHostBuffer); + err = AudioDeviceSetProperty( pahsc->output.audioDeviceID, 0, 0, IS_OUTPUT, + kAudioDevicePropertyBufferFrameSize, dataSize, + &pahsc->framesPerHostBuffer); if( err != noErr ) { - ERR_RPT(("Could not force buffer size!")); - result = paHostError; - goto error; + DBUG(("PaOSX_OpenOutputDevice: Need converter for buffer size = %d\n", pahsc->framesPerHostBuffer)); + needConverter = 1; } - + // setup conversion procedure result = PaConvert_SetupOutput( past, paFloat32 ); - return result; + if( needConverter ) + { + AudioStreamBasicDescription sourceStreamFormat, destStreamFormat; + DBUG(("PaOSX_OpenOutputDevice: using AUConverter!\n")); + /* Get target device format */ + dataSize = sizeof(destStreamFormat); + err = AudioDeviceGetProperty(pahsc->output.audioDeviceID, 0, IS_OUTPUT, + kAudioDevicePropertyStreamFormat, &dataSize, &destStreamFormat); + if( err != noErr ) + { + PRINT_ERR("PaOSX_OpenOutputDevice: Could not get output device format", err); + sSavedHostError = err; + return paHostError; + } -error: + /* Set source user format. */ + sourceStreamFormat = destStreamFormat; + sourceStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code + sourceStreamFormat.mChannelsPerFrame = past->past_NumOutputChannels; // the number of channels in each frame + + /* Allocate an output buffer because we need it between the user callback and the converter. */ + pahsc->output.converterBuffer = PaHost_AllocateFastMemory( pahsc->output.bytesPerUserNativeBuffer ); + err = AudioConverterNew ( + &sourceStreamFormat, + &destStreamFormat, + &pahsc->output.converter); + if( err != noErr ) + { + PRINT_ERR("Could not create output format converter", err); + sSavedHostError = err; + return paHostError; + } + } + return result; } -/*******************************************************************/ -PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - return pahsc->pahsc_FramesPerHostBuffer; -} - - /******************************************************************* * Determine how many User Buffers we can put into our CoreAudio stream buffer. * Uses: * past->past_FramesPerUserBuffer, etc. * Sets: * past->past_NumUserBuffers -* pahsc->pahsc_UserBuffersPerHostBuffer -* pahsc->pahsc_FramesPerHostBuffer +* pahsc->framesPerHostBuffer +* pahsc->input.bytesPerUserNativeBuffer +* pahsc->output.bytesPerUserNativeBuffer */ -static void PaHost_CalcHostBufferSize( internalPortAudioStream *past ) +static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ) { PaHostSoundControl *pahsc = ( PaHostSoundControl *)past->past_DeviceData; - unsigned int minNumUserBuffers; - // Determine number of user buffers based on minimum latency. - minNumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); - // Compare to user requested number in user wants more than the minimum. - past->past_NumUserBuffers = ( minNumUserBuffers > past->past_NumUserBuffers ) ? - minNumUserBuffers : past->past_NumUserBuffers; - DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); + // Determine number of user buffers based strictly on minimum reasonable buffer size. + // We ignore the Pa_OpenStream numBuffer parameter because CoreAudio has a big + // mix buffer and handles latency automatically. + past->past_NumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); - // For CoreAudio, we only have one Host buffer, so... - pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers; // Calculate size of CoreAudio buffer. - pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; + pahsc->framesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; - DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); - DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); + // calculate buffer sizes in bytes + pahsc->input.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * + Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; + pahsc->output.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * + Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; + + DBUG(("PaOSX_CalcNumHostBuffers: past_NumUserBuffers = %ld\n", past->past_NumUserBuffers )); + DBUG(("PaOSX_CalcNumHostBuffers: framesPerHostBuffer = %d\n", pahsc->framesPerHostBuffer )); + DBUG(("PaOSX_CalcNumHostBuffers: input.bytesPerUserNativeBuffer = %d\n", pahsc->input.bytesPerUserNativeBuffer )); + DBUG(("PaOSX_CalcNumHostBuffers: output.bytesPerUserNativeBuffer = %d\n", pahsc->output.bytesPerUserNativeBuffer )); } -/*******************************************************************/ +/*****************************************************************************/ +/************** Internal Host API ********************************************/ +/*****************************************************************************/ PaError PaHost_OpenStream( internalPortAudioStream *past ) { PaError result = paNoError; PaHostSoundControl *pahsc; + Boolean useInput; + Boolean useOutput; /* Allocate and initialize host data. */ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl)); @@ -971,43 +1310,53 @@ PaError PaHost_OpenStream( internalPortAudioStream *past ) } memset( pahsc, 0, sizeof(PaHostSoundControl) ); past->past_DeviceData = (void *) pahsc; + pahsc->primaryDeviceID = kAudioDeviceUnknown; + pahsc->input.audioDeviceID = kAudioDeviceUnknown; + pahsc->output.audioDeviceID = kAudioDeviceUnknown; + + PaOSX_CalcHostBufferSize( past ); + + /* Setup constants for CPU load measurement. */ + pahsc->inverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->framesPerHostBuffer); - // If we are using both input and out, then they must be on the same CoreAudio device, - // until we implement a FIFO between two devices. - if( (past->past_OutputDeviceID != paNoDevice) && (past->past_InputDeviceID != paNoDevice) ) + useOutput = (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0); + useInput = (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0); + + // Set device IDs + if( useOutput ) { - AudioDeviceID inputID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; - AudioDeviceID outputID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; - if( inputID != outputID ) + pahsc->output.audioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + pahsc->primaryDeviceID = pahsc->output.audioDeviceID; + if( useInput ) { - ERR_RPT(("PortAudio: input and output must use same CoreAudio device!\n")); - return paInvalidDeviceId; + pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + if( pahsc->input.audioDeviceID != pahsc->primaryDeviceID ) + { + pahsc->usingSecondDevice = TRUE; // Use two separate devices! + } } } - - PaHost_CalcHostBufferSize( past ); - + else { - int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate); - PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency )); + /* Just input, not output. */ + pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + pahsc->primaryDeviceID = pahsc->input.audioDeviceID; } + DBUG(("outputDeviceID = %ld\n", pahsc->output.audioDeviceID )); + DBUG(("inputDeviceID = %ld\n", pahsc->input.audioDeviceID )); + DBUG(("primaryDeviceID = %ld\n", pahsc->primaryDeviceID )); - /* Setup constants for CPU load measurement. */ - pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); - /* ------------------ OUTPUT */ - if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) ) + if( useOutput ) { - pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; - result = PaHost_OpenOutputStream( past ); + result = PaOSX_OpenOutputDevice( past ); if( result < 0 ) goto error; } /* ------------------ INPUT */ - if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) ) + if( useInput ) { - pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; - result = PaHost_OpenInputStream( past ); + result = PaOSX_OpenInputDevice( past ); if( result < 0 ) goto error; } @@ -1041,21 +1390,49 @@ PaError PaHost_StartEngine( internalPortAudioStream *past ) past->past_StopNow = 0; past->past_IsActive = 1; +/* If full duplex and using two separate devices then start input device. */ + if( pahsc->usingSecondDevice ) + { + // Associate an IO proc with the device and pass a pointer to the audio data context + err = AudioDeviceAddIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback, past); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc secondary failed", err ); + goto error; + } + + // start playing sound through the device + err = AudioDeviceStart(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceStart secondary failed", err ); + PRINT(("The program may succeed if you run it again!\n")); + goto error; + } + } + // Associate an IO proc with the device and pass a pointer to the audio data context - err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past); - if (err != noErr) goto error; + err = AudioDeviceAddIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback, past); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc primary failed", err ); + goto error; + } // start playing sound through the device - err = AudioDeviceStart(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); - if (err != noErr) goto error; + err = AudioDeviceStart(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceStart primary failed", err ); + PRINT(("The program may succeed if you run it again!\n")); + goto error; + } + return result; -#if PA_TRACE_START_STOP - AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result ); -#endif - error: - return paHostError; // FIXME - save host error + sSavedHostError = err; + return paHostError; } /*************************************************************************/ @@ -1077,16 +1454,29 @@ PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) #endif // FIXME - we should ask proc to stop instead of stopping abruptly - err = AudioDeviceStop(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); - if (err != noErr) goto Bail; + err = AudioDeviceStop(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); + if (err != noErr) + { + goto error; + } - err = AudioDeviceRemoveIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); - if (err != noErr) goto Bail; + err = AudioDeviceRemoveIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); + if (err != noErr) goto error; + +/* If full duplex and using two separate devices then start input device. */ + if( pahsc->usingSecondDevice ) + { + err = AudioDeviceStop(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); + if (err != noErr) goto error; + err = AudioDeviceRemoveIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); + if (err != noErr) goto error; + } return paNoError; -Bail: - return paHostError; // FIXME - save err +error: + sSavedHostError = err; + return paHostError; } /*************************************************************************/ @@ -1109,70 +1499,47 @@ PaError PaHost_CloseStream( internalPortAudioStream *past ) if( past == NULL ) return paBadStreamPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; if( pahsc == NULL ) return paNoError; + + //PaOSX_DumpDeviceInfo( sDeviceInfos[past->past_OutputDeviceID].audioDeviceID, IS_OUTPUT ); #if PA_TRACE_START_STOP AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); #endif + if( pahsc->output.converterBuffer != NULL ) + { + PaHost_FreeFastMemory( pahsc->output.converterBuffer, pahsc->output.bytesPerUserNativeBuffer ); + } + if( pahsc->input.converterBuffer != NULL ) + { + PaHost_FreeFastMemory( pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); + } + if( pahsc->ringBufferData != NULL ) + { + PaHost_FreeFastMemory( pahsc->ringBufferData, pahsc->ringBuffer.bufferSize ); + } + if( pahsc->output.converter != NULL ) + { + verify_noerr(AudioConverterDispose (pahsc->output.converter)); + } + if( pahsc->input.converter != NULL ) + { + verify_noerr(AudioConverterDispose (pahsc->input.converter)); + } + free( pahsc ); past->past_DeviceData = NULL; return paNoError; } -/************************************************************************* -** Determine minimum number of buffers required for this host based -** on minimum latency. Latency can be optionally set by user by setting -** an environment variable. For example, to set latency to 200 msec, put: -** -** set PA_MIN_LATENCY_MSEC=200 -** -** in the cshrc file. -*/ -#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") - -#if 1 -int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) -{ - int minBuffers; - int denominator; - int minLatencyMsec = PA_MIN_LATENCY_MSEC; - char *minLatencyText = getenv(PA_LATENCY_ENV_NAME); - if( minLatencyText != NULL ) - { - PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText )); - minLatencyMsec = atoi( minLatencyText ); - if( minLatencyMsec < 1 ) minLatencyMsec = 1; - else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000; - } - - denominator = 1000.0 * framesPerBuffer; - minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); - if( minBuffers < 1 ) minBuffers = 1; - return minBuffers; -} -#else -/************************************************************************* -** Determine minimum number of buffers required for this host based -** on minimum latency. +/********************************************************************** +** Initialize Host dependant part of API. */ -int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +PaError PaHost_Init( void ) { - int minUserBuffers; - int minFramesPerHostBuffer; - - // Calculate minimum and maximum sizes based on timing and sample rate. - minFramesPerHostBuffer = (int) (PA_MIN_LATENCY_MSEC * sampleRate / 1000.0); - // round up to nearest multiple of 8 - minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; - DBUG(("Pa_GetMinNumBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); - - minUserBuffers = (minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; - if( minUserBuffers < 1 ) minUserBuffers = 1; - - return minUserBuffers; + return PaOSX_MaybeQueryDevices(); } -#endif /************************************************************************* ** Cleanup device info. @@ -1185,8 +1552,10 @@ PaError PaHost_Term( void ) { for( i=0; i<sNumPaDevices; i++ ) { - if( sDeviceInfos[i].paInfo.name != NULL ) free( (char*)sDeviceInfos[i].paInfo.name ); - if( sDeviceInfos[i].paInfo.sampleRates != NULL ) free( (void*)sDeviceInfos[i].paInfo.sampleRates ); + if( sDeviceInfos[i].paInfo.name != NULL ) + { + free( (char*)sDeviceInfos[i].paInfo.name ); + } } free( sDeviceInfos ); sDeviceInfos = NULL; @@ -1196,19 +1565,6 @@ PaError PaHost_Term( void ) return paNoError; } -/*************************************************************************/ -void Pa_Sleep( long msec ) -{ -#if 0 - struct timeval timeout; - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; - select( 0, NULL, NULL, NULL, &timeout ); -#else - usleep( msec * 1000 ); -#endif -} - /************************************************************************* * Allocate memory that can be accessed in real-time. * This may need to be held in physical memory so that it is not @@ -1217,7 +1573,7 @@ void Pa_Sleep( long msec ) */ void *PaHost_AllocateFastMemory( long numBytes ) { - void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */ + void *addr = malloc( numBytes ); /* FIXME - do we need physical memory, not virtual memory? */ if( addr != NULL ) memset( addr, 0, numBytes ); return addr; } @@ -1242,6 +1598,57 @@ PaError PaHost_StreamActive( internalPortAudioStream *past ) return (PaError) past->past_IsActive; } +/*******************************************************************/ +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return pahsc->framesPerHostBuffer; +} + +/*****************************************************************************/ +/************** External User API ********************************************/ +/*****************************************************************************/ + +/********************************************************************** +** Query devices and use result. +*/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + PaError result = PaOSX_MaybeQueryDevices(); + if( result < 0 ) return result; + return sDefaultInputDeviceID; +} + +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + PaError result = PaOSX_MaybeQueryDevices(); + if( result < 0 ) return result; + return sDefaultOutputDeviceID; +} + + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Because CoreAudio manages latency, this just sets +** a reasonable small buffer size. +*/ +int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) +{ + int minBuffers; + double denominator; + int minLatencyMsec = PA_MIN_LATENCY_MSEC; + denominator = 1000.0 * framesPerBuffer; + minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); + if( minBuffers < 1 ) minBuffers = 1; + return minBuffers; +} + +/*************************************************************************/ +void Pa_Sleep( long msec ) +{ + usleep( msec * 1000 ); +} + /*************************************************************************/ PaTimestamp Pa_StreamTime( PortAudioStream *stream ) { @@ -1252,10 +1659,37 @@ PaTimestamp Pa_StreamTime( PortAudioStream *stream ) if( past == NULL ) return paBadStreamPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; - AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp); + AudioDeviceGetCurrentTime(pahsc->primaryDeviceID, &timeStamp); streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ? timeStamp.mSampleTime : past->past_FrameCount; return streamTime; } + +/************************************************************************************/ +long Pa_GetHostError() +{ + return sSavedHostError; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumPaDevices <= 0 ) Pa_Initialize(); + return sNumPaDevices; +} + +/************************************************************************* +** PaDeviceInfo structures have already been created +** so just return the pointer. +** +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + if( id < 0 || id >= sNumPaDevices ) + return NULL; + + return &sDeviceInfos[id].paInfo; +} + diff --git a/pd/src/configure b/pd/src/configure index 9205ddb3..aa179b0c 100755 --- a/pd/src/configure +++ b/pd/src/configure @@ -3121,7 +3121,9 @@ fi if test `uname -s` = Darwin; then - LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio -framework Carbon -framework CoreMIDI" + LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio \ + -framework AudioUnit -framework AudioToolbox \ + -framework Carbon -framework CoreMIDI" EXT=pd_darwin MORECFLAGS="-DMACOSX -I/usr/X11R6/include -I../portaudio/pa_common \ -I../portaudio/pablio -I../portaudio/portmidi-macosx -Wno-error" diff --git a/pd/src/configure.in b/pd/src/configure.in index edc2250c..da70b29e 100644 --- a/pd/src/configure.in +++ b/pd/src/configure.in @@ -169,7 +169,9 @@ fi if test `uname -s` = Darwin; then - LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio -framework Carbon -framework CoreMIDI" + LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio \ + -framework AudioUnit -framework AudioToolbox \ + -framework Carbon -framework CoreMIDI" EXT=pd_darwin MORECFLAGS="-DMACOSX -I/usr/X11R6/include -I../portaudio/pa_common \ -I../portaudio/pablio -I../portaudio/portmidi-macosx -Wno-error" diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c index db562404..2cd4994a 100644 --- a/pd/src/g_all_guis.c +++ b/pd/src/g_all_guis.c @@ -147,16 +147,30 @@ int iemgui_modulo_color(int col) return(col); } -void iemgui_raute2dollar(t_symbol *s) +t_symbol *iemgui_raute2dollar(t_symbol *s) { - if(s->s_name[0] == '#') - s->s_name[0] = '$'; + if (s->s_name[0] == '#') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '$'; + return (gensym(buf)); + } + else return (s); } -void iemgui_dollar2raute(t_symbol *s) +t_symbol *iemgui_dollar2raute(t_symbol *s) { - if(s->s_name[0] == '$') - s->s_name[0] = '#'; + if (s->s_name[0] == '$') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '#'; + return (gensym(buf)); + } + else return (s); } t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag) @@ -504,22 +518,16 @@ int iemgui_compatible_col(int i) void iemgui_all_dollar2raute(t_symbol **srlsym) { - if(srlsym[0]->s_name[0] == '$') - srlsym[0]->s_name[0] = '#'; - if(srlsym[1]->s_name[0] == '$') - srlsym[1]->s_name[0] = '#'; - if(srlsym[2]->s_name[0] == '$') - srlsym[2]->s_name[0] = '#'; + srlsym[0] = iemgui_dollar2raute(srlsym[0]); + srlsym[1] = iemgui_dollar2raute(srlsym[1]); + srlsym[2] = iemgui_dollar2raute(srlsym[2]); } void iemgui_all_raute2dollar(t_symbol **srlsym) { - if(srlsym[0]->s_name[0] == '#') - srlsym[0]->s_name[0] = '$'; - if(srlsym[1]->s_name[0] == '#') - srlsym[1]->s_name[0] = '$'; - if(srlsym[2]->s_name[0] == '#') - srlsym[2]->s_name[0] = '$'; + srlsym[0] = iemgui_raute2dollar(srlsym[0]); + srlsym[1] = iemgui_raute2dollar(srlsym[1]); + srlsym[2] = iemgui_raute2dollar(srlsym[2]); } void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s) @@ -534,9 +542,8 @@ void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s) oldsndrcvable += IEM_GUI_OLD_SND_FLAG; if(!strcmp(s->s_name, "empty")) sndable = 0; - iemgui_raute2dollar(s); iemgui_fetch_unique(iemgui); - snd = s; + snd = iemgui_raute2dollar(s); if(iemgui_is_dollarzero(snd)) { iemgui->x_fsf.x_snd_is_unique = 1; @@ -577,8 +584,7 @@ void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s) oldsndrcvable += IEM_GUI_OLD_SND_FLAG; if(!strcmp(s->s_name, "empty")) rcvable = 0; - iemgui_raute2dollar(s); - rcv = s; + rcv = iemgui_raute2dollar(s); iemgui_fetch_unique(iemgui); if(iemgui_is_dollarzero(rcv)) { @@ -628,8 +634,7 @@ void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s) int pargc, tail_len, nth_arg; t_atom *pargv; - iemgui_raute2dollar(s); - lab = s; + lab = iemgui_raute2dollar(s); iemgui_fetch_unique(iemgui); if(iemgui_is_dollarzero(lab)) diff --git a/pd/src/g_all_guis.h b/pd/src/g_all_guis.h index ec9d4e69..5fab095d 100644 --- a/pd/src/g_all_guis.h +++ b/pd/src/g_all_guis.h @@ -277,8 +277,6 @@ extern char *iemgui_vu_scale_str[]; EXTERN int iemgui_clip_size(int size); EXTERN int iemgui_clip_font(int size); EXTERN int iemgui_modulo_color(int col); -EXTERN void iemgui_raute2dollar(t_symbol *s); -EXTERN void iemgui_dollar2raute(t_symbol *s); EXTERN t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag); EXTERN t_symbol *iemgui_sym2dollararg(t_symbol *s, int nth_arg, int tail_len); EXTERN t_symbol *iemgui_dollarzero2unique(t_symbol *s, int unique_num); diff --git a/pd/src/g_array.c b/pd/src/g_array.c index 2cdb3d8e..7a9fb3f7 100644 --- a/pd/src/g_array.c +++ b/pd/src/g_array.c @@ -336,7 +336,7 @@ static void array_motion(void *z, t_floatarg dx, t_floatarg dy) { if (i == 0) { - float newy = ywas + dy; + float newy = ywas + dy * array_motion_yperpix; if (newy < 0) newy = 0; template_setfloat(array_motion_template, @@ -346,7 +346,8 @@ static void array_motion(void *z, t_floatarg dx, t_floatarg dy) else { template_setfloat(array_motion_template, - array_motion_yfield, thisword, ywas + dy, 1); + array_motion_yfield, thisword, + ywas + dy * array_motion_yperpix, 1); } } } @@ -488,9 +489,8 @@ int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, elem = (char *)array->a_vec; memmove(elem + elemsize * (i+1), elem + elemsize * i, - (array->a_n - i) * elemsize); + (array->a_n - i - 1) * elemsize); i++; - (array->a_n)++; } if (xonset >= 0) { @@ -516,7 +516,7 @@ int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, array_motion_yfield = gensym("w"); array_motion_ycumulative = *(float *)((elem + elemsize * i) + wonset); - array_motion_xperpix *= array_motion_fatten; + array_motion_yperpix *= array_motion_fatten; } else if (yonset >= 0) { diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c index 8bad09a9..1eba8ac5 100644 --- a/pd/src/g_canvas.c +++ b/pd/src/g_canvas.c @@ -38,7 +38,7 @@ struct _canvasenvironment #define GLIST_DEFCANVASHEIGHT 300 #ifdef MACOSX -#define GLIST_DEFCANVASYLOC 20 +#define GLIST_DEFCANVASYLOC 22 #else #define GLIST_DEFCANVASYLOC 0 #endif @@ -186,8 +186,6 @@ void canvas_getargs(int *argcp, t_atom **argvp) *argvp = e->ce_argv; } -t_symbol *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew); - t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) { t_symbol *ret; @@ -196,7 +194,8 @@ t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) { t_canvasenvironment *env = canvas_getenv(x); canvas_setcurrent(x); - ret = realizedollsym(gensym(name+1), env->ce_argc, env->ce_argv, 1); + ret = binbuf_realizedollsym(gensym(name+1), + env->ce_argc, env->ce_argv, 1); canvas_unsetcurrent(x); } else ret = s; @@ -397,6 +396,10 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) } else x->gl_env = 0; + if (yloc < GLIST_DEFCANVASYLOC) + yloc = GLIST_DEFCANVASYLOC; + if (xloc < 0) + xloc = 0; x->gl_x1 = 0; x->gl_y1 = 0; x->gl_x2 = 1; @@ -957,7 +960,7 @@ void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv) if (ap->a_type == A_DOLLSYM) { t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); - canvas_rename(x, realizedollsym(ap->a_w.w_symbol, + canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol, e->ce_argc, e->ce_argv, 1), 0); } else if (ap->a_type == A_SYMBOL) diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h index 5348f25f..e4eecfc0 100644 --- a/pd/src/g_canvas.h +++ b/pd/src/g_canvas.h @@ -577,3 +577,7 @@ EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname, /* ----------------------- guiconnects, g_guiconnect.c --------- */ EXTERN t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym); EXTERN void guiconnect_notarget(t_guiconnect *x, double timedelay); + +/* ------------- IEMGUI routines used in other g_ files ---------------- */ +EXTERN t_symbol *iemgui_raute2dollar(t_symbol *s); +EXTERN t_symbol *iemgui_dollar2raute(t_symbol *s); diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c index 101c655a..470030e9 100644 --- a/pd/src/g_editor.c +++ b/pd/src/g_editor.c @@ -494,6 +494,7 @@ static void canvas_undo_cut(t_canvas *x, void *z, int action) else if (mode == UCUT_TEXT) { t_gobj *y1, *y2; + glist_noselect(x); for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2) ; if (y1) @@ -1113,6 +1114,8 @@ void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, if (!shiftmod) glist_noselect(x); sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n", x, xpos, ypos, xpos, ypos); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; x->gl_editor->e_onmotion = MA_REGION; } } @@ -1354,7 +1357,7 @@ void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av) } if (keynumsym->s_thing && down) pd_float(keynumsym->s_thing, keynum); - if (keyupsym->s_thing && down) + if (keyupsym->s_thing && !down) pd_float(keyupsym->s_thing, keynum); if (keynamesym->s_thing) { @@ -1967,8 +1970,9 @@ static void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno, if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad; if (glist_isvisible(x)) { - sys_vgui(".x%x.c create line %d %d %d %d -tags l%x\n", - glist_getcanvas(x), 0, 0, 0, 0, oc); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), 0, 0, 0, 0, + (canvas_issigoutlet(objsrc, outno) ? 2 : 1),oc); canvas_fixlinesfor(x, objsrc); } return; diff --git a/pd/src/g_hdial.c b/pd/src/g_hdial.c index caca3a22..9f0db2cc 100644 --- a/pd/src/g_hdial.c +++ b/pd/src/g_hdial.c @@ -244,7 +244,7 @@ static void hradio_save(t_gobj *z, t_binbuf *b) (t_int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist), (t_int)text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist), (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class ? - gensym("vdl") : gensym("vradio")), + gensym("hdl") : gensym("hradio")), x->x_gui.x_w, x->x_change, (*ip1)&IEM_INIT_ARGS_ALL, x->x_number, srl[0], srl[1], srl[2], @@ -657,7 +657,7 @@ static void hradio_ff(t_hradio *x) void g_hradio_setup(void) { - hradio_class = class_new(gensym("hdl"), (t_newmethod)hradio_new, + hradio_class = class_new(gensym("hradio"), (t_newmethod)hradio_new, (t_method)hradio_ff, sizeof(t_hradio), 0, A_GIMME, 0); class_addbang(hradio_class, hradio_bang); class_addfloat(hradio_class, hradio_float); @@ -714,43 +714,43 @@ void g_hradio_setup(void) class_addcreator((t_newmethod)hradio_new, gensym("radiobut"), A_GIMME, 0); class_addcreator((t_newmethod)hradio_new, gensym("radiobutton"), A_GIMME, 0); - class_addbang(hradio_class, hradio_bang); - class_addfloat(hradio_class, hradio_float); - class_addmethod(hradio_class, (t_method)hradio_click, gensym("click"), + class_addbang(hradio_old_class, hradio_bang); + class_addfloat(hradio_old_class, hradio_float); + class_addmethod(hradio_old_class, (t_method)hradio_click, gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); - class_addmethod(hradio_class, (t_method)hradio_dialog, gensym("dialog"), + class_addmethod(hradio_old_class, (t_method)hradio_dialog, gensym("dialog"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_loadbang, + class_addmethod(hradio_old_class, (t_method)hradio_loadbang, gensym("loadbang"), 0); - class_addmethod(hradio_class, (t_method)hradio_set, + class_addmethod(hradio_old_class, (t_method)hradio_set, gensym("set"), A_FLOAT, 0); - class_addmethod(hradio_class, (t_method)hradio_size, + class_addmethod(hradio_old_class, (t_method)hradio_size, gensym("size"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_delta, + class_addmethod(hradio_old_class, (t_method)hradio_delta, gensym("delta"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_pos, + class_addmethod(hradio_old_class, (t_method)hradio_pos, gensym("pos"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_color, + class_addmethod(hradio_old_class, (t_method)hradio_color, gensym("color"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_send, + class_addmethod(hradio_old_class, (t_method)hradio_send, gensym("send"), A_DEFSYM, 0); - class_addmethod(hradio_class, (t_method)hradio_receive, + class_addmethod(hradio_old_class, (t_method)hradio_receive, gensym("receive"), A_DEFSYM, 0); - class_addmethod(hradio_class, (t_method)hradio_label, + class_addmethod(hradio_old_class, (t_method)hradio_label, gensym("label"), A_DEFSYM, 0); - class_addmethod(hradio_class, (t_method)hradio_label_pos, + class_addmethod(hradio_old_class, (t_method)hradio_label_pos, gensym("label_pos"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_label_font, + class_addmethod(hradio_old_class, (t_method)hradio_label_font, gensym("label_font"), A_GIMME, 0); - class_addmethod(hradio_class, (t_method)hradio_init, + class_addmethod(hradio_old_class, (t_method)hradio_init, gensym("init"), A_FLOAT, 0); - class_addmethod(hradio_class, (t_method)hradio_number, + class_addmethod(hradio_old_class, (t_method)hradio_number, gensym("number"), A_FLOAT, 0); - class_addmethod(hradio_class, (t_method)hradio_single_change, + class_addmethod(hradio_old_class, (t_method)hradio_single_change, gensym("single_change"), 0); - class_addmethod(hradio_class, (t_method)hradio_double_change, + class_addmethod(hradio_old_class, (t_method)hradio_double_change, gensym("double_change"), 0); - class_setwidget(hradio_class, &hradio_widgetbehavior); - class_sethelpsymbol(hradio_class, gensym("hradio")); + class_setwidget(hradio_old_class, &hradio_widgetbehavior); + class_sethelpsymbol(hradio_old_class, gensym("hradio")); } diff --git a/pd/src/g_numbox.c b/pd/src/g_numbox.c index 092c2718..b8f962bc 100644 --- a/pd/src/g_numbox.c +++ b/pd/src/g_numbox.c @@ -318,8 +318,6 @@ static void my_numbox_draw_select(t_my_numbox *x, t_glist *glist) x->x_buf[0] = 0; (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); } - glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.ob_g, - 0, my_numbox_key, 0, 0); sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", @@ -331,7 +329,6 @@ static void my_numbox_draw_select(t_my_numbox *x, t_glist *glist) } else { - glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", @@ -533,6 +530,7 @@ static void my_numbox_motion(t_my_numbox *x, t_floatarg dx, t_floatarg dy) my_numbox_clip(x); (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); my_numbox_bang(x); + clock_unset(x->x_clock_reset); } static void my_numbox_click(t_my_numbox *x, t_floatarg xpos, t_floatarg ypos, diff --git a/pd/src/g_text.c b/pd/src/g_text.c index d59afb3a..b502446b 100644 --- a/pd/src/g_text.c +++ b/pd/src/g_text.c @@ -65,7 +65,12 @@ void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv) glist_add(gl, &x->te_g); glist_noselect(gl); glist_select(gl, &x->te_g); - gobj_activate(&x->te_g, gl, 1); + /* it would be nice to "activate" here, but then the second, + "put-me-down" click changes the text selection, which is quite + irritating, so I took this back out. It's OK in messages + and objects though since there's no text in them at menu + creation. */ + /* gobj_activate(&x->te_g, gl, 1); */ canvas_startmotion(glist_getcanvas(gl)); } } @@ -410,8 +415,13 @@ typedef struct _gatom char a_buf[ATOMBUFSIZE];/* string buffer for typing */ char a_shift; /* was shift key down when dragging started? */ char a_wherelabel; /* 0-3 for left, right, above, below */ + t_symbol *a_expanded_to; /* a_symto after $0, $1, ... expansion */ } t_gatom; + /* prepend "-" as necessary to avoid empty strings, so we can + use them in Pd messages. A more complete solution would be + to introduce some quoting mechanism; but then we'd be much more + complicated. */ static t_symbol *gatom_escapit(t_symbol *s) { if (!*s->s_name) @@ -424,15 +434,24 @@ static t_symbol *gatom_escapit(t_symbol *s) shmo[99] = 0; return (gensym(shmo)); } - else return (s); + else return (iemgui_dollar2raute(s)); } + /* undo previous operation: strip leading "-" if found. */ static t_symbol *gatom_unescapit(t_symbol *s) { if (*s->s_name == '-') return (gensym(s->s_name+1)); - else return (s); + else return (iemgui_raute2dollar(s)); +} + +#if 0 /* ??? */ + /* expand leading $0, $1, etc. in the symbol */ +static t_symbol *gatom_realizedollar(t_gatom *x, t_symbol *s) +{ + return (canvas_realizedollar(x->a_glist, s)); } +#endif static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { @@ -453,13 +472,13 @@ static void gatom_bang(t_gatom *x) { if (x->a_text.te_outlet) outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float); - if (*x->a_symto->s_name && x->a_symto->s_thing) + if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing) { if (x->a_symto == x->a_symfrom) pd_error(x, "%s: atom with same send/receive name (infinite loop)", x->a_symto->s_name); - else pd_float(x->a_symto->s_thing, x->a_atom.a_w.w_float); + else pd_float(x->a_expanded_to->s_thing, x->a_atom.a_w.w_float); } } else if (x->a_atom.a_type == A_SYMBOL) @@ -472,7 +491,7 @@ static void gatom_bang(t_gatom *x) pd_error(x, "%s: atom with same send/receive name (infinite loop)", x->a_symto->s_name); - else pd_symbol(x->a_symto->s_thing, x->a_atom.a_w.w_symbol); + else pd_symbol(x->a_expanded_to->s_thing, x->a_atom.a_w.w_symbol); } } } @@ -565,9 +584,15 @@ static void gatom_key(void *z, t_floatarg f) } else if (len < (ATOMBUFSIZE-1)) { - x->a_buf[len] = c; - x->a_buf[len+1] = 0; - goto redraw; + /* for numbers, only let reasonable characters through */ + if ((x->a_atom.a_type == A_SYMBOL) || + (c >= '0' && c <= '9' || c == '.' || c == '-' + || c == 'e' || c == 'E')) + { + x->a_buf[len] = c; + x->a_buf[len+1] = 0; + goto redraw; + } } return; redraw: @@ -648,11 +673,14 @@ static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv) x->a_wherelabel = ((int)wherelabel & 3); x->a_label = label; if (*x->a_symfrom->s_name) - pd_unbind(&x->a_text.te_pd, x->a_symfrom); + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); x->a_symfrom = symfrom; if (*x->a_symfrom->s_name) - pd_bind(&x->a_text.te_pd, x->a_symfrom); + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); x->a_symto = symto; + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); gobj_vis(&x->a_text.te_g, x->a_glist, 1); /* glist_retext(x->a_glist, &x->a_text); */ @@ -667,7 +695,8 @@ static void gatom_getwherelabel(t_gatom *x, t_glist *glist, int *xp, int *yp) height = y2 - y1; if (x->a_wherelabel == ATOM_LABELLEFT) { - *xp = x1 - 3 - strlen(x->a_label->s_name) * + *xp = x1 - 3 - + strlen(canvas_realizedollar(x->a_glist, x->a_label)->s_name) * sys_fontwidth(glist_getfont(glist)); *yp = y1 + 2; } @@ -710,7 +739,8 @@ static void gatom_vis(t_gobj *z, t_glist *glist, int vis) sys_vgui("pdtk_text_new .x%x.c %x.l %f %f {%s} %d %s\n", glist_getcanvas(glist), x, (double)x1, (double)y1, - x->a_label->s_name, sys_hostfontsize(glist_getfont(glist)), + canvas_realizedollar(x->a_glist, x->a_label)->s_name, + sys_hostfontsize(glist_getfont(glist)), "black"); } else sys_vgui(".x%x.c delete %x.l\n", glist_getcanvas(glist), x); @@ -733,7 +763,7 @@ void canvas_atom(t_glist *gl, t_atomtype type, x->a_wherelabel = 0; x->a_label = &s_; x->a_symfrom = &s_; - x->a_symto = &s_; + x->a_symto = x->a_expanded_to = &s_; if (type == A_FLOAT) { x->a_atom.a_w.w_float = 0; @@ -764,9 +794,11 @@ void canvas_atom(t_glist *gl, t_atomtype type, x->a_label = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); x->a_symfrom = gatom_unescapit(atom_getsymbolarg(7, argc, argv)); if (*x->a_symfrom->s_name) - pd_bind(&x->a_text.te_pd, x->a_symfrom); + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); x->a_symto = gatom_unescapit(atom_getsymbolarg(8, argc, argv)); + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); if (x->a_symto == &s_) outlet_new(&x->a_text, x->a_atom.a_type == A_FLOAT ? &s_float: &s_symbol); @@ -805,7 +837,8 @@ void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) static void gatom_free(t_gatom *x) { if (*x->a_symfrom->s_name) - pd_bind(&x->a_text.te_pd, x->a_symfrom); + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); gfxstub_deleteforkey(x); } diff --git a/pd/src/g_vdial.c b/pd/src/g_vdial.c index e1cd2a5d..4974227a 100644 --- a/pd/src/g_vdial.c +++ b/pd/src/g_vdial.c @@ -2,7 +2,7 @@ * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ -/* vradio.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* vdial.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ /* name change to vradio by MSP (it's a radio button really) and changed to put out a "float" as in sliders, toggles, etc. */ diff --git a/pd/src/m_binbuf.c b/pd/src/m_binbuf.c index 1398ad84..fab30baf 100644 --- a/pd/src/m_binbuf.c +++ b/pd/src/m_binbuf.c @@ -419,7 +419,7 @@ t_atom *binbuf_getvec(t_binbuf *x) int canvas_getdollarzero( void); -t_symbol *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) /* IOhannes: not static any more */ +t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) { int argno = atol(s->s_name), lastnum; char buf[MAXPDSTRING], c, *sp; @@ -475,7 +475,8 @@ void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv) } else if (at->a_type == A_DOLLSYM) { - if (!(s = realizedollsym(at->a_w.w_symbol, argc, argv, 0))) + if (!(s = binbuf_realizedollsym(at->a_w.w_symbol, + argc, argv, 0))) { error("$%s: not enough arguments supplied", at->a_w.w_symbol->s_name); @@ -554,7 +555,7 @@ void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv) } break; case A_DOLLSYM: - s9 = realizedollsym(at->a_w.w_symbol, argc, argv, + s9 = binbuf_realizedollsym(at->a_w.w_symbol, argc, argv, target == &pd_objectmaker); if (!s9) goto broken; diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h index c6ddb0c9..5e8b20bb 100644 --- a/pd/src/m_pd.h +++ b/pd/src/m_pd.h @@ -6,6 +6,8 @@ extern "C" { #endif +#define PD_VERSION 0.36 + #ifdef NT // #pragma warning( disable : 4091 ) #pragma warning( disable : 4305 ) /* uncast const double to float */ @@ -283,6 +285,8 @@ EXTERN int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname, EXTERN int binbuf_write(t_binbuf *x, char *filename, char *dir, int crflag); EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); +EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, + int tonew); /* ------------------ clocks --------------- */ diff --git a/pd/src/makefile.nt b/pd/src/makefile.nt index 91d34051..4a301882 100644 --- a/pd/src/makefile.nt +++ b/pd/src/makefile.nt @@ -1,6 +1,6 @@ # Makefile for portaudio ASIO driver version of PD -all: pd gui ..\bin\pd.tk +all: pd gui ..\bin\pd.tk ..\bin\pdsend.exe ..\bin\pdreceive.exe VC = "C:\Program Files\Microsoft Visual Studio\VC98" #VC="\Program Files\DevStudio\Vc" @@ -76,6 +76,14 @@ gui: ..\bin\pdtcl.dll ..\bin\pd.tk: u_main.tk; copy u_main.tk ..\bin\pd.tk +..\bin\pdsend.exe: u_pdsend.obj + link $(LFLAGS) /out:..\bin\pdsend.exe /INCREMENTAL:NO u_pdsend.obj \ + $(LIB) + +..\bin\pdreceive.exe: u_pdreceive.obj + link $(LFLAGS) /out:..\bin\pdreceive.exe /INCREMENTAL:NO u_pdreceive.obj \ + $(LIB) + # explicit rules to compile portaudio sources: pa_lib.obj: $(PADIR)\pa_common\pa_lib.c cl /c $(ALLCF) $(PADIR)\pa_common\pa_lib.c diff --git a/pd/src/notes.txt b/pd/src/notes.txt index 03a8728f..1f0937e8 100644 --- a/pd/src/notes.txt +++ b/pd/src/notes.txt @@ -1,32 +1,17 @@ -done for 0.35: -bug fix: flipping canvases -bug fix: canvas coordinates saved correctly -signal lines fatter than control ones -number box labels, send&receive -rework [vh]dial -'make install' depending on Pd, etc -undo -fix symbol binding GUI problem -new expr object -test on p4s -undo paste and duplicate -fix flipped window resizing to move comments +done for 0.36: ---------------- dolist -------------------- -pddp doc +pddp doc scheduler to do DSP computations even if no audio hook to scheduler to let others get called for DSP I/O figure out how to avoid "dac freeze" if nosound try again to fix the font scene -look at zeros to IIR filters -compile pdsend, pdreceive for windows and document somehow addcomma message to message pasting should look at current mouse location problems: -number boxes should ignore non-numeric characters messages & comments don't come up with text activated? arrays of non-existent templates crash don't draw in/outlets on gui objects in graphs diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c index b9f52d68..eed90b38 100644 --- a/pd/src/s_inter.c +++ b/pd/src/s_inter.c @@ -603,14 +603,22 @@ int sys_startgui(const char *guidir) char *homedir = getenv("HOME"), filename[250]; struct stat statbuf; if (!homedir || strlen(homedir) > 150) - goto nexttry; + goto nohomedir; + sprintf(filename, + "%s/Applications/Utilities/Wish shell.app/Contents/MacOS/Wish Shell", + homedir); + if (stat(filename, &statbuf) >= 0) + goto foundit; sprintf(filename, "%s/Applications/Wish shell.app/Contents/MacOS/Wish Shell", homedir); - if (stat(filename, &statbuf) >= 0) goto foundit; - nexttry: + nohomedir: + strcpy(filename, + "/Applications/Utilities/Wish Shell.app/Contents/MacOS/Wish Shell"); + if (stat(filename, &statbuf) >= 0) + goto foundit; strcpy(filename, "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell"); foundit: diff --git a/pd/src/s_mac.c b/pd/src/s_mac.c index 9d3c1543..941fa93c 100644 --- a/pd/src/s_mac.c +++ b/pd/src/s_mac.c @@ -321,7 +321,7 @@ void sys_set_priority(int higher) #else /* no priority scheduling, so renice and wish for something better */ int retval; errno = 0; - retval = setpriority(PRIO_PROCESS, 0, (higher? -20 : -19)); + retval = setpriority(PRIO_PROCESS, 0, (higher? 0 : -20)); if (retval == -1 & errno != 0) { perror("setpriority"); diff --git a/pd/src/s_main.c b/pd/src/s_main.c index cb08960a..fdf36772 100644 --- a/pd/src/s_main.c +++ b/pd/src/s_main.c @@ -7,7 +7,7 @@ * 1311:forum::für::umläute:2001 */ -char pd_version[] = "Pd version 0.36 PRELIMINARY TEST 5\n"; +char pd_version[] = "Pd version 0.36-0\n"; char pd_compiletime[] = __TIME__; char pd_compiledate[] = __DATE__; @@ -47,6 +47,7 @@ static t_symbol *sys_guidir; static t_namelist *sys_externlist; static t_namelist *sys_openlist; static t_namelist *sys_messagelist; +static int sys_version; int sys_nmidiout = 1; #ifdef NT @@ -241,8 +242,10 @@ int sys_main(int argc, char **argv) #endif if (sys_argparse(argc, argv)) return (1); /* parse cmd line */ sys_addextrapath(); - if (sys_verbose) fprintf(stderr, "%s compiled %s %s\n", + if (sys_verbose || sys_version) fprintf(stderr, "%scompiled %s %s\n", pd_version, pd_compiletime, pd_compiledate); + if (sys_version) /* if we were just asked our version, exit here. */ + return (0); /* open audio and MIDI */ sys_open_midi(sys_nmidiin, sys_midiindevlist, sys_nmidiout, sys_midioutdevlist); @@ -322,6 +325,7 @@ static char *(usagemessage[]) = { "-lib <file> -- load object library(s)\n", "-font <n> -- specify default font size in points\n", "-verbose -- extra printout on startup and when searching for files\n", +"-version -- don't run Pd; just print out which version it is \n", "-d <n> -- specify debug level\n", "-noloadbang -- suppress all loadbangs\n", "-nogui -- suppress starting the GUI\n", @@ -609,6 +613,11 @@ int sys_argparse(int argc, char **argv) sys_verbose = 1; argc--; argv++; } + else if (!strcmp(*argv, "-version")) + { + sys_version = 1; + argc--; argv++; + } else if (!strcmp(*argv, "-d") && argc > 1 && sscanf(argv[1], "%d", &sys_debuglevel) >= 1) { diff --git a/pd/src/t_tkcmd.c b/pd/src/t_tkcmd.c index 3415691b..a862beda 100644 --- a/pd/src/t_tkcmd.c +++ b/pd/src/t_tkcmd.c @@ -168,7 +168,11 @@ void pdgui_setupsocket(void) { struct sockaddr_in server; struct hostent *hp; - +#ifdef UNIX + int retry = 10; +#else + int retry = 1; +#endif #ifdef NT short version = MAKEWORD(2, 0); WSADATA nobby; @@ -197,14 +201,41 @@ void pdgui_setupsocket(void) server.sin_port = htons((unsigned short)portno); /* try to connect */ - if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) - pdgui_sockerror("connecting stream socket"); - + while (1) + { + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) >= 0) + goto gotit; + retry--; + if (retry <= 0) + break; + /* In UNIX there's a race condition; the child won't be + able to connect before the parent (pd) has shed its + setuid-ness. In case this is the problem, sleep and + retry. */ + else + { +#ifdef UNIX + fd_set readset, writeset, exceptset; + struct timeval timout; + + timout.tv_sec = 0; + timout.tv_usec = 100000; + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + fprintf(stderr, "retrying connect...\n"); + if (select(1, &readset, &writeset, &exceptset, &timout) < 0) + perror("select"); +#endif /* UNIX */ + } + } + pdgui_sockerror("connecting stream socket"); +gotit: ; #ifdef UNIX /* in unix we ask TK to call us back. In NT we have to poll. */ Tk_CreateFileHandler(sockfd, TK_READABLE | TK_EXCEPTION, pd_readsocket, 0); -#endif +#endif /* UNIX */ } /**************************** commands ************************/ diff --git a/pd/src/u_main.tk b/pd/src/u_main.tk index da17b720..9af14282 100644 --- a/pd/src/u_main.tk +++ b/pd/src/u_main.tk @@ -159,19 +159,11 @@ proc menu_new {} { proc menu_open {} { global pd_opendir - global pd_nt -# workaround -- initialdir doesn't work on MACOSX yet --- - if {$pd_nt == 2} { - cd $pd_opendir - set filename [tk_getOpenFile -defaultextension .pd \ - -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} ] - } else { - set filename [tk_getOpenFile -defaultextension .pd \ - -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} \ - -initialdir $pd_opendir] - } -# puts stderr $filename + set filename [tk_getOpenFile -defaultextension .pd \ + -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} \ + -initialdir $pd_opendir] + if {$filename != ""} { set directory [string range $filename 0 \ [expr [string last / $filename ] - 1]] @@ -242,17 +234,10 @@ set help_directory $pd_guidir/doc proc menu_documentation {} { global help_directory - global pd_nt - if {$pd_nt == 2} { - cd $help_directory - set filename [tk_getOpenFile -defaultextension .pd \ - -filetypes { {{documentation} {.pd .txt .htm}} } ] - } else { - set filename [tk_getOpenFile -defaultextension .pd \ - -filetypes { {{documentation} {.pd .txt .htm}} } \ - -initialdir $help_directory] - } + set filename [tk_getOpenFile -defaultextension .pd \ + -filetypes { {{documentation} {.pd .txt .htm}} } \ + -initialdir $help_directory] if {$filename != ""} { if {[string first .txt $filename] >= 0} { @@ -983,9 +968,9 @@ proc pdtk_canvas_keyup {name key iso} { # puts stderr [concat up key= $key iso= $iso] if {$iso != ""} { scan $iso %c keynum - pd [canvastosym $name] key 0 $keynum \; + pd [canvastosym $name] key 0 $keynum 0 \; } else { - pd [canvastosym $name] key 0 $key \; + pd [canvastosym $name] key 0 $key 0 \; } } @@ -1063,17 +1048,10 @@ set saveas_dir nowhere ############ pdtk_canvas_saveas -- run a saveas dialog ############## proc pdtk_canvas_saveas {name initfile initdir} { - global pd_nt - if {$pd_nt == 2} { - cd $initdir - set filename [tk_getSaveFile -initialfile $initfile \ - -defaultextension .pd \ - -filetypes { {{pd files} {.pd}} {{max files} {.pat}} }] - } else { - set filename [tk_getSaveFile -initialfile $initfile \ - -initialdir $initdir -defaultextension .pd \ - -filetypes { {{pd files} {.pd}} {{max files} {.pat}} }] - } + set filename [tk_getSaveFile -initialfile $initfile \ + -initialdir $initdir -defaultextension .pd \ + -filetypes { {{pd files} {.pd}} {{max files} {.pat}} }] + if {$filename != ""} { set directory [string range $filename 0 \ [expr [string last / $filename ] - 1]] @@ -1184,9 +1162,14 @@ proc gatom_escape {sym} { set ret [string replace $sym 0 0 "--"] # puts stderr [concat escape $sym $ret] } else { - set ret $sym -# puts stderr [concat escape $sym "no change"] - } + if {[string equal -length 1 $sym "$"]} { + set ret [string replace $sym 0 0 "#"] +# puts stderr [concat unescape $sym $ret] + } else { + set ret $sym +# puts stderr [concat escape $sym "no change"] + } + } } concat $ret } @@ -1196,8 +1179,13 @@ proc gatom_unescape {sym} { set ret [string replace $sym 0 0 ""] # puts stderr [concat unescape $sym $ret] } else { - set ret $sym -# puts stderr [concat unescape $sym "no change"] + if {[string equal -length 1 $sym "#"]} { + set ret [string replace $sym 0 0 "$"] +# puts stderr [concat unescape $sym $ret] + } else { + set ret $sym +# puts stderr [concat unescape $sym "no change"] + } } concat $ret } @@ -1221,7 +1209,7 @@ proc dogatom_apply {id} { global $var_gatomsymto # set cmd [concat $id param $gatomwidth $gatomlo $gatomhi \;] - + set cmd [concat $id param \ [eval concat $$var_gatomwidth] \ [eval concat $$var_gatomlo] \ @@ -2596,7 +2584,7 @@ proc texteditor_send {name} { {incr i 1} { set cha [$name get [concat 0.0 + $i chars]] scan $cha %c keynum - pd [concat pd key 1 $keynum \;] + pd [concat pd key 1 $keynum 0 \;] } } @@ -2639,14 +2627,8 @@ proc pdtk_pd_texteditor {stuff} { proc pdtk_openpanel {target} { global pd_opendir - global pd_nt - if {$pd_nt == 2} { - cd $pd_opendir - set filename [tk_getOpenFile ] - } else { - set filename [tk_getOpenFile \ - -initialdir $pd_opendir] - } + set filename [tk_getOpenFile \ + -initialdir $pd_opendir] if {$filename != ""} { set directory [string range $filename 0 \ [expr [string last / $filename ] - 1]] diff --git a/pd/src/u_pdreceive.c b/pd/src/u_pdreceive.c index 8d3f83e9..c01be01c 100644 --- a/pd/src/u_pdreceive.c +++ b/pd/src/u_pdreceive.c @@ -6,14 +6,14 @@ from Pd via the netsend/netreceive ("FUDI") protocol, and copies them to standard output. */ -#include <sys/time.h> #include <sys/types.h> #include <string.h> #include <stdio.h> #include <errno.h> -#include <unistd.h> #include <stdlib.h> #ifdef UNIX +#include <sys/time.h> +#include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> @@ -38,7 +38,7 @@ static int sockfd; static int protocol; static void sockerror(char *s); -static void closesocket(int fd); +static void x_closesocket(int fd); static void dopoll(void); #define BUFSIZE 4096 @@ -47,6 +47,10 @@ int main(int argc, char **argv) int portno; struct sockaddr_in server; int nretry = 10; +#ifdef NT + short version = MAKEWORD(2, 0); + WSADATA nobby; +#endif if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0) goto usage; if (argc >= 3) @@ -58,6 +62,9 @@ int main(int argc, char **argv) else goto usage; } else protocol = SOCK_STREAM; +#ifdef NT + if (WSAStartup(version, &nobby)) sockerror("WSAstartup"); +#endif sockfd = socket(AF_INET, protocol, 0); if (sockfd < 0) { @@ -82,7 +89,7 @@ int main(int argc, char **argv) if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { sockerror("bind"); - closesocket(sockfd); + x_closesocket(sockfd); return (0); } if (protocol == SOCK_STREAM) @@ -90,7 +97,7 @@ int main(int argc, char **argv) if (listen(sockfd, 5) < 0) { sockerror("listen"); - closesocket(sockfd); + x_closesocket(sockfd); exit(1); } } @@ -132,7 +139,7 @@ static void rmport(t_fdpoll *x) { if (fp == x) { - closesocket(fp->fdp_fd); + x_closesocket(fp->fdp_fd); free(fp->fdp_inbuf); while (i--) { @@ -164,16 +171,22 @@ static void udpread(void) if (ret < 0) { sockerror("recv (udp)"); - close(sockfd); + x_closesocket(sockfd); exit(1); } else if (ret > 0) { +#ifdef UNIX if (write(1, buf, ret) < ret) { perror("write"); exit(1); } +#else + int j; + for (j = 0; j < ret; j++) + putchar(buf[j]); +#endif } } @@ -196,8 +209,16 @@ static int tcpmakeoutput(t_fdpoll *x) if (inbuf[intail] == '\n') intail = (intail+1)&(BUFSIZE-1); *bp++ = '\n'; - write(1, messbuf, bp - messbuf); - x->fdp_inhead = inhead; +#ifdef UNIX + write(1, messbuf, bp - messbuf); +#else + { + int j; + for (j = 0; j < bp - messbuf; j++) + putchar(messbuf[j]); + } +#endif + x->fdp_inhead = inhead; x->fdp_intail = intail; return (1); } @@ -207,7 +228,6 @@ static int tcpmakeoutput(t_fdpoll *x) static void tcpread(t_fdpoll *x) { - char *semi; int readto = (x->fdp_inhead >= x->fdp_intail ? BUFSIZE : x->fdp_intail-1); int ret; @@ -294,7 +314,7 @@ static void sockerror(char *s) fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); } -static void closesocket(int fd) +static void x_closesocket(int fd) { #ifdef UNIX close(fd); diff --git a/pd/src/u_pdsend.c b/pd/src/u_pdsend.c index 87e7150d..896c5646 100644 --- a/pd/src/u_pdsend.c +++ b/pd/src/u_pdsend.c @@ -9,9 +9,9 @@ from its standard input to Pd via the netsend/netreceive ("FUDI") protocol. */ #include <string.h> #include <stdio.h> #include <errno.h> -#include <unistd.h> #include <stdlib.h> #ifdef UNIX +#include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> @@ -21,7 +21,7 @@ from its standard input to Pd via the netsend/netreceive ("FUDI") protocol. */ #endif void sockerror(char *s); -void closesocket(int fd); +void x_closesocket(int fd); #define BUFSIZE 4096 int main(int argc, char **argv) @@ -31,6 +31,10 @@ int main(int argc, char **argv) struct hostent *hp; char *hostname; int nretry = 10; +#ifdef NT + short version = MAKEWORD(2, 0); + WSADATA nobby; +#endif if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0) goto usage; if (argc >= 3) @@ -45,6 +49,9 @@ int main(int argc, char **argv) else goto usage; } else protocol = SOCK_STREAM; +#ifdef NT + if (WSAStartup(version, &nobby)) sockerror("WSAstartup"); +#endif sockfd = socket(AF_INET, protocol, 0); if (sockfd < 0) @@ -58,7 +65,7 @@ int main(int argc, char **argv) if (hp == 0) { fprintf(stderr, "%s: unknown host\n", hostname); - closesocket(sockfd); + x_closesocket(sockfd); exit(1); } memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); @@ -81,7 +88,7 @@ int main(int argc, char **argv) goto connected; sockerror("connect"); } - closesocket(sockfd); + x_closesocket(sockfd); exit(1); connected: ; #else @@ -89,7 +96,7 @@ connected: ; if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { sockerror("connect"); - closesocket(sockfd); + x_closesocket(sockfd); exit(1); } #endif @@ -139,7 +146,7 @@ void sockerror(char *s) fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); } -void closesocket(int fd) +void x_closesocket(int fd) { #ifdef UNIX close(fd); diff --git a/pd/src/z.pd b/pd/src/z.pd index a415176c..22c5b0d9 100644 --- a/pd/src/z.pd +++ b/pd/src/z.pd @@ -1,31 +1,9 @@ -#N canvas 176 287 548 368 12; -#X obj 51 82 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 --1 0; -#X obj 110 93 vdl 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 -1 -0; -#X obj 78 240 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 --1 0; -#X floatatom 149 52 5 0 0 0 - - -; -#X floatatom 240 177 5 0 0 0 - - -; -#X obj 361 97 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 --1 0; -#X floatatom 374 257 5 0 0 0 - - -; -#X obj 252 280 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10 --262144 -1 -1 0 256; -#X floatatom 478 73 5 0 0 0 receive asd -; -#X floatatom 241 241 5 0 0 0 - - -; -#X floatatom 477 102 5 0 0 0 send - fgdfh; -#X floatatom 474 141 5 0 0 0 both wer rty; -#X obj 242 120; -#X floatatom 287 111 5 0 0 0 - - -; -#X obj 186 116 + 5; -#X obj 172 311 expr~ $v1 + 4; -#X connect 3 0 1 0; -#X connect 3 0 0 0; -#X connect 3 0 5 0; -#X connect 3 0 14 0; -#X connect 4 0 2 0; -#X connect 5 0 6 0; -#X connect 9 0 7 0; -#X connect 14 0 4 0; -#X coords 0 0 1 1 300 200 1; +#N canvas 0 0 450 300 10; +#X obj 111 114 +~; +#X obj 112 56 +~; +#N canvas 281 44 450 300 foo 1; +#X obj 111 114 +~; +#X obj 112 56 +~; +#X connect 1 0 0 0; +#X restore 206 67 pd foo; +#X connect 1 0 0 0; diff --git a/pd/src/z2.pd b/pd/src/z2.pd index cd1e0745..c49a070f 100644 --- a/pd/src/z2.pd +++ b/pd/src/z2.pd @@ -1,25 +1,3 @@ -#N canvas 176 287 548 368 12; -#X obj 51 82 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 --1 0; -#X obj 150 82 vdl 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 -1 -0; -#X obj 78 240 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 --1 0; -#X floatatom 149 52 5 0 0 0 - - -; -#X floatatom 244 182 5 0 0 0 - - -; -#X obj 361 97 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 --1 0; -#X floatatom 374 257 5 0 0 0 - - -; -#X obj 256 285 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10 --262144 -1 -1 0 256; -#X floatatom 478 73 5 0 0 0 receive asd -; -#X floatatom 245 246 5 0 0 0 - - -; -#X floatatom 475 156 5 0 0 0 send weghg -; -#X floatatom 466 220 5 0 0 0 both - rty; -#X connect 3 0 1 0; -#X connect 3 0 0 0; -#X connect 3 0 5 0; -#X connect 4 0 2 0; -#X connect 5 0 6 0; -#X connect 9 0 7 0; -#X coords 0 0 1 1 300 200 1; +#N canvas 0 0 450 300 10; +#X obj 114 57 1; +#X obj 111 114 1; diff --git a/pd/src/z3.pd b/pd/src/z3.pd index 76e698de..837a79c0 100644 --- a/pd/src/z3.pd +++ b/pd/src/z3.pd @@ -1,14 +1,17 @@ -#N canvas 409 180 735 452 12; -#X floatatom 267 57 0 0 0 1 input-dB - -; -#X obj 267 82 r tuning; -#X obj 267 129 r rest; -#X obj 267 35 r in-DB; -#X floatatom 267 104 0 0 0 1 tuning - -; -#X obj 267 10 tgl 20 0 meters set-meters meters 24 8 192 12 -262144 --1 -1 0 1; -#X obj 267 152 tgl 20 0 empty empty rest 24 8 0 12 -262144 -1 -1 0 +#N canvas 0 0 450 300 10; +#X floatatom 87 39 5 0 0 0 - - -; +#X floatatom 294 40 5 0 0 0 - - -; +#X obj 88 75 vdl 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 -1 1; -#X connect 1 0 4 0; -#X connect 2 0 6 0; -#X connect 3 0 0 0; -#X coords 0 0 1 1 300 200 1; +#X obj 294 69 hdl 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 -1 +0; +#X floatatom 88 206 5 0 0 0 - - -; +#X floatatom 295 207 5 0 0 0 - - -; +#X obj 172 121 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; +#X obj 175 91 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10 +-262144 -1 -1 0 256; +#X connect 0 0 2 0; +#X connect 1 0 3 0; +#X connect 2 0 4 0; +#X connect 3 0 5 0; |