Digitech JamSync - How does it work?

All about modern commercial stompbox circuits from Electro Harmonix over MXR, Boss and Ibanez into the nineties.
User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

Hi guys, I'm working on jamsync. First of all: "pz", thank you for your work.
English is not my primary language, I beg your pardon in advance for mistakes or incomprehensible sentences that I can write;-)

I have a JamMan Express Xt, it has only one big button to push. The guy at fuzzysynth has done a big work, but he owns a "JamMan Solo Xt" that allows to set BPM and others functions. With ExpressXt the BPM lacks of meaning, The only property of the loop is its lenght (ms, samples).

However, I want to share my experiences.

I've attached the minjack-to-din adapter cable from the OUT of Digitech to the MIDI IN of my Novation Impulse. It does not recognise any data (because 3.3v ?). Instead my Behringer BCR2000 can receive data, I think it is more tolerant with the voltage.


I've set a metronome to 90bpm and I've recorded some loop of differents lenght.

I've found changing and fixed values:
F0 00 00 10 7F 62 01 v1 01 v2 v3 v4 v5 02 04 v6 03 v7 v8 v9 w1 w2 w3 F7

values v(x) and w(x) change

v1 ( byte 8 ) : 42, 4A but I've found also 5A,62

w2 is the "command"
02, restart loop after a stop
04, stop
05, loop running
06, initial play

w1 is the most significant byte of the lenght ( I think)
3F, half measure (2/4)
40, 1 or 2 measures (4/4 or 8/4)
41, 4 measure (16/4)
42, about a minute

with Sekaiju (a midi sequencer) I am able to send back the data to ExpressXt.

I've put on track 1 the sync stream made of 11 bytes. It keeps active the "link". If this signal feeds too slow, every one of them restart the link and the ExpressXt flashes its leds again and again.

On track 2 I've put some midi note on for trigger drum sound, useful to play over.

On track 3 I've put "arbitrary sysex" message, with the 24 bytes chunk. With random values v(x),w(x) but command w2 properly set are not recognized, ExpressXt does not react.
Instead the 24 bytes chunk created by ExpressXt, previously read, works. Using a chunk of an audio loop longer than the "midi" loop almost works:

ex.
F0 00 00 10 7F 62 01 5A 01 1D 44 33 42 02 04 78 03 05 63 2A 40 05 07 F7

You have to record the full lenght, but then it restart in sync every time that the command is issued.

If you force stop manually the recording of the loop, then it is not linked and it is out of sync.

I hope that my experiences can help someone else and we all can improve the knowledge of the protocol and figure out something else.

Rgds,
Calde

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

Bit 23 is a checksum value:

JamSync calculate it how a BitXor of the bytes 8, 9, .... 21, 22

for example in the following 24 bytes sysex message:
F0 00 00 10 7F 62 01 7A 01 4D 42 30 42 02 04 50 03 47 4B 2D 40 05 37 F7

the byte number 23, hex value 37 (dec 55) is calculated as Bit Xor of the bytes from 8 to 22
-- -- -- -- -- -- -- 7A 01 4D 42 30 42 02 04 50 03 47 4B 2D 40 05 -- --

This works for every sysex message that I've tried.

I've test with OpenOffice macro:

Function BitXOR( val1 , val2 )
BitXOR = val1 XOR val2
End Function

User avatar
ashimoke
Information
Posts: 3
Joined: 19 Apr 2012, 20:11
Been thanked: 6 times

Post by ashimoke »

Hi! Glad someone is working on this. I've done some work on it before but I got tired of it eventually.
Here is some data I got from the jamsync output, might be useful for some:

Code: Select all

(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 11, 62, 3B, 42,  2,  4, 58,  3, 36, 71, 23, 40,  5, 3D, F7, ) // BPM: 50,	loop lenght: 4800
(F0,  0,  0, 10, 7F, 62,  1, 6A,  1, 56, 7B, 43, 42,  2,  4, 48,  3,  C, 26, 1D, 40,  5, 78, F7, ) // BPM: 52,	loop lenght: 4615
(F0,  0,  0, 10, 7F, 62,  1, 6A,  1, 53, 31, 4B, 42,  2,  4, 48,  3, 63, 2F, 17, 40,  5, 53, F7, ) // BPM: 54,	loop lenght: 4444
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 34, 77, 52, 42,  2,  4, 58,  3, 62, 1D, 11, 40,  5, 5E, F7, ) // BPM: 56,	loop lenght: 4285
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 78, 72, 5A, 42,  2,  4, 48,  3, 7D, 4E,  C, 40,  5, 5E, F7, ) // BPM: 58,	loop lenght: 4137
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 21, 49, 62, 42,  2,  4, 48,  3,  7, 75,  7, 40,  5, 4E, F7, ) // BPM: 60,	loop lenght: 4000
(F0,  0,  0, 10, 7F, 62,  1, 6A,  1, 11, 76, 6A, 42,  2,  4, 68,  3, 51,  5,  3, 40,  5, 5B, F7, ) // BPM: 62,	loop lenght: 3870
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 38, 62, 72, 42,  2,  4, 60,  3,  1, 7B, 7D, 40,  5,  6, F7, ) // BPM: 64,	loop lenght: 3750
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 47, 30, 7A, 42,  2,  4, 50,  3, 23, 13, 75, 40,  5, 51, F7, ) // BPM: 66,	loop lenght: 3636
(F0,  0,  0, 10, 7F, 62,  1, 62,  1, 6F,  4,  1, 43,  2,  4, 60,  3, 1E, 1A, 6E, 40,  5,  0, F7, ) // BPM: 68,	loop lenght: 3529
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 21, 58,  4, 43,  2,  4, 60,  3, 2D, 3E, 67, 40,  5, 19, F7, ) // BPM: 70,	loop lenght: 3428
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 44, 53,  8, 43,  2,  4, 50,  3, 4A,  4, 60, 40,  5, 11, F7, ) // BPM: 72,	loop lenght: 3333
(F0,  0,  0, 10, 7F, 62,  1, 52,  1, 5C, 3E,  C, 43,  2,  4, 60,  3, 6E, 44, 5A, 40,  5, 2E, F7, ) // BPM: 74,	loop lenght: 3243
(F0,  0,  0, 10, 7F, 62,  1, 72,  1,  D,  8, 10, 43,  2,  4, 70,  3, 48,  B, 54, 40,  5,  2, F7, ) // BPM: 76,	loop lenght: 3157
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1, 70,  8, 14, 42,  2,  4, 70,  3, 22, 52, 4E, 40,  5, 7B, F7, ) // BPM: 78,	loop lenght: 3076
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1, 38,  7, 18, 42,  2,  4, 60,  3, 47, 67, 49, 40,  5, 77, F7, ) // BPM: 80,	loop lenght: 3000
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 4C, 65, 1C, 42,  2,  4, 60,  3, 43, 6C, 44, 40,  5, 77, F7, ) // BPM: 82,	loop lenght: 2926
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 25, 64, 20, 42,  2,  4, 50,  3, 1E,  8, 3F, 40,  5, 51, F7, ) // BPM: 84,	loop lenght: 2857
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 45,  E, 24, 42,  2,  4, 70,  3, 28, 2E, 3A, 40,  5, 5A, F7, ) // BPM: 86,	loop lenght: 2790
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 58, 42, 28, 42,  2,  4, 50,  3, 54, 13, 36, 40,  5, 5A, F7, ) // BPM: 88,	loop lenght: 2727
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 31,  1, 2C, 42,  2,  4, 70,  3,  E, 19, 32, 40,  5,  0, F7, ) // BPM: 90,	loop lenght: 2666
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1, 6F, 60, 2F, 42,  2,  4, 70,  3, 77, 2A, 2E, 40,  5, 7A, F7, ) // BPM: 92,	loop lenght: 2608
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1,  C, 61, 33, 42,  2,  4, 70,  3,  F, 47, 2A, 40,  5, 35, F7, ) // BPM: 94,	loop lenght: 2553
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1, 74, 6D, 37, 42,  2,  4, 60,  3, 35,  5, 27, 40,  5,  0, F7, ) // BPM: 96,	loop lenght: 2500
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 2A, 1B, 3B, 42,  2,  4, 70,  3, 62, 3E, 23, 40,  5, 7C, F7, ) // BPM: 98,	loop lenght: 2448
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1,  E,  B, 3F, 42,  2,  4, 60,  3, 30, 61, 20, 40,  5, 72, F7, ) // BPM: 100,	loop lenght: 2400
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1, 7F, 41, 43, 42,  2,  4, 50,  3, 20, 6E, 1C, 40,  5, 26, F7, ) // BPM: 102,	loop lenght: 2352
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1,  0,  8, 47, 42,  2,  4, 70,  3, 17, 75, 19, 40,  5, 3D, F7, ) // BPM: 106,	loop lenght: 2264
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 68, 1D, 4F, 42,  2,  4, 60,  3, 50, 52, 14, 40,  5,  5, F7, ) // BPM: 108,	loop lenght: 2222
(F0,  0,  0, 10, 7F, 62,  1, 6A,  1, 6A,  7, 53, 42,  2,  4, 50,  3, 33, 12, 11, 40,  5, 37, F7, ) // BPM: 110,	loop lenght: 2181
(F0,  0,  0, 10, 7F, 62,  1, 6A,  1, 69, 29, 57, 42,  2,  4, 50,  3, 67, 46,  E, 40,  5,  1, F7, ) // BPM: 112,	loop lenght: 2142
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 32, 7B, 5A, 42,  2,  4, 60,  3, 65, 48,  C, 40,  5, 2B, F7, ) // BPM: 114,	loop lenght: 2105
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 3B, 5E, 5E, 42,  2,  4, 70,  3, 12, 56,  9, 40,  5, 7F, F7, ) // BPM: 116,	loop lenght: 2068
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1, 7B, 78, 62, 42,  2,  4, 40,  3, 11, 59,  7, 40,  5, 37, F7, ) // BPM: 118,	loop lenght: 2033
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 53, 6B, 66, 42,  2,  4, 40,  3, 4C,  8,  5, 40,  5, 26, F7, ) // BPM: 120,	loop lenght: 2000
(F0,  0,  0, 10, 7F, 62,  1, 7A,  1, 3B, 46, 6A, 42,  2,  4, 50,  3, 14, 59,  2, 40,  5, 71, F7, ) // BPM: 122,	loop lenght: 1967
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 3F, 70, 6E, 42,  2,  4, 70,  3, 19, 56,  0, 40,  5, 57, F7, ) // BPM: 124,	loop lenght: 1935
(F0,  0,  0, 10, 7F, 62,  1, 5A,  1,  6,  D, 72, 42,  2,  4, 68,  3, 44, 4E, 7D, 3F,  5, 40, F7, ) // BPM: 126,	loop lenght: 1904
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 2D, 76, 76, 42,  2,  4, 68,  3, 4F, 49, 79, 3F,  5,  C, F7, ) // BPM: 128,	loop lenght: 1875
(F0,  0,  0, 10, 7F, 62,  1, 6A,  1, 15, 1D, 7D, 42,  2,  4, 58,  3, 28, 3C, 72, 3F,  5, 5D, F7, ) // BPM: 130,	loop lenght: 1846
(F0,  0,  0, 10, 7F, 62,  1, 4A,  1, 3C, 4E, 7E, 42,  2,  4, 58,  3, 5D, 19, 71, 3F,  5, 57, F7, ) // BPM: 132,	loop lenght: 1818
(F0,  0,  0, 10, 7F, 62,  1, 62,  1, 6F,  4,  1, 43,  2,  4, 68,  3, 1E, 1A, 6E, 3F,  5, 77, F7, ) // BPM: 134,	loop lenght: 1791
(F0,  0,  0, 10, 7F, 62,  1, 62,  1, 79,  9,  3, 43,  2,  4, 48,  3, 22, 6F, 6A, 3F,  5,  3, F7, ) // BPM: 136,	loop lenght: 1764
(F0,  0,  0, 10, 7F, 62,  1, 42,  1, 2E, 46,  7, 43,  2,  4, 48,  3, 2A, 18, 63, 3F,  5, 49, F7, ) // BPM: 138,	loop lenght: 1739
(F0,  0,  0, 10, 7F, 62,  1, 52,  1, 29, 69,  6, 43,  2,  4, 78,  3, 3F, 34, 63, 3F,  5, 79, F7, ) // BPM: 140,	loop lenght: 1714
(F0,  0,  0, 10, 7F, 62,  1, 42,  1, 37, 11,  9, 43,  2,  4, 68,  3, 21, 1F, 60, 3F,  5, 26, F7, ) // BPM: 142,	loop lenght: 1690
(F0,  0,  0, 10, 7F, 62,  1, 72,  1,  2, 4C,  A, 43,  2,  4, 68,  3, 55, 53, 5D, 3F,  5, 78, F7, ) // BPM: 144,	loop lenght: 1666
(F0,  0,  0, 10, 7F, 62,  1, 52,  1, 3B, 5B,  C, 43,  2,  4, 48,  3, 31, 18, 5A, 3F,  5, 78, F7, ) // BPM: 146,	loop lenght: 1643
(F0,  0,  0, 10, 7F, 62,  1, 52,  1, 35, 4D,  E, 43,  2,  4, 68,  3, 28, 1F, 57, 3F,  5, 51, F7, ) // BPM: 148,	loop lenght: 1621
(F0,  0,  0, 10, 7F, 62,  1, 52,  1, 62, 1F, 10, 43,  2,  4, 48,  3, 3B, 6A, 54, 3F,  5,  F, F7, ) // BPM: 150,	loop lenght: 1600
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 64, 4B, 12, 43,  2,  4, 68,  3, 75, 44, 51, 3F,  5, 3A, F7, ) // BPM: 152,	loop lenght: 1578
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 4D, 48, 14, 43,  2,  4, 48,  3, 29, 79, 4E, 3F,  5, 48, F7, ) // BPM: 154,	loop lenght: 1558
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 16, 32, 16, 43,  2,  4, 58,  3, 1A, 5A, 4B, 3F,  5, 6E, F7, ) // BPM: 156,	loop lenght: 1538
(F0,  0,  0, 10, 7F, 62,  1, 52,  1, 1B, 5C, 18, 43,  2,  4, 78,  3, 6F, 77, 48, 3F,  5, 58, F7, ) // BPM: 158,	loop lenght: 1518
(F0,  0,  0, 10, 7F, 62,  1, 42,  1, 61, 67, 1A, 43,  2,  4, 58,  3, 74, 75, 46, 3F,  5, 3C, F7, ) // BPM: 160,	loop lenght: 1500
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 4C, 1A, 1C, 43,  2,  4, 68,  3, 28, 29, 44, 3F,  5, 68, F7, ) // BPM: 162,	loop lenght: 1481
(F0,  0,  0, 10, 7F, 62,  1, 62,  1,  A, 70, 1E, 43,  2,  4, 58,  3, 12, 64, 41, 3F,  5, 14, F7, ) // BPM: 164,	loop lenght: 1463
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 25,  9, 20, 43,  2,  4, 48,  3, 61, 5B, 3F, 3F,  5, 4E, F7, ) // BPM: 166,	loop lenght: 1445
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 66, 1D, 22, 43,  2,  4, 58,  3,  F, 69, 3C, 3F,  5, 54, F7, ) // BPM: 168,	loop lenght: 1428
(F0,  0,  0, 10, 7F, 62,  1, 62,  1,  8, 5D, 24, 43,  2,  4, 78,  3, 14, 66, 3A, 3F,  5, 5E, F7, ) // BPM: 170,	loop lenght: 1411
(F0,  0,  0, 10, 7F, 62,  1, 42,  1,  2, 3B, 26, 43,  2,  4, 78,  3, 3B, 4D, 38, 3F,  5, 16, F7, ) // BPM: 172,	loop lenght: 1395
(F0,  0,  0, 10, 7F, 62,  1, 62,  1, 71, 75, 28, 43,  2,  4, 48,  3, 68, 5B, 36, 3F,  5, 7E, F7, ) // BPM: 174,	loop lenght: 1379
(F0,  0,  0, 10, 7F, 62,  1, 62,  1,  3, 56, 2A, 43,  2,  4, 68,  3, 6E, 58, 34, 3F,  5,  A, F7, ) // BPM: 176,	loop lenght: 1363
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 28, 6B, 2B, 43,  2,  4, 78,  3, 6D, 2F, 32, 3F,  5, 6F, F7, ) // BPM: 178,	loop lenght: 1348
(F0,  0,  0, 10, 7F, 62,  1, 42,  1, 5B,  C, 2E, 43,  2,  4, 78,  3, 35,  0, 30, 3F,  5, 3B, F7, ) // BPM: 180,	loop lenght: 1333
(F0,  0,  0, 10, 7F, 62,  1, 42,  1, 1B, 24, 30, 43,  2,  4, 68,  3, 5C, 67, 2E, 3F,  5, 4D, F7, ) // BPM: 182,	loop lenght: 1318
(F0,  0,  0, 10, 7F, 62,  1, 62,  1, 42, 31, 32, 43,  2,  4, 48,  3, 61, 65, 2C, 3F,  5, 3E, F7, ) // BPM: 184,	loop lenght: 1304
(F0,  0,  0, 10, 7F, 62,  1, 62,  1,  6, 1C, 34, 43,  2,  4, 78,  3, 23,  F, 2A, 3F,  5, 4F, F7, ) // BPM: 186,	loop lenght: 1290
(F0,  0,  0, 10, 7F, 62,  1, 42,  1, 4A, 42, 36, 43,  2,  4, 58,  3, 29,  D, 28, 3F,  5, 55, F7, ) // BPM: 188,	loop lenght: 1276
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 45, 79, 37, 43,  2,  4, 78,  3,  6, 7A, 26, 3F,  5, 26, F7, ) // BPM: 190,	loop lenght: 1263
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 3D, 52, 39, 43,  2,  4, 68,  3,  5, 51, 25, 3F,  5, 40, F7, ) // BPM: 192,	loop lenght: 1250
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 2C, 68, 3B, 43,  2,  4, 68,  3, 47, 7B, 23, 3F,  5,  7, F7, ) // BPM: 194,	loop lenght: 1237
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 76, 3B, 3D, 43,  2,  4, 58,  3, 24, 69, 21, 3F,  5, 4B, F7, ) // BPM: 196,	loop lenght: 1224
(F0,  0,  0, 10, 7F, 62,  1, 62,  1, 11,  3, 40, 43,  2,  4, 58,  3,  7, 7D, 1F, 3F,  5, 70, F7, ) // BPM: 198,	loop lenght: 1212
(F0,  0,  0, 10, 7F, 62,  1, 72,  1, 16, 19, 41, 43,  2,  4, 78,  3,  0, 2D, 1E, 3F,  5,  A, F7, ) // BPM: 200,	loop lenght: 1200 

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

Hi ashimoke, your data is definitely useful.

As I've the express xt, I can't be precise with my data.

I've already got some formulas to/from the ysex string to BMP and loop lenght, now I can verify them against your table.

Loop lenght => what is the Unit Of Measure? (ms; milliseconds)
Enrico

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

I've done some tests and it seems to work. Perhaps someone can try it for testing and improve it.

Code: Select all

#include <math.h>

/*
 * THANKS TO
 * @ https://www.freestompboxes.org/viewtopic.php?f=1&t=26184
 * to the user pz to have starting all
 * to the user smithoid to initial analysis
 * to the user ashimoke for sharing his data and support
 * @ http://fuzzysynth.blogspot.it/2015/06/digitech-jam-man.html
 * to fuzzy music for sharing the work on protocol
 * @ http://chemiker1981.blogspot.it/2010/10/1-reading-midi-clock-to-read-midi-clock.html
 * to chemiker1981 for midi clock code
 * 
 * r0 - initial release for testing
*/

// MIDI STATUS BYTES
byte midi_start    = 0xfa;
byte midi_stop     = 0xfc;
byte midi_clock    = 0xf8;
byte midi_continue = 0xfb;

// INTERNALS
byte play_flag = 0;
byte incoming_data;

// DATA FOR CALCULATIONS
unsigned int QPM = 4;  // QUARTERS PER MEASURE
unsigned int NOM = 1;  // MINIMUM NUMBERS OF MEASURE OF THE LOOP
unsigned int PPQ = 24; // TICK OR PULSES PER QUARTER
unsigned int BPM = 0;  // BEATS PER MINUTE

// TIMINGS AND COUNTERS
unsigned int Tick_Counter = 0;
unsigned long jamsync_measure_timer;
unsigned long jamsync_link_timer ;

// SYSEX ARRAYS
unsigned int jamsync_sync[] {0x00,0xF0,0x00,0x00,0x10,0x7F,0x62,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xF7};
unsigned int jamsync_link[] {0x00,0xF0,0x00,0x00,0x10,0x7F,0x62,0x01,0x00,0x01,0x01,0xF7};
unsigned int jamsync_stop[] {0x00,0xF0,0x00,0x00,0x10,0x7F,0x62,0x01,0x4A,0x01,0x04,0x46,0x30,0x42,0x02,0x04,0x40,0x03,0x4E,0x46,0x2E,0x40,0x04,0x5C,0xF7};

void setup() {
  Serial.begin(31250);
	jamsync_link_timer = micros();
	}

void loop() {
  
  // READ THE INCOMING MIDI
	CheckSerial();
  
  // KEEP LINK ACTIVE
	if (micros() - jamsync_link_timer > 400000) {sendLink();}
  
  // SEND SYNC COMMAND
	if (Tick_Counter == NOM*QPM*PPQ) {
    GetBPM();
	  Sendjamsync_sync();
    Tick_Counter = 0;
	}
   
}
	
	void CheckSerial() {
		if(Serial.available() > 0) {
			incoming_data = Serial.read();
      
			if(incoming_data == midi_start) {
				play_flag = 1;
        if (BPM > 0) {Sendjamsync_sync();}
				jamsync_measure_timer = micros();
				Tick_Counter = 0; 
			}
			else if(incoming_data == midi_continue) {
				play_flag = 1;
			}
			else if(incoming_data == midi_stop) {
				play_flag = 0;
        sendStop();
			}
			else if((incoming_data == midi_clock) && (play_flag == 1)) {
				Tick_Counter++;
			}
		}
	}
 
  void sendLink() {
    for (int i = 1; i < 12; i++) { Serial.write(jamsync_link[i]); }
    jamsync_link_timer = micros();
  }
  
  void sendStop() {
    for (int i = 1; i < 25; i++) { Serial.write(jamsync_stop[i]); }
  }
	
	void GetBPM() {
		jamsync_measure_timer = micros() - jamsync_measure_timer;
		BPM = QPM * 60000000 / jamsync_measure_timer;
		jamsync_measure_timer = micros();
	}
	
	void Sendjamsync_sync(){
	  unsigned long LoopTime;
    int b163 = 0;
    int w;
		int x;
		int y;
		int z=0; //xor checksum

    
    LoopTime = floor(1000* NOM * QPM * 60.0 / BPM);
    x = floor(log(LoopTime/2000.0)/log(4.0));
    b163 = (LoopTime/(2000.0 * pow(4.0,x)))>2;
    y = 2 * pow(2,b163) * pow(4,x);
    w = floor(LoopTime / y);

		// BPM
		jamsync_sync[8]  = 66 + 8 * ((63<BPM) && (BPM<128) || BPM>191) ;
		jamsync_sync[12] = (4*BPM>127 && 4*BPM<256)*(4*BPM-128) + 
		                   (2*BPM>127 && 2*BPM<256)*(2*BPM -128) + 
		                   (BPM>127 && BPM<256)*(BPM-128);
		jamsync_sync[13] = 1*(BPM>127)+66;
		
		// lenght
		jamsync_sync[16] = 64 + 8*b163;
		jamsync_sync[21] = 64 + x;
		jamsync_sync[20] = 128 *(0.001*w-1);
		jamsync_sync[19] = pow(128.0,2) *(0.001*w-1 - jamsync_sync[20]/128.0);
		jamsync_sync[18] = pow(128.0,3) *(0.001*w-1 - jamsync_sync[20]/128.0 - jamsync_sync[19]/pow(128.0,2));
		
		// command
		jamsync_sync[22] = 5;
		
		// checksum
		for (int i = 8; i < 23; i++) {
			z = z ^ int(jamsync_sync[i]); // checksum XOR
		}
		jamsync_sync[23] = z;
		
		for (int i = 1; i < 25; i++) {
			Serial.write(int(jamsync_sync[i]));
		}
	}
	

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

r1, fix LoopTime lenght to improve the sync of 1st loop.

Code: Select all

#include <math.h>

/*
 * THANKS TO
 * @ https://www.freestompboxes.org/viewtopic.php?f=1&t=26184
 * to the user pz to have starting all
 * to the user smithoid to initial analysis
 * to the user ashimoke for sharing his data and support
 * @ http://fuzzysynth.blogspot.it/2015/06/digitech-jam-man.html
 * to fuzzy music for sharing the work on protocol
 * @ http://chemiker1981.blogspot.it/2010/10/1-reading-midi-clock-to-read-midi-clock.html
 * to chemiker1981 for midi clock code
 * 
 * r0 - initial release for testing
 * r1 - LoopTime = effective lenght
*/

// MIDI STATUS BYTES
byte midi_start    = 0xfa;
byte midi_stop     = 0xfc;
byte midi_clock    = 0xf8;
byte midi_continue = 0xfb;

// INTERNALS
byte play_flag = 0;
byte incoming_data;

// DATA FOR CALCULATIONS
unsigned int QPM = 4;  // QUARTERS PER MEASURE
unsigned int NOM = 1;  // MINIMUM NUMBERS OF MEASURE OF THE LOOP
unsigned int PPQ = 24; // TICK OR PULSES PER QUARTER
unsigned int BPM = 0;  // BEATS PER MINUTE

