mungewell / zoom-zt2

Python script to install/remove effects from the Zoom G1Four pedal
MIT License
50 stars 10 forks source link

B1On testing - Current Patch == F0 52 00 65 29 F7 #24

Open shooking opened 3 years ago

shooking commented 3 years ago

Returns some info about the current patch.

pi@raspberrypi:~/Software/ZoomPedalB1ON $ more CurrentPatch.sh 
export MIDI_DEV=`amidi -l | grep ZOOM | awk '{print $2}'`
amidi -p ${MIDI_DEV} -S "f0 52 00 65 29 f7" -r temp.bin -t 1 ; hexdump -C temp.bin

Now I choose a few

pi@raspberrypi:~/Software/ZoomPedalB1ON $ ./CurrentPatch.sh 

134 bytes read
00000000  f0 52 00 65 28 00 71 00  00 0e 0d 00 00 08 08 40  |.R.e(.q........@|
00000010  4b 49 03 00 00 00 00 00  00 00 01 00 00 00 00 00  |KI..............|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 01 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 20 00 50 04  |............ .P.|
00000070  00 00 00 20 03 00 00 00  00 42 61 73 73 00 69 63  |... .....Bass.ic|
00000080  6b 53 79 6e 00 f7                                 |kSyn..|
00000086
pi@raspberrypi:~/Software/ZoomPedalB1ON $ ./CurrentPatch.sh 

134 bytes read
00000000  f0 52 00 65 28 00 21 00  20 4a 03 70 00 28 0c 60  |.R.e(.!. J.p.(.`|
00000010  01 45 04 00 00 00 00 00  5c 00 21 01 00 40 04 05  |.E......\.!..@..|
00000020  10 00 07 40 20 30 00 00  4c 03 00 00 00 00 00 01  |...@ 0..L.......|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 01 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 40 00 64 04  |............@.d.|
00000070  00 00 00 20 03 00 00 00  00 47 2d 42 6f 00 6f 67  |... .....G-Bo.og|
00000080  69 65 20 20 00 f7                                 |ie  ..|
00000086
pi@raspberrypi:~/Software/ZoomPedalB1ON $ ./CurrentPatch.sh 

134 bytes read
00000000  f0 52 00 65 28 08 31 00  00 44 03 68 00 3e 0c 00  |.R.e(.1..D.h.>..|
00000010  01 01 01 01 0c 01 00 00  00 00 61 00 40 03 02 08  |..........a.@...|
00000020  50 00 0a 00 24 00 04 00  00 00 00 00 00 00 00 01  |P...$...........|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 01 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 40 00 78 04  |............@.x.|
00000070  00 00 00 20 03 00 00 00  00 52 45 43 20 00 53 4f  |... .....REC .SO|
00000080  4c 4f 5f 44 00 f7                                 |LO_D..|
shooking commented 3 years ago

Hi @mungewell In your experience where does G1X store any checksums in a Sysex? I describe below the format I have found in the B1On. Also should I start my own github and make referenced to yours? Or is there some place I can start to put B1On related cruft?

Been making a few YT vids - trying to ensure I give you/Barsik/SixEight etc credit where due. Great stuff you have done.

What I found

So I got a lot of the patch understood now. VERY similar to the other CDR70. Always work in the unpacked space!! SysExTones has done fine work as has sixeight BUT they are working in raw (not cooked!) space.

18 is the magic number for "29" response. Start at byte after 28 - this is your 1st column for overflow bytes. So then 8 bytes on is the next column. Because we are removing one byte per 8 we have a contraction mapping. In the upacked landscape get your decoder to write out 18 bytes in a row. The first 5 are the first 5 FX. It is ID/folder in first 4 bytes. 1st column & 1 == 1 => On. Otherwise off. So in the below this should be consider 0141 so FX is on. the 0e04 I guess is 040e .. and is something like 32 x the folder value. then we have 12 bits per FX. So masking is required.

41 01 00 0e 04 20 00 8c 60 04 00 00 00 00 00 00 00 00 
f1 00 00 8c 0d b0 00 2c 40 80 0c 00 00 00 00 00 00 00 
21 01 00 12 02 a0 01 28 c0 c7 e9 0b 80 0c 00 00 00 00 
41 01 20 ca 02 50 00 0a c0 61 23 0f 60 01 00 00 8a 00 
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
80 00 04 00 00 00 20 03 00 00 00 41 2d 52 65 6d 61 72 
6b 20 20 00 

We can see the last 11 bytes form the name. so this leaves (in above 1st column value 80) 80 00 04 00 00 00 20 03 00 00 00 The 2nd column is the patch volume. I am trying to isolate the next bytes. Tempo? I guess I am looking for per-patch values.

shooking commented 3 years ago

I modified GetCurrentPatch.sh a litle pi@raspberrypi:~/Software/ZoomPedalB1ON $ more ./GetCurrentPatch.sh

#!/bin/bash
export MIDI_DEV=`amidi -l | grep ZOOM | awk '{print $2}'`

#            F0 52 00 65 29 00 F7
probeString="F0 52 00 65 29 F7"
echo ${probeString}
theFile=currentPatch.bin
rm ${theFile}
amidi -p ${MIDI_DEV} -S ${probeString} -r ${theFile} -t 1 ; hexdump -C ${theFile
}

the fundamental difference is I write output to currentPatch.bin

Then I read this output into some C++ (code changes as I find more semantics for the bytes)

pi@raspberrypi:~/Software/ZoomPedalB1ON $ more ./B1OnPatchUnpack000.cpp
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>

#include <string>
#include <cmath>
#include <vector>
using namespace std;
typedef unsigned char BYTE;

vector<BYTE> unpack (vector<BYTE> &vi);

vector<BYTE> unpack ( vector<BYTE> &sysex )
{
    // Unpack data 7bit to 8bit, MSBs in first byte
    //data = bytearray(b"")
    int loop = -1;
    uint8_t hibits = 0;

    int j = 0;
    vector<BYTE>    unpacked;

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

    // Start to summarize what we know
    for (int i = 0; i < 5; i++)
    {
        int p[] = {
            ( ( ((unpacked[ 18* i + 4]      ) << 8) + (unpacked[18* 
i +  3]) ) >> 6 ),
            ( ( ((unpacked[ 18* i + 6]      ) << 8) + (unpacked[18* 
i +  5]) ) >> 3 ),
            ( ( ((unpacked[ 18* i + 8] & 0xF) << 8) + (unpacked[18* 
i +  7]) )     ),
            ( ( ((unpacked[ 18* i + 9] & 0xF) << 8) + (unpacked[18* 
i +  8]) ) >> 5 ),
            ( ( ((unpacked[18* i + 10] & 0xF) << 8) + (unpacked[18* 
i +  9]) ) >> 5 ),
            ( ( ((unpacked[18* i + 11] & 0xF) << 8) + (unpacked[18* 
i + 10]) ) >> 5 ),
            ( ( ((unpacked[18* i + 12] & 0xF) << 8) + (unpacked[18* 
i + 11]) ) >> 5 ),
            ( ( ((unpacked[18* i + 13] & 0xF) << 8) + (unpacked[18* 
i + 12]) ) >> 5 ) 
        };

        for (j = 0; j < 8; j++)
        {
            printf("FX%d P%d = %d (%02x) ", i+1, j+1, p[j], p[j]);
        }
        cout << endl;
    }

    printf("Volume = %d (%02x)\n", unpacked[91], unpacked[91]);

    for (j = 0; j < 10; j++)
    {
        cout << unpacked[unpacked.size() - 11 + j] ;
    }
    cout << endl;

    return unpacked;
}

vector<BYTE> readFile(char* filename)
{
    // open the file:
    ifstream file(filename, ios::binary | ios::in);

    // Stop eating new lines in binary mode!!!
    file.unsetf(ios::skipws);

    // get its size:
    streampos fileSize;

    file.seekg(0, ios::end);
    fileSize = file.tellg();
    file.seekg(0, ios::beg);

    vector<BYTE> vec;
    // reserve capacity
    vec.reserve(fileSize);

    // read the data:
    vec.insert(vec.begin(),
               istream_iterator<BYTE>(file),
               istream_iterator<BYTE>());
    // cout << "vec was " << vec.size() << endl;
    file.close();
    return vec;
}

int
main (int argc, char **argv)
{
    /* read the file in, strip off header and F7 */

    cout << "Infile: " << argv[1] << endl;
    ifstream in_f(argv[1], ios::in | ios::binary);
    // ofstream out_f(argv[1], ios::out | ios::binary);

    vector<BYTE> vo;
    vector<BYTE> vi = readFile(argv[1]);

    cout << "INPUT\n";
    int ctr = 0;
    for(auto i: vi)
    {
        if (ctr == 5) cout << endl;
        if (ctr > 5 && ((ctr - 5) % 16 == 0)) cout << endl;
        ctr++;
        int value = i;
        cout << setfill ('0') << setw (2) << hex << (0xff & (BYTE) value
) << " ";
    }
    cout << endl;

    // cout << "file size: " << vi.size() << endl;
    vo = unpack (vi);

    ctr = 0;
    // we expect 5 x 18 bytes then rest.
    cout << "OUTPUT\n";
    for (auto i: vo)
    {
        if (ctr % 18 == 0) cout << endl;
        ctr++;
        int value = i;
        cout << setfill ('0') << setw (2) << hex << (0xff & (BYTE) value
) << " ";
    }
    cout << endl;
    return 0;
}

Currently produces sample output

pi@raspberrypi:~/Software/ZoomPedalB1ON $ ./GetCurrentPatch.sh 
F0 52 00 65 29 F7

134 bytes read
00000000  f0 52 00 65 28 00 41 01  00 0e 04 20 00 40 0c 60  |.R.e(.A.... .@.`|
00000010  04 00 00 00 00 04 00 00  00 00 71 00 00 51 0c 0d  |..........q..Q..|
00000020  30 00 2c 40 00 00 0c 00  00 00 00 00 00 01 00 21  |0.,@...........!|
00000030  01 00 12 02 20 1d 01 28  40 47 69 0b 00 00 0c 00  |.... ..(@Gi.....|
00000040  00 00 00 41 01 21 20 4a  02 50 00 0a 40 00 61 23  |...A.! J.P..@.a#|
00000050  0f 60 01 00 00 40 0a 00  01 00 00 00 00 00 00 00  |.`...@..........|
00000060  00 00 00 00 00 01 00 00  00 00 00 00 00 00 78 04  |..............x.|
00000070  00 00 00 20 03 00 00 00  00 41 2d 52 65 00 6d 61  |... .....A-Re.ma|
00000080  72 6b 20 20 00 f7                                 |rk  ..|
00000086
pi@raspberrypi:~/Software/ZoomPedalB1ON $ ./B1OnPatchUnpack000 currentPatch.bin
Infile: currentPatch.bin
INPUT
f0 52 00 65 28 
00 41 01 00 0e 04 20 00 40 0c 60 04 00 00 00 00 
04 00 00 00 00 71 00 00 51 0c 0d 30 00 2c 40 00 
00 0c 00 00 00 00 00 00 01 00 21 01 00 12 02 20 
1d 01 28 40 47 69 0b 00 00 0c 00 00 00 00 41 01 
21 20 4a 02 50 00 0a 40 00 61 23 0f 60 01 00 00 
40 0a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 
01 00 00 00 00 00 00 00 00 78 04 00 00 00 20 03 
00 00 00 00 41 2d 52 65 00 6d 61 72 6b 20 20 00 
f7 
FX1 P1 = 16 (10) FX1 P2 = 4 (04) FX1 P3 = 140 (8c) FX1 P4 = 35 (23) FX1 P5 = 0 (00) FX1 P6 = 0 (00) FX1 P7 = 0 (00) FX1 P8 = 0 (00) 
FX2 P1 = 54 (36) FX2 P2 = 22 (16) FX2 P3 = 44 (2c) FX2 P4 = 2 (02) FX2 P5 = 100 (64) FX2 P6 = 0 (00) FX2 P7 = 0 (00) FX2 P8 = 0 (00) 
FX3 P1 = 8 (08) FX3 P2 = 52 (34) FX3 P3 = 40 (28) FX3 P4 = 62 (3e) FX3 P5 = 78 (4e) FX3 P6 = 95 (5f) FX3 P7 = 0 (00) FX3 P8 = 100 (64) 
FX4 P1 = 11 (0b) FX4 P2 = 10 (0a) FX4 P3 = 10 (0a) FX4 P4 = 14 (0e) FX4 P5 = 27 (1b) FX4 P6 = 121 (79) FX4 P7 = 0 (00) FX4 P8 = 11 (0b) 
FX5 P1 = 0 (00) FX5 P2 = 0 (00) FX5 P3 = 0 (00) FX5 P4 = 0 (00) FX5 P5 = 0 (00) FX5 P6 = 0 (00) FX5 P7 = 0 (00) FX5 P8 = 0 (00) 
Volume = 120 (78)
A-Remark  
OUTPUT

41 01 00 0e 04 20 00 8c 60 04 00 00 00 00 00 00 00 00 
f1 00 00 8c 0d b0 00 2c 40 80 0c 00 00 00 00 00 00 00 
21 01 00 12 02 a0 01 28 c0 c7 e9 0b 80 0c 00 00 00 00 
41 01 20 ca 02 50 00 0a c0 61 23 0f 60 01 00 00 8a 00 
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
80 78 04 00 00 00 20 03 00 00 00 41 2d 52 65 6d 61 72 
6b 20 20 00 
mungewell commented 3 years ago

In your experience where does G1X store any checksums in a Sysex?

For the G1Four, and friends, the Patch SysEx is checksum'ed at the packed level (as read by Midi). This is a CRC32: https://github.com/mungewell/zoom-zt2/blob/master/zoomzt2.py#L383

I don't believe the unpacked 'ZPTC' patch contains a checksum, however I don't have experience of re-encoding values and uploading to a device. I have achieved some milestones in my other project(s) and have more experience with Construct now... so may revisit my code to fix the 'build' issue for EDTB section.

WRT G1Xon going forward; you are using an lot of C++ code which is outside my ideas for this project. It might be best if you fire up a project for it, I'm happy to share information and help where I can... Cheers, Simon.

shooking commented 3 years ago

OK makes sense. I used the C++ simply because other user was using it. I can use Python (you notice most of stuff is Bash). And in the limit when I move to Ctrlr it will become Lua.

I will create a GIT to record info for folks who are interested in the B1ON.

On Sun, Jan 31, 2021 at 5:29 PM mungewell notifications@github.com wrote:

In your experience where does G1X store any checksums in a Sysex?

For the G1Four, and friends, the Patch SysEx is checksum'ed at the packed level (as read by Midi). This is a CRC32: https://github.com/mungewell/zoom-zt2/blob/master/zoomzt2.py#L383

I don't believe the unpacked 'ZPTC' patch contains a checksum, however I don't have experience of re-encoding values and uploading to a device. I have achieved some milestones in my other project(s) and have more experience with Construct now... so may revisit my code to fix the 'build' issue for EDTB section.

WRT G1Xon going forward; you are using an lot of C++ code which is outside my ideas for this project. It might be best if you fire up a project for it, I'm happy to share information and help where I can... Cheers, Simon.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mungewell/zoom-zt2/issues/24#issuecomment-770418002, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFO7EVYXYLI5HZR4SWWHSQLS4WHQ5ANCNFSM4WO6UG7A .

shooking commented 3 years ago

In your experience where does G1X store any checksums in a Sysex?

For the G1Four, and friends, the Patch SysEx is checksum'ed at the packed level (as read by Midi). This is a CRC32: https://github.com/mungewell/zoom-zt2/blob/master/zoomzt2.py#L383

I don't believe the unpacked 'ZPTC' patch contains a checksum, however I don't have experience of re-encoding values and uploading to a device. I have achieved some milestones in my other project(s) and have more experience with Construct now... so may revisit my code to fix the 'build' issue for EDTB section.

WRT G1Xon going forward; you are using an lot of C++ code which is outside my ideas for this project. It might be best if you fire up a project for it, I'm happy to share information and help where I can... Cheers, Simon.

Hallo Simon I finally did it. https://github.com/shooking/ZoomPedalFun I have tried to give due credit to you, Barsik, SixEight and a load of other folks.

What I am doing now is gradually staging my commits to my new project. Combo of Wiki (like I wanted to do here) so folks can simply read a distilled reference. Links to others work (like your, Barsik etc). Links to my YT videos as I share how I go about the reverse engineering. And hopefully some areas if people want to collaborate.

For example right now based on the work we did with DKS2000 on another thread I have added a lot of additional B1On automation.

The C++ I modified based on your Python is revealing more stuff. zB according to SixEight if you send

9) Set effect type:

Selecting effect from the editor:
selecting Comp in slot 6 sends F0 52 00 5A 31 05 01 17 00 F7,
selecting MComp in slot 6 sends F0 52 00 5A 31 05 01 19 00 F7,
selecting ZNR: F0 52 00 5A 31 05 01 1B 00 F7
selecting Pedal pitch: F0 52 00 5A 31 00 01 45 00 F7

So the 8th byte is the effect type.
This is consistent with the effect type list below: 17hex = 23 dec

Hmmh change the 5A to 65 for a B1On ... it doesnt do anything. Or does it? Well I send the commands in a dual loop. And the pedal doesnt respond anything. BUT ... if I then grab the current patch I can see I AM updating bytes in the pedal!!!

So now I can spy on what is is doing. Neat eh?

I will be sharing this on my Wiki's etc and I am pretty certain there is read across to the other Zoom pedals.

Getting exciting.