Magazine Archive

Home -> Magazines -> Issues -> Articles in this issue -> View

Adventures In Midiland (Part 2)

Part 2: Martin Russ continues his trek through the MIDI programming jungle with an investigation of running status.


Armed with the experience gained from our first expedition, we are now ready to take a close look at some rather more exhilarating sights. Note On and Note Off messages are often used to introduce MIDI, since they refer to the basic elements of music, the notes themselves. Unfortunately, because these messages are presented as being straightforward, the unwary MIDI explorer falls headlong into a trap which is set when you forget running status !

RUNNING STATUS



The MIDI Specification, and most explanations of MIDI, seem almost to dismiss running status — often just a quick passing reference is given, as if it hardly matters. As with many things, the truth is often different to its presentation in the media, and running status will produce the lion's share of the problems we will encounter on our safaris.

Running Status is a simple concept: when a series of MIDI messages all begin with the same status byte, only the initial status byte is used, with pairs of data values replacing the sets of three bytes. This produces a significant economy, both in the number of bytes needed to transmit the messages and in the time it takes to send them. For example, the hexadecimal bytes:

90 3C 72 90 40 70 90 43 74

represent the three notes (C, E, G) which make up the C major chord (expressed as three 3-byte MIDI messages), whilst:

90 3C 72 40 70 43 74

has the same meaning, except that only seven bytes are used: one status byte and three pairs of bytes. The savings can be more significant for larger groups of notes. Of the two gains, perhaps the most important is the reduction in time which accompanies running status. Although the two bytes reduction in the length of the message does not seem important, the overall difference of one byte per Note On message is much more important. In the case of the 7-byte message, the whole chord has been specified before the last note of the 9-byte message has been sent.

For large groups of events which occur in a short time interval, this time compression can be very important. Most real-world instruments tend to use running status for chords or groups of notes, where the timing needs to be as coincident as possible, and this often means any occurrence of more than one note at once! This is easily verified by using the MDISP MIDI Display program (explained in Part One): play a few arpeggios and then some chords or note clusters. You should see the single notes as 3-byte messages, whilst the blocks of notes will often be blocks of two bytes using running status. A more easily interpreted display is given by the RSTAT program (included on the program disk which accompanies this series), which shows the current status as either 'normal' or 'running'.

THE TRAP



If running status is advantageous, what is the problem? Running status helps the MIDI user, since it speeds up transmission of critical events, but makes the MIDI programmer's job harder. The problem for MIDI explorers lies in the decoding of messages which use running status, since it is necessary to keep track of the current status and the current byte (note number or velocity) all the time, as well as remaining alert for a different status byte. This means that the following simple approach to decoding MIDI Note messages:

DO
DO
INPUT byte
LOOP UNTIL byte = Note On Status Byte
INPUT next byte (Note Number)
INPUT next byte (Velocity)
LOOP


will not work properly, since if the byte which follows a velocity byte is not a status byte then it will be ignored, with the result that all running status bytes except the one just after the status byte are ignored. The MSIMPL example program takes exactly this wrong approach — it looks for incoming Note On or Note Off status bytes ($90 or $80) and then interprets the next two bytes as note number and velocity respectively. The note data is then echoed at the MIDI Out.

Part of the MSIMPL program:

start:
n = INP(3)
a = n AND &HF0
escape$ = INKEY$
IF escapeS <>"" THEN GOTO leave
IF NOT((a = &H90) OR (a = &H80)) THEN GOTO start
OUT 3, n
n = INP(3)
OUT 3, n
v= INP(3)
OUT 3, v
GOTO start


When you run the program and play some notes slowly, then all should be well, but chords or fast runs will probably result in either lost notes, held notes, or wrong notes. This is not a problem with speed (see next part) or the processing, but a direct result of not taking running status into account.

So it looks like each byte has to be examined to see if it is a status byte or a data byte, and the appropriate inferences made from that. Sounds complicated? Actually, it is not as bad as it might sound. Once you know that you need to keep track of the current status, then a couple of changes to the basic loop will suffice in coping with anything which running status can throw at us.

By using the status byte detector described in Part 1 last month, we could detect if each incoming byte is a data byte or a status byte, and then act accordingly. However, since the Note On or Note Off status bytes are always followed by two bytes (note number, then velocity) we can just check the following byte. If running status is active then it will be a data value for the next note number, whereas if running status is not active then it will be a status byte. The overall structure is therefore as follows:

DO
DO
INPUT byte
LOOP UNTIL byte = Note On Status Byte
DO
INPUT next byte (Note Number)
INPUT next byte (Velocity)
LOOP UNTIL next byte = Status Byte
LOOP


The program segment itself follows directly from this:

start:
n = INP(3)
retest:
a = n AND &HF0
escape$ = INKEY$
IF escapeS <>"" THEN GOTO leave
IF NOT((a = &H90) OR (a = &H80)) THEN GOTO start
OUT 3, n
n = INP(3)
do_next:
OUT 3, n
v = INP(3)
OUT 3, v
n = INP(3)
a = n AND &H80
IF (a <> 8.H80) THEN GOTO do next
GOTO retest

Notice that the same program we started with for MDISP is still visible inside this code, the only additions are to cope with running status. The loop which copes with running status starts at the do_next label and ends with the IF statement, which loops back to the do_next label. Because we have to input the byte in order to check it, we must not get another byte when we discover a status byte, and so the main loop now returns to the retest label instead of the start. This program segment is the key to almost all the examples which follow.

NOTE NUMBER



Now that we have a functioning MIDI processing loop, we can do all the 'classic' manipulations to the messages. Starting with just the note number, we can transpose or alter the relationship between the incoming and outgoing notes. The relevant section of program for producing transposition looks like this:

Part of the MKTRN program:
n = INP(3)
do_next:
z = (n AND 8tH7F) + transpose
IF z > 127 THEN z = 127
IF z < 0 THEN z = 0
OUT 3, z
v = INP(3)
OUT 3, v
n = INP(3)
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

The (n AND &H7F) statement is used because the Atari ST assumes that the incoming MIDI is a 16-bit value rather than an 8-bit value. This can mean that the numbers do not appear to be correct, and so it is good practice to always mask them to 8-bits. In this case, the note number, n is a 7-bit value and so is masked with &H7F. The variable called 'transpose' holds the number of semitones we wish to shift the note number — an octave shift would have a transpose value of 12. The two IF statements which check the value of z are to make sure that we do not exceed the allowable range of values for a data byte (0 to 127).

Reversing or inverting the keyboard involves choosing a suitable value to set as the centre, so that no key change occurs — I chose to make C major constant:

Part of MKREV program:
n = INP(3) AND &H7F
do_next:
OUT 3, 120-n
v = INP(3)
OUT 3, v
n = INP(3) AND &HFF
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

The OUT 3, 120-n statement creates the reversal by subtracting the note number from 120. Middle C (often called C3) is specified as note number 60 in the MIDI Specification, and 120 is the highest value which is a multiple of 12 and also below 127. The result of the subtraction is a reversal around middle C.

By keeping the status the same and adding in new note number and velocity values, it is possible to make any incoming note produce two or more notes at the output:

Part of MKADD program:
n = INP(3) AND &HFF
do_next:
OUT 3, n
v = INP(3)
OUT 3, v
OUT 3, (n+add) AND &H7F
OUT 3, v
n = INP(3) AND &HFF
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

The added note is produced by the two extra OUT 3 statements. The variable 'add' is added to the original note number and ANDed with &H7F to keep the value within the 0 to 127 allowable range. The transpose example above shows an alternative way of doing this limiting function. The IF statements method limits the value to 0 and 127 whilst the ANDing method wraps around, so that values larger than 127 appear as low values. In this particular case, I felt that obtaining a low note was preferable to fixed high notes, although the key does change for the low note. You may like to try and work out how to do the wrapping so that it does not change the key — the Reverse example above may give you some clues...

Adding more than one note is just a matter of adding extra note number and velocity OUT statements:

Part of MKCHD program:
n = INP(3) AND &H7F
do_next:
OUT 3, n
v = INP(3)
OUT 3, v
OUT 3, (n+add) AND &H7F
OUT 3, v
OUT 3, (n+add2) AND &H7F
OUT 3, v
n = INP(3) AND &HFF
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

In these examples the output notes are forced to track the velocity of the incoming note, by making the velocity of these extra notes the same as the original incoming note. You might like to experiment with fixed velocities...

Notice that we are forced to keep the status the same in all these examples — if we need to change channel, say, then the status will need to be changed also, and so we cannot use this technique to change channels.

In the next part we will see how to get around this restriction.

TIME CHANGES



By inserting a time delay into the loop we do rather more than just delay the MIDI events; instead, we lengthen the minimum time between successive events. Since the running status bytes are delayed in the same way as the ordinary notes, this can turn chords into arpeggios! This can be useful as a means of 'flamming' chords:

Part of MKFLM program:
n = INP(3)
do_next:
t! = TIMER
DO:LOOP UNTIL (TIMER-t!)>Flam!
OUT 3, n
v = INP(3)
OUT 3, v
n = INP(3)
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

The delay uses a special BASIC variable called TIMER, which returns a number that represents the system clock. The following program segment:

t! = TIMER
DO:LOOP UNTIL (TIMER-t!)>Flam!


sets the variable t! to the value of the system time and then waits until a time set by the 'Flam!' variable has passed.

By moving the delay statements so that the delay only happens for events which are not using running status, we can create a delay which is more like what you might expect!

Part of MKDLY program:
n = INP(3)
t! = TIMER
DO:LOOP UNTIL (TIMER-t!)>delay!
do_next:
OUT 3, n
v = INP(3)
OUT 3, v
n = INP(3)
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

In this case, we really are using running status to our own advantage, since without it, the program needed to detect blocks of events happening closely together, and delaying them as a group would be horrendous! Also notice that because the value of the note number is not changed, the ANDing of received bytes with &HFF or &H7F used in previous examples is not required, because the OUT statement only sends the lower eight bits.

VELOCITY



What about the velocity byte? So far the note number and time have been changed, but what can you do to velocity? The potential trap when manipulating the velocity value is hidden in the fact that some instruments use a Note On message with a velocity value of zero as the equivalent of a Note Off message (Yamaha instruments do this, for example). So the program must always cater for the possibility of zero velocity values, otherwise notes will not turn off!

With this in mind, reversing the velocity values so that hard and soft playing are inverted is very similar to the keyboard reversing example shown above, but with the zero detection added:

Part of MVREV program:
n = INP(3)
do_next:
OUT 3, n
v = INP(3) AND &H7F
IF v <> 0 THEN v = 128-v
OUT 3, v
n = INP(3)
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

Something we did not try with note numbers (although you could, if you wished! I can't think of any sane reason to do so) is to quantise the value so that it uses a coarser representation. Many instruments do not use all the 128 possible velocity values anyway, so this can demonstrate just how few different velocity values you can actually detect by ear:

Part of MVQNT program:
IF mask_bits = 1 THEN mask = &H40
IF mask_bits = 2 THEN mask = &H60
IF mask_bits = 3 THEN mask = &H70
IF mask.bits = 4 THEN mask = &H78
IF mask_bits = 5 THEN mask = &H7C
IF mask_bits = 6 THEN mask = &H7E
IF mask_bits = 7 THEN mask = &H7F
n = INP(3)
do_next:
OUT 3, n
v= INP(3) AND &H7F
IF v <> 0 THEN v = v AND mask
OUT 3, v
n = INP(3)
a = n AND &H80
IF (a <> &H80) THEN GOTO do_next
GOTO retest

The variable 'mask_bits' sets the number of bits which are used to represent the velocity value. ANDing the velocity with this mask is equivalent to a quantise operation (if only sequencer quantising were as easy!).

CONTROLLERS



If you have been using MDISP to look at MIDI messages by monitoring the output of your master keyboard, then you might have noticed that Note On and Off messages are not the only MIDI messages which can use running status. In fact, whenever the same status byte would be repeated (any byte from $8n to $En) you can omit the status byte and use running status. Commonly encountered examples include the Pitch Bend message ($En) and Controllers like the Modulation Wheel ($Bn,$01). For example:

EO 40 00 F8 41 00 42 00 B0 01 00 01 01 F8 01 02 01 03

is the start of a Pitch Bend message which is interrupted by a MIDI clock ($F8) and then followed by a Modulation Wheel message, which is in turn interrupted by a further MIDI clock.

As you might expect, each of these strings of running status cannot be interrupted by another status byte without the original status being restored — there is no default status. The $E0 Pitch Bend message was followed by the $B0 Modulation Wheel message, and if further Pitch Bend messages followed, they would start with another $E0 byte to set the status.

The only exception to this are the System Real Time messages: MIDI Clock, Start, Stop, Continue, Active Sensing, and System Reset — all of these are single byte commands. For example:

90 3C 65 F8 40 67 F8 43 60

is the same C major chord as in our earlier example, but interrupted by two MIDI clocks ($F8), and with the running status intact. A System Real Time byte temporarily suspends the running status but the status continues once the byte has been dealt with. Because the System Real Time bytes are all greater than or equal to $F8, it is quite easy to detect them:

a = INP(3) AND &HF8
IF (a = &HF8) THEN PRINT "System Real Time"

This simple program segment forms the basis of the MSRTD System Real Time Detector program. If you have a drum machine or a workstation equipped with a hardware sequencer, then you should be able to detect the MIDI clock messages, and using MDISP should show that these bytes can occur even during a block of running status bytes.

Although the Active Sensing byte ($FE) is a System Real Time message, you will not normally see it interrupting a running status message. This is because the Active Sensing byte is only sent when there is no other MIDI activity — and so the two are mutually exclusive.

Just as the MDISP program could be expanded to provide a complete MIDI channel activity display program (like the MIDIscope program described in my 'System Exclusive' series [SOS April 1989 to March 1990], so the DSRTD program could be used as the skeleton on which to build a System activity display program, like the SystemScope program described in the same series.

CONCLUSION



As we erect our tents and sit around the camp fire [not more safari analogies - Ed!!], it is a good time to reflect on what we have discovered on this month's safari. We have looked in detail at the purpose and side-effects of running status, and used it to help produce some useful and not so useful changes to a stream of MIDI messages. So far the changes have all been made without altering the current status — our next trek will take us into the world of status swaps, where we need to juggle with two or more statuses (stati?) at once. So get your pith helmets and jungle knives ready for Part 3!

PROGRAM DISK



An Atari ST floppy disk accompanies this series which contains all the example programs mentioned. It is available from SOS Software, price £7 inc postage.

SOS Software, (Contact Details).

WHY USE BASIC?

Some readers may be wondering why I have chosen to use BASIC for the programming examples, instead of a more 'serious' language like C. There are two reasons: Firstly, the examples are just that — illustrations of concepts rather than program segments which could be used in the real world. A modern structured BASIC, like HiSoft BASIC, offers program segments which look very much like a generalised pseudo-code, but can also be compiled and used for learning purposes. This readability and writability make BASIC a good choice for users who may be unfamiliar with programming.

Secondly, the examples are almost exclusively very 'low level' program segments, dealing with the fundamental bits and bytes of MIDI messages. In most real world programs, these would be programmed in assembler, not a high-level language, and so C would be inappropriate. I feel that BASIC is much easier to understand than 68000 assembler, especially for a technical musician. The actual program segments described in this series translate very closely to the sort of assembler routines I have used for manipulating MIDI (GOTOs are JUMPS, after all), and so the use of BASIC seems completely justified.


PROGRAM SKELETON

All of the programs on the Atari disk have a similar structure. The program segments used to illustrate the concepts described in the text are the major MIDI processing part of the program. The program structure looks like this:

Initialisation
Setup Buffer
Input Parameters
DO
DO
Special MIDI Processing
LOOP UNTIL Qwerty key PRESSED
IF key = C THEN GOTO Input Parameters
DO
Normal MIDI Processing = bypass
LOOP UNTIL Qwerty key PRESSED
IF key = C THEN GOTO Input Parameters
LOOP UNTIL key = Q
Send All Notes Off
Restore Buffer

The buffering will be described in the next part of this series.

The 'Special MIDI Processing' is the segment normally shown in the article text, since it is where the MIDI manipulation occurs. The program normally loops around between the Special and Normal loops, with the Atari's spacebar toggling between them. When the program is exited, All Notes Off messages are sent on all the MIDI channels. This should help to prevent any problems with unwanted sustaining notes, although changing the sound with a Program Change often resets the instrument's own MIDI software.


EXAMPLE PROGRAMS

The following is a list of all the example programs on the disk that accompanies this series:
MKKEY Keyboard Remapper
MKREV Keyboard Reverser
MKDLY Keyboard Delayer
MKOCT Keyboard Octave Reverser
MKFLM Keyboard Flammer
MKADD Keyboard Add Octave/Fifth, etc
MKSTC Keyboard Staccato-iser
MKTRN Keyboard Transposer
MKCHD Keyboard Chorder
MK2CH Keyboard 2 Channels At Once
MK4CH Keyboard 4 Channels At once
MKSWP Keyboard Velocity/Note Swapper
MKV2N Keyboard Velocity Transpose
MKV2T Keyboard Velocity Transpose with constant velocity
MKV2P Keyboard Velocity to Pitch Bend
MKSPL Keyboard Splitter
MVREV Velocity Reverser
MVQNT Velocity Quantiser
MVSPL Velocity Splitter
MCHAN01 Channeliser
MCHAN04 GEM Channeliser
RSTAT Running Status Detector
MDRS Running Status Remover
MBUFF MIDI Buffer Display
MDISP MIDI Flex Display
MPQNT Pitch Bend Quantiser
MCLOCK MIDI Clock Humaniser
MDRADD Note to Drum Converter
MCLKHA MIDI Clock Halver
MCLKDB MIDI Clock Doubler
MASCLK Active Sensing to MIDI Clock
MKN2D Note to Start/Stop Control
MKN2PC Keyboard Note to Program Change
MSIMPL How not to do it!
MAP Mapper


Series

Read the next part in this series:
Adventures In MIDILand (Part 3)



Previous Article in this issue

Ryuichi Sakamoto

Next article in this issue

MOTU MIDI Time Piece


Sound On Sound - Copyright: SOS Publications Ltd.
The contents of this magazine are re-published here with the kind permission of SOS Publications Ltd.

 

Sound On Sound - Sep 1990

Donated by: Bert Jansch / Adam Jansch

Topic:

Computing

MIDI


Series:

Adventures In MIDILand

Part 1 | Part 2 (Viewing) | Part 3 | Part 4 | Part 5 | Part 6


Feature by Martin Russ

Previous article in this issue:

> Ryuichi Sakamoto

Next article in this issue:

> MOTU MIDI Time Piece


Help Support The Things You Love

mu:zines is the result of thousands of hours of effort, and will require many thousands more going forward to reach our goals of getting all this content online.

If you value this resource, you can support this project - it really helps!

Donations for August 2020
Issues donated this month: 1

New issues that have been donated or scanned for us this month.

Funds donated this month: £34.00

All donations and support are gratefully appreciated - thank you.

If you're enjoying the site, please consider supporting me to help build this archive...

...with a one time Donation, or a recurring Donation of just £2 a month. It really helps - thank you!
muzines_logo_02

Small Print

Terms of usePrivacy