Saturday, 5 January 2013

MIDIfied Tube Screamer

My Tube Screamer clone (Blogs passim) now sports a MIDI interface and can be controlled externally by MIDI "Control Change" messages...


I've played with sending MIDI from PICs a lot - that being the essence of my Virtual Organ Project; both in generating Note On / Note off messages from my MIDI bass pedals and various Control Change messages from organ drawbars, switches and other controls. But MIDI Tx is easy. It turns out that receiving MIDI is only slightly more tricky but interpreting and acting on MIDI messages is a whole lot tougher!

In the aerial photograph above, you can see the new MIDI elements at top right. There's another Breadboard Module with the obligatory 5-pin 180 degree DIN socket. There's also a little bit of electronics to squeeze MIDI into the PIC. MIDI is a current loop interface and needs an opto-isolator to terminate the Rx end correctly. I had some 4N25s in the junk box, but couldn't get these to work sweetly. Fortunately, I also had some CNW 136s and these worked very well.

Here's my MIDI Rx schematic...


After this simple circuit, the MIDI signal goes to the Rx pin on the 16F887 at the heart of my Digital TS. Of course, masochists can use bit-banging methods to receive the serial MIDI data (just as I continue to use Ross Bencina's code for sending MIDI in the Virtual Organ). However, for MIDI reception, you really need to use the PIC's UART (or EUSART, as Microchip insist on calling it).

There are any number of experts out there who will explain how to set up the UART for MIDI reception (standard 8-bit at 31250 baud). They will have you looking at the RCIF flag bit of the PIR1 register to see when data has arrived. You'll soon appreciate that you need to do this in an interrupt service routine, such that you can get on and do other things in the meantime (such as servicing the local control interface with its buttons and display). After some messing around, you'll get a MIDI stream into your controller - but what do you do with it?

The hard part isn't the reception of MIDI data (despite what you may think if you're struggling to get it going). The hard part is making your system react to it. Here's how I went about it...

Currently I have four "controls" in the digital tube screamer, matching exactly the user controls of the original guitar effect pedal. These controls are 'Drive', 'Tone', 'Level' and 'Bypass'. I want to be able to send values for each of these controls via MIDI from a control device.

MIDI has a feature exactly matched to this requirement (no surprise, since this is exactly what MIDI was conceived for), called "Control Change Messages". These messages identify a particular control on a particular MIDI channel (together constituting an "address" for that control) and pass it a numerical value (an integer between 0 and 127). It is the job of the receiving hardware to understand the message and to apply the value to the control (equivalent in this case to setting the position of one of the knobs on the Tube Screamer or operating its Bypass switch).

