shooking / ZoomPedalFun

A collection of tips and tricks for Zoom B1On, B1XFour and G1XFour pedals.
Creative Commons Zero v1.0 Universal
60 stars 2 forks source link

Trying to decode G1Xon patch #39

Open dkts2000 opened 4 months ago

dkts2000 commented 4 months ago

First of all thank you for your work, your code and your info. I am trying to decode a patch from G1Xon pedal (1.21 firmware) using the code from file B1OnPatchUnpack000.cpp I use the sysex F0 52 00 64 29 F7 to retrieve the patch

the output is: ` INPUT F0 52 00 64 28 00 11 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 00 10 48 0B 10 01 64 00 45 22 26 48 08 02 00 00 06 00 00 21 00 00 12 03 40 08 00 2E 20 06 2C 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 00 64 01 00 00 00 20 03 00 00 00 00 42 6C 75 65 00 20 4C 65 61 64 20 00 F7 Blue Lead

[17][0][0][2] FXID[1] (ON) = 17 (11), GROUPID = 1 (01) FX id= 11 00 P1 = 0 (00) P2 = 0 (00) P3 = 0 (00) P4 = 0 (00) P5 = 0 (00) P6 = 0 (00) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) [33][0][0][72] FXID[2] (ON) = 33 (21), GROUPID = 4 (04) FX id= 21 00 P1 = 45 (2d) P2 = 50 (32) P3 = 100 (64) P4 = 40 (28) P5 = 50 (32) P6 = 65 (41) P7 = 70 (46) P8 = 16 (10) P9 = 3 (03) [33][0][0][18] FXID[3] (ON) = 33 (21), GROUPID = 1 (01) FX id= 21 00 P1 = 12 (0c) P2 = 8 (08) P3 = 46 (2e) P4 = 49 (31) P5 = 100 (64) P6 = 1 (01) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) [1][0][0][0] FXID[4] (ON) = 1 (01), GROUPID = 0 (00) FX id= 01 00 P1 = 0 (00) P2 = 0 (00) P3 = 0 (00) P4 = 0 (00) P5 = 0 (00) P6 = 0 (00) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) [1][0][0][0] FXID[5] (ON) = 1 (01), GROUPID = 0 (00) FX id= 01 00 P1 = 0 (00) P2 = 0 (00) P3 = 0 (00) P4 = 0 (00) P5 = 0 (00) P6 = 0 (00) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) NumFX = 3 (03) Volume = 100 (64)

OUTPUT

11 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 00 48 0B 90 01 64 00 45 26 C8 08 02 00 80 06 00 21 00 00 12 03 40 00 2E 20 86 2C 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 64 01 00 00 00 20 03 00 00 00 42 6C 75 65 20 4C 65 61 64 20 00

INPUT F0 52 00 64 28 08 21 00 00 04 04 78 00 14 0C 40 41 61 02 0C 00 04 00 00 00 00 01 02 00 00 46 10 58 02 64 00 00 00 00 00 00 00 00 00 00 00 00 11 00 00 12 14 68 08 01 3E 00 65 26 00 00 02 00 00 00 00 00 41 00 20 00 02 02 20 03 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 42 4B 22 0A 00 00 70 01 00 00 00 00 41 63 6F 75 00 73 74 69 78 20 20 00 F7 Acoustix

[33][0][0][132] FXID[1] (ON) = 33 (21), GROUPID = 2 (02) FX id= 21 00 P1 = 18 (12) P2 = 15 (0f) P3 = 12 (0c) P4 = 10 (0a) P5 = 14 (0e) P6 = 19 (13) P7 = 100 (64) P8 = 0 (00) P9 = 0 (00) [129][2][0][70] FXID[2] (ON) = 129 (81), GROUPID = 3 (03) FX id= 81 200 P1 = 65 (41) P2 = 75 (4b) P3 = 100 (64) P4 = 0 (00) P5 = 0 (00) P6 = 0 (00) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) [17][0][0][18] FXID[3] (ON) = 17 (11), GROUPID = 1 (01) FX id= 11 00 P1 = 80 (50) P2 = 45 (2d) P3 = 62 (3e) P4 = 40 (28) P5 = 55 (37) P6 = 1 (01) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) [193][0][0][130] FXID[4] (ON) = 193 (c1), GROUPID = 1 (01) FX id= c1 00 P1 = 10 (0a) P2 = 100 (64) P3 = 0 (00) P4 = 0 (00) P5 = 0 (00) P6 = 0 (00) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) [1][0][0][0] FXID[5] (ON) = 1 (01), GROUPID = 0 (00) FX id= 01 00 P1 = 0 (00) P2 = 0 (00) P3 = 0 (00) P4 = 0 (00) P5 = 0 (00) P6 = 0 (00) P7 = 0 (00) P8 = 0 (00) P9 = 0 (00) NumFX = 4 (04) Volume = 203 (cb)

OUTPUT

21 00 00 84 04 78 00 0C 40 C1 61 82 0C 00 00 00 00 00 81 02 00 46 10 58 02 64 00 00 00 00 00 00 00 00 00 00 11 00 00 12 14 68 01 3E 00 E5 26 00 00 00 00 00 00 00 C1 00 00 82 02 20 03 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 CB 22 0A 00 00 F0 01 00 00 00 41 63 6F 75 73 74 69 78 20 20 00 `

I highlighted in the first output (patch Blue Lead) the FXID1 (Comp) with first four bytes 11 00 00 02 which gives id=11 and group=1 (hex) and in the second output (patch Acoustix) the FXID3 (HD Hall) with first four bytes 11 00 00 12 which also gives id=11 and group=1 (hex).

the code snippet which calculates the id and the group is: ` uint8_t FXID = (unpacked[ 18 i + 1] & 15) 256 + unpacked[18 * i]; uint8_t FXGroup;

    printf("\n");
    if ((unpacked[18 * i + 2] & 0XC0) == 0)
    {
        FXGroup = (unpacked[18*i + 3] & 0x0F) / 2;
    } else {
        FXGroup = (
                  ( (unpacked[18*i + 3] & 0x0F) << 8 ) + 
                  ( (unpacked[18*i + 2] & 0xC0)      )
                                                     ) >> 5;
    }

`

Do you have any idea of why this is happening?

shooking commented 4 months ago

hi there The simplest idea I have is I f**ked up :-)

What would help (Mungwell has this idea) - you know of the Tonelib utilities for Zoom pedals? If you are talking about factory patches then they have also decoded them. So if the patches above are stock - check out their decode and send me a link so I can debug my program.

Probably there is a higher bit I need to take into account so as to differentiate between the GID/FXID (group and fx id).

I will try to take a look this week.

Best regards Steve

EDIT - I notice the volume looks "off" too.

shooking commented 4 months ago

ok so I was formatting an SD card so I had some time to check

https://tonelib.net/forums/threads/blues-sound.4986/


Effect: "NoiseGate" (Dynamics / Filter), active - "yes"
"THRSH" = 5​
"Level" = 150​

