home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Elysian Archive
/
AmigaElysianArchive.iso
/
sound
/
midi
/
midi20.arc
/
XFile.46
< prev
next >
Wrap
Text File
|
1988-10-27
|
39KB
|
975 lines
MIDI LIBRARY
==== =======
WHAT IS THE MIDI LIBRARY?
---- -- --- ---- --------
The MIDI library is a standard Amiga disk-based library that provides
multitasking access to MIDI resources such as the serial port. The
library is based around a "copying" message system so that a single
message sent in can cause several messages to be sent back out. Included
as part of the library are tasks that convert serial I/O to MIDI messages
which can be sent to any task that cares to listen. This provides for
unlimited sharing of the serial port by MIDI applications.
The need for such a service seems obvious in a multitasking environment.
Without this sort of interface, two tasks trying to receive MIDI data
from the serial port at the same time will exclude each other from
working correctly. Neither of them will get all the data they seek. By
using the MIDI library this problem is eliminated because there is only
one task actually receiving data from the serial port. Data is then
distributed to all who are waiting for it.
A similar problem occurs with transmitting. MIDI is based around a
message system. Each message contains a certain number of bytes and must
be received intact to make sense to the receiver. If two tasks try to
transmit data to the serial port at the same time it is likely that the
partial messages from each stream will merge producing disastrous
results. The MIDI library handles this by keeping messages intact, and
again only one task is actually sending data to the serial port.
The MIDI library is intended for developers writing MIDI applications to
use with their products. The library takes care of all the grunginess of
transferring MIDI data between your applications and the serial port.
Instead of getting an unformatted stream of bytes your programs receive
complete messages as asynchronous events.
Features
--------
* Unlimited MIDI message merging.
* Provides an interface for multiple MIDI applications to access
the serial port without conflict.
* Versatile MIDI filtering including message, channel, controller,
and SysEx ID number filtering.
* Exists as a standard disk-based Amiga library less than 10K in
size.
* Supports the full MIDI message specification including system
exclusive, 3 byte SysEx ID's, and MTC.
* Transfers MIDI packets using the Amiga Executive message system.
* Entirely written in Assembly.
OVERVIEW
--------
Tasks wishing to perform MIDI I/O create "source" and "destination"
nodes. These nodes are where messages are sent to and received from.
Sources and destinations are then "routed" together. Once connected in
this manner, MIDI messages that are placed at the source node are
distributed to destination nodes connected to that source. Any number of
source and destination nodes may exist at one time and any number of
routes can be connected to a source or destination.
In addition to piping information along, a route processes MIDI data.
The following operations can be performed by a route in any combination:
Message filtering
Channel filtering
Channel offset
Note offset
Controller number filtering
Sys/Ex id number filtering
In addition to nodes that the applications create there are "resident"
nodes that launch built-in tasks. Currently the only built-in tasks are
for transmitting and receiving MIDI messages to and from the Amiga's
serial port.
Covered in this remainder of this document are in depth descriptions of
each of the major sections and concepts within the MIDI Library. In
general, there are a few rules you should keep in mind when using the
MIDI Library (and all other Amiga resources):
1. You must free any resource that you allocate (such as an MSource,
MDest, MRoute, MidiPacket, etc) when you are done with it.
2. Only those fields within a structure that are explicitly
documented as writable should be treated as such.
3. Any shared lists must be locked prior to examination (there are
various list locking functions described in the Inner Workings
section).
More detailed information about each MIDI Library function can be found
in midi.doc. Examine midi/midi.h (or midi/midi.i) for more information
on the structures used within the MIDI Library and some handy constants
not described in this document.
This document assumes that you have access to and are familiar with MIDI
1.0 Spec v4.0 from the IMA. Also, familiarity with C and the Amiga Rom
Kernel are assumed.
LIBRARY
-------
Before using the MIDI library it must be opened like any other Amiga
library. The global symbol for the library base is "MidiBase".
MidiBase = OpenLibrary (MIDINAME,MIDIVERSION);
The constants MIDINAME and MIDIVERSION are defined in the include file
midi/midi.h. MIDINAME is "midi.library" and as of this writing
MIDIVERSION is 7.
When done using the library you should close it:
CloseLibrary (MidiBase);
SOURCE AND DESTINATION NODES
------ --- ----------- -----
The MIDI source and destination nodes are the terminals for MIDI
communication. Messages are placed at a source to be distributed to
destinations through whatever routes are connected to the source.
Nodes can be either public or private. Private nodes can only be
accessed by the task that created them. Public nodes are available to
everyone. This way an application can perform it's own private
communications or can be made available for routing in a patch bay.
The MIDI source is defined in "midi/midi.h" is follows:
struct MSource {
struct Node Node;
struct Image *Image;
struct MinList RPList;
APTR UserData;
UWORD RouteMsgFlags;
UWORD RouteChanFlags;
};
Node
An instance of an Exec Node used for linking public nodes into
the library's source list. The ln_Name field is set for public
nodes to the name supplied by the caller. ln_Type is set to
NT_MSOURCE for user MSource's or NT_RESMSOURCE for resident
MSource's.
Image
Points to an Intuition Image structure. The image may be used
by graphics-based patch bay applications. It is only used for
public nodes. A NULL here indicates that the creator of this
node does not have an Image for it; patch bay programs may deal
with that as they see fit.
RPList
A linked list of MRoutePtr structures. Each member of the list
is attached to an MRoute structure. Scanning this list allows
you to determine what other nodes are connected to this one.
UserData
A generic pointer to any user extension data. For resident
nodes this points to an instance of an MTaskInfo structure.
RouteMsgFlags
RouteChanFlags
The sum of MsgFlags and ChanFlags, respectively, in all routes
attached to the MSource. This is used internally by the
message distributor as a pre-check for which message classes
actually need to be transmitted.
In general you will only be concerned with a pointer to an MSource, not
its contents. It is not safe to touch anything in the MSource structure
except UserData and then only if you created the MSource. You are free
to examine anything in the MSource structure such as RPList to determine
the sort of route connections to the MSource. If you plan to do this, be
sure to read the section on the Inner Workings so that you will
understand all the necessary precautions.
There are three functions dealing with MSource's:
CreateMSource (name,image)
Allocates, initializes, and returns a pointer to a new MSource
structure. If name is non-NULL the MSource will be considered
public and will be linked into the library's source list.
Otherwise it will be considered private and won't be linked
into the library's source list.
DeleteMSource (source)
Removes and frees a source created by CreateMSource().
FindMSource (name)
Looks for a public MSource with the specified name. If it is
found a pointer to it is returned.
See "midi.doc" for more thorough information on these and other MIDI
Library functions.
The MIDI destination is defined in "midi/midi.h" is follows:
struct MDest {
struct Node Node;
struct Image *Image;
struct MinList RPList;
struct MsgPort *DestPort;
APTR UserData;
struct MRouteInfo DefaultRouteInfo;
};
These fields differ from an MSource
Node
The same as in an MSource except ln_Type is set to NT_MDEST for
user MDest's or NT_RESMDEST for resident MDest's.
DestPort
Points to an Exec MsgPort that is automatically created. This
is where MIDI messages will arrive. The port flags are set to
PA_SIGNAL so you can Wait() for something to arrive. You don't
GetMsg() from this port, however. See the messages section for
the proper way to read messages.
DefaultRouteInfo
Used as storage for the default connection options for this
MDest. There is more on this subject in the Routing section.
The only field in this structure that you are likely to ever need to
access is the DestPort since you will need to Wait() for messages to
arrive there. Otherwise, the same guidelines that apply to MSource's
apply to MDest's: don't tamper with anything other than UserData and
only if you created the MDest.
As with MSource there are three functions dealing with MDest's that have
essentially the same functions as those for MSource's:
CreateMDest (name,image)
Allocates, initializes, and returns a pointer to a new MDest
structure. If name is non-NULL the MDest will be considered
public and will be linked into the library's destination list.
Otherwise it will be considered private.
DeleteMDest (source)
Removes and frees a source created by CreateMDest(). Also any
unread messages will be freed.
FindMDest (name)
Looks for a public MDest with the specified name. If it is
found a pointer to it is returned.
FlushMDest (dest)
Disposes of any MidiPackets pending at the specified MDest.
At this point it is worth discussing the Resident Nodes currently
available. Resident nodes are public nodes that are always present in
the public node lists and are physically part of the MIDI Library. They
can be routed to just like any other public node as will be described in
the next section. There are currently two resident nodes.
name type description
---- ---- -----------
MidiIn source Converts incoming data from the Amiga's serial port
to MIDI Messages. Any MDest's that you route to
MidiIn will be able to receive messages from any
MIDI devices connected to the serial port.
MidiOut dest Converts MidiPackets to data sent out the Amiga's
serial port. Any MSource's that you route to
MidiOut will then be able to transmit MIDI messages
to devices connected to the serial port. Any
number of MSource's can be routed to it without
fear of mangled data; messages are merged
properly. Each message sent to it will be
transmitted to the serial port in the order
received.
ROUTES
------
Routes are the "cables" that connect sources to destinations. Nodes can
have any number of routes connected to them and routes may be connected
in parallel. Additionally routes process MIDI messages as they are
transferred along them.
The route structure and related structures are defined in "midi/midi.h"
is follows:
The MRoute structure is the actual route structure managed by the
library. You probably need not be concerned with it's contents.
struct MRoute {
struct MSource *Source;
struct MDest *Dest;
struct MRoutePtr SRoutePtr, DRoutePtr;
struct MRouteInfo RouteInfo;
};
Source
Points to the source end or input to this route. If is
NULL, then the source no longer exists.
Dest
Points to the destination end or output of this route. If
is NULL, then the destination no longer exists.
SRoutePtr
DRoutePtr
Instances of the MRoutePtr structure. These are linked
into the source and destination RPLists respectively. See
midi/midi.h for the actual MRoutePtr structure definition.
RouteInfo
An instance of an MRouteInfo structure. This is a copy of
data supplied by the user.
When creating a route you have the option of using the default MRouteInfo
structure within the target MDest or supplying your own. This structure
is what determines the behavior of a route: filtering, event processing,
etc. There is a great deal of flexibility offered by this system.
struct MRouteInfo {
UWORD MsgFlags;
UWORD ChanFlags;
BYTE ChanOffset;
BYTE NoteOffset;
struct RIMatch SysExMatch;
struct RIMatch CtrlMatch;
};
MsgFlags
Flag bits indicating which messages are to be supported by this
route. "midi/midi.h" contains MMF_ constants which may be ORed
together. A value of MMF_ALL indicates all message types.
ChanFlags
Flag bits indicating which channels for channel messages are to
be supported by this route. The least significant bit
corresponds to MIDI channel 1, the most significant bit
corresponds to MIDI channel 16. A value of 0xffff indicates
all channels.
ChanOffset
A signed offset to be applied to all channel messages passed
through this route. If the resulting channel is out of range
the message is not sent.
NoteOffset
A signed transposition offset to be applied to note on and off
messages in half steps. If the resulting note number is out of
range the message is not sent.
SysExMatch
An instance of an RIMatch structure. It allows you to specify
up to three one-byte System Exclusive ID numbers or one
three-byte ID to pass or block through this route. If left
unset, all will be passed. To use this the MMF_SYSEX bit must
be set in MsgFlags. See below for more on this.
CtrlMatch
An instance of an RIMatch structure. It allows you to specify
up to three controller numbers to pass or block through this
route. If left unset, all will be passed. To use this the
MMF_CTRL bit must be set in MsgFlags.
The RIMatch structure is used to specify up to three values to match.
struct RIMatch {
UBYTE Flags;
UBYTE Match[RIM_MAXCOUNT];
};
Flags
Indicates how the value(s) in Match should be treated. A value
of 0 causes Match to be ignored. A value of 1 to 3 is used to
specify how many one-byte values are contained in Match.
Without any other flags set, this indicates that only the
value(s) in Match will pass through this route.
If the RIMF_EXCLUDE bit is set (OR with value of 1-3), the
logic is reversed such that all except those values in Match
are passed.
SysExMatch only: If the RIMF_EXTID bit is set, Match is
assumed to contain exactly one three-byte System Exclusive ID.
This may be combined with RIMF_EXCLUDE.
Match
Value(s) to match packed left to right.
In order to create a route you need to have a source pointer, a
destination pointer, and optionally a properly filled out MRouteInfo
structure. The contents of your MRouteInfo is copied to the created
route so you do not need to preserve it after creation.
There are several functions dealing with route management. First a note
about public nodes. Since public nodes are available to other tasks
besides the creator there needs to be a way to insure the validity of a
given node between the time that someone finds it and the time that
someone attempts to route to it. There is a pair of functions to lock
the node lists to prevent a node from being removed while you are dealing
with it. Additionally there are some special routing functions for
dealing with public nodes that lock the lists for you. Thus, there is
route creation function for every permutation of public and private nodes.
source dest function
------ ---- --------
private private CreateMRoute
private public MRouteSource
public private MRouteDest
public public MRoutePublic
CreateMRoute (source,dest,routeinfo)
Allocates and links a route into the specified source and
destination nodes. The data pointed to by routeinfo, if
supplied, is copied to the new route structure. This is
primarily used when both source and destination are private.
MRouteSource (source,destname,routeinfo)
Routes a source to a named public destination.
MRouteDest (sourcename,dest,routeinfo)
Routes a named public source to a destination.
MRoutePublic (sourcename,destname,routeinfo)
Routes a named public source to a named public destination.
In all cases above, you may supply your own MRouteInfo by passing a
pointer to it or use the DefaultMRouteInfo within the target MDest by
passing NULL instead.
The remaining functions deal with modifying and deleting routes:
ModifyMRoute (route,newrouteinfo)
Copies the contents of the new MRouteInfo structure to the
specified route.
DeleteMRoute (route)
Unlinks and frees a route.
SetDefaultMRouteInfo (dest,newrouteinfo)
Copies the contents of the supplied to MRouteInfo to
DefaultMRouteInfo in the supplied MDest. This does not affect
any routes already connected to the MDest, only the template
for creating new routes. You should only call this routine on
MDest's that you created.
You are responsible for deleting any routes you create even if the nodes
it connected have been deleted. You should not delete or modify anyone
else's routes.
MESSAGES AND PACKETS
-------- --- -------
From the point of view of the MIDI Library, a MIDI Message is simply an
array of UBYTEs containing a status byte as its first byte and any data
bytes associated with that message. With the exception of system
exclusive, all messages are from 1 to 3 bytes in length. A given status
byte always has the same number of data bytes. Messages never contain
embedded Real Time status bytes as these are sent as separate messages.
Messages never rely on running status so there will always be a status
byte at the beginning of every message and there will never be more than
one message worth of data bytes within that message.
System exclusive messages can be any length. Also, the last byte in a
system exclusive message is the EOX status byte used as a terminator
(like '\0' in a C string).
The MidiPacket structure is built around the above MIDI Message concept.
This structure is what arrives at the DestPort within an MDest.
struct MidiPacket {
struct Message ExecMsg;
UWORD Type;
UWORD Length;
ULONG reserved;
UBYTE MidiMsg[4];
};
ExecMsg
The standard Exec Message node used to send to MsgPorts.
Type
An MMF_ message type value for the message contained in this
packet as returned by MidiMsgType().
Length
The length of this message in bytes as returned by
MidiMsgLength().
MidiMsg
The actual message as described above. There always at least 4
bytes here, but the actual length that you should use is found
in Length. Specifically, system exclusive packets more often
than not have more than 4 bytes here.
MIDI Messages (just the UBYTE array) are used for transmitting messages
to an MSource. MidiPackets are used for receiving messages at an MDest.
MidiPackets are always created by the MIDI Library, never by you.
Consider them entirely read-only.
These functions deal with receiving MidiPackets:
GetMidiPacket (dest)
Gets the first MidiPacket available at a destination and
returns a pointer to it. If none are available NULL is
returned. Any MidiPacket that you receive from GetMidiPacket()
should be freed with FreeMidiPacket(). See below for an
example packet reader.
FreeMidiPacket (packet)
Frees a MidiPacket received from GetMidiPacket().
These functions deal with sending MIDI Messages:
PutMidiMsg (source,msg)
Places a single MIDI Message at a source for distribution.
Upon return you may recycle your msg buffer since its contents
has been copied to any MidiPackets that have been sent. This
function assumes that you have constructed a valid MIDI message
as described above. Invalid or undefined messages are ignored,
however an unterminated system exclusive message cannot be
detected and runs the risk of crashing the machine.
PutMidiStream (source,fillbuffer,buf,bufsize,cursize)
Converts an unformatted data stream into MIDI messages which
are then sent by calling PutMidiMsg(). See midi.doc for proper
usage.
These functions give you information about specific MIDI Messages:
MidiMsgType (msg)
Returns an MMF_ flag indicating the type message. It returns 0
for invalid or undefined messages.
MidiMsgLength (msg)
Returns the number of bytes in a message. For non-exclusive
messages this is the status byte + data bytes. For system
exclusive messages it is status byte + data bytes + EOX. 0 is
returned for invalid or undefined messages.
These functions deal with receiving MIDI Messages w/o the packets and are
primarily here for backwards compatibility. They do little more than
apply an offset from the beginning of the MidiPacket to the MidiMsg field
and call the corresponding MidiPacket function. It is not recommended
that these be used in new code since they are less efficient from the
point of the Library and the caller:
GetMidiMsg (dest)
Gets the first MIDI message available at a destination and
returns a pointer to it. If none are available NULL is
returned. Any message that you receive from GetMidiMsg()
should be freed with FreeMidiMsg(). See below for an example
message reader.
FreeMidiMsg (msg)
Frees a message received from GetMidiMsg().
Some additional notes about messages:
1. Invalid or undefined status bytes are ignored.
2. EOX is not considered a valid status byte on its own. It is only
used to terminate system exclusive messages and will be ignored
if sent by itself using PutMidiMsg().
3. Incomplete messages (such as system exclusive messages without a
manufacturer ID) are ignored.
4. Note On messages with velocity == 0 are considered Note Off
messages. MidiMsgType() will return MMF_NOTEOFF rather than
MMF_NOTEON for these.
5. Controller numbers 121-127 are reserved for MIDI Mode Messages
and are treated as such. MidiMsgType() will return MMF_MODE
rather than MMF_CTRL for these.
Here is a code fragment showing the recommended MidiPacket receive
technique:
domidi(dest)
struct Dest *dest;
{
struct MidiPacket *packet;
while (!done) {
Wait (1L << dest->DestPort->mp_SigBit);
while (packet = GetMidiPacket (dest)) {
/* process the packet: */
/* perhaps examine packet->Type */
/* or packet->Length */
/* or the actual Message in packet->MidiMsg */
.
.
/* then free it */
FreeMidiPacket (packet);
}
}
}
It's important to understand a little about how MidiPackets are
allocated. For all message types other than System Exclusive,
MidiPackets are allocated from a pre-allocated pool of MidiPackets. This
is considerably quicker than having to call AllocMem() each time a
MidiPacket needs to be allocated. The pool gets initialized the first
time you create a route. If the pool becomes full, it gets extended but
will not be pruned until someone calls FlushMDest() or DeleteMDest(). It
gets totally expunged when the last user of the library calls
CloseLibrary().
If you cache MidiPackets for any length of time, your memory may vanish
to increase the size of the packet pool. In order to get it back you
will have to call FlushMDest().
System Exclusive packets are allocated using AllocMem() which explains
why they are marginally slower to route than non-exclusive messages.
INNER WORKINGS
----- --------
This section is for the more adventurous MIDI programmer. The
information here is necessary for authors of patch bay or other route
managing applications.
MidiBase
--------
The structure of the library base is defined in "midi/midibase.h". This
structure is strictly READ-ONLY. You may examine fields with care as
noted below.
struct MidiBase {
struct Library libnode;
struct List SourceList, DestList;
struct SignalSemaphore ListSemaphore;
struct SignalSemaphore RouteSemaphore;
BPTR SegList;
APTR SysBase, DosBase;
struct MinList SignalList;
};
libnode
Standard Amiga library node.
SourceList
An Exec linked list containing all public MSource nodes.
DestList
An Exec linked list containing all public MDest nodes.
ListSemaphore
A SignalSemaphore for locking the source and destination
lists. This is the semaphore used when you call LockMidiBase()
or UnlockMidiBase(). Exclusive access to lists is required
when managing or scanning either the source or destination
list. It is not required for message propagation. It is
required for route management only when a public node is
involved.
RouteSemaphore
A SignalSemaphore for locking the route system. The routines
LockMRoutes() and UnlockMRoutes() use this semaphore.
Exclusive access to the routing system is required for message
propagation and route management. It is not needed for
managing or scanning the node lists. Use this with care since
it blocks message propagation.
SegList
A BPTR to the segment list for the library.
SysBase & DosBase
The library's pointers to Exec and Dos.
SignalList
The list of MListSignal nodes.
Only SourceList and DestList are likely to be of any interest to patch
bay programmers. The rest can be ignored.
These routines are useful for examining the base:
LockMidiBase()
Gains exclusive access to the source and destination lists.
Use of this will block anyone else from managing nodes or
scanning the lists. Messages propagation is not blocked,
however. Calls may be nested but each call must be matched
with a call to UnlockMidiBase().
UnlockMidiBase()
Relinquishes exclusive access to the source and destination
lists.
These routines are useful for examining the routes attached to a specific
source or destination:
LockMRoutes()
Gains exclusive access to the route system. Use of this will
block message propagation and route management. Calls may be
nested but each call must be matched with a call to
UnlockMRoutes().
UnlockMidiBase()
Relinquishes exclusive access to the route system.
You shouldn't be very likely to need to lock the route system, however,
and if you do it should only be to read the route lists not to modify
anything. A patch bay application should not use this approach to keep
track of its routes. Instead it must maintain its own list of routes.
As long as everyone observes the rule of modifying only their own
structures there shouldn't be any trouble.
Signals
-------
There is a system by which your task can be signaled when either of the
public node lists changes. This permits a patch bay system to be
notified when it needs to redraw its display of available nodes.
struct MListSignal {
struct MinNode Node;
struct Task *SigTask;
UBYTE SigBit;
UBYTE Flags;
};
Node
Standard Exec MinNode used to link this MListSignal into
SignalList.
SigTask
Pointer to the task owning this MListSignal.
SigBit
Signal bit for this MListSignal. Used to Signal() SigTask on
public node list change.
Flags
Flag bits describing which lists to watch for this signal.
This entire structure should be considered READ-ONLY. The only field you
should be concerned with examining is SigBit since you'll need to Wait()
for that in order to find out when a list changes. The signals do not
accumulate and you don't know what changed when you receive one, just
that something you were looking for has changed since you last received a
signal.
These functions manage MListSignals:
CreateMListSignal (flags)
Adds your task to the list of those to be signaled when the
public node lists change. Flags contains either MLSF_SOURCE,
MLSF_DEST or both depending on which list(s) you want to
monitor. The function returns a pointer to an MListSignal
which you must keep and pass to DeleteMListSignal() when you
are done with it. A signal bit is allocated for you when you
call this and is placed in SigBit.
DeleteMListSignal (signal)
Removes and frees an MListSignal allocated by CreateMListSignal.
Resident Nodes
-------- -----
As noted before there are some nodes that are always present. They are
created by the library when it is first loaded. These nodes are attached
to built-in tasks (actually AmigaDOS Processes) that are launched when
the nodes are routed to. There are currently only two resident nodes:
one for receiving from the serial port and one for transmitting to the
serial port.
MidiIn source Launches a task called MidiIn running at +30
priority when it is first routed to. It receives
data from the serial port and converts it to MIDI
messages which are posted to its MSource node.
Anyone with destinations routed to this node will
receive MIDI messages from it.
MidiOut dest Launches a task called MidiOut which also runs at
+30 priority. It is responsible for receiving
messages from it's MDest node and sending them to
the serial port.
Resident nodes can be identified by a node type value (in ln_Type) of
NT_RESMSOURCE or NT_RESMDEST. Resident nodes have an additional
MTaskInfo structure attached to them that is pointed to by the UserData
field in the MSource or MDest structure.
This rest of the information about resident nodes is provided merely for
the curious and is not terribly useful. Consider ALL of this stuff to be
READ ONLY. And be forewarned that it may change if necessary. The
MTaskInfo structure is defined in "midi/midibase.h".
struct MTaskInfo {
char *Name;
WORD Pri;
void (*Entry)();
UWORD Stack;
UWORD Sources;
struct MNodeInfo *SourceList;
struct Dests;
struct MNodeInfo *DestList;
struct SignalSemaphore Semaphore;
UWORD UsageCount;
struct MsgPort *TaskPort;
BPTR Segment;
};
These fields are defined within each task's module:
Name
Points to the name of the task.
Pri
The task's priority.
Entry
The start of the task's code.
Stack
The size of stack to allocate for the task.
Sources
The the number of MSource nodes defined for this task.
SourceList
Points to an array of MNodeInfo structures containing the
definition of this task's MSource nodes.
Dests
Is the the number of MDest nodes defined for this task.
DestList
Points to an array of MNodeInfo structures containing the
definition of this task's MDest nodes.
These fields are managed by the library:
Semaphore
A signal semaphore for locking this MTaskInfo structure. This
is only used when launching or shutting down this task.
UsageCount
The number of accessors to this task. If it is non-zero the
task is running otherwise it is dormant.
TaskPort
Points to the message port used to communicate with the task.
Segment
A BPTR to a fake segment list necessary for CreateProc(). The
segment contains a jump instruction to the startup code for the
task. The task's code is actually part of the library's
segment list. This might be considered making use of
undocumented features, but as long as no one tries to
UnloadSeg() this segment (and no one should) there shouldn't be
any trouble.
The MNodeInfo structure referred to above is used to define the resident
source or destination nodes attached to this task. Tasks are not
restricted to just one resident node, but that is the current usage.
This structure is also defined in "midi/midibase.h".
struct MNodeInfo {
char *Name;
struct Image *Image;
APTR Node;
};
Name
Points to the name of the node. This shouldn't ever be NULL.
Image
Points to an Intuition Image structure for this node. This may
be NULL (and currently is in all cases).
Node
Points to the node allocated for this MNodeInfo structure so
that the task can find it easily.
SERIAL I/O CONSIDERATIONS
------ --- --------------
The resident nodes that access the serial port do so by using
serial.device - the standard device driver for the serial port.
serial.device does however have a hard time receiving MIDI data at full
speed, even with the SERF_RAD_BOOGIE flag set. It occasionally loses a
byte or two on larger transfers such as long system exclusive messages.
I have heard that there are also similar problems when running at 19200
baud, so it seems likely that some attention will be given to improving
serial.device in future releases of the operating system (like v1.4).
In the meantime, it is strongly recommended that you make sure that you
get what you expect when reading system exclusive messages from MidiIn.
Use whatever error detection methods are appropriate for the data being
transferred (like dump sizes or checksums).
Some sequencers use interrupt driven serial I/O for speed reasons.
These, from what I've seen, do not suffer from the lost data problems
that serial.device does. I have given the matter of using my own serial
interrupt handler some thought and these are my reasons for not doing so
at this time.
1. serial.device is the polite way of accessing the serial hardware
and it should be used whenever possible as it does provide a nice
interface to the hardware and is presumably compatible with any
future serial hardware that comes along.
2. Serial interrupt handlers are exclusive: only one task can own
the serial interrupts at a time. Right now there are a few MIDI
applications that use serial.device directly for MIDI. These do
not directly conflict with the MIDI Library so long as both are
not attempting to read or write data to the serial port at
exactly the same time. If MIDI Library were to use its own
interrupt system, these types of applications will not be able to
access MIDI at all while someone is using MIDI Library.
3. The problem does not appear bad enough (at least in my own
applications) to warrant it.
4. Commodore will presumably fix the problem eventually.
5. MIDI Library uses memory allocations occasionally during message
routing. Interrupt code cannot call the memory allocator. (This
can be worked around by implement the interrupt handler as simply
a queue that is read by a task which in turn calls MIDI Library)
CONCLUSION
----------
Hopefully this document has provided you with an understanding of the
MIDI library and will enable you to make use of its features in your own
MIDI applications.
Good luck.
COPYRIGHT AND DISTRIBUTIONS
--------- --- -------------
The MIDI Library is Copyright (c) 1987, 1988, Pregnant Badger Music. All
rights reserved. The MIDI Library may be used in and included with
commercial products with the following provisos:
1. Pregnant Badger Music should be kept informed of your intent to
release products that make use of the MIDI Library so that we may
keep you informed of changes, updates, etc.
2. The above copyright notice should be included somewhere in the
documentation accompanying any such commercial products.
Bill Barton
Pregnant Badger Music
1111 El Sur Way
Sacramento, CA 95864
(916) 487-9472
Bix: peabody
PLink: peabody