Here's my interrupt service routine which does the job...
;=====================================================================================
; PIC 16f887 (etc) Interrupt Service Routine
; to parse a MIDI stream for control change messages of format...
; b'1011nnnn', b'0ccccccc', b'0vvvvvvv'
; where
; nnnn is the MIDI channel number-1 (0:15)
; ccccccc is the control number (0:127)
; vvvvvvv is the control value (0:127)
;
; MIDIST is a byte of flags indicating MIDI State
; MIDI_Ch is the MIDI channel-1 (nnnn), stored as a variable
; LvlCtl, TonCtl, DrvCtl & BypCtl are control numbers (ccccccc), defined as constants
;
; These controls were specific to my MIDI Tube Screamer Application -
; you can modify / add to suit your own project
;
; m0xpd
; shack.nasties "at gee male dot" com
; http://www.m0xpd.blogspot.com
; January 2013
;
; Terms of use:
; Tell me if you like it,
; Mention me if you use it,
; Thank me if you sell it,
; Don't blame me if it doesn't work.
;=====================================================================================
IntSR
movwf _w ; Save the Current Values
swapf STATUS, w
movwf _status
bcf STATUS, RP0 ; Make Sure We're in Bank 0
;
call MIDIRx ; get MIDI byte (saved in RX_TEMP)
;
;==================================================================
; Parse MIDI Stream for valid Control Change message...
;==================================================================
; Check the MIDIST Byte...
btfsc MIDIST,7 ; was last MIDI byte a Control Change?
goto Control ; YES - so go deal with it
; NO - but is THIS byte Control Change?
movf MIDI_Ch,w ; get the MIDI Channel
addlw 0xB0 ; add the Control Change prefix
subwf RX_TEMP ; compare to the current MIDI byte...
btfss STATUS,Z ; is it CC on the correct Channel?
goto NoCC ; No it isn't
clrf MIDIST ; Yes, so put 0x80 into MIDIST
bsf MIDIST,7 ; to set the "CC" Flag
goto Idone ; and wait 'til next byte
NoCC
clrf MIDIST ; Not a CC - so reset MIDI state
goto Idone ; and wait for next byte
Control
movf MIDIST,w ; If we get here, CC has been set...
andlw 0x7F ; is any other flag set?
btfss STATUS,Z
goto setMIDIControl ; Yes - so this byte is the
; control value - go deal with it
movf RX_TEMP,w ; No - see if byte is a valid Control...
sublw LvlCtl
btfss STATUS,Z ; is it Level ?
goto Control2 ; No - try next...
bsf MIDIST,6 ; Yes - set Level Flag
goto Idone ; and wait til next MIDI byte
Control2
movf RX_TEMP,w
sublw DrvCtl
btfss STATUS,Z ; is it Drive ?
goto Control3 ; No - try next...
bsf MIDIST,5 ; Yes - set Drive Flag
goto Idone ; and wait til next MIDI byte
Control3
movf RX_TEMP,w
sublw TonCtl
btfss STATUS,Z ; is it Tone ?
goto Control4 ; No - try next...
bsf MIDIST,4 ; Yes - set Tone Flag
goto Idone ; and wait til next MIDI byte
Control4
movf RX_TEMP,w
sublw BypCtl
btfss STATUS,Z ; is it Bypass ?
goto ControlFail ; No - give up...
bsf MIDIST,3 ; Yes - set Tone Flag
goto Idone ; and wait til next MIDI byte
;
; add in code to handle three more Flags in MIDIST here, if needed
; or use one of them to handle a System Exclusive message
;
ControlFail
clrf MIDIST ; No valid control received
goto Idone ; so reset and wait...
setMIDIControl
; We have a Control Flag set -
; find which one it is...
btfsc MIDIST,6 ; it is Level?
goto setMIDILevel ;
btfsc MIDIST,5 ; it is Drive?
goto setMIDIDrive
btfsc MIDIST,4 ; it is Tone?
goto setMIDITone
btfsc MIDIST,3 ; it is Bypass?
goto setMIDIBypass
setMIDIControlFail
clrf MIDIST ; No valid control received
goto Idone ; so reset and wait...
setMIDILevel
; Set the Level control
; Change these lines to do
; something different in your application
; but make sure it doesn't take too long
; or you may miss the next MIDI byte
movf RX_TEMP,w
movwf M3_Opt
call SetLevl
; If you changed stuff, pick up here...
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
setMIDIDrive
; Set the Drive control
movf RX_TEMP,w
movwf M1_Opt
call SetDrive
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
setMIDITone
; Set the Tone control
movf RX_TEMP,w
movwf M2_Opt
call SetTone
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
setMIDIBypass
; Set the Bypass State
movf RX_TEMP,w
btfsc RX_TEMP,6 ; If control value >63
goto Turn_On ; branch
; otherwise
bsd 0x03 ; turn effect off
; 'bsd' and 'bcd' are MACROS which set and clear
; PORTD bits safely
; (DON'T use bsf or bcf on a PORT!!!)
goto setMIDIBypass_Done
Turn_On
bcd 0x03 ; turn effect on
setMIDIBypass_Done
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
Idone
swapf _status, w ; Return from Interrupt with Valid Regs
movwf STATUS
swapf _w
swapf _w, w
retfie
;
; End of Interrupt Service Routine
;========================================================================
view raw PIC MIDI Parser hosted with ❤ by GitHub
You can modify this code to parse a MIDI stream for your own application - the only thing it doesn't (try to) do at the moment is handle "System Exclusive" messages. In my case, I'm happy to take the (very small) risk that one such message might mess with the controls of my Tube Screamer.

I tested my MIDIfied Tube Screamer by generating MIDI using the superb MIDI-OX software. I guess the next step is to make a little controller to replace the computer and MIDI-OX.

Wait a minute - wouldn't it have been easier to just put knobs on the Tube Screamer? Now there's a thought...

 ...-.- de m0xpd

No comments:

Post a Comment