Effect: "Comp" (Dynamics / Filter), active - "yes"
"Sense" = 4​
"Tone" = 10​
"Level" = 150​
"ATTCK" = Fast​

Effect: "DELUXE-R" (Amp simulators), active - "yes"
"Gain" = 29​
"Tube" = 100​
"Level" = 150​
"Trebl" = 25​
"Middl" = 25​
"Bass" = 75​
"Prese" = 48​
"CAB" = US BLUES 4x10​
"OUT" = COMBO POWER AMP​

Effect: "Hall" (Delay / Reverb), active - "yes"
"Decay" = 19​
"Tone" = 10​
"Mix" = 24​
"PreD" = 34​
"Level" = 150​
"Tail" = On​

Patch Volume: 120
shooking commented 4 months ago

https://tonelib.net/forums/threads/acoustic.13254/#post-18011

Effect: "GraphicEQ" (Dynamics / Filter), active - "yes" "160Hz" = 6​ "400Hz" = 3​ "800Hz" = 0​ "3.2kHz" = -2​ "6.4kHz" = 2​ "12kHz" = 7​ "Level" = 100​

Effect: "Aco.Sim" (Overdrive / Distortion), active - "yes" "Top" = 65​ "Body" = 75​ "Level" = 100​

Effect: "HD Hall" (Delay / Reverb), active - "yes" "PreD" = 81​ "Decay" = 45​ "Mix" = 62​ "LoDMP" = 40​ "HiDMP" = 55​ "Tail" = On​

Effect: "NoiseGate" (Dynamics / Filter), active - "yes" "THRSH" = 11​ "Level" = 100​

Patch Volume: 75

shooking commented 4 months ago

Ok regarding the volume. I am highlighting 0xCB = 11001011

ToneLib says 45 = 0010 1101

So it looks like I have some wrong bit mask. Any way you can sent me the binary sysex outputs please so I can run them?

dkts2000 commented 4 months ago

Sorry what do you mean. The bin files created using the amidi -p hw:3,0,0 -S "f0 52 00 64 29 f7" -r temp.bin -t 1 ; hexdump -C temp.bin command? if yes, I attach the 2 files: BlueLead&Acoustic.zip

Volume is 75 on Acoustic patch and 100 on Blue Lead on pedal (stock patches)

EDIT: I ran the code for all patches and i noticed that it never goes to the second part of the if statement:

if ((unpacked[18 i + 2] & 0XC0) == 0) { FXGroup = (unpacked[18i + 3] & 0x0F) / 2; } else { FXGroup = ( ( (unpacked[18i + 3] & 0x0F) << 8 ) + ( (unpacked[18i + 2] & 0xC0) ) ) >> 5; }

This does not mean its wrong, i am just pointing it out, if it might help

nomadbyte commented 4 months ago

I am trying to decode a patch from G1Xon pedal (1.21 firmware)...

Here it is:

G1Xon:"Blue Lead":3:(Level:100):

  1. DYN:"0x01000008.Comp":On:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  2. AMP:"0x04000010.DELUXE-R":On:(45, 50, 100, 40, 50, 65, 70, 0x04000010, 3, 0)
  3. REV:"0x09000010.Hall":On:(12, 8, 46, 49, 100, 1, 0, 0, 0, 0)

NOTE: Parameter values above are 0-based raw values (not mapped onto defined ranges), the names are as listed in the G1Xon effects list pdf. Compressor apparently is enabled but all parameters are set to min values. AMP is using the default CAB.

If you have the actual pedal, you can either send the patch SysEx via MIDI or use Edit&Share or ToneLib.

dkts2000 commented 4 months ago

@nomadbyte i don't understand. What is the "0x01000008.Comp" hex? How did you get those?'

i have the pedal, i get the sysex message after i send the "get current patch" 0x29 command, i unpack it and "read" it using shooking's code but i get the same GID for two different effects. In this case, in "Blue Lead" patch the Comp effect gets analyzed by shooking's code as FXID[1] (ON) = 17 (11), GROUPID = 1 (01) and in "Acoustix" the HD Hall effect also gets analyzed as FXID[3] (ON) = 17 (11), GROUPID = 1 (01) But the hex in the unpacked sysex is [11][0][0][2] for the Comp and [11][0][0][12] for the HD Hall

nomadbyte commented 4 months ago

...What is the "0x01000008.Comp"

0x01000008 is the hex effect-id (high 1 byte is CategoryId:0x01, DYN), it's encoded in the patch (well, the lower 29bits of the full 32bit effect-id). "Comp" is effect name (from the corresponding COMP.ZDL file; it also contains the full effect-id too @0x40 offset), I just appended it to the id for clarity.

I'm not sure what your objective is but you equally can send the patch SysEx:0x28 back into the pedal and you'll see the settings.

In the SysEx:0x28 the patch data is packed from 38bit-bytes into (1+3)7bit-bytes, first is the key-byte which contains the 8th bits of the following 3 bytes.

dkts2000 commented 4 months ago

I'm not sure what your objective is

i want to make a controller (MIDI over USB using a Pi pico) that will send commands to the G1Xon (change patch, on/off effects, etc) and receive sysex messages from the pedal, "decrypt" them and display info (patch name, number of effects etc) on a screen. I am using C++. I receive the sysex "current patch" from 0x29 command and i want to "decrypt" which effects are included. and their parameters. The problem is that,i get the same GID for 2 different effects when i use shooking's code.

0x01000008 is the hex effect-id (high 1 byte is CategoryId:0x01, DYN), it's encoded in the patch (well, the lower 29bits of the full 32bit effect-id). "Comp" is effect name (from the corresponding COMP.ZDL file; it also contains the full effect-id too @0x40 offset), I just appended it to the id for clarity.

I totally lost you here. Do you read the binary ZDL effect files? How do you read a specific patch? Is there something I could read to understand what you say?

nomadbyte commented 4 months ago

... Is there something I could read to understand what you say?

Read @shooking code (C++) or @mungewell zoom-zt2 code (Python) to get an idea of the 7-to-8bit unpacking. It is one of common MIDI/SysEx practices to deal with binary stream contents. I also described this to you in the last paragraph above. First you need to unpack the patch file data from the SysEx:0x28 message.

If you're generally familiar with the G1Xon patch file structure, all the layout becomes more clear.

The G1Xon patch file is a set of 5 effect-data parts (each 18 bytes long) and patch-data part (11+10+1 bytes), which includes the patch name. Effect-data part does not include effect name, only the effect-id; it also includes raw parameter values.

The raw values are numeric, usually ranging from 0 and to parameter-specific limit. In general these raw values are not the same as what you see on the pedal's screen (even though they do match often). The actual value should be mapped onto the values defined for given effect's parameter. Patch file does not have this mapping information; these details are kept in effect's file (ZDL file), the ZDL file also lists the effect's name.

Effect-id is used to link the effect reference in the patch to the effect's ZDL file. The effect-id is a 4 byte integer, which you can locate in a ZDL file at offset 0x40. However, in the G1Xon patch file, the effect-id is allocated only 29bits; this is mostly dues to historical evolution of the ZOOM multieffect patch format. Anyway, effect-id by max value does fit into 29bits.

Of course, it's possible to create your own effect details reference, and list all the effect/parameter information. This is what's @mungewell and @shooking have done, also zoom-ms-utilty took this path.

...The problem is that,i get the same GID for 2 different effects when i use shooking's code.

I'm not sure what the GID means in your case. You need to understand the patch layout, so that you can make your own conclusions about the data. Again, as I mentioned earlier, the effect-id is a 4-byte integer (e.g. 0x01000008) tied to a given ZDL file; the effect-id is unique within model's realm. High 1 byte of effect-id is used for grouping purposes ( categoryId = effectId >> 24).

dkts2000 commented 4 months ago

Thank you very much for the info. The GID is the group id as @shooking describes in his code. In the upacked sysex the first 4 bytes are the fx id and group id but in reverse order as he says. (byte 1 byte 0) = fx id and (byte3 byte2) = group id

Can you please clarify something that confuses me? we have the unpacked sysex ` 11 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // Comp

21 00 00 48 0B 90 01 64 00 45 26 C8 08 02 00 80 06 00 // Deluxe-R

21 00 00 12 03 40 00 2E 20 86 2C 00 00 00 00 00 00 00 // Hall

01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // Bypass

01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // Bypass `

and in the corresponding ZDL files we find at offset 0x40 the effect id 08 00 00 01, 10 00 00 04 and 10 00 00 09 How do we convert the 11 00 00 02 (found in unpacked sysex) to 0x01000008 and why it is "inverted" from 08 00 00 01 found in COMP.ZDL file? (EDIT: well not inverted, it has to do with bit shifting. Right?)

nomadbyte commented 4 months ago

...How do we convert the 11 00 00 02 (found in unpacked sysex) to 0x01000008

There are many ways to analyze binary data, you're probably applying your own. I mentioned earlier that patch file contains only 29 lower bits of effect-id.

print("{:032b}".format(0x01000008))  # effectId from COMP.ZDL
00000001000000000000000000001000

print("{:032b}".format(0x02000011))  # G1Xon patch effect data, first 4 bytes
00000010000000000000000000010001
shooking commented 3 months ago

ok @dkts2000 Firstly thanks for helping me test this stuff. The volume needs to be & 0x75. Just pushed that change.

I will look into the FX business. Did you see in the wiki the Python G1ON program?

https://github.com/SysExTones/g1on

These older pedals - to my ears they "sounds" a bit better on similar presets and they have a much better default set than G1x (IMHO). But the midi was very incomplete - hence why I went down same route to decode them.

The ToneLib almost supports them - notionally for free - but when modified then they dont display the new FX.

I will dust off my B1On and experiment with the ZoomEffectsManager to see if I can replicate more of the patches. I might also add a lookup for the FX Names.

shooking commented 3 months ago

Thank you very much for the info. The GID is the group id as @shooking describes in his code. In the upacked sysex the first 4 bytes are the fx id and group id but in reverse order as he says. (byte 1 byte 0) = fx id and (byte3 byte2) = group id

Can you please clarify something that confuses me? we have the unpacked sysex ` 11 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // Comp

21 00 00 48 0B 90 01 64 00 45 26 C8 08 02 00 80 06 00 // Deluxe-R

21 00 00 12 03 40 00 2E 20 86 2C 00 00 00 00 00 00 00 // Hall

01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // Bypass

01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // Bypass `

and in the corresponding ZDL files we find at offset 0x40 the effect id 08 00 00 01, 10 00 00 04 and 10 00 00 09 How do we convert the 11 00 00 02 (found in unpacked sysex) to 0x01000008 and why it is "inverted" from 08 00 00 01 found in COMP.ZDL file? (EDIT: well not inverted, it has to do with bit shifting. Right?)

So the inversion is due to the processor - which I think is a TMS variant. Modern processors use different "endian". For example, we look at 123 as 1 x 100 + 2 x 10 + 3.

But we could look at it as 1 + 2 x 10 + 3 x 100 ... the byte swapping is a little more complex but you get the idea. It is convention - the Zoom packs differently to how we need to interpret the number.

Some people talk about these magic "every 8th byte" and it can turn into a slanging match. I made a YT about it to explain to someone else - but in a nutshell the Midi standard created by Dave Smith (RIP) and others has an 8 bit opening command then a series of 7 bits and finally the message stops when it finds most 8 bit values (0xFE and 0xFF heartbeats are an exception and generally need to be filtered out).

So how can you send values larger than 0x7F (recall the fix I just made for the volume!). Well starting in "unpacked" space [[ie the real values you want to store not the midi encoding]] you examine 7 values and take off any top bits. You store them as 1 or 0 in position of the byte (sometimes it is reverse order - which I think is how Zoom works). And you use an extra byte to store the 7 1 or 0's - then pack the top-bit truncated midi bytes. Send that down the wire - there are no pesky top bits to stop the message - data gets thru - now you need to reconstitute the "real" message with these unpack algorithms.

At one point I will open up my Miniak decoders on a github. There I called them Raw and Cooked - well everyone needs some Fine Young Cannibals in their musical life.

If you are really interested it's here - https://www.youtube.com/watch?v=SLLPHac2BCo

Hope this helps.

shooking commented 3 months ago

hi @dkts2000

So I pushed a new version - I had to teach myself C++ maps.

I can see these G1On FX are not currently known to me. I will see if I can derive their values from the firmware.

Example

Acoustix

FXID[1] (ON) =  33 (21), GROUPID = 2 (02)

FX = UKNOWN FX!!
        P1 =   18 (12)  P2 =   15 (0f)  P3 =   12 (0c)  P4 =   10 (0a)  P5 =   14 (0e)  P6 =   19 (13)  P7 =  100 (64)  P8 =    0 (00)  P9 =    0 (00)

FXID[2] (ON) = 641 (281), GROUPID = 3 (03)

FX = UKNOWN FX!!
        P1 =   65 (41)  P2 =   75 (4b)  P3 =  100 (64)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[3] (ON) =  17 (11), GROUPID = 1 (01)

FX = HD Hall
        P1 =   80 (50)  P2 =   45 (2d)  P3 =   62 (3e)  P4 =   40 (28)  P5 =   55 (37)  P6 =    1 (01)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[4] (ON) = 193 (c1), GROUPID = 1 (01)

FX = UKNOWN FX!!
        P1 =   10 (0a)  P2 =  100 (64)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[5] (ON) =   1 (01), GROUPID = 0 (00)

FX = BYPASS
        P1 =    0 (00)  P2 =    0 (00)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)
NumFX =  4 (04)

Volume = 75 (4b)

whereas if I take one of the factory patches from the B1On (see my DerivedData\1.30\FactoryPatches for a list)

UK Groove

FXID[1] (ON) =  33 (21), GROUPID = 22 (16)

FX = BASS DRIVE
        P1 =    0 (00)  P2 =    0 (00)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[2] (ON) =  33 (21), GROUPID = 5 (05)

FX = SVT
        P1 =    0 (00)  P2 =    0 (00)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[3] (ON) =   1 (01), GROUPID = 0 (00)

FX = BYPASS
        P1 =    0 (00)  P2 =    0 (00)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[4] (ON) =   1 (01), GROUPID = 0 (00)

FX = BYPASS
        P1 =    0 (00)  P2 =    0 (00)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)

FXID[5] (ON) =   1 (01), GROUPID = 0 (00)

FX = BYPASS
        P1 =    0 (00)  P2 =    0 (00)  P3 =    0 (00)  P4 =    0 (00)  P5 =    0 (00)  P6 =    0 (00)  P7 =    0 (00)  P8 =    0 (00)  P9 =    0 (00)
NumFX =  2 (02)

Volume = 50 (32)

The FX agree with ToneLib - the values do NOT - because I can see that I set all the parameters to 0 - this was allow me to "see" the FXIDs.

dkts2000 commented 3 months ago

Folowing the previous analysis by @nomadbyte and @shooking code i managed to extract effect names from sysex with the following code.

This is a list of the G1Xon effects as derived from ZDL files (1.21 firmware) 4 bytes starting at offset 0x40 (which gives the id)

n EFFECT byte 40 byte 41 byte 42 byte 43 TYPE DISPLAY NAME
0 COMP 8 0 0 1 DYNAMICS / FILTER Comp
1 ZNR 50 0 0 1 DYNAMICS / FILTER ZNR
2 160_COMP 35 0 0 1 DYNAMICS / FILTER 160 Comp
3 NOISEGTE 60 0 0 1 DYNAMICS / FILTER NoiseGate
4 SLWATK 40 0 0 1 DYNAMICS / FILTER SlowATTCK
5 OPTCOMP 30 0 0 1 DYNAMICS / FILTER OptComp
6 GEQ 10 0 0 2 DYNAMICS / FILTER GraphicEQ
7 STEP a0 0 0 2 DYNAMICS / FILTER Step
8 FCYCLE d0 0 0 2 DYNAMICS / FILTER fCycle
9 PEQ 20 0 0 2 DYNAMICS / FILTER ParaEQ
10 M_FILTER 90 0 0 2 DYNAMICS / FILTER M-Filter
11 RNDMFLTR c0 0 0 2 DYNAMICS / FILTER RndmFLTR
12 CRY 70 0 0 2 DYNAMICS / FILTER Cry
13 SEQFLTR b0 0 0 2 DYNAMICS / FILTER SeqFLTR
14 AUTOWAH 50 0 0 2 DYNAMICS / FILTER AutoWah
15 EXCITER 30 0 0 2 DYNAMICS / FILTER Exciter
16 Z_WILD 10 1 0 3 OVERDRIVE / DISTORTION Z Wild
17 DISTPLUS 40 0 0 3 OVERDRIVE / DISTORTION Dist+
18 LEAD 20 1 0 3 OVERDRIVE / DISTORTION Lead
19 EXTRMDS 30 1 0 3 OVERDRIVE / DISTORTION ExtremeDS
20 GRTMUFF 80 0 0 3 OVERDRIVE / DISTORTION GreatMuff
21 Z_CLEAN b0 0 0 3 OVERDRIVE / DISTORTION Z Clean
22 MTLWRLD 90 0 0 3 OVERDRIVE / DISTORTION MetalWRLD
23 FUZZSML 70 0 0 3 OVERDRIVE / DISTORTION FuzzSmile
24 Z_MP1 c0 0 0 3 OVERDRIVE / DISTORTION Z MP1
25 T_SCREAM 20 0 0 3 OVERDRIVE / DISTORTION T Scream
26 GOVERNOR 30 0 0 3 OVERDRIVE / DISTORTION Governor
27 OVRDRV 10 0 0 3 OVERDRIVE / DISTORTION OverDrive
28 Z_SCREAM f0 0 0 3 OVERDRIVE / DISTORTION Z Scream
29 ACOSIM 40 1 0 3 OVERDRIVE / DISTORTION Aco.Sim
30 SQUEAK 60 0 0 3 OVERDRIVE / DISTORTION Squeak
31 BOOSTER 8 0 0 3 OVERDRIVE / DISTORTION Booster
32 DISTONE 50 0 0 3 OVERDRIVE / DISTORTION Dist 1
33 HOTBOX a0 0 0 3 OVERDRIVE / DISTORTION HotBox
34 BG_CRNCH 60 0 0 4 AMP SIMULATOR BG CRUNCH
35 ALIEN 40 1 0 4 AMP SIMULATOR ALIEN
36 TONECITY a0 0 0 4 AMP SIMULATOR TONECITY
37 REVO_1 50 1 0 4 AMP SIMULATOR REVO-1
38 VX_COMBO 40 0 0 4 AMP SIMULATOR VX COMBO
39 MS_1959 f0 0 0 4 AMP SIMULATOR MS 1959
40 DZ_DRIVE 30 1 0 4 AMP SIMULATOR DZ DRIVE
41 FDCOMBO 8 0 0 4 AMP SIMULATOR FD COMBO
42 TW_ROCK 90 0 0 4 AMP SIMULATOR TW ROCK
43 DELUXE_R 10 0 0 4 AMP SIMULATOR DELUXE-R
44 MSCRUNCH e0 0 0 4 AMP SIMULATOR MS CRUNCH
45 BGN_DRV 10 1 0 4 AMP SIMULATOR BGN DRIVE
46 HW_STACK b0 0 0 4 AMP SIMULATOR HW STACK
47 CAR_DRV 80 0 0 4 AMP SIMULATOR CAR DRIVE
48 MATCH_30 70 0 0 4 AMP SIMULATOR MATCH 30
49 FD_VIBRO 20 0 0 4 AMP SIMULATOR FD VIBRO
50 TANGERIN c0 0 0 4 AMP SIMULATOR TANGERINE
51 VX_JMI 50 0 0 4 AMP SIMULATOR VX JIMI
52 BG_DRV 20 1 0 4 AMP SIMULATOR BG DRIVE
53 US_BLUES 30 0 0 4 AMP SIMULATOR US BLUES
54 B_BRKR d0 0 0 4 AMP SIMULATOR B-BREAKER
55 MS_DRV 0 1 0 4 AMP SIMULATOR MS DRIVE
56 SUPERCHO d0 0 0 6 MODULATION / SFX SuperCho
57 PTCHSHFT 20 1 0 6 MODULATION / SFX PitchSHFT
58 ENSEMBLE b0 0 0 6 MODULATION / SFX Ensemble
59 VINFLNGR d8 0 0 6 MODULATION / SFX VinFLNGR
60 BENDCHO 50 1 0 6 MODULATION / SFX BendCho
61 TREMOLO 8 0 0 6 MODULATION / SFX Tremolo
62 SLICER 20 0 0 6 MODULATION / SFX Slicer
63 MNPIT 30 1 0 6 MODULATION / SFX PDL MnPit
64 HPS 40 1 0 6 MODULATION / SFX HPS
65 DUOPHASE 35 0 0 6 MODULATION / SFX Duo-Phase
66 THEVIBE 40 0 0 6 MODULATION / SFX TheVibe
67 PHASER 30 0 0 6 MODULATION / SFX Phaser
68 CHORUS 70 0 0 6 MODULATION / SFX Chorus
69 DETUNE 80 0 0 6 MODULATION / SFX Detune
70 CRN_TRI 0 2 0 6 MODULATION / SFX CoronaTri
71 VIBRATO 0 1 0 6 MODULATION / SFX Vibrato
72 RINGMOD 70 1 0 6 MODULATION / SFX RingMod
73 STCHO a0 0 0 6 MODULATION / SFX StereoCho
74 OCTAVE 10 1 0 6 MODULATION / SFX Octave
75 FLANGER e0 0 0 6 MODULATION / SFX Flanger
76 RTCLOSET 60 0 0 7 MODULATION / SFX Rt Closet
77 BOMBER 20 0 0 7 MODULATION / SFX Bomber
78 BITCRUSH 10 0 0 7 MODULATION / SFX BitCrush
79 Z_ORGAN 40 0 0 7 MODULATION / SFX Z-Organ
80 DELAY 8 0 0 8 DELAY / REVERB Delay
81 STDELAY 90 0 0 8 DELAY / REVERB StereoDly
82 MLTTPDLY 50 0 0 8 DELAY / REVERB MultiTapD
83 STOMPDLY c0 0 0 8 DELAY / REVERB StompDly
84 CRBNDLY f0 0 0 8 DELAY / REVERB CarbonDly
85 PITCHDLY 80 0 0 8 DELAY / REVERB PitchDly
86 TAPEECHO 10 0 0 8 DELAY / REVERB TapeEcho
87 FLTRDLY 70 0 0 8 DELAY / REVERB FilterDly
88 RVRSDLY 40 0 0 8 DELAY / REVERB ReverseDL
89 PLATE 90 0 0 9 DELAY / REVERB Plate
90 HALL 10 0 0 9 DELAY / REVERB Hall
91 SPRING63 c0 0 0 9 DELAY / REVERB Spring63
92 AIR 70 0 0 9 DELAY / REVERB Air
93 TLDROOM 30 0 0 9 DELAY / REVERB TiledRoom
94 HDHALL 8 0 0 9 DELAY / REVERB HD Hall
95 EARLYREF 60 0 0 9 DELAY / REVERB EarlyRed
96 PARTICLE 90 1 0 9 DELAY / REVERB ParticleR
97 ROOM 20 0 0 9 DELAY / REVERB Room
98 ARENA 50 0 0 9 DELAY / REVERB Arena
99 MODREV a0 0 0 9 DELAY / REVERB ModReverb
100 PEDALPIT 40 0 0 0b PEDAL PDL Pitch
101 PEDALMNP 50 0 0 0b PEDAL PDF MnPit
102 WH100 30 0 0 0b PEDAL WAH100
103 PEDALCRY 10 0 0 0b PEDAL PedalCry
104 PEDALVX 20 0 0 0b PEDAL PdealVx
105 CMN_DRV 0 0 0 0f Bypass Bypass

