This section explains how the MIDI functions work so that a task can use them directly. This is useful if you wish to take advantage of the score player's voice management for something other than playing back a MIDI score (e.g. a sound effects system , see tsc_soundfx.c for an example of how to do this). You should note that the functions require a full MIDI environment to be set up, a score context with all of its attendant data structures.
int32 InterpretMIDIMessage( ScoreContext *ScoreCon, char *MIDIMsg, int32 IfMute )
InterpretMIDIEvent()
, which is used as an interpreter procedure for MIDI objects. InterpretMIDIMessage()
accepts three arguments: *ScoreCon
, a pointer to the score context used to play a MIDI score; *MIDIMsg
, a pointer to a character string containing the MIDI message to be executed; and IfMute
, a boolean flag that turns muting on and off.
When InterpretMIDIMessage()
executes, it reads the specified MIDI message and if it recognizes the message, it passes the message and its accompanying data on to the appropriate MIDI function, which executes the message. If the IfMute
flag is set to TRUE, InterpretMIDIMessage()
does not recognize any Note On messages with velocity values greater than 0. In other words, it does not start any new notes, although it turns notes off and executes other MIDI messages such as program change messages and control change messages.
If successful, InterpretMIDIMessage()
returns 0. If unsuccessful, it returns a negative value (an error code).
MIDI messages are defined by the MIDI standard as being a series of bytes. The first byte is the message, subsequent bytes (if necessary) are data pertaining to the message. The string that InterpretMIDIMessage()
accepts is a string of MIDI message bytes, with the MIDI message in the first byte and data bytes following it.
InterpretMIDIMessage()
receives a Note On message, it makes this call:
int32 StartScoreNote( ScoreContext *scon, int32 Channel, int32 Note, int32 Velocity )
*scon
, which is a pointer to the score context keeping track of note allocation; Channel
, which is the channel number (1 to 16) of the note being started; Note
, which is a MIDI pitch value from 0 to 127; and Velocity
, which is a MIDI velocity value from 0 to 127.
When it executes, StartScoreNote()
uses the voice allocation algorithm to find an appropriate instrument and then start a note on that instrument using the specified pitch and velocity. The channel number specified determines which instrument template should be used for the instrument. If successful, it returns 0. If unsuccessful, it returns a negative value (an error code).
When InterpretMIDIMessage()
receives a Note Off message, it makes this call:
int32 ReleaseScoreNote( ScoreContext *scon, int32 Channel, int32 Note, int32 Velocity )
*scon
, which is a pointer to the score context keeping track of note allocation; Channel
, which is the channel number (1 to 16) of the note being released; Note
, which is a MIDI pitch value from 0 to 127; and Velocity
, which is a MIDI velocity value from 0 to 127.
When it executes, ReleaseScoreNote()
finds the note of the specified pitch using the specified channel, and releases the note using the specified velocity. A released note does not necessarily stop playing immediately, so the instrument used for the note may continue to play for a while longer until the note stops and the instrument is abandoned. If successful, the call returns 0. If unsuccessful, it returns a negative value (an error code).
ReleaseScoreNote()
and StartScoreNote()
use voice allocation as they start and release notes. Once a voice is allocated, they call lower level functions to start or release an instrument for the voice.This call starts a specified instrument without using the MIDI environment's voice allocation mechanism:
int32 NoteOnIns( Item Instrument, int32 Note, int32 Velocity )
Instrument
, which is the item number of the instrument to start; Note
, which is a MIDI pitch value from 0 to 127; and Velocity
, which is a MIDI velocity value from 0 to 127.
When it executes, NoteOnIns()
uses the Audio folio to start the specified instrument using the pitch specified by Note
and the amplitude specified by Velocity
. If successful, the call returns 0. If unsuccessful, it returns a negative value (an error code).
This call releases a note on a specified instrument without using the MIDI environment's voice allocation mechanism:
int32 NoteOffIns( Item Instrument, int32 Note, int32 Velocity )
Instrument
, which is the item number of the instrument to release; Note
, which is a MIDI pitch value from 0 to 127; and Velocity
, which is a MIDI velocity value from 0 to 127.
When executed, NoteOnIns()
uses the Audio folio to release the specified instrument using the release value specified by Velocity. If successful, the call returns 0. If unsuccessful, it returns a negative value (an error code).
InterpretMIDIMessage()
receives a Program Change message, it makes this call:
int32 ChangeScoreProgram( ScoreContext *ScoreCon, int32 Channel, int32 ProgramNum )
*ScoreCon
, which is a pointer to the score context controlling the MIDI environment; Channel
, which is the number of the channel (0 to 15) for which the program should be changed; and ProgramNum
, a number from 1 to 128 that specifies the new program to be used for the specified channel.
When it executes, ChangeScoreProgram()
changes the program number associated with a given channel in the MIDI environment. Any future notes played in that channel use the instrument template specified in the PIMap for the new program. If successful, the call returns 0. If unsuccessful, it returns a negative value (an error code).
InterpretMIDIMessage()
receives a Control message, it makes this call:
int32 ChangeScoreControl( ScoreContext *scon, int32 Channel, int32 Index, int32 Value )
*scon
, which is a pointer to the score context controlling the MIDI environment; Channel
, which is the number of the channel (0 to 15) for which a controller should be set; Index
, which is the number of the controller to be set; and Value
, which contains the value of the new controller setting.
ChangeScoreControl()
accepts only two different Index
values: 7, which specifies a change in volume; and 10, which specifies a change in panning. When ChangeScoreControl()
executes, it checks the Index
value. If it is 7, it looks for a value from 0 to 127 in Value
, which it uses to set the overall volume for the channel. 0 is no volume, 127 is maximum volume.
If the Index
value is 10, the function looks for a value from 0 to 127 in Value
, which it uses to set panning for the channel. Panning is the position of the channel's notes in the stereo output signal, which the function sets in the MIDI mixer. 0 is all the way to the left of the output, 63 is the middle of the output, and 127 is all the way to the right of the output.
When a pitch wheel is turned, it sends a stream of Pitch Bend Change messages with accompanying data values that range from 0x0 to 0x3FFF. These data values describe the positions through which the wheel moves. Value 0x0 means that the wheel is turned all the way down, signifying maximum pitch bend down. Value 0x3FFF means that the wheel is turned all the way up, signifying maximum pitch bend up. And value 0x2000 means that the wheel is set to its center position, so it does not bend pitch at all. When the wheel has stopped moving, it stops sending Pitch Bend Change messages, and pitch bend freezes at the wheel's last reported position.
The actual amount of pitch bend applied to a channel's voices is the result of the MIDI pitch bend value (the wheel's position) applied to the pitch bend range. For example, if the pitch bend value is 0x1000 (half way down to the lowest possible pitch bend value of 0x0) and the pitch bend range is a whole step, then the final pitch bend applied to the channel's voices is down by one half step, one half of the whole step range.
ScoreContext
data structure. The pitch bend range value is used together with a MIDI pitch bend value to determine how far the pitch of the channel's voices should be bent.To set a score's pitch bend range value (the maximum distance from normal pitch that any score voices can be bent), use this call:
Err SetScoreBendRange( ScoreContext *scon, int32 BendRange )
*scon
, a pointer to the ScoreContext
data structure in which to set the pitch bend range; and BendRange
, a pitch bend range value from 1 to 12 that specifies the number of semitones (half steps) in the pitch bend range. A minimum BendRange
value of 1 means that all voices in a score can be bent up or down a maximum of one half step. And a maximum BendRange
value of 12 means that all voices in a score can be bent up or down a maximum of one octave (12 half steps).
When it executes, SetScoreBendRange()
writes the BendRange
value into the appropriate element of the specified score context. It returns 0 if successful; it returns a negative value (an error code) if unsuccessful.
To see what a score context's current pitch bend range value is, use this call:
int32 GetScoreBendRange( ScoreContext *scon )
*scon
, a pointer to the ScoreContext
data structure you want to check. When it executes, it returns the current pitch bend range value of the score context if successful. If unsuccessful, it returns a negative value (an error code).
InterpretMIDIMessage()
, which makes this call once for each Pitch Bend Change message:
Err ChangeScorePitchBend( ScoreContext *scon, int32 Channel, int32 Bend )
*scon
, a pointer to the ScoreContext
data structure controlling playback; Channel
, the number of the channel (0 to 15) specified for the pitch bending; and Bend
, a MIDI pitch bend value from 0x0 to 0x7FFF, which specifies the amount of pitch bend.
ChangeScorePitchBend()
, when it executes, reads the score's current pitch bend range value from the specified score context. It then calls ConvertPitchBend()
and passes it the pitch bend range value and the MIDI pitch bend value (Bend
). ConvertPitchBend()
returns an internal pitch bend value, which ChangeScorePitchBend()
writes to the ScoreChannel
data structure as the channel's current pitch bend value. ChangeScorePitchBend()
also calls the audio function BendInstrumentPitch()
to bend each of the instruments used for the specified channel's voices. Any new instruments created to support the channel's voices are set to bend pitch by the amount specified in the ScoreChannel
data structure.
If the preceding description seems complicated, consider the final results of ChangeScorePitchBend()
's execution: all channel voices are bent to the amount specified in the MIDI message, and the current bend setting is saved until a new bend setting is specified.
If successful, ChangeScorePitchBend()
returns 0. If unsuccessful, it returns a negative value (an error code).
Note that although ChangeScorePitchBend()
is usually called by InterpretMIDIMessage()
, a task can call it directly if it wants to reset a channel's pitch bend.
ConvertPitchBend()
directly if it wants to translate a MIDI pitch bend value (contained as data in a Pitch Bend Change message) into an internal pitch bend value that can be used by the Audio folio:
Err ConvertPitchBend( int32 Bend, int32 SemitoneRange, frac16 *BendFractionPtr )
Bend
, which is a MIDI pitch bend value from 0x0 to 0x3FFF; SemitoneRange
, which is the pitch bend range value measured in semitones (half steps); and *BendFractionPtr
, which is a pointer to a frac16
variable in which to store the internal pitch bend value calculated by the function.
When it executes, ConvertPitchBend()
applies the MIDI pitch bend value to the pitch bend range to see just how far pitch must be bent. It calculates an internal pitch bend value appropriate for that much pitch bend, and writes it into the frac16
variable. If successful, it returns 0; if unsuccessful, it returns a negative value (an error code). The BendFraction can then be used with BendInstrumentPitch()
.