<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 INVESTIGATIONING III
The Messages
Well first of all, we need a way to grab and format messages.
Step1:  We want a way to grab the messages with the appropriate time, put them in a list, also print them out as they arrive, except for the clock ones, which we don't want because they would spam the whole thing. Also maybe include a nice signal to terminate, such as the foot pedal or something.
Step2:  Now let's try it with the initial send after power on.
Step3:  Hmmm, what about if we have a song selected instead of a style?
Step4:  That's with song 001 selected.
How about a blank user song?
Step5:  Huh, it looks like it actually sends a pedal sustain for that.
Let's use a note_on instead.
Step6:  With a blank user song, it looks like there are more of the settings set (to defaults?)
How about if we have an initial send, but with the song out OFF?
Step7:  And how about song and style out OFF?
Step8:  I guess with voice out OFF it wouldn't send that either
Step9:  Whoops on that one, I needed to turn keyboard OUT back on to send the note. Ah well.
Now for some QUESTIONS.
Sending messages doesn't change the internal instrument thing, so it shouldn't affect the initial send thing at all.
Step10:  Yep, that does nothing.
... wait, have i got the chorus type wrong this whole time?
Step11:  Okay, i've stuffed something up here.
Step12:  Ok, so that's fixed.
I've noticed that the GM SYSTEM ON resets some things on the panel. Could it reset some other stuff as well?
Here we have the reverb type and chorus type set to weird things, and for good measure let's also change the pitch bend range and the panel sustain and other stuff
Step13:  Now we do GM_SYSTEM ON
Step14:  It looks like the GM_ON reset only affects the reverb and chorus panel settings. nice to know.
Step15:  Huh, it looks like Reset Controllers does nothing to volume either.
Step16:  But it works with Modulation for some reason. hmmmmmm.
Step17:  Let's start from the top
Step18:  No sound.
Step19:  Still no sound.
Step20:  We can conclude that RESET_CONTROLS does nothing to voice volume, and I have no idea what GM_ON resets it to.
Step21:  All sound on the left.
Step22:  All sound still on the left.
Just checking if i'm not missing anything
Step23:  Nope, the reset doesn't work for pan either.
Step24:  Still reverby.
Step25:  Neither does the chorus
Step26:  Pedal
Step27:  Hey, it does work for pedal! Turns it OFF.
Step28:  Also Panel Sustain, that is, release time
Step29:  I think the default is 0x40, probably.
Step30:  The modulation wheel, I guess, is reset to zero.
Step31:  Expression can be reset as well. Dunno what the default is.
Step32:  I don't even know what portamento even does.
Step33:  Later
I've been reading through the MIDI Specs. Things to note
Step34:  Both notes turn on, and both notes have to be turned off.
Step35:  I hear no difference, but PTC should have one extra effect
Step36:  Now the first note switches off as soon as the second comes on, and doesn't need to be switched off separately.
Step37:  Okay, I think I have a grip on what Portamento Control does. Also, the channel mode messages act on each channel separately, it seems
Step38:  Now, let's try pitch bend.
Step39:  Let's set RPN
Step40:  and decrement
Step41:  Hmm, no change.
How about setting?
Step42:  That's definitely incrementing by semitones, there.
What if we change the RPN and back?
Step43:  Okay, that's some weird stuff.
Step44:  Hypothesis
Step45:  I've annotated the above with the results of the experiment and what I think's going on. Internally, INC/DEC seem to alter the value of an MSB variable, then set the MSB of the current RPN to what the value is now. It's the same variable, no matter which RPN is currently active; it doesn't get reset on GM_ON or XG ON or XG reset (which actually seems to work otherwise). The lesson
Step46:  Okay, so now it looks like if you go below 0 to "-1", you reach a state where it doesn't actually change the value anymore until you INC, and then it jumps to 1. So it's something like
Step47:  No, it does not.
Does setting the LSB change the decrement?
Step48:  No.
Does setting the LSB drag the MSB along with it?
Step49:  No.
F0 43 1n 4C aa aa aa dd .. dd F7
Parameter settings.
Step50:  Seems like the only one that works is the XG Resets.
Does the DGX-505 respond to XG parameter requests?
Step51:  Nope.
Step52:  What about when we just power on?
Step53:  I guess MSB starts at 0.
Step54:  Testing Continues
I suppose now we need to test some other stuff.
Step55:  Pitch bend
Step56:  Release time
Step57:  Attack time, check
Step58:  Harmonic, check
Step59:  Now for Portamento Control
Step60:  Hmmm, it looks like XG reset also resets the MIDI Master Tuning, as well!
Step61:  Seems to be the only difference
Step62:  What About The Bank
Step63:  Let's review
Step64:  There are a lot of different organ voices for program 16, MSB 0.
MSB 127 program 16 is the Rock Kit.
Let's set channel 1 to the DrawOrg, Channel 2 to the Jazz Organ 1.
Step65:  Let's set Channel 0 to the rock kit, program 16
Step66:  Now, Bank LSB should be totally ignored for drum kits.
Step67:  The Bank LSB is treated as 0 (or at least not recognised for the drum kits), but it is still remembered so that if we change the MSB, the LSB value is used then. Setting the MSB does not reset the LSB to zero.
Step68:  It seems like when the LSB is not supported, it falls back to zero, but I'm willing to bet it still remembers it.
Step69:  Yep, it seems like it.
Step70:  Yep, and the bank doesn't get reset either.
What about unsupported banks?
Step71:  They're silent.
Step72:  MSB 0 always has a fallback voice for any program.
What if the program is not supported?
Step73:  If the program is not supported for drum kit, it just won't change the program.
Step74:  If the program is not supported for SFX kit, it just won't play at all.
Step75:  And it seems like that for the SFX voices as well.
Step76:  The LSB fallback works. for SFX voices.
Step77:  but not for SFX kits.
Step78:  Does the INC DEC weirdness leak across channels.
Step79:  No, each channel has its own weirdness variable completely separate.
Even More!
Step80:  LOCAL is not reset by XG on, GM on, or XG Reset
What's the default for Master Volume? I assume 7F
Step81:  How does portamento control work with changed voices or drum kits?
Step82:  Sending the same note seems to require turning the note off twice.
Step83:  Portamento does not work with drum kits.
Step84:  Or SFX kits.
Step85:  SFX voices seem fine, though.
<ASSISTANT_TASK:>
Python Code:
%cd -q '..'
import mido
import copy
from commons import mido_util
from commons import timers
Explanation: INVESTIGATIONING III
The Messages
Well first of all, we need a way to grab and format messages.
End of explanation
from commons.messages import controlstate, controls
def collector(state=None):
    if state is None:
        newstate = controlstate.MidiControlState()
    else:
        newstate = copy.deepcopy(state)
    collection_list = []
    with mido.open_input("DGX-505 MIDI 1") as inport:
        timer = timers.offsetTimer()
        for msg in inport:
            msg.time = timer()
            wrapped = newstate.feed_message(msg)
            collection_list.append(wrapped)
            if msg.type != "clock":
                print(wrapped)
            if wrapped.wrap_type == controls.Control.PEDAL:
                break
    return newstate, collection_list
def writeout(outlist, outname):
    with open(outname, 'w') as outfile:
        for wrapped in outlist:
            outfile.write(str(wrapped.message)+'\n')
Explanation: We want a way to grab the messages with the appropriate time, put them in a list, also print them out as they arrive, except for the clock ones, which we don't want because they would spam the whole thing. Also maybe include a nice signal to terminate, such as the foot pedal or something.
End of explanation
s, c = collector()
len(c)
writeout(c, 'documents/data/initial_send.txt')
s._dump()
Explanation: Now let's try it with the initial send after power on.
End of explanation
s1, c1 = collector(s)
writeout(c1, 'documents/data/initial_send_song1.txt')
Explanation: Hmmm, what about if we have a song selected instead of a style?
End of explanation
s2, c2 = collector(s1)
writeout(c2, 'documents/data/initial_send_user.txt')
Explanation: That's with song 001 selected.
How about a blank user song?
End of explanation
def collector(state=None):
    if state is None:
        newstate = controlstate.MidiControlState()
    else:
        newstate = copy.deepcopy(state)
    collection_list = []
    with mido.open_input("DGX-505 MIDI 1") as inport:
        timer = timers.offsetTimer()
        for msg in inport:
            msg.time = timer()
            wrapped = newstate.feed_message(msg)
            collection_list.append(wrapped)
            if msg.type != "clock":
                print(wrapped)
            if msg.type == "note_on":
                break
    return newstate, collection_list
s3, c2 = collector(s2)
writeout(c2, 'documents/data/initial_send_user.txt')
s3._dump()
Explanation: Huh, it looks like it actually sends a pedal sustain for that.
Let's use a note_on instead.
End of explanation
s4, c4 = collector(s3)
writeout(c4, 'documents/data/initial_send_songoff.txt')
Explanation: With a blank user song, it looks like there are more of the settings set (to defaults?)
How about if we have an initial send, but with the song out OFF?
End of explanation
s5, c5 = collector(s4)
writeout(c5, 'documents/data/initial_send_stylesongoff.txt')
Explanation: And how about song and style out OFF?
End of explanation
s6, c6 = collector(s5)
writeout(c6, 'documents/data/initial_send_alloff.txt')
Explanation: I guess with voice out OFF it wouldn't send that either
End of explanation
s6
s6._channels[0].control_value(controls.Control.VOLUME)
with mido.open_output('DGX-505 MIDI 1') as outport:
    for i in range(16):
        outport.send(controls.cc(controls.Control.VOLUME, channel=i, value=1))
s7, c7 = collector(s6)
Explanation: Whoops on that one, I needed to turn keyboard OUT back on to send the note. Ah well.
Now for some QUESTIONS.
Sending messages doesn't change the internal instrument thing, so it shouldn't affect the initial send thing at all.
End of explanation
s8, c8 = collector(s7)
Explanation: Yep, that does nothing.
... wait, have i got the chorus type wrong this whole time?
End of explanation
cc8 = [x for x in c8 if x.message.type != "clock"]
cc8
" ".join(format(x, "02X") for x in (cc8[1].message.data))
%load_ext autoreload
%autoreload
from commons.messages import wrappers
from commons import enums
wrappers.wrap(cc8[1].message)
Explanation: Okay, i've stuffed something up here.
End of explanation
ns, nsc = collector(s8)
Explanation: Ok, so that's fixed.
I've noticed that the GM SYSTEM ON resets some things on the panel. Could it reset some other stuff as well?
Here we have the reverb type and chorus type set to weird things, and for good measure let's also change the pitch bend range and the panel sustain and other stuff:
End of explanation
with mido.open_output('DGX-505 MIDI 1') as outport:
     outport.send(controls.gm_on())
rs, rsc = collector(ns)
def nonclock(l):
    return (x for x in l if x.message.type != 'clock')
for n, r in zip(nonclock(nsc), nonclock(rsc)):
    if str(n) != str(r):
        print(n, r)
Explanation: Now we do GM_SYSTEM ON
End of explanation
with mido.open_output('DGX-505 MIDI 1') as outport:
    for i in range(16):
        outport.send(controls.cc(controls.Control.VOLUME, channel=i, value=1))
with mido.open_output('DGX-505 MIDI 1') as outport:
    for i in range(16):
        outport.send(controls.cc(controls.Control.RESET_CONTROLS, channel=i, value=1))
with mido.open_output('DGX-505 MIDI 1') as outport:
    for i in range(16):
        outport.send(controls.cc(controls.Control.VOLUME, channel=i, value=127))
Explanation: It looks like the GM_ON reset only affects the reverb and chorus panel settings. nice to know.
End of explanation
with mido.open_output('DGX-505 MIDI 1') as outport:
    for i in range(16):
        outport.send(controls.cc(controls.Control.MODULATION, channel=i, value=127))
with mido.open_output('DGX-505 MIDI 1') as outport:
    for i in range(16):
        outport.send(controls.cc(controls.Control.RESET_CONTROLS, channel=i, value=1))
Explanation: Huh, it looks like Reset Controllers does nothing to volume either.
End of explanation
outport = mido.open_output('DGX-505 MIDI 1')
outport.send(controls.gm_on())
outport.send(controls.local(False))
Explanation: But it works with Modulation for some reason. hmmmmmm.
End of explanation
cc = controls.cc
C = controls.Control
outport.send(cc(C.VOLUME, 0))
Explanation: Let's start from the top: voice volume.
End of explanation
outport.send(cc(C.RESET_CONTROLS, 64))
Explanation: No sound.
End of explanation
outport.send(cc(C.VOLUME, 64))
outport.send(controls.gm_on())
outport.send(cc(C.VOLUME, 110))
Explanation: Still no sound.
End of explanation
outport.send(cc(C.PAN, 0))
Explanation: We can conclude that RESET_CONTROLS does nothing to voice volume, and I have no idea what GM_ON resets it to.
End of explanation
outport.send(cc(C.RESET_CONTROLS, 42))
Explanation: All sound on the left.
End of explanation
for i in range(128):
    outport.send(cc(C.RESET_CONTROLS, i))
Explanation: All sound still on the left.
Just checking if i'm not missing anything:
End of explanation
outport.send(controls.gm_on())
outport.send(cc(C.REVERB, 127))
outport.send(cc(C.RESET_CONTROLS, 42))
Explanation: Nope, the reset doesn't work for pan either.
End of explanation
outport.send(controls.gm_on())
outport.send(cc(C.CHORUS, 127))
outport.send(cc(C.RESET_CONTROLS, 3))
Explanation: Still reverby.
End of explanation
outport.send(controls.gm_on())
outport.send(cc(C.PEDAL, 127))
Explanation: Neither does the chorus
End of explanation
outport.send(cc(C.RESET_CONTROLS, 3))
Explanation: Pedal
End of explanation
outport.send(cc(C.RELEASE, 127))
outport.send(cc(C.RESET_CONTROLS, 3))
Explanation: Hey, it does work for pedal! Turns it OFF.
End of explanation
outport.send(cc(C.RELEASE, 0))
outport.send(cc(C.RESET_CONTROLS, 3))
outport.send(controls.gm_on())
Explanation: Also Panel Sustain, that is, release time
End of explanation
outport.send(cc(C.MODULATION, 127))
outport.send(cc(C.RESET_CONTROLS, 3))
outport.send(cc(C.MODULATION, 0))
Explanation: I think the default is 0x40, probably.
End of explanation
outport.send(cc(C.EXPRESSION, 127))
outport.send(cc(C.EXPRESSION, 64))
outport.send(cc(C.RESET_CONTROLS, 3))
Explanation: The modulation wheel, I guess, is reset to zero.
End of explanation
outport.send(cc(C.PORTAMENTO_CTRL, 12))
Explanation: Expression can be reset as well. Dunno what the default is.
End of explanation
outport.send(cc(0x05, 127))
Explanation: I don't even know what portamento even does.
End of explanation
import time
outport = mido.open_output('DGX-505 MIDI 1')
# Let's use a square lead on channel 5, everyone's favourite channel
controls.multisend(outport, controls.set_voice_numbers(101, 5))
outport.send(mido.Message('note_on', channel=5, note=64))
time.sleep(0.5)
outport.send(mido.Message('note_on', channel=5, note=68))
time.sleep(0.5)
outport.send(mido.Message('note_off', channel=5, note=68))
time.sleep(0.5)
outport.send(mido.Message('note_off', channel=5, note=64))
time.sleep(0.5)
Explanation: Later
I've been reading through the MIDI Specs. Things to note:
The MIDI implementation chart is dated 1 December 2003
The keyboard implements General MIDI Level 1
And also Yamaha's XGLite.
The specification for XG 2.0 can be found through http://www.jososoft.dk/yamaha/docs_specs.htm
dated April 2001. XGLite is a subset of this specification.
Interesting Things From The XG Spec
I'm going to assume that the implementation of XGLite is like XG, only with a whole lot fewer controls and voices available, so no super fancy stuff. Nevertheless, we do have our 16 channel multi timbral tone generator (with 32 note polyphony).
3.1.2 Program Change
There are melody voices and there are rhythym voices (drum kits). 
The specification says:
If the tone generator does not have a drum kit corresponding to the specified program number, it will ignore the message and continue to use the current drum kit.
If Program change makes a change from normal voice to drum kit, the part mode shall revert to what it was before normal mode was invoked. Upon receipt of XG System ON, the system shall virtually set this "revert-to" mode to Drum Setup 2 for all Parts 1 to 16, except for Part 10.
3.1.6.1 Bank Select MSB/LSB
Bank MSB:
|MSB||
|-----------|---|
| 00      | Melody voice |
| 01–3F | Model-exclusive area |
| 40      | SFX voice |
| 41–77 | Reserved for XG extensions |
| 78      | GM L2 Rhythm Kit |
| 79      | GM L2 Melody voice |
| 7A–7D | Reserved for XG extensions |
| 7E      | SFX kit |
| 7F      | Rhythm kit |
Bank LSB is an extension area.
If support is included for one or more voices in an extension bank, then all the other program change numbers in that bank are filled with the corresponding voices of Bank 0 (basic voices)
Note 1: By default, channel 10 plays rhythm voices while other channels use bank 0 melody voices (as in GM Level 1)
Note 2: If the new Bank Select MSB is 00 (melody voice) but the tone generator does not support the melody voice corresponding to the last recieved Bank Select LSB, the channel reverts to the Bank Select LSB corresponding to its most recently played melody voice.
Note 3: If the new Bank Select MSB is 7F (rhythm voice) the tone generator unconditionally uses LSB 00 without using the most recently received Bank Select LSB. If the tone generator does not support a drum kit corresponding to the channel's most recently received Program Change, the channel will revert to the Program Change corresponding to its most recently played rhythm kit.
Note 4: If a Bank Select MSB value of 01–77 or 7A–7E (model-exclusive area, SFX voice, or XG extension voice) is received and the tone generator does not have a voice corresponding to the last received LSB and Program  change, the tone generator shall produce no sound for that channel regardless of subsequent Key On messages.
In the case where a melody voice is being changed first to a voice in bank-LSB A and then to a voice in bank-LSB B, and the change to A is possible but the change to B is not possible, A will be used as the substitute for B. If neither the change to A nor the change to B is possible, the voice of the previous bank will [be] substituted for A and B.
In the case where a rhythm voice is being changed first program number kit A and then to program number kit B, and the change to A is possible but the change to B is not possible, A will be used as the substitute for B. If neither the change to A nor the change to B is possible, the voice of the previous bank will [be] substituted for A and B.
3.1.6.2 Modulation
Control 01, defaults to 00
By default, this message controls vibrato depth only
3.1.6.3 Portamento Time
Has no effect on portamento control. Turns out those things aren't the same thing? Is this a difference in the MIDI Spec?
3.1.6.4 Data Entry MSB / LSB
06 and 26, as usual
3.1.6.5 Main Volume
Control 07, default 64
In the DGX-505 chart this is called "Part Volume".
3.1.6.6 Panpot
Control 0A, default 40
Apparently 00 and 01 are the same (L63)... it goes from there up as you would expect to 7F being R63.
Also, that's what Pan stands for?
3.1.6.7 Expression
Control 0B. default 7F
3.1.6.8 Sustain (Hold)
Control 40. OFF/ON. default 00 (OFF).
This setting also affects the release part of the sound following note-off (after-damper effect).
3.1.6.9–11 Portamento, Sostenuto, Soft Pedal
Not supported by 505.
3.1.6.12–15 Harmonic Content, Release Time, Attack Time, Brightness
Controls 47, 48, 49, 4A respectively
Range 00:-64, 40:0, 7F:+63; default 40 (0)
On some voices, the effective range is narrower than the range which can be set.
Harmonic content adjusts resonance, release/attack adjusts the envelope, brightness adjusts the filter cutoff frequency. Controls are relative to the default 40.
3.1.6.16 Portamento Control
Control 54.
Portamento time is always 0.
... So, what, it just does the portamento instantly or something?
3.1.6.17 Effect Send 1 (Reverb)
Control 5B. Default 28
3.1.6.18 Effect Send 3 (Chorus)
Control 5D. Default 00
3.1.6.19 Effect Send 4 (Variation)
Not supported by 505
3.1.6.20 Data Increment/Decrement
Controls 60, 61.
The data byte is ignored.
3.1.6.21 NRPN
Not supported by 505. This is full of complex stuff for the full XG I suppose.
3.1.6.22 RPN
LSB on 64, MSB on 65. Default: 7F 7F.
The order here is LSB MSB, but the instrument transmits in MSB LSB order.
Pitch Bend Sensitivity
MSB LSB: 00 00. Data on MSB, default 02. LSB ignored.
[Channel] Fine Tune & Coarse Tune
MSB LSB: 00 01, 00 02 respectively.
Data on MSB, with range 00:-64, 40:0, 7F:+63.
Default 40.
Looks like LSB is probably ignored here too.
3.1.7.1 All Sound Off
Control 78, with data 00.
Silences all notes on the channel, but does not reset stuff that was set by channel messages
3.1.7.2 Reset All Controllers
Control 79, with data 00.
Resets to default:
* Pitch Bend
* Modulation
* Expression
* Sustain
* (Portamento, Sostenuto, Soft Pedal)
* Registed Parameter Number.
It also sets off reception of Portamento Control. It does not reset the Portamento source key.
Huh, so how does the portamento control work? Must consult the MIDI spec?
3.1.7.3 All Notes Off
Control 7B, data 00
Turns all notes that are ON, OFF. Doesn't stop playing if in sustain or sostenuto phase until those phases become off as well.
3.1.7.4–7 Omni ON/OFF/Mono/Poly
Not particularly relevant, as instrument only supports mode 3.
Thus, these just act as notes/sound off messages (depending on type: 7C, 7D as Notes Off and 7E, 7F as Sound Off. 
Interestingly according to the spec OMNI ON does not implement OMNI ON.
3.2.1 XG System Exclusive Messages
Parameter Change
Apparently in XG, special parameters can be set with 
F0 43 1n 4C aa aa aa dd .. dd F7.
n is the device number, 4C the model ID, aa aa aa the address hi/mid/lo, dd .. dd the data.
A single message shall be used to set the data value, even if the value's data size consists of multiple bytes. The tone generator shall not accept the message if the number of sent data bytes is less than the required number of data bytes. It shall not be possible, when sending data for a multibyte parameter (such as master tuning), to send the high-order byte only.
Note that during the Initial Send, a sysEx message is recieved:
F0 43 10 4C 00 00 7E 00 F7
So now we have some idea of what this is:
Device 0, set parameter address 00 00 7E to value 00.
Bulk Dump
Sets parameters as a block
F0 43 0n 4C bb bb aa aa aa dd .. dd cc F7
Here, bb bb are Byte Count MSB/LSB, which corresponds to the "TOTAL SIZE" figures in Attached Chart 5 of the spec.
cc is a checksum, such that low-order 7 bits of sum of byte count, address, data, checksum itself = 0.
If sending consecutive bulk dumps, leave an interval of about 10ms between the F7 and the next F0.
Parameter Request
Optional: has no meaning on tone generators without MIDI OUT
Gets the value of a specified parameter
F0 43 4n 4C aa aa aa F7
Dump Request
Optional, as above
F0 43 2n 4C aa aa aa F7
3.2.1.1 System Data Parameters
There are ten zillion of these, which probably aren't implemented in XGLite.
3.2.1.1.1 Master Tuning
Address 00 00 00, data aa bb cc dd, default 00 04 00 00
Master tuning as aa×1000 + bb×0100 + cc×0010 + dd×0001 - 0400 in units of 0.1 cents.
Note: This is not the "MIDI Master Tuning" SysEx message
F0 43 1n 27 30 00 00 mm ll cc F7
3.2.1.1.2 Master Volume
Address 00 00 04, data dd, default 7F.
This parameter value can also be changed by the Universal SysEx Master Volume message
F0 7F 7F 04 01 ll mm F7,
which is documented in the DGX-505 manual (ll ignored)
3.2.1.1.3 Master Attenuator
Address 00 00 05, data dd, default 00. Optional.
3.2.1.1.4 Master Transpose
Address 00 00 06, data dd, default 40, range 28–58 (-24 to +24)
3.2.1.1.5 Drum Setup Reset
Address 00 00 7D, data dd, default --, range 00–(Drum setup count - 1). Initialises drum setup parameters, but doesn't change kit number.
3.2.1.1.6 XG System ON
Address 00 00 7E, data dd. If data is 00, resets tone generator to XG initial state.
Aha, now this is what our message F0 43 10 4C 00 00 7E 00 F7 is.
3.2.1.1.7 All Parameter Reset
Address 00 00 7F, data dd, If data is 00, resets tone generator mode to factory defaults
3.2.1.3 Multi Effect Data Parameters
These are for the Reverb and Chorus (and Variation, but not supported?) Types. Effect type is set by MSB and LSB.
For unsupported MSB, No Effect is used, which is 0.
For unsupported LSB, older models change non-supported LSBs to 0, while newer models calculate
Substitute LSB = Integer part of (given LSB/32) × 32
ending up with 0, 32, 64, or 96.
Considering that the DGX-505 does not support any LSB greater than 32, I think having it be 0 is kind of the same thing.
3.2.2 Yamaha System Exclusive
Optional, for the purpose of compatibility with pre-XG models.
Just the one message:
3.2.2.1 MIDI Master Tuning
F0 43 1n 27 30 00 00 0m 0l cc F7
The DGX-505 implements this one.
3.2.3 Universal System Exclusive
3.2.3.1 GM System ON
F0 7E 7F 09 01 F7
Resets all settings besides MIDI Master Tuning to default.
Acts on MIDI Master Tuning "the same" as XG System ON.
The DGX-505 implements this one.
3.2.3.2 Midi Master Volume
F0 7F 7F 04 01 ll mm F7
The DGX-505 implements this one, ignoring ll.
4.1 Master Tuning
XG has 3 ways of changing master tuning:
XG Master Tuning, for tuning of the song
MIDI Master Tuning, for tuning the system
Panel Settings, for tuning the system
2 and 3 are the same thing, of which latest setting is effective. XG System ON / GM System ON would return the tuning to the most recent 2 or 3 value, while All Parameter Rest / Factory Set would reset everything including 2 and 3.
Relevant Parts From The MIDI Spec
These are documents from the official MIDI specifications, available at https://www.midi.org/specifications (a signup required). There's a whole bunch of documents, which include the complete MIDI 1.0 Specification (v4.2, Februrary 1996) and a whole bunch of recommended practices (RPs) and assorted updates since then.
Running Status
Interesting, but apparently (from some Googling) not supported by USB-MIDI and quite possibly undone by the operating system libraries. So not particularly relevant to us, outside of clearing up some of the examples in the specs themselves. Mido doesn't seem to support parsing it.
Note Off
Note On with velocity 0 is equivalent to Note Off (and useful for Running Status), but a separate Note Off is also provided with its own velocity for devices to use if they want. The DGX505 says it doesn't recognise Note Off velocity (it recognises Note Off messages, as a good implementer would).
Controller Numbers
| Range | |
|-------| |
| 00–1F | MSB for most continuous Controller Data |
| 20–3F | optional LSB for the controllers 00–1F |
| 40–45 | Single byte, usually switches/pedals |
| 46–4F | Single byte, Sound control |
| 50–5A | Other single byte |
| 5B–5F | Single byte, External effect depth |
| 60–65 | Inc/Dec, Parameters |
| 66–77 | Undefined, as of 1996 |
78 to 7F are technically Channel Mode messages, not Control Change messages, but mido doesn't care.
The way MSB/LSB are supposed to work: LSB is optional. Once MSB is sent, then the LSB can be adjusted without sending another MSB message (as long as we don't need to change the MSB, obviously). When setting MSB, the LSB is implicitly set to zero.
Question: Does this apply to Bank MSB/LSB, which falls in the 00–3F range? How about Data Entry MSB/LSB?
The switches work on 00–3F as OFF, 40–7F as ON. An instrument does not technically have to interpret them as switches, and can interpret a non-switch as a switch, if it wants to. The spec recommends values of 00 and 7F for OFF and ON.
General Purpose Controllers
10–13 (with LSB 30–33), and 50–53 (single byte) are the General Purpose Controllers, with manufacturer defined purpose. We don't use any of them.
Bank Select
MIDI transmitters must transmit MSB and LSB as a pair immediately followed by Program Change, as to not confuse if another message is inserted in a gap. 
Sound Controllers
46 through 4A. All except the first are implemented by DGX505. (see the XG section).
4B through 4F have no specified defaults (i.e. up to manufacturer, with no suggestion from the association).
Portamento Controller
54. This is the complicated one, introduced for legato with portamento on POLY mode.
When a Note On is recieved after a Portamento Control, the voice pitch should glide from that specified in the Portamento Control (the pitch reference) to the new Note On, at a rate set by the Portamento Time Controller, ignoring Portamento ON/OFF (which I gather is for some other mode). It only affects the next Note-On on its channel, and does not affect any other notes that are sounding when it is recieved or overlapped (at least for Poly mode), with the exception of the reference note on that channel if it is already on. Note-Off must match the Note-On message, not the Portamento Controller message.
Okay, since the Portamento Time is set to 0 for this purpose in XG, I suppose this means that Portamento Control is only used for legato and not for slower sweeps.
RPN and NRPN
RPN are agreed by the Association, NRPN are manufacturer specific.
NRPN reception should be disabled by default on startup.
When reception of RPN/NRPN is enabled, devices should wait until both MSB and LSB have been set.
Recievers should work with only LSB or MSB set messages, but transmitters should send both just in case each time it is changed.
| RPN (MSB LSB) | parameter |
|---------|-----------|
| 00 00 | Pitch Bend Sensitivity (MSB = semitones, LSB = cents) |
| 00 01 | Fine Tuning |
| 00 02 | Coarse Tuning |
| 00 03 | Tuning Program Select |
| 00 04 | Tuning Bank Select | 
Additional RPNs have since been registered since 1996 as well, specified in the other documents.
Data Inc and Dec
The behaviour was vague in the 1996 spec, so a Recommended Practice document was issued (RP-018, Response to Data Inc/Dec Controllers, approved October 1997):
The value byte is ignored in Data Inc/Dec messages.
For RPN 0 and 1 (Pitch Bend Sensitivity and Fine Tuning), Inc/Dec should adjust the LSB primarily (changing the MSB on overflow). Because Pitch Bend Sensitivity works in cents, it overflows at 100 instead of 128.
For RPN 2, 3, 4, Inc/Dec works on the MSB.
For devices which don't support LSB for Pitch Bend Sensitivity / Fine Tuning, the number of Inc/Dec should be kept track of anyway so the overflow should be made at the right time (for example it should take 100 Incs to change 1 semitone, regardless of whether the sensitivity actually changes inbetween).
Aftertouch
Not implemented on the DGX505. Mido has these as 'aftertouch' for the channel pressure and 'polytouch' for the polyphonic pressure.
Channel Mode Messages
These are the All Notes Off/ Sound Off / Omni / Poly as well as Reset All Controllers and Local Control. These are supposed to be recognised only when sent on the Basic Channel, and affect all the notes on the channels controlled by that Channel. I think the DGX-505 counts as 16 different instruments for this purpose, each their own Basic Channel??
Reset All Controllers
This message should "reset the condition" of all continuous and switch controllers, pitch bend etc to an ideal initial state. This is a little vague in the 1996 spec, so a Recommended Practice was issued (RP-015, Response to Reset All Controllers, approved May 1999), which specifies what should be set default:
|  Control/setting    | default          |
|---------------------| -----------------|
| 0B Expression     | 7F             |
| 01 Modulation     | 00             |
| 40–43 Pedals    | 00             |
| 62–65 (N)RPN    | 7F (null)      |
| Pitch Bend          | 00 40 (centre) |
| Channel pressure    | 0                |
| Polyphonic pressure | 0                |
And what should not be set default:
07 Volume
0A Pan
46–4F Sound controllers
5B–5F Effect controllers
78–7F Channel Mode messages
00&20 Bank Select
Program Change
(N)RPN parameters
This pretty much lines up with the XG spec.
General MIDI ON should perform this as well alongside anything else necessary (so I suppose it would also reset the things that wouldn't normally be reset).
Local Control
Interrupts the "internal control path" from keyboard to sound generator. 
System Real-Time
Clock messages F8 are sent 24 times per quarternote; Start FA and Stop FC are self explanatory. FB is the Continue command, which is used in conjuction with the song position/select messages which aren't implemented by the DGX505.
End Of Exclusive
Exclusive messages can apparently end with any Non-Real-Time Status Byte (with real-time messages inbetween), but F7 should be sent at the end of all SysEx messages. This is possibly unsupported or abstracted away by USB-MIDI.
Active Sensing
FE is sent at least every 300 milliseconds if there is no other data.
System Reset
FF resets everything to power-on. Not implemented by DGX505.
Universal System Exclusive
F0 &lt;id&gt; &lt;device id&gt; &lt;sub id 1&gt; &lt;sub id 2&gt; .. F7
&lt;id&gt; is 7E for Non-Real Time Universal SysEx, and 7F for real time.
&lt;device id&gt; is a byte that should specify the physical device in most cases (as opposed to a channel or virtual device), except for when you need to address the parts separately. 7F is the "all-call" broadcast address.
&lt;sub id&gt; parts are for specifying what type of Universal SysEx message it is.
There's a whole protocol for exchanging files and what not over universal system exclusive messages, with handshakes etc. From inspecting the messages exchanged while transferring MIDI files using Musicsoft Downloader, the DGX-505 does not use this (it uses some Yamaha specific protocol) but it might be similar. Something to investigate later, perhaps.
Interestingly, the way that this protocol "7-bit-izes" octets is different from the way that the DGX-505 uses for its bulk dump: the leading bits are transmitted before the rest instead of afterward.
This avoids the need for padding at the end.
There's a whole thing for microtuning and sample banks and dumps.
There's also a message for turning General MIDI OFF, F0 7E &lt;id&gt; 09 02 F7 if the device supports it.
Questions
Now I've read through the spec, we still have some Questions:
Does the DGX-505 support any of the more advanced XG settings beyond those listed in the manual?
What's the deal with LSB when MSB is set for bank and data entry? Does adjusting the LSB use the MSB previously set or will it jump somehow to the last one used, even for a different RPN?
Does DGX-505 (and/or XG(Lite) in general) have the proper behaviour for Data Inc/Dec as specified in RPN-018?
What is up with the Basic Channel?
What, exactly, does Portamento Control do?
Let's do these in reverse order.
First up, Portamento Control.
End of explanation
outport.send(controls.cc(controls.Control.PORTAMENTO_CTRL, channel=5, value=64))
outport.send(mido.Message('note_on', channel=5, note=68))
time.sleep(0.5)
outport.send(mido.Message('note_off', channel=5, note=68))
Explanation: Both notes turn on, and both notes have to be turned off.
End of explanation
outport.send(mido.Message('note_on', channel=5, note=64))
time.sleep(0.5)
outport.send(controls.cc(controls.Control.PORTAMENTO_CTRL, channel=5, value=64))
outport.send(mido.Message('note_on', channel=5, note=68))
time.sleep(0.5)
outport.send(mido.Message('note_off', channel=5, note=68))
Explanation: I hear no difference, but PTC should have one extra effect:
End of explanation
outport.send(mido.Message('note_on', channel=5, note=64))
outport.send(mido.Message('note_on', channel=5, note=68))
outport.send(mido.Message('note_on', channel=5, note=71))
time.sleep(0.5)
outport.send(controls.cc(controls.Control.PORTAMENTO_CTRL, channel=5, value=68))
outport.send(mido.Message('note_on', channel=5, note=56))
time.sleep(0.5)
outport.send(controls.cc(controls.Control.PORTAMENTO_CTRL, channel=5, value=64))
outport.send(mido.Message('note_on', channel=5, note=56))
time.sleep(0.5)
outport.send(controls.cc(controls.Control.PORTAMENTO_CTRL, channel=5, value=71))
outport.send(mido.Message('note_on', channel=5, note=56))
time.sleep(0.5)
outport.send(controls.cc(controls.Control.SOUND_OFF_XPOLY, channel=5, value=0))
outport.send(mido.Message('note_on', channel=5, note=64, velocity=64))
time.sleep(0.5)
outport.send(controls.cc(controls.Control.PORTAMENTO_CTRL, channel=5, value=64))
outport.send(mido.Message('note_on', channel=5, note=68, velocity=1))
time.sleep(0.5)
outport.send(mido.Message('note_on', channel=5, note=68, velocity=0))
Explanation: Now the first note switches off as soon as the second comes on, and doesn't need to be switched off separately.
End of explanation
def panic():
    for i in range(16):
        outport.send(controls.cc(controls.Control.SOUND_OFF, channel=i, value=0))
Explanation: Okay, I think I have a grip on what Portamento Control does. Also, the channel mode messages act on each channel separately, it seems
End of explanation
def pb(iv=0.5):
    outport.send(mido.Message('pitchwheel', channel=5, pitch=0))
    outport.send(mido.Message('note_on', channel=5, note=64))
    time.sleep(iv)
    outport.send(mido.Message('pitchwheel', channel=5, pitch=+8191))
    time.sleep(iv)
    outport.send(mido.Message('note_off', channel=5, note=64))
    time.sleep(iv)
Explanation: Now, let's try pitch bend.
End of explanation
C = controls.Control
cc = controls.cc
outport.send(cc(C.RPN_MSB, channel=5, value=0))
outport.send(cc(C.RPN_LSB, channel=5, value=0))
Explanation: Let's set RPN:
End of explanation
outport.send(cc(C.DATA_DEC, channel=5, value=0))
pb()
Explanation: and decrement:
End of explanation
outport.send(cc(C.DATA_MSB, channel=5, value=12))
pb()
outport.send(cc(C.DATA_DEC, channel=5, value=0))
outport.send(cc(C.DATA_MSB, channel=5, value=0))
for i in range(12):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
Explanation: Hmm, no change.
How about setting?
End of explanation
outport.send(cc(C.RPN_LSB, channel=5, value=1))
outport.send(cc(C.RPN_LSB, channel=5, value=0))
for i in range(12):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
outport.send(cc(C.DATA_MSB, channel=5, value=0))
pb()
outport.send(cc(C.DATA_MSB, channel=5, value=0))
for i in range(1):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
outport.send(cc(C.RPN_LSB, channel=5, value=1))
for i in range(1):
    pb(0.15)
outport.send(cc(C.RPN_LSB, channel=5, value=0))
for i in range(1):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
outport.send(cc(C.RPN_MSB, channel=5, value=1))
for i in range(1):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
outport.send(cc(C.RPN_MSB, channel=5, value=0))
outport.send(cc(C.RPN_MSB, channel=5, value=0x7F))
outport.send(cc(C.RPN_LSB, channel=5, value=0x7F))
for i in range(4):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
outport.send(cc(C.RPN_MSB, channel=5, value=0))
outport.send(cc(C.RPN_LSB, channel=5, value=0))
for i in range(4):
    outport.send(cc(C.DATA_INC, channel=5, value=0))
    pb(0.15)
Explanation: That's definitely incrementing by semitones, there.
What if we change the RPN and back?
End of explanation
outport.send(controls.gm_on())
def pb(iv=0.1):
    outport.send(mido.Message('pitchwheel', pitch=0))
    outport.send(mido.Message('note_on', note=64, velocity=90))
    time.sleep(iv)
    outport.send(mido.Message('pitchwheel', pitch=+8191))
    time.sleep(iv)
    outport.send(mido.Message('note_off', note=64))
    time.sleep(iv)
Explanation: Okay, that's some weird stuff.
End of explanation
XGRESET = mido.Message.from_hex("F0 43 10 4C 00 00 7E 00 F7")
outport.send(controls.gm_on()) # reset all
outport.send(cc(C.DATA_MSB, value=0))  # MSB = 0
pb()  # PBS == 2
for i in range(2):
    outport.send(cc(C.DATA_INC, value=0)) # MSB = 2
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0))  # RPN -> PBS
pb() # PBS == 2
#outport.send(cc(C.DATA_MSB, value=0))
outport.send(cc(C.DATA_INC, value=0)) # MSB = 3, PBS = MSB
pb()  # PBS == 3
outport.send(controls.gm_on())
# reset, RPN -> NULL, PBS = 2, but MSB stays 3 (?!)
pb() # PBS == 2
outport.send(cc(C.DATA_INC, value=0))  # MSB = 4
outport.send(cc(C.RPN_MSB, value=0))  # RPN -> PBS
outport.send(cc(C.RPN_LSB, value=0))
pb() # PBS == 2
outport.send(cc(C.DATA_INC, value=0)) # MSB = 5
pb()  # PBS == 5
outport.send(cc(C.RPN_MSB, value=127))
outport.send(cc(C.RPN_LSB, value=127)) # RPN -> NULL
for i in range(4):
    outport.send(cc(C.DATA_DEC, value=0)) # MSB = 1
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0))  # RPN -> PBS
pb() # PBS == 5
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # PBS == 0
outport.send(mido.Message.from_hex("F0 43 10 4C 00 00 7E 00 F7"))
# XG ON
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
outport.send(cc(C.DATA_INC, value=0)) # MSB = 1
pb() # PBS == 1
outport.send(mido.Message.from_hex("F0 43 10 4C 00 00 7F 00 F7"))
# XG Reset
pb() # PBS == 2
outport.send(cc(C.DATA_INC, value=0)) # MSB = 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
outport.send(cc(C.DATA_INC, value=0)) # MSB = 3
pb() # PBS == 3
outport.send(mido.Message.from_hex("F0 43 10 4C 00 00 7F 00 F7"))
# XG Reset, RPN -> NULL
pb() # PBS == 2
outport.send(cc(C.DATA_MSB, value=12)) # MSB = 12
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 11
pb() # PBS == 11
Explanation: Hypothesis: INC/DEC doesn't do anything until MSB/LSB has at least been set once. Even if that setting doesn't actually do anything.
End of explanation
outport.send(controls.gm_on()) # reset all
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0))  # RPN -> PBS
outport.send(cc(C.DATA_MSB, value=0)) # MSB = 0
pb() # PBS == 0
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0? -1?
pb() # PBS == 0
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0? -1?
pb() # PBS == 0
outport.send(cc(C.DATA_INC, value=0)) # MSB = 1
pb() # PBS == 1
outport.send(cc(C.RPN_LSB, value=1)) # RPN -> Fine Tune
pb() # PBS == 1
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # PBS == 1, but we've jumped down a semitone
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
pb() # PBS == 1
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0? -1 ? 
pb() # PBS == 1
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0? -1 ? 
pb() # PBS == 1
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0? -1 ? 
pb() # PBS == 1
outport.send(cc(C.DATA_INC, value=0)) # MSB = 1 ? 
pb() # PBS == 1
outport.send(cc(C.DATA_INC, value=0)) # MSB = 2 ? 
pb() # PBS == 2
outport.send(controls.gm_on()) # reset all
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0))  # RPN -> PBS
outport.send(cc(C.DATA_MSB, value=4)) # MSB = 4
pb() # PBS == 4
outport.send(cc(C.RPN_LSB, value=1)) # RPN -> Fine Tune
pb() # PBS == 4
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 4
pb() # PBS == 4, but we've jumped down almost a semitone
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 3
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 2
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 1
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # PBS == 4,
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
pb() # PBS == 4
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # PBS == 4
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # PBS == 4
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # PBS == 4
outport.send(cc(C.DATA_INC, value=0)) # MSB = 1
pb() # PBS == 1
outport.send(cc(C.DATA_INC, value=0)) # MSB = 2
pb() # PBS == 2
outport.send(controls.gm_on()) # reset all
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=1))  # RPN -> Fine Tune
outport.send(cc(C.DATA_MSB, value=127)) # MSB = 127
pb() # PBS == 2, but we've jumped up a semitone
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
pb() # PBS == 2
outport.send(cc(C.DATA_INC, value=0)) # MSB = 127
pb() # PBS == 2
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 126
pb() # PBS == 126 (it gets clamped to 24 in the tone generator)
Explanation: I've annotated the above with the results of the experiment and what I think's going on. Internally, INC/DEC seem to alter the value of an MSB variable, then set the MSB of the current RPN to what the value is now. It's the same variable, no matter which RPN is currently active; it doesn't get reset on GM_ON or XG ON or XG reset (which actually seems to work otherwise). The lesson: always set the MSB before using the INC/DEC, because weird things happen; it's definitely not the behaviour recommended in RPN-018.
Also, weird things happen if you go below zero, and above 127?? Might need even more testing!
End of explanation
outport.send(controls.gm_on()) # reset all
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=2))  # RPN -> Coarse Tune
outport.send(cc(C.DATA_MSB, value=62)) # MSB = 62
pb() # PBS == 2, down 2 semitones
outport.send(cc(C.RPN_MSB, value=1))
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 61
pb() # PBS == 2, down 2 semitones
outport.send(cc(C.RPN_MSB, value=0)) # RPN -> Coarse Tune again
pb() # PBS == 2, down 2 semitones
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 60
pb() # PBS == 2, down 4 semitones
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 59
pb() # PBS == 2, down 5 semitones
Explanation: Okay, so now it looks like if you go below 0 to "-1", you reach a state where it doesn't actually change the value anymore until you INC, and then it jumps to 1. So it's something like:
upon recieving DATA_DEC:
    if MSB &gt; 0:
        MSB -= 1
        set RPN data to MSB