The following file contains stock patches (A0 to I7) as received with the 0x29 sysex command stock_patches_A0-I7.txt

This code reads the txt file, skips the lines starting with ##, one line is one patch, unpacks it and gets effect names

#include <sstream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>

std::vector<uint8_t> Unpack(std::vector<uint8_t>);

std::vector<std::vector<uint8_t>> Read_Patches(std::string);

std::vector<uint8_t> GetPatchData(std::vector<uint8_t>);

void PrintDecodedSySex(std::vector<uint8_t>);

void PrintCodedSySex(std::vector<uint8_t>);

std::string GetEffectNameById(std::vector<uint8_t>);

class DeviceEffect
{
public:
    DeviceEffect(std::string name, std::string dname, std::vector<uint8_t> id)
    {
        Name = name;
        DisplayName = dname;
        Id = id;
    };
    std::string Name;
    std::string DisplayName;
    std::vector<uint8_t> Id;
};

std::vector<DeviceEffect> DeviceEffects{
    DeviceEffect("COMP","Comp",{0x8, 0x0, 0x0, 0x1}),
    DeviceEffect("ZNR","ZNR",{0x50, 0x0, 0x0, 0x1}),
    DeviceEffect("160_COMP","160 Comp",{0x35, 0x0, 0x0, 0x1}),
    DeviceEffect("NOISEGTE","NoiseGate",{0x60, 0x0, 0x0, 0x1}),
    DeviceEffect("SLWATK","SlowATTCK",{0x40, 0x0, 0x0, 0x1}),
    DeviceEffect("OPTCOMP","OptComp",{0x30, 0x0, 0x0, 0x1}),
    DeviceEffect("GEQ","GraphicEQ",{0x10, 0x0, 0x0, 0x2}),
    DeviceEffect("STEP","Step",{0xa0, 0x0, 0x0, 0x2}),
    DeviceEffect("FCYCLE","fCycle",{0xd0, 0x0, 0x0, 0x2}),
    DeviceEffect("PEQ","ParaEQ",{0x20, 0x0, 0x0, 0x2}),
    DeviceEffect("M_FILTER","M-Filter",{0x90, 0x0, 0x0, 0x2}),
    DeviceEffect("RNDMFLTR","RndmFLTR",{0xc0, 0x0, 0x0, 0x2}),
    DeviceEffect("CRY","Cry",{0x70, 0x0, 0x0, 0x2}),
    DeviceEffect("SEQFLTR","SeqFLTR",{0xb0, 0x0, 0x0, 0x2}),
    DeviceEffect("AUTOWAH","AutoWah",{0x50, 0x0, 0x0, 0x2}),
    DeviceEffect("EXCITER","Exciter",{0x30, 0x0, 0x0, 0x2}),
    DeviceEffect("Z_WILD","Z Wild",{0x10, 0x1, 0x0, 0x3}),
    DeviceEffect("DISTPLUS","Dist+",{0x40, 0x0, 0x0, 0x3}),
    DeviceEffect("LEAD","Lead",{0x20, 0x1, 0x0, 0x3}),
    DeviceEffect("EXTRMDS","ExtremeDS",{0x30, 0x1, 0x0, 0x3}),
    DeviceEffect("GRTMUFF","GreatMuff",{0x80, 0x0, 0x0, 0x3}),
    DeviceEffect("Z_CLEAN","Z Clean",{0xb0, 0x0, 0x0, 0x3}),
    DeviceEffect("MTLWRLD","MetalWRLD",{0x90, 0x0, 0x0, 0x3}),
    DeviceEffect("FUZZSML","FuzzSmile",{0x70, 0x0, 0x0, 0x3}),
    DeviceEffect("Z_MP1","Z MP1",{0xc0, 0x0, 0x0, 0x3}),
    DeviceEffect("T_SCREAM","T Scream",{0x20, 0x0, 0x0, 0x3}),
    DeviceEffect("GOVERNOR","Governor",{0x30, 0x0, 0x0, 0x3}),
    DeviceEffect("OVRDRV","OverDrive",{0x10, 0x0, 0x0, 0x3}),
    DeviceEffect("Z_SCREAM","Z Scream",{0xf0, 0x0, 0x0, 0x3}),
    DeviceEffect("ACOSIM","Aco.Sim",{0x40, 0x1, 0x0, 0x3}),
    DeviceEffect("SQUEAK","Squeak",{0x60, 0x0, 0x0, 0x3}),
    DeviceEffect("BOOSTER","Booster",{0x8, 0x0, 0x0, 0x3}),
    DeviceEffect("DISTONE","Dist 1",{0x50, 0x0, 0x0, 0x3}),
    DeviceEffect("HOTBOX","HotBox",{0xa0, 0x0, 0x0, 0x3}),
    DeviceEffect("BG_CRNCH","BG CRUNCH",{0x60, 0x0, 0x0, 0x4}),
    DeviceEffect("ALIEN","ALIEN",{0x40, 0x1, 0x0, 0x4}),
    DeviceEffect("TONECITY","TONECITY",{0xa0, 0x0, 0x0, 0x4}),
    DeviceEffect("REVO_1","REVO-1",{0x50, 0x1, 0x0, 0x4}),
    DeviceEffect("VX_COMBO","VX COMBO",{0x40, 0x0, 0x0, 0x4}),
    DeviceEffect("MS_1959","MS 1959",{0xf0, 0x0, 0x0, 0x4}),
    DeviceEffect("DZ_DRIVE","DZ DRIVE",{0x30, 0x1, 0x0, 0x4}),
    DeviceEffect("FDCOMBO","FD COMBO",{0x8, 0x0, 0x0, 0x4}),
    DeviceEffect("TW_ROCK","TW ROCK",{0x90, 0x0, 0x0, 0x4}),
    DeviceEffect("DELUXE_R","DELUXE-R",{0x10, 0x0, 0x0, 0x4}),
    DeviceEffect("MSCRUNCH","MS CRUNCH",{0xe0, 0x0, 0x0, 0x4}),
    DeviceEffect("BGN_DRV","BGN DRIVE",{0x10, 0x1, 0x0, 0x4}),
    DeviceEffect("HW_STACK","HW STACK",{0xb0, 0x0, 0x0, 0x4}),
    DeviceEffect("CAR_DRV","CAR DRIVE",{0x80, 0x0, 0x0, 0x4}),
    DeviceEffect("MATCH_30","MATCH 30",{0x70, 0x0, 0x0, 0x4}),
    DeviceEffect("FD_VIBRO","FD VIBRO",{0x20, 0x0, 0x0, 0x4}),
    DeviceEffect("TANGERIN","TANGERINE",{0xc0, 0x0, 0x0, 0x4}),
    DeviceEffect("VX_JMI","VX JIMI",{0x50, 0x0, 0x0, 0x4}),
    DeviceEffect("BG_DRV","BG DRIVE",{0x20, 0x1, 0x0, 0x4}),
    DeviceEffect("US_BLUES","US BLUES",{0x30, 0x0, 0x0, 0x4}),
    DeviceEffect("B_BRKR","B-BREAKER",{0xd0, 0x0, 0x0, 0x4}),
    DeviceEffect("MS_DRV","MS DRIVE",{0x0, 0x1, 0x0, 0x4}),
    DeviceEffect("SUPERCHO","SuperCho",{0xd0, 0x0, 0x0, 0x6}),
    DeviceEffect("PTCHSHFT","PitchSHFT",{0x20, 0x1, 0x0, 0x6}),
    DeviceEffect("ENSEMBLE","Ensemble",{0xb0, 0x0, 0x0, 0x6}),
    DeviceEffect("VINFLNGR","VinFLNGR",{0xd8, 0x0, 0x0, 0x6}),
    DeviceEffect("BENDCHO","BendCho",{0x50, 0x1, 0x0, 0x6}),
    DeviceEffect("TREMOLO","Tremolo",{0x8, 0x0, 0x0, 0x6}),
    DeviceEffect("SLICER","Slicer",{0x20, 0x0, 0x0, 0x6}),
    DeviceEffect("MNPIT","PDL MnPit",{0x30, 0x1, 0x0, 0x6}),
    DeviceEffect("HPS","HPS",{0x40, 0x1, 0x0, 0x6}),
    DeviceEffect("DUOPHASE","Duo-Phase",{0x35, 0x0, 0x0, 0x6}),
    DeviceEffect("THEVIBE","TheVibe",{0x40, 0x0, 0x0, 0x6}),
    DeviceEffect("PHASER","Phaser",{0x30, 0x0, 0x0, 0x6}),
    DeviceEffect("CHORUS","Chorus",{0x70, 0x0, 0x0, 0x6}),
    DeviceEffect("DETUNE","Detune",{0x80, 0x0, 0x0, 0x6}),
    DeviceEffect("CRN_TRI","CoronaTri",{0x0, 0x2, 0x0, 0x6}),
    DeviceEffect("VIBRATO","Vibrato",{0x0, 0x1, 0x0, 0x6}),
    DeviceEffect("RINGMOD","RingMod",{0x70, 0x1, 0x0, 0x6}),
    DeviceEffect("STCHO","StereoCho",{0xa0, 0x0, 0x0, 0x6}),
    DeviceEffect("OCTAVE","Octave",{0x10, 0x1, 0x0, 0x6}),
    DeviceEffect("FLANGER","Flanger",{0xe0, 0x0, 0x0, 0x6}),
    DeviceEffect("RTCLOSET","Rt Closet",{0x60, 0x0, 0x0, 0x7}),
    DeviceEffect("BOMBER","Bomber",{0x20, 0x0, 0x0, 0x7}),
    DeviceEffect("BITCRUSH","BitCrush",{0x10, 0x0, 0x0, 0x7}),
    DeviceEffect("Z_ORGAN","Z-Organ",{0x40, 0x0, 0x0, 0x7}),
    DeviceEffect("DELAY","Delay",{0x8, 0x0, 0x0, 0x8}),
    DeviceEffect("STDELAY","StereoDly",{0x90, 0x0, 0x0, 0x8}),
    DeviceEffect("MLTTPDLY","MultiTapD",{0x50, 0x0, 0x0, 0x8}),
    DeviceEffect("STOMPDLY","StompDly",{0xc0, 0x0, 0x0, 0x8}),
    DeviceEffect("CRBNDLY","CarbonDly",{0xf0, 0x0, 0x0, 0x8}),
    DeviceEffect("PITCHDLY","PitchDly",{0x80, 0x0, 0x0, 0x8}),
    DeviceEffect("TAPEECHO","TapeEcho",{0x10, 0x0, 0x0, 0x8}),
    DeviceEffect("FLTRDLY","FilterDly",{0x70, 0x0, 0x0, 0x8}),
    DeviceEffect("RVRSDLY","ReverseDL",{0x40, 0x0, 0x0, 0x8}),
    DeviceEffect("PLATE","Plate",{0x90, 0x0, 0x0, 0x9}),
    DeviceEffect("HALL","Hall",{0x10, 0x0, 0x0, 0x9}),
    DeviceEffect("SPRING63","Spring63",{0xc0, 0x0, 0x0, 0x9}),
    DeviceEffect("AIR","Air",{0x70, 0x0, 0x0, 0x9}),
    DeviceEffect("TLDROOM","TiledRoom",{0x30, 0x0, 0x0, 0x9}),
    DeviceEffect("HDHALL","HD Hall",{0x8, 0x0, 0x0, 0x9}),
    DeviceEffect("EARLYREF","EarlyRed",{0x60, 0x0, 0x0, 0x9}),
    DeviceEffect("PARTICLE","ParticleR",{0x90, 0x1, 0x0, 0x9}),
    DeviceEffect("ROOM","Room",{0x20, 0x0, 0x0, 0x9}),
    DeviceEffect("ARENA","Arena",{0x50, 0x0, 0x0, 0x9}),
    DeviceEffect("MODREV","ModReverb",{0xa0, 0x0, 0x0, 0x9}),
    DeviceEffect("PEDALPIT","PDL Pitch",{0x40, 0x0, 0x0, 0x0b}),
    DeviceEffect("PEDALMNP","PDF MnPit",{0x50, 0x0, 0x0, 0x0b}),
    DeviceEffect("WH100","WAH100",{0x30, 0x0, 0x0, 0x0b}),
    DeviceEffect("PEDALCRY","PedalCry",{0x10, 0x0, 0x0, 0x0b}),
    DeviceEffect("PEDALVX","PdealVx",{0x20, 0x0, 0x0, 0x0b}),
    DeviceEffect("CMN_DRV","",{0x0, 0x0, 0x0, 0x0f})
};