// TIMINGS AND COUNTERS
unsigned int Tick_Counter = 0;
unsigned long jamsync_measure_timer;
unsigned long jamsync_loop_timer;
unsigned long jamsync_link_timer ;


// SYSEX ARRAYS
unsigned int jamsync_sync[] {0x00,0xF0,0x00,0x00,0x10,0x7F,0x62,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xF7};
unsigned int jamsync_link[] {0x00,0xF0,0x00,0x00,0x10,0x7F,0x62,0x01,0x00,0x01,0x01,0xF7};
unsigned int jamsync_stop[] {0x00,0xF0,0x00,0x00,0x10,0x7F,0x62,0x01,0x4A,0x01,0x04,0x46,0x30,0x42,0x02,0x04,0x40,0x03,0x4E,0x46,0x2E,0x40,0x04,0x5C,0xF7};

void setup() {
  Serial.begin(31250);
	jamsync_link_timer = micros();
	}

void loop() {
  
  // READ THE INCOMING MIDI
	CheckSerial();
  
  // KEEP LINK ACTIVE
	if (micros() - jamsync_link_timer > 400000) {sendLink();}
  
  // SEND SYNC COMMAND
	if (Tick_Counter == NOM*QPM*PPQ) {
    GetBPM();
	  Sendjamsync_sync();
    Tick_Counter = 0;
	}
   
}
	
	void CheckSerial() {
		if(Serial.available() > 0) {
			incoming_data = Serial.read();
      
			if(incoming_data == midi_start) {
				play_flag = 1;
        if (BPM > 0) {Sendjamsync_sync();}
				jamsync_measure_timer = micros();
				Tick_Counter = 0; 
			}
			else if(incoming_data == midi_continue) {
				play_flag = 1;
			}
			else if(incoming_data == midi_stop) {
				play_flag = 0;
        sendStop();
			}
			else if((incoming_data == midi_clock) && (play_flag == 1)) {
				Tick_Counter++;
			}
		}
	}
 
  void sendLink() {
    for (int i = 1; i < 12; i++) { Serial.write(jamsync_link[i]); }
    jamsync_link_timer = micros();
  }
  
  void sendStop() {
    for (int i = 1; i < 25; i++) { Serial.write(jamsync_stop[i]); }
  }
	
	void GetBPM() {
		jamsync_measure_timer = micros() - jamsync_measure_timer;
    jamsync_loop_timer = jamsync_measure_timer;
		BPM = round(QPM * 60000000.0 / jamsync_measure_timer);
		jamsync_measure_timer = micros();
	}
	
	void Sendjamsync_sync(){
	  unsigned long LoopTime;
    int b163 = 0;
    int w;
		int x;
		int y;
		int z=0; //xor checksum

    
    // LoopTime = floor(1000* NOM * QPM * 60.0 / BPM);
    LoopTime = floor(jamsync_loop_timer / 1000.0);
    x = floor(log(LoopTime/2000.0)/log(4.0));
    b163 = (LoopTime/(2000.0 * pow(4.0,x)))>2;
    y = 2 * pow(2,b163) * pow(4,x);
    w = floor(LoopTime / y);

		// BPM
		jamsync_sync[8]  = 66 + 8 * ((63<BPM) && (BPM<128) || BPM>191) ;
		jamsync_sync[12] = (4*BPM>127 && 4*BPM<256)*(4*BPM-128) + 
		                   (2*BPM>127 && 2*BPM<256)*(2*BPM -128) + 
		                   (BPM>127 && BPM<256)*(BPM-128);
		jamsync_sync[13] = 1*(BPM>127)+66;
		
		// lenght
		jamsync_sync[16] = 64 + 8*b163;
		jamsync_sync[21] = 64 + x;
		jamsync_sync[20] = 128 *(0.001*w-1);
		jamsync_sync[19] = pow(128.0,2) *(0.001*w-1 - jamsync_sync[20]/128.0);
		jamsync_sync[18] = pow(128.0,3) *(0.001*w-1 - jamsync_sync[20]/128.0 - jamsync_sync[19]/pow(128.0,2));
		
		// command
		jamsync_sync[22] = 5;
		
		// checksum
		for (int i = 8; i < 23; i++) {
			z = z ^ int(jamsync_sync[i]); // checksum XOR
		}
		jamsync_sync[23] = z;
		
		for (int i = 1; i < 25; i++) {
			Serial.write(int(jamsync_sync[i]));
		}
	}
	