upon receiving DATA_INC:
    if MSB &lt; 127:
        MSB += 1
        set RPN data to MSB
As a result if we change RPNs while MSB is at 0 already, then another DATA_DEC doesn't actually change the RPN data, which causes this odd behaviour; a DATA_INC increases the MSB to 1 and then sets the RPN data to 1, the second-lowest possible value. A weird edge case.
Does changing the RPN MSB also set the RPN LSB to zero?
End of explanation
outport.send(controls.gm_on()) # reset all
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=2))  # RPN -> Coarse Tune
outport.send(cc(C.DATA_MSB, value=62)) # MSB = 62
pb() # PBS == 2, down 2 semitones
outport.send(cc(C.DATA_LSB, value=32))
pb() # PBS == 2, down 2 semitones
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 60
pb() # PBS == 2, down 4 semitones
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 59
pb() # PBS == 2, down 5 semitones
Explanation: No, it does not.
Does setting the LSB change the decrement?
End of explanation
outport.send(controls.gm_on()) # reset all
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=2))  # RPN -> Coarse Tune
outport.send(cc(C.DATA_MSB, value=65)) # MSB = 62
pb() # PBS == 2, up 1 semitone
outport.send(cc(C.RPN_LSB, value=0))  # RPN -> PBS
pb() # PBS ==2, up 1 semitone
outport.send(cc(C.DATA_LSB, value=42)) # LSB = 42
pb() # PBS == 2, still
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 41
pb() # PBS == 41 (clamped to 24)
Explanation: No.
Does setting the LSB drag the MSB along with it?
End of explanation
outport.send(controls.gm_on()) # reset all
pb() 
outport.send(mido.Message.from_hex(
    "F0 43 10 4C 00 00 04 03 F7"))