// as is from shooking code
std::vector<uint8_t> Unpack(std::vector<uint8_t> sysex)
{
    // Unpack data 7bit to 8bit, MSBs in first byte
    int loop = -1;
    uint8_t hibits = 0;

    std::vector<uint8_t> unpacked;

    // remove the last element F7
    sysex.pop_back();
    // remove the first 5 elements F0 52 00 64 28
    sysex.erase(sysex.begin(), sysex.begin() + 5);

    for (size_t i = 0; i < sysex.size(); i++)
    {
        // byte in packet:
        uint8_t CurrentByte = sysex[i];
        if (loop != -1)
        {
            uint8_t p = pow(2, loop);
            if (hibits & p)
            {
                CurrentByte = 0x80 + CurrentByte; // data.append(128 + byte)
                unpacked.push_back(CurrentByte);
            }
            else
            {
                unpacked.push_back(CurrentByte);
            }
            loop = loop - 1;
        }
        else
        {
            hibits = CurrentByte;
            // do we need to acount for short sets (at end of block)?
            loop = 6;
        }
    }
    return unpacked;
}

// based on shooking's code and nomadbyte's analysis
std::vector<uint8_t> GetPatchData(std::vector<uint8_t> unpacked)
{
    for (size_t j = 0; j < 10; j++)
    {
        printf("%c", unpacked[unpacked.size() - 11 + j]);
    }

    printf("\n");

    for (size_t i = 0; i < 5; i++) // max 5 effect slots in patch
    {
        std::vector<uint8_t> FX_id;
        for (size_t j = 0 ; j < 4; j++) 
        {
            FX_id.push_back((unpacked[18 * i + j] >> 1)); // fill FX_id with first 4 bytes of each effect shifted right by 1
        }
        if ((unpacked[18 * i + 1] & 1) == 1)
        {
            FX_id[0] += 0x80;
        }
        FX_id[3] = FX_id[3] & 0x0F; // 00001111

        std::string fx_name = GetEffectNameById(FX_id);

        printf("FX %zu [ %.3s ] %02X %02X %02X %02X - %10s\r\n",
               i + 1,
               (unpacked[18 * i] & 1) == 0 ? "OFF" : "ON",
               FX_id[0],
               FX_id[1],
               FX_id[2],
               FX_id[3],
               fx_name.c_str()
               );
    }

    printf("\nnFX= %2d %02x\n", (unpacked[90] & 0xF0) >> 5, (unpacked[90] & 0xF0) >> 5);

    printf("Vol= %2d %02x\n\n", unpacked[91] & 0x75, unpacked[91] & 0x75);

    return unpacked;
}

