In [1]:
import sys
sys.path.append('..')
In [2]:
import collections
In [3]:
import mido
In [4]:
from commons import dgxdump
from commons.dumpdata import messages, songdata, regdata, regvalues
In [6]:
old_syx_messages = mido.read_syx_file('../data/syxout5.syx')
clear_syx_messages = mido.read_syx_file('../data/clear_bulk.txt')
In [7]:
o_dump = dgxdump.DgxDump(old_syx_messages)
c_dump = dgxdump.DgxDump(clear_syx_messages)
In [5]:
# songs slices
songslices = collections.OrderedDict([
('songs', slice(0x00, 0x01)),
('mystery', slice(0x01, 0x15D)),
('tracks', slice(0x15D, 0x167)),
('durations', slice(0x167, 0x17B)),
('trackdurations', slice(0x17B, 0x1F3)),
('presetstyle', slice(0x1F3, 0x22F)),
('beginningblocks', slice(0x22F, 0x24D)),
('nextblocks', slice(0x24D, 0x2CF)),
('startmarker', slice(0x2CF, 0x2D5)),
('blockdata', slice(0x2D5, 0x106D5)),
('endmarker', slice(0x106D5, None)),
])
EXPECTED_SIZE = 0x106DB
PRESETSTYLE = b'PresetStyle\0'*5
MARKER = b'PK0001'
In [6]:
def hex_string(data):
return " ".join("{:02X}".format(b) for b in data)
def bin_string(data):
return " ".join("{:08b}".format(b) for b in data)
def line_hex(data, head=None, tail=0):
if head is None:
head = len(data)
tailstart = len(data) - tail
if tailstart <= head:
return (hex_string(data))
else:
return ("{} .. {}".format(hex_string(data[:head]), hex_string(data[tailstart:])))
def song_section(dump, section):
return dump.song_data.data[songslices[section]]
In [10]:
for sec in songslices:
print(sec)
print(line_hex(song_section(o_dump, sec), 32, 4))
print(line_hex(song_section(c_dump, sec), 32, 4))
In [11]:
song_section(o_dump, 'mystery') == song_section(c_dump, 'mystery')
Out[11]:
The mystery section remains the same.
In [12]:
all(b==0 for b in song_section(c_dump, 'nextblocks'))
Out[12]:
In [13]:
all(b==0 for b in song_section(c_dump, 'blockdata'))
Out[13]:
All the blocks are empty.
In [14]:
bytes(song_section(c_dump, 'presetstyle'))
Out[14]:
The 'PresetStyle' settings are empty, too.
In [15]:
print(line_hex(o_dump.reg_data.data, 32, 4))
print(line_hex(c_dump.reg_data.data, 32, 4))
In [16]:
for bank in range(1, 8+1):
for button in range(1, 2+1):
print(bank, button)
print(line_hex(o_dump.reg_data.settings.get_setting(bank, button).data))
print(line_hex(c_dump.reg_data.settings.get_setting(bank, button).data))
Each of the registry settings are completely blank. Interesting things to note: the first byte is 0 instead of 1, which probably indicates that the setting is unused. The bytes that were FF in each recorded setting are 00 here.
According to the manual (page 49), the following settings can be saved to backup, i.e. persistent memory for startup bu holding the FUNCTION button:
These backup settings are also cleared with the rest of the memory.
The default values for these settings are as follows:
setting | default |
---|---|
Touch response | ON |
Tuning | 000 |
Split point | 54 (F#2) |
Touch sensitivity | 2 (Medium) |
Style volume | 100 |
Song volume | 100 |
Metronome volume | 100 |
Grade | ON |
Demo cancel | OFF |
Language | English |
Media Select | Flash Memory |
Panel sustain | OFF |
As an experiment, I changed the values of the function settings:
setting | new value |
---|---|
Touch response | ON |
Tuning | 057 |
Split point | 112 (E7) |
Touch sensitivity | 3 (Hard) |
Style volume | 045 |
Song volume | 079 |
Metronome volume | 121 |
Grade | OFF |
Demo cancel | ON |
Language | Japanese |
Media Select | Smart Media |
Panel sustain | ON |
and without making a backup:
All of these files were identical to each other, which suggests that these backup settings are not stored any part we can retrieve.
However, there is one thing interesting about these files, in that they differ from the dump I got immediately after resetting the memory (clear_bulk.txt).
In [17]:
for x in range(2, 7):
!diff -qs ../data/backup_experiment/cb1.txt ../data/backup_experiment/cb{x}.txt
!diff -qs ../data/backup_experiment/cb1.txt ../data/clear_bulk.txt
In [18]:
c2_syx_messages = mido.read_syx_file('../data/backup_experiment/cb1.txt')
c2_dump = dgxdump.DgxDump(c2_syx_messages)
In [19]:
c_dump.song_data.data == c2_dump.song_data.data
Out[19]:
In [20]:
c_dump.reg_data.data == c2_dump.reg_data.data
Out[20]:
In [21]:
for sec in songslices:
c_sec = song_section(c_dump, sec)
c2_sec = song_section(c2_dump, sec)
if c_sec != c2_sec:
print(sec)
print(line_hex(c_sec, 32, 4))
print(line_hex(c2_sec, 32, 4))
In [22]:
for n, (a, b) in enumerate(zip(c_dump.song_data.data, c2_dump.song_data.data)):
if a != b:
print("{0:02X}: {1:02X} {2:02X} ({1:03d} {2:03d})".format(n, a, b))
The only difference seems to be two bytes in the mystery section, at offsets 0x07 and 0x08.
Perhaps this has to do with some kind of internal wear levelling or something.
Now that the memory has been cleared, we can hopefully figure out more about the registration settings.
Recording Bank 3, Button 2 as the following settings:
setting | value |
---|---|
Style | 092 |
Accompaniment | ON |
Split point | 053 |
Main A/B | A |
Style vol | 050 |
Main voice | 060 |
Main Octave | -1 |
Main Volume | 054 |
Main Pan | 092 |
Main Reverb | 078 |
Main Chorus | 103 |
Split | ON |
Split voice | 003 |
Split Octave | 0 |
Split Volume | 108 |
Split Pan | 064 |
Split Reverb | 032 |
Split Chorus | 127 |
Dual | OFF |
Dual voice | 201 |
Dual Octave | +2 |
Dual Volume | 095 |
Dual Pan | 048 |
Dual Reverb | 017 |
Dual Chorus | 082 |
Pitch bend range | 05 |
Reverb type | --(Room) |
Chorus type | --(Celeste) |
Harmony | OFF |
Harmony type | 06(Trill1/4) |
Harmony volume | 085/---* |
Transpose | +03 |
Tempo | 080 |
Panel Sustain | ON |
*This was set using a different Harmony type setting.
In [23]:
r1_dump = dgxdump.DgxDump(mido.read_syx_file('../data/post_clear/1reg.syx'))
In [25]:
c2_dump.song_data.data == r1_dump.song_data.data
Out[25]:
In [26]:
c2_dump.reg_data.data == r1_dump.reg_data.data
Out[26]:
In [35]:
for bank in range(1, 8+1):
for button in range(1, 2+1):
if not all(x == 0 for x in r1_dump.reg_data.settings.get_setting(bank, button).data):
print(bank, button)
In [38]:
line_hex(r1_dump.reg_data.settings.get_setting(3, 2).data)
Out[38]:
In [47]:
for bb in [(3, 2), (1, 1)]:
sets = r1_dump.reg_data.settings.get_setting(*bb)
print(line_hex(sets.data))
sets.print_settings()
sets.print_unusual()
I believe the only real way to get unrecorded settings is to reset the memory, which clears all the values to zero.
This means that the first byte which has a value of 01
for all recorded settings can indeed be used as a flag... along with the FF
byte at offset 24
, and any other setting that cannot be set to a value of zero, such as the Pitch Bend range, Reverb type, Chorus type, and panel Sustain.
Personally, I think it makes more sense for the first byte to act as the recorded flag, so I think I'll use that.
In [34]:
r2_dump = dgxdump.DgxDump(mido.read_syx_file('../data/post_clear/2reg.txt'))
sets = r2_dump.reg_data.settings.get_setting(2,2)
sets.print_settings()
sets.print_unusual()
The voice number 000 is used for the default voice for the whichever song or style is selected. If saved to a registration setting, the number 000 is not actually recorded, but rather the actual voice settings are saved.
According to the manual, page 45, the following data is recorded in melody tracks:
And on the style track (A):
Note that the split voice and notes are not recorded at all (p.46). I suspect this may be because with five tracks each with main and dual, plus accompaniment, plus the actual keyboard voices, there aren't enough MIDI channels to accomodate them.
In [ ]: