In the MusicKit, MIDI messages are represented as
MKNote
s, where each
MKNote
can represent one or more MIDI
messages. The MIDI message is represented by a combination of the
MKNote
's type, tag and parameters.
The MusicKit takes care of verifying the validity of the MIDI
stream and throws away invalid messages, such as duplicate noteOffs or
noteOffs for inactive Key/Channel pairs. However, you can turn off
this feature by enabling "inclusive translation mode" with the
function MKSetInclusiveMidiTranslation()
.
This discussion assumes a familiarity with MIDI. For further
details, consult the MIDI Specification. For further information no
methods for reading a Standard MIDI File, see the
MKScore
class description. For further
information on processing or sending MIDI in real time, see the
MKMidi
class description.
MKNote
s
to MIDIThese rules deal with sending MIDI messages to a
MKMidi
object, which represents the OUT
jack on your MIDI interface, as well as writing
MKScore
objects as Standard MIDI files.
MIDI Channel Voice messages are the most common type of message. For all the Channel Voice messages, the MIDI channel is determined as follows:
For writing a MKScore
object to a
Standard MIDI File, each MKNote
is checked for
the presence of an MK_midiChan
parameter. If one is found, its value ([1-16]) is used. Otherwise,
if the MKPart
info
MKNote
has an MK_midiChan parameter, it is used as the
default. Otherwise a default of channel 1 is used.
For writing a MKNote
to a
MKMidi
object, if the
MKNote
is sent via the 0'th
MKNoteReceiver
of the
MKMidi
object, each
MKNote
is checked for the presence of an
MK_midiChan parameter. If one is
found, its value ([1-16]) is used. Otherwise, the channel of the
MKNoteReceiver
is used. For example, all
MKNote
s sent via the 2nd
MKNoteReceiver
are assigned to MIDI channel 2.
A noteOn message is
represented by a MKNote
object with the
noteType MK_noteOn. Additionally, an MK_keyNum parameter provides the key number
([0-127]) and the MK_velocity
parameter provides the velocity ([0-127]). If no MK_keyNum is present, the MK_freq parameter is checked and, if it is
present, converted to a MIDI key number using the currently installed
MKTuningSystem
. Finally, a noteTag
must be provided so that noteOff MKNote
s and
polyphonic aftertouch MKNote
s can be properly matched.
Alternatively, you can send a MKNote
of
type MK_noteDur with a duration supplied, which is equivalent to a
noteOn/noteOff pair. In this case, no noteTag is needed, though it
may still be supplied if desired to match against polyphonic key
pressure MKNote
s and other noteOn
MKNote
s in this phrase.
The following actions are taken by the MusicKit to make sure MusicKit semantics are properly converted to MIDI semantics:
If two successive noteOn
MKNote
s have the same noteTag and the same
MK_keyNum value, a noteOff message is
generated on the same channel and with the same key number as those
for the noteOn MKNote
s.
If two successive noteOn
MKNote
s have the same noteTag but different
MK_keyNum values, the second MIDI
noteOn message is followed by a MIDI noteOff message with the key
number of the first noteOn MKNote
. This is to
accommodate MIDI Mono Mode.
For example, the following code creates a noteOn note and sends
it to MIDI OUT. This is a very simple case with no MKConductor
to
manage timing.
#import <MusicKit/MusicKit.h> #import <MusicKit/keynums.h> MKMidi *aMidi = [[MKMidi alloc] init]; /* Create MKMidi object */ MKNote *aNote = [[MKNote alloc] init]; /* Create a MKNote */ [aMidi run]; /* Start it up */ [aNote setNoteTag: MKNoteTag()]; /* Set tag */ [aNote setPar: MK_keyNum toInt: c4k]; /* Set key number */ [aNote setPar: MK_velocity toInt: 64]; /* Set velocity */ [aNote setPar: MK_midiChan toInt: 1]; /* MIDI channel */ [aNote setNoteType: MK_noteOn]; /* Set type */ [[aMidi noteReceiver] receiveNote: aNote]; /* Send it */ [aMidi close]; |
A noteOff message is
represented by a MKNote
object with the note
type MK_noteOff. You should give the
MKNote
a noteTag that is the same as you gave
the corresponding noteOn MKNote
. When you send
such a MKNote
to the
MKMidi
object or write it to a Standard MIDI
file, the key number is determined by the
MKNote
's noteTag, which is matched against the
MKNote
previously sent with the same tag. If
there is no active noteTag then and inclusive translation mode has
been selected (using the function MKSettInclusiveMidiTranslation()
), the
MKNote
is checked for the presence of an
MK_keyNum parameter ([0-127]).
Otherwise, or if none is found, the noteOff is omitted. Additionally,
an MK_releaseVelocity parameter
provides the noteOff velocity. MKNote
that if
the presence or absence of MK_releaseVelocitydetermines whether the MIDI is sent as a true MIDI noteOff
message or as a zero-velocity MIDI noteOn message.
If you are generating a series of notes in a single phrase
(i.e. using MIDI Mono Mode), keep in mind that MusicKit semantics
require only a single noteOff MKNote
to
conclude the phrase, rather than a noteOff
MKNote
for every noteOn
MKNote
. For example, if you send a series of
MKNote
s with the same noteTag that are part of
a mono phrase, you should simply send a series of noteOns and one
noteOff at the end. The MusicKit does the proper translation to MIDI
semantics automatically.
A polyphonic
aftertouch (aka “key pressure”) message is
represented by a MKNote
object of type
MK_noteUpdate. The key number is determined by the
MKNote
's noteTag, which is matched against the
MKNote
previously sent with the same tag. If
there is no active noteTag then, if inclusive translation mode has
been selected (using the function
MKSettInclusiveMidiTranslation()
), the
MKNote
is checked for the presence of an
MK_keyNum parameter. If none is
found, the polyphonic aftertouch message is omitted. The parameter
MK_keyPressure provides the value of
the key pressure, which is between 0 and 127.
A controller change
message is represented by a MKNote
object with
the parameter MK_controlChange,
which provides the value of the
controller to be changed ([0-121]), while the parameter MK_controlVal is the value of the controller
([0-127]). If you send such a MKNote
to a
MKSynthInstrument
, its noteType should be set
to MK_noteUpdate.
A program change
message is represented a MKNote
object with the
parameter MK_programChange, which
provides the value of the program change ([0-127]).
A channel pressure
(aka “after touch”) message is represented by a
MKNote
object with the parameter MK_afterTouch, which provides the value of the
afterTouch. If you send such a MKNote
to a
MKSynthInstrument
, its noteType should be set
to MK_noteUpdate. Macros for common controller names are found in
MusicKit/midi_spec.h. For
example, MIDI_MODWHEEL
is defined as 1.
A pitch bend message
is represented by a MKNote
object with the
parameter MK_pitchBend, which
provides the value of the pitch bend. As in MIDI, pitch bend is a
14-bit signed integer, centered around 0x2000. That is, 0 is maximum
negative bend and 0x3fff is maximum positive bend. If you send such a
MKNote
to a
MKSynthInstrument
, its noteType should be set
to MK_noteUpdate.
For example, the following code plays a note and then does a gradual pitch bend.
#import <MusicKit/MusicKit.h> #import <MusicKit/keynums.h> int i; MKMidi *aMidi = [[MKMidi alloc] init]; /* Create MKMidi object */ MKNote *aNote = [[MKNote alloc] init]; /* Create a Note */ MKNote *pbNote = [[MKNote alloc] init]; /* Another one */ #define SEND(note) [[aMidi noteReceiver] receiveNote: note] [aMidi run]; /* Start it up */ [aNote setNoteTag: MKNoteTag()]; /* Set tag */ [aNote setPar: MK_keyNum toInt: c4k]; /* Set key number */ [aNote setPar: MK_velocity toInt: 64]; /* Set velocity */ [aNote setPar: MK_midiChan toInt: 1]; /* MIDI channel */ [aNote setNoteType: MK_noteOn]; /* Set type */ SEND(aNote); [pbNote setNoteType:MK_noteUpdate]; [pbNote setPar:MK_midiChan toInt:1]; /* MIDI channel */ for (i = 0; i < 0x3fff; i++) { [pbNote setPar: MK_pitchBend toInt: i]; /* Bend */ SEND(pbNote); usleep(10000); /* Wait 10 milliseconds */ } [aNote setNoteType: MK_noteOff]; /* Now stop the note */ SEND(aNote); [aMidi close]; |
Note that several messages may be specified in a single
MKNote
object, where feasible. For example, a
noteOn can be combined with an
after touch message. An example
where this is not possible is to set two controllers in a single
MKNote
―this is impossible because only one
MK_controlChange parameter may be
present in a MKNote
.
MIDI Channel Mode messages are represented by a
MKNote
object with the parameter MK_basicChan set to the basic channel ([1-16])
and MK_chanMode set to one of the
following int values (defined in MusicKit/params.h):
MK_resetControllers
MK_localControlModeOn
MK_localControlModeOff
MK_allNotesOff
MK_omniModeOff
MK_omniModeOn
MK_monoMode
MK_polyMode
In addition, for monoMode, the parameter MK_monoChans specifies the number of Mono channels ([0-127).
A MIDI time code quarter framemessage is represented by a MKNote
with the parameter MK_timeCodeQ, with
a value as defined in the MIDI Time Code Specification
([0-127)].
A song position pointermessage is represented by a MKNote
with the parameter MK_songPosition,
which is a 14-bit integer ([0-0x3ff]). See the MIDI Specification for
details.
A song selectmessage is represented by a MKNote
with the parameter MK_songSelect with
the integer value of the song to be selected ([0-127]).
A tune requestmessage is represented by a MKNote
with the parameter MK_tuneRequest.
The value of the parameter is irrelevant―only its presence is
significant.
MIDI System Real
Time messages are represented by a
MKNote
object with the parameter MK_sysRealTime set to one of the following int
values (defined in
MusicKit/params.h):
MK_sysClock
MK_sysStart
MK_sysContinue
MK_sysStop
MK_sysActiveSensing
MK_sysReset
MIDI System
Exclusive messages are represented by a
MKNote
object with the parameter MK_sysExclusive with a string
value. The string consists of hexadecimal system exclusive
bytes separated by any non-digit delimiter. The musickit uses the
comma as a delimiter (','). For example, "f0, 8,13,f7". The string
may, but need not, begin with f0 and end with f7. If these are
missing, they are automatically added. Note that you should
not include the conventional C hex prefix "0x".
This will be interpreted as a single byte 0!
Note also that if you want to give each sysex byte a different
delay, you need to put it in a separate MKNote
object as a separate system exclusive message. The MusicKit does not
support system exclusive messages that span
MKNote
objects.
For example, the following program sends the system exclusive message f0, 8 13 f7:
#import <MusicKit/MusicKit.h> main() { MKMidi *aMidi = [[Midi alloc] init]; /* Create MKMidi object */ MKNote *aNote = [[MKNote alloc] init]; /* Create a MKNote */ [aMidi run]; /* Start it up */ [aNote setPar: MK_sysExclusive toString: @"f0,8,13,f7"]; [[aMidi noteReceiver] receiveNote: aNote]; /* Send it */ [aMidi close]; exit(0); } |
MKNote
sThese rules deal with receiving MIDI messages from a
MKMidi
object that represents the MIDIN jack on
your interface, as well as reading Standard MIDI files into
MKScore
objects.
Note that the MKNote
s created by a
MKMidi
object and sent during a performance
should not be stored or modified. The rule here is "copy on modify or
store"―to store or modify a MKNote
simply
send it the copy message to create a
new copy.
MIDI Channel Voice messages are the most common type of message. For all the Channel Voice messages, the MIDI channel is specified as follows:
For reading a MKScore
object from a
Standard MIDI File, the way the channel is interpreted depends on the
"level" of the MIDI file. See the Standard MIDI File Specification
for more information on Standard MIDI Files.
For a level 0 file, each MKPart
corresponds to a MIDI channel. Therefore, each
MKPart
info is given a MK_midiChan parameter that specifies its
channel ([1-16]). The MKScore
method midiPart: takes a channel argument and returns
a MKPart
with that channel specified in its
info MKNote
.
For a level 1 or level 2 file, each
MKNote
is given a MK_midiChan parameter in the range [1-16]. In
addition, the MKPart
info is given a MK_midiChan parameter of the first
MKNote
found in that
MKPart
, since it is a common case that all
MKNote
s in a track have the same
channel.
For receiving incoming MKNote
s from a
MKMidi
object, the way the channel is specified
depends on how the method setMergeInput: was invoked. If you send
[midiObj setMergeInput:YES], then
each MKNote
is sent out the zero'th
MKNoteSender
with the MIDI channel specified in
a MK_midiChan parameter. Keep in
mind that this applies only to MIDI Chanel Voice messages. For all
other kinds of messages, the MKNote
s are sent
out the 0'th MKNoteSender
. On the other hand,
if you send [midiObj
setMergeInput:NO], each MKNote
is
sent out the c'th MKNoteSender
, where c is the
MIDI channel; in this case, no MK_midiChan is supplied.
For each noteOn
message, a MKNote
object is created and
forwarded to the appropriate MKNoteSender
, as
explained above. It is also given a noteType of MK_noteOn, MK_keyNum parameter that provides the key
number ([0-127]) and a MK_velocity
parameter that provides the velocity ([0-127]). Additionally, it is
given a noteTag ([0-MAXINT]) corresponding to its Key Number and
Channel. However, you should not depend on the particular value of
the noteTag to derive the Key Number and Channel―use the
parameters instead. The noteTag is essential in that it makes it
possible to match noteOff and polyphonic key pressure
MKNote
s with the corresponding noteOn
MKNote
s.
Currently multiple noteOns on the same Key Number and Channel are interpreted as rearticulations, rather than as additional voices.
For each noteOff
message for which an unmatched noteOn was previously received , a
MKNote
object is created with type MK_noteOff.
It is given a noteTag that is the same as you gave the corresponding
noteOn MKNote
. If inclusive MIDI translation
mode is selected (using the function
MKSettInclusiveMidiTranslation()
), then a
MK_keyNum parameter is additionally
included ([0-127]) and unmatched noteOffs are passed along to the
application, rather than being filtered out.
If multiple noteOff messages are received (for a particular
Channel/Note number) without intervening noteOn messages, only the
first noteOff message is converted into a
MKNote
object. The others are suppressed.
However, if inclusive MIDI translation mode is selected, the noteOff
is not surpressed and is passed on to the application.
If a noteOff message has a release velocity of 0, the MK_releaseVelocity parameter in the
corresponding MKNote
object is omitted.
For each polyphonic
aftertouch (aka “key pressure”) message for a
channel/key number for which a noteOn was received, a
MKNote
object is created of type MK_noteUpdate,
with a noteTag that matches the noteOn received on the same
channel/key number pair. The key number is not explicitly included,
unless inclusive translation mode is enabled, in which case a
MK_keyNum parameter is additionally
included. The key pressure is specified in the parameter MK_keyPressure ([0-127]).
For each controller
change message, a MKNote
is created with
the parameter MK_controlChange,
which specifies the value of the
controller to be changed ([0-121]), and the parameter MK_controlVal, which specifies the value of the
controller ([0-127]). The noteType is set to MK_noteUpdate.
For each program change message, a MKNote
is created with
the parameter MK_programChange, which
provides the value of the program change ([0-127]). The noteType is
set to MK_noteUpdate.
For each channel
pressure (aka “after touch”) message, a
MKNote
is created with the parameter MK_afterTouch, which provides the value of the
afterTouch ([0-127]). The noteType is set to MK_noteUpdate.
For each pitch bend
message, a MKNote
is created with the parameter
MK_pitchBend, which provides the
value of the afterTouch ([0-0x3fff]). The noteType is set to
MK_noteUpdate.
Incoming Channel Mode messages are handled by the
MKMidi
object by fashioning them into
MKNote
objects and sending them to the 0'th
MKNoteSender
. Similarly, when a Standard MIDI
file containing Channel Mode messages is read, they are turned into
MKNote
s and added to the
MKPart
returned by sending the
MKScore
the message midiPart: with an argument of 0.
MIDI Channel Mode messages are represented by a
MKNote
object with the parameter MK_basicChan set to the basic channel ([1-16])
and MK_chanMode set to one of the
following int values (defined in
MusicKit/params.h):
MK_resetControllers
MK_localControlModeOn
MK_localControlModeOff
MK_allNotesOff
MK_omniModeOff
MK_omniModeOn
MK_monoMode
MK_polyMode
In addition, for monoMode, the parameter MK_monoChans specifies the number of Mono channels ([0-127).
Incoming System messages are handled by the
MKMidi
object by fashioning them into
MKNote
objects and sending them to the 0'th
MKNoteSender
. Similarly, when a Standard MIDI
file containing System messages are read, they are turned into
MKNote
s and added to the
MKPart
returned by sending the
MKScore
the message midiPart: with an argument of 0.
A MIDI time code quarter framemessage is represented by a MKNote
with the parameter MK_timeCodeQ, with
a value as defined in the MIDI Time Code Specification
([0-127)].
A song position
pointer message is represented by a
MKNote
with the parameter MK_songPosition, which is a 14-bit integer
([0-0x3ff]). See the MIDI Specification for details.
A song selectmessage is represented by a MKNote
with the parameter MK_songSelect with
the integer value of the song to be selected ([0-127]).
A tune requestmessage is represented by a MKNote
with the parameter MK_tuneRequest.
The value of the parameter is irrelevant―only its presence is
significant.
MIDI System Real
Time messages are represented by a
MKNote
object with the parameter MK_sysRealTime set to one of the following int
values (defined in MusicKit/params.h):
MK_sysClock
MK_sysStart
MK_sysContinue
MK_sysStop
MK_sysActiveSensing
MK_sysReset
MIDI System
Exclusive messages are represented by a
MKNote
object with the parameter MK_sysExclusive with a string
value. The string consists of hexadecimal system exclusive
bytes separated by a comma. For example, "f0, 8,13,f7". The string
begins with f0 and ends with f7.
In addition to the conversions described above, a few special rules apply when converting to or from a Standard MIDI file.
Standard MIDI Files come in three varieties: level 0, level 1 and level 2. The MusicKit always writes level 1 files. It reads all three varieties.
When a level 0 file is read, the Channel Voice messages are
written into 16 MKPart
s, one for each channel.
Channel Mode and System messages are combined in an additional
MKPart
. The midi channel of a particular
MKPart
can be determined by examining the
MK_midiChan parameter of the
MKPart
info MKNote
. The
special system MKPart
has no MK_midiChan parameter.
When a level 1 file is read, each track is written to a separate
MKPart
. The track number is set in the
MKPart
info's MK_track parameter. SMTPTE offset is set in
the MK_smpteOffset parameter of the
MKScore
info.
When a level 2 file is read, each track is written to a separate
MKPart
. The track number is set in the
MKPart
info's MK_sequence parameter. SMTPTE offset is set in
the MK_smpteOffset parameter of the
MKPart
info.
If an MK_smpteOffset is included, it is encoded as a string consisting of five hex numbers, separated by spaces. Similarly, when writing a Standard MIDI file, the MusicKit expects to see such a string as the value of the MK_smpteOffset parameter. See Standard MIDI file spec for details.
When reading a file, tempo is encoded in the MK_tempo parameter of the
MKScore
's info and also represented in
MKNote
s with a MK_tempo parameter. The
MKScore
class method +setMidifilesEvaluateTempo: controls whether
tempo is merely passed along to the application (if the argument to
setMidifilesEvaluateTempo: is YES) or
whether it is factored into the MKNote
s'
timestamps. If you want the tempo track to be meaningful, you should
set
setMidifilesEvaluateTempo:NO. Similarly, when
writing, the MKScore
info's tempo is written,
as are any MKNote
s with MK_tempo parameters, and the value set with
+setMidifilesEvaluateTempo:
determines whether tempo is factored into the time stamps
written.
Copyright is represented in the MKScore
's
info as an MK_copyright parameter
with a string value.
The info of each MKPart
that corresponds
to a track is represented as an MK_instrumentName parameter if a corresponding
meta-event appears in the file. This parameter has a string
value.
Other MIDI file meta-events such as time signature, lyric,
etc. appear as corresponding MKNote
parameters
in mute MKNote
s in the appropriate
MKPart
. These include MK_lyric, MK_marker, MK_cuePoint, MK_text, MK_timeSignature and MK_keySignature. These all have string
values.