### From The `sfront` Reference Manual by John Lazzaro and John Wawrzynek.

# Part II/2C: Writing Control Drivers

## Sections

- Introduction.
- Conventions. Explains naming conventions, and shows syntax of the simple functions that a control driver defines.
- The simple `csys` functions.. The `csys_setup`, `csys_newdata` and `csys_shutdown` functions.
- Writing `csys_midievent`. Shows how to send MIDI control commands.
- Writing `csys_saslevent`. Shows how to send SASL and special control commands.
- Registering your control driver. Explains how to tell the sfront source code about your driver.
- Bug Reports. Tell us if you have problems or suggestions.

## MIDI Commands

NoteOn, NoteOff, Poly Aftertouch, Control Change, Program Change, Channel Aftertouch, Pitch Wheel, No-op, NewTempo, EndTime.

## SASL Commands

Instr, Control, Table, Endtime, Tempo, No-op.

## Introduction

This section describes the process of writing a control driver in detail, building on the data structure and architecture descriptions of the two previous chapters.

We begin by describing the programming conventions control drivers should follow, concerning issues such as variable, function, and file names. We then describe how to write the functions that control drivers must supply. These functions are introduced in an earlier section of this chapter.

We conclude by describing the registration process for a new control driver, which involves making several small changes in the sfront source code distribution.

## Previous Sections

Part II/2A: Control Driver Overview
Part II/2B: Control Drivers Data Structures

## Conventions

Each control driver needs a name. The name is the string that follows `-cin` on the sfront command line. It also is the name of the C file that contains the driver. This driver file should be placed in the directory: ``

    sfront/src/lib/csys/

The right panel shows the control driver naming convention for the file streaming decoder that is a part of the sfront distribution.

The control driver may include macro and constant defines. These defines must begin with the string `CSYSI_`.

The control driver may also include C or C++ global variable and type definitions, as well as internal C or C++ function definitions. The names of these elements must begin with `csysi_`.

We will use the `csysi_` and `CSYSI_` naming convention to enable backward compatible support of multiple control drivers in a future sfront release (presently sfront only supports a single control driver flag). If your control driver violates this naming scheme, it may break somewhere down the line.

Control driver functions that begin with `csys_` are called by the main `sa.c` program. The right panel lists the `csys_` that a control driver may define. The bulk of this chapter describes these functions in detail.

Note that either `csys_midievent` or `csys_saslevent` may be omitted from a control driver (but not both).

Finally, a control driver may let the user specify command-line options for the `sa.c` program. We describe the naming conventions for control driver command-line options in an earlier section of this chapter..

## Control Driver Names

`

    Example:

    File streaming encoder (fstr).

    sfront invocation:

    sfront -cin fstr

    driver file/location:

    sfront/src/lib/csys/fstr.c

`

## Inside Control Drivers:

`

    symbolic constants and
    macros must begin with:

    CSYSI_

    global variables must
    begin with:

    csysi_

    internal functions must
    begin with:

    csysi_

    All control drivers must
    define:

    int csys_setup(void) {}
    int csys_newdata(void) {}
    void csys_shutdown(void) {}

    control drivers that can
    return MIDI commands must
    define:

    int csys_midievent(...) {}

    control drivers that can
    return SASL commands must
    define:

    int csys_saslevent(...) {}

    see Part II/2B for the
    conventions for
    command-line flags a
    control driver defines
    for the sa.c file.

`

## Simple `csys` Functions

The three `csys` functions shown on the right panel must be defined in all control drivers. In this section, we describe the semantics of these functions.

### `csys_setup`

The `csys_setup` function is called near the start of execution of the `sa.c` file. A control driver usually initializes its own internal variables and opens files and devices during the `csys_setup` call. It may also parse `sa.c` command-line arguments, as described in an earlier section.

