aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pd/doc/1.manual/x1.htm6
-rw-r--r--pd/doc/1.manual/x5.htm37
-rw-r--r--pd/portaudio/pa_mac_core/notes.txt14
-rw-r--r--pd/portaudio/pa_mac_core/pa_mac_core.c1512
-rwxr-xr-xpd/src/configure4
-rw-r--r--pd/src/configure.in4
-rw-r--r--pd/src/g_all_guis.c53
-rw-r--r--pd/src/g_all_guis.h2
-rw-r--r--pd/src/g_array.c10
-rw-r--r--pd/src/g_canvas.c13
-rw-r--r--pd/src/g_canvas.h4
-rw-r--r--pd/src/g_editor.c10
-rw-r--r--pd/src/g_hdial.c46
-rw-r--r--pd/src/g_numbox.c4
-rw-r--r--pd/src/g_text.c65
-rw-r--r--pd/src/g_vdial.c2
-rw-r--r--pd/src/m_binbuf.c7
-rw-r--r--pd/src/m_pd.h4
-rw-r--r--pd/src/makefile.nt10
-rw-r--r--pd/src/notes.txt19
-rw-r--r--pd/src/s_inter.c14
-rw-r--r--pd/src/s_mac.c2
-rw-r--r--pd/src/s_main.c13
-rw-r--r--pd/src/t_tkcmd.c41
-rw-r--r--pd/src/u_main.tk82
-rw-r--r--pd/src/u_pdreceive.c42
-rw-r--r--pd/src/u_pdsend.c19
-rw-r--r--pd/src/z.pd40
-rw-r--r--pd/src/z2.pd28
-rw-r--r--pd/src/z3.pd29
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, &currentRusage ) == 0 )
{
- usecsElapsed = SubtractTime_AminusB( &currentRusage.ru_utime, &pahsc->pahsc_EntryRusage.ru_utime );
+ usecsElapsed = SubtractTime_AminusB( &currentRusage.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;