User avatar
ashimoke
Information
Posts: 3
Joined: 19 Apr 2012, 20:11
Been thanked: 6 times

Post by ashimoke »

THIS THING WORKS!

User avatar
ashimoke
Information
Posts: 3
Joined: 19 Apr 2012, 20:11
Been thanked: 6 times

Post by ashimoke »

When I got Calde's code working I was thinking about permanent instalation of the arduino sync box and decided to make it internal. I used arduino nano and placed it in the battery compartment — I never use batteries with this pedal so it was an easy decision. I unsoldered the Sync in jack and put panel mounted 3,5mm for midi input. There is a on/off toggle switch so the pedal can be used standalone.
It also got a new knob and paintjob. Which I don't like that much now, will work on that some more.
Image

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

Hey Ashimoke, great work! I'm happy that my code works well for you. Congrats for the vintage layout.

If someone wants to try, I've used the following schematics https://www.olimex.com/Products/Duino/S ... DI-sch.pdf without the analog inputs and leds.

tips: When jamsync and an arduino running my code are properly connected, jamsync flashes both 1st and 3rd led three times, to show an active working link.

User avatar
Chris_Summerfield
Information
Posts: 4
Joined: 05 Nov 2015, 09:03

Post by Chris_Summerfield »

Hey all, I have a jamman express solo xt I'd like to set to loop exactly to tempo to be in time with a drum machine. Where's the best place for me to start reading to understand all that you've been talking about. Thanks.

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

hi.Chris, all you need is in this topic, including the link for the work of fuzzysynth. I have not found other useful informations elsewhere.
the posts from the user 'pz' explain how to connect a midi cable to the jamman. After you need an arduino (uno for example) and a midi shield (buy or diy). Finally you can upload my sketch in to the arduino, connect the midi out to the jamsync in port, the midi in to the midi out of the drummachine.

I have tested succesfully with a korg electribe er1. Every device that sends midiclock data should work.

My sketch analyzes the duration of the first measure and then it starts to sync the jamman to every measure (or a multiple of...) on the midi stream.

User Ashimoke offers a solution for mounting the arduino inside the jamman.

do everything at you risk, a wrong connection can damage your devices.

User avatar
jorymil
Information
Posts: 3
Joined: 16 Feb 2017, 06:49
Has thanked: 3 times

Post by jorymil »

Thanks to all who've made this work. I've always wanted to start tinkering with Arduinos - you just gave me an excuse to go out and buy one. My aims are pretty modest: just act as a battery-powered JamSync master so I can add a dedicated footswitch or two without having to go out and buy a Solo XT. Being battery-powered is helpful for outdoor use, and if I can't power an Arduino Nano (or similar) with a 9V battery, then I need to go back and retake my college electronics classes. I don't need a MIDI clock source if I'm only sending out JamSync traffic, so one of the smaller, shield-less models will hopefully suffice.

I just picked up a Vocal XT, which is now selling for $35. 3 of these plus an arduino and a fs3x (or just the fs3x parts) makes for a pretty badass little 3-track looper for under $150. The Express XTs can be had for under $60 now too. Hoping that people don't catch on until I've bought one or two more ;-)

John

User avatar
jorymil
Information
Posts: 3
Joined: 16 Feb 2017, 06:49
Has thanked: 3 times

Post by jorymil »

@calde: Enrico, have you thought about putting your code out on GitHub/GitLab/etc.?

John

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

jorymil wrote:@calde: Enrico, have you thought about putting your code out on GitHub/GitLab/etc.?

John
Good idea,

I've uploaded the code to https://github.com/Calde-github/Looperino.

Please note that:
1st time on Github
1st time on Arduino

Regards,
Enrico

User avatar
teebee
Information
Posts: 4
Joined: 27 Apr 2017, 21:30
Has thanked: 2 times

Post by teebee »

Hello to all!
Now, this is a cool thread, thanks a lot to all the work that has been done on this topic so far! It is exactly what I was looking for. I am trying to get this sync setup working. So far I do get the sync signal (Jamman XT express LED 1 and 3 blinking a couple of times), after pressing record the red LED is blinking in wait mode but it never goes past this state. Any idea what I could have done wrong? I am not exactly sure how to connect the output of the arduino to the jamman though, do I need the 220 resistors (as per the MIDI specs) OR a voltage divider? DO I need to connect ground? Any help is greatly appreciated!

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

Hi Teebee,

if jamsync blink to sync signal, I think that Arduino-Jamsync side is Ok.

Jamsync wait for a start command... what do you connect to arduino as "Master clock"?

I made the interface at olimex https://www.olimex.com/Products/Duino/S ... DI-sch.pdf without the the components attached to A0-A5 and D6-D7

User avatar
teebee
Information
Posts: 4
Joined: 27 Apr 2017, 21:30
Has thanked: 2 times

Post by teebee »

@calde: Thanks a lot for your reply, I came to the same conclusion. I should have a closer look at the MIDI-in section, as I get a sync signal even without MIDI-in connected. So far I tried 2 different schematics (olimex and another one I found), which I hooked up to a Yamaha DX-200 that is definitely sending a MIDI-clock. Will the RX LED on the arduino itself blink upon receiving a MIDI-in signal? Knowing this would be really helpful for further troubleshooting.

User avatar
calde
Information
Posts: 13
Joined: 09 Sep 2016, 23:11
Has thanked: 2 times
Been thanked: 10 times

Post by calde »

@Teebee, the link between arduino and digitech is up with or without midi device connected.

Midi is received silently, no led flashes for debug.

For debug you could put led ON after
incoming_data = Serial.read();
and put OFF into
void sendLink() {

try...

User avatar
Nikogo
Information
Posts: 6
Joined: 30 May 2017, 01:52
Has thanked: 2 times
Been thanked: 4 times

Post by Nikogo »

Hello gentlemen,
Looks that you made a fantastic work and that is exactly what I need.
I am first time here. Please advise, I need to synchronize the JamMan Solo XT to the BeatBuddy (the BB - master; JM - slave).
Is it possible to use your code on Attiny85 with a schematics similar to https://ambinaut.files.wordpress.com/20 ... =768&h=320
Of course the output would be modified for the JM sync IN.
I am confident enough with electronics, but have no experience with programming.
Thanks for any help.

User avatar
teebee
Information
Posts: 4
Joined: 27 Apr 2017, 21:30
Has thanked: 2 times

Post by teebee »

@Nikogo: I don't think this will work, the proposed circuit is for the footswitch tap tempo and no midi signal as far as I understand.
@calde: Thanks for the code proposal, have to get back into this right now, as I have been travelling recently.

Post Reply