If the control driver is able to support the execution of the `sa.c`, `csys_setup` should return the value `CSYS_DONE`. To signal an initialization problem (for example, not being able to open a file or device), `csys_setup` should return the value `CSYS_ERROR`. On receipt of `CSYS_ERROR`, the `sa.c` prints a generic error message indicating control driver failure on `stderr`, and terminates with a -1 error code.

### `csys_newdata`

The `csys_newdata` function is called once per i-pass. The return value of the function tells the `sa.c` program if there are new MIDI or SASL events the control driver wishes to execute on this i-pass. The right panel described the four return values (CSYS_NONE, CSYS_MIDIEVENTS, CSYS_SASLEVENTS, and CSYS_EVENTS) that the `csys_newdata` function may use.

In some applications, the control driver may need to know the current execution time of the `sa.c` program, in order to determine if events are ready. A previous section of this chapter describes global variables that the `csys_newdata` function may access for this purpose.

### `csys_shutdown`

The `csys_shutdown` function is called near the end of execution of the `sa.c` program. The control driver can close files and devices, and do other tasks to ensure an orderly shutdown.

This function is usually called as a part of the normal termination process. Normal termination happens when the current score time exceeds the value of the `endtime` variable.

Since the `endtime` variable is available to the control driver to read, and can be changed during a `csys_midievent` or `csys_saslevent` call, the control driver can monitor and influence the total execution time of the `sa.c` program. However, by the time `csys_shutdown` is called, there is no way for the control driver to reverse the termination decision.

If sfront is invoked with the `-except` option, the `csys_shutdown` option is also called in the event of the abnormal end of execution. An abnormal termination may happen if the SAOL program causes a floating-point exception, if the `sa.c` program process is terminated by the operating system, or for other reasons.

## `csys_setup`

`

    int csys_setup(void) { }

    Called once, near the start
    of execution of the sa.c
    file. Return values:

    CSYS_DONE : if control driver
                initialization is
                successful.

    CSYS_ERROR: if control driver
                wishes to terminate
                the sa.c file due
                to initialization
                problems.

`

## `csys_newdata`

`

    int csys_newdata(void) {}

    Called at the start of each
    i-pass. Legal return values:

    CSYS_NONE:

      No new MIDI or SASL events
      for this i-pass. The sa.c
      file will not call the
      csys_midievent() or the
      csys_saslevent() function
      on this i-pass.

    CSYS_MIDIEVENTS:

      New MIDI events, but no
      new SASL events, on this
      i-pass. The sa.c file will
      call csys_midievent() to
      receive the event(s).

    CSYS_SASLEVENTS:

      New SASL events, but no
      new MIDI events, on this
      i-pass. The sa.c file will
      call csys_saslevent() to
      receive the event(s).

    CSYS_EVENTS:

      New MIDI events and
      new SASL events on this
      i-pass. The sa.c file will
      call csys_midievent() and
      csys_saslevent() to
      receive the events.

`

## `csys_shutdown`

`

    void csys_shutdown(void) {}

    Called at the normal end of
    execution of the sa.c
    program, so that the control
    driver may close devices and
    files.

    If the sfront command-line
    option -except is invoked, the
    csys_shutdown function is also
    called in the case of abnormal
    termination, such as a floating
    point exception.

`

## `csys_midievent`

The `csys_midievent` function is called during an i-pass if the return value for the `csys_newdata` call is CSYS_MIDIEVENTS or CSYS_EVENTS.

As shown on the right panel, the arguments for the `csys_midievent` function are all pointers. This function places information about the new MIDI event into the variables pointed to by these arguments.

A `csys_midievent` call communicates a single MIDI event. If there is another pending MIDI event, the `csys_midievent` function should return CSYS_EVENTS to request another `csys_midievent` call. The `csys_midievent` function is repeatedly called until it returns the value CSYS_NONE.

## `csys_midievent`

`

    int csys_midievent(

        unsigned char * cmd,
        unsigned char * ndata,
        unsigned char * vdata,
        unsigned short * extchan,
        float * fval);

    Place MIDI event information
    in pointer, i.e:

       *cmd = CSYS_MIDI_NOTEON;
       *ndata = 40;
       *vdata = 64;
       *extchan = 1;
       *fval = 0.0F;

    Return values:

       CSYS_EVENTS:

          Requests another call to
          csys_midievent on this
          i-pass, to communicate
          another MIDI event.

       CSYS_NONE:

          Indicates there are no
          more MIDI events for this
          i-pass.

`

#### Command Syntax

The right panel lists the possible `*cmd` values for `csys_midievent`. These values code the MIDI commands supported by the MP4-SA standard, as well as several special commands.

The numeric values for the MIDI commands match the syntax of MIDI events sent on a wire. However, the lower nibble of MIDI commands, which holds the MIDI channel number, is ignored by the `sa.c` program.

Instead, the `*extchan` variable is used to code the MIDI channel information. This variable can be used to code multiple ports of 16-channel MIDI streams, using the formula shown on the right panel. The maximum number of MIDI channels for a control driver is arbitrary, and is specified as part of the registration process.

The implementation of running status (where consecutive identical MIDI commands on a wire can skip the command byte) is the responsibility of the control driver. The `sa.c` program expects a valid `*cmd` value on every call.

#### MIDI Channel Number Spaces

In the MP4 file format, it is possible to include a complete MIDI File in the configuration part of the file. The channel number space of this MIDI File is independent from the channel number space of the MIDI control driver. This independence is in keeping with the MP4-SA specification, and lets programmers use the control driver interface to write compliant MP4-SA streaming applications.

## `*cmd` values

`

    MIDI commands:

      symbolic         hex

    CSYS_MIDI_NOTEOFF   80
    CSYS_MIDI_NOTEON    90
    CSYS_MIDI_PTOUCH    A0
    CSYS_MIDI_CC        B0
    CSYS_MIDI_PROGRAM   C0
    CSYS_MIDI_CTOUCH    D0
    CSYS_MIDI_WHEEL     E0

    Special commands:

    CSYS_MIDI_NOOP      70
    CSYS_MIDI_NEWTEMPO  71
    CSYS_MIDI_ENDTIME   72

`

## `*extchan`

`

    The *extchan field
    should be set to the extended
    MIDI channel for the command.
    For a simple system (a single
    MIDI stream with 16 channels)
    set *extchan to the
    MIDI channel number (0 to 15).

    For a complicated system with
    several 16-channel MIDI ports,
    set *extchan to

    16*P + N

    where P is the MIDI port
    (numbered from 0) and N is
    the MIDI channel number (0-15).

`

#### Command Semantics

The right panel shows the semantics for each command supported by the `csys_midievent` function. The description specifies the meaning of the `*ndata`, `*vdata`, and `*fval` fields for each command.

The MIDI program command is used to select the SAOL instrument that is created in response to the MIDI NoteOn command. The program number specified by the MIDI program command is mapped into a SAOL preset number, which unambiguously denotes a SAOL instrument.

To map MIDI program numbers into SAOL instrument preset numbers, use this formula: ``

    preset = program + bank*128

where `bank` is the MIDI program bank number. This value defaults to 0, and may be changed by using the MIDI control change command for controller 0.

In addition, as described in an earlier section, the control driver may access data structures that map SAOL preset numbers to SAOL instrument names.

Use these links to jump to a particular command:

- MIDI NoteOn Command
- MIDI NoteOff Command
- MIDI Poly Aftertouch Command
- MIDI Control Change Command
- MIDI Program Change Command
- MIDI Channel Aftertouch Command
- MIDI Pitch Wheel Command
- Special No-op Command
- Special NewTempo Command
- Special EndTime Command

## `CSYS_MIDI_NOTEON`

`

    Start a MIDI note on *extchan.

    *cmd = CSYS_MIDI_NOTEON
    *ndata = [note number, 0-127]
    *vdata = [velocity, 0-127]

    Note: *vdata = 0 treated as a
    CSYS_MIDI_NOTEOFF.

`

## `CSYS_MIDI_NOTEOFF`

`

    End a MIDI note on *extchan.

    *cmd = CSYS_MIDI_NOTEOFF
    *ndata = [note number, 0-127]

    Note: MP4-SA does not support
    release velocity.

`

## `CSYS_MIDI_PTOUCH`

`

    Polyphonic aftertouch on *extchan.

    *cmd = CSYS_MIDI_PTOUCH
    *ndata = [note number, 0-127]
    *vdata = [touch value, 0-127]

`

## `CSYS_MIDI_CC`

`

    Controller change on *extchan

    *cmd = CSYS_MIDI_CC
    *ndata = [controller number, 0-127]
    *vdata = [controller value, 0-127]

    Note: See this section
    for symbolic MIDI controller
    numbers.

`

## `CSYS_MIDI_PROGRAM`

`

    Change program on *extchan

    *cmd = CSYS_MIDI_PROGRAM
    *ndata = [program number, 0-127]

    Note: See left panel for
    MP4-SA semantics.

`

## `CSYS_MIDI_CTOUCH`

`

    Channel aftertouch on *extchan.

    *cmd = CSYS_MIDI_CTOUCH
    *ndata = [touch value, 0-127]

`

## `CSYS_MIDI_WHEEL`

`

    Pitch wheel value on *extchan.

    *cmd = CSYS_MIDI_WHEEL
    *ndata = [wheel LSB, 0-127]
    *vdata = [wheel MSB, 0-127]

    Note: Value of MIDIbend will
    be read as (*vdata)*128 +
    (*ndata). Coding follows the
    MIDI command syntax for easy
    parsing.

`

## `CSYS_MIDI_NOOP`

`

    Do nothing.

    *cmd = CSYS_MIDI_NOOP

`

## `CSYS_MIDI_NEWTEMPO`

`

    Change the tempo value.

    *cmd = CSYS_MIDI_NEWTEMPO
    *fval = [new tempo, beats/min]

    Note: The current tempo is
    available in the global
    variable tempo.

`

## `CSYS_MIDI_ENDTIME`

`

    Change the endtime value

    *cmd = CSYS_MIDI_ENDTIME
    *fval = [endtime, in beats]

    Note: Global variables
    are available that tell the
    current endtime status. To
    halt execution after the
    current execution cycle, set
    *fval to the value of the
    global variable scorebeats.

`

## `csys_saslevent`

The `csys_saslevent` function is called during an i-pass if the return value for the `csys_newdata` call is CSYS_SASLEVENTS or CSYS_EVENTS.

As shown on the right panel, the arguments for the `csys_saslevent` function are all pointers. This function places information about the new SASL event into the variables pointed to by these arguments.

A `csys_saslevent` call communicates a single SASL event. If there is another pending SASL event, the `csys_saslevent` function should return CSYS_EVENTS to request another `csys_saslevent` call. The `csys_saslevent` function is repeatedly called until it returns the value CSYS_DONE.

## `csys_saslevent`

`

    int csys_saslevent(

        unsigned char * cmd,
        unsigned char * priority,
        unsigned short * id,
        unsigned short *label,
        float * fval,
        unsigned int * pnum,
        float ** p)

    Place SASL event information
    in pointer, i.e:

       *cmd = CSYS_SASL_TEMPO;
       *fval = 61.0F;

    Return values:

       CSYS_EVENTS:

          Requests another call to
          csys_saslevent on this
          i-pass, to communicate
          another SASL event.

       CSYS_NONE:

          Indicates there are no
          more SASL events for this
          i-pass.

`

#### Command Syntax

The right panel lists the possible `*cmd` values for `csys_saslevent`. These values code the command set of the SASL language, as well as special commands. The numeric values for the SASL values maps to the MPEG 4 binary coding of `class score_line`.

The `*priority` field of `csys_saslevent` can take two values. A zero value codes normal priority. It indicates that if CPU cycles are a scarce commodity, the `sa.c` should feel free to ignore the command.

If the `*priority` field is set to 1, the SASL command is considered a high priority command, and is given preference for execution if computational resources are running low.

## `*cmd` values

`

    SASL commands:

      symbolic         hex

    CSYS_SASL_INSTR    0x00
    CSYS_SASL_CONTROL  0x01
    CSYS_SASL_TABLE    0x02
    CSYS_SASL_ENDTIME  0x04
    CSYS_SASL_TEMPO    0x05

    Special commands:

    CSYS_SASL_NOOP     0x06

    Note that hexadecimal
    values for the SASL
    command encoding matches
    the MP4-SA binary encoding
    for class score_line.

`

## `*priority` values

`

    Normal priority commands:

    *priority = 0;

    High priority commands:

    *priority = 1;

    See right panel for details.

`

#### Command Semantics

The right panel shows the semantics for each command supported by the `csys_saslevent` function. The description specifies the meaning of the `*id`, `*label`, `*fval`, `*pnum`, and `*p` fields for each command.

The right panel descriptions include many hyperlinks to the data structure descriptions in the previous section. These data structures map the `sa.c` coding of instruments and variables (the `index` field) to the SASL coding of instruments and variables (MP4 symbols and ASCII names).

Use these links to jump to a particular command:

- SASL Instr Command
- SASL Control Command
- SASL Table Command
- SASL Endtime Command
- SASL Tempo Command
- Special No-op command

## `CSYS_SASL_INSTR`

`

    Create a SAOL instr instance.

    *cmd   = CSYS_SASL_INSTR
    *id    = [instr to create]
    *label = [label for instr]
    *fval  = [duration, or -1]
    *pnum  = [number of parameters]
    *p     = [pointer to float array]

    Notes:

    [1] The *id field specifies
    the SAOL instr to create, by
    using the csys_instr[].index
    number.

    [2] The *label field is the
    SASL label, if any, of the
    instrument. Use the constant
    CSYS_NOLABEL to signify an
    unlabelled instrument, use a
    constant value greater than
    CSYS_LABELNUM to create unique
    new labels, or see this
    section for details on how
    to specify label names from a
    SASL score in the configuration
    block.

    [3] The *fval field is the
    duration of the instr, in
    units of scorebeats, or is
    -1 if the instr has indefinite
    duration.

    [4] The *p field points to a
    float array of size *pnum that
    holds the parameter values for
    the instr.  The csys_instr
    structure has information on the
    number and names of the instr
    parameters.

`

## Global `CSYS_SASL_CONTROL`

`

    Updates a SAOL global variable.

    *cmd   = CSYS_SASL_CONTROL
    *id    = CSYS_SASL_NOINSTR
    *label = CSYS_NOLABEL
    *pnum  = [variable to change]
    *fval  = [new variable value]

    [1] Set *pnum to the
    csys_global[].index
    value of the variable to be
    changed.

    [2] Set *fval to the new value of
    this variable.

`

## Labelled `CSYS_SASL_CONTROL`

`

    Updates an imports
    variable in a labelled
    instance of a SAOL instr.
    The variable must not
    have a matching variable
    in the global block.

    *cmd   = CSYS_SASL_CONTROL
    *label = [label name]
    *id    = [instr name]
    *pnum  = [variable to change]
    *fval  = [new variable value]

    [1] The *label field is
    set to the label number
    of the target instr
    instances. If the control
    driver is labelling its
    CSYS_SASL_INSTR commands
    using unique labels, it
    will know the correct label
    number to use. If the
    control command targets an
    instr created by a SASL
    instr command in the
    configuration block, see
    this section for details
    on how to specify the label
    names.

    [2] The same SASL label can
    be applied to instances of
    multiple instr types. The
    CSYS_SASL_CONTROL needs to
    be invoked multiple times in
    this case, one for each
    instr type. The *id field
    specifies the SAOL instr
    a CSYS_SASL_CONTROL command
    targets, by using the
    csys_instr[].index number.

    [3] The *pnum field is the
    csys_instr[].csys_vars[].index
    number for the variable to
    be changed (see Note below).

    [4] This data structure
    simplifies the search for the
    correct *id and *pnum values
    for some types of control
    driver applications.

    [5] Set *fval to the new value of
    the variable.

    Note: Even though the MP4-SA
    specification only allows imports
    variables to be changed, the
    CSYS_SASL_CONTROL command will
    change all instr signal variables,
    to support control drivers which
    help in SAOL program debugging.
    Changing a non-imports variable
    is not guaranteed to change SAOL
    program behavior unless the -O0
    option is used, since sfront may
    have optimized away the variable
    that is being changed.

`

## `CSYS_SASL_TABLE`

`

    Updates a SAOL global or future
    table.

    *cmd   = CSYS_SASL_TABLE
    *id    = [table to modify]
    *label = [wavetable generator]
    *pnum  = [number of parameters]
    *p     = [pointer to float array]

    [1] Set *id to the
    csys_global[].index
    value of the table to be changed.
    Note that both global and future
    tables are in this array.

    [2] Set *label to one of these
    constants to indicate the
    wavetable generator:

      symbolic                hex

    CSYS_SASL_TGEN_SAMPLE      6F
    CSYS_SASL_TGEN_DATA        70
    CSYS_SASL_TGEN_RANDOM      71
    CSYS_SASL_TGEN_STEP        72
    CSYS_SASL_TGEN_LINESEG     73
    CSYS_SASL_TGEN_EXPSEG      74
    CSYS_SASL_TGEN_CUBICSEG    75
    CSYS_SASL_TGEN_POLYNOMIAL  76
    CSYS_SASL_TGEN_SPLINE      77
    CSYS_SASL_TGEN_WINDOW      78
    CSYS_SASL_TGEN_HARM        79
    CSYS_SASL_TGEN_HARM_PHASE  7A
    CSYS_SASL_TGEN_PERIODIC    7B
    CSYS_SASL_TGEN_BUZZ        7C
    CSYS_SASL_TGEN_CONCAT      7D
    CSYS_SASL_TGEN_EMPTY       7E
    CSYS_SASL_TGEN_DESTROY     7F

    Note that the numeric values match
    the MP4 symbol token values for
    these wavetable generators. Also
    note that opcode and wavetable
    "buzz" are different token!

    [3] With the exception of the
    SAMPLE generator, the *pnum
    field is the number of wavetable
    parameters, and the *p field points
    to an array of floats that
    are the parameters of the
    wavetable generator. Note the
    CONCAT generator has table
    values for some of its parameters.
    For these parameters, use the
    csys_global[].index value for
    the table, cast into a float.

    [4] The SAMPLE generator uses
    the *pnum and *p arguments in
    a different way. The *pnum value
    is 4 + the number of actual
    samples in the wavetable. The
    control driver is responsible
    for executing the semantics of
    the size and skip
    field, to figure out this actual
    size.

    The first four elements in the
    *p array have special meanings,
    that map into fields of the
    class sample of the MP4 binary
    file:

    (*p)[0]  sampling rate (Hz) (srate)
    (*p)[1]  loop start (index) (loopstart)
    (*p)[2]  loop end  (index)  (loopend)
    (*p)[3]  base frequency (Hz) (basecps)

    A value of -1 for these parameters
    codes "unknown". The actual sample
    elements are placed in elements
    (*p)[4] to (*p)[(*pnum)-1]. Note
    that the loop start and loop end
    values are not offset by 4 -- so
    a loop start at the top of the
    table would be set to 0 (not 4)
    and a loop end at the last element
    of the table would be set to
    (*pnum) - 4 (not *pnum).

`

## `CSYS_SASL_ENDTIME`

`

    Executes SASL endtime command.

    *cmd = CSYS_SASL_ENDTIME
    *fval = [endtime in scorebeats]

    Note: Global variables
    are available that tell the
    current endtime status. To
    halt execution after the
    current execution cycle, set
    *fval to the value of the
    global variable scorebeats.

`

## `CSYS_SASL_TEMPO`

`

    The SASL tempo command

    *cmd = CSYS_SASL_TEMPO
    *fval = [new tempo, beats/min]

    Note: The current tempo is
    available in the global
    variable tempo.

`

## `CSYS_SASL_NOOP`

`

    Do nothing.

    *cmd = CSYS_MIDI_NOOP

`

## Registration

The right panel shows how to register your control driver with the `sfront` sources. Registration is necessary in order for sfront to add your control driver flag to the permissible arguments to the `-cin` command line switch.

## Step 1: Create Libraries

`

    [1] cd sfront/src/lib

    [2] Edit Makefile, and add
    name of your control driver
    to the CSYS list.

    [3] Type "make". This will
    create:

    sfront/src/csyslib.c
    sfront/src/csyslib.h

    which includes an embedded
    version of your driver. Search
    in the files for your driver
    name to verify.

    [4] Whenever you change your
    driver code in sfront/src/lib/csys
    you will need to remake the
    csyslib.c file.

`

## Step 2: Edit `sfront/src/control.c`

`

    In sfront/src/control.c, make
    these additions, to add driver
    "mydriver".

    [1] Add the constant definition

    #define CDRIVER_MYDRIVER

    top the top of the file. Give it
    the current numerical value of
    CDRIVER_END, and then increase
    the value of CDRIVER_END by 1.

    [2] Add a printf line to the
    function

    void printcontrolhelp(void)

    that describes the -cin mydriver
    flag. This will be printed out
    when "sfront -help" is invoked.

    [3] Add an if statement to the
    funtion cinfilecheck that takes
    this form:

    if (!strcmp(fname,"mydriver"))
     {
       cin = CDRIVER_MYDRIVER;
       csasl = 1;
       cmidi = 1;
       cmaxchan = 3;
       clatency = ?_LATENCY_DRIVER;
       return 0;
      }

      [a] Set csasl to 1 if your
          driver supports SASL
          commands, else set it
          to 0. If you set it to
          1, the sa.c file expects
          your driver to define the
          csys_saslevent() function.

      [b] Set cmidi to 1 if your
          driver supports MIDI
          commands, else set it to 0.
          If you set it to 1, the sa.c
          file expects your driver to
          define the csys_midievent()
          function.

      [c] If you set cmidi to 1, set
          cmaxchan to 1 + the number
          of extended MIDI channels
          your driver supports. Don't
          set this value unnecessarily
          high, since it makes the
          executable larger.

      [d] Set clatency to

          LOW_LATENCY_DRIVER

          if your driver is
          interactive, for example a
          a MIDI In jack driver.

          Otherwise, set clatency
          to

          HIGH_LATENCY_DRIVER;

    [4] Add this case entry to the
    switch statement in

    void makecontroldriver(int num)

      case CDRIVER_MYDRIVER:
        makemydriver();
        break;

    This call actually does the code
    insertion into the sa.c file: it
    calls the function created in
    csyslib.c from your driver file.

`

## Step 3: Compile `sfront`

`

    Type "make" in sfront/src to
    compile sfront with your driver.

    During driver development, you
    should edit your original driver
    source in

    sfront/src/lib/csys/mydriver.c

    then to test first:

    cd sfront/src/lib/
    make

    and then:

    cd sfront/src
    make

`

## Bug Reports

Please follow these guidelines when sending along bug reports and suggestions for the control driver interface. Thanks!

Next section: Part II/3: Audio Drivers.

Copyright 1999 John Lazzaro and John Wawrzynek.