|
DixShtix | |||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: INNER | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object | +--com.dixshtix.midi.Event
The indivisible building blocks of all MIDI datafiles.
This class represents Midi File Events which differ from MIDI on the wire.
Overview of Midi Data on the Wire
MIDI data is a serial stream of bytes representing MIDI commands. A MIDI command comprises a status byte followed by a variable number of data bytes (possibly none).
Status bytes are identified due to their top bit being set. Consequently data bytes contain 7-bit values (0-127).
There are two main types of status bytes : Channel (or Voice) messages and System messages.
It has become the main route for expansion of the standard. It provides for manufacturer defined, device-specific messages, and any other messages involving larger amounts of data (ie blocks of data rather than the odd byte or two) than allowed for in all other messages.
Field Summary | |
(package private) int |
delta_time
Variable length time; 0 - 0x0fffffff, 0 - 268,435,455 pulses. |
(package private) java.lang.Object[] |
params
Various parameters to the above EventType. |
(package private) EventType |
type_name
The type of Events in a MIDI file differ from those on the wire. |
Constructor Summary | |
Event(EventType et,
int delta_time,
java.lang.Object[] parameters)
Initializer. |
Method Summary | |
static Event[] |
decode(byte[] data,
boolean eot_magic,
EventType[] incl,
EventType[] excl,
EventCallback pre_processor,
EventCallback post_processor)
Decode an opaque array of bytes into Event objects. |
static byte[] |
encode(Event[] events,
boolean eot_magic,
boolean add_eot,
boolean running_status,
EventCallback unknown_processor)
Encode an array of events into a format suitable for storing in a MIDI file or a part of a Track. |
(package private) static byte[] |
fetchBER(byte[] data,
int offset)
Extract a copy of the bytes which represent a variable-length encoded quantity. |
int |
getDTime()
Accessor Method. |
int |
getNParam()
Accessor Method. |
java.lang.Object |
getParam(int index)
Accessor Method. |
java.lang.Object[] |
getParams()
|
EventType |
getType()
Accessor Method. |
(package private) static byte[] |
packBER(int num)
Some numbers in MTrk chunks are represented in a form called a variable- length quantity. |
static int |
read_14_bit(byte[] bytes,
int offset)
Decode a pair of 7-bit bytes into a 14 bit unsigned integer. |
java.lang.String |
toString()
Encode event as human-readable String. |
(package private) static int |
unpackBER(byte[] data)
Given a array of bytes, return the Variable-Length encoded 28-bit integer. |
static void |
write_14_bit(int num,
byte[] bytes,
int offset)
Encode a 14 bit number into a hi and lo 7-bit parts. |
Methods inherited from class java.lang.Object |
|
Field Detail |
int delta_time
In a MIDI file, an event's "time" precedes the data bytes that make up that event itself. In other words, the bytes that make up the event's time-stamp come first. A given event's time-stamp is referenced from the previous event. For example, if the first event occurs 4 clocks after the start of play, then its "delta-time" is 04. If the next event occurs simultaneously with that first event, its time is 00. So, a delta-time is the duration (in clocks) between an event and the preceding event.
NOTE: Since all tracks start with an assumed time of 0, the first event's delta-time is referenced from 0.
A delta-time is stored as a series of bytes which is called a variable length quantity. Only the first 7 bits of each byte is significant (right-justified; sort of like an ASCII byte). So, if you have a 32-bit delta-time, you have to unpack it into a series of 7-bit bytes (ie, as if you were going to transmit it over midi in a SysEx message). Of course, you will have a variable number of bytes depending upon your delta-time. To indicate which is the last byte of the series, you leave bit #7 clear. In all of the preceding bytes, you set bit #7. So, if a delta-time is between 0-127, it can be represented as one byte. The largest delta-time allowed is 0FFFFFFF, which translates to 4 bytes variable length. Here are examples of delta-times as 32-bit values, and the variable length quantities that they translate to:
Number | Number (hex) | Representation (hex) |
0 | 00000000 | 00 |
64 | 00000040 | 40 |
127 | 0000007F | 7F |
128 | 00000080 | 81 00 |
8,192 | 00002000 | C0 00 |
16,383 | 00003FFF | FF 7F |
16,384 | 00004000 | 81 80 00 |
1,048,576 | 00100000 | C0 80 00 |
2,097,151 | 001FFFFF | FF FF 7F |
2,097,152 | 00200000 | 81 80 80 00 |
134,217,728 | 08000000 | C0 80 80 00 |
268,435,455 | 0FFFFFFF | FF FF FF 7F |
EventType type_name
SysEx (system exclusive) events (status = F0) are a special case because a SysEx event can be any length. After the F0 status (which is always stored -- no running status here), you'll find yet another series of variable length bytes. Combine them with ReadVarLen() and you'll come up with a 32-bit value that tells you how many more bytes follow which make up this SysEx event. This length doesn't include the F0 status.
For example, consider the following SysEx MIDI message:
F0 7F 7F 04 01 7F 7F F7This would be stored in a MIDI file as the following series of bytes (minus the delta-time bytes which would precede it):
F0 07 7F 7F 04 01 7F 7F F7Some midi units send a system exclusive message as a series of small "packets" (with a time delay in-between transmission of each packet). The first packet begins with an F0, but it doesn't end with an F7. The subsequent packets don't start with an F0 nor end with F7. The last packet doesn't start with an F0, but does end with the F7. So, between the first packet's opening F0 and the last packet's closing F7, there's 1 SysEx message there. (Note: only extremely poor designs, such as the crap marketed by Casio exhibit these aberrations). Of course, since a delay is needed in-between each packet, you need to store each packet as a separate event with its own time in the MTrk. Also, you need some way of knowing which events shouldn't begin with an F0 (ie, all of them except the first packet). So, the MIDI file spec redefines a midi status of F7 (normally used as an end mark for SysEx packets) as a way to indicate an event that doesn't begin with F0. If such an event follows an F0 event, then it's assumed that the F7 event is the second "packet" of a series. In this context, it's referred to as a SysEx CONTINUATION event. Just like the F0 type of event, it has a variable length followed by data bytes. On the other hand, the F7 event could be used to store MIDI REAL-TIME or MIDI COMMON messages. In this case, after the variable length bytes, you should expect to find a MIDI Status byte of F1, F2, F3, F6, F8, FA, FB, FC, or FE. (Note that you wouldn't find any such bytes inside of a SysEx CONTINUATION event). When used in this manner, the F7 event is referred to as an ESCAPED event.
A status of FF is reserved to indicate a special non-MIDI event. (Note that FF is used in MIDI to mean "reset", so it wouldn't be all that useful to store in a data file. Therefore, the MIDI file spec arbitrarily redefines the use of this status). After the FF status byte is another byte that tells you what Type of non-MIDI event it is. It's sort of like a second status byte. Then after this byte is another byte(s -- a variable length quantity again) that tells how many more data bytes follow in this event (ie, its Length). This Length doesn't include the FF, Type byte, nor the Length byte. These special, non-MIDI events are called Meta-Events, and most are optional unless otherwise noted. What follows are some defined Meta-Events (including the FF Status and Length). Note that unless otherwise mentioned, more than one of these events can be placed in an Mtrk (even the same Meta-Event) at any delta-time. (Just like all midi events, Meta-Events have a delta-time from the previous event regardless of what type of event that may be. So, you can freely intermix MIDI and Meta events).
java.lang.Object[] params
Constructor Detail |
public Event(EventType et, int delta_time, java.lang.Object[] parameters)
et
- An Event Type.delta_time
- A non-negative number of ticks of the MIDI Clock.parameters
- A list of the other parameters, if any.Method Detail |
public EventType getType()
public int getDTime()
public int getNParam()
public java.lang.Object getParam(int index)
public java.lang.Object[] getParams()
public java.lang.String toString()
toString
in class java.lang.Object
static byte[] packBER(int num)
Some numbers in MTrk chunks are represented in a form called a variable- length quantity. These numbers are represented 7 bits per byte, most significant bits first. All bytes except the last have bit 7 set, and the last byte has bit 7 clear. If the number is between 0 and 127, it is thus represented exactly as one byte.
Number | Number (hex) | Representation (hex) |
0 | 00000000 | 00 |
64 | 00000040 | 40 |
127 | 0000007F | 7F |
128 | 00000080 | 81 00 |
8,192 | 00002000 | C0 00 |
16,383 | 00003FFF | FF 7F |
16,384 | 00004000 | 81 80 00 |
1,048,576 | 00100000 | C0 80 00 |
2,097,151 | 001FFFFF | FF FF 7F |
2,097,152 | 00200000 | 81 80 80 00 |
134,217,728 | 08000000 | C0 80 80 00 |
268,435,455 | 0FFFFFFF | FF FF FF 7F |
The largest number which is allowed is 0FFFFFFF so that the variable- length representation must fit in 32 bits in a routine to write variable-length numbers. Theoretically, larger numbers are possible, but 2 x 108 96ths of a beat at a fast tempo of 500 beats per minute is four days, long enough for any delta-time!
num
- the number to be converted into variable-length form.static byte[] fetchBER(byte[] data, int offset)
Some numbers in MIDI Files are represented is a form called VARIABLE-LENGTH QUANTITY. These numbers are represented 7 bits per byte, most significant bits first. All bytes except the last have bit 7 set, and the last byte has bit 7 clear. If the number is between 0 and 127, it is thus represented exactly as one byte.
Here are some examples of numbers represented as variable-length quantities:
00000000 | 00 |
00000040 | 40 |
0000007F | 7F |
00000080 | 81 00 |
00002000 | C0 00 |
00003FFF | FF 7F |
00004000 | 81 80 00 |
00100000 | C0 80 00 |
001FFFFF | FF FF 7F |
00200000 | 81 80 80 00 |
08000000 | C0 80 80 00 |
0FFFFFFF | FF FF FF 7F |
data
- An arbitrary array of bytes, guaranteed not to end prematurely.offset
- A number of bytes to ignore before extracting.unpackBER(byte[])
static int unpackBER(byte[] data)
data
- An array of 1-5 bytes.public static int read_14_bit(byte[] bytes, int offset)
bytes
- An array of bytes.offset
- The offset at which to read the first of two bytes.EventStatus.ksnPitchWheelChange
,
EventType.kPitchWheelChange
public static void write_14_bit(int num, byte[] bytes, int offset)
num
- The number to be written.bytes
- An array of bytes.offset
- The offset at which to write the first of two bytes.public static Event[] decode(byte[] data, boolean eot_magic, EventType[] incl, EventType[] excl, EventCallback pre_processor, EventCallback post_processor)
The MTrk chunk type is where actual song data is stored. It is simply a stream of MIDI events (and non-MIDI events), preceded by delta-time values.
data
- List of bytes.eot_magic
- Substitute zero-length text for End of Track. Allows for easy appends.incl
- List of EventTypes to include, excluding all others.excl
- List of EventTypes to exclude. Not compatible with incl.pre_processor
- WriteBack to pre-massage event stream.post_processor
- Data sink that removes all Events.fetchBER(byte[],int)
,
Here is the syntax of an MTrk chunk:
<track data> = <MTrk event>+
<MTrk event> = <delta-time> <event>
<delta-time> is stored as a variable-length quantity. It
represents the amount of time before the following event. If the
first event in a track occurs at the very beginning of a track,
or if two events occur simultaneously, a delta-time of zero is
used. Delta-times are always present. (Not storing delta-times
of 0 requires at least two bytes for any other value, and most
delta-times aren't zero.) Delta-time is in some fraction of
a beat (or a second, for recording a track with SMPTE times),
as specified in the header chunk.
<event> = <MIDI event> | <SysEx event>
| <meta-event>
<MIDI event> is any MIDI channel message. Running
status is used: status bytes may be omitted after the first
byte. The first event in a file must specify status. Delta-time
is not considered an event itself: it is an integral part of
the specification. Notice that running status occurs across
delta-times.
<meta-event> specifies non-MIDI information useful to
this format or to sequencers, with this syntax:
FF <type> <length> <bytes>
All meta-events begin with FF, then have an event type byte
(which is always less than 128), and then have the length of
the data stored as a variable-length quantity, and then the
data itself. If there is no data, the length is 0. As with
SysEx events, running status is not allowed. As with chunks,
future meta-events may be designed which may not be known to
existing programs, so programs must properly ignore meta-events
which they do not recognize, and indeed, should expect to see
them. New for 0.06: programs must never ignore the length of
a meta-event which they do recognize, and they shouldn't be
surprised if it's bigger than they expected. If so, they must
ignore everything past what they know about. However, they must
not add anything of their own to the end of a meta-event.
<SysEx event> is used to specify a MIDI system exclusive
message, or as an "escape" to specify any arbitrary bytes to
be transmitted. Unfortunately, some synthesizer manufacturers
specify that their system exclusive messages are to be
transmitted as little packets. Each packet is only part of
an entire syntactical system exclusive message, but the times
they are transmitted at are important. Examples of this are the
bytes sent in a CZ patch dump, or the FB-01's "system exclusive
mode" in which microtonal data can be transmitted. To be able to
handle situations like these, two forms of <SysEx event>
are provided:
F0 <length> <bytes to be transmitted after F0>
F7 <length> <all bytes to be transmitted>
In both cases, <length> is stored as a variable-length
quantity. It is equal to the number of bytes following it,
not including itself or the message type (F0 or F7), but all
the bytes which follow, including any F7 at the end which is
intended to be transmitted. The first form, with the F0 code,
is used for syntactically complete system exclusive messages,
or the first packet an a series Q that is, messages in which
the F0 should be transmitted. The second form is used for the
remainder of the packets within a syntactic SysEx message, which
do not begin with F0. Of course, the F7 is not considered part
of the system exclusive message. Of course, just as in MIDI,
running status is not allowed, in this case because the length
is stored as a variable-length quantity which may or may not
start with bit 7 set.
(New to 0.06) A syntactic system exclusive message must always
end with an F7, even if the real-life device didn't send one,
so that you know when you've reached the end of an entire
SysEx message without looking ahead to the next event in the
MIDI file. This principle is repeated and illustrated in the
paragraphs below.
The vast majority of system exclusive messages will just use
the F0 format. For instance, the transmitted message F0 43 12
00 07 F7 would be stored in a MIDI file as F0 05 43 12 00 07
F7. As mentioned above, it is required to include the F7 at the
end so that the reader of the MIDI file knows that it has read
the entire message.
For special situations when a single system exclusive message
is split up, with parts of it being transmitted at different
times, such as in a Casio CZ patch transfer, or the FB-01's
"system exclusive mode", the F7 form of SysEx event is used
for each packet except the first. None of the packets would
end with an F7 except the last one, which must end with an
F7. There also must not be any transmittable MIDI events in-
between the packets of a multi-packet system exclusive message.
Here is an example: suppose the bytes F0 43 12 00 were to be
sent, followed by a 200-tick delay, followed by the bytes 43
12 00 43 12 00, followed by a 100-tick delay, followed by the
bytes 43 12 00 F7, this would be in the MIDI File:
F0 03 43 12 00
81 48 200-tick delta-time
F7 06 43 12 00 43 12 00
64 100-tick delta-time
F7 04 43 12 00 F7
The F7 event may also be used as an "escape" to transmit any
bytes whatsoever, including real-time bytes, song pointer,
or MIDI Time Code, which are not permitted normally in this
specification. No effort should be made to interpret the bytes
used in this way. Since a system exclusive message is not being
transmitted, it is not necessary or appropriate to end the F7
event with an F7 in this case.
public static byte[] encode(Event[] events, boolean eot_magic, boolean add_eot, boolean running_status, EventCallback unknown_processor)
TRACK CHUNK
The track chunks (type MTrk) are where actual song data is stored. Each track chunk is simply a stream of MIDI events (and non-MIDI events), preceded by delta-time values. The format for Track Chunks (described below) is exactly the same for all three formats (0, 1, and 2: see "Header Chunk" above) of MIDI Files.
TRACK CHUNK SYNTAX
Here is the syntax of an MTrk chunk (the + means "one or more" -- at least one MTrk event must be present):
<MTrk chunk>= <'MTrk'> <length> <MTrk event>+The syntax of an MTrk event is very simple:
<MTrk event> = <delta-time> <MIDI-file-event><delta-time> is stored as a variable-length quantity. It represents the amount of time before the following event. If the first event in a track occurs at the very beginning of a track, or if two events occur simultaneously, a delta-time of zero is used. Delta-times are always present. (Not storing delta-times of 0 requires at least two bytes for any other value, and most delta-times aren't zero.) Delta-time is in some fraction of a beat (or a second, for recording a track with SMPTE times), as specified in the header chunk.
<MIDI-file-event> = <MIDI-channel-message> | <MIDI-system-exclusive-message> | <MIDI-meta-event><MIDI-channel-message> is any MIDI channel message. Running status is used: status bytes of MIDI channel messages may be omitted if the preceding event is a MIDI channel message with the same status. The first event in each MTrk chunk must specify status. Delta-time is not considered an event itself: it is an integral part of the syntax for an MTrk event. Notice that running status occurs across delta-times.
<MIDI-system-exclusive-message> is used to specify a MIDI system exclusive message, either as one unit or in packets, or as an "escape" to specify any arbitrary bytes to be transmitted. A normal complete system exclusive message is stored in a MIDI File in this way:
F0 <length> <bytes>The length is stored as a variable-length quantity. It specifies the number of bytes which follow it, not including the F0 or the length itself. For instance, the transmitted message F0 43 12 00 07 F7 would be stored in a MIDI File as F0 05 43 12 00 07 F7. It is required to include the F7 at the end so that the reader of the MIDI File knows that it has read the entire message.
Another form of SysEx event is provided which does not imply that an F0 should be transmitted. This may be used as an "escape" to provide for the transmission of things which would not otherwise be legal, including system realtime messages, song pointer or select, MIDI Time Code, etc. This uses the F7 code:
F7 <length> <bytes>Unfortunately, some synthesizer manufacturers specify that their system exclusive messages are to be transmitted as little packets. Each packet is only part of an entire syntactical system exclusive message, but the times they are transmitted are important. Examples of this are the bytes sent in a CZ patch dump, or the FB-01's "system exclusive mode" in which microtonal data can be transmitted.
The F0 and F7 SysEx events may be used together to break up syntactically complete system exclusive messages into timed packets. An F0 SysEx event is used for the first packet in a series -- it is a message in which the F0 should be transmitted. An F7 SysEx event is used for the remainder of the packets, which do not begin with F0. (Of course, the F7 is not considered part of the system exclusive message). A syntactic system exclusive message must always end with an F7, even if the real-life device didn't send one, so that you know when you've reached the end of an entire SysEx message without looking ahead to the next event in the MIDI File. If it's stored in one complete F0 SysEx event, the last byte must be an F7. There also must not be any transmittable MIDI events in between the packets of a multi-packet system exclusive message. This principle is illustrated in the paragraph below.
Here is a MIDI File of a multi-packet system exclusive message: suppose the bytes F0 43 12 00 were to be sent, followed by a 200-tick delay, followed by the bytes 43 12 00 43 12 00, followed by a 100-tick delay, followed by the bytes 43 12 00 F7, this would be in the MIDI File:
F0 03 43 12 00 81 48When reading a MIDI File, and an F7 SysEx event is encountered without a preceding F0 SysEx event to start a multi-packet system exclusive message sequence, it should be presumed that the F7 event is being used as an "escape". In this case, it is not necessary that it end with an F7, unless it is desired that the F7 be transmitted.200-tick delta time
F7 06 43 12 00 43 12 00 64
100-tick delta timeF7 04 43 12 00 F7
<MIDI-meta-event> specifies non-MIDI information useful to this format or to sequencers, with this syntax:
FF <event type> <length> <optional bytes>All meta-events begin with FF, then have an event type byte (which is always less than 128), and then have the length of the data stored as a variable-length quantity, and then the data itself. If there is no data, the length is 0. As with chunks, future meta-events may be designed which may not be known to existing programs, so programs must properly ignore meta-events which they do not recognize, and indeed should expect to see them. Programs must never ignore the length of a meta-event which they do not recognize, and they shouldn't be surprised if it's bigger than expected. If so, they must ignore everything past what they know about. However, they must not add anything of their own to the end of the meta- event. SysEx events and meta events cancel any running status which was in effect. Running status does not apply to and may not be used for these messages.
events
- The Array of Events.eot_magic
- Use zero-length text events to mark end of Track.add_eot
- Force array to end with End of Track Event.running_status
- Compress Voice output (marginally).unknown_processor
- Don't throw Errors if defined.
|
DixShtix | |||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: INNER | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |