Copyright (c) Hyperion Entertainment and contributors.

RealTime Library

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Realtime.library provides a convenient, higher-level interface to underlying hardware timers that is easy to use. Realtime.library also provides for the distribution of timing pulses to an unlimited number of client applications on a priority basis, thus supporting multitasking in the most robust manner possible.

Conductors and Playerlnfos

The realtime.library uses the Conductor structure to manage timing. There can be any number of Conductor structures, each of which represents a separate and independent timing context (i.e., a group of applications that wants to be synced together).

Each Conductor can have one or more client applications. A second structure called a PlayerInfo, is set up by cach client application that wants to get timing information from the Conductor. There is typically one Playerlnfo for each task that wants to get timing information (a task could have more than one but this would be unusual).

Both the Conductor and Playerlnfo structures follow the conventions of Release 2. You never create these structures by allocating and initializing them yourself. The system provides the functions to do this for you. Also the structures are read-only. To change the fields within these structures, use the system-provided functions and tags.

An application will usually create a single PlayerInfo structure and then attach it to a Conductor. If the Conductor does not yet exist, it will be created by the system. To create a PlayerInfo structure you call CreatePlayerO:

struct PlayerInfo *pi = IRealTime->CreatePlayer(Tag tag, ...);

This call takes a list of tag items that describe the attributes of the Playerlnfo structure you want to create. (A complete list of all the tags available can be found in the Autodoc for SetPlayerAttrs().) It returns a pointer to the PlayerInfo. Here’s a fragment showing how to set up a PlayerInfo:

struct PlayerInfo *pPlayerInfo = IRealTime->CreatePlayer(
  PLAYER_Name, "My_player",
  PLAYER_Conductor, "My_conductor",
  TAG_END);
 
if (pPlayerInfo != NULL )
{
  // Your real-time application goes here...
 
  IRealTime->DeletePlayer(pPlayerInfo);
}

In the code above, a PlayerInfo will be created with the name of "My_player". It will be attached to the Conductor structure named "My_conductor". If a Conductor structure named "My_conductor" does not already exist, the system will create one. Other applications could also attach their PlayerInfos to "My_conductor".

When your application finishes, you should delete any PlayerInfos you created by calling DeletePlayer(). The Conductor will be automatically deleted by the system (however this won’t happen until *all* the PlayerInfos attached to a Conductor are deleted).

Once you have a PlayerInfo and Conductor set up, you can obtain timing pulses for your application. Timing pulses come from underlying timer chips and are passed to your application through the Conductor.

Getting Clock Ticks

The realtime.library uses a tick frequency of 600 Hz so clock pulses are delivered approximately every 1.66 ms. There are two ways to get this timing information:

  • An alarm's signal
  • A clock tick callback hook

You can ask realtime.library to signal your task at some future time by using its alarm facility. This allows you to operate asynchronously. For instance, you could start a group of MIDI notes playing and set the realtime alarm to signal you when they should be stopped, then go on to some other job such as preparing the next group of notes before calling Wait() on the alarm signal.

The fragment below shows how to set up the realtime.library’s alarm clock to signal the calling task at time = 1000 ticks (the fragment assumes that the realtime.library interface is already obtained).

// This fragment assumes the that IRealTime is already obtained.
int8 midiSignal = IExec->AllocSignal(-1);  // Allocate a wake-up signal bit
if (midiSignal != -1)
{
  struct PlayerInfo *pPlayerInfo = IRealTime->CreatePlayer(
    PLAYER_Name, "My_player",
    PLAYER_Conductor, "My_conductor",
    PLAYER_SignalTask, IExec->FindTask(NULL),
    PLAYER_AlarmSigBit, midiSignal,
    TAG_END);
 
  if (pPlayerInfo != NULL)
  {
    // Start the realtime clock running.
    int32 res = IRealTime->SetConductorState(pPlayerInfo, CLOCKSTATE_RUNNING, 0);
    if (!res)
    {
      BOOL timerr = IRealTime->SetPlayerAttrs(pPlayerInfo,
        PLAYER_AlarmTime, 1000,
        PLAYER_Ready, TRUE,
        TAG_END);
      if (timerr)
      {
        // You could do some other job before
        // calling Wait() on the alarm signal.
        IExec->Wait(1UL << midiSignal);
      }
      else IDOS->Print("Couldn't set alarm\n");
    }
    else IDOS->Printf("Couldn't start clock\n");
  }
  else IDOS->Printf("Couldn't set up PlayerInfo structure.\n");
}
else IDOS->Printf("Couldn't allocate signal.\n");

In the fragment above, the calling task requests a PlayerInfo with an alarm clock feature by passing the PLAYER_AlarmSigBit tag to CreatePlayer() The ti_Data field of this tag contains the signal bit that will be set by the realtime.library when the alarm goes off. The PLAYER_SignalTask tag indicates which task will be signaled.

The realtime clock is then started by calling SetConductorState() (discussed below). This is important since any alarm requests made when the clock is stopped will be ignored.

Finally the alarm time is set by calling SetPlayerAttrs(). The parameters to this call are:

BOOL result SetPlayerAttrs(struct PlayerInfo *pi, Tag tag, ...);