TweetFollow Us on Twitter

Inter-Computer Coordination

Volume Number: 13 (1997)
Issue Number: 11
Column Tag: Programming Techniques

High-Speed Inter-Computer Coordination

by Dr. Scott B. Steinman

Combining C++, FrontierScript, LabVIEW and multiple Macs to solve a real world problem


A few years ago, I was asked to put together an infant vision clinic that would offer not only state of the art tests for assessing the function and health of an infant's visual system, but which would also be fast, easily operated and flexible enough to readily allow future expansion. It didn't take much thinking to come to the conclusion that only the Macintosh could do it. But the programming was far from straightforward at the time, since some of the system software that I needed had only just become available and was not well documented.

This article will discuss the specific programming issues that had to be solved in order to achieve the design goals of the clinic, as well as many of the workarounds that had to be made. These issues also apply to other medical, scientific and engineering tasks. We'll see that sometimes a seemingly more roundabout design actually produces a simpler, faster and more flexible solution.

Design Decisions

The infant vision tests typically involved displaying a rapidly animated visual stimulus that moved targets on a screen or changed the target's contrast dynamically. At the same time, physiological data such as eye movement recordings or evoked potential recordings ("brainwaves") have to be obtained in response to the stimulus. Both tasks need to be executed rapidly -- the animation may be run at refresh rates up to 60 Hz, while the data acquisition may occur at up to 1000 Hz.

While speed and flexibility are always two important design goals, the system had additional restrictions that made these goals even more critical. First, the time required to conduct the full test sequence needed to be extremely short since our subjects were very young infants whose attention span is limited in duration. The examiner has to collect data extremely rapidly on any given test, then quickly proceed to the next test. Second, the same examiner who has to control the data acquisition also has the responsibility of directing the infant's attention to a visual stimulus. This not only reinforced the need for speed of testing, but also ease of operation. Finally, the software system needed to be modular and easily extended when new tests of infant vision were introduced.

I chose National Instruments' LabVIEW(tm) as the language in which to write the data recording and analysis code. LabVIEW is a true "visual" (that is, iconic) dataflow programming paradigm, much like Prograph CPX(tm). Visual programming allows rapid application development (RAD) in a fraction of the time required to write an equivalent C/C++ program (for example, see Steinman & Carver, 1995). LabVIEW also includes a huge library of data acquisition and data analysis code modules (called Virtual Instruments, or VIs for short) that may be used to build a recording system. In addition, LabVIEW's integrated user interface design tools provide a uniform intuitive "front panel" of controls that can be easily operated even by assistants.

Unfortunately, while LabVIEW is well suited for laboratory data acquisition and analysis, it is not capable of performing acquisition and analysis while simultaneously generating rapid animated visual stimulus displays. One solution that was considered was to call Macintosh Toolbox and QuickDraw routines via external C language external code modules, but these graphics routines could not be called in parallel with the data acquisition VIs. Even if these routines were called in separate programs, running all of these tasks on a single machine could still be risky since processor interrupts of the data acquisition hardware and those of the visual stimulus animation code could potentially interfere with each other -- that is, data samples could be "skipped" during graphics operations or graphics could "shear" when data samples are acquired.

We therefore chose to implement the system on two Macintosh Quadra computers -- one solely for recording and the other solely for stimulus display. This would allow each computer to perform its single specialized task at maximum speed without interfering with each other.

Both the recording program on one computer and the stimulus program on the other computer had to "know" what the other was doing at any given moment. Their operations needed to be tightly synchronized if the two-computer system were to act as a single unit. The overall operation of the laboratory software would be controlled by the examiner on the recording computer, and the second computer would act as a "slave" to the recording computer. This computer would act only in response to commands sent to it by the "master" recording computer.

This article will discuss the specific programming issues that had to be solved in order to achieve real-time inter-computer coordination while meeting the design goals of the clinic. I'll discuss many of the workarounds that needed to be made. These issues also apply to other medical, scientific and engineering tasks. We'll see that sometimes a seemingly more roundabout design actually produces a simpler, faster and more flexible solution.

Clearly, two forms of communication were required. Each would be executed independently. The first was to maintain tight control over the timing of the sequence of events during data acquisition. Digital I/O lines (NB-DIO-96, National Instruments) joined by a ribbon cable were used to transmit these signals.

The second form of communication was to transmit commands from the recording computer to the stimulus computer to "tell" it what stimulus to display and when. This would, of course, involve Apple events. These Apple events would be sent across an EtherTalk network cable that interconnected these two computers alone. In initial testing of the software system, it was found that LocalTalk was too slow. When Apple event commands were sent to the stimulus computer, the recording computer often had to wait for a reply that indicated that the command was received correctly. An Apple event could "time out" while waiting for this reply if the transmission time was too long, locking up the data acquisition when the recording computer kept waiting for the reply. The much quicker EtherTalk was the solution to this problem, as replies could be received fairly instantaneously.

Finally, two more decisions related to programming language had to be made. The first was which programming language to use for the stimulus display software. LabVIEW would not do. While LabVIEW has some capabilities for sending Apple events (with provisos to be mentioned below), it has extremely limited abilities to respond to Apple events. C++, on the other hand, is well suited for writing Apple event handlers, but also allows for rapid interrupt code for accurately-timed stimulus display animation (see Steinman and Nawrot, or Steinman for such graphics techniques).

Although Apple events would be involved in the inter-program communication between the data acquisition and stimulus display computers, they could not be used in a uniform manner throughout the software due to limitations in LabVIEW's inter-program communication code. One possible solution to this problem would be to use AppleScript, but for several reasons to be discussed below, inter-program communication was implemented with the UserLand Frontier(tm) scripting language (now available free at

It could be argued that the ethernet connection could be replaced by a simple high baud rate serial line that carried messages from one computer to the other. However, there is one disadvantage to doing so. In the current system, both the LabVIEW recording program and the C++ stimulus display program are not aware of whether they reside on two computers or on a single computer. One side effect of using Frontier as an intermediary for passing commands is that Frontier hides these details from the LabVIEW and C++ programs. This means that if the present stimulus generation and recording system could be implemented on a single computer in the future, all that would need to be done is to edit one subset of Frontier scripts -- neither the recording software nor stimulus display software would need rewriting. If a serial line had been used instead, all of the inter-program communication code would need to be completely rewritten.

Software System Components

For a better understanding of the workings of the software, the general infant testing sequence is as follows: The main program establishes the connection between the recording and stimulus computers across the EtherTalk network. The digital I/O lines are then initialized. At this point, the examiner selects a test to be run.

A command is then transmitted to the stimulus computer to launch a particular stimulus display program, whose stimulus parameters are transmitted to the display program by a second command. The examiner then clicks a button on the recording computer screen to start recording. A digital signal is sent to the stimulus computer to enable the stimulus animation, and the stimulus computer returns a digital signal to trigger data acquisition, time-locked to the stimulus display. After data collection is completed, commands are sent to the stimulus computer to stop the stimulus presentation, then quit the stimulus display program.

The software system has been designed to be both flexible and modular. It is composed of three major components: The first, implemented in LabVIEW(tm) on the recording computer, initiates an Apple event link between the two computers, initializes the data acquisition and digital I/O boards, allows the examiner to select which to run and specification of stimulus and recording parameters, as well as starting, pausing or halting data acquisition. A uniform user interface across all tests simplifies operation of the tests.

Visual stimuli are presented via the second component, a collection of very small programs written in Metrowerks CodeWarrior C++ and Mathemaesthetics Resorcerer(tm). These programs take advantage of a reusable code library of drawing and animation routines that allows both palette animation (see Baro) and frame animation (see Steinman and Nawrot, or Steinman). These programs are small because they perform only three chores: (a) receive Apple event commands, (b) display animated graphics, and (c) read and write to the digital I/O lines for synchronization with the recording software.

The third component coordinates the stimulus generation and data acquisition via Apple Open Scripting Architecture-compatible scripts in the Frontier(tm) scripting language.

Inter-Program Communication Problems and Solutions

With a combination of different programming tools, several problems that are specific to real-time simultaneous visual stimulation and data acquisition have been solved. Specifically, we will present an easy way to synchronize two Macintosh computers to work as a single laboratory device, via software commands and hardware signals passed between.

The first problem which we had to confront and solve dealt with shortcomings in LabVIEW's capacities for inter-program communication. While Apple event support is included in LabVIEW, it is mostly specialized as VIs that are used to execute other VIs, such as AESend Run VI, AESend Open, Run, Close VI, AESend Close VI, AESend Abort VI and AESend VI Active?, or responses to these commands. For programs composed of code other than its own VIs, LabVIEW is more capable of responding to commands than sending them. A few other Apple event-related VIs exist, which are geared towards starting or quitting other programs, but these are not sufficient for laboratory program control.

Fortunately, a LabVIEW VI that is often overlooked by programmers just happens to be the one VI that is critical for controlling the operation of the stimulus graphics programs by the recording computer. This VI is called AESend Do Script. As its name implies, it is specialized towards sending scripts. When such scripts are received by a target program, their text must be decoded into a series of instructions to be carried out by that program.

The next decision was to choose a specific scripting language. AppleScript presented several obstacles. The first is that while AppleScript is relatively slow, even on PowerPC-based Macintoshes. The second problem is that AppleScript was not primarily designed for sending commands quickly over a network to a second computer. To send an AppleScript or Apple event across a network, the PPC Browser must be invoked. The PPC Browser is intended to allow users to choose which computer and application should be sent a command, but it also has one shortcoming for our purposes. It provides a level of security via the User Identity dialog box. While this is useful for preventing unwanted connections across the Internet, it is a severe impediment to our design goals. Every time we need to send an AppleScript or individual Apple event to each stimulus display program, we will be faced with the PPC Browser and User Identity dialog boxes! This is not only disruptive to the examiner using the software, but will also slow down our data collection.

A third problem is that if only AppleScripts are transmitted to the stimulus computer to initiate a stimulus display, these stimulus programs must be capable of receiving and parsing the AppleScripts. This presents a very difficult task for several reasons: (1) AppleScript programming is not entirely intuitive. (2) AppleScript programming requires programming knowledge about 'aete' resources that identify the Apple events "understood" by the receiving program. (3) In the present software system, this would require adding AppleScript support to each of a dozen small stimulus display programs. While adding AppleScript is a fruitful option for single large commercial programs, the time and effort required to add AppleScript support to several small specialized laboratory programs is simply not cost effective. We have selected an alternative that is simpler, yet overcomes many of the limitations of AppleScript.

That alternative is UserLand Frontier(tm). Frontier can transmit sequences of Apple events across a network at least ten times more rapidly than AppleScript scripts, and does not require the creation of 'aete' resources.

In our system, Frontier is installed on both the recording computer and the stimulus computer. When a command must be sent from the LabVIEW program on the recording computer to a C++ visual display program on the stimulus computer, it is first sent as a script from LabVIEW to a "master" copy of Frontier on the recording computer. This Frontier application is "told" to run a script stored within its Object Database that essentially transmits the command contained in the LabVIEW script across the Ethernet network to a "slave" copy of Frontier on the second computer. This second copy of Frontier translates the command into Apple event format and relays the command to the stimulus program, where the command is carried out.

Why use such a circuitous route? Why not just send scripts directly from the LabVIEW recording program to the stimulus display program on the second computer? Two reasons have already been mentioned: (1) Frontier speeds up the transmission of commands across the network, and (2) to avoid the need to add AppleScript-parsing code to each stimulus program. Let us add a to more important reasons to use Frontier as an intermediary in passing along commands: (3) Frontier simplifies our programming task by translating the original command from LabVIEW's Do Script VI, which is in textual script format, into a form that can be handled with simple code in the stimulus programs -- Apple event handlers. (4) Because Frontier itself sends the commands across the ethernet network, the intrusive PPC Browser and User Identity dialog boxes are avoided during the time-critical portions of data collection.