std::vector<std::vector<uint8_t>> Read_Patches(std::string filename)
{
    std::vector<std::vector<uint8_t>> patches;

    std::fstream read_file;

    std::stringstream ss;

    // open a file to perform read operation using file object.
    read_file.open(filename, std::ios::in);

    // Checking whether the file is open.
    if (read_file.is_open())
    {
        std::string line;
        bool new_patch = false;
        std::string patch_name = "";
        // Read data from the file object and put it into a string.
        while (std::getline(read_file, line))
        {
            std::vector<uint8_t> patch;
            // std::cout << line << std::endl;
            if (line.find("##") != std::string::npos)
            {
                patch_name = line.substr(2, 2);
                new_patch = true;
                patch.clear();
                continue;
            }
            // Print the data of the string.
            std::stringstream oss;
            std::stringstream iss(line);
            std::string data;
            while (std::getline(iss, data, ' '))
            {
                try
                {
                    // uint8_t b  = atol(data.c_str());
                    uint8_t b = std::stol(data, nullptr, 16);
                    patch.push_back(b);
                }
                catch (...)
                {
                    printf("ERROR>>>>>>>>>>>> %s", patch_name.c_str());
                    printf("                  %s", data.c_str());
                }
            }
            patches.push_back(patch);
        }

        // Close the file object.
        read_file.close();
    }

    return patches;
}

void PrintCodedSySex(std::vector<uint8_t> vi)
{
    printf("\nOriginal SySex message (current patch):\n");

    size_t ctr = 0;
    for (uint8_t i : vi)
    {
        if (ctr == 5)
        {
            printf("\n");
        }
        if (ctr > 5 && ((ctr - 5) % 16 == 0))
        {
            printf("\n");
        }
        ctr++;
        printf("%02X ", (0xff & i));
    }
    printf("\n");
}

void PrintDecodedSySex(std::vector<uint8_t> vo)
{
    size_t ctr = 0;
    // we expect 5 x 18 bytes then rest.
    printf("\nDecoded patch:");
    for (uint8_t i : vo)
    {
        if ((ctr % 18 == 0 && ctr < 91) || ctr == 91 || ctr == 92 || ctr == 101)
        {
            printf("\n");
        }
        ctr++;
        printf("%02X ", (0xff & i));
    }
    printf("\n");
}

