Andy Honeybone gets weaving with this month's piece of software... a MIDI transposer. Crazy project, crazy guy.
ARMED WITH a sequencer, MIDI keyboard bank, orchestral score and overwhelming desire to tread the path worn deep by Walter Carlos and Isao Tomita, you would soon find the need for this month's software project.
In short, it's a MIDI transposer to get the sequencer always to record concert pitch. But its uses are manifold. Suppose that you've slaved for weeks to produce a sequenced backing track. When you finally meet with Aretha, she wants it in a different key.
Solution — pass the MIDI data through this code and shift every channel up or down at a stroke. And the same code will allow you to 'tune' your digital oscillators in fifths for those groovy Egg and Van Der Graaf Generator Hammond-sounds from the old days.
Enough hype. This is a further example of real-time data processing similar to the MIDI Mapper featured previously. The Mapper only had to process status bytes and so could do so "on the fly". The Transposer, however, leaves the status bytes and just affects the note (key) data. Because of the scourge of running status (last status remains in force until superseded by a change in status), the program has to keep tabs on the current status so that the following data can be correctly interpreted.
For this application, a circular buffer is used which is filled by an interrupt routine. The main part of the program removes bytes from this buffer such that it never (hopefully) laps itself. The result is that time is very slightly 'bent' to our advantage. As space is short, I'll cut the philosophy and get on with a more detailed description of the code.
There are three MIDI status commands concerned with key values: note on, note off and polyphonic aftertouch. Although keyboards which generate the latter status type are thin on the ground, that is no reason for omitting its handling by this program (especially as it's easy).
The transposition algorithm can be explained thus: first, examine an input byte and if it is a status byte, store it as the running status unless it is system real-time. Pass on all status bytes to the output. If the initial byte is not a status byte check to see if the running status is one of the three requiring transposition. If so, add an offset to the received byte. Check that the new value is within the allowed data range and send the altered byte out.
Next grab the following byte from the buffer (key velocity or aftertouch) and send it out unadulterated. Finally, if the byte was not a status byte and the running status was such that no transposition was required, the byte is simply passed on. Phew!
I've introduced some new programming styles this time so a few words won't go amiss. The interrupt code is written without redundant entry points into FORTH. The assembler vocabulary is selected, then the starting address of the routine is pushed on to the stack by the word BEGIN for later storage as a CONSTANT.
The circular buffer is 256 bytes 'round' and additions are made by indexing via the Y register. With continued incrementation, the pointer to the buffer eventually overflows and hence points back to the start. Data is extracted from the buffer by the definition check_buf which maintains the output pointer and count values.
To get good speed performance, the system interrupts of the Beeb are turned off and this makes it imperative that the program is excited correctly so that normal operation can be restored.
The definition prepare initialises the pointers to the circular buffer, turns off the maskable interrupts, and loads a JMP instruction and the address of the non-maskable interrupt service routine over the existing code. The complementary tidy_up restores the original nmi service routine, resets the MIDI interface and enables the system interrupts.
Finding if the running status is concerned with key values is the work of is_key which shifts the byte four places to the right and masks out the top five bits. This gives the seven status commands numbers one to seven irrespective of channel. Fortunately, or perhaps by design, the first three commands are those which require key value transposition and their differentiation is hence easily accomplished.
System real-time commands are single byte affairs such as clock, stop, start, continue, and active sensing. They may be sent at any time and do not affect the running status. Their detection in is_rt is by masking with &F8 which is the lowest value real-time command.
It is important during transposition that a data byte is not elevated to a status byte which, if not trapped, would cause all hell to break loose. The definition limit keeps the transposed data bytes within the allowed range 0 to 127.
The main definition is the algorithm described above. Additionally, depending on the state of the echo byte, the original untransposed bytes are also transmitted so that a mother keyboard can play parallel intervals.
During development, I made a guff which you may care to avoid while hacking. To allow the echoing of the untransposed data, I included the necessary code in the interrupt service routine. With hindsight it's easy to see that this data would interleave the transmissions from the main body of the code and result in total confusion. Another slight error of judgment was to forget to turn the echo function off when transposing a whole multichannel sequence.
The coding of this particular utility really shows off the advantages of FORTH. Traditionally, the entire works would have been written in assembler. Admittedly, Pascal or C could be used to generate the code but neither can offer interactive debugging or the compactness or the ease of dropping into assembler when absolutely necessary. End of commercial.
Hopefully, you'll be able to use the transposer with no instruction other than to enter the interval as a number of semitones — a negative number for down, positive for up. Unless the connected device sends continuous clocks or active sensing bytes, you'll have to do something to generate some MIDI transmission before the code will scan the function keys. It's worth it though — vocalists will flock to your micro.
Feature by Andy Honeybone
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!