When the LabVIEW recording program must find a program on the stimulus computer to which to "connect" and send commands, it connects to the "master" copy of Frontier on its own machine. Similarly, the "slave" copy of Frontier on the stimulus computer connects to stimulus programs that reside on its own computer.. These two communication paths do not invoke the PPC Browser or User Identity dialog boxes, since each connection between programs is made within a single machine. The connection between the two machines is made by Frontier before the LabVIEW program is executed. Before launching the LabVIEW recording program, a small Frontier script is run on the recording Macintosh that results in the "master" copy of Frontier on that Macintosh establishing a connection across the network to the "slave" copy of Frontier on the stimulus Macintosh and telling that computer to do something trivial such as sounding a beep. This is the only time at which the PPC Browser and User Identity dialog boxes appear. During all subsequent experimental testing, these dialog boxes never reappear even when the experimenter switches between test types and stimulus types, because the connection across the network has already been established from one copy of Frontier to the other.

Let's examine the Frontier code that sets up the inter-computer communication across the network. Listing 1 is a Frontier script entitled testConnect that first establishes the network connection between the two Macintoshes prior to the launching of the LabVIEW recording program. Each copy of Frontier contains an Object Database of frequently-used scripts and data that may be thought of as the commands that Frontier itself can execute, send or receive, as well as parameters for those commands. The testConnect script is stored in the Object Database of Frontier on the recording computer. The testConnect script simply sends a command to the stimulus computer to sound a beep. Because this is the first script to be transmitted across the EtherTalk network, it forces the PPC Browser and User Identity dialog boxes to be displayed at this time. This is beneficial because data acquisition hasn't initiated yet, so these dialogs cannot slow us the test sequence.

Listing 1: testConnect script


Establishes interconnection with copy of Frontier on second computer. This Frontier script on the "master" recording computer transmits a trivial script to sound a beep to Frontier on the "slave" stimulus computer to force the display of the PPC Browser and User Identity dialog boxes prior to test parameter selection and data acquisition. This script is simpler than that of Listing 3 because no parameters are transmitted with the "stimulus.beep" command.

© 1997 by Scott B. Steinman. All rights reserved.

on testConnect()
    Create local variable named "netScriptStr" to construct
    text of new script

  local (netScriptStr)

    Script will instruct stimulus computer to run script
    named "speaker.beep" in Object Database of copy of
    Frontier on stimulus computer

  netScriptStr = "speaker.beep()"

    Construct script command:
    1. Create new script at location "scratchpad.netScript"
    in Object Database of Frontier on recording computer.
    This script will be to be sent to stimulus computer
    2. Tell Frontier that next few operations will work on
    the newly-created script by setting target of Frontier's
    operations to that location
    3. Clear contents at location "scratchpad.netScript"
    4. Set contents of location "scratchpad.netScript" to
    text contained in netStringStr
    5. Reset Frontier operation target to the testConnect 
  new (scriptType,@scratchpad.netScript)
  target.set (@scratchpad.netScript)
  op.wipe()     Clear current contents of netScript
  op.setLineText (netScriptStr)
  target.clear ()

    Send script command stored at "scratchpad.netScript"
    across network to "slave" stimulus computer

  NetFrontier.runScript (@scratchpad.netScript,true)
    Clean up

  delete (@scratchpad.netScript)

The testConnect script starts by creating a variable named netScriptStr to contain the text of the command to be sent to the "slave" copy of Frontier. This string is simply the name of a Frontier script ("speaker.beep") within the stimulus computer's Frontier Object Database that we want executed. NetFrontier has been designed to transmit scripts stored at a specific locations in the Object Database rather than transmitting raw text strings, so we need to transfer this command string into a new script, then transmit that script. In order to do this, we create a temporary entry named "netScript" in the recording computer's Object Database's "scatchpad" area to contain this text (its Frontier data type is, appropriately enough, scriptType). We then must copy the text of the script ("speaker.beep") from the netScriptStr string into the newly created script. We are now ready to transmit the contents of this newly-created script text across the network with NetFrontier's runScript command.

How is this testConnect script called to perform these actions if the LabVIEW recording program isn't running yet? Along with its own internal scripts in the Object Database, Frontier can create stand-alone, double-clickable scripts. A stand-alone script called ConnectMacs is executed by the experimenter before launching the LabVIEW recording program. It calls the testConnect script.

Experienced Frontier programmers might notice that we have added a second parameter to the runScript NetFrontier script to allow the option of waiting or not waiting for a reply during transmission of the script to the stimulus computer. This is because by default the Apple event Frontier command that sends information from Frontier to other applications waits for a reply from the receiving application. When we send commands to show visual stimuli just as we are about to begin data acquisition, we cannot afford the luxury of waiting for an Apple event reply, as this would delay the onset of the data acquisition. It is therefore imperative to avoid a reply by using Frontier's finderEvent command instead of Apple event to send the information; finderEvent by default does not wait for a response. This command was originally intended for sending scripts from Frontier to the Macintosh System 7's scriptable Finder, but it also suites our purposes well. The modified script is shown in Listing 2.

Listing 2: runScript script


Modified runScript Frontier script. This Frontier script, part of the NetFrontier suite, sends a script across a network to control a second Macintosh computer. It has been modified to allow the option of waiting for an Apple event reply or ignoring the reply.

© 1997 by UserLand & Scott B. Steinman. All rights reserved.

