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
FernandoSérgio
Information
Posts: 2
Joined: 19 Jun 2021, 16:23

Post by FernandoSérgio »

Hey Jamman Synkers, :D

I have tried Calde's code (looperino), and I think I'm having problem with the function Sendjamsync_sync()

I mean, I can connect my AKAY XR20 MIDI OUT to JAMSync IN on my Jamman Solo XT (firmware 1.06) But BPM Sync doesn't happen.

Solo XT shows SYNC on the display and a dot is seen at the bottom right. If I stop XR20, Solo XT stops. I think SYNC messages are not been considered by Solo XT, somehow.

I have made some debug on Sendjamsync_sync() to show the BPM + JamSync container sent to Solo XT. But I don't have a clue...

IF someone could please help, I don't think I will get help from somewhere else, since it's too specific of a problem

Thanks,

Code: Select all

JAMSYNCED BPM:55
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x42 0x1 0x0 0x0 0x5C 0x42 0x2 0x4 0x48 0x3 0x16 0x32 0xB 0x40 0x5 0x7A 0xF7 
JAMSYNCED BPM:90
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x34 0x42 0x2 0x4 0x40 0x3 0xD  0x2F 0x2A 0x40 0x5 0x35 0xF7 
JAMSYNCED BPM:91
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x36 0x42 0x2 0x4 0x40 0x3 0x7B 0x28 0x28 0x40 0x5 0x44 0xF7 
JAMSYNCED BPM:100
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x48 0x42 0x2 0x4 0x40 0x3 0x4  0x2C 0x19 0x40 0x5 0x70 0xF7 
JAMSYNCED BPM:101
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x4A 0x42 0x2 0x4 0x40 0x3 0x23 0x36 0x17 0x40 0x5 0x41 0xF7 
JAMSYNCED BPM:104
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x50 0x42 0x2 0x4 0x40 0x3 0x4D 0x19 0x13 0x40 0x5 0x1E 0xF7 
JAMSYNCED BPM:107
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x56 0x42 0x2 0x4 0x40 0x3 0xA  0x2E 0xF  0x40 0x5 0x74 0xF7 
JAMSYNCED BPM:113
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x62 0x42 0x2 0x4 0x40 0x3 0x7B 0x28 0x8  0x40 0x5 0x30 0xF7 
JAMSYNCED BPM:120
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x70 0x42 0x2 0x4 0x48 0x3 0x6C 0x4E 0x7F 0x3F 0x5 0x53 0xF7 
JAMSYNCED BPM:121
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x72 0x42 0x2 0x4 0x48 0x3 0x6D 0x79 0x7D 0x3F 0x5 0x65 0xF7 
JAMSYNCED BPM:140
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x42 0x1 0x0 0x0 0xC  0x43 0x2 0x4 0x48 0x3 0x34 0x11 0x5B 0x3F 0x5 0x5  0xF7 
JAMSYNCED BPM:142
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x42 0x1 0x0 0x0 0xE  0x43 0x2 0x4 0x48 0x3 0x67 0x77 0x57 0x3F 0x5 0x3E 0xF7 
JAMSYNCED BPM:191
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x42 0x1 0x0 0x0 0x3F 0x43 0x2 0x4 0x48 0x3 0x13 0x31 0x20 0x3F 0x5 0x4A 0xF7 
JAMSYNCED BPM:199
0xF0 0x0 0x0 0x10 0x7F 0x62 0x1 0x4A 0x1 0x0 0x0 0x47 0x43 0x2 0x4 0x48 0x3 0x2B 0xE  0x1A 0x3F 0x5 0x7  0xF7 

User avatar
FernandoSérgio
Information
Posts: 2
Joined: 19 Jun 2021, 16:23

Post by FernandoSérgio »

Sorry about that, my fault.

I guess the code only works when you select an empty slot on Jamman, so that BPM will be set from the jamsync SYNC container sent over Jamsync input (coming from arduino).

At first, I was trying to see the BPM change a slot already pre-recorded on Jamman, this code does not do that, or it is a limitation of Jamman itself (not setting BPM from jamsync SYNC container if slot is not empty).

User avatar
Bernys
Information
Posts: 1
Joined: 10 Jul 2021, 20:30
Has thanked: 1 time

Post by Bernys »

calde wrote: 29 Jan 2017, 22:00 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]));
		}
	}
	
Hi calde!
Very nice work on reversing the jamsync protocol!
I have used it to sync my jamman express XT to Ableton Live with an Arduino Micro as MIDI USB device and it works perfectly!

Now I want to do the opposite: Sync Ableton Live to my jamman extress XT. But I cannot figure out how to calculate the BPM. And I'm afraid I cannot reverse it from your code.
I have found this calculation in another forum but it doesn't work neither:

Code: Select all

if byte 8 is 42, tempo = 32 + byte 12 / 4
if byte 8 is 4A, tempo = 64 + byte 12 /2
if byte 13 is 43, tempo = 128 + byte 12
Do you have a description of how the BPM is coded? Or do you have a formula?

Thanks a lot!

User avatar
Fattore_di_cresta
Information
Posts: 1
Joined: 08 Jan 2023, 13:46

Post by Fattore_di_cresta »

Hi, thanks to everyone who worked on this wonderful project!
I used an arduino uno and a DIY shield to connect the DAW (Reaper) to my Jamman solo xt. My need is to have the metronome in my (and others) headphones. I used @calde's code and it works perfectly but, since I often play in non-4/4 time signatures, I need to be able to change the signature.

Unfortunately the jamman always plays in 4/4 time, no matter if I change the signature from the pedal, from the DAW or if I change the QPM from the code.

Ideal for me would be to have the loop start and loop stop snap to each beat and not to the measure. Would it be possible to do this?

If not, how can I change the signature? would it also be possible to add buttons to the arduino so that you can change the signature?

Thank you very much for helping!

User avatar
vargero
Information
Posts: 1
Joined: 14 Jun 2023, 07:42

Post by vargero »

Hey guys! Anyone here had any luck with making this work? I’m trying with the JamMan Express XT and so far no luck. Instead of using a MIDI jack as on the schematics that @Calde suggested, I’m using a TSR connector for Out, connecting source 4 to ring, shield 2 to sleeve and sink 5 to tip.
Besides that, I’m reading the sync signal from the Volca FM which is also a TRS, so instead of MIDI in I did a similar connection to what I did on out, but I don’t believe it’s transmitting serial MIDI message, just the 5V 15ms pulse that I don’t know how to read.

Anyone ran into similar issues?

User avatar
Glin76
Information
Posts: 1
Joined: 25 Nov 2023, 11:07

Post by Glin76 »

Hello,
It works fine for me (Solo XT with arduino and OLIMEX MIDI shield)
The only thing is I've changed the code setting NOM = 4
That way when sequence is on and I launch the JamMan, it starts at beat 1 ( when setting NOM=1, it starts at the next beat)

Post Reply