Contents page

Rules for Tools/ToolMaster Structure

The ToolMaster Structure

Since multiple copies of a Tool can be made, you don't define each Tool individually in your program code. Instead, a central data structure, ToolMaster, provides all of the information needed to define the Tool. Multiple copies of a Tool all refer to the ToolMaster structure for the information they share, such as name, icon image, the size of the Tool structure itself, and the routine that processes events when they enter a Tool. When Bars&Pipes first loads a Tool from a file, it calls a routine in the Tool file that initializes the ToolMaster structure and returns a pointer to it. It is the ToolMaster structure that is represented in the ToolBox. When you drag a Tool from the ToolBox onto the PipeLine, Bars&Pipes consults the ToolMaster structure to determine everything it needs to know in order to create the instance of the Tool. Each Tool, in turn, carries a pointer to the ToolMaster structure that describes it.

struct ToolMaster {
    struct ToolMaster *next;        /* Next in this list. */
    long toolid;                    /* Tool ID. */
    struct Image *image;            /* Icon for this tool. */
    struct Image *upimage;          /* Icon for branching up. */
    short x,y;                      /* Position in toolbox. */
    char name[100];                 /* Tool name. */
    char filename[100];             /* File location. */
    struct Tool *(*createtool)();   /* Allocate a new tool. */
    void (*edittool)();             /* Edit tool parameters. */
    struct Event *(*processevent)();/* Process an event. */
    void (*processclip)();          /* Unused. */
    void (*deletetool)();           /* Routine to delete. */
    void (*removetool)();           /* Routine to close. */
    long (*savesize)();             /* Returns size for save. */
    long (*savetool)();             /* Routine to save. */
    struct Tool *(*loadtool)();     /* Routine to load. */
    long (*expanda)();              /* Future routine? */
    long (*expandb)();              /* Future routine? */
    long (*expandc)();              /* Future routine? */
    long segment;                   /* Segment list. */
    long altsegment;                /* Alternate segment. */
    struct Track *intrack;          /* Input track. */
    short toolsize;                 /* Tool size. */
    char inedit;                    /* Editing now. */
    char selected;                  /* Icon selected flag. */
    long tooltype;                  /* Type of tool. */

Fields in the ToolMaster structure that are of interest:

     Each ToolMaster has a unique long word identifier, composed of
     four ASCII characters clumped together.  For example, the MIDI Out
     Tool is "MIOT", or 0x4D494F54.  It is very important that no two
     commercially available Tools have the same identifier; please
     communicate with The Blue Ribbon Soundworks to make sure your Tool
     identifiers are unique.

     A set of bits determine the characteristics of each Tool (see
     Tool Type Flags).  These bits are placed in the tooltype field.
     Some of the bits are:

          This is an input Tool.  It can only exist on the input of a
          PipeLine as the source for a Track.

          This is an output Tool.  It can only exist as the very last
          Tool on a Track's PipeLine.

          This Tool can be placed in any position on a PipeLine, other
          than input or output.

          Notes sent to this Tool must be given to the Tool by
          Bars&Pipes' pipeline system exactly at the time stamped on
          the note.  If not, the note may arrive early, providing extra
          time for processing overhead.  Most Tools do not need to set
          this flag.  The MIDI Out Tool does, because it needs to send
          notes to synthesizers when they are due to be played, not
          ahead of time.

          This Tool can be connected by a vertical pipe to a Merge In
          Tool on another PipeLine.

`image' `upimage'
     You must provide a pointer to an Intuition Image structure that
     defines the Tool's icon in the image field, and this Image must
     have the dimensions of 24 pixels wide by 12 pixels high by 3
     bitplanes deep (8 colors.)  If the Tool is capable of branching,
     place an alternate Image that shows how the Tool would look if it
     were branching up (connecting to a pipe from above instead of
     below) in the `upimage' field.  We use DPaint to create icons and
     a public domain utility, icon2c, to translate them into C source
     code.  Several sample icon images are included in the source disk.
     Load one into DPaint (as a brush) and use it to set the color
     palette.  Paint your own icon and save it as a brush.  The brush
     must be exactly 24 pixels wide by 12 pixels high.  Convert the
     brush to C code with icon2c, then insert the code in your source
     file with your text editor.  Be sure to compile the icon to load
     into chip memory. (This is easily accomplished in Lattice by using
     the chip keyword.)

     In order to create, delete, save, and load copies of your Tool,
     Bars&Pipes needs to know the size of the Tool structure for your
     Tool.  This size can vary from Tool to Tool since you can add
     additional fields to the Tool structure for your Tool. (For an
     example, look at the DelayTool structure in the Delay Tool example
     code).  You can, in most cases, just use the `sizeof()' function
     of the C compiler to calculate the size of the `Tool' structure.
     Place the result in the toolsize field.

     Each Tool has a name, which is a null terminated ASCII string, up
     to 100 characters in length.

     Probably the single most important feature of a Tool, processevent
     points to a routine that handles each MIDI Event sent to this
     Tool.  This routine defines the behavior of the Tool.  Although
     the processevent routine is always passed one Event, it may return
     any number of Events, strung in a linked list.  For example, the
     Echo Tool returns all of the echo notes it generates linked in a
     list.  On the other hand, the Plug Tool frees each note it
     receives and returns 0.

     Many Tools require user interfaces that can be opened by double
     clicking on the Tool in the PipeLine.  Define a reentrant routine
     that opens a window and lets the user set the Tool's parameters
     and place a pointer to that routine in edittool.  For those of you
     who might be a bit foggy on writing reentrant code, just remember
     that you shouldn't use global or static variables in your code.
     If you do, you'll have to make sure that you provide adequate
     locking mechanisms for those variables to ensure that if several
     copies of your Tool are executing at the same time, they won't
     trash your global or static variables. As long as you store your
     data in the Tool structure, writing a reentrant routine should
     pose no problem - with one notable exception.   Intuition
     structures (such as windows for your Tool) need to be duplicated
     for each Tool.  Inovatools provides a function to duplicate a
     Window structure and all of the Gadgets associated with it.  If
     you aren't using Inovatools, you'll have to write your own routine
     to duplicate the Window structure.

     This is a routine that Bars&Pipes calls to allocate a Tool
     structure.  Usually, you don't need to provide this routine,
     because Bars&Pipes knows how large a Tool structure to create just
     by checking the toolsize field.  If, however, you need to do
     anything special like allocate dynamic data structures or
     initialize tasks or other resources, write a routine to do so and
     have `createtool' point to it.  Then, Bars&Pipes calls your
     routine to allocate and initialize the Tool structure rather than
     doing so itself.  Your `createtool' routine must return a pointer
     to the allocated Tool, or 0 if it is unable to do so.

     This is the companion to `createtool'.  Once again, Bars&Pipes is
     happy to delete your Tool since it knows the size of the Tools
     structure.  But, if you have done anything special, like allocate
     dynamic data structures, provide a deletetool routine to remove
     them along with the Tool.  `deletetool' is passed one parameter:
     the Tool to delete.

     If you provide a `removetool' routine, Bars&Pipes will call it
     once when Bars&Pipes removes your ToolMaster from the system. This
     occurs either when Bars&Pipes is closing down or when the user
     selects Remove Tool from the ToolBox menu.  Most Tools don't have
     a `removetool' routine.  Others, like the MIDI In Tool, use the
     `removetool' routine to deallocate system resources previously
     allocated when the Tool was loaded.  Tools that install transport
     handlers usually have a removetool routine to remove the handler.

     Usually, when Bars&Pipes reads a Tool from disk as part of a song,
     it reads a chunk of data equivalent to the size of the Tool
     structure (as defined in `toolsize'), creates a Tool structure, and
     places the data in it.  If, however, the Tool provides a `loadtool'
     routine, that routine reads the data directly.  This is necessary
     if the Tool has dynamic data structures.  Tools that use
     `loadtool' also have `savetool' and `sizetool' routines for saving
     their data.  Bars&Pipes calls `loadtool' with two parameters:  a
     file pointer and the size of the data segment to read.

          long file; long size; struct Tool *tool;

tool = loadtool(file,size);

     The `loadtool' routine returns either a pointer to a Tool
     structure or 0 for an unsuccessful read.  In either case, it must
     read exactly the number of bytes in the data segment.  For reading
     and writing to disk, Bars&Pipes uses its own buffered file i/o
     system.  As a result, the file pointer must be used in conjunction
     with the `fastread()' (see fastopen()) call (See the Bars&Pipes
     library summary.)

     Provide this routine in conjunction with `loadtool' and `savetool'
     only if your Tool saves a variable amount of data to disk as part
     of a song.  When Bars&Pipes saves a song, it needs to know how
     large each data section is before saving it.  It will call your
     `savesize' routine, passing one parameter - the Tool to save. Your
     `savesize' routine should calculate the number of bytes needed to
     save the tool and return that number.

     The second step in saving a Tool involves a second routine that
     you provide - `savetool'.  Bars&Pipe calls `savetool', passing it
     the file handle and the Tool to save.  You must use the
     `fastwrite()' command (see fastopen()) for writing. (See the
     Bars&Pipes library summary.)  Your `savetool' routine first writes
     the four letter tool identifier, then the size of the Tool data,
     then the data itself.

savetool(file,tool) long file; struct Tool *tool; {
    long size;
    size = savesize(tool);
    /*Save the tool structure, etc., here. */
    /* If unable to complete the save, return 1, */
    /* otherwise return 0 for success. */
    return(0); }

We've talked about the ToolMaster structure, and in doing so, we've also talked about the Tool structure which accompanies each instance of a ToolMaster structure. Let's look at the Tool structure in more detail.