on runScript (adr,waitReply)
  on callback (netAddress)
    local (data,val)
    pack (adr^,@data)
    if waitReply equals true  
        Wait for reply from Apple event
        (by default, all Apple event transmissions produce
        reply in Frontier)

      if not AppleEvent (netAddress,'netf','inst',1,
        return (false)
      val = AppleEvent (netAddress,'netf','runs',1,
        finderEvent command sends Apple event but 
        ignores reply

      if not finderEvent (netAddress,'netf','inst',1,
        return (false)
      val = finderEvent(netAddress,'netf','runs',1,
      return (val)

    NetFrontier.buddyLoop (@callback)

Once the inter-computer connection is made, we launch the LabVIEW recording program and set stimulus and recording parameters prior to data collection. Setting stimulus parameters requires sending commands and data across the EtherTalk network, as dictated by scripts sent from LabVIEW to Frontier within the recording computer. But LabVIEW's script transmission VIs expect to be sending commands by connecting to another program across a network, and we don't want that to occur. We want LabVIEW to communicate only with Frontier on the same computer, and let Frontier handle the communication across the network. We therefore have to force LabVIEW to establish a "connection" directly to Frontier within the recording computer. Figure 1 displays LabVIEW's graphical code for locating this particular copy of the Frontier application, then sending a small test script to Frontier to confirm that the connect was made properly.

Figure 1. LabVIEW ConnectMacs VI.

The Find Frontier VI in Figure 2 takes advantage of a LabVIEW PPC Toolkit VI named Get Target ID, which receives the name of the application to find and returns the its target ID, a LabVIEW structure (or "cluster" in LabVIEW terminology) that stores the location of a program on a network. We restrict the search to the copy of Frontier on the same machine hosting the LabVIEW recording program. The target ID returned by this search is used in all subsequent command transmissions to Frontier.

Figure 2. LabVIEW Find Frontier VI.

Now let's discuss the specific programming steps required to send commands and data from the LabVIEW recording program to their real target--the C++ stimulus display programs. In the example code to follow, the runStimulus command will be explained. This code is used once recording and stimulus parameters have been chosen by the examiner and we are ready to initiate data acquisition in response to a visual stimulus. The LabVIEW program initializes its data acquisition VIs, then sends a runStimulus command containing stimulus settings in the form of an Apple event to Frontier. Frontier then repackages the text contained within this Apple event into a Frontier script and transmits it. Upon receipt of the restructured script, the stimulus Macintosh's copy of Frontier executes the script, extracts the parameters of the script, and sends both a command and the parameters in an Apple event to the stimulus display program. An Apple event handler in that program retrieves the stimulus settings from the Apple event parameters, and displays the stimulus after sending a sync signal via the DIO lines to let the recording Macintosh know that it is time to start acquiring data. The net result is that the stimulus program only needs to receive an Apple event and its parameters in an easy-to-extract format, rather than a textual AppleScript that requires complex code to decipher.

The first step in this process is handled by the LabVIEW recording program. Once stimulus parameters have been selected and the experimenter is ready to record data, the Send RunStim Dosc AE VI is called (Figure 3) to tell Frontier to execute a script that receives stimulus parameters and then ships them to the stimulus computer. The script to be executed on the recording computer is named Stimuli.PVEP.netRunStim, and it receives as its argument the text contained in the script transmitted by LabVIEW. The script text contains the name of the Frontier script to execute -- Stimuli.PVEP.netRunStim (a stimulus display program running a Pattern VEP visual stimulus) -- and an argument to that script containing the list of the values to which the stimulus parameters are to be set ("50 380 8").

Figure 3. LabVIEW RunStim Dosc AE VI.

The netRunStim Frontier script is shown in Listing 3. The purpose of this script is to relay the command specified within the LabVIEW script to the stimulus computer, that is, a command to prepare a stimulus display, along with the desired stimulus parameters received as a single string argument to the netRunStim script named "str". This is done by packaging the command and stimulus parameters into a form that may be transmitted across the network by NetFrontier.

Listing 3: netRunStim script


Frontier script within the recording computer's Object Database for interpreting the LabVIEW runStimulus script and forwarding it as a Frontier script to the stimulus computer. The command extracted from the LabVIEW script and the arguments for that command are transmitted separately by Frontier to the stimulus computer, using the NetFrontier.broadcast and NetFrontier.runScript commands, respectively. Returns true if successful, false otherwise

© 1997 by Scott B. Steinman. All rights reserved.

on netRunScript(str)

    Create local variable named "netScriptStr" to
    construct text of new script

  local (netScriptStr)
  netScriptStr = "Stimuli.PVEP.runStim()"

    Construct script command:
    1. Create new script at location "scratchpad.netScript"
    in Object Database of Frontier on recording computer.
    This script will be to be sent to stimulus computer
    2. Tell Frontier that next few commands will operate on
    the newly-created script by setting target of Frontier's
    operations to that location
    3. Clear contents at location "scratchpad.netScript"
    4. Set contents of location "scratchpad.netScript" to
    text contained in netStringStr
    5. Reset Frontier operation target to the testConnect 

  new (scriptType,@scratchpad.netScript)
  target.set (@scratchpad.netScript)
  op.wipe()     Clear current contents of netScript
  op.setLineText (netScriptStr)
  target.clear ()

    Construct script argument:
    1. Delete current contents at Object Database location
      "scratchpad.scriptArgument", if any
    2. Create new string at location 
    "scratchpad.scriptArgument" to   
    hold parameters transmitted with command to run
    "Stimuli.PVEP.runStim" script.

  if defined (scratchpad.scriptArgument)
    delete (@scratchpad.scriptArgument)
  new (stringType,@scratchpad.scriptArgument)

    Send argument to stimulus computer via 

  if not NetFrontier.broadcast (@scratchpad.netArgument)
    speaker.beep ()
    delete (@scratchpad.netScript)
    delete (@scratchpad.netArgument)
    return (false)

    Send script command to stimulus computer via

  if not NetFrontier.runScript (@scratchpad.netScript,false)
    speaker.beep ()
    delete (@scratchpad.netScript)
    delete (@scratchpad.netArgument)
    return (false)

    Clean up

  delete (@scratchpad.netScript)
  delete (@scratchpad.netArgument)
  return (true)

As in the testConnect script of Listing 1, The netRunStim script creates a local string variable to hold the text of the command that will be sent via NetFrontier to Frontier on the stimulus Macintosh. Remember that the purpose of NetFrontier is to direct a copy of Frontier on another computer to execute one of its own scripts. This is what netRunStim will do -- instruct Frontier on the stimulus Macintosh to execute a script named runStim contained in its own Object Database. A temporary script (containing the Frontier script to be executed on the stimulus computer) is constructed exactly as was done in Listing 1, but in this case the name of the script we are asking to execute on the stimulus computer is "Stimuli.PVEP.runStim". In other words, the runStim script resides in the stimulus computer's Frontier Object Database in its Stimuli.PVEP script table.

The next steps that the netRunStim script take are to send data to the stimulus computer -- data that specifies how the stimulus display will appear. This data forms the arguments to the runStim script that will be called on the stimulus computer. As we did above for the script itself, storage for the argument of the script is constructed in the "scratchpad" region of the Frontier Object Database, this time a string variable stored at the "scratchpad.netArgument" location. If a pre-existing argument string is still lingering at that location from a previous execution of this script, it is cleared. Finally, the content of the string "str" -- the stimulus parameter list "50 380 8" -- is copied directly into the string stored at "scratchpad.netArgument."

Now we are ready to transmit the script and the script argument to the stimulus computer. This is accomplished in two steps: (1) The NetFrontier broadcast command is first used to send the script argument across the network. If this transmission fails, the user is warned with a beep and all allocated storage is cleared. (2) The copy of Frontier on the stimulus computer is told to execute the script (named Stimuli.PVEP.runStim), using the script arguments just shipped, via the NetFrontier runScript command.

The copy of Frontier running on the stimulus computer must now receive this command along with its argument, decode it, and execute its own runStim script. This script is shown in Listing 4.

Listing 4: runStim script


Frontier script within the stimulus computer's Object Database for receiving script sent by the recording computer in Listing 3.The runStim script extracts the script command sent by NetFrontier.runScript and the stimulus parameters sent by NetFrontier.broadcast, then constructs an Apple event containing the stimulus parameters to the stimulus display program. Returns true if successful, false otherwise

© 1997 by Scott B. Steinman. All rights reserved.

Extract stimulus parameter strings from incoming Frontier script from recording computer. Place stimulus parameter values into local integer variables

local (sPer=long( 
        string.nthWord( scratchpad.scriptArgument,1 )))
local (diam=short( 
        string.nthWord( scratchpad.scriptArgument,2 )))
local (tPer=long( 
        string.nthWord( scratchpad.scriptArgument,3 )))

  Construct Apple event to transmit parameters to stimulus
  program. This Apple event will instruct the stimulus
  display program to modify the grating stimulus parameters
  and set up the graphical stimulus display. However, the
  stimulus will not be displayed by the stimulus program a
  until 'program ready' sync signal is received across the
  digital I/O lines from the recording computer.

  The arguments to the Frontier "Apple event" command are:
  1. 'STIM' -- the suite or group of Apple events to which
  the stimulus-running Apple event belongs
  2. 'RnSt' -- the identifier for the stimulus-running Apple
  event that tells the stimulus display program to prepare a
  stimulus display
  3. 'scyc' -- the identifier for the parameter that
  specifies the number of grating spatial cycles to display
  4. sPer -- the value of the spatial cycles parameter,
  stored in the Frontier long integer variable sPer created 
  5. 'diam' -- the identifier for the parameter that
  specifies the grating diameter
  6. diam -- the value of the diameter parameter, stored in
  the Frontier short integer variable diam created above.
  7. 'tcyc' -- the identifier for the parameter that
  specifies the number of grating temporal cycles (contrast 
  reversals) to display
  8. tPer -- the value of the temporal cycles parameter,
  stored in the Frontier long integer variable tPer created 

if not AppleEvent (,'STIM','RnSt','scyc',sPer,'diam',diam,
    If not successful, clean up and warn user with beep

  delete (@scratchpad.scriptArgument)
  speaker.beep ()
  return (false)

  Clean up

delete (@scratchpad.scriptArgument)
return (true)

The program first extracts each individual stimulus parameter contained in the script argument from a string in the scratchpad. Local variables are created in the form of named variables of specific types. The first such variable, named sPer, is a long integer to hold the first script argument, the desired grating spatial period. The grating diameter is the second of these arguments, and the grating reversal period is the third. With the stimulus parameters retrieved and separated, they can be packaged into individual Apple event parameters to be transmitted to the counterphase sinewave grating display program. Frontier's Apple event command does this for us. The Apple event command is passed the application signature of the grating stimulus program, stored in the Object Database location The signature for the C++ sinewave grating display program is 'SinG'. Following this is the sinewave grating display program's Apple event Suite; that is, an identifier for a group of Apple events specific to that program. We use the Apple event Suite identifier 'STIM'. The following identifier is that of the sinewave grating display program's runStimulus Apple event. This identifier allows the display program to recognize the command to display a sinewave grating stimulus. All that remains is to pass the individual Apple event parameters that define the desired appearance of the sinewave. These form the three remaining pairs of arguments to the Apple event command. Each pair consists of an Apple event parameter identifier for the stimulus parameter to be used when the stimulus program decodes the Apple event, and the value of the parameter. For example, the grating spatial period is given the Apple event parameter identifier 'scyc' and the value of the Frontier sPer variable follows it.

When the Frontier Apple event command is executed, Frontier transmits a raw Apple event that will be received by the stimulus display program. This forms the last step in the chain of message-passing from LabVIEW, to Frontier on the recording computer, to the second copy of Frontier on the stimulus computer, to the stimulus display program. It is now the job of that program to decipher the incoming Apple event to determine what the stimulus display program has been asked to do by LabVIEW. In other words, the display program need not "understand" the scripting code output by LabVIEW, nor the scripts transmitted by Frontier. Such code is difficult to decipher by C++ programs. Rather, it need only understand very basic, low-level Apple events, which require a minimum of programming to decode. Our Frontier programming has allowed us to simplify our programming task for each stimulus display program to the point that each program needs only be a small skeleton program written in C++ consisting solely of Apple event-handling and stimulus generation graphics code.

The Apple event handler called when the 'RnSt' Apple event is received is shown in Listing 5. Before this code is examined, the code to set up Apple event handling must be explained. Prior to entering the event loop of the stimulus display program, the means by which the program receives high-level events like Apple events, the Apple event handler must be installed with a call to the InitAEStuff function of Listing 5.

Listing 5: AppleEvents.cp


// C++ code in the stimulus display program that receives and
// handles the Apple event transmitted by the runStim
// Frontier script of Listing 4. This Apple event handler
// parses the stimulus parameters from the Apple event, then
// calls the DoAETrial function to display the stimulus 
// (a sinewave grating in this case).
// Each stimulus parameter has its own Apple event keyword
// identifier to allow easy extraction from the Apple event.
// © 1997 by Scott B. Steinman. All rights reserved.

#include AppleEvents.h
#include EPPC.h
#include GestaltEqu.h
#include PPCToolbox.h
#include Processes.h

static ProcessSerialNumber  gPSN;

void InitAEStuff( void )
  static AEinstalls HandlersToInstall[] = {
    // Required Apple events (not shown), 
    // plus our custom Apple event.
      { 'STIM', 'RnSt',
        (AEEventHandlerUPP) AERunStimulusHandler }
    OSErr   aevtErr = noErr;
    long   aLong = 0;
    Boolean   gHasAppleEvents = false;
    // Get process serial number of this program.
    aevtErr = GetCurrentProcess( &gPSN );
    // Check machine for ability to handle Apple events.
    // If not present (ie, not System 7.0 or above), exit.
    gHasAppleEvents = 
      (Gestalt( gestaltAppleEventsAttr, &aLong ) == noErr);
    // Installs our Apple event Handler. Whenever an Apple
    // event is received and we call AEProcessEvent, the
    // Apple event manager will check our list of handlers
    // and dispatch to our custom Apple event handler, if it 
    // exists.
    if ( gHasAppleEvents ) {
    // Required Apple events would also be installed here...
      aevtErr = AEInstallEventHandler( 'STIM', 'RnSt',
        (AEEventHandlerUPP) AERunStimulusHandler, 0, false );
      if ( aevtErr ) 
        ExitToShell();  // Just abort program if error occurs
      AESetInteractionAllowed( gInteractNow );
    } else

// Apple event handler called when Apple event with
// identifier of 'RnSt' in suite 'STIM' is received.

pascal OSErr
AERunStimulusHandler( AppleEvent *messagein, 
                      AppleEvent *reply, long /* refIn */ )
  DescType  returnedType;
  Size      actualSize;
  OSErr    error;

  // Bring stimulus display window to front
  error = SetFrontProcess( &gPSN );
  error = AEInteractWithUser( kAEDefaultTimeout, 0,
                              (AEIdleUPP) idleProc );
  if (error != errAENoUserInteraction) {
    // Extract stimulus parameters from incoming 'RnSt'
    // Apple event

    // Stimulus spatial period
    error = AEGetParamPtr( messagein, (AEKeyword) 'scyc',
      typeLongInteger, &returnedType, (Ptr) &spatialPeriod,
      sizeof( spatialPeriod ), &actualSize );
    if (error) return( -1111 );  // Our own unique error code

    // Stimulus grating diameter
    error = AEGetParamPtr( messagein, (AEKeyword) 'diam',
      typeShortInteger, &returnedType, (Ptr) &diameter,
      sizeof( diameter ), &actualSize );
    if (error) return( -2222 );

    // Stimulus temporal period
    error = AEGetParamPtr( messagein, (AEKeyword) 'tcyc',
      typeLongInteger, &returnedType, (Ptr) &cycleVBlanks,
      sizeof( cycleVBlanks ), &actualSize );
    if (error) return( -3333 );
    return( -1 );

  return( noErr );

void DoAETrial( void )
  // PlayGrating waits for digital signal before displaying
  // sinewave grating visual stimulus.

InitAEStuff first determines the process serial number of this stimulus display program. The process serial number is a unique identifier for each program or task currently executing on the computer. The GetCurrentProcess Toolbox routine retrieves this serial number for us, and stores it in the global variable gPSN. After confirming with the Gestalt Manager that this machine does in fact support Apple events, we install the Apple event handlers. Here we show only the example handler and omit for the sake of brevity the four required Apple events for opening a program or program file, printing and quitting a program. Finally, we call AESetInteractionAllowed to set the program interaction mode, that is, how the user is permitted to interact with this Apple event "slave" program -- is the program run solely in the background, can it receive mouse clicks from the program user, and must the calling program be on the same computer to allow such interaction? We permit user interaction from calling programs on any computer, as most programs will.

Now we can examine the Apple event handler code itself. The first act of the example AERunStimulusHandler handler is to bring the stimulus graphics display window to the forefront by calling SetFrontProcess with the process serial number of the stimulus program, then we initiate program interaction if it was permitted. At this point, we can retrieve the stimulus parameters contained in the Apple event that will determine the appearance and behavior of the stimulus display. Each parameter is extracted from the Apple event with AEGetParamPtr, which is passed as two of its parameters an identifier or Apple event keyword that determines which parameter is retrieved, and the type of variable the parameter is. In the case of this sample code, the stimulus is a spatial sinewave grating. The first stimulus parameter stored in the Apple event to be retrieved is the spatial period of the sinewave. This parameter's keyword is 'scyc' and its type is a long integer. AEGetParamPtr stores the retrived parameter in the stimulus program's long integer variable spatialPeriod. If any error occurs in this process, the process is aborted. A similar sequence of steps is used to retrieve the grating diameter and counterphase reversal temporal period. If each parameter is extracted successfully, the DoAETrial routine is entered, which is responsible for displaying the grating stimulus. DoAETrial sends a sync signal via the digital I/O line (noted by the Boolean variable stimEnabled) to the recording computer to signal that the stimulus is ready and recording may begin.

One aspect of this inter-program control that has not yet been discussed is the launching and quitting of the stimulus display program prior to and following data acquisition. Frontier simplifies these tasks as well. NetFrontier can instruct the copy of Frontier on the stimulus computer to execute two other Frontier commands: The first is Frontier's launch command, which sends an Apple event via the Finder to launch a program. The form of this command that we use is launch.usingID( This command finds the application whose signature is stored in a variable called "id" in the Object Database table PVEP within the Stimulus script suite. The second command, to be used after data collection, is Frontier's quit command. This command takes the form core.quit(, "no"), where the second argument is a string stating whether or not a data file is to be saved when the requested program is quit. The example code demonstrates that Frontier can be used to handle all aspects of the inter-program communication that is at the core of the laboratory electrophysiology software. Frontier launches the stimulus generation programs, signals each to display stimuli with specific stimulus parameters, then forces them to quit when we no longer need them. The sole purpose of the digital I/O lines is to ensure the tight time-locking of stimulus display and data recording.


In the construction of software systems, complex decisions regarding individual aspects of the system design must be made that have serious implications for the design of the remainder of the system. In the present case, the overwhelming need for program execution speed dictated the choice of a dual-computer system and the method of communication between the computers. Such a system could not have been constructed easily without combining the strengths of LabVIEW, Frontier, and C++. The use of LabVIEW shortened the program development cycle considerably. In addition, inter-computer communication of the speed and complexity used here would not have been achieved easily using C++ without the addition of Frontier. Despite the complexity of this system, the software is easy to operate by the clinician due to the intuitive user interface imposed by LabVIEW's instrument panel paradigm and the operation of all tests from a single program on the recording computer. More importantly, the system design does not sacrifice modularity and expandability. Adding a new test requires adding only a small skeleton C++ stimulus display program, a few LabVIEW VIs to select stimulus parameters, send scripts and record data, and a few Frontier scripts. These code modules are very similar in each of the recording, stimulus and intercommunication programs, in great deal due to a high degree of code reuse in LabVIEW, Frontier and C++ libraries for handling Apple events and generating animated graphics. Much of the task of creating a new set of tests simply involves duplicating, then modifying, existing code.

In the future, it may be possible to remove some of the complexity of the system once it is possible to perform all of the stimulus display and recording tasks on a single dual-monitor dual-processor computer. The inter-program communication could then be restricted to one machine, eliminating the need for NetFrontier -- scripts could be sent from LabVIEW to Frontier and directly translated into Apple events to be received by the stimulus program. Frontier can use the fast Component Manager of Power Macintoshes for Apple event transmission within a single machine, which increases the speed of inter-program communication even more. However, even with the present single-processor technology, we have demonstrated that the creation of powerful, flexible, real-time software is facilitated when it makes use of innovations found on the Macintosh computer. The same programming principles outlined in this paper may be applied to a wide range of applications.

Bibliography and References

  • Apple Computer Company, Inside Macintosh: Inter-program Communication, Addison-Wesley.
  • Baro, John A. and Hughes, Howard C. "The Display And Animation Of Full-Color Images In Real Time On The Macintosh Computer". Behavioral Research Methods, Instruments and Computers, 23 (1991), pp. 537-545.
  • Johnson, Gary W. "LabVIEW Graphical Programming: Practical Applications In Instrumentation And Control". (1994), New York: MacGraw-Hill.
  • Steinman, Scott B. "Simple Real-Time Color Frame Animation". MacTech, 9:9 (September 1993), pp. 21-35.
  • Steinman, Scott B. "Extendable Real-Time Simultaneous Data Acquisition And Stimulus Generation On The Macintosh Computer". Behavioral Research Methods, Instruments and Computers, In Press-.
  • Steinman, Scott B. and Carver, Kevin. "Visual Programming with Prograph CPX(tm)". (1995), Greenwich, Connecticut: Manning Publications / Prentice-Hall.
  • Steinman, Scott B. and Nawrot, Mark. "Real-Time Color Frame Animation For Visual Psychophysics On The Macintosh Computer". Behavioral Research Methods, Instruments and Computers, 24 (1992), pp. 439-452.

Dr. Scott Steinman is a vision scientist and Chair of the Department of Biomedical Sciences at the Southern College of Optometry. He develops software for clinical vision testing, research and education. Scott programs in C++, LabVIEW, Frontier, Prograph and Java. He has published numerous articles on computer programming, two of them previously in MacTech. You may have noticed his book, "Visual Programming with Prograph CPX", on sale in the DevDepot (do I have to hint more than this?). You can reach him at


Community Search:
MacTech Search:

Software Updates via MacUpdate

TextSoap 8.4.1 - Automate tedious text d...
TextSoap can automatically remove unwanted characters, fix up messed up carriage returns, and do pretty much anything else that we can think of to text. Save time and effort. Be more productive. Stop... Read more
Backblaze - Online backup servi...
Backblaze is an online backup service designed from the ground-up for the Mac. With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Numi 3.15 - Menu-bar calculator supports...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
EtreCheck 3.3.3 - For troubleshooting yo...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
BusyContacts 1.1.8 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
TunnelBear 3.0.14 - Subscription-based p...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
Apple Final Cut Pro X 10.3.4 - Professio...
Apple Final Cut Pro X is a professional video editing solution.Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more
Hopper Disassembler 4.2.1- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32-bit and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about... Read more
Slack 2.6.2 - Collaborative communicatio...
Slack is a collaborative communication app that simplifies real-time messaging, archiving, and search for modern working teams. Version 2.6.2: Fixed Inexplicably, context menus and spell-check... Read more
Arq 5.8.5 - Online backup to Google Driv...
Arq is super-easy online backup for Mac and Windows computers. Back up to your own cloud account (Amazon Cloud Drive, Google Drive, Dropbox, OneDrive, Google Cloud Storage, any S3-compatible server... Read more

Latest Forum Discussions

See All

The best new games we played this week
We were quite busy this week. A bunch of big mobile games launched over the past few days, alongside a few teeny surprises. There're lots of quality games to load your phone with. We've gone and picked out five of our favorites for the week. [... | Read more »
Magikarp Jump beginner's guide
Magikarp Jump is a mystifying little game. Part Tamagotchi, part idle clicker, there's not a whole lot of video game there, per se, but for some reason we can't help coming back to it again and again. Your goal is to train up a little Magikarp to... | Read more »
Goat Simulator PAYDAY (Games)
Goat Simulator PAYDAY 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: ** IMPORTANT - SUPPORTED DEVICES **iPhone 4S, iPad 2, iPod Touch 5 or better Goat Simulator: Payday is the most... | Read more »
GRID Autosport delayed until autumn
Sorry mobile racing fans -- GRID Autosport has been delayed a few months. The game is now expected to launch this fall on iOS. Feral Interactive announced that they wanted more time to work on the game's UI and overall performance before launching... | Read more »
Zombie Gunship Survival Beginner's...
The much anticipated Zombie Gunship Survival is here. In this latest entry in the Zombie Gunship franchise, you're tasked with supporting ground troops and protecting your base from the zombie horde. There's a lot of rich base building fun, and... | Read more »
Mordheim: Warband Skirmish (Games)
Mordheim: Warband Skirmish 1.2.2 Device: iOS Universal Category: Games Price: $3.99, Version: 1.2.2 (iTunes) Description: Explore the ruins of the City of Mordheim, clash with other scavenging warbands and collect Wyrdstone -... | Read more »
Mordheim: Warband Skirmish brings tablet...
Legendary Games has just launched Mordheim: Warband Skirmish, a new turn-based action game for iOS and Android. | Read more »
Magikarp Jump splashes onto Android worl...
If you're tired ofPokémon GObut still want something to satisfy your mobilePokémon fix,Magikarp Jumpmay just do the trick. It's out now on Android devices the world over. While it looks like a simple arcade jumper, there's quite a bit more to it... | Read more »
Purrfectly charming open-world RPG Cat Q...
Cat Quest, an expansive open-world RPG from former Koei-Tecmo developers, got a new gameplay trailer today. The video showcases the combat and exploration features of this feline-themed RPG. Cat puns abound as you travel across a large map in a... | Read more »
Jaipur: A Card Game of Duels (Games)
Jaipur: A Card Game of Duels 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: ** WARNING: iPad 2, iPad Mini 1 & iPhone 4S are NOT compatible. ** *** Special Launch Price for a limited... | Read more »

Price Scanner via

Memorial Day savings: 13-inch Touch Bar MacBo...
B&H Photo has the 2016 Apple 13″ Touch Bar MacBook Pros in stock today and on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 13″ 2.9GHz/512GB... Read more
Apple refurbished 13-inch MacBook Airs availa...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $... Read more
Apple restocks refurbished 11-inch MacBook Ai...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models recently discontinued by Apple), available for up to $170 off original MSRP. An Apple one-year warranty is included with each... Read more
12-inch 1.2GHz Retina MacBooks on sale for up...
B&H has 12″ 1.2GHz Retina MacBooks on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 12″ 1.2GHz Space Gray Retina MacBook: $1449.99 $150 off... Read more
15-inch 2.7GHz Silver Touch Bar MacBook Pro o...
MacMall has the 15-inch 2.7GHz Silver Touch Bar MacBook Pro (MLW82LL/A) on sale for $2569 as part of their Memorial Day sale. Shipping is free. Their price is $230 off MSRP. Read more
Free Tread Wisely Mobile App Endorsed By Fath...
Just in time for the summer driving season, Cooper Tire & Rubber Company has announced the launch of a new Tread Wisely mobile app. Designed to promote tire and vehicle safety among teens and... Read more
Commercial Notebooks And Detachable Tablets W...
Worldwide shipments of personal computing devices (PCDs), comprised of traditional PCs (a combination of desktop, notebook, and workstations) and tablets (slates and detachables), are forecast to... Read more
Best value this Memorial Day weekend: Touch B...
Apple has Certified Refurbished 2016 15″ and 13″ MacBook Pros available for $230 to $420 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2.6GHz... Read more
13-inch MacBook Airs on sale for up to $130 o... has 13″ MacBook Airs on sale for up to $130 off MSRP including free shipping: - 13″ 1.6GHz/128GB MacBook Air (sku MMGF2LL/A): $869.99 $130 off MSRP - 13″ 1.6GHz/256GB MacBook Air (sku... Read more
2.8GHz Mac mini available for $973 with free...
Adorama has the 2.8GHz Mac mini available for $973, $16 off MSRP, including a free copy of Apple’s 3-Year AppleCare Protection Plan. Shipping is free, and Adorama charges sales tax in NY & NJ... Read more

Jobs Board

*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**509643BR** **Job Title:** Best Buy Apple Computing Master **Location Number:** 001482- Apple Valley-Store **Job Description:** **What does a Best Buy Apple Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
*Apple* Mac and Mobility Engineer - Infogrou...
Title: Apple Mac and Mobility Engineer Location: Portland, OR Area Type: 12 month contract Job: 17412 Here's a chance to take your skills to the limit, learn new Read more
*Apple* Retail - Multiple Positions, White P...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.