std::string GetEffectNameById(std::vector<uint8_t> v)
{
    std::string name="NOT FOUND";
    size_t start = 0;
    size_t stop = 0;
    switch (v[3])
        {
            //case 0x00:
            //  name = "Bypass";
            //  return name;
            case 0x01:
                start = 0;
                stop = 5;   
                break;
            case 0x02:
                start = 6;
                stop = 15;  
                break;
            case 0x03:
                start = 16;
                stop = 33;  
                break;              
            case 0x04:
                start = 34;
                stop = 55;  
                break;              
            case 0x06:
                start = 56;
                stop = 75;  
                break;  
            case 0x07:
                start = 76;
                stop = 79;  
                break;  
            case 0x08:
                start = 80;
                stop = 88;  
                break;  
            case 0x09:
                start = 89;
                stop = 99;  
                break;  
            case 0x0B:
                start = 100;
                stop = 104; 
                break;
            case 0x0F:
                start = 105;
                stop = 105;
                break;                                                                              
        }
    for (size_t i = start; i < (stop + 1); i++)
    {
        if (v[0] == DeviceEffects[i].Id[0] && v[1] == DeviceEffects[i].Id[1] && v[2] == DeviceEffects[i].Id[2])
        {
            name = DeviceEffects[i].DisplayName;
            return name;
        }
    }
    return name;
}

int main(int argc, char **argv)
{
    std::string filename = "stock_patches_A0-I7.txt";
    printf("Infile: %s...", filename.c_str());
    std::vector<std::vector<uint8_t>> patches = Read_Patches(filename);
    printf("Found: %ld patches\n", patches.size());

    for (size_t i = 0; i < patches.size(); i++)
    {
        std::vector<uint8_t> p = patches[i];
        // PrintCodedSySex(p);
        std::vector<uint8_t> unpacked_p = Unpack(p);
        std::vector<uint8_t> unpacked_f = GetPatchData(unpacked_p);
        // PrintDecodedSySex(unpacked_p);
    }

    return 0;
}

For example: Patch F2 - MUFFBIG Effect No2 = GreatMuff

from ZDL file GRTMUFF.ZDL : offset 0x40 0x41 0x42 0x43 80 0 0 3 10000000 00000000 00000000 00000011

from the unpacked sysex we have at byte [18 * 1] 1 1 0 46 00000001 00000001 00000000 01000110

shifted >> 1 , we get 0 0 0 3 00000000 00000000 00000000 00100011

and because unpacked[18 1 + 1] == 1 (the bold bits above) unpacked[18 1 + 0] += 0x80 // 1000000

we get 10000000 00000000 00000000 00100011

finally unpacked[18 1 + 3] = unpacked[18 1 + 3] & 0x0F; // 00001111 10000000 00000000 00000000 00000011 which is the id from the ZDL file


another example: Patch A8 - Acoustix Effect No3 = HD Hall

from ZDL file HDHALL.ZDL : offset 0x40 0x41 0x42 0x43 8 0 0 9 00001000 00000000 00000000 00001001

from the unpacked sysex we have at byte [18 * 2] 11 0 0 12 00010001 00000000 00000000 00010010

shifted >> 1 , we get 0 0 0 3 00001000 00000000 00000000 00001001

because unpacked[18 * 1 + 1] == 0 (the bold bits above) do nothing

finally unpacked[18 1 + 3] = unpacked[18 1 + 3] & 0x0F; // 00001111 00001000 00000000 00000000 00001001 which is the id from the ZDL file

i have checked around 35 effects (out of 105) and they comply with the above procedure. I don't have the knowledge to explain "logically" and deeply the whole procedure.

the output of the above code is in this file effect_names.txt

shooking commented 3 months ago

Hey @dkts2000 - neat. Not sure about the start / stop ... I rarely like constants. But I love what you did here.

If you know how to make a Pico act as a USB host please send me some pointers to your code.

What I did with the B1XFour was to solder into the buttons / encoders via a 25 pin cable then use an ESP32 to send pulses or one can use momentary switches. The idea being I can Bluetooth to the ESP32 to receive commands (and send the pulses to the unit) or I can BT out to advance song PDFs.

Ideally if I could also send the midi down the USB then I can use that where it makes sense and send pulses for things like looper / rhythm. I was using a 22" touchscreen and a Pi as well to control.

Mostly once I have my patches dialed in I don't need to mod them (save turn an FX on/off) - and the G5n mostly does this.

I will revisit your code - I can find a way to let you check it into ZPF. So if I shift the 4 bytes it is easier to manipulate? For sure I could see on set was x 2.

Best regards Steve

dkts2000 commented 3 months ago

Hi @shooking

The start/stop is just a way to avoid iterating through all effects in DeviceEffects vector when it is comparing the given id. I should have explained it more. the DeviceEffects vector has 106 elements (effects) which are sorted by the 4th element of their Id vector (which is the byte at 0x43 offset in the corresponding ZDL file). I added a column in the table above to make it more clear

Not sure about the start / stop ... I rarely like constants

EDIT: sorry maybe i misunderstood what you said. What do you mean?

EDIT 2:

If you know how to make a Pico act as a USB host please send me some pointers to your code.

I used this library https://github.com/rppicomidi/usb_midi_host by @rppicomidi

shooking commented 3 months ago

Sure. You could use a map right? That's what I did last night. Instead of a pair I should reuse your object. We know the id is unique. So let that be the key (I will look into the 2 shift at the weekend).

Thanks for usb midi tip. I am using PICO for Atari sidecart. They seem like decent ESP32 like device

nomadbyte commented 3 months ago

@dkts2000 A lot of work you've done, I guess you have better view of the details now.

If I understand your code correctly, the intent is to list the effects sequence in a given patch. For this, you use hardcoded definitions of all G1Xon effect modules (ZDL files) with some id attributes as stored in a std::vector which then used for looking up effect's name by its id.

In my opinion, it would be much simpler to achieve this by putting the effects into a std::map keyed on effectId. effectId is just an integer, like 0x01000008 (for Comp). If needed, you could derive effect's category as categoryId = effectId >> 24 (0x01 for Comp). Thestd::map is sorted by key, so this also automatically lists the effects by category.

Similarly, I'd create a map of category name (and short tag) keyed on categoryId, so it could be looked up for display.

dkts2000 commented 3 months ago

Yes, you are right. I was already thinking of an implementation with an std::map

shooking commented 3 months ago

i did a naive map but using a pair yesterday and checked it in - the only reason I preferred ID-GID pair is that's how you would implement the sysex for your next step. But sure - you can cut the 4 bytes in a plethora of ways to produce necessary key/keys. I guess we dont have Zoom's code to know how they really do it.

I did also find my notes (when I was trying to get the GIDs 1, 2, 3, etc) in subfolder. I will revisit for my own benefit.

thanks again @dkts2000 for your work - I added it locally into my git - let me know if you want to be able to check stuff in directly - AFAIK it is just a case of me adding you rights to the repo?

dkts2000 commented 3 months ago

So, the best (and fastest) solution would be an std::map<int, std::string> where int is the 4 bytes of the effect id = (unpacked[0] << 24) + (unpacked[1] << 16) + (unpacked[2] << 8) + unpacked[3] ?

shooking commented 3 months ago

A single integer key is the simplest (we won't eprk about the versions of the FX). Again, I checked in a two key solution simply because that how the midi cmds will work. But since you use an object it would easy to get FXiD and get GID.

Thanks again for your input here. Really appreciate it.