pb() # XG volume
outport.send(mido.Message.from_hex(
"F0 7F 7F 04 01 00 48 F7"))
pb() # USE volume
outport.send(mido.Message.from_hex(
    "F0 43 11 4C 00 00 7F 00 F7"))
pb() # XG Reset
Explanation: No.
F0 43 1n 4C aa aa aa dd .. dd F7
Parameter settings.
End of explanation
inport = mido.open_input('DGX-505 MIDI 1')
list(inport.iter_pending())
outport.send(mido.Message.from_hex(
    "F0 43 40 4C 00 00 00 F7")) # request
outport.send(mido.Message.from_hex(
    "F0 43 20 4C 00 00 01 F7")) # dump request
list(inport.iter_pending())
Explanation: Seems like the only one that works is the XG Resets.
Does the DGX-505 respond to XG parameter requests?
End of explanation
inport.close()
outport.close()
Explanation: Nope.
End of explanation
outport = mido.open_output('DGX-505 MIDI 1')
pb() # MSB = 0, probably
outport.send(cc(C.DATA_INC, value=0)) # MSB = 1
pb() # PBS == 2
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=0)) # RPN -> PBS
outport.send(cc(C.DATA_INC, value=0)) # MSB = 2
pb() # PBS == 2
outport.send(cc(C.DATA_INC, value=0)) # MSB = 3
pb() # PBS == 3
Explanation: What about when we just power on?
End of explanation
outport.close()
# power on
outport = mido.open_output('DGX-505 MIDI 1')
pb()
outport.send(cc(C.RPN_MSB, value=0))
outport.send(cc(C.RPN_LSB, value=2)) # RPN -> Coarse Tuning
outport.send(cc(C.DATA_DEC, value=0)) # MSB = 0
pb() # Coarse Tuning = 64
outport.send(cc(C.DATA_INC, value=0)) # MSB 
Explanation: I guess MSB starts at 0.
End of explanation
o = mido.open_output('DGX-505 MIDI 1')
def pulse(port, sleep=0.5, note=60, velocity=100, channel=0):
    port.send(mido.Message('note_on', note=note, velocity=velocity, channel=channel))
    time.sleep(sleep)
    o.send(mido.Message('note_on', note=note, velocity=0, channel=channel))
    time.sleep(sleep)
pulse(o)
# Pitch Bend
o.send(controls.gm_on())
pulse(o)
o.send(mido.Message('pitchwheel', pitch=-8192))
pulse(o)
o.send(cc(C.RESET_CONTROLS, value=0))
pulse(o)
Explanation: Testing Continues
I suppose now we need to test some other stuff.
End of explanation
def pulsetest(port, m, channel=0, reset=True, voice=None, *args, **kwargs):
    if reset:
        port.send(controls.gm_on())
    if voice is not None:
        controls.multisend(port, controls.set_voice_numbers(voice, channel=channel))
    pulse(port, channel=channel, *args, **kwargs)
    port.send(m)
    pulse(port, channel=channel, *args, **kwargs)
    port.send(cc(C.RESET_CONTROLS, value=0, channel=channel))
    pulse(port, channel=channel, *args, **kwargs)
pulsetest(o, cc(C.RELEASE, value=0), voice=103)
Explanation: Pitch bend: check.
End of explanation
pulsetest(o, cc(C.ATTACK, value=80), voice=103)
pulsetest(o, cc(C.ATTACK, value=80), voice=120)
Explanation: Release time: check
End of explanation
pulsetest(o, cc(C.HARMONIC, value=127), voice=103)
Explanation: Attack time, check
End of explanation
pulsetest(o, cc(C.BRIGHTNESS, value=0), voice=1)
Explanation: Harmonic, check
End of explanation
def porta(port, sleep=0.5, note1=60, note2=64, velocity=100, channel=0, reset=True, voice=None):
    if reset:
        port.send(controls.gm_on())
    if voice is not None:
        controls.multisend(port, controls.set_voice_numbers(voice, channel=channel))
        
    port.send(mido.Message('note_on', note=note1, velocity=velocity, channel=channel))
    time.sleep(sleep)
    port.send(cc(C.PORTAMENTO_CTRL, value=note1))
    port.send(mido.Message('note_on', note=note2, velocity=1, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note2, velocity=0, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note1, velocity=0, channel=channel))
    time.sleep(sleep)
    
    port.send(mido.Message('note_on', note=note1, velocity=velocity, channel=channel))
    time.sleep(sleep)
    port.send(cc(C.PORTAMENTO_CTRL, value=note1))
    port.send(cc(C.RESET_CONTROLS, value=0, channel=channel))
    port.send(mido.Message('note_on', note=note2, velocity=1, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note2, velocity=0, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note1, velocity=0, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note1, velocity=velocity, channel=channel))
    time.sleep(sleep)
    port.send(cc(C.PORTAMENTO_CTRL, value=note1))
    port.send(mido.Message('note_on', note=note2, velocity=1, channel=channel))
    port.send(cc(C.RESET_CONTROLS, value=0, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note2, velocity=0, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_on', note=note1, velocity=0, channel=channel))
    time.sleep(sleep)
porta(o, voice=24)
def pbp(port, sleep=0.1, channel=0, note=64, velocity=100):
    port.send(mido.Message('pitchwheel', pitch=0, channel=channel))
    port.send(mido.Message('note_on', note=note, velocity=velocity, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('pitchwheel', pitch=+8191, channel=channel))
    time.sleep(sleep)
    port.send(mido.Message('note_off', note=note,channel=channel))
    time.sleep(sleep)
o.send(controls.gm_on())
controls.multisend(o, controls.set_voice_numbers(1))
o.send(cc(C.RPN_MSB, value=0))
o.send(cc(C.RPN_LSB, value=2))
pbp(o)
o.send(cc(C.DATA_MSB, value=65))
pbp(o)
o.send(cc(C.RESET_CONTROLS, value=0))
pbp(o)
pbp(o)
o.send(cc(C.RPN_MSB, value=0))
o.send(cc(C.DATA_INC, value=0))
pbp(o)
o.send(cc(C.RPN_LSB, value=2))
o.send(cc(C.DATA_INC, value=0))
pbp(o)
i = mido.open_input('DGX-505 MIDI 1')
list(i.iter_pending())
d1 = list(i.iter_pending())
cs = controlstate.MidiControlState()
def grab():
    return list(cs.feed_message(m) for m in i.iter_pending())
w1 = [cs.feed_message(m) for m in d1]
o.send(controls.master_vol(2))
w2 = grab()
all(a.message == b.message for a, b in zip(w1, w2))
grab()
w3 = grab()
[(a, b) for a, b in zip(w2, w3) if a.message != b.message]
o.send(mido.Message.from_hex("F0 43 10 4C 00 00 7E 00 F7")) # XG ON
w4 = grab()
[(a, b) for a, b in zip(w3, w4) if a.message != b.message]
grab()
w5 = grab()
[(a, b) for a, b in zip(w4, w5) if a.message != b.message]
o.send(mido.Message.from_hex("F0 43 10 4C 00 00 7F 00 F7")) # XG RESET
w6 = grab()
[(a, b) for a, b in zip(w5, w6) if a.message != b.message]
Explanation: Now for Portamento Control
End of explanation
o.send(controls.master_tune_val(100))
o.send(controls.gm_on())
o.send(mido.Message.from_hex("F0 43 10 4C 00 00 7E 00 F7")) # XG ON
o.send(mido.Message.from_hex("F0 43 10 4C 00 00 7F 00 F7")) # XG RESET
Explanation: Hmmm, it looks like XG reset also resets the MIDI Master Tuning, as well!
End of explanation
grab()
Explanation: Seems to be the only difference
End of explanation
i.close()
Explanation: What About The Bank
End of explanation
i = mido.open_input('DGX-505 MIDI 1')
grab()
progs = (0,1,8,16,24,25,27,32,40,48)
o.send(cc(C.BANK_MSB, value=127))
o.send(mido.Message('program_change', program=0))
for p in progs:
    o.send(mido.Message('program_change', program=p))
    
    o.send(mido.Message('note_on', note=40, velocity=33))
    
    time.sleep(0.2)
o.send(cc(C.BANK_LSB, value=127))
from commons.messages import voices
def voice_list(prog):
    v = []
    for l in range(128):
        try:
            v.append(voices.from_bank_program(0, l, prog))
        except KeyError:
            pass
    return v
[voice_list(x) for x in progs]
organvoices = voice_list(16)
print('\n'.join(x.voice_string_extended() for x in organvoices))
Explanation: Let's review:
3.1.2 Program Change
There are melody voices and there are rhythym voices (drum kits). 
The specification says:
If the tone generator does not have a drum kit corresponding to the specified program number, it will ignore the message and continue to use the current drum kit.
If Program change makes a change from normal voice to drum kit, the part mode shall revert to what it was before normal mode was invoked. Upon receipt of XG System ON, the system shall virtually set this "revert-to" mode to Drum Setup 2 for all Parts 1 to 16, except for Part 10.
3.1.6.1 Bank Select MSB/LSB
Bank MSB:
|MSB||
|-----------|---|
| 00      | Melody voice |
| 01–3F | Model-exclusive area |
| 40      | SFX voice |
| 41–77 | Reserved for XG extensions |
| 78      | GM L2 Rhythm Kit |
| 79      | GM L2 Melody voice |
| 7A–7D | Reserved for XG extensions |
| 7E      | SFX kit |
| 7F      | Rhythm kit |
Bank LSB is an extension area.
If support is included for one or more voices in an extension bank, then all the other program change numbers in that bank are filled with the corresponding voices of Bank 0 (basic voices)
Note 1: By default, channel 10 plays rhythm voices while other channels use bank 0 melody voices (as in GM Level 1)
Note 2: If the new Bank Select MSB is 00 (melody voice) but the tone generator does not support the melody voice corresponding to the last recieved Bank Select LSB, the channel reverts to the Bank Select LSB corresponding to its most recently played melody voice.
Note 3: If the new Bank Select MSB is 7F (rhythm voice) the tone generator unconditionally uses LSB 00 without using the most recently received Bank Select LSB. If the tone generator does not support a drum kit corresponding to the channel's most recently received Program Change, the channel will revert to the Program Change corresponding to its most recently played rhythm kit.
Note 4: If a Bank Select MSB value of 01–77 or 7A–7E (model-exclusive area, SFX voice, or XG extension voice) is received and the tone generator does not have a voice corresponding to the last received LSB and Program  change, the tone generator shall produce no sound for that channel regardless of subsequent Key On messages.
In the case where a melody voice is being changed first to a voice in bank-LSB A and then to a voice in bank-LSB B, and the change to A is possible but the change to B is not possible, A will be used as the substitute for B. If neither the change to A nor the change to B is possible, the voice of the previous bank will [be] substituted for A and B.
In the case where a rhythm voice is being changed first program number kit A and then to program number kit B, and the change to A is possible but the change to B is not possible, A will be used as the substitute for B. If neither the change to A nor the change to B is possible, the voice of the previous bank will [be] substituted for A and B.
We have the following supported kits:
|No.| Name | MSB | LSB | Prog | Note 40 |
|---|-------|-|-|-|-|
|122|Standard Kit 1|127|0|0| Snare H Hard|
|123|Standard Kit 2|127|0|1| Snare H Hard 2|
|124|Room Kit|127|0|8| SD Room H|
|125|Rock Kit|127|0|16| SD Rock Rim|
|126|Electronic Kit|127|0|24| SD Rock H|
|127|Analog Kit|127|0|25| Analog Snare 2|
|128|Dance Kit|127|0|27| AnSD Ana+Acoustic|
|129|Jazz Kit|127|0|32| SD Jazz M|
|130|Brush Kit|127|0|40| Brush Tap|
|131|Symphony Kit|127|0|48| Marching Sn H|
|132|SFX Kit 1|126|0|0| | 
|133|SFX Kit 2|126|0|1| Scratch|
Note 40 is different for each of them.
End of explanation
controls.multisend(o, controls.set_voice_numbers(176, channel=1))
controls.multisend(o, controls.set_voice_numbers(19, channel=2))
pulse(o, channel=1, note=40)
pulse(o, channel=2, note=40)
o.send(mido.Message('program_change', program=16, channel=0))
Explanation: There are a lot of different organ voices for program 16, MSB 0.
MSB 127 program 16 is the Rock Kit.
Let's set channel 1 to the DrawOrg, Channel 2 to the Jazz Organ 1.
End of explanation
pulse(o, channel=0, note=40)
Explanation: Let's set Channel 0 to the rock kit, program 16:
End of explanation
o.send(cc(C.BANK_MSB, value=127, channel=0))
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=112, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=127, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, channel=0, note=40)
Explanation: Now, Bank LSB should be totally ignored for drum kits.
End of explanation
olsbs = [v.lsb for v in organvoices]
olsbs
o.send(cc(C.BANK_LSB, value=114, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=115, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=0, channel=0))
for l in range(120):
    print(l, end=" ")
    o.send(cc(C.BANK_LSB, value=l, channel=0))
    o.send(mido.Message('program_change', program=16, channel=0))
    pulse(o, sleep=0.1, channel=0, note=40)
Explanation: The Bank LSB is treated as 0 (or at least not recognised for the drum kits), but it is still remembered so that if we change the MSB, the LSB value is used then. Setting the MSB does not reset the LSB to zero.
End of explanation
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=8, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=32, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=8, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
# change to violins
o.send(mido.Message('program_change', program=40, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=40, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=8, channel=0))
o.send(mido.Message('program_change', program=40, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
Explanation: It seems like when the LSB is not supported, it falls back to zero, but I'm willing to bet it still remembers it.
End of explanation
o.send(cc(C.BANK_LSB, value=114, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.RESET_CONTROLS, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
Explanation: Yep, it seems like it.
End of explanation
o.send(cc(C.BANK_MSB, value=0, channel=0))
o.send(cc(C.BANK_LSB, value=114, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=62, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=116, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(cc(C.BANK_LSB, value=114, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
Explanation: Yep, and the bank doesn't get reset either.
What about unsupported banks?
End of explanation
allvoices = [voices.from_number(x) for x in range(1, 495)]
zerovoices = sorted((v for v in allvoices if v.lsb == 0), key=lambda v: (v.msb, v.prog))
zerovoices
for v in allvoices:
    try:
        voices.from_bank_program(v.msb, 0, v.prog)
    except KeyError:
        print(v)
sorted((v for v in allvoices if v.msb != 0 and v.lsb != 0), key=lambda v: (v.msb, v.lsb, v.prog))
Explanation: They're silent.
End of explanation
o.send(cc(C.BANK_MSB, value=127, channel=0))
for p in range(128):
    print(p, end=" ")
    o.send(mido.Message('program_change', program=p, channel=0))
    pulse(o, sleep=0.1, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=126, channel=0))
o.send(cc(C.BANK_LSB, value=0, channel=0))
for p in range(128):
    print(p, end=" ")
    o.send(mido.Message('program_change', program=p, channel=0))
    pulse(o, sleep=0.05, channel=0, note=84)
progs
o.send(cc(C.BANK_MSB, value=127, channel=0))
o.send(mido.Message('program_change', program=0, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(mido.Message('program_change', program=3, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
o.send(mido.Message('program_change', program=3, channel=0))
pulse(o, sleep=0.2, channel=0, note=40)
Explanation: MSB 0 always has a fallback voice for any program.
What if the program is not supported?
End of explanation
o.send(cc(C.BANK_MSB, value=126, channel=0))
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=0, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=6, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=1, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=3, channel=0))
pulse(o, sleep=0.2, channel=0, note=84 )
Explanation: If the program is not supported for drum kit, it just won't change the program.
End of explanation
voice_list(40)
o.send(cc(C.BANK_MSB, value=64, channel=0))
for p in range(128):
    print(p, end=" ")
    o.send(mido.Message('program_change', program=p, channel=0))
    pulse(o, sleep=0.05, channel=0, note=40)
o.send(cc(C.BANK_MSB, value=64, channel=0))
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=70, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=71, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=64, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=71, channel=0))
pulse(o, sleep=0.2, channel=0, note=84 )
Explanation: If the program is not supported for SFX kit, it just won't play at all.
End of explanation
o.send(cc(C.BANK_MSB, value=64, channel=0))
for l in range(128):
    print(l, end=" ")
    o.send(cc(C.BANK_LSB, value=l, channel=0))
    o.send(mido.Message('program_change', program=70, channel=0))
    pulse(o, sleep=0.1, channel=0, note=40)
Explanation: And it seems like that for the SFX voices as well.
End of explanation
o.send(cc(C.BANK_MSB, value=126, channel=0))
for l in range(10):
    print(l, end=" ")
    o.send(cc(C.BANK_LSB, value=l, channel=0))
    o.send(mido.Message('program_change', program=0, channel=0))
    pulse(o, sleep=0.1, channel=0, note=84)
o.send(cc(C.BANK_MSB, value=126, channel=0))
o.send(cc(C.BANK_LSB, value=1, channel=0))
o.send(mido.Message('program_change', program=0, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=6, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=1, channel=0))
pulse(o, sleep=0.2, channel=0, note=84)
o.send(mido.Message('program_change', program=3, channel=0))
pulse(o, sleep=0.2, channel=0, note=84 )
Explanation: The LSB fallback works. for SFX voices.
End of explanation
o.send(controls.gm_on())
pulse(o, sleep=0.2, channel=0, note=60)
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
o.send(cc(C.BANK_LSB, value=113, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
# drum channel 
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message('program_change', program=16, channel=9))
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message('program_change', program=0, channel=9))
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message.from_hex("F0 43 10 4C 00 00 7E 00 F7")) # XG ON
pulse(o, sleep=0.2, channel=0, note=60)
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
o.send(cc(C.BANK_LSB, value=113, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
# drum channel 
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message('program_change', program=16, channel=9))
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message('program_change', program=0, channel=9))
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message.from_hex("F0 43 10 4C 00 00 7F 00 F7")) # XG RESET
pulse(o, sleep=0.2, channel=0, note=60)
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
o.send(cc(C.BANK_LSB, value=113, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
o.send(cc(C.BANK_LSB, value=0, channel=0))
o.send(mido.Message('program_change', program=16, channel=0))
pulse(o, sleep=0.2, channel=0, note=60)
# drum channel 
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message('program_change', program=16, channel=9))
pulse(o, sleep=0.2, channel=9, note=40)
o.send(mido.Message('program_change', program=0, channel=9))
pulse(o, sleep=0.2, channel=9, note=40)
Explanation: but not for SFX kits.
End of explanation
o.send(controls.gm_on())
controls.multisend(o, controls.set_voice_numbers(1))
pbp(o)
o.send(cc(C.DATA_MSB, value=59))
o.send(cc(C.RPN_MSB, value=0))
o.send(cc(C.RPN_LSB, value=2))
pbp(o)
o.send(cc(C.DATA_INC, value=0))
pbp(o)
controls.multisend(o, controls.set_voice_numbers(1, channel=2))
pbp(o, channel=2)
#o.send(cc(C.DATA_MSB, value=59, channel=1))
o.send(cc(C.RPN_MSB, value=0, channel=2))
o.send(cc(C.RPN_LSB, value=2, channel=2))
pbp(o, channel=2)
o.send(cc(C.DATA_INC, value=0, channel=2))
pbp(o, channel=2)
Explanation: Does the INC DEC weirdness leak across channels.
End of explanation
controls.xg_on()
ii = mido.open_input('DGX-505 MIDI 1')
def grabm(p):
    return list(p.iter_pending())
o = mido.open_output('DGX-505 MIDI 1')
for i in range(16):
    o.send(controls.master_tune_val(-100))    
    controls.multisend(o, controls.set_voice_numbers(60))
    pulse(o, 0.1)
    o.send(controls.xg_parameter_change(0, 0, 0x7E, 0, n=i))
    pulse(o, 0.1)
    o.send(controls.xg_parameter_change(0, 0, 0x7F, 0, n=i))
    pulse(o, 0.1)
grabm(ii)
o.send(controls.cc(controls.Control.LOCAL, value=0x70, channel=15))
o.send(controls.gm_on())
Explanation: No, each channel has its own weirdness variable completely separate.
Even More!
End of explanation
o.send(controls.master_vol(100))
pulse(o, 0.1)
o.send(controls.xg_parameter_change(0, 0, 0x7E, 0, n=i))
pulse(o, 0.1)
o.send(controls.master_vol(127))
pulse(o, 0.1)
o.send(controls.xg_parameter_change(0, 0, 0x7E, 0, n=i))
pulse(o, 0.1)
Explanation: LOCAL is not reset by XG on, GM on, or XG Reset
What's the default for Master Volume? I assume 7F
End of explanation
o.send(controls.xg_reset())
controls.multisend(o, controls.set_voice_numbers(60))
o.send(mido.Message('note_on', note=60))
time.sleep(0.5)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=60))
o.send(mido.Message('note_on', note=60))
#o.send(mido.Message('note_on', note=61))
time.sleep(0.5)
o.send(mido.Message('note_off', note=60))
o.send(mido.Message('note_off', note=60))
#o.send(mido.Message('note_off', note=61))
Explanation: How does portamento control work with changed voices or drum kits?
End of explanation
o.send(controls.xg_reset())
controls.multisend(o, controls.set_voice_numbers(60))
o.send(mido.Message('note_on', note=60))
time.sleep(0.5)
controls.multisend(o, controls.set_voice_numbers(101))
o.send(mido.Message('note_on', note=60))
time.sleep(0.5)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=60))
o.send(mido.Message('note_on', note=61))
time.sleep(0.5)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=60))
o.send(mido.Message('note_on', note=61))
time.sleep(0.5)
o.send(mido.Message('note_off', note=61))
time.sleep(0.5)
o.send(mido.Message('note_off', note=61))
o.send(controls.xg_reset())
controls.multisend(o, controls.set_voice_numbers(122))
o.send(mido.Message('note_on', note=29))
time.sleep(0.5)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=29))
o.send(mido.Message('note_on', note=28))
time.sleep(0.5)
o.send(mido.Message('note_off', note=28))
Explanation: Sending the same note seems to require turning the note off twice.
End of explanation
o.send(controls.xg_reset())
controls.multisend(o, controls.set_voice_numbers(133))
o.send(mido.Message('note_on', note=36))
time.sleep(0.5)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=36))
o.send(mido.Message('note_on', note=37))
time.sleep(0.5)
o.send(mido.Message('note_off', note=37))
Explanation: Portamento does not work with drum kits.
End of explanation
o.send(controls.xg_reset())
controls.multisend(o, controls.set_voice_numbers(474))
o.send(mido.Message('note_on', note=36))
time.sleep(0.1)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=36))
o.send(mido.Message('note_on', note=37))
time.sleep(0.5)
o.send(mido.Message('note_off', note=37))
Explanation: Or SFX kits.
End of explanation
o.send(controls.xg_reset())
controls.multisend(o, controls.set_voice_numbers(434))
o.send(mido.Message('note_on', note=60, velocity=127))
time.sleep(0.2)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=60))
o.send(mido.Message('note_on', note=61, velocity=127))
time.sleep(0.5)
o.send(mido.Message('note_off', note=61))
time.sleep(1)
o.send(cc(C.ATTACK, value=50))
o.send(cc(C.BRIGHTNESS, value=127))
o.send(cc(C.RELEASE, value=127))
o.send(mido.Message('note_on', note=60, velocity=127))
time.sleep(0.2)
o.send(controls.cc(controls.Control.PORTAMENTO_CTRL, value=60))
o.send(mido.Message('note_on', note=61, velocity=127))
time.sleep(0.5)
o.send(mido.Message('note_off', note=61))
voices.from_number(434)
Explanation: SFX voices seem fine, though.
End of explanation